home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
assemblr
/
library
/
sampler0
/
dos_ed.asm
< prev
next >
Wrap
Assembly Source File
|
1987-10-30
|
61KB
|
1,207 lines
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
ESCCHAR 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!
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 Int
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
MOV AH, OUTPUT_CHR ; (Display 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 scr
OR AL,0F8H ; Get the number of spaces to the
NEG AL ; next 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 0
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 ptr
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
MOV AH, SET_CURSOR_POSN ; 10 funct 2 call (Set cursor pos)
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 pos
inline_mod3:
CMP AX,0888H ; Compare current cursor position to end of line pos
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 char
; 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 editor to
POP ES ;; 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 val
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 addr 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, ESCCHAR ; Is it an escape character
JE escape_handling ; If so, go take care of it
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
JNE acti_ret ; on then we 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
JMP note_shorter_change ; trailing 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
OR DI,DI ; been made, 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 str 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 char 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 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:
XOR SI,SI ; Restore DI & 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 char 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 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 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 ptr 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.)
aalign:
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 found 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 aalign ; Go back for more
no_prev_tabs: ; Ok, we only get here if there were no previous
; chars 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 not 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 byte ptr 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