Category : Assembly Language Source Code
Archive   : ASMSUB2.ZIP
Filename : DOS-EDIT.ASM

 
Output of file : DOS-EDIT.ASM contained in archive : ASMSUB2.ZIP
PAGE 60,132
;***************************************************************************
; General comments
;***************************************************************************
; The following comprises the disassembled and documented version of DOS-ED
; as it exists on Compuserve's XA6 data base. If one reassembles this
; program one will obtain the exact version of DOS-ED.COM that exists on
; that database. No attempt has been made to make improvements to this code
; other than to indicate in the margin were some could (and probably should)
; be made. If you notice that the program sizes are different between this
; reassembled and relinked version and the version that has been generated
; from the HEX file on CIS, that is because there is some trailing garbage
; on the CIS file. You can strip it off in the debugger and the files will
; compare exactly.
;
; Even after disassembling and documenting this code I have a few questions
; about what was being done in places. If my comments seem spurious at
; times, then just take them with a grain of salt. They should help you
; understand this code for the most part.
;
; When linking this program you should expect to receive a No Stack warning
; message along with an Unsatisfied external error on DOS_RETURN. These
; are normal and should be ignored.
;
; DOS-ED is a fine piece of programming performed by Jack Gersbach.
; This disassembly and documentation was done by myself.
; - Scott W. Killen
;
;***************************************************************************
; Definitions and commentary on conventions in coding
;***************************************************************************
; Register usage: (Most common (but not exclusive) uses are listed)
; AX, DX, CX - Used for many reasons
; DH - Usually current row on crt
; DL - Usually current col on crt
; SI - Usually points to offset of current character in input buffer (first character is offset 0)
; DI - Usually has the value of one of the following:
; * The number of characters currently in the input buffer *or*
; * Points to a destination in the text holding buffer
; BX - Usually contains the address at which the first character in the input buffer is located.
; (The two characters before this are the max buffer size and length of string
; characters.)
; BP - Usually a register of flags organized as bits |01234567| where:
; Bits 0-3 have no function
; Bit 4 - Off means the input buffer has been altered since interrupt processing began.
; On means *only* lateral cursor positioning commands have been issued since interrupt
; processing began.
; Bit 5 - Off means previous line from holding buffer, On means original line
; Bit 6 - Off means no changes to current line in input buffer, On means changes have been made
; Bit 7 - Applies to the redisplay of lines on the screen after an intra-line edit operation
; Off means line wasn't shortened therefore no trailing blanks needed
; On means line is now shorter and blanks are needed over trailing characters
;
; Memory Usage:
; ES:[0450H] - Contains current cursor position in Bios data area
; ES:[044AH] - Contains # of columns of current crt screen in Bios data area
; ES:[0417H] - Keyboard flag, containing insert, shift, num lock, scroll lock etc in Bios data area
; insert flag bit is 80h
;
; CS:[inline_mod1+1] - Jump return address to normal DOS funcion processing
; CS:[inline_mod2+1] - Current cursor definition (start/stop lines)
; CS:[inline_mod3+1] - Screen location of end of line (row/col)
; CS:[inline_mod4+2] - Higher of DS or CS on entry into interrupt.
; CS:[inline_mod5+2] - Maximum size of input buffer
;
; start_off - Offset of start of input buffer on screen (example if prompt is A> then screen column position
; of input buffer start would be 2
; current_col - Current column position on screen (1st column is 0)
; last_entry - Address (offset) of last string accessed in text buffer
; next_entry - Address (offset) of next available block in text buffer
; buffer_bot - Base address of text buffer
; buffer_top - Top address of text buffer
; aux_last \
; aux_next \ Same as previous four except for auxiliary buffer
; aux_bot /
; aux_top /
;

; Character equates
ACK equ 06h ; Acknowledge character (Cntrl F)
BELL equ 07h ; Bell character (Cntrl G)
BACKSPACE equ 08h ; Backspace character
TAB equ 09h ; Tab character
ESC equ 1Bh ; Escape character
BLANK equ 20h ; Space character
CR equ 0dh ; carriage return
LF equ 0ah ; Line feed
HAT equ 5eh ; "^" character
SMALLA equ 61h ; "a" character
LEFT_CURLY_BRACE equ 7Bh ; "{" character
CNTL_BACKSPACE equ 7fh ; Control and backspace pressed simultaneously

; Masks
INSERT_ON_MASK equ 80h ; Insert key bit on, corresponding to keyboard status word
INSERT_OFF_MASK equ 7fh ; Insert key bit off, corresponding to keyboard status word
LENGTH_BYTE_MASK equ 80h ; Mask to identify the text string length bit in text buffer
LENGTH_OFF_MASK equ 7fh ; Mask to turn of the length byte identifier
UPPER_CASE_MASK equ 40h ; Convert control character to upper case character
LOWER_CASE_MASK equ 20h ; Convert upper case character to lower case character

; Physical characteristics
ROWS_ON_CRT equ 19h ; Ordinal number of lines on crt
LAST_ROW_ON_CRT equ ROWS_ON_CRT - 1 ; Offset of last line on crt (top line is line 0)
MONO_CUR_DEF equ 0B0Ch ; Start, stop lines of cursor
MONO_CUR_STOP equ 0Ch ; Stop line for standard cursor
GRAPHICS_CUR_DEF equ 0607h ; Start, stop lines of cursor for graphics device
GRAPHICS_CUR_STOP equ 07h ; Stop line for standard cursor for graphics device
TEXT_BUFFER_START equ 60h ; Start address for text holding buffer
TEXT_BUFFER_END equ 160h ; End address for text holding buffer
TEXT_BUFFER_SIZE equ TEXT_BUFFER_END - TEXT_BUFFER_START ; Extent of text buffer
BOTH_BUFFS_SIZE equ TEXT_BUFFER_SIZE + TEXT_BUFFER_SIZE ; Combined buffer size
AUX_BUFFER_START equ 160h ; Auxiliary holding buffer start address
AUX_BUFFER_END equ 260h ; Auxilliary holding buffer end address
; *NOTE* TEXT_BUFFER_END - TEXT_BUFFER_START =
; AUX_BUFFER_END - AUX_BUFFER_START

; Bios interrupt 10 functions
SET_CURSOR_TYPE equ 01h
SET_CURSOR_POSN equ 02h
READ_CURSOR_POSN equ 03h
READ_VIDEO_STATE equ 0fh

; Dos interrupt 21 functions
INPUT_CHR_NOECHO equ 08h
OUTPUT_CHR equ 02h

;***************************************************************************
; BIOS data segment definition
;***************************************************************************
BIOSEG SEGMENT at 0h
org 417h
kbd_flag db ?
org 44ah
crt_cols dw ?
org 450h
cursor_posn dw ?
BIOSEG ends

;***************************************************************************
; DOS-ED code segment begins
;***************************************************************************
CSEG SEGMENT
ASSUME CS:CSEG
ORG 100H
DOSED PROC NEAR

MOV SI,offset loadlabel ; Source of code to move is at loadlable
MOV DI,offset initialize ; Destination is at end of file at initialize
MOV CX,offset line_table ; Above the code to be moved is linetable
SUB CX,SI ; The difference is the amount of code to be moved
repz MOVSB ; Move it!!
JMP initialize ; Now go do it.

loadlabel:
XOR AX,AX ; Clear AX
MOV DS,AX ; Prepare to save original interrupt
MOV SI,0084H ; Source for INT 21h as provided for in the interrupt table
MOV DI, offset [inline_mod1+1] ; Destination in the code segment for moving this pointer
MOVSW ; Move the two word address
MOVSW
MOV AX, offset entry_point ; Move this programs starting address into the dos interrupt slot
MOV [SI-04H],AX ; Move offset in line
MOV [SI-02H],CS ; Move segment value in line
MOV DI, TEXT_BUFFER_START ; Working buffer begins at offset 60h which is Formatted parameter
MOV CX, BOTH_BUFFS_SIZE ; area 1 in the Program Segment Prefix.
MOV AL, CR ; Fill 512 bytes with CR characters. Note that this technique uses
repz STOSB ; 161 bytes of PSP for storage! Very Clever!
MOV DX, offset initialize ; Dos may load above this address
INT 27H ; Terminate but stay resident

line_table db 300 dup (?)

entry_point:
CMP AH,0AH ; Is this function 10?
JE wrapper ; Nope, continue with normal Dos 21 interrupt
EXTRN DOS_RETURN:FAR

inline_mod1:
JMP DOS_RETURN ; This code is modified in line!
; Go to normal Dos function handling.

wrapper:
PUSH AX
PUSH BX
PUSH CX
PUSH DX
PUSH SI
PUSH DI
PUSH BP
PUSH DS
PUSH ES
STI
CALL begin_dosed
POP ES
POP DS
POP BP
POP DI
POP SI
POP DX
POP CX
POP BX
POP AX
MOV AL,00H
IRET

;***************************************************************************
; Extended keystroke recognition and branching tables
;***************************************************************************
; Table 1 of extended keyboard function codes
table1:
t1_1 db 0fh ; Back Tab
t1_2 db 47h ; Home key
t1_3 db 48h ; Up arrow
t1_4 db 4bh ; Left arrow
t1_5 db 4dh ; Right arrow
t1_6 db 4fh ; End key
t1_7 db 50h ; Down Arrow
t1_8 db 53h ; Del key
t1_9 db 73h ; Cntrl Left Arrow
t1_a db 74h ; Cntrl Right Arrow
t1_b db 75h ; Cntrl End
t1_c db 77h ; Cntrl Home
t1_d db 76h ; Cntrl PgDn
t1_e db 84h ; Cntrl PgUp
t1_f db 00h ; ?
; Table 2 indexes correspond to table 1 indexes
; and points to code address where the function is handled.
table2:
t2_f dw offset error_return ; 0ah, 05h ; ?
t2_e dw offset cntrl_page_up ; 74h, 07h ; Cntrl PgUp
t2_d dw offset cntrl_page_down ; 0a9h, 06h ; Cntrl PgDn
t2_c dw offset cntrl_home ; 0bh, 05h ; Cntrl Home
t2_b dw offset cntrl_end ; 27h, 05h ; Cntrl End
t2_a dw offset cntrl_right_arrow ; 41h, 05h ; Cntrl Right Arrow
t2_9 dw offset cntrl_left_arrow ; 83h, 05h ; Cntrl Left Arrow
t2_8 dw offset del_key ; 0edh, 05h ; Del key
t2_7 dw offset down_arrow ; 44h, 06h ; Down Arrow
t2_6 dw offset end_key ; 0e4h, 04h ; End key
t2_5 dw offset right_arrow ; 67h, 05h ; Right arrow
t2_4 dw offset left_arrow ; 99h, 05h ; Left arrow
t2_3 dw offset up_arrow ; 43h, 06h ; Up arrow
t2_2 dw offset home_key ; 0e1h, 05h ; Home
t2_1 dw offset backtab ; 8dh, 05h ; Backtab

;***************************************************************************
; Display and output services
;***************************************************************************
screen_and_display: ; Screen out control characters of form ^x
CMP AL, BLANK ; Compare AL to blank character
JNB display_one_char ; If we have a printable character go to display routine
CMP AL, TAB ; Is this a forward tab?
JE display_one_char ; If so, go display.
PUSH AX ; No, this is a "normal" control character
MOV AL,HAT ; Place a "^" character in AL
CALL display_one_char ; Print the "^" out.
POP AX ; Now convert the Control character to its
OR AL, UPPER_CASE_MASK ; upper case equivalent and print it. Result is "^E"
; construct for representing control characters.

display_one_char:
CMP AL, BLANK ; Does AL contain a blank character
JB display_cntrl_chr ; Jump if we have a control character

ready_display:
CMP AL, CNTL_BACKSPACE ; Don't increment column counter if this
JE output_to_display ; is a control Backspace character
INC BYTE PTR CS:[current_col] ; Increment column position because we received
; something printable
output_to_display:
PUSH DX ; Save the DX register and set up the
XCHG AX,DX ; AH register for a DOS function 2 (Display
MOV AH, OUTPUT_CHR ; output) call.
INT 21H
XCHG AX,DX ; Restore the AX register
POP DX ; and restore the DX register
RET

display_cntrl_chr: ; Process a control character for output
CMP AL, CR ; Is this a carriage return character?
JE cr_out ; If so go take care of it
CMP AL, BACKSPACE ; Is this a backspace?
JE bs_out ; If so we need to take care of it
CMP AL, TAB ; Maybe its a tab character
JNE relay_to_output ; Continue if its not a tab
MOV AL,BYTE PTR CS:[current_col] ; Set AL to current column on screen
OR AL,0F8H ; Get the number of spaces to the next
NEG AL ; 8 byte boundary
PUSH CX ; Save the CX register
MOV CL,AL ; Place the count of blanks to be output in CL
mov ch, 00h ; Zero out high byte
JCXZ skip_tabbing ; Dont do anything if count is zero

tab_loop:
MOV AL, BLANK ; Ok, now print out all necessary
CALL display_one_char ; blanks to reach an 8 character
LOOP tab_loop ; boundary.

skip_tabbing:
POP CX ; Restore the CX register.
RET

cr_out:
MOV CS:BYTE PTR [current_col],00H ; Reset current column pointer to zero

relay_to_output:
JMP SHORT output_to_display

bs_out:
DEC BYTE PTR CS:[current_col] ; Decrement the current column pointer
PUSH AX ; Save the character in register AX
PUSH DX ; Save the DX register
MOV DX,ES:[0050H] ; Get the Cursor position from Bios data area
MOV AH,ES:[004AH] ; Get the # of CRT cols on screen from Bios data area
OR DL,DL ; Are we at column zero?
JNE bs_end ; If not forget the rest.
OR DH,DH ; Are off the screen at the top?
JNE set_cur_loc ; If not then dont reset rows
MOV DH, ROWS_ON_CRT ; (Re)set row value to 25

set_cur_loc:
DEC DH ; Decrement the row count
MOV DL,AH ; Move in # of columns. (0, 0) is home.
PUSH BX ; Save BX
MOV BH,00H ; Prepare for BIOS interrupt 10 funtion 2
MOV AH, SET_CURSOR_POSN ; call (Set cursor position)
INT 10H ; Do it.
POP BX ; Restore BX

bs_end:
POP DX ; Restore DX register
POP AX ; and the AX register
JMP SHORT relay_to_output ; and now we are done.

decrement_col_row: ; Wrap back if line spans two screen lines
OR DL,DL ; Are we at column zero
JNE dcr_end ; If so then dont worry about row update
DEC DH ; Yep, decrement the row pointer
MOV DL,BYTE PTR ES:[crt_cols] ; Get the # of CRT cols per screen from Bios data area

dcr_end:
DEC DL ; Decrement the column pointer
RET

display_blank:
MOV AL, BLANK

display_and_test: ; Display and test for updated cursor location
PUSH CX ; Save CX register
MOV CX,ES:[cursor_posn] ; Load CX with cursor position from Bios
CALL screen_and_display ; Send the character in AL to output display
CMP CH, LAST_ROW_ON_CRT ; Are we on the last row?
JNE dat_end ; If not then no further tests are needed
CMP CL,BYTE PTR ES:[cursor_posn] ; Are we now on a new column? (alternative question
; is: have we scrolled?)
JBE dat_end ; If current location > cl then yes we have scrolled
; up a line and need to decrement the line pointer
DEC DH ; Decrement the line pointer

dat_end:
POP CX ; Restore CX
RET
;***************************************************************************

set_cursor:
PUSH BX
PUSH CX
PUSH DX
PUSH BP ; Save registers
PUSH SI
PUSH DI

inline_mod2:
MOV CX, MONO_CUR_DEF ; Assume Standard char def
; (Note that this word is modified
; in line if it is later determined
; that a graphics board is in use)
TEST BYTE PTR ES:[kbd_flag], INSERT_ON_MASK ; Is insert on?
JE after_box ; If not then branch
MOV CH,CL ; Yep, turn the cursor into
SHR CH,1 ; a half sized box

after_box:
MOV AH, SET_CURSOR_TYPE ; Set up and call Bios for
INT 10H ; support.
POP DI
POP SI
POP BP ; Restore Registers
POP DX
POP CX
POP BX
MOV AX,WORD PTR ES:[cursor_posn] ; Load AX with current cursor position

inline_mod3:
CMP AX,0888H ; Compare current cursor position to end of line position
JB sc_ret ; If less than then dont update end of line position
MOV CS:WORD PTR [inline_mod3+1],AX ; Modify end of line address with value of current cursor position

sc_ret:
RET

credit db 'DOS EDITOR BY J. Gersbach',CR,LF,00


begin_dosed: ; Save registers
PUSH BX
PUSH CX
PUSH DX
MOV BH,00H ; Set up to read current cursor position
MOV AH, READ_CURSOR_POSN ; Cursor position returned as DH, DL is row, col
INT 10H ; Read the current cursor position into DH, DL
MOV CS:[start_off],DL ; Move current column into storage
MOV CS:[current_col],DL ; Move current column into storage
CMP CL, MONO_CUR_STOP ; Check: Is current end line for cursor = 12
JB bd2 ; Branch if less than 12
MOV AH, READ_VIDEO_STATE ; Set up to return current video state
INT 10H ; Go get it
CMP AL, GRAPHICS_CUR_STOP ; Is this a 80x25 BW card
MOV CX, MONO_CUR_DEF ; Set start/stop lines for cursor to 11/12
JE bd1 ; Yes it is 80x25 BW card, branch
MOV CX, GRAPHICS_CUR_DEF ; No its not 80x25 card, start/stop lines are 6/7

bd1:
MOV AH, SET_CURSOR_TYPE ; Set function call to set cursor type
INT 10H ; Do it

bd2:
MOV CS:WORD PTR [inline_mod2+1],CX ; Save the cursor type setting in line
POP DX
POP CX
POP BX
MOV BX,DX ; DX points to offset of input buffer
MOV AL,[BX] ; Get the number of characters in the buffer
CMP AL,02H ; Is it bigger than 2 characters?
JB sc_ret ; Nope, we have a problem here!
DEC AL ; Now decrement AL to get number of real characters
MOV AH, 00H ; buffer han hold. Zero out AH
MOV CS:WORD PTR [inline_mod5+2],AX ; AX now contains the max number of chars that the buffer
; can hold.
ADD BX,02H ; BX points to address at which to put first real character
; in input buffer
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; AUTOMATIC BUFFER TOGGLE (Why, I don't know)
auto_buffer_toggle: ;;
MOV BP,DS ;; Place the DS register into BP
PUSH DS ;; Save the value of the DS register
MOV DI,CS ;; Place the CS register into DI
MOV DS,DI ;; and also into DS
CMP BP,DI ;; Compare DS : CS values
JB abt1 ;; If DS < CS then jump
;;
inline_mod4: ;;
CMP DI,0888H ;; Compare CS : AUX (this in-line address will be called AUXiliary)
JE abt2 ;; If its equal (if we came this way last time!) then go swap
JMP SHORT after_swap_loop ;; If so then avoid swaps
;;
abt1: ;;
MOV BP,DI ;; Set BP to highest register value (now determined to be CS)
CMP DI,DS:WORD PTR [inline_mod4+2] ;; Compare CS : AUX
JE after_swap_loop ;; If its not equal (if we came this way last time!) then go swap
;;
abt2: ;;
PUSH CS ;; Now we must swap the appropriate memory locations to allow the
POP ES ;; editor to use the other buffer (There are two buffers that one
;; can transfer between depending on DS : CS relationship.
MOV SI,offset last_entry ;; Load SI with starting address
LEA DI,[SI+08H] ;; Load DI with second starting address
MOV CX,0004H ;; 4 words are to be moved ... in order ... address of last string accessed,
;; address of highest available byte, base address of text holding buffer,
;; high address of holding buffer.
;; Values are initiallized to:
;; 1st buffer - 60h, 60h, 60h, 160h
;; 2nd buffer - 160h, 160h, 160h, 260h
;;
swap_loop: ;;
MOV AX,[DI] ;; Save the value in the secondary buffer
MOVSW ;; Move the primary buffer value into the secondary buffer position
MOV [SI-02H],AX ;; Replace emptied location with saved value
LOOP swap_loop ;; Go back and swap next set of values
;;
after_swap_loop: ;;
MOV DS:WORD PTR [inline_mod4+2],BP ;; Load AUX such that AUX = Max(CS, DS)
POP DS ;; Restore DS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MOV BP,000CH
JMP SHORT rib1

reinit_buffer:
MOV BP,0004H

rib1:
XOR SI,SI ; Zero out Source index
MOV CS:WORD PTR [inline_mod3+1],SI ; Modify inline address just above
XOR DI,DI ; Zero out Destination index
MOV ES,DI ; Zero out the ES register
AND BYTE PTR ES:[kbd_flag], INSERT_OFF_MASK ; Turn insert off

start_read_char:
AND BP,0FFFEH ; Turn off low bit in BP (Line may be shorter)
CALL set_cursor ; Set the cursor shape as per mode
XCHG AX,DX ; Save AX in DX
MOV AH, INPUT_CHR_NOECHO ; Get a character from the keyboard
INT 21H ; Call DOS for service
CMP AL, ACK ; Is it an acknowledge character?
JE start_read_char ; If so ... just forget it and go back
MOV CX, offset start_read_char ; Save address of start_read_char label
PUSH CX ; This is a return address for a nonexistent call!
OR AL,AL ; Set up for test
JNE normal_keyboard_chr ; Jump if non null character input

extended_keyboard_chr:
MOV AH, INPUT_CHR_NOECHO ; Null character, get ready to read next character
INT 21H ; Get it
PUSH ES ; Save ES
PUSH CS ; Set ES to value in CS
POP ES
PUSH DI ; Save DI
MOV CX,000FH ; Set counter to number of items in table 1
MOV DI, offset [table1] ; Set starting address of table 1
repnz SCASB ; Scan for occurrence of second character in table 1
POP DI ; Restore DI
POP ES ; and ES
XCHG CX,BX ; Let BX contain pointer to item found in table 1
ADD BX,BX ; Double it
PUSH CS:WORD PTR [BX+table2] ; Index into table 2 for starting address of service code
; The value is pushed onto the stack to allow for the
; return (Without a corresponding call!)
MOV BX,CX ; Restore original BX value
RET ; Execute the fake return


;***************************************************************************
; Individual service routines
;***************************************************************************
;*********************************************************************************** Escape Key
escape_handling: ; Escape character here
CALL home_key ; Go to start of line
CALL cntrl_end ; Delete to end

restart_input:
POP DI ; Pop the return address off of the stack
JMP SHORT reinit_buffer ; Jump to reinitialize buffer and read

normal_keyboard_chr:
CMP AL, CNTL_BACKSPACE ; Control Backspace?
JE nkc1 ; if so, go to backspace handling
CMP AL, BACKSPACE ; Backspace?
JNE nkc2 ; If not continue

nkc1:
JMP backspace_handling ; Backspace here, take care of it

nkc2:
CMP AL, CR ; Is it a carriage return?
JNE nkc3 ; If not continue
JMP cr_key ; Yes, go take care of it

nkc3:
CMP AL, ESC ; Is it an escape character
JE escape_handling ; If so, go take care of it

CMP AL, TAB ; Bad code here ** remove this **
JE nkc4 ; Bad code here ** remove this **

inline_mod5:
nkc4: CMP DI,0888H ; Compare DI to max buffer size (modified in line in Begin_Dosed)
JB printable_char ; If less than max then continue with printable character
MOV AL, BELL ; Prepare to ring the bell
JMP display_one_char ; Do it


;*********************************************************************************** Non editing character
printable_char:
AND BP,0FFF7H ; Turn off the no changes made flag
OR BP,0002H ; Turn on the changes made to this line flag
PUSH AX ; Save AX
CALL display_and_test ; go display the character
MOV DX,ES:[cursor_posn] ; Place the current cursor position in DX
TEST BYTE PTR ES:[kbd_flag], INSERT_ON_MASK ; Is insert mode mask on?
JE add_chr_to_inbuf ; If not then branch around shift up string
PUSH DI ; Save the DI register

shift_up:
CMP DI,SI ; Are we pointing past the end?
JBE end_shift_up ; Have we decended to the original point?
MOV AL,[BX+DI-01H] ; Move this character in input buffer up
MOV [BX+DI],AL ; one byte in memory
DEC DI ; Next character down
JMP SHORT shift_up ; Return for more

end_shift_up:
POP DI ; Restore original end of input buffer pointer
INC DI ; Increment it by one
INC SI ; Also increment the current character pointer
CALL redisplay_line
DEC SI ; Now decrement source so that it will look
; like normal overwrite to next code section

add_chr_to_inbuf:
POP AX ; Restore AX
MOV AH,AL ; Move the current character into AH
XCHG AL,[BX+SI] ; Put it into the input buffer
INC SI ; Point to next character position
CMP DI,SI ; Have we gone past the end
JNB acti1 ; If not then forget update of DI
MOV DI,SI ; DI is now reset to SI (which was >)

acti1:
AND AL,AH ; AND old character against new character
CMP AL, BLANK ; If both are printable then we are finished
; because we have coundn't have shortened
JNB acti_ret ; the line with the overwrite. (eg. A over ^E
; would shorten line by one character)
TEST BYTE PTR ES:[kbd_flag], INSERT_ON_MASK ; If insert mode was on then we
JNE acti_ret ; are finished also because we have already
; printed the string
CMP SI,DI ; Also we can forget if we are at the end of the line
JE acti_ret ; None of the above hold, and we may have trailing
JMP note_shorter_change ; characters on the line that need to be blanked
; over. (caused by overwriting a ^x diagraph or a
; Tab character with a single character.)
acti_ret:
RET


;*********************************************************************************** End key
end_key:
TEST BP,0008H ; Have we made any changes to this buffer?
JE move_to_end ; If so then go ahead an position to end of line.
AND BP,0FFF7H ; No changes, lets now indicate that changes have been made,
OR DI,DI ; and restore the input buffer. Start by checking DI for zero.
JNE move_to_end ; Go restore if something is there.
MOV AL,[BX-01H] ; Get the length of the string in the holding buffer.
CMP [BX-02H],AL ; Is it larger than the max buffer size?
JB recycle_input ; If so then we have problems and a restart is necessary.
MOV AH,00H ; Zero out AH
XCHG AX,DI ; Move the new length into DI
CMP BYTE PTR [BX+DI], CR ; Is there a carriage return here
JE move_to_end ; If so then go ahead and restore it

recycle_input:
JMP restart_input ; No, we must have garbage in the input buffer, go back and restart

move_to_end:
CALL right_arrow ; move over one character to the right
JB move_to_end ; keep going if not at end

error_return:
RET


;*********************************************************************************** Control home key
cntrl_home:
PUSH SI ; Save the pointer into the input buffer
CALL home_key ; Move to start of line
POP SI ; Restore pointer
MOV CX,DI ; Set CX to character count in string and remove the
SUB CX,SI ; characters from start of line to current position from count.
XOR DI,DI ; Clear out the DI register
PUSH DI ; Save it. ** get rid of this **
PUSH CX ; and the new count of characters.
JCXZ after_drop_down ; If the new count of characters is zero then nothing else to do.

drop_text_down:
MOV AL,[BX+SI] ; Get the current character
INC SI ; Advance source pointer to next character
MOV [BX+DI],AL ; Store it down into the next destination available
INC DI ; Increment the destination pointer
LOOP drop_text_down ; and continue for the count

after_drop_down:
POP DI ; Restore DI
POP SI ; and SI ** change this to xor SI,SI **
JMP note_shorter_change ; Now go redisplay line on screen


;*********************************************************************************** Control end key
cntrl_end:
OR BP,0002H ; Turn on the change to line flag
MOV CX,CS:WORD PTR [inline_mod3+1] ; Get the screen position of the last character in the screen

blank_to_end:
CMP CX,ES:[cursor_posn] ; Is the current screen position at the end of line?
JBE after_blanking ; If so then jump
CALL display_blank ; Display a blank
JMP SHORT blank_to_end ; and continue until end of line position on screen is reached

after_blanking:
MOV DI,SI ; The new line length is current offset in the line
JMP place_cursor ; and now place the cursor on the screen


;*********************************************************************************** Control right arrow
cntrl_right_arrow: ; Move right to start of next word
CALL right_arrow ; Move right one character
JNB cra_ret ; If at the end then fall out of loop
CALL check_non_by_alpha ; Check for non-alpha/alpha combination (return issued from subroutine
; if the condition is satisfied)
JMP SHORT cntrl_right_arrow ; continue the cycle

cra_ret:
RET

check_non_by_alpha:
MOV AL,[BX+SI] ; Get character
CALL check_alpha ; See if this is an alphabetic character
JB cnba_ret ; If not then we are finished
MOV AL,[BX+SI-01H] ; Get previous character
CALL check_alpha ; Is this an alpha?
JNB cnba_ret ; If so then we are still not happy and must continue searching.
POP AX ; Pop off the fake return address if this location points
; to an alpha character and the previous one doesnt.
cnba_ret:
RET

check_alpha:
OR AL, LOWER_CASE_MASK ; Or in lower case bit
CMP AL, SMALLA ; Is this the letter "a"?
JB cnba_ret ; It is less than the letter "a" ... return
CMP AL, LEFT_CURLY_BRACE ; Is this the character "{"? (one greater than "z")
CMC ; Complement Carry flag so its on if < "{"
RET

;*********************************************************************************** Right arrow
right_arrow:
CMP SI,DI ; Are we past the last character
JNE redisplay_char ; If not then continue
RET

redisplay_char:
MOV AL,[BX+SI] ; Move the current character into AL
INC SI ; Bump the pointer #U8z]ge9^Z
into input buffer
CALL display_and_test ; Redisplay that character
STC ; Set the carry flag
RET

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
L0474: CALL right_arrow ;\ ;;
JNB L0482 ; \ ;;
MOV AL,07H ; \ This code is not called. ;;
AND AL, BYTE PTR ES:[cursor_posn] ; / ;;
JNE L0474 ; / ;;
L0482: RET ;/ ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


;*********************************************************************************** Control left arrow
cntrl_left_arrow:
CALL left_arrow ; Move left one character
JB bt_ret ; Jump if we are at the end of the buffer
CALL check_non_by_alpha ; See if we are at the start of an alph character string.
JMP SHORT cntrl_left_arrow ; If not then loop (if we were then the return was issued from
; check_non_by_alpha)

;*********************************************************************************** Backtab key
backtab:
CALL left_arrow ; move left one character
JB bt_ret ; If at the start of the line then quit
MOV AL,07H ; set up the mask
AND AL,DL ; check to see if we are on an 8 byte column.
JNE backtab ; If not then continue left.

bt_ret:
RET


;*********************************************************************************** Left Arrow
left_arrow:
OR SI,SI ; Are we at the start of the line
STC ; Set the carry flag
JE cnba_ret ; If so then do nothing
DEC SI ; Move the input buffer pointer back one
CALL decrement_col_row ; Adjust the col row positon
CMP BYTE PTR [BX+SI], TAB ; Have we just run over a tab?
JE back_over_tab ; If so then go expand it
CMP BYTE PTR [BX+SI], BLANK ; Compare for printable character
JNB la_end ; If we have one then jump to final section
CALL decrement_col_row ; Decrement col row one more time (for ^ character)
JMP SHORT la_end ; Finish up
; Expand the tab character
back_over_tab:
PUSH SI ; Save the SI pointer into input buffer
MOV CX,SI ; Set the count to its value
MOV AL,CL ; and also AL
MOV AH,07H ;
JCXZ no_prev_tabs ; At start of line (no previous tab to align us)
; See if we can find another tab in front of us to
; align us on screen (character after that tab
; would be properly aligned on 8 byte boundary.)
align:
DEC SI ; Decrement the pointer into input buffer
CMP BYTE PTR [BX+SI], BLANK ; Check for printable character
JNB align_end ; If found cycle in loop
CMP BYTE PTR [BX+SI], TAB ; Have we succeded in finding another tab?
JE sub_prev_char_cnt ; If so then we know we are aligned so jump
DEC AH ; No, this must be a control character diagraph (^x form)
; so we need to subract an additional character from the
align_end: ; count of characters found so far.
LOOP align ; Go back for more

no_prev_tabs: ; Ok, we only get here if there were no previous
; characters in input buffer or if there were no previous
; tabs in the buffer
SUB AH,CS:[start_off] ; Subtract out the screen bias position for location 0 of the
; input buffer pointer.
sub_prev_char_cnt:
SUB AH,AL ; These two statements remove the count of all previous characters
ADD CL,AH ; in the buffer (either to tab boundary or start of line).
; Additional diagraph hats "^" have been previously subtracted
; from AH.
AND CL,07H ; take modulo 8
JE end_jump_tab ; Did this tab expand into only one blank (remember we've already
; moved over one!), if so jump
jump_tab:
CALL decrement_col_row ; Calculate new column and row on screen
LOOP jump_tab

end_jump_tab:
POP SI ; Restore input buffer pointer

la_end:
CLC ; Clear the carry flag
JMP SHORT place_cursor ; Go place the cursor on the screen
NOP


;*********************************************************************************** Home key
home_key: ; home key service begins here
CALL left_arrow ; move over one
JB pc_ret ; jump if back at start
JMP SHORT home_key ; Not there yet, keep going


;*********************************************************************************** Backspace key
backspace_handling: ; backspace handling begins here
CALL left_arrow ; first just move over one character
JB pc_ret ; If we are already at the start of the line
; then we neednt continue

;*********************************************************************************** Delete Key
del_key: ; Delete key handling begins here
OR DI,DI ; Is the string length zero?
JE pc_ret ; If so go to end
CMP SI,DI ; Are we past the end of the string?
JE pc_ret ; If so go to end
PUSH SI ; Save the SI pointer to input buffer

shift_down:
CMP SI,DI ; Are we past the end:
JE after_shift_down ; If so, finish up
MOV AL,[BX+SI+01H] ; Get next character up
MOV [BX+SI],AL ; then shift it down into this slot
INC SI ; Increment the input buffer pointer
JMP SHORT shift_down ; and cycle

after_shift_down:
POP SI ; Restore the input text buffer pointer
DEC DI ; Decrement the length of the input buffer string

note_shorter_change:
OR BP,0003H ; Indicate that we have changed and potentially shortened the string.

redisplay_line:
AND BP,0FFF7H
PUSH SI ; Save the pointer into the input buffer

display_next_char:
CMP SI,DI ; Are we past the end of the string?
JNB after_display_char ; If so dont display
MOV AL,[BX+SI] ; Get the character from the input buffer
CALL display_and_test ; send it to the display
INC SI ; update buffer pointer
JMP SHORT display_next_char ; continue to end

after_display_char:
CALL set_cursor ; Set type of cursor, AX returns with current cursor location (end of line)
TEST BP,0001H ; Is the old line longer?
JE after_blank_pad ; If not then forget blanking the tail
XCHG AX,CS:WORD PTR [inline_mod3+1] ; Put current cursor positon in end of line indicator and
; save that value for blanking operation
XCHG AX,CX ; Put former end of line position in CX


add_blank_pad:
CALL display_blank ; Display a blank
CMP CX,ES:[cursor_posn] ; Is current cursor position now at end of line
JNBE add_blank_pad ; If not, then continue blanking loop.

after_blank_pad:
POP SI

place_cursor:
MOV AH, SET_CURSOR_POSN ; Load AH for Bios call
PUSH BX ; Save BX and CX registers
PUSH CX
MOV CS:[current_col],DL ; Record current column position
MOV BH,00H
INT 10H ; Call bios for service
POP CX ; Restore registers
POP BX

pc_ret:
RET


;*********************************************************************************** Up Arrow
up_arrow: ; Up arrow service starts here
STD ; Set direction flag to decrement

;*********************************************************************************** Down Arrow
down_arrow: ; Down arrow service starts here
AND BP,0FFF7H
CALL home_key ; Go to beginning of line
CALL cntrl_end ; Delete to end of line

da1:
PUSH ES ; Save ES register
PUSH DS ; Set ES to DS
POP ES
PUSH CS ; Set DS to CS
POP DS
MOV SI,DS:[last_entry] ; Get pointer to first byte of current string in
; input buffer (from text buffer)
LODSB ; Obtain the character
LEA DI,[BX-01H] ; Load DI with the offset of the length character
; of the input buffer
MOV CX, TEXT_BUFFER_SIZE ; Set counter to look at each location in the text buffer

search_for_len_byte:
CALL get_char ; Get a character from the text holding buffer
TEST AL, LENGTH_BYTE_MASK ; Is it a length byte?
LOOPZ search_for_len_byte ; Nope, go back and try again
CLD ; Yep, clear the direction flag
JNE found_a_len_byte ; Jump if we found a length byte
PUSH ES ; Restore DS
POP DS
POP ES ; Restore ES
JMP restart_input

found_a_len_byte:
AND BP,0FFF1H ; Indicate new line from holding buffer, and no changes made to it.
CALL get_char ; Get a dummy character to readjust the SI pointer
TEST BYTE PTR [SI], LENGTH_BYTE_MASK ; Is next location a length byte
JNE recover_string ; If so (then down arrow issued) jump to recover string
DEC SI ; No, an up arrow was issued and we need to back SI down two bytes to
; set up for next get_char to be the length byte.
CALL swap_and_get_tp ; Back a-one
DEC SI
CALL swap_and_get_tp ; and a-two

recover_string:
MOV DS:[last_entry],SI ; Load the address of the last accessed buffer string
CALL get_char ; Get the length character from holding buffer
AND AL, LENGTH_OFF_MASK ; Mask off the length byte identifier
MOV AH, BYTE PTR ES:[DI-01H] ; Place Max buffer size in AH
CMP AL,AH ; Is text string smaller than max buffer size?
JB continue_recovery ; If so then continue recovery
MOV AL,AH ; Uh-oh, take the max buffer size as the count of chars to restore

continue_recovery:
STOSB ; Save the length count
CBW ; Get rid of high portion of AX
XCHG AX,CX ; Place the count in CX
PUSH CX ; and save it for later

load_chr_and_display:
CALL get_char ; Get a character from the holding buffer
STOSB ; Store this character in the input buffer
CALL display_and_test ; Display this character
LOOP load_chr_and_display ; Continue until all characters are done
POP DI ; Place the count of the number of characters in the
; string into DI
MOV SI,DI ; Require the current pointer to be just past the end
PUSH ES ; Restore DS
POP DS
POP ES ; Restore ES

lcad_ret:
RET

;*********************************************************************************** Control Page Down
cntrl_page_down:
TEST BP,0006H ; Are we on the original line or have we made mods
JNE lcad_ret ; If so then do nothing
PUSH DS ; Save some registers
PUSH ES
PUSH BX
CALL home_key ; Move to start of line
CALL cntrl_end ; Delete to the end of the line
PUSH CS ; Set ES register to CS ** change to mov **
POP ES
PUSH CS ; Set DS register to CS
POP DS
MOV DI,DS:[last_entry] ; Get address of last string accessed in holding buffer
PUSH DI ; Save it.
MOV SI,DI ; Point the source index to it.

skip:
CALL get_char ; Get a character from the text buffer
CMP AL, CR ; Is it a carriage return?
JNE skip ; If n~r~_ot keep going

move_down:
CMP SI,DS:[next_entry] ; Are we at the next available location in buffer
JE pad_crs ; If so then break out of loop
CALL get_char ; Get this character
CALL save_char ; and move it down in the holding buffer.
JMP SHORT move_down ; Continue the loop.

pad_crs:
MOV AL, CR ; Load AL with a carriage return
MOV [DI],AL ; and into the buffer
MOV BX,DI ; Now reset the base pointer one beyond last string in holding buffer.
XCHG BX,DS:[next_entry] ; Put the old value in BX and the new value in place
POP SI ; Restore the pointer to last string accessed

continue_pad_cr:
CMP BX,DI ; Are we at the old next_available slot yet?
JE after_pad_cr ; If yes the break out

CALL save_char ; No, save the carriage return
JMP SHORT continue_pad_cr ; Keep going

after_pad_cr:
DEC SI ; Point to just before this entry (so Down arrow will pick it up)
MOV DS:[last_entry],SI ; Save it as last accessed text entry
POP BX ; Restore registers.
POP ES
POP DS
JMP da1 ; Go perform a down arrow operation

;*********************************************************************************** Carriage Return
cr_key:
AND BYTE PTR ES:[kbd_flag], INSERT_OFF_MASK ; Turn off insert mode
CALL set_cursor ; Change the cursor on the screen
POP AX ; Pop the fake return address off the stack (so we really return)
CALL move_to_end ; Move the cursor to the end of the line
MOV AL, CR ; Move a CR into AL
CALL display_one_char ; Display it
MOV [BX+DI],AL ; Add the CR to the input buffer
MOV AX,DI ; Move the length into AX
MOV [BX-01H],AL ; Now move it into the length byte of the input buffer
MOV SI,BX ; Point the Source index to the input buffer
CMP AL,02H ; Are there at least two characters? ** remove this? **
JBE crk_ret ; If not then just forget the save
PUSH CS ; Set ES to value in CS
POP ES
INC WORD PTR CS:[last_entry] ; Bump the pointer to the strings location in the text buffer
TEST BP,0002H ; Have changes been made to this line?
JE crk_ret ; If not then we are through
MOV DI,CS:[next_entry] ; Point the destination to the next available slot
OR AL, LENGTH_BYTE_MASK ; Add in the length byte bit mask

save_string:
CALL save_char ; Add it to the holding buffer
CMP AL, CR ; Is this a carriage return?
JE after_save_string ; If so, break out of loop
LODSB ; Get the next character from the input buffer
JMP SHORT save_string

after_save_string:
MOV CS:[next_entry],DI ; Save the new pointer to the text holding buffer

omit_partial_string:
CMP CS:[DI],AL ; Is it a carriage return?
JE after_omit_partial ; If so we are finished
CALL save_char ; No, that means there is a partial string here
JMP SHORT omit_partial_string ; so fill with CRs until next CR is found

after_omit_partial:
MOV CS:[last_entry],DI

crk_ret:
RET

get_char: ; Obtain a character from the text holding buffer
CALL swap_and_get_tp ; First we must reverse SI and DI pointers
LODS CS:[SI] ; Load the character from the text holding buffer into AL

swap_and_get_tp: ; Reverse SI and DI pointers because
XCHG SI,DI ; get_text_pointer assumes that DI indexes
CALL get_text_pointer ; into the text holding buffer and not SI
XCHG SI,DI ; Verify legal pointer into holding buffer
RET

save_char: ; Add a character to the text holding buffer
CALL get_text_pointer ; Verify correctness of pointer
STOSB ; Move the character in AL to the text holding buffer

get_text_pointer: ; Obtain a legal pointer into the text holding buffer
CMP DI,CS:[buffer_bot] ; Is the current pointer too small?
JNB gtp1 ; If not, then continue
MOV DI,CS:[buffer_top] ; Yes, set to upper limit
DEC DI ; and decrement one

gtp1:
CMP DI,CS:[buffer_top] ; Is the current pointer too big?
JB gtp_ret ; If not jump to end
MOV DI,CS:[buffer_bot] ; Yes, set to lower limit

gtp_ret:
RET

;*********************************************************************************** Control Page Up
cntrl_page_up: ; Control Page Up service begins here
CALL home_key ; Go to start of line
CALL cntrl_end ; And delete forward
PUSH DS ; Save DS
MOV AX,CS ; Set DS and ES to value in CS
MOV DS,AX
MOV ES,AX
MOV DI,DS:[buffer_bot] ; Get base address of text buffer
MOV DS:[last_entry],DI ; Set current pointer into text buffer to base
MOV DS:[next_entry],DI ; Set next new pointer into text buffer to base
MOV CX, TEXT_BUFFER_SIZE ; Set count to size of text holding buffer
MOV AL, CR ; Set AL with CR and fill holding buffer
repz STOSB ; with carriage returns
POP DS ; Restore DS
JMP restart_input

; Data area
start_off db 0h
current_col db 0h
last_entry dw TEXT_BUFFER_START
next_entry dw TEXT_BUFFER_START
buffer_bot dw TEXT_BUFFER_START
buffer_top dw TEXT_BUFFER_END
aux_last dw AUX_BUFFER_START
aux_next dw AUX_BUFFER_START
aux_bot dw AUX_BUFFER_START
aux_top dw AUX_BUFFER_END

initialize:

DOSED ENDP
CSEG ENDS
END DOSED


  3 Responses to “Category : Assembly Language Source Code
Archive   : ASMSUB2.ZIP
Filename : DOS-EDIT.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/