How to print a number in assembly NASM?

43,053

Solution 1

If you're already on Linux, there's no need to do the conversion yourself. Just use printf instead:

;
; assemble and link with:
; nasm -f elf printf-test.asm && gcc -m32 -o printf-test printf-test.o
;
section .text
global main
extern printf

main:

  mov eax, 0xDEADBEEF
  push eax
  push message
  call printf
  add esp, 8
  ret

message db "Register = %08X", 10, 0

Note that printf uses the cdecl calling convention so we need to restore the stack pointer afterwards, i.e. add 4 bytes per parameter passed to the function.

Solution 2

You have to convert it in a string; if you're talking about hex numbers it's pretty easy. Any number can be represented this way:

0xa31f = 0xf * 16^0 + 0x1 * 16^1 + 3 * 16^2 + 0xa * 16^3

So when you have this number you have to split it like I've shown then convert every "section" to its ASCII equivalent.
Getting the four parts is easily done with some bit magic, in particular with a right shift to move the part we're interested in in the first four bits then AND the result with 0xf to isolate it from the rest. Here's what I mean (soppose we want to take the 3):

0xa31f -> shift right by 8 = 0x00a3 -> AND with 0xf = 0x0003

Now that we have a single number we have to convert it into its ASCII value. If the number is smaller or equal than 9 we can just add 0's ASCII value (0x30), if it's greater than 9 we have to use a's ASCII value (0x61).
Here it is, now we just have to code it:

    mov si, ???         ; si points to the target buffer
    mov ax, 0a31fh      ; ax contains the number we want to convert
    mov bx, ax          ; store a copy in bx
    xor dx, dx          ; dx will contain the result
    mov cx, 3           ; cx's our counter

convert_loop:
    mov ax, bx          ; load the number into ax
    and ax, 0fh         ; we want the first 4 bits
    cmp ax, 9h          ; check what we should add
    ja  greater_than_9
    add ax, 30h         ; 0x30 ('0')
    jmp converted

greater_than_9:
    add ax, 61h         ; or 0x61 ('a')

converted:
    xchg    al, ah      ; put a null terminator after it
    mov [si], ax        ; (will be overwritten unless this
    inc si              ; is the last one)

    shr bx, 4           ; get the next part
    dec cx              ; one less to do
    jnz convert_loop

    sub di, 4           ; di still points to the target buffer

PS: I know this is 16 bit code but I still use the old TASM :P

PPS: this is Intel syntax, converting to AT&T syntax isn't difficult though, look here.

Solution 3

Linux x86-64 with printf

main.asm

default rel            ; make [rel format] the default, you always want this.
extern printf, exit    ; NASM requires declarations of external symbols, unlike GAS
section .rodata
    format db "%#x", 10, 0   ; C 0-terminated string: "%#x\n" 
section .text
global main
main:
    sub   rsp, 8             ; re-align the stack to 16 before calling another function

    ; Call printf.
    mov   esi, 0x12345678    ; "%x" takes a 32-bit unsigned int
    lea   rdi, [rel format]
    xor   eax, eax           ; AL=0  no FP args in XMM regs
    call  printf

    ; Return from main.
    xor   eax, eax
    add   rsp, 8
    ret

GitHub upstream.

Then:

nasm -f elf64 -o main.o main.asm
gcc -no-pie -o main.out main.o
./main.out

Output:

0x12345678

Notes:

If you want hex without the C library: Printing Hexadecimal Digits with Assembly

Tested on Ubuntu 18.10, NASM 2.13.03.

Solution 4

It depends on the architecture/environment you are using.

For instance, if I want to display a number on linux, the ASM code will be different from the one I would use on windows.

Edit:

You can refer to THIS for an example of conversion.

Share:
43,053

Related videos on Youtube

AR89
Author by

AR89

Software Engineer with mobile professional experience. Constantly learning new languages and tools, passionate about solid code and challenging UI.

Updated on June 16, 2020

Comments

  • AR89
    AR89 almost 4 years

    Suppose that I have an integer number in a register, how can I print it? Can you show a simple example code?

    I already know how to print a string such as "hello, world".

    I'm developing on Linux.

  • AR89
    AR89 over 12 years
    A Linux example would be fine.
  • moongoal
    moongoal over 12 years
    @AR89 it's a bad job.. You have to convert the number to ASCII first. Take a look at the edited question.
  • AR89
    AR89 over 12 years
    Thanks, it seems to be the what I was looking for. Do you know if it works also on Mac os X?
  • Figen Güngör
    Figen Güngör over 11 years
    How to compile it on 64-bit?
  • Andrei Bârsan
    Andrei Bârsan over 11 years
    You don't need AT&T syntax to run this on linux.
  • BlackBear
    BlackBear over 11 years
    @AndreiBârsan: You're right, fixed that.. It's such an old answer :)
  • Andrei Bârsan
    Andrei Bârsan over 11 years
    IMHO, this answer is better since you don't need the C runtime (which a call to printf(...) requires.
  • BlackBear
    BlackBear over 11 years
    @AndreiBârsan yes, and it's kind of pointless using the C runtime in assembly
  • Peter Cordes
    Peter Cordes almost 7 years
    You can add '0' and store your digits in a buffer as you produce them. Use dec do move the pointer downwards. When you're done, you have a pointer to the last digit you stored, so you can pass that to sys_write() (along with the digit count). This is much more efficient than making a separate system call for every byte, and doesn't really take more code. It's easy to allocate a buffer long enough to hold the longest possible string of digits, and start at the end, because you know how many decimal digits 2^32 has.
  • Peter Cordes
    Peter Cordes almost 7 years
    related: I wrote an integer->string loop as part of this extended-precision Fibonacci code-golf answer. See the .toascii_digit: loop. Of course, that's optimized for size, so it uses a slow div instead of a multiply trick.
  • baz
    baz almost 7 years
    Thank you this is definitely preferable than calling sys_write for every digit:)
  • Peter Cordes
    Peter Cordes over 6 years
    I posted my int->string + sys_write code as a stand-alone function on another question, with comments.
  • Peter Cordes
    Peter Cordes over 5 years
    You're using space below ESP. That's only safe in cases like this where you know there are no signal handlers installed, and shouldn't be used in functions that could be called in other contexts. 32-bit Linux doesn't guarantee a red-zone. Also, use xor edx,edx / div or cdq / idiv so the zero or sign-extension of the dividend matches the signedness of the division. In this case you want xor/div so you always have a positive remainder. If you want to treat your input as signed, you'll want to test/js and print the unsigned absolute value (with a leading - if needed).
  • TigerTV.ru
    TigerTV.ru over 5 years
    @PeterCordes, Hi, Peter! You're right about safety. It's a partial solution and I didn't think about signed numbers.
  • Peter Cordes
    Peter Cordes over 5 years
    You should still change idiv to div so it works for the full range of unsigned numbers. Hmm, actually this might be safe anyway, because 2^32-1 / 10 doesn't overflow EAX. zero-extending into edx:eax gives you a signed non-negative dividend from 0..2^32-1.
  • TigerTV.ru
    TigerTV.ru over 5 years
    @PeterCordes, The idiv has been replaced. Also I added base for the number. What do you think about it? And else I reserved a buffer on stack for number string with size 32.
  • Peter Cordes
    Peter Cordes over 5 years
    add esp, 32 should be sub to reserve space. You're stepping on the caller's stack space. mov byte [ecx], 10 would be more efficient than setting a register first. Or even push 10 / mov ecx, esp / sub esp, 32. (For your current version, a large number with base=2 will use 32 digits, but you use up one of your 32 with a newline.)
  • TigerTV.ru
    TigerTV.ru over 5 years
    Sure, I'm wrong. :) It didn't even break stack in the context. Fixed.
  • Peter Cordes
    Peter Cordes about 5 years
    Please don't recommend mov for putting static addresses in registers in 64-bit mode. Use RIP-relative LEA, unless you're optimizing for position-dependent code where you can use mov r32, imm32.
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com about 5 years
    Hi @PeterCordes thanks for the edit. Yes, I think I didn't know what PIE was at the time + many other details :-) If you feel like making it work with -pie as well, that would be cool ;-) I'm lazy to research that now.
  • Peter Cordes
    Peter Cordes about 5 years
    I already included call printf wrt ..plt in my first edit. I put it back in in a more appropriate place now that you made a bullet point for it. I had to look up the NASM equivalent of GAS call *printf@GOTPCREL(%rip) , for no-plt style code that does early binding of dynamic symbols instead of lazy linking via the PLT. (But with the upside of just indirect call instead of call + jmp for lazy dynamic linking with a PLT.)
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com about 5 years
    @PeterCordes ah OK, I thought that was just a pseudo notation, what a weird syntax!
  • Peter Cordes
    Peter Cordes about 5 years
    Agreed. .plt is the section name, and I guess there's an extra . in there maybe to go with the with-respect-to abbreviation?
  • Peter Cordes
    Peter Cordes almost 5 years
    Can't call C standard library function on 64-bit Linux from assembly (yasm) code has a bit more detail about call [rel printf wrt ..got] and call printf wrt ..plt. I just updated it to work for NASM as well as YASM.
  • Peter Cordes
    Peter Cordes over 3 years
    2021 update: you may need gcc -m32 -no-pie, or at least it's a good idea if you're going to do call printf instead of call printf@plt, and also to use absolute addresses as immediates, not position-independent. In practice for 32-bit code you can usually get away with it.
  • Peter Cordes
    Peter Cordes over 3 years
    32-bit code: How to convert a binary integer number to a hex string?. 32-bit / 64-bit conversion to decimal: How do I print an integer in Assembly Level Programming without printf from the c library? with 64-bit Linux syscall to write to stdout.