home *** CD-ROM | disk | FTP | other *** search
- ;--------------------------------------------------------------------------;
- ; Program: Recall .Asm ;
- ; Purpose: Commandline editor and history TSR. ;
- ; Notes: Compiles under TURBO Assembler, v2.0. Requires DOS v2.xx ;
- ; or higher. Editing keys are coded as PC extended scan ;
- ; codes; otherwise, this uses only DOS calls. ;
- ; The overall design is derived from RDE (aka, Rainbow DOS ;
- ; Editor) by Joe Kneidel. The methods used to install and ;
- ; uninstall this TSR are from _MS-DOS Developer's Guide_, ;
- ; by Angermayer and Jaeger. ;
- ; Status: Released into the >>>public domain<<<. Enjoy! If you use ;
- ; it, let me know what you think. You don't have to send ;
- ; any money, just comments and suggestions. ;
- ; Updates: 24-Oct-90, v1.0a, GAT ;
- ; - initial version ;
- ; 28-Oct-90, v1.0b, GAT ;
- ; - renamed get_LineFromUser to get_CmdLine and ;
- ; add_LineToBuffer to store_CmdInBuf. ;
- ; - made sure to zero out CH in add_LineToBuffer. ;
- ; - excluded CR from byte count in get_CmdLine. ;
- ; - kept track of CurCmd rather than PrevCmd/NextCmd and ;
- ; moved checks on command from recall_CmdFromBuf to ;
- ; mov_pcmd and mov_ncmd. ;
- ; - specified command table as an array of structures and ;
- ; revised ways it was accessed in get_CmdLine. ;
- ; - rearranged various procedures. ;
- ; - spruced up comments. ;
- ; 31-Oct-90, v1.1a, GAT ;
- ; - removed notices about preliminary notices. ;
- ; - cleanup up help message a bit. ;
- ; - avoided use of LABELs. ;
- ; - added list_CmdLines to list recall buffer contents. ;
- ;--------------------------------------------------------------------------;
-
- ;--------------------------------------------------------------------------;
- ; Author: George A. Theall ;
- ; Phone: +1 215 662 0558 ;
- ; SnailMail: TifaWARE ;
- ; 506 South 41st St., #3M ;
- ; Philadelphia, PA. 19104 USA ;
- ; E-Mail: GTHEALL@PENNDRLS.UPENN.EDU (Internet) ;
- ;--------------------------------------------------------------------------;
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; D I R E C T I V E S ;
- ;--------------------------------------------------------------------------;
- DOSSEG
- MODEL tiny
-
- IDEAL
- LOCALS
- JUMPS
-
- ;
- ; This section comes from D:\ASM\INCLUDE\Equates.Inc.
- ;
- EOS EQU 0 ; terminates strings
- BELL EQU 7
- BS EQU 8
- TAB EQU 9
- CR EQU 13
- LF EQU 10
- ESCAPE EQU 27 ; nb: ESC is a TASM keyword
- SPACE EQU ' '
- KEY_F1 EQU 3bh
- KEY_F2 EQU 3ch
- KEY_F3 EQU 3dh
- KEY_F4 EQU 3eh
- KEY_F5 EQU 3fh
- KEY_F6 EQU 40h
- KEY_F7 EQU 41h
- KEY_F8 EQU 42h
- KEY_F9 EQU 43h
- KEY_F10 EQU 44h
- KEY_HOME EQU 47h
- KEY_UP EQU 48h
- KEY_PGUP EQU 49h
- KEY_LEFT EQU 4bh
- KEY_RIGHT EQU 4dh
- KEY_END EQU 4fh
- KEY_DOWN EQU 50h
- KEY_PGDN EQU 51h
- KEY_INS EQU 52h
- KEY_DEL EQU 53h
- KEY_C_F1 EQU 5eh
- KEY_C_F2 EQU 5fh
- KEY_C_F3 EQU 60h
- KEY_C_F4 EQU 61h
- KEY_C_F5 EQU 62h
- KEY_C_F6 EQU 63h
- KEY_C_F7 EQU 64h
- KEY_C_F8 EQU 65h
- KEY_C_F9 EQU 66h
- KEY_C_F10 EQU 67h
- KEY_C_LEFT EQU 73h
- KEY_C_RIGHT EQU 74h
- KEY_C_END EQU 75h
- KEY_C_PGDN EQU 76h
- KEY_C_HOME EQU 77h
- KEY_C_PGUP EQU 84h
- KEY_F11 EQU 85h
- KEY_F12 EQU 86h
- KEY_C_F11 EQU 89h
- KEY_C_F12 EQU 8ah
- DOS EQU 21h ; main MSDOS interrupt
- STDIN EQU 0 ; standard input
- STDOUT EQU 1 ; standard output
- STDERR EQU 2 ; error output
- STDAUX EQU 3 ; COM port
- STDPRN EQU 4 ; printer
-
- ;
- ; This section comes from D:\ASM\INCLUDE\Macros.Inc.
- ;
- MACRO Pop_M RegList ;; Pops registers off stack.
- IRP Reg, <RegList>
- IFIDNI <Reg>, <flags>
- popf
- ELSE
- pop Reg
- ENDIF
- ENDM
- ENDM
- MACRO Push_M RegList ;; Pushes registers onto stack.
- IRP Reg, <RegList>
- IFIDNI <Reg>, <flags>
- pushf
- ELSE
- push Reg
- ENDIF
- ENDM
- ENDM
- MACRO Zero Reg ;; Zeros any register.
- xor Reg, Reg
- ENDM
-
-
- ; BUFSIZE specifies size of the recall buffer for collecting commandlines.
- ; Values of 255 or below are risky because that's the maximum buffer size
- ; for subfunction 10 of Int 21h and my code in add_LineToBuffer does not
- ; make sure commandlines will fit. I foresee no problems, however, with
- ; larger values up to about 60K.
- BUFSIZE equ 1024 ; >>>CHANGE AT YOUR RISK<<<
- ENTRY equ OFFSET handle_Int21 ; entry point for my ISR
- VERSION equ '1.1a' ; current version of RECALL
- ERRH equ 1 ; errorlevel if help given
- ERRINS equ 10 ; errorlevel if install failed
- ERRNYI equ 20 ; errorlevel if not yet installed
- OFF equ 0
- ON equ 1
-
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; C O D E S E G M E N T ;
- ;--------------------------------------------------------------------------;
- CODESEG
-
- ORG 0 ; address of code segment start
- SegStart DB ? ; used in install_TSR
-
- ORG 2ch ; address of environment segment
- EnvSeg DW ? ; used in install_TSR
-
- ORG 80h ; address of commandline
- CmdLen DB ?
- CmdLine DB 127 DUP (?)
-
- ORG 100h ; start of .COM file
- STARTUPCODE
- jmp main
-
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; R E S I D E N T D A T A ;
- ;--------------------------------------------------------------------------;
- TSR_Sig DB 'TifaWARE RECALL v', VERSION, ' is now installed.'
- DB CR, LF, EOS
- OldInt21 DD ? ; address of old INT 21H handler
- ; MUST BE ofs 1st, then seg!
- OldAX DW ? ; value of AX register when my
- ; handler is first called
- OldStack DD ? ; address of caller's stack
- CurCmd DW 0 ; pointer to current command
- ; in recall buffer
- InsMode DB ON ; InsertMode toggle flag
-
- STRUC CMD ; structure for editing cmd
- Key DB ? ; extended code for key
- Function DW ? ; address of editing function
- ENDS
- CmdTbl CMD <KEY_LEFT, OFFSET mov_lchar>
- CMD <KEY_RIGHT, OFFSET mov_rchar>
- CMD <KEY_PGUP, OFFSET mov_lword>
- CMD <KEY_PGDN, OFFSET mov_rword>
- CMD <KEY_HOME, OFFSET mov_bol>
- CMD <KEY_END, OFFSET mov_eol>
- CMD <KEY_UP, OFFSET mov_pcmd>
- CMD <KEY_DOWN, OFFSET mov_ncmd>
- CMD <BS, OFFSET del_lchar>
- CMD <KEY_C_LEFT, OFFSET del_lchar>
- CMD <KEY_DEL, OFFSET del_rchar>
- CMD <KEY_C_RIGHT, OFFSET del_rchar>
- CMD <KEY_C_PGUP, OFFSET del_lword>
- CMD <KEY_C_PGDN, OFFSET del_rword>
- CMD <KEY_C_HOME, OFFSET del_bol>
- CMD <KEY_C_END, OFFSET del_eol>
- CMD <ESCAPE, OFFSET del_line>
- CMD <KEY_C_F9, OFFSET del_buf>
- CMD <KEY_INS, OFFSET toggle_InsMode>
- CMD <0, OFFSET ring_Bell> ; >>>must be last<<<
-
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; L O C A L S T A C K ;
- ;--------------------------------------------------------------------------;
- DB 16 dup("STACK ") ; 128 bytes for local stack
- StackTop = $
-
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; R E S I D E N T C O D E ;
- ;--------------------------------------------------------------------------;
- ;---- is_CharWhite ------------------------------------------------------;
- ; Purpose: Tests if character is either a blank or a tab. ;
- ; Notes: none ;
- ; Entry: AL = character to be tested. ;
- ; Exit: Zero flag set if true, cleared otherwise. ;
- ; Calls: none ;
- ; Changes: flags ;
- ;--------------------------------------------------------------------------;
- PROC is_CharWhite
-
- cmp al, SPACE ; if == SPACE then zf = 1
- jz SHORT @@Fin
- cmp al, TAB ; if == TAB then zf = 1
- @@Fin:
- ret
- ENDP is_CharWhite
-
-
- ;---- get_KeyNoEcho -----------------------------------------------------;
- ; Purpose: Reads key from STDIN, waiting as necessary. ;
- ; Notes: Allows DESQview to operate efficiently if task inactive. ;
- ; Ctrl-C and Ctrl-Break generate Int 23h. ;
- ; Entry: n/a ;
- ; Exit: AL = character (0 => extended code available next). ;
- ; Calls: none ;
- ; Changes: AX ;
- ;--------------------------------------------------------------------------;
- PROC get_KeyNoEcho
-
- mov ah, 8
- int DOS
- ret
- ENDP get_KeyNoEcho
-
-
- ;---- ring_Bell ---------------------------------------------------------;
- ; Purpose: Rings the console bell as a warning to user. ;
- ; Notes: none ;
- ; Entry: n/a ;
- ; Exit: n/a ;
- ; Calls: none ;
- ; Changes: none ;
- ;--------------------------------------------------------------------------;
- PROC ring_Bell
-
- push ax dx
- mov ah, 2
- mov dl, BELL
- int DOS
- pop dx ax
- ret
- ENDP ring_Bell
-
-
- ;---- display_Char ------------------------------------------------------;
- ; Purpose: Displays character on STDOUT and advances to next char. ;
- ; Notes: Do *not* call this procedure to display backspaces; use ;
- ; backup_Cursor instead. Though they'd be displayed ;
- ; properly, BX would be *incremented* here. ;
- ; Does *not* adjust CH or CL. ;
- ; Entry: AL = character to display, ;
- ; BX = pointer to current position in commandline. ;
- ; Exit: BX++ ;
- ; Calls: none ;
- ; Changes: BX ;
- ;--------------------------------------------------------------------------;
- PROC display_Char
-
- push ax dx
- mov dl, al
- mov ah, 2
- int DOS
- inc bx ; move to next char on cmdline
- pop dx ax
- ret
- ENDP display_Char
-
-
- ;---- advance_Cursor ----------------------------------------------------;
- ; Purpose: Moves the cursor forwards on the screen. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; SI = # of bytes to advance. ;
- ; Exit: BX += SI, ;
- ; CH -= SI. ;
- ; Calls: display_Char ;
- ; Changes: AX, CH, ;
- ; BX (display_Char) ;
- ;--------------------------------------------------------------------------;
- PROC advance_Cursor
-
- or si, si ; anything to skip over?
- jz SHORT @@Fin
-
- ; Adjust CH now. (This could be left until later - no big deal.)
- mov al, ch
- Zero ah
- sub ax, si
- mov ch, al ; CH -= SI
-
- ; Display SI characters on commandline.
- push cx
- mov cx, si
- @@NextChar:
- mov al, [bx]
- call display_Char ; nb: increments BX too
- loop SHORT @@NextChar
- pop cx
-
- @@Fin:
- ret
- ENDP advance_Cursor
-
-
- ;---- backup_Cursor -----------------------------------------------------;
- ; Purpose: Moves the cursor backwards on the screen. ;
- ; Notes: Does *not* handle properly line-wrapping yet. ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; SI = # of bytes to back up. ;
- ; Exit: BX -= SI, ;
- ; CH += SI. ;
- ; Calls: none ;
- ; Changes: AX, BX, CH ;
- ;--------------------------------------------------------------------------;
- PROC backup_Cursor
-
- or si, si ; anything to skip over?
- jz SHORT @@Fin
-
- ; Adjust BX and CH now. (This could be left until later - no big deal.)
- sub bx, si ; BX -= SI
- mov al, ch
- Zero ah
- add ax, si
- mov ch, al ; CH += SI
-
- ; Back up cursor by displaying non-destructive backspaces.
- push cx dx
- mov ah, 2
- mov cx, si
- mov dl, BS
- @@PrevChar:
- int DOS
- loop SHORT @@PrevChar
- pop dx cx
-
- @@Fin:
- ret
- ENDP backup_Cursor
-
-
- ;---- delete_Chars ------------------------------------------------------;
- ; Purpose: Deletes characters from commandline. ;
- ; Notes: No checks are done on SI's validity; ie, caller should ;
- ; ensure there are enough characters on line to delete. ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline, ;
- ; SI = # of characters to delete. ;
- ; Exit: CH -= SI, ;
- ; CL += SI. ;
- ; Calls: display_Char, backup_Cursor ;
- ; Changes: CH, CL, SI, ;
- ; AX (backup_Cursor) ;
- ;--------------------------------------------------------------------------;
- PROC delete_Chars
-
- or si, si ; anything to delete?
- jz SHORT @@Fin
-
- ; Adjust CH and CL now while I have SI handy. At the same time
- ; I am also computing the number of characters to shift.
- mov al, cl
- Zero ah
- add ax, si
- mov cl, al ; CL += SI
- mov al, ch
- sub ax, si
- mov ch, al ; CH -= SI
- push cx ; final values of CH and CL
- push ax ; used to back up cursor
-
- ; Shift CH - SI characters remaining on line to the left by SI characters.
- mov cx, ax ; CX = CH - SI
- jcxz SHORT @@Coverup ; skip if deleting to eol
- @@NextChar:
- mov al, [bx+si]
- mov [bx], al
- call display_Char ; nb: increments BX too
- loop SHORT @@NextChar
-
- ; Display spaces to overwrite chars remaining on line.
- @@CoverUp:
- mov al, SPACE
- mov cx, si
- @@NextBlank:
- call display_Char ; nb: increments BX too
- loop SHORT @@NextBlank
-
- ; Back up cursor to its location on invocation.
- pop ax ; CH - SI
- add si, ax ; SI += (CH - SI)
- call backup_Cursor ; nb: decrements BX too
- pop cx
-
- ; NB: BX will be incremented (CH - SI) + SI times by display_Char and
- ; decremented CH times by backup_Cursor so overall it won't change.
- @@Fin:
- ret
- ENDP delete_Chars
-
-
- ;---- add_CharToLine ----------------------------------------------------;
- ; Purpose: Adds a character to commandline buffer. ;
- ; Notes: Checks to see if buffer would overflow first. ;
- ; Entry: AL = character to add, ;
- ; BX = pointer to current position in line, ;
- ; CH = number of characters until end of line, ;
- ; CL = number of bytes left in line. ;
- ; Exit: BX and CX changed as appropriate. ;
- ; Calls: display_Char, backup_Cursor, ring_Bell ;
- ; Changes: AX, BX, CX ;
- ;--------------------------------------------------------------------------;
- PROC add_CharToLine
-
- ; Check for space unless insert mode is OFF *and* not at eol.
- cmp [cs:InsMode], ON
- je SHORT @@CheckForSpace
- or ch, ch
- jz SHORT @@CheckForSpace
-
- ; Overwrite existing character while in not at eol.
- mov [bx], al
- call display_Char ; nb: increments BX too
- dec ch
- jmp SHORT @@Fin
-
- @@CheckForSpace:
- or cl, cl
- jz SHORT @@Abort
- or ch, ch
- jnz SHORT @@AddWithShift
-
- ; At end of line.
- mov [bx], al
- call display_Char ; nb: increments BX too
- dec cl
- jmp SHORT @@Fin
-
- ; Add character and shift everything to right over by 1 position.
- @@AddWithShift:
- push cx dx
- mov cl, ch ; CH = chars to eol
- Zero ch
- mov si, cx ; save for backing up
- inc cl ; but add 1 to display new char
- @@NextChar:
- mov dl, [bx] ; use DL as temporary storage
- mov [bx], al
- call display_Char ; nb: increments BX too
- mov al, dl ; recover previous char
- loop SHORT @@NextChar
- call backup_Cursor ; nb: decrements BX too
- pop dx cx
- dec cl
- jmp SHORT @@Fin
-
- @@Abort:
- call ring_Bell ; if out of space
-
- @@Fin:
- ret
- ENDP add_CharToLine
-
-
- ;---- find_StartofPrevWord ----------------------------------------------;
- ; Purpose: Locates start of previous word in commandline. ;
- ; Notes: "Words" are delineated by blanks and/or start/finish of ;
- ; the commandline. ;
- ; Entry: BX = pointer to current position in commandline. ;
- ; Exit: SI = # of characters from BX to start of previous word. ;
- ; Calls: is_CharWhite ;
- ; Changes: AX, SI ;
- ;--------------------------------------------------------------------------;
- PROC find_StartofPrevWord
-
- push bx dx
- inc dx
- inc dx ; DX now points to bol
- mov si, bx ; SI = current position
-
- ; Skip over any whitespace. Note: don't bother with 1st character -
- ; think of how it should behave if positioned at start of a word.
- @@SkipWhite:
- dec bx
- cmp bx, dx
- jb SHORT @@Fin ; done if BX < bol
- mov al, [bx]
- call is_CharWhite
- jz SHORT @@SkipWhite
-
- ; Next skip over non-blanks until the start of the word.
- @@SkipWord:
- dec bx
- cmp bx, dx
- jb SHORT @@Fin ; done if BX < bol
- mov al, [bx]
- call is_CharWhite
- jnz SHORT @@SkipWord
-
- ; Finally compute how many characters must be skipped.
- @@Fin:
- inc bx ; backed up 1 too many
- sub si, bx
- pop dx bx
- ret
- ENDP find_StartofPrevWord
-
-
- ;---- find_StartofNextWord ----------------------------------------------;
- ; Purpose: Locates start of next word in commandline. ;
- ; Notes: "Words" are delineated by blanks and/or start/finish of ;
- ; the commandline. ;
- ; Entry: BX = pointer to current position in commandline. ;
- ; Exit: SI = # of characters from BX to start of next word. ;
- ; Calls: is_CharWhite ;
- ; Changes: AX, SI ;
- ;--------------------------------------------------------------------------;
- PROC find_StartofNextWord
-
- push bx dx
- mov dx, bx
- mov al, ch
- Zero ah
- add dx, ax ; DX now points to eol
-
- ; Skip over any existing word. Note: unlike find_StartofPrevWord, here
- ; we do not want to initially skip ahead - imagine if cursor were at
- ; a blank before the start of a word.
- @@SkipWord:
- cmp bx, dx
- je SHORT @@Fin ; done if BX = eol
- mov al, [bx]
- inc bx
- call is_CharWhite
- jnz SHORT @@SkipWord
-
- ; Next skip over whitespace until the start of the word.
- @@SkipWhite:
- cmp bx, dx
- je SHORT @@Fin ; done if BX = eol
- mov al, [bx]
- inc bx
- call is_CharWhite
- jz SHORT @@SkipWhite
- dec bx ; point back to white space
-
- ; Finally compute how many characters must be skipped.
- @@Fin:
- mov si, bx ; where we are now
- pop dx bx
- sub si, bx ; less where we started from
- ret
- ENDP find_StartofNextWord
-
-
- ;---- recall_CmdFromBuf -------------------------------------------------;
- ; Purpose: Replaces current with a commandline from buffer. ;
- ; Notes: Does *not* check SI's validity. ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline, ;
- ; SI = pointer to command in recall buffer. ;
- ; Entry: BX = pointer to end of new commandline, ;
- ; CH = 0, ;
- ; CL = # of bytes left in new commandline. ;
- ; Calls: del_line, display_Char, ring_Bell ;
- ; Changes: AL, BX, CH, CL, SI ;
- ;--------------------------------------------------------------------------;
- PROC recall_CmdFromBuf
-
- ; Clear current commandline and display new one.
- push si ; since del_line zaps SI
- call del_line ; changes CH and CL such that CX
- ; == max # of chars in buffer
- pop si
- @@NextChar:
- mov al, [cs:si]
- cmp al, CR
- je SHORT @@Fin
- inc si
- mov [bx], al
- call display_Char ; nb: increments BX too
- loop SHORT @@NextChar ; continue as long as CX > 0
- cmp [BYTE cs:si], CR ; did loop end prematurely?
- je SHORT @@Fin ; no
- call ring_Bell ; yes, warn user
-
- @@Fin:
- ret
- ENDP recall_CmdFromBuf
-
-
- ;---- mov_lchar ---------------------------------------------------------;
- ; Purpose: Moves cursor left in the commandline. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line. ;
- ; Exit: BX--, ;
- ; CH++, ;
- ; SI = 1 (or 0 if already at bol). ;
- ; Calls: backup_Cursor ;
- ; Changes: SI, ;
- ; BX, CH (backup_Cursor) ;
- ;--------------------------------------------------------------------------;
- PROC mov_lchar
-
- mov si, bx
- sub si, dx
- cmp si, 2
- ja SHORT @@MoveIt ; at bol if BX - DX <= 2
- Zero si
- jmp SHORT @@Fin
-
- @@MoveIt:
- mov si, 1 ; move 1 character
- call backup_Cursor ; nb: decrements BX too
-
- @@Fin:
- ret
- ENDP mov_lchar
-
-
- ;---- mov_rchar ---------------------------------------------------------;
- ; Purpose: Moves cursor right in the commandline. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line. ;
- ; Exit: BX++, ;
- ; CH--, ;
- ; SI = 1 (or 0 if already at eol). ;
- ; Calls: advance_Cursor ;
- ; Changes: SI, ;
- ; AX, BX, CH (advance_Cursor) ;
- ;--------------------------------------------------------------------------;
- PROC mov_rchar
-
- Zero si ; set SI = 0 first
- or ch, ch
- jz SHORT @@Fin ; abort if CH = 0
- inc si ; move 1 character
- call advance_Cursor ; nb: increments BX
-
- @@Fin:
- ret
- ENDP mov_rchar
-
-
- ;---- mov_lword ---------------------------------------------------------;
- ; Purpose: Moves cursor to start of previous word. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line. ;
- ; Exit: BX and CH adjusted as appropriate. ;
- ; Calls: find_StartofPrevWord, backup_Cursor ;
- ; Changes: SI, (find_StartofPrevWord) ;
- ; AX, BX, CH (backup_Cursor) ;
- ;--------------------------------------------------------------------------;
- PROC mov_lword
-
- call find_StartofPrevWord
- call backup_Cursor
- ret
- ENDP mov_lword
-
-
- ;---- mov_rword ---------------------------------------------------------;
- ; Purpose: Moves cursor to start of next word. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line. ;
- ; Exit: BX and CH adjusted as appropriate. ;
- ; Calls: find_StartofNextWord, advance_Cursor ;
- ; Changes: SI, (find_StartofNextWord) ;
- ; AX, BX, CH (advance_Cursor) ;
- ;--------------------------------------------------------------------------;
- PROC mov_rword
-
- call find_StartofNextWord
- call advance_Cursor
- ret
- ENDP mov_rword
-
-
- ;---- mov_bol -----------------------------------------------------------;
- ; Purpose: Moves cursor to start of commandline. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line. ;
- ; Exit: BX = DX + 2, ;
- ; CH = # of characters in commandline, ;
- ; SI = # of characters backed up. ;
- ; Calls: backup_Cursor ;
- ; Changes: SI, ;
- ; AX, BX, CH (backup_Cursor) ;
- ;--------------------------------------------------------------------------;
- PROC mov_bol
-
- mov si, bx
- sub si, dx
- dec si
- dec si ; SI = BX - (DX + 2)
- call backup_Cursor
- ret
- ENDP mov_bol
-
-
- ;---- mov_eol -----------------------------------------------------------;
- ; Purpose: Moves cursor to end of commandline. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line. ;
- ; Exit: BX += CH, ;
- ; CH = 0, ;
- ; SI = # of characters advanced. ;
- ; Calls: advance_Cursor ;
- ; Changes: SI, ;
- ; AX, BX, CH (advance_Cursor) ;
- ;--------------------------------------------------------------------------;
- PROC mov_eol
-
- mov al, ch
- Zero ah
- mov si, ax ; SI = CH
- call advance_Cursor
- ret
- ENDP mov_eol
-
-
- ;---- mov_pcmd ----------------------------------------------------------;
- ; Purpose: Replaces current with previous commandline from buffer. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Entry: BX = pointer to end of new commandline, ;
- ; CH = 0, ;
- ; CL = # of bytes left in new commandline, ;
- ; [CurCmd] adjusted. ;
- ; Calls: recall_CmdFromBuf, del_line ;
- ; Changes: CurCmd, ;
- ; AX, BX, CH, CL, SI, (recall_CmdFromBuf) ;
- ;--------------------------------------------------------------------------;
- PROC mov_pcmd
-
- push di
-
- ; Point DI to 2 bytes before CurCmd. Abort if this lies at or before
- ; start of recall buffer.
- mov di, [cs:CurCmd]
- dec di ; now at possible CR
- dec di ; now at possible cmd's last char
- cmp di, OFFSET RecallBuf
- jbe SHORT @@Abort
-
- ; Scan backwards to start of buffer or until finding another CR.
- push cx ; CH/CL for recall_CmdFromBuf
- pushf
- mov al, CR
- mov cx, di
- sub cx, OFFSET RecallBuf - 1
- std ; scan backwards
- repne scasb ; uses ES:DI
- popf
- pop cx
- inc di ; should point to CR
- cmp [BYTE es:di], CR
- jne SHORT @@Abort
-
- ; Point SI to start of command and recall it.
- inc di
- mov si, di
- mov [cs:CurCmd], si
- call recall_CmdFromBuf
- jmp SHORT @@Fin
-
- ; Nothing to recall, so point CurCmd to start of buffer
- ; and delete current line.
- @@Abort:
- mov [cs:CurCmd], OFFSET RecallBuf
- call del_line
-
- @@Fin:
- pop di
- ret
- ENDP mov_pcmd
-
-
- ;---- mov_ncmd ----------------------------------------------------------;
- ; Purpose: Replaces current with next commandline from buffer. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Entry: BX = pointer to end of new commandline, ;
- ; CH = 0, ;
- ; CL = # of bytes left in new commandline, ;
- ; [CurCmd] adjusted. ;
- ; Calls: recall_CmdFromBuf, del_line ;
- ; Changes: [CurCmd], ;
- ; AX, BX, CH, CL, SI (recall_CmdFromBuf) ;
- ;--------------------------------------------------------------------------;
- PROC mov_ncmd
-
- push di
-
- ; Point DI to CurCmd. Abort if this lies at or after LastByte.
- mov di, [cs:CurCmd]
- cmp di, OFFSET LastByte
- jae SHORT @@Abort
-
- ; Scan forwards to end of buffer or until finding another CR.
- ; NB: Scan stops before final CR in the recall buffer since
- ; there can never be a command after that; saves having to
- ; check DI against OFFSET LastByte.
- push cx ; CH/CL for recall_CmdFromBuf
- mov al, CR
- mov cx, OFFSET LastByte ; *not* OFFSET LastByte + 1
- sub cx, di
- repne scasb ; uses ES:DI
- pop cx
- dec di ; should point to CR
- cmp [BYTE es:di], CR
- jne SHORT @@Abort
-
- ; Point SI to start of command and recall it.
- inc di ; point to 1st char in next cmd
- mov si, di
- mov [cs:CurCmd], si
- call recall_CmdFromBuf
- jmp SHORT @@Fin
-
- ; Nothing to recall, so point CurCmd to just past end
- ; of recall buffer and delete current line.
- @@Abort:
- mov [cs:CurCmd], OFFSET LastByte + 1
- call del_line
-
- @@Fin:
- pop di
- ret
- ENDP mov_ncmd
-
-
- ;---- del_lchar ---------------------------------------------------------;
- ; Purpose: Deletes character to left of cursor. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CL = # of bytes left in commandline. ;
- ; Exit: BX--, ;
- ; CL++. ;
- ; Calls: mov_lchar, delete_Chars ;
- ; Changes: BX, (mov_lchar), ;
- ; AX, CH, CL, SI (delete_Chars) ;
- ;--------------------------------------------------------------------------;
- PROC del_lchar
-
- call mov_lchar ; sets SI = 0 (at bol) or 1
- call delete_Chars
- ret
- ENDP del_lchar
-
-
- ;---- del_rchar ---------------------------------------------------------;
- ; Purpose: Deletes character at cursor. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Exit: BX--, ;
- ; CH--, ;
- ; CL++. ;
- ; Calls: delete_Chars ;
- ; Changes: AX, CH, CL, SI (delete_Chars) ;
- ;--------------------------------------------------------------------------;
- PROC del_rchar
-
- or ch, ch
- jz SHORT @@Fin ; abort if already at eol
- mov si, 1
- call delete_Chars
- @@Fin:
- ret
- ENDP del_rchar
-
-
- ;---- del_lword ---------------------------------------------------------;
- ; Purpose: Deletes word to left of cursor. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Exit: BX, CH, and CL adjusted as appropriate. ;
- ; Calls: mov_lword, delete_Chars ;
- ; Changes: BX, (mov_lword), ;
- ; AX, CH, CL, SI (delete_Chars) ;
- ;--------------------------------------------------------------------------;
- PROC del_lword
-
- call mov_lword ; sets SI = 0 (at bol) or > 0
- call delete_Chars
- ret
- ENDP del_lword
-
-
- ;---- del_rword ---------------------------------------------------------;
- ; Purpose: Deletes word to right of cursor. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Exit: CH, and CL adjusted as appropriate. ;
- ; Calls: find_StartofNextWord, delete_Chars ;
- ; Changes: AX, CH, CL, SI (delete_Chars) ;
- ;--------------------------------------------------------------------------;
- PROC del_rword
-
- call find_StartofNextWord ; sets SI = 0 (at eol) or > 0
- call delete_Chars
- ret
- ENDP del_rword
-
-
- ;---- del_bol -----------------------------------------------------------;
- ; Purpose: Deletes from cursor to start of commandline. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Exit: BX = DX + 2, ;
- ; CH = number of characters in commandline, ;
- ; CL -= BX - DX - 2. ;
- ; Calls: mov_bol, delete_Chars ;
- ; Changes: BX, (mov_bol) ;
- ; AX, CH, CL, SI (delete_Chars) ;
- ;--------------------------------------------------------------------------;
- PROC del_bol
-
- call mov_bol ; sets SI = 0 (at bol) or > 0
- call delete_Chars
- ret
- ENDP del_bol
-
-
- ;---- del_eol -----------------------------------------------------------;
- ; Purpose: Deletes from cursor to end of commandline. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Exit: BX = DX + 2, ;
- ; CH = 0, ;
- ; CL -= CH. ;
- ; Calls: delete_Chars ;
- ; Changes: AX, CH, CL, SI (delete_Chars) ;
- ;--------------------------------------------------------------------------;
- PROC del_eol
-
- mov al, ch
- Zero ah
- mov si, ax
- call delete_Chars
- ret
- ENDP del_eol
-
-
- ;---- del_line ----------------------------------------------------------;
- ; Purpose: Deletes entire commandline. ;
- ; Notes: none ;
- ; Entry: BX = pointer to current position in commandline, ;
- ; CH = # of bytes to end of line, ;
- ; CL = # of bytes left in commandline. ;
- ; Exit: BX = DX + 2, ;
- ; CH = 0, ;
- ; CL = [DX]. ;
- ; Calls: mov_bol, del_eol ;
- ; Changes: BX, (mov_bol) ;
- ; AX, CH, CL, SI (del_eol) ;
- ;--------------------------------------------------------------------------;
- PROC del_line
-
- call mov_bol
- call del_eol
- ret
- ENDP del_line
-
-
- ;---- del_buf -----------------------------------------------------------;
- ; Purpose: Deletes all commands in recall buffer. ;
- ; Notes: Does not affect current commandline. ;
- ; This function is not documented elsewhere. ;
- ; Entry: n/a ;
- ; Exit: [CurCmd] = OFFSET LastByte + 1. ;
- ; Calls: init_Buf ;
- ; Changes: AX, [CurCmd] (init_Buf) ;
- ;--------------------------------------------------------------------------;
- PROC del_buf
-
- mov [WORD cs:CurCmd], 0
- call init_Buf ; nb: changes CurCmd
- ret
- ENDP del_buf
-
-
- ;---- toggle_InsMode ----------------------------------------------------;
- ; Purpose: Toggles flag for insert mode. ;
- ; Notes: none ;
- ; Entry: n/a ;
- ; Exit: [InsMode] toggled. ;
- ; Calls: none ;
- ; Changes: [InsMode] ;
- ;--------------------------------------------------------------------------;
- PROC toggle_InsMode
-
- xor [cs:InsMode], 1
- ret
- ENDP toggle_InsMode
-
-
- ;---- init_Buf ----------------------------------------------------------;
- ; Purpose: Initializes recall buffer if necessary. ;
- ; Notes: Clears recall buffer if CurCmd is zero. Normally, CurCmd ;
- ; will take on values OFFSET RecallBuf and LastByte, ;
- ; or less. Zero should not otherwise occur. ;
- ; This is needed when scanning for previous commands - ;
- ; spurious CRs should not be encountered. ;
- ; Entry: [CurCmd] = pointer to current command in recall buffer. ;
- ; Exit: [CurCmd] = OFFSET LastByte + 1 if buffer is initialized. ;
- ; Calls: none ;
- ; Changes: AX, [CurCmd] possibly ;
- ;--------------------------------------------------------------------------;
- PROC init_Buf
-
- ; Abort if [CurCmd] is non-zero.
- cmp [WORD cs:CurCmd], 0
- jne SHORT @@Fin
-
- ; Initialize buffer by zeroing out all but last byte. There put a CR
- ; so when searching backwards for commands I'll find at least one.
- push cx di
- Zero al ; fill with zeros
- mov cx, BUFSIZE - 1
- mov di, OFFSET RecallBuf
- rep stosb ; uses ES:DI
- mov [BYTE es:di], CR ; buffer ends with CR
- pop di cx
-
- ; Point current command to past end of recall buffer. This is so both
- ; mov_pcmd and mov_ncmd will not find any commands yet still function
- ; without error (which would happen if [CurCmd] were left at 0).
- mov [cs:CurCmd], OFFSET LastByte + 1
-
- @@Fin:
- ret
- ENDP init_Buf
-
-
- ;---- get_CmdLine -------------------------------------------------------;
- ; Purpose: Reads a commandline from user. ;
- ; Notes: The caller's buffer is used as a scratch area to keep ;
- ; memory requirements to a minimum. ;
- ; Entry: DS:DX = buffer for storing commandline, ;
- ; [BYTE DS:DX] = maximum number of bytes to read. ;
- ; Exit: [BYTE DS:DX+1] = number of bytes actually read, ;
- ; [BYTE DS:DX+2] = 1st byte read from user. ;
- ; Calls: get_KeyNoEcho, add_CharToLine, [CmdTbl] ;
- ; Changes: AX, BX, CX, BP, [InsMode], [PrevCmd], [NextCmd] ;
- ;--------------------------------------------------------------------------;
- PROC get_CmdLine
-
- mov bx, dx ; BX used for indexed addressing
- Zero ch ; bytes to end of line
- mov cl, [bx] ; space left in buffer
- dec cl ; less 1 for final CR
- inc bx ; pointer to first spot in buffer
- inc bx
-
- ; Get key and determine if it's an editing key or a regular character.
- @@NewKey:
- call get_KeyNoEcho ; get key from user
- cmp al, CR ; is user done yet?
- jz SHORT @@Fin
- cmp al, BS ; BS is an editing key
- je SHORT @@EditKey
- cmp al, ESCAPE ; ESCAPE is another
- je SHORT @@EditKey
- or al, al ; was key zero?
- jnz SHORT @@RegularChar ; no, then it's a regular char
- call get_KeyNoEcho ; yes, get extended scan code
-
- ; Process extended key as an editing key. Invalid keys are not added
- ; to the commandline buffer; instead, they merely result in a bell.
- @@EditKey:
- mov bp, OFFSET CmdTbl ; point to table of editing cmds
-
- @@NewCmd:
- cmp [(CMD PTR cs:bp).Key], al
- je SHORT @@ProcessCmd
- add bp, SIZE CmdTbl
- cmp [(CMD PTR cs:bp).Key], 0 ; zero marks end of table
- jne SHORT @@NewCmd ; and must point to ring_Bell
- ; so execution drops thru!!!
- @@ProcessCmd:
- call [(CMD PTR cs:bp).Function]
- jmp SHORT @@NewKey
-
- ; It's an ordinary character so add it to the commandline.
- @@RegularChar:
- call add_CharToLine
- jmp SHORT @@NewKey
-
- ; Now determine number of bytes in buffer, put that count in [DX+1],
- ; and terminate buffer with a CR. NB: count excludes final CR.
- @@Fin:
- mov bx, dx ; point back to start of buffer
- mov al, [bx] ; compute count
- sub al, cl
- dec al ; exclude final CR
- Zero ah
- mov [bx+1], al ; [DS:DX+1] = count
- add bx, ax
- mov [BYTE bx+2], CR ; place CR at end of buffer
- ret
- ENDP get_CmdLine
-
-
- ;---- store_CmdInBuf ----------------------------------------------------;
- ; Purpose: Stores the commandline at the end of the recall buffer. ;
- ; Notes: Commandlines consisting of 1 character (CR) are not saved. ;
- ; Entry: DS:DX = pointer to start of commandline, ;
- ; [BYTE DS:DX+1] = maximum number of bytes to read. ;
- ; Exit: [CurCmd] = LastByte + 1. ;
- ; Calls: none ;
- ; Changes: AX, BX, CX, DI, SI, [CurCmd] ;
- ;--------------------------------------------------------------------------;
- PROC store_CmdInBuf
-
- ; Check length of commandline.
- mov bx, dx
- mov cl, [bx+1]
- or cl, cl ; CL does not include final CR
- jz SHORT @@Fin ; so if = 0, nothing's there
- inc cl ; else set CX = # bytes in cmd
- Zero ch ; *including* final CR
-
- ; Make room in recall buffer for commandline by shifting everything
- ; back by [DS:DX+1] characters.
- push cx ds ; need both to copy to recall buf
- mov ax, cs
- mov ds, ax
- mov di, OFFSET RecallBuf ; to start of buffer
- mov si, di
- add si, cx ; from start + CX
- neg cx
- add cx, BUFSIZE ; for BUFSIZE - [BYTE BX+1]
- rep movsb ; move them
- pop ds cx
-
- ; Add commandline in empty space at end of recall buffer. By this
- ; point DI will point to space for current commandline.
- mov si, bx
- inc si
- inc si
- rep movsb ; from DS:SI to ES:DI
- mov [cs:CurCmd], OFFSET LastByte + 1
-
- @@Fin:
- ret
- ENDP store_CmdInBuf
-
-
- ;---- handle_Int21 ------------------------------------------------------;
- ; Purpose: Passes calls to input strings along to my own handler. ;
- ; Notes: none ;
- ; Entry: AH = subfunction to perform ;
- ; Exit: If AH = 10, DS:DX points to buffer read from user. ;
- ; Calls: init_Buf, get_CmdLine, store_CmdInBuf ;
- ; Changes: n/a ;
- ;--------------------------------------------------------------------------;
- PROC handle_Int21 FAR
-
- ; If the call is for buffered input, then use my handler;
- ; otherwise, pass it along to the old handler.
- cmp ah, 10
- jz SHORT @@SwitchStack
- jmp [cs:OldInt21] ; old vector issues IRET
-
- ; Switch over to my own stack and save callers registers.
- @@SwitchStack:
- mov [cs:OldAX], ax ; can't push it on my stack yet
- cli ; critical part - disallow INTs
- mov [WORD cs:OldStack], sp
- mov [WORD cs:OldStack+2], ss
- mov ax, cs
- mov ss, ax
- mov sp, OFFSET StackTop
- sti ; ok, out of critical section
- push bx cx dx di si bp ds es
- pushf
-
- ; Meat of my interrupt handler.
- mov es, ax ; set ES = CX
- cld
- call init_Buf
- call get_CmdLine
- call store_CmdInBuf
-
- ; Restore caller's registers.
- popf
- pop es ds bp si di dx cx bx
- cli
- mov ss, [WORD cs:OldStack+2]
- mov sp, [WORD cs:OldStack]
- sti
- mov ax, [cs:OldAX]
-
- iret ; return to caller
- ENDP handle_Int21
-
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; R E C A L L B U F F E R ;
- ;--------------------------------------------------------------------------;
- RecallBuf = $ ; will overlay transient portion
- LastByte = RecallBuf + BUFSIZE - 1 ; room for BUFSIZE characters
- ; and end of resident portion
-
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; T R A N S I E N T D A T A ;
- ;--------------------------------------------------------------------------;
- ProgName DB 'recall: '
- DB EOS
- EOL DB '.', CR, LF
- DB EOS
- HelpMsg DB CR, LF
- DB 'TifaWARE RECALL, v', VERSION, ', ', ??Date
- DB ' - commandline editor and history TSR.', CR, LF
- DB 'Usage: recall [-options]', CR, LF, LF
- DB 'Options:', CR, LF
- DB ' -i = install in memory', CR, LF
- DB ' -l = list commandlines in recall buffer', CR, LF
- DB ' -r = remove from memory', CR, LF
- DB ' -? = display this help message', CR, LF, LF
- DB 'Only one option can be specified at a time.', CR, LF
- DB EOS
- ErrMsgOpt DB 'illegal option -- '
- OptCh DB ? ; room for offending character
- DB EOS
- ErrMsgRes DB 'already resident'
- DB EOS
- ErrMsgMem DB 'memory control blocks corrupt'
- DB EOS
- ErrMsgNYI DB 'not yet installed'
- DB EOS
- RemoveMsg DB 'successfully removed'
- DB EOS
-
- SwitCh DB '-' ; char introducing options
- HFlag DB 0 ; flag for on-line help
- IFlag DB 0 ; flag for installing TSR
- LFlag DB 0 ; flag for listing commandlines
- RFlag DB 0 ; flag for removing TSR
-
-
- %NEWPAGE
- ;--------------------------------------------------------------------------;
- ; T R A N S I E N T C O D E ;
- ;--------------------------------------------------------------------------;
- ;---- check_ifInstalled -------------------------------------------------;
- ; Purpose: Checks if TSR has already been installed. ;
- ; Notes: Works by looking for TSR_Sig in segment owned by current ;
- ; interrupt handler. ;
- ; Entry: AL = interrupt to be tested. ;
- ; Exit: zf = 1 if installed; 0 otherwise. ;
- ; Calls: getvect, strcmp ;
- ; Changes: flags ;
- ;--------------------------------------------------------------------------;
- PROC check_ifInstalled
-
- push bx di si ds es
- call getvect ; address of handler now in ES:BX
- mov si, OFFSET TSR_Sig ; offset in DS to signature
- mov di, si ; should be at same offset in ES
- call strcmp ; do they match?
- pop es ds si di bx
- ret
- ENDP check_ifInstalled
-
-
- ;---- install_TSR -------------------------------------------------------;
- ; Purpose: Installs TSR in memory after hooking into the interrupt ;
- ; specified by AL. ;
- ; Notes: Will not load if already present in memory. ;
- ; Does *NOT* return if installation is successful. ;
- ; Labels "SegStart" and "LastByte" must be defined for start ;
- ; and end of resident stuff respectively. ;
- ; "Entry" must be defined as the entry point to the ISR. ;
- ; Entry: AL = interrupt vector to hook, ;
- ; DS:DX = doubleword (DD) storage for old handler. ;
- ; Exit: AL = ERRINS if installation failed. ;
- ; Calls: check_ifInstalled, getvect, setvect, errmsg ;
- ; Changes: AL, DX, flags ;
- ;--------------------------------------------------------------------------;
- PROC install_TSR
-
- call check_ifInstalled
- jnz SHORT @@FreeMemory ; proceed if not yet installed
- mov dx, OFFSET ErrMsgRes ; else point to error message
- jmp SHORT @@Fin ; and abort
-
- ; Free memory used by environment. NB: DOS will not make
- ; use of this memory block until the TSR is removed.
- @@FreeMemory:
- push ax es
- mov es, [EnvSeg] ; point to the block
- mov ah, 49h ; and let DOS free it
- int DOS
- pop es ax
- jnc SHORT @@GrabVector ; proceed if no error occured
- mov dx, OFFSET ErrMsgMem ; else point to error message
- jmp SHORT @@Fin ; and abort
-
- ; NB: This is the point of no return - if the execution reaches this
- ; point it will result in the program going resident such that the
- ; procedure does *NOT* return.
- ;
- ; Now save old interrupt handler. NB: This must be in the *resident*
- ; data area since: (1) calls to int 21h not for buffered input will be
- ; passed along, and (2) it's needed to successfully uninstall the TSR.
- @@GrabVector:
- push es ; original ES needed in fputs()
- call getvect ; returns handler addr in ES:BX
- xchg dx, bx ; BX needed for index addressing
- mov [bx], dx ; save offset followed by segment
- mov [bx+2], es
- pop es
-
- ; Now set it to my own handler.
- mov dx, ENTRY
- call setvect
-
- ; Let user know code's been installed and then go resident.
- mov bx, 1 ; report goes to STDOUT
- mov dx, OFFSET TSR_Sig
- call fputs
- ; ****************************************************************************
- ; NB: TASM's IDEAL mode treats arguments of the OFFSET operator in a peculiar
- ; fashion, as can be seen by browsing the lexical grammer in Appendix A of
- ; the _Reference Guide_. If MASM mode were used, the expression below would
- ; be written "(OFFSET LastByte - OFFSET SegStart + 16) SHR 5". However, in
- ; IDEAL mode not only would "OFFSET SegStart + 16" be parsed as "OFFSET
- ; (SegStart + 16)" but also the result would be viewed as a relative quantity.
- ; As it is, TASM replaces labels below with their respective address values
- ; thereby computing the "correct" amount of memory to save.
- ; ****************************************************************************
- ; NB: While Angermayer and Jaeger in their book say 15 should be used
- ; below, I've found 16 is necessary to handle cases in which LastByte
- ; lies at the start of a paragraph. So what if I'm wasting an entire
- ; paragraph!
- ; ****************************************************************************
- mov dx, (LastByte - SegStart + 16) SHR 4
- mov ax, 3100h ; terminate/stay resident, rc = 0
- int DOS ; via DOS
-
- ; This point is only reached on error.
- @@Fin:
- call errmsg
- mov al, ERRINS
- ret
- ENDP install_TSR
-
-
- ;---- uninstall_TSR -----------------------------------------------------;
- ; Purpose: Uninstalls TSR. ;
- ; Notes: none ;
- ; Entry: AL = interrupt vector to restore, ;
- ; DX = offset to doubleword (DD) storage for old handler as ;
- ; saved by install_TSR. ;
- ; Exit: AL = ERRNYI if uninstallation failed. ;
- ; Calls: check_ifInstalled, errmsg, getvect, setvect ;
- ; Changes: AX (AH too), BX, DX, flags ;
- ;--------------------------------------------------------------------------;
- PROC uninstall_TSR
-
- call check_ifInstalled
- jz SHORT @@FindTSRSeg ; proceed if installed
- mov al, ERRNYI ; else flag error
- mov dx, OFFSET ErrMsgNYI ; and point to error message
- jmp SHORT @@Fin
-
- ; Locate segment holding TSR.
- @@FindTSRSeg:
- push es ; needed by errmsg() below
- call getvect ; returns in ES:BX
-
- ; Restore previous interrupt handler stored in *RESIDENT* data segment.
- push ds ; needed by errmsg() below
- mov bx, dx ; ES:BX points to storage area
- mov dx, [es:bx]
- mov ds, [es:bx+2]
- call setvect ; DS:DX = address of old handler
- pop ds
-
- ; Release memory occupied by TSR. NB: ES already holds segment
- ; address of resident data and code based on calling getvect().
- mov ah, 49h
- int DOS
- pop es
- jnc SHORT @@FlagNoError ; continue if no error
- mov al, ERRNYI ; else flag error
- mov dx, OFFSET ErrMsgMem ; and point to error message
- jmp SHORT @@Fin
-
- @@FlagNoError:
- Zero al ; indicate no error
- mov dx, OFFSET RemoveMsg
-
- @@Fin:
- call errmsg ; display message
- ret
- ENDP uninstall_TSR
-
-
- ;---- list_CmdLines -----------------------------------------------------;
- ; Purpose: Lists commandlines in recall buffer. ;
- ; Notes: none ;
- ; Entry: AL = interrupt vector in use. ;
- ; Exit: AL = 0 if successful; ERRNYI otherwise. ;
- ; Calls: check_ifInstalled, getvect, errmsg ;
- ; Changes: AL, BX, CX, DX, DI ;
- ;--------------------------------------------------------------------------;
- PROC list_CmdLines
-
- call check_ifInstalled
- jnz SHORT @@Abort ; not installed
-
- ; Locate segment controlled by resident code. Note I already
- ; know my ISR has been installed.
- push ds es
- call getvect ; returns ES:BX
- mov bx, es
- mov ds, bx
-
- ; Point to start of 1st complete command in recall buffer. NB:
- ; There will always be at least one command - "recall -l".
- mov al, CR
- mov cx, BUFSIZE
- mov di, OFFSET RecallBuf
- repne scasb ; uses ES:DI
-
- ; Display rest of buffer.
- mov ah, 2 ; DOS subfunction to display char
- @@NextChar:
- mov dl, [di] ; get char
- inc di
- int DOS ; display it
- cmp dl, CR ; need to display CR/LF?
- loopne SHORT @@NextChar ; always decrements CX
- mov dl, LF ; display LF now
- int DOS
- or cx, cx ; done yet?
- jnz SHORT @@NextChar
-
- pop es ds
- Zero al ; flag no error
- jmp SHORT @@Fin
-
- @@Abort:
- mov dx, OFFSET ErrMsgNYI
- call errmsg
- mov al, ERRNYI
-
- @@Fin:
- ret
- ENDP list_CmdLines
-
-
- ;---- skip_Spaces -------------------------------------------------------;
- ; Purpose: Skips past spaces in a string. ;
- ; Notes: Scanning stops with either a non-space *OR* CX = 0. ;
- ; Entry: DS:SI = start of string to scan. ;
- ; Exit: AL = next non-space character, ;
- ; CX is adjusted as necessary, ;
- ; DS:SI = pointer to next non-space. ;
- ; Calls: none ;
- ; Changes: AL, CX, SI ;
- ;--------------------------------------------------------------------------;
- PROC skip_Spaces
-
- jcxz SHORT @@Fin
- @@NextCh:
- lodsb
- cmp al, ' '
- loopz @@NextCh
- jz SHORT @@Fin ; CX = 0; don't adjust
-
- inc cx ; adjust counters if cx > 0
- dec si
-
- @@Fin:
- ret
- ENDP skip_Spaces
-
-
- ;---- get_Opt -----------------------------------------------------------;
- ; Purpose: Get a commandline option. ;
- ; Notes: none ;
- ; Entry: AL = option character, ;
- ; Exit: n/a ;
- ; Calls: tolower, errmsg ;
- ; Changes: AX, DX, [OptCh], [HFlag], [IFlag], [LFlag], [RFlag] ;
- ;--------------------------------------------------------------------------;
- PROC get_Opt
-
- mov [OptCh], al ; save for later
- call tolower ; use only lowercase in cmp.
- cmp al, 'i'
- jz SHORT @@OptI
- cmp al, 'l'
- jz SHORT @@OptL
- cmp al, 'r'
- jz SHORT @@OptR
- cmp al, '?'
- jz SHORT @@OptH
- mov dx, OFFSET ErrMsgOpt ; unrecognized option
- call errmsg ; then *** DROP THRU *** to OptH
-
- ; Various possible options.
- @@OptH:
- mov [HFlag], ON ; set help flag
- jmp SHORT @@Fin
-
- @@OptI:
- mov [IFlag], ON ; install in memory
- jmp SHORT @@Fin
-
- @@OptL:
- mov [LFlag], ON ; list cmds in recall buffer
- jmp SHORT @@Fin
-
- @@OptR:
- mov [RFlag], ON ; remove from memory
-
- @@Fin:
- ret
- ENDP get_Opt
-
-
- ;---- process_CmdLine ---------------------------------------------------;
- ; Purpose: Processes commandline arguments. ;
- ; Notes: A switch character by itself is ignored. ;
- ; Entry: n/a ;
- ; Exit: n/a ;
- ; Calls: skip_Spaces, get_Opt ;
- ; Changes: AX, CX, SI, ;
- ; DX, [OptCh], [HFlag], [IFlag], [LFlag], [RFlag] (get_Opt) ;
- ; Direction flag is cleared. ;
- ;--------------------------------------------------------------------------;
- PROC process_CmdLine
-
- cld ; forward, march!
- Zero ch, ch
- mov cl, [CmdLen] ; length of commandline
- mov si, OFFSET CmdLine ; offset to start of commandline
-
- call skip_Spaces ; check if any args supplied
- or cl, cl
- jnz SHORT @@ArgLoop
-
- mov [HFlag], ON ; assume user needs help
- jmp SHORT @@Fin
-
- ; For each blank-delineated argument on the commandline...
- @@ArgLoop:
- lodsb ; next character
- dec cl
- cmp al, [SwitCh] ; is it the switch character?
- jnz SHORT @@NonOpt ; no
-
- ; Isolate each option and process it. Stop when a space is reached.
- @@OptLoop:
- jcxz SHORT @@Fin ; abort if nothing left
- lodsb
- dec cl
- cmp al, ' '
- jz SHORT @@NextArg ; abort when space reached
- call get_Opt
- jmp @@OptLoop
-
- ; Any argument which is *not* an option is invalid. Set help flag and abort.
- @@NonOpt:
- mov [HFlag], ON
- jmp SHORT @@Fin
-
- ; Skip over spaces until next argument is reached.
- @@NextArg:
- call skip_Spaces
- or cl, cl
- jnz @@ArgLoop
-
- @@Fin:
- ret
- ENDP process_CmdLine
-
-
- ;---- main --------------------------------------------------------------;
- ; Purpose: Main section of program. ;
- ; Notes: none ;
- ; Entry: Arguments as desired ;
- ; Exit: Return code as follows: ;
- ; 0 => program ran successfully, ;
- ; ERRH => on-line help supplied, ;
- ; ERRINS => program could not be installed, ;
- ; ERRNYI => program was not yet installed. ;
- ; Calls: process_CmdLine, fputs, list_CmdLines, install_TSR, ;
- ; uninstall_TSR ;
- ; Changes: n/a ;
- ;--------------------------------------------------------------------------;
- main:
-
- ; Process commandline arguments.
- call process_CmdLine ; process commandline args
- mov al, 21h ; vector to hook
- mov dx, OFFSET OldInt21 ; storage for old intr handler
- cmp [IFlag], ON ; install it?
- je SHORT @@Install
- cmp [LFlag], ON ; list commands?
- je SHORT @@List
- cmp [RFlag], ON ; remove it?
- je SHORT @@Remove
- mov bx, STDERR ; user must need help
- mov dx, OFFSET HelpMsg
- call fputs
- mov al, ERRH
- jmp SHORT @@Fin
-
- @@List:
- call list_CmdLines
- jmp SHORT @@Fin
-
- @@Install:
- call install_TSR
- jmp SHORT @@Fin
-
- @@Remove:
- call uninstall_TSR
-
- ; Terminate the program using as return code what's in AL.
- @@Fin:
- mov ah, 4ch
- int DOS
- EVEN
- ;-------------------------------------------------------------------------;
- ; Purpose: Writes an ASCIIZ string to specified device.
- ; Notes: A zero-length string doesn't seem to cause problems when
- ; this output function is used.
- ; Requires: 8086-class CPU and DOS v2.0 or better.
- ; Entry: BX = device handle,
- ; DS:DX = pointer to string.
- ; Exit: Carry flag set if EOS wasn't found or handle is invalid.
- ; Calls: strlen
- ; Changes: none
- ;-------------------------------------------------------------------------;
- PROC fputs
-
- IF @DataSize EQ 0
- Push_M <ax, cx, di>
- ELSE
- Push_M <ax, cx, di, es>
- mov ax, ds
- mov es, ax
- ENDIF
- mov di, dx
- call strlen ; set CX = length of string
- jc SHORT @@Fin ; abort if problem finding end
- mov ah, 40h ; MS-DOS raw output function
- int DOS
- @@Fin:
- IF @DataSize EQ 0
- Pop_M <di, cx, ax>
- ELSE
- Pop_M <es, di, cx, ax>
- ENDIF
- ret
-
- ENDP fputs
-
-
- EVEN
- ;-------------------------------------------------------------------------;
- ; Purpose: Writes an error message to stderr.
- ; Notes: none
- ; Requires: 8086-class CPU and DOS v2.0 or better.
- ; Entry: DS:DX = pointer to error message.
- ; Exit: n/a
- ; Calls: fputs
- ; Changes: none
- ;-------------------------------------------------------------------------;
- PROC errmsg
-
- Push_M <bx, dx>
- push dx ; save again calling parameters
- mov bx, STDERR
- mov dx, OFFSET ProgName ; display program name
- call fputs
- pop dx ; recover calling parameters
- call fputs ; display error message
- mov dx, OFFSET EOL
- call fputs
- Pop_M <dx, bx>
- ret
-
- ENDP errmsg
-
-
- EVEN
- ;--------------------------------------------------------------------------;
- ; Purpose: Gets address of an interrupt handler.
- ; Notes: none
- ; Requires: 8086-class CPU and DOS v2.0 or better.
- ; Entry: AL = interrupt of interest.
- ; Exit: ES:BX = address of current interrupt handler
- ; Calls: none
- ; Changes: ES:BX
- ;--------------------------------------------------------------------------;
- PROC getvect
-
- push ax
- mov ah, 35h ; find address of handler
- int DOS ; returned in ES:BX
- pop ax
- ret
- ENDP getvect
-
-
- ;--------------------------------------------------------------------------;
- ; Purpose: Sets an interrupt vector.
- ; Notes: none
- ; Requires: 8086-class CPU and DOS v1.0 or better.
- ; Entry: AL = interrupt of interest,
- ; DS:DX = address of new interrupt handler
- ; Exit: n/a
- ; Calls: none
- ; Changes: none
- ;--------------------------------------------------------------------------;
- PROC setvect
-
- push ax
- mov ah, 25h ; set address of handler
- int DOS
- pop ax
- ret
- ENDP setvect
-
-
- EVEN
- ;-------------------------------------------------------------------------;
- ; Purpose: Converts character to lowercase.
- ; Notes: none
- ; Requires: 8086-class CPU.
- ; Entry: AL = character to be converted.
- ; Exit: AL = converted character.
- ; Calls: none
- ; Changes: AL
- ; flags
- ;-------------------------------------------------------------------------;
- PROC tolower
-
- cmp al, 'A' ; if < 'A' then done
- jb SHORT @@Fin
- cmp al, 'Z' ; if > 'Z' then done
- ja SHORT @@Fin
- or al, 20h ; make it lowercase
- @@Fin:
- ret
-
- ENDP tolower
-
-
- EVEN
- ;-------------------------------------------------------------------------;
- ; Purpose: Calculates length of an ASCIIZ string.
- ; Notes: Terminal char is _not_ included in the count.
- ; Requires: 8086-class CPU.
- ; Entry: ES:DI = pointer to string.
- ; Exit: CX = length of string,
- ; cf = 0 and zf = 1 if EOS found,
- ; cf = 1 and zf = 0 if EOS not found within segment.
- ; Calls: none
- ; Changes: CX,
- ; flags
- ;-------------------------------------------------------------------------;
- PROC strlen
-
- Push_M <ax, di, flags>
- cld ; scan forward only
- mov al, EOS ; character to search for
- mov cx, di ; where are we now
- not cx ; what's left in segment - 1
- push cx ; save char count
- repne scasb
- je SHORT @@Done
- scasb ; test final char
- dec cx ; avoids trouble with "not" below
-
- @@Done:
- pop ax ; get original count
- sub cx, ax ; subtract current count
- not cx ; and invert it
- popf ; restore df
- dec di
- cmp [BYTE PTR es:di], EOS
- je SHORT @@Fin ; cf = 0 if equal
- stc ; set cf => error
-
- @@Fin:
- Pop_M <di, ax>
- ret
-
- ENDP strlen
-
-
- EVEN
- ;--------------------------------------------------------------------------;
- ; Purpose: Compares two ASCIIZ strings.
- ; Notes: none
- ; Requires: 8086-class CPU.
- ; Entry: DS:SI = start of 1st string,
- ; ES:DI = start of 2nd string.
- ; Exit: zf = 1 if equal.
- ; cf = 1 if EOS not found within segment.
- ; Calls: strlen
- ; Changes: flags
- ;--------------------------------------------------------------------------;
- PROC strcmp
-
- Push_M <ax, cx, di, si>
- call strlen ; get length on 1 of the strings
- jc SHORT @@Fin ; error
- inc cx ; account for EOS
-
- ; There will always be at least one char in each string
- ; to compare - the terminal null.
- pushf ; save direction flag
- cld
- repe cmpsb ; compare both strings
- popf ; recover direction flag
- dec di
- dec si
- cmpsb ; set flags based on final char
-
- @@Fin:
- Pop_M <si, di, cx, ax>
- ret
- ENDP strcmp
-
-
- END
-