home *** CD-ROM | disk | FTP | other *** search
- ;WATCH.ASM
- ;resident routine watches programs going resident
- ;and keeps a list of interrupt vector changes in an internal data structure
- ;==============================================================================
- ; to be assembled by TASM
- ; Copyright (c) 1986,1991 Kim Kokkonen, TurboPower Software.
- ; May be freely distributed but not sold except by permission.
- ; telephone: 719-260-6641, Compuserve 76004,2611
- ;==============================================================================
- ; version 2.2 3/4/87
- ; First release, version to be consistent with MAPMEM.PAS
- ; :
- ; long intervening history
- ; :
- ; version 3.0 9/24/91
- ; add tracking for TSRs that unload themselves
- ; add support for TSRs loaded high
- ; WATCH may be loaded high
- ; version 3.1 11/4/91
- ; rewrite again to solve problems with SWAPMM, FSP, DATAPATH, DATAMON
- ; version 3.2 11/22/91
- ; change method of accessing high memory
- ; deal with DOS 5 MODE int trapping (int seg < psp seg)
- ; version 3.3 1/8/92
- ; relocate AddChain code so that it doesn't get overwritten if there
- ; are lots of initial memory blocks
- ; version 3.4 2/14/92
- ; no change
- ;==============================================================================
- ;
- ;uncomment following line to generate more publics in MAP file
- ; debug = 1
-
- cseg segment public para
- assume cs:cseg, ds:nothing, es:nothing, ss:nothing
- locals @@
-
- org 080H
- cmdline label byte ;pointer to command line
-
- org 100H
- pentry: jmp init
-
- ;always put the following in WATCH.MAP to update MEMU.PAS
- public nextchange,emesg,changevectors,origvectors
-
- ;***********************************************************************
- ;data structures part of COM file
- even
- nextchange dw 0 ;next position to write in changes area
-
- firstmcb dw ? ;first MCB segment
- firsthimcb dw 0 ;first MCB segment in high memory
-
- ;temporary stack used by interrupt handler
- newsp dw ? ;initial stack pointer
- newss dw ? ;segment of our temporary stack (=cseg)
- tmpret dw ? ;used while switching stacks
-
- ;information saved about the calling program
- oldsp dw ? ;stack pointer
- oldss dw ? ;stack segment
-
- ;previous interrupt handlers
- dos_int label dword
- old21 dw 2 dup (?) ;old int21 vector
- tsr_int label dword
- old27 dw 2 dup (?) ;old int27 vector
-
- ;XMS access
- xmsadr label dword ;XMS control address
- xmsxxx dw 2 dup (0)
-
- ;id code for a PSP data block
- pspid equ 0FFFFH ;id used to indicate a PSP block
-
- ;structure of a changevectors data block
- pspblock struc
- id dw ? ;id word, always pspid
- psp dw ? ;psp segment
- len dw ? ;length of psp
- unu1 dw ? ;unused
- pspblock ends
- vecblock struc
- vec dw ? ;vector number 0..255
- veco dw ? ;vector offset
- vecs dw ? ;vector segment
- unu2 dw ? ;unused
- vecblock ends
-
- ;***********************************************************************
- ;resident data structures not part of COM file
- changevectors = offset emesg ;data area overwrites emesg & beyond
- vrecsize = 8 ;number of bytes per vector change record
- maxchanges = 128 ;maximum number of vector changes
- vsize = maxchanges*vrecsize ;size of vector change area in bytes
-
- ;vector table buffers
- origvectors = offset changevectors+vsize ;location of original vector table
- veclen = 1024 ;size of vector table in bytes
- newstackpos = origvectors+veclen ;location of newstack
- ssize = 128 ;number of bytes in temporary stack
- newloc = newstackpos+ssize ;location for relocated installation code
-
- ;***********************************************************************
- ;int21 handler
- ; traps functions 31, 49, 4C, and 7761
- int21h proc far
- ifdef debug
- public int21h
- endif
- assume ds:nothing
- pushf ;save flags
- sti ;allow interrupts
-
- cmp ah,31H ;terminate and stay resident call?
- jne @@1
- call addcurrpsp ;dx = paras to keep
- jmp short @@4
-
- @@1: cmp ah,49H ;deallocate block call?
- jne @@2
- call remblock ;remove specified block if a psp
- jmp short @@4
-
- @@2: cmp ah,4CH ;normal program halt?
- jne @@3
- call checkblocks
- jmp short @@4
-
- @@3: cmp ax,7761H ;"wa"tch ID call?
- jne @@4
- call checkblocks ;assure change list up to date
- push bp
- mov bp,sp ;set up stack frame
- and word ptr [bp+8],0FFFEH ;clear carry flag
- pop bp
- xchg ah,al ;flip ah and al as a signature
- mov bx,cs ;return WATCH psp in bx
- popf
- iret ;return to caller
-
- @@4: popf
- jmp dos_int ;let DOS take over
- int21h endp
-
- ;***********************************************************************
- ;int27 handler
- ; watches for programs going resident
- int27h proc far
- ifdef debug
- public int27h
- endif
- assume ds:nothing
- pushf
- sti
- push dx
- add dx,15 ;pass size of block in paras to addcurrpsp
- shr dx,1
- shr dx,1
- shr dx,1
- shr dx,1
- call addcurrpsp ;get current psp and add block to list
- pop dx
- popf
- jmp tsr_int
- int27h endp
-
- ;***********************************************************************
- ;get current PSP in bx and add new block
- ;entry: dx = paragraphs to keep
- addcurrpsp proc near
- ifdef debug
- public addcurrpsp
- endif
- assume ds:nothing
- call setup ;switch stacks and save registers
- assume ds:cseg
- mov ah,51H ;get current PSP in bx
- pushf
- call dos_int
- call addblock ;add block at bx, length dx to changes
- call shutdown ;restore registers and switch stacks
- assume ds:nothing
- ret
- addcurrpsp endp
-
- ;***********************************************************************
- ;remove PSP block, if any, specified by es
- remblock proc near
- ifdef debug
- public remblock
- endif
- assume ds:nothing
- call setup ;switch stacks and save registers
- assume ds:cseg
- mov bx,es ;save segment being deallocated in bx
- call matchpsp ;return offset in changevectors of segment
- or si,si ;any matching block?
- jz @@1
- call rempsp ;remove psp
- @@1: call shutdown ;restore registers and switch stacks
- assume ds:nothing
- ret
- remblock endp
-
- ;***********************************************************************
- ;scan chain of mcbs starting at segment ax
- ;remove halting psp from change list if needed
- checkchain proc near
- ifdef debug
- public checkchain
- endif
- @@1: mov es,ax
- mov bx,es:[0001h] ;bx = psp of block
- mov dx,es:[0003h] ;dx = len of block
- inc ax
- cmp ax,bx ;does psp = mcb+1?
- jne @@2 ;jump if not
- cmp ax,cx ;does psp = current program?
- je @@2 ;jump if so
- push dx
- call matchpsp ;find matching psp in changevectors
- pop dx
- or si,si ;is there a matching psp?
- jnz @@2 ;jump if so
- push ax
- push cx
- push dx
- call addblock ;add this psp
- pop dx
- pop cx
- pop ax
- @@2: cmp byte ptr es:[0000h],'Z' ;end of chain
- je @@3
- add ax,dx
- jmp @@1
- @@3: ret
- checkchain endp
-
- ;***********************************************************************
- ;check for new memory blocks and add if needed
- ;remove halting psp from change list if needed
- checkblocks proc near
- ifdef debug
- public checkblocks
- endif
- assume ds:nothing
- call setup ;switch stacks and save registers
- assume ds:cseg
-
- mov ah,51H ;get current psp in bx
- pushf
- call dos_int
-
- call matchpsp ;is current program in change list?
- or si,si
- jz @@0 ;jump if not
- call rempsp ;remove it if not
-
- @@0: mov cx,bx ;cx = psp of halting program
- mov ax,firstmcb ;start with first mcb
- call checkchain ;check this chain
- mov ax,firsthimcb ;scan high memory
- or ax,ax
- jz @@1
- call checkchain
- @@1: call shutdown ;restore registers and switch stacks
- assume ds:nothing
- ret
- checkblocks endp
-
- ;***********************************************************************
- ;setup routine for interrupt hook routines
- ; switches stacks, saves registers, sets ds=cs
- setup proc near
- ifdef debug
- public setup
- endif
- assume ds:nothing
- pop cs:tmpret ;save return address as we switch stacks
- mov oldss,ss ;save current stack
- mov oldsp,sp
- cli ;switch to our stack
- mov ss,newss
- mov sp,newsp
- sti
- push ax ;store registers
- push bx
- push cx
- push dx
- push si
- push di
- push bp
- push ds
- push es
- push cs ;set ds=cs
- pop ds
- assume ds:cseg
- push cs:tmpret ;return
- ret
- setup endp
-
- ;***********************************************************************
- ;shutdown routine for interrupt hook routines
- ; restores registers, switches stacks
- shutdown proc near
- ifdef debug
- public shutdown
- endif
- pop cs:tmpret
- pop es ;restore registers
- pop ds
- assume ds:nothing
- pop bp
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- cli ;restore stack
- mov ss,cs:oldss
- mov sp,cs:oldsp
- sti
- push cs:tmpret ;return
- ret
- shutdown endp
-
- ;***********************************************************************
- ;add specified block to changes
- ; entry: bx = psp of block, dx = length of block in paras
- addblock proc near
- ifdef debug
- public addblock
- endif
- assume ds:cseg
- call addhdr ;add a psp header block
- call addvecs ;add blocks for each hooked vector
- ret
- addblock endp
-
- ;***********************************************************************
- ;add header for a psp block
- ; entry: bx = psp of block, dx = length of block in paras
- ; exit: alters di
- addhdr proc near
- ifdef debug
- public addhdr
- endif
- assume ds:nothing
- mov di,nextchange
- cmp di,vsize-vrecsize ;assure room for next record
- ja @@1
- mov word ptr cs:changevectors[di].id,pspid
- mov cs:changevectors[di].psp,bx
- mov cs:changevectors[di].len,dx
- add di,vrecsize
- mov nextchange,di
- @@1: ret
- addhdr endp
-
- ;***********************************************************************
- ;add vector blocks for each hooked vector
- ; entry: bx = psp of block, dx = length of block in paras
- ; exit: alters ax,cx,dx,si,di,bp
- addvecs proc near
- ifdef debug
- public addvecs
- endif
- assume ds:cseg
- push ds
- add dx,bx ;now dx points to end of block
- mov di,nextchange ;cs:changevectors[di] -> output area
- xor si,si
- mov ds,si ;ds:si -> vectors
- assume ds:nothing
- xor cx,cx ;cx = vector counter
- cld ;forward
-
- @@1: lodsw ;ax = vector offset
- mov bp,ax ;save vector offset
- lodsw ;ax = vector segment
-
- cmp ax,dx ;is vector above high limit?
- jae @@3
-
- push cx
- mov cx,ax
- cmp bp,800h ;don't add unless a small offset
- ja @@1a ;(this is a trap for DOS 5 MODE)
-
- push bp
- shr bp,1
- shr bp,1
- shr bp,1
- shr bp,1
- add cx,bp ;ax = equivalent segment of interrupt
- pop bp
-
- @@1a: cmp cx,bx ;is vector above low limit?
- jb @@2
-
- pop cx
- cmp di,vsize-vrecsize ;room for another entry?
- ja @@3
- mov cs:changevectors[di].vec,cx ;save entry for this vector
- mov cs:changevectors[di].veco,bp
- mov cs:changevectors[di].vecs,ax
- add di,vrecsize
- jmp short @@3
-
- @@2: pop cx
- @@3: inc cx ;next vector
- cmp cx,0FFh
- jbe @@1
-
- mov nextchange,di
- pop ds
- assume ds:cseg
- ret
- addvecs endp
-
- ;***********************************************************************
- ;find changeblock matching psp
- ; entry: bx = psp to match
- ; exit: si = matching block, or 0 if none
- ; destroys dx
- matchpsp proc near
- ifdef debug
- public matchpsp
- endif
- assume ds:cseg
- mov si,offset changevectors
- mov dx,si
- add dx,nextchange ;dx = next unused spot in changevectors
- @@1: cmp si,dx ;end of table
- jae @@3
- cmp word ptr [si].id,pspid ;psp indicator?
- jnz @@2 ;jump if not
- cmp [si].psp,bx ;matching psp?
- jnz @@2 ;jump if not
- ret ;else return with match
- @@2: add si,vrecsize
- jmp @@1
- @@3: xor si,si ;no match if here
- ret
- matchpsp endp
-
- ;***********************************************************************
- ;remove all blocks associated with psp at offset si
- ; exit: alters cx,dx,si,di,es
- rempsp proc near
- ifdef debug
- public rempsp
- endif
- assume ds:cseg
- mov di,si ;save destination
- add si,vrecsize ;move to next record
- mov dx,offset changevectors
- add dx,nextchange ;dx = address of next unused
- @@1: cmp si,dx ;end of table?
- jae @@2 ;jump if so
- cmp word ptr [si].id,pspid ;next psp indicator?
- je @@2 ;jump if so
- add si,vrecsize ;next block
- jmp @@1 ;and loop
- @@2: mov cx,dx
- sub cx,si
- shr cx,1 ;cx = words to move
- push cs
- pop es ;es = ds = cs
- cld
- rep movsw ;copy down remaining blocks
- sub si,di
- sub nextchange,si ;update nextchange
- ret
- rempsp endp
-
- ;***********************************************************************
- ;resident portion above, temporary portion below
- ;***********************************************************************
- align 16
- emesg db 'Cannot install WATCH more than once....',13,10,36
- mesg db 'WATCH 3.4, Copyright 1991 TurboPower Software',13,10
- db 'Installed successfully',13,10,36
- pname db 'TSR WATCHER'
- plen equ $-pname ;length of string
-
- ;***********************************************************************
- init proc near
- ifdef debug
- public init
- endif
- assume ds:cseg
-
- ;use int 21h test to check for previous installation
- mov ax,7761H ;special id function
- int 21H
- jc @@1 ;not installed if function fails
- cmp ax,6177H
- jnz @@1 ;not installed if id code not returned
-
- ;error exit
- mov dx,offset emesg ;error message
- mov ah,09H
- int 21H ;DOS print string
- mov ax,4C01H ;exit with error
- int 21H
-
- ;not already installed
- @@1: mov dx,offset mesg ;success message
- mov ah,09H
- int 21H ;DOS print string
-
- ;initialize location of WATCH stack
- mov newsp,newstackpos+ssize
- mov newss,cs ;stack seg is code seg
-
- ;put an id label at offset 80H to allow other programs to recognize WATCH
- 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
-
- ;relocate ourselves out of the way of the resident tables
- push cs
- pop es
- mov di,newloc+10H
- push di ;will act as a return address
- mov si,offset @@2
- mov cx,endcode-@@2
- rep movsb ;move code
- ret ;"return" to the relocated code
-
- @@2:
- ;add psp records for all blocks already resident
- call adddummypsp
-
- ;store image of original vector table (overwrites messages and non-res code)
- push cs
- pop es
- mov di,origvectors
- push ds
- xor si,si ;offset 0
- mov ds,si ;source address segment 0
- mov cx,200H ;512 words to store
- rep movsw ;copy vectors to our table
- pop ds
-
- ;store current int 21 and 27 vectors
- mov ax,3527H
- int 21H
- mov old27,bx
- mov old27[2],es
- mov ax,3521H
- int 21H
- mov old21,bx
- mov old21[2],es
-
- ;install new vectors
- mov ax,2527H
- mov dx,offset int27h
- int 21H
- mov ax,2521H
- mov dx,offset int21h
- int 21H
-
- ;terminate and stay resident
- mov dx,newloc
- add dx,15
- mov cl,4
- shr dx,cl
- mov ax,3100H ;return success code
- int 21H ;note WATCH will track itself
- @@3:
- init endp
-
- ;***********************************************************************
- ;add dummy changeblocks for all psps already resident in chain starting at ax
- addchain proc near
- ifdef debug
- public addchain
- endif
- assume ds:cseg
- @@1: mov es,ax
- mov bx,es:[0001h] ;bx = psp of block
- mov dx,es:[0003h] ;dx = len of block
- inc ax
- cmp ax,bx ;does psp = mcb+1?
- jne @@2 ;jump if not
- cmp ax,newss ;does psp = WATCH itself?
- je @@2 ;jump if so
- mov cx,offset addhdr
- call cx
- ; call addhdr ;add a header for this block
- @@2: cmp byte ptr es:[0000h],'Z' ;end of chain
- je @@3
- add ax,dx
- jmp @@1
- @@3: ret
- addchain endp
-
- ;***********************************************************************
- ;return segment of first high memory mcb in ax
- findhimemstart proc near
- ifdef debug
- public findhimemstart
- endif
- mov ax,3000h ;get DOS version
- int 21H
- cmp al,3
- jb @@7 ;no XMS driver possible
- mov ax,4300h
- int 2Fh ;multiplex call for XMS
- cmp al,80h ;proper signature?
- jne @@7 ;no XMS driver
- mov ax,4310h
- int 2Fh
- mov xmsxxx,bx ;save XMS control address
- mov xmsxxx[2],es
- mov ah,10h
- mov dx,0FFFFh
- call xmsadr ;ask to allocate FFFF paras of UMB
- cmp bl,0B0h ;will fail with B0 if UMBs avail
- je @@0
- cmp bl,0B1h ;will fail with B1 if UMBs all allocated
- jne @@7 ;no UMBs exist
- @@0: int 12H
- mov cl,6
- shl ax,cl ;get segment of top of memory
-
- @@1: mov es,ax
- cmp byte ptr es:[0000h],'M' ;potential mcb?
- jnz @@6 ;not an mcb, try next segment
- @@2: mov cx,ax ;save potential start mcb in cx
- @@3: inc ax
- add ax,es:[0003h] ;ax = start of next mcb
- jc @@5 ;can't be an mcb if we wrapped
- mov es,ax ;address of next mcb
- mov dl,es:[0000h]
- cmp dl,'M'
- jz @@3 ;good start mcb
- cmp dl,'Z'
- jz @@9 ;good end mcb
- @@5: mov ax,cx ;restore last start segment
- @@6: cmp ax,0FFFFh ;top of memory?
- je @@7
- inc ax ;try next segment
- jmp @@1
-
- @@7: xor cx,cx ;no matching UMB
- @@9: mov ax,cx ;return segment in ax
- ret
- findhimemstart endp
-
- ;***********************************************************************
- ;add dummy changeblocks for all psps already resident
- adddummypsp proc near
- ifdef debug
- public adddummypsp
- endif
- assume ds:cseg
- mov ah,52H
- int 21H ;get DOS list of lists
- mov ax,es:[bx-2] ;get first MCB segment
- mov firstmcb,ax ;save it for use later too
- call addchain
-
- call findhimemstart ;find first high memory mcb
- mov firsthimcb,ax ;save it for use later too
- or ax,ax
- jz @@1
- call addchain ;add blocks in high memory too
- @@1: ret
- adddummypsp endp
-
- endcode:
- cseg ends
- end pentry