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
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.
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).
Printing an integer with x86 32-bit Linux sys_write (NASM)
Your numbers will quickly grow larger than just a single digit. What you ought to be doing is have an integer in num
rather than a character, and then convert that integer into a string that you can print with sys_write
.
Here's one way of doing the conversion: repeated division by 10, getting the lowest digit first as the remainder:
; Input:
; eax = integer value to convert
; esi = pointer to buffer to store the string in (must have room for at least 10 bytes)
; Output:
; eax = pointer to the first character of the generated string
; ecx = length of the generated string
int_to_string:
add esi,9
mov byte [esi],0 ; String terminator
mov ebx,10
.next_digit:
xor edx,edx ; Clear edx prior to dividing edx:eax by ebx
div ebx ; eax /= 10
add dl,'0' ; Convert the remainder to ASCII
dec esi ; store characters in reverse order
mov [esi],dl
test eax,eax
jnz .next_digit ; Repeat until eax==0
; return a pointer to the first digit (not necessarily the start of the provided buffer)
mov eax,esi
ret
Which you can use like this:
mov dword [num],1
...
mov eax,[num] ; function args using our own private calling convention
mov esi,buffer
call int_to_string
; eax now holds the address that you pass to sys_write
...
section .bss
num resd 1
buffer resb 10
Your number-doubling can be simplified to shl dword [num],1
. Or better, double it at some point while it's still in a register with add eax,eax
.
Scan an integer and print the interval (1, integer) in NASM
There are several problems:
1. The parameters to printf, as discussed in the comments. In x86-64, the first few parameters are passed in registers.
2. printf does not preserve the value of eax.
3. The stack is misaligned.
4. rbx is used without saving the caller's value.
5. The address of integer
is being loaded instead of its value.
6. Since printf is a varargs function, eax needs to be set to 0 before the call.
7. Spurious int 80h
after the call to scanf.
I'll repeat the entire function in order to show the necessary changes in context.
main:
push rbx ; This fixes problems 3 and 4.
mov eax, 4
mov ebx, 1
mov ecx, message1
mov edx, message1Len
int 80h
mov rdi, formatin
mov rsi, integer
mov al, 0
call scanf
mov ebx, [integer] ; fix problems 2 and 5
loop:
mov rdi, formatout ; fix problem 1
mov esi, ebx
xor eax, eax ; fix problem 6
call printf
dec ebx
jnz loop
pop rbx ; restore caller's value
mov rax,0
ret
P.S. To make it count up instead of down, change the loop like this:
mov ebx, 1
loop:
<call printf>
inc ebx
cmp ebx, [integer]
jle loop
How to print a character in Linux x86 NASM?
ecx
should contain a pointer to the start of your char buffer. So you have to have your buffer in memory. You can do the following:
; Print 'A' character
mov eax, 4 ; __NR_write from asm/unistd_32.h (32-bit int 0x80 ABI)
mov ebx, 1 ; stdout fileno
push 'A'
mov ecx, esp ; esp now points to your char
mov edx, 1 ; edx should contain how many characters to print
int 80h ; sys_write(1, "A", 1)
; return value in EAX = 1 (byte written), or error (-errno)
add esp, 4 ; restore esp if necessary
You can mov byte [esp], 'A'
or whatever other address if it's OK to overwrite whatever is on the stack.
Or you can have a character array in section .rodata
instead of storing on the fly.
Making a write()
system call with the const void *buf
arg being some small number (like 'A'
) will make it return -EFAULT
without printing anything. The kernel has to check the pointer anyway, and system calls return an error instead of raising SIGSEGV on bad pointers.
Use strace ./my_program
to trace the system calls you actually made, including decoding the return values.
Related Topics
Find Directories Having Size Greater Than X Mb
Check If File Is Open with Lsof
Get the Characters After the Last Index of a Substring from a String
Null Modem Emulator (Com0Com) for Linux
What Unit Is Used to Display Redis CPU Usage
How to Specify Time in Cron Considering Year
Newbie on Debian and Trying to Make Java 7 the Default Java Version Used
Mpi_Send Takes Huge Part of Virtual Memory
Parse CSV in Bash and Assign Variables
Adding Users to Sudoers Through Shell Script
Avrdude: Ser_Open(): Can't Open Device "/Dev/Ttyacm0": Device or Resource Busy
Linking Boost Library in Linux
Interrupting Epoll_Wait with a Non-Io Event, No Signals
Unsupported Protocol While Download Tar.Gz Package
Shell Script to Copy and Prepend Folder Name to Files from Multiple Subdirectories
Find Command in Bash Script Resulting in "No Such File or Directory" Error Only for Directories