;WATCH.ASM
;resident routine watches programs going resident
;and keeps a list of changes in interrupt vectors to an internal data structure
;==============================================================================
; written for MASM (version 4 or later) or TASM
; by Kim Kokkonen, TurboPower Software
; telephone: 408-438-8608, Compuserve 72457,2131
;==============================================================================
; VERSION 2.2  3/4/87
;   First release, version to be consistent with MAPMEM.PAS
; VERSION 2.3  5/1/87
;   Sets up a separate stack for the interrupt handlers
;     to solve problem first noticed with EMMCACHE and WATCH
;   Changes approach - store all vector information in RAM buffers
;   No disk access at all by WATCH
;   Better check for multiple installation
;   Changed to use MASM
; VERSION 2.4  5/17/87
;   for consistency with RELEASE
; VERSION 2.5  5/28/87
;   save flags in int21 handler
; VERSION 2.6  1/15/89
;   for consistency with MARK
; VERSION 2.7
;   skipped
; VERSION 2.8
;   for consistency with MARKNET/RELNET
; VERSION 2.9
;   for consistency with MARKNET/RELNET

Cseg      segment public para
          assume  cs:Cseg, ds:nothing, es:nothing, ss:nothing

          org     080H
cmdline   label   byte                 ;pointer to command line

          org     100H
comentry: jmp     init

;******************************************************************************
;resident data section follows
;******************************************************************************

;put the following in the MAP file
public bmesg,origv,prevv,vpos,vchg

;resident data structures not part of COM file
vchg           equ     220H            ;location of data area (>= offset bmesg)
vrecsize       equ     8               ;number of bytes per vector change record
vsize          equ     80H*vrecsize    ;size of vector change area in bytes
                                       ;space for 128 changes of vector here

;vector table buffers
origv          equ     vchg+vsize      ;location of original vector table
veclen         equ     400H            ;size of vector table in bytes
prevv          equ     origv+veclen    ;location of current vector table
newstackpos    equ     prevv+veclen    ;location of newstack
ssize          equ     0080H           ;number of bytes in temporary stack
newloc         equ     newstackpos+ssize ;location for relocated installation code

               even
vpos           dw      0               ;next position to write in data area

;temporary stack used by interrupt handler
newss          dw      0               ;segment of our temporary stack
newsp          dw      0               ;initial stack pointer

;information saved about the calling program
oldss          dw      ?               ;stack segment
oldsp          dw      ?               ;stack pointer
curpsp         dw      ?               ;program segment

;id code for a PSP data block
pspid          equ     0FFFFH          ;id used to indicate a PSP block
;int16H function call for id check
getid          equ     'wa'            ;function code for int16
chkid          equ     'WA'            ;returned by int16 getid check

;previous interrupt handlers
kbd_int        label dword
old16          dw 2 dup (?)            ;old int16 vector
dos_int        label dword
old21          dw 2 dup (?)            ;old int21 vector
tsr_int        label dword
old27          dw 2 dup (?)            ;old int27 vector

;***********************************************************************
;interrupt handler for int16
;used only to avoid reinstallation
newint16 proc  near
         assume ds:nothing
         pushf                  ;save application flags
         sti
         cmp    ax,getid        ;see if our id function
         jne    ex16            ;no, pass on to previous int16

         mov    ax,chkid        ;return id code
         popf
         iret                   ;back to caller

ex16:    popf
         jmp    kbd_int         ;transfer control to the previous int16
newint16 endp

;***********************************************************************
;interrupt handler for int21
newint21 proc  near
         assume ds:nothing
         pushf                  ;save flags
         cmp    ah,31H
         jne    ex21
         call   checkvec        ;call routine to check vector table
ex21:    popf
         jmp    dos_int         ;transfer control to the previous int21 vector
newint21 endp

;***********************************************************************
;interrupt handler for int27
newint27 proc  near
         assume ds:nothing
         call   checkvec
ex27:    jmp    tsr_int         ;transfer control to the previous int27 vector
newint27 endp

;***********************************************************************
;procedure checkvec
;  compares vectors to previous installation
;  stores a new vector buffer
;  writes information to wfile

checkvec proc  near
       assume ds:nothing

;save current stack
       mov     oldss,ss
       mov     oldsp,sp

;switch to our stack
       cli
       mov     ss,newss
       mov     sp,newsp
       sti

;store registers
       push    ax
       push    bx
       push    cx
       push    dx
       push    si
       push    di
       push    ds
       push    es

;get DS set up as CS
       mov     ax,cs
       mov     ds,ax
       assume  ds:cseg

       mov     ah,51H           ;get current PSP
       pushf
       call    dos_int          ;bx returns PSP

       call    vechdr           ;store the PSP segment of the program going resident
       call    cmpvec           ;scan the vector table looking for changes from our buffer
       mov     di,prevv         ;save the new version of the vector table
       call    savevec

;restore registers
       pop     es
       pop     ds
       assume  ds:nothing
       pop     di
       pop     si
       pop     dx
       pop     cx
       pop     bx
       pop     ax

;restore stack
       cli
       mov     ss,oldss
       mov     sp,oldsp
       sti

       ret
checkvec endp

;**************************************************************************
;procedure vechdr
;  writes a header to the vector data area for this new TSR
;  on entry:
;    bx has PSP of the new TSR
;
vechdr proc near
       assume ds:cseg
       cmp      vpos,vsize-vrecsize       ;assure room for next record
       ja       vecex                     ;ignore if no room

       push     di
       mov      di,vpos                   ;index into vchg array
       mov      word ptr [di+vchg],pspid  ;store id word
       mov      word ptr [di+vchg+2],bx   ;store PSP value
                                          ;two words in record left unitialized
       add      vpos,vrecsize             ;move to next data element
       pop      di

vecex: ret
vechdr endp

;**************************************************************************
;procedure wrchg
;  writes information about changed vectors to the data area
;  on entry
;    ax has changed vector number
;    ds points to segment 0
;  on exit:
;    flags changed
;
wrchg  proc     near
       assume   ds:nothing, es:nothing
       cmp      vpos,vsize-vrecsize ;assure room for next record
       ja       wrcex

       push     ax
       push     si
       push     di

       mov      di,vpos             ;index into vchg array
       mov      cs:[di+vchg],ax     ;store interrupt vector number

       mov      si,ax
       shl      si,1
       shl      si,1                ;get address of vector
       mov      ax,[si]             ;get offset of vector
       mov      cs:[di+vchg+2],ax   ;store vector offset
       mov      ax,[si+2]           ;get segment
       mov      cs:[di+vchg+4],ax   ;store segment
                                    ;one word in record left unused
       add      vpos,vrecsize       ;move to next data element

       pop      di
       pop      si
       pop      ax

wrcex: ret
wrchg  endp

;**************************************************************************
;procedure cmpvec
;  compares vectors in buffer to those in use
;  writes numbers of those different to data area
;  on exit:
;    ax,si,di destroyed
;    flags changed
;
cmpvec  proc   near
        assume ds:nothing, es:nothing
        push    ds
        push    es

        xor     si,si           ;source offset 0
        mov     ds,si           ;source address segment 0
        mov     ax,cs
        mov     es,ax
        xor     ax,ax           ;vector counter
        mov     di,prevv        ;destination offset
        cld                     ;upward direction

nexvec: cmpsw                   ;compare offsets
        je      cmpseg          ;compare segments if offsets equal
        call    wrchg           ;write changed vector
        cmpsw                   ;compare next word, ignore result
        jmp short vecinc

cmpseg: cmpsw                   ;compare segments
        je      vecinc
        call    wrchg           ;write changed vector

vecinc: inc     ax              ;next vector number
        cmp     ax,00FFH
        jbe     nexvec          ;continue until 256 vectors checked

        pop     es
        pop     ds
        ret
cmpvec  endp

;**************************************************************************
;procedure savevec
;  saves image of interrupt vectors
;  on entry:
;    di has destination offset
;  on exit:
;    ax,cx,si,di destroyed
;    flags changed
;
savevec proc   near
        assume ds:nothing, es:nothing
        push    ds
        push    es
        xor     si,si           ;offset 0
        mov     ds,si           ;source address segment 0
        mov     ax,cs
        mov     es,ax           ;destination always in this code segment
        mov     cx,200H         ;512 integers to store
        cld                     ;copy up
        rep     movsw           ;copy vectors to our table
        pop     es
        pop     ds
        ret
savevec endp                    ;of proc savevec


;**************************************************************************
;resident portion above, temporary portion below
;**************************************************************************

;temporary strings
               even
bmesg   db     13,10,'Cannot install WATCH more than once....',13,10,36
mesg    db     13,10,'WATCH 2.9 successfully installed',13,10,36
pname   db     'TSR WATCHER'
plen    equ    $-pname             ;length of string

;**************************************************************************
;install new handlers for DOS go-resident services
init    proc   near
        assume ds:cseg

;use int 16h test to check for previous installation
        mov     ax,getid        ;int16h diagnostic request
        int     16h             ;now, ax=chkid if installed
        cmp     ax,chkid        ;TSR already installed?
        jne     success         ;no - jump if not installed

;error exit
        mov    dx,offset bmesg  ;error message
        mov    ah,09H
        int    21H              ;DOS print string
        mov    ax,4C01H         ;exit with error
        int    21H

;print a success message
success: mov    dx,offset mesg  ;start of message to write
         mov    ah,09H
         int    21H             ;DOS print string

;relocate ourselves out of the way of the vector tables
        mov     ax,cs
        mov     es,ax
        mov     di,newloc+10H
        push    di                    ;will act as a return address
        mov     si,offset newstk
        mov     cx,lastcode-newstk
        rep     movsb                 ;move code
        ret                           ;"return" to the relocated code

;initialize location of WATCH stack
newstk: mov     newsp,newstackpos+ssize
        mov     newss,cs        ;stack seg is code seg

;get int 16H vector
        mov    ax,3516H         ;GetVector DOS function call
        int    21H
        mov    old16,bx         ;store first word of old21
        mov    old16[2],es      ;store second word

;get int 21H vector
        mov    ax,3521H         ;GetVector DOS function call
        int    21H
        mov    old21,bx         ;store first word of old21
        mov    old21[2],es      ;store second word

;get int 27H vector
        mov    ax,3527H         ;GetVector DOS function call
        int    21H
        mov    old27,bx         ;store first word of old27 (offset)
        mov    old27[2],es      ;store second word (segment)

;put an id label at offset 80H to allow other programs to recognize WATCH
        mov    ax,cs
        mov    es,ax
        mov    cx,plen          ;length of name string
        mov    si,offset pname  ;offset of name string
        mov    di,offset cmdline ;offset of DOS command line
        cld                     ;transfer in forward direction
        mov    al,cl
        stosb                   ;store length byte first
        rep    movsb            ;transfer characters

;store image of original vector table (overwrites messages)
        mov    di,origv
        mov    ax,offset savevec
        call   ax               ;absolute call works as code is moved

;store it again into the current vector table
        mov    di,prevv
        mov    ax,offset savevec
        call   ax               ;absolute call works as code is moved

;install the new vectors
        mov    ax,2516H
        mov    dx,offset newint16
        int    21H
        mov    ax,2521H
        mov    dx,offset newint21
        int    21H
        mov    ax,2527H
        mov    dx,offset newint27
        int    21H

;terminate and stay resident
        mov    dx,(newloc+15) shr 4
        mov    ax,3100H        ;return success code
        int    21H

lastcode:
init    endp

Cseg    ends
        end     ComEntry
