Multiplicação de Inteiros em Assembly NASM: As Instruções mul e imul
A arquitetura x86-64 fornece duas instruções primárias para a multiplicação de inteiros, cuja escolha depende do contexto dos dados:
mul: Realiza a multiplicação de inteiros sem sinal (unsigned).imul: Realiza a multiplicação de inteiros com sinal (signed), tratando os valores na representação em complemento de dois.
Ambas as instruções, em sua forma mais fundamental, operam de maneira implícita, utilizando o registrador RAX (ou suas partes menores EAX, AX, AL) como um dos fatores e o destino principal.
mul e imul: A Forma Clássica de Um Operando
A sintaxe mais básica e poderosa para ambas as instruções aceita um único operando, que pode ser um registrador ou um endereço de memória.
mul source ou imul source
O funcionamento desta forma é projetado para lidar com o fato de que a multiplicação de dois números de n bits pode gerar um resultado de até 2n bits.
- O valor em
source(o operando explícito) é multiplicado por um operando implícito, que éRAXou uma de suas subpartes (AL,AX,EAX), dependendo do tamanho desource. - O resultado completo (
2nbits) é armazenado em um par de registradores para evitar perda de dados.
A tabela a seguir detalha essa operação:
Tamanho de source | Operando Implícito | Operação Realizada | Destino (Parte Alta : Parte Baixa) |
|---|---|---|---|
| 8 bits (byte) | AL | AL * source | AH : AL (ou seja, AX) |
| 16 bits (word) | AX | AX * source | DX : AX |
| 32 bits (dword) | EAX | EAX * source | EDX : EAX |
| 64 bits (qword) | RAX | RAX * source | RDX : RAX |
Se a parte alta do resultado (ex: RDX) for diferente de zero, significa que o resultado excedeu a capacidade do registrador da parte baixa (ex: RAX). Nesse caso, as instruções mul e imul ativam os flags de Carry (CF) e Overflow (OF) para sinalizar o estouro.
Exemplo 1: Resultado que Cabe na Parte Baixa (Linux)
global _start
section .text
_start:
mov rax, 4 ; Operando implícito
mov rdi, 2 ; Operando explícito (source)
mul rdi ; Executa RDX:RAX = RAX * RDI. Resultado: 4 * 2 = 8.
; RDX fica com 0, RAX fica com 8. CF e OF são 0.
mov rdi, rax ; rdi = 8 (para o código de saída)
mov rax, 60
syscallExemplo 2: Resultado que Utiliza a Parte Alta (Linux)
global _start
section .text
_start:
mov rax, 0xC000000000000002
mov rdi, 4
mul rdi ; RDX:RAX = 0xC000000000000002 * 4
; Resultado completo: 0x30000000000000008
; RDX = 3 (parte alta)
; RAX = 8 (parte baixa)
mov rdi, rdx ; Inspecionando a parte alta do resultado.
mov rax, 60
syscallEsta forma de um operando é essencial quando a precisão máxima é necessária e a perda de dados por estouro não é uma opção.
As Formas Convenientes da Instrução imul
Diferentemente de mul, a instrução imul (para números com sinal) oferece sintaxes mais flexíveis e intuitivas com dois e três operandos, que se assemelham mais às operações em linguagens de alto nível.
Importante: Essas formas de
imulsão mais convenientes, mas têm uma limitação: a parte alta do resultado da multiplicação é descartada. Elas são úteis apenas quando você tem certeza de que o resultado caberá no registrador de destino.
1. imul com Dois Operandos
imul destination, source
Esta forma multiplica destination por source e armazena o resultado (apenas a parte baixa) em destination.
destination: Registrador.source: Registrador, memória ou constante.
global _start
section .text
_start:
mov rdi, 3
imul rdi, 5 ; rdi = rdi * 5 => rdi = 15
mov rax, 60
syscall2. imul com Três Operandos
imul destination, source, constant
Esta forma multiplica source pela constant e armazena o resultado (apenas a parte baixa) em destination.
destination: Registrador.source: Registrador ou memória.constant: Valor imediato.
global _start
section .text
_start:
mov rdx, 3
imul rdi, rdx, 4 ; rdi = rdx * 4 => rdi = 12
mov rax, 60
syscallResumo
mulé usada para multiplicação de inteiros sem sinal e existe apenas na forma de um operando.imulé usada para multiplicação de inteiros com sinal e existe em três formas (um, dois e três operandos).- A forma de um operando é a única que preserva o resultado completo de
2nbits em um par de registradores (ex:RDX:RAX), sendo essencial para cálculos de alta precisão. - As formas de dois e três operandos de
imulsão mais convenientes e legíveis, mas descartam a parte alta do resultado, sendo seguras apenas quando não há risco de estouro. - Os flags
CFeOFsão ativados em todas as formas se o resultado da multiplicação exceder a capacidade do registrador de destino principal.