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