Arquitetura x86
O processador Intel x86 usa a arquitetura CISC (complex instruction set computer), o que significa que há um número modesto de registros para fins especiais em vez de grandes quantidades de registros de uso geral. Significa também que predominarão instruções complexas para fins especiais.
O processador x86 traça sua herança pelo menos tão longe quanto o processador Intel 8080 de 8 bits. Muitas peculiaridades no conjunto de instruções x86 são devido à retrocompatibilidade com esse processador (e com sua variante Zilog Z-80).
Microsoft Win32 usa o processador x86 em modo plano de 32 bits. Esta documentação se concentrará apenas no modo plano.
Registos
A arquitetura x86 consiste nos seguintes registros inteiros sem privilégios.
eax |
Acumulador |
ebx |
Registo de base |
ecx |
Registo de contador |
edx |
Registro de dados - pode ser usado para acesso à porta de E/S e funções aritméticas |
esi |
Registo do índice de origem |
edi |
Registo do índice de destino |
ebp |
Registo de ponteiro base |
esp |
Ponteiro de pilha |
Todos os registros inteiros são de 32 bits. No entanto, muitos deles têm sub-registros de 16 bits ou 8 bits.
machado |
Os 16 bits inferiores de eax |
bx |
Baixo 16 bits de ebx |
cx |
Os 16 bits inferiores de ecx |
dx |
Baixo 16 bits de edx |
si |
16 bits inferiores de esi |
di |
Baixo 16 bits de edi |
bp |
Baixo 16 bits de ebp |
sp |
Baixos 16 bits de esp |
al |
Os 8 bits inferiores de eax |
ah |
Parte alta dos 8 bits de ax |
bl |
Baixo 8 bits de ebx |
BH |
Alta de 8 bits de bx |
cl |
Baixo 8 bits de ecx |
ch |
8 bits superiores de cx |
dl |
Baixo 8 bits de edx |
dh |
8 bits superiores de dx |
A exploração num subregisto afeta apenas o subregisto e nenhuma das partes fora do subregisto. Por exemplo, armazenar no ax registro deixa os 16 bits altos do registro eax inalteradas.
Ao usar o ? (Avaliar expressão) comando, os registos devem ser precedidos de um sinal "at" ( @ ). Por exemplo, você deve usar ? @ax em vez de ? ax. Isso garante que o depurador reconheça ax como um registro em vez de um símbolo.
No entanto, o (@) não é necessário no comando
Dois outros registros são importantes para o estado atual do processador.
eip |
ponteiro de instrução |
bandeiras |
bandeiras |
O ponteiro de instrução é o endereço da instrução que está sendo executada.
O registo de sinalizadores é um conjunto de sinalizadores de um único bit. Muitas instruções alteram as bandeiras para descrever o resultado da instrução. Esses sinalizadores podem então ser testados por instruções de salto condicional. Consulte Flags x86 para obter detalhes.
Convenções de chamada
A arquitetura x86 tem várias convenções de chamada diferentes. Felizmente, todos eles seguem as mesmas regras de preservação de registro e retorno de função:
As funções devem preservar todos os registros, exceto eax, ecxe edx, que podem ser alterados através de uma chamada de função, e esp, que devem ser atualizados de acordo com a convenção de chamada.
O registro eax recebe valores de retorno de função se o resultado for de 32 bits ou menor. Se o resultado for de 64 bits, o resultado será armazenado no par edx:eax
.
A seguir está uma lista de convenções de chamada usadas na arquitetura x86:
Win32 (__stdcall)
Os parâmetros de função são passados na pilha, empurrados da direita para a esquerda, e o destinatário limpa a pilha.
Chamada de método C++ nativo (também conhecida como thiscall)
Os parâmetros de função são passados na pilha, empurrados da direita para a esquerda, o ponteiro "este" é passado no registro de ecx
e o destinatário limpa a pilha. COM (__stdcall para chamadas de método C++)
Os parâmetros da função são passados na pilha, empurrados da direita para a esquerda, em seguida, o ponteiro "este" é empurrado na pilha e, em seguida, a função é chamada. O destinatário limpa a pilha.
__fastcall
Os dois primeiros argumentos DWORD ou menores são passados nos registos ecx e edx. Os parâmetros restantes são passados sobre a pilha, empurrados da direita para a esquerda. O destinatário limpa a pilha.
__cdecl
Os parâmetros de função são passados na pilha, empurrados da direita para a esquerda, e o chamador limpa a pilha. A convenção de chamada __cdecl é usada para todas as funções com parâmetros de comprimento variável.
Exibição do depurador de registros e sinalizadores
Aqui está um exemplo de exibição de registro do depurador:
eax=00000000 ebx=008b6f00 ecx=01010101 edx=ffffffff esi=00000000 edi=00465000
eip=77f9d022 esp=05cffc48 ebp=05cffc54 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000286
Na depuração em modo de utilizador, pode-se ignorar o iopl e toda a última linha do ecrã do depurador.
Bandeiras x86
No exemplo anterior, os códigos de duas letras no final da segunda linha são indicadores. Estes são registradores de bit único e têm uma variedade de usos.
A tabela a seguir lista os sinalizadores x86:
Código da bandeira | Nome da bandeira | Valor | Estado da Bandeira | Descrição |
---|---|---|---|---|
de | Sinalizador de estouro | 0 1 | nvov | Sem transbordo - Transbordo |
df | Bandeira de direção | 0 1 | updn | Direção para cima - Direção para baixo |
se | Sinalizador de interrupção | 0 1 | diei | Interrupções desativadas - Interrupções ativadas |
sf | Sinalizar Bandeira | 0 1 | plng | Positivo (ou zero) - Negativo |
zf | Bandeira Zero | 0 1 | nzzr | Não zero - Zero |
af | Bandeira de transporte auxiliar | 0 1 | naac | Sem transporte auxiliar - Transporte auxiliar |
pf | Bandeira de paridade | 0 1 | pepo | Paridade ímpar - Paridade par |
cf | Bandeira de transporte | 0 1 | nccy | Sem transporte - Transportar |
TF | Bandeira de armadilha | Se tf for igual a 1, o processador gerará uma exceção STATUS_SINGLE_STEP após a execução de uma instrução. Esse sinalizador é usado por um depurador para implementar o rastreamento de etapa única. Não deve ser utilizado por outras aplicações. | ||
iopl | Nível de privilégio de E/S | Nível de privilégio de E/S Este é um inteiro de dois bits, com valores entre zero e 3. Ele é usado pelo sistema operacional para controlar o acesso ao hardware. Não deve ser utilizado por aplicações. |
Quando os registros são exibidos como resultado de algum comando na janela Comando do Depurador, é a de status do sinalizador
Na janela Registradores do WinDbg, o código do sinalizador é usado para exibir ou alterar sinalizadores. O status do sinalizador não é suportado.
Aqui está um exemplo. Na exibição de registro anterior, o status da bandeira ng aparece. Isso significa que o sinalizador de sinal está atualmente definido como 1. Para alterar isso, use o seguinte comando:
r sf=0
Isso define o sinalizador de sinal como zero. Se fizeres outra apresentação de registo, o código de estado ng não aparecerá. Em vez disso, o código de estado pl será exibido.
A Bandeira de Sinal, a Bandeira Zero e a Bandeira de Transporte são as bandeiras mais usadas.
Condições
Uma condição descreve o estado de um ou mais sinalizadores. Todas as operações condicionais no x86 são expressas em termos de condições.
O montador usa uma abreviatura de uma ou duas letras para representar uma condição. Uma condição pode ser representada por várias abreviaturas. Por exemplo, AE ("acima ou igual") é a mesma condição que NB ("não abaixo"). A tabela a seguir lista algumas condições comuns e seu significado.
Nome da condição | Bandeiras | Significado |
---|---|---|
Z |
ZF=1 |
O resultado da última operação foi zero. |
Nova Zelândia |
ZF=0 |
O resultado da última operação não foi zero. |
C |
CF=1 |
A última operação teve necessidade de uma retenção ou empréstimo. (Para inteiros não assinados, isso indica overflow.) |
NC |
CF=0 |
A última operação não exigiu um carry ou empréstimo. (Para inteiros não assinados, isso indica transbordo.) |
S |
SF=1 |
Resultado da última operação tem seu bit alto definido. |
NS |
SF=0 |
Resultado da última operação tem o seu bit superior desativado. |
O |
OF=1 |
Quando tratada como uma operação inteira assinada, a última operação causou um estouro ou subfluxo. |
NÃO |
OF=0 |
Quando tratada como operação inteira assinada, a última operação não causou um estouro ou subfluxo. |
As condições também podem ser usadas para comparar dois valores. A instrução cmp compara seus dois operandos e, em seguida, define sinalizadores como se subtraísse um operando do outro. As seguintes condições podem ser usadas para verificar o resultado de cmpvalue1, value2.
Nome da condição | Bandeiras | Ou seja, depois de uma operação CMP. |
---|---|---|
E |
ZF=1 |
valor1 == valor2. |
NE |
ZF=0 |
valor1 != valor2. |
GE Países Baixos | SF=OF |
valor1>= valor2. Os valores são tratados como inteiros assinados. |
LE NG | ZF=1 ou SF!=OF |
valor1<= valor2. Os valores são tratados como inteiros assinados. |
G NLE | ZF=0 e SF=OF |
valor1>valor2. Os valores são tratados como inteiros assinados. |
L NGE | SF!=OF |
valor1<valor2. Os valores são tratados como inteiros assinados. |
AE NB | CF=0 |
valor1>= valor2. Os valores são tratados como inteiros não assinados. |
SEJA NA | CF=1 ou ZF=1 |
valor1<= valor2. Os valores são tratados como inteiros não assinados. |
Um NBE | CF=0 e ZF=0 |
valor1>valor2. Os valores são tratados como inteiros não assinados. |
B NAE | CF=1 |
valor1<valor2. Os valores são tratados como inteiros não assinados. |
As condições são geralmente usadas para agir sobre o resultado de uma instrução cmp ou teste . Por exemplo
cmp eax, 5
jz equal
Compara o registo EAX com o número 5 calculando a expressão (EAX - 5) e definindo sinalizadores de acordo com o resultado. Se o resultado da subtração for zero, então o indicador zr será ativado, e a condição jz será verdadeira, então o salto será realizado.
Tipos de dados
Byte: 8 bits
Palavra: 16 bits
dword: 32 bits
qword: 64 bits (inclui duplos de ponto flutuante)
Tword: 80 bits (inclui duplos estendidos de ponto flutuante)
oword: 128 bits
Notação
A tabela a seguir indica a notação usada para descrever as instruções da linguagem assembly.
Notação | Significado |
---|---|
r, r1, r2... |
Registos |
m |
Endereço de memória (consulte a seção Modos de endereçamento subsequentes para obter mais informações.) |
#n |
Constante imediata |
r/m |
Registo ou memória |
r/#n |
Registo ou constante imediata |
r/m/#n |
Registro, memória ou constante imediata |
cc |
Um código de condição listado na seção Condições anterior. |
T |
"B", "W" ou "D" (byte, palavra ou dword) |
accT |
Tamanho T acumulador: al se T = "B", ax se T = "W", ou eax se T = "D" |
Modos de endereçamento
Existem vários modos de endereçamento diferentes, mas todos eles assumem a forma T ptr [expr], onde T é algum tipo de dados (consulte a seção Tipos de dados anterior) e expr é alguma expressão envolvendo constantes e registros.
A notação para a maioria dos modos pode ser deduzida sem muita dificuldade. Por exemplo, BYTE PTR [esi+edx*8+3] significa "pegue o valor do registro esi, adicione a ele oito vezes o valor do registro edx, adicione três e acesse o byte no endereço resultante."
Tubulação
O Pentium é dual-issue, o que significa que pode executar até duas ações em um ciclo de relógio. No entanto, as regras sobre quando é capaz de realizar duas ações ao mesmo tempo (conhecidas como emparelhamento ) são muito complicadas.
Como x86 é um processador CISC, você não precisa se preocupar com slots de atraso de salto.
Acesso sincronizado à memória
As instruções de carregamento, modificação e armazenamento podem receber o prefixo de bloqueio , que modifica a instrução da seguinte maneira:
Antes de emitir a instrução, a CPU liberará todas as operações de memória pendentes para garantir a coerência. Todos os pré-carregamentos de dados são cancelados.
Ao emitir a instrução, a CPU terá acesso exclusivo ao barramento. Isso garante a atomicidade da operação de carga/modificação/armazenamento.
A instrução xchg obedece automaticamente às regras anteriores sempre que troca um valor com a memória.
Todas as outras instruções, por padrão, são de não bloqueio.
Previsão de salto
Prevê-se que os saltos incondicionais sejam realizados.
Prevê-se que os saltos condicionais sejam tomados ou não, dependendo se foram tomados na última vez que foram executados. O cache para gravar o histórico de saltos é limitado em tamanho.
Se a CPU não tiver um registro de se o salto condicional foi feito ou não na última vez que foi executado, ele prevê saltos condicionais para trás como tomados e saltos condicionais para frente como não realizados.
Alinhamento
O processador x86 corrigirá automaticamente o acesso à memória não alinhada, o que resulta numa penalização de desempenho. Nenhuma exceção é levantada.
Um acesso à memória é considerado alinhado se o endereço for um múltiplo inteiro do tamanho do objeto. Por exemplo, todos os acessos BYTE estão alinhados (tudo é um múltiplo inteiro de 1), os acessos WORD a endereços pares estão alinhados e os endereços DWORD devem ser um múltiplo de 4 para serem alinhados.
O bloqueio prefixo não deve ser usado para acessos à memória desalinhados.