How to Print a Number in Assembly Nasm

How to print a number in assembly NASM?

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.

NASM - how to print a number using int 080h?

If you want to print a number in ASCII string, you have to convert the number to a string.

This will work for nonnegative number:

    ; dummy to tell the program the end of data
push -1
; set the number to convert
xor eax, eax
mov al, [lett]
; convert the number to string (sequence of character)
convert_loop:
xor edx, edx
mov ebx, 10
div ebx
add edx, '0'
push edx
test eax, eax
jnz convert_loop
; print the converted string
print_loop:
cmp dword [esp], 0
jl print_loop_end ; break when -1 is found
mov eax, 4
mov ebx, 1
mov ecx, esp
mov edx, 1
int 080h
pop eax ; move on next character
jmp print_loop
print_loop_end:
pop eax ; clean -1

UPDATE: Another version without using push / pop instruction:

section .bss
; 32-bit unsigned integer won't be longer than 10 digits in decimal
strtonum_convert_buffer resb 12

section .text

; dummy to tell the program the end of data
mov ecx, strtonum_convert_buffer
mov byte [ecx], 0
; set the number to convert
xor eax, eax
mov al, [lett]
; convert the number to string (sequence of character)
convert_loop:
xor edx, edx
mov ebx, 10
div ebx
add edx, '0'
inc ecx
mov [ecx], dl
test eax, eax
jnz convert_loop
; print the converted string
print_loop:
cmp byte [ecx], 0
je print_loop_end ; break when 0 is found
mov eax, 4
mov ebx, 1
; there is already the pointer to the character on ecx
mov edx, 1
int 080h
dec ecx ; move on next character
jmp print_loop
print_loop_end:

Printing a number in assembly NASM using printf

Apparently you don't even know how printf works which makes it hard to invoke it from assembly.

To print a number, printf expects two arguments, a format string and the number to print of course. Example: printf("%d\n", 12345).

Now to turn that into assembly, you obviously need to declare that format string, and then pass both arguments using the appropriate convention.

Since you seem to be using sysv abi, this means the first two arguments go into rdi and rsi, respectively. You already seem to know you have to zero al to indicate no SSE registers used. As such, the relevant part could look like:

lea rdi, [rel fmt]
mov rsi, 12345 ; or mov rsi, [count]
mov al, 0
call printf
...
fmt: db "%d", 0x0a, 0

printing numbers in nasm

Answer rewritten after some experimentation.

There two errors in your code, and a few inefficiencies.

First, you add 0x30 to the number (to turn it from the number 1 to the ASCII 1). However, you do that increment inside the loop. As a result, your first iteration cx is 0x31, second 0x62 ("b"), third 0x93 (invalid UTf-8 sequence) etc.

Just initialize cx to 0x30 and remove the add from inside the loop.

But there's another problem. RCX is clobbered during system calls. Replacing cx with r12 causes the program to work.

In addition to that, you pass the buffer's length to write, but it only has one character. The program so far:

section .bss

lena equ 1024
outbuff resb lena

section .data

section .text

global _start
_start:
nop
mov r12,30h

incre:
inc r12
mov [outbuff],r12

cmp r12,39h
jg done

cmp r12,39h
jl print


print:
mov rax,1 ;sys_write
mov rdi,1
mov rsi,outbuff
mov rdx,1
syscall
jmp incre

done:
mov rax,60 ;sys_exit
mov rdi,0
syscall

Except even now, the code is extremely inefficient. You have two compares on the same condition, one of them branches to the very next instruction.

Also, your code would be much much much faster and smaller if you moved the breaking condition to the end of the code. Also, cx is a 16 bit register. r12 is a 64 bit register. We actually only need 8 bits. Using larger registers than needed means all of our immediates waste up space in memory and the cache. We therefor switch to the 8 bit variant of r12. After these changes, we get:

section .bss

lena equ 1024
outbuff resb lena

section .data

section .text

global _start
_start:
nop
mov r12b,30h

incre:
inc r12b
mov [outbuff],r12b

mov rax,1 ;sys_write
mov rdi,1
mov rsi,outbuff
mov rdx,1
syscall

cmp r12b,39h
jl incre

mov rax,60 ;sys_exit
mov rdi,0
syscall

There's still lots more you can do. For example, you call the write system call 9 times, instead of filling the buffer and then calling it once (despite the fact that you've allocated a 1024 bytes buffer). It will probably be faster to initialize r12 with zero (xor r12, r12) and then add 0x30. (not relevant for the 8 bit version of the register).

NASM Linux Assembly Printing Integers

This is adding, not storing:

add edx, ecx        ; stores ecx in edx

This copies ecx to eax and then overwrites it with 4:

mov eax, ecx        ; moves ecx to eax for writing
mov eax, 4 ; sys call for write

EDIT:

For a 'write' system call:

eax = 4
ebx = file descriptor (1 = screen)
ecx = address of string
edx = length of string

Cannot print a number in NASM

Three bugs:

section .data:

This assembles into a section called .data: which is different from .data. Drop the colon. A section directive is not a label. The same problem occurs in all your other section directives.

message_done: db "you have entered the number "
message_done_length equ $-message

Should be $-message_done. As it stands you are writing too many bytes. That is the likely cause of the garbage you see after your message.

        mov eax,3               ;set the next syscall to read
mov ebx, 2 ;set the fd to stdout
mov ecx, num ;set the output to be num
mov edx, 5 ;set edx to the length of the num
int 0x80 ;syscall

You want the fd to be stdin (0), your comment says stdout, and file descriptor 2 is actually stderr. Make it mov ebx, 0. It will probably work as is when you run the program from a terminal, because then file descriptors 0, 1 and 2 are all open on the terminal and are all read-write, but it will misbehave if you ever use input redirection.



Related Topics



Leave a reply



Submit