# Printing a decimal number as a string

I've written a short script to print a number to a string. This actually was very difficult for me to do -- I'm not sure if this is actually a tricky task or just because I'm so new to asm I was having a hard time. Here it is:

SYS_EXIT    = 60
SYS_WRITE   = 1
SYS_STDOUT  = 1

.section .rodata
number: .long   774728

.text
.globl _start
_start:

# set up stack, align on 16 for syscalls
push %rbp
mov %rsp, %rbp
push number
sub $16, %rsp # r12 will store the size of the string to print xor %r12d, %r12d # on the stack we will store: # rbp-16 (properly sorted number to print) # rbp-8 (number that remains) # rbp-4 (current right-most digit, ie,remainder) loop: # if the number is zero, jump to print it cmpl$0, -8(%rbp)
je print
inc %r12d # increment the size of the string to print

# Divide by ten
# - %rdx will give us the remainder (right-most digit)
# - %rax will give us the new number (for our next iteration of the loop
xor %edx,       %edx
movl -8(%rbp),  %eax
mov $10, %ebx div %ebx movl %eax, -8(%rbp) # (a) add 48, because ASCII is number + 48, for example ord('7') ==> 55 addl$48,   %edx
# (b) move the asci number to rbp-8-len (to print in reverse)
# store offset (8+len) in %r13
mov $-8, %r13 sub %r12, %r13 movb %dl, (%rbp, %r13) jmp loop print: # print(%edi:stdout(int), %esi:mem_of_string(* void), %edx:len_of_string(int)) # (b) How to print the digit to asci (probably fixed offset for 0-9) # Our output has length %r12 and starts at rbp-8-len # Which, conveniently enough, is (%rbp, %r13)! mov$SYS_STDOUT,    %edi
lea (%rbp, %r13),   %rsi    # remember, this is an 8-byte MEMORY ADDRESS (pointer), not a number
mov %r12d,          %edx
mov $SYS_WRITE, %eax syscall exit: add$8, %rsp    # <-- can also do an empty pop, such as pop %rcx
mov             %rbp, %rsp
pop             %rbp
mov $0, %rdi mov$SYS_EXIT,  %eax
syscall


And running it:

13_strings$as file.s -o file.o && ld file.o -o file && ./file # 774728  Thank you for taking the time to review. Any feedback would be greatly appreciated! • There's no need to use any call-preserved registers for this (R12..R15, or RBX or RBP). Printing an integer as a string with AT&T syntax, with Linux system calls instead of printf / How do I print an integer in Assembly Level Programming without printf from the c library? are my AT&T and NASM examples of doing this nicely. – Peter Cordes Sep 19 at 5:29 • Did you copy that push number from What to do with an empty pop?? Like I commented there, that's a qword (8-byte) load from a 4-byte .long. The high bytes could hold random garbage depending what gets linked next to .rodata. (However, you later only look at the low 4 bytes). Also, IDK why you're using stack space at all; x86-64 has lots of registers. – Peter Cordes Sep 19 at 5:37 • @PeterCordes thanks for your tips and feedback. How were you able to use/import the mov$__NR_write, %eax ? – samuelbrody1249 Sep 19 at 5:38
• From #include <asm/unistd_64.h>, and assemble with gcc foo.S so it preprocesses your file with CPP before assembling. – Peter Cordes Sep 19 at 5:39
• @PeterCordes registers: I think when I planned writing it I was going to call functions so I didn't want to use any call-clobbered registers, but then I did it all in _start. Oh I see about the high byte, it could hold an invalid/garbage value so needs to be accounted for. – samuelbrody1249 Sep 19 at 5:42