除法的意外结果取决于我使用的是 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 行使用 EAX
或 AX
寄存器时得到错误的除法结果,但使用 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
如果您愿意,可以将 sub
和 add
合并为 lea
:
movzx eax, byte [esi]
lea edi, [edi + eax - 48]
好吧,我有一个有限的(仅输入一位数的求和数)求和程序,它借助将余数保存在堆栈中并在之后打印求和结果来打印求和结果。
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 行使用 EAX
或 AX
寄存器时得到错误的除法结果,但使用 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
如果您愿意,可以将 sub
和 add
合并为 lea
:
movzx eax, byte [esi]
lea edi, [edi + eax - 48]