Jump to content
Marioh

Duvida com a div no nasm

 Read less than a minute

Recommended Posts

 Read less than a minute

Tava mexendo no nasm fazendo umas funções e resolvi fazer uma função que imprime numeros em decimal. Até onde eu sei a instrução div divide o
rax pelo valor em outro registrador, colocando o resultado da divisão no rax e o resto no rdx, porém se na hora da divisão o rdx não estiver zerado
alguma coisa maluca acontece e é essa minha duvida. Oque que rola se o rdx não estiver zerado e fizermos uma div ?

Aqui o código da função:

_printnum:
	push rbp
	mov rbp, rsp
	sub rsp, 8

	mov r10, 10
	push r10                     ; quebra de linha
	mov qword [rbp - 8], 1       ; length

_prntn_lp_bgn:
	mov rdx, 0
	idiv r10
	add rdx, 0x30
	push rdx
	inc qword[rbp - 8]

	cmp rax, 0
	jne _prntn_lp_bgn

	mov rax, qword[rbp - 8]
	mov r10, 8
	mul r10
	mov rdx, rax      ;gambiarra da stack

	mov rax, 1
	mov rdi, 1
	mov rsi, rsp
	syscall

	mov rax, qword [rbp - 8]       ; length
	mov rsp, rbp
	pop rbp
	ret

Sem a linha mov rdx, 0 logo depois do loop o bagulho buga todo.

(Nem reparem na gambiarra que eu fiz na stack, dicas são muito bem vindas)

 

Share this post


Link to post
Share on other sites
 Read less than a minute

A questão é que usando um registrador de 64 bits, a instrução divide o valor contido em RDX:RAX pelo valor passado como operando. Considere que RDX são os 8 bytes mais significativos do valor, e RAX seriam os 8 bytes menos significativos.

Simplesmente zerar RDX traria um resultado incorreto se o número em RAX fosse negativo, o certo seria fazer um sign-extend e a instrução CQO faz isso para você:

mov rax, 2345
cqo   ; RDX:RAX = sign_extend(RAX)
idiv rbx

 

  • l33t 1

Share this post


Link to post
Share on other sites
Posted (edited)
 Read less than a minute

A ideia de empilhar os restos foi interessante, mas tem um problema: Vocẽ está empilhando QWORDs, não BYTES.
Pra quê alocar o "tamanho" na pilha... Existem 16 registradores de uso geral disponíveis.
Existem alguns "acertos" a serem feitos no código... Por exemplo:
XOR EDX,EDX (sim EDX, não RDX) é mais performático e menor que MOV RDX,0, mas, como o @Felipe.Silva disse, estender o sinal é melhor.
E você acabará com um problema com valores negativos (acho que não testou esses, né?)... -12, por exemplo, resultará num resto de -2 na primeira divisão, o que empilhará '0'-2 ou o caracter '.'.

Ainda... multiplicações são sempre mais rápidas que divisões. Veja isso: https://is.gd/rB9Vlb

Como calcular o resto... Ora... se tem o quociente Q, o resto de A/10 é A - (Q*10)

"Q*10" pode ser fácilmente calculado com 'LEA RDX,[RAX+RAX*4] / ADD EDX,EDX' (unsigned).

Edited by fredericopissarra
  • Curtir 1

Share this post


Link to post
Share on other sites
 Read 1 minute

Recomendo algo assim:
 

#include <unistd.h>

void print_decimal( long n )
{
  char buffer[24];   // buffer local que conterá a string.
  char *p;           // ponteiro para o buffer.
  unsigned long m;   // O valor "sem sinal" de n.
  unsigned long r;   // resto.
  _Bool signaled;
  size_t size;

  p = buffer + sizeof buffer - 1; // aponta para o fim do buffer.

  // Verifica sinal.
  signaled = 0;
  m = n;

  // O caso de n==0 falha, então precisamos disso.
  if ( ! n )
  {
    size = 1;
    *p-- = '0';
    goto print;
  }
  
  if ( n < 0 )
  {
    signaled = !signaled;
    m = -n;
  }

  size = 0;
  while ( m )
  {
    r = m % 10;
    m /= 10;
    *p-- = '0' + r;
    size++;
  }

  if ( signaled )
  {
    *p-- = '-';
    size++;
  }

print:
  write( STDOUT_FILENO, p+1, size );
}

int main( void )
{
  print_decimal(-12);
  putchar('\n');
}

Convertido para asm:

; NASM code.
bits 64
default rel

section .text

  global print_decimal
print_decimal:
  sub   rsp, 40                     ; Aloca espaço na pilha para o buffer.

  test  rdi, rdi                    ; testa se n é 0 ou negativo.
  je    .zero_case
  js    .negative

  xor   r9d, r9d                    ; Usa R9 como booleano (signaled).
.prepare:
  xor   ecx, ecx                    ; RCX é size
  lea   rsi, [rsp+23]               ; Aponta para o final do buffer.
  mov   r8, -3689348814741910323    ; Constante usada no "macete" da multiplicação por 1/10, abaixo.
  jmp   .divide
.loop:
  mov   rcx, rdx
.divide:
  mov   rax, rdi

  sub   rsi, 1

  mul   r8

  shr   rdx, 3              ; Obtém o quociente da divisão por 10 em RAX
  mov   rax, rdx

  lea   rdx, [rdx+rdx*4]    ; Calcula o resto em RDX.
  add   rdx, rdx
  sub   rdi, rdx
  mov   rdx, rdi

  mov   rdi, rax            ; O quociente é o novo dividendo.

  add   edx, '0'
  mov   [rsi+1], dl
  lea   rdx, [rcx+1]

  test  rax, rax            ; Dividendo é 0?
  jne   .loop               ; Não? permanece no loop.
  test  r9d, r9d            ; Tem sinal?
  je    .print              ; Não? salta para impressão.

  mov   rax, rsi
  lea   rdx, [rcx+2]
  sub   rsi, 1
  mov   BYTE [rax], '-'

.print:
  add   rsi, 1              ; de volta para o primeiro caracter do buffer.
  mov   edi, 1              ; STDOUT_FILENO.
  mov   eax, edi            ; SysV ABI write syscall
  syscall

  add   rsp, 40             ; Dealloca espaço para o buffer.
  ret

.zero_case:
  mov   BYTE [rsp+23], '0'
  mov   edx, 1
  lea   rsi, [rsp+22]
  jmp   .print

.negative:
  neg   rdi
  mov   r9d, 1
  jmp   .prepare

Dá pra melhorar? Yep... meu ponto é que criar uma rotina em ASM não necessariamente a faz mais rápida (essa ai é mais rápida que a sua - medida!), nem menor.

  • Curtir 2

Share this post


Link to post
Share on other sites
 Read less than a minute

PS: Usei um buffer de 24 bytes para mantê-lo alinhado na pilha, um buffer de 20 bytes seria suficiente, já que ceil(log10(2⁶³-1)) = 19.

Share this post


Link to post
Share on other sites
 Read less than a minute
Quote

Vocẽ está empilhando QWORDs, não BYTES.

Na verdade eu tentei fazer tudo pela stack mesmo, e até onde eu sei não se empilha byte por causa do alinhamento (até onde eu sei...), então
eu ia fazer com DWORD e depois só multiplicar por 4, mas deixei QWORD mesmo já que é uma "gambiarra" anyway.

Muito f**a seu artigo, precisei ler com mais calma da segunda vez! Suspeitamente parecido com LaTex...
(segundo paragrafo, acho que tem um erro ortográfico)

Valeu mesmo @fredericopissarra !


 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...