除法的意外结果取决于我使用的是 EAX、AX 还是 AL

Unexpected results from division depending on whether I use EAX, AX, or AL

好吧,我有一个有限的(仅输入一位数的求和数)求和程序,它借助将余数保存在堆栈中并在之后打印求和结果来打印求和结果。

  1 global _start
  2 section .data
  3 NEW_LINE            db  10
  4 NEW_LINE_LENGTH     equ $-NEW_LINE
  5 EXIT_SUCCESS_CODE   equ 0
  6 STDIN               equ 0
  7 STDOUT              equ 1
  8 STDERR              equ 2
  9 SYSCALL_EXIT        equ 1
 10 SYSCALL_READ        equ 3
 11 SYSCALL_WRITE       equ 4
 12 
 13 section .bss
 14 sum                 resb 4
 15 remainder           resb 4
 16 buffer              resb 120
 17 
 18 section .text
 19 print_digit: 
 20     mov eax, SYSCALL_WRITE  ; 4
 21     mov ebx, STDOUT         ; 1
 22     int 80h
 23     ret 
 24 _start: 
 25     mov esi, buffer         ; buffer adress --> ESI
 26 again:
 27     ; input number:
 28     mov eax, SYSCALL_READ   ; 3
 29     mov ebx, STDIN          ; 0
 30     mov ecx, esi            ; buffer adress --> ecx
 31     mov edx, 1              ; length
 32     int 80h
 33     cmp byte [esi], 0       ; EOF ?
 34     je end_input
 35     inc esi                 ; next byte of buffer memory
 36     jmp again
 37 end_input:
 38     mov esi, buffer         ; buffer adress --> ESI
 39     xor edi, edi
 40     ; EDI will hold sum:
 41 loop:
 42     cmp byte [esi], 0       ; EOF ?
 43     je div_preparation      
 44     cmp byte [esi], 10      ; line feed ?
 45     je .next_digit
 46     sub byte [esi], 48      ; get value of digit
 47     add edi, [esi]          ; add to summ
 48     add byte [esi], 48
 49 .next_digit:
 50     inc esi                 ; next symbol(?) in buffer memory
 51     jmp loop
 52     ; EDI holds summ now
 53 div_preparation:
 54     mov [sum], edi
 55     xor edx, edx
 56     xor eax, eax
 57     xor ebp, ebp
 58     mov al, [sum]           ; ??????????????????
 59     mov esi, 10
 60 push_remainder:
 61     div esi
 62     push edx                ; push remainder
 63     xor edx, edx            
 64     inc ebp                 ; remainders counter
 65     test eax, eax           ; if the quotient equal to 0 (cmp eax, 0)
 66     jne push_remainder
 67 pop_remainder:
 68     xor eax, eax
 69     test ebp, ebp           ; if remainders counter equals to 0
 70     je quit
 71     pop eax
 72     mov [remainder], eax
 73     add byte [remainder], 48; get digit
 74     mov ecx, remainder
 75     mov edx, 4
 76     call print_digit
 77     dec ebp
 78     jmp pop_remainder
 79 quit:

为什么在第 58 行使用 EAXAX 寄存器时得到错误的除法结果,但使用 AL 结果是正确的?

这部分代码:

 46     sub byte [esi], 48      ; get value of digit
 47     add edi, [esi]          ; add to summ

看起来本意是要在总和上加上一个数字,但实际情况并非如此。 add edi, [esi] 隐式地与 add edi, dword [esi] 相同,因此实际上以一种有趣的方式将 4 个字符添加到总和中,尽管只有底部的字符是有意的,并且其中只有一个字符是根据 ASCII 调整的char 到它的数字等价物(顺便说一下,我不太明白为什么你把它们改回 ASCII 字符,但原则上应该没问题,只是多余的)。

只使用结果的低字节是有效的,因为低字节永远不会被“污染”,无关的字符被添加到 eax 的高字节中。

如何解决:确保一次只添加一个字符。例如

movzx eax, byte [esi]
sub eax, 48
add edi, eax

如果您愿意,可以将 subadd 合并为 lea:

movzx eax, byte [esi]
lea edi, [edi + eax - 48]