我正在阅读杰夫·丹特曼(Jeff Duntemann)令人惊叹的装配书,我几天坚持使用一段代码。
我的想法是使用内存引用“查找”Digits表中的一个字符:“任何人都可以剖析下面的代码并用愚蠢的细节解释它吗?
mov al,byte [Digits+eax] ; Look up the char equivalent of nibble
Digits: db "0123456789ABCDEF"
请参阅以下完整源代码:
SECTION .bss ; Section containing uninitialized data
BUFFLEN equ 16 ; We read the file 16 bytes at a time
Buff: resb BUFFLEN ; Text buffer itself
SECTION .data ; Section containing initialised data
HexStr: db " 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00",10
HEXLEN equ $-HexStr
Digits: db "0123456789ABCDEF"
SECTION .text ; Section containing code
global _start ; Linker needs this to find the entry point!
_start:
nop ; This no-op keeps gdb happy...
; Read a buffer full of text from stdin:
Read:
mov eax,3 ; Specify sys_read call
mov ebx,0 ; Specify File Descriptor 0: Standard Input
mov ecx,Buff ; Pass offset of the buffer to read to
mov edx,BUFFLEN ; Pass number of bytes to read at one pass
int 80h ; Call sys_read to fill the buffer
mov ebp,eax ; Save # of bytes read from file for later
cmp eax,0 ; If eax=0, sys_read reached EOF on stdin
je Done ; Jump If Equal (to 0, from compare)
; Set up the registers for the process buffer step:
mov esi,Buff ; Place address of file buffer into esi
mov edi,HexStr ; Place address of line string into edi
xor ecx,ecx ; Clear line string pointer to 0
; Go through the buffer and convert binary values to hex digits:
Scan:
xor eax,eax ; Clear eax to 0
; Here we calculate the offset into the line string, which is ecx X 3
mov edx,ecx ; Copy the pointer into line string into edx
; shl edx,1 ; Multiply pointer by 2 using left shift
; add edx,ecx ; Complete the multiplication X3
lea edx,[edx*2+edx]
; Get a character from the buffer and put it in both eax and ebx:
mov al,byte [esi+ecx] ; Put a byte from the input buffer into al
mov ebx,eax ; Duplicate the byte in bl for second nybble
; Look up low nybble character and insert it into the string:
and al,0Fh ; Mask out all but the low nybble
mov al,byte [Digits+eax] ; Look up the char equivalent of nybble
mov byte [HexStr+edx+2],al ; Write the char equivalent to line string
; Look up high nybble character and insert it into the string:
shr bl,4 ; Shift high 4 bits of char into low 4 bits
mov bl,byte [Digits+ebx] ; Look up char equivalent of nybble
mov byte [HexStr+edx+1],bl ; Write the char equivalent to line string
; Bump the buffer pointer to the next character and see if we're done:
inc ecx ; Increment line string pointer
cmp ecx,ebp ; Compare to the number of characters in the buffer
jna Scan ; Loop back if ecx is <= number of chars in buffer
; Write the line of hexadecimal values to stdout:
mov eax,4 ; Specify sys_write call
mov ebx,1 ; Specify File Descriptor 1: Standard output
mov ecx,HexStr ; Pass offset of line string
mov edx,HEXLEN ; Pass size of the line string
int 80h ; Make kernel call to display line string
jmp Read ; Loop back and load file buffer again
; All done! Let's end this party:
Done:
mov eax,1 ; Code for Exit Syscall
mov ebx,0 ; Return a code of zero
int 80H ; Make kernel call
答案 0 :(得分:8)
它并不多。 Digits
是一个包含16个字符的数组,每个字符代表一个十六进制数字。在执行mov al,byte [Digits+eax]
时,eax
包含0..15范围内的值(这由xor eax,eax
和and al,0Fh
确保),因此它会读取Digits
中相应索引处的字符。
它与你在C中写的一样基本相同:
char Digits[] = "0123456789ABCDEF";
al = Digits[eax];
答案 1 :(得分:4)
摘要:此代码将最多15个字符的字符串(无溢出,最后一个char始终为0x0A
)转换为其ASCII值的十六进制表示。
输入字符串的位置保存在ECX
中,基数为ESI
mov esi,Buff ; Place address of file buffer into esi --- base
mov edi,HexStr ; Place address of line string into edi
xor ecx,ecx ; Clear line string pointer to 0 --- pos = 0
此位置乘以
三lea edx,[edx*2+edx]
使用base&#39; HexStr&#39;在EDI
中获取目标索引。然后使用
EAX
(并复制到EBX
)
xor eax,eax ; Clear eax to 0
; Get a character from the buffer and put it in both eax and ebx:
mov al,byte [esi+ecx] ; Put a byte from the input buffer into al
mov ebx,eax ; Duplicate the byte in bl for second nybble
之后,最低的四位被隔离并根据Digits
(查找)表按索引替换,以获得带有
; Look up low nybble character and insert it into the string:
and al,0Fh ; Mask out all but the low nybble
mov al,byte [Digits+eax] ; Look up the char equivalent of nybble
现在AL
包含第一个输入字节下半部分的ASCII值。现在将BL
的高四位向右移动四位以获得更高的半字节(&#39; nybble&#39;)并使用EBX
重复上一步。
此角色的十六进制表示已完成。它用这两行写入结果缓冲区
mov byte [HexStr+edx+2],al ; Write the char equivalent to line string
mov byte [HexStr+edx+1],bl ; Write the char equivalent to line string
EDX+2
分别表示两个char十六进制值ASCII表示的最右边/低半字节和EDX+1
高半字节。 EDX+0
是十六进制表示(跳过)之间的空格字符。
现在增加ECX
中的输入索引并检查它是否是最后一个字符。
; Bump the buffer pointer to the next character and see if we're done:
inc ecx ; Increment line string pointer
cmp ecx,ebp ; Compare to the number of characters in the buffer
jna Scan ; Loop back if ecx is <= number of chars in buffer
如果按0x0A
键完成输入,则最后一个字符始终为RETURN
,因为sys_read
在结果中包含UNIX换行符。使用sys_read
完成CTRL-D
输入可以避免这种情况(感谢@Peter Cordes)。