1

I write a program that does division and mod operations with a long int and an int array. When I want to write my division operation's result but I can't. I can't figure out the problem, but I think I used the register assignments process from the data section wrong. Please help me. I think I call array and int array wrong from the data section or I have problems with registers using. This is my code:

         global    _start

          section   .text
_start:   
          
          mov r9, number        ; rdi stores adress of number array
          mov r12,  0             ; get number of k for determine index of number array
     
          
     L3:                       ;  function begin
     
          mov r15,  0                    ; get number of i because using i of loop register
          mov r14, [digits]          ; get number of digits
          mov r13, [r9 + r12*4]      ; get array number for using array adress. We use 4*k because we take k. index of array     
          
      
          syscall
          cmp r13,  0          ; determine number is negative or positive
          jl  L2               ; if it is negative we goto else part for print minus
          
          ; print blank         otherwise we print blank 
          mov rax, 1
          mov rdi, 1
          mov rsi, blank
          mov rdx, 1
          syscall
          
          jmp L1               ; after printing, jump L1 statement
  
     L2:                       ; if number is negative continiuos here so Else part  
          ; print minus
          mov rax, 1
          mov rdi, 1
          mov rsi, minus
          mov rdx, 1
          syscall   
          
          neg r13              ; convert the negative number to positive number      
      
     L1:                        ; into the loop     
           
          ; division operation  digit = (number/digits)
          mov rax, r13         ; dividend
          cqo
          mov rbx, r14         ; divisor
          idiv rbx              ; after the div operation rbx store division
          mov r10, rax         ; result is egual digit (r10)
         
          
          ; print digit
          add rsp , 8 
          add r10, 48  
          mov rax , 1 
          mov rdi, 1
          push r10
          mov rsi, rsp 
          mov rdx, 1
          syscall
      
          ; mod operation   number = number % digits
          mov rax, r13         ; dividend
          cqo
          mov rbx, r14         ; divisor
          xor rdx, rdx         ; rdx which store reminder be zero
          idiv rbx              ; after the div operation rbx store division    
          mov r13, rdx         ; rdx store reminder and reminder egual number
 
          ; division operation  digits = (digits/10)
          mov rdx, 1
          mov rax, r14         ; dividend
          mov rbx, 10          ; divisor
          div rbx              ; after the div operation rbx store division
          mov r14, rbx         ; result is egual digit (r14)   
   
          inc r15              ; increment the loop register
        
          cmp r15, 9           ; compare loop register with 9 so loop work 8 times
          jl  L1               ; if loop register less than 9 return loop
          
          ; print new line  
          mov rax, 1
          mov rdi, 1
          mov rsi, newline
          mov rdx, 1
          syscall  
          
        
          inc r12            ; increment k by 1  
          

          cmp r12, 8         ; compare k with numberCount
          jl L3              ; if k less than numberCount, goto L3 that is begining write9digits function 
         
         
     Exit:                                  ; if array index(k) greater than numberCount work Exit
          mov       rax, 60                 ; system call for exit
          xor       rdi, rdi                ; exit code 0
          syscall                           ; invoke operating system to exit

          section   .data
newline      db       10      ; note the newline at the end
blank        db       ' '
minus        db       '-'
i            dd       0      
k            dd       0      
digits       dd       100000000
number       dd       321762410, 9, 2943018, 0, 19372039, 18, -76241, -208424 

this code will print this

  00000000

  0�0000000

  0�0000000

  0�0000000

  000000000

-0�0000000

-0�0000000

  0_0000000

but Acorrding to my c code result must be this:

 321762410
 000000009
 002943018
 000000000
 019371039
 000000018
-000076241
-000208424

I tried to use qword to convert take integer but nothing changed. After I changed the integers' format to hexadecimal format, nothing changed. Finally, I tried to change [digits] and [r9 + r12*4].

          mov r14, [digits]          ; get number of digits
          mov r13, [r9 + r12*4]      ; get array number for using array adress. We use 4*k 

if I changed the [digits] as 5 and [r9 + r12*4] as 21, I take this output:

          mov r14, 5       ; get number of digits
          mov r13, 21      ; get array number for using array adress. We use 4*k 
  400000000

  400000000

  400000000

  400000000

  400000000

  400000000

  400000000

  400000000
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • 1
    If you want to know from an expert about how to do division of long int by int or vice versa, try a compiler: something like `int f(long int a, int b) { return a/b; }`. Use godbolt.org; play with the various types as needed. – Erik Eidt Apr 15 '23 at 21:28
  • 2
    `mov r13, [r9 + r12*4]`: your `number` is `dd` dwords but you load into `r13` which is a qword. For starters use `movsx` there. What is the random `syscall` doing in the middle of the code? If you instead want long ints (64 bit) as in the question title then use `dq` to define the numbers and also scale by 8 not 4. – Jester Apr 15 '23 at 21:33
  • As I've linked multiple times on your previous questions, [How do I print an integer in Assembly Level Programming without printf from the c library? (itoa, integer to decimal ASCII string)](https://stackoverflow.com/a/46301894) shows working code for int->string conversion. For signed integers, the only sensible way I know of is to remember the sign and start with `unsigned tmp = abs(x);` (and do unsigned division). Signed division with negative numbers will give you negative remainders, so `digit + '0'` doesn't work to get a decimal digit. – Peter Cordes Apr 16 '23 at 00:39
  • @PeterCordes well that seems to be in the code already, see the `neg r13` just before `L1`. – Jester Apr 16 '23 at 00:47
  • @Jester: Oh, yes. It's still doing signed division so that limits it to a max magnitude of LONG_MAX instead of ULONG_MAX, but the inputs are much smaller than that so that isn't the bug that's giving wrong digits. Single-step debugging should make it fairly clear what's going on, after fixing the `dd` vs. `dq` issue (and the scale factor to match), or the operand-size of the load. That was explained in comments on a previous question, but is still not fixed. – Peter Cordes Apr 16 '23 at 01:02
  • @PeterCordes, I changed dd to dq and I deleted the syscall that was empty. Also, I changed cqo to xor rdx, rdx. Finally, I deleted add rsp, 8 where on the bottom of the printdigit. Now, my output is: 3!0000000 000000000 0�0000000 000000000 0c0000000 010000000 -0�0000000 -0�0000000 I tried to unsigned division and I investigate "How do I print an integer in Assembly...", but I can't understand and I can't fix my code. What do I need to do? Could you help me, please? I am sorry, I am new to this job. – Mustafa Yilmaz Apr 16 '23 at 09:19
  • Did you also fix `mov r13, [r9 + r12*4]` to use `*8` to match the 8-byte element size you're using now (`long` instead of `int`)? But that would only affect loading the number; you definitely wouldn't be getting all zeros in most of the digits, or any non-decimal digits, if you didn't have other bugs. Are you using GDB to single-step your code and look at register values? If not, do that, it'll save you lots of time in the long run. See the bottom of https://stackoverflow.com/tags/x86/info for asm GDB tips. – Peter Cordes Apr 16 '23 at 09:26
  • @PeterCordes, yes I fixed mov r13, [r9 + r12*4] to use *8 but it still doesn't work. I don't use GDB, but I will use it on your advice. Sir, I can compile my code without bugs but I still don't take the right output. What do you think may be the problem – Mustafa Yilmaz Apr 16 '23 at 10:26
  • Not all bugs are assemble-time errors. With assembly language especially, it's *very* easy to make a total mess of things at run-time. The assembler only checks that each individual instruction is valid. The whole concept of a "debugger" as a program to single-step another program and examine its state is based on the understanding that bugs are a run-time phenomenon. If only things were so simple that compiling or assembling successfully meant a program didn't have bugs... But that's not the case. Your program doesn't do what you want, so it has at least one bug remaining. – Peter Cordes Apr 16 '23 at 10:32
  • @PeterCordes Sir, I thank your help. Actually, I learn new things thanks to you. Maybe this project fails but for other projects, I remember your advice. – Mustafa Yilmaz Apr 16 '23 at 17:37

1 Answers1

1

Problem revealing review

mov r9, number        ; rdi stores adress of number array

R9 is not the same as RDI. Change the comment. And isn't R9 call-clobbered? (See my improved code below that does not use R9)

mov r14, [digits]     ; get number of digits

What you load here is the initial value (100,000,000) for the divisor. And digits was defined a dword, therefore loading this value requires writing mov r14d, [digits] or movzx r14, dword [digits].

mov r13, [r9 + r12*4] ; get array number for using array adress. We use 4*k because we take k. index of array

The array elements are dwords and so the *4 scale factor is fine, but R13 is a qword register and so you should load the value with sign extension:

movsx r13, dword [r9 + r12 * 4]

Alternatively, you could define your array elements as qwords and then write:

mov   r13, [r9 + r12 * 8]
syscall

You have a redundant SYSCALL instruction. To be removed.

The code that deals with negative inputs would look better if written as:

    mov  rsi, blank
    test r13, r13      ; determine number is negative or positive
    jns  IsPos         ; if it is positive we print BLANK
    mov  rsi, minus    ; else we print MINUS
    neg  r13           ; convert the negative number to positive number      
IsPos:          
    mov rax, 1
    mov rdi, 1
    mov rdx, 1
    syscall
L1:
mov  rbx, r14         ; divisor
idiv rbx              ; after the div operation rbx store division

There's no gain in first moving the divisor from R14 into RBX. You can simply write: idiv r14.

; print digit
add rsp , 8 

In print digit, the add rsp, 8 operation exists to remove the push r10 value off the stack. Obviously, you can only do this after the push got executed.

; print digit
add  r10, 48  
mov  rax, 1 
mov  rdi, 1
push r10         ; (1)
mov  rsi, rsp 
mov  rdx, 1
syscall
add  rsp, 8      ; (1)
; mod operation   number = number % digits
mov rax, r13         ; dividend
cqo
mov rbx, r14         ; divisor
xor rdx, rdx         ; rdx which store reminder be zero
idiv rbx             ; after the div operation rbx store division    

In the mod operation, xor rdx, rdx destroys the effort from executing cqo which is normal prior to using idiv. You can use xor rdx, rdx but in conjunction with div (the unsigned division).

; division operation  digits = (digits/10)
mov rdx, 1
mov rax, r14         ; dividend
mov rbx, 10          ; divisor
div rbx              ; after the div operation rbx store division
mov r14, rbx         ; result is egual digit (r14)   

In the part that reduces the divisor by a factor of 10, you have introduced the error of setting RDX equal to 1, where prior to using div it should have been zeroed. Also, the quotient from the division is in RAX so move that to R14.

Improved code

The code is not optimal. Number conversion using a shrinking divisor is costlier than using a fixed divisor of 10 and storing backwards.

         global    _start

         section   .text
_start:   
          
    xor  r12d, r12d    ; get number of k for determine index of number array
L3:                    ;  function begin
     
    xor  r15d, r15d    ; get number of i
    mov  r14, [divisor]          ; get initial DIVISOR
    mov  r13, [number + r12 * 8] ; get array number for using array address. We use 8*k because we take k. index of array     

    mov  rsi, blank
    test r13, r13      ; determine number is negative or positive
    jns  IsPos         ; if it is positive we print BLANK
    mov  rsi, minus    ; else we print MINUS
    neg  r13           ; convert the negative number to positive number      
IsPos:          
    mov rax, 1
    mov rdi, 1
    mov rdx, 1
    syscall
L1:
    ; division operation  digit = (number/digits)
    mov rax, r13         ; dividend
    cqo
    idiv r14
    mov  r10, rax        ; result is equal digit (r10)

    ; print digit
    add  r10, 48  
    mov  rax, 1 
    mov  rdi, 1
    push r10             ; (1)
    mov  rsi, rsp 
    mov  rdx, 1
    syscall
    add  rsp, 8          ; (1)
    
    ; mod operation   number = number % digits
    mov  rax, r13        ; dividend
    cqo
    idiv r14
    mov  r13, rdx        ; rdx store remainder and remainder equal number
 
    ; division operation  digits = (digits/10)
    mov  rax, r14        ; dividend
    xor  edx, edx        ; Equivalent and better than `mov rdx, 0`
    mov  rbx, 10         ; divisor
    div  rbx
    mov  r14, rax
    
    inc  r15             ; increment the loop register
    cmp  r15, 9          ; compare loop register with 9 so loop work 8 times
    jb   L1              ; if loop register less than 9 return loop
          
    ; print new line  
    mov  rax, 1
    mov  rdi, 1
    mov  rsi, newline
    mov  rdx, 1
    syscall  
          
    inc  r12            ; increment k by 1  
    cmp  r12, 8         ; compare k with numberCount
    jb   L3             ; if k less than numberCount, goto L3 that is beginning write9digits function 
         
         
Exit:                   ; if array index(k) greater than numberCount work Exit
    mov  rax, 60        ; system call for exit
    xor  rdi, rdi       ; exit code 0
    syscall             ; invoke operating system to exit

          section   .data
newline      db       10      ; note the newline at the end
blank        db       ' '
minus        db       '-'
i            dq       0      
k            dq       0      
divisor      dq       100000000
number       dq       321762410, 9, 2943018, 0, 19372039, 18, -76241, -208424 
Sep Roland
  • 33,889
  • 7
  • 43
  • 76