Metropoli BBS
VIEWER: hexedit.asm MODE: TEXT (ASCII)
;======================================================================
; HEXEDIT * 1.00 Copyright (c) 1992, Robert L. Hummel
; PC Magazine Assembly Language Lab Notes
;----------------------------------------------------------------------
CSEG		SEGMENT	PARA	PUBLIC	'CODE'
	ASSUME	CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG

		ORG	100H			;COM file format
ENTPT:		JMP	MAIN

;======================================================================
; Program data area.
;----------------------------------------------------------------------
CR		EQU	13	;ASCII carriage return
LF		EQU	10	;ASCII line feed
BLANK		EQU	32	;ASCII blank

RARROW		EQU	4DH	;Move forward 1 char
DARROW		EQU	50H	;Move forward 1 row
PGDN		EQU	51H	;Move forward 1 screen

LARROW		EQU	4BH	;Move backward 1 char
UARROW		EQU	48H	;Move backward 1 row
PGUP		EQU	49H	;Move backward 1 screen

F7KEY		EQU	41H	;Exit the editor
F8KEY		EQU	42H	;Switch between HEX and ASCII displays
;----------------------------------------------------------------------
; Messages.
;----------------------------------------------------------------------
COPYRIGHT$	DB	CR,LF,"HEXEDIT 1.00 ",254," Copyright (c) 1992"
		DB	", Robert L. Hummel",CR,LF
		DB	"PC Magazine Assembly Language Lab Notes",LF
CRLF$		DB	CR,LF,"$"

USAGE$		DB	"Usage: HEXEDIT [d:][path]filename.ext$"
CONFIRM$	DB	"Exiting. Make changes permanent? (Y/N) $"

ERR_VIDEO$	DB	"Video mode must be text, 80 or more columns$"
ERR_MEM$	DB	"There's not enough memory to execute$"
ERR_DRIVE$	DB	"Drive is invalid$"
ERR_FIND$	DB	"Can't find the file$"
ERR_TMP$	DB	"Trouble with HEXEDIT~.@@@ scratch file$"
ERR_SRC$	DB	"Trouble reading/writing the file$"
ERR_REN$	DB	"Can't rename file. Changes in HEXEDIT~.@@@$"
;----------------------------------------------------------------------
; File data.
;----------------------------------------------------------------------
SRC_HANDLE	DW	0			;Source file handle
SRC_NAME	DW	0			;Pointer to file name

TMP_NAMEZ	DB	"HEXEDIT~.@@@",0	;Scratch file name
TMP_HANDLE	DW	0			;Scratch file handle
;----------------------------------------------------------------------
; Buffer and display window data.
;----------------------------------------------------------------------
FILE_ALTERED	DB	0	;>0 if changes were made to file

BUF_ANCHOR	DD	0	;Pos in file of 1st byte of buffer
BUF_ANCHOR_MAX	DD	0	;Furthest forward buffer can start
BUF_SIZE	DW	0	;Maximum number bytes buffer can hold
BUF_BYTES	DW	0	;# valid bytes in buffer
FULLY_BUFFERED	DB	0	;>0 if entire file fits in buffer
BUF_ALTERED 	DB	0	;>0 if buffer has been altered
REDO_BUF	DB	0	;>0 if buffer has moved wrt file

GRID_ANCHOR	DW	0	;Offset into buffer of 1st dislay byte
REDO_GRID	DB	0	;Non-zero if need to redraw grid fm buf

GRIDROW		DB	0	;Current grid row
GRIDROWMAX	EQU	15
GRIDCOL		DB	0	;Current grid column
GRIDCOLMAX	EQU	15
GRID_LEN	EQU	(GRIDCOLMAX + 1)*(GRIDROWMAX+1)

LEFT		EQU	0F0H	;HEX mode, left digit
RIGHT		EQU	00FH	;HEX mode, right digit
MODE_MASK	EQU	13H	;Tri-state mask
MODE		DB	0	;Mode, 0 = ASCII
;----------------------------------------------------------------------
; Video data.
;----------------------------------------------------------------------
ROW_0		EQU	5	;Screen row for grid row 0
OFF_COL		EQU	2	;Starting screen column for offset
HEX_COL		EQU	12	;Starting screen column for hex display
ASC_COL		EQU	61	;Starting screen column for ASCII chars

VPAGE		DB	0	;Active video page
VATTR		DB	07H	;Attribute for display (mono default)
ATTR_COLOR	EQU	17H	;Attribute for color displays

VCURSOR		DW	0	;Holds row/column for screen cursor

;======================================================================
; MAIN procedure.
;----------------------------------------------------------------------
MAIN		PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
;----------------------------------------------------------------------
; Initialize.
;----------------------------------------------------------------------
		CLD				;String moves forward
		MOV	CL,AL			;Save drive status
;----------------------------------------------------------------------
; Check the video mode and initialize the display variables.
;----------------------------------------------------------------------
		CALL	VIDEO_SETUP		;Examine video hardware
		MOV	DX,OFFSET ERR_VIDEO$	;Assume an error
		JC	M_1
;----------------------------------------------------------------------
; Make sure there's enough room for the stack and relocate it to the
; end of the code.
;----------------------------------------------------------------------
		MOV	DX,OFFSET ERR_MEM$	;Assume error
		MOV	AX,OFFSET STACK_TOP	;We want stack here
		CMP	SP,AX			;Are we beyond it?
		JBE	M_1

		CLI				;Disable interrupts
		XCHG	AX,SP			; and re-position stack
		STI				;Allow interrupts
;----------------------------------------------------------------------
; Determine how many bytes can be allocated in the remainder of the
; segment. We must have at least 512 bytes.
;----------------------------------------------------------------------
		SUB	AX,SP			;Free bytes
		CMP	AX,512			;Minimum allowed
		JB	M_1

		AND	AL,0FEH			;Make number even
		MOV	[BUF_SIZE],AX		;Save buffer size
;----------------------------------------------------------------------
; Check if an invalid drive was specified on the command line.
;----------------------------------------------------------------------
		MOV	DX,OFFSET ERR_DRIVE$	;Assume an error
		INC	CL			;If was FF, now 00
		JNZ	M_2
;----------------------------------------------------------------------
; Exit the program, displaying the message passed in DX and the
; copyright notice.
;----------------------------------------------------------------------
M_1:
		MOV	AH,9			;Display string
		INT	21H			; thru DOS
M_EXIT:
		MOV	AH,9			;Display string
		MOV	DX,OFFSET CRLF$		;New line
		INT	21H			; thru DOS

		MOV	AH,9			;Display string fn
		MOV	DX,OFFSET COPYRIGHT$	; located here
		INT	21H			; thru DOS

		MOV	AH,4CH			;Terminate process
		INT	21H			; thru DOS
;----------------------------------------------------------------------
; If no characters are in the command tail, show the usage message.
;----------------------------------------------------------------------
M_2:
		MOV	DX,OFFSET USAGE$	;Assume no characters

		MOV	SI,80H			;Point to tail length
		LODSB				;Get length in AL
		OR	AL,AL			;Any chars?
		JZ	M_1

		CBW				;Change length to word
		MOV	DI,SI			;Starting offset
		ADD	DI,AX			; + length = end offset
		MOV	[DI],AH			;Convert to ASCIIZ
;----------------------------------------------------------------------
; Find the first non-blank char. If it's a zero, show usage message.
;----------------------------------------------------------------------
M_3A:
		LODSB				;Get char in AL
		CMP	AL,BLANK		;Leading blank?
		JE	M_3A

		OR	AL,AL			;If 0, no chars
		JZ	M_1

		DEC	SI			;Point to 1st non-blank
		MOV	DI,SI			;Save it in DI
;----------------------------------------------------------------------
; Find the end of the string as indicated by a blank or the zero.
;----------------------------------------------------------------------
M_3B:
		LODSB				;Get next char
		OR	AL,AL			;Stop if zero
		JZ	M_3C

		CMP	AL,BLANK		;Continue unless blank
		JNE	M_3B
M_3C:
		DEC 	SI			;Point to last char
		MOV	[SI],AH			;Make ASCIIZ

		MOV	[SRC_NAME],DI		;Save pointer to name
;----------------------------------------------------------------------
; Attempt to open the file found on the command line.
;----------------------------------------------------------------------
		MOV	AX,3D00H		;Open file for reading
		MOV	DX,DI			;Point DS:DX to name
		INT	21H			; thru DOS
		MOV	DX,OFFSET ERR_FIND$	;Assume error
		JC	M_1

		MOV	[SRC_HANDLE],AX		;Save source handle
;----------------------------------------------------------------------
; Create a scratch file and copy the file over.
;----------------------------------------------------------------------
		MOV	AH,3CH			;Create file
		SUB	CX,CX			;Normal attributes
		MOV	DX,OFFSET TMP_NAMEZ	;This name
		INT	21H			; thru DOS
		MOV	DX,OFFSET ERR_TMP$	;Assume error
		JC	M_1

		MOV	[TMP_HANDLE],AX		;Save file handle
;----------------------------------------------------------------------
; Read data from the source file and write it to the destination file
; until the entire file has been copied.
;----------------------------------------------------------------------
M_4A:
		MOV	AH,3FH			;Read file
		MOV	BX,[SRC_HANDLE]		; from this handle
		MOV	CX,[BUF_SIZE]		; CX bytes
		MOV	DX,OFFSET BUFFER	;Put data here
		INT	21H			; thru DOS
		JNC	M_4C
M_4B:
		MOV	DX,OFFSET ERR_SRC$	;Error reading file
		JMP	M_1
M_4C:
		OR	AX,AX			;No bytes read?
		JZ	M_4F

		MOV	CX,AX			;Write the same #

		MOV	AH,40H			;Write file
		MOV	BX,[TMP_HANDLE]		; to this handle
		INT	21H			; thru DOS
		JNC	M_4A
M_4D:
		MOV	DX,OFFSET ERR_TMP$	;Error writing file
M_4E:
		JMP	M_1
M_4F:
;----------------------------------------------------------------------
; Close the source file; we won't need it again.
;----------------------------------------------------------------------
		MOV	AH,3EH			;Close handle fn
		MOV	BX,[SRC_HANDLE]		;Handle for source file
		INT	21H			; thru DOS
		JC	M_4B
;----------------------------------------------------------------------
; Get the size of the file by seeking to the end of the file.
;----------------------------------------------------------------------
		MOV	AX,4202H		;Seek, offset from end
		MOV	BX,[TMP_HANDLE]		;Handle for the file
		SUB	CX,CX			;CX:DX =
		SUB	DX,DX			; offset from end
		INT	21H			; thru DOS
		JC	M_4D
;----------------------------------------------------------------------
; If the number of bytes in the file is smaller or equal to the number
; of bytes the buffer can hold, all moves can be handled in the buffer.
;----------------------------------------------------------------------
		MOV	BX,[BUF_SIZE]		;Buffer capacity

		OR	DX,DX			;If not 0, file > 64k
		JNZ	M_5A

		CMP	AX,BX			;Fit in buffer?
		JA	M_5A

		INC	[FULLY_BUFFERED]	;Yes, set flag
		JMP	SHORT M_5B
;----------------------------------------------------------------------
; If the file is larger than the buffer, calculate the maximum buffer
; anchor value so we don't have to do it repeatedly later.
;----------------------------------------------------------------------
M_5A:
		PUSH	AX			;Save file length
		PUSH	DX			; in DX:AX

		SUB	AX,BX			;Figure max anchor
		SBB	DX,CX			;(CX is 0)

		MOV	WORD PTR [BUF_ANCHOR_MAX][0],AX	;Max BUF_ANCHOR
		MOV	WORD PTR [BUF_ANCHOR_MAX][2],DX	; position

		POP	DX			;Restore file length
		POP	AX
M_5B:
;----------------------------------------------------------------------
; Draw the graphics characters that make up the window background.
;----------------------------------------------------------------------
		CALL	DRAW_BKGND
;----------------------------------------------------------------------
; Invoke the editor. If it returns with the carry flag set, a file
; error was encountered. Print a message and abort the edit.
;----------------------------------------------------------------------
		CALL	EDIT			;Edit the file
		JC	M_4E
;----------------------------------------------------------------------
; Close the temporary file to commit the changes to disk.
;----------------------------------------------------------------------
		MOV	AH,3EH			;Close file handle
		MOV	BX,[TMP_HANDLE]		;This handle
		INT	21H			; thru DOS
		JC	M_4D
;----------------------------------------------------------------------
; Clear the screen and position the cursor to the top left corner.
;----------------------------------------------------------------------
		MOV	AX,0700H		;Scroll window fn
		MOV	BH,[VATTR]		; clear to this color
		SUB	CX,CX			;Topleft row,col
		MOV	DX,(24 SHL 8 + 79)	;Lowright row,col
		INT	10H			; thru BIOS

		MOV	AH,2			;Position cursor
		MOV	BH,[VPAGE]		;On this video page
		SUB	DX,DX			;Row 0, col 0
		INT	10H			; thru BIOS
;----------------------------------------------------------------------
; If changes were made to the file, ask if they should be permanent.
;----------------------------------------------------------------------
		CMP	[FILE_ALTERED],0	;Flag 0 if no changes
		JE	M_7

		MOV	AH,9			;Display string fn
		MOV	DX,OFFSET CONFIRM$	;Make permanent?
		INT	21H			; thru DOS
M_6:
		MOV	AH,8			;Get a key
		INT	21H			; thru DOS
		AND	AL,NOT 20H		;Capitalize it

		CMP	AL,"Y"			;If Yes, jump
		JE	M_8A

		CMP	AL,"N"			;If not No, try again
		JNE	M_6
;----------------------------------------------------------------------
; Don't save the changes, just delete the temporary file and exit.
;----------------------------------------------------------------------
M_7:
		MOV	AH,41H			;Delete file handle
		MOV	DX,OFFSET TMP_NAMEZ	;This name
		INT	21H			; thru DOS
		JNC	M_8D
		JMP	M_4D
;----------------------------------------------------------------------
; Make the changes permanent. Delete the original file. If the original
; file can't be renamed, leave the temporary file intact.
;----------------------------------------------------------------------
M_8A:
		MOV	AH,41H			;Delete file handle
		MOV	DX,[SRC_NAME]		;This name
		INT	21H			; thru DOS
		JNC	M_8C
M_8B:
		MOV	AH,9			;Display string fn
		MOV	DX,OFFSET CRLF$		;Go to new line
		INT	21H			; thru DOS

		MOV	DX,OFFSET ERR_REN$	;Renaming error
		JMP	M_1
;----------------------------------------------------------------------
; Rename the temp file to the original name.
;----------------------------------------------------------------------
M_8C:
		MOV	AH,56H			;Rename file
		MOV	DX,OFFSET TMP_NAMEZ	;Old name at DS:DX
		MOV	DI,[SRC_NAME]		;New name at ES:DI
		INT	21H			; thru DOS
		JC	M_8B
M_8D:
		JMP	M_EXIT

MAIN		ENDP

;======================================================================
; VIDEO_SETUP (Near)
;
; This procedure ensures that we're in a text mode and 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 and screen size is okay
;	CY = Can't use this video mode or not enough columns
;----------------------------------------------------------------------
; Changes: AX BX
;----------------------------------------------------------------------
VIDEO_SETUP	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		MOV	AH,0FH			;Get video mode
		INT	10H			; thru BIOS
;----------------------------------------------------------------------
; Now make sure we're in a text mode.
;----------------------------------------------------------------------
		CMP	AL,7			;7 = monochrome
		JE	VS_1

		MOV	[VATTR],ATTR_COLOR	;Assume color mode
		CMP	AL,4			;Carry clear if NG
		JNB	VS_2
VS_1:
;----------------------------------------------------------------------
; Make sure there are at least 80 columns.
;----------------------------------------------------------------------
		CMP	AH,80			;Enough columns?
		JB	VS_EXIT			;JB=JC=carry set

		MOV	[VPAGE],BH		;Save current page

		STC				;Set carry...
VS_2:
		CMC				;...then reverse it
VS_EXIT:
		RET

VIDEO_SETUP	ENDP

;======================================================================
; DRAW_BKGND (Near)
;
; Clear the screen and draw the framework for the editing window.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: AX BX CX DX SI
;----------------------------------------------------------------------
TITLE$		DB	"HEXEDIT 1.00 ",254," PC Magazine Assembly "
		DB	"Language Lab Notes ",254," Robert L. Hummel$"
TITLE_LEN	EQU	$-OFFSET TITLE$

HELP$		DB	"Editing Keys: ",27,32,32,26,32,32,24,32,32,25
		DB	"  PgUp  PgDn  F7 = Save/Abort  F8=Hex/ASCII$"
HELP_LEN	EQU	$-OFFSET HELP$

OFFSET$		DB	"OFFSET$"
OFFSET_POS	EQU	0303H

HEX$		DB	"HEX DATA$"
HEX_POS		EQU	0320H

ASCII$		DB	"ASCII DATA$"
ASCII_POS	EQU	0340H

WIN_CHARS	DB	 1,201,205,205,187
		DB	 1,186, 32, 32,186
		DB	 1,199,196,194,182
		DB	18,186, 32,179,186
		DB	 1,199,196,193,182
		DB	 2,186, 32, 32,186
		DB	 1,200,205,205,188
		DB	-1

DRAW_BKGND	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,[VATTR]		;Clear to this color
		SUB	CX,CX			;Topleft row,col
		MOV	DX,(24 SHL 8 + 79)	;Lowright row,col
		INT	10H			; thru BIOS
;----------------------------------------------------------------------
; Prepare to draw the screen.
;----------------------------------------------------------------------
		MOV	BH,[VPAGE]		;Get active page
		MOV	SI,OFFSET WIN_CHARS	;Point to array
		SUB	DH,DH			;Starting row = 0
;----------------------------------------------------------------------
; The first byte indicates how many rows to draw with this set of
; characters. If -1, we're done.
;----------------------------------------------------------------------
DB_1:
		LODSB				;Get # rows to draw
		OR	AL,AL			;Check if -1
		JS	DB_3

		CBW				;Convert count to word
		MOV	CX,AX			;Put in count register
;----------------------------------------------------------------------
; Repeat this procedure once for each row.
;----------------------------------------------------------------------
DB_2:
		PUSH	CX			;Save row counter
;----------------------------------------------------------------------
; Position to the current row, column 0 and write the leftmost char.
; Write TTY automatically advances the cursor.
;----------------------------------------------------------------------
		MOV	AH,2			;Position cursor
		SUB	DL,DL			; to column 0
		INT	10H			; thru BIOS

		MOV	AL,[SI]			;Get leftmost char
		MOV	AH,0EH			;Write 1 char TTY
		INT	10H			; thru BIOS
;----------------------------------------------------------------------
; Fill the middle 78 chars with the next char in the array.
;----------------------------------------------------------------------
		MOV	AL,[SI+1]		;Get middle char
		MOV	AH,0AH			;Write repeated char
		MOV	CX,78			; this many
		INT	10H			; thru BIOS
;----------------------------------------------------------------------
; Position the cursor to the two interior partition spots and draw the
; character required.
;----------------------------------------------------------------------
		MOV	AH,2			;Position cursor
		MOV	DL,10			; to first partition
		INT	10H			; thru BIOS

		MOV	AL,[SI+2]		;Get partition char
		MOV	AH,0EH			;Write 1 char TTY
		INT	10H			; thru BIOS

		MOV	AH,2			;Position cursor
		MOV	DL,60			; to first partition
		INT	10H			; thru BIOS

		MOV	AH,0EH			;Write 1 char TTY
		INT	10H			; thru BIOS
;----------------------------------------------------------------------
; Position to the right side of the screen and draw the final character
; for this row. Don't use Write TTY because it scrolls the screen.
;----------------------------------------------------------------------
		MOV	AH,2			;Position cursor
		MOV	DL,79			; to far right
		INT	10H			; thru BIOS

		MOV	AL,[SI+3]		;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	DB_2
;----------------------------------------------------------------------
; Move to the next array row and continue.
;----------------------------------------------------------------------
		ADD	SI,4
		JMP	DB_1
;----------------------------------------------------------------------
; Display the title, column headings, and help prompt.
;----------------------------------------------------------------------
DB_3:
		MOV	AH,2			;Position cursor
		MOV	DX,100H+(80-TITLE_LEN)/2 ; to this row,col
		INT	10H			; thru BIOS

		MOV	AH,9			;Display string
		MOV	DX,OFFSET TITLE$	; showing title
		INT	21H			; thru DOS

		MOV	AH,2			;Position cursor
		MOV	DX,OFFSET_POS
		INT	10H			; thru BIOS

		MOV	AH,9			;Display string
		MOV	DX,OFFSET OFFSET$	;Offset heading
		INT	21H			; thru DOS

		MOV	AH,2			;Position cursor
		MOV	DX,HEX_POS
		INT	10H			; thru BIOS

		MOV	AH,9			;Display string
		MOV	DX,OFFSET HEX$		;Hex data heading
		INT	21H			; thru DOS

		MOV	AH,2			;Position cursor
		MOV	DX,ASCII_POS
		INT	10H			; thru BIOS

		MOV	AH,9			;Display string
		MOV	DX,OFFSET ASCII$	;ASCII data heading
		INT	21H			; thru DOS

		MOV	AH,2			;Position cursor
		MOV	DX,1700H+(80-HELP_LEN)/2 ; to this row/col
		INT	10H			; thru BIOS

		MOV	AH,9			;Display string
		MOV	DX,OFFSET HELP$		; showing help
		INT	21H			; thru DOS

		RET

DRAW_BKGND	ENDP

;======================================================================
; EDIT (Near)
;
; On entry, the background editing screen has been drawn and the buffer
; has been initialized to hold the first portion of the file.
;----------------------------------------------------------------------
; Entry: None
; Exit :
;	CY - indicates a file error occurred while buffering
;	NC - everything went okay
;----------------------------------------------------------------------
; Changes: AX BX CX DX
;----------------------------------------------------------------------
EDIT		PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:CSEG, SS:CSEG
;----------------------------------------------------------------------
; Set the flags so the buffer will be filled and the display updated.
;----------------------------------------------------------------------
		INC	[REDO_BUF]		;Fill buffer
		INC	[REDO_GRID]		;Draw display
		JMP	SHORT EDIT_0B
;----------------------------------------------------------------------
; The move couldn't be performed within the grid. Move the grid so
; that the desired byte is visible.
;----------------------------------------------------------------------
EDIT_0A:
		CALL	MOVE_GRID
		JNC	EDIT_0B
;----------------------------------------------------------------------
; If MOVE_GRID returned with CY, a file error occurred.
;----------------------------------------------------------------------
EDIT_EXIT:
		RET
;----------------------------------------------------------------------
; If required, FILL_BUFFER will refresh the buffer.
;----------------------------------------------------------------------
EDIT_0B:
		CALL	FILL_BUFFER		;Refresh the buffer
		JC	EDIT_EXIT		;Exit if error
;----------------------------------------------------------------------
; If required, translate the visible portion of the buffer to the screen.
;----------------------------------------------------------------------
EDIT_0C:
		CALL	DISPLAY_GRID		;Refresh screen
;----------------------------------------------------------------------
; Set the cursor so it appears under the correct screen character.
;----------------------------------------------------------------------
EDIT_0D:
		CALL	SET_CURSOR		;Place cursor
;----------------------------------------------------------------------
; Get a key from the keyboard and act on it.
;----------------------------------------------------------------------
EDIT_1A:
		SUB	AH,AH			;Fetch a key
		INT	16H			; thru BIOS

		OR	AL,AL			;If AL=0, is extended
		JZ	EDIT_1B
		JMP	EDIT_11
EDIT_1B:
;----------------------------------------------------------------------
; *** Right arrow.
;----------------------------------------------------------------------
		CMP	AH,RARROW		;Right arrow
		JNE	EDIT_3A
EDIT_1C:
		TEST	[MODE],MODE_MASK	;Check hex/ascii
		JZ	EDIT_2C			;Jump if ASCII
		JPE	EDIT_2B			;Jump if RIGHT
;----------------------------------------------------------------------
; HEX mode. If cursor is on left hex digit, move to right hex digit.
;----------------------------------------------------------------------
		NOT	[MODE]			;Mode = right hex digit
		MOV	AL,1			;Move right 1 column
EDIT_2A:
		CALL	MOVE_CURSOR		;Move cursor
		JMP	EDIT_0C
;----------------------------------------------------------------------
; If cursor is on right hex digit, move to left hex digit, then...
;----------------------------------------------------------------------
EDIT_2B:
		MOV	[MODE],LEFT		;Change to left digit
;----------------------------------------------------------------------
; Move to next grid byte.
;----------------------------------------------------------------------
EDIT_2C:
		CMP	[GRIDCOL],GRIDCOLMAX	;Are we at far right?
		JE	EDIT_2E

		INC	[GRIDCOL]		;No - move to next byte
		JMP	EDIT_0C			;Recalc cursor
EDIT_2E:
		CMP	[GRIDROW],GRIDROWMAX	;Are we at bottom?
		JE	EDIT_2G

		MOV	[GRIDCOL],0		;Move to first byte
EDIT_2F:
		INC	[GRIDROW]		; in next row
		JMP	EDIT_0C			;Recalc cursor
EDIT_2G:
		MOV	AX,1			;Move buffer +1 byte
		JMP	EDIT_0A			;Redo buffer
;----------------------------------------------------------------------
; *** Down arrow.
;----------------------------------------------------------------------
EDIT_3A:
		CMP	AH,DARROW
		JNE	EDIT_4A

		CMP	[GRIDROW],GRIDROWMAX	;Are we at bottom?
		JNE	EDIT_2F

		MOV	AX,16			;Move 1 row
		JMP	EDIT_0A			;Redo buffer
;----------------------------------------------------------------------
; *** PgDn.
;----------------------------------------------------------------------
EDIT_4A:
		CMP	AH,PGDN
		JNE	EDIT_5A

		MOV	AX,GRID_LEN		;Move 1 full screen
		JMP	EDIT_0A			;Redo buffer
;----------------------------------------------------------------------
; *** Left arrow.
;----------------------------------------------------------------------
EDIT_5A:
		CMP	AH,LARROW		;Left arrow
		JNE	EDIT_6A

		TEST	[MODE],MODE_MASK	;Check hex/ascii
		JZ	EDIT_5C			;Jump if ASCII
		JPO	EDIT_5B			;Jump if LEFT
;----------------------------------------------------------------------
; If cursor is on right hex digit, move to left hex digit.
;----------------------------------------------------------------------
		NOT	[MODE]			;Change to left digit
		MOV	AL,-1			;Back up cursor 1 col
		JMP	EDIT_2A
;----------------------------------------------------------------------
; If cursor is on left hex digit, move to right hex digit, then...
;----------------------------------------------------------------------
EDIT_5B:
		MOV	[MODE],RIGHT		;Reset to right digit
;----------------------------------------------------------------------
; Move to previous grid byte.
;----------------------------------------------------------------------
EDIT_5C:
		CMP	[GRIDCOL],0		;Far left column?
		JE	EDIT_5D

		DEC	[GRIDCOL]		;No - back up
		JMP	EDIT_0C			;Recalc cursor
EDIT_5D:
		CMP	[GRIDROW],0		;Top of grid?
		JE	EDIT_5F

		MOV	[GRIDCOL],GRIDCOLMAX	;No - back around
EDIT_5E:
		DEC	[GRIDROW]		;Previous row
		JMP	EDIT_0C			;Recalc cursor
EDIT_5F:
		MOV	AX,-1			;Back up buffer 1 byte
		JMP	EDIT_0A			;Redo buffer
;----------------------------------------------------------------------
; *** Up arrow.
;----------------------------------------------------------------------
EDIT_6A:
		CMP	AH,UARROW
		JNE	EDIT_7A

		CMP	[GRIDROW],0		;At top row?
		JNE	EDIT_5E

		MOV	AX,-16			;Back up 16 bytes
		JMP	EDIT_0A			;Redo buffer
;----------------------------------------------------------------------
; *** PgUp.
;----------------------------------------------------------------------
EDIT_7A:
		CMP	AH,PGUP
		JNE	EDIT_8A

		MOV	AX,-(GRID_LEN)		;Back up 1 screen
		JMP	EDIT_0A			;Redo buffer
;----------------------------------------------------------------------
; F8 - switch modes.
;----------------------------------------------------------------------
EDIT_8A:
		CMP	AH,F8KEY
		JNE	EDIT_9

		SUB	AL,AL			;Create 0 (ascii mode)
		CMP	AL,[MODE]		;Is mode 0 (ascii)?
		JNE	EDIT_8B

		MOV	AL,LEFT			;Make hex
EDIT_8B:
		MOV	[MODE],AL		;Save mode
		JMP	EDIT_0D			;Recalc cursor
;----------------------------------------------------------------------
; F7 - Exit the editor.
;----------------------------------------------------------------------
EDIT_9:
		CMP	AH,F7KEY
		JNE	EDIT_10

		CALL	COMMIT_BUFFER		;Flush if needed
		JMP	EDIT_EXIT
;----------------------------------------------------------------------
; If here, was not a recognized keystroke. Ignore it and continue.
;----------------------------------------------------------------------
EDIT_10:
		JMP	EDIT_1A
;----------------------------------------------------------------------
; Key dispatch for non-extended keys.
; If HEX mode, only 0-9 and A-F are accepted.
; In ASCII mode, anything goes!
;
; First, determine the offset into the buffer of the active grid byte.
;----------------------------------------------------------------------
EDIT_11:
		MOV	DL,AL			;Put char in DL
		MOV	AL,[GRIDROW]		;Current row
		MOV	AH,GRIDCOLMAX+1		;* row length
		MUL	AH
		ADD	AL,[GRIDCOL]		;+ current column
		MOV	BX,[GRID_ANCHOR]	;+ anchor
		ADD	BX,AX			;= offset of byte
		ADD	BX,OFFSET BUFFER	;= addr of byte

		MOV	CH,[MODE]		;Save mode
		OR	CH,CH
		JNZ	EDIT_12B
;----------------------------------------------------------------------
; ASCII mode. Substitute the character in DL for the current byte.
; The byte will always be in the buffer, even if not visible!
;----------------------------------------------------------------------
		MOV	[BX],DL			;Place char in buf
EDIT_12A:
		MOV	[BUF_ALTERED],1		;Say buffer is changed
		INC	[REDO_GRID]		;Request redraw
		JMP	EDIT_1C			;Goto right arrow
;----------------------------------------------------------------------
; HEX MODE: Check if character is a hex digit.
;----------------------------------------------------------------------
EDIT_12B:
		CMP	DL,"0"			;Below this, leave
		JB	EDIT_10
		CMP	DL,"9"			;Above this, check A-F
		JA	EDIT_12D

		SUB	DL,"0"			;Convert char to number
;----------------------------------------------------------------------
; Mask the digit in the lower 4 bits of DL into the current byte.
;----------------------------------------------------------------------
EDIT_12C:
		MOV	CL,4
		MOV	DH,DL
		SHL	DH,CL
		OR	DL,DH
		AND	DL,CH			;Mask appropriate bit
		NOT	CH			;Reverse mask
		AND	[BX],CH			;Clear old digit
		OR	[BX],DL			; and substitute new
		JMP	EDIT_12A
;----------------------------------------------------------------------
; See if A-F.
;----------------------------------------------------------------------
EDIT_12D:
		OR	DL,20H			;Make lowercase
		CMP	DL,"a"			;Ignore if below "a"
		JB	EDIT_10

		CMP	DL,"f"			; or above "f"
		JA	EDIT_10

		SUB	DL,57H			;Convert to digit
		JMP	EDIT_12C

EDIT		ENDP

;======================================================================
; MOVE_GRID (Near)
;
; This routine moves the grid so that the desired data is visible.
;----------------------------------------------------------------------
; Entry:
;	AX = integer number of bytes to move the grid
; Exit :
;	CY - file error during I/O
;	NC - no file errors
;----------------------------------------------------------------------
; Changes: AX BX CX DX SI DI BP
;----------------------------------------------------------------------
MOVE_GRID	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Load some needed values into registers for speed.
;----------------------------------------------------------------------
		MOV	BX,[BUF_BYTES]		;Bytes in buffer
		MOV	BP,[GRID_ANCHOR]	;Grid anchor

		MOV	SI,WORD PTR [BUF_ANCHOR][0]	;Buffer anchor
		MOV	DI,WORD PTR [BUF_ANCHOR][2]
;----------------------------------------------------------------------
; If the entire file is smaller than 1 grid, we can just leave now as
; no buffer movement is possible.
;----------------------------------------------------------------------
		CMP	BX,GRID_LEN		;File < 1 display?
		JBE	MG_2
;----------------------------------------------------------------------
; Branch depending on the direction of the move.
;----------------------------------------------------------------------
		OR	AX,AX			;Which direction?
		JS	MG_1A
;----------------------------------------------------------------------
; FWD: Can this move be performed within the current buffer?
;----------------------------------------------------------------------
		SUB	BX,GRID_LEN		;Max grid anchor

		ADD	BP,AX			;Desired grid anchor
		CMP	BP,BX			;Desired <= maximum?
		JBE	MG_1D
;----------------------------------------------------------------------
; The desired grid anchor exceeds the maximum grid anchor.
; If file is larger than the buffer, jump to the more complex
; buffer-moving routines. Otherwise, just use the maximum anchor.
;----------------------------------------------------------------------
		CMP	[FULLY_BUFFERED],0	;0=larger than buffer
		JE	MG_3A			;Go to buffer routines

		MOV	BP,BX			;Use max anchor
		JMP	SHORT MG_1D
;----------------------------------------------------------------------
; Determine if this backward move can be performed within the buffer.
; (Backing up is hard to do...)
;----------------------------------------------------------------------
MG_1A:
		NEG	AX			;Make AX positive

		CMP	AX,BP			;Back up past anchor?
		JA	MG_1B

		SUB	BP,AX			;No, set new anchor
		JMP	SHORT MG_1D
MG_1B:
;----------------------------------------------------------------------
; If the file is larger than the buffer, jump to the more complex
; buffer-moving routines. Otherwise, just move to the buffer start.
;----------------------------------------------------------------------
		CMP	[FULLY_BUFFERED],0	;0 = larger than buffer
		JNE	MG_1C
		JMP	SHORT MG_4A		;Go to buffer routines
MG_1C:
		OR	BP,BP			;If already at start
		JZ	MG_2			; do no more

		SUB	BP,BP			;Else go to start
;----------------------------------------------------------------------
; If we're not moving the anchor, just ignore the request.
;----------------------------------------------------------------------
MG_1D:
		CMP	BP,[GRID_ANCHOR]
		JE	MG_2

		MOV	[GRID_ANCHOR],BP	;Set new anchor
MG_1E:
		INC	[REDO_GRID]		;Request grid redraw
;----------------------------------------------------------------------
; Exit to caller.
;----------------------------------------------------------------------
MG_2:
		CLC				;Signal success
U_EXIT:
		RET
;----------------------------------------------------------------------
; If we get here, we know:
;   1. we've been asked to move forward past the end of the buffer.
;   2. the file size is greater than the buffer size.
;   3. the buffer is filled to capacity.
;----------------------------------------------------------------------
MG_3A:
		MOV	DX,WORD PTR [BUF_ANCHOR_MAX][0]	;Get value
		MOV	CX,WORD PTR [BUF_ANCHOR_MAX][2]	; in registers
;----------------------------------------------------------------------
; If the current buffer anchor is as far forward as it can go, we can't
; move it any farther forward.
;----------------------------------------------------------------------
		CMP	DI,CX			;Test hi offset
		JNE	MG_3B

		CMP	SI,DX			; and lo offset
		JNE	MG_3B
;----------------------------------------------------------------------
; The end of the buffer is already at the EOF. Move the grid anchor so
; the last byte in the buffer is the last byte shown on the grid.
;----------------------------------------------------------------------
		CMP	BX,[GRID_ANCHOR]	;Are we already there?
		JE	MG_2

		MOV	[GRID_ANCHOR],BX	;Move to last
		JMP	MG_1E
;----------------------------------------------------------------------
; The buffer anchor is not at its maximum forward point and is
; definitely going to be moved forward.
;
; We can move the buffer forward BUF_SIZE/2 bytes only if:
; BUF_ANCHOR + BUF_SIZE/2 < BUF_ANCHOR_MAX
;----------------------------------------------------------------------
MG_3B:		
		MOV	BX,[BUF_SIZE]		;Get BUF_SIZE
		SHR	BX,1			; and divide by 2

		ADD	SI,BX			;Calc new 32-bit
		ADC	DI,0			; buf anchor

		CMP	DI,CX			;Test hi offset
		JA	MG_3C
		JB	MG_3D

		CMP	SI,DX			; and lo offset
		JBE	MG_3D
MG_3C:
;----------------------------------------------------------------------
; The new anchor would be beyond the maximum. Set it to the maximum
; and exit.
;----------------------------------------------------------------------
		MOV	DI,CX			;Set to maximum
		MOV	SI,DX			; buffer anchor
;----------------------------------------------------------------------
; Save the new buffer anchor.
;----------------------------------------------------------------------
MG_3D:
		MOV	CX,DI			;Save new anchor
		MOV	DX,SI			; in CX:DX

		XCHG	SI,WORD PTR [BUF_ANCHOR][0]	;Get old anchor
		XCHG	DI,WORD PTR [BUF_ANCHOR][2]	; in DI:SI

		SUB	CX,DI			;Figure difference
		SBB	DX,SI
;----------------------------------------------------------------------
; If we didn't move as far as requested, maximize the grid anchor.
;----------------------------------------------------------------------
		CMP	AX,DX			;Request <= move?
		JBE	MG_3E

		MOV	AX,[BUF_BYTES]		;Bytes in buffer
		SUB	AX,GRID_LEN		;-bytes on screen
		MOV	[GRID_ANCHOR],AX	;is new anchor
		INC	[REDO_BUF]		;Request buffer fill
		JMP	MG_1E
MG_3E:
		SUB	DX,AX			;Account for DISP
		SUB	[GRID_ANCHOR],DX	;Adjust anchor

		INC	[REDO_BUF]		;Refill buffer
		JMP	MG_1E
;----------------------------------------------------------------------
; If we get here, we know:
;   1. we've been asked to move back past the beginning of the buffer.
;   2. the file size is greater than the buffer size.
;   3. the buffer is filled to capacity.
;----------------------------------------------------------------------
MG_4A:
;----------------------------------------------------------------------
; If the current buffer anchor is as far back as it can go (0:0), we
; can't move the buffer.
;----------------------------------------------------------------------
		OR	DI,DI			;Test hi offset
		JNZ	MG_4B
;----------------------------------------------------------------------
; After this, we know that DI is 0; so we work only with SI.
;----------------------------------------------------------------------
		OR	SI,SI			;And lo
		JNZ	MG_4E
;----------------------------------------------------------------------
; The start of the buffer is at 0:0 (BOF). Move the grid anchor so the
; first byte in the buffer is the first byte shown on the grid.
;----------------------------------------------------------------------
		CMP	[GRID_ANCHOR],0		;Already at 0?
		JE	MG_2

		MOV	[GRID_ANCHOR],0		;Make it 0
		JMP	MG_1E
;----------------------------------------------------------------------
; Backing up is easy to do when the buf anchor > 64k.
;----------------------------------------------------------------------
MG_4B:
		SUB	SI,BX			;Back up 1/2 buffer
		SBB	DI,0			; (32-bit)
MG_4C:
		SUB	BX,AX			;Now move the disp
		ADD	[GRID_ANCHOR],BX	; and save new anchor
MG_4D:
		MOV	WORD PTR [BUF_ANCHOR][0],SI	;Save new
		MOV	WORD PTR [BUF_ANCHOR][2],DI	; buf anchor

		INC	[REDO_BUF]		;Request buffer refill
		JMP	MG_1E
;----------------------------------------------------------------------
; The buffer anchor is not at the start of the file, so we can move the
; buffer backward in the file.
;
; Move to either SI-BX or 0, whichever is farther forward in the file.
;----------------------------------------------------------------------
MG_4E:
		SHR	BX,1			;BUF_BYTES/2
		CMP	SI,BX			;Buf anchor > buf/2?
		JAE	MG_4F
;----------------------------------------------------------------------
; Since we can't back up the full amount, back up to BOF.
;----------------------------------------------------------------------
		SUB	SI,SI			;Zero lower anchor
		MOV	[GRID_ANCHOR],SI	;Set anchor to 0
		JMP	MG_4D
;----------------------------------------------------------------------
; If the current buffer anchor is > buffer size/2, back it up by
; buffer size/2. 
;----------------------------------------------------------------------
MG_4F:
		SUB	SI,BX			;SI = new buf anchor
		JMP	MG_4C

MOVE_GRID	ENDP

;======================================================================
; FILL_BUFFER (Near)
;
; Copies data from the file to the buffer. Up to BUF_SIZE bytes are
; copied from the file beginning at offset BUF_ANCHOR.
;----------------------------------------------------------------------
; Entry: None
; Exit :
;	CY - if file error
;	NC - no file error
;----------------------------------------------------------------------
; Changes: AX BX CX DX
;----------------------------------------------------------------------
FILL_BUFFER	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		SUB	CX,CX			;Create a zero
		XCHG	CL,[REDO_BUF]		;Swap for flag
		JCXZ	FB_EXIT			;Exit if 0
;----------------------------------------------------------------------
; If the current buffer data has changed, write it out to the file.
;----------------------------------------------------------------------
		CALL	COMMIT_BUFFER
		JC	FB_EXIT
;----------------------------------------------------------------------
; Position the scratch file to the byte specified in BUF_ANCHOR.
;----------------------------------------------------------------------
FB_2A:
		MOV	AX,4200H		;Position file pointer
		MOV	BX,[TMP_HANDLE]		; for this handle
		MOV	DX,WORD PTR [BUF_ANCHOR][0] ;Lo word
		MOV	CX,WORD PTR [BUF_ANCHOR][2] ;Hi word
		INT	21H			; thru DOS
		JC	FB_EXIT
;----------------------------------------------------------------------
; Attempt to fill the buffer from the file.
;----------------------------------------------------------------------
		MOV	AH,3FH			;Read file
		MOV	BX,[TMP_HANDLE]		; from this handle
		MOV	CX,[BUF_SIZE]		; this many bytes
		MOV	DX,OFFSET BUFFER	;Put data here
		INT	21H			; thru DOS
;----------------------------------------------------------------------
; After a successful read, AX contains the number of bytes actually
; read. If carry set, the calling program just ignores it. 
;----------------------------------------------------------------------
		MOV	[BUF_BYTES],AX		;Save # bytes in buffer
FB_EXIT:
		RET

FILL_BUFFER	ENDP

;======================================================================
; COMMIT_BUFFER (Near)
;
; If the data in the buffer has been changed, write the buffer back to
; the file beginning at offset BUF_ANCHOR.
;----------------------------------------------------------------------
; Entry: None
; Exit :
;	CY - if file error
;	NC - no file error
;----------------------------------------------------------------------
; Changes: AX BX CX DX
;----------------------------------------------------------------------
COMMIT_BUFFER	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		SUB	CX,CX			;Create a zero
		XCHG	CL,[BUF_ALTERED]	;Fetch/clear flag
		JCXZ	CB_EXIT

		MOV	[FILE_ALTERED],CL	;Non-zero = altered
;----------------------------------------------------------------------
; Position the scratch file to the byte specified in BUF_ANCHOR.
;----------------------------------------------------------------------
		MOV	AX,4200H		;Position file pointer
		MOV	BX,[TMP_HANDLE]		; for this handle
		MOV	DX,WORD PTR [BUF_ANCHOR][0] ;Lo word
		MOV	CX,WORD PTR [BUF_ANCHOR][2] ;Hi word
		INT	21H			; thru DOS
		JC	CB_EXIT
;----------------------------------------------------------------------
; Write the buffer contents to the file.
;----------------------------------------------------------------------
		MOV	AH,40H			;Write to file fn
		MOV	BX,[TMP_HANDLE]		;To this handle
		MOV	CX,[BUF_BYTES]		;This many bytes
		MOV	DX,OFFSET BUFFER	;Get data from here
		INT	21H			; thru DOS
		JC	CB_EXIT
;----------------------------------------------------------------------
; After a successful write, AX contains the number of bytes actually
; written. If it doesn't match the number we tried to write, error.
;----------------------------------------------------------------------
		CMP	AX,CX			;Write them all?
		JB	CB_EXIT			;JB = JC = carry set
CB_EXIT:
		RET

COMMIT_BUFFER	ENDP

;======================================================================
; DISPLAY_GRID (Near)
;
; Starting with the current pointer into the buffer, put as many chars
; as possible on the screen.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: CX DX
;----------------------------------------------------------------------
DISPLAY_GRID	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		SUB	CX,CX			;Create a zero
		XCHG	CL,[REDO_GRID]		;Get/reset flag
		JCXZ	DG_EXIT
;----------------------------------------------------------------------
; Draw rows 0 through 15 on the screen
;----------------------------------------------------------------------
		MOV	CX,16			;Row counter
		SUB	DX,DX			;Row # to draw
DG_1:
		CALL	DISPLAY_ROW		;Display row
		INC	DX			;Next row, please
		LOOP	DG_1
DG_EXIT:
		RET

DISPLAY_GRID	ENDP

;======================================================================
; DISPLAY_ROW (Near)
;
; Display one row of the grid on the display.
;----------------------------------------------------------------------
; Entry:
;	DX = # of row to display
; Exit: None
;----------------------------------------------------------------------
; Changes: AX BX SI DI BP
;----------------------------------------------------------------------
DISPLAY_ROW	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		PUSH	CX			;Save used registers
		PUSH	DX

		MOV	BH,[VPAGE]		;Active video page
;----------------------------------------------------------------------
; From the row number, figure out how far we are into the display
; buffer. From that figure out how far we are into the file buffer.
;----------------------------------------------------------------------
		MOV	SI,DX			;Put row in SI and
		MOV	CL,4			; multiply by 16
		SHL	SI,CL			; offset into display
		ADD	SI,[GRID_ANCHOR]	; -> offset into buffer
		MOV	CX,SI			;Save in CX

		MOV	BP,OFFSET BUFFER	;Addr of buffer in mem
		ADD	SI,BP			;SI -> memory addr
		MOV	DI,SI			;Save it in DI
		ADD	BP,[BUF_BYTES]		;BP -> last valid byte
;----------------------------------------------------------------------
; Calculate the current screen row.
;----------------------------------------------------------------------
		ADD	DL,ROW_0		;Bias by screen row
		MOV	BL,DL			; and save it
;----------------------------------------------------------------------
; Position the cursor to display the offset.
;----------------------------------------------------------------------
		MOV	DH,OFF_COL		;Column for OFFSET
		XCHG	DH,DL			;Row,col order
		MOV	AH,2			;Position cursor
		INT	10H			; thru BIOS
;----------------------------------------------------------------------
; Does the first byte in this row point to a byte that is actually
; part of the file? If not, just blank out this entire row.
;----------------------------------------------------------------------
		CMP	SI,BP			;Current < invalid?
		JBE	DR_1A

		MOV	AX,0A00H+254		;Write repeated char
		MOV	CX,8			; this many
		INT	10H			; thru BIOS

		JMP	SHORT DR_1B
;----------------------------------------------------------------------
; Calculate and show the file offset of the first byte in this row.
;----------------------------------------------------------------------
DR_1A:
		MOV	DX,WORD PTR [BUF_ANCHOR][0]	;Get buffer
		MOV	AX,WORD PTR [BUF_ANCHOR][2]	; offset

		ADD	DX,CX			;Add current byte
		ADC	AX,0			; to 32-bit #

		CALL	HEX4			;Display high part
		MOV	AX,DX			; and
		CALL	HEX4			;Display low part
DR_1B:
;----------------------------------------------------------------------
; Display the hex bytes for this row.
;----------------------------------------------------------------------
		MOV	AH,2			;Position cursor
		MOV	DH,BL			;To row
		MOV	DL,HEX_COL		; and column
		INT	10H			; thru BIOS

		MOV	CX,16			;Bytes in a row
DR_2A:
		CMP	SI,BP			;Past valid bytes?
		JBE	DR_2B

		MOV	AX,0E00H+254		;Put a block
		INT	10H			; thru BIOS
		MOV	AX,0E00H+254		; and again
		INT	10H			; also thru BIOS
		JMP	SHORT DR_2C
DR_2B:
		LODSB				;Get hex byte
		CALL	HEX2			;Display it TTY
DR_2C:
		MOV	AX,0E00H+BLANK		;Write a blank
		INT	10H			; thru BIOS

		LOOP	DR_2A			;Repeat
;----------------------------------------------------------------------
; Display the ASCII characters for this row.
;----------------------------------------------------------------------
		MOV	DH,BL			;To row
		MOV	DL,ASC_COL		; and column

		MOV	SI,DI			;Point to start of row

		MOV	CX,16			;Bytes in a row
DR_3A:
		MOV	AH,2			;Position cursor
		INT	10H			; thru BIOS

		CMP	SI,BP			;Past valid bytes?
		JBE	DR_3B

		MOV	AX,0E00H+BLANK		;Write a blank
		INT	10H			; thru BIOS
		JMP	SHORT DR_3C
DR_3B:
		PUSH	CX
		MOV	AH,0AH			;Write char at cursor
		LODSB				;Get hex byte
		MOV	CX,1			;Write 1 copy
		INT	10H			; thru BIOS
		POP	CX

		INC	DL			;Next column
DR_3C:
		LOOP	DR_3A			;Repeat
;----------------------------------------------------------------------
; Return to caller
;----------------------------------------------------------------------
		POP	DX			;Restore registers
		POP	CX

		RET

DISPLAY_ROW	ENDP

;======================================================================
; HEX4 (Near)
;
; Write AX as 4 hex digits using BIOS Write TTY.
;----------------------------------------------------------------------
; Entry:
;	AX = value to display
; Exit : None
;----------------------------------------------------------------------
; CHANGES: None
;----------------------------------------------------------------------
HEX4		PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		PUSH	CX			;Save used registers
		PUSH	DX

		MOV	CX,4			;Number of digits
		JMP	SHORT H_1
;======================================================================
; HEX2 (Near, nested)
;
; Write AX as 2 hex digits using BIOS Write TTY.
;----------------------------------------------------------------------
; Entry:
;	AL = value to display
; Exit : None
;----------------------------------------------------------------------
; CHANGES: AX
;----------------------------------------------------------------------
HEX2		PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		PUSH	CX			;Save used registers
		PUSH	DX

		MOV	AH,AL			;Put byte in AH
		MOV	CX,2			;Number of digits
H_1:
		PUSH	CX			;(Save count)
		MOV	CL,4			;Shift count
		ROL	AX,CL			;Rotate into position
		POP	CX			;(Restore count)

		PUSH	AX			;Preserve digits

		AND	AL,0FH			;Mask off single digit
		ADD	AL,90H			;Convert digit to ASCII
		DAA				; using a small
		ADC	AL,40H			; code sequence
		DAA				; that's tricky

		MOV	AH,2			;Display char
		MOV	DL,AL			; in DL
		INT	21H			; thru DOS

		POP	AX			;Restore digits
		LOOP	H_1			;Repeat until done

		POP	DX			;Restore registers
		POP	CX

		RET

HEX2		ENDP
HEX4		ENDP

;======================================================================
; MOVE_CURSOR (Near)
;
; Move the screen cursor the number of columns specified in AL. If AL>0
; cursor is moved to the right. Result is not checked.
;----------------------------------------------------------------------
; Entry:
;	AL = columns to move
; Exit:
;	None
;----------------------------------------------------------------------
; Changes: AX BX DX
;----------------------------------------------------------------------
MOVE_CURSOR	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG

		ADD	BYTE PTR [VCURSOR],AL	;Adjust columns

		MOV	AH,2			;Position cursor
		MOV	BH,[VPAGE]		; on this page
		MOV	DX,[VCURSOR]		; to these coordinates
		INT	10H			; thru BIOS
		RET

MOVE_CURSOR	ENDP

;======================================================================
; SET_CURSOR (Near)
;
; Position the cursor under the correct character on the screen.
;----------------------------------------------------------------------
; Entry: None
; Exit : None
;----------------------------------------------------------------------
; Changes: AX BX DX
;----------------------------------------------------------------------
SET_CURSOR	PROC	NEAR
	ASSUME	CS:CSEG, DS:CSEG, ES:NOTHING, SS:CSEG
;----------------------------------------------------------------------
; Figure the screen column based on whether the mode is hex or ascii.
;----------------------------------------------------------------------
		MOV	DL,[GRIDCOL]		;Get grid column

		TEST	[MODE],MODE_MASK	;Check flag for mode
		JNZ	SC_2A

		ADD	DL,ASC_COL		;Bias for ASCII column
SC_1:
;----------------------------------------------------------------------
; Figure the screen row.
;----------------------------------------------------------------------
		MOV	DH,[GRIDROW]		;Get grid row
		ADD	DH,ROW_0		;Bias to screen row

		MOV	[VCURSOR],DX		;Save current cursor
		MOV	BH,[VPAGE]		;Use current page
		MOV	AH,2			;Set cursor position
		INT	10H			; thru BIOS

		RET
;----------------------------------------------------------------------
; Figure the column for the hex display.
;----------------------------------------------------------------------
SC_2A:
		MOV	AL,DL			;Grid column
		ADD	DL,DL			; *2
		ADD	DL,AL			; total is *3
		ADD	DL,HEX_COL		;Add start of hex cols

		CMP	[MODE],LEFT		;What digit?
		JE	SC_1

		INC	DL			;Add 1 if right digit
		JMP	SC_1

SET_CURSOR	ENDP

;======================================================================
; Allocated after program loads.
;----------------------------------------------------------------------
PC		=	$			;End of code

PC		=	PC + 512
STACK_TOP	=	PC			;Top of stack

BUFFER		=	PC			;Bottom of buffer

CSEG		ENDS
		END	ENTPT
[ RETURN TO DIRECTORY ]