This is an x86-64 Linux program to print a non-negative decimal integer. I would appreciate any simple optimizations for size and readability. I am aware that division by a constant is usually done by multiplication by the magic constant reciprocal, but I used div
here for simplicity.
; nasm -f elf64 print_dec.asm && ld print_dec.o -o print_dec
global _start
section .text
; print non-negative decimal integer in rdi
print_dec:
push rbp ; callee-saved
mov rbp, rsp ; save sp
mov rcx, 10 ; divisor base
mov rax, rdi ; dividend from arg0
L1:
xor rdx, rdx ; zero upper dividend
div rcx ; unsigned divide rdx:rax by rcx
; rax := quotient, rdx := remainder
add rdx, '0' ; convert digit to ASCII
push rdx ; push remainder digit
cmp rax, 0
jne L1 ; do while (rax != 0)
L2:
mov rax, 1 ; call number for write
mov rdi, 1 ; write to stdout (fd=1)
mov rsi, rsp ; use char on stack
mov rdx, 1 ; write 1 char
syscall
add rsp, 8 ; "pop" stack
cmp rbp, rsp ; do while (stack still has digits)
jne L2
pop rbp
ret
_start:
mov rdi, 1234 ; int to print
call print_dec
mov eax, 60 ; exit call number
xor rdi, rdi ; exit code 0
syscall
Before I wrote the code, I wrote my simple stack-based algorithm (pretty much the only way I think you could write it) in C first and cheated a bit by looking at the godbolt output. But I don't know how to force the compiler to use push
and pop
as I did because it just does indirect addressing like mov QWORD PTR [rsp+8+rbx*8], rdx
. So the "stack" is implemented in C and not the x86 stack. If there is a way to, please let me know.
// print non-negative integer in decimal
#include <stdio.h>
typedef unsigned long long ull;
void print_dec(ull x)
{
ull stack[20];
ull sp = 0; // not actual x86 sp
ull bp = sp;
ull d;
do // run at least once to print 0
{
d = x % 10;
stack[sp++] = d + '0'; // push
x /= 10;
} while (x);
do
{
d = stack[--sp]; // pop
putc(d, stdout);
} while (sp != bp);
}
int main()
{
print_dec(1234);
}