NASM - The Netwide Assembler
NASM Forum => Programming with NASM => Topic started by: vlobunet 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
-
Hi viobunet.
Welcome to the Forum.
I don't know, but:
mov rsp, 45
doesn't look right to me. Did you mean
mov [rsp], 45
?
"45" may not be valid memory. Sorry I can't help more...
Best,
Frank
-
hello coders! Sorry, but I do not know English very well!
Welcome! I am not a native english speaker myself (brazillian!)
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...
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:
/* 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:
; 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