Category : Utilities for DOS and Windows Machines
Archive   : DE.ZIP
Filename : DE.ASM

 
Output of file : DE.ASM contained in archive : DE.ZIP

DE_INSTALL equ 1 ; Un-comment to allow de-installation

;
; DE.ASM - DOS command-line editor by Paul Ketrick
;
; You may do whatever you want with this program; I only require that my
; name be left at the top of the source code and at the end of the
; sign-on prompt if you use or modify any of this code.
;
; Version 1.3
;
; Program features:
;
; 1. Allows editing of text entered through DOS line-input function call
; using following keys:
; a. Left/right arrows move cursor left/right one character.
; b. Home/end keys move cursor to start/end of typed line.
; c. Ctrl+Left/right arrows move back/forward one word.
; d. Backspace deletes the character before the cursor.
; e. Delete key deletes the character under the cursor.
; f. Ctrl+W or Ctrl+Backspace deletes the word before the cursor.
; g. Ctrl+T deletes the word under the cursor.
; h. Esc, Ctrl+U or Ctrl+X erases the entire typed line.
; i. Insert key toggles insert mode (whose default state is set by
; a command-line parameter).
; j. Ctrl+Return performs expansion of aliases and "repeat" commands
; (see #3 and #4 below), displays the command line after expan-
; sion and continues editing.
;
; 2. The up/down arrows can be used to scroll backward/forward through
; previously entered text lines for automatic re-entering or editing of
; previous commands.
;
; 3. The editor expands the following metasequences when they are found
; anywhere in the text line:
; a. !$ expands to the last word of the previous command line
; b. !* expands to the 2nd+ words of the previous command line
; c. !-N expands to the Nth previous command line
; d. !-N$ expands to the last word of the Nth previous command line
; e. !-N* expands to the 2nd+ words of the previous command line
; f. !X expands to the last command line beginning with X, where X

; is any string (may not contain spaces, $, ^ or * characters).
; g. !X$ and !X* are also expanded correctly.
; i. !! expands to the last command line entered.
; j. !!$ and !!* are also expanded correctly.
;
; 4. Can expand "aliases," or command abbreviations, to their full
; equivalents, based on an "alias list" file (see below).
;
; Command-line usage for loading DE:
;
; DE [/D] [/Bn] [/I] [/R] [filename]
;
; /D is optional and removes the Dos Editor from memory (de-installs it),
; if it has already been installed.
;
; /Bn is optional and specifies the size of the previous-command buffer,
; where "n" is the number of kBytes the buffer is to use. Default size
; is 2Kbytes.
;
; /I is optional and causes the insert mode to initially be set to ON
; whenever a line of text is to be entered.
;
; /R is optional and causes the insert mode to initially be set to OFF
; (replace mode, hence the R) whenever a line of text is to be entered.
;
; "filename" specifies the drive, path and filename of the alias list file.
; If omitted, no alias file will be loaded. The alias file contains
; text lines, each terminated by a CR/LF combination, such as in the
; following example:
;
; c cd\c
; asm cd\asm
; backup copy *.asm b:/v
;
; Using the above alias list, when you type in "C" and press Return,
; the Dos Editor will substitute the line "cd\c"; likewise, it will
; substitute "copy *.asm b:/v" whenever you type "backup". Note that
; the alias must be typed in by itself; that is, typing "c\lib" would
; not cause the Editor to substitute "cd\c\lib". Aliases may be
; entered in upper or lower case.
;

; PC Keyboard key definitions:

RETURN equ 13 ; Return key
CRETURN equ 10 ; Control+Return
BS equ 8 ; Backspace
CBS equ 127 ; Control+Backspace
LEFT equ 4B00H ; Left arrow
RIGHT equ 4D00H
UP equ 4800H
DOWN equ 5000H
CLEFT equ 7300H ; Ctrl-Left Arrow
CRIGHT equ 7400H ; Ctrl-Right Arrow
HOME equ 4700H
ENDKEY equ 4F00H
DELETE equ 5300H
INSERT equ 5200H
ESC equ 27
F3 equ 3D00H ; F3 key
CTRLT equ 20 ; Ctrl-T
CTRLU equ 21 ; Ctrl-U
CTRLW equ 23 ; Ctrl-W
CTRLX equ 24 ; Ctrl-X

; Declare all segments first -- this is done so that references to ENDSEG
; which occur in the code segment are not forward references which can cause
; the famous "phase error between passes." (Thanks to David Cherin for
; solving that one).

DATA segment para public 'DATA'
DATA ends

CODE segment para public 'CODE'
CODE ends

ENDSEG segment ; Marks end of program -- used by TSR
ENDSEG ends ; code to find size of program

DATA segment para public 'DATA'

cmdline db 128 dup(0) ; Local copy of command line passed
; to DE
cmdptr dw offset DATA:cmdline ; Used in scanning command line

inbuf db 260 dup(0) ; Local text input buffer
psp_seg dw 0 ; DE's PSP segment
par_sav dw 0 ; # paragraphs this program needs
; DOS to keep

max_in dw 0 ; Max # of input characters
ntyped dw 0 ; # characters actually typed
bufptr dw 0 ; Cursor position in "inbuf"
bufend dw 0 ; Last typed char + 1 in "inbuf"
tempptr dw 0 ; Used by move_csr
mStart dw 0 ; Start of metasequence in inbuf
xStart dw 0 ; Start of "x" search string
wordPtr dw 0 ; Used by expandMeta
inWord db 0 ; Used by expandMeta
nPrev dw 0 ; Used by expandMeta

buf_seg dw 0 ; Input buffer defined by
buf_off dw 0 ; INT 21H caller

start_x db 0 ; Cursor X & Y of start of
start_y db 0 ; input field
cur_x db 0 ; Current cursor X & Y
cur_y db 0
temp_x db 0 ; Temporary variable
temp_y db 0 ; Temporary variable
max_x db 80 ; Total # of columns on screen

csr_st db 0 ; Cursor starting scan line
csr_end db 0 ; Cursor ending scan line

cb_seg dw 0 ; Previous-command buffer segment
cb_size dw 2048 ; Previous-command buffer size in
; bytes--2K default
cb_in dw 0 ; "In" pointer
cb_out dw 0 ; "Out" pointer
cb_view dw 0 ; Current command being viewed
ins_def db 1 ; Insert mode default status
ins_mod db 0 ; Insert mode current status

min_cmd dw 1 ; Length of smallest command to
; save in previous-command buffer

al_seg dw 0 ; Alias list segment
al_size dw 0 ; Size in bytes of alias list
al_fd dw 0 ; File descriptor of alias list

DATA ends

CODE segment para public 'CODE'

public new_i21, entry ; Public for debugging
public load_alias, TSRCutoff
public ProgramEnd

assume cs:CODE

old_i21 dw 2 dup(0) ; This resides in the code segment so
; no funky code is needed to load DS
; in "new_i21"

sig db 'PKDOSEDIT1.01' ; Signature showing this program
; is installed -- see de_inst
siglen equ $-sig ; Length of signature

new_i21 proc far ; Our new INT 21H handler

assume ds:NOTHING, es:NOTHING

cmp ah, 10 ; "Line input" function call?
je new_edit ; If so, use new line editing handler
jmp dword ptr cs:[old_i21] ; Use old DOS call handler for
; everything else

new_edit: ; AH=10 (0AH),
; DS:DX=Input buffer:
; Byte 0 = Size of buffer
; Byte 1 = # Chars actually typed
; Bytes 2+ = Text buffer
push ax
push bx
push cx
push dx
push si
push di
push es
mov ax, DATA
mov es, ax
assume es:DATA

mov es:[buf_seg], ds ; Store caller-defined input buffer
mov es:[buf_off], dx
mov bx, dx
mov cl, [bx] ; Get size of buffer (includes CR)
or cl, cl
jnz edit0a

jmp edit_exit ; Buffer size of 0 is invalid

edit0a:
dec cl ; Buf size - 1 = max # input chars
xor ch, ch
mov es:[max_in], cx ; Save in local variable

push ds
mov ax, DATA
mov ds, ax ; Use our data segment
assume ds:DATA

ifdef DE_INSTALL ; Include de-install code?

cmp buf_seg, 0FFFFH ; Are we being asked to de-install?
jne edit0 ; If not, go do line-input stuff
cmp buf_off, 0000H ; Well, are we, punk?
jne edit0 ; If not, do line-input

call de_inst ; De-install this program

mov es, psp_seg
mov ah, 49H
int 21H ; De-allocate program segment--NOTE:
; this can crash multi-tasking systems,
; because we're now executing code
; in a freed memory block! Oh well...
jmp edit_exit ; Get outta here

endif ; DE_INSTALL

edit0: ; Begin line-input procedures here
call get_csr ; Get current cursor X & Y coord's
mov start_x, dl ; Save 'em
mov start_y, dh
mov cur_x, al ; Set current cursor X & Y coordinates
mov cur_y, ah
mov csr_st, ch ; Save cursor start scan line
mov csr_end, cl ; Save cursor end scan line

mov ah, 0FH ; Get current video mode--puts
int 10H ; # display columns in AH
mov max_x, ah

mov al, ins_def ; Get default insert-mode status
call set_ins ; Set current insert-mode status

; The input text will initially be stored in the local buffer
; "inbuf"; it gets copied to the buffer specified by the calling
; program when Return is pressed.

mov bx, offset DATA:inbuf ; Addr of start of local input buffer
mov bufptr, bx ; Set addr of cursor in buffer
mov bufend, bx ; Set addr of last character in buffer
mov ntyped, 0 ; Clear # of characters typed
mov bx, cb_in ; Addr of last command in previous-
; command buffer + 1
mov cb_view, bx ; Set addr of current "previous cmd"
; being viewed (see edit_up, edit_dn)

edit1: ; Main keyboard input loop here

mov bx, bufptr ; Get position of cursor in buffer
call move_csr ; Move physical cursor there

call get_key ; Wait for a keystroke

cmp al, RETURN ; Return key - return input line
je edit1a
cmp al, BS ; Backspace
je edit1b
cmp ax, LEFT ; Move cursor left one character
je edit1c
cmp ax, RIGHT ; Move cursor right one character
je edit1d
cmp ax, DELETE ; Delete character under cursor
je edit1e
cmp ax, HOME ; Move to beginning of line
je edit1f
cmp ax, ENDKEY ; Move to end of line
je edit1g
cmp ax, ESC ; Delete entire line
je edit1h
cmp ax, CTRLU ; Delete entire line
je edit1h
cmp ax, CTRLX ; Delete entire line
je edit1h
cmp ax, UP ; Move to previous command
je edit1i
cmp ax, DOWN ; Move to next command
je edit1j
cmp ax, CLEFT ; Move left one word
je edit1k
cmp ax, CRIGHT ; Move right one word
je edit1l
cmp ax, CTRLW ; Delete left one word
je edit1n
cmp ax, CBS ; Delete left one word
je edit1n
cmp ax, CTRLT ; Delete right one word
je edit1o
cmp ax, F3 ; Like DOS--re-enter last command
je edit1m
cmp ax, INSERT ; Toggle insert mode on/off
je edit1p
cmp ax, CRETURN ; Perform substitutions & continue
je edit1q
jmp text ; Insert typed character in buffer

edit1a: jmp edit_ret
edit1b: jmp edit_bs
edit1c: jmp edit_lt
edit1d: jmp edit_rt
edit1e: jmp edit_del
edit1f: jmp edit_hm
edit1g: jmp edit_end
edit1h: jmp edit_esc
edit1i: jmp edit_up
edit1j: jmp edit_dn
edit1k: jmp edit_lw
edit1l: jmp edit_rw
edit1m: jmp edit_f3
edit1n: jmp edit_dwl
edit1o: jmp edit_dwr
edit1p: jmp edit_ins
edit1q: jmp edit_sub

text: ; A text character was typed
cmp ins_mod, 0 ; Are we in insert mode?
jne text_ins ; If so, go insert the character
mov bx, bufptr ; We're in replace mode--delete
cmp bx, bufend ; character under cursor, if there
je text_ins ; is one
push ax ; Save typed character
call del_char
pop ax

text_ins:
call ins_char ; Insert AL @ cursor position
; bx=bufptr from ins_char
jc text0 ; CY=1 if couldn't insert

inc bx
mov bufptr, bx ; Set new cursor pointer
dec bx
call draw_eol ; Redraw from BX to end of line
jmp edit1 ; Wait for next keystroke

text0: jmp edit1 ; Ignore typed text if no room

edit_sub:
mov bx, bufend ; Addr of last typed character in
; buffer + 1
mov byte ptr [bx], 0 ; Terminate local buffer for
; expandText
call expandText ; Convert aliases to desired text --
; also handes !-metachar expansions
; and draws converted text line

jmp edit1 ; Continue editing

edit_ret: ; Return key was hit
call reset_csr ; Restore old cursor type
mov bx, bufend ; Addr of last typed character in
; buffer + 1
mov byte ptr [bx], 0 ; Terminate local buffer for
; expandText
call expandText ; Convert aliases to desired text --
; also handes !-metachar expansions
; and draws converted line

call store_cmd ; Store typed line in circular
; command buffer

mov bx, bufend ; Addr of last char in buffer + 1
call move_csr ; Move cursor there
mov al, 13
call prt_chr ; Move to beginning of last line

mov es, buf_seg ; Get pointer to calling program-
mov di, buf_off ; defined buffer
add di, 2 ; Point to first text char
mov cx, bufend ; Addr of last typed char + 1
mov si, offset DATA:inbuf ; Addr of first typed char
sub cx, si ; Get # of typed characters
jz edit_r1 ; If no characters were typed
push cx
cld
rep movsb ; Copy text to calling prog's buffer
pop cx

edit_r1:mov al, 13 ; Terminate w/CR
stosb
mov di, buf_off ; Pointer to caller's buffer
mov es:[di+1], cl ; Store # chars typed in 2nd byte

edit_exit: ; Restore registers and ...
pop ds ; get outta here!
pop es
pop di
pop si
pop dx
pop cx
pop bx
pop ax
clc ; Indicate "no error" to caller
ret 2 ; Pop old flags off stack

edit_lt: ; Move one char to the left
mov bx, bufptr
cmp bx, offset DATA:inbuf ; Is cursor at start of buffer?
ja edit_l1
jmp edit1 ; If so, do nothing

edit_l1:dec bx
mov bufptr, bx ; We need only adjust bufptr--
jmp edit1 ; edit1 will move physical cursor

edit_rt: ; Move one char to the right
mov bx, bufptr
cmp bx, bufend ; Are we at last char in buffer?
jb edit_rt1
jmp edit1 ; If so, do nothing

edit_rt1:
inc bx
mov bufptr, bx
jmp edit1

edit_lw: ; Move left one word
mov bx, bufptr
mov di, offset DATA:inbuf ; Beginning of text buffer
cmp bx, di
je edit_lw0 ; If we're already at start of buffer

edit_lw1:
dec bx ; Back up a character
cmp bx, di
je edit_lw0
call chk_word ; Sets CY if character at [BX] is
; part of a word
jnc edit_lw1 ; If word delimiter, keep looking

edit_lw2:
dec bx ; Back up a character
cmp bx, di ; At start of buffer?
je edit_lw0 ; If so, stop
call chk_word ; Part of a word?
jc edit_lw2 ; If so, keep looking
inc bx ; Point to 1st character of word

edit_lw0:
mov bufptr, bx
jmp edit1

edit_rw: ; Move right one word
mov bx, bufptr
mov di, bufend ; Last char in buffer + 1
cmp bx, di ; Is cursor at end of buffer?
je edit_rw0 ; If so, do nothing

edit_rw1:
call chk_word ; Is char at [BX] part of a word?
jnc edit_rw2 ; If not, go to 2nd loop
inc bx
cmp bx, di ; At end of buffer?
jb edit_rw1 ; Keep looking if not
jmp short edit_rw0

edit_rw2:
call chk_word ; Part of a word?
jc edit_rw0 ; If so, we've found next word
inc bx
cmp bx, di ; End of buffer?
jb edit_rw2

edit_rw0:
mov bufptr, bx
jmp edit1

edit_hm: ; Move to beginning of text buffer
mov bx, offset DATA:inbuf
mov bufptr, bx
jmp edit1

edit_end: ; Move to end of typed text
mov bx, bufend
mov bufptr,bx
jmp edit1

edit_esc:
call era_text ; Erase all text typed in on the line
mov bx, offset DATA:inbuf ; Reset everything to start of buffer:
mov bufptr, bx
mov bufend, bx
mov ntyped, 0 ; Clear # typed characters
jmp edit1

edit_del: ; Delete character under cursor
mov bx, bufptr
cmp bx, bufend ; Do we have something to delete?
jb edit_b2 ; If so, go for it
jmp edit1 ; If not, bail out

edit_bs: ; Delete character before cursor
mov bx, bufptr
cmp bx, offset DATA:inbuf ; Is there a char before cursor?
ja edit_b1 ; If ja, do it
jmp edit1

edit_b1:dec bx ; Back cursor up one space--
mov bufptr, bx ; code below deletes char at cursor

edit_b2: ; Delete character @ cursor
mov bx, bufend ; Find cursor y/x coordinates after
call move_csr ; last character in buffer
mov dl, cur_x ; Get coordinates in DL/DH
mov dh, cur_y
push dx ; Save for later
call del_char ; Delete character @ cursor
mov bx, bufptr
push bx
call move_csr ; Move physical cursor to correct spot
pop bx
call draw_eol ; Redraw to end of typed line
mov bx, bufend
call move_csr ; Move cursor to end of typed line
pop dx ; We'll now erase to previous "end of
; typed line"
xchg dl, cur_x
xchg dh, cur_y ; Old EOL in cur_y/cur_x, new in DH/DL
call era_t0 ; Erase from DH/DL to end of line
; given by cur_y/cur_x
jmp edit1

edit_up: ; Up arrow: get previous command
call prv_cmd ; Move "cb_view" back one command
jnc edit_u1 ; CY=no previous command in buffer
jmp edit1

edit_u1:call era_text ; Erase current command from screen
mov di, offset DATA:inbuf ; Point to start of input buffer
mov bx, cb_view ; Pointer to the previous command

edit_u3:mov al, es:[bx] ; Copy previous command to inbuf
cmp al, 13 ; End of the line?
je edit_u4
mov [di],al
inc bx
call chk_cb ; Wrap BX around if necessary
inc di
jmp short edit_u3

edit_u4:mov bufend, di ; Store pertinent variables:
mov bufptr, di
sub di, offset DATA:inbuf
mov ntyped, di

edit_u5:
mov bx, offset DATA:inbuf
call move_csr ; Move cursor to start of line
mov bx, offset DATA:inbuf
call draw_eol ; Print new command on screen
jmp edit1 ; edit1 will move physical cursor
; to end of line

edit_dn: ; Down arrow: get next command
mov es, cb_seg
mov bx, cb_view ; Get pointer to start of the current
; previous command being viewed
cmp bx, cb_in ; Is it last command in buffer?
jne edit_d0
jmp edit1 ; If so, we can't move down

edit_d0:cmp byte ptr es:[bx], 13 ; End of the next line?
je edit_d1
inc bx ; Back up a character
call chk_cb ; Adjust pointer if necessary
cmp bx, cb_in ; At end of circular buffer?
jne edit_d0 ; If not, keep searching
jmp edit1 ; There is no next command

edit_d1:
inc bx ; Move to start of next line
call chk_cb ; Adjust pointer if necessary
cmp bx, cb_in
jne edit_d2
jmp edit1 ; There is no next command

edit_d2:mov cb_view, bx ; Set pointer
call era_text ; Erase current command from screen
mov di, offset DATA:inbuf
mov bx, cb_view

edit_d3:mov al, es:[bx] ; Copy command from buffer to "inbuf"
cmp al, 13
je edit_d4
mov [di],al
inc bx
call chk_cb
inc di
jmp short edit_d3

edit_d4:mov bufend, di ; Store pertinent information:
mov bufptr, di
sub di, offset DATA:inbuf
mov ntyped, di

jmp edit_u5

edit_f3: ; Re-enter last command--same as DOS
mov bx, cb_in ; Reset "cb_view" to point to after
mov cb_view, bx ; the last previous command in buffer
call prv_cmd
jnc edit_f3b

edit_f3a:
jmp edit1 ; No previous command

edit_f3b:
call era_text

mov cx, bufptr ; Compute # chars from start of
mov bufend, cx
sub cx, offset DATA:inbuf ; command line to cursor
mov bx, cb_view
jcxz edit_f3d

edit_f3c:
mov al, [bx]
cmp al, 13 ; CR?
je edit_f3e
inc bx
call chk_cb
loop edit_f3c

edit_f3d:
mov al, es:[bx]
cmp al, 13
je edit_f3e

push bx
call ins_char
pop bx
jc edit_f3e

inc bufptr
inc bx
call chk_cb

jmp short edit_f3d

edit_f3e:
jmp edit_u5

edit_dwl: ; Delete word before cursor
mov bx, bufend ; Find cursor y/x coordinates of
call move_csr ; last character in buffer + 1
mov dl, cur_x
mov dh, cur_y
push dx ; Save for later

edit_dl1:
mov bx, bufptr
cmp bx, offset DATA:inbuf ; Anything before cursor to delete?
je edit_dl0 ; If not, skip town
dec bx ; Back up a character
mov bufptr, bx
call chk_word ; Part of a word?
jc edit_dl2 ; If yes, go to next loop
call del_char ; Waste it
jmp short edit_dl1 ; Keep going

edit_dl2:
call chk_word ; Part of a word?
jnc edit_dl3 ; If not, stop
call del_char ; Otherwise, waste it and move on
mov bx, bufptr
cmp bx, offset DATA:inbuf
je edit_dl0
dec bx
mov bufptr, bx
jmp short edit_dl2

edit_dl3:
inc bx ; Move back to 1st character of word
mov bufptr, bx

edit_dl0:
mov bx, bufptr
push bx
call move_csr ; Move physical cursor to correct spot
pop bx
call draw_eol ; Redraw to end of line
mov bx, bufend
call move_csr ; Move cursor to end of line
pop dx ; Now we'll space over old text @
; end of line
xchg dl, cur_x
xchg dh, cur_y ; Old EOL in cur_y/cur_x, new in DH/DL
call era_t0 ; Erase from DH/DL to end of line
; given by cur_y/cur_x
jmp edit1

edit_dwr: ; Delete word at cursor
mov bx, bufend ; Find cursor y/x coordinates of
call move_csr ; last character in buffer + 1
mov dl, cur_x
mov dh, cur_y
push dx ; Save for later

edit_dr1:
mov bx, bufptr
cmp bx, bufend
je edit_dr0
call chk_word
jnc edit_dr2
call del_char
jmp short edit_dr1

edit_dr2:
mov bx, bufptr
cmp bx, bufend
je edit_dr0
call chk_word
jc edit_dr0
call del_char
jmp short edit_dr2

edit_dr0:
mov bx, bufptr
push bx
call move_csr ; Move physical cursor to correct spot
pop bx
call draw_eol
mov bx, bufend
call move_csr
pop dx
xchg dl, cur_x
xchg dh, cur_y ; Old EOL in cur_y/cur_x, new in DH/DL
call era_t0 ; Erase from DH/DL to end of line
; given by cur_y/cur_x
jmp edit1

edit_ins:
mov al, ins_mod
or al, al
mov al, 0
jnz edit_in1
mov al, 1

edit_in1:
call set_ins ; Set insert mode
jmp edit1

new_i21 endp

set_ins proc near ; Set insert mode to AL

mov ins_mod, al
mov cl, csr_end
mov ch, cl ; End line --> start line
dec ch
or al, al
jz set_in1
sub ch, 3

set_in1:call set_ctyp ; Set cursor start/stop line to CH/CL
ret

set_ins endp

reset_csr proc near ; Reset cursor type to the way it
; was before we started
mov ch, csr_st
mov cl, csr_end
call set_ctyp
ret

reset_csr endp

prv_cmd proc near ; Move "cb_view" to previous command

mov es, cb_seg
mov bx, cb_view ; Get pointer to start of the current
; previous command being viewed
cmp bx, cb_out ; Is it 1st command in buffer?
jne prv_c1

prv_c0: stc ; Indicate no previous command
ret

prv_c1: dec bx ; Back up to CR ending previous line
call chk_cb ; Adjust pointer if necessary
cmp bx, cb_out
je prv_c0

prv_c2: dec bx ; Back up a character
call chk_cb ; Adjust pointer if necessary
cmp bx, cb_out
je prv_c3
cmp byte ptr es:[bx], 13 ; End of the previous line?
jne prv_c2

inc bx ; Move to start of this line
call chk_cb ; Adjust pointer if necessary

prv_c3: mov cb_view, bx
clc
ret

prv_cmd endp

chk_cb proc near ; Make sure BX points to within
; previous-command buffer
cmp bx, -1 ; Less than zero?
jne chk_cb1
mov bx, cb_size ; Wrap backwards to end of buffer
dec bx ; minus one
ret

chk_cb1:cmp bx, cb_size ; >= buffer size?
jb chk_cb2
xor bx, bx ; If so, wrap around to zero

chk_cb2:ret

chk_cb endp

store_cmd proc near ; Store command in "inbuf" in
; circular previous-command buffer
mov cx, bufend
sub cx, offset DATA:inbuf ; # chars in buffer in CX
cmp cx, min_cmd ; Too small to bother saving?
jnc store_c1
ret

store_c1:
add cx, 2 ; Just to be safe, add 2 bytes
call make_room ; Make sure there are CX bytes
; free in buffer
mov bx, cb_in ; "In" pointer to prev-cmd buffer
mov es, cb_seg ; Prev-cmd buffer segment #
mov si, offset DATA:inbuf

store_c2:
cmp si, bufend ; Have we stored entire command?
jnc store_c3 ; If so, break out of loop
mov al, [si]
mov es:[bx], al ; Copy one char to prev-cmd buffer
inc bx
call chk_cb ; Wrap BX around if needed
inc si
jmp short store_c2

store_c3:
mov byte ptr es:[bx], 13 ; Store CR in prev-cmd buffer
inc bx
call chk_cb
mov cb_in, bx ; Store new "in" pointer
ret

store_cmd endp

make_room proc near ; Make sure there are CX bytes
; free in previous-command buffer
call chk_free ; Return free bytes in AX
cmp ax, cx ; Enough free space already?
jb make_r1
ret

make_r1:push cx ; We need to clear out some space--
; do this by moving "cb_out" forward
; one full line in the buffer,
; clearing out a line of text
mov bx, cb_out
mov cx, cb_size ; Only run through "cb_size" bytes,
; so we don't get stuck in an
; endless loop if something's wrong
; with the command buffer
mov es, cb_seg

make_r2:cmp byte ptr es:[bx], 13 ; End of a line?
je make_r3 ; If so, we've found new "out" pointer
inc bx
call chk_cb ; Wrap around if needed
loop make_r2
pop cx

; If we get here, no CR was found in the buffer, indicating a
; rather serious screw-up...just empty the buffer and return:

xor bx, bx
mov cb_in, bx
mov cb_out, bx
ret

make_r3:pop cx
inc bx ; Move to start of next line
call chk_cb ; Adjust pointer if needed
mov cb_out, bx ; Store new "out" pointer
jmp short make_room ; See if we have enough room now

make_room endp

chk_free proc near ; Return # free bytes in circular
; command buffer in AX
mov ax, cb_out
sub ax, cb_in
jnc chk_f1 ; If out > in, skip next line
add ax, cb_size ; Buffer not wrapped around--adjust

chk_f1: dec ax
ret
chk_free endp

expandText proc near

call era_text ; Erase current text line; modified
; line will be redrawn later

call alias_chk ; Convert aliases

call expandMeta ; Expand metacharacters etc.

; Reset previous-command pointer used by up/down arrows to the
; very last command entered, as this pointer has been destroyed by
; expandMeta:

mov bx, cb_in ; Addr of last command in previous-
; command buffer + 1
mov cb_view, bx ; Set addr of current "previous cmd"
; being viewed (see edit_up, edit_dn)

mov bx, bufend ; Put cursor at new end of text line
mov bufptr, bx

mov bx, offset DATA:inbuf
call move_csr ; Move cursor to start of line
mov bx, offset DATA:inbuf
call draw_eol ; Print new text line

ret

expandText endp

; alias_chk below does the work of converting aliases to their
; respective full commands

alias_chk proc near

mov bx, offset DATA:inbuf

mov ax, al_seg ; Get alias list segment #
or ax, ax ; Is there an alias list?
jz alias_ret

mov es, ax
mov di, 0 ; ES:DI=start of alias list

alias2: call skip_white ; Skip tabs, spaces
jnc alias3 ; NC=Not at end of line or list
or al, al ; End of list?
jnz alias2a ; No, must be end of a line

alias_ret:
ret

alias2a:inc di ; Skip CR
jmp short alias2 ; Check out next line

alias3: mov al, es:[di]
cmp al, 'a' ; Lowercase character?
jb alias3a
cmp al, 'z'
ja alias3a
sub al, 'a' - 'A' ; Convert to uppercase

alias3a:mov ah, [bx] ; Lowercase character?
cmp ah, 'a'
jb alias3b
cmp ah, 'z'
ja alias3b
sub ah, 'a' - 'A' ; Convert to uppercase

alias3b:cmp al, ah ; Match?
je alias4
jmp alias5

alias4: inc di
inc bx
mov al, es:[di]
cmp al, ' ' ; White space character?
je got_cmd ; If so, we've matched an alias!
cmp al, 9 ; TAB character
je got_cmd
or al, al ; End of alias list?
jz alias_ret
cmp al, 13 ; End of this alias line?
jne alias3

inc di ; Point to start of next alias
mov al, es:[di]
or al, al
jz alias_ret ; Return if end of alias list
jmp alias2

got_cmd:cmp byte ptr [bx], 0 ; Must also be end of typed command
; for an exact match
jne alias5

call skip_white ; Skip white space--ES:DI now points
; to the text to replace the
; alias with
mov bx, offset DATA:inbuf
mov cx, max_in

got_cm1:mov al, es:[di]
or al, al ; End of alias list?
jz got_cm2
cmp al, 13 ; End of line?
je got_cm2
mov [bx], al ; Copy to inbuf
inc bx
inc di
loop got_cm1

got_cm2:mov bufend, bx ; Store appropriate variables:
mov bufptr, bx
sub bx, offset DATA:inbuf
mov ntyped, bx

got_cm3:
ret

alias5: ; No match
mov al, es:[di]
inc di
or al, al ; End of alias list?
je alias5a
cmp al, 13 ; End of line?
jne alias5 ; If not, keep looking for EOL
inc di ; Start of next line
cmp byte ptr es:[di], 0 ; End of list?
je alias5a
mov bx, offset DATA:inbuf ; Start w/beginning of inbuf
jmp alias2

alias5a:
jmp alias_ret

alias_chk endp

; expandMeta takes care of expanding !-N, !X, !$, !$, etc.
; constructs when they are found in the text

expandMeta proc near

mov bx, offset DATA:inbuf
mov bufptr, bx

exp1: mov bx, bufptr ; Main loop is at exp1/exp1a

exp1a: cmp bx, bufend
jnc exp3
mov al, [bx]
cmp al, '!'
je exp10

exp2: mov bx, bufptr
inc bx
mov bufptr, bx
jmp short exp1a

exp3: ret ; All done

exp10: mov mStart, bx ; Save start of metachar sequence
inc bx
cmp bx, bufend ; End of line?
jnc exp2 ; If so, not a valid metasequence

mov bufptr, bx
mov al, [bx] ; Get character after '!'
cmp al, '$'
je exp11
cmp al, '*'
je exp11

jmp exp12

exp11: push bx
mov bx, cb_in
mov cb_view, bx ; Point to end of prev-cmd buffer
call prv_cmd ; [cb_seg]:cb_view points to the last
; command that was entered
pop bx
jnc exp11b ; If previous command was found

exp11a: jmp exp2 ; If not, invalid metasequence

exp11b: jmp exp20

exp12: cmp al, '-' ; !-N construct?
je exp12b

cmp al, '!'
je exp12a

jmp exp15 ; Must be !X type

exp12a:
push bx
mov bx, cb_in
mov cb_view, bx
call prv_cmd
pop bx
jc exp11a ; No prev cmd -- invalid metasequence
inc bx
jmp exp20 ; Handle !!, !!$, etc.

exp12b: ; Handle !-N sequence
inc bx
push bx
mov bx, bufend
mov byte ptr [bx], 0 ; Terminate input buffer
pop bx
call readnum ; Read decimal number @ DS:BX into AX

or ax, ax ; If no number was there,
jz exp11a ; Invalid metasequence
mov nPrev, ax ; # prev cmds to move back

push bx
mov bx, cb_in
mov cb_view, bx

exp13: call prv_cmd
jc exp14
dec nPrev
jnz exp13

pop bx
jmp exp20

exp14: ; Couldn't find nth prev command
pop bx
jmp exp2 ; Invalid metasequence

exp15: ; Handle !X sequence -- find last
; command beginning with "X"

mov xStart, bx ; Save start of "X" search string

mov bx, cb_in
mov cb_view, bx ; Point to end of prev-cmd buffer

rep1: call prv_cmd ; Back up one command
jnc rep2

jmp exp2 ; No more previous commands--we
; couldn't find last "X" command

rep2: ; BX equals cb_view (from prv_cmd)
mov si, xStart

rep3: mov al, [si]
or al, al ; End of typed line?
je rep4 ; If so, we have a match!
cmp al, ' ' ; Space terminates search string
je rep4
cmp al, 9 ; So does TAB
je rep4
cmp al, '$' ; So does $
je rep4
cmp al, '*' ; So does *
je rep4
cmp al, '^' ; And ^
je rep4

cmp al, 'a' ; Lowercase?
jb rep3a
cmp al, 'z'
ja rep3a
sub al, 'a' - 'A' ; Convert to uppercase

rep3a: mov ah, es:[bx]
cmp ah, 'a' ; Lowercase?
jb rep3b
cmp ah, 'z'
ja rep3b
sub ah, 'a' - 'A' ; Convert to uppercase

rep3b: cmp al, ah ; Match?
jne rep1 ; No match, try previous command
inc si
inc bx
call chk_cb ; Adjust BX if necessary
jmp short rep3 ; Check next character

rep4: ; We found command--copy to inbuf
mov bx, si ; Point to character following "X"
jmp exp20

exp20: ; Here, cb_view points to the command
; we'll be grabbing text from, and bx
; points to the metacharacter ($, *,
; etc.)

mov al, [bx] ; Get metacharacter; $*^ etc.
cmp al, '$' ; !..$ substitutes last word of
; command-line
je exp21
jmp exp30

exp21: push bx ; Save pointer to $

mov bx, cb_view ; Start of our "graft" command line
mov inWord, 0 ; Not in a word

exp22: mov al, es:[bx]
cmp al, 13 ; CR
je exp27

cmp inWord, 0
je exp24

; We're in a word; see if we've hit a word delimiter:

cmp al, ' '
je exp23 ; Not in a word now
cmp al, 9 ; TAB
je exp23 ; Not in a word now
jmp short exp25 ; Look at next character

exp23: mov inWord, 0
jmp short exp25

exp24: cmp al, ' '
je exp25
cmp al, 9 ; TAB
je exp25

; We've found the start of a word:

mov inWord, 1
mov wordPtr, bx

exp25: inc bx
call chk_cb ; Make sure BX is in range
jmp short exp22 ; Try next character

exp27: pop bx ; Restore pointer to $

call del_meta ; Delete metachar sequence

call ins_text ; Insert wordPtr..EOL in text

jmp exp1 ; bufptr is correct; keep going

exp30:
cmp al, '*' ; Substitute 2nd+ word[s]
je exp31 ; !..* substitutes 2nd+ words of
; command-line
jmp exp40

exp31: push bx ; Save pointer to '*'

mov bx, cb_view ; Start of our "graft" command line

exp32: mov al, es:[bx]
cmp al, 13 ; CR
je exp37 ; No 2nd word

cmp al, ' '
je exp33 ; Not in a word now
cmp al, 9 ; TAB
je exp33 ; Not in a word now

inc bx
call chk_cb
jmp short exp32


exp33: inc bx
call chk_cb

exp34: mov al, es:[bx]
cmp al, 13
je exp37 ; No 2nd word

cmp al, ' '
je exp33
cmp al, 9 ; TAB
je exp33

; We've found start of 2nd word -- save it
mov wordPtr, bx

pop bx ; Restore pointer to $

call del_meta ; Delete metachar sequence

call ins_text ; Insert wordPtr..EOL in text

jmp exp1 ; bufptr is correct; keep going

exp37: pop bx
jmp exp1 ; Invalid metasequence

exp40: ; No $, *, etc. metacharacter--assume
; we are to graft entire text line

mov ax, cb_view ; Start of "graft" command line
mov wordPtr, ax ; This is start of text to graft

dec bx ; Point to last character in metaseq.
call del_meta

call ins_text

jmp exp1

expandMeta endp

ins_text proc near

mov bx, wordPtr

ins_t1: mov al, es:[bx]
cmp al, 13 ; CR?
je ins_t2

push bx
call ins_char
pop bx
jc ins_t2 ; If buffer full, stop

inc bx
call chk_cb ; Make sure BX is in range

inc bufptr ; Advance bufptr to point after
; inserted character
jmp short ins_t1

ins_t2: ret

ins_text endp

; del_meta deletes from the input buffer the characters from
; [mStart] through BX, inclusive. Returns CY=0 if OK, CY=1 if error

del_meta proc near

mov ax, mStart
mov bufptr, ax

sub bx, ax
inc bx

mov cx, bx ; CX=# characters to delete

del_m1: push cx
call del_char
jc del_m2
pop cx
loop del_m1
clc
ret

del_m2: pop cx
ret

del_meta endp

skip_white proc near ; Used by alias_chk; skips white space
; at ES:[DI]
mov al, es:[di]
cmp al, ' '
je skip
cmp al, 9 ; TAB character
je skip
cmp al, 13 ; End of line
je eol
or al, al ; End of alias list
jz eol
clc
ret

skip: inc di
jmp short skip_white

eol: stc
ret

skip_white endp

get_csr proc near ; Call BIOS to get cursor X/Y coords.
mov ah, 3
xor bx, bx
int 10h ; DH/DL=Cursor Y/X; CH/CL=Csr strt/end
ret
get_csr endp

set_ctyp proc near ; Set CH/CL=Cursor start/end line
mov ah, 1
int 10H
ret
set_ctyp endp

mov_pcsr proc near ; Move physical cursor; DH/DL=Csr Y/X
mov ah, 2
xor bx, bx
int 10H
ret
mov_pcsr endp

draw_eol proc near ; Redraw from [BX] to end of inbuf
; Physical cursor must be at
; correct place on screen

mov al, cur_x
mov temp_x, al
mov al, cur_y
mov temp_y, al

draw_e1:cmp bx, bufend ; Are we done?
jc draw_e2 ; If not, keep on truckin
ret

draw_e2:mov al, [bx]
push bx
call prt_chr
call get_csr
cmp dl, temp_x ; Has cursor X coord decreased?
; (meaning cursor wrapped to nxt line)
jnc draw_e3
cmp dh, temp_y ; Is cursor Y coord the same?
ja draw_e3
; If it is, then screen has scrolled
dec start_y ; up and we should decrement the Y
; coordinate of start of input field

draw_e3:mov temp_x, dl ; Store new current cursor X & Y
mov temp_y, dh
pop bx
inc bx
jmp short draw_e1

draw_eol endp

; prt_chr handles displaying of characters found in the text buffer; it
; handles expansion of control codes to ^A..^Z, etc. Note that the
; expanded sizes of characters (e.g. ^A occupies 2 display spaces) must
; match the code in move_csr.

prt_chr proc near

cmp al, 32 ; ASCII characters,
jnc prt_c1
cmp al, 13 ; CR,
je prt_c1
cmp al, 10 ; LF,
je prt_c1
cmp al, 9 ; and Tab
je prt_c1 ; ... are handled directly by BIOS

push ax ; Convert control chars to ^A, etc.
mov al, '^'
mov ah, 0EH
xor bx, bx
int 10H
pop ax
add al, 'A'-1
mov ah, 0EH
xor bx, bx
int 10H
ret

prt_c1: mov ah, 0EH ; Use BIOS to print character
xor bx, bx
int 10H
ret

prt_chr endp

; Note: characters' displayed widths as computed in this procedure must
; match their widths as printed in prt_chr (see above).

move_csr proc near ; Move physical cursor to location
; in "inbuf" specified by BX

mov tempptr, bx
mov bx, offset DATA:inbuf ; Count from start of input buffer
mov al, start_x
mov ah, start_y

move_c1:cmp bx, tempptr ; Have we found desired location yet?
jb move_c2 ; If not, keep trying

mov cur_x, al ; Store cursor coordinates
mov cur_y, ah
mov dx, ax
call mov_pcsr ; Move physical cursor there
ret

move_c2:mov cl, [bx]
cmp cl, 9
je move_tab
cmp cl, 32
jb move_ctrl
inc al ; Most characters use one space
jmp move_c3

move_tab:
add al, 8 ; Move ahead 8 spaces
and al, 248 ; Round down to multiple of 8
jmp move_c3

move_ctrl:
add al, 2 ; ^A, etc. take up 2 spaces
jmp move_c3

move_c3:cmp al, max_x ; Past end of physical line?
jb move_c4
sub al, max_x ; Adjust X coord to be in range
inc ah ; Increment Y coord...we don't
; have to worry about scrolling
; here--draw_eol takes care of that
jmp short move_c3

move_c4:inc bx
jmp move_c1

move_csr endp

chk_word proc near ; Set CY if character at [BX] is
; part of a word
mov al, [bx]
cmp al, '0'
jb chk_w1
cmp al, '9'
jbe is_word ; Digits count as part of a word
chk_w1: cmp al, 'A'
jb chk_w2
cmp al, 'Z'
jbe is_word ; So do uppercase letters
chk_w2: cmp al, 'a'
jb chk_w3
cmp al, 'z'
jbe is_word ; and lowercase letters
chk_w3: clc ; Everything else is a
ret ; "word delimiter"

is_word:stc
ret

chk_word endp

ins_char proc near ; Insert char in AL @ bufptr

mov bx, ntyped
cmp bx, max_in
jb ins_c0

stc
ret

ins_c0:
mov bx, bufend

ins_c1: cmp bx, bufptr
jbe ins_c2
mov cl, [bx-1]
mov [bx], cl
dec bx
jmp short ins_c1

ins_c2: mov [bx], al
inc bufend ; Increment "last char in buf" pointer
inc ntyped ; Increment # chars in buffer
clc
ret

ins_char endp

del_char proc near ; Delete character at [bufptr]

cmp ntyped, 0
jne del_c0

stc
ret

del_c0:
mov bx, bufptr
inc bx

del_c1: cmp bx, bufend
jnc del_c2
mov al,[bx]
mov [bx-1],al
inc bx
jmp short del_c1

del_c2: dec bufend ; Decrement "last char in buf" pointer
dec ntyped ; Decrement # chars in buffer
clc
ret

del_char endp

get_key proc near

;; xor ah, ah
;; int 16H ; Call BIOS to wait for keystroke
;; or al, al ; Extended code (xx00H)?
;; jz get_k1
;; xor ah, ah ; ASCII code (00xxH)
;;
;;get_k1: ret

mov ah, 8 ; It appears to be safe to use DOS
int 21H ; console-input routine...this also
mov ah, 0 ; provides benefit of ^C and ^Break
or al, al ; checking
jnz get_k1

mov ah, 8
int 21H
mov ah, al
xor al, al

get_k1: ret

get_key endp

era_text proc near ; Erase all typed text from screen

mov bx, bufend
call move_csr ; Find cursor position of end;
; store in cur_x/cur_y
mov dl, start_x
mov dh, start_y

era_t0: ; DH/DL=Start coord.
push dx
call mov_pcsr
pop dx
jmp short era_t2

era_t1: push dx
mov al, ' '
call prt_chr ; Space over text
pop dx
inc dl
cmp dl, max_x ; Past end of line?
jb era_t2
xor dl,dl
inc dh

era_t2: cmp dh, cur_y ; Done?
jb era_t1 ; If not...
cmp dl, cur_x ; Done?
jb era_t1 ; If not...
ret
era_text endp

readnum proc near ; Read decimal # @ DS:BX into AX
; Destroys CX, DX
xor ax, ax
xor ch, ch

readn1: mov cl, [bx]
sub cl, '0'
jb readn2
cmp cl, 10
jnc readn2
mov dx, ax
shl ax, 1
shl ax, 1
add ax, dx
shl ax, 1
add ax, cx
inc bx
jmp short readn1

readn2: ret

readnum endp

ifndef DE_INSTALL

;*
;* NOTE: ALL CODE AND DATA AFTER THIS POINT WILL NOT BE KEPT AFTER
;* THE TSR CALL!!!
;*

TSRCutoff label byte

endif ; !DE_INSTALL

de_inst proc near
mov dx, old_i21
mov ax, 2[old_i21]
mov bx, ax
or bx, dx ; Was new INT 21H vector installed?
jz de_i1 ; If not, don't set it to 0000:0000!
push ds
mov ds, ax
mov ax, 2521H
int 21H ; Put old INT 21H vector back
pop ds

de_i1: mov ax, cb_seg ; Get previous-command buffer segment
or ax, ax ; Has it been allocated yet?
jz de_i2 ; If not, don't de-allocate
mov es, ax
mov ah, 49H
int 21H ; De-allocate previous-command buffer

de_i2: mov ax, al_seg ; Get alias list segment
or ax, ax ; Has it been allocated yet?
jz de_i3 ; If not...
mov es, ax
mov ah, 49H
int 21H ; De-allocate alias list segment

de_i3: mov es, psp_seg
mov bx, es:[002CH] ; Get environment segment
or bx, bx ; Is there one?
jz de_i4
mov es, bx
mov ah, 49H
int 21H ; De-allocate environment seg

de_i4: mov word ptr cs:[sig], 0 ; Erase signature so this process
; can't be de-installed again
ret

de_inst endp

ifdef DE_INSTALL

;*
;* NOTE: ALL CODE AND DATA AFTER THIS POINT WILL NOT BE KEPT AFTER
;* THE TSR CALL!!!
;*

TSRCutoff label byte

endif ; DE_INSTALL

ames1 db 'Alias List:', 13, 10, 0
crlf db 13, 10, 0
mes1 db 13, 10
db 'DE DOS Editor Version 1.3 by Paul Ketrick', 13, 10
db 0
mes2 db 'DE: Bad option: ', 0
mes3 db 'DE: Invalid size for previous command buffer.', 13, 10, 0
mes4 db 'DE: Not enough memory.', 13, 10, 0
mes5 db 'DE Version 1.3 by Paul Ketrick', 13, 10
db 'Usage: DE [/D] [/Bn] [/I] [/R] [filename]', 13, 10
db '/D de-installs editor.', 13, 10
db '/Bn sets previous-command buffer to "n" Kbytes long.', 13, 10
db '/I sets insert mode default to ON.', 13, 10
db '/R sets insert mode default to OFF.', 13, 10
db 'Optional filename is alias list file.', 13, 10
db 13, 10
db 'DE expands the following metacharacter sequences anywhere in a command line:', 13, 10
db '!! Previous command line.', 13, 10
db '!X Last command line starting with X; X is a string which may not', 13, 10
db ' contain space, $ or ^ characters.', 13, 10
db '!-N Nth previous command, where N is a number, 1+.', 13, 10
db '!$ Last word of the last command line.', 13, 10
db '!* 2nd+ words of the last command line.', 13, 10
db '!!$, !!*, !-N$, !-N*, etc. constructs are also supported.', 13, 10
db 0
mes6 db 'DE: De-Installing.', 13, 10, 0
mes8 db 'DE: Not Installed!', 13, 10, 0

entry proc far ; Program entry point here

mov ax, DATA
mov es, ax
assume es:DATA

mov ax, ds ; PSP segment in ES
mov es:[psp_seg], ax ; Store PSP segment

mov si, 81H
mov cl, [si-1] ; Get length of cmd-line parm
xor ch, ch
mov di, offset DATA:cmdline ; Point to local copy of command line
cld
rep movsb ; Copy cmd line to "cmdline"
xor al, al
stosb ; Terminate w/zero byte

mov ax, DATA
mov ds, ax
assume ds:DATA

mov dx, offset CODE:mes1 ; Print sign-on message
call prt_mes

mov dx, offset TSRCutoff + 15
shr dx, 1 ; Compute # paragraphs from start of
shr dx, 1 ; CODE segment to TSR cutoff point
shr dx, 1
shr dx, 1
mov ax, cs
add dx, ax
sub dx, psp_seg ; Compute size of whole program
inc dx ; Round up a paragraph
mov par_sav, dx ; # paragraphs to save

mov bx, dx
push es
mov es, psp_seg
mov ah, 4AH
int 21H ; Resize this program's memory block
; so more memory can be allocated

call scan_cmd ; Check out command line
call init_buf ; Allocate & initialize prev-cmd buffer
call load_alias ; Allocate memory for alias list
; and load it

mov ax, 3521H
int 21H ; Get old INT 21H handler
mov cs:old_i21, bx
mov cs:2[old_i21], es ; Store it

push ds
mov ax, cs
mov ds, ax
mov dx, offset CODE:new_i21
mov ax, 2521H
int 21H ; Set new INT 21H handler
pop ds

tsr_exit:

ifndef DE_INSTALL

mov word ptr cs:[sig], 0 ; Erase signature so another copy
; of DE won't try to de-install us

endif ; !DE_INSTALL

mov dx, par_sav ; Total # paragraphs we need
mov ax, 3100H
int 21H ; Terminate + stay resident

err_exit:
call de_inst ; De-install the program if it
; has been installed
mov ax, 4C01H ; Exit code of 1
int 21H

entry endp

prt_mes proc near ; Send message @ CS:DX to console

mov bx, dx

prt_m1: mov al, cs:[bx]
or al, al
jz prt_m2
push bx
call prt_chr
pop bx
inc bx
jmp short prt_m1

prt_m2: ret

prt_mes endp

scan_cmd proc near ; Scan command line

mov bx, cmdptr
scan_c1:mov al, [bx]
or al, al
jz scan_c2
cmp al, ' '
je scan_c3
cmp al, 9 ; Tab character
je scan_c3
cmp al, '-' ; - or / indicates cmd-line option
je scan_c4
cmp al, '/'
je scan_c4
cmp al, '?' ; Request for help?
je show_help
jmp scan_c5 ; Read alias list filename

show_help: ; User typed "DE ?"
mov dx, offset CODE:mes5 ; Help him/her out
call prt_mes
jmp err_exit ; Abort (don't TSR)

scan_c2:ret

scan_c3:inc bx
jmp short scan_c1

scan_c4:inc bx
scan_4a:mov al, [bx]
inc bx
cmp al, 'b' ; /Bn specifies prev-cmd buffer size
je scan_c6
cmp al, 'B'
je scan_c6
cmp al, 'd' ; /D de-installs program
je scan_4b
cmp al, 'D'
je scan_4b
cmp al, 'i'
je scan_4c
cmp al, 'I' ; Set insert mode default to ON
je scan_4c
cmp al, 'r'
je scan_4d
cmp al, 'R' ; Set insert mode default to OFF
je scan_4d
cmp al, ' '
je scan_c1
cmp al, 9
je scan_c1
or al, al
jz scan_c1
push bx
push ax
mov dx, offset CODE:mes2 ; "Invalid option" message
call prt_mes
pop ax
call prt_chr ; Show which char caused error
mov dx, offset CODE:crlf
call prt_mes
pop bx
jmp short scan_4a

scan_4b:jmp scan_c11 ; Go de-install the program

scan_4c:mov ins_def, 1
jmp short scan_4a ; Read next parameter

scan_4d:mov ins_def, 0
jmp short scan_4a

scan_c6:call readnum ; Read number @ DS:BX into AX
mov cmdptr, bx
or ax, ax
jz scan_c7 ; Must have at least 1K
cmp ax, 63
ja scan_c7 ; 63K is largest size
mov bx, 1024
mul bx ; Convert K to bytes
mov cb_size, ax

mov bx, cmdptr
jmp short scan_4a

scan_c7:mov dx, offset CODE:mes3 ; Invalid buffer size--tell user
call prt_mes
mov bx, cmdptr
jmp short scan_4a

scan_c5:mov cmdptr, bx ; Copy name of alias file to "inbuf"
mov di, offset DATA:inbuf
scan_c9:mov al, [bx]
cmp al, ' '
je scan_c10
cmp al, 9
je scan_c10
or al, al
jz scan_c10
mov [di], al
inc bx
inc di
jmp short scan_c9

scan_c10:
mov byte ptr [di], 0 ; Terminate filename
jmp scan_c1 ; Continue scanning command-line

scan_c11:
mov ax, 3521H ; Get current INT 21H handler
int 21H
sub bx, siglen ; Point to start of signature, if
; it's there
mov di, bx
mov si, offset CODE:sig ; See if signature is present
push ds
mov ax, cs
mov ds, ax
mov cx, siglen
cld
rep cmpsb
pop ds
jnz scan_c12 ; Signature not found--bark at user

push ds
mov dx, 0FFFFH
mov ds, dx
inc dx ; DS:DX=FFFF:0000 -- this signals
mov ah, 0AH ; installed DE to de-install
int 21H ; De-install TSR'ed copy of DE
pop ds

mov dx, offset CODE:mes6 ; Tell user it's been de-installed
call prt_mes
jmp err_exit

scan_c12:
mov dx, offset CODE:mes8 ; Tell user DE is not already
call prt_mes ; installed
jmp err_exit

scan_cmd endp

load_alias proc near ; Load alias list; filename is in
; "inbuf", null-terminated
mov al_fd, -1
mov ax, 3D00H ; Open alias file for read access
mov dx, offset DATA:inbuf
int 21H
jnc load_a1

la_err: mov al_seg, 0 ; Error in loading alias list
mov al_size, 0
mov bx, al_fd
cmp bx, -1 ; Is file open?
je la_er1 ; If not, don't close it
mov ah, 3EH
int 21H
mov al_fd, -1

la_er1: ret

load_a1:mov al_fd, ax
mov bx, ax
mov ax, 4202H
xor dx, dx
xor cx, cx
int 21H ; Get file size in DX:AX
jc la_err
or dx, dx ; Size must be < 64K
jnz la_err
cmp ax, 0FFF8H ; Leave a few extra bytes free
jnc la_err
mov al_size, ax ; Save total # bytes memory we'll need
inc ax ; For zero byte we'll store at end

mov cl, 4
shr ax, cl ; Compute # paragraphs needed
inc ax ; Round up a paragraph
mov bx, ax
mov ah, 48H
int 21H
jc la_err ; Memory allocation error
mov al_seg, ax

mov ax, 4200H
mov bx, al_fd
xor cx, cx
xor dx, dx
int 21H ; Rewind file

push ds
mov bx, al_fd
mov cx, al_size
mov ds, al_seg
xor dx, dx
mov ah, 3FH ; Read file into memory
int 21H
pop ds
jc la_err
cmp cx, al_size
jne la_err

mov bx, al_fd
mov ah, 3EH
int 21H
mov al_fd, -1

push ds
mov bx, al_size
mov ds, al_seg
mov byte ptr [bx], 0 ; Terminate alias list w/couple zeroes
pop ds

call prt_alias ; Print out alias list

ret

load_alias endp

prt_alias proc near ; Print alias list to console

mov dx, offset CODE:ames1 ; "Alias List:"
call prt_mes

push ds
xor bx, bx
mov cx, al_size
mov ds, al_seg
assume ds:NOTHING

prt_a1: mov al, [bx]
or al, al
je prt_a2
cmp al, 26 ; ^Z
je prt_a2
push bx
push cx
call prt_chr
pop cx
pop bx
inc bx
loop prt_a1

prt_a2: pop ds
assume ds:DATA
mov dx, offset CODE:crlf
call prt_mes

ret

prt_alias endp

init_buf proc near ; Allocate & initialize previous-
; command buffer

push bp
mov bx, cb_size
mov cl, 4
shr bx, cl ; Convert to paragraphs
inc bx ; Round up 1 paragraph
mov ah, 48H ; Allocate BX paragraphs of memory
int 21H
jc init_b1
mov cb_seg, ax
xor ax, ax
mov cb_in, ax ; Clear circular command buffer
mov cb_out, ax
pop bp
ret

init_b1:mov dx, offset CODE:mes4 ; Error allocating memory
call prt_mes
jmp err_exit ; Abort

init_buf endp

ProgramEnd label byte

CODE ends

end entry


  3 Responses to “Category : Utilities for DOS and Windows Machines
Archive   : DE.ZIP
Filename : DE.ASM

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/