9.9 KiB
Códigos de operação
A passagem de operandos nas instruções pode envolver:
- Registradores;
- Endereços de memória;
- Valores imediatos.
Quando consultamos o manual, dos códigos de operações, pode ser muito difícil entender o que utilizar e de que forma. Por isso, precisamos conhecer alguns conceitos básicos sobre a formação dos OpCodes no nível do código de máquina.
De modo simplificado, podemos dizer que os bytes dos códigos de operação da arquitetura Intel x86_64 seguem este formato:
[prefixos][operação][operandos]
Dentre as várias possibilidades, nós temos:
- Prefixo REX, para habilitar operações de 64 bits.
- Prefixo de tamanho de operandos (
0x66
), para alterar o tamanho padrão dos operandos. - Prefixo de tamanho de endereços (
0x67
), para possibilitar o uso de endereços de tamanhos diferentes do padrão. - Codificação de operandos com o byte ModR/M.
- Codificação de endereços efetivos com o byte SIB.
Prefixo REX
Byte opcional da arquitetura x86_64 para:
- Habilitar operandos de 64 bits;
- Habilitar uso dos registradores
r8
ar15
; - Estender os campos da codificação de operandos.
Formato:
┌──────┬───┬───┬───┬───┐ Bits │ 7-4 │ 3 │ 2 │ 1 │ 0 │ ├──────┼───┼───┼───┼───┤ Info │ 0100 │ W │ R │ X │ B │ └──────┴───┴───┴───┴───┘ │ │ │ └─── [B]ase extension │ │ └─────── Inde[x] extension │ └─────────── [R]egister extension └─────────────── [W]ide
Onde:
Bit | Função |
---|---|
0100 | Identifica o byte como prefixo REX |
W | Usa operandos de 64 bits |
R | Bit mais significativo para o campo reg do ModR/M |
X | Bit mais significativo para o campo index do byte SIB |
B | Bit mais significativo para o campo r/m (ModR/M) ou base (SIB) |
Nota: os bits R, X e B acrescentam 1 bit (mais significativo) aos 3 bits dos campos relacionados, ampliando de 8 para 16 os valores possíveis.
Prefixo de tamanho de operando (operand-size override)
No conjunto de instruções x86, o tamanho dos operandos pode ser
alterado por um prefixo especial com valor hexadecimal 0x66
. Esse
prefixo é utilizado para substituir o tamanho padrão dos operandos
da instrução de acordo com o modo de operação (16, 32 ou 64 bits).
Efeito do prefixo 0x66
:
Modo | Sem prefixo | Com prefixo |
---|---|---|
16 bits | 16 bits | 32 bits |
32 bits | 32 bits | 16 bits |
64 bits | 32 bits | 16 bits |
Notas:
- No modo 64 bits, o prefixo
0x66
não ativa operandos de 64 bits; para isso, utiliza-se o prefixo REX com o bitW=1
. - O prefixo
0x66
é necessário para instruções que operam sobre registradores de 16 bits (ax
,cx
,r8w
, etc.), pois o tamanho padrão no modo 64 é de 32 bits.
Prefixo de tamanho de endereço (address-size override)
O prefixo 0x67
altera o tamanho do cálculo de endereço, permitindo
usar endereços de 16 ou 32 bits em modos onde estes não seriam o
padrão.
- No modo 64 bits, permite usar endereçamento de 32 bits;
- No modo 32 bits, permite usar endereçamento de 16 bits;
- No modo 16 bits, permite endereçamento de 32 bits.
Codificação de operandos
Em algumas situações, os operandos podem ser codificados como:
- Byte ModR/M
- Deslocamento (displacement)
- Byte SIB
ModR/M
┌─────┬───────┬───────┐ Bits │ 7 6 │ 5 4 3 │ 2 1 0 │ ├─────┼───────┼───────┤ Info │ mod │ reg │ r/m │ └─────┴───────┴───────┘
Nota: a ordem de operandos de origem e destino varia com a instrução.
mod | Significado |
---|---|
00 |
Endereçamento indireto sem deslocamento |
01 |
Endereçamento indireto com deslocamento de 8 bits |
10 |
Endereçamento indireto com deslocamento de 32 bits |
11 |
Campo r/m é um registrador |
Se campos reg
e r/m
forem registradores (mod = 11
):
bits | 8 bits | 16 bits | 32 bits | 64 bits |
---|---|---|---|---|
000 |
al |
ax |
eax |
rax |
001 |
cl |
cx |
ecx |
rcx |
010 |
dl |
dx |
edx |
rdx |
011 |
bl |
bx |
ebx |
rbx |
100 |
ah / spl |
sp |
esp |
rsp |
101 |
ch / bpl |
bp |
ebp |
rbp |
110 |
dh / sil |
si |
esi |
rsi |
111 |
bh / dil |
di |
edi |
rdi |
Notas:
- Os valores binários de
000
a111
representam registradores quandomod=11
. - No modo de 8 bits, os registradores
spl
,bpl
,sil
edil
só são válidos com prefixo REX. Sem REX,100
a111
correspondem aah
,ch
,dh
ebh
.
Se o campo r/m
for memória (mod != 11
):
r/m | mod 00 |
mod diferente de 00 |
---|---|---|
000 |
[rax] |
[rax + disp8/disp32] |
001 |
[rcx] |
[rcx + disp8/disp32] |
010 |
[rdx] |
[rdx + disp8/disp32] |
011 |
[rbx] |
[rbx + disp8/disp32] |
100 |
SIB obrigatório | SIB obrigatório |
101 |
[disp32] |
[rbp + disp8/disp32] |
110 |
[rsi] |
[rsi + disp8/disp32] |
111 |
[rdi] |
[rdi + disp8/disp32] |
Notas:
- Quando
r/m = 100
, o byte seguinte é um byte SIB. - Com
mod = 00
er/m = 101
, não há um registrador base e o dado seguinte é um deslocamento escrito em 4 bytes.
Deslocamento (displacement)
O deslocamento é um valor constante (signed) adicionado ao endereço base
calculado a partir dos campos mod
e r/m
(e opcionalmente do byte SIB) para
obter o endereço efetivo de memória.
O tipo e o tamanho do deslocamento são determinados pelo valor de mod
:
mod | Tipo | Tamanho | Observação |
---|---|---|---|
00 |
Nenhum | 0 | Com r/m = 101 , há disp32 absoluto |
01 |
disp8 |
1 byte (8 bits) | Sempre somado ao endereço base |
10 |
disp32 |
4 bytes (32 bits) | Sempre somado ao endereço base |
Notas:
- O deslocamento é armazenado logo após o byte ModR/M ou após o byte SIB, se houver.
-
Ele é com sinal, ou seja:
- Valores positivos aumentam o endereço base;
- Valores negativos o reduzem.
- O deslocamento é sempre somado ao endereço base calculado pelos campos
r/m
eSIB
(se houver). - No caso especial de
mod = 00
er/m = 101
, o deslocamento de 32 bits é o próprio endereço efetivo, sem registrador base.
Byte SIB
O byte SIB (Scale-Index-Base) é usado para formar endereços de memória mais complexos, da forma:
[base + index × scale + displacement]
Ele aparece obrigatoriamente após o byte ModR/M quando o campo mod != ~11
(o operando é memória) e o campo r/m = 100
.
┌───────┬───────┬───────┐ Bits │ 7 6 │ 5 4 3 │ 2 1 0 │ ├───────┼───────┼───────┤ Info │ scale │ index │ base │ └───────┴───────┴───────┘
O campo scale (bits 7–6) representa o fator de multiplicação:
Bits | Fator |
---|---|
00 |
1 |
01 |
2 |
10 |
4 |
11 |
8 |
O campo index (bits 5–3) determina o registrador que contém valor que será multiplicado pela escala:
Bits | index |
---|---|
000 |
rax |
001 |
rcx |
010 |
rdx |
011 |
rbx |
100 |
(suprimido) |
101 |
rbp |
110 |
rsi |
111 |
rdi |
Notas:
- O campo index também é codificado como registrador, como os
campos
reg
er/m
. - Se
index = 100
(rsp
), o campoindex
é suprimido e o termoindex × scale
é ignorado.
O campo base (bits 2–0) representa o registrador que contém o endereço base para o cálculo do endereço efetivo:
Bits | base |
---|---|
000 |
rax |
001 |
rcx |
010 |
rdx |
011 |
rbx |
100 |
rsp |
101 |
rbp ou disp32 |
110 |
rsi |
111 |
rdi |
Nota: se base = 101
e mod = 00
, não há registrador base e os 4 bytes
seguintes são um disp32
(endereço absoluto).
Grupos de Instruções (Instruction Groups)
Alguns opcodes na arquitetura x86/x86_64 não definem uma única instrução,
mas sim um grupo de instruções. Esses grupos utilizam um único byte de
opcode base e o campo reg
, do byte ModR/M (bits 5–3), como uma extensão
para determinar a operação específica. Deste modo, é possível agrupar
até 8 instruções diferentes com um mesmo opcode.
Formato geral:
[opcode] /n [outros bytes...]
Onde:
opcode
: byte base do grupo/n
: valor esperado no camporeg
do byte ModR/M (de000
a111
)
Se o valor do campo reg
não for o esperado, a instrução será inválida
ou interpretada incorretamente.