initial import
[staff/goc4/2018bti7061.git] / asm-7-procedures / examples / hexdump2 / hexdump2.asm
1 ; Executable name : hexdump2
2 ; Version : 1.0
3 ; Created date : 4/15/2009
4 ; Last update : 4/20/2009
5 ; Author : Jeff Duntemann
6 ; Modified by : Emmanuel Benoist
7 ; Date modification: 11/14/2016
8 ; Description : A simple hex dump utility demonstrating the use of
9 ; assembly language procedures
10 ;
11 ; Modification : Adapatation for 64-bit architecture
12 ;
13 ; Build using these commands:
14 ; nasm -f elf64 -g -F stabs hexdump2.asm
15 ; ld -o hexdump2 hexdump2.o
16 ;
17
18 SECTION .bss ; Section containing uninitialized data
19
20 BUFFLEN EQU 10
21 Buff resb BUFFLEN
22
23 SECTION .data ; Section containing initialised data
24
25 ; Here we have two parts of a single useful data structure, implementing
26 ; the text line of a hex dump utility. The first part displays 16 bytes in
27 ; hex separated by spaces. Immediately following is a 16-character line
28 ; delimited by vertical bar characters. Because they are adjacent, the two
29 ; parts can be referenced separately or as a single contiguous unit.
30 ; Remember that if DumpLin is to be used separately, you must append an
31 ; EOL before sending it to the Linux console.
32
33 DumpLin: db " 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 "
34 DUMPLEN EQU $-DumpLin
35 ASCLin: db "|................|",10
36 ASCLEN EQU $-ASCLin
37 FULLLEN EQU $-DumpLin
38
39 ; The HexDigits table is used to convert numeric values to their hex
40 ; equivalents. Index by nybble without a scale: [HexDigits+eax]
41 HexDigits: db "0123456789ABCDEF"
42
43 ; This table is used for ASCII character translation, into the ASCII
44 ; portion of the hex dump line, via XLAT or ordinary memory lookup.
45 ; All printable characters "play through" as themselves. The high 128
46 ; characters are translated to ASCII period (2Eh). The non-printable
47 ; characters in the low 128 are also translated to ASCII period, as is
48 ; char 127.
49 DotXlat:
50 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
51 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
52 db 20h,21h,22h,23h,24h,25h,26h,27h,28h,29h,2Ah,2Bh,2Ch,2Dh,2Eh,2Fh
53 db 30h,31h,32h,33h,34h,35h,36h,37h,38h,39h,3Ah,3Bh,3Ch,3Dh,3Eh,3Fh
54 db 40h,41h,42h,43h,44h,45h,46h,47h,48h,49h,4Ah,4Bh,4Ch,4Dh,4Eh,4Fh
55 db 50h,51h,52h,53h,54h,55h,56h,57h,58h,59h,5Ah,5Bh,5Ch,5Dh,5Eh,5Fh
56 db 60h,61h,62h,63h,64h,65h,66h,67h,68h,69h,6Ah,6Bh,6Ch,6Dh,6Eh,6Fh
57 db 70h,71h,72h,73h,74h,75h,76h,77h,78h,79h,7Ah,7Bh,7Ch,7Dh,7Eh,2Eh
58 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
59 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
60 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
61 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
62 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
63 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
64 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
65 db 2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh,2Eh
66
67
68 SECTION .text ; Section containing code
69
70 ;-------------------------------------------------------------------------
71 ; ClearLine: Clear a hex dump line string to 16 0 values
72 ; UPDATED: 4/15/2009
73 ; IN: Nothing
74 ; RETURNS: Nothing
75 ; MODIFIES: Nothing
76 ; CALLS: DumpChar
77 ; DESCRIPTION: The hex dump line string is cleared to binary 0 by
78 ; calling DumpChar 16 times, passing it 0 each time.
79
80 ClearLine:
81 push rax ; Save all used registers
82 push rdx
83 mov edx,15 ; We're going to go 16 pokes, counting from 0
84 .poke: mov eax,0 ; Tell DumpChar to poke a '0'
85 call DumpChar ; Insert the '0' into the hex dump string
86 sub edx,1 ; DEC doesn't affect CF!
87 jae .poke ; Loop back if EDX >= 0
88 pop rdx ; Restore all used registers
89 pop rax ;
90 ret ; Go home
91
92
93 ;-------------------------------------------------------------------------
94 ; DumpChar: "Poke" a value into the hex dump line string.
95 ; UPDATED: 4/15/2009
96 ; IN: Pass the 8-bit value to be poked in EAX.
97 ; Pass the value's position in the line (0-15) in EDX
98 ; RETURNS: Nothing
99 ; MODIFIES: EAX, ASCLin, DumpLin
100 ; CALLS: Nothing
101 ; DESCRIPTION: The value passed in EAX will be put in both the hex dump
102 ; portion and in the ASCII portion, at the position passed
103 ; in EDX, represented by a space where it is not a
104 ; printable character.
105
106 DumpChar:
107 push rbx ; Save caller's RBX
108 push rdi ; Save caller's RDI
109 ; First we insert the input char into the ASCII portion of the dump line
110 mov bl,byte [DotXlat+eax] ; Translate nonprintables to '.'
111 mov byte [ASCLin+edx+1],bl ; Write to ASCII portion
112 ; Next we insert the hex equivalent of the input char in the hex portion
113 ; of the hex dump line:
114 mov ebx,eax ; Save a second copy of the input char
115 lea edi,[edx*2+edx] ; Calc offset into line string (ECX X 3)
116 ; Look up low nybble character and insert it into the string:
117 and eax,0000000Fh ; Mask out all but the low nybble
118 mov al,byte [HexDigits+eax] ; Look up the char equiv. of nybble
119 mov byte [DumpLin+edi+2],al ; Write the char equiv. to line string
120 ; Look up high nybble character and insert it into the string:
121 and ebx,000000F0h ; Mask out all the but second-lowest nybble
122 shr ebx,4 ; Shift high 4 bits of byte into low 4 bits
123 mov bl,byte [HexDigits+ebx] ; Look up char equiv. of nybble
124 mov byte [DumpLin+edi+1],bl ; Write the char equiv. to line string
125 ;Done! Let's go home:
126 pop rdi ; Restore caller's RDI
127 pop rbx ; Restore caller's RBX
128 ret ; Return to caller
129
130
131 ;-------------------------------------------------------------------------
132 ; PrintLine: Displays DumpLin to stdout
133 ; UPDATED: 4/15/2009
134 ; IN: Nothing
135 ; RETURNS: Nothing
136 ; MODIFIES: Nothing
137 ; CALLS: Kernel sys_write
138 ; DESCRIPTION: The hex dump line string DumpLin is displayed to stdout
139 ; using INT 80h sys_write. All GP registers are preserved.
140
141 PrintLine:
142 push rax ; Save all used registers
143 push rbx ; Save all used registers
144 push rcx ; Save all used registers
145 push rdx ; Save all used registers
146 mov eax,4 ; Specify sys_write call
147 mov ebx,1 ; Specify File Descriptor 1: Standard output
148 mov ecx,DumpLin ; Pass offset of line string
149 mov edx,FULLLEN ; Pass size of the line string
150 int 80h ; Make kernel call to display line string
151 pop rdx ; Restore all caller's registers
152 pop rcx ;
153 pop rbx ;
154 pop rax ;
155 ret ; Return to caller
156
157
158 ;-------------------------------------------------------------------------
159 ; LoadBuff: Fills a buffer with data from stdin via INT 80h sys_read
160 ; UPDATED: 4/15/2009
161 ; IN: Nothing
162 ; RETURNS: # of bytes read in EBP
163 ; MODIFIES: ECX, EBP, Buff
164 ; CALLS: Kernel sys_write
165 ; DESCRIPTION: Loads a buffer full of data (BUFFLEN bytes) from stdin
166 ; using INT 80h sys_read and places it in Buff. Buffer
167 ; offset counter ECX is zeroed, because we're starting in
168 ; on a new buffer full of data. Caller must test value in
169 ; EBP: If EBP contains zero on return, we hit EOF on stdin.
170 ; Less than 0 in EBP on return indicates some kind of error.
171
172 LoadBuff:
173 push rax ; Save caller's EAX
174 push rbx ; Save caller's EBX
175 push rdx ; Save caller's EDX
176 mov eax,3 ; Specify sys_read call
177 mov ebx,0 ; Specify File Descriptor 0: Standard Input
178 mov ecx,Buff ; Pass offset of the buffer to read to
179 mov edx,BUFFLEN ; Pass number of bytes to read at one pass
180 int 80h ; Call sys_read to fill the buffer
181 mov ebp,eax ; Save # of bytes read from file for later
182 xor ecx,ecx ; Clear buffer pointer ECX to 0
183 pop rdx ; Restore caller's EDX
184 pop rbx ; Restore caller's EBX
185 pop rax ; Restore caller's EAX
186 ret ; And return to caller
187
188
189 GLOBAL _start
190
191 ; ------------------------------------------------------------------------
192 ; MAIN PROGRAM BEGINS HERE
193 ;-------------------------------------------------------------------------
194 _start:
195 nop ; No-ops for GDB
196 nop
197
198 ; Whatever initialization needs doing before the loop scan starts is here:
199 xor esi,esi ; Clear total byte counter to 0
200 call LoadBuff ; Read first buffer of data from stdin
201 cmp ebp,0 ; If ebp=0, sys_read reached EOF on stdin
202 jbe Exit
203
204 ; Go through the buffer and convert binary byte values to hex digits:
205 Scan:
206 xor eax,eax ; Clear EAX to 0
207 mov al,byte[Buff+ecx] ; Get a byte from the buffer into AL
208 mov edx,esi ; Copy total counter into EDX
209 and edx,0000000Fh ; Mask out lowest 4 bits of char counter
210 call DumpChar ; Call the char poke procedure
211
212 ; Bump the buffer pointer to the next character and see if buffer's done:
213 inc esi ; Increment total chars processed counter
214 inc ecx ; Increment buffer pointer
215 cmp ecx,ebp ; Compare with # of chars in buffer
216 jb .modTest ; If we've processed all chars in buffer...
217 call LoadBuff ; ...go fill the buffer again
218 cmp ebp,0 ; If ebp=0, sys_read reached EOF on stdin
219 jbe Done ; If we got EOF, we're done
220
221 ; See if we're at the end of a block of 16 and need to display a line:
222 .modTest:
223 test esi,0000000Fh ; Test 4 lowest bits in counter for 0
224 jnz Scan ; If counter is *not* modulo 16, loop back
225 call PrintLine ; ...otherwise print the line
226 call ClearLine ; Clear hex dump line to 0's
227 jmp Scan ; Continue scanning the buffer
228
229 ; All done! Let's end this party:
230 Done:
231 call PrintLine ; Print the "leftovers" line
232 Exit: mov eax,1 ; Code for Exit Syscall
233 mov ebx,0 ; Return a code of zero
234 int 80H ; Make kernel call