Condividi tramite


Architettura x86

Il processore Intel x86 usa un'architettura CISC (Instruction Set Computer) complessa, il 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 predominieranno.

Il processore x86 traccia la sua origine 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 quel processore (e con la sua variante Zilog Z-80).

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

Registri

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

eax

Accumulatore

ebx

Registro di base

ecx

Registro del contatore

edx

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

esi

Registro dell'indice di sorgente

edi

Registro degli indici di destinazione

ebp

Registro del puntatore di base

esp

Puntatore dello stack

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

ax

Bassi 16 bit di eax

bx

Bassi 16 bit di ebx

cx

I 16 bit meno significativi di ecx

dx

I 16 bit inferiori di edx

si

Basso 16 bit di esi

di

Parte bassa dei 16 bit di edi

bp

16 bit più bassi di ebp

sp

I 16 bit bassi di esp

al

Basso 8 bit di eax

ah

Alti 8 bit di ax

bl

Gli 8 bit inferiori di ebx

bh

I più alti 8 bit di bx

cl

Bassi 8 bit di ecx

ch

Parte superiore degli 8 bit di cx

dl

Bassi 8 Bit dei registri edx

dh

Gli alti 8 bit di dx

L'uso di un sottoregistro influisce solo sul sottoregistro e non su nessuna delle parti esterne al sottoregistro. Ad esempio, l'archiviazione nel registro ax lascia invariati i 16 bit alti del registro eax.

Quando si usa il ? (Evaluate Expression) comando, i registri devono essere preceduti da un segno "at" ( @ ). Ad esempio, è consigliabile usare ? @ax anziché ? ax. In questo modo il debugger riconosce ax come registro anziché come simbolo.

Tuttavia, il parametro (@) non è necessario 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 all'istruzione

bandiere

Bandiere

Il puntatore all'istruzione è l'indirizzo dell'istruzione in esecuzione.

Il registro dei flag è un insieme di flag a singoli bit. Molte istruzioni modificano i flag per descrivere il risultato dell'istruzione. Questi flag possono quindi essere testati tramite istruzioni di salto condizionale. Per informazioni dettagliate, vedere flag x86.

Convenzioni di chiamata

L'architettura x86 ha diverse convenzioni di chiamata. Fortunatamente, tutti seguono le stesse regole di conservazione dei registri e restituzione delle funzioni:

  • Le funzioni devono mantenere tutti i registri, ad eccezione di eax, ecxe edx, che possono essere modificati durante una chiamata di funzione e esp, che deve essere aggiornato secondo la convenzione di chiamata.

  • Il registro eax riceve i valori restituiti dalla funzione se il risultato è di 32 bit o minore. 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, spinti da destra a sinistra e la funzione chiamata pulisce lo stack.

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

    I parametri della funzione vengono passati nello stack, inseriti da destra a sinistra; il puntatore "this" viene passato nel registro ecx, e la funzione chiamata pulisce lo stack.

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

    I parametri della funzione vengono passati sullo stack, vengono inseriti da destra verso sinistra, successivamente viene eseguito il push del puntatore this sullo stack e quindi viene chiamata la funzione. Il chiamato pulisce lo stack.

  • __fastcall

    I primi due argomenti DWORD o più piccoli vengono passati nei registri ecx e edx. I parametri rimanenti vengono passati nello stack, inseriti da destra a sinistra. Il chiamato pulisce lo stack.

  • __cdecl

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

Visualizzazione dei registri e dei flag nel debugger

Di seguito è riportato un esempio di visualizzazione del registro del debugger:

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 debugging in modalità utente, è possibile ignorare il iopl e l'intera ultima riga della schermata del debugger.

Le Flag x86

Nell'esempio precedente i codici a due lettere alla fine della seconda riga sono bandiere. Si tratta di registri a bit singolo e con un'ampia gamma di usi.

La tabella seguente elenca i flag x86:

Codice della Bandiera Nome contrassegno Valore Stato della bandiera Descrizione
di Indicatore di Overflow 0 1 nvov Nessun overflow - Eccesso
df Bandiera di direzione 0 1 updn Direzione verso l'alto - Direzione verso il basso
se Flag di Interruzione 0 1 diei Interruzioni disabilitate - Interruzioni abilitate
sf Indicatore di Segnalazione 0 1 plng Positivo (o zero) - Negativo
zf Contrassegno zero 0 1 nzzr Diverso da zero - Zero
af Bandiera porta ausiliaria 0 1 naac Nessun trasporto ausiliario - Trasporto ausiliario
pf Contrassegno parità 0 1 pepo Parità dispari - Parità pari
cf Contrassegno di trasporto 0 1 nccy Nessun carico - Carico
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 il tracciamento passo-passo. Non deve essere usato da altre applicazioni.
iopl Livello di privilegi di I/O Livello di privilegio I/O Questo è un numero 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 Comandi del Debugger, è lo stato del flag che viene visualizzato. Tuttavia, se si vuole modificare un segnale usando il comando r (Registri), è necessario farvi riferimento con il codice flag .

Nella finestra Registri di WinDbg, il codice flag viene usato per visualizzare o alterare i flag. Lo stato della segnalazione non è supportato.

Ecco un esempio. Nella visualizzazione del registro precedente viene visualizzato lo stato del flag ng. Questo significa che il segno del flag è impostato attualmente a 1. Per modificare questa operazione, usare il comando seguente:

r sf=0

In questo modo il flag del segno viene impostato su zero. Se si esegue un'altra schermata del registro, il codice di stato non verrà visualizzato. Verrà invece visualizzato il codice di stato pl.

La bandiera del segno, la bandiera zero e la bandiera di trasporto sono i flag più usati.

Condizioni

Una condizione descrive lo stato di uno o più flag. Tutte le operazioni condizionali su x86 sono 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 ("non inferiore"). Nella tabella seguente sono elencate alcune condizioni comuni e il relativo significato.

Nome condizione Bandiere Significato

Z

ZF=1

Risultato dell'ultima operazione pari a zero.

NZ

ZF=0

Il risultato dell'ultima operazione non è stato zero.

C

CF=1

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

NC

CF=0

L'ultima operazione non richiedeva un trasporto o un prestito. Per i numeri interi senza segno, indica quando si verifica un overflow.

S

SF=1

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

NS

SF=0

Il risultato dell'ultima operazione ha il bit più significativo azzerato.

O

OF=1

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

NO

OF=0

Se considerato come operazione con segno integer, 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 suoi due operandi e quindi imposta i flag come se un operando fosse sottratto dall'altro. È possibile usare le condizioni seguenti per verificare il risultato di cmpvalue1, value2.

Nome condizione Bandiere 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 numeri interi con segno.

LE NG

ZF=1 o SF!=OF

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

G NLE

ZF=0 e SF=OF

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

L NGE

SF!=OF

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

AE NB

CF=0

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

BE NA

CF=1 o ZF=1

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

A NBE

CF=0 e ZF=0

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

B NAE

CF=1

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

Le condizioni vengono in genere usate per operare sul risultato di un'istruzione cmp o un'istruzione di test . Per 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à vera in modo che venga eseguito il salto.

Tipi di dati

  • byte: 8 bit

  • parola: 16 bit

  • dword: 32 bit

  • qword: 64 bit (include numeri in virgola mobile a doppia precisione)

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

  • oword: 128 bit

Notazione

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

Notazione Significato

r, r1, r2...

Registri

m

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

#n

Costante immediata

r/m

Registro o memoria

r/#n

Registro o costante immediato

r/m/#n

Registro, memoria o costante immediata

cc

Codice di condizione elencato nella sezione Condizioni precedente.

T

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

accT

Dimensione T accumulatore: al if T = "B", ax if T = "W" o eax if T = "D"

Modalità di indirizzamento

Esistono diverse modalità di indirizzamento, ma tutte assumono il formato T ptr [expr], dove T è un tipo di dati (vedere la sezione Tipi di dati precedente) e 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 "prendere il valore del registro esi, aggiungerlo otto volte al valore del edx register, aggiungere tre, quindi accedere al byte all'indirizzo risultante".

Pipelining

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

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

Accesso alla memoria sincronizzato

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

  1. Prima di eseguire l'istruzione, la CPU scarica tutte le operazioni di memoria in sospeso per garantire la coerenza. Tutte le preletture dei dati vengono abbandonate.

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

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

Per impostazione predefinita, tutte le altre istruzioni non vengono bloccate.

Predizione del Salto

I salti incondizionato vengono stimati per essere presi.

I salti condizionali sono previsti per essere presi o non presi, a seconda che siano stati presi l'ultima volta che sono stati eseguiti. La cache per la registrazione della cronologia dei salti è limitata.

Se la CPU non ha un record di se il salto condizionale è stato preso o meno l'ultima volta in cui è stato eseguito, prevede i salti condizionali all'indietro come presi e quelli in avanti come non presi.

Allineamento

Il processore x86 correggerà automaticamente l'accesso alla memoria non allineata, comportando una penalizzazione delle prestazioni. Non viene generata alcuna eccezione.

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

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

Vedere anche

Architettura x64

X86-64 Wikipedia