Compare commits

..

6 commits
main ... main

5 changed files with 488 additions and 44 deletions

View file

@ -80,40 +80,3 @@ As datas e tópicos podem mudar de acordo com o desenrolar do curso!
Ao final do curso, será fornecido um certificado de conclusão mediante a Ao final do curso, será fornecido um certificado de conclusão mediante a
apresentação de um projeto final. apresentação de um projeto final.
* Inscrições e forma de pagamento
- *Inscrições:* até 27 de junho de 2025
- *Valor parcelado:* R$300,00 (em até 6x)
- *À vista no PIX:* R$270,00 (10% de desconto)
* Como se inscrever
** 1. Realize o pagamento pela forma escolhida
- *PIX (com desconto):* cursos@blauaraujo.com
- *Parcelamentos:* [[https://link.picpay.com/p/17500877656850385526c47][link de pagamento (PicPay)]]
** 2. Envie um e-mail com seus dados
*Destinatário:* cursos@blauaraujo.com
*Informações (obrigatórias):*
- Comprovante de pagamento
- Nome completo
- E-mail de contato
- CPF ou CNPJ
- Endereço postal completo com CEP
Esses dados são necessários para a emissão da nota fiscal.
** 3. Aguarde a resposta
Seu e-mail será respondido em até 24h para confirmar sua inscrição e dar algumas
informações sobre o início das aulas. Você também receberá a nota fiscal eletrônica
no e-mail informado.
* Outras informações e casos especiais
Envie um e-mail para cursos@blauaraujo.com ou me procure no [[https://t.me/blau_araujo][Telegram]].

View file

@ -6,7 +6,11 @@
* 0 -- Introdução à linguagem Assembly (NASM) * 0 -- Introdução à linguagem Assembly (NASM)
[[./aula-00.org][Texto]] | [[#][Vídeo]] - [[./aula-00.org][Texto]]
- [[https://youtu.be/NsFUbSoz12c][Vídeo #0: Sobre o curso]]
- [[https://youtu.be/LUuNAMezDOM][Vídeo #1: O que é Assembly]]
*Conteúdo:*
- O que é Assembly - O que é Assembly
- Linguagem dependente da arquitetura - Linguagem dependente da arquitetura
@ -21,7 +25,10 @@
* 1 -- Arquitetura de computadores * 1 -- Arquitetura de computadores
[[./aula-01.org][Texto]] | [[#][Vídeo]] - [[./aula-01.org][Texto]]
- [[https://youtu.be/nW1OrIisvV0][Vídeo #2: Arquiteturas]]
*Conteúdo:*
- Modelo de von Neumann - Modelo de von Neumann
- Arquiteturas x86 - Arquiteturas x86

View file

@ -4,6 +4,11 @@
#+options: toc:3 #+options: toc:3
*Vídeos relacionados:*
- [[https://youtu.be/NsFUbSoz12c][Sobre o curso]]
- [[https://youtu.be/LUuNAMezDOM][O que é Assembly]]
* Objetivos * Objetivos
- Apresentar as características e os elementos básicos da linguagem Assembly. - Apresentar as características e os elementos básicos da linguagem Assembly.
@ -12,6 +17,110 @@
- Aprender as instruções e diretivas essenciais para começar a programar. - Aprender as instruções e diretivas essenciais para começar a programar.
- Criar executar um primeiro programa em Assembly. - Criar executar um primeiro programa em Assembly.
* Máquina de Turing
No começo do século 20, surge uma questão no campo da matemática: até onde
seria possível solucionar problemas seguindo estritamente um conjunto de
regras fixas estabelecidas em um /algoritmo/?
#+begin_quote
*Nota:* em matemática, /algoritmos/ são sequências finitas de ações executáveis
que visam obter soluções para determinados tipos de problema.
#+end_quote
Nos anos 1930, o matemático Alan Turing estava trabalhando em um problema de
lógica formal (campo que estuda a estrutura de enunciados e suas regras). Como
parte do que estava tentando demonstrar, ele precisava encontrar uma forma
geral para descrever como algoritmos poderiam ser implementados e executados
mecanicamente.
A partir disso, ele formulou o conceito matemático de uma máquina abstrata que
manipularia automaticamente os símbolos em uma fita de acordo com uma tabela
de regras: a sua /a-machine/ (de /"automatic machine"/), mais tarde chamada de
/Máquina de Turing/ por seu orientador de doutorado -- termo que utilizamos
até hoje.
Então, a Máquina de Turing é um modelo matemático que descreve um dispositivo
muito simples que seria capaz de expressar computações arbitrárias e, com
isso, explorar as propriedades e as limitações da computação mecânica.
#+begin_quote
*Nota:* /modelos matemáticos computacionais/ são modelos que descrevem como a
saída de uma função matemática é computada a partir de uma dada entrada.
#+end_quote
Turing descreveu a imagem física de sua máquina como consistindo de...
- Uma fita dividida em células contendo símbolos ou espaços vazios;
- Um cabeçote para ler e escrever símbolos na fita;
- Um registrador de estados, para armazenar o estado corrente de um conjunto
finito de estados possíveis;
- Uma tabela de instruções que, dado o estado atual e o que estiver sendo
lido na fita, diz à máquina o que fazer na sequência.
Por exemplo:
#+begin_example
Estado atual: q1
Fita:
┌───┬───┬───┬───┬───┬───┬───┐
│ ␣ │ 1 │ 1 │ 0 │ ␣ │ ␣ │ ␣ │
...└───┴───┴───┴───┴───┴───┴───┘...
Cabeçote
Regra:
Se (q1, 0) -> escreva 1, mova para a direita, mude estado para q2
#+end_example
*Ilustração moderna:* [[https://www.youtube.com/watch?v=E3keLeMwfHY][vídeo de Mike Davey]]
Esse modelo simples, apesar de abstrato, revelou-se poderoso o suficiente para
representar qualquer cálculo que possa ser feito por um algoritmo. Contudo,
a computação sequencial das células de uma fita é lenta demais para aplicações
práticas modernas. Mesmo assim, a máquina que Turing descreveu tornou-se uma
das principais inspirações para a construção das /unidades de processamento/,
utilizadas nos computadores até os nossos dias.
** Uma longa fita de bits
Na computação moderna, os nossos programas ainda podem ser vistos como uma
fita contendo uma longa sequência de bits (valores binários =0= e =1=). Mas,
diferente da máquina de Turing:
- Os bits são armazenados na memória principal do computador, onde podem ser
acessados de forma aleatória e quase instantânea;
- Em vez de símbolos individuais por célula e uma tabela de estados, os bits
são interpretados em blocos de tamanho fixo, expressando as instruções
que a CPU deve executar e os dados envolvidos nessas operações;
- A tabela de estados agora está incorporada nos processadores na forma de
um conjunto fixo de instruções específico de cada CPU, conhecido como ISA
(/Instruction Set Architecture/).
Portanto, /escrever um programa/ significa, em essência, representar, nessa
longa fita de bits, os códigos relativos aos dados e instruções que uma
máquina em particular será capaz de interpretar e executar. Isso é o que
chamamos de /código de máquina/ (ou, popularmente, /linguagem de máquina/) e,
em vários momentos da história da computação, os programas foram escritos
desta forma.
Obviamente, escrever diretamente o código de máquina é um processo demorado,
tedioso e propenso a erros. Por isso, já nos anos 1950, começaram a ser
criados programas dedicados à tradução de representações simbólicas de
instruções para as representações binárias do código de máquina. Na prática,
o que esses programas fazem é utilizar as representações simbólicas como um
"manual de instruções" para montar o código de máquina correspondente -- daí
serem chamados de /montadores/ (ou /assemblers/, em inglês).
Vale observar que, inicialmente, essas representações simbólicas não eram
escritas em arquivos de texto como fazemos hoje. Em geral, os códigos das
instruções e dados eram registrados fisicamente, em fitas ou cartões
perfurados, que depois eram lidos por máquinas. Assim, uma /linguagem de
montagem/ (/assembly language/) já existia, mas ainda se passava por uma longa
fase onde o processo de escrita de programas era manual e bastante sujeito
a erros mecânicos e operacionais.
* O que é Assembly * O que é Assembly
Assembly é uma linguagem de programação que oferece formas de representar, em Assembly é uma linguagem de programação que oferece formas de representar, em
@ -162,7 +271,7 @@ quantos bytes quisermos, em qualquer endereço, desde que tenhamos permissão
do sistema. do sistema.
#+end_quote #+end_quote
** Por que aprender Assembly? * Por que aprender Assembly?
*** Entendimento do funcionamento de computadores *** Entendimento do funcionamento de computadores
@ -434,7 +543,7 @@ Quando escrevemos programas em NASM que serão ligados com outros módulos,
como bibliotecas externas ou arquivos objeto, nós precisamos informar ao como bibliotecas externas ou arquivos objeto, nós precisamos informar ao
editor de ligações quais símbolos devem ser /exportados/ para esses módulos editor de ligações quais símbolos devem ser /exportados/ para esses módulos
e quais devem ser /importados/ de outros lugares. No NASM, isso é feito com e quais devem ser /importados/ de outros lugares. No NASM, isso é feito com
as diretivas =global= (importação) e =extern= (exportação). as diretivas =global= (exportação) e =extern= (importação).
No GNU/Linux, especialmente quando não usamos uma linguagem de mais alto No GNU/Linux, especialmente quando não usamos uma linguagem de mais alto
nível (como C), nós precisamos especificar manualmente o ponto de entrada nível (como C), nós precisamos especificar manualmente o ponto de entrada
@ -738,7 +847,7 @@ imul destino, origem (resultado em destino)
imul destino, origem, imediato (resultado em destino) imul destino, origem, imediato (resultado em destino)
#+end_example #+end_example
- Multiplica =origem= pelo valor de =imediato= e armazena o resultado em =origem=. - Multiplica =origem= pelo valor de =imediato= e armazena o resultado em =destino=.
- Se o resultado exceder a capacidade de =destino=, apenas os bytes mais - Se o resultado exceder a capacidade de =destino=, apenas os bytes mais
baixos serão armazenados (não há extensão de registradores). baixos serão armazenados (não há extensão de registradores).
- Afeta as flags =CF= e =OF= no caso de estouro da capacidade de =destino=. - Afeta as flags =CF= e =OF= no caso de estouro da capacidade de =destino=.
@ -1249,19 +1358,20 @@ instalados no seu sistema:
- Um editor de código da sua preferência (Vim, Emacs, Geany, etc) - Um editor de código da sua preferência (Vim, Emacs, Geany, etc)
- =git= - =git=
- =nasm= - =nasm= e =ndisasm=
- =gcc= - =gcc=
- =as= - =as=
- =ld= - =ld=
- =gdb= - =gdb=
- =readelf= - =readelf=
- =objdump= - =objdump=
- =objcopy=
- =nm= - =nm=
- =ldd= - =ldd=
- =hexedit= - =hexedit=
- =xxd= (geralmente instalado com o editor Vim) - =xxd= (geralmente instalado com o editor Vim)
No Debian, a maioria deles é instalada com os comandos: No Debian e derivados, a maioria deles é instalada com os comandos:
#+begin_example #+begin_example
sudo apt update sudo apt update

277
docs/opcodes.org Normal file
View file

@ -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 76) representa o fator de multiplicação:
| Bits | Fator |
|------+-------|
| ~00~ | ~1~ |
| ~01~ | ~2~ |
| ~10~ | ~4~ |
| ~11~ | ~8~ |
O campo *index* (bits 53) 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 20) 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 53), 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.

87
docs/prefixo-rex.org Normal file
View file

@ -0,0 +1,87 @@
* Prefixos REX na arquitetura Intel 64 (x86-64)
O prefixo REX é um byte opcional usado no modo 64 bits para:
- Acessar registradores estendidos (~r8r15~)
- Usar operandos de ~64~ bits
- Estender campos ~ModR/M~, ~SIB~ ou ~opcode~
** Formato geral do prefixo REX (1 byte)
#+begin_example
Bits: 7 6 5 4 | 3 | 2 | 1 | 0
0 1 0 0 W R X B
#+end_example
| Bit | Nome | Função |
|-----+------+--------------------------------------------------------------------|
| ~74~ | ~0100~ | Identifica o byte como prefixo REX |
| ~3~ | ~W~ | ~1~ = operandos de ~64~ bits |
| ~2~ | ~R~ | Estende o campo ~Reg~ no byte ~ModR/M~ |
| ~1~ | ~X~ | Estende o campo ~Index~ no byte ~SIB~ |
| ~0~ | ~B~ | Estende o campo ~Base~ no ~ModR/M~, ~SIB~ ou campo ~reg~ em certos opcodes |
** Tabela dos 16 prefixos REX possíveis (~0x400x4F~)
| Hex | Binário | ~W~ | ~R~ | ~X~ | ~B~ | Significado resumido |
|------+----------+---+---+---+---+-------------------------------------------------|
| ~0x40~ | ~01000000~ | ~0~ | ~0~ | ~0~ | ~0~ | Sem extensão nem ~64~-bit |
| ~0x41~ | ~01000001~ | ~0~ | ~0~ | ~0~ | ~1~ | ~B = 1~ (extensão do campo Base) |
| ~0x42~ | ~01000010~ | ~0~ | ~0~ | ~1~ | ~0~ | ~X = 1~ (extensão do campo Index) |
| ~0x43~ | ~01000011~ | ~0~ | ~0~ | ~1~ | ~1~ | ~X = 1~, ~B = 1~ |
| ~0x44~ | ~01000100~ | ~0~ | ~1~ | ~0~ | ~0~ | ~R = 1~ (extensão do campo Reg) |
| ~0x45~ | ~01000101~ | ~0~ | ~1~ | ~0~ | ~1~ | ~R = 1~, ~B = 1~ |
| ~0x46~ | ~01000110~ | ~0~ | ~1~ | ~1~ | ~0~ | ~R = 1~, ~X = 1~ |
| ~0x47~ | ~01000111~ | ~0~ | ~1~ | ~1~ | ~1~ | ~R = 1~, ~X = 1~, ~B = 1~ |
| ~0x48~ | ~01001000~ | ~1~ | ~0~ | ~0~ | ~0~ | ~W = 1~ (operandos de ~64~ bits) |
| ~0x49~ | ~01001001~ | ~1~ | ~0~ | ~0~ | ~1~ | ~W = 1~, ~B = 1~ |
| ~0x4A~ | ~01001010~ | ~1~ | ~0~ | ~1~ | ~0~ | ~W = 1~, ~X = 1~ |
| ~0x4B~ | ~01001011~ | ~1~ | ~0~ | ~1~ | ~1~ | ~W = 1~, ~X = 1~, ~B = 1~ |
| ~0x4C~ | ~01001100~ | ~1~ | ~1~ | ~0~ | ~0~ | ~W = 1~, ~R = 1~ |
| ~0x4D~ | ~01001101~ | ~1~ | ~1~ | ~0~ | ~1~ | ~W = 1~, ~R = 1~, ~B = 1~ |
| ~0x4E~ | ~01001110~ | ~1~ | ~1~ | ~1~ | ~0~ | ~W = 1~, ~R = 1~, ~X = 1~ |
| ~0x4F~ | ~01001111~ | ~1~ | ~1~ | ~1~ | ~1~ | ~W = 1~, ~R = 1~, ~X = 1~, ~B = 1~ (todas as extensões) |
** Observações
- O prefixo REX só é válido no modo 64 bits.
- Pode ser necessário mesmo com operandos de 32 bits se registradores estendidos
forem usados.
** Exemplo de desmontagem
#+begin_example
48 89 c8 => mov rax, rcx
#+end_example
Nesse caso:
- ~48~: É o prefixo REX com ~W = 1~, ~R = 0~, ~X = 0~, ~B = 0~ (operandos de 64 bits).
- ~89~: OpCode ~mov r/m, reg~ (como primeiro operando é ~r/m~, temos que ver o byte ModR/M).
- ~c8~: Byte ModR/M, onde...
#+begin_example
Bits: 7 8 | 5 4 3 | 2 1 0
modo | origem | destino
c8 → 11 | 001 | 000
regs | rcx | rax
#+end_example
Portanto... ~48 89 c8 => mov rax, rcx~
** Tabela de registradores no byte ModR/M
O modo ~11~ indica que os dois operandos estão em registradores. Os 3 bits
dos campos ~reg~ e ~r/m~ indicam os registradores, com ou sem extensão via
prefixo ~REX~.
| Código | Reg. | ~REX.R~ ou ~REX.B~ |
|--------+------+----------------|
| ~000~ | ~rax~ | ~r8~ |
| ~001~ | ~rcx~ | ~r9~ |
| ~010~ | ~rdx~ | ~r10~ |
| ~011~ | ~rbx~ | ~r11~ |
| ~100~ | ~rsp~ | ~r12~ |
| ~101~ | ~rbp~ | ~r13~ |
| ~110~ | ~rsi~ | ~r14~ |
| ~111~ | ~rdi~ | ~r15~ |