x86-arkitektur
Intel x86-processorn använder komplex CISC-arkitektur (instruction set computer), vilket innebär att det finns ett blygsamt antal särskilda register i stället för stora mängder allmänna register. Det innebär också att komplexa specialinstruktioner kommer att dominera.
X86-processorn spårar sitt arv minst så långt tillbaka som 8-bitars Intel 8080-processorn. Många egenheter i x86-instruktionsuppsättningen beror på bakåtkompatibiliteten med processorn (och med dess Zilog Z-80-variant).
Microsoft Win32 använder x86-processorn i 32-bitars platt läge. Den här dokumentationen fokuserar endast på det platta läget.
Register
X86-arkitekturen består av följande heltalsregister utan privilegier.
eax |
Ackumulator |
ebx |
Basregister |
ecx |
Räknarregister |
edx |
Dataregister – kan användas för I/O-portåtkomst och aritmetiska funktioner |
esi |
Källindexregister |
edi |
Målindexregister |
ebp |
Register för baspekare |
esp |
Stackpekare |
Alla heltalsregister är 32 bitar. Många av dem har dock 16-bitars eller 8-bitars underregister.
yxa |
Låga 16 bitar eax |
bx |
Låga 16 bitar ebx |
cx |
De 16 lägsta bitarna av ecx |
dx |
Lägre 16 bitar av edx |
si |
Lägsta 16 bitarna av esi |
di |
De lägre 16 bitarna av edi |
bp |
Lägsta 16 bitarna av ebp |
sp |
Låga 16 bitar esp |
al |
De låga 8 bitarna av eax |
ah |
De 8 högsta bitarna av ax |
bl |
De lägsta 8 bitarna av ebx |
bh |
De höga 8 bitarna av bx |
cl |
Låg 8 bitar av ecx |
ch |
De högsta 8 bitarna av cx |
dl |
Låg 8 bitar av edx |
dh |
De högsta 8 bitarna av dx |
Att arbeta i ett underregister påverkar endast underregistret och ingen av delarna utanför underregistret. Om du till exempel lagrar i ax-registret lämnar det de övre 16 bitarna av eax-registret oförändrade.
När du använder ? (Utvärdera uttryck)-kommandot bör register prefixas med ett "at"-tecken ( @ ). Du bör till exempel använda ? @ax i stället för ? ax. Detta säkerställer att felsökaren identifierar ax- som ett register i stället för en symbol.
(@) krävs dock inte i kommandot r (Register). Till exempel tolkas r ax=5 alltid korrekt.
Två andra register är viktiga för processorns aktuella tillstånd.
eip |
instruktionspekare |
flaggor |
flaggor |
Instruktionspekaren är adressen till instruktionen som körs.
Flaggornas register är en samling enbitsflaggor. Många instruktioner ändrar flaggorna för att beskriva resultatet av instruktionen. Dessa flaggor kan sedan testas med villkorliga hoppinstruktioner. Mer information finns i x86 Flags.
Samtalskonventioner
X86-arkitekturen har flera olika anropskonventioner. Lyckligtvis följer alla samma regler för registerbevarande och funktionsretur:
Funktioner måste bevara alla register, förutom eax, ecxoch edx, som kan ändras i ett funktionsanrop och esp, som måste uppdateras enligt anropskonventionen.
eax- register tar emot funktionens returvärden om resultatet är 32 bitar eller mindre. Om resultatet är 64 bitar lagras resultatet i edx:eax par.
Följande är en lista över anropskonventioner som används i x86-arkitekturen:
Win32 (__stdcall)
Funktionsparametrar skickas på stacken, push-överförs från höger till vänster och anroparen rensar stacken.
Inbyggt C++-metodanrop (kallas även thiscall)
Funktionsparametrar skickas på stacken, trycks från höger till vänster, this-pekaren skickas i ecx--registret och den anropade koden rensar stacken.
COM (__stdcall för C++-metodanrop)
Funktionsparametrar läggs till på stacken från höger till vänster, sedan läggs pekaren "this" till på stacken, och därefter anropas funktionen. Den anropade rensar stacken.
__fastcall
De första två argumenten som är DWORD eller mindre skickas i ecx och edx-register. De återstående parametrarna skickas på stacken och läggs till från höger till vänster. Den som anropas rensar stacken.
__cdecl
Funktionsparametrar skickas på stacken, läggs från höger till vänster, och anroparen rensar stacken. Anropskonventionen __cdecl används för alla funktioner med parametrar med variabel längd.
Felsökningsprogram Visning av register och flaggor
Här är ett exempel på en registervisning för felsökningsprogram:
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
I felsökning för användarläge kan du ignorera iopl- och hela den sista raden på felSökarens skärm.
x86-flaggor
I föregående exempel är koderna med två bokstäver i slutet av den andra raden flaggor. Det här är enstaka register och har en mängd olika användningsområden.
I följande tabell visas x86-flaggorna:
Flaggkodex | Flaggnamn | Värde | Flaggstatus | Beskrivning |
---|---|---|---|---|
av | Överflödesflagga | 0 1 | nvov | Inget översvämning – Översvämning |
df | Riktningsflagga | 0 1 | Riktning uppåt – Riktning nedåt | |
om | Avbrottsflagga | 0 1 | diei | Avbrott inaktiverade – Avbrott aktiverade |
sf | Skyltflagga | 0 1 | plng | Positiv (eller noll) – Negativ |
zf | Nollflagga | 0 1 | nzzr | Nonzero – noll |
af | Extra bärflagga | 0 1 | naac | Ingen extra bär - Extra bär |
pf | Paritetsflagga | 0 1 | pepo | Paritet udda - paritet jämn |
jfr | Överföringsflagga | 0 1 | nccy | Inget bärande - Bärande |
tf | Trap Flag | Om tf är lika med 1 skapar processorn ett STATUS_SINGLE_STEP undantag efter körningen av en instruktion. Den här flaggan används av ett felsökningsprogram för att implementera enstegsspårning. Det bör inte användas av andra program. | ||
iopl | I/O-behörighetsnivå | I/O-behörighetsnivå Det här är ett två bitars heltal med värden mellan noll och 3. Det används av operativsystemet för att styra åtkomsten till maskinvara. Den bör inte användas av program. |
När register visas som ett resultat av något kommando i felsökningskommandofönstret är det flaggstatus som visas. Men om du vill ändra en flagga med hjälp av kommandot r (Register) bör du referera till den med -flaggkoden.
I fönstret Register i WinDbg används flaggkoden för att visa eller ändra flaggor. Flaggstatus stöds inte.
Här är ett exempel. I föregående registervisning visas flaggstatusen ng. Det innebär att teckenflaggan för närvarande är inställd på 1. Om du vill ändra detta använder du följande kommando:
r sf=0
Detta sätter teckenflaggan till noll. Om du gör en annan registervisning visas inte statuskoden ng. I stället visas statuskoden pl.
Sign Flag, Zero Flag och Carry Flag är de vanligaste flaggorna.
Villkor
Ett villkor beskriver tillståndet för en eller flera flaggor. Alla villkorsstyrda åtgärder på x86 uttrycks i villkor.
Assemblern använder en en eller två bokstavsförkortning för att representera ett villkor. Ett villkor kan representeras av flera förkortningar. Till exempel är AE ("ovan eller lika") samma villkor som NB ("inte nedan"). I följande tabell visas några vanliga villkor och deras innebörd.
Villkorsnamn | Flaggor | Betydelse |
---|---|---|
Z |
ZF=1 |
Resultatet av den senaste åtgärden var noll. |
NZ |
ZF=0 |
Resultatet av den senaste åtgärden var inte noll. |
C |
CF=1 |
Den senaste åtgärden krävde en överföring eller låning. (För osignerade heltal indikerar detta översvämning.) |
NC |
CF=0 |
Den senaste operationen krävde inte överföring eller lån. (För osignerade heltal indikerar detta överspill.) |
S |
SF=1 |
Resultatet av den senaste åtgärden har en hög bituppsättning. |
NS |
SF=0 |
Resultatet av den senaste åtgärden har sin högsta bit rensad. |
O |
OF=1 |
När den behandlades som en signerad heltalsåtgärd orsakade den senaste åtgärden ett spill eller underflöde. |
NEJ |
OF=0 |
När den behandlades som en signerad heltalsoperation orsakade den senaste operationen inte ett överflöde eller underflöde. |
Villkor kan också användas för att jämföra två värden. Instruktionen cmp jämför sina två operander och anger sedan flaggor som om de subtraherade en operande från den andra. Följande villkor kan användas för att kontrollera resultatet av cmpvalue1, value2.
Villkorsnamn | Flaggor | Betydelse efter en CMP-åtgärd. |
---|---|---|
E |
ZF=1 |
värde1 == värde2. |
NE |
ZF=0 |
value1 != value2. |
GE NL | SF=OF |
value1>= value2. Värden behandlas som signerade heltal. |
LE NG | ZF=1 eller SF!=OF |
value1<= value2. Värden hanteras som signerade heltal. |
G NLE | ZF=0 och SF=OF |
värde1>värde2. Värden behandlas som tecknade heltal. |
L NGE | SF!=OF |
värde1<värde2. Värden behandlas som signerade heltal. |
AE NB | CF=0 |
value1>= value2. Värden behandlas som osignerade heltal. |
BE NA | CF=1 eller ZF=1 |
value1<= value2. Värden behandlas som osignerade heltal. |
En NBE | CF=0 och ZF=0 |
värde1>värde2. Värden behandlas som osignerade heltal. |
B NAE | CF=1 |
värde1<värde2. Värden behandlas som osignerade heltal. |
Villkor används vanligtvis för att agera på resultatet av en cmp- eller test instruktion. Till exempel
cmp eax, 5
jz equal
jämför eax-registret mot talet 5 genom att beräkna uttrycket (eax - 5) och sätta flaggor enligt resultatet. Om resultatet av subtraktionen är noll, anges flaggan zr, och jz-villkoret är sant, vilket innebär att ett hopp utförs.
Datatyper
byte: 8 bitar
Word: 16 bitar
dword: 32 bitar
qword: 64 bitar (inkluderar flyttalsdubblar)
tword: 80 bitar (inkluderar flyttalsutökade dubbla)
oword: 128 bitar
Notation
Följande tabell anger den notation som används för att beskriva instruktioner för sammansättningsspråk.
Notation | Betydelse |
---|---|
r, r1, r2... |
Register |
m |
Minnesadress (mer information finns i det följande avsnittet om adresseringslägen.) |
#n |
Omedelbar konstant |
r/m |
Registrera eller minne |
r/#n |
Register eller omedelbar konstant |
r/m/#n |
Register, minne eller omedelbar konstant |
cc |
En villkorskod som anges i avsnittet villkor ovan. |
T |
"B", "W" eller "D" (byte, ord eller dubbelord) |
accT |
Storlek T ackumulator: al om T = "B", ax om T = "W" eller eax om T = "D" |
Adresslägen
Det finns flera olika adresseringslägen, men de har alla formuläret T ptr [expr], där T är en viss datatyp (se avsnittet datatyper ovan) och uttr är ett uttryck som omfattar konstanter och register.
Notationen för de flesta lägen kan härledas utan större svårigheter. Till exempel betyder BYTE PTR [esi+edx*8+3] "ta värdet från esi-registern, lägg till det åtta gånger värdet från edx-registern, lägg till tre och kom sedan åt bytet vid den resulterande adressen."
Pipelining
Pentium är dubbelavlastad, vilket innebär att den kan utföra upp till två åtgärder i en klockcykel. Reglerna för när den kan utföra två åtgärder samtidigt (kallas parkoppling) är dock mycket komplicerade.
Eftersom x86 är en CISC-processor behöver du inte oroa dig för hoppfördröjningsfack.
Synkroniserad minnesåtkomst
Läsa in, ändra och lagra instruktioner kan ta emot ett lås prefix, som ändrar instruktionen enligt följande:
Innan instruktionen utfärdas rensar processorn alla väntande minnesåtgärder för att säkerställa samtidighet. Alla dataförhämtningar övergavs.
Vid utfärdandet av instruktionen kommer processorn att ha exklusiv åtkomst till bussen. Detta säkerställer atomariteten i belastnings-/ändrings-/lagringsoperationen.
Instruktionen xchg följer automatiskt de tidigare reglerna när den utbyter ett värde med minne.
Alla andra instruktioner är som standard utan låsning.
Hoppprognos
Ovillkorliga hopp förväntas tas.
Villkorliga hopp förutses tas eller inte tas, beroende på om de togs den senaste gången de utfördes. Cachen för inspelning av hopphistorik är begränsad i storlek.
Om processorn inte har en post om huruvida det villkorliga hoppet togs eller inte togs förra gången det kördes, förutsäger den bakåtriktade villkorliga hopp som tagits och framåtriktade villkorliga hopp som inte tagits.
Justering
x86-processorn korrigerar automatiskt ojusterad minnesåtkomst, med en prestandaförlust. Inget undantag kastas.
En minnesåtkomst anses vara justerad om adressen är en heltalsmultipel av objektstorleken. Till exempel är alla BYTE-åtkomster justerade (allt är en heltalsmultipel på 1), WORD-åtkomst till jämna adresser justeras och DWORD-adresser måste vara en multipel av 4 för att justeras.
Prefixet lås ska inte användas för ojusterade minnesåtkomster.