0

I am working on 64-bit assembly code (yasm), trying to output arbitrarily large decimal values so that they read correctly, and not as weirdness, or as a single digit. I had a look around but can't seem to find any suitable solutions online. My code currently outputs a 2-value decimal figure (output quotient, output remainder when divided by 10). For larger values I was thinking along the lines of a do-while loop construction that would repeatedly divide by 10 and output the remainder while the remainder is not zero. Problem with this method is that it would output the result in reverse, adding additional complexity to the code to try and reverse this. Does anyone know of a solution that would work? I had a look at aaa, aam etc but I don't entirely get how they work and suspect these are available to 32-bit operations only.

The code structure is as follows:

section .data   
  nl db "",2,0
  nlf db "",10,0
  input db '',2,0  
  fact dq 1
  y db 10
  store dq 0
  rem dq 0
  quot dq 0
  check dq 0

section .text
  global _start      

_start:         

 ; reading input value 
  mov rax, 0        ; system read
  mov rdi, 0        ; STD IN
  mov rsi, input    ; address first byte in output
  mov rdx, 1        ; load length into rdx
  syscall

  ; reading newline
  mov rax, 0        ; system read
  mov rdi, 0        ; STD IN
  mov rsi, nl       ; address first byte in output
  mov rdx, 1        ; load length into rdx
  syscall

  mov rbx,  [input]     ; for calculating factorial 
  sub rbx, '0'  
  call  calc_fact
  call de_ASCII_fy

  ;add   rax, 30h   

  mov rax, 1        
  mov rdi, 1        
  mov rsi, nlf      
  mov rdx, 1        
  syscall 

  ; exit
  xor rdi, rdi
  push 0x3c
  pop rax
  syscall

  calc_fact:
      cmp   bl, 1
      jg    do_calculation
      mov   rax, 1
      ret

  do_calculation:
      dec   bl
      call  calc_fact
      inc   bl
      mul   bl        
      ret

  de_ASCII_fy:
      mov  [fact], rax
      movzx rax, byte [fact]
      cmp rax, 0
      je decimal_loop
      movzx rbx, byte [y]
      xor rdx, rdx
      div rbx
      xor rcx, rcx
      mov rcx, rdx ; store remainder
      add rax, '0'
      add rdx, '0'
      mov [rem], rdx
      mov [quot], rax
      cmp rcx, 0
      jnz full_print
            mov rax, 1      ; system write
            mov rdi, 1      
            mov rsi, rem    
            mov rdx, 1
            syscall
            ret
            full_print:
            mov rax, 1      ; system write
            mov rdi, 1      
            mov rsi, quot   
            mov rdx, 1      
            syscall
            mov rax, 1      ; system write
            mov rdi, 1      
            mov rsi, rem    
            mov rdx, 1
            syscall
            jmp endif
      endif:ret
      decimal_loop:
      ret

I am calculating a factorial value, trying to display the output for 4! as 24 and 5! as 120. Now, at the moment I can only get to show two decimal values (for some reason the first part of the full_print condition gets skipped, so 3! is printed as 06 instead of 6), but 24 gets printed correctly. I have been racking my brain for a simple way to print out a 3-digit decimal value, but it starts to get very messy with conditionals.

cadebe
  • 651
  • 1
  • 12
  • 35
  • 2
    The first method is the most common. You can `push` the remainders onto the stack in a first loop and `pop` and print/store them in a second loop. Because the stack is organized as LIFO-Memory, you get easily the digits in reverse order. – rkhb Sep 13 '14 at 19:43
  • Can you show us some code ? – User.1 Sep 14 '14 at 08:30

1 Answers1

2

Take a look at this example:

global _start

section .bss
    decimal resb 32

section .data

    number dq 10000000000000000000
    lf db 10

section .text

_start:
    mov rdi, decimal
    mov rsi, [number]
    call IntegerToDecimal

    mov   eax, 1        ; sys_write
    mov   edi, 1        ; STDOUT
    mov   rsi, decimal  ; String address
    mov   edx, 32       ; Max. string length
    syscall

    mov   eax, 1        ; sys_write
    mov   edi, 1        ; STDOUT
    mov   rsi, lf       ; Line Feed address
    mov   edx, 1        ; Max. string length
    syscall

    mov eax, 60         ; sys_exit
    xor edi, edi        ; return 0 (success)
    syscall

IntegerToDecimal:
    mov rax, rsi
    mov ebx, 10         ; Divisor
    xor ecx, ecx        ; RCX=0 (Anzahl der Ziffern)
  .Loop_1:
    xor edx, edx
    div rbx             ; RDX:RAX / RBX = RAX Remainder RDX
    push dx             ; LIFO
    add cl, 1
    or  rax, rax        ; RAX == 0?
    jnz .Loop_1         ; no: once more
  .Loop_2:
    pop ax              ; Get back pushed digits
    or al, 00110000b    ; Convert to ASCII
    mov [rdi], al       ; Store character
    add rdi, 1          ; Increment target address
    loop .Loop_2        ; Until there are no digits left
    mov byte [rdi], 0   ; ASCIIZ-null-terminator

    ret

BTW: You had got the example yesterday, if I had known the OS (Windows or Linux) and assembly style (Nasm or Gas). ;-)

rkhb
  • 14,159
  • 7
  • 32
  • 60
  • Thank you so much. I got my output to work now, but I can see there are some overflow issues I need to address in my original code. I am fairly new to assembly code and still fumbling in the dark as to translating the logic of structures and techniques, that are so simple in C++ and Java, into assembly. It really helps to be able to look at how other people code. – cadebe Sep 14 '14 at 11:01