Freigeben über


x86-Anweisungen

In den Listen in diesem Abschnitt sind anweisungen, die mit einem Sternchen (*) gekennzeichnet sind, besonders wichtig. Anweisungen, die nicht so gekennzeichnet sind, sind nicht kritisch.

Auf dem x86-Prozessor haben Anweisungen eine variable Größe, sodass das Disassemblieren nach hinten eine Übung beim Musterabgleich ist. Um von einer Adresse rückwärts zu disassemblieren, sollten Sie mit dem Disassemblieren zu einem Punkt beginnen, der weiter zurück ist, als Sie wirklich möchten, und dann nach vorne schauen, bis die Anweisungen sinnvoll sind. Die ersten Anweisungen machen möglicherweise keinen Sinn, da Sie möglicherweise mitten in einer Anweisung mit dem Disassemblieren begonnen haben. Es besteht leider die Möglichkeit, dass die Disassemblierung nie mit dem Anweisungsstream synchronisiert wird und Sie an einem anderen Ausgangspunkt disassemblieren müssen, bis Sie einen Ausgangspunkt gefunden haben, der funktioniert.

Bei gut gepackten Switch-Anweisungen gibt der Compiler Daten direkt in den Codestream aus, sodass das Disassemblieren über eine switch-Anweisung in der Regel auf Anweisungen stößt, die keinen Sinn ergeben (da es sich wirklich um Daten handelt). Suchen Sie das Ende der Daten, und fahren Sie dort mit der Disassemblierung fort.

Anweisungsnotation

Die allgemeine Schreibweise für Anweisungen besteht darin, das Zielregister auf der linken Seite und die Quelle auf der rechten Seite zu platzieren. Es kann jedoch einige Ausnahmen von dieser Regel geben.

Arithmetische Anweisungen bestehen in der Regel aus zwei Registern, wobei quell- und zielregister kombiniert werden. Das Ergebnis wird im Ziel gespeichert.

Einige der Anweisungen enthalten sowohl 16-Bit- als auch 32-Bit-Versionen, aber nur die 32-Bit-Versionen sind hier aufgeführt. Hier nicht aufgeführt sind Gleitkommaanweisungen, privilegierte Anweisungen und Anweisungen, die nur in segmentierten Modellen verwendet werden (die Microsoft Win32 nicht verwendet).

Um Platz zu sparen, werden viele der Anweisungen in kombinierter Form ausgedrückt, wie im folgenden Beispiel gezeigt.

*

MOV

r1, r/m/#n

r1 = r/m/#n

bedeutet, dass der erste Parameter ein Register sein muss, der zweite kann jedoch ein Register, ein Speicherverweis oder ein sofortiger Wert sein.

Um noch mehr Platz zu sparen, können Anweisungen auch wie im Folgenden dargestellt ausgedrückt werden.

*

MOV

r1/m, r/m/#n

r1/m = r/m/#n

Dies bedeutet, dass der erste Parameter ein Register oder ein Speicherverweis sein kann, und der zweite kann ein Register, ein Speicherverweis oder ein sofortiger Wert sein.

Sofern nicht anders angegeben, können Sie bei Verwendung dieser Abkürzung nicht den Arbeitsspeicher für Quelle und Ziel auswählen.

Darüber hinaus kann ein Bitgrößensuffix (8, 16, 32) an die Quelle oder das Ziel angefügt werden, um anzugeben, dass der Parameter diese Größe aufweisen muss. Beispielsweise bedeutet r8 ein 8-Bit-Register.

Arbeitsspeicher, Datenübertragung und Datenkonvertierung

Anweisungen zur Speicher- und Datenübertragung wirken sich nicht auf Flags aus.

Effektive Adresse

*

LEA

r, m

Laden Sie die effektive Adresse.

(r = Adresse von m)

Beispielsweise bedeutet LEA eax, [esi+4]eax = esi + 4. Diese Anweisung wird häufig verwendet, um Arithmetik auszuführen.

Datenübertragung

MOV

r1/m, r2/m/#n

r1/m = r/m/#n

MOVSX

r1, r/m

Verschieben mit Vorzeichenerweiterung.

*

MOVZX

r1, r/m

Verschieben mit der Erweiterung "Null".

MOVSX und MOVZX sind spezielle Versionen der mov-Anweisung , die eine Vorzeichenerweiterung oder null-Erweiterung von der Quelle zum Ziel ausführen. Dies ist die einzige Anweisung, die die Größe von Quelle und Ziel erlaubt. (Und in der Tat müssen sie unterschiedliche Größen haben.

Stapelbearbeitung

Auf den Stapel zeigt das esp-Register . Der Wert bei esp ist der obere Rand des Stapels (zuletzt gepusht, zuerst eingeklallt); ältere Stapelelemente befinden sich an höheren Adressen.

PUSH

r/m/#n

Pushen sie den Wert auf den Stapel.

POP

r/m

Pop-Wert aus Stapel.

PUSHFD

Pushen von Flags auf den Stapel.

POPFD

Popflags aus dem Stapel.

PUSHAD

Pushen aller ganzzahligen Register.

POPAD

Alle ganzzahligen Register auffüllen.

EINGABETASTE

#n, #n

Buildstapelrahmen.

*

VERLASSEN

Abreißen des Stapelrahmens

Der C/C++-Compiler verwendet die Enter-Anweisung nicht. (Die Enter-Anweisung wird verwendet, um geschachtelte Prozeduren in Sprachen wie Algol oder Pascal zu implementieren.)

Die Leave-Anweisung entspricht:

mov esp, ebp
pop ebp

Datenkonvertierung

CBW

Konvertieren Sie Byte (al) in Wort (ax).

CWD

Konvertieren von Wort (ax) in dword (dx:ax).

CWDE

Konvertieren von Wort (ax) in dword (eax).

CDQ

konvertieren Sie dword (eax) in qword (edx:eax).

Alle Konvertierungen führen eine Vorzeichenerweiterung durch.

Arithmetik und Bitbearbeitung

Alle Anweisungen zur Arithmetik- und Bitbearbeitung ändern Flags.

Arithmetische

ADD

r1/m, r2/m/#n

r1/m += r2/m/#n

ADC

r1/m, r2/m/#n

r1/m += r2/m/#n + carry

SUB

r1/m, r2/m/#n

r1/m -= r2/m/#n

SBB

r1/m, r2/m/#n

r1/m -= r2/m/#n + carry

NEG

r1/m

r1/m = -r1/m

INC

r/m

r/m += 1

DEC

r/m

r/m -= 1

CMP

r1/m, r2/m/#n

Compute r1/m – r2/m/#n

Die cmp-Anweisung berechnet die Subtraktion und legt die Flags entsprechend dem Ergebnis fest, löst jedoch das Ergebnis aus. Auf sie folgt in der Regel eine bedingte Sprunganweisung , die das Ergebnis der Subtraktion testet.

MUL

r/m8

Ax = Al * r/m8

MUL

r/m16

dx:ax = ax * r/m16

MUL

r/m32

edx:eax = eax * r/m32

IMUL

r/m8

Ax = Al * r/m8

IMUL

r/m16

dx:ax = ax * r/m16

IMUL

r/m32

edx:eax = eax * r/m32

IMUL

r1, r2/m

r1 *= r2/m

IMUL

r1, r2/m, #n

r1 = r2/m * #n

Nicht signierte und signierte Multiplikation. Der Status von Flags nach der Multiplikation ist nicht definiert.

DIV

r/m8

(ah, al) = (ax % r/m8, ax / r/m8)

DIV

r/m16

(dx, ax) = dx:ax / r/m16

DIV

r/m32

(edx, eax) = edx:eax / r/m32

IDIV

r/m8

(ah, al) = ax / r/m8

IDIV

r/m16

(dx, ax) = dx:ax / r/m16

IDIV

r/m32

(edx, eax) = edx:eax / r/m32

Nicht signierte und signierte Division. Das erste Register in der Pseudocodeerklärung empfängt den Rest und das zweite den Quotienten. Wenn das Ergebnis das Ziel überläuft, wird eine Bereichsüberlaufausnahme generiert.

Der Status von Flags nach der Division ist nicht definiert.

*

SETcc

r/m8

Legen Sie r/m8 auf 0 oder 1 fest.

Wenn die Bedingung cc true ist, wird der 8-Bit-Wert auf 1 festgelegt. Andernfalls wird der 8-Bit-Wert auf 0 (null) festgelegt.

Binärcodierte Dezimalstellen

Diese Anweisungen werden nur angezeigt, wenn Sie Code debuggen, der in COBOL geschrieben wurde.

DAA

Dezimalanpassung nach dem Hinzufügen.

DAS

Dezimalanpassung nach der Subtraktion.

Diese Anweisungen passen das Al-Register an, nachdem eine gepackte binärcodierte Dezimaloperation ausgeführt wurde.

AAA

ASCII-Anpassung nach dem Hinzufügen.

AAS

ASCII-Anpassung nach der Subtraktion.

Diese Anweisungen passen das Al-Register an, nachdem ein entpackter binärcodierter Dezimalvorgang ausgeführt wurde.

AAM

ASCII-Anpassung nach der Multiplikation.

AAD

ASCII-Anpassung nach der Division.

Diese Anweisungen passen die Register al und ah an, nachdem ein entpackter binärcodierter Dezimalvorgang ausgeführt wurde.

Bits

UND

r1/m, r2/m/#n

r1/m = r1/m und r2/m/#n

oder

r1/m, r2/m/#n

r1/m = r1/m oder r2/m/#n

XOR

r1/m, r2/m/#n

r1/m = r1/m xor r2/m/#n

NICHT

r1/m

r1/m = bitweise nicht r1/m

*

TEST

r1/m, r2/m/#n

Compute r1/m und r2/m/#n

Die Testanweisung berechnet den logischen AND-Operator und legt Flags entsprechend dem Ergebnis fest, verschieft das Ergebnis jedoch. Es folgt in der Regel eine bedingte Sprunganweisung, die das Ergebnis des logischen AND testet.

SHL

r1/m, cl/#n

r1/m <<= cl/#n

SHR

r1/m, cl/#n

r1/m >>= cl/#n zero-fill

*

SAR

r1/m, cl/#n

r1/m >>= cl/#n Sign-Fill

Das letzte ausgelagerte Bit wird im Carry platziert.

SHLD

r1, r2/m, cl/#n

Doppelt umschalten.

Verschieben Sie r1 links durch cl/#n, und füllen Sie die oberen Bits von r2/m. Das letzte ausgelagerte Bit wird im Carry platziert.

SHRD

r1, r2/m, cl/#n

Doppel umschalten nach rechts.

Verschieben Sie r1 nach rechts durch cl/#n, und füllen Sie die unteren Bits von r2/m. Das letzte ausgelagerte Bit wird im Carry platziert.

ROL

r1, cl/#n

Rotieren Sie r1 links durch cl/#n.

ROR

r1, cl/#n

Drehen Sie r1 nach rechts durch cl/#n.

RCL

r1, cl/#n

Rotieren Sie r1/C links von cl/#n.

RCR

r1, cl/#n

Rotieren Sie r1/C nach rechts durch cl/#n.

Die Drehung ähnelt der Verschiebung, mit dem Unterschied, dass die bits, die verschoben werden, als die eingehenden Füllbits wieder angezeigt werden. In der C-Sprachversion der Drehanweisungen wird das Carry-Bit in die Drehung integriert.

BT

r1, r2/#n

Kopieren Sie bit r2/#n von r1 in carry.

BTS

r1, r2/#n

Legen Sie bit r2/#n von r1 fest, kopieren Sie den vorherigen Wert in carry.

BTC

r1, r2/#n

Löschen Sie bit r2/#n von r1, kopieren Sie den vorherigen Wert in carry.

Ablaufsteuerung

Jcc

Dest

Branch bedingt.

JMP

Dest

Direkt springen.

JMP

r/m

Indirekter Sprung.

CALL

Dest

Rufen Sie direkt an.

*

CALL

r/m

Indirekt aufrufen.

Die Anrufanweisung pusht die Rückgabeadresse auf den Stapel und springt dann zum Ziel.

*

RET

#n

Rückgabewert

Die ret-Anweisung springt und springt zur Rückgabeadresse im Stapel. Ein nonzero #n in der RET-Anweisung gibt an, dass nach dem Poppen der Rückgabeadresse der Wert #n dem Stapelzeiger hinzugefügt werden soll.

SCHLEIFE

Dekrementieren Sie ecx , und springen Sie, wenn das Ergebnis nicht zero ist.

LOOPZ

Dekrementieren Sie ecx , und springen Sie, wenn das Ergebnis nonzero ist und zr festgelegt wurde.

LOOPNZ

Dekrementieren Sie ecx und springen Sie, wenn das Ergebnis nicht zero und zr eindeutig ist.

JECXZ

Springen, wenn ecx 0 ist.

Diese Anweisungen sind Reste des CISC-Erbes des x86 und sind in den letzten Prozessoren tatsächlich langsamer als die entsprechenden Anweisungen, die auf langem Weg geschrieben wurden.

Zeichenfolgenbearbeitung

MOVST

Verschieben Sie T von esi zu edi.

CMPST

Vergleichen Sie T from esi mit edi.

SCAST

Scan T from edi for accT.

LODST

Laden Sie T von esi in gemäßT.

STOST

Store T to edi from accT.

Nach der Ausführung des Vorgangs werden das Quell- und Zielregister entsprechend der Einstellung des Richtungsflags (nach oben oder unten) durch sizeof(T) erhöht oder dekrementiert.

Die Anweisung kann durch REP präfixiert werden, um den Vorgang in der vom ecx-Register angegebenen Anzahl von Zeiten zu wiederholen.

Die Rep mov-Anweisung wird verwendet, um Speicherblöcke zu kopieren.

Die Rep stos-Anweisung wird verwendet, um einen Speicherblock mit accT zu füllen.

Flaggen

LAHF

Laden Sie ah aus Flags.

SAHF

Speichern Sie ah in Flags.

STC

Legen Sie "carry" fest.

CLC

Klares Tragen.

CMC

Komplement tragen.

STD

Legen Sie die Richtung auf nach unten fest.

CLD

Richten Sie die Richtung nach oben ein.

STI

Aktivieren Sie Interrupts.

Befehlszeilenschnittstelle (CLI)

Deaktivieren Sie Interrupts.

Ineinandergreifen von Anweisungen

XCHG

r1, r/m

Tauschen Sie r1 und r/m aus.

XADD

r1, r/m

Fügen Sie r1 zu r/m hinzu, und geben Sie den ursprünglichen Wert in r1 ein.

CMPXCHG

r1, r/m

Bedingtes Vergleichen und Austauschen.

Die cmpxchg-Anweisung ist die atomische Version der folgenden:

   cmp     accT, r/m
   jz      match
   mov     accT, r/m
   jmp     done
match:
   mov     r/m, r1
done:

Sonstige

INT

#n

Trap to Kernel.

GEBUNDEN

r, m

Trap, wenn r nicht im Bereich liegt.

*

NOP

Keine Operation.

XLATB

al = [ebx + al]

BSWAP

r

Bytereihenfolge im Register austauschen.

Hier ist ein Sonderfall der int-Anweisung .

INT

3

Debugger-Breakpoint-Trap.

Der Opcode für INT 3 ist 0xCC. Der Opcode für NOP ist 0x90.

Beim Debuggen von Code müssen Sie möglicherweise code patchen. Sie können dies tun, indem Sie die verletzenden Bytes durch 0x90 ersetzen.

Idiome

XOR

r, r

r = 0

TEST

r, r

Überprüfen Sie, ob r = 0 ist.

*

ADD

r, r

Verschieben Sie r um 1 nach links.