diff --git a/docs/opcodes.org b/docs/opcodes.org new file mode 100644 index 0000000..bc744cf --- /dev/null +++ b/docs/opcodes.org @@ -0,0 +1,277 @@ +* 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: + +#+begin_example +[prefixos][operação][operandos] +#+end_example + +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~ a ~r15~; +- Estender os campos da codificação de operandos. + +Formato: + +#+begin_example + ┌──────┬───┬───┬───┬───┐ +Bits │ 7-4 │ 3 │ 2 │ 1 │ 0 │ + ├──────┼───┼───┼───┼───┤ +Info │ 0100 │ W │ R │ X │ B │ + └──────┴───┴───┴───┴───┘ + │ │ │ └─── [B]ase extension + │ │ └─────── Inde[x] extension + │ └─────────── [R]egister extension + └─────────────── [W]ide +#+end_example + +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 bit ~W=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 + +#+begin_example + ┌─────┬───────┬───────┐ +Bits │ 7 6 │ 5 4 3 │ 2 1 0 │ + ├─────┼───────┼───────┤ +Info │ mod │ reg │ r/m │ + └─────┴───────┴───────┘ +#+end_example + +*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~ a ~111~ representam registradores quando ~mod=11~. +- No modo de 8 bits, os registradores ~spl~, ~bpl~, ~sil~ e ~dil~ só são válidos + com prefixo REX. Sem REX, ~100~ a ~111~ correspondem a ~ah~, ~ch~, ~dh~ e ~bh~. + +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~ e ~r/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~ e ~SIB~ (se houver). +- No caso especial de ~mod = 00~ e ~r/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: + +#+begin_example +[base + index × scale + displacement] +#+end_example + +Ele aparece obrigatoriamente após o byte ModR/M quando o campo ~mod != ~11~ +(o operando é memória) e o campo ~r/m = 100~. + +#+begin_example + ┌───────┬───────┬───────┐ +Bits │ 7 6 │ 5 4 3 │ 2 1 0 │ + ├───────┼───────┼───────┤ +Info │ scale │ index │ base │ + └───────┴───────┴───────┘ +#+end_example + +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~ e ~r/m~. +- Se ~index = 100~ (~rsp~), o campo ~index~ é suprimido e o termo + ~index × 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: + +#+begin_example +[opcode] /n [outros bytes...] +#+end_example + +Onde: + +- ~opcode~: byte base do grupo +- ~/n~: valor esperado no campo ~reg~ do byte ModR/M (de ~000~ a ~111~) + +Se o valor do campo ~reg~ não for o esperado, a instrução será inválida +ou interpretada incorretamente.