home *** CD-ROM | disk | FTP | other *** search
/ ProfitPress Mega CDROM2 …eeware (MSDOS)(1992)(Eng) / ProfitPress-MegaCDROM2.B6I / UTILITY / SYSTEM / EATMEM11.ZIP / EATMEM.ASM next >
Encoding:
Assembly Source File  |  1991-11-16  |  59.0 KB  |  1,525 lines

  1. ;--------------------------------------------------------------------------;
  2. ;  Program:    EatMem  .Asm                                                ;
  3. ;  Purpose:    TSR utility to limit available memory.                      ;
  4. ;  Notes:      Compiles under TURBO Assembler, v2.0.                       ;
  5. ;  Status:     Source released into the public domain. If you find this    ;
  6. ;                 program useful, please send a postcard.                  ;
  7. ;  Updates:    14-Apr-91, v1.0a, GAT                                       ;
  8. ;                 - initial version                                        ;
  9. ;              06-May-91, v1.0b, GAT                                       ;
  10. ;                 - added option for user to select multiplex ID.          ;
  11. ;              09-Nov-91, v1.1a, GAT                                       ;
  12. ;                 - revised include file names.                            ;
  13. ;                 - added pseudo-environment so program name will show up  ;
  14. ;                   with things like PMAP, MANIFEST, and MEM.              ;
  15. ;                 - uses INT 2D rather than 2F as per Ralf Brown's         ;
  16. ;                   Alternate Multiplex Interrupt proposal.                ;
  17. ;                 - shares interrupts as per IBM's Interrupt Sharing       ;
  18. ;                   Protocol.                                              ;
  19. ;              16-Nov-91, GAT                                              ;
  20. ;                 - made minor changes in return values from the Int 2d    ;
  21. ;                   handler to track Ralf's proposal.                      ;
  22. ;--------------------------------------------------------------------------;
  23.  
  24. ;--------------------------------------------------------------------------;
  25. ;  Author:     George A. Theall                                            ;
  26. ;  Phone:      +1 215 662 0558                                             ;
  27. ;  SnailMail:  TifaWARE                                                    ;
  28. ;              506 South 41st St., #3M                                     ;
  29. ;              Philadelphia, PA.  19104   USA                              ;
  30. ;  E-Mail:     theall@gdalsrv.sas.upenn.edu (Internet)                     ;
  31. ;--------------------------------------------------------------------------;
  32.  
  33. %NEWPAGE
  34. ;--------------------------------------------------------------------------;
  35. ;                          D I R E C T I V E S                             ;
  36. ;--------------------------------------------------------------------------;
  37. DOSSEG
  38. MODEL     tiny
  39.  
  40. IDEAL
  41. LOCALS
  42. JUMPS
  43.  
  44. ;
  45. ; This section comes from Misc.Inc.
  46. ;
  47. @16BIT              EQU       (@CPU AND 8) EQ 0
  48. @32BIT              EQU       (@CPU AND 8)
  49. MACRO    ZERO     RegList                    ;; Zeros registers
  50.    IRP      Reg, <RegList>
  51.          xor      Reg, Reg
  52.    ENDM
  53. ENDM
  54.  
  55. ;
  56. ; This section comes from DOS.Inc.
  57. ;
  58. BELL                EQU       7
  59. BS                  EQU       8
  60. TAB                 EQU       9
  61. CR                  EQU       13
  62. LF                  EQU       10
  63. ESCAPE              EQU       27             ; nb: ESC is a TASM keyword
  64. SPACE               EQU       ' '
  65. KEY_F1              EQU       3bh
  66. KEY_F2              EQU       3ch
  67. KEY_F3              EQU       3dh
  68. KEY_F4              EQU       3eh
  69. KEY_F5              EQU       3fh
  70. KEY_F6              EQU       40h
  71. KEY_F7              EQU       41h
  72. KEY_F8              EQU       42h
  73. KEY_F9              EQU       43h
  74. KEY_F10             EQU       44h
  75. KEY_HOME            EQU       47h
  76. KEY_UP              EQU       48h
  77. KEY_PGUP            EQU       49h
  78. KEY_LEFT            EQU       4bh
  79. KEY_RIGHT           EQU       4dh
  80. KEY_END             EQU       4fh
  81. KEY_DOWN            EQU       50h
  82. KEY_PGDN            EQU       51h
  83. KEY_INS             EQU       52h
  84. KEY_DEL             EQU       53h
  85. KEY_C_F1            EQU       5eh
  86. KEY_C_F2            EQU       5fh
  87. KEY_C_F3            EQU       60h
  88. KEY_C_F4            EQU       61h
  89. KEY_C_F5            EQU       62h
  90. KEY_C_F6            EQU       63h
  91. KEY_C_F7            EQU       64h
  92. KEY_C_F8            EQU       65h
  93. KEY_C_F9            EQU       66h
  94. KEY_C_F10           EQU       67h
  95. KEY_C_LEFT          EQU       73h
  96. KEY_C_RIGHT         EQU       74h
  97. KEY_C_END           EQU       75h
  98. KEY_C_PGDN          EQU       76h
  99. KEY_C_HOME          EQU       77h
  100. KEY_C_PGUP          EQU       84h
  101. KEY_F11             EQU       85h
  102. KEY_F12             EQU       86h
  103. KEY_C_F11           EQU       89h
  104. KEY_C_F12           EQU       8ah
  105. DOS                 EQU       21h            ; main MSDOS interrupt
  106. STDIN               EQU       0              ; standard input
  107. STDOUT              EQU       1              ; standard output
  108. STDERR              EQU       2              ; error output
  109. STDAUX              EQU       3              ; COM port
  110. STDPRN              EQU       4              ; printer
  111. TSRMAGIC            EQU       424bh          ; magic number
  112. STRUC     ISR
  113.           Entry     DW        10EBh          ; short jump ahead 16 bytes
  114.           OldISR    DD        ?              ; next ISR in chain
  115.           Sig       DW        TSRMAGIC       ; magic number
  116.           EOIFlag   DB        ?              ; 0 (80) if soft(hard)ware int
  117.           Reset     DW        ?              ; short jump to hardware reset
  118.           Reserved  DB        7 dup (0)
  119. ENDS
  120. STRUC     ISRHOOK
  121.           Vector    DB        ?              ; vector hooked
  122.           Entry     DW        ?              ; offset of TSR entry point
  123. ENDS
  124. STRUC     TSRSIG
  125.           Company   DB        8 dup (" ")    ; blank-padded company name
  126.           Product   DB        8 dup (" ")    ; blank-padded product name
  127.           Desc      DB        64 dup (0)     ; ASCIIZ product description
  128. ENDS
  129. GLOBAL at : PROC
  130. GLOBAL errmsg : PROC
  131.    GLOBAL ProgName : BYTE                    ; needed for errmsg()
  132.    GLOBAL EOL : BYTE                         ; ditto
  133. GLOBAL fgetc : PROC
  134. GLOBAL fputc : PROC
  135. GLOBAL fputs : PROC
  136. GLOBAL getchar : PROC
  137. GLOBAL getdate : PROC
  138. GLOBAL getswtch : PROC
  139. GLOBAL gettime : PROC
  140. GLOBAL getvdos : PROC
  141. GLOBAL getvect : PROC
  142. GLOBAL isatty : PROC
  143. GLOBAL kbhit : PROC
  144. GLOBAL pause : PROC
  145. GLOBAL putchar : PROC
  146. GLOBAL setvect : PROC
  147. GLOBAL sleep : PROC
  148. GLOBAL find_NextISR : PROC
  149. GLOBAL find_PrevISR : PROC
  150. GLOBAL hook_ISR : PROC
  151. GLOBAL unhook_ISR : PROC
  152. GLOBAL free_Env : PROC
  153. GLOBAL fake_Env : PROC
  154. GLOBAL check_ifInstalled : PROC
  155. GLOBAL install_TSR : PROC
  156. GLOBAL remove_TSR : PROC
  157.  
  158. ;
  159. ; This section comes from Math.Inc.
  160. ;
  161. GLOBAL atoi : PROC
  162. GLOBAL atou : PROC
  163. GLOBAL utoa : PROC
  164.  
  165. ;
  166. ; This section comes from String.Inc.
  167. ;
  168. EOS                 EQU       0              ; terminates strings
  169. GLOBAL isdigit : PROC
  170. GLOBAL islower : PROC
  171. GLOBAL isupper : PROC
  172. GLOBAL iswhite : PROC
  173. GLOBAL memcmp : PROC
  174. GLOBAL strchr : PROC
  175. GLOBAL strcmp : PROC
  176. GLOBAL strlen : PROC
  177. GLOBAL tolower : PROC
  178. GLOBAL toupper : PROC
  179.  
  180.  
  181. VERSION   equ       '1.1a'                   ; current version of program
  182.                                              ; nb: change TSR_Ver too!
  183. ERRH      equ       1                        ; errorlevel if help given
  184. ERRVER    equ       5                        ; errorlevel if incorrect DOS ver
  185. ERRINS    equ       10                       ; errorlevel if install failed
  186. ERRUNI    equ       20                       ; errorlevel if uninstall failed
  187. ERRNYI    equ       25                       ; errorlevel if not yet installed
  188. OFF       equ       0
  189. ON        equ       1
  190.  
  191. %NEWPAGE
  192. ;--------------------------------------------------------------------------;
  193. ;                        C O D E    S E G M E N T                          ;
  194. ;--------------------------------------------------------------------------;
  195. CODESEG
  196.  
  197. ORG       0                                  ; address of code segment start
  198. SegStart  DB        ?                        ;    used in when installing
  199.  
  200. ORG       80h                                ; address of commandline
  201. CmdLen    DB        ?
  202. CmdLine   DB        127 DUP (?)
  203.  
  204. ORG       100h                               ; start of .COM file
  205. STARTUPCODE
  206.           jmp       main
  207.  
  208.  
  209. %NEWPAGE
  210. ;--------------------------------------------------------------------------;
  211. ;                        R E S I D E N T   D A T A                         ;
  212. ;--------------------------------------------------------------------------;
  213. TSR_Sig   TSRSIG    <'TifaWARE', 'EATMEM  ', 'limits available memory'>
  214. TSR_Ver   DW        (1 SHL 8) + 1            ; (minor shl 8) + major
  215. MPlex     DB        ?                        ; multiplex ID
  216. HookTbl   ISRHOOK   <2dh, do_Int2D>          ; 2d must be last!!!
  217.  
  218.  
  219. %NEWPAGE
  220. ;--------------------------------------------------------------------------;
  221. ;                        R E S I D E N T   C O D E                         ;
  222. ;--------------------------------------------------------------------------;
  223. ;----  do_Int2D  ----------------------------------------------------------;
  224. ;  Purpose:    Handle INT 2D.                                              ;
  225. ;  Notes:      Only the install check is truly supported.                  ;
  226. ;  Entry:      AH = Multiplex ID,                                          ;
  227. ;              AL = function code                                          ;
  228. ;  Exit:       AL = FF in the case of an install check,                    ;
  229. ;              CX = TSR version,                                           ;
  230. ;              DX:DI points to resident copy of TSR signature.             ;
  231. ;  Calls:      n/a                                                         ;
  232. ;  Changes:    AL, CX, DX, DI                                              ;
  233. ;--------------------------------------------------------------------------;
  234. PROC do_Int2D  FAR
  235.  
  236. ; This structure is used to share intrrupts. The real entry point
  237. ; follows immediately after it.
  238. my_Int2D  ISR       < , , , 0, ((@@hw_reset - $ - 2) SHL 8 + 0ebh), >
  239.  
  240. ; Test if request is for me. Pass it along to next ISR in chain if not.
  241.           cmp       ah, [cs:MPlex]           ; my multiplex ID?
  242.           jz        SHORT @@forMe            ;   yes
  243.           jmp       [cs:my_Int2d.OldISR]     ;   no, pass it along
  244.                                              ;      nb: old vector issues IRET
  245.  
  246. ; Check function as specified in AL.
  247. @@forMe:
  248.           cmp       al, 0                    ; installation check
  249.           jz        SHORT @@InstallCheck
  250.           cmp       al, 1                    ; get entry point
  251.           jz        SHORT @@GetEntryPoint
  252.           cmp       al, 2                    ; uninstall
  253.           jz        SHORT @@Uninstall
  254.           ZERO      al                       ; mark as not implemented
  255.           jmp       SHORT @@Fin
  256.  
  257. @@InstallCheck:
  258.           dec       al                       ; set AL = FF
  259.           mov       cx, [cs:TSR_Ver]         ; CH = major; CL = minor
  260.           mov       dx, cs                   ; DX:DI points to sig string
  261.           mov       di, OFFSET TSR_Sig
  262.           jmp       SHORT @@Fin
  263.  
  264. @@GetEntryPoint:
  265.           ZERO      al                       ; mark as not supported
  266.           jmp       SHORT @@Fin
  267.  
  268. @@Uninstall:
  269.           ZERO      al                       ; not implemented in API
  270.           jmp       SHORT @@Fin
  271.  
  272. @@Fin:
  273.           iret                               ; return to caller
  274.  
  275. ; Required for IBM Interrupt Sharing Protocol. Normally it is used
  276. ; only by hardware interrupt handlers.
  277. @@hw_reset:
  278.           retf
  279. ENDP do_Int2D
  280.  
  281.  
  282. ;--------------------------------------------------------------------------;
  283. ;                E N D   O F   R E S I D E N T   S E C T I O N             ;
  284. ;--------------------------------------------------------------------------;
  285. LastByte  =         $                        ; end of resident section
  286.  
  287.  
  288. %NEWPAGE
  289. ;--------------------------------------------------------------------------;
  290. ;                       T R A N S I E N T   D A T A                        ;
  291. ;--------------------------------------------------------------------------;
  292. ProgName  DB        'eatmem: '
  293.           DB        EOS
  294. EOL       DB        '.', CR, LF
  295.           DB        EOS
  296. HelpMsg   DB        CR, LF
  297.           DB        'TifaWARE EATMEM, v', VERSION, ', ', ??Date
  298.           DB        ' - TSR utility to limit available memory.', CR, LF
  299.           DB        'Usage: eatmem [-options] Kbytes', CR, LF, LF
  300.           DB        'Options:', CR, LF
  301.           DB        '  -r    = remove from memory', CR, LF
  302.           DB        '  -?    = display this help message', CR, LF, LF
  303.           DB        'Kbytes is the amount of conventional memory to reserve.'
  304.           DB        CR, LF, EOS
  305. ErrMsgOpt DB        'illegal option -- '
  306. OptCh     DB        ?                        ; room for offending character
  307.           DB        EOS
  308. ErrMsgArg DB        'invalid argument'
  309.           DB        EOS
  310. ErrMsgVer DB        'DOS v1 is not supported'
  311.           DB        EOS
  312. ErrMsgRes DB        'unable to go resident'
  313.           DB        EOS
  314. ErrMsgRem DB        'unable to remove from memory'
  315.           DB        EOS
  316. ErrMsgNYI DB        'not yet installed'
  317.           DB        EOS
  318. InstalMsg DB        'TifaWARE EATMEM, v', VERSION
  319.           DB        ' now installed. Type "eatmem -r" when finished.'
  320.           DB        CR, LF, EOS
  321. RemoveMsg DB        'successfully removed'
  322.           DB        EOS
  323.  
  324. SwitCh    DB        '-'                      ; char introducing options
  325. HFlag     DB        0                        ; flag for on-line help
  326. IFlag     DB        0                        ; flag for installing TSR
  327. RFlag     DB        0                        ; flag for removing TSR
  328. KBytes    DW        0                        ; amount of memory to leave free
  329.  
  330.  
  331. %NEWPAGE
  332. ;--------------------------------------------------------------------------;
  333. ;                       T R A N S I E N T   C O D E                        ;
  334. ;--------------------------------------------------------------------------;
  335. ;----  go_Resident  -------------------------------------------------------;
  336. ;  Purpose:    Attempts to make TSR resident.                              ;
  337. ;  Notes:      Aborts if there's not enough memory to satisfy request.     ;
  338. ;              This procedure ONLY EXITS ON ERROR.                         ;
  339. ;  Entry:      DS = segment address of program's PSP, which also holds     ;
  340. ;                   HookTbl, a structure of type ISRHOOK.                  ;
  341. ;  Exit:       none                                                        ;
  342. ;  Calls:      check_ifInstalled, fputs, fake_Env, install_TSR, errmsg     ;
  343. ;  Changes:    AX, BX, CX, DX, DI, SI, ES                                  ;
  344. ;--------------------------------------------------------------------------;
  345. PROC go_Resident
  346.  
  347. ; See if there's already a copy resident. nb: only interested in AX
  348. ; on return from the install check.
  349.           mov       si, OFFSET TSR_SIG
  350.           call      check_ifInstalled        ; -> AX, CX, and DX:DI
  351.           cmp       al, 2                    ; out of multiplex ids?
  352.           jz        SHORT @@Abort            ;   yes, abort
  353.           cmp       al, 1                    ; already loaded?
  354.           jz        SHORT @@Abort            ;   yes
  355.           mov       [MPlex], ah              ; save mplex id
  356.  
  357. ; Calculate how many paragraphs to reserve when going resident.
  358. ; NB: DOS reserved largest chunk of memory when loading COM files.
  359. ; The size of this block is stored as a word at offset 3 in the MCB,
  360. ; which is located at the segment before the PSP.
  361.           mov       bx, [KBytes]
  362.           mov       cl, 6                    ; effectively multiplies by 64
  363.           shl       bx, cl                   ; 1K * 64 = number of paragraphs
  364.  
  365. ; Make sure enough memory is available to satisfy this request.
  366.           mov       ax, ds                   ; point to PSP
  367.           dec       ax                       ; and now to MCB
  368.           mov       es, ax
  369.           mov       dx, [WORD PTR es:3]      ; number of paragraphs reserved
  370.           inc       ax
  371.           mov       es, ax                   ; ES = PSP needed later
  372.           sub       dx, bx                   ; # paragraphs to reserve
  373.           dec       dx                       ; adjust for endpoint
  374.           jc        SHORT @@Abort            ; abort if not enough
  375.  
  376. ; Make sure enough memory is left for my ISR. Note that DX
  377. ; is the number of paragraphs to reserve from above.
  378.           cmp       dx, (LastByte - SegStart + 16) SHR 4
  379.           jb        SHORT @@Abort
  380.           push      dx                       ; # paragraphs to reserve
  381.  
  382. ; This is the point of no-return -- if we get here we're going resident.
  383.           mov       bx, STDOUT
  384.           mov       dx, OFFSET InstalMsg
  385.           call      fputs
  386.  
  387. ; Create a fake environment and free existing one.
  388.           ZERO      cx                       ; tells fake_Env to fake it
  389.           call      fake_Env
  390.  
  391. ; Ok, all that's left is to go resident. Note that at this point
  392. ; ES = DS, and that HookTbl is relative to that.
  393.           mov       bx, OFFSET HookTbl       ; pointer to ISRHOOK structure
  394.           pop       dx                       ; recover # paragraphs to reserve
  395.           call      install_TSR              ; never returns
  396.  
  397. ; Execution gets here only on error because:
  398. ;  - all multiplex ids are in use! 
  399. ;  - the TSR is already resident.
  400. ;  - there is less than KBytes of memory currently free.
  401. ;  - reserving KBytes would not leave room for the TSR itself.
  402. @@Abort:
  403.           mov       dx, OFFSET ErrMsgRes     ; "unable to go resident"
  404.           call      errmsg
  405.           ret
  406. ENDP go_Resident
  407.  
  408.  
  409. ;----  clear_Resident  ----------------------------------------------------;
  410. ;  Purpose:    Attempts to remove a TSR from memory.                       ;
  411. ;  Notes:      none                                                        ;
  412. ;  Entry:      DS = segment address of program's PSP.                      ;
  413. ;  Exit:       AL = 0 if removal succeeded; ERRNYI if not installed;       ;
  414. ;                   ERRUNI otherwise.                                      ;
  415. ;  Calls:      check_ifInstalled, remove_TSR, errmsg                       ;
  416. ;  Changes:    AX, BX, CX, DX, DI, SI, ES                                  ;
  417. ;--------------------------------------------------------------------------;
  418. PROC clear_Resident
  419.  
  420. ; See if there's already a copy resident.
  421.           mov       si, OFFSET TSR_SIG
  422.           call      check_ifInstalled        ; DS:SI -> AX, CX, DX:DI
  423.           cmp       al, 1                    ; already loaded?
  424.           jz        SHORT @@Removal          ;   yes
  425.           mov       al, ERRNYI               ;   no, set return code
  426.           mov       dx, OFFSET ErrMsgNYI     ;     "not yet installed"
  427.           jmp       SHORT @@Fin
  428.  
  429. ; Try to remove it.
  430. @@Removal:
  431.           mov       bx, OFFSET HookTbl       ; HookTbl in resident data area
  432.           mov       es, dx                   ; install check returns DX:DI
  433.           call      remove_TSR               ; ES:BX -> n/a
  434.           jc        SHORT @@Abort
  435.           ZERO      al
  436.           mov       dx, OFFSET RemoveMsg
  437.           jmp       SHORT @@Fin
  438.  
  439. @@Abort:
  440.           mov       al, ERRUNI
  441.           mov       dx, OFFSET ErrMsgRem     ; "unable to remove"
  442.  
  443. @@Fin:
  444.           call      errmsg
  445.           ret
  446. ENDP clear_Resident
  447.  
  448.  
  449. ;----  skip_Spaces  -------------------------------------------------------;
  450. ;  Purpose:    Skips past spaces in a string.                              ;
  451. ;  Notes:      Scanning stops with either a non-space *OR* CX = 0.         ;
  452. ;  Entry:      DS:SI = start of string to scan.                            ;
  453. ;  Exit:       AL = next non-space character,                              ;
  454. ;              CX is adjusted as necessary,                                ;
  455. ;              DS:SI = pointer to next non-space.                          ;
  456. ;  Calls:      none                                                        ;
  457. ;  Changes:    AL, CX, SI                                                  ;
  458. ;--------------------------------------------------------------------------;
  459. PROC skip_Spaces
  460.  
  461.           jcxz      SHORT @@Fin
  462. @@NextCh:
  463.           lodsb
  464.           cmp       al, ' '
  465.           loopz     @@NextCh
  466.           jz        SHORT @@Fin              ; CX = 0; don't adjust
  467.  
  468.           inc       cx                       ; adjust counters if cx > 0
  469.           dec       si
  470.  
  471. @@Fin:
  472.           ret
  473. ENDP skip_Spaces
  474.  
  475.  
  476. ;----  get_Opt  -----------------------------------------------------------;
  477. ;  Purpose:    Get a commandline option.                                   ;
  478. ;  Notes:      none                                                        ;
  479. ;  Entry:      AL = option character,                                      ;
  480. ;              CX = count of characters left in commandline,               ;
  481. ;              DS:SI = pointer to argument to process.                     ;
  482. ;  Exit:       CX = count of characters left _after_ processing,           ;
  483. ;              DS:SI = pointer to whitespace _after_ argument.             ;
  484. ;  Calls:      tolower, errmsg                                             ;
  485. ;  Changes:    DX,                                                         ;
  486. ;              [OptCh], [HFlag], [RFlag].                                  ;
  487. ;--------------------------------------------------------------------------;
  488. PROC get_Opt
  489.  
  490.           mov       [OptCh], al              ; save for later
  491.           call      tolower                  ; use only lowercase in cmp.
  492.           cmp       al, 'r'
  493.           jz        SHORT @@OptR
  494.           cmp       al, '?'
  495.           jz        SHORT @@OptH
  496.           mov       dx, OFFSET ErrMsgOpt     ; unrecognized option
  497.           call      errmsg                   ; then *** DROP THRU *** to OptH
  498.  
  499. ; Various possible options.
  500. @@OptH:
  501.           mov       [HFlag], ON              ; set help flag
  502.           jmp       SHORT @@Fin
  503.  
  504. @@OptR:
  505.           mov       [RFlag], ON              ; remove from memory
  506.  
  507. @@Fin:
  508.           ret
  509. ENDP get_Opt
  510.  
  511.  
  512. ;----  get_Arg  -----------------------------------------------------------;
  513. ;  Purpose:    Reads a number from the commandline. Prints message and     ;
  514. ;                   sets HFlag if number is invalid.                       ;
  515. ;  Notes:      none                                                        ;
  516. ;  Entry:      CX = count of characters left in commandline,               ;
  517. ;              DS:SI = pointer to argument to process.                     ;
  518. ;  Exit:       cf = 0 if no errors in conversion,                          ;
  519. ;              AX = digit read (garbage if cf = 1),                        ;
  520. ;              CX = count of characters left _after_ processing,           ;
  521. ;              DS:SI = pointer to whitespace _after_ argument.             ;
  522. ;  Calls:      isdigit, atou, errmsg                                       ;
  523. ;  Changes:    AX, CX, DX, SI,                                             ;
  524. ;              [HFlag]                                                     ;
  525. ;--------------------------------------------------------------------------;
  526. PROC get_Arg
  527.  
  528.           call      isdigit                  ; if not a digit, trouble!
  529.           jz        SHORT @@ReadNum
  530.  
  531.           mov       dx, si                   ; flag arg as bad
  532.           xchg      di, si
  533.           mov       al, ' '
  534.           repne     scasb                    ; find end of argument
  535.           xchg      di, si
  536.           jne       SHORT @@BadNum
  537.           dec       si                       ; overshot so back up 1 char
  538.           inc       cx
  539.           jmp       SHORT @@BadNum           ; tell user it's bad
  540.  
  541. @@ReadNum:
  542.           mov       dx, si                   ; save to adjust CX and if error
  543.           call      atou
  544.           pushf                              ; preserve flags
  545.           add       cx, dx                   ; adjust counter
  546.           sub       cx, si
  547.           popf                               ; restore flags
  548.           jnc       SHORT @@Fin
  549.  
  550. @@BadNum:
  551.           mov       dx, OFFSET ErrMsgArg     ; invalid argument
  552.           call      errmsg
  553.           mov       [HFlag], ON
  554.           stc                                ; flag error
  555.  
  556. @@Fin:
  557.           ret
  558. ENDP get_Arg
  559.  
  560.  
  561. ;----  process_CmdLine  ---------------------------------------------------;
  562. ;  Purpose:    Processes commandline arguments.                            ;
  563. ;  Notes:      A switch character by itself is ignored.                    ;
  564. ;              No arguments whatsoever causes help flag to be set.         ;
  565. ;  Entry:      n/a                                                         ;
  566. ;  Exit:       n/a                                                         ;
  567. ;  Calls:      skip_Spaces, get_Opt, get_Arg                               ;
  568. ;  Changes:    AX, CX, SI,                                                 ;
  569. ;              [IFlag], [KBytes],                                          ;
  570. ;              DX, [OptCh], [HFlag], [RFlag] (get_Opt)                     ;
  571. ;              Direction flag is cleared.                                  ;
  572. ;--------------------------------------------------------------------------;
  573. PROC process_CmdLine
  574.  
  575.           cld                                ; forward, march!
  576.           ZERO      ch
  577.           mov       cl, [CmdLen]             ; length of commandline
  578.           mov       si, OFFSET CmdLine       ; offset to start of commandline
  579.  
  580.           call      skip_Spaces              ; check if any args supplied
  581.           or        cl, cl
  582.           jnz       SHORT @@ArgLoop
  583.  
  584.           mov       [HFlag], ON              ; assume user needs help
  585.           jmp       SHORT @@Fin
  586.  
  587. ; For each blank-delineated argument on the commandline...
  588. @@ArgLoop:
  589.           lodsb                              ; next character
  590.           dec       cl
  591.           cmp       al, [SwitCh]             ; is it the switch character?
  592.           jnz       SHORT @@NonOpt           ;   no
  593.  
  594. ; Isolate each option and process it. Stop when a space is reached.
  595. @@OptLoop:
  596.           jcxz      SHORT @@Fin              ; abort if nothing left
  597.           lodsb
  598.           dec       cl
  599.           cmp       al, ' '
  600.           jz        SHORT @@NextArg          ; abort when space reached
  601.           call      get_Opt
  602.           jmp       @@OptLoop
  603.  
  604. ; Process the current argument, which is *not* an option.
  605. ; Then, *drop thru* to advance to next argument.
  606. @@NonOpt:
  607.           dec       si                       ; back up one character
  608.           inc       cl
  609.           call      get_Arg
  610.           jc        SHORT @@NextArg          ; error reading number?
  611.           mov       [IFlag], ON
  612.           mov       [KBytes], ax
  613.  
  614. ; Skip over spaces until next argument is reached.
  615. @@NextArg:
  616.           call      skip_Spaces
  617.           or        cl, cl
  618.           jnz       @@ArgLoop
  619.  
  620. @@Fin:
  621.           ret
  622. ENDP process_CmdLine
  623.  
  624.  
  625. ;----  main  --------------------------------------------------------------;
  626. ;  Purpose:    Main section of program.                                    ;
  627. ;  Notes:      none                                                        ;
  628. ;  Entry:      Arguments as desired                                        ;
  629. ;  Exit:       Return code as follows:                                     ;
  630. ;                   0 => program ran successfully,                         ;
  631. ;                   ERRH => on-line help supplied,                         ;
  632. ;                   ERRDOS => incorrect DOS version,                       ;
  633. ;                   ERRINS => install failed,                              ;
  634. ;                   ERRUNI => uninstall failed,                            ;
  635. ;                   ERRNYI => program was not yet installed.               ;
  636. ;  Calls:      getvdos, errmsg, process_CmdLine, fputs, go_Resident,       ;
  637. ;                   clear_Resident                                         ;
  638. ;  Changes:    n/a                                                         ;
  639. ;--------------------------------------------------------------------------;
  640. main:
  641.  
  642. ; Must be running at least DOS v2.x.
  643.           call      getvdos
  644.           cmp       al, 2
  645.           jae       SHORT @@ReadCmds
  646.           mov       dx, OFFSET ErrMsgVer     ; gotta have at least DOS v2
  647.           call      errmsg
  648.  
  649. ; Parse commandline.
  650. @@ReadCmds:
  651.           call      process_CmdLine          ; process commandline args
  652.           cmp       [HFlag], ON              ; user needs help?
  653.           je        SHORT @@GiveHelp
  654.           cmp       [IFlag], ON              ; install it?
  655.           je        SHORT @@Install
  656.           cmp       [RFlag], ON              ; remove it?
  657.           je        SHORT @@Remove
  658.  
  659. ; Display help if we get to this point. NB: errmsg is not used
  660. ; because it would write "eatmem: " first.
  661. @@GiveHelp:
  662.           mov       bx, STDERR               ; user must need help
  663.           mov       dx, OFFSET HelpMsg
  664.           call      fputs
  665.           mov       al, ERRH
  666.           jmp       SHORT @@Fin
  667.  
  668. @@Install:
  669.           call      go_Resident              ; returns on error only
  670.           mov       al, ERRINS
  671.           jmp       SHORT @@Fin
  672.  
  673. @@Remove:
  674.           call      clear_Resident
  675.  
  676. ; Terminate the program using as return code what's in AL.
  677. @@Fin:
  678.           mov       ah, 4ch
  679.           int       DOS
  680. EVEN
  681. ;-------------------------------------------------------------------------;
  682. ;  Purpose:    Writes an ASCIIZ string to specified device.
  683. ;  Notes:      A zero-length string doesn't seem to cause problems when
  684. ;                 this output function is used.
  685. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  686. ;  Entry:      BX = device handle,
  687. ;              DS:DX = pointer to string.
  688. ;  Exit:       Carry flag set if EOS wasn't found or handle is invalid.
  689. ;  Calls:      strlen
  690. ;  Changes:    none
  691. ;-------------------------------------------------------------------------;
  692. PROC fputs
  693.  
  694.    push     ax cx di es
  695.    mov      ax, ds
  696.    mov      es, ax
  697.    mov      di, dx
  698.    call     strlen                        ; set CX = length of string
  699.    jc       SHORT @@Fin                   ; abort if problem finding end
  700.    mov      ah, 40h                       ; MS-DOS raw output function
  701.    int      DOS
  702. @@Fin:
  703.    pop      es di cx ax
  704.    ret
  705.  
  706. ENDP fputs
  707.  
  708.  
  709. EVEN
  710. ;-------------------------------------------------------------------------;
  711. ;  Purpose:    Writes an error message to stderr.
  712. ;  Notes:      none
  713. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  714. ;  Entry:      DS:DX = pointer to error message.
  715. ;  Exit:       n/a
  716. ;  Calls:      fputs
  717. ;  Changes:    none
  718. ;-------------------------------------------------------------------------;
  719. PROC errmsg
  720.  
  721.    push     bx dx
  722.    mov      bx, STDERR
  723.    mov      dx, OFFSET ProgName           ; display program name
  724.    call     fputs
  725.    pop      dx                            ; recover calling parameters
  726.    push     dx                            ; and save again to avoid change
  727.    call     fputs                         ; display error message
  728.    mov      dx, OFFSET EOL
  729.    call     fputs
  730.    pop      dx bx
  731.    ret
  732.  
  733. ENDP errmsg
  734.  
  735.  
  736. EVEN
  737. ;-------------------------------------------------------------------------;
  738. ;  Purpose:    Gets version of DOS currently running.
  739. ;  Notes:      none
  740. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  741. ;  Entry:      n/a
  742. ;  Exit:       AL = major version number,
  743. ;              AH = minor version number (2.1 = 10).
  744. ;  Calls:      none
  745. ;  Changes:    AX
  746. ;-------------------------------------------------------------------------;
  747. PROC getvdos
  748.  
  749.    push     bx cx                         ; DOS destroys bx and cx!
  750.    mov      ah, 30h
  751.    int      DOS
  752.    pop      cx bx
  753.    ret
  754.  
  755. ENDP getvdos
  756.  
  757.  
  758. EVEN
  759. ;--------------------------------------------------------------------------;
  760. ;  Purpose:    Gets address of an interrupt handler.
  761. ;  Notes:      none
  762. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  763. ;  Entry:      AL = interrupt of interest.
  764. ;  Exit:       ES:BX = address of current interrupt handler
  765. ;  Calls:      none
  766. ;  Changes:    ES:BX
  767. ;--------------------------------------------------------------------------;
  768. PROC getvect
  769.  
  770.    push     ax
  771.    mov      ah, 35h                       ; find address of handler
  772.    int      DOS                           ; returned in ES:BX
  773.    pop      ax
  774.    ret
  775. ENDP getvect
  776.  
  777.  
  778. ;--------------------------------------------------------------------------;
  779. ;  Purpose:    Sets an interrupt vector.
  780. ;  Notes:      none
  781. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  782. ;  Entry:      AL = interrupt of interest,
  783. ;              DS:DX = address of new interrupt handler
  784. ;  Exit:       n/a
  785. ;  Calls:      none
  786. ;  Changes:    none
  787. ;--------------------------------------------------------------------------;
  788. PROC setvect
  789.  
  790.    push     ax
  791.    mov      ah, 25h                       ; set address of handler
  792.    int      DOS
  793.    pop      ax
  794.    ret
  795. ENDP setvect
  796.  
  797.  
  798. EVEN
  799. ;--------------------------------------------------------------------------;
  800. ;  Purpose:    Finds the next in a chain of ISRs.
  801. ;  Notes:      ISRs must be shared according to the IBM Interrupt
  802. ;                 Sharing Protocol.
  803. ;  Requires:   8086-class CPU.
  804. ;  Entry:      ES:BX = entry point for a given ISR.
  805. ;  Exit:       ES:BX = entry point for next ISR in the chain,
  806. ;              cf = 1 on error
  807. ;  Calls:      none
  808. ;  Changes:    BX, ES, cf
  809. ;--------------------------------------------------------------------------;
  810. PROC find_NextISR
  811.  
  812. ; Save DS, then set it to ES. This will avoid segment overrides below.
  813.    push     ds es
  814.    pop      ds
  815.  
  816. ; Run three tests to see if the ISR obeys the protocol.
  817. ;1) Entry should be a short jump (opcode 0EBh).
  818. ;2) Sig should equal a special value ("KB").
  819. ;3) Reset should be another short jump.
  820.    cmp      [BYTE PTR (ISR PTR bx).Entry], 0ebh
  821.    jnz      SHORT @@Abort
  822.    cmp      [(ISR PTR bx).Sig], TSRMAGIC
  823.    jnz      SHORT @@Abort
  824.    cmp      [BYTE PTR (ISR PTR bx).Reset], 0ebh
  825.    jnz      SHORT @@Abort
  826.  
  827. ; Ok, looks like the ISR is following the Interrupt Sharing Protocol.
  828. ; nb: cf will be clear as a result of the last comparison.
  829.    les      bx, [(ISR PTR bx).OldISR]
  830.    jmp      SHORT @@Fin
  831.  
  832. ; Uh, oh, somebody's not being very cooperative or we've hit DOS/BIOS.
  833. @@Abort:
  834.    stc                                    ; flag error
  835.  
  836. @@Fin:
  837.    pop      ds
  838.    ret
  839.  
  840. ENDP find_NextISR
  841.  
  842.  
  843. ;--------------------------------------------------------------------------;
  844. ;  Purpose:    Finds the previous in a chain of ISRs.
  845. ;  Notes:      ISRs must be shared according to the IBM Interrupt
  846. ;                 Sharing Protocol.
  847. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  848. ;  Entry:      AL = vector hooked,
  849. ;              ES:BX = entry point for a given ISR.
  850. ;  Exit:       ES:BX = entry point for next ISR in the chain,
  851. ;              cf = 1 on error
  852. ;  Calls:      getvect, find_NextISR
  853. ;  Changes:    BX, ES, cf
  854. ;--------------------------------------------------------------------------;
  855. PROC find_PrevISR
  856.  
  857.    push     ax cx dx
  858.  
  859. ; Stack holds previous ISR. Initialize it to a null pointer.
  860.    ZERO     cx
  861.    push     cx cx
  862.  
  863. ; Point CX:DX to current ISR, then get first ISR in the chain.
  864.    mov      cx, es
  865.    mov      dx, bx
  866.    call     getvect                       ; AL -> ES:BX
  867.    jmp      SHORT @@Cmp
  868.  
  869. ; Cycle through ISRs until either a match is found or we can't go further.
  870. @@Next:
  871.    add      sp, 4                         ; get rid of two words on stack
  872.    push     es bx                         ; now save ES:BX
  873.    call     find_NextISR                  ; ES:BX -> ES:BX
  874.    jc       SHORT @@Fin                   ; abort on error
  875. @@Cmp:
  876.    mov      ax, es                        ; are segs the same?
  877.    cmp      ax, cx
  878.    jnz      SHORT @@Next
  879.    cmp      dx, bx                        ; what about offsets?
  880.    jnz      SHORT @@Next
  881.  
  882. @@Fin:
  883.    pop      bx es                         ; pointer to previous ISR
  884.    pop      dx cx ax
  885.    ret
  886.  
  887. ENDP find_PrevISR
  888.  
  889.  
  890. ;--------------------------------------------------------------------------;
  891. ;  Purpose:    Hooks into an ISR and keeps track of previous ISR.
  892. ;  Notes:      none
  893. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  894. ;  Entry:      AL = vector to hook,
  895. ;              ES:BX = pointer to a structure of type ISR.
  896. ;  Exit:       n/a
  897. ;  Calls:      getvect, setvect
  898. ;  Changes:    n/a
  899. ;--------------------------------------------------------------------------;
  900. PROC hook_ISR
  901.  
  902.    push     bx dx bp ds es
  903.  
  904. ; Save old vector to it can be restored later. Then set new hook.
  905.    push     es bx                         ; need them later
  906.    call     getvect                       ; AL -> ES:BX
  907.    pop      dx ds                         ; recover pointer to ISR
  908.    mov      bp, dx                        ; use BP for indexing
  909.    mov      [WORD (ISR PTR bp).OldISR], bx
  910.    mov      [WORD ((ISR PTR bp).OldISR)+2], es
  911.    call     setvect                       ; uses DS:DX
  912.  
  913.    pop      es ds bp dx bx
  914.    ret
  915.  
  916. ENDP hook_ISR
  917.  
  918.  
  919. ;--------------------------------------------------------------------------;
  920. ;  Purpose:    Unhooks an ISR if possible.
  921. ;  Notes:      Unhooking an ISR is more complicated than hooking one
  922. ;                 because of the need to support interrupt sharing.
  923. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  924. ;  Entry:      AL = vector hooked,
  925. ;              ES:BX = entry point of current ISR.
  926. ;  Exit:       cf = 1 on error
  927. ;  Calls:      find_PrevISR, setvect
  928. ;  Changes:    cf
  929. ;--------------------------------------------------------------------------;
  930. PROC unhook_ISR
  931.  
  932.    push     bx cx dx ds es
  933.  
  934. ; Point DS:DX to next ISR, then ES:BX to previous ISR in the chain.
  935.    lds      dx, [(ISR PTR es:bx).OldISR]
  936.    call     find_PrevISR                  ; ES:BX -> ES:BX
  937.    jc       SHORT @@Fin                   ; abort on error
  938.  
  939. ; If find_PrevISR() returned a null pointer, then the current ISR
  940. ; is first in the chain; just use DOS to reassign the vector.
  941. ; Otherwise, update the OldISR entry in the previous handler.
  942.    mov      cx, es                        ; did find_PrevISR() ...
  943.    or       cx, bx                        ; return null pointer?
  944.    jnz      SHORT @@Update                ;   no. update OldISR
  945.    call     setvect                       ;   yes, hook AL to DS:DX
  946.    jmp      SHORT @@Fin
  947. @@Update:
  948.    mov      [WORD (ISR PTR es:bx).OldISR], dx
  949.    mov      [WORD ((ISR PTR es:bx).OldISR)+2], ds
  950.  
  951. @@Fin:
  952.    pop      es ds dx cx bx
  953.    ret
  954.  
  955. ENDP unhook_ISR
  956.  
  957.  
  958. EVEN
  959. AMI         equ      2dh                  ; Alternate Multiplex Interrupt
  960. ENVBLK      equ      2ch                  ; ptr in PSP to environment block
  961.  
  962.  
  963. ;--------------------------------------------------------------------------;
  964. ;  Purpose:    Frees up a program's environment block.
  965. ;  Notes:      Programs such as PMAP or MEM scan environment blocks to
  966. ;                 learn names of TSRs. Freeing it means such programs
  967. ;                 will not be able to identify the TSR.
  968. ;              It's ASSUMED the ENV BLOCK has NOT ALREADY been FREED.
  969. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  970. ;  Entry:      ES = segment of program's PSP.
  971. ;  Exit:       none
  972. ;  Calls:      none
  973. ;  Changes:    none
  974. ;--------------------------------------------------------------------------;
  975. PROC free_Env
  976.  
  977.    push     ax ds es
  978.  
  979.    push     es                            ; point DS to PSP too
  980.    pop      ds
  981.    mov      es, [ENVBLK]                  ; pointer to env block
  982.    mov      ah, 49h                       ; free memory block
  983.    int      DOS
  984.    mov      [WORD PTR ENVBLK], 0          ; make it 0
  985.  
  986.    pop      es ds ax
  987.    ret
  988.  
  989. ENDP free_Env
  990.  
  991.  
  992. ;--------------------------------------------------------------------------;
  993. ;  Purpose:    Replaces a program's real environment with a smaller, fake
  994. ;                 one to save space. Programs like PMAP and MEM though
  995. ;                 will still be able to identify TSRs.
  996. ;  Notes:      If run with DOS version lower than v3.10, the environment
  997. ;                 block is merely freed.
  998. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  999. ;  Entry:      CX = size in bytes of pseudo-environment block (if 0 
  1000. ;                 one is created containing just program name/args),
  1001. ;              DS:SI = pointer to pseudo-environment block,
  1002. ;              ES = segment of program's PSP.
  1003. ;  Exit:       none
  1004. ;  Calls:      getvdos, free_Env, strlen
  1005. ;  Changes:    none
  1006. ;--------------------------------------------------------------------------;
  1007. PROC fake_Env
  1008.  
  1009.    push     ax bx cx di si ds es
  1010.    pushf
  1011.  
  1012. ; Make sure DOS is v3.10 or better. If not, just free environment.
  1013. ; nb: I could code this to handle old versions so long as caller
  1014. ; supplies a real block, but why bother?
  1015.    call     getvdos                       ; get DOS version
  1016.    xchg     al, ah
  1017.    cmp      ax, (3 SHL 8) + 10            ; v3.10 or better?
  1018.    jae      SHORT @@FindEnv               ;   yes
  1019.    call     free_Env                      ;   no, just free it
  1020.    jmp      SHORT @@Fin
  1021.  
  1022. ; Locate environment block.
  1023. @@FindEnv:
  1024.    mov      bx, [es:ENVBLK]               ; pointer to env block
  1025.    mov      es, bx
  1026.  
  1027. ; If CX is zero, point DS:SI to just the program name/args in the
  1028. ; current environment. This format was introducted with DOS v3.10.
  1029. ;
  1030. ; nb: Refer to _Undocumented DOS, p 399 for format of environment block.
  1031.    cld                                    ; scasb and movsb must go forward
  1032.    or       cx, cx                        ; is CX zero?
  1033.    jnz      SHORT @@GetMem                ;   no
  1034.    mov      ds, bx                        ; point DS to env block too
  1035.    ZERO     al                            ; ends of ASCIIz strings
  1036.    ZERO     di                            ; start at offset 0
  1037. @@NextString:
  1038.    call     strlen                        ; find length of string at ES:DI
  1039.    add      di, cx                        ; update DI
  1040.    inc      di                            ;   and past EOS
  1041.    scasb                                  ; are we at another 0?
  1042.    jne      SHORT @@NextString            ;   no
  1043.    mov      si, di                        ; point SI to
  1044.    dec      si                            ;   EOS in ...
  1045.    dec      si                            ;   last string
  1046.    mov      [WORD PTR es:di], 1           ; only want prog name/args
  1047.    inc      di                            ; point to start of string
  1048.    inc      di
  1049.    call     strlen                        ; find its length
  1050.    add      cx, di                        ; get # bytes to move
  1051.    sub      cx, si
  1052.    inc      cx
  1053.  
  1054. ; At this point, CX holds number of bytes to allocate and DS:SI point
  1055. ; to a copy of the pseudo-environment block.
  1056. @@GetMem:
  1057.    ZERO     di                            ; either way, destination = 0
  1058.    mov      bx, cx                        ; from # bytes
  1059.    REPT     4
  1060.       shr      bx, 1                      ; get # paragraphs
  1061.    ENDM
  1062.    inc      bx                            ; think what if CX < 0fh
  1063.    push     bx                            ; must save BX if DOS fails
  1064.    mov      ah, 48h                       ; allocate memory
  1065.    int      DOS                           ; returns block in AX
  1066.    pop      bx
  1067.    jc       SHORT @@JustResize            ; cf => failure
  1068.  
  1069. ; Memory allocation succeeded so: (1) Copy to new block. (2) Adjust
  1070. ; pointer in program's PSP. (3) Free old block.
  1071.    push     es                            ; points to old env block
  1072.    mov      es, ax                        ; new block
  1073.    rep      movsb
  1074.    mov      ah, 62h                       ; get program's PSP
  1075.    int      DOS                           ; returns it in BX
  1076.    mov      ds, bx
  1077.    mov      [ENVBLK], es                  ; pointer to new env block
  1078.    pop      es                            ; recover pointer to old env
  1079.    mov      ah, 49h                       ; free it
  1080.    int      DOS
  1081.    jmp      SHORT @@Fin
  1082.  
  1083. ; Memory allocation failed so we'll use existing block and resize it.
  1084. @@JustResize:
  1085.    rep      movsb
  1086.    mov      ah, 4ah                       ; modify allocation
  1087.    int      DOS
  1088.  
  1089. @@Fin:
  1090.    popf
  1091.    pop      es ds si di cx bx ax
  1092.    ret
  1093.  
  1094. ENDP fake_Env
  1095.  
  1096.  
  1097. ;--------------------------------------------------------------------------;
  1098. ;  Purpose:    Checks if a TSR has been installed in memory.
  1099. ;  Notes:      For a description of the steps followed here, see Ralf
  1100. ;                 Brown's alternate multiplex proposal.
  1101. ;              This procedure MUST BE RUN before going resident and
  1102. ;                 the multiplex id returned SHOULD BE SAVED in the
  1103. ;                 resident data area.
  1104. ;  Requires:   8086-class CPU
  1105. ;  Entry:      DS:SI = pointer to TSR's signature string.
  1106. ;  Exit:       AL = 0 if not installed, = 1 if installed, = 2 if all
  1107. ;                 multiplex ids are in use,
  1108. ;              AH = multiplex id to use based on AL,
  1109. ;              CX = TSR version number if installed,
  1110. ;              DX:DI = pointer to resident copy of TSR's sig if AL = 1.
  1111. ;  Calls:      getvect, memcmp
  1112. ;  Changes:    AX, CX, DX, DI
  1113. ;--------------------------------------------------------------------------;
  1114. PROC check_ifInstalled
  1115.  
  1116.    push     bx es
  1117.  
  1118. ; Do a quick check to see if 2d is hooked. 
  1119.    mov      al, AMI                       ; alternate multiplex interrupt
  1120.    call     getvect                       ; handler address in ES:BX
  1121.    ZERO     ax
  1122.    cmp      [BYTE PTR es:bx], 0cfh        ; is it IRET opcode?
  1123.    jz       SHORT @@Fin                   ;   yes, return with AX = 0
  1124.  
  1125. ; Do an install check on each possible multiplex id. 
  1126.    ZERO     bx                            ; marks 1st unused mplex id
  1127. @@CheckIt:
  1128.    ZERO     al                            ; be sure to do install check
  1129.    int      AMI                           ; might trash CX and DX:DI
  1130.    or       al, al                        ; is AL zero still?
  1131.    jnz      SHORT @@CmpSigs               ;   no, multiplex's in use
  1132.  
  1133. ; It's not in use. Save if it's the first.
  1134.    or       bl, bl                        ; 1st available id found already?
  1135.    jnz      SHORT @@NextMPlex             ;   yes
  1136.    inc      bl                            ;   no, but flag it now
  1137.    mov      bh, ah                        ;     and hold onto mplex
  1138.    jmp      SHORT @@NextMPlex
  1139.  
  1140. ; Compare first 16 bytes of sigs. DS:SI points to a known sig;
  1141. ; DX:DI to one somewhere in resident code.
  1142. @@CmpSigs:
  1143.    push     cx                            ; save TSR version number
  1144.    mov      cx, 16                        ; # bytes in sigs to compare
  1145.    mov      es, dx                        ; memcmp() needs ES:DI and DS:SI
  1146.    call     memcmp
  1147.    pop      cx                            ; recover TSR version number
  1148.    jnz      SHORT @@NextMPlex
  1149.    mov      al, 1
  1150.    jmp      SHORT @@Fin
  1151.  
  1152. ; Move on to next multiplex number.
  1153. @@NextMPlex:
  1154.    add      ah, 1                         ; sets zf if AH was 255. Done?
  1155.    jnz      SHORT @@CheckIt               ;   no, back for more
  1156.    mov      ah, bh                        ;   yes, AH = 1st available id
  1157.    or       dl, bl                        ;   did we run out?
  1158.    jnz      SHORT @@Fin                   ;     no
  1159.    mov      al, 2                         ;     yes
  1160.  
  1161. @@Fin:
  1162.    pop      es bx
  1163.    ret
  1164.  
  1165. ENDP check_ifInstalled
  1166.  
  1167.  
  1168. ;--------------------------------------------------------------------------;
  1169. ;  Purpose:    Installs a TSR in memory.
  1170. ;  Notes:      This procedure never returns.
  1171. ;              No changes are made here to the environment block.
  1172. ;              Entry points are assumed relative to ES.
  1173. ;              Call check_ifInstalled() to determine which multiplex
  1174. ;                 id will be used.
  1175. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  1176. ;  Entry:      DX = number of paragraphs to reserve,
  1177. ;              ES:BX = pointer to a structure of ISRHOOK.
  1178. ;  Exit:       n/a
  1179. ;  Calls:      hook_ISR
  1180. ;  Changes:    n/a
  1181. ;--------------------------------------------------------------------------;
  1182. PROC install_TSR
  1183.  
  1184. ; Set hooks as specified by ISRHOOK structure. 
  1185.    mov      bp, bx                        ; BX needed when hooking ISRs
  1186. @@NextHook:
  1187.    mov      al, [(ISRHOOK PTR bp).Vector]
  1188.    mov      bx, [(ISRHOOK PTR bp).Entry]
  1189.    call     hook_ISR                      ; AL, ES:BX -> n/a
  1190.    add      bp, SIZE ISRHOOK
  1191.    cmp      al, AMI                       ; at end of table?
  1192.    jnz      SHORT @@NextHook              ;   no
  1193.  
  1194. ; And now go resident. Note that DX already holds # paragraphs to keep.
  1195.    mov      ax, 3100h                     ; terminate/stay resident, rc = 0
  1196.    int      DOS                           ; via DOS
  1197.    ret                                    ; ***never reached***
  1198.  
  1199. ENDP install_TSR
  1200.  
  1201.  
  1202. ;--------------------------------------------------------------------------;
  1203. ;  Purpose:    Removes a TSR if possible.
  1204. ;  Notes:      Caller should use check_ifInstalled() to make sure the
  1205. ;                 TSR has first been installed.
  1206. ;              Entry points are assumed to be relative to ES.
  1207. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  1208. ;  Entry:      ES:BX = pointer to a structure of ISRHOOK.
  1209. ;  Exit:       cf set if operation failed
  1210. ;  Calls:      find_PrevISR, unhook_ISR
  1211. ;  Changes:    AX, cf
  1212. ;--------------------------------------------------------------------------;
  1213. PROC remove_TSR
  1214.  
  1215.    push     bx dx bp ds es                ; save registers
  1216.  
  1217. ; Set DS to ES to avoid segment overrides. Also, use BP for indexing into 
  1218. ; the hook table, and save it in DX as it's needed later.
  1219.    push     es
  1220.    pop      ds
  1221.    mov      bp, bx
  1222.    mov      dx, bx
  1223.  
  1224. ; For each vector in the hook table, make sure the ISR can be unhooked.
  1225. @@NextVect:
  1226.    mov      al, [(ISRHOOK PTR bp).Vector]
  1227.    mov      bx, [(ISRHOOK PTR bp).Entry]
  1228.    push     es                            ; hang onto this
  1229.    call     find_PrevISR                  ; able to find it?
  1230.    pop      es
  1231.    jc       SHORT @@Fin                   ;   no, abort
  1232.    add      bp, SIZE ISRHOOK
  1233.    cmp      al, AMI                       ; at end of table?
  1234.    jnz      SHORT @@NextVect              ;   no
  1235.  
  1236. ; It's possible to unhook all vectors, so go to it. 
  1237.    mov      bp, dx
  1238. @@NextHook:
  1239.    mov      al, [(ISRHOOK PTR bp).Vector]
  1240.    mov      bx, [(ISRHOOK PTR bp).Entry]
  1241.    call     unhook_ISR                    ; AL, ES:BX -> n/a
  1242.    jc       SHORT @@Fin                   ; it had better succeed!
  1243.    add      bp, SIZE ISRHOOK
  1244.    cmp      al, AMI                       ; at end of table?
  1245.    jnz      SHORT @@NextHook              ;   no
  1246.  
  1247. ; Now free TSR's memory.
  1248.    mov      bx, [ENVBLK]
  1249.    or       bx, bx                        ; any environment block?
  1250.    jz       SHORT @@MainMem               ;   no
  1251.    mov      es, bx                        ;   yes, free it
  1252.    mov      ah, 49h
  1253.    int      DOS                           ; trashes AH
  1254.    jc       SHORT @@Fin                   ; shouldn't be necessary
  1255. @@MainMem:
  1256.    mov      ah, 49h
  1257.    mov      bx, ds                        ; free TSR's memory
  1258.    mov      es, bx
  1259.    int      DOS
  1260.  
  1261. @@Fin:
  1262.    pop      es ds bp dx bx                ; pop registers
  1263.    ret
  1264.  
  1265. ENDP remove_TSR
  1266.  
  1267.  
  1268. EVEN
  1269. ;-------------------------------------------------------------------------;
  1270. ;  Purpose:    Converts string of digits to an *unsigned* integer in
  1271. ;              range [0, 65535].
  1272. ;  Notes:      Conversion stops with first non-numeric character.
  1273. ;  Requires:   8086-class CPU.
  1274. ;  Entry:      DS:SI = pointer to string of digits.
  1275. ;  Exit:       AX = unsigned integer (garbage if cf = 1),
  1276. ;              DS:SI = pointer to first non-digit found,
  1277. ;              cf = 1 if number is too big.
  1278. ;  Calls:      none
  1279. ;  Changes:    AX, SI
  1280. ;              flags
  1281. ;-------------------------------------------------------------------------;
  1282. PROC atou
  1283.  
  1284.    push     bx cx dx                      ; DX destroyed by MUL below
  1285.    ZERO     ax                            ; AX = digit to convert
  1286.    ZERO     bx                            ; BX = integer word
  1287.    mov      cx, 10                        ; CX = conversion factor
  1288.  
  1289. @@NextCh:
  1290.    mov      bl, [si]                      ; get character
  1291.    cmp      bl, '0'                       ; test if a digit
  1292.    jb       SHORT @@Fin
  1293.    cmp      bl, '9'
  1294.    ja       SHORT @@Fin
  1295.    inc      si                            ; bump up pointer
  1296.    mul      cx                            ; multiply old result by 10
  1297.    jc       SHORT @@Overflow
  1298.    sub      bl, '0'                       ; convert digit
  1299.    add      ax, bx                        ; add current value
  1300.    jnc      @@NextCh                      ; continue unless result too big
  1301.  
  1302. @@Overflow:
  1303.    ZERO     cx                            ; denotes overflow
  1304.    jmp      @@NextCh
  1305.  
  1306. @@Fin:
  1307.    cmp      cx, 10                        ; cf = (cx != 10)
  1308.    pop      dx cx bx
  1309.    ret
  1310.  
  1311. ENDP atou
  1312.  
  1313.  
  1314. EVEN
  1315. ;-------------------------------------------------------------------------;
  1316. ;  Purpose:    Tests if character is a valid ASCII digit.
  1317. ;  Notes:      none
  1318. ;  Requires:   8086-class CPU.
  1319. ;  Entry:      AL = character to be tested.
  1320. ;  Exit:       Zero flag set if true, cleared otherwise.
  1321. ;  Calls:      none 
  1322. ;  Changes:    flags
  1323. ;-------------------------------------------------------------------------;
  1324. PROC isdigit
  1325.  
  1326.    cmp      al, '0'                       ; if < '0' zf = 0
  1327.    jb       SHORT @@Fin
  1328.    cmp      al, '9'                       ; if > '9' zf = 0
  1329.    ja       SHORT @@Fin
  1330.    cmp      al, al                        ; set Z flag
  1331. @@Fin:
  1332.    ret
  1333.  
  1334. ENDP isdigit
  1335.  
  1336.  
  1337. ;-------------------------------------------------------------------------;
  1338. ;  Purpose:    Tests if character is lowercase.
  1339. ;  Notes:      none
  1340. ;  Requires:   8086-class CPU.
  1341. ;  Entry:      AL = character to be tested.
  1342. ;  Exit:       Zero flag set if true, cleared otherwise.
  1343. ;  Calls:      none 
  1344. ;  Changes:    flags
  1345. ;-------------------------------------------------------------------------;
  1346. PROC islower
  1347.  
  1348.    cmp      al, 'a'                       ; if < 'a' zf = 0
  1349.    jb       SHORT @@Fin
  1350.    cmp      al, 'z'                       ; if > 'z' zf = 0
  1351.    ja       SHORT @@Fin
  1352.    cmp      al, al                        ; set Z flag
  1353. @@Fin:
  1354.    ret
  1355.  
  1356. ENDP islower
  1357.  
  1358.  
  1359. ;-------------------------------------------------------------------------;
  1360. ;  Purpose:    Tests if character is uppercase.
  1361. ;  Notes:      none
  1362. ;  Requires:   8086-class CPU.
  1363. ;  Entry:      AL = character to be tested.
  1364. ;  Exit:       Zero flag set if true, cleared otherwise.
  1365. ;  Calls:      none 
  1366. ;  Changes:    flags
  1367. ;-------------------------------------------------------------------------;
  1368. PROC isupper
  1369.  
  1370.    cmp      al, 'A'                       ; if < 'A' zf = 0
  1371.    jb       SHORT @@Fin
  1372.    cmp      al, 'Z'                       ; if > 'Z' zf = 0
  1373.    ja       SHORT @@Fin
  1374.    cmp      al, al                        ; set Z flag
  1375. @@Fin:
  1376.    ret
  1377.  
  1378. ENDP isupper
  1379.  
  1380.  
  1381. ;-------------------------------------------------------------------------;
  1382. ;  Purpose:    Tests if character is an ASCII whitespace.
  1383. ;  Notes:      none
  1384. ;  Requires:   8086-class CPU.
  1385. ;  Entry:      AL = character to be tested.
  1386. ;  Exit:       Zero flag set if true, cleared otherwise.
  1387. ;  Calls:      none 
  1388. ;  Changes:    flags
  1389. ;-------------------------------------------------------------------------;
  1390. PROC iswhite
  1391.  
  1392.    cmp      al, SPACE                     ; if == SPACE then zf = 1
  1393.    jz       SHORT @@Fin
  1394.    cmp      al, TAB                       ; if == TAB then zf = 1
  1395.    jz       SHORT @@Fin
  1396.    cmp      al, LF                        ; if == LF then zf = 1
  1397.    jz       SHORT @@Fin
  1398.    cmp      al, CR                        ; if == CR then zf = 1
  1399. @@Fin:
  1400.    ret
  1401.  
  1402. ENDP iswhite
  1403.  
  1404.  
  1405. EVEN
  1406. ;-------------------------------------------------------------------------;
  1407. ;  Purpose:    Converts character to lowercase.
  1408. ;  Notes:      none
  1409. ;  Requires:   8086-class CPU.
  1410. ;  Entry:      AL = character to be converted.
  1411. ;  Exit:       AL = converted character.
  1412. ;  Calls:      none
  1413. ;  Changes:    AL
  1414. ;              flags
  1415. ;-------------------------------------------------------------------------;
  1416. PROC tolower
  1417.  
  1418.    cmp      al, 'A'                       ; if < 'A' then done
  1419.    jb       SHORT @@Fin
  1420.    cmp      al, 'Z'                       ; if > 'Z' then done
  1421.    ja       SHORT @@Fin
  1422.    or       al, 20h                       ; make it lowercase
  1423. @@Fin:
  1424.    ret
  1425.  
  1426. ENDP tolower
  1427.  
  1428.  
  1429. ;-------------------------------------------------------------------------;
  1430. ;  Purpose:    Converts character to uppercase.
  1431. ;  Notes:      none
  1432. ;  Requires:   8086-class CPU.
  1433. ;  Entry:      AL = character to be converted.
  1434. ;  Exit:       AL = converted character.
  1435. ;  Calls:      none
  1436. ;  Changes:    AL
  1437. ;              flags
  1438. ;-------------------------------------------------------------------------;
  1439. PROC toupper
  1440.  
  1441.    cmp      al, 'a'                       ; if < 'a' then done
  1442.    jb       SHORT @@Fin
  1443.    cmp      al, 'z'                       ; if > 'z' then done
  1444.    ja       SHORT @@Fin
  1445.    and      al, not 20h                   ; make it lowercase
  1446. @@Fin:
  1447.    ret
  1448.  
  1449. ENDP toupper
  1450.  
  1451.  
  1452. EVEN
  1453. ;--------------------------------------------------------------------------;
  1454. ;  Purpose:    Compares two regions of memory.
  1455. ;  Notes:      none
  1456. ;  Requires:   8086-class CPU.
  1457. ;  Entry:      CX = number of bytes to compare,
  1458. ;              DS:SI = start of 1st region of memory,
  1459. ;              ES:DI = start of 2nd region.
  1460. ;  Exit:       zf = 1 if equal.
  1461. ;  Calls:      none
  1462. ;  Changes:    zf
  1463. ;--------------------------------------------------------------------------;
  1464. PROC memcmp
  1465.  
  1466.    push     cx di si
  1467.    pushf                                  ; save direction flag
  1468.    cld
  1469.    repe     cmpsb                         ; compare both areas
  1470.    popf                                   ; recover direction flag
  1471.    dec      di
  1472.    dec      si
  1473.    cmpsb                                  ; set flags based on final byte
  1474.    pop      si di cx
  1475.    ret
  1476.  
  1477. ENDP memcmp
  1478.  
  1479.  
  1480. EVEN
  1481. ;-------------------------------------------------------------------------;
  1482. ;  Purpose:    Calculates length of an ASCIIZ string.
  1483. ;  Notes:      Terminal char is _not_ included in the count.
  1484. ;  Requires:   8086-class CPU.
  1485. ;  Entry:      ES:DI = pointer to string.
  1486. ;  Exit:       CX = length of string,
  1487. ;              cf = 0 and zf = 1 if EOS found,
  1488. ;              cf = 1 and zf = 0 if EOS not found within segment.
  1489. ;  Calls:      none
  1490. ;  Changes:    CX,
  1491. ;              flags
  1492. ;-------------------------------------------------------------------------;
  1493. PROC strlen
  1494.  
  1495.    push     ax di
  1496.    pushf
  1497.    cld                                    ; scan forward only
  1498.    mov      al, EOS                       ; character to search for
  1499.    mov      cx, di                        ; where are we now
  1500.    not      cx                            ; what's left in segment - 1
  1501.    push     cx                            ; save char count
  1502.    repne    scasb
  1503.    je       SHORT @@Done
  1504.    scasb                                  ; test final char
  1505.    dec      cx                            ; avoids trouble with "not" below
  1506.  
  1507. @@Done:
  1508.    pop      ax                            ; get original count
  1509.    sub      cx, ax                        ; subtract current count
  1510.    not      cx                            ; and invert it
  1511.    popf                                   ; restore df
  1512.    dec      di
  1513.    cmp      [BYTE PTR es:di], EOS
  1514.    je       SHORT @@Fin                   ; cf = 0 if equal
  1515.    stc                                    ; set cf => error
  1516.  
  1517. @@Fin:
  1518.    pop      di ax
  1519.    ret
  1520.  
  1521. ENDP strlen
  1522.  
  1523.  
  1524. END
  1525.