home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / KEYBOARD / RECALL11.ZIP / RECALL.ASM < prev    next >
Encoding:
Assembly Source File  |  1991-05-20  |  83.4 KB  |  1,901 lines

  1. ;--------------------------------------------------------------------------;
  2. ;  Program:    Recall  .Asm                                                ;
  3. ;  Purpose:    Commandline editor and history TSR.                         ;
  4. ;  Notes:      Compiles under TURBO Assembler, v2.0. Requires DOS v2.xx    ;
  5. ;                 or higher. Editing keys are coded as PC extended scan    ;
  6. ;                 codes; otherwise, this uses only DOS calls.              ;
  7. ;              The overall design is derived from RDE (aka, Rainbow DOS    ;
  8. ;                 Editor) by Joe Kneidel. The methods used to install and  ;
  9. ;                 uninstall this TSR are from _MS-DOS Developer's Guide_,  ;
  10. ;                 by Angermayer and Jaeger.                                ;
  11. ;  Status:     Released into the >>>public domain<<<. Enjoy! If you use    ;
  12. ;                 it, let me know what you think. You don't have to send   ;
  13. ;                 any money, just comments and suggestions.                ;
  14. ;  Updates:    24-Oct-90, v1.0a, GAT                                       ;
  15. ;                 - initial version                                        ;
  16. ;              28-Oct-90, v1.0b, GAT                                       ;
  17. ;                 - renamed get_LineFromUser to get_CmdLine and            ;
  18. ;                   add_LineToBuffer to store_CmdInBuf.                    ;
  19. ;                 - made sure to zero out CH in add_LineToBuffer.          ;
  20. ;                 - excluded CR from byte count in get_CmdLine.            ;
  21. ;                 - kept track of CurCmd rather than PrevCmd/NextCmd and   ;
  22. ;                   moved checks on command from recall_CmdFromBuf to      ;
  23. ;                   mov_pcmd and mov_ncmd.                                 ;
  24. ;                 - specified command table as an array of structures and  ;
  25. ;                   revised ways it was accessed in get_CmdLine.           ;
  26. ;                 - rearranged various procedures.                         ;
  27. ;                 - spruced up comments.                                   ;
  28. ;              31-Oct-90, v1.1a, GAT                                       ;
  29. ;                 - removed notices about preliminary notices.             ;
  30. ;                 - cleanup up help message a bit.                         ;
  31. ;                 - avoided use of LABELs.                                 ;
  32. ;                 - added list_CmdLines to list recall buffer contents.    ;
  33. ;--------------------------------------------------------------------------;
  34.  
  35. ;--------------------------------------------------------------------------;
  36. ;  Author:     George A. Theall                                            ;
  37. ;  Phone:      +1 215 662 0558                                             ;
  38. ;  SnailMail:  TifaWARE                                                    ;
  39. ;              506 South 41st St., #3M                                     ;
  40. ;              Philadelphia, PA.  19104   USA                              ;
  41. ;  E-Mail:     GTHEALL@PENNDRLS.UPENN.EDU (Internet)                       ;
  42. ;--------------------------------------------------------------------------;
  43.  
  44. %NEWPAGE
  45. ;--------------------------------------------------------------------------;
  46. ;                          D I R E C T I V E S                             ;
  47. ;--------------------------------------------------------------------------;
  48. DOSSEG
  49. MODEL     tiny
  50.  
  51. IDEAL
  52. LOCALS
  53. JUMPS
  54.  
  55. ;
  56. ; This section comes from D:\ASM\INCLUDE\Equates.Inc.
  57. ;
  58. EOS                 EQU       0              ; terminates strings
  59. BELL                EQU       7
  60. BS                  EQU       8
  61. TAB                 EQU       9
  62. CR                  EQU       13
  63. LF                  EQU       10
  64. ESCAPE              EQU       27             ; nb: ESC is a TASM keyword
  65. SPACE               EQU       ' '
  66. KEY_F1              EQU       3bh
  67. KEY_F2              EQU       3ch
  68. KEY_F3              EQU       3dh
  69. KEY_F4              EQU       3eh
  70. KEY_F5              EQU       3fh
  71. KEY_F6              EQU       40h
  72. KEY_F7              EQU       41h
  73. KEY_F8              EQU       42h
  74. KEY_F9              EQU       43h
  75. KEY_F10             EQU       44h
  76. KEY_HOME            EQU       47h
  77. KEY_UP              EQU       48h
  78. KEY_PGUP            EQU       49h
  79. KEY_LEFT            EQU       4bh
  80. KEY_RIGHT           EQU       4dh
  81. KEY_END             EQU       4fh
  82. KEY_DOWN            EQU       50h
  83. KEY_PGDN            EQU       51h
  84. KEY_INS             EQU       52h
  85. KEY_DEL             EQU       53h
  86. KEY_C_F1            EQU       5eh
  87. KEY_C_F2            EQU       5fh
  88. KEY_C_F3            EQU       60h
  89. KEY_C_F4            EQU       61h
  90. KEY_C_F5            EQU       62h
  91. KEY_C_F6            EQU       63h
  92. KEY_C_F7            EQU       64h
  93. KEY_C_F8            EQU       65h
  94. KEY_C_F9            EQU       66h
  95. KEY_C_F10           EQU       67h
  96. KEY_C_LEFT          EQU       73h
  97. KEY_C_RIGHT         EQU       74h
  98. KEY_C_END           EQU       75h
  99. KEY_C_PGDN          EQU       76h
  100. KEY_C_HOME          EQU       77h
  101. KEY_C_PGUP          EQU       84h
  102. KEY_F11             EQU       85h
  103. KEY_F12             EQU       86h
  104. KEY_C_F11           EQU       89h
  105. KEY_C_F12           EQU       8ah
  106. DOS                 EQU       21h            ; main MSDOS interrupt
  107. STDIN               EQU       0              ; standard input
  108. STDOUT              EQU       1              ; standard output
  109. STDERR              EQU       2              ; error output
  110. STDAUX              EQU       3              ; COM port
  111. STDPRN              EQU       4              ; printer
  112.  
  113. ;
  114. ; This section comes from D:\ASM\INCLUDE\Macros.Inc.
  115. ;
  116. MACRO    Pop_M    RegList                    ;; Pops registers off stack.
  117.    IRP      Reg, <RegList>
  118.       IFIDNI   <Reg>, <flags>
  119.          popf
  120.       ELSE
  121.          pop      Reg
  122.       ENDIF
  123.    ENDM
  124. ENDM
  125. MACRO    Push_M   RegList                    ;; Pushes registers onto stack.
  126.    IRP      Reg, <RegList>
  127.       IFIDNI   <Reg>, <flags>
  128.          pushf
  129.       ELSE
  130.          push     Reg
  131.       ENDIF
  132.    ENDM
  133. ENDM
  134. MACRO    Zero     Reg                        ;; Zeros any register.
  135.          xor      Reg, Reg
  136. ENDM
  137.  
  138.  
  139. ; BUFSIZE specifies size of the recall buffer for collecting commandlines.
  140. ; Values of 255 or below are risky because that's the maximum buffer size
  141. ; for subfunction 10 of Int 21h and my code in add_LineToBuffer does not 
  142. ; make sure commandlines will fit. I foresee no problems, however, with 
  143. ; larger values up to about 60K.
  144. BUFSIZE   equ       1024                     ; >>>CHANGE AT YOUR RISK<<<
  145. ENTRY     equ       OFFSET handle_Int21      ; entry point for my ISR
  146. VERSION   equ       '1.1a'                   ; current version of RECALL
  147. ERRH      equ       1                        ; errorlevel if help given
  148. ERRINS    equ       10                       ; errorlevel if install failed
  149. ERRNYI    equ       20                       ; errorlevel if not yet installed
  150. OFF       equ       0
  151. ON        equ       1
  152.  
  153.  
  154. %NEWPAGE
  155. ;--------------------------------------------------------------------------;
  156. ;                        C O D E    S E G M E N T                          ;
  157. ;--------------------------------------------------------------------------;
  158. CODESEG
  159.  
  160. ORG       0                                  ; address of code segment start
  161. SegStart  DB        ?                        ;    used in install_TSR
  162.  
  163. ORG       2ch                                ; address of environment segment
  164. EnvSeg    DW        ?                        ;    used in install_TSR
  165.  
  166. ORG       80h                                ; address of commandline
  167. CmdLen    DB        ?
  168. CmdLine   DB        127 DUP (?)
  169.  
  170. ORG       100h                               ; start of .COM file
  171. STARTUPCODE
  172.           jmp       main
  173.  
  174.  
  175. %NEWPAGE
  176. ;--------------------------------------------------------------------------;
  177. ;                        R E S I D E N T   D A T A                         ;
  178. ;--------------------------------------------------------------------------;
  179. TSR_Sig   DB        'TifaWARE RECALL v', VERSION, ' is now installed.'
  180.           DB        CR, LF, EOS
  181. OldInt21  DD        ?                        ; address of old INT 21H handler
  182.                                              ;    MUST BE ofs 1st, then seg!
  183. OldAX     DW        ?                        ; value of AX register when my
  184.                                              ;    handler is first called
  185. OldStack  DD        ?                        ; address of caller's stack
  186. CurCmd    DW        0                        ; pointer to current command
  187.                                              ;    in recall buffer
  188. InsMode   DB        ON                       ; InsertMode toggle flag
  189.  
  190. STRUC     CMD                                ; structure for editing cmd
  191.           Key       DB        ?              ;    extended code for key
  192.           Function  DW        ?              ;    address of editing function
  193. ENDS
  194. CmdTbl    CMD       <KEY_LEFT,     OFFSET mov_lchar>
  195.           CMD       <KEY_RIGHT,    OFFSET mov_rchar>
  196.           CMD       <KEY_PGUP,     OFFSET mov_lword>
  197.           CMD       <KEY_PGDN,     OFFSET mov_rword>
  198.           CMD       <KEY_HOME,     OFFSET mov_bol>
  199.           CMD       <KEY_END,      OFFSET mov_eol>
  200.           CMD       <KEY_UP,       OFFSET mov_pcmd>
  201.           CMD       <KEY_DOWN,     OFFSET mov_ncmd>
  202.           CMD       <BS,           OFFSET del_lchar>
  203.           CMD       <KEY_C_LEFT,   OFFSET del_lchar>
  204.           CMD       <KEY_DEL,      OFFSET del_rchar>
  205.           CMD       <KEY_C_RIGHT,  OFFSET del_rchar>
  206.           CMD       <KEY_C_PGUP,   OFFSET del_lword>
  207.           CMD       <KEY_C_PGDN,   OFFSET del_rword>
  208.           CMD       <KEY_C_HOME,   OFFSET del_bol>
  209.           CMD       <KEY_C_END,    OFFSET del_eol>
  210.           CMD       <ESCAPE,       OFFSET del_line>
  211.           CMD       <KEY_C_F9,     OFFSET del_buf>
  212.           CMD       <KEY_INS,      OFFSET toggle_InsMode>
  213.           CMD       <0,            OFFSET ring_Bell>   ; >>>must be last<<<
  214.  
  215.  
  216. %NEWPAGE
  217. ;--------------------------------------------------------------------------;
  218. ;                          L O C A L   S T A C K                           ;
  219. ;--------------------------------------------------------------------------;
  220.           DB        16 dup("STACK   ")       ; 128 bytes for local stack
  221. StackTop  =         $
  222.  
  223.  
  224. %NEWPAGE
  225. ;--------------------------------------------------------------------------;
  226. ;                        R E S I D E N T   C O D E                         ;
  227. ;--------------------------------------------------------------------------;
  228. ;----  is_CharWhite  ------------------------------------------------------;
  229. ;  Purpose:    Tests if character is either a blank or a tab.              ;
  230. ;  Notes:      none                                                        ;
  231. ;  Entry:      AL = character to be tested.                                ;
  232. ;  Exit:       Zero flag set if true, cleared otherwise.                   ;
  233. ;  Calls:      none                                                        ;
  234. ;  Changes:    flags                                                       ;
  235. ;--------------------------------------------------------------------------;
  236. PROC is_CharWhite
  237.  
  238.           cmp       al, SPACE                ; if == SPACE then zf = 1
  239.           jz        SHORT @@Fin
  240.           cmp       al, TAB                  ; if == TAB then zf = 1
  241. @@Fin:
  242.           ret
  243. ENDP is_CharWhite
  244.  
  245.  
  246. ;----  get_KeyNoEcho  -----------------------------------------------------;
  247. ;  Purpose:    Reads key from STDIN, waiting as necessary.                 ;
  248. ;  Notes:      Allows DESQview to operate efficiently if task inactive.    ;
  249. ;              Ctrl-C and Ctrl-Break generate Int 23h.                     ;
  250. ;  Entry:      n/a                                                         ;
  251. ;  Exit:       AL = character (0 => extended code available next).         ;
  252. ;  Calls:      none                                                        ;
  253. ;  Changes:    AX                                                          ;
  254. ;--------------------------------------------------------------------------;
  255. PROC get_KeyNoEcho
  256.  
  257.           mov       ah, 8
  258.           int       DOS
  259.           ret
  260. ENDP get_KeyNoEcho
  261.  
  262.  
  263. ;----  ring_Bell  ---------------------------------------------------------;
  264. ;  Purpose:    Rings the console bell as a warning to user.                ;
  265. ;  Notes:      none                                                        ;
  266. ;  Entry:      n/a                                                         ;
  267. ;  Exit:       n/a                                                         ;
  268. ;  Calls:      none                                                        ;
  269. ;  Changes:    none                                                        ;
  270. ;--------------------------------------------------------------------------;
  271. PROC ring_Bell
  272.  
  273.           push      ax dx
  274.           mov       ah, 2
  275.           mov       dl, BELL
  276.           int       DOS
  277.           pop       dx ax
  278.           ret
  279. ENDP ring_Bell
  280.  
  281.  
  282. ;----  display_Char  ------------------------------------------------------;
  283. ;  Purpose:    Displays character on STDOUT and advances to next char.     ;
  284. ;  Notes:      Do *not* call this procedure to display backspaces; use     ;
  285. ;                   backup_Cursor instead. Though they'd be displayed      ;
  286. ;                   properly, BX would be *incremented* here.              ;
  287. ;              Does *not* adjust CH or CL.                                 ;
  288. ;  Entry:      AL = character to display,                                  ;
  289. ;              BX = pointer to current position in commandline.            ;
  290. ;  Exit:       BX++                                                        ;
  291. ;  Calls:      none                                                        ;
  292. ;  Changes:    BX                                                          ;
  293. ;--------------------------------------------------------------------------;
  294. PROC display_Char
  295.  
  296.           push      ax dx
  297.           mov       dl, al
  298.           mov       ah, 2
  299.           int       DOS
  300.           inc       bx                       ; move to next char on cmdline
  301.           pop       dx ax
  302.           ret
  303. ENDP display_Char
  304.  
  305.  
  306. ;----  advance_Cursor  ----------------------------------------------------;
  307. ;  Purpose:    Moves the cursor forwards on the screen.                    ;
  308. ;  Notes:      none                                                        ;
  309. ;  Entry:      BX = pointer to current position in commandline,            ;
  310. ;              CH = # of bytes to end of line,                             ;
  311. ;              SI = # of bytes to advance.                                 ;
  312. ;  Exit:       BX += SI,                                                   ;
  313. ;              CH -= SI.                                                   ;
  314. ;  Calls:      display_Char                                                ;
  315. ;  Changes:    AX, CH,                                                     ;
  316. ;              BX (display_Char)                                           ;
  317. ;--------------------------------------------------------------------------;
  318. PROC advance_Cursor
  319.  
  320.           or        si, si                   ; anything to skip over?
  321.           jz        SHORT @@Fin
  322.  
  323. ; Adjust CH now. (This could be left until later - no big deal.)
  324.           mov       al, ch
  325.           Zero      ah
  326.           sub       ax, si
  327.           mov       ch, al                   ; CH -= SI
  328.  
  329. ; Display SI characters on commandline.
  330.           push      cx
  331.           mov       cx, si
  332. @@NextChar:
  333.           mov       al, [bx]
  334.           call      display_Char             ; nb: increments BX too
  335.           loop      SHORT @@NextChar
  336.           pop       cx
  337.  
  338. @@Fin:
  339.           ret
  340. ENDP advance_Cursor
  341.  
  342.  
  343. ;----  backup_Cursor  -----------------------------------------------------;
  344. ;  Purpose:    Moves the cursor backwards on the screen.                   ;
  345. ;  Notes:      Does *not* handle properly line-wrapping yet.               ;
  346. ;  Entry:      BX = pointer to current position in commandline,            ;
  347. ;              CH = # of bytes to end of line,                             ;
  348. ;              SI = # of bytes to back up.                                 ;
  349. ;  Exit:       BX -= SI,                                                   ;
  350. ;              CH += SI.                                                   ;
  351. ;  Calls:      none                                                        ;
  352. ;  Changes:    AX, BX, CH                                                  ;
  353. ;--------------------------------------------------------------------------;
  354. PROC backup_Cursor
  355.  
  356.           or        si, si                   ; anything to skip over?
  357.           jz        SHORT @@Fin
  358.  
  359. ; Adjust BX and CH now. (This could be left until later - no big deal.)
  360.           sub       bx, si                   ; BX -= SI
  361.           mov       al, ch
  362.           Zero      ah
  363.           add       ax, si
  364.           mov       ch, al                   ; CH += SI
  365.  
  366. ; Back up cursor by displaying non-destructive backspaces.
  367.           push      cx dx
  368.           mov       ah, 2
  369.           mov       cx, si
  370.           mov       dl, BS
  371. @@PrevChar:
  372.           int       DOS
  373.           loop      SHORT @@PrevChar
  374.           pop       dx cx
  375.  
  376. @@Fin:
  377.           ret
  378. ENDP backup_Cursor
  379.  
  380.  
  381. ;----  delete_Chars  ------------------------------------------------------;
  382. ;  Purpose:    Deletes characters from commandline.                        ;
  383. ;  Notes:      No checks are done on SI's validity; ie, caller should      ;
  384. ;                   ensure there are enough characters on line to delete.  ;
  385. ;  Entry:      BX = pointer to current position in commandline,            ;
  386. ;              CH = # of bytes to end of line,                             ;
  387. ;              CL = # of bytes left in commandline,                        ;
  388. ;              SI = # of characters to delete.                             ;
  389. ;  Exit:       CH -= SI,                                                   ;
  390. ;              CL += SI.                                                   ;
  391. ;  Calls:      display_Char, backup_Cursor                                 ;
  392. ;  Changes:    CH, CL, SI,                                                 ;
  393. ;              AX (backup_Cursor)                                          ;
  394. ;--------------------------------------------------------------------------;
  395. PROC delete_Chars
  396.  
  397.           or        si, si                   ; anything to delete?
  398.           jz        SHORT @@Fin
  399.  
  400. ; Adjust CH and CL now while I have SI handy. At the same time
  401. ; I am also computing the number of characters to shift.
  402.           mov       al, cl
  403.           Zero      ah
  404.           add       ax, si
  405.           mov       cl, al                   ; CL += SI
  406.           mov       al, ch
  407.           sub       ax, si
  408.           mov       ch, al                   ; CH -= SI
  409.           push      cx                       ; final values of CH and CL
  410.           push      ax                       ; used to back up cursor
  411.  
  412. ; Shift CH - SI characters remaining on line to the left by SI characters.
  413.           mov       cx, ax                   ; CX = CH - SI
  414.           jcxz      SHORT @@Coverup          ; skip if deleting to eol
  415. @@NextChar:
  416.           mov       al, [bx+si]
  417.           mov       [bx], al
  418.           call      display_Char             ; nb: increments BX too
  419.           loop      SHORT @@NextChar
  420.  
  421. ; Display spaces to overwrite chars remaining on line.
  422. @@CoverUp:
  423.           mov       al, SPACE
  424.           mov       cx, si
  425. @@NextBlank:
  426.           call      display_Char             ; nb: increments BX too
  427.           loop      SHORT @@NextBlank
  428.  
  429. ; Back up cursor to its location on invocation.
  430.           pop       ax                       ; CH - SI
  431.           add       si, ax                   ; SI += (CH - SI)
  432.           call      backup_Cursor            ; nb: decrements BX too
  433.           pop       cx
  434.  
  435. ; NB: BX will be incremented (CH - SI) + SI times by display_Char and
  436. ; decremented CH times by backup_Cursor so overall it won't change.
  437. @@Fin:
  438.           ret
  439. ENDP delete_Chars
  440.  
  441.  
  442. ;----  add_CharToLine  ----------------------------------------------------;
  443. ;  Purpose:    Adds a character to commandline buffer.                     ;
  444. ;  Notes:      Checks to see if buffer would overflow first.               ;
  445. ;  Entry:      AL = character to add,                                      ;
  446. ;              BX = pointer to current position in line,                   ;
  447. ;              CH = number of characters until end of line,                ;
  448. ;              CL = number of bytes left in line.                          ;
  449. ;  Exit:       BX and CX changed as appropriate.                           ;
  450. ;  Calls:      display_Char, backup_Cursor, ring_Bell                      ;
  451. ;  Changes:    AX, BX, CX                                                  ;
  452. ;--------------------------------------------------------------------------;
  453. PROC add_CharToLine
  454.  
  455. ; Check for space unless insert mode is OFF *and* not at eol.
  456.           cmp       [cs:InsMode], ON
  457.           je        SHORT @@CheckForSpace
  458.           or        ch, ch
  459.           jz        SHORT @@CheckForSpace
  460.  
  461. ; Overwrite existing character while in not at eol.
  462.           mov       [bx], al
  463.           call      display_Char             ; nb: increments BX too
  464.           dec       ch
  465.           jmp       SHORT @@Fin
  466.  
  467. @@CheckForSpace:
  468.           or        cl, cl
  469.           jz        SHORT @@Abort
  470.           or        ch, ch
  471.           jnz       SHORT @@AddWithShift
  472.  
  473. ; At end of line.
  474.           mov       [bx], al
  475.           call      display_Char             ; nb: increments BX too
  476.           dec       cl
  477.           jmp       SHORT @@Fin
  478.  
  479. ; Add character and shift everything to right over by 1 position.
  480. @@AddWithShift:
  481.           push      cx dx
  482.           mov       cl, ch                   ; CH = chars to eol
  483.           Zero      ch
  484.           mov       si, cx                   ; save for backing up
  485.           inc       cl                       ; but add 1 to display new char
  486. @@NextChar:
  487.           mov       dl, [bx]                 ; use DL as temporary storage
  488.           mov       [bx], al
  489.           call      display_Char             ; nb: increments BX too
  490.           mov       al, dl                   ; recover previous char
  491.           loop      SHORT @@NextChar
  492.           call      backup_Cursor            ; nb: decrements BX too
  493.           pop       dx cx
  494.           dec       cl
  495.           jmp       SHORT @@Fin
  496.  
  497. @@Abort:
  498.           call      ring_Bell                ; if out of space
  499.  
  500. @@Fin:
  501.           ret
  502. ENDP add_CharToLine
  503.  
  504.  
  505. ;----  find_StartofPrevWord  ----------------------------------------------;
  506. ;  Purpose:    Locates start of previous word in commandline.              ;
  507. ;  Notes:      "Words" are delineated by blanks and/or start/finish of     ;
  508. ;                   the commandline.                                       ;
  509. ;  Entry:      BX = pointer to current position in commandline.            ;
  510. ;  Exit:       SI = # of characters from BX to start of previous word.     ;
  511. ;  Calls:      is_CharWhite                                                ;
  512. ;  Changes:    AX, SI                                                      ;
  513. ;--------------------------------------------------------------------------;
  514. PROC find_StartofPrevWord
  515.  
  516.           push      bx dx
  517.           inc       dx
  518.           inc       dx                       ; DX now points to bol
  519.           mov       si, bx                   ; SI = current position
  520.  
  521. ; Skip over any whitespace. Note: don't bother with 1st character -
  522. ; think of how it should behave if positioned at start of a word.
  523. @@SkipWhite:
  524.           dec       bx
  525.           cmp       bx, dx
  526.           jb        SHORT @@Fin              ; done if BX < bol
  527.           mov       al, [bx]
  528.           call      is_CharWhite
  529.           jz        SHORT @@SkipWhite
  530.  
  531. ; Next skip over non-blanks until the start of the word.
  532. @@SkipWord:
  533.           dec       bx
  534.           cmp       bx, dx
  535.           jb        SHORT @@Fin              ; done if BX < bol
  536.           mov       al, [bx]
  537.           call      is_CharWhite
  538.           jnz       SHORT @@SkipWord
  539.  
  540. ; Finally compute how many characters must be skipped.
  541. @@Fin:
  542.           inc       bx                       ; backed up 1 too many
  543.           sub       si, bx
  544.           pop       dx bx
  545.           ret
  546. ENDP find_StartofPrevWord
  547.  
  548.  
  549. ;----  find_StartofNextWord  ----------------------------------------------;
  550. ;  Purpose:    Locates start of next word in commandline.                  ;
  551. ;  Notes:      "Words" are delineated by blanks and/or start/finish of     ;
  552. ;                   the commandline.                                       ;
  553. ;  Entry:      BX = pointer to current position in commandline.            ;
  554. ;  Exit:       SI = # of characters from BX to start of next word.         ;
  555. ;  Calls:      is_CharWhite                                                ;
  556. ;  Changes:    AX, SI                                                      ;
  557. ;--------------------------------------------------------------------------;
  558. PROC find_StartofNextWord
  559.  
  560.           push      bx dx
  561.           mov       dx, bx
  562.           mov       al, ch
  563.           Zero      ah
  564.           add       dx, ax                   ; DX now points to eol
  565.  
  566. ; Skip over any existing word. Note: unlike find_StartofPrevWord, here
  567. ; we do not want to initially skip ahead - imagine if cursor were at
  568. ; a blank before the start of a word.
  569. @@SkipWord:
  570.           cmp       bx, dx
  571.           je        SHORT @@Fin              ; done if BX = eol
  572.           mov       al, [bx]
  573.           inc       bx
  574.           call      is_CharWhite
  575.           jnz       SHORT @@SkipWord
  576.  
  577. ; Next skip over whitespace until the start of the word.
  578. @@SkipWhite:
  579.           cmp       bx, dx
  580.           je        SHORT @@Fin              ; done if BX = eol
  581.           mov       al, [bx]
  582.           inc       bx
  583.           call      is_CharWhite
  584.           jz        SHORT @@SkipWhite
  585.           dec       bx                       ; point back to white space
  586.  
  587. ; Finally compute how many characters must be skipped.
  588. @@Fin:
  589.           mov       si, bx                   ; where we are now
  590.           pop       dx bx
  591.           sub       si, bx                   ; less where we started from
  592.           ret
  593. ENDP find_StartofNextWord
  594.  
  595.  
  596. ;----  recall_CmdFromBuf  -------------------------------------------------;
  597. ;  Purpose:    Replaces current with a commandline from buffer.            ;
  598. ;  Notes:      Does *not* check SI's validity.                             ;
  599. ;  Entry:      BX = pointer to current position in commandline,            ;
  600. ;              CH = # of bytes to end of line,                             ;
  601. ;              CL = # of bytes left in commandline,                        ;
  602. ;              SI = pointer to command in recall buffer.                   ;
  603. ;  Entry:      BX = pointer to end of new commandline,                     ;
  604. ;              CH = 0,                                                     ;
  605. ;              CL = # of bytes left in new commandline.                    ;
  606. ;  Calls:      del_line, display_Char, ring_Bell                           ;
  607. ;  Changes:    AL, BX, CH, CL, SI                                          ;
  608. ;--------------------------------------------------------------------------;
  609. PROC recall_CmdFromBuf
  610.  
  611. ; Clear current commandline and display new one.
  612.           push      si                       ; since del_line zaps SI
  613.           call      del_line                 ; changes CH and CL such that CX
  614.                                              ;   == max # of chars in buffer
  615.           pop       si
  616. @@NextChar:
  617.           mov       al, [cs:si]
  618.           cmp       al, CR
  619.           je        SHORT @@Fin
  620.           inc       si
  621.           mov       [bx], al
  622.           call      display_Char             ; nb: increments BX too
  623.           loop      SHORT @@NextChar         ; continue as long as CX > 0
  624.           cmp       [BYTE cs:si], CR         ; did loop end prematurely?
  625.           je        SHORT @@Fin              ;   no
  626.           call      ring_Bell                ;   yes, warn user
  627.  
  628. @@Fin:
  629.           ret
  630. ENDP recall_CmdFromBuf
  631.  
  632.  
  633. ;----  mov_lchar  ---------------------------------------------------------;
  634. ;  Purpose:    Moves cursor left in the commandline.                       ;
  635. ;  Notes:      none                                                        ;
  636. ;  Entry:      BX = pointer to current position in commandline,            ;
  637. ;              CH = # of bytes to end of line.                             ;
  638. ;  Exit:       BX--,                                                       ;
  639. ;              CH++,                                                       ;
  640. ;              SI = 1 (or 0 if already at bol).                            ;
  641. ;  Calls:      backup_Cursor                                               ;
  642. ;  Changes:    SI,                                                         ;
  643. ;              BX, CH (backup_Cursor)                                      ;
  644. ;--------------------------------------------------------------------------;
  645. PROC mov_lchar
  646.  
  647.           mov       si, bx
  648.           sub       si, dx
  649.           cmp       si, 2
  650.           ja        SHORT @@MoveIt           ; at bol if BX - DX <= 2
  651.           Zero      si
  652.           jmp       SHORT @@Fin
  653.  
  654. @@MoveIt:
  655.           mov       si, 1                    ; move 1 character
  656.           call      backup_Cursor            ; nb: decrements BX too
  657.  
  658. @@Fin:
  659.           ret
  660. ENDP mov_lchar
  661.  
  662.  
  663. ;----  mov_rchar  ---------------------------------------------------------;
  664. ;  Purpose:    Moves cursor right in the commandline.                      ;
  665. ;  Notes:      none                                                        ;
  666. ;  Entry:      BX = pointer to current position in commandline,            ;
  667. ;              CH = # of bytes to end of line.                             ;
  668. ;  Exit:       BX++,                                                       ;
  669. ;              CH--,                                                       ;
  670. ;              SI = 1 (or 0 if already at eol).                            ;
  671. ;  Calls:      advance_Cursor                                              ;
  672. ;  Changes:    SI,                                                         ;
  673. ;              AX, BX, CH (advance_Cursor)                                 ;
  674. ;--------------------------------------------------------------------------;
  675. PROC mov_rchar
  676.  
  677.           Zero      si                       ; set SI = 0 first
  678.           or        ch, ch
  679.           jz        SHORT @@Fin              ; abort if CH = 0
  680.           inc       si                       ; move 1 character
  681.           call      advance_Cursor           ; nb: increments BX
  682.  
  683. @@Fin:
  684.           ret
  685. ENDP mov_rchar
  686.  
  687.  
  688. ;----  mov_lword  ---------------------------------------------------------;
  689. ;  Purpose:    Moves cursor to start of previous word.                     ;
  690. ;  Notes:      none                                                        ;
  691. ;  Entry:      BX = pointer to current position in commandline,            ;
  692. ;              CH = # of bytes to end of line.                             ;
  693. ;  Exit:       BX and CH adjusted as appropriate.                          ;
  694. ;  Calls:      find_StartofPrevWord, backup_Cursor                         ;
  695. ;  Changes:    SI, (find_StartofPrevWord)                                  ;
  696. ;              AX, BX, CH (backup_Cursor)                                  ;
  697. ;--------------------------------------------------------------------------;
  698. PROC mov_lword
  699.  
  700.           call      find_StartofPrevWord
  701.           call      backup_Cursor
  702.           ret
  703. ENDP mov_lword
  704.  
  705.  
  706. ;----  mov_rword  ---------------------------------------------------------;
  707. ;  Purpose:    Moves cursor to start of next word.                         ;
  708. ;  Notes:      none                                                        ;
  709. ;  Entry:      BX = pointer to current position in commandline,            ;
  710. ;              CH = # of bytes to end of line.                             ;
  711. ;  Exit:       BX and CH adjusted as appropriate.                          ;
  712. ;  Calls:      find_StartofNextWord, advance_Cursor                        ;
  713. ;  Changes:    SI, (find_StartofNextWord)                                  ;
  714. ;              AX, BX, CH (advance_Cursor)                                 ;
  715. ;--------------------------------------------------------------------------;
  716. PROC mov_rword
  717.  
  718.           call      find_StartofNextWord
  719.           call      advance_Cursor
  720.           ret
  721. ENDP mov_rword
  722.  
  723.  
  724. ;----  mov_bol  -----------------------------------------------------------;
  725. ;  Purpose:    Moves cursor to start of commandline.                       ;
  726. ;  Notes:      none                                                        ;
  727. ;  Entry:      BX = pointer to current position in commandline,            ;
  728. ;              CH = # of bytes to end of line.                             ;
  729. ;  Exit:       BX = DX + 2,                                                ;
  730. ;              CH = # of characters in commandline,                        ;
  731. ;              SI = # of characters backed up.                             ;
  732. ;  Calls:      backup_Cursor                                               ;
  733. ;  Changes:    SI,                                                         ;
  734. ;              AX, BX, CH (backup_Cursor)                                  ;
  735. ;--------------------------------------------------------------------------;
  736. PROC mov_bol
  737.  
  738.           mov       si, bx
  739.           sub       si, dx
  740.           dec       si
  741.           dec       si                       ; SI = BX - (DX + 2)
  742.           call      backup_Cursor
  743.           ret
  744. ENDP mov_bol
  745.  
  746.  
  747. ;----  mov_eol  -----------------------------------------------------------;
  748. ;  Purpose:    Moves cursor to end of commandline.                         ;
  749. ;  Notes:      none                                                        ;
  750. ;  Entry:      BX = pointer to current position in commandline,            ;
  751. ;              CH = # of bytes to end of line.                             ;
  752. ;  Exit:       BX += CH,                                                   ;
  753. ;              CH = 0,                                                     ;
  754. ;              SI = # of characters advanced.                              ;
  755. ;  Calls:      advance_Cursor                                              ;
  756. ;  Changes:    SI,                                                         ;
  757. ;              AX, BX, CH (advance_Cursor)                                 ;
  758. ;--------------------------------------------------------------------------;
  759. PROC mov_eol
  760.  
  761.           mov       al, ch
  762.           Zero      ah
  763.           mov       si, ax                   ; SI = CH
  764.           call      advance_Cursor
  765.           ret
  766. ENDP mov_eol
  767.  
  768.  
  769. ;----  mov_pcmd  ----------------------------------------------------------;
  770. ;  Purpose:    Replaces current with previous commandline from buffer.     ;
  771. ;  Notes:      none                                                        ;
  772. ;  Entry:      BX = pointer to current position in commandline,            ;
  773. ;              CH = # of bytes to end of line,                             ;
  774. ;              CL = # of bytes left in commandline.                        ;
  775. ;  Entry:      BX = pointer to end of new commandline,                     ;
  776. ;              CH = 0,                                                     ;
  777. ;              CL = # of bytes left in new commandline,                    ;
  778. ;              [CurCmd] adjusted.                                          ;
  779. ;  Calls:      recall_CmdFromBuf, del_line                                 ;
  780. ;  Changes:    CurCmd,                                                     ;
  781. ;              AX, BX, CH, CL, SI, (recall_CmdFromBuf)                     ;
  782. ;--------------------------------------------------------------------------;
  783. PROC mov_pcmd
  784.  
  785.           push      di
  786.  
  787. ; Point DI to 2 bytes before CurCmd. Abort if this lies at or before
  788. ; start of recall buffer.
  789.           mov       di, [cs:CurCmd]
  790.           dec       di                       ; now at possible CR
  791.           dec       di                       ; now at possible cmd's last char
  792.           cmp       di, OFFSET RecallBuf
  793.           jbe       SHORT @@Abort
  794.  
  795. ; Scan backwards to start of buffer or until finding another CR.
  796.           push      cx                       ; CH/CL for recall_CmdFromBuf
  797.           pushf
  798.           mov       al, CR
  799.           mov       cx, di
  800.           sub       cx, OFFSET RecallBuf - 1
  801.           std                                ; scan backwards
  802.           repne     scasb                    ; uses ES:DI
  803.           popf
  804.           pop       cx
  805.           inc       di                       ; should point to CR
  806.           cmp       [BYTE es:di], CR
  807.           jne       SHORT @@Abort
  808.  
  809. ; Point SI to start of command and recall it.
  810.           inc       di
  811.           mov       si, di
  812.           mov       [cs:CurCmd], si
  813.           call      recall_CmdFromBuf
  814.           jmp       SHORT @@Fin
  815.  
  816. ; Nothing to recall, so point CurCmd to start of buffer
  817. ; and delete current line.
  818. @@Abort:
  819.           mov       [cs:CurCmd], OFFSET RecallBuf
  820.           call      del_line
  821.  
  822. @@Fin:
  823.           pop       di
  824.           ret
  825. ENDP mov_pcmd
  826.  
  827.  
  828. ;----  mov_ncmd  ----------------------------------------------------------;
  829. ;  Purpose:    Replaces current with next commandline from buffer.         ;
  830. ;  Notes:      none                                                        ;
  831. ;  Entry:      BX = pointer to current position in commandline,            ;
  832. ;              CH = # of bytes to end of line,                             ;
  833. ;              CL = # of bytes left in commandline.                        ;
  834. ;  Entry:      BX = pointer to end of new commandline,                     ;
  835. ;              CH = 0,                                                     ;
  836. ;              CL = # of bytes left in new commandline,                    ;
  837. ;              [CurCmd] adjusted.                                          ;
  838. ;  Calls:      recall_CmdFromBuf, del_line                                 ;
  839. ;  Changes:    [CurCmd],                                                   ;
  840. ;              AX, BX, CH, CL, SI (recall_CmdFromBuf)                      ;
  841. ;--------------------------------------------------------------------------;
  842. PROC mov_ncmd
  843.  
  844.           push      di
  845.  
  846. ; Point DI to CurCmd. Abort if this lies at or after LastByte.
  847.           mov       di, [cs:CurCmd]
  848.           cmp       di, OFFSET LastByte
  849.           jae       SHORT @@Abort
  850.  
  851. ; Scan forwards to end of buffer or until finding another CR.
  852. ; NB: Scan stops before final CR in the recall buffer since
  853. ; there can never be a command after that; saves having to
  854. ; check DI against OFFSET LastByte.
  855.           push      cx                       ; CH/CL for recall_CmdFromBuf
  856.           mov       al, CR
  857.           mov       cx, OFFSET LastByte      ; *not* OFFSET LastByte + 1
  858.           sub       cx, di
  859.           repne     scasb                    ; uses ES:DI
  860.           pop       cx
  861.           dec       di                       ; should point to CR
  862.           cmp       [BYTE es:di], CR
  863.           jne       SHORT @@Abort
  864.  
  865. ; Point SI to start of command and recall it.
  866.           inc       di                       ; point to 1st char in next cmd
  867.           mov       si, di
  868.           mov       [cs:CurCmd], si
  869.           call      recall_CmdFromBuf
  870.           jmp       SHORT @@Fin
  871.  
  872. ; Nothing to recall, so point CurCmd to just past end
  873. ; of recall buffer and delete current line.
  874. @@Abort:
  875.           mov       [cs:CurCmd], OFFSET LastByte + 1
  876.           call      del_line
  877.  
  878. @@Fin:
  879.           pop       di
  880.           ret
  881. ENDP mov_ncmd
  882.  
  883.  
  884. ;----  del_lchar  ---------------------------------------------------------;
  885. ;  Purpose:    Deletes character to left of cursor.                        ;
  886. ;  Notes:      none                                                        ;
  887. ;  Entry:      BX = pointer to current position in commandline,            ;
  888. ;              CL = # of bytes left in commandline.                        ;
  889. ;  Exit:       BX--,                                                       ;
  890. ;              CL++.                                                       ;
  891. ;  Calls:      mov_lchar, delete_Chars                                     ;
  892. ;  Changes:    BX, (mov_lchar),                                            ;
  893. ;              AX, CH, CL, SI (delete_Chars)                               ;
  894. ;--------------------------------------------------------------------------;
  895. PROC del_lchar
  896.  
  897.           call      mov_lchar                ; sets SI = 0 (at bol) or 1
  898.           call      delete_Chars
  899.           ret
  900. ENDP del_lchar
  901.  
  902.  
  903. ;----  del_rchar  ---------------------------------------------------------;
  904. ;  Purpose:    Deletes character at cursor.                                ;
  905. ;  Notes:      none                                                        ;
  906. ;  Entry:      BX = pointer to current position in commandline,            ;
  907. ;              CH = # of bytes to end of line,                             ;
  908. ;              CL = # of bytes left in commandline.                        ;
  909. ;  Exit:       BX--,                                                       ;
  910. ;              CH--,                                                       ;
  911. ;              CL++.                                                       ;
  912. ;  Calls:      delete_Chars                                                ;
  913. ;  Changes:    AX, CH, CL, SI (delete_Chars)                               ;
  914. ;--------------------------------------------------------------------------;
  915. PROC del_rchar
  916.  
  917.           or        ch, ch
  918.           jz        SHORT @@Fin              ; abort if already at eol
  919.           mov       si, 1
  920.           call      delete_Chars
  921. @@Fin:
  922.           ret
  923. ENDP del_rchar
  924.  
  925.  
  926. ;----  del_lword  ---------------------------------------------------------;
  927. ;  Purpose:    Deletes word to left of cursor.                             ;
  928. ;  Notes:      none                                                        ;
  929. ;  Entry:      BX = pointer to current position in commandline,            ;
  930. ;              CH = # of bytes to end of line,                             ;
  931. ;              CL = # of bytes left in commandline.                        ;
  932. ;  Exit:       BX, CH, and CL adjusted as appropriate.                     ;
  933. ;  Calls:      mov_lword, delete_Chars                                     ;
  934. ;  Changes:    BX, (mov_lword),                                            ;
  935. ;              AX, CH, CL, SI (delete_Chars)                               ;
  936. ;--------------------------------------------------------------------------;
  937. PROC del_lword
  938.  
  939.           call      mov_lword                ; sets SI = 0 (at bol) or > 0
  940.           call      delete_Chars
  941.           ret
  942. ENDP del_lword
  943.  
  944.  
  945. ;----  del_rword  ---------------------------------------------------------;
  946. ;  Purpose:    Deletes word to right of cursor.                            ;
  947. ;  Notes:      none                                                        ;
  948. ;  Entry:      BX = pointer to current position in commandline,            ;
  949. ;              CH = # of bytes to end of line,                             ;
  950. ;              CL = # of bytes left in commandline.                        ;
  951. ;  Exit:       CH, and CL adjusted as appropriate.                         ;
  952. ;  Calls:      find_StartofNextWord, delete_Chars                          ;
  953. ;  Changes:    AX, CH, CL, SI (delete_Chars)                               ;
  954. ;--------------------------------------------------------------------------;
  955. PROC del_rword
  956.  
  957.           call      find_StartofNextWord     ; sets SI = 0 (at eol) or > 0
  958.           call      delete_Chars
  959.           ret
  960. ENDP del_rword
  961.  
  962.  
  963. ;----  del_bol  -----------------------------------------------------------;
  964. ;  Purpose:    Deletes from cursor to start of commandline.                ;
  965. ;  Notes:      none                                                        ;
  966. ;  Entry:      BX = pointer to current position in commandline,            ;
  967. ;              CH = # of bytes to end of line,                             ;
  968. ;              CL = # of bytes left in commandline.                        ;
  969. ;  Exit:       BX = DX + 2,                                                ;
  970. ;              CH = number of characters in commandline,                   ;
  971. ;              CL -= BX - DX - 2.                                          ;
  972. ;  Calls:      mov_bol, delete_Chars                                       ;
  973. ;  Changes:    BX, (mov_bol)                                               ;
  974. ;              AX, CH, CL, SI (delete_Chars)                               ;
  975. ;--------------------------------------------------------------------------;
  976. PROC del_bol
  977.  
  978.           call      mov_bol                  ; sets SI = 0 (at bol) or > 0
  979.           call      delete_Chars
  980.           ret
  981. ENDP del_bol
  982.  
  983.  
  984. ;----  del_eol  -----------------------------------------------------------;
  985. ;  Purpose:    Deletes from cursor to end of commandline.                  ;
  986. ;  Notes:      none                                                        ;
  987. ;  Entry:      BX = pointer to current position in commandline,            ;
  988. ;              CH = # of bytes to end of line,                             ;
  989. ;              CL = # of bytes left in commandline.                        ;
  990. ;  Exit:       BX = DX + 2,                                                ;
  991. ;              CH = 0,                                                     ;
  992. ;              CL -= CH.                                                   ;
  993. ;  Calls:      delete_Chars                                                ;
  994. ;  Changes:    AX, CH, CL, SI (delete_Chars)                               ;
  995. ;--------------------------------------------------------------------------;
  996. PROC del_eol
  997.  
  998.           mov       al, ch
  999.           Zero      ah
  1000.           mov       si, ax
  1001.           call      delete_Chars
  1002.           ret
  1003. ENDP del_eol
  1004.  
  1005.  
  1006. ;----  del_line  ----------------------------------------------------------;
  1007. ;  Purpose:    Deletes entire commandline.                                 ;
  1008. ;  Notes:      none                                                        ;
  1009. ;  Entry:      BX = pointer to current position in commandline,            ;
  1010. ;              CH = # of bytes to end of line,                             ;
  1011. ;              CL = # of bytes left in commandline.                        ;
  1012. ;  Exit:       BX = DX + 2,                                                ;
  1013. ;              CH = 0,                                                     ;
  1014. ;              CL = [DX].                                                  ;
  1015. ;  Calls:      mov_bol, del_eol                                            ;
  1016. ;  Changes:    BX, (mov_bol)                                               ;
  1017. ;              AX, CH, CL, SI (del_eol)                                    ;
  1018. ;--------------------------------------------------------------------------;
  1019. PROC del_line
  1020.  
  1021.           call      mov_bol
  1022.           call      del_eol
  1023.           ret
  1024. ENDP del_line
  1025.  
  1026.  
  1027. ;----  del_buf  -----------------------------------------------------------;
  1028. ;  Purpose:    Deletes all commands in recall buffer.                      ;
  1029. ;  Notes:      Does not affect current commandline.                        ;
  1030. ;              This function is not documented elsewhere.                  ;
  1031. ;  Entry:      n/a                                                         ;
  1032. ;  Exit:       [CurCmd] = OFFSET LastByte + 1.                             ;
  1033. ;  Calls:      init_Buf                                                    ;
  1034. ;  Changes:    AX, [CurCmd] (init_Buf)                                     ;
  1035. ;--------------------------------------------------------------------------;
  1036. PROC del_buf
  1037.  
  1038.           mov       [WORD cs:CurCmd], 0
  1039.           call      init_Buf                 ; nb: changes CurCmd
  1040.           ret
  1041. ENDP del_buf
  1042.  
  1043.  
  1044. ;----  toggle_InsMode  ----------------------------------------------------;
  1045. ;  Purpose:    Toggles flag for insert mode.                               ;
  1046. ;  Notes:      none                                                        ;
  1047. ;  Entry:      n/a                                                         ;
  1048. ;  Exit:       [InsMode] toggled.                                          ;
  1049. ;  Calls:      none                                                        ;
  1050. ;  Changes:    [InsMode]                                                   ;
  1051. ;--------------------------------------------------------------------------;
  1052. PROC toggle_InsMode
  1053.  
  1054.           xor       [cs:InsMode], 1
  1055.           ret
  1056. ENDP toggle_InsMode
  1057.  
  1058.  
  1059. ;----  init_Buf  ----------------------------------------------------------;
  1060. ;  Purpose:    Initializes recall buffer if necessary.                     ;
  1061. ;  Notes:      Clears recall buffer if CurCmd is zero. Normally, CurCmd    ;
  1062. ;                   will take on values OFFSET RecallBuf and LastByte,     ;
  1063. ;                   or less. Zero should not otherwise occur.              ;
  1064. ;              This is needed when scanning for previous commands -        ;
  1065. ;                   spurious CRs should not be encountered.                ;
  1066. ;  Entry:      [CurCmd] = pointer to current command in recall buffer.     ;
  1067. ;  Exit:       [CurCmd] = OFFSET LastByte + 1 if buffer is initialized.    ;
  1068. ;  Calls:      none                                                        ;
  1069. ;  Changes:    AX, [CurCmd] possibly                                       ;
  1070. ;--------------------------------------------------------------------------;
  1071. PROC init_Buf
  1072.  
  1073. ; Abort if [CurCmd] is non-zero.
  1074.           cmp       [WORD cs:CurCmd], 0
  1075.           jne       SHORT @@Fin
  1076.  
  1077. ; Initialize buffer by zeroing out all but last byte. There put a CR
  1078. ; so when searching backwards for commands I'll find at least one.
  1079.           push      cx di
  1080.           Zero      al                       ; fill with zeros
  1081.           mov       cx, BUFSIZE - 1
  1082.           mov       di, OFFSET RecallBuf
  1083.           rep       stosb                    ; uses ES:DI
  1084.           mov       [BYTE es:di], CR         ; buffer ends with CR
  1085.           pop       di cx
  1086.  
  1087. ; Point current command to past end of recall buffer. This is so both
  1088. ; mov_pcmd and mov_ncmd will not find any commands yet still function
  1089. ; without error (which would happen if [CurCmd] were left at 0).
  1090.           mov       [cs:CurCmd], OFFSET LastByte + 1
  1091.  
  1092. @@Fin:
  1093.           ret
  1094. ENDP init_Buf
  1095.  
  1096.  
  1097. ;----  get_CmdLine  -------------------------------------------------------;
  1098. ;  Purpose:    Reads a commandline from user.                              ;
  1099. ;  Notes:      The caller's buffer is used as a scratch area to keep       ;
  1100. ;                   memory requirements to a minimum.                      ;
  1101. ;  Entry:      DS:DX = buffer for storing commandline,                     ;
  1102. ;              [BYTE DS:DX] = maximum number of bytes to read.             ;
  1103. ;  Exit:       [BYTE DS:DX+1] = number of bytes actually read,             ;
  1104. ;              [BYTE DS:DX+2] = 1st byte read from user.                   ;
  1105. ;  Calls:      get_KeyNoEcho, add_CharToLine, [CmdTbl]                     ;
  1106. ;  Changes:    AX, BX, CX, BP, [InsMode], [PrevCmd], [NextCmd]             ;
  1107. ;--------------------------------------------------------------------------;
  1108. PROC get_CmdLine
  1109.  
  1110.           mov       bx, dx                   ; BX used for indexed addressing
  1111.           Zero      ch                       ; bytes to end of line
  1112.           mov       cl, [bx]                 ; space left in buffer
  1113.           dec       cl                       ;    less 1 for final CR
  1114.           inc       bx                       ; pointer to first spot in buffer
  1115.           inc       bx
  1116.  
  1117. ; Get key and determine if it's an editing key or a regular character.
  1118. @@NewKey:
  1119.           call      get_KeyNoEcho            ; get key from user
  1120.           cmp       al, CR                   ; is user done yet?
  1121.           jz        SHORT @@Fin
  1122.           cmp       al, BS                   ; BS is an editing key
  1123.           je        SHORT @@EditKey
  1124.           cmp       al, ESCAPE               ; ESCAPE is another
  1125.           je        SHORT @@EditKey
  1126.           or        al, al                   ; was key zero?
  1127.           jnz       SHORT @@RegularChar      ;   no, then it's a regular char
  1128.           call      get_KeyNoEcho            ;   yes, get extended scan code
  1129.  
  1130. ; Process extended key as an editing key. Invalid keys are not added
  1131. ; to the commandline buffer; instead, they merely result in a bell.
  1132. @@EditKey:
  1133.           mov       bp, OFFSET CmdTbl        ; point to table of editing cmds
  1134.  
  1135. @@NewCmd:
  1136.           cmp       [(CMD PTR cs:bp).Key], al
  1137.           je        SHORT @@ProcessCmd
  1138.           add       bp, SIZE CmdTbl
  1139.           cmp       [(CMD PTR cs:bp).Key], 0 ; zero marks end of table
  1140.           jne       SHORT @@NewCmd           ;   and must point to ring_Bell
  1141.                                              ;   so execution drops thru!!!
  1142. @@ProcessCmd:
  1143.           call      [(CMD PTR cs:bp).Function]
  1144.           jmp       SHORT @@NewKey
  1145.  
  1146. ; It's an ordinary character so add it to the commandline.
  1147. @@RegularChar:
  1148.           call      add_CharToLine
  1149.           jmp       SHORT @@NewKey
  1150.  
  1151. ; Now determine number of bytes in buffer, put that count in [DX+1], 
  1152. ; and terminate buffer with a CR. NB: count excludes final CR.
  1153. @@Fin:
  1154.           mov       bx, dx                   ; point back to start of buffer
  1155.           mov       al, [bx]                 ; compute count
  1156.           sub       al, cl
  1157.           dec       al                       ; exclude final CR
  1158.           Zero      ah
  1159.           mov       [bx+1], al               ; [DS:DX+1] = count
  1160.           add       bx, ax
  1161.           mov       [BYTE bx+2], CR          ; place CR at end of buffer
  1162.           ret
  1163. ENDP get_CmdLine
  1164.  
  1165.  
  1166. ;----  store_CmdInBuf  ----------------------------------------------------;
  1167. ;  Purpose:    Stores the commandline at the end of the recall buffer.     ;
  1168. ;  Notes:      Commandlines consisting of 1 character (CR) are not saved.  ;
  1169. ;  Entry:      DS:DX = pointer to start of commandline,                    ;
  1170. ;              [BYTE DS:DX+1] = maximum number of bytes to read.           ;
  1171. ;  Exit:       [CurCmd] = LastByte + 1.                                    ;
  1172. ;  Calls:      none                                                        ;
  1173. ;  Changes:    AX, BX, CX, DI, SI, [CurCmd]                                ;
  1174. ;--------------------------------------------------------------------------;
  1175. PROC store_CmdInBuf
  1176.  
  1177. ; Check length of commandline.
  1178.           mov       bx, dx
  1179.           mov       cl, [bx+1]
  1180.           or        cl, cl                   ; CL does not include final CR
  1181.           jz        SHORT @@Fin              ; so if = 0, nothing's there
  1182.           inc       cl                       ; else set CX = # bytes in cmd
  1183.           Zero      ch                       ;   *including* final CR
  1184.  
  1185. ; Make room in recall buffer for commandline by shifting everything
  1186. ; back by [DS:DX+1] characters. 
  1187.           push      cx ds                    ; need both to copy to recall buf
  1188.           mov       ax, cs
  1189.           mov       ds, ax
  1190.           mov       di, OFFSET RecallBuf     ; to start of buffer
  1191.           mov       si, di
  1192.           add       si, cx                   ; from start + CX
  1193.           neg       cx
  1194.           add       cx, BUFSIZE              ; for BUFSIZE - [BYTE BX+1]
  1195.           rep       movsb                    ; move them
  1196.           pop       ds cx
  1197.  
  1198. ; Add commandline in empty space at end of recall buffer. By this
  1199. ; point DI will point to space for current commandline.
  1200.           mov       si, bx
  1201.           inc       si
  1202.           inc       si
  1203.           rep       movsb                    ; from DS:SI to ES:DI
  1204.           mov       [cs:CurCmd], OFFSET LastByte + 1
  1205.  
  1206. @@Fin:
  1207.           ret
  1208. ENDP store_CmdInBuf
  1209.  
  1210.  
  1211. ;----  handle_Int21  ------------------------------------------------------;
  1212. ;  Purpose:    Passes calls to input strings along to my own handler.      ;
  1213. ;  Notes:      none                                                        ;
  1214. ;  Entry:      AH = subfunction to perform                                 ;
  1215. ;  Exit:       If AH = 10, DS:DX points to buffer read from user.          ;
  1216. ;  Calls:      init_Buf, get_CmdLine, store_CmdInBuf                       ;
  1217. ;  Changes:    n/a                                                         ;
  1218. ;--------------------------------------------------------------------------;
  1219. PROC handle_Int21   FAR
  1220.  
  1221. ; If the call is for buffered input, then use my handler;
  1222. ; otherwise, pass it along to the old handler.
  1223.           cmp       ah, 10
  1224.           jz        SHORT @@SwitchStack
  1225.           jmp       [cs:OldInt21]            ; old vector issues IRET
  1226.  
  1227. ; Switch over to my own stack and save callers registers.
  1228. @@SwitchStack:
  1229.           mov       [cs:OldAX], ax           ; can't push it on my stack yet
  1230.           cli                                ; critical part - disallow INTs
  1231.           mov       [WORD cs:OldStack], sp
  1232.           mov       [WORD cs:OldStack+2], ss
  1233.           mov       ax, cs
  1234.           mov       ss, ax
  1235.           mov       sp, OFFSET StackTop
  1236.           sti                                ; ok, out of critical section
  1237.           push      bx cx dx di si bp ds es
  1238.           pushf
  1239.  
  1240. ; Meat of my interrupt handler.
  1241.           mov       es, ax                   ; set ES = CX
  1242.           cld
  1243.           call      init_Buf
  1244.           call      get_CmdLine
  1245.           call      store_CmdInBuf
  1246.  
  1247. ; Restore caller's registers.
  1248.           popf
  1249.           pop       es ds bp si di dx cx bx
  1250.           cli
  1251.           mov       ss, [WORD cs:OldStack+2]
  1252.           mov       sp, [WORD cs:OldStack]
  1253.           sti
  1254.           mov       ax, [cs:OldAX]
  1255.  
  1256.           iret                               ; return to caller
  1257. ENDP handle_Int21
  1258.  
  1259.  
  1260. %NEWPAGE
  1261. ;--------------------------------------------------------------------------;
  1262. ;                        R E C A L L   B U F F E R                         ;
  1263. ;--------------------------------------------------------------------------;
  1264. RecallBuf =         $                        ; will overlay transient portion
  1265. LastByte  =         RecallBuf + BUFSIZE - 1  ; room for BUFSIZE characters
  1266.                                              ; and end of resident portion
  1267.  
  1268.  
  1269. %NEWPAGE
  1270. ;--------------------------------------------------------------------------;
  1271. ;                       T R A N S I E N T   D A T A                        ;
  1272. ;--------------------------------------------------------------------------;
  1273. ProgName  DB        'recall: '
  1274.           DB        EOS
  1275. EOL       DB        '.', CR, LF
  1276.           DB        EOS
  1277. HelpMsg   DB        CR, LF
  1278.           DB        'TifaWARE RECALL, v', VERSION, ', ', ??Date
  1279.           DB        ' - commandline editor and history TSR.', CR, LF
  1280.           DB        'Usage: recall [-options]', CR, LF, LF
  1281.           DB        'Options:', CR, LF
  1282.           DB        '  -i = install in memory', CR, LF
  1283.           DB        '  -l = list commandlines in recall buffer', CR, LF
  1284.           DB        '  -r = remove from memory', CR, LF
  1285.           DB        '  -? = display this help message', CR, LF, LF
  1286.           DB        'Only one option can be specified at a time.', CR, LF
  1287.           DB        EOS
  1288. ErrMsgOpt DB        'illegal option -- '
  1289. OptCh     DB        ?                        ; room for offending character
  1290.           DB        EOS
  1291. ErrMsgRes DB        'already resident'
  1292.           DB        EOS
  1293. ErrMsgMem DB        'memory control blocks corrupt'
  1294.           DB        EOS
  1295. ErrMsgNYI DB        'not yet installed'
  1296.           DB        EOS
  1297. RemoveMsg DB        'successfully removed'
  1298.           DB        EOS
  1299.  
  1300. SwitCh    DB        '-'                      ; char introducing options
  1301. HFlag     DB        0                        ; flag for on-line help
  1302. IFlag     DB        0                        ; flag for installing TSR
  1303. LFlag     DB        0                        ; flag for listing commandlines
  1304. RFlag     DB        0                        ; flag for removing TSR
  1305.  
  1306.  
  1307. %NEWPAGE
  1308. ;--------------------------------------------------------------------------;
  1309. ;                       T R A N S I E N T   C O D E                        ;
  1310. ;--------------------------------------------------------------------------;
  1311. ;----  check_ifInstalled  -------------------------------------------------;
  1312. ;  Purpose:    Checks if TSR has already been installed.                   ;
  1313. ;  Notes:      Works by looking for TSR_Sig in segment owned by current    ;
  1314. ;                   interrupt handler.                                     ;
  1315. ;  Entry:      AL = interrupt to be tested.                                ;
  1316. ;  Exit:       zf = 1 if installed; 0 otherwise.                           ;
  1317. ;  Calls:      getvect, strcmp                                             ;
  1318. ;  Changes:    flags                                                       ;
  1319. ;--------------------------------------------------------------------------;
  1320. PROC check_ifInstalled
  1321.  
  1322.           push      bx di si ds es
  1323.           call      getvect                  ; address of handler now in ES:BX
  1324.           mov       si, OFFSET TSR_Sig       ; offset in DS to signature
  1325.           mov       di, si                   ; should be at same offset in ES
  1326.           call      strcmp                   ; do they match?
  1327.           pop       es ds si di bx
  1328.           ret
  1329. ENDP check_ifInstalled
  1330.  
  1331.  
  1332. ;----  install_TSR  -------------------------------------------------------;
  1333. ;  Purpose:    Installs TSR in memory after hooking into the interrupt     ;
  1334. ;                   specified by AL.                                       ;
  1335. ;  Notes:      Will not load if already present in memory.                 ;
  1336. ;              Does *NOT* return if installation is successful.            ;
  1337. ;              Labels "SegStart" and "LastByte" must be defined for start  ;
  1338. ;                   and end of resident stuff respectively.                ;
  1339. ;              "Entry" must be defined as the entry point to the ISR.      ;
  1340. ;  Entry:      AL = interrupt vector to hook,                              ;
  1341. ;              DS:DX = doubleword (DD) storage for old handler.            ;
  1342. ;  Exit:       AL = ERRINS if installation failed.                         ;
  1343. ;  Calls:      check_ifInstalled, getvect, setvect, errmsg                 ;
  1344. ;  Changes:    AL, DX, flags                                               ;
  1345. ;--------------------------------------------------------------------------;
  1346. PROC install_TSR
  1347.  
  1348.           call      check_ifInstalled
  1349.           jnz       SHORT @@FreeMemory       ; proceed if not yet installed
  1350.           mov       dx, OFFSET ErrMsgRes     ;    else point to error message
  1351.           jmp       SHORT @@Fin              ;    and abort
  1352.  
  1353. ; Free memory used by environment. NB: DOS will not make
  1354. ; use of this memory block until the TSR is removed.
  1355. @@FreeMemory:
  1356.           push      ax es
  1357.           mov       es, [EnvSeg]             ; point to the block
  1358.           mov       ah, 49h                  ; and let DOS free it
  1359.           int       DOS
  1360.           pop       es ax
  1361.           jnc       SHORT @@GrabVector       ; proceed if no error occured
  1362.           mov       dx, OFFSET ErrMsgMem     ;    else point to error message
  1363.           jmp       SHORT @@Fin              ;    and abort
  1364.  
  1365. ; NB: This is the point of no return - if the execution reaches this 
  1366. ; point it will result in the program going resident such that the
  1367. ; procedure does *NOT* return.
  1368. ;
  1369. ; Now save old interrupt handler. NB: This must be in the *resident*
  1370. ; data area since: (1) calls to int 21h not for buffered input will be
  1371. ; passed along, and (2) it's needed to successfully uninstall the TSR.
  1372. @@GrabVector:
  1373.           push      es                       ; original ES needed in fputs()
  1374.           call      getvect                  ; returns handler addr in ES:BX
  1375.           xchg      dx, bx                   ; BX needed for index addressing
  1376.           mov       [bx], dx                 ; save offset followed by segment
  1377.           mov       [bx+2], es
  1378.           pop       es
  1379.  
  1380. ; Now set it to my own handler.
  1381.           mov       dx, ENTRY
  1382.           call      setvect
  1383.  
  1384. ; Let user know code's been installed and then go resident.
  1385.           mov       bx, 1                    ; report goes to STDOUT
  1386.           mov       dx, OFFSET TSR_Sig
  1387.           call      fputs
  1388. ; ****************************************************************************
  1389. ; NB: TASM's IDEAL mode treats arguments of the OFFSET operator in a peculiar 
  1390. ; fashion, as can be seen by browsing the lexical grammer in Appendix A of
  1391. ; the _Reference Guide_. If MASM mode were used, the expression below would
  1392. ; be written "(OFFSET LastByte - OFFSET SegStart + 16) SHR 5". However, in
  1393. ; IDEAL mode not only would "OFFSET SegStart + 16" be parsed as "OFFSET
  1394. ; (SegStart + 16)" but also the result would be viewed as a relative quantity.
  1395. ; As it is, TASM replaces labels below with their respective address values 
  1396. ; thereby computing the "correct" amount of memory to save.
  1397. ; ****************************************************************************
  1398. ; NB: While Angermayer and Jaeger in their book say 15 should be used
  1399. ; below, I've found 16 is necessary to handle cases in which LastByte
  1400. ; lies at the start of a paragraph. So what if I'm wasting an entire
  1401. ; paragraph!
  1402. ; ****************************************************************************
  1403.           mov       dx, (LastByte - SegStart + 16) SHR 4
  1404.           mov       ax, 3100h                ; terminate/stay resident, rc = 0
  1405.           int       DOS                      ; via DOS
  1406.  
  1407. ; This point is only reached on error.
  1408. @@Fin:
  1409.           call      errmsg
  1410.           mov       al, ERRINS
  1411.           ret
  1412. ENDP install_TSR
  1413.  
  1414.  
  1415. ;----  uninstall_TSR  -----------------------------------------------------;
  1416. ;  Purpose:    Uninstalls TSR.                                             ;
  1417. ;  Notes:      none                                                        ;
  1418. ;  Entry:      AL = interrupt vector to restore,                           ;
  1419. ;              DX = offset to doubleword (DD) storage for old handler as   ;
  1420. ;                   saved by install_TSR.                                  ;
  1421. ;  Exit:       AL = ERRNYI if uninstallation failed.                       ;
  1422. ;  Calls:      check_ifInstalled, errmsg, getvect, setvect                 ;
  1423. ;  Changes:    AX (AH too), BX, DX, flags                                  ;
  1424. ;--------------------------------------------------------------------------;
  1425. PROC uninstall_TSR
  1426.  
  1427.           call      check_ifInstalled
  1428.           jz        SHORT @@FindTSRSeg       ; proceed if installed
  1429.           mov       al, ERRNYI               ;   else flag error
  1430.           mov       dx, OFFSET ErrMsgNYI     ;   and point to error message
  1431.           jmp       SHORT @@Fin
  1432.  
  1433. ; Locate segment holding TSR.
  1434. @@FindTSRSeg:
  1435.           push      es                       ; needed by errmsg() below
  1436.           call      getvect                  ; returns in ES:BX
  1437.  
  1438. ; Restore previous interrupt handler stored in *RESIDENT* data segment.
  1439.           push      ds                       ; needed by errmsg() below
  1440.           mov       bx, dx                   ; ES:BX points to storage area
  1441.           mov       dx, [es:bx]
  1442.           mov       ds, [es:bx+2]
  1443.           call      setvect                  ; DS:DX = address of old handler
  1444.           pop       ds
  1445.  
  1446. ; Release memory occupied by TSR. NB: ES already holds segment 
  1447. ; address of resident data and code based on calling getvect().
  1448.           mov       ah, 49h
  1449.           int       DOS
  1450.           pop       es
  1451.           jnc       SHORT @@FlagNoError      ; continue if no error
  1452.           mov       al, ERRNYI               ;   else flag error
  1453.           mov       dx, OFFSET ErrMsgMem     ;   and point to error message
  1454.           jmp       SHORT @@Fin
  1455.  
  1456. @@FlagNoError:
  1457.           Zero      al                       ; indicate no error
  1458.           mov       dx, OFFSET RemoveMsg
  1459.  
  1460. @@Fin:
  1461.           call      errmsg                   ; display message
  1462.           ret
  1463. ENDP uninstall_TSR
  1464.  
  1465.  
  1466. ;----  list_CmdLines  -----------------------------------------------------;
  1467. ;  Purpose:    Lists commandlines in recall buffer.                        ;
  1468. ;  Notes:      none                                                        ;
  1469. ;  Entry:      AL = interrupt vector in use.                               ;
  1470. ;  Exit:       AL = 0 if successful; ERRNYI otherwise.                     ;
  1471. ;  Calls:      check_ifInstalled, getvect, errmsg                          ;
  1472. ;  Changes:    AL, BX, CX, DX, DI                                          ;
  1473. ;--------------------------------------------------------------------------;
  1474. PROC list_CmdLines
  1475.  
  1476.           call      check_ifInstalled
  1477.           jnz       SHORT @@Abort            ; not installed
  1478.  
  1479. ; Locate segment controlled by resident code. Note I already
  1480. ; know my ISR has been installed.
  1481.           push      ds es
  1482.           call      getvect                  ; returns ES:BX
  1483.           mov       bx, es
  1484.           mov       ds, bx
  1485.  
  1486. ; Point to start of 1st complete command in recall buffer. NB:
  1487. ; There will always be at least one command - "recall -l".
  1488.           mov       al, CR
  1489.           mov       cx, BUFSIZE
  1490.           mov       di, OFFSET RecallBuf
  1491.           repne     scasb                    ; uses ES:DI
  1492.  
  1493. ; Display rest of buffer.
  1494.           mov       ah, 2                    ; DOS subfunction to display char
  1495. @@NextChar:
  1496.           mov       dl, [di]                 ; get char
  1497.           inc       di
  1498.           int       DOS                      ; display it
  1499.           cmp       dl, CR                   ; need to display CR/LF?
  1500.           loopne    SHORT @@NextChar         ; always decrements CX 
  1501.           mov       dl, LF                   ; display LF now
  1502.           int       DOS
  1503.           or        cx, cx                   ; done yet?
  1504.           jnz       SHORT @@NextChar
  1505.  
  1506.           pop       es ds
  1507.           Zero      al                       ; flag no error
  1508.           jmp       SHORT @@Fin
  1509.  
  1510. @@Abort:
  1511.           mov       dx, OFFSET ErrMsgNYI
  1512.           call      errmsg
  1513.           mov       al, ERRNYI
  1514.  
  1515. @@Fin:
  1516.           ret
  1517. ENDP list_CmdLines
  1518.  
  1519.  
  1520. ;----  skip_Spaces  -------------------------------------------------------;
  1521. ;  Purpose:    Skips past spaces in a string.                              ;
  1522. ;  Notes:      Scanning stops with either a non-space *OR* CX = 0.         ;
  1523. ;  Entry:      DS:SI = start of string to scan.                            ;
  1524. ;  Exit:       AL = next non-space character,                              ;
  1525. ;              CX is adjusted as necessary,                                ;
  1526. ;              DS:SI = pointer to next non-space.                          ;
  1527. ;  Calls:      none                                                        ;
  1528. ;  Changes:    AL, CX, SI                                                  ;
  1529. ;--------------------------------------------------------------------------;
  1530. PROC skip_Spaces
  1531.  
  1532.           jcxz      SHORT @@Fin
  1533. @@NextCh:
  1534.           lodsb
  1535.           cmp       al, ' '
  1536.           loopz     @@NextCh
  1537.           jz        SHORT @@Fin              ; CX = 0; don't adjust
  1538.  
  1539.           inc       cx                       ; adjust counters if cx > 0
  1540.           dec       si
  1541.  
  1542. @@Fin:
  1543.           ret
  1544. ENDP skip_Spaces
  1545.  
  1546.  
  1547. ;----  get_Opt  -----------------------------------------------------------;
  1548. ;  Purpose:    Get a commandline option.                                   ;
  1549. ;  Notes:      none                                                        ;
  1550. ;  Entry:      AL = option character,                                      ;
  1551. ;  Exit:       n/a                                                         ;
  1552. ;  Calls:      tolower, errmsg                                             ;
  1553. ;  Changes:    AX, DX, [OptCh], [HFlag], [IFlag], [LFlag], [RFlag]         ;
  1554. ;--------------------------------------------------------------------------;
  1555. PROC get_Opt
  1556.  
  1557.           mov       [OptCh], al              ; save for later
  1558.           call      tolower                  ; use only lowercase in cmp.
  1559.           cmp       al, 'i'
  1560.           jz        SHORT @@OptI
  1561.           cmp       al, 'l'
  1562.           jz        SHORT @@OptL
  1563.           cmp       al, 'r'
  1564.           jz        SHORT @@OptR
  1565.           cmp       al, '?'
  1566.           jz        SHORT @@OptH
  1567.           mov       dx, OFFSET ErrMsgOpt     ; unrecognized option
  1568.           call      errmsg                   ; then *** DROP THRU *** to OptH
  1569.  
  1570. ; Various possible options.
  1571. @@OptH:
  1572.           mov       [HFlag], ON              ; set help flag
  1573.           jmp       SHORT @@Fin
  1574.  
  1575. @@OptI:
  1576.           mov       [IFlag], ON              ; install in memory
  1577.           jmp       SHORT @@Fin
  1578.  
  1579. @@OptL:
  1580.           mov       [LFlag], ON              ; list cmds in recall buffer
  1581.           jmp       SHORT @@Fin
  1582.  
  1583. @@OptR:
  1584.           mov       [RFlag], ON              ; remove from memory
  1585.  
  1586. @@Fin:
  1587.           ret
  1588. ENDP get_Opt
  1589.  
  1590.  
  1591. ;----  process_CmdLine  ---------------------------------------------------;
  1592. ;  Purpose:    Processes commandline arguments.                            ;
  1593. ;  Notes:      A switch character by itself is ignored.                    ;
  1594. ;  Entry:      n/a                                                         ;
  1595. ;  Exit:       n/a                                                         ;
  1596. ;  Calls:      skip_Spaces, get_Opt                                        ;
  1597. ;  Changes:    AX, CX, SI,                                                 ;
  1598. ;              DX, [OptCh], [HFlag], [IFlag], [LFlag], [RFlag] (get_Opt)   ;
  1599. ;              Direction flag is cleared.                                  ;
  1600. ;--------------------------------------------------------------------------;
  1601. PROC process_CmdLine
  1602.  
  1603.           cld                                ; forward, march!
  1604.           Zero      ch, ch
  1605.           mov       cl, [CmdLen]             ; length of commandline
  1606.           mov       si, OFFSET CmdLine       ; offset to start of commandline
  1607.  
  1608.           call      skip_Spaces              ; check if any args supplied
  1609.           or        cl, cl
  1610.           jnz       SHORT @@ArgLoop
  1611.  
  1612.           mov       [HFlag], ON              ; assume user needs help
  1613.           jmp       SHORT @@Fin
  1614.  
  1615. ; For each blank-delineated argument on the commandline...
  1616. @@ArgLoop:
  1617.           lodsb                              ; next character
  1618.           dec       cl
  1619.           cmp       al, [SwitCh]             ; is it the switch character?
  1620.           jnz       SHORT @@NonOpt           ;   no
  1621.  
  1622. ; Isolate each option and process it. Stop when a space is reached.
  1623. @@OptLoop:
  1624.           jcxz      SHORT @@Fin              ; abort if nothing left
  1625.           lodsb
  1626.           dec       cl
  1627.           cmp       al, ' '
  1628.           jz        SHORT @@NextArg          ; abort when space reached
  1629.           call      get_Opt
  1630.           jmp       @@OptLoop
  1631.  
  1632. ; Any argument which is *not* an option is invalid. Set help flag and abort.
  1633. @@NonOpt:
  1634.           mov       [HFlag], ON
  1635.           jmp       SHORT @@Fin
  1636.  
  1637. ; Skip over spaces until next argument is reached.
  1638. @@NextArg:
  1639.           call      skip_Spaces
  1640.           or        cl, cl
  1641.           jnz       @@ArgLoop
  1642.  
  1643. @@Fin:
  1644.           ret
  1645. ENDP process_CmdLine
  1646.  
  1647.  
  1648. ;----  main  --------------------------------------------------------------;
  1649. ;  Purpose:    Main section of program.                                    ;
  1650. ;  Notes:      none                                                        ;
  1651. ;  Entry:      Arguments as desired                                        ;
  1652. ;  Exit:       Return code as follows:                                     ;
  1653. ;                   0 => program ran successfully,                         ;
  1654. ;                   ERRH => on-line help supplied,                         ;
  1655. ;                   ERRINS => program could not be installed,              ;
  1656. ;                   ERRNYI => program was not yet installed.               ;
  1657. ;  Calls:      process_CmdLine, fputs, list_CmdLines, install_TSR,         ;
  1658. ;                   uninstall_TSR                                          ;
  1659. ;  Changes:    n/a                                                         ;
  1660. ;--------------------------------------------------------------------------;
  1661. main:
  1662.  
  1663. ; Process commandline arguments. 
  1664.           call      process_CmdLine          ; process commandline args
  1665.           mov       al, 21h                  ; vector to hook
  1666.           mov       dx, OFFSET OldInt21      ; storage for old intr handler
  1667.           cmp       [IFlag], ON              ; install it?
  1668.           je        SHORT @@Install
  1669.           cmp       [LFlag], ON              ; list commands?
  1670.           je        SHORT @@List
  1671.           cmp       [RFlag], ON              ; remove it?
  1672.           je        SHORT @@Remove
  1673.           mov       bx, STDERR               ; user must need help
  1674.           mov       dx, OFFSET HelpMsg
  1675.           call      fputs
  1676.           mov       al, ERRH
  1677.           jmp       SHORT @@Fin
  1678.  
  1679. @@List:
  1680.           call      list_CmdLines
  1681.           jmp       SHORT @@Fin
  1682.  
  1683. @@Install:
  1684.           call      install_TSR
  1685.           jmp       SHORT @@Fin
  1686.  
  1687. @@Remove:
  1688.           call      uninstall_TSR
  1689.  
  1690. ; Terminate the program using as return code what's in AL.
  1691. @@Fin:
  1692.           mov       ah, 4ch
  1693.           int       DOS
  1694. EVEN
  1695. ;-------------------------------------------------------------------------;
  1696. ;  Purpose:    Writes an ASCIIZ string to specified device.
  1697. ;  Notes:      A zero-length string doesn't seem to cause problems when
  1698. ;                 this output function is used.
  1699. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  1700. ;  Entry:      BX = device handle,
  1701. ;              DS:DX = pointer to string.
  1702. ;  Exit:       Carry flag set if EOS wasn't found or handle is invalid.
  1703. ;  Calls:      strlen
  1704. ;  Changes:    none
  1705. ;-------------------------------------------------------------------------;
  1706. PROC fputs
  1707.  
  1708. IF @DataSize EQ 0
  1709.    Push_M   <ax, cx, di>
  1710. ELSE
  1711.    Push_M   <ax, cx, di, es>
  1712.    mov      ax, ds
  1713.    mov      es, ax
  1714. ENDIF
  1715.    mov      di, dx
  1716.    call     strlen                        ; set CX = length of string
  1717.    jc       SHORT @@Fin                   ; abort if problem finding end
  1718.    mov      ah, 40h                       ; MS-DOS raw output function
  1719.    int      DOS
  1720. @@Fin:
  1721. IF @DataSize EQ 0
  1722.    Pop_M    <di, cx, ax>
  1723. ELSE
  1724.    Pop_M    <es, di, cx, ax>
  1725. ENDIF
  1726.    ret
  1727.  
  1728. ENDP fputs
  1729.  
  1730.  
  1731. EVEN
  1732. ;-------------------------------------------------------------------------;
  1733. ;  Purpose:    Writes an error message to stderr.
  1734. ;  Notes:      none
  1735. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  1736. ;  Entry:      DS:DX = pointer to error message.
  1737. ;  Exit:       n/a
  1738. ;  Calls:      fputs
  1739. ;  Changes:    none
  1740. ;-------------------------------------------------------------------------;
  1741. PROC errmsg
  1742.  
  1743.    Push_M   <bx, dx>
  1744.    push     dx                            ; save again calling parameters
  1745.    mov      bx, STDERR
  1746.    mov      dx, OFFSET ProgName           ; display program name
  1747.    call     fputs
  1748.    pop      dx                            ; recover calling parameters
  1749.    call     fputs                         ; display error message
  1750.    mov      dx, OFFSET EOL
  1751.    call     fputs
  1752.    Pop_M    <dx, bx>
  1753.    ret
  1754.  
  1755. ENDP errmsg
  1756.  
  1757.  
  1758. EVEN
  1759. ;--------------------------------------------------------------------------;
  1760. ;  Purpose:    Gets address of an interrupt handler.
  1761. ;  Notes:      none
  1762. ;  Requires:   8086-class CPU and DOS v2.0 or better.
  1763. ;  Entry:      AL = interrupt of interest.
  1764. ;  Exit:       ES:BX = address of current interrupt handler
  1765. ;  Calls:      none
  1766. ;  Changes:    ES:BX
  1767. ;--------------------------------------------------------------------------;
  1768. PROC getvect
  1769.  
  1770.    push     ax
  1771.    mov      ah, 35h                       ; find address of handler
  1772.    int      DOS                           ; returned in ES:BX
  1773.    pop      ax
  1774.    ret
  1775. ENDP getvect
  1776.  
  1777.  
  1778. ;--------------------------------------------------------------------------;
  1779. ;  Purpose:    Sets an interrupt vector.
  1780. ;  Notes:      none
  1781. ;  Requires:   8086-class CPU and DOS v1.0 or better.
  1782. ;  Entry:      AL = interrupt of interest,
  1783. ;              DS:DX = address of new interrupt handler
  1784. ;  Exit:       n/a
  1785. ;  Calls:      none
  1786. ;  Changes:    none
  1787. ;--------------------------------------------------------------------------;
  1788. PROC setvect
  1789.  
  1790.    push     ax
  1791.    mov      ah, 25h                       ; set address of handler
  1792.    int      DOS
  1793.    pop      ax
  1794.    ret
  1795. ENDP setvect
  1796.  
  1797.  
  1798. EVEN
  1799. ;-------------------------------------------------------------------------;
  1800. ;  Purpose:    Converts character to lowercase.
  1801. ;  Notes:      none
  1802. ;  Requires:   8086-class CPU.
  1803. ;  Entry:      AL = character to be converted.
  1804. ;  Exit:       AL = converted character.
  1805. ;  Calls:      none
  1806. ;  Changes:    AL
  1807. ;              flags
  1808. ;-------------------------------------------------------------------------;
  1809. PROC tolower
  1810.  
  1811.    cmp      al, 'A'                       ; if < 'A' then done
  1812.    jb       SHORT @@Fin
  1813.    cmp      al, 'Z'                       ; if > 'Z' then done
  1814.    ja       SHORT @@Fin
  1815.    or       al, 20h                       ; make it lowercase
  1816. @@Fin:
  1817.    ret
  1818.  
  1819. ENDP tolower
  1820.  
  1821.  
  1822. EVEN
  1823. ;-------------------------------------------------------------------------;
  1824. ;  Purpose:    Calculates length of an ASCIIZ string.
  1825. ;  Notes:      Terminal char is _not_ included in the count.
  1826. ;  Requires:   8086-class CPU.
  1827. ;  Entry:      ES:DI = pointer to string.
  1828. ;  Exit:       CX = length of string,
  1829. ;              cf = 0 and zf = 1 if EOS found,
  1830. ;              cf = 1 and zf = 0 if EOS not found within segment.
  1831. ;  Calls:      none
  1832. ;  Changes:    CX,
  1833. ;              flags
  1834. ;-------------------------------------------------------------------------;
  1835. PROC strlen
  1836.  
  1837.    Push_M   <ax, di, flags>
  1838.    cld                                    ; scan forward only
  1839.    mov      al, EOS                       ; character to search for
  1840.    mov      cx, di                        ; where are we now
  1841.    not      cx                            ; what's left in segment - 1
  1842.    push     cx                            ; save char count
  1843.    repne    scasb
  1844.    je       SHORT @@Done
  1845.    scasb                                  ; test final char
  1846.    dec      cx                            ; avoids trouble with "not" below
  1847.  
  1848. @@Done:
  1849.    pop      ax                            ; get original count
  1850.    sub      cx, ax                        ; subtract current count
  1851.    not      cx                            ; and invert it
  1852.    popf                                   ; restore df
  1853.    dec      di
  1854.    cmp      [BYTE PTR es:di], EOS
  1855.    je       SHORT @@Fin                   ; cf = 0 if equal
  1856.    stc                                    ; set cf => error
  1857.  
  1858. @@Fin:
  1859.    Pop_M    <di, ax>
  1860.    ret
  1861.  
  1862. ENDP strlen
  1863.  
  1864.  
  1865. EVEN
  1866. ;--------------------------------------------------------------------------;
  1867. ;  Purpose:    Compares two ASCIIZ strings.
  1868. ;  Notes:      none
  1869. ;  Requires:   8086-class CPU.
  1870. ;  Entry:      DS:SI = start of 1st string,
  1871. ;              ES:DI = start of 2nd string.
  1872. ;  Exit:       zf = 1 if equal.
  1873. ;              cf = 1 if EOS not found within segment.
  1874. ;  Calls:      strlen
  1875. ;  Changes:    flags
  1876. ;--------------------------------------------------------------------------;
  1877. PROC strcmp
  1878.  
  1879.    Push_M   <ax, cx, di, si>
  1880.    call     strlen                        ; get length on 1 of the strings
  1881.    jc       SHORT @@Fin                   ; error
  1882.    inc      cx                            ; account for EOS
  1883.  
  1884. ; There will always be at least one char in each string 
  1885. ; to compare - the terminal null. 
  1886.    pushf                                  ; save direction flag
  1887.    cld
  1888.    repe     cmpsb                         ; compare both strings
  1889.    popf                                   ; recover direction flag
  1890.    dec      di
  1891.    dec      si
  1892.    cmpsb                                  ; set flags based on final char
  1893.  
  1894. @@Fin:
  1895.    Pop_M    <si, di, cx, ax>
  1896.    ret
  1897. ENDP strcmp
  1898.  
  1899.  
  1900. END
  1901.