Ich habe mich gefragt, ob Befehlssatz und Assemblersprache dasselbe sind.

Wenn nicht, wie unterscheiden sie sich und wie sind ihre Beziehungen?

Danke und Grüße!

- Nmichaels 21. März 11 um 18:51 Uhr

, Was meinst du? - Tim 21. März 11 um 19:11 Uhr

7 Antworten 7

Ich denke, jeder gibt Ihnen die gleiche Antwort. Befehlssatz ist der Satz (wie in Mathe) aller Befehle, die der Prozessor ausführen oder verstehen kann. Assemblersprache ist eine Programmiersprache.

Lassen Sie mich anhand einiger Fragen, die Sie stellen, einige Beispiele ausprobieren. Und ich werde mit dem Code, den ich zur Hand habe, von Prozessor zu Prozessor springen.

Befehls- oder Operationscode oder Binär- oder Maschinensprache, welcher Begriff auch immer Sie für die Bits / Bytes verwenden möchten, die in den Prozessor geladen werden, um dekodiert und ausgeführt zu werden. Ein Beispiel

Die Assemblersprache wäre

Für diesen speziellen Prozessor. In diesem Fall bedeutet das r11 = r11 + r12. Also habe ich diesen Text, das Add r12, r11, in eine Textdatei eingefügt und benutze einen Assembler (ein Programm, das die Assemblersprache kompiliert / zusammensetzt), um ihn in eine Art Binärdatei zusammenzusetzen. Wie in jeder Programmiersprache erstellen Sie manchmal Objektdateien und verknüpfen sie dann miteinander. Manchmal können Sie direkt zu einer Binärdatei wechseln. Und es gibt viele Formen von Binärdateien, die in ASCII- und Binärform vorliegen, und eine ganze andere Diskussion.

Was können Sie nun in Assembler tun, der nicht Teil des Befehlssatzes ist? Wie unterscheiden sie sich? Gut für den Anfang können Sie Makros haben:

Makros sind wie Inline-Funktionen, sie sind keine aufgerufenen Funktionen, sondern generieren Code in der Zeile. Zum Beispiel nicht anders als ein C-Makro. Sie können sie zum Speichern von Eingaben oder zum Abstrahieren von Aufgaben verwenden, die Sie immer wieder ausführen möchten und die sich an einem Ort ändern können und nicht jede Instanz berühren müssen. Das obige Beispiel generiert im Wesentlichen Folgendes:

Ein weiterer Unterschied zwischen Befehlssatz und Assemblersprache sind Pseudobefehle. Für diesen speziellen Befehlssatz gibt es beispielsweise keinen Pop-Befehl, um Dinge vom Stapel zu entfernen, zumindest nicht mit diesem Namen, und ich werde erklären, warum. Sie können jedoch einige Eingaben speichern und ein Popup in Ihrem Code verwenden:

Der Grund, warum es kein Pop gibt, ist, dass die Adressierungsmodi flexibel genug sind, um einen Lesevorgang von der Adresse im Quellregister zu veranlassen, den Wert im Zielregister abzulegen und das Quellregister um ein Wort zu erhöhen. Was im Assembler für diesen Befehlssatz steht

sowohl der pop als auch der mov ergeben den opcode 0x413C.

Ein weiteres Beispiel für Unterschiede zwischen Anweisungssatz und Assembler, das Wechseln von Anweisungssätzen, ist etwa Folgendes:

Was für diese Assemblersprache bedeutet, dass die Adresse von bob in Register 0 geladen wird, dafür gibt es keine Anweisung. Der Assembler generiert etwas, das so aussehen würde, wenn Sie es von Hand in Assembler schreiben würden:

Im Wesentlichen wird an einem von dieser Anweisung aus erreichbaren Ort, nicht im Ausführungspfad, ein Wort erstellt, das der Linker mit der Adresse für bob ausfüllt. Der ldr-Befehl, der ebenfalls vom Assembler oder Linker ausgeführt wird, wird mit einem ldr eines pc-relativen Befehls codiert.

Das führt zu einer ganzen Reihe von Unterschieden zwischen Befehlssatz und Assemblersprache

Maschinencode kann nicht wissen, was Spaß macht oder wo er zu finden ist. Für diesen Befehlssatz mit seinen vielen Adressierungsmodi (Hinweis: Ich vermeide ausdrücklich und absichtlich die Benennung der von mir verwendeten Befehlssätze, da dies für die Diskussion nicht relevant ist) kann der Assembler oder Linker verwendet werden (je nachdem, wo die Spaßfunktion endet) relativ zu dieser Anweisung sein).

Der Assembler kann diesen Befehl als PC-Relativ codieren. Wenn die Spaßfunktion 40 Byte vor dem Aufrufbefehl liegt, kann er ihn mit dem Äquivalent von Aufruf PC + 36 codieren (nehmen Sie vier davon ab, da der PC zur Ausführungszeit einen Befehl voraus ist und dies ist ein 4-Byte-Befehl).

Oder der Assembler weiß möglicherweise nicht, wo oder was Spaß macht, und überlässt es dem Linker. In diesem Fall gibt der Linker möglicherweise die absolute Adresse der Funktion an, die dem Aufruf von # 0xD00D ähnelt.

Gleiches gilt für Lasten und Geschäfte, einige Befehlssätze haben einen nahen und einen fernen PC-Verwandten, einige haben eine absolute Adresse usw. Und es ist Ihnen vielleicht egal, ob Sie eine Auswahl treffen oder nicht, können Sie einfach sagen

und der Assembler oder Linker oder eine Kombination der beiden kümmert sich um den Rest.

Beachten Sie, dass bei einigen Befehlssätzen Assembler und Linker in einem Programm gleichzeitig vorkommen können. Heutzutage sind wir es gewohnt, Objekte zu kompilieren und dann zu verknüpfen, aber nicht alle Assembler folgen diesem Modell.

Einige weitere Fälle, in denen die Assemblersprache einige Verknüpfungen annehmen kann:

Der hang: b hang macht Sinn, verzweige zu dem Label hang. Im Wesentlichen ein Zweig zu sich selbst. Und wie der Name schon sagt, handelt es sich um eine Endlosschleife. Aber für diese Assemblersprache b. bedeutet Verzweigung zu sich selbst, eine Endlosschleife, aber ich musste keine Bezeichnung erfinden, sie eingeben und zu ihr verzweigen. Eine andere Abkürzung ist die Verwendung von Zahlen b 1b bedeutet Verzweigung nach 1 zurück, der Assembler sucht nach dem Etikett Nummer 1 hinter oder über der Anweisung. Das b 1f, das keine Verzweigung zu sich selbst ist, bedeutet Verzweigung 1 vorwärts, dies ist ein vollkommen gültiger Code für diesen Assembler. Es wird vorwärts oder unterhalb der Codezeile für ein Label Nummer 1 schauen: Und Sie können Nummer 1 wie verrückt in Ihrem Assemblersprachenprogramm für diesen Assembler wiederverwenden, erspart das Erfinden von Labelnamen für einfache kurze Verzweigungen. Das zweite b 1b verzweigt sich zum zweiten 1. und ist ein Zweig zum Selbst.

Es ist wichtig zu verstehen, dass die Firma, die den Prozessor erstellt hat, den Befehlssatz und den Maschinencode oder die Opcodes definiert oder welchen Begriff sie oder Sie für die Bits und Bytes verwenden, die der Prozessor dekodiert und ausführt. Sehr oft erstellt diese Firma ein Dokument mit Assemblersprache für diese Anweisungen, eine Syntax. Häufig erstellt diese Firma ein Assembler-Programm zum Kompilieren / Assemblieren dieser Assemblersprache. Verwenden Sie diese Syntax. Dies bedeutet jedoch nicht, dass jede andere Person auf dem Planeten, die einen Assembler für diesen Befehlssatz schreibt, diese Syntax verwenden muss. Dies ist beim x86-Befehlssatz sehr offensichtlich. Ebenso müssen alle Pseudo-Befehle wie das Popup oder die Makrosyntax oder andere Abkürzungen wie das b 1b von einem Assembler zum anderen beachtet werden. Und sehr oft nicht, sehen Sie dies bei ARM zum Beispiel das universelle Kommentarsymbol von, funktioniert nicht bei Gnu-Assembler, Sie müssen stattdessen @ verwenden. ARMs Assembler verwendet das Zeichen, (Anmerkung, ich schreibe meinen Arm-Assembler mit, @, um ihn portabel zu machen). Mit Gnu-Tools wird es noch schlimmer. Sie können beispielsweise C-Sprachen wie #define und / * comment * / in Ihren Assembler einfügen und den C-Compiler anstelle des Assemblers verwenden, und es wird funktionieren. Ich bevorzuge es, so rein wie möglich zu bleiben, um maximale Portabilität zu gewährleisten, aber Sie können natürlich auch die Funktionen des Tools verwenden.

CMP-Anweisung

Der CMP-Befehl vergleicht zwei Operanden. Es wird im Allgemeinen bei der bedingten Ausführung verwendet. Diese Anweisung subtrahiert im Grunde genommen einen Operanden vom anderen, um zu vergleichen, ob die Operanden gleich sind oder nicht. Die Ziel- oder Quelloperanden werden nicht gestört. Es wird zusammen mit der bedingten Sprunganweisung für die Entscheidungsfindung verwendet.

#1

  • FRATER DOMUS
  • 88 Beiträge
    • Standort: Südflorida, Vereinigte Staaten
    • Fraktion: Rabengarde, Astra Militarum

    (Entschuldigung, wenn dies im falschen Abschnitt des Forums gepostet wird.)

    Also habe ich vor einiger Zeit 10 Primaris Intercessors gekauft. Ging nicht durch die GW-Website, also war ich ein wenig besorgt darüber, was ich in der Post bekommen würde.

    Es dauerte eine Weile, bis ich ankam, und das Paket war viel kleiner als ich erwartet hatte. Ich dachte, die Jungs wären immer noch in ihren Sprues, aber zu meiner Überraschung war das ganze Zeug bereits ausgeschnitten und in einer Plastiktüte.

    Ich gehe davon aus, dass ich genug Arme, Beine, Köpfe und alles habe, um 10 Fürbitter zusammenzustellen. Das einzige Problem ist, das ist alles was kam. Nichts als die Teile. Meine Frage lautet also: Wo finde ich Anweisungen, die mir die richtigen Kombinationen aufzeigen, die ich beim Zusammenstellen dieser Typen verwenden kann?

    Ich habe vor, den Verkäufer zu erreichen, aber wenn er nicht antwortet, hoffe ich, dass jemand anders die Antwort hat, nach der ich suche.

    Mnemonik und Opcodes

    Jeder x86-Assembler-Befehl wird durch eine Mnemonik dargestellt, die häufig in Kombination mit einem oder mehreren Operanden einen oder mehrere als Opcode bezeichnete Bytes übersetzt, der NOP-Befehl beispielsweise in 0x90 und der HLT-Befehl in 0xF4. Es gibt potenzielle Opcodes ohne dokumentierte Mnemonik, die von verschiedenen Prozessoren möglicherweise unterschiedlich interpretiert werden, sodass sich ein Programm, das sie verwendet, inkonsistent verhält oder auf einigen Prozessoren sogar eine Ausnahme auslöst. Diese Opcodes tauchen häufig in Code-Schreibwettbewerben auf, um den Code zu verkleinern, schneller und eleganter zu machen oder nur die Fähigkeiten des Autors zu demonstrieren.

    Einführung

    Jahrelang verwendeten PC-Programmierer die x86-Assembly, um leistungskritischen Code zu schreiben. 32-Bit-PCs werden jedoch durch 64-Bit-PCs ersetzt, und der zugrunde liegende Assemblycode wurde geändert. Dieses Whitepaper ist eine Einführung in die x64-Assembly. Es sind keine Vorkenntnisse in x86-Code erforderlich, obwohl dies den Übergang erleichtert.

    x64 ist ein generischer Name für die 64-Bit-Erweiterungen der 32-Bit-x86-Befehlssatzarchitektur (ISA) von Intel und AMD. AMD führte die erste Version von x64 ein, ursprünglich x86-64 genannt und später in AMD64 umbenannt. Intel nannte ihre Implementierung IA-32e und dann EMT64. Es gibt einige leichte Inkompatibilitäten zwischen den beiden Versionen, aber der meiste Code funktioniert in beiden Versionen einwandfrei. Einzelheiten finden Sie in den Intel® 64- und IA-32-Architekturen-Software-Entwicklerhandbüchern und den AMD64 Architecture Tech Docs. Wir nennen diese Schnittmenge x64. Beides ist nicht mit der 64-Bit-Intel® Itanium®-Architektur zu verwechseln, die als IA-64 bezeichnet wird.

    Dieses Whitepaper behandelt keine Hardwaredetails wie Caches, Verzweigungsvorhersage und andere fortgeschrittene Themen. Am Ende des Artikels werden einige Referenzen zur weiteren Lektüre in diesen Bereichen angegeben.

    Assembly wird häufig für leistungskritische Teile eines Programms verwendet, obwohl es für die meisten Programmierer schwierig ist, einen guten C ++ - Compiler zu übertreffen. Assembly-Kenntnisse sind hilfreich für das Debuggen von Code. Manchmal erstellt ein Compiler falschen Assembly-Code, und das Durchlaufen des Codes in einem Debugger hilft, die Ursache zu lokalisieren. Code-Optimierer machen manchmal Fehler. Eine andere Verwendung für die Assemblierung ist die Verknüpfung mit oder das Korrigieren von Code, für den Sie keinen Quellcode haben. Durch Disassembly können Sie vorhandene ausführbare Dateien ändern / reparieren. Die Montage ist notwendig, wenn Sie wissen möchten, wie Ihre bevorzugte Sprache funktioniert - warum manche Dinge langsam und andere schnell sind. Schließlich sind Assembler-Code-Kenntnisse für die Diagnose von Malware unverzichtbar.

    Syntax

    Die x86-Assemblersprache hat zwei Hauptsyntaxzweige: Intel-Syntax, ursprünglich zur Dokumentation der x86-Plattform verwendet, und AT & T-Syntax. Intel-Syntax ist in der MS-DOS- und Windows-Welt vorherrschend, und die AT & T-Syntax ist in der Unix-Welt vorherrschend, da Unix bei AT & T Bell Labs erstellt wurde. Hier ist eine Zusammenfassung der Hauptunterschiede zwischen Intel-Syntax und AT & T-Syntax:

    AT & TIntel
    ParameterreihenfolgeQuelle vor dem Ziel.

    Ziel vor Quelle.
    ParametergrößeMnemonics sind mit einem Buchstaben versehen, der die Größe der Operanden angibt: q für qword, l für lang (dword), w für Wort und b für byte. Abgeleitet vom Namen des verwendeten Registers (z. rax, eax, axe, al implizieren q, l, w, b, beziehungsweise).
    Siegel Sofortige Werte, denen ein "$" vorangestellt ist, Register, denen ein "%" vorangestellt ist. Der Assembler erkennt automatisch die Art der Symbole, d. H. Ob es sich um Register, Konstanten oder etwas anderes handelt.
    Effektive Adressen Allgemeine Syntax von DISP (BASE, INDEX, SCALE). Beispiel: Arithmetische Ausdrücke in eckigen Klammern, zusätzlich Größenschlüsselwörter wie Byte, Wort, oder Dword müssen verwendet werden, wenn die Größe nicht aus den Operanden bestimmt werden kann. Beispiel:

    Viele x86-Assembler verwenden Intel-Syntax, einschließlich NASM, FASM, MASM, TASM und YASM. GAS unterstützt beide Syntaxen seit Version 2.10 über das .intel_syntax Richtlinie.

    Die Architektur

    Wenn Sie die Assembly für eine bestimmte Plattform lernen, müssen Sie zunächst den Registersatz lernen.

    Allgemeine Architektur
    Da die 64-Bit-Register den Zugriff für viele Größen und Positionen ermöglichen, definieren wir ein Byte als 8 Bits, ein Wort als 16 Bits, ein Doppelwort als 32 Bits, ein Vierfachwort als 64 Bits und ein Doppelvierfachwort als 128 Bits. Intel speichert Bytes "Little Endian", was bedeutet, dass niederwertige Bytes in niedrigeren Speicheradressen gespeichert werden.


    1 zeigt 16 64-Bit-Universalregister, von denen die ersten acht (aus historischen Gründen) als RAX, RBX, RCX, RDX, RBP, RSI, RDI und RSP bezeichnet sind. Die zweiten acht heißen R8-R15. Durch Ersetzen des anfänglichen R durch ein E in den ersten acht Registern ist es möglich, auf die unteren 32 Bits (EAX für RAX) zuzugreifen. In ähnlicher Weise ist für RAX, RBX, RCX und RDX der Zugriff auf die unteren 16 Bits möglich, indem das anfängliche R (AX für RAX) und das untere Byte davon durch Umschalten des X für L (AL für AX) entfernt werden. und das höhere Byte der niedrigen 16 Bits unter Verwendung eines H (AH für AX). Auf die neuen Register R8 bis R15 kann auf ähnliche Weise zugegriffen werden: R8 (qword), R8D (unteres dword), R8W (unteres Wort), R8B (MASM-Stil mit dem niedrigsten Byte, Intel-Stil R8L). Beachten Sie, dass es kein R8H gibt.

    Aufgrund von Codierungsproblemen im REX-Opcode-Präfix, das für die neuen Register verwendet wird, gibt es ungerade Einschränkungen beim Zugriff auf die Byte-Register: Ein Befehl kann nicht auf ein älteres hohes Byte (AH, BH, CH, DH) und eines der neuen Byte-Register gleichzeitig verweisen Zeit (wie z. B. R11B), es können jedoch ältere niedrige Bytes (AL, BL, CL, DL) verwendet werden. Dies wird durch Ändern von (AH, BH, CH, DH) in (BPL, SPL, DIL, SIL) für Anweisungen mit einem REX-Präfix erzwungen.

    Der 64-Bit-Befehlszeiger RIP zeigt auf den nächsten auszuführenden Befehl und unterstützt ein 64-Bit-Flachspeichermodell. Das Layout der Speicheradressen in aktuellen Betriebssystemen wird später behandelt.

    Der Stapelzeiger RSP zeigt auf den letzten auf den Stapel geschobenen Gegenstand, der zu niedrigeren Adressen hin wächst. Der Stack wird zum Speichern von Rücksprungadressen für Subroutinen, zum Übergeben von Parametern in höheren Sprachen wie C / C ++ und zum Speichern des in Aufrufkonventionen abgedeckten "Schattenraums" verwendet.

    Das RFLAGS-Register speichert Flags, die für die Ergebnisse von Operationen und zur Steuerung des Prozessors verwendet werden. Dies wird aus dem x86-32-Bit-Register EFLAGS gebildet, indem höhere 32 Bits hinzugefügt werden, die reserviert und derzeit nicht verwendet werden. In Tabelle 1 sind die nützlichsten Flags aufgeführt. Die meisten anderen Flags werden für Aufgaben auf Betriebssystemebene verwendet und sollten immer auf den zuvor gelesenen Wert gesetzt werden.

    Tabelle 1 - Gemeinsame Flaggen

    SymbolBitNameStellen Sie ein, wenn.
    CF.0TragenDie Operation hat einen Übertrag oder eine Ausleihe generiert
    PF2ParitätDas letzte Byte hat eine gerade Zahl von 1, sonst 0
    AF4EinstellenBezeichnet den Übertrag von binär codierten Dezimalzahlen im Byte
    ZF6NullErgebnis war 0
    SF7ZeichenDas höchstwertige Bit des Ergebnisses ist 1
    VON11ÜberlaufÜberlauf bei signierter Operation
    DF10RichtungAnweisungen für Richtungszeichenfolgen werden ausgeführt (Inkrementieren oder Dekrementieren)
    ICH WÜRDE21IdentifizierungDie Änderbarkeit zeigt das Vorhandensein eines CPUID-Befehls an


    Die Gleitkommaeinheit (FPU) enthält acht Register FPR0-FPR7, Status- und Steuerregister und einige andere Spezialregister. FPR0-7 kann jeweils einen Wert der in Tabelle 2 gezeigten Typen speichern. Gleitkommaoperationen entsprechen IEEE 754. Beachten Sie, dass die meisten C / C ++ - Compiler die 32- und 64-Bit-Typen als float und double unterstützen, nicht jedoch den 80-Bit-Typ ab montage lieferbar. Diese Register teilen sich den Speicherplatz mit den acht 64-Bit-MMX-Registern.

    Tabelle 2 - Gleitkommatypen

    DatentypLängePräzision (Bits)Dezimalstellen PräzisionDezimalbereich
    Mit einfacher Genauigkeit322471,18 * 10 ^ -38 bis 3,40 * 10 ^ 38
    Doppelte Genauigkeit6453152,23 * 10 ^ -308 bis 1,79 * 10 ^ 308
    Erweiterte Präzision8064193,37 * 10 ^ -4932 bis 1,18 * 10 ^ 4932


    BCD (Binary Coded Decimal) wird von einigen 8-Bit-Befehlen unterstützt, und ein von den Gleitkommaregistern unterstütztes Oddball-Format ergibt einen 80-Bit-BCD-Typ mit 17 Stellen.

    Die sechzehn 128-Bit-XMM-Register (acht mehr als x86) werden ausführlicher behandelt.

    Zu den endgültigen Registern gehören Segmentregister (die in x64 zumeist nicht verwendet werden), Steuerregister, Speicherverwaltungsregister, Debug-Register, Virtualisierungsregister und Leistungsregister, die alle Arten von internen Parametern nachverfolgen (Cache-Treffer / Fehler, Verzweigungstreffer / -fehler, ausgeführte Mikrooperationen, Zeitsteuerung) , und vieles mehr). Der Opcode mit der bemerkenswertesten Leistung ist RDTSC, der zum Zählen von Prozessorzyklen zum Profilieren kleiner Codeteile verwendet wird.

    Ausführliche Informationen finden Sie im fünfbändigen Set "Intel® 64- und IA-32-Architekturen - Software-Entwicklerhandbücher" unter http://www.intel.com/content/www/us/en/processors/architectures-software-developer- manuals.html. Sie stehen zum kostenlosen Download als PDF zur Verfügung, können auf CD bestellt werden und können oft kostenlos als Hardcover-Set bestellt werden, wenn sie aufgelistet sind.

    SIMD-Architektur
    SIMD-Befehle (Single Instruction Multiple Data) führen einen einzelnen Befehl für mehrere Daten gleichzeitig aus und werden häufig für Assembler-Routinen verwendet. MMX- und SSE-Befehle (unter Verwendung der MMX- bzw. XMM-Register) unterstützen SIMD-Operationen, die eine Anweisung für bis zu acht Daten gleichzeitig ausführen. Beispielsweise können mit MMX acht Bytes zu acht Bytes in einem Befehl hinzugefügt werden.

    Die acht 64-Bit-MMX-Register MMX0-MMX7 sind auf FPR0-7 ausgerichtet, was bedeutet, dass bei Code-Mixing-Operationen von FP und MMX darauf geachtet werden muss, dass die erforderlichen Werte nicht überschrieben werden. Die MMX-Befehle arbeiten mit Integer-Typen, sodass Byte-, Wort- und Doppelwortoperationen für Werte in den MMX-Registern parallel ausgeführt werden können. Die meisten MMX-Anweisungen beginnen mit 'P' für "Gepackt". Arithmetik, Verschieben / Drehen, Vergleich, z. B .: PCMPGTB "Vergleiche gepackte Ganzzahlen mit Vorzeichen für größer als".

    Die sechzehn 128-Bit-XMM-Register ermöglichen parallele Operationen mit vier Werten mit einfacher oder zwei Werten mit doppelter Genauigkeit pro Befehl. Einige Anweisungen funktionieren auch für Ganzzahlen aus gepackten Bytes, Wörtern, Doppelwörtern und Quadwörtern. Diese Anweisungen, die als Streaming SIMD Extensions (SSE) bezeichnet werden, gibt es in vielen Varianten: SSE, SSE2, SSE3, SSSE3, SSE4 und möglicherweise noch mehr, wenn diese gedruckt werden. Intel hat weitere Erweiterungen in dieser Richtung mit dem Namen Intel® Advanced Vector Extensions (Intel® AVX) mit einem neuen 256-Bit-breiten Datenpfad angekündigt. SSE-Anweisungen enthalten Verschieben, Arithmetik, Vergleichen, Mischen und Entpacken sowie bitweise Operationen für Gleitkomma- und Ganzzahltypen. Zu den Befehlsnamen gehören solche Schönheiten wie PMULHUW und RSQRTPS. Schließlich führte SSE einige Anweisungen zum Vorabrufen von Speicher (für die Leistung) und zum Speichern von Zäunen (für die Sicherheit mit mehreren Threads) ein.

    In Tabelle 3 sind einige Befehlssätze, die Registertypen, die Anzahl der parallel bearbeiteten Elemente und der Elementtyp aufgeführt. Mit SSE3 und den 128-Bit-XMM-Registern können Sie beispielsweise 2 (64-Bit-) Gleitkommawerte oder sogar 16 (Byte-) Ganzzahlwerte parallel verarbeiten.

    Um herauszufinden, welche Technologien ein bestimmter Chip unterstützt, gibt es einen CPUID-Befehl, der prozessorspezifische Informationen zurückgibt.

    Tisch 3

    TechnologieRegistergröße / -typGegenstandsartArtikel parallel
    MMX64 MMXGanze Zahl8, 4, 2, 1
    SSE64 MMXGanze Zahl8,4,2,1
    SSE128 XMMSchweben4
    SSE2 / SSE3 / SSSE3.64 MMXGanze Zahl2,1
    SSE2 / SSE3 / SSSE3.128 XMMSchweben2
    SSE2 / SSE3 / SSSE3.128 XMMGanze Zahl16,8,4,2,1

    Bedingungsloser Sprung

    Wie bereits erwähnt, wird dies durch den JMP-Befehl ausgeführt. Bei der bedingten Ausführung wird häufig die Steuerung an die Adresse eines Befehls übertragen, der nicht dem aktuell ausgeführten Befehl folgt. Die Übertragung der Kontrolle kann vorwärts erfolgen, um einen neuen Befehlssatz auszuführen, oder rückwärts, um dieselben Schritte erneut auszuführen.

    Register

    x86-Prozessoren verfügen über eine Sammlung von Registern, die als Speicher für Binärdaten verwendet werden können. Zusammen werden die Daten- und Adressregister als allgemeine Register bezeichnet. Jedes Register hat einen besonderen Zweck zusätzlich zu dem, was sie alle tun können:

    • AX multiplizieren / teilen, String laden & speichern
    • CX-Anzahl für Zeichenfolgenoperationen und -verschiebungen
    • DX-Port-Adresse für IN und OUT
    • BX-Indexregister für MOVE
    • SP zeigt auf die Stapelspitze
    • BP zeigt auf die Basis des Stapelrahmens
    • SI zeigt auf eine Quelle in Stream-Operationen
    • DI zeigt auf ein Ziel in Stream-Operationen

    Neben den allgemeinen Registern gibt es zusätzlich die:

    • IP-Befehlszeiger
    • FLAGGEN
    • Segmentregister (CS, DS, ES, FS, GS, SS), die bestimmen, wo ein 64k-Segment beginnt (kein FS und GS in 80286 und früher)
    • Zusätzliche Erweiterungsregister (MMX, 3DNow !, SSE usw.) (nur Pentium und höher).

    Das IP-Register zeigt auf den Speicheroffset des nächsten Befehls im Codesegment (es zeigt auf das erste Byte des Befehls). Auf das IP-Register kann der Programmierer nicht direkt zugreifen.

    Die x86-Register können mithilfe der MOV-Anweisungen verwendet werden. Zum Beispiel in der Intel-Syntax:

    Werkzeuge

    Assembler
    Eine Internetsuche zeigt x64-fähige Assembler wie den Netwide Assembler NASM, einen NASM-Rewrite namens YASM, den schnellen Flat Assembler FASM und den traditionellen Microsoft MASM. Es gibt sogar eine kostenlose IDE für x86- und x64-Assembly namens WinASM. Jeder Assembler unterstützt die Makros und die Syntax anderer Assembler unterschiedlich, aber der Assembler-Code ist für Assembler wie C ++ oder Java * nicht quellkompatibel.

    Für die folgenden Beispiele verwende ich die 64-Bit-Version von MASM, ML64.EXE, die im Plattform-SDK frei verfügbar ist. Beachten Sie für die folgenden Beispiele, dass die MASM-Syntax von der Form ist Anweisungsziel, Quelle

    Einige Assembler vertauschen Quelle und Ziel. Lesen Sie Ihre Dokumentation daher sorgfältig durch.

    C / C ++ - Compiler
    C / C ++ - Compiler ermöglichen häufig das Einbetten von Assemblys in den Code mithilfe der Inline-Assembly. Microsoft Visual Studio * C / C ++ hat dies jedoch für x64-Code entfernt, was die Aufgabe des Codeoptimierers wahrscheinlich vereinfacht. Dies lässt zwei Optionen übrig: Verwenden Sie separate Assembly-Dateien und einen externen Assembler oder verwenden Sie Intrinsics aus der Header-Datei "intrn.h" (siehe Birtolo und MSDN). Andere Compiler bieten ähnliche Optionen.

    Einige Gründe für die Verwendung von Intrinsics:

    • Inline asm wird in x64 nicht unterstützt.
    • Benutzerfreundlichkeit: Sie können Variablennamen verwenden, anstatt die Registerzuordnung manuell zu jonglieren.
    • Plattformübergreifender als Assembler: Der Compilerhersteller kann die Eigenschaften auf verschiedene Architekturen portieren.
    • Der Optimierer arbeitet besser mit Intrinsics.

    Beispielsweise hat Microsoft Visual Studio * 2008 eine intrinsische

    unsigned short _rot16 (unsigned short a, unsigned char b)

    Dadurch werden die Bits in einem 16-Bit-Wert um b Bits nach rechts gedreht und die Antwort zurückgegeben. Dies in C zu tun, gibt

    unsigned short a1 = (b >> c) | (b

    Dies wird auf fünfzehn Assembly-Anweisungen erweitert (bei Debug-Builds - bei Release-Builds wurde durch die Optimierung des gesamten Programms die Trennung erschwert, die Länge war jedoch ähnlich), während die entsprechende intrinsische Methode verwendet wurde

    vorzeichenloser Kurzschluss a2 = _rotr16 (b, c),

    wird auf vier Anweisungen erweitert. Weitere Informationen finden Sie in der Header-Datei und in der Dokumentation.

    Segmentierte Adressierung

    Die x86-Architektur im realen und virtuellen 8086-Modus verwendet einen Prozess namens Segmentierung Speicher zu adressieren, nicht die flaches Speichermodell in vielen anderen Umgebungen verwendet. Die Segmentierung umfasst das Zusammensetzen einer Speicheradresse aus zwei Teilen, a Segment und ein OffsetDas Segment zeigt auf den Anfang einer 64-KB-Adressgruppe und der Versatz bestimmt, wie weit die gewünschte Adresse von dieser Anfangsadresse entfernt ist. Bei der segmentierten Adressierung werden zwei Register für eine vollständige Speicheradresse benötigt. Eine um das Segment zu halten, die andere um den Offset zu halten. Um wieder in eine flache Adresse zu übersetzen, wird der Segmentwert um vier Bits nach links verschoben (entsprechend der Multiplikation mit 2 4 oder 16) und dann zum Offset addiert, um die vollständige Adresse zu bilden, wodurch die 64k-Grenze durch geschickte Auswahl von Adressen überschritten werden kann Dies macht die Programmierung jedoch erheblich komplexer.

    Im Real-Modus / Nur geschützt: Wenn DS beispielsweise die Hexadezimalzahl 0xDEAD enthält und DX die Zahl 0xCAFE enthält, verweisen sie zusammen auf die Speicheradresse 0xDEAD * 0x10 + 0xCAFE = 0xEB5CE. Daher kann die CPU im Real-Modus bis zu 1.048.576 Byte (1 MB) adressieren. Durch Kombinieren Segment und Offset Werte finden wir eine 20-Bit-Adresse.

    Der ursprüngliche IBM PC beschränkte Programme auf 640 KB, aber eine erweiterte Speicherspezifikation wurde verwendet, um ein Bank-Switching-Schema zu implementieren, das nicht mehr verwendet wurde, wenn spätere Betriebssysteme wie Windows die größeren Adressbereiche neuerer Prozessoren verwendeten und ihren eigenen virtuellen Speicher implementierten Schemata.

    Der geschützte Modus, der mit Intel 80286 begann, wurde von OS / 2 verwendet. Mehrere Mängel, wie die Unfähigkeit, auf das BIOS zuzugreifen, und die Unfähigkeit, ohne Zurücksetzen des Prozessors in den Real-Modus zurückzukehren, verhinderten, dass der 80286 auch weiterhin auf die Adressierung des Speichers in 16-Bit-Segmenten beschränkt war, dh nur 2 16 Bytes (64 Kilobyte) kann gleichzeitig zugegriffen werden. Um auf die erweiterte Funktionalität des 80286 zuzugreifen, versetzte das Betriebssystem den Prozessor in den geschützten Modus und ermöglichte eine 24-Bit-Adressierung und damit 2 24-Byte-Speicher (16 Megabyte).

    Im geschützten Modus kann der Segmentselektor in drei Teile unterteilt werden: einen 13-Bit-Index, a Tabellenanzeige Bit, das bestimmt, ob der Eintrag im GDT oder LDT ist, und ein 2-Bit Angeforderte BerechtigungsstufeWeitere Informationen finden Sie unter x86-Speichersegmentierung.

    Bei der Bezugnahme auf eine Adresse mit einem Segment und einem Offset wird die Notation von Segment:Offset verwendet wird, so wird im obigen Beispiel die Flache Adresse 0xEB5CE kann als 0xDEAD: 0xCAFE oder als Segment-Offset-Registerpaar DS: DX geschrieben werden.

    Es gibt einige spezielle Kombinationen von Segmentregistern und allgemeinen Registern, die auf wichtige Adressen verweisen:

    • CS: IP (CS ist Codesegment, IP ist Anweisungszeiger) zeigt auf die Adresse, an der der Prozessor das nächste Byte abruft.
    • SS: SP (SS ist Stapelsegment, SP ist Stapelzeiger) zeigt auf die Adresse des obersten Stapels, d. h. auf das zuletzt übertragene Byte.
    • DS: SI (DS ist Datensegment, SI ist Quellenindex) wird häufig verwendet, um auf Zeichenfolgendaten zu verweisen, die in ES: DI kopiert werden sollen.
    • ES: DI (ES ist Zusätzliches SegmentIst DI Zielindex) wird normalerweise verwendet, um auf das Ziel für eine Zeichenfolgekopie zu verweisen, wie oben erwähnt.

    Der Intel 80386 verfügte über drei Betriebsmodi: Real-Modus, Geschützter Modus und Virtueller Modus. Der im 80286 eingeführte geschützte Modus wurde erweitert, um dem 80386 die Adressierung von bis zu 4 GB Arbeitsspeicher zu ermöglichen, dem brandneuen virtuellen 8086-Modus (VM86) ermöglichte die Ausführung eines oder mehrerer Programme im Real-Modus in einer geschützten Umgebung, die weitgehend den Real-Modus emulierte, obwohl einige Programme nicht kompatibel waren (in der Regel aufgrund von Tricks bei der Speicheradressierung oder der Verwendung nicht angegebener Op-Codes).

    Das 32-Bit-Flachspeichermodell des erweiterten geschützten Modus des 80386 ist möglicherweise die wichtigste Funktionsänderung für die x86-Prozessorfamilie, bis AMD 2003 x86-64 herausbrachte, da es dazu beitrug, Windows 3.1 (das auf dem geschützten Modus beruhte) in großem Maßstab zu übernehmen ), da Windows jetzt mithilfe von virtuellem Speicher und einfachem Multitasking viele Anwendungen auf einmal ausführen kann, einschließlich DOS-Anwendungen.

    Grundlagen der Anleitung

    Adressierungsmodi
    Bevor Sie einige grundlegende Anweisungen behandeln, müssen Sie die Adressierungsmodi kennen, mit denen eine Anweisung auf Register oder Speicher zugreifen kann. Die folgenden Adressierungsmodi werden häufig mit Beispielen verwendet:

    • Sofort: Der Wert wird in der Anweisung gespeichert. ADD EAX, 14, addiere 14 zu 32-Bit-EAX
    • Registrieren Sie sich, um sich zu registrieren ADD R8L, AL, addiere 8-Bit-AL in R8L
    • Indirekt: Dies ermöglicht die Verwendung einer 8-, 16- oder 32-Bit-Verschiebung, beliebiger Universalregister für Basis und Index sowie einer Skala von 1, 2, 4 oder 8, um den Index zu multiplizieren. Technisch kann diesen auch das Segment FS: oder GS: vorangestellt werden, dies ist jedoch selten erforderlich. MOV R8W, 12348 * RAX + RCX, Wort an Adresse 8 * RAX + RCX + 1234 in R8W verschieben

    Es gibt viele legale Möglichkeiten, dies zu schreiben. Die folgenden sind äquivalent dword ptr teilt dem Assembler mit, wie der Code codiert werden soll MOV Anweisung.

    • RIP-relative Adressierung: Dies ist neu für x64 und ermöglicht den Zugriff auf Datentabellen und dergleichen im Code relativ zum aktuellen Befehlszeiger, wodurch positionsunabhängiger Code einfacher zu implementieren ist.

    MOV AL, RIP, RIP zeigt auf die nächste Anweisung, auch bekannt als NOP
    NOP

    Leider erlaubt MASM diese Form von Opcode nicht, andere Assembler wie FASM und YASM jedoch. Stattdessen bettet MASM die RIP-relative Adressierung implizit ein.
    MOV EAX, TABLE, verwendet die RIP-relative Adressierung, um die Tabellenadresse abzurufen

    • Spezialfälle: Einige Opcodes verwenden Register auf eindeutige Weise basierend auf dem Opcode. Beispiel: Ganzzahlige Division mit Vorzeichen IDIV Bei einem 64-Bit-Operandenwert wird der 128-Bit-Wert in geteilt RDX: RAX durch den Wert, Speichern des Ergebnisses in RAX und der Rest in RDX .

    Befehlssatz
    In Tabelle 4 sind einige allgemeine Anweisungen aufgeführt. * gibt an, dass es sich bei diesem Eintrag um mehrere Opcodes handelt, wobei * ein Suffix bedeutet.

    Tabelle 4 - Allgemeine Opcodes

    OpcodeBedeutungOpcodeBedeutung
    MOVZum / vom / zwischen Speicher und Registern wechselnUND / ODER / XOR / NICHTBitweise Operationen
    CMOV *Verschiedene bedingte BewegungenSHR / SARNach rechts schieben logisch / arithmetisch
    XCHGAustauschSHL / SALNach links schieben logisch / arithmetisch
    BSWAPBytetauschROR / ROLNach rechts / links drehen
    PUSH POPStapelverwendungRCR / RCLDrehen Sie rechts / links durch das Tragebit
    ADD / ADCHinzufügen / mit tragenBT / BTS / BTRBit Test / und Set / und Reset
    SUB / SBCSubtrahieren / mit CarryJMPBedingungsloser Sprung
    MUL / IMULMultiplizieren / nicht signiertJE / JNE / JC / JNC / J *Springe wenn gleich / nicht gleich / trage / trage nicht / viele andere
    DIV / IDIVTeilen / ohne VorzeichenLOOP / LOOPE / LOOPNESchleife mit ECX
    INC / DECInkrementieren / DekrementierenCALL / RETUnterprogramm aufrufen / zurück
    NEGNegierenNOPKeine Operation
    CMPVergleichen SieCPUIDCPU-Informationen

    Eine übliche Anweisung ist die LOOP-Anweisung, die je nach Verwendung RCX, ECX oder CX dekrementiert und dann springt, wenn das Ergebnis nicht 0 ist.

    Weniger gebräuchliche Opcodes implementieren Zeichenfolgenoperationen, Präfixe für Wiederholungsanweisungen, E / A-Befehle für Ports, Setzen / Löschen / Testen von Fließkommaoperationen (beginnen normalerweise mit einem F und unterstützen das Verschieben von Ganzzahlen, Arithmetik, Vergleich, Transzendental und Algebraik und Steuerfunktionen), Cache- und Speicher-Opcodes für Multithreading- und Leistungsprobleme und mehr. Das Softwareentwicklerhandbuch für Intel® 64- und IA-32-Architekturen, Band 2, besteht aus zwei Teilen und behandelt jeden Opcode im Detail.

    Beispiel

    CMP wird häufig zum Vergleichen verwendet, ob ein Zählerwert die Häufigkeit erreicht hat, mit der eine Schleife ausgeführt werden muss. Betrachten Sie den folgenden typischen Zustand -

    Bedingungsloser Sprung

    Wie bereits erwähnt, wird dies durch den JMP-Befehl ausgeführt. Bei der bedingten Ausführung wird häufig die Steuerung an die Adresse eines Befehls übertragen, der nicht dem aktuell ausgeführten Befehl folgt. Die Übertragung der Kontrolle kann vorwärts erfolgen, um einen neuen Befehlssatz auszuführen, oder rückwärts, um dieselben Schritte erneut auszuführen.

    Syntax

    Der JMP-Befehl enthält einen Labelnamen, über den der Kontrollfluss sofort übertragen wird. Die Syntax des JMP-Befehls lautet:

    Beispiel

    Der folgende Codeausschnitt veranschaulicht die JMP-Anweisung:

    Ausführungsmodi

    Die x86-Prozessoren unterstützen fünf Betriebsmodi für x86-Code. Realer Modus, Sicherheitsmodus, Langer Modus, Virtual 86-Modus, und Systemverwaltungsmodus, in denen einige Anweisungen zur Verfügung stehen und andere nicht. Auf den 16-Bit-x86-Prozessoren (8086, 8088, 80186, 80188 und 80286) ist eine 16-Bit-Teilmenge von Anweisungen verfügbar. Diese Anweisungen sind auf allen x86-Prozessoren im Real-Modus und im geschützten 16-Bit-Modus verfügbar (Ab 80286) sind zusätzliche Anweisungen zum geschützten Modus verfügbar. On the 80386 and later, 32-bit instructions (including later extensions) are also available in all modes, including real mode, on these CPUs, V86 mode and 32-bit protected mode are added, with additional instructions provided in these modes to manage their features. SMM, with some of its own special instructions, is available on some Intel i386SL, i486 and later CPUs. Finally, in long mode (AMD Opteron onwards), 64-bit instructions, and more registers, are also available. The instruction set is similar in each mode but memory addressing and word size vary, requiring different programming strategies.

    The modes in which x86 code can be executed in are:

    Betriebssysteme

    64-bit systems allow addressing 2 to the 64th power bytes of data in theory, but no current chips allow accessing all 16 exabytes (18,446,744,073,709,551,616 bytes). For example, AMD architecture uses only the lower 48 bits of an address, and bits 48 through 63 must be a copy of bit 47 or the processor raises an exception. Thus addresses are 0 through 00007FFF`FFFFFFFF, and from FFFF8000`00000000 through FFFFFFFF`FFFFFFFF, for a total of 256 TB (281,474,976,710,656 bytes) of usable virtual address space. Another downside is that addressing all 64 bits of memory requires a lot more paging tables for the OS to store, using valuable memory for systems with less than all 16 exabytes installed. Note these are virtual addresses, not physical addresses.

    As a result, many operating systems use the higher half of this space for the OS, starting at the top and growing down, while user programs use the lower half, starting at the bottom and growing upwards. Current Windows* versions use 44 bits of addressing (16 terabytes = 17,592,186,044,416 bytes). The resulting addressing is shown in Figure 2. The resulting addresses are not too important for user programs since addresses are assigned by the OS, but the distinction between user addresses and kernel addresses are useful for debugging.

    A final OS-related item relates to multithreaded programming, but this topic is too large to cover here. The only mention is that there are memory barrier opcodes for helping to keep shared resources uncorrupted.

    Conditional Jump

    If some specified condition is satisfied in conditional jump, the control flow is transferred to a target instruction. There are numerous conditional jump instructions depending upon the condition and data.

    Following are the conditional jump instructions used on signed data used for arithmetic operations −

    InstructionBeschreibungFlags tested
    JE/JZJump Equal or Jump ZeroZF
    JNE/JNZJump not Equal or Jump Not ZeroZF
    JG/JNLEJump Greater or Jump Not Less/EqualOF, SF, ZF
    JGE/JNLJump Greater/Equal or Jump Not LessOF, SF
    JL/JNGEJump Less or Jump Not Greater/EqualOF, SF
    JLE/JNGJump Less/Equal or Jump Not GreaterOF, SF, ZF

    Following are the conditional jump instructions used on unsigned data used for logical operations −

    InstructionBeschreibungFlags tested
    JE/JZJump Equal or Jump ZeroZF
    JNE/JNZJump not Equal or Jump Not ZeroZF
    JA/JNBEJump Above or Jump Not Below/EqualCF, ZF
    JAE/JNBJump Above/Equal or Jump Not BelowCF.
    JB/JNAEJump Below or Jump Not Above/EqualCF.
    JBE/JNAJump Below/Equal or Jump Not AboveAF, CF

    The following conditional jump instructions have special uses and check the value of flags −

    InstructionBeschreibungFlags tested
    JXCZJump if CX is Zerokeiner
    JCJump If CarryCF.
    JNCJump If No CarryCF.
    JOJump If OverflowOF
    JNOJump If No OverflowOF
    JP/JPEJump Parity or Jump Parity EvenPF
    JNP/JPOJump No Parity or Jump Parity OddPF
    JSJump Sign (negative value)SF
    JNSJump No Sign (positive value)SF

    The syntax for the J set of instructions −

    Switching modes

    The processor runs in real mode immediately after power on, so an operating system kernel, or other program, must explicitly switch to another mode if it wishes to run in anything but real mode. Switching modes is accomplished by modifying certain bits of the processor's control registers after some preparation, and some additional setup may be required after the switch.

    Calling Conventions

    Interfacing with operating system libraries requires knowing how to pass parameters and manage the stack. These details on a platform are called a calling convention.

    A common x64 calling convention is the Microsoft 64 calling convention used for C style function calling (see MSDN, Chen, and Pietrek). Under Linux* this would be called an Application Binary Interface (ABI). Note the calling convention covered here is different than the one used on x64 Linux* systems.

    For the Microsoft* x64 calling convention, the additional register space let fastcall be the only calling convention (under x86 there were many: stdcall, thiscall, fastcall, cdecl, etc.). The rules for interfacing with C/C++ style functions:

    • RCX, RDX, R8, R9 are used for integer and pointer arguments in that order left to right.
    • XMM0, 1, 2, and 3 are used for floating point arguments.
    • Additional arguments are pushed on the stack left to right.
    • Parameters less than 64 bits long are not zero extended, the high bits contain garbage.
    • It is the caller's responsibility to allocate 32 bytes of "shadow space" (for storing RCX, RDX, R8, and R9 if needed) before calling the function.
    • It is the caller's responsibility to clean the stack after the call.
    • Integer return values (similar to x86) are returned in RAX if 64 bits or less.
    • Floating point return values are returned in XMM0.
    • Larger return values (structs) have space allocated on the stack by the caller, and RCX then contains a pointer to the return space when the callee is called. Register usage for integer parameters is then pushed one to the right. RAX returns this address to the caller.
    • The stack is 16-byte aligned. The "call" instruction pushes an 8-byte return value, so the all non-leaf functions must adjust the stack by a value of the form 16n+8 when allocating stack space.
    • Registers RAX, RCX, RDX, R8, R9, R10, and R11 are considered volatile and must be considered destroyed on function calls.
    • RBX, RBP, RDI, RSI, R12, R14, R14, and R15 must be saved in any function using them.
    • Note there is no calling convention for the floating point (and thus MMX) registers.
    • Further details (varargs, exception handling, stack unwinding) are at Microsoft's site.

    Instruction types

    In general, the features of the modern x86 instruction set are:

    • A compact encoding
      • Variable length and alignment independent (encoded as little endian, as is all data in the x86 architecture)
      • Mainly one-address and two-address instructions, that is to say, the first operand is also the destination.
      • Memory operands as both source and destination are supported (frequently used to read/write stack elements addressed using small immediate offsets).
      • Both general and implicit register usage, although all seven (counting ebp ) general registers in 32-bit mode, and all fifteen (counting rbp ) general registers in 64-bit mode, can be freely used as accumulators or for addressing, most of them are also implicitly used by certain (more or less) special instructions, affected registers must therefore be temporarily preserved (normally stacked), if active during such instruction sequences.
    • Produces conditional flags implicitly through most integer ALU instructions.
    • Supports various addressing modes including immediate, offset, and scaled index but not PC-relative, except jumps (introduced as an improvement in the x86-64 architecture).
    • Includes floating point to a stack of registers.
    • Contains special support for atomic read-modify-write instructions ( xchg , cmpxchg / cmpxchg8b , xadd , and integer instructions which combine with the lock prefix)
    • SIMD instructions (instructions which perform parallel simultaneous single instructions on many operands encoded in adjacent cells of w >Stack instructions

      The x86 architecture has hardware support for an execution stack mechanism. Instructions such as push , pop , call and ret are used with the properly set up stack to pass parameters, to allocate space for local data, and to save and restore call-return points. The ret Größe instruction is very useful for implementing space efficient (and fast) calling conventions where the callee is responsible for reclaiming stack space occupied by parameters.

      When setting up a stack frame to hold local data of a recursive procedure there are several choices, the high level enter instruction (introduced with the 80386) takes a procedure-nesting-depth argument as well as a local size argument, and may be faster than more explicit manipulation of the registers (such as push bp , mov bp, sp , sub sp, Größe ). Whether it is faster or slower depends on the particular x86-processor implementation as well as the calling convention used by the compiler, programmer or particular program code, most x86 code is intended to run on x86-processors from several manufacturers and on different technological generations of processors, which implies highly varying microarchitectures and microcode solutions as well as varying gate- and transistor-level design choices.

      The full range of addressing modes (including sofortig und base+offset) even for instructions such as push and pop , makes direct usage of the stack for integer, floating point and address data simple, as well as keeping the ABI specifications and mechanisms relatively simple compared to some RISC architectures (require more explicit call stack details).

      Integer ALU instructions

      x86 assembly has the standard mathematical operations, add , sub , mul , with idiv , the logical operators and , or , xor , neg , bitshift arithmetic and logical, sal / sar , shl / shr , rotate with and without carry, rcl / rcr , rol / ror , a complement of BCD arithmetic instructions, aaa , aad , daa and others.

      Fazit

      This has been a necessarily brief introduction to x64 assembly programming. The next step is to browse the Intel® 64 and IA-32 Architectures Software Developer's Manuals. Volume 1 contains the architecture details and is a good start if you know assembly. Other places are assembly books or online assembly tutorials. To get an understanding of how your code executes, it is instructive to step through code in debugger, looking at the disassembly, until you can read assembly code as well as your favorite language. For C/C++ compilers, debug builds are much easier to read than release builds so be sure to start there. Finally, read the forums at masm32.com for a lot of material.

      Floating-point instructions

      x86 assembly language includes instructions for a stack-based floating-point unit (FPU). The FPU was an optional separate coprocessor for the 8086 through the 80386, it was an on-chip option for the 80486 series, and it is a standard feature in every Intel x86 CPU since the 80486, starting with the Pentium. The FPU instructions include addition, subtraction, negation, multiplication, division, remainder, square roots, integer truncation, fraction truncation, and scale by power of two. The operations also include conversion instructions, which can load or store a value from memory in any of the following formats: binary-coded decimal, 32-bit integer, 64-bit integer, 32-bit floating-point, 64-bit floating-point or 80-bit floating-point (upon loading, the value is converted to the currently used floating-point mode). x86 also includes a number of transcendental functions, including sine, cosine, tangent, arctangent, exponentiation with the base 2 and logarithms to bases 2, 10, or e.

      The stack register to stack register format of the instructions is usually fop st, st(n) or fop st(n), st , where st is equivalent to st(0) , and st(n) is one of the 8 stack registers ( st(0) , st(1) , . st(7) ). Like the integers, the first operand is both the first source operand and the destination operand. fsubr and fdivr should be singled out as first swapping the source operands before performing the subtraction or division. The addition, subtraction, multiplication, division, store and comparison instructions include instruction modes that pop the top of the stack after their operation is complete. So, for example, faddp st(1), st performs the calculation st(1) = st(1) + st(0) , then removes st(0) from the top of stack, thus making what was the result in st(1) the top of the stack in st(0) .

      SIMD instructions

      Modern x86 CPUs contain SIMD instructions, which largely perform the same operation in parallel on many values encoded in a wide SIMD register. Various instruction technologies support different operations on different register sets, but taken as complete whole (from MMX to SSE4.2) they include general computations on integer or floating point arithmetic (addition, subtraction, multiplication, shift, minimization, maximization, comparison, division or square root). So for example, paddw mm0, mm1 performs 4 parallel 16-bit (indicated by the w ) integer adds (indicated by the padd ) of mm0 values to mm1 and stores the result in mm0 . Streaming SIMD Extensions or SSE also includes a floating point mode in which only the very first value of the registers is actually modified (expanded in SSE2). Some other unusual instructions have been added including a sum of absolute differences (used for motion estimation in video compression, such as is done in MPEG) and a 16-bit multiply accumulation instruction (useful for software-based alpha-blending and digital filtering). SSE (since SSE3) and 3DNow! extensions include addition and subtraction instructions for treating paired floating point values like complex numbers.

      These instruction sets also include numerous fixed sub-word instructions for shuffling, inserting and extracting the values around within the registers. In addition there are instructions for moving data between the integer registers and XMM (used in SSE)/FPU (used in MMX) registers.

      Data manipulation instructions

      The x86 processor also includes complex addressing modes for addressing memory with an immediate offset, a register, a register with an offset, a scaled register with or without an offset, and a register with an optional offset and another scaled register. So for example, one can encode mov eax, Table + ebx + esi*4 as a single instruction which loads 32 bits of data from the address computed as (Table + ebx + esi * 4) offset from the ds selector, and stores it to the eax register. In general x86 processors can load and use memory matched to the size of any register it is operating on. (The SIMD instructions also include half-load instructions.)

      The x86 instruction set includes string load, store, move, scan and compare instructions ( lods , stos , movs , scas and cmps ) which perform each operation to a specified size ( b for 8-bit byte, w for 16-bit word, d for 32-bit double word) then increments/decrements (depending on DF, direction flag) the implicit address register ( si for lods , di for stos and scas , and both for movs and cmps ). For the load, store and scan operations, the implicit target/source/comparison register is in the al , ax or eax register (depending on size). The implicit segment registers used are ds for si and es for di . The cx or ecx register is used as a decrementing counter, and the operation stops when the counter reaches zero or (for scans and comparisons) when inequality is detected.

      The stack is implemented with an implicitly decrementing (push) and incrementing (pop) stack pointer. In 16-bit mode, this implicit stack pointer is addressed as SS:SP, in 32-bit mode it is SS:ESP, and in 64-bit mode it is RSP. The stack pointer actually points to the last value that was stored, under the assumption that its size will match the operating mode of the processor (i.e., 16, 32, or 64 bits) to match the default width of the push / pop / call / ret instructions. Also included are the instructions enter and leave which reserve and remove data from the top of the stack while setting up a stack frame pointer in bp / ebp / rbp . However, direct setting, or addition and subtraction to the sp / esp / rsp register is also supported, so the enter / leave instructions are generally unnecessary.

      This code in the beginning of a function:

      . is functionally equivalent to just:

      Other instructions for manipulating the stack include pushf / popf for storing and retrieving the (E)FLAGS register. The pusha / popa instructions will store and retrieve the entire integer register state to and from the stack.

      Values for a SIMD load or store are assumed to be packed in adjacent positions for the SIMD register and will align them in sequential little-endian order. Some SSE load and store instructions require 16-byte alignment to function properly. The SIMD instruction sets also include "prefetch" instructions which perform the load but do not target any register, used for cache loading. The SSE instruction sets also include non-temporal store instructions which will perform stores straight to memory without performing a cache allocate if the destination is not already cached (otherwise it will behave like a regular store.)

      Most generic integer and floating point (but no SIMD) instructions can use one parameter as a complex address as the second source parameter. Integer instructions can also accept one memory parameter as a destination operand.

      Program flow

      The x86 assembly has an unconditional jump operation, jmp , which can take an immediate address, a register or an indirect address as a parameter (note that most RISC processors only support a link register or short immediate displacement for jumping).

      Also supported are several conditional jumps, including jz (jump on zero), jnz (jump on non-zero), jg (jump on greater than, signed), jl (jump on less than, signed), ja (jump on above/greater than, unsigned), jb (jump on below/less than, unsigned). These conditional operations are based on the state of specific bits in the (E)FLAGS register. Many arithmetic and logic operations set, clear or complement these flags depending on their result. The comparison cmp (compare) and test instructions set the flags as if they had performed a subtraction or a bitwise AND operation, respectively, without altering the values of the operands. There are also instructions such as clc (clear carry flag) and cmc (complement carry flag) which work on the flags directly. Floating point comparisons are performed via fcom or ficom instructions which eventually have to be converted to integer flags.

      Each jump operation has three different forms, depending on the size of the operand. EIN kurz jump uses an 8-bit signed operand, which is a relative offset from the current instruction. EIN near jump is similar to a short jump but uses a 16-bit signed operand (in real or protected mode) or a 32-bit signed operand (in 32-bit protected mode only). EIN weit jump is one that uses the full segment base:offset value as an absolute address. There are also indirect and indexed forms of each of these.

      In addition to the simple jump operations, there are the call (call a subroutine) and ret (return from subroutine) instructions. Before transferring control to the subroutine, call pushes the segment offset address of the instruction following the call onto the stack, ret pops this value off the stack, and jumps to it, effectively returning the flow of control to that part of the program. In the case of a far call , the segment base is pushed following the offset, far ret pops the offset and then the segment base to return.

      There are also two similar instructions, int (interrupt), which saves the current (E)FLAGS register value on the stack, then performs a far call , except that instead of an address, it uses an interrupt vector, an index into a table of interrupt handler addresses. Typically, the interrupt handler saves all other CPU registers it uses, unless they are used to return the result of an operation to the calling program (in software called interrupts). The matching return from interrupt instruction is iret , which restores the flags after returning. Soft Interrupts of the type described above are used by some operating systems for system calls, and can also be used in debugging hard interrupt handlers. Hard interrupts are triggered by external hardware events, and must preserve all register values as the state of the currently executing program is unknown. In Protected Mode, interrupts may be set up by the OS to trigger a task switch, which will automatically save all registers of the active task.

      Lassen Sie Ihren Kommentar