Ir para conteúdo

Duvida com a div no nasm


Marioh

Posts Recomendados

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)

 

Link para o comentário
Compartilhar em outros sites

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

 

Link para o comentário
Compartilhar em outros sites

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

Link para o comentário
Compartilhar em outros sites

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.

Link para o comentário
Compartilhar em outros sites

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 !


 

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...