It's a simple hexdump that prints to stdout. I wanted to handle correctly input coming from the user typing at the terminal, this made the logic a little more complicated than just exiting after reading less than requested. It doesn't use the stack or memory variables, except for the tables and buffers, so I employed every register freely.
I'm learning, so I would like for every possible improvement to be pointed out.
; Description : Prints stdin to stdout as hex and displays ASCII next to it.
; It handles correctly the cases in which a sys_read returns
; less than was requested but an EOF was not received, this
; allows the user to dump arbitrary text from stdin to a file
; in the following way: ./hexdump >> dump.txt
;
; Build using these commands:
; nasm -f elf hexdump.asm
; ld -o hexdump hexdump.o -m elf_i386 -s
;
; Usage:
; ./hexdump << input_file
section .data
STDIN: equ 0
STDOUT: equ 1
STDERR: equ 2
EXIT_SUCCESS: equ 0
SYS_READ: equ 3
SYS_WRITE: equ 4
SYS_EXIT: equ 1
NEW_LINE: equ 0ah
AsciiTable: db '................................ !"',"#$%&'()*+,-./0123456789:;"
db '<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz'
db '{|}~...........................................................'
db '...............................................................'
db '.......'
align 4
Template: db '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 '
DivPos: db '| '
AsciiPart: db '................',NEW_LINE
TEMPLATELENGTH: equ $-Template
align 2
HexTable: db '000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2'
db '02122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40'
db '4142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606'
db '162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f8081'
db '82838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a'
db '2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2'
db 'c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e'
db '3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff'
section .bss
BUFFERSIZE: equ 160
Buffer: resb BUFFERSIZE
section .text
global _start ; make it visible
_start:
xor esi,esi ; position in buffer
xor ebp,ebp ; buffer size
mov edi,Read ; address to jmp
mov esp,BUFFERSIZE ; how much to read
Fill: mov eax,SYS_READ
xor ebx,ebx ; STDIN
lea ecx,[Buffer+ebp] ; destination
mov edx,esp ; size
int 80h ; call
cmp eax,0 ; check return value
jg Ok ; read something
jl Err ; < 0 -> error
jmp Last ; 0 = EOF
Ok: add ebp,eax ; update size
Read: lea esp,[esi+16] ; where we'll be if we print
sub esp,ebp ; how much we have to read to reach 16
jg Fill ; esp > ebp
jnz NotZ ; we only reset when esp = ebp
mov edi,_start ; reset
NotZ: mov ecx,15 ; offset, not counter
xor eax,eax ; clear bits 8-31
xor edx,edx ; zero-out the upper half
.l0: mov al,[Buffer+esi+ecx] ; read byte from buffer
mov bx,[HexTable+eax*2] ; get corresponding symbols
mov [Template+ecx*2+ecx],bx ; write to template
mov dl,[AsciiTable+eax] ; get ASCII symbol
mov [AsciiPart+ecx],dl ; write to template
sub ecx,1 ; next iteration
jns .l0 ; ecx is an offset not a counter
; Print the Template to stdout
Out: mov eax,SYS_WRITE
mov ebx,STDOUT
mov ecx,Template
mov edx,TEMPLATELENGTH
int 80h
cmp eax,TEMPLATELENGTH
jne Err
add esi,16 ; move index
jmp edi ; go to Read or _start
Last: sub ebp,esi ; make it a count
jz Exit ; if 0, we are done
sub ebp,1 ; make it an offset
mov ecx,15 ; 16*4=64 (0 ... 15)
.l0: mov dword [Template+ecx*4],' ' ; fill buffer with spaces
sub ecx,1 ; next iteration
jns .l0 ; ecx is an offset
mov byte [DivPos],'|' ; it was replaced by a space
mov word [Template+64],' ' ; finish writing spaces
xor ebx,ebx ; clear
.l1: mov bl,[Buffer+esi+ebp] ; get byte
mov cx,[HexTable+ebx*2] ; get corresponding hex code
mov [Template+ebp*2+ebp],cx ; place hex code
mov bl,[AsciiTable+ebx] ; get ascii symbol
mov [AsciiPart+ebp],bl ; place it
sub ebp,1 ; dec doesn't affect CF
jns .l1
mov edi,End ; return address
jmp Out ; print last line
Err: mov ebx,-1 ; EXIT_FAILURE
jmp Exit
; Exit the program
End: xor ebx,ebx ; EXIT_SUCCESS
Exit: mov eax,SYS_EXIT
int 80h
echo "abc" > i1
./hexdump < ./i1
->03 31 13 05 | abc.
. Is it just me and the web online emulator I used, or the code has bugs? Anyway, thatAsciiTable
andHexTable
is sort of mesmerizing for me, I did plenty of LUTs in my own code before, but very rarely cases like "x == lut[x]" or "simple_transformation(x) == lut[x]" survived. \$\endgroup\$.l1:
, on the linemov cx,[HexTable+ebx] ; get corresponding hex code
,ebx
should beebx*2
since each code is 2 bytes. I edited the code to fix it. I'm using the tables because they made the code easier to write and I believe they are faster, I'll benchmark. \$\endgroup\$