Author Topic: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?  (Read 20927 times)

Offline Ferran

  • Jr. Member
  • *
  • Posts: 23
  • Country: ad
How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« 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.

Code: [Select]
;-----------------------------------------------------------------
; 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?

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #1 on: September 06, 2020, 11:12:35 PM »
Because you need to transform the binary value in a string...

Offline Ferran

  • Jr. Member
  • *
  • Posts: 23
  • Country: ad
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #2 on: September 06, 2020, 11:59:41 PM »
I saw in a book in 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

Code: [Select]
;-----------------------------------------------------------------
; 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.

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #3 on: September 07, 2020, 12:55:57 AM »
it is wrong. But I already shown how to do it, in another post...

Offline debs3759

  • Global Moderator
  • Full Member
  • *****
  • Posts: 224
  • Country: gb
    • GPUZoo
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #4 on: September 07, 2020, 01:27:26 AM »
For starters, you are trying to print a number without converting it to an ASCII string first, as fredericopissarra already pointed out.
My graphics card database: www.gpuzoo.com

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #5 on: September 07, 2020, 02:06:47 AM »
But he is!

Using a 64 bit syscall (1) with int 80h looks like a problem, Try 4!

Best.
Frank


Offline Ferran

  • Jr. Member
  • *
  • Posts: 23
  • Country: ad
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #6 on: September 07, 2020, 11:34:11 AM »
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:

Code: [Select]
;------------------------------------------------------------------------
; 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?
« Last Edit: September 07, 2020, 11:37:31 AM by Ferran »

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #7 on: September 07, 2020, 12:31:38 PM »
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:

Code: [Select]
; 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:
Code: [Select]
;------------------------------------------------------------------------
; 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

Offline Ferran

  • Jr. Member
  • *
  • Posts: 23
  • Country: ad
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #8 on: September 07, 2020, 01:59:07 PM »
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.

Offline Ferran

  • Jr. Member
  • *
  • Posts: 23
  • Country: ad
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #9 on: September 07, 2020, 05:57:14 PM »
@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:

Code: [Select]
;------------------------------------------------------------------------
; 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...

Offline Ferran

  • Jr. Member
  • *
  • Posts: 23
  • Country: ad
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #10 on: September 08, 2020, 03:06:31 PM »
@federicopissarra

I have a question .Why is not needed in

Code: [Select]
mov   [rsi],dl      ; store it on the buffer.

to add 48 to dl like an ASCII code ?

Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #11 on: September 09, 2020, 10:41:44 AM »
I have a question .Why is not needed in

Code: [Select]
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.
« Last Edit: September 09, 2020, 10:45:33 AM by fredericopissarra »

Offline Ferran

  • Jr. Member
  • *
  • Posts: 23
  • Country: ad
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #12 on: September 09, 2020, 04:02:20 PM »
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.


Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?
« Reply #13 on: September 09, 2020, 04:29:10 PM »
You don't said that '0' represents the ascii code directly
Why should I?

Quote from: Ferran
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?