Architettura x86

Il processore Intel x86 usa un'architettura CISC (Complex Instruction Set Computer), che significa che esiste un numero modesto di registri speciali anziché grandi quantità di registri per utilizzo generico. Significa anche che le istruzioni speciali complesse saranno predominanti.

Il processore x86 traccia il suo patrimonio almeno fino al processore Intel 8080 a 8 bit. Molte peculiarità nel set di istruzioni x86 sono dovute alla compatibilità con le versioni precedenti con tale processore (e con la sua variante Zilog Z-80).

Microsoft Win32 usa il processore x86 in modalità flat a 32 bit. Questa documentazione si concentra solo sulla modalità flat.

Registri

L'architettura x86 è costituita dai registri interi senza privilegi seguenti.

Eax

Accumulatore

Ebx

Registro di base

ecx

Registro contatori

Edx

Registro dati: può essere usato per l'accesso alle porte di I/O e per le funzioni aritmetiche

Esi

Registro degli indici di origine

Edi

Registro degli indici di destinazione

Ebp

Registro puntatore di base

Spagnolo

Puntatore dello stack

Tutti i registri interi sono a 32 bit. Tuttavia, molti di essi hanno sottoregisti a 16 bit o a 8 bit.

Ax

Basso 16 bit di eax

Bx

Basso 16 bit di ebx

Cx

Basso 16 bit di ecx

Dx

Basso 16 bit di edx

si

Basso 16 bit di esi

di

Basso 16 bit di edi

bp

Basso 16 bit di ebp

sp

Basso 16 bit di esp

al

Basso 8 bit di eax

Ah

Alto 8 bit di ax

Beato

Basso 8 bit di ebx

Bh

Alto 8 bit di bx

cl

Basso 8 bit di ecx

Ch

Alto 8 bit di cx

Dl

Basso 8 bit di edx

Dh

Alto 8 bit di dx

Il funzionamento in una sottoregister influisce solo sulla sottoregister e nessuno delle parti esterne alla sottoregister. Ad esempio, l'archiviazione nel registro ax lascia invariati i 16 bit elevati del registro eax .

Quando si usa ? (Valuta espressione) comando, i registri devono essere preceduti da un segno "at" ( @ ). Ad esempio, è consigliabile usare ? @ax anziché ? ax. Ciò garantisce che il debugger riconosca l'ax come registro anziché un simbolo.

Tuttavia, il comando r (@) non è obbligatorio nel comando r (Registri). Ad esempio, r ax=5 verrà sempre interpretato correttamente.

Due altri registri sono importanti per lo stato corrente del processore.

Eip

puntatore alle istruzioni

flags

flags

Il puntatore all'istruzione è l'indirizzo dell'istruzione eseguita.

Il registro flag è una raccolta di flag a bit singolo. Molte istruzioni modificano i flag per descrivere il risultato dell'istruzione. Questi flag possono quindi essere testati dalle istruzioni di salto condizionale. Per informazioni dettagliate, vedere Flag x86 .

Convenzioni di chiamata

L'architettura x86 presenta diverse convenzioni di chiamata. Fortunatamente, seguono tutte le stesse regole di conservazione e restituzione delle funzioni del registro:

  • Le funzioni devono mantenere tutti i registri, ad eccezione di eax, ecx edx, che possono essere modificati in una chiamata di funzione e esp, che devono essere aggiornati in base alla convenzione chiamante.

  • Il registro eax riceve valori restituiti dalla funzione se il risultato è 32 bit o più piccolo. Se il risultato è a 64 bit, il risultato viene archiviato nella coppia edx:eax .

Di seguito è riportato un elenco di convenzioni di chiamata usate nell'architettura x86:

  • Win32 (__stdcall)

    I parametri della funzione vengono passati nello stack, spostati a destra a sinistra e il chiamante pulisce lo stack.

  • Chiamata al metodo Native C++ (nota anche come thiscall)

    I parametri della funzione vengono passati nello stack, premuti a destra a sinistra, il puntatore "this" viene passato nel registro ecx e il chiamato pulisce lo stack.

  • COM (__stdcall per le chiamate al metodo C++)

    I parametri della funzione vengono passati nello stack, premuti a destra a sinistra, quindi il puntatore "questo" viene eseguito il push nello stack e quindi viene chiamata la funzione. Il chiamato esegue la pulizia dello stack.

  • __fastcall

    I primi due argomenti DWORD o più piccoli vengono passati nei registri ecx edx. I parametri rimanenti vengono passati nello stack, premuti a destra a sinistra. Il chiamato esegue la pulizia dello stack.

  • __cdecl

    I parametri della funzione vengono passati nello stack, premuti a destra a sinistra e il chiamante pulisce lo stack. La convenzione di chiamata __cdecl viene usata per tutte le funzioni con parametri a lunghezza variabile.

Visualizzazione debugger di registri e flag

Ecco una visualizzazione del registro del debugger di esempio:

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

Nel debug in modalità utente è possibile ignorare l'iopl e l'intera ultima riga della visualizzazione del debugger.

Flag x86

Nell'esempio precedente, i codici a due lettere alla fine della seconda riga sono flag. Questi sono registri a bit singolo e hanno un'ampia gamma di usi.

Nella tabella seguente sono elencati i flag x86:

Contrassegno codice Nome flag Valore Stato flag Descrizione
di Overflow Flag 0 1 nvov Nessun overflow - Overflow
Df Flag di direzione 0 1 updn Direzione verso l'alto - Direzione verso il basso
if Flag di interruzione 0 1 diei Interruzioni disabilitate - Interruzioni abilitate
Sf Flag di firma 0 1 plng Positivo (o zero) - Negativo
Zf Contrassegno zero 0 1 nzzr Non zero - Zero
Af Flag di trasporto ausiliario 0 1 naac Nessun trasporto ausiliario - Trasporto ausiliario
Pf Flag di parità 0 1 Pepo Parità di parità - Parità anche
Cfr Flag di trasporto 0 1 nccy Nessun trasporto - Trasporto
Tf Trap Flag Se tf è uguale a 1, il processore genererà un'eccezione STATUS_SINGLE_STEP dopo l'esecuzione di un'istruzione. Questo flag viene usato da un debugger per implementare la traccia in singolo passaggio. Non deve essere usato da altre applicazioni.
Iopl Livello di privilegi di I/O Livello privilegi I/O Questo è un intero a due bit, con valori compresi tra zero e 3. Viene usato dal sistema operativo per controllare l'accesso all'hardware. Non deve essere usato dalle applicazioni.

Quando i registri vengono visualizzati come risultato di un comando nella finestra comando debugger, è lo stato del flag visualizzato. Tuttavia, se si vuole modificare un flag usando il comando r (Registers), è consigliabile farvi riferimento dal codice del flag.

Nella finestra Registri di WinDbg viene usato il codice del flag per visualizzare o modificare i flag. Lo stato del flag non è supportato.

Ecco un esempio. Nella visualizzazione del registro precedente viene visualizzato lo stato del flag. Ciò significa che il flag di firma è attualmente impostato su 1. Per modificare questa operazione, usare il comando seguente:

r sf=0

Questo imposta il flag di segno su zero. Se si esegue un'altra visualizzazione del registro, il codice di stato ng non verrà visualizzato. Verrà invece visualizzato il codice di stato pl .

Il flag di firma, il flag zero e il flag di trasporto sono i flag più comunemente usati.

Condizioni

Una condizione descrive lo stato di uno o più flag. Tutte le operazioni condizionali sulla x86 vengono espresse in termini di condizioni.

L'assembler usa un'abbreviazione di una o due lettere per rappresentare una condizione. Una condizione può essere rappresentata da più abbreviazioni. Ad esempio, AE ("sopra o uguale") è la stessa condizione di NB ("not sotto"). Nella tabella seguente sono elencate alcune condizioni comuni e il relativo significato.

Nome condizione Flags Significato

Z

ZF=1

Il risultato dell'ultima operazione era zero.

NZ

ZF=0

Il risultato dell'ultima operazione non era zero.

C

CF=1

L'ultima operazione richiedeva un trasporto o un prestito. Per i numeri interi senza segno, questo indica overflow.

NC

CF=0

L'ultima operazione non richiedeva un trasporto o un prestito. Per gli interi senza segno, questo indica l'overflow.

S

SF=1

Il risultato dell'ultima operazione ha un set di bit elevato.

NS

SF=0

Il risultato dell'ultima operazione ha un bit elevato chiaro.

O

OF=1

Se trattato come operazione con segno, l'ultima operazione ha causato un overflow o un underflow.

NO

OF=0

Se trattato come operazione con segno, l'ultima operazione non ha causato un overflow o un underflow.

Le condizioni possono essere usate anche per confrontare due valori. L'istruzione cmp confronta i due operandi e quindi imposta i flag come se sottraesse un operando dall'altro. Per controllare il risultato di cmpvalue1, value2, è possibile usare le condizioni seguenti.

Nome condizione Flags Significato dopo un'operazione CMP.

E

ZF=1

value1 == value2.

NE

ZF=0

value1 != value2.

GE NL

SF=OF

value1>= value2. I valori vengono considerati come interi con segno.

LE NG

ZF=1 o SF!=OF

value1<= value2. I valori vengono considerati come interi con segno.

G NLE

ZF=0 e SF=OF

value1>value2. I valori vengono considerati come interi con segno.

L NGE

SF!=OF

value1<value2. I valori vengono considerati come interi con segno.

AE NB

CF=0

value1>= value2. I valori vengono considerati come interi senza segno.

BE NA

CF=1 o ZF=1

value1<= value2. I valori vengono considerati come interi senza segno.

Un'be di rete

CF=0 e ZF=0

value1>value2. I valori vengono considerati come interi senza segno.

B NAE

CF=1

value1<value2. I valori vengono considerati come interi senza segno.

Le condizioni vengono in genere usate per agire sul risultato di un'istruzione cmp o di test . Ad esempio,

cmp eax, 5
jz equal

confronta il registro eax con il numero 5 calcolando l'espressione (eax - 5) e impostando i flag in base al risultato. Se il risultato della sottrazione è zero, verrà impostato il flag zr e la condizione jz sarà true in modo che venga eseguito il salto.

Tipi di dati

  • byte: 8 bit

  • word: 16 bit

  • dword: 32 bit

  • qword: 64 bit (include double a virgola mobile)

  • tword: 80 bit (include double estesi a virgola mobile)

  • oword: 128 bit

Notazione

La tabella seguente indica la notazione utilizzata per descrivere le istruzioni del linguaggio assembly.

Notation Significato

r, r1, r2...

Registri

m

Indirizzo di memoria (vedere la sezione Modalità di indirizzamento riuscito per altre informazioni).

#n

Costante immediata

r/m

Registrare o memoria

r/#n

Registra o costante immediata

r/m/#n

Registra, memoria o costante immediata

Cc

Codice di condizione elencato nella sezione Condizioni precedente.

T

"B", "W" o "D" (byte, word o dword)

accT

Size T accumulator: al if T = "B", ax se T = "W" o eax se T = "D"

Modalità di indirizzamento

Esistono diverse modalità di indirizzamento, ma tutte prendono il formato T ptr [expr], dove T è un tipo di dati (vedere la sezione Tipi di dati precedente) ed expr è un'espressione che coinvolge costanti e registri.

La notazione per la maggior parte delle modalità può essere dedotta senza molta difficoltà. Ad esempio, BYTE PTR [esi+edx*8+3] significa "accettare il valore del registro esi , aggiungerlo otto volte il valore del registro edx , aggiungere tre, quindi accedere al byte all'indirizzo risultante".

Pipelining

Il Pentium è a doppio problema, il che significa che può eseguire fino a due azioni in un tick di orologio. Tuttavia, le regole su quando è in grado di eseguire due azioni contemporaneamente (noto come associazione) sono molto complicate.

Poiché x86 è un processore CISC, non è necessario preoccuparsi degli slot di ritardo dei salti.

Accesso alla memoria sincronizzato

Le istruzioni di caricamento, modifica e archiviazione possono ricevere un prefisso di blocco , che modifica l'istruzione come indicato di seguito:

  1. Prima di emettere l'istruzione, la CPU scarica tutte le operazioni di memoria in sospeso per garantire la coesistenza. Tutte le prefetche di dati vengono abbandonate.

  2. Durante l'emissione dell'istruzione, la CPU avrà accesso esclusivo al bus. Ciò garantisce l'atomicità dell'operazione di caricamento/modifica/archiviazione.

L'istruzione xchg obbedisce automaticamente alle regole precedenti ogni volta che scambia un valore con memoria.

Tutte le altre istruzioni predefinite non vengono bloccate.

Stima dei salti

I salti incondizionati sono stimati per essere presi.

I salti condizionali vengono stimati per essere eseguiti o meno, a seconda che siano stati eseguiti l'ultima volta che sono stati eseguiti. La cache per la registrazione della cronologia dei salti è limitata in dimensioni.

Se la CPU non ha un record che indica se il salto condizionale è stato eseguito o meno l'ultima volta che è stato eseguito, stima i salti condizionali indietro come eseguiti e i salti condizionali in avanti non presi.

Allineamento

Il processore x86 corregge automaticamente l'accesso alla memoria non idoneo, con una penalità delle prestazioni. Non viene generata alcuna eccezione.

Un accesso alla memoria viene considerato allineato se l'indirizzo è un numero intero multiplo delle dimensioni dell'oggetto. Ad esempio, tutti gli accessi BYTE sono allineati (tutto è un numero intero multiplo di 1), gli accessi di WORD agli indirizzi anche sono allineati e gli indirizzi DWORD devono essere un multiplo di 4 per essere allineati.

Il prefisso di blocco non deve essere usato per gli accessi alla memoria non consentiti.