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 ]