Tapper speed patch by dOnut

To play this classic at playable speed, start it with "TAPPERP.COM".

This was really a pain. Not a single other game I patched up to now
was so complicated to get to work. So I don't comment any further
on the sick techniques I used. I know, this patch is far from
being perfect, but the game (including the bonus games) is
playable and I'm just not in the mood to continue this.

For those who want to get insane in the membrane, here's the
source code:

----------------------------------------------------------------------

Title patcher.asm
; by dOnut hOle

.286
.model tiny

.code
        org 100h
begin:  jmp init


; ****** MACROS ********
CR         equ     0Dh
LF         equ     0Ah
EOLN       equ     '$'
SIGNATURE  equ     0ffffh
SIGNATURE2 equ     0fffeh
SIGNATURE3 equ     0fffdh
SIGNATURE4 equ     0fffch
SIGNATURE5 equ     0fffbh
SIGNATURE6 equ     0fffah
INTERRUPT  equ     10h

;********* Resident Data **********
START             equ $
oldint_ofs        dw  0000h
oldint_seg        dw  0000h
int_no            db  INTERRUPT
was_sig6          db  0
data_start        db  01h                      ;Number of S&R-Loops
                  db  00h                      ;Flag: already Replaced ?
                  dw  0000                     ;offset to data_start2
                  db  02h                      ;search_seg1  (02=CS)
                  dw  01ec6h                   ;search_ofs1
                  dw  0000h                    ;search_seg_add1
                  db  09h                      ;search_bytes1
                  db  0bah,0dah,003h,0ech      ;search_data1
                  db  0a8h,008h,0ech,0a8h,008h
                  db  11                       ;replace_loops

                  db  02h                      ;replace_seg1 (02=CS)
                  dw  01ec6h                   ;replace_ofs1
                  dw  0000h                    ;replace_seg_add1
                  db  09h                      ;replace_bytes1
                  db  0b8h,0feh,0ffh           ;replace_data1
                  db  0cdh,010h,90h,90h,90h,90h

                  db  02h                      ;replace_seg2 (02=CS)
                  dw  02516h                   ;replace_ofs2
                  dw  0000h                    ;replace_seg_add2
                  db  07h                      ;replace_bytes2
                  db  0b8h,0fdh,0ffh           ;replace_data2
                  db  0cdh,010h,090h,090h

                  db  02h                      ;replace_seg3 (02=CS)
                  dw  02539h                   ;replace_ofs3
                  dw  0000h                    ;replace_seg_add3
                  db  07h                      ;replace_bytes3
                  db  0b8h,0fdh,0ffh           ;replace_data3
                  db  0cdh,010h,090h,090h

                  db  02h                      ;replace_seg4 (02=CS)
                  dw  0255ch                   ;replace_ofs4
                  dw  0000h                    ;replace_seg_add4
                  db  07h                      ;replace_bytes4
                  db  0b8h,0fdh,0ffh           ;replace_data4
                  db  0cdh,010h,090h,090h

                  db  02h                      ;replace_seg5 (02=CS)
                  dw  025dah                   ;replace_ofs5
                  dw  0000h                    ;replace_seg_add5
                  db  07h                      ;replace_bytes5
                  db  0b8h,0fdh,0ffh           ;replace_data5
                  db  0cdh,010h,090h,090h

                  db  02h                      ;replace_seg6 (02=CS)
                  dw  02669h                   ;replace_ofs6
                  dw  0000h                    ;replace_seg_add6
                  db  07h                      ;replace_bytes6
                  db  0b8h,0fdh,0ffh           ;replace_data6
                  db  0cdh,010h,090h,090h

                  db  02h                      ;replace_seg7 (02=CS)
                  dw  02404h                   ;replace_ofs7
                  dw  0000h                    ;replace_seg_add7
                  db  07h                      ;replace_bytes7
                  db  050h,0b8h,0fch,0ffh      ;replace_data7
                  db  0cdh,010h,058h

                  db  02h                      ;replace_seg8 (02=CS)
                  dw  027a1h                   ;replace_ofs8
                  dw  0000h                    ;replace_seg_add8
                  db  07h                      ;replace_bytes8
                  db  0b8h,0fdh,0ffh           ;replace_data8
                  db  0cdh,010h,90h,90h

                  db  02h                      ;replace_seg9 (02=CS)
                  dw  02828h                   ;replace_ofs9
                  dw  0000h                    ;replace_seg_add9
                  db  05h                      ;replace_bytes9
                  db  0b8h,0fdh,0ffh           ;replace_data9
                  db  0cdh,010h

                  db  02h                      ;replace_seg10 (02=CS)
                  dw  029ffh                   ;replace_ofs10
                  dw  0000h                    ;replace_seg_add10
                  db  08h                      ;replace_bytes10
                  db  0b8h,0fbh,0ffh           ;replace_data10
                  db  0cdh,010h,090h,090h,090h

                  db  02h                      ;replace_seg11 (02=CS)
                  dw  02b96h                   ;replace_ofs11
                  dw  0000h                    ;replace_seg_add11
                  db  05h                      ;replace_bytes11
                  db  0b8h,0fah,0ffh           ;replace_data10
                  db  0cdh,010h


save_sp           dw      0
                  db 100 DUP(0)
STACKFRAME        equ $

;***** a procedure of the interrupt-routine ******
set_address  proc near
          lodsb                     ; load segment from ds:si
          cmp al,00                 ; 00h=DS
          jz _DS_
          cmp al,01                 ; 01h=ES
          jz _ES_
          cmp al,02                 ; 02h=CS
          jz _CS_
          mov dx,ss                 ; >02h = SS
          jmp FOUR
_DS_:     mov dx,ss:[-02+bp]
          jmp FOUR
_ES_:      mov dx,ss:[-04+bp]
          jmp FOUR
_CS_:     mov dx,ss:[14h+bp]
FOUR:     lodsw                    ; load segment_ofs from ds:si
          mov di,ax                ; and store it into di
          lodsw                    ; load segment_add from ds:si 
          add ax,dx                ; add it to segment in dx
          mov es,ax                ; es:di = (seg+seg_add):seg_ofs
          ret
endp set_address

;******* this is the interrupt-routine *********
new_int   proc far
          pushf
          pusha

          cmp ax,SIGNATURE         ; asking for signature ???
          jne normal

givesig:  popa
          popf
          mov bx,SIGNATURE         ; signature FFFF in bx given back
          iret

normal:   mov bp,sp
          push ds
          push es
          cld
          push cs
          pop ds

          mov was_sig6,0
          cmp ax,SIGNATURE2        ; called from replacement
          jne sig3
          mov cx,01
          jmp waitscan

sig3:     cmp ax,SIGNATURE3
          jne sig4
          mov cx,03
          jmp waitscan

sig4:     cmp ax,SIGNATURE4
          jne sig5
          mov cx,03
          jmp waitscan

sig5:     cmp ax,SIGNATURE5
          jne sig6
          mov cx,100
          jmp waitscan


sig6:     cmp ax,SIGNATURE6
          jne normal2
          mov cx,10
          mov was_sig6,1

waitscan: mov dx,03dah
wait1:    in al,dx
          test al,08h
          jz wait1
wait2:    in al,dx
          test al,08h
          jnz wait2
          loop waitscan

          cmp was_sig6,1
          je out2
          pop es
          pop ds
          popa
          popf
          iret
out2:     pop es
          pop ds
          popa
          popf
          mov si,[bp+4]
          mov bx,bp
          iret

normal2:  mov si,offset data_start
          lodsb                     ; load s&R-loops from ds:si
          cbw
          xchg ax,cx                ; set outer loop-counter
search:       push cx
              mov bx,si
              lodsb                 ; load replaced-flag from ds:si
              or al,al              ; flag = 0 ?
              lodsw                 ; load offset to next data from ds:si
              push ax
              jnz endloop           ; if flag <> 0 then jump over this loop
              call set_address      ; set address to search
              lodsb                 ; load search_bytes from ds:si
              cbw
              xchg ax,cx            ; and store it into inner loop-counter
              repz cmpsb            ; compare with search_data
              jnz endloop           ; not equal (string not found)
              lodsb                 ; load replace-loops from ds:si
              cbw
              xchg ax,cx            ; and store it into middle loop counter
Loop_me:          push cx
                  call set_address  ; set address to replace
                  lodsb             ; load replace_bytes from ds:si
                  cbw
                  xchg ax,cx        ; and store it into inner loop counter
                  repz movsb        ; replace bytes
                  ;mov byte ptr [bx],01  ; set flag to 'replaced'
                  pop cx
              loop loop_me  
endloop:      pop si                ; offset to next data
              pop cx
          loop search
          pop es
          pop ds
jumpold:  popa
          popf
          jmp dword ptr cs:[oldint_ofs]
new_int endp


;********* not resident data ************
FINISH      equ     $
MESS        db      'Tapper speed patch by dOnut hOle 1997',CR,LF,EOLN
PROG        db      'tapper.com',0
FNAME       db      60 dup(0)
PARAM       dw      0
            db      80h,0
PARAM1      dw      5 dup(0)
ERR1        db      'ERROR: Not enough memory.  '
ERR2        db      'Starting as TSR.',CR,LF,EOLN
REMOVE      db      'Removing TSR. ',CR,LF,EOLN

;******* the main program is starting here ************
init:
            cli
            mov [save_sp],sp
            mov sp,offset STACKFRAME    ; define stack
            sti

            mov ax,SIGNATURE
            xor bx,bx
            int INTERRUPT               ; check if already installed
            cmp bx,SIGNATURE
            jne install                 ; interrupt not yet hooked

            ;******* Uninstall TSR ********
            mov al,[int_no]
            mov ah,35h
            int 21h                     ; get segment of interrupt (es)
            push ds
            lds dx,dword ptr es:[oldint_ofs]
            mov ah,25h
            int 21h                     ; restore interrupt-vector
            pop ds

            push es
            mov es,es:[02ch]            ; get segment from PSP
            mov ah,49h
            int 21h                     ; free Memory
            pop es
            mov ah,49h
            int 21h                     ; free Memory

            push cs
            pop ds
            mov ah,9h
            mov dx,offset REMOVE
            int 21h                     ; print message
            jmp exit

install:    ;********* install interrupt **********
            mov ah,35h
            mov al,cs:[int_no]
            int 21h                     ; read interrupt vector
            mov cs:[oldint_ofs],bx      ; store offset 
            mov cs:[oldint_seg],es      ; store segment 
            mov ah,25h  
            mov dx,offset new_int
            int 21h                     ; set new interrupt vector

            mov ah,9h
            mov dx,offset MESS
            int 21h                     ; print message

            push cs
            pop es
            mov bx,offset ENDCODE       ;Get end of memory
            add bx,15
            mov cl,4                
            shr bx,cl                   ;div 16
            mov ah,4Ah            
            int 21h                     ;Reallocate memory
            jnc GetName                 ;If no error, continue

            mov ah,9h
            mov dx,offset ERR1
            int 21h                     ;Write error-string
            jmp tsr

GetName:    ;****** get the path from the environment ****
            mov es,cs:[02ch]            ;Segment of Environment
            xor di,di
            xor ax,ax
SearchEnv:    cmp al,es:[di]            ;Search two zeroes
              je SearchEnd
              mov cx,0ffffh
              repnz scasb
            jmp SearchEnv
SearchEnd:  add di,03                   ;es:di points at path+name
            push di
            mov cx,100h                 ;Search End of string 
            repnz scasb
            not cl                      ;Cl contains length
            std
            mov al,'\'
            repnz scasb                 ;search last '\'
            add cl,3
            cld
            ;******* assemble full name from path and given name ********
            pop di
            mov si,di
            mov di,offset FNAME
            push es
            pop ds
            push cs
            pop es
            rep movsb                   ; path is copied to NAME
            push cs
            pop ds
            mov si,offset PROG
COPY_PROG:  cmp byte ptr [si],00
            je COPY_END
            MOVSB                       ; PROG is copied to NAME
            jmp COPY_PROG               
COPY_END:   MOVSB
            ;****** load and execute the child program **********
            mov ax,cs
            mov PARAM,ax
            mov PARAM1,ax
            mov bx,offset PARAM
            mov dx,offset FNAME
            mov ah,4Bh
            mov al,00h
            int 21h                     ;Load and execute child program
            jnc uninstall               ;If no error, continue

            mov ah,9h                   
            mov dx,offset ERR2
            int 21h                     ;Write error- string
            jmp tsr

uninstall:  ;********** restore old interrupt ***********
            mov ah,25h                  ;Restore interrupt vector
            mov al,[int_no]
            mov dx,[oldint_ofs]
            mov bx,[oldint_seg]
            mov ds,bx
            int 21h

EXIT:       push cs
            pop ds
            cli
            mov sp,[save_sp]
            sti
            mov ax,4C00h                     ;Exit with error code 0
            int 21h

TSR:        ;********** go resident ***********
            mov dx,offset FINISH             ;Offset of booster
            add dx,15
            mov cl,4
            shr dx,cl
            mov ax,3100h
            int 21h                          ;Exit with ejection of booster
                                         
ENDCODE     equ $
end      begin
