NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: Ferran on September 06, 2020, 08:36:30 PM
-
Hello everybody !
Suddenly a poor programmer forgot how to sum 2 numbers. He told me he was happy with i386 machines in 32 bits, but now in 64 bits he don't. He gave me this code if its possible we can help him.
;-----------------------------------------------------------------
; sum.asm
; Learning to sum 2 numbers with 64 bit assembly
; (for GNU/Linux Assembly x80_64 Intel syntax & NASM compiler)
;
; Compile: nasm -f elf64 sum.asm
; Link: ld -s -o sum sum.o
; Run: ./sum
;
; Author: anonymous ashamed...
;------------------------------------------------------------------
segment .data
num1: dd 12
num2: dd 55
lf: db " ", 10, 0
res: dd " ", 10, 0
len_res equ $ - res
segment .text
global _start
_start:
.sum:
mov rax, [num1]
add rax, [num2]
mov [res], rax
.show_result:
mov rax, 1
mov rdi, 1
mov rsi, res
mov rdx, len_res
syscall
.showLF:
mov rax, 1
mov rdi, 1
mov rsi, lf
mov rdx, 2
syscall
.exit:
xor edi, edi
mov rax, 60
syscall
I appeal to the charity of the good people of this forum to help to this poor programmer.
Why don't works the code?
-
Because you need to transform the binary value in a string...
-
I saw in a book in Assembly64 pdf http://www.egr.unlv.edu/~ed/assembly64.pdf (http://www.egr.unlv.edu/~ed/assembly64.pdf) one script that convert integer to ascii (itoa function), i mixed it with our script but it not prints nothing :(
this is the mix i did
;-----------------------------------------------------------------
; sum.asm
; Learning to sum 2 numbers with 64 bit assembly
; (for GNU/Linux Assembly x80_64 Intel syntax & NASM compiler)
;
; Compile: nasm -f elf64 sum.asm
; Link: ld -s -o sum sum.o
; Run: ./sum
;
; Author: anonymous ashamed...
;------------------------------------------------------------------
segment .data
num1 dd 1111111111
num2 dd 2222222222
res dd 0 ; The number to print
newline db 10
; Syscall information
sys_exit equ 60
sys_write equ 1
; Streams
stdout equ 1
; Constants
BUFFER_SIZE equ 10
section .bss
numbuf resb BUFFER_SIZE ; A buffer to store our string of numbers in
segment .text
global _start
_start:
.sum:
mov rax, [num1]
add rax, [num2]
mov [res], rax
mov rdi,[res]
call itoa
; Write the string returned in rax out to stdout
mov rdi,rax ; The string pointer is returned in rax - move it to rdi for the function call
mov rsi,rcx
call print
; Write the newline character to stdout
mov rdi,newline
mov rsi,1
call print
; Exit
mov rax,sys_exit
mov rbx,0
int 0x80
; Args: (rdi: char*, rsi: int)
print:
mov rax,sys_write
mov rbx,stdout
mov rcx,rdi
mov rdx,rsi
int 0x80
ret
itoa:
push rbp
mov rbp,rsp
sub rsp,4 ; allocate 4 bytes for our local string length counter
mov rax,rdi ; Move the passed in argument to rax
lea rdi,[numbuf+10] ; load the end address of the buffer (past the very end)
mov rcx,10 ; divisor
mov [rbp-4],dword 0 ; rbp-4 will contain 4 bytes representing the length of the string - start at zero
.divloop:
xor rdx,rdx ; Zero out rdx (where our remainder goes after idiv)
idiv rcx ; divide rax (the number) by 10 (the remainder is placed in rdx)
add rdx,0x30 ; add 0x30 to the remainder so we get the correct ASCII value
dec rdi ; move the pointer backwards in the buffer
mov byte [rdi],dl ; move the character into the buffer
inc dword [rbp-4] ; increase the length
cmp rax,0 ; was the result zero?
jnz .divloop ; no it wasn't, keep looping
mov rax,rdi ; rdi now points to the beginning of the string - move it into rax
mov rcx,[rbp-4] ; rbp-4 contains the length - move it into rcx
leave ; clean up our stack
ret
I will follow searching the solution, but it seems difficult (for me). See you.
-
it is wrong. But I already shown how to do it, in another post...
-
For starters, you are trying to print a number without converting it to an ASCII string first, as fredericopissarra already pointed out.
-
But he is!
Using a 64 bit syscall (1) with int 80h looks like a problem, Try 4!
Best.
Frank
-
Ok, I did some changes and I'm still searching solutions to convert a decimal number to a printable string. I can't find the post of @federicopisarra about it.
The last one is using the function dec2stru from BASELIB(c) - owned by stressful, but it don't works to me.
The scripts now is this:
;------------------------------------------------------------------------
; sum.asm
; Learning to sum 2 numbers with 64 bit assembly
; (for GNU/Linux Assembly x80_64 Intel syntax & NASM compiler)
;
; Compile: nasm -f elf64 sum.asm
; Link: ld -s -o sum sum.o
; Run: ./sum
;
; Author: Ferran
;
; NOTE: Included here the function dec2stru(2) extracted from BASELIB(c)
; (owned by stressful - Nasm forum member)
;-------------------------------------------------------------------------
segment .data
num1: dd 654321
num2: dd 123456
res: dd 0
len_res equ $ - res
lf: db " ",10, 0
segment .bss
buffer resd 10
segment .text
global _start
_start:
mov rbx, [buffer] <---- Maybe is this the problem ?
.sum:
mov rax, [num1]
add rax, [num2]
; --- using a BASELIB(c) function ----
call dec2stru
; ------------------------------------
.show_result:
mov rax, 1
mov rdi, 1
mov rsi, res
mov rdx, len_res
syscall
.showLF:
mov rax, 1
mov rdi, 1
mov rsi, lf
mov rdx, 2
syscall
.exit:
xor edi, edi
mov rax, 60
syscall
;---------------------------
;dec2stru(2)
;Convert unsigned int to
;0-ended dec string
;---------------------------
;RBX : Buffer's address
;RAX : Integer to convert
;---------------------------
;Ret : String in buffer
;Note : Buffer must be large enough
;---------------------------
align 8
dec2stru:
push rax
push rdx
push rcx
push rsi
push rdi
cld
mov rdi,rbx
xor rsi,rsi
mov rcx,10
.more: xor rdx,rdx
div rcx
push rdx
inc rsi
test rax,rax
jnz .more
.get: pop rax
add al,30h
stosb
sub rsi,1
jnz .get
xor al,al
stosb
pop rdi
pop rsi
pop rcx
pop rdx
pop rax
ret
Failed. Terminated with an "Segment fault" message.
What is the issue now?
-
Your decstru funcion is pushing lots of values on stack and not poping enough.
And it is still wrong. You should pay attention to details instead of copying someone else's code.
The algorithm should be:
; p = pointer to the end of buffer;
; c = counter
; q = quotient;
; r = remainder;
p = buffer + sizeofbuffer - 1;
c = 0;
do { q = n / 10; r = n % 10; n = q; *p-- = r + '0'; c++; } while ( q != 0 );
return ++p, c;
A modified examlpe (with many corrections) to your code:
;------------------------------------------------------------------------
; sum.asm
;
; Compile: nasm -f elf64 sum.asm -o sum.o
; ld -s -o sum sum.o
;
; It is garanteed initializing E?? portion of R?? will zero
; the upper 32 bits, so using 'mov eax,1' is *shorter* than
; 'mov rax,1' and has the same effect.
;
; Using LEA instead of MOV to load pointers is my preference.
; There is nothing wrong using MOV.
;-------------------------------------------------------------------------
section .data
; If you intend to read two QWORDs, these must be declares as QWORDs,
; not DWORDs.
num1: dq 654321
num2: dq 123456
section .rodata
lf: db `\n`
lf_len equ ($ - lf)
section .text
global _start
_start:
mov rax,[num1] ; Reading QWORDs, see?
add rax,[num2]
call printulong
mov eax,1
mov edi,eax
lea rsi,[lf]
mov edx,lf_len
syscall
mov eax,60
xor edi,edi
syscall
;---------------------------
; printulong
; Print unsigned long int in decimal.
;---------------------------
; Entry: RAX: unsigned long int to convert
; Destroys: RAX,RBX, RCX, RDX and RSI
; Unless you save and restore them.
;---------------------------
align 4
printulong:
; push rax ; Don't need to save them
; push rbx ; for this code.
; push rcx
; push rsi
; push rsi
sub rsp,32 ; Allocate space for buffer on stack.
; I could allocate only 20 bytes, but the stack
; pointer must be aligned by 8.
;
; Why 20 bytes? The maximum decimal value for
; an unsigned QWORD is "18446744073709551615".
lea rsi,[rsp+24] ; Points to the end of the buffer.
xor ecx,ecx ; counter = 0.
mov ebx,10 ; divisor.
align 4
.loop:
xor edx,edx
div rbx
add dl,'0' ; add '0' to the remainder, not the quotient!
mov [rsi],dl ; store it on the buffer.
inc ecx ; we have one more char there.
dec rsi ; points to 'next' position.
test rax,rax ; quotient == 0?
jnz .loop ; no, loop.
inc rsi ; points to the begining of the buffer.
mov eax,1 ; sys_write
mov edi,eax ; stdout
mov edx,ecx ; string size.
syscall
add rsp,32 ; Reclaim allocated space.
; pop rsi ; Don't need to restore them
; pop rdx ; for this code.
; pop rcx
; pop rbx
; pop rax
ret
-
OK @federicopissarra I understand the algorithmic process for to converse a binary to decimal number, and after make an string with it. This idea is entered in my head. My little problem is how to codify this.
Now, I'm going to study your code and i'll come back soon. Thank you for all.
-
@federicopissarra
here i go again. I tested the code, it works perfectly :)
Only I needed to adapt the code to my project of a modular way (the printing is out of function), but the rest is the same. This already allows me to add a whole series of arithmetic functions of unsigned integers and they can be printed every time it is requested.
The code now is this:
;------------------------------------------------------------------------
; sum.asm
;
; Compile: nasm -f elf64 sum.asm -o sum.o
; ld -s -o sum sum.o
;
; It is garanteed initializing E?? portion of R?? will zero
; the upper 32 bits, so using 'mov eax,1' is *shorter* than
; 'mov rax,1' and has the same effect.
;
; Using LEA instead of MOV to load pointers is my preference.
; There is nothing wrong using MOV.
;-------------------------------------------------------------------------
section .data
num1: dq 654321 ;
num2: dq 123456
lres: db 0
section .rodata
lf: db `\n`
lf_len equ ($ - lf)
section .bss
res: resb 8
section .text
global _start
_start:
; --- token sum res = (num1 + num2) --------------
mov rax,[num1] ; number 1
add rax,[num2] ; number 2
call printulong ; conversion to dec
mov [res], rsi ; result of the sum
mov [lres], ecx ; length of result
; ----------- token show (res) -------------------
mov eax,1 ; sys_write
mov edi,eax ; stdout
mov rsi, [res] ; string to display
mov edx, [lres] ; string size.
syscall
; ----------token show (lf) ----------------------
mov eax,1
mov edi,eax
lea rsi,[lf]
mov edx,lf_len
syscall
; -----------token exit --------------------------
mov eax,60
xor edi,edi
syscall
;---------------------------
; printulong
; Print unsigned long int in decimal.
;---------------------------
; Entry: RAX: unsigned long int to convert
; Destroys: RAX,RBX, RCX, RDX and RSI
; Unless you save and restore them.
;---------------------------
align 4
printulong:
sub rsp,32 ; Allocate space for buffer on stack.
lea rsi,[rsp+24] ; Points to the end of the buffer.
xor ecx,ecx ; counter = 0.
mov ebx,10 ; divisor.
.loop:
xor edx,edx
div rbx
add dl,'0' ; add '0' to the remainder, not the quotient!
mov [rsi],dl ; store it on the buffer.
inc ecx ; we have one more char there.
dec rsi ; points to 'next' position.
test rax,rax ; quotient == 0?
jnz .loop ; no, loop.
inc rsi ; points to the begining of the buffer.
add rsp,32 ; Reclaim allocated space.
ret
NOTE: As if, ithe code works well when num1 is positive and num2 is negative, making the substraction :)
In other hand I will be able to adapt this program easily when I would load values into variables by arguments, arrays, lists, functions or file data.
Now i need to do the same with unsigned and floating point numbers, but this is another history...
-
@federicopissarra
I have a question .Why is not needed in
mov [rsi],dl ; store it on the buffer.
to add 48 to dl like an ASCII code ?
-
I have a question .Why is not needed in
mov [rsi],dl ; store it on the buffer.
to add 48 to dl like an ASCII code ?
You really did't study the code, did ya?
Look at previous instruction. And read the comments.
-
You don't said that '0' represents the ascii code directly, but i thought you should add a 48 in decimal, or a 30h in hexadecimal, or octal or elsewhere, because adding 48 in decimal it let forward 48 positions in the ascii table to locate the equivalent sign in ascii code, so it will be readable. If it is possible do it adding with a '0' it's allright.
I see you are a bit negative with me. Never miind, I just have to thank you for all your help.
-
You don't said that '0' represents the ascii code directly
Why should I?
but i thought you should add a 48 in decimal, or a 30h in hexadecimal, or octal or elsewhere, because adding 48 in decimal it let forward 48 positions in the ascii table to locate the equivalent sign in ascii code, so it will be readable. If it is possible do it adding with a '0' it's allright.
You do know '0' is the same thing as 48 or 0x30 or 060 or 0b00110000, don't ya?