Author Topic: nasm recursion  (Read 11192 times)

Offline vlobunet

  • New Member
  • Posts: 1
nasm recursion
« on: February 11, 2020, 06:14:50 AM »
hello coders! Sorry, but I do not know English very well!  on CIS forums, alas, there is no solution to my question. I am solving a problem. You need to display the number on the screen. In assembler, the number comes from the si function and looks like this:
void ft_putnbr_fd(int nbr, int fd)
... where fd is a descriptor and nbr is a number that may be negative.
I check the number first. if it is greater than or equal to 0, then I go to the body of recursion (_body). otherwise, I display the symbol '-' on the screen, and I make the number positive.
In the recursion body, I save the whole and the remainder each time on the stack. after that I divide the integer by 10. if after dividing the integer value is greater than 0 then I go to _body and when the integer is 0 I go to the output of _display
and here I have problems. I am using lldb debugger, intel mnemonics, x86_64, ios
during debugging, I go all the way to _display but after displaying the character on the screen, I see the following message:
->  0x100000dd4 <+26>: retq
0x100000dd5:       nop
0x100000dd6:       nop
0x100000dd7:       nop
after that the program crashes.
my code

%define CALL(n)      0x2000000 | n
%define WRITE      4

global  _ft_putnbr_fd
extern  _ft_putchar_fd

section .data
   pos dq 0
   minus db '-'

section .text

_ft_putnbr_fd:
   push   rbp;
   mov      rbp, rsp;

   mov      rax, rdi
   mov      rdx, 0
   cmp      rax, 0
   jae      _body
   neg      rax
   push   rax
   mov      rsp, 45
   call   _ft_putchar_fd
   pop      rax
   jmp      _body
   ret


_body:
   push   rdx
   xor      rdx, rdx
   mov      rcx, 10
   div      rcx
   cmp      rax, 0
   jne      _body
   jmp      _display
   ret

_display:
   add    rdx, '0'
   push   rdi
   mov      rdx, 1
   mov      rdi, rsi
   mov      rsi, rdx
   mov      rax, CALL(WRITE)
   syscall

   pop      rdi
   ;call   _ft_putchar_fd
   pop      rdx
   ret

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: nasm recursion
« Reply #1 on: February 11, 2020, 11:25:42 PM »
Hi viobunet.
Welcome to the Forum.

I don't know, but:
Code: [Select]
mov rsp, 45
doesn't look right to me. Did you mean
Code: [Select]
mov [rsp], 45
?
"45" may not be valid memory. Sorry I can't help more...

Best,
Frank



Offline fredericopissarra

  • Full Member
  • **
  • Posts: 373
  • Country: br
Re: nasm recursion
« Reply #2 on: February 12, 2020, 03:08:21 PM »
hello coders! Sorry, but I do not know English very well!

Welcome! I am not a native english speaker myself (brazillian!)

Quote from: vlobunet
on CIS forums, alas, there is no solution to my question. I am solving a problem. You need to display the number on the screen.
Which number? I didn't get it what you are trying to do...

Quote from: vlobunet
In assembler, the number comes from the si function and looks like this:
void ft_putnbr_fd(int nbr, int fd)
... where fd is a descriptor and nbr is a number that may be negative.
So, As I understand, you want to write 'nbr', in decimal, to the file descriptor 'fd'? Is so, here's a tip: Write the code in C, compile it and generate the assembly code and tweak it:
Code: [Select]
/* test.c */
static void write_buffer( int, void *, size_t );

void ft_putnbr_fd ( int nbr, int fd )
{
  // 10 bytes buffer (9 chars to 'ascii' numbers and 1 char to '-', if needed.
  char buffer[10];
  char *p, *q;
  int ch, tmp;

  // Points to the end of the buffer.
  p = q = buffer + sizeof buffer - 1;

  tmp = nbr;
  if ( tmp < 0 )
    tmp = -tmp;

  do
  {
    ch = tmp % 10;
    tmp /= 10;

    // Don't write left hand '0's...
    if ( ch || p == q )
      *p-- = ch + '0';
  } while ( tmp );

  // If negative, add '-' to the string.
  if ( nbr < 0 )
    *p = '-';
  else
    p++;  // not negative? points back to the beginning of the string.

  // write the string to fd.
  write_buffer( fd, p, sizeof buffer - ( p - buffer ) + 1 );
}

void write_buffer( int fd, void *ptr, size_t size )
{
#if ! defined( __linux__ ) && ! defined( __x86_64 )
#error This works only on x86-64 linux.
#endif

  __asm__ __volatile__ (
    "syscall"
    : : "a" (1), "D" (fd), "S" (ptr), "d" (size) : "r11"
  );
}

Take a look at the code GCC creates (test.s), in assembly, using: gcc -O2 -S -masm=intel test.c

One direct implementation based on this code could be:
Code: [Select]
; NASM x86-64 code for ft_putnbr_fd().
bits    64
default rel

section .text

global ft_putnbr_fd

; void ft_putnbr_fd( int nbr, int fd );
; ENTRY: edi = nbr, esi = fd
ft_putnbr_fd:
  ; Save registers that needed to be preserved.
  push  rbx

  ; Allocate 16 bytes in the stack (need to be QWORD aligned).
  sub   rsp,16

  ; Use r8 as pointer to the end of the buffer.
  mov   r8,rsp

  ; ECX is the counter of the number of chars in the buffer.
  xor   ecx,ecx

  mov   eax,edi       ; eax = tmp = nbr;
  test  edi,edi       ; TEST affect SF!
  jns   .positive
  neg   eax
.positive:

.loop:
  xor   edx,edx
  mov   ebx,10
  div   ebx           ; here eax = tmp / 10; edx = tmp % 10
                      ; GCC change this to a multiplication, which is faster!

  test  edx,edx
  jnz   .store
  cmp   rsp,r8        ; Just store '0' if we are at the end of the buffer.
  je    .store

  test  eax,eax       ; while tmp isn't 0, stay in the loop.
  jnz   .loop

  test  edi,edi
  jns   .advance
  add   ecx,1
  mov   byte [r8],'-'
  jmp   .continue
.advance:
  add   r8,1
.continue:

  ; Write syscall.
  mov   eax,1   ; WRITE syscall
  mov   edi,esi ; fd
  mov   rsi,r8  ; points to buffer.
  mov   edx,ecx ; size of the buffer.
  syscall
 
  ; Dealloate 16 bytes from stack.
  add   rsp,16

  ; Restore registers that needed to be preserved.
  pop   rbx

  ; Return to the caller.
  ret

  ; Store DL in the buffer and returns to the loop.
.store:
  add   dl,'0'
  mov   [r8],dl
  add   ecx,1
  sub   r8,1
  jmp   .loop

« Last Edit: February 12, 2020, 03:55:28 PM by fredericopissarra »