;**********************************************************;
;* HEXEDIT.ASM -- Tiny 2K Hex Editor, few features but it *;
;* is tiny.  This program uses a temporary file, so it is *;
;*  slow on floppies.  Running it from a HD is not slow.  *;
;*                                                        *;
;*                    By Tenie Remmel                     *;
;*                                                        *;
;*                                                        *;
;* This program is public domain.  However, if you intend *;
;* to distribute the source code, you MUST retain this    *;
;* header in its original form.                           *;
;**********************************************************;

Ideal
Jumps

Model Tiny
CodeSeg

Org 80h

ComlineLen     db ?
FileName       db ?

Org 100h

Start:         jmp Program

;********************************************************************

LEFT_MASK      = 00Fh
RIGHT_MASK     = 0F0h
BUFFER_LEN     = 04000h

KEY_UP         = 48h
KEY_DOWN       = 50h
KEY_LEFT       = 4Bh
KEY_RIGHT      = 4Dh
KEY_PGUP       = 49h
KEY_PGDN       = 51h
KEY_F1         = 3Bh
KEY_F2         = 3Ch

ERR_MEM_LEN    = 13
ERR_FIND_LEN   = 14
ERR_ACCESS_LEN = 17
ERR_ZERO_LEN   = 14

;********************************************************************

ErrMem         db 'Out of memory'
ErrFind        db 'File not found'
ErrAccess      db 'File access error'
ErrZero        db 'Zero-byte file'
UsageStr       db 'Syntax: HEXEDIT <file>'
HelpStr        db 'Keys: ',24,32,25,32,27,32,26
               db ' PgUp PgDn  F1=Save/Abort  F2=ASCII table'
TitleStr       db 'HexEditor 1.10 þ Tenie Remmel'
OffsetStr      db 'OFFSET'              ;Length 06h, column 03h
HexDataStr     db 'HEX DATA'            ;Length 08h, column 1Fh
AscDataStr     db 'ASCII DATA'          ;Length 0Ah, column 41h
QuitMsg        db 'Save Changes (Y/N)? '     ;Length 14h

WindowChars    db 1, 'ÉÍ»Í'
               db 1, 'º º '
               db 1, 'ÇÄ¶Â'
               db 18,'º º³'
               db 1, 'ÇÄ¶Á'
               db 2, 'º º '
               db 1, 'ÈÍ¼Í'
               db 0

FileLen        dd 00000000
BufferPos      dd 00000000
MaxBufferPos   dd 00000000
FitsInBuffer   db 00
FitsInGrid     db 00
GridPos        dw 0000
MaxGridPos     dw BUFFER_LEN-0100h
CurRow         db 00
CurCol         db 00
CurPos         db 00
CurMask        db 00Fh
               db ' :'
TempStr        db 'HEXEDIT.TMP',0
TempName       dw offset TempStr
SrcHandle      dw 0000
TempHandle     dw 0000
PartialRow     db 00


;********************************************************************

Proc           Program

               lea ax,[Buffer]          ;Check for memory
               cmp sp,ax
               jna NoMem                ;Extremely tiny?
               lea ax,[StackTop]        ;New stack top
               cli                      ;No interrupts
               xchg sp,ax               ;Reset stack
               sti                      ;Now interrupts
               sub ax,sp                ;Big enough for buffer?
               sub ax,2
               cmp ax,BUFFER_LEN
               jna MemOK                ;If not, Out of Memory error

MemOK:         mov di,80h               ;Make filename ASCIIZ...
               cmp [byte di],1          ;No command line?
               jle NoCmdLine
               inc di
               mov bl,[ComlineLen]      ;Check for leading spaces
               xor bh,bh
               mov cx,bx
               mov al,' '
               repe scasb
               dec di
               mov si,di                ;Shift back filename
               mov di,81h
               mov cx,bx
               rep movsb
               mov di,81h               ;Find the CR at the end
               mov cx,bx
               mov al,0dh
               repne scasb
               dec di                   ;Put a 0 there (ASCIIZ)
               xor al,al
               stosb
               mov di,81h               ;Check for trailing spaces
               mov cx,bx
               mov al,20h
               repne scasb
               dec di                   ;Put a 0 there (ASCIIZ)
               xor al,al
               stosb

               lea si,[FileName]        ;File on another drive?
               lodsb
               mov [TempStr-2],al       ;Add 'd:' to TempName...
               lodsb
               cmp al,':'
               jnz NoDrive
               sub [TempName],2         ;Point TempName to new name
               
NoDrive:       mov ax,3d00h             ;Open source file
               lea dx,[FileName]
               int 21h
               jnc FileOK
               jmp NotFound
FileOK:        mov [SrcHandle],ax
               
               mov ah,3ch               ;Open temp file
               xor cx,cx
               mov dx,[TempName]
               int 21h
               jc FileError             ;Check
               mov [TempHandle],ax

               lea dx,[Buffer]          ;Offset of buffer
CopyLoop:      mov ah,3Fh               ;Read function
               mov bx,[SrcHandle]       ;Source handle
               mov cx,BUFFER_LEN        ;Buffer length
               int 21h                  ;Read file
               jnc copy1                ;Check for Zero etc.
               cmp ax,0
               jnz FileError
copy1:         mov cx,ax                ;Get length
               mov ah,40h               ;Write function
               mov bx,[TempHandle]      ;Tempfile handle
               int 21h                  ;Write data
               jc FileError             ;Check
               cmp cx,BUFFER_LEN        ;End of file?
               jz CopyLoop              ;If not, loop back

CopyDone:      mov ah,3eh               ;Close source file
               mov bx,[SrcHandle]
               int 21h
               
               mov ax,4202h             ;Get file size
               mov bx,[TempHandle]
               xor cx,cx
               xor dx,dx
               int 21h
               jc FileError             ;Check
               mov [word FileLen],ax    ;Save it
               mov [word FileLen+2],dx

               cmp dx,0                 ;Does it fit?
               jnz NotFit
               cmp ax,0                 ;Zero byte file?
               jnz size1
               jmp ZeroErr
size1:         cmp ax,BUFFER_LEN        ;Fits in buffer?
               ja NotFit
               mov [FitsInBuffer],1
               mov [word MaxBufferPos],0     ;If so, max buffer pos = 0
               mov [word MaxBufferPos+2],0
               mov cx,ax                ;And max grid pos = length - 100h
               sub cx,0100h
               mov [MaxGridPos],cx
               cmp ax,0100h             ;Fits in grid?
               ja FitIn
               mov [FitsInGrid],1       ;If so, max grid pos = 0
               mov [MaxGridPos],0
               jmp FitIn

NotFit:        sub ax,BUFFER_LEN        ;Save max buffer pos
               sbb dx,0
               mov [word MaxBufferPos],ax
               mov [word MaxBufferPos+2],dx

FitIn:         mov ax,4200h             ;Return file pointer to start
               mov bx,[TempHandle]
               xor cx,cx
               xor dx,dx
               int 21h
               jc FileError             ;Check

               mov ax,3
               int 10h
               
               call DrawBackground      ;Draw background
               call SetupAscTable       ;Set up ASCII table
               
               call FillBuffer          ;Initialize
               jmp ReDraw

KeyLoop:       mov ah,0                 ;Get key
               int 16h

               cmp al,0                 ;Not ASCII, command
               jz Command
HexDigit:      cmp al,'0'               ;Digit?
               jl KeyLoop               ;No
               cmp al,'9'
               jle Is_0_to_9            ;Yes
               cmp al,'A'               ;"A" to "F"?
               jl KeyLoop               ;No
               cmp al,'F'
               jle Is_A_to_F            ;Yes
               sub al,('a' - 'A')       ;"a" to "f"?
               cmp al,'A'
               jl KeyLoop               ;No?
               cmp al,'F'
               jg KeyLoop               ;No?
Is_A_to_F:     sub al,('A' - 0ah)       ;Yes, make binary
               jmp conv1
Is_0_to_9:     sub al,'0'               ;Make binary

conv1:         mov ah,[CurMask]         ;At the left?
               cmp ah,LEFT_MASK
               jnz conv2
               mov cl,4                 ;If so, shift left
               shl al,cl
conv2:         mov bl,[CurPos]          ;Cursor position
               xor bh,bh
               mov si,[GridPos]         ;Grid position
               mov dl,[byte Buffer+si+bx]     ;Byte from buffer
               and dl,ah                ;Replace digit
               or dl,al
               mov [byte Buffer+si+bx],dl     ;Write back to buffer
               jmp GoRight              ;Advance cursor

Command:       cmp ah,KEY_UP            ;Up?
               jz GoUp
               cmp ah,KEY_DOWN          ;Down?
               jz GoDown
               cmp ah,KEY_LEFT          ;Left?
               jz GoLeft
               cmp ah,KEY_RIGHT         ;Right?
               jz GoRight
               cmp ah,KEY_PGUP          ;PgUp?
               jz GoPgUp
               cmp ah,KEY_PGDN          ;PgDn?
               jz GoPgDn
               cmp ah,KEY_F1            ;F1?
               jz Quit
               cmp ah,KEY_F2            ;F2?
               jz ShowAscTable
               jmp KeyLoop              ;Loop back

GoUp:          mov bx,10h               ;Move of 16
               cmp [CurRow],0           ;First row?
               jz MoveGridLeft          ;Move grid up
               sub [CurPos],10h         ;Otherwise,
               dec [CurRow]             ;move cursor up
               jmp ReDraw               ;Redraw screen

GoDown:        mov bx,10h               ;Move of 16
               cmp [CurRow],0Fh         ;Last row?
               jz MoveGridRight         ;Move grid down
               add [CurPos],10h         ;Otherwise,
               inc [CurRow]             ;move cursor down
               jmp ReDraw               ;Redraw screen

GoLeft:        cmp [CurMask],RIGHT_MASK ;Right side?
               jnz LeftSide
               not [CurMask]            ;Other side
               jmp ReDraw               ;Redraw screen
LeftSide:      not [CurMask]            ;Other side
               mov bx,1                 ;Move of 1
               cmp [CurCol],0           ;First column?
               jz PrevRow               ;Prev. row
               dec [CurCol]             ;Otherwise,
               dec [CurPos]             ;prev. column
               jmp ReDraw
PrevRow:       cmp [CurRow],0           ;First row?
               jz MoveGridLeft          ;Move grid left
               mov [CurCol],0Fh         ;Otherwise,
               dec [CurRow]             ;prev. row, last column
               dec [CurPos]
               jmp ReDraw               ;Redraw screen

GoRight:       cmp [CurMask],LEFT_MASK  ;Left side?
               jnz RightSide
               not [CurMask]            ;Other side
               jmp ReDraw               ;Redraw screen
RightSide:     not [CurMask]            ;Other side
               mov bx,1                 ;Move of 1
               cmp [CurCol],0Fh         ;Last column?
               jz NextRow               ;Next row
               inc [CurCol]             ;Otherwise,
               inc [CurPos]             ;next column
               jmp ReDraw
NextRow:       cmp [CurRow],0Fh         ;Last row?
               jz MoveGridRight         ;Move grid right
               mov [CurCol],0           ;Otherwise,
               inc [CurRow]             ;next row, first column
               inc [CurPos]
               jmp ReDraw               ;Redraw screen

GoPgUp:        mov bx,0100h             ;Move of 100h
               jmp MoveGridLeft         ;Move grid left

GoPgDn:        mov bx,0100h             ;Move of 100h
               jmp MoveGridRight        ;Move grid right

MoveGridLeft:  mov ax,[GridPos]         ;Moving off left of buffer?
               cmp ax,bx                ;If not, just move the grid
               jl BufLeft               ;left and redraw the screen.
               sub [GridPos],bx
               jmp ReDraw
BufLeft:       call WriteBuffer         ;Write buffer
               mov ax,[word BufferPos]  ;Get buffer position
               mov dx,[word BufferPos+2]
               mov cx,[GridPos]         ;Grid position to center
               add cx,(BUFFER_LEN / 2)  ;of new buffer
               sub ax,(BUFFER_LEN / 2)
               sbb dx,0
               jnc NotOffLeft           ;Off the start of the file?
               xor dx,dx                ;Start at the start
               xor ax,ax
               mov cx,[GridPos]         ;Grid where it goes...
               add cx,[word BufferPos]
               cmp cx,bx                ;Totally off the left?
               jge NotOffLeft
               mov [CurRow],0
               mov [CurCol],0
               mov [CurPos],0
               mov [CurMask],LEFT_MASK
               mov cx,bx
NotOffLeft:    sub cx,bx
               mov [GridPos],cx
               mov [word BufferPos],ax  ;New buffer position
               mov [word BufferPos+2],dx
               call FillBuffer          ;Fill buffer
               jmp ReDraw               ;Redraw screen

MoveGridRight: mov ax,[GridPos]         ;Moving off right of buffer?
               add ax,bx                ;If not, just move the grid
               cmp ax,[MaxGridPos]      ;right and redraw the screen.
               jg BufRight
               add [GridPos],bx
               jmp ReDraw
BufRight:      cmp [FitsInBuffer],0     ;Fits in the buffer?
               jz NoFitR
               mov [CurRow],0Fh
               mov [CurCol],0Fh
               mov [CurPos],0FFh
               mov [CurMask],RIGHT_MASK
               push [MaxGridPos]
               pop [GridPos]
               jmp ReDraw
NoFitR:        call WriteBuffer         ;Write buffer
               mov ax,[word BufferPos]  ;Get buffer position
               mov dx,[word BufferPos+2]
               mov cx,[GridPos]         ;Grid position to center
               sub cx,(BUFFER_LEN / 2)  ;of new buffer
               add ax,(BUFFER_LEN / 2)
               adc dx,0
               add cx,bx
               cmp dx,[word MaxBufferPos+2]  ;Off the end of the file?
               jl NotOffRight
               cmp ax,[word MaxBufferPos]
               jbe NotOffRight
               mov ax,[word MaxBufferPos]    ;Maximum position
               mov dx,[word MaxBufferPos+2]
               mov cx,[GridPos]         ;Grid where it goes...
               mov si,ax
               mov di,dx
               sub si,[word BufferPos]
               sbb di,[word BufferPos+2]
               sub cx,si
               add cx,bx
               cmp cx,[MaxGridPos]      ;Totally off the right?
               jbe NotOffRight
               mov cx,[MaxGridPos]
               mov [CurRow],0Fh
               mov [CurCol],0Fh
               mov [CurPos],0FFh
               mov [CurMask],RIGHT_MASK
NotOffRight:   mov [GridPos],cx
               mov [word BufferPos],ax  ;New buffer position
               mov [word BufferPos+2],dx
               call FillBuffer          ;Fill buffer

ReDraw:        call DrawGrid            ;Redraw screen
               mov ah,2                 ;Position Cursor func.
               mov bh,0                 ;Page 0
               mov dh,[CurRow]          ;Row + 5
               add dh,5
               mov dl,[CurCol]          ;Column * 3
               mov bl,dl
               shl dl,1
               add dl,bl
               add dl,12                ; + 12
               mov al,[CurMask]         ; + 1 if right digit
               not al
               and al,1
               add dl,al
               int 10h                  ;Do it
               jmp KeyLoop              ;Jump to key loop

ShowAscTable:  mov ax,0501h             ;Show page 1
               int 10h
               mov ah,0                 ;Wait for key
               int 16h
               mov ax,0500h             ;Reset to page 0
               int 10h
               jmp KeyLoop              ;Jump to key loop

Quit:          mov ax,3                 ;Reset video mode
               int 10h
               call WriteBuffer         ;Write buffer
               mov ah,3eh               ;Close temp file
               mov bx,[TempHandle]
               int 21h
               jc FileError             ;Check
               lea bp,[QuitMsg]         ;Display 'Save?' message
               push ds
               pop es
               mov ax,1301h
               mov bx,0007h
               mov cx,20
               xor dx,dx
               int 10h

QuitKeyLoop:   mov ah,0                 ;Get a key
               int 16h
               cmp al,'N'               ;No
               jz NoSave
               cmp al,'n'
               jz NoSave
               cmp al,'Y'               ;Yes
               jz Save
               cmp al,'y'
               jz Save
               jmp QuitKeyLoop          ;Loop back

NoSave:        mov al,'N'               ;Show char
               int 29h
               mov al,0ah               ;Two linefeeds
               int 29h
               int 29h
               mov ah,41h               ;Delete temp file
               mov dx,[TempName]
               int 21h
               jc FileError             ;Check
               jmp Exit                 ;Exit

Save:          mov al,'Y'               ;Show char
               int 29h
               mov al,0ah               ;Two linefeeds
               int 29h
               int 29h
               mov ah,41h               ;Delete old file
               lea dx,[FileName]
               int 21h
               jc FileError             ;Check
               mov dx,[TempName]        ;Rename new file
               lea di,[FileName]        ;to old filename
               mov ah,56h
               int 21h
               jc FileError             ;Check

Exit:          mov ax,4c00h
               int 21h

EndP           Program

;********************************************************************

Proc           DrawBackground

               lea si,[WindowChars]     ;Get offset of table
               xor ch,ch                ;Clear CH
               mov ax,0b800h            ;Point ES to video memory
               mov es,ax
               xor di,di                ;Point DI to UL corner
DrawLoop1:     lodsb                    ;Get byte
               cmp al,0                 ;End of table?
               jz DBGDone               ;Done.
               mov cl,al                ;Put number of times in CX
DrawLoop2:     push di                  ;Save DI
               mov bx,si                ;Get pointer in BX
               mov al,[bx]              ;Get first char
               stosb                    ;Store it
               inc di
               mov al,[bx+1]            ;Get line char
               push cx                  ;Save CX
               mov cx,78                ;78 chars
DrawLoop3:     stosb                    ;Store it
               inc di
               loop DrawLoop3           ;Loop back
               pop cx                   ;Get back CX
               mov ax,[bx+2]            ;Get end char
               stosb                    ;Store it
               pop di                   ;Get back DI
               add di,20                ;Char 10
               mov ax,[bx+3]            ;Get middle char
               stosb                    ;Store it
               add di,99                ;Char 60
               stosb                    ;Store it
               add di,39                ;Next line
               loop DrawLoop2           ;Loop back
               add si,4                 ;Increment SI
               jmp DrawLoop1            ;Loop back
DBGDone:       push ds                  ;Get DS in ES
               pop es
               mov ax,1300h             ;BIOS Write String
               mov bx,0007h             ;Attribute 07 (Wh/Bk)
               mov cx,29                ;29 chars,
               mov dx,0119h             ;Row 01h, Column 19h.
               lea bp,[TitleStr]        ;Title string
               int 10h
               mov cx,54                ;54 chars,
               mov dx,170Dh             ;Row 17h, Column 0Dh.
               lea bp,[HelpStr]         ;Help string
               int 10h
               mov cx,6                 ;6 chars,
               mov dx,0303h             ;Row 03h, Column 03h.
               lea bp,[OffsetStr]       ;"OFFSET"
               int 10h
               mov cx,8                 ;8 chars,
               mov dx,031fh             ;Row 03h, Column 1Fh.
               lea bp,[HexDataStr]      ;"HEX DATA"
               int 10h
               mov cx,10                ;10 chars,
               mov dx,0341h             ;Row 03h, Column 41h.
               lea bp,[AscDataStr]      ;"ASCII DATA"
               int 10h
               ret                      ;Exit

EndP           DrawBackground

;********************************************************************

Proc           FillBuffer

               mov ax,4200h             ;Move pointer to BufferPos
               mov bx,[TempHandle]
               mov cx,[word BufferPos+2]
               mov dx,[word BufferPos]
               int 21h
               jc FileError             ;Check
               mov ah,3fh               ;Read file
               mov bx,[TempHandle]
               mov cx,BUFFER_LEN        ;Fill buffer
               lea dx,[Buffer]
               int 21h
               jc FileError             ;Check
               ret

EndP           FillBuffer

;********************************************************************

Proc           WriteBuffer

               push ax bx cx dx         ;Push registers
               mov ax,4200h             ;Move pointer to BufferPos
               mov bx,[TempHandle]
               mov cx,[word BufferPos+2]
               mov dx,[word BufferPos]
               int 21h
               jc FileError             ;Check
               mov ah,40h               ;Write file
               mov bx,[TempHandle]
               cmp [FitsInBuffer],0
               jz NoFit
               mov cx,[word FileLen]    ;File length or
               jmp DoWrite
NoFit:         mov cx,BUFFER_LEN        ;Buffer length
DoWrite:       lea dx,[Buffer]
               int 21h
               jc FileError             ;Check
               pop dx cx bx ax          ;Pop registers
               ret

EndP           WriteBuffer

;********************************************************************

Proc           SetupAscTable
               
               mov ax,0b90ah            ;Point to video memory
               mov es,ax                ;at page 01, shifted-row
               xor di,di
               mov cx,2000              ;Clear screen
               mov ax,0720h
               rep stosw
               mov ax,2000h             ;AH = ' ', AL = 0
AscLoop1:      xor ch,ch                ;Row 0
               push ax
               mov al,14                ;DI = 14 * column + 4
               mul cl
               add ax,4
               mov di,ax
               pop ax
AscLoop2:      call PutHex              ;Put byte in hex
               xchg ah,al
               stosb                    ;Two spaces
               inc di
               stosb
               inc di
               xchg ah,al
               stosb                    ;Put byte in ASCII
               inc di
               add di,150               ;Next row
               inc al                   ;All the chars?
               jz AscLines              ;Done.
               inc ch                   ;Next row
               cmp ch,24                ;Last row?
               jb AscLoop2              ;Loop back
               inc cl                   ;Next column
               cmp cl,11                ;Last column?
               jb AscLoop1              ;Loop back
AscLines:      mov al,'³'               ;Vertical lines
               mov di,2                 ;Start at column 01
ALinesLoop:    push di
               mov cx,24                ;24 rows
VLineLoop:     stosb                    ;Draw the line
               add di,159               ;Next row
               loop VLineLoop           ;Loop back
               pop di
               add di,14                ;Next column
               cmp di,160               ;Past the end?
               jb ALinesLoop            ;Loop back
               mov ah,2                 ;Hide cursor
               mov bh,1
               mov dx,1901h
               int 10h
               ret                      ;Exit


EndP           SetupAscTable

;********************************************************************

Proc           DrawGrid

               mov ax,0b800h            ;Point to video memory
               mov es,ax
               cmp [FitsInGrid],1       ;Fits in grid?
               jz DrawSmall
               mov ax,[word BufferPos]  ;Get offset in file
               mov dx,[word BufferPos+2]
               add ax,[GridPos]         ;Adjust for grid position
               adc dx,0
               mov bx,0500h             ;Start at row 05
               mov cx,10h               ;10h rows
               lea si,[Buffer]          ;Get offset of data
               add si,[GridPos]
GridLoop:      call DrawRow             ;Draw a row
               loop GridLoop            ;Repeat
               jmp GridDone             ;Done.
DrawSmall:     xor ax,ax                ;Fits, offset = 0
               xor dx,dx
               mov bx,[word FileLen]    ;File length
               push bx
               mov cl,4                 ;length / 10h rows
               shr bx,cl
               mov cx,bx
               mov bx,0500h             ;Start at row 05
               lea si,[Buffer]          ;Get offset of data
GridLoopS:     call DrawRow             ;Draw a row
               loop GridLoopS           ;Repeat
               pop cx                   ;File length MOD 10h
               and cx,0Fh               ;bytes in partial row
               cmp cx,0                 ;Zero? Done.
               jz GridDone
               mov bl,1
               call DrawRow             ;Draw partial row
GridDone:      ret                      ;Exit

EndP           DrawGrid
               
;********************************************************************

Proc           DrawRow
               ;Args: DX:AX=Offset, BH=physical row, SI=16 bytes data
               ;At end: DX:AX inc by 10h, BH inc by 1, SI inc by 10h

               push cx di bx            ;Push registers
               push ax ax
               mov [PartialRow],bl
               mov al,160               ;Get offset for (0, BH) in DI
               mul bh
               mov di,ax
               pop ax
               add di,4                 ;column 2
               call PrintLong
               add di,4                 ;Past the line
               xor bx,bx                ;Start byte 0
               cmp [PartialRow],1       ;Partial row?
               jz part1
               mov cx,10h               ;10h bytes
               jmp HexLoop
part1:         push cx                  ;Save CX and DI
               push di
HexLoop:       mov al,[si+bx]           ;Current byte
               call PutHex              ;Put byte in hex
               mov al,' '               ;A space
               stosb                    ;Store it
               inc di                   ;Skip attribute
               inc bx                   ;Next byte
               loop HexLoop             ;Loop back
               cmp [PartialRow],1       ;Partial row?
               jz part2
               add di,4                 ;Past the line
               mov cx,10h               ;10h bytes
               jmp AscLoop
part2:         pop di                   ;Get back DI
               add di,100               ;To ASCII window
               pop cx                   ;Get back CX
AscLoop:       movsb                    ;Move byte
               inc di                   ;Skip attribute
               loop AscLoop             ;Loop back
               pop ax                   ;Get back AX
               add ax,10h               ;DX:AX = DX:AX + 10h
               adc dx,0
               pop bx                   ;Get back BX
               inc bh                   ;Next Row
               pop di cx                ;Pop registers
               ret                      ;Exit

EndP           DrawRow

;********************************************************************

Proc           PrintLong
               ;Args: DX:AX=num,DI=offset, assumes ES=video memory

               push ax
               mov al,dh                ;First byte
               call PutHex
               mov al,dl                ;Second
               call PutHex
               mov al,ah                ;Third
               call PutHex
               pop ax                   ;Fourth
               call PutHex
               ret                      ;Exit

EndP           PrintLong
               
;********************************************************************

Proc           PutHex
               ;Args: AL=byte,DI=offset; assumes es=video memory

               push ax cx               ;Push these
               push ax                  ;Save AL
               mov cl,4                 ;Do high nibble
               shr al,cl                ;Put in low nibble
               call ConvShow            ;Convert and show
               pop ax                   ;Get back AL
               and al,0fh               ;Only low nibble
               call ConvShow            ;Convert and show
               pop cx ax                ;Pop saved registers
               ret                      ;Exit

ConvShow:      add     al,90h           ;Allison's algorithm
               daa
               adc     al,40h
               daa
               stosb                    ;Store and show
               inc di
               ret                      ;Return

EndP           PutHex

;********************************************************************

Proc           ErrorExit

NoCmdLine:     lea bp,[UsageStr]        ;No command line,
               mov cx,22                ;print usage message
               jmp ErrExit

NotFound:      lea bp,[ErrFind]         ;File not found
               mov cx,ERR_FIND_LEN
               jmp ErrExit

FileError:     lea bp,[ErrAccess]       ;File access error
               mov cx,ERR_ACCESS_LEN
               jmp ErrExit

ZeroErr:       lea bp,[ErrZero]         ;Zero-byte file
               mov cx,ERR_ZERO_LEN
               jmp ErrExit

NoMem:         lea bp,[ErrMem]          ;Out of memory
               mov cx,ERR_MEM_LEN

ErrExit:       mov ax,3                 ;Show a string,
               int 10h                  ;specified in BP, and
               mov ax,1301h             ;exit with an error.
               mov bx,0007h             ;BIOS show string
               xor dx,dx
               push ds
               pop es
               int 10h                  ;BIOS position cursor
               mov ah,2
               mov dx,0100h
               int 10h
               mov ah,4ch
               int 21h

EndP           ErrorExit

PC             = $
PC             = PC + 256
StackTop       = PC - 2                 ;Stack position
Buffer         = PC                     ;Data buffer

End Start
