Category : Pascal Source Code
Archive   : TSRSRC33.ZIP
Filename : 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
;==============================================================================
;
;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.3, 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
Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!
This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.
But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/