NASM Forum > Programming with NASM

How to sum 2 numbers (in Gnu/linux assembly x80_64 & Nasm)?

<< < (2/3) > >>

Frank Kotler:
But he is!

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

Best.
Frank

Ferran:
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: ---;------------------------------------------------------------------------
; 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


--- End code ---

Failed. Terminated with an "Segment fault" message.

What is the issue now?

fredericopissarra:
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: ---; 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;
--- End code ---

A modified examlpe (with many corrections) to your code:

--- 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
--- End code ---

Ferran:
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.

Ferran:
@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: ---;------------------------------------------------------------------------
; 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


--- End code ---

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...

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version