;**********************************************************;
;* 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