如何理解NASM中的Lookup表?

时间:2016-04-29 10:30:26

标签: assembly nasm

我正在阅读杰夫·丹特曼(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

2 个答案:

答案 0 :(得分:8)

它并不多。 Digits是一个包含16个字符的数组,每个字符代表一个十六进制数字。在执行mov al,byte [Digits+eax]时,eax包含0..15范围内的值(这由xor eax,eaxand 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)。