home *** CD-ROM | disk | FTP | other *** search
/ Frostbyte's 1980s DOS Shareware Collection / floppyshareware.zip / floppyshareware / GLEN / TSRSRC32.ZIP / WATCH.ASM < prev    next >
Assembly Source File  |  1991-11-22  |  24KB  |  671 lines

  1. ;WATCH.ASM
  2. ;resident routine watches programs going resident
  3. ;and keeps a list of interrupt vector changes in an internal data structure
  4. ;==============================================================================
  5. ; to be assembled by TASM
  6. ; Copyright (c) 1986,1991 Kim Kokkonen, TurboPower Software.
  7. ; May be freely distributed but not sold except by permission.
  8. ; telephone: 719-260-6641, Compuserve 76004,2611
  9. ;==============================================================================
  10. ; version 2.2  3/4/87
  11. ;   First release, version to be consistent with MAPMEM.PAS
  12. ; :
  13. ; long intervening history
  14. ; :
  15. ; version 3.0  9/24/91
  16. ;   add tracking for TSRs that unload themselves
  17. ;   add support for TSRs loaded high
  18. ;   WATCH may be loaded high
  19. ; version 3.1  11/4/91
  20. ;   rewrite again to solve problems with SWAPMM, FSP, DATAPATH, DATAMON
  21. ; version 3.2  11/22/91
  22. ;   change method of accessing high memory
  23. ;   deal with DOS 5 MODE int trapping (int seg < psp seg)
  24. ;==============================================================================
  25. ;
  26. ;uncomment following line to generate more publics in MAP file
  27. ;       debug   = 1
  28.  
  29. cseg    segment public para
  30.         assume  cs:cseg, ds:nothing, es:nothing, ss:nothing
  31.         locals  @@
  32.  
  33.         org     080H
  34. cmdline label   byte                    ;pointer to command line
  35.  
  36.         org     100H
  37. pentry: jmp     init
  38.  
  39. ;always put the following in WATCH.MAP to update MEMU.PAS
  40. public nextchange,emesg,changevectors,origvectors
  41.  
  42. ;***********************************************************************
  43. ;data structures part of COM file
  44.                 even
  45. nextchange      dw      0               ;next position to write in changes area
  46.  
  47. firstmcb        dw      ?               ;first MCB segment
  48. firsthimcb      dw      0               ;first MCB segment in high memory
  49.  
  50. ;temporary stack used by interrupt handler
  51. newsp           dw      ?               ;initial stack pointer
  52. newss           dw      ?               ;segment of our temporary stack (=cseg)
  53. tmpret          dw      ?               ;used while switching stacks
  54.  
  55. ;information saved about the calling program
  56. oldsp           dw      ?               ;stack pointer
  57. oldss           dw      ?               ;stack segment
  58.  
  59. ;previous interrupt handlers
  60. dos_int         label dword
  61. old21           dw 2 dup (?)            ;old int21 vector
  62. tsr_int         label dword
  63. old27           dw 2 dup (?)            ;old int27 vector
  64.  
  65. ;id code for a PSP data block
  66. pspid           equ     0FFFFH          ;id used to indicate a PSP block
  67.  
  68. ;structure of a changevectors data block
  69. pspblock        struc
  70.                 id      dw      ?       ;id word, always pspid
  71.                 psp     dw      ?       ;psp segment
  72.                 len     dw      ?       ;length of psp
  73.                 unu1    dw      ?       ;unused
  74. pspblock        ends
  75. vecblock        struc
  76.                 vec     dw      ?       ;vector number 0..255
  77.                 veco    dw      ?       ;vector offset
  78.                 vecs    dw      ?       ;vector segment
  79.                 unu2    dw      ?       ;unused
  80. vecblock        ends
  81.  
  82. ;***********************************************************************
  83. ;resident data structures not part of COM file
  84. changevectors   =       offset emesg            ;data area overwrites emesg & beyond
  85. vrecsize        =       8                       ;number of bytes per vector change record
  86. maxchanges      =       128                     ;maximum number of vector changes
  87. vsize           =       maxchanges*vrecsize     ;size of vector change area in bytes
  88.  
  89. ;vector table buffers
  90. origvectors     =       offset changevectors+vsize ;location of original vector table
  91. veclen          =       1024                    ;size of vector table in bytes
  92. newstackpos     =       origvectors+veclen      ;location of newstack
  93. ssize           =       128                     ;number of bytes in temporary stack
  94. newloc          =       newstackpos+ssize       ;location for relocated installation code
  95.  
  96. ;***********************************************************************
  97. ;int21 handler
  98. ;  traps functions 31, 49, 4C, and 7761
  99. int21h  proc far
  100. ifdef   debug
  101.         public  int21h
  102. endif
  103.         assume ds:nothing
  104.         pushf                           ;save flags
  105.         sti                             ;allow interrupts
  106.  
  107.         cmp     ah,31H                  ;terminate and stay resident call?
  108.         jne     @@1
  109.         call    addcurrpsp              ;dx = paras to keep
  110.         jmp     short @@4
  111.  
  112. @@1:    cmp     ah,49H                  ;deallocate block call?
  113.         jne     @@2
  114.         call    remblock                ;remove specified block if a psp
  115.         jmp     short @@4
  116.  
  117. @@2:    cmp     ah,4CH                  ;normal program halt?
  118.         jne     @@3
  119.         call    checkblocks
  120.         jmp     short @@4
  121.  
  122. @@3:    cmp     ax,7761H                ;"wa"tch ID call?
  123.         jne     @@4
  124.         call    checkblocks             ;assure change list up to date
  125.         push    bp
  126.         mov     bp,sp                   ;set up stack frame
  127.         and     word ptr [bp+8],0FFFEH  ;clear carry flag
  128.         pop     bp
  129.         xchg    ah,al                   ;flip ah and al as a signature
  130.         mov     bx,cs                   ;return WATCH psp in bx
  131.         popf
  132.         iret                            ;return to caller
  133.  
  134. @@4:    popf
  135.         jmp     dos_int                 ;let DOS take over
  136. int21h  endp
  137.  
  138. ;***********************************************************************
  139. ;int27 handler
  140. ;  watches for programs going resident
  141. int27h  proc far
  142. ifdef   debug
  143.         public int27h
  144. endif
  145.         assume ds:nothing
  146.         pushf
  147.         sti
  148.         push   dx
  149.         add    dx,15            ;pass size of block in paras to addcurrpsp
  150.         shr    dx,1
  151.         shr    dx,1
  152.         shr    dx,1
  153.         shr    dx,1
  154.         call   addcurrpsp       ;get current psp and add block to list
  155.         pop    dx
  156.         popf
  157.         jmp     tsr_int
  158. int27h  endp
  159.  
  160. ;***********************************************************************
  161. ;get current PSP in bx and add new block
  162. ;entry: dx = paragraphs to keep
  163. addcurrpsp proc near
  164. ifdef   debug
  165.         public  addcurrpsp
  166. endif
  167.         assume  ds:nothing
  168.         call    setup           ;switch stacks and save registers
  169.         assume  ds:cseg
  170.         mov     ah,51H          ;get current PSP in bx
  171.         pushf
  172.         call    dos_int
  173.         call    addblock        ;add block at bx, length dx to changes
  174.         call    shutdown        ;restore registers and switch stacks
  175.         assume  ds:nothing
  176.         ret
  177. addcurrpsp endp
  178.  
  179. ;***********************************************************************
  180. ;remove PSP block, if any, specified by es
  181. remblock proc near
  182. ifdef   debug
  183.         public  remblock
  184. endif
  185.         assume  ds:nothing
  186.         call    setup           ;switch stacks and save registers
  187.         assume  ds:cseg
  188.         mov     bx,es           ;save segment being deallocated in bx
  189.         call    matchpsp        ;return offset in changevectors of segment
  190.         or      si,si           ;any matching block?
  191.         jz      @@1
  192.         call    rempsp          ;remove psp
  193. @@1:    call    shutdown        ;restore registers and switch stacks
  194.         assume  ds:nothing
  195.         ret
  196. remblock endp
  197.  
  198. ;***********************************************************************
  199. ;scan chain of mcbs starting at segment ax
  200. ;remove halting psp from change list if needed
  201. checkchain proc near
  202. ifdef   debug
  203.         public  checkchain
  204. endif
  205. @@1:    mov     es,ax
  206.         mov     bx,es:[0001h]           ;bx = psp of block
  207.         mov     dx,es:[0003h]           ;dx = len of block
  208.         inc     ax
  209.         cmp     ax,bx                   ;does psp = mcb+1?
  210.         jne     @@2                     ;jump if not
  211.         cmp     ax,cx                   ;does psp = current program?
  212.         je      @@2                     ;jump if so
  213.         push    dx
  214.         call    matchpsp                ;find matching psp in changevectors
  215.         pop     dx
  216.         or      si,si                   ;is there a matching psp?
  217.         jnz     @@2                     ;jump if so
  218.         push    ax
  219.         push    cx
  220.         push    dx
  221.         call    addblock                ;add this psp
  222.         pop     dx
  223.         pop     cx
  224.         pop     ax
  225. @@2:    cmp     byte ptr es:[0000h],'Z' ;end of chain
  226.         je      @@3
  227.         add     ax,dx
  228.         jmp     @@1
  229. @@3:    ret
  230. checkchain endp
  231.  
  232. ;***********************************************************************
  233. ;check for new memory blocks and add if needed
  234. ;remove halting psp from change list if needed
  235. checkblocks proc near
  236. ifdef   debug
  237.         public  checkblocks
  238. endif
  239.         assume  ds:nothing
  240.         call    setup                   ;switch stacks and save registers
  241.         assume  ds:cseg
  242.  
  243.         mov     ah,51H                  ;get current psp in bx
  244.         pushf
  245.         call    dos_int
  246.  
  247.         call    matchpsp                ;is current program in change list?
  248.         or      si,si
  249.         jz      @@0                     ;jump if not
  250.         call    rempsp                  ;remove it if not
  251.  
  252. @@0:    mov     cx,bx                   ;cx = psp of halting program
  253.         mov     ax,firstmcb             ;start with first mcb
  254.         call    checkchain              ;check this chain
  255.         mov     ax,firsthimcb           ;scan high memory
  256.         or      ax,ax
  257.         jz      @@1
  258.         call    checkchain
  259. @@1:    call    shutdown                ;restore registers and switch stacks
  260.         assume  ds:nothing
  261.         ret
  262. checkblocks endp
  263.  
  264. ;***********************************************************************
  265. ;setup routine for interrupt hook routines
  266. ; switches stacks, saves registers, sets ds=cs
  267. setup   proc    near
  268. ifdef   debug
  269.         public  setup
  270. endif
  271.         assume  ds:nothing
  272.         pop     cs:tmpret       ;save return address as we switch stacks
  273.         mov     oldss,ss        ;save current stack
  274.         mov     oldsp,sp
  275.         cli                     ;switch to our stack
  276.         mov     ss,newss
  277.         mov     sp,newsp
  278.         sti
  279.         push    ax              ;store registers
  280.         push    bx
  281.         push    cx
  282.         push    dx
  283.         push    si
  284.         push    di
  285.         push    bp
  286.         push    ds
  287.         push    es
  288.         push    cs              ;set ds=cs
  289.         pop     ds
  290.         assume  ds:cseg
  291.         push    cs:tmpret       ;return
  292.         ret
  293. setup   endp
  294.  
  295. ;***********************************************************************
  296. ;shutdown routine for interrupt hook routines
  297. ; restores registers, switches stacks
  298. shutdown proc near
  299. ifdef   debug
  300.         public  shutdown
  301. endif
  302.         pop     cs:tmpret
  303.         pop     es              ;restore registers
  304.         pop     ds
  305.         assume ds:nothing
  306.         pop     bp
  307.         pop     di
  308.         pop     si
  309.         pop     dx
  310.         pop     cx
  311.         pop     bx
  312.         pop     ax
  313.         cli                     ;restore stack
  314.         mov     ss,cs:oldss
  315.         mov     sp,cs:oldsp
  316.         sti
  317.         push    cs:tmpret       ;return
  318.         ret
  319. shutdown endp
  320.  
  321. ;***********************************************************************
  322. ;add specified block to changes
  323. ;  entry: bx = psp of block, dx = length of block in paras
  324. addblock proc near
  325. ifdef   debug
  326.         public  addblock
  327. endif
  328.         assume  ds:cseg
  329.         call    addhdr          ;add a psp header block
  330.         call    addvecs         ;add blocks for each hooked vector
  331.         ret
  332. addblock endp
  333.  
  334. ;***********************************************************************
  335. ;add header for a psp block
  336. ;  entry: bx = psp of block, dx = length of block in paras
  337. ;  exit:  alters di
  338. addhdr  proc near
  339. ifdef   debug
  340.         public  addhdr
  341. endif
  342.         assume  ds:nothing
  343.         mov     di,nextchange
  344.         cmp     di,vsize-vrecsize       ;assure room for next record
  345.         ja      @@1
  346.         mov     word ptr cs:changevectors[di].id,pspid
  347.         mov     cs:changevectors[di].psp,bx
  348.         mov     cs:changevectors[di].len,dx
  349.         add     di,vrecsize
  350.         mov     nextchange,di
  351. @@1:    ret
  352. addhdr  endp
  353.  
  354. ;***********************************************************************
  355. ;add vector blocks for each hooked vector
  356. ;  entry: bx = psp of block, dx = length of block in paras
  357. ;  exit:  alters ax,cx,dx,si,di,bp
  358. addvecs proc    near
  359. ifdef   debug
  360.         public  addvecs
  361. endif
  362.         assume  ds:cseg
  363.         push    ds
  364.         add     dx,bx                   ;now dx points to end of block
  365.         mov     di,nextchange           ;cs:changevectors[di] -> output area
  366.         xor     si,si
  367.         mov     ds,si                   ;ds:si -> vectors
  368.         assume  ds:nothing
  369.         xor     cx,cx                   ;cx = vector counter
  370.         cld                             ;forward
  371.  
  372. @@1:    lodsw                           ;ax = vector offset
  373.         mov     bp,ax                   ;save vector offset
  374.         lodsw                           ;ax = vector segment
  375.  
  376.         cmp     ax,dx                   ;is vector above high limit?
  377.         jae     @@3
  378.  
  379.         push    cx
  380.         mov     cx,ax
  381.         cmp     bp,800h                 ;don't add unless a small offset
  382.         ja      @@1a                    ;(this is a trap for DOS 5 MODE)
  383.  
  384.         push    bp
  385.         shr     bp,1
  386.         shr     bp,1
  387.         shr     bp,1
  388.         shr     bp,1
  389.         add     cx,bp                   ;ax = equivalent segment of interrupt
  390.         pop     bp
  391.  
  392. @@1a:   cmp     cx,bx                   ;is vector above low limit?
  393.         jb      @@2
  394.  
  395.         pop     cx
  396.         cmp     di,vsize-vrecsize       ;room for another entry?
  397.         ja      @@3
  398.         mov     cs:changevectors[di].vec,cx ;save entry for this vector
  399.         mov     cs:changevectors[di].veco,bp
  400.         mov     cs:changevectors[di].vecs,ax
  401.         add     di,vrecsize
  402.         jmp     short @@3
  403.  
  404. @@2:    pop     cx
  405. @@3:    inc     cx                      ;next vector
  406.         cmp     cx,0FFh
  407.         jbe     @@1
  408.  
  409.         mov     nextchange,di
  410.         pop     ds
  411.         assume  ds:cseg
  412.         ret
  413. addvecs endp
  414.  
  415. ;***********************************************************************
  416. ;find changeblock matching psp
  417. ;  entry: bx = psp to match
  418. ;  exit: si = matching block, or 0 if none
  419. ;        destroys dx
  420. matchpsp proc near
  421. ifdef   debug
  422.         public  matchpsp
  423. endif
  424.         assume  ds:cseg
  425.         mov     si,offset changevectors
  426.         mov     dx,si
  427.         add     dx,nextchange           ;dx = next unused spot in changevectors
  428. @@1:    cmp     si,dx                   ;end of table
  429.         jae     @@3
  430.         cmp     word ptr [si].id,pspid  ;psp indicator?
  431.         jnz     @@2                     ;jump if not
  432.         cmp     [si].psp,bx             ;matching psp?
  433.         jnz     @@2                     ;jump if not
  434.         ret                             ;else return with match
  435. @@2:    add     si,vrecsize
  436.         jmp     @@1
  437. @@3:    xor     si,si                   ;no match if here
  438.         ret
  439. matchpsp endp
  440.  
  441. ;***********************************************************************
  442. ;remove all blocks associated with psp at offset si
  443. ;  exit: alters cx,dx,si,di,es
  444. rempsp  proc near
  445. ifdef   debug
  446.         public  rempsp
  447. endif
  448.         assume  ds:cseg
  449.         mov     di,si                   ;save destination
  450.         add     si,vrecsize             ;move to next record
  451.         mov     dx,offset changevectors
  452.         add     dx,nextchange           ;dx = address of next unused
  453. @@1:    cmp     si,dx                   ;end of table?
  454.         jae     @@2                     ;jump if so
  455.         cmp     word ptr [si].id,pspid  ;next psp indicator?
  456.         je      @@2                     ;jump if so
  457.         add     si,vrecsize             ;next block
  458.         jmp     @@1                     ;and loop
  459. @@2:    mov     cx,dx
  460.         sub     cx,si
  461.         shr     cx,1                    ;cx = words to move
  462.         push    cs
  463.         pop     es                      ;es = ds = cs
  464.         cld
  465.         rep     movsw                   ;copy down remaining blocks
  466.         sub     si,di
  467.         sub     nextchange,si           ;update nextchange
  468.         ret
  469. rempsp  endp
  470.  
  471. ;***********************************************************************
  472. ;resident portion above, temporary portion below
  473. ;***********************************************************************
  474.                 align 16
  475. emesg   db      'Cannot install WATCH more than once....',13,10,36
  476. mesg    db      'WATCH 3.2, Copyright 1991 TurboPower Software',13,10
  477.         db      'Installed successfully',13,10,36
  478. pname   db      'TSR WATCHER'
  479. plen    equ     $-pname                 ;length of string
  480. xmsadr  label   dword                   ;XMS control address
  481. xmsxxx  dw      2 dup (0)
  482.  
  483. ;***********************************************************************
  484. ;add dummy changeblocks for all psps already resident in chain starting at ax
  485. addchain proc near
  486. ifdef   debug
  487.         public  addchain
  488. endif
  489.         assume  ds:cseg
  490. @@1:    mov     es,ax
  491.         mov     bx,es:[0001h]           ;bx = psp of block
  492.         mov     dx,es:[0003h]           ;dx = len of block
  493.         inc     ax
  494.         cmp     ax,bx                   ;does psp = mcb+1?
  495.         jne     @@2                     ;jump if not
  496.         cmp     ax,newss                ;does psp = WATCH itself?
  497.         je      @@2                     ;jump if so
  498.         call    addhdr                  ;add a header for this block
  499. @@2:    cmp     byte ptr es:[0000h],'Z' ;end of chain
  500.         je      @@3
  501.         add     ax,dx
  502.         jmp     @@1
  503. @@3:    ret
  504. addchain endp
  505.  
  506. ;***********************************************************************
  507. ;return segment of first high memory mcb in ax
  508. findhimemstart proc near
  509. ifdef   debug
  510.         public findhimemstart
  511. endif
  512.         mov     ax,3000h                ;get DOS version
  513.         int     21H
  514.         cmp     al,3
  515.         jb      @@7                     ;no XMS driver possible
  516.         mov     ax,4300h
  517.         int     2Fh                     ;multiplex call for XMS
  518.         cmp     al,80h                  ;proper signature?
  519.         jne     @@7                     ;no XMS driver
  520.         mov     ax,4310h
  521.         int     2Fh
  522.         mov     xmsxxx,bx               ;save XMS control address
  523.         mov     xmsxxx[2],es
  524.         mov     ah,10h
  525.         mov     dx,0FFFFh
  526.         call    xmsadr                  ;ask to allocate FFFF paras of UMB
  527.         cmp     bl,0B0h                 ;will fail with B0 if UMBs avail
  528.         je      @@0
  529.         cmp     bl,0B1h                 ;will fail with B1 if UMBs all allocated
  530.         jne     @@7                     ;no UMBs exist
  531. @@0:    int     12H
  532.         mov     cl,6
  533.         shl     ax,cl                   ;get segment of top of memory
  534.  
  535. @@1:    mov     es,ax
  536.         cmp     byte ptr es:[0000h],'M' ;potential mcb?
  537.         jnz     @@6                     ;not an mcb, try next segment
  538. @@2:    mov     cx,ax                   ;save potential start mcb in cx
  539. @@3:    inc     ax
  540.         add     ax,es:[0003h]           ;ax = start of next mcb
  541.         jc      @@5                     ;can't be an mcb if we wrapped
  542.         mov     es,ax                   ;address of next mcb
  543.         mov     dl,es:[0000h]
  544.         cmp     dl,'M'
  545.         jz      @@3                     ;good start mcb
  546.         cmp     dl,'Z'
  547.         jz      @@9                     ;good end mcb
  548. @@5:    mov     ax,cx                   ;restore last start segment
  549. @@6:    cmp     ax,0FFFFh               ;top of memory?
  550.         je      @@7
  551.         inc     ax                      ;try next segment
  552.         jmp     @@1
  553.  
  554. @@7:    xor     cx,cx                   ;no matching UMB
  555. @@9:    mov     ax,cx                   ;return segment in ax
  556.         ret
  557. findhimemstart endp
  558.  
  559. ;***********************************************************************
  560. ;add dummy changeblocks for all psps already resident
  561. adddummypsp proc near
  562. ifdef   debug
  563.         public  adddummypsp
  564. endif
  565.         assume  ds:cseg
  566.         mov     ah,52H
  567.         int     21H                     ;get DOS list of lists
  568.         mov     ax,es:[bx-2]            ;get first MCB segment
  569.         mov     firstmcb,ax             ;save it for use later too
  570.         call    addchain
  571.  
  572.         call    findhimemstart          ;find first high memory mcb
  573.         mov     firsthimcb,ax           ;save it for use later too
  574.         or      ax,ax
  575.         jz      @@1
  576.         call    addchain                ;add blocks in high memory too
  577. @@1:    ret
  578. adddummypsp endp
  579.  
  580. ;***********************************************************************
  581. init    proc    near
  582. ifdef   debug
  583.         public  init
  584. endif
  585.         assume  ds:cseg
  586.  
  587. ;use int 21h test to check for previous installation
  588.         mov     ax,7761H                ;special id function
  589.         int     21H
  590.         jc      @@1                     ;not installed if function fails
  591.         cmp     ax,6177H
  592.         jnz     @@1                     ;not installed if id code not returned
  593.  
  594. ;error exit
  595.         mov    dx,offset emesg          ;error message
  596.         mov    ah,09H
  597.         int    21H                      ;DOS print string
  598.         mov    ax,4C01H                 ;exit with error
  599.         int    21H
  600.  
  601. ;not already installed
  602. @@1:    mov    dx,offset mesg           ;success message
  603.         mov    ah,09H
  604.         int    21H                      ;DOS print string
  605.  
  606. ;initialize location of WATCH stack
  607.         mov     newsp,newstackpos+ssize
  608.         mov     newss,cs                ;stack seg is code seg
  609.  
  610. ;put an id label at offset 80H to allow other programs to recognize WATCH
  611.         mov     cx,plen                 ;length of name string
  612.         mov     si,offset pname         ;offset of name string
  613.         mov     di,offset cmdline       ;offset of DOS command line
  614.         cld                             ;transfer in forward direction
  615.         mov     al,cl
  616.         stosb                           ;store length byte first
  617.         rep     movsb                   ;transfer characters
  618.  
  619. ;add psp records for all blocks already resident
  620.         call    adddummypsp
  621.  
  622. ;relocate ourselves out of the way of the resident tables
  623.         push    cs
  624.         pop     es
  625.         mov     di,newloc+10H
  626.         push    di                      ;will act as a return address
  627.         mov     si,offset @@2
  628.         mov     cx,@@3-@@2
  629.         rep     movsb                   ;move code
  630.         ret                             ;"return" to the relocated code
  631.  
  632. ;store image of original vector table (overwrites messages and non-res code)
  633. @@2:    mov     di,origvectors
  634.         push    ds
  635.         xor     si,si                   ;offset 0
  636.         mov     ds,si                   ;source address segment 0
  637.         mov     cx,200H                 ;512 words to store
  638.         rep     movsw                   ;copy vectors to our table
  639.         pop     ds
  640.  
  641. ;store current int 21 and 27 vectors
  642.         mov     ax,3527H
  643.         int     21H
  644.         mov     old27,bx
  645.         mov     old27[2],es
  646.         mov     ax,3521H
  647.         int     21H
  648.         mov     old21,bx
  649.         mov     old21[2],es
  650.  
  651. ;install new vectors
  652.         mov    ax,2527H
  653.         mov    dx,offset int27h
  654.         int    21H
  655.         mov    ax,2521H
  656.         mov    dx,offset int21h
  657.         int    21H
  658.  
  659. ;terminate and stay resident
  660.         mov    dx,newloc
  661.         add    dx,15
  662.         mov    cl,4
  663.         shr    dx,cl
  664.         mov    ax,3100H         ;return success code
  665.         int    21H              ;note WATCH will track itself
  666. @@3:
  667. init    endp
  668.  
  669. cseg    ends
  670.         end     pentry
  671.