Printing an Int (or Int to String)

assembly, int, nasm, string

Solution

One approach is to use recursion. In this case you divide the number by 10 (getting a quotient and a remainder) and then call yourself with the quotient as the number to display; and then display the digit corresponding to the remainder.

An example of this would be:

;Input
; eax = number to display

    section .data
const10:    dd 10
    section .text

printNumber:
    push eax
    push edx
    xor edx,edx          ;edx:eax = number
    div dword [const10]  ;eax = quotient, edx = remainder
    test eax,eax         ;Is quotient zero?
    je .l1               ; yes, don't display it
    call printNumber     ;Display the quotient
.l1:
    lea eax,[edx+'0']
    call printCharacter  ;Display the remainder
    pop edx
    pop eax
    ret

Another approach is to avoid recursion by changing the divisor. An example of this would be:

;Input
; eax = number to display

    section .data
divisorTable:
    dd 1000000000
    dd 100000000
    dd 10000000
    dd 1000000
    dd 100000
    dd 10000
    dd 1000
    dd 100
    dd 10
    dd 1
    dd 0
    section .text

printNumber:
    push eax
    push ebx
    push edx
    mov ebx,divisorTable
.nextDigit:
    xor edx,edx          ;edx:eax = number
    div dword [ebx]      ;eax = quotient, edx = remainder
    add eax,'0'
    call printCharacter  ;Display the quotient
    mov eax,edx          ;eax = remainder
    add ebx,4            ;ebx = address of next divisor
    cmp dword [ebx],0    ;Have all divisors been done?
    jne .nextDigit
    pop edx
    pop ebx
    pop eax
    ret

This example doesn't suppress leading zeros, but that would be easy to add.

Problem

I am looking for a way to print an integer in assembler (the compiler I am using is NASM on Linux), however, after doing some research, I have not been able to find a truly viable solution. I was able to find a description for a basic algorithm to serve this purpose, and based on that I developed this code: ``` global _start section .bss digit: resb 16 count: resb 16 i: resb 16 section .data section .text _start: mov dword[i], 108eh ; i = 4238 mov dword[count], 1 L01: mov eax, dword[i] cdq mov ecx, 0Ah div ecx mov dword[digit], edx add dword[digit], 30h ; add 48 to digit to make it an ASCII char call write_digit inc dword[count] mov eax, dword[i] cdq mov ecx, 0Ah div ecx mov dword[i], eax cmp dword[i], 0Ah jg L01 add dword[i], 48 ; add 48 to i to make it an ASCII char mov eax, 4 ; system call #4 = sys_write mov ebx, 1 ; file descriptor 1 = stdout mov ecx, i ; store *address* of i into ecx mov edx, 16 ; byte size of 16 int 80h jmp exit exit: mov eax, 01h ; exit() xor ebx, ebx ; errno int 80h write_digit: mov eax, 4 ; system call #4 = sys_write mov ebx, 1 ; file descriptor 1 = stdout mov ecx, digit ; store *address* of digit into ecx mov edx, 16 ; byte size of 16 int 80h ret ``` C# version of what I want to achieve (for clarity): ``` static string int2string(int i) { Stack<char> stack = new Stack<char>(); string s = ""; do { stack.Push((char)((i % 10) + 48)); i = i / 10; } while (i > 10); stack.Push((char)(i + 48)); foreach (char c in stack) { s += c; } return s; } ``` The issue is that it outputs the characters in reverse, so for `4238`, the output is `8324`. At first, I thought that I could use the x86 stack to solve this problem, push the digits in, and pop them out and print them at the end, however when I tried implementing that feature, it flopped and I could no longer get an output. As a result, I am a little bit perplexed about how I can implement a stack in to this algorithm in order to accomplish my goal, aka printing an integer. I would also be interested in a simpler/better solution if one is available (as it's one of my first assembler programs).

Original source

Related problems