;======================================================================
; ENVEDIT 1.00 * Copyright (c) 1992, Robert L. Hummel
; PC Magazine Assembly Language Lab Notes
;
; ENVEDIT -- an editor that allows changes to be made to the DOS master
; environment interactively.
;======================================================================
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
ORG 100H ;Starting offset
ENTPT: JMP MAIN ;Jump over data
;----------------------------------------------------------------------
; Some common equates.
;----------------------------------------------------------------------
CR EQU 13 ;Common equates
LF EQU 10
INSKEY EQU 52H ;Extended ASCII values
DEL EQU 53H
F7KEY EQU 41H
F9KEY EQU 43H
HOME EQU 47H
ENDKEY EQU 4FH
RARROW EQU 4DH
LARROW EQU 4BH
UARROW EQU 48H
DARROW EQU 50H
BS EQU 0E08H ;Scan/Ascii code
;----------------------------------------------------------------------
; Text messages.
;----------------------------------------------------------------------
COPYRIGHT$ DB CR,"ENVEDIT 1.00 ",254," Copyright (c) 1992,"
DB " Robert L. Hummel",CR,LF
DB "PC Magazine Assembly Language Lab Notes"
DB CR,LF,"$"
NOENV$ DB "Can't Find The Environment",LF,"$"
MEMSIZ$ DB "Not Enough Memory",LF,"$"
MEMERR$ DB "Error Allocating Memory",LF,"$"
BADMODE$ DB "Can't Use This Video Mode",LF,"$"
SAVE$ DB "Save Changes? (Y/N) $"
;----------------------------------------------------------------------
; Video parameters.
;----------------------------------------------------------------------
NUM_COLS DB 0 ;Number cols on screen
COL_MAX DB 0 ;Rightmost column
VPAGE DB 0 ;Active page
ATTR DB 1FH ;Default is color
BW_ATTR EQU 07H ;May switch to mono
;----------------------------------------------------------------------
; Environment parameters.
;----------------------------------------------------------------------
ENV_SEG DW 0 ;Segment of master env
STR_COUNT DW 0 ;Number strings in Env
ENV_FREE DW 0 ;Free space in bytes
ENV_USED DW 0 ;Len of strings in Env
STR_LEN DW 0 ;Len of current str
;======================================================================
; MAIN (Near)
;
; This procedure invokes the subroutines and defines program operation.
;----------------------------------------------------------------------
MAIN PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
CLD ;Default moves forward
;----------------------------------------------------------------------
; Editing requires that we be in a mode with at least 80 columns on
; the screen. If the mode isn't okay, terminate with an error.
;----------------------------------------------------------------------
CALL VIDEO_SETUP ;Examine video hardware
JNC M_0
MOV DX,OFFSET BADMODE$
JMP M_ERR
M_0:
;----------------------------------------------------------------------
; Relocate the stack downward in the segment. Then shrink the program's
; allocated memory to hold just the program code and the stack.
;----------------------------------------------------------------------
CLI ;Disable interrupts
MOV SP,OFFSET STACK_TOP ; and Re-position stack
STI ;Allow interrupts
MOV AH,4AH ;Modify memory block
MOV BX,(OFFSET STACK_TOP - CSEG + 15) SHR 4
INT 21H ; Thru DOS
JNC M_1
MOV DX,OFFSET MEMERR$ ;Unspecified error
JMP M_ERR
M_1:
;----------------------------------------------------------------------
; Locate the master environment. By our definition, the master
; environment is the environment owned by the first copy of COMMAND.
; Note: if a new, permanent copy of COMMAND has been loaded with /P,
; this technique won't work.
;----------------------------------------------------------------------
MOV AH,52H ;Undocumented function
INT 21H ; returns ES:BX
ASSUME ES:NOTHING
MOV AX,ES:[BX-2] ;First MCB block adr
;----------------------------------------------------------------------
; Find the first PSP block in the chain that claims to be its own
; parent. Subject to disclaimer, this is the first copy of COMMAND.
;----------------------------------------------------------------------
M_2A:
MOV ES,AX ;Address MCB
ASSUME ES:NOTHING
INC AX ;Address of PSP
CMP AX,ES:[1] ;MCB = owner?
JNE M_2B
;----------------------------------------------------------------------
; This block owns itself, and so is a PSP.
; If it is also its own parent, we have the first copy of COMMAND.
;----------------------------------------------------------------------
CMP AX,ES:[16H+10H] ;Parent = MCB?
JE M_2D
;----------------------------------------------------------------------
; Didn't own itself. Move to the next block.
;----------------------------------------------------------------------
M_2B:
CMP BYTE PTR ES:[0],"Z" ;Is this last block?
JE M_2C
ADD AX,ES:[3] ;Go to next block
JMP M_2A
M_2C:
MOV DX,OFFSET NOENV$ ;Couldn't find it
JMP SHORT M_ERR
M_2D:
;----------------------------------------------------------------------
; We have located the first copy of COMMAND. In DOS versions 3.30+, the
; word at PSP:2C contains the segment address of the env. In earlier
; versions, the environment point is 0; use the following MCB.
;----------------------------------------------------------------------
MOV CX,WORD PTR ES:[3CH] ;Get env segment
JCXZ M_3A ;If 0, use next MCB
DEC CX ;Point to env's MCB
JMP SHORT M_3B
M_3A:
MOV CX,AX ;Block adr +
ADD CX,ES:[3] ; length = next MCB
M_3B:
MOV ES,CX ;Put env's MCB in ES
ASSUME ES:NOTHING
;----------------------------------------------------------------------
; Verify that this block is owned by COMMAND's PSP.
;----------------------------------------------------------------------
CMP AX,ES:[1] ;Proper ownership?
JNE M_2C
;----------------------------------------------------------------------
; Now, CX = ES -> MCB of what we'll call the master environment.
; From the MCB, get the length (<32k) of the block, and save it.
;----------------------------------------------------------------------
MOV BX,CX ;Put MCB in BX
MOV AX,ES:[3] ;Get length in paras
MOV DX,AX ;(Save for later use)
MOV CL,4 ; SHL 4 = *16
SHL AX,CL ; convert to bytes
MOV [ENV_FREE],AX ;Say its all free
;----------------------------------------------------------------------
; Now scan the entire environment and count the number of strings.
;----------------------------------------------------------------------
INC BX ;Point to env adr
MOV [ENV_SEG],BX ;Save for update/exit
MOV DS,BX ;Point ES and DS
ASSUME DS:NOTHING
MOV ES,BX ; to Master env
ASSUME ES:NOTHING
;----------------------------------------------------------------------
; Initialize the counter and the pointer. Jump to middle of loop.
;----------------------------------------------------------------------
SUB DI,DI ;DI = count of strings
SUB SI,SI ;SI = offset into env
;----------------------------------------------------------------------
; A zero byte indicates the end of a string.
;----------------------------------------------------------------------
M_4A:
LODSB ;Get a char
OR AL,AL ;If not 0, scan again
JNZ M_4A
INC DI ;Increase string count
;----------------------------------------------------------------------
; If a double-zero, that was the last string.
;----------------------------------------------------------------------
M_4B:
LODSB ;Get char
OR AL,AL ;If not 0, keep going
JNZ M_4A
;----------------------------------------------------------------------
; Save the string count and calculate space used and free.
;----------------------------------------------------------------------
PUSH CS ;Point DS to CSEG
POP DS
ASSUME DS:CSEG
DEC DI ;Make count 0-based
MOV [STR_COUNT],DI ; 0 = 1 empty string
MOV [ENV_USED],SI ; 1 = empty
SUB [ENV_FREE],SI ; = amount left
;----------------------------------------------------------------------
; Allocate a block of memory large enough to hold a full copy of this
; environment.
;----------------------------------------------------------------------
MOV BX,DX ;Get back size in paras
MOV DX,OFFSET MEMSIZ$ ;Assume an error
MOV AH,48H ;Allocate memory fn
INT 21H ; Thru DOS
JNC M_5
;----------------------------------------------------------------------
; Common exit to display an error message.
;----------------------------------------------------------------------
M_ERR:
MOV AH,9 ;Display string
INT 21H ; Thru DOS
;----------------------------------------------------------------------
; Common exit.
;----------------------------------------------------------------------
M_EXIT:
MOV DX,OFFSET COPYRIGHT$ ;Say who we are
MOV AH,9 ;Display string fn
INT 21H ; Thru DOS
MOV AH,4CH ;Terminate
INT 21H ; Thru DOS
;----------------------------------------------------------------------
; Make a scratch copy of this environment. Move from DS:SI to ES:DI.
;----------------------------------------------------------------------
M_5:
MOV CX,[ENV_USED] ;Move just strings
SUB SI,SI ;From DS:SI
PUSH ES
POP DS
ASSUME DS:NOTHING
SUB DI,DI ;To ES:DI
MOV ES,AX
ASSUME ES:NOTHING
REP MOVSB ;Transfer
PUSH CS ;Restore segment
POP DS
ASSUME DS:CSEG
;----------------------------------------------------------------------
; Draw the edit window.
;----------------------------------------------------------------------
CALL CLR_BOX ;Draw the window
;----------------------------------------------------------------------
; Invoke the string editor. Returns when F7 is pressed.
;----------------------------------------------------------------------
CALL EDIT ;String editor
;----------------------------------------------------------------------
; Ask if changes should be copied to the environment.
;----------------------------------------------------------------------
MOV BYTE PTR [CURSOR_COL],0 ;Reposition cursor
MOV BYTE PTR [CURSOR_ROW],NROW ; for message
CALL CUR_SET
MOV DX,OFFSET SAVE$ ;Clone the changes?
MOV AH,9 ;Display string fn
INT 21H ; Thru DOS
M_6:
MOV AH,8 ;Get a key
INT 21H ; Thru DOS
AND AL,NOT 20H ;Capitalize it
CMP AL,"N" ;If No...
JE M_7
CMP AL,"Y" ;If not Yes, try again
JNE M_6
;----------------------------------------------------------------------
; Copy the strings down to the master block. DS:SI TO ED:DI
;----------------------------------------------------------------------
MOV CX,[ENV_USED] ;New length of block
SUB SI,SI
PUSH ES ;Point DS:SI to copy
POP DS
ASSUME DS:NOTHING
SUB DI,DI
MOV AX,[ENV_SEG] ;ES:DI to master
MOV ES,AX
ASSUME ES:NOTHING
REP MOVSB ;Transfer
PUSH CS ;Restore segment
POP DS
ASSUME DS:CSEG
;----------------------------------------------------------------------
; Exit the program.
;----------------------------------------------------------------------
M_7:
JMP M_EXIT
MAIN ENDP
;======================================================================
; VIDEO_SETUP (Near)
;
; This procedure ensures that the number of columns on the screen is 80
; or greater and gets the current video page. It also adjusts the
; screen attribute for monochrome screens. It will allow the program to
; run in graphics mode, but won't guarantee a pretty screen. Return
; with carry set if display is in an incompatible mode.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; NC = Video mode is okay
; CY = Can't use this video mode
;----------------------------------------------------------------------
; Changes: AX BX DX
;----------------------------------------------------------------------
VIDEO_SETUP PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; The Get Video Mode function returns the number of columns (not the
; max col number) on screen in AH. We require at least 80 characters.
;----------------------------------------------------------------------
MOV AH,0FH ;Get video mode
INT 10H ; Thru BIOS
CMP AH,80 ;Enough columns?
JAE VS_1
;----------------------------------------------------------------------
; Note: We get here if AH is below 80; the carry flag is set by CMP.
;----------------------------------------------------------------------
JMP SHORT VS_EXIT
;----------------------------------------------------------------------
; Save the number of columns and the active video page.
; Change the display attribute for monochrome display modes.
;----------------------------------------------------------------------
VS_1:
MOV [VPAGE],BH ;Save current page
MOV [NUM_COLS],AH ;Save cols
SUB AH,(2+1) ;Indent 2 (1-based)
MOV [COL_MAX],AH ;Is rightmost column
CMP AL,7 ;Normal mono
JE VS_2
CMP AL,15 ;EGA mono
JNE VS_3
VS_2:
MOV [ATTR],BW_ATTR ;Assume mono
VS_3:
CLC ;Indicate success
VS_EXIT:
RET
VIDEO_SETUP ENDP
;======================================================================
; CLR_BOX (Near)
;
; Clear a window (box) for our information on the screen.
; Add a border for a nice touch.
;----------------------------------------------------------------------
; Entry: None
; Exit: None
;----------------------------------------------------------------------
; Changes: AX BX CX DX SI
;----------------------------------------------------------------------
TITLEZ DB 181,"ENVEDIT 1.00",198,0
TITLE_LEN EQU $-TITLEZ
HELPZ DB "STRING: ",27,32,26," INS DEL ",24,32,25
DB " F7 = Exit/Save F9=Add String",0
BOX_CHARS DB 201,205,187 ;Describes left, middle, and
DB 186, 32,186 ; right chars for each row
DB 199,196,182
DB 186, 32,186
DB 199,196,182
DB 186, 32,186
DB 200,205,188
NROW EQU ($-OFFSET BOX_CHARS)/3 ;Number of rows
CLR_BOX PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Clear the box area to set the attribute for the characters.
;----------------------------------------------------------------------
MOV AX,0700H ;Scroll window fn
MOV BH,[ATTR] ; clear to this color
SUB CX,CX ;Start row,col
MOV DH,NROW+3 ;3 lines below box
MOV DL,[NUM_COLS] ;Max column
DEC DL
INT 10H ;Thru BIOS
;----------------------------------------------------------------------
; Draw the edit window row by row.
;----------------------------------------------------------------------
MOV BH,[VPAGE] ;Get active page
MOV SI,OFFSET BOX_CHARS ;Edit window chars
MOV CX,NROW ;Number of rows to draw
SUB DH,DH ;Starting row
CB_1:
PUSH CX ;Save counter
SUB DL,DL ;Column=0
MOV AH,2 ;Mov to DH,DL
INT 10H ; Thru BIOS
LODSB ;Get leftmost char
MOV AH,0EH ;Write char TTY
INT 10H ; Thru BIOS
LODSB ;Get middle char
MOV AH,0AH ;Write repeated char
MOV CL,[NUM_COLS] ;Width of box
SUB CH,CH ; into CX
DEC CX ;Minus left side
DEC CX ;Minus right side
INT 10H ; Thru BIOS
MOV AH,2 ;Position cursor
MOV DL,[NUM_COLS] ; to far right
DEC DL ; column
INT 10H ; Thru BIOS
LODSB ;Get rightmost char
MOV AH,0AH ;Write char
MOV CX,1 ;1 copy
INT 10H ; Thru BIOS
INC DH ;Next row
POP CX ;Restore counter
LOOP CB_1
;----------------------------------------------------------------------
; Embed the program name and version in the border.
;----------------------------------------------------------------------
SUB BH,BH
MOV BL,[COL_MAX] ;Rightmost column
SUB BL,TITLE_LEN+2 ;Backup
MOV SI,OFFSET TITLEZ ;Program name
CALL WRITE_MSG
;----------------------------------------------------------------------
; Display the help prompts.
;----------------------------------------------------------------------
MOV BX,0102H ;Row/col
MOV SI,OFFSET HELPZ ;Instructions
CALL WRITE_MSG
RET
CLR_BOX ENDP
;======================================================================
; WRITE_MSG (Near)
;
; Write an ASCIIZ string to the screen at the indicated row and column.
;----------------------------------------------------------------------
; Entry:
; BH = screen row
; BL = screen column
; DS:SI = Offset of ASCII string to display
;----------------------------------------------------------------------
; Changes: AX BX
;----------------------------------------------------------------------
WRITE_MSG PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
XCHG BX,[CURSOR_POS] ;Set requested position
PUSH BX ;Save old one
CALL CUR_SET ;Position cursor
WM_1:
MOV BH,[VPAGE]
LODSB
OR AL,AL
JZ WM_2
MOV AH,0EH
INT 10H
JMP WM_1
WM_2:
POP [CURSOR_POS] ;Restore prev cursor
CALL CUR_SET
RET
WRITE_MSG ENDP
;======================================================================
; EDIT (Near)
;
; The EDIT procedure handles all the editing. It keeps track of the
; environment strings and displays them on the screen as they change.
;----------------------------------------------------------------------
; Changes:
;----------------------------------------------------------------------
STR_NUMBER DW 0 ;Number of env string
STR_START DW 0 ; and offset into seg
STR_LEFT DW 0 ;Offset leftmost char
; displayed on screen
CURSOR_POS LABEL WORD
CURSOR_COL DB 0 ;Current cursor
CURSOR_ROW DB 0 ; position on screen
INS_STATE DB 0 ;0=INS FF=TYPEOVER
FAR_LEFT EQU 2 ;Leftmost column
DISPLAY_ROW EQU 5 ;Strings appear here
EDIT PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Initialize the pointer to point to the current string.
; Display the selected string on the screen as read from memory.
;----------------------------------------------------------------------
MOV [STR_NUMBER],0 ;First string
MOV [CURSOR_ROW],DISPLAY_ROW ; goes here
;----------------------------------------------------------------------
; If environment is empty,insert an empty string to work on.
;----------------------------------------------------------------------
EDIT_1A:
CMP [STR_COUNT],-1 ;-1=no strings
JNE EDIT_1B
CALL ADD_STRING ;Add an empty string
EDIT_1B:
CALL GET_START ;Point to it
MOV [CURSOR_COL],FAR_LEFT ;Init cursor
CALL UPDATE
;----------------------------------------------------------------------
; Display the current string in the window.
;----------------------------------------------------------------------
EDIT_2:
CALL DISPLAYX ;Show the string
;----------------------------------------------------------------------
; Get a key from the keyboard and act on it.
;----------------------------------------------------------------------
EDIT_3A:
SUB AH,AH ;Fetch a key
INT 16H ; Thru BIOS
OR AL,AL ;If AL=0, is extended
JZ EDIT_5A ; which means command
CMP AX,BS ;If not actual BS key
JNE EDIT_4A ;Process as char
;----------------------------------------------------------------------
; The backspace key is the only key that requires special handling.
; Treat BS as a CURSOR-LEFT/DELETE combination.
;----------------------------------------------------------------------
CALL CURSOR_LEFT
JC EDIT_3A
EDIT_3B:
CALL STRING_DEL ;Delete char at cursor
JC EDIT_3A
JMP EDIT_2
;----------------------------------------------------------------------
; Put the character on the screen and in the string.
;----------------------------------------------------------------------
EDIT_4A:
CMP [INS_STATE],0 ;If insert
JE EDIT_4B ; jump
;----------------------------------------------------------------------
; If we're at the end of the string, typeover works just like insert.
;----------------------------------------------------------------------
CALL LOCATE_SI ;If char isn't 0
CMP BYTE PTR ES:[SI],0 ; goto overwrite
JNZ EDIT_4C
;----------------------------------------------------------------------
; Move chars to the right, add the new character.
;----------------------------------------------------------------------
EDIT_4B:
CALL STRING_INS ;Create hole at cursor
JC EDIT_3A
EDIT_4C:
CALL LOCATE_SI ;Point to cursor loc
MOV ES:[SI],AL ; and pop in char
CALL DISPLAYX ;Show changes
;----------------------------------------------------------------------
; -> Move the cursor to the right one space.
;----------------------------------------------------------------------
EDIT_4D:
CALL CURSOR_RIGHT ;Move cursor along
JMP EDIT_3A
;----------------------------------------------------------------------
; Key is an extended key. Must be an instruction.
;----------------------------------------------------------------------
EDIT_5A:
CMP AH,F9KEY ;F9=make new string
JNE EDIT_5B
CALL PURGE_STR ;Del string if empty
CALL ADD_STRING ;Add an empty string
JMP EDIT_1B
EDIT_5B:
CMP AH,F7KEY ;F7 is the exit key
JNE EDIT_6
CALL PURGE_STR ;Del string if empty
CMP [STR_COUNT],-1 ;Env totally empty?
JNE EDIT_5C
CALL ADD_STRING ;Min is one empty str
EDIT_5C:
RET ; and the only way out
;----------------------------------------------------------------------
; All remaining key dispatch done from here.
;----------------------------------------------------------------------
EDIT_6:
CMP AH,DEL ;Kill char at cursor
JE EDIT_3B
CMP AH,RARROW ;Move right 1 char
JE EDIT_4D
CMP AH,LARROW ;Move left
JE EDIT_9A
CMP AH,UARROW ;Move up
JE EDIT_7A
CMP AH,DARROW ;Move down
JE EDIT_8A
CMP AH,INSKEY ;Use Insert mode
JE EDIT_10
CMP AH,ENDKEY ;Move to end of string
JE EDIT_11
CMP AH,HOME ;Move to start of str
JE EDIT_12
JMP EDIT_3A ;Didn't recognize it
;----------------------------------------------------------------------
; ^: Move to the previous string.
;----------------------------------------------------------------------
EDIT_7A:
CALL PURGE_STR ;Clean up empties
DEC [STR_NUMBER]
JNS EDIT_7C
MOV BX,[STR_COUNT] ; reset to end
EDIT_7B:
MOV [STR_NUMBER],BX ;Update pointer
EDIT_7C:
CLC
JMP EDIT_1A ;Start over
;----------------------------------------------------------------------
; v: Move to the next string.
;----------------------------------------------------------------------
EDIT_8A:
CALL PURGE_STR ;Delete if empty
MOV BX,[STR_NUMBER] ;Get current string
JC EDIT_8B ;If del, don't adjust
INC BX ; otherwise go to next
EDIT_8B:
CMP BX,[STR_COUNT] ;Okay if in range
JBE EDIT_7B
SUB BX,BX ;Reset to zero
JMP EDIT_7B
;----------------------------------------------------------------------
; <- Move the cursor to the left one space.
;----------------------------------------------------------------------
EDIT_9A:
CALL CURSOR_LEFT ;Move cursor left
EDIT_9B:
JMP EDIT_3A ;Failed, ignore it
;----------------------------------------------------------------------
; Toggle the insert/typeover state.
;----------------------------------------------------------------------
EDIT_10:
NOT [INS_STATE] ;Toggle the flag
JMP EDIT_3A
;----------------------------------------------------------------------
; Move to end of string.
;----------------------------------------------------------------------
EDIT_11:
CALL CURSOR_RIGHT ;Move to the right
JNC EDIT_11 ; as long as successful
JMP EDIT_3A
;----------------------------------------------------------------------
; Move to start of string.
;----------------------------------------------------------------------
EDIT_12:
CALL CURSOR_LEFT ;Move to the left
JNC EDIT_12 ; as long as successful
JMP EDIT_3A
EDIT ENDP
;======================================================================
; UPDATE (Near)
;
; Update the string size and environment free numbers in the status
; line and display on the screen.
;----------------------------------------------------------------------
; Entry: None
; Exit: None
;----------------------------------------------------------------------
; Changes: BX CX DX SI
;----------------------------------------------------------------------
STATUSZ DB "String Size: "
US_SIZE DB "00000 Env Free: "
US_FREE DB "00000",0
UPDATE PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV SI,OFFSET US_SIZE+4 ;Point to end of size
MOV BX,[STR_LEN] ;Length in bytes
CALL US_1 ;Make denary/ASCII
MOV SI,OFFSET US_FREE+4 ;Repeat for free bytes
MOV BX,[ENV_FREE]
;----------------------------------------------------------------------
; Get the string size and convert to an ASCII denary number.
;----------------------------------------------------------------------
US_1:
PUSH AX ;Preserve register
MOV AX,BX ;Size in AX
MOV BX,10 ;Base is 10
MOV CX,5 ;Digits to convert
US_2:
SUB DX,DX ;32-bit in DX:AX
DIV BX ;Remainder in DX
ADD DL,30H ;Make into ASCII
MOV [SI],DL ;Store in string
DEC SI ;Move to higher digit
LOOP US_2
;----------------------------------------------------------------------
; Display the static text in the size status line.
;----------------------------------------------------------------------
MOV BX,0302H ;Row/col
MOV SI,OFFSET STATUSZ ;Status line text
CALL WRITE_MSG ;Put on screen
POP AX ;Restore register
RET
UPDATE ENDP
;======================================================================
; DISPLAYX (Near)
;
; This procedure will write the active string to the screen from the
; current cursor position forward. It is called only when a char is
; added to the string, the window is pushed, or to show a new string.
;----------------------------------------------------------------------
; Entry: None
; Exit: None
;----------------------------------------------------------------------
; Changes: AX BX CX SI
;----------------------------------------------------------------------
DISPLAYX PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CALL CUR_SET ;Position the cursor
CALL LOCATE_SI ;Point SI to string
MOV CH,[CURSOR_COL] ;Cursor column
MOV CL,[COL_MAX] ;Rightmost column
;----------------------------------------------------------------------
; Read the string and display on the screen.
;----------------------------------------------------------------------
DISPLAY_0:
LODS BYTE PTR ES:[SI] ;Get string char
OR AL,AL ;0 = end of string
JNZ DISPLAY_1
;----------------------------------------------------------------------
; If at the end of the string, display a space, then keep looping until
; the entire line has been overwritten.
;----------------------------------------------------------------------
DEC SI ;Back up to zero
MOV AL,20H ;Display a space
;----------------------------------------------------------------------
; Write the char in AL to the screen.
;----------------------------------------------------------------------
DISPLAY_1:
CALL CUR_SET ;Position the cursor
PUSH CX ;Save cursor position
MOV AH,0AH ;Write Char in AL
MOV BH,[VPAGE] ; on active page
MOV CX,1 ; 1 copy
INT 10H ; Thru BIOS
POP CX ;Restore cursor pos
INC [CURSOR_COL] ;Move to next column
CMP CL,[CURSOR_COL] ;Is col <= end?
JAE DISPLAY_0 ;Yes, continue
;----------------------------------------------------------------------
; Past the end of the window - done with display.
;----------------------------------------------------------------------
MOV [CURSOR_COL],CH ;Return to old spot
CALL CUR_SET ; do it
RET
DISPLAYX ENDP
;======================================================================
; PURGE_STR (Near)
;
; Examine the current string. If it's an empty string, remove it from
; the environment. If not, change the environment variable to all CAPS.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; NC - string is valid
; CY - string was empty and was deleted
;----------------------------------------------------------------------
; Changes: AX SI
;----------------------------------------------------------------------
PURGE_STR PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV SI,[STR_START] ;Point to start of str
CMP BYTE PTR ES:[SI],0 ;Is first char 0?
JNE PS_0
;----------------------------------------------------------------------
; Remove the string from the environment.
;----------------------------------------------------------------------
DEC [STR_COUNT] ;Reduce string count
CALL CHAR_KILL ;Remove 0
STC ;CY = str empty
JMP SHORT PS_EXIT
;----------------------------------------------------------------------
; Scan the string, capitalizing all chars to the left of the "=".
;----------------------------------------------------------------------
PS_0:
MOV AL,BYTE PTR ES:[SI] ;Is first char 0?
CMP AL,"=" ;= ends scan
JE PS_2
OR AL,AL ;0 ends scan
JZ PS_2
CMP AL,"a" ;If lower case...
JB PS_1
CMP AL,"z" ;...alphabetic
JA PS_1
AND BYTE PTR ES:[SI],NOT(20H) ;Capitalize
PS_1:
INC SI ;Move to next char
JMP PS_0
PS_2:
CLC ;Carry off
PS_EXIT:
RET
PURGE_STR ENDP
;======================================================================
; ADD_STRING (Near)
;
; Adds an empty string to the environment.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; NC - string added okay
; CY - string not added, no room in environment
;----------------------------------------------------------------------
; Changes: AX SI
;----------------------------------------------------------------------
ADD_STRING PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CALL STRING_INS ;Add empty string
JC AS_EXIT
MOV [CURSOR_COL],FAR_LEFT ;Reposition cursor
MOV SI,[STR_START] ;Point to 1st char
MOV BYTE PTR ES:[SI],0 ;Make it null
INC [STR_COUNT] ;Up string count
CLC ;Success
AS_EXIT:
RET
ADD_STRING ENDP
;======================================================================
; GET_START (Near)
;
; Find the starting offset of the current string into the environment
; segment and the length of that string.
;----------------------------------------------------------------------
; Entry: None
; Exit: None
;----------------------------------------------------------------------
; Changes: AX BX SI DI
;----------------------------------------------------------------------
GET_START PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV BX,[STR_NUMBER] ;String to look for
SUB SI,SI ;Start at offset zero
SUB DI,DI ;String counter
JMP SHORT GS_3
;----------------------------------------------------------------------
; Scan to the end of the string. Increment the count.
;----------------------------------------------------------------------
GS_1:
LODS BYTE PTR ES:[SI] ;Get char
OR AL,AL ;Scan for 0
JNZ GS_1
GS_2:
INC DI ;Increase string count
;----------------------------------------------------------------------
; If this is the string we're looking for, we're done.
;----------------------------------------------------------------------
GS_3:
CMP BX,DI ;Right string?
JNE GS_1
;----------------------------------------------------------------------
; Set edit pointers and count the length of the string.
;----------------------------------------------------------------------
MOV [STR_START],SI ;Save offset
MOV [STR_LEFT],SI
SUB DI,DI ;String length counter
GS_4:
LODS BYTE PTR ES:[SI] ;Get char
INC DI ;Count it
OR AL,AL ;It is 0?
JNZ GS_4
DEC DI ;Don't count the 0
MOV [STR_LEN],DI
RET
GET_START ENDP
;======================================================================
; LOCATE_SI (Near)
;
; Point SI to the same char in the string that is currently above the
; cursor on the screen.
;----------------------------------------------------------------------
; Entry: None
; Exit: None
;----------------------------------------------------------------------
; Changes: CX SI
;----------------------------------------------------------------------
LOCATE_SI PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV SI,[STR_LEFT] ;Leftmost char position
SUB CH,CH ;Adjust the start of
MOV CL,[CURSOR_COL] ; the string to point
SUB CL,FAR_LEFT ; to the char at the
ADD SI,CX ; cursor
RET
LOCATE_SI ENDP
;======================================================================
; STRING_INS (Near)
;
; Create a hole in the string by moving all chars to the right of the
; current char one place to the right.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; NC - hole created okay
; CY - no room left in environment
;----------------------------------------------------------------------
; Changes: CX SI DI
;----------------------------------------------------------------------
STRING_INS PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CMP [ENV_FREE],0 ;Any free space?
JNE SI_1
STC ;Failure
JMP SHORT SI_EXIT
;----------------------------------------------------------------------
; There's room, so perform the insertion.
;----------------------------------------------------------------------
SI_1:
CALL LOCATE_SI ;Point SI=current char
MOV CX,[ENV_USED] ;End of strings offset
MOV DI,CX ;Is target for move
SUB CX,SI ;Bytes to move
MOV SI,DI ;Copy to src register
DEC SI ;Copy from prev byte
PUSH ES ;Both same segment
POP DS
ASSUME DS:NOTHING
STD ;Move backwards
REP MOVSB ; whole string
CLD ;Restore direction
PUSH CS ;Restore DS
POP DS
ASSUME DS:CSEG
INC [ENV_USED] ;File is longer
INC [STR_LEN] ; so is string
DEC [ENV_FREE] ; less left
CALL UPDATE ;Display new counts
CLC ;Success
SI_EXIT:
RET
STRING_INS ENDP
;======================================================================
; STRING_DEL (Near)
;
; Delete the char at the cursor. Close up the string.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; NC = char was removed
; CY = no char to kill
;----------------------------------------------------------------------
; Changes: SI
;----------------------------------------------------------------------
STRING_DEL PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CALL LOCATE_SI ;Point to current char
CMP BYTE PTR ES:[SI],0 ;If 0, don't delete
JNE SD_1
STC ;No char to kill
JMP SHORT SD_EXIT
SD_1:
CALL CHAR_KILL ;Always returns NC
SD_EXIT:
RET
STRING_DEL ENDP
;======================================================================
; CHAR_KILL (Near)
;
; Closes up the string to eliminate the current character.
;----------------------------------------------------------------------
; Entry:
; SI = offset of char to kill
; ES = segment of char
; Exit:
; CF = NC -- always cleared
;----------------------------------------------------------------------
; Changes: CX SI DI
;----------------------------------------------------------------------
CHAR_KILL PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV CX,[ENV_USED] ;End of strings offset
SUB CX,SI ;Bytes to move
DEC CX ;Is one less
JZ CK_1
MOV DI,SI ;ES:DI is dest
INC SI ;Copy from next byte
PUSH ES ;Point DS:SI to src
POP DS
ASSUME DS:NOTHING
REP MOVSB ;Move all env
PUSH CS ;Restore DS
POP DS
ASSUME DS:CSEG
DEC [ENV_USED] ;File gets shorter
DEC [STR_LEN] ; as does string
INC [ENV_FREE] ; with more to spare
CALL UPDATE ;Freshen counts
CK_1:
CLC ;Success!
RET
CHAR_KILL ENDP
;======================================================================
; CURSOR_RIGHT (Near)
;
; Move the cursor right 1 char.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; NC = success
; CY = failed
;----------------------------------------------------------------------
; Changes: CX SI
;----------------------------------------------------------------------
CURSOR_RIGHT PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
CALL LOCATE_SI ;Point to current char
CMP BYTE PTR ES:[SI],0 ;Are we on last char
JNE CR_0 ;of string? jmp if yes
;----------------------------------------------------------------------
; Exit with CF=1:failure.
;----------------------------------------------------------------------
CR_A:
STC ;Signal failure
JMP SHORT CR_EXIT
;----------------------------------------------------------------------
; If we're at the end of the line, we've got to scroll.
;----------------------------------------------------------------------
CR_0:
MOV CL,[CURSOR_COL] ;Is cursor positioned
CMP CL,[COL_MAX] ; at screen edge?
JE CR_2
;----------------------------------------------------------------------
; Cursor can move on-screen. Scrolling isn't required.
;----------------------------------------------------------------------
INC CL ;Move to next col
CR_1:
MOV [CURSOR_COL],CL ;Save column...
CR_1B:
CALL CUR_SET ;...move cursor
CLC ;Signal success
CR_EXIT:
RET
;----------------------------------------------------------------------
; Slide to the right and redraw the entire string.
;----------------------------------------------------------------------
CR_2:
INC [STR_LEFT] ;Move the start
PUSH [CURSOR_POS] ;Save current cursor
MOV [CURSOR_COL],FAR_LEFT ;Redo from left side
CALL CUR_SET ;Set cursor
CALL DISPLAYX ;Draw string
POP [CURSOR_POS] ;Reset old cursor
JMP CR_1B
;======================================================================
; CURSOR_LEFT (Near) [NESTED PROC]
;
; Move the cursor left 1 char.
;----------------------------------------------------------------------
; Entry: None
; Exit:
; NC = cursor was moved left
; CY = cursor was at far left of string - not moved
;----------------------------------------------------------------------
; Changes: CX SI
;----------------------------------------------------------------------
CURSOR_LEFT PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
MOV CL,[CURSOR_COL] ;Is cursor
CMP CL,FAR_LEFT ; at 1st column?
JE CL_1 ;Yes, jump
DEC CL ;Back up cursor
JMP CR_1
CL_1:
MOV SI,[STR_LEFT] ;Start of window
CMP SI,[STR_START] ;Past start of string?
JE CR_A ;Yes, jump
DEC SI
MOV [STR_LEFT],SI
CALL DISPLAYX
CLC ;Indicate success
JMP CR_EXIT
CURSOR_LEFT ENDP
CURSOR_RIGHT ENDP
;======================================================================
; CUR_SET (Near)
;
; Position the cursor to the screen row and column stored in
; [CURSOR_POS]. Row, col values are not checked.
;----------------------------------------------------------------------
; Entry: None
; Exit: None
;----------------------------------------------------------------------
; Changes: BX DX
;----------------------------------------------------------------------
CUR_SET PROC NEAR
ASSUME CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
PUSH AX ;Save used register
MOV AH,2 ;Position cursor fn
MOV BH,[VPAGE] ; current page
MOV DX,[CURSOR_POS] ; new cursor position
INT 10H ; Thru BIOS
POP AX ;Restore register
RET
CUR_SET ENDP
;======================================================================
; Allocated after program loads.
;----------------------------------------------------------------------
PC = $
PC = PC + 256
STACK_TOP = PC
CSEG ENDS
END ENTPT