Well, I have a limited (input just one-digit summands) summation program that prints the result of summation with the help of holding remainders in the stack and printing them after that.
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:
Why do I get wrong division results while using EAX
or AX
registers on line 58, but with AL
results are correct?
In this part of the code:
46 sub byte [esi], 48 ; get value of digit
47 add edi, [esi] ; add to summ
It looks like the intent is to add a single digit to the sum, but that is not what actually happens. add edi, [esi]
is implicitly the same thing as add edi, dword [esi]
, so really 4 characters are being added to the sum in an interesting way, though only the bottom one is intended, and only one of them is adjusted from an ASCII char to its numeric equivalent (by the way I don't quite understand why you change them back to ASCII characters, but that should be fine in principle, just redundant).
Using only the low byte of the result works, because the low byte is never "polluted" that way, the extraneous characters are added into the high bytes of eax
.
How to fix: make sure you add only one char at the time. For example
movzx eax, byte [esi]
sub eax, 48
add edi, eax
If you prefer, you can merge the sub
and add
into lea
:
movzx eax, byte [esi]
lea edi, [edi + eax - 48]