Metropoli BBS
VIEWER: envedit.asm MODE: TEXT (ASCII)
;======================================================================
; 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
[ RETURN TO DIRECTORY ]