Assembler-Befehle Intel 8086 Referenzbuch. Assembler-Programmierung für Anfänger und mehr

Die ersten Mikroprozessoren waren 4-Bit-Geräte. Das bedeutet, dass sie jeweils nur vier Bits an Informationen verarbeiten konnten. Um mehr als vier Bits zu verarbeiten, mussten sie mehrere aufeinanderfolgende Operationen ausführen. Dies verlangsamte natürlich die Arbeit.

Der erste industrielle 8-Bit-Mikroprozessor (der 8 Bit Informationen gleichzeitig verarbeitet) war der 8008-Mikroprozessor, der 1972 von Intel herausgebracht wurde. Er gilt als der beste 8-Bit-Mikroprozessor der ersten Generation. In seiner Architektur ähnelt dieser Mikroprozessor einem Taschenrechner; Es verfügt über einen Akkumulator, sechs Allzweckregister, einen Stapelzeiger (ein spezielles Arbeitszellenadressregister), acht Adressregister und spezielle Anweisungen für die Dateneingabe und -ausgabe. 1973 brachte Intel eine zweite Generation des Mikroprozessors 8008 auf den Markt, den 8080.

Im Vergleich zum 8008-Mikroprozessor konnte der 8080-Mikroprozessor mehr Speicher adressieren, verfügte über erweiterte E/A-Fähigkeiten, zusätzliche Befehle und war schneller. Obwohl die Ideen der 8008-Mikroprozessorarchitektur von Intel weitgehend auf den 8080-Mikroprozessor übertragen wurden, wurde seine interne Organisation so stark verbessert, dass der 8080-Mikroprozessor zum De-facto-Standard für Mikroprozessoren der zweiten Generation wurde; Und wenn es um Mikroprozessoren geht, denken viele Menschen als Erstes an den 8080-Mikroprozessor.

Fortschritte in der Technologie ermöglichten es Intel, 1976 eine verbesserte Version des 8080-Mikroprozessors namens 8085 auf den Markt zu bringen. Er unterschied sich vom 8080-Mikroprozessor im Design des Gehäuses, verfügte über einen Reset auf den Ausgangszustand (zur Initialisierung des Mikroprozessors) und einen Vektor Interrupts (zur Bedienung von Peripheriegeräten), ein serieller Eingangs-Ausgang (zum Anschluss von Druckern und anderen Peripheriegeräten). Darüber hinaus war nur ein +5-V-Netzteil erforderlich (der 8080-Mikroprozessor benötigte zwei Netzteile).

Als der 8085-Mikroprozessor auf den Markt kam, sah sich Intel ernsthafter Konkurrenz auf dem 8-Bit-Mikroprozessormarkt ausgesetzt. Der Z80-Mikroprozessor der Zilog Corporation, eine Weiterentwicklung des 8080-Mikroprozessors, begann Erfolg zu haben, ebenso wie die 6800-Mikroprozessoren von Motorola und 6502 von MOS Technology (heute Commodore), die sich in ihrer Architektur deutlich vom 8080 unterschieden. Und anstatt weiterhin auf dem überfüllten 8-Bit-Mikroprozessormarkt zu kämpfen, machte Intel einen Quantensprung nach vorne und brachte 1978 den 16-Bit-Mikroprozessor 8086 auf den Markt, der Daten zehnmal schneller verarbeiten konnte als der 8080-Mikroprozessor.

Der 8086-Mikroprozessor ist auf Assembler-Ebene softwarekompatibel mit dem 8080-Mikroprozessor. Dies bedeutet, dass für den 8080-Mikroprozessor geschriebene Programme mit einigen geringfügigen Änderungen neu übersetzt und auf dem 8086-Mikroprozessor ausgeführt werden können. Diese Kompatibilität wird durch die Tatsache gewährleistet, dass die Register und der Befehlssatz des 8080-Mikroprozessors Teilmengen der Register und Befehle des 8086-Mikroprozessors sind Dies ermöglichte es Intel, die umfangreiche Erfahrung des 8080-Mikroprozessors zu nutzen und sich bei komplexeren Anwendungen einen Vorteil zu verschaffen.

Da viele Entwickler in ihren 16-Bit-Systemen immer noch lieber günstigere 8-Bit-Hilfs- und Peripheriechips verwendeten, brachte Intel eine Version des 8086-Mikroprozessors auf den Markt, die im Inneren das gleiche Gerät, aber außen einen 8-Bit-Datenbus hatte. Diese Version (8088-Mikroprozessor) ist identisch mit der 8086, benötigt jedoch mehr Zeit für die Übertragung von 16-Bit-Daten, was durch zwei aufeinanderfolgende 8-Bit-Übertragungen erreicht wird. In Anwendungen, die hauptsächlich 8-Bit-Werte verarbeiten, liegt die Leistung des 8088 jedoch innerhalb von 10 % der Leistung des 8086.

Sie können also davon ausgehen, dass Ihr IBM-Personalcomputer über einen 16-Bit-Mikroprozessor verfügt. (Und daher können Sie die umfangreiche Literatur zum 8086-Mikroprozessor nutzen.) Lassen Sie uns diese Einführung abschließen und mit der Erörterung der Fähigkeiten des 8088-Mikroprozessors fortfahren.

Um Assemblerprogramme schreiben zu können, müssen wir wissen, welche Prozessorregister vorhanden sind und wie sie verwendet werden können. Alle x86-Prozessoren (auch Multi-Core-, große und komplexe) sind entfernte Nachkommen des alten Intel 8086 und mit seiner Architektur kompatibel. Das bedeutet, dass in der Assemblersprache 8086 geschriebene Programme auf allen modernen x86-Prozessoren funktionieren.

Alle internen Register des Intel 8086-Prozessors sind 16-Bit:

Insgesamt enthält der Prozessor 12 per Software zugängliche Register sowie ein Flag-Register (FLAGS) und einen Befehlszeiger (IP).

Universalregister (RON) AX, BX, CX und DX werden zum Speichern von Daten und zum Ausführen verschiedener arithmetischer und logischer Operationen verwendet. Darüber hinaus ist jedes dieser Register in zwei 8-Bit-Teile unterteilt, die als 8-Bit-Register (AH, AL, BH, BL, CH, CL, DH, DL) verwendet werden können. Die unteren Teile der Register tragen den Buchstaben L im Namen (vom Wort Niedrig), und die älteren sind H (vom Wort Hoch). Einige Anweisungen verwenden implizit ein bestimmtes Register, beispielsweise kann CX als Schleifenzähler fungieren.

Indexregister sind für die Speicherung von Indizes bei der Arbeit mit Arrays gedacht. SI ( Quellenindex) enthält den Quellindex und DI ( Reisezielindex) ist der Zielindex, sie können jedoch auch als Allzweckregister verwendet werden.

Zeigerregister BP und SP werden verwendet, um mit dem Stapel zu arbeiten. Blutdruck ( Basiszeiger) ermöglicht Ihnen die Arbeit mit Variablen auf dem Stapel. Es kann auch für andere Zwecke verwendet werden. SP ( Stapelzeiger) zeigt auf die Oberseite des Stapels. Es wird von Befehlen verwendet, die mit dem Stapel arbeiten. (Auf den Stack werde ich in einem separaten Teil der Schulung ausführlich eingehen)

Segmentregister CS ( Codesegment), DS ( Datensegment), SS ( Stapelsegment) und ES ( Erweitertes Segment) dienen der Segmentadressierung. Der Code befindet sich im Codesegment, die Daten befinden sich im Datensegment, der Stapel befindet sich im Stapelsegment und es gibt ein zusätzliches Datensegment. Die tatsächliche physikalische Adresse wird erhalten, indem der Inhalt des Segmentregisters um 4 Bits nach links verschoben und ein Offset (die relative Adresse innerhalb des Segments) hinzugefügt wird. Die Segmentadressierung wird in Teil 31 ausführlicher besprochen.

Ein COM-Programm befindet sich immer in einem einzelnen Segment, das gleichzeitig Code-, Daten- und Stapelsegment ist. Wenn Sie ein COM-Programm ausführen, enthalten die Segmentregister dieselben Werte.

Befehlsindex IP ( Anweisungszeiger) enthält die Adresse des Befehls (im Codesegment). Sie können den Inhalt nicht direkt ändern, aber der Prozessor macht das selbst. Bei der Ausführung regulärer Befehle erhöht sich der IP-Wert um die Größe des ausgeführten Befehls. Es gibt auch Steuerübertragungsanweisungen, die den IP-Wert ändern, um Übergänge innerhalb des Programms durchzuführen.

Flaggenregister FLAGS enthält separate Bits: Steuerflags und Ergebnisflags. Steuerflags ändern den Betriebsmodus des Prozessors:

  • D ( Richtung) - Richtungsflagge. Steuert die Richtung der Verarbeitung von Datenleitungen: DF=0 – von niedrigen zu hohen Adressen, DF=1 – von hohen zu niedrigen Adressen (für spezielle String-Befehle).
  • ICH ( Unterbrechen) – Interrupt-Flag. Wenn der Wert dieses Bits 1 ist, sind Interrupts aktiviert, andernfalls sind sie deaktiviert.
  • T ( Fangen) – Tracing-Flag. Wird vom Debugger verwendet, um das Programm Schritt für Schritt auszuführen.

Ergebnisattribute werden nach der Ausführung arithmetischer und logischer Befehle gesetzt:

  • S( Zeichen) - Vorzeichen des Ergebnisses, gleich dem Vorzeichenbit des Operationsergebnisses. Wenn gleich 1, ist das Ergebnis negativ.
  • Z ( Null) – Null-Ergebnis-Flag. ZF=1, wenn das Ergebnis Null ist.
  • P( Parität) - ein Zeichen für die Gleichmäßigkeit des Ergebnisses.
  • C ( Tragen) - Flagge tragen. CF=1, wenn es bei der Addition/Subtraktion zu einem Übertrag/Borrow von der höchsten Ziffer kommt. Speichert bei Verschiebungen den Wert des herausgeschobenen Bits.
  • A ( Hilfs) - zusätzliche Carry-Flagge. Wird bei Operationen mit gepackten binären Dezimalzahlen verwendet.
  • Ö( Überlauf) – Überlaufflag. CF=1, wenn das Ergebnis außerhalb des akzeptablen Wertebereichs liegt.

Machen Sie sich keine Sorgen, wenn etwas unklar erscheint. Aus der weiteren Erklärung wird klar, was was ist und wie man das alles nutzt :)


Emu8086 ist ein kostenpflichtiges Programm. Sie können es jedoch 30 Tage lang kostenlos testen.

Sie haben also das Programm Emu8086 heruntergeladen und auf Ihrem Computer installiert. Wir starten es und erstellen eine neue Datei über das Menü DATEI – NEU – COM-VORLAGE (Datei – Neu – COM-Dateivorlage). Im Quellcode-Editor sehen wir danach Folgendes:

Reis. 1.1. Erstellen einer neuen Datei in Emu8086.

Hierbei ist zu beachten, dass es zwei Arten von Programmen gibt, die mit Assemblern für Computer unter Windows erstellt wurden: COM und EXE. Wir werden uns die Unterschiede zwischen diesen Dateien später ansehen, aber zunächst reicht es für Sie zu wissen, dass wir zum ersten Mal ausführbare Dateien mit der COM-Erweiterung erstellen werden, da diese einfacher sind.

Nachdem Sie mit der oben beschriebenen Methode eine Datei in Emu8086 erstellt haben, sehen Sie im Quellcode-Editor die Zeile „Fügen Sie Ihren Code hinzu“ – „Fügen Sie Ihren Code hier hinzu“ (Abb. 1.1). Wir löschen diese Zeile und fügen an ihrer Stelle folgenden Text ein:

MOV AH, 02h MOV DL, 41h INT 21h INT 20h Der vollständige Text des Programms sieht also so aus: ORG 100h MOV AH, 02h MOV DL, 41h INT 21h INT 20h RET Darüber hinaus gibt es oben auch Kommentare (in Abb. 1.1 – das ist grüner Text). Ein Kommentar in Assemblersprache beginnt mit einem Symbol; (Semikolon) und fährt bis zum Ende der Zeile fort. Wenn Sie nicht wissen, was Kommentare sind und warum sie benötigt werden, lesen Sie das Buch „How to Become a Programmer“. Wie ich bereits sagte, werden wir hier nicht die Grundlagen des Programmierens erklären, da das Buch, das Sie gerade lesen, für Personen gedacht ist, die mit den Grundlagen des Programmierens vertraut sind.

Beachten Sie außerdem, dass die Groß-/Kleinschreibung von Zeichen in der Assemblersprache keine Rolle spielt. Sie können RET, ret oder Ret schreiben – es ist derselbe Befehl.

Sie können diese Datei irgendwo auf der Festplatte speichern. Aber Sie müssen es nicht speichern. Um das Programm auszuführen, drücken Sie die EMULATE-Taste (mit dem grünen Dreieck) oder die F5-Taste. Es öffnen sich zwei Fenster: das Emulatorfenster und das Quellcodefenster (Abb. 1.2).

Reis. 1.2. Emu8086-Emulatorfenster.

Das Emulatorfenster zeigt Register an und enthält Schaltflächen zur Programmsteuerung. Das Quellfenster zeigt den Quellcode Ihres Programms an und hebt die Zeile hervor, die gerade ausgeführt wird. All dies ist sehr praktisch zum Studieren und Debuggen von Programmen. Aber das brauchen wir noch nicht.

Im Emulatorfenster können Sie Ihr Programm als Ganzes (Schaltfläche „RUN“) oder im Schritt-für-Schritt-Modus (Schaltfläche „EINZELSCHRITT“) ausführen. Der Schritt-für-Schritt-Modus eignet sich zum Debuggen. Nun starten wir das Programm zur Ausführung mit der RUN-Taste. Danach (sofern Sie im Programmtext keine Fehler gemacht haben) erscheint eine Meldung über das Ende des Programms (Abb. 1.3). Hier wird Ihnen mitgeteilt, dass das Programm die Kontrolle an das Betriebssystem übergeben hat, also das Programm erfolgreich abgeschlossen wurde. Klicken Sie in diesem Fenster auf OK und Sie sehen schließlich das Ergebnis Ihres ersten Programms in Assemblersprache (Abb. 1.4).

Reis. 1.3. Meldung zum Abschluss des Programms.

Reis. 1.4. Ihr erstes Programm ist abgeschlossen.

Wie bereits erwähnt, zeigt unser erstes Programm den englischen Buchstaben „A“ auf dem Bildschirm an. Das Ergebnis entsprach unseren Erwartungen – auf dem Bildschirm wurde der Buchstabe „A“ angezeigt.

Es ist erwähnenswert, dass Emu8086 ein EMULATOR ist, das heißt, er emuliert den Betrieb eines Computers mit einem 8086-Prozessor. Daher wird das Programm im oben beschriebenen Beispiel nicht vom Betriebssystem, sondern vom Emulator ausgeführt. Emu8086 kann auch echte Programme erstellen, die unabhängig auf einem Computer ausgeführt werden können. Eine Beschreibung der Arbeit mit Emu8086 ist in unseren Plänen jedoch nicht enthalten. Lesen Sie die Hilfe und experimentieren Sie – alles wird für Sie klappen.

In unserem Fall spielt es keine Rolle, wie das Programm ausgeführt wird – von einem Emulator oder einem Betriebssystem. Die Hauptsache besteht darin, die Problematik der Erstellung von Programmen in Assemblersprache zu verstehen. Lassen Sie uns daher unser einfaches Programm im Detail analysieren.

#make_COM#– 1. Zeile. Dieser Befehl ist spezifisch für Emu8086. Es wird verwendet, um den Typ der zu erstellenden Datei zu bestimmen. In unserem Fall handelt es sich um eine Datei mit der Erweiterung .COM.

ORG 100h– 2. Zeile. Dieser Befehl setzt den Programmzähler auf 100 Stunden, da DOS beim Laden einer COM-Datei in den Speicher die ersten 256 Bytes (dezimal 256 entspricht hexadezimal 100) dem PSP-Datenblock zuweist. Erst hinter diesem Block befindet sich der Programmcode. Alle Programme, die in Dateien vom Typ COM kompiliert werden, müssen mit dieser Anweisung beginnen.

MOV AH, 02h– 3. Zeile. Der MOV-Befehl (oder Befehl) platziert den Wert des zweiten Operanden im ersten Operanden. Das heißt, der Wert 02h wird im AN-Register abgelegt. Warum wird das gemacht? 02h ist eine DOS-Funktion, die ein Symbol auf dem Bildschirm anzeigt. Wir schreiben ein Programm für DOS, also verwenden wir die Befehle dieses Betriebssystems (OS). Und wir schreiben diese Funktion (bzw. ihre Nummer) in das AH-Register, da Interrupt 21h dieses Register verwendet.

MOV DL, 41h– 4. Zeile. Der Zeichencode „A“ wird in das DL-Register eingetragen. Der ASCII-Code für das Zeichen „A“ ist 41h.

INT 21h– 5. Zeile. Dies ist derselbe Interrupt 21h – ein Befehl, der die im AH-Register angegebene DOS-Systemfunktion aufruft (in unserem Beispiel ist dies Funktion 02h). Der Befehl INT 21h ist das wichtigste Mittel zur Interaktion zwischen Programmen und dem Betriebssystem.

INT 20h– 6. Zeile. Dabei handelt es sich um einen Interrupt, der das Betriebssystem anweist, das Programm zu beenden und die Steuerung an die Konsolenanwendung zu übergeben. Dies bedeutet, dass bei Verwendung von INT 20h in unserem Beispiel die Steuerung an das Programm Emu8086 übergeben wird. Und wenn das Programm bereits vom Betriebssystem kompiliert und gestartet wurde, führt uns der Befehl INT 20h zum Betriebssystem zurück (z. B. zu DOS). Im Fall von Emu8086 könnte dieser Befehl im Prinzip übersprungen werden, da die gleiche Funktion vom RET-Befehl ausgeführt wird, der beim Erstellen einer neuen Datei mithilfe einer Vorlage automatisch in den Quelltext eingefügt wird (wie wir es zuvor getan haben). Aber ich habe mich auch hier entschieden, INT 20h zu verwenden, um die Kompatibilität mit anderen Assemblern zu gewährleisten.

Für diejenigen, die aus diesen Erklärungen nicht alles verstehen, empfehle ich die Lektüre des Buches „Wie werde ich Programmierer“ sowie der folgenden Kapitel.

MONTAGESPRACHEMIKROPROZESSOR 8088

Assemblersprache ist Maschinensprache, die in symbolischer Form geschrieben ist. Während Adressen und Anweisungen in der Maschinensprache Zahlen sind, ermöglicht die Assemblersprache die Zuweisung eindeutiger Buchstabennamen an Speicheradressen. Also Da die Aktoren der Mikroprozessoren 8086 und 8088 identisch sind, erweist sich auch die Assemblersprache 8088 als identisch mit der Assemblersprache 8086 und ist Teil der Assemblersprache der Mikroprozessoren 80186 und 80286, bei denen es sich um verbesserte Modifikationen der 8086-CPU handelt.

Ein in der Assemblersprache 8088 geschriebenes Programm kann mithilfe eines Programms namens „ Assembler. Der in diesem Buch beschriebene Assembler ist ein Microsoft-Makroassembler, der im IBM-Personalcomputer mit dem PC-DOS-Festplattenbetriebssystem verwendet wird.

Es gibt andere Versionen von Assemblern, die für die 8086/8088-Mikroprozessorfamilie akzeptabel sind, wie z. B. der von der Intel Corporation entwickelte IASM-86-Assembler, der standardmäßige mnemonische Anweisungen enthält, einschließlich arithmetischer Anweisungen für Gleitkommazahlen im 8087-Prozessor. Obwohl die Unterschiede zwischen diesen Assemblern vorhanden sind, sind ihre Strukturen und die Befehlsformate der Sprachen weitgehend kompatibel.

Warum ist Assemblersprache notwendig?

Es ist sehr umständlich, mühsam und manchmal schwierig, Programme direkt in Maschinensprache zu schreiben. In diesem Fall muss der Programmierer viele mühsame und routinemäßige Operationen durchführen: die Verteilung der Zellen im Speicher des Mikrocomputers überwachen, um Befehle oder Programmdaten aufzuzeichnen, damit diese Adressen dann im Programm angegeben oder ermittelt werden können; Erinnern Sie sich an tatsächliche Maschinenanweisungen, dargestellt im Binär- oder Hexadezimalcode, und an die damit verbundenen Mikroadressierungsmethoden


Prozessor; Alle Daten in einem Programm müssen in Binärcode umgewandelt werden, damit sie in ein Maschinenspracheprogramm eingebunden werden können. Offensichtlich können bei dieser Arbeit viele Fehler gemacht werden.

Assemblersprache ist eine Möglichkeit, Programme symbolisch in Maschinensprache zu schreiben. Beispielsweise kann der Maschinenbefehlscode ZS02 (hexadezimal) in symbolischer Form in Assembler als CMP AL,02 geschrieben werden (dieser Befehl vergleicht den Inhalt des AL-Registers mit der Hexadezimalzahl 02). Somit sind routinemäßige Konvertierungen der oben aufgeführten Maschinencodes und Programmdaten möglich. Übertragen Sie so viel wie möglich in das Assembler-Programm. Daher besteht der Hauptzweck von Assembler darin, in Assemblersprache geschriebene Programme in Maschinensprache (Maschinencodes) zu übersetzen.

Der Assembler verfolgt die verwendeten Speicherorte, sodass Anweisungen im Programm symbolisch auf diese Adressen verweisen und Namen und Beschriftungen verwenden, die tatsächliche Werte ersetzen. Beispielsweise kann Maschinencode in Hexadezimalschreibweise C606001201 in Assemblersprache als MOV BYTE-COUNT,01 geschrieben werden, wobei BYTE-COUNT der symbolische Name der Speicheradresse 1200H ist.

Der Assembler ermöglicht die symbolische Darstellung von Konstanten und Programmdaten und führt alle notwendigen Konvertierungen derselben in die entsprechenden Binärcodes durch, berechnet die korrekte Anzahl von Bytes oder Wörtern, die gespeichert werden müssen, und stellt eine Tabelle mit Namen oder Labels und ihren entsprechenden Adressen zusammen.

In Assembler kann ein Programmierer eine Prozedur (Unterroutine) als unabhängiges und völlig separates Modul schreiben. Der Assembler generiert die notwendigen Informationen, damit ein Prozedurobjektmodul beim Laden in den Speicher des Mikroprozessors mit anderen Modulen kombiniert werden kann, die in separaten Programmbibliotheken enthalten sind.

Das Ändern von Programmen, die in Assemblersprache geschrieben sind, ist viel einfacher als das Ändern von Programmen, die direkt in Maschinensprache geschrieben sind. Beispielsweise kann sich das Einfügen von Befehlen ändern




Platzieren der Adressen von Operanden oder Programmdaten im Speicher nach der Einfügemarke. Wenn das Programm daher in Maschinencode geschrieben ist, muss der Programmierer die Adressen aller Operanden und Programmdaten ändern, die sich nach dem eingegebenen Befehl befinden. Dies ist ein sehr mühsamer und zeitaufwändiger Vorgang, ganz zu schweigen davon, dass er fehleranfällig ist. Erfolgt die Einfügung in einem in Assembler geschriebenen Programm, erfolgt die Neunummerierung aller Operandenadressen im Speicher automatisch entsprechend der neuen Struktur und Platzierung der Daten im geänderten Programm.

Der Assembler kann eine Quellprogrammliste (Text) erstellen, die die ursprünglichen Anweisungen in der Assemblersprache, zugehörige Maschinensprache und Datencodes, Diagnoseergebnisse und/oder Fehlermeldungen enthält, die während des Übersetzungsprozesses generiert werden. Daher können viele Softwarefehler bereits im Übersetzungsstadium erkannt werden. Eine Programmliste enthält Symbole, Namen oder Beschriftungen, die symbolische Darstellungen von Konstanten oder Programmdaten sind. Die Auflistung selbst ist ein praktisches Tool zum Analysieren und Korrigieren von Fehlern, die beim Debuggen von Programmen auftreten können.

Programmformat

Jede einzelne sogenannte Assembler-Anweisung wird typischerweise in eine einzelne Maschinenanweisung übersetzt. In den meisten Assemblersprachen verfügt jede Anweisung über vier eindeutig definierte Felder, die verschiedene Attribute der zu übersetzenden Anweisung beschreiben. Diese Felder sind das Beschriftungsfeld, das Operationsfeld, das Operandenfeld und das Kommentarfeld. Jedes Feld kann eine variable Länge haben und muss durch ein oder mehrere Leerzeichen von seinen Nachbarn getrennt sein.

Das erste Feld wird verwendet, um den Namen oder das Symbol des Speicherorts anzugeben, der den Befehl, die Konstante oder die Daten enthält. Die Adresse dieser Zelle ist in enthalten

Der eingebaute Assembler (im Folgenden kurz Assembler) ermöglicht die Programmierung auf der Ebene einzelner Maschinenanweisungen. Dies ist der Hauptunterschied zwischen Assembler und Pascal, und alle seine Vor- und Nachteile konzentrieren sich auf diesen Unterschied. Der Vorteil besteht darin, dass der Programmierer beim Programmieren in Assembler normalerweise eine Folge von Maschinenanweisungen wählt, um die gewünschten Berechnungen mit maximaler Geschwindigkeit und minimalem Speicherverbrauch durchzuführen, während selbst ein so sehr fortgeschrittener Compiler wie der Turbo Pascal-Compiler zwangsläufig dies einführt Code weist eine gewisse Redundanz auf, was die Berechnungsgeschwindigkeit verringert und die Speicherkosten erhöht. Andererseits ist die Programmierung auf der Ebene von Maschinenanweisungen eine äußerst mühsame Aufgabe und kann in der Geschwindigkeit der Programmentwicklung nicht mit der Programmierung in Pascal verglichen werden – darin liegt der Hauptnachteil von Assembler.

Um Assembler-Tools verwenden zu können, müssen Sie die Details der Intel 80x86-Mikroprozessorarchitektur genau kennen. Zu dieser Familie gehören Mikroprozessoren:

8086 – 16-Bit-Mikroprozessor, der im IBM PC/XT verwendet wird;

8088 ist ein Analogon von 8086 und unterscheidet sich von diesem nur in der Interaktion mit dem Speicher: 8086 kann sowohl Bytes als auch 16-Bit-Wörter mit dem Speicher austauschen, während 8088 nur Bytes austauschen kann;

80286 – eine verbesserte Version des 8086, der im IBM AT PC verwendet wird; kann in zwei Modi betrieben werden: im Real-Modus, der den Betrieb des MP 8086 vollständig emuliert, und im geschützten Modus, in dem er Speicher bis zu 16 MB adressieren kann (im Real-Modus - bis zu 1 MB);

80386 – 32-Bit-Variante von 80286; kann bis zu 4 GB adressieren;

80486 - Kombination 80386/80387, d.h. verfügt über ein internes Subsystem zur Implementierung von Gleitkommaoperationen;

80586 (Pentium) – verfügt über eine Reihe von Verbesserungen, die ihm eine 2- bis 3-fache Leistungssteigerung im Vergleich zum 80486 ermöglichen, einschließlich der Möglichkeit, 64-Bit-Zahlen zu verarbeiten.

Mikroprozessoren dieser Familie erweitern ihre Fähigkeiten in der aufgeführten Reihenfolge, sind jedoch von jüngeren Modellen zu älteren Modellen streng kompatibel: Alles, was der 8086/8088 kann, wird vom Pentium umgesetzt, nicht jedoch umgekehrt. Die Architektur (interne Struktur, Adressierungsmethoden und Befehlssystem) des MP 8086/8088 wird im Folgenden erläutert.



12.1.1. Registriert

Der MP 8086/8088 verfügt über 14 Register. Funktionell sind sie in Gruppen unterteilt:

· Allzweckregister (AX, BX, CX, DX); dienen der Speicherung von Operanden und der Ausführung grundlegender Anweisungen; jedes davon kann als Satz von zwei unabhängigen 8-Bit-Registern verwendet werden: das High-Byte des Registers (AN, VN, CH, DH) und das Low-Byte (AL, BL, CL, DL); AX besteht beispielsweise aus AN und AL;

· Segmentregister (CS, DS, SS, ES); Wird verwendet, um beim Adressieren des Speichers ein Segment anzuzeigen.

· Zeigerregister (SP, BP, IP); Wird verwendet, um einen Offset beim Adressieren des Speichers anzugeben.

· Indexregister (SI, DI); wird zur Indexadressierung verwendet;

· Flaggenregister; Wird verwendet, um Hinweise auf den Prozessorstatus zu speichern.

Innerhalb derselben Funktionsgruppe werden Register auf unterschiedliche Weise verwendet. Die Besonderheiten der Verwendung von Registern werden im Folgenden beschrieben.

Registrieren Sie AX. Es ist der Hauptaddierer. Wird bei allen arithmetischen Operationen (Addieren, Multiplizieren usw.) verwendet. Nur mit Hilfe von AX und seinen AHIAL-Halbregistern ist es möglich, Daten mit I/O-Ports auszutauschen.

VX-Register. Wird als Addierer in arithmetischen Operationen und auch als Basisregister für die Indexadressierung verwendet.

Registrieren Sie CX. Wird hauptsächlich als Zähler bei der Durchführung von Wiederholungs- und Verschiebungsvorgängen verwendet. Kann auch an arithmetischen Operationen teilnehmen.

DX-Register. Wird als Datenregister bei E/A-Vorgängen und auch als Addierer bei der Verarbeitung langer Ganzzahlen (32 Bit) verwendet.

CS-Register. Enthält die Nummer des Speichersegments (Codesegment), in dem sich der aktuelle Maschinenbefehl befindet. Um die vollständige Adresse des nächsten Befehls zu erhalten, wird dessen Inhalt um 4 Bits nach links verschoben und dem IP-Pointer-Register hinzugefügt. Der Inhalt des CS wird bei Fernsprung- (Intersegment-)Sprung- und Prozeduraufrufbefehlen automatisch geändert.

IP-Register. Bestimmt den Offset relativ zum Anfang des CS-Codesegments der nächsten ausführbaren Maschinenanweisung. Der Inhalt der IP wird während der Befehlsausführung automatisch geändert, um sicherzustellen, dass Befehle in der richtigen Reihenfolge aus dem Speicher abgerufen werden.

DS-Register. Enthält die Nummer des Speichersegments (Datensegment), in dem sich die Daten (Konstanten und Variablen) befinden. Alle globalen Variablen und typisierten Konstanten eines Turbo Pascal-Programms befinden sich immer in einem einzigen Segment, das von diesem Register adressiert wird.

SS-Register. Enthält die Stapelsegmentnummer. Der Stapel ist ein Abschnitt des automatisch adressierbaren Speichers, der zum vorübergehenden Speichern von Operanden dient. Mithilfe des Stacks organisiert TurboPascal den Datenaustausch zwischen Programm und Prozeduren und platziert darin alle lokalen Variablen (d. h. Variablen, die innerhalb der Prozedur deklariert werden). Der Stapelspeicher wird nach der Last-In-First-Out-Regel verwendet: Der zuletzt auf den Stapel verschobene Operand wird als erster von ihm entfernt.

SP-Register. Zeigt auf die Oberseite des Stapels, d. h. Adressiert zusammen mit Register 55 die Speicherzelle, in der der Operand abgelegt oder von der er abgerufen wird. Der Inhalt dieses Registers wird automatisch dekrementiert, nachdem der nächste Operand auf den Stapel gelegt wurde, und inkrementiert, nachdem der Operand vom Stapel entfernt wurde.

VR-Register. Der sogenannte Basiszeiger. Erleichtert die Erstellung und Verwendung eines lokalen Stapels (d. h. eines Stapels zur Verwendung innerhalb einer Prozedur).

ES-Register. Das optionale Segmentregister ES wird für den Datenaustausch zwischen Segmenten und einige String-Operationen verwendet.

SI-Register. Bestimmt die Adresse der Informationsquelle bei der Indizierung der Datenadressierung (z. B. bei der Verarbeitung von Arrays). Wird normalerweise in Verbindung mit dem DS-Register verwendet.

DI-Register. In Verbindung mit dem £5-Register bestimmt es den Informationsempfänger für den Datenaustausch zwischen den Segmenten.

Flaggenregister. Die einzelnen Ziffern (Bits) dieses Registers haben folgenden Zweck.

Tragen Sie die Flagge CF. Enthält 1, wenn eine Einheit während der Addition getragen oder eine Einheit während der Subtraktion ausgeliehen wurde. Wird auch bei zyklischen Operationen und Vergleichsoperationen verwendet.

PF-Paritätsflag. Enthält 1, wenn die Operation eine Zahl mit einer geraden Anzahl signifikanter Stellen ergibt, d. h. ergänzt das Ergebnis zu einer ungeraden Zahl – wird bei Austauschoperationen zur Steuerung von Daten verwendet.

Externe Carry-Flagge AF. Steuert die Übertragung ab dem 3. Datenbit. Nützlich für Operationen mit gepackten Dezimalzahlen.

ZF-Nullflagge. Gleich 1, wenn das Ergebnis der Operation Null ist, andernfalls gleich 0.

SF-Zeichenflagge. Gleich 1, wenn das Ergebnis der Operation eine negative Zahl ist (mit einer in der höchstwertigen Ziffer).

TF-Trace-Flag. Gleich 1, wenn das Programm schrittweise ausgeführt wird und die Steuerung nach jedem ausgeführten Befehl per Interrupt mit Vektor 1 übertragen wird.

IF-Interrupt-Flag. Enthält 1, wenn der Mikroprozessor für die Verarbeitung von Interrupts aktiviert ist.

Richtungsflagge DF. Steuert die Richtung der Datenübertragung: Wenn es 0 enthält, wird nach jeder Indexoperation der Inhalt der Indexregister um 1 erhöht, andernfalls wird er um 1 verringert.

Überlaufflag OF. Wird auf eins gesetzt, wenn die Operation zu einer Zahl führt, die außerhalb des Bitrasters des Mikroprozessors liegt.

12.1.2. Adressierung

In der MP 8086/8088-Architektur wird die Adresse eines beliebigen Bytes durch zwei 16-Bit-Wörter angegeben – ein Segment und einen Offset. Bei der Bildung der 20-Bit-Volladresse, die zur Adressierung innerhalb von 1 MB benötigt wird, wird das Segment um 4 Bit nach links verschoben (multipliziert mit 16) und mit einem Offset addiert. Da die 16-Bit-Offsetkapazität 65536 Werte beträgt, können bis zu 64 KB in einem einzelnen Segment adressiert werden.

Die MP-Architektur ermöglicht die Verwendung von sieben verschiedenen Adressierungsmethoden.

Registrieren

Ruft einen Operanden aus einem Register ab oder platziert ihn in einem Register. Beispiele:

mov akh, bх (Aus BX entfernt und in AX platziert)

cx,ax hinzufügen (Der Inhalt von AX wird zu CX hinzugefügt)

drücken ex (Den Inhalt von CX auf den Stapel schieben)

Direkte

Der Operand (8- oder 16-Bit-Konstante) ist direkt im Befehlskörper enthalten. Beispiele:

MOV-Axt, 100 (Laden Sie den Wert 100 in AX)

Axt hinzufügen,5 (Füge 5 zum Inhalt von AX hinzu)

mov cx,$FFFF (Platzieren Sie den Wert 65535 in CX)

Gerade

Der Operandenoffset wird im Programmrumpf angegeben und zum DS-Register hinzugefügt; Zum Beispiel:

X:Wort; In: Byte;

mov ah, X (Wir senden den Wert des Variablen-X-Registers AX)

füge ah,B hinzu (Wir addieren den Wert der Variablen B zum Inhalt des Registers AN)

mov X,ax (Wir übertragen den Inhalt des AX-Registers in den Speicherbereich der X-Variablen)

Indirektes Register

Die ausführende Adresse des Operanden (genauer gesagt sein Offset) ist in einem der Register ВХ, BP, SI oder DI enthalten. Um die indirekte Adressierung anzuzeigen, muss dieses Register in eckige Klammern eingeschlossen werden, zum Beispiel:

mov ax, (Der Inhalt des 16-Bit-Worts, das im Speicher unter der Adresse DS:BX gespeichert ist, wird an das AX-Register gesendet);

Jedes der BX...DI-Register arbeitet standardmäßig mit einem eigenen Segmentregister:

DS:BX, SS:BP, DS:SI, ES:DI

Eine explizite Angabe des Segmentregisters ist zulässig, wenn es vom Standardregister abweicht, zum Beispiel:

Adressierung per Datenbank

Das Basisregister BX (oder BP) enthält die Basis (die Adresse am Anfang eines Speicherfragments), relativ zu der der Assembler den Offset berechnet, zum Beispiel:

mov ax,[bx]+10 (Das 10. Byte vom Anfang der Speicherbasis an der Adresse DS-.BX in AX laden);

Indexadressierung

Eines der Indexregister SI oder DI gibt die Position des Elements relativ zum Anfang eines Speicherbereichs an. Sei beispielsweise AOB der Name eines Arrays von Werten vom Typ Byte. Dann können Sie die folgenden Fragmente verwenden:

mov si,15 (Platzieren Sie die Konstante 15 in SI)

mov ah, AOB (Wir senden das 16. Byte vom Anfang des Arrays an AN)

mov AOB, ah (Wir senden das, was wir erhalten haben, an das allererste Element des Arrays)

Adressierung per Datenbank mit Indizierung

Eine Variante der Indexadressierung für den Fall, dass der indizierte Speicherbereich durch seine Basis angegeben wird. Zum Beispiel:

Diese Art der Adressierung ist bei der Verarbeitung zweidimensionaler Arrays nützlich. Wenn beispielsweise AOB ein Array von 10x10 Bytes der Form ist

AOB: Array von Byte;

Um dann auf das AOB-Element zuzugreifen, können Sie das folgende Fragment verwenden

mov bx,20 (Zeilenbasis 2)

mov si,2 (3. Elementnummer)

mov ax,AOB (Elementzugriff)

12.1.3. Befehlssystem

Die folgenden Tabellen enthalten eine Mnemonik für alle gültigen Anweisungen für den 8086/8088 MP. Zur Vereinfachung der Verwendung sind alle Befehle in 6 Funktionsgruppen unterteilt: Datenübertragung, Arithmetik, Bit, Zeichenfolge, Steuerübertragungen, Interrupts. Innerhalb jeder Gruppe werden Teams anhand gemeinsamer zusätzlicher Merkmale zu Untergruppen zusammengefasst.

Eine detaillierte Analyse aller MP 8086/8088-Befehle würde zu viel Platz beanspruchen, daher werden in den Erläuterungen im Anschluss an die Tabellen nur die gängigsten Befehle berücksichtigt. Eine ausführliche Beschreibung aller Befehle finden Sie in,.

Datenübertragungsbefehle

Mnemonik Format Erläuterung
Allzweckbefehle
MOV MOV-Senke, Quelle Vorwärtswert
DRÜCKEN PUSH-Quelle Auf den Stapel legen
POP POP-Empfänger Vom Stapel holen
XCHG XCHG Senke, Quelle Werte tauschen
XLAT XLAT-Tabelle Laden Sie ein Byte aus der Tabelle in AL
I/O-Befehle
IN IN Batterie, Port Vom Port lesen
AUS OUT-Anschluss, Batterie An Port schreiben
Befehle zur Adressweiterleitung
LEA LEA-Register 16, Speicher 16 Geschäftsführeradresse laden
HLT LDS-Register 16, Speicher 32 Laden Sie die vollständige Adresse in DS:register16
LES LES-Register 16, Speicher 32 Laden Sie die vollständige Adresse in ES:register16
Flag-Weiterleitungsbefehle
LAHF LAHF Laden Sie Flaggen nach NA hoch
SAHF SAHF Setzen Sie Flaggen von NA
PUSHF PUSHF Legen Sie die Flaggen auf den Stapel
POPF POPF Pop-Flaggen vom Stapel

Einer der am häufigsten verwendeten Befehle, MOV, ermöglicht Ihnen die sichere Übertragung eines Bytes oder Wortes von Register zu Register, von Speicher zu Register oder von Register zu Speicher. Die Art der übertragenen Daten (Byte oder Wort) wird durch das an der Übertragung beteiligte Register bestimmt. Im Folgenden finden Sie Beispiele für die Verwendung des Befehls:

mov ah,Tisch (Ein Wort aus dem Speicher an AX weiterleiten)

mov Tisch, ah (Weiterleiten eines Bytes von AN an den Speicher)

mov ds,ax (Weiter zum Datensegment)

moves:,ax (Ein Wort an den Speicher weiterleiten: Grundlegende Adressierung mit Segmentersetzung)

mov ch,-17 (Konstante zur Registrierung weiterleiten)

mov-Tabelle,$FF (Konstante an den Speicher senden)

MOV kann nicht zum Senden verwendet werden:

von Speicher zu Speicher, zum Beispiel, statt

sollte benutzt werden

· Eine Konstante oder eine Variable in DS kann beispielsweise nicht sein

· Es ist beispielsweise unmöglich, ein Segmentregister in ein anderes umzuwandeln

· zum CS-Register; Der Wert dieses Registers (Codesegments) ändert sich automatisch, wenn CALL- und JMP-Befehle über große Entfernungen ausgeführt werden. Darüber hinaus wird es vom Stapel geladen, wenn die RETF-Anweisung (Exit Far Procedure) ausgeführt wird.

Die Stapelanweisungen PUSH und POP werden häufig zum vorübergehenden Speichern von Registern und Daten sowie zum Austauschen von Werten zwischen Registern verwendet. Jeder von ihnen arbeitet mit einem Wort, d.h. Sie können kein einzelnes Byte auf den Stapel verschieben oder einfügen. Bei der Ausführung von PUSH wird zunächst der Inhalt des SP-Zeigers um 2 dekrementiert und dann der Operand an der Adresse SS:SP platziert. Beim Herausnehmen aus dem Stapel wird zuerst der Speicher an der Adresse SS:SP gelesen und dann wird SP um 2 erhöht. Wenn also der Stapel voll ist, wird der obere Teil des Stapelzeigers SP auf niedrige Adressen verschoben, und wenn er freigegeben ist, auf hohe Adressen . Bei der Arbeit mit dem Stapel sollten Sie die Besonderheiten der Verwendung des Stapelspeichers („last in, first out“) sowie die Tatsache berücksichtigen, dass dieser Speicher beim Aufrufen von Prozeduren intensiv genutzt wird, d. h. Der Zustand des Stapels zum Zeitpunkt des Beendens der Prozedur muss strikt mit dem weiteren Betrieb des Programms übereinstimmen. Die erste Bedingung bestimmt die Reihenfolge, in der Daten vom Stapel abgelegt werden – sie muss umgekehrt zur Reihenfolge sein, in der die Daten auf den Stapel abgelegt wurden. Die zweite Bedingung bedeutet eigentlich, dass der SP-Zeiger nach dem Verlassen der Prozedur den gleichen Offset enthalten muss wie beim Eintritt. Mit anderen Worten, die Prozedur sollte kein zusätzliches Wort auf dem Stapel „vergessen“ oder mehr als nötig davon nehmen.

Der LEA-Ladeadressenbefehl lädt die Adresse (Offset) des gewünschten Speicherorts in ein Register. Dies kann auch erreicht werden, indem das reservierte Wort OFFSET vor dem Variablennamen verwendet wird. Zum Beispiel:

mov ax, OFFSET X (Offset X in AX laden)

lea Axt,X (Gleiche Aktion)

Der Unterschied besteht darin, dass der LEA-Befehl die Verwendung der Indexadressierung ermöglicht, was besonders beim Senden von Massendaten praktisch ist.

Die anderen beiden Adressladeanweisungen, LDS und LES, laden das erste 16-Bit-Wort von der Quelle in das Zielregister und dann das nächste Wort in das DS- oder ES-Register, d. h. Sie dienen dazu, die vollständige Adresse des Operanden (Segment und Offset) zu laden.

Arithmetische Befehle

Mnemonik Format Ein Kommentar
Additionsbefehle
HINZUFÜGEN Empfänger und Quelle hinzufügen Falten
ADC ADC-Empfänger, Quelle Falten, Tragen hinzufügen
AAA AAA Ergänzung für ASCII-Tabelle anpassen
DAA DAA Korrekte Addition für BCD-Zahlen
INC INC-Empfänger Um eins erhöhen
Subtraktionsbefehle
SUB SUB-Receiver, Quelle Subtrahieren
SBB SBB-Empfänger, Quelle Mit Darlehen subtrahieren
A.A.S. A.A.S. Passen Sie die Subtraktion für die ASCII-Tabelle an
DAS DAS Passen Sie die Subtraktion für BCD-Zahlen an
DEZ DEC-Empfänger Um eins verringern
N.E.G. NEG-Empfänger Umgekehrtes Vorzeichen
Bau- und Installationsarbeiten SMR-Empfänger, Quelle Vergleichen
Multiplikationsanweisungen
MUL MUL-Quelle Ohne Vorzeichen multiplizieren
IMUL IMUL-Quelle Mit Vorzeichen multiplizieren
AAM AAM Passen Sie die Multiplikation für die ASCII-Tabelle an
Divisionskommandos
DIV DIV-Quelle Ohne Vorzeichen teilen
IDIV IDIV-Quelle Mit Vorzeichen teilen
AAD AAD Passen Sie die Aufteilung für die ASCII-Tabelle an
Erweiterungsbefehle signieren
CBW CBW Byte in Wort umwandeln
CWD CWD Wort in Doppelwort umwandeln

Bedenken Sie bei der Verwendung arithmetischer Befehle, dass der MP vorzeichenbehaftete Zahlen, vorzeichenlose Zahlen und BCD-Zahlen verarbeiten kann. Vorzeichenlose Zahlen verwenden alle Bits, um den Wert darzustellen. diese. Sie entsprechen den Typen Byte und Word, während vorzeichenbehaftete Zahlen an der höchstwertigen Position das Vorzeichen der Zahl speichern und den Typen ShortInt und Integer entsprechen. BCD-Zahlen verwenden 4 Bits für jede Dezimalstelle und können gepackt oder entpackt werden. Im ersten Fall speichert ein Byte zwei Dezimalstellen (die höchstwertige befindet sich im höchstwertigen Halbbyte), im zweiten Fall nur eine (das höchstwertige Halbbyte wird nicht verwendet). Die Grundrechenbefehle des MP (ADD, SUB, MUL, DIV) berücksichtigen nicht die binär-dezimale Form von Zahlen, daher sind die Ergebniskorrekturbefehle in der MP-Architektur enthalten.

Bit-Anweisungen

Mnemonik Format Ein Kommentar
Logische Befehle
UND UND Senke, Quelle UND ausführen
ODER ODER Senke, Quelle ODER ausführen
XOR XOR-Empfänger, Quelle XOR ausführen
NICHT NICHT Empfänger NICHT ausführen
PRÜFEN TEST-Empfänger, Quelle Überprüfen
Umschaltbefehle
SAL/SHL SAL-Empfänger, Zähler Geh nach links
SAR/SHR SAR-Empfänger, Zähler Nach rechts bewegen
ROL ROL-Empfänger, Zähler Bewegen Sie sich zyklisch nach links.
ROR ROR-Empfänger, Zähler Bewegen Sie sich zyklisch nach rechts
RCL RCL-Empfänger, Zähler Bewegen Sie sich mit Tragen nach links
RCR RCR-Empfänger, Zähler Mit Wrap nach rechts bewegen

Bitanweisungen werden bei der Berechnung logischer Ausdrücke sowie in Fällen verwendet, in denen einzelne Bits des Operanden geändert werden müssen. Die logischen Anweisungen AND, OR, XOR und NOT entsprechen den entsprechenden Turbo Pascal-Operationen, wenn die Operanden ganzzahlige Ausdrücke sind. Der TEST-Befehl führt eine ganzzahlige Operation der bitweisen Summierung UND aus, ändert jedoch nicht die Werte der Operanden, sondern setzt nur Flags entsprechend dem Wert des Vergleichsergebnisses: setzt CF und OF zurück, ändert PF, ZF, SF und ändert AF nicht (das ZF-Flag wird auf 1 gesetzt, wenn beide Operanden in mindestens einem entsprechenden Bit eins enthalten). Die SHL/SHR-Schiebebefehle entsprechen den gleichnamigen Turbo Pascal-Operationen und unterscheiden sich von den zyklischen ROLIROR-Schiebebefehlen dadurch, dass die bei ihrer Ausführung verschobenen signifikanten Bits verloren gehen, während diese Bits bei einer zyklischen Verschiebung „auf der anderen Seite“ erscheinen “. Wenn Sie beispielsweise das Fragment ausführen

mov al,1 (Laden Sie die Einheit in AL)

shr al,1 (Um 1 Ziffer nach rechts verschieben)

Das AL-Register enthält 0 (das nach rechts verschobene wird in CF platziert), während es nach dem Ersetzen des SHR-Befehls durch ROR den Wert $80=128 enthält (das verschobene wird im höchstwertigen Bit des Registers platziert). ).

Beachten Sie, dass der Zähler in Schiebeanweisungen 1 oder die im CL-Register angegebene Anzahl von Verschiebungen sein kann.

Steuerübertragungsbefehle

Mnemonik Format Ein Kommentar
Bedingungslose Sprünge
ANRUF Spitzname Geben Sie die Prozedur ein
RET RET [Anzahl Parameter] Rückkehr vom Verfahren
SPRINGEN JUMP-Name Gehen
Bedingte Sprünge
JA/JNBE JA close_mark Gehe, wenn höher (nach dem Vergleich vorzeichenloser Operanden)
JAE/JNB JAE close_mark Springe, wenn größer oder gleich
JB/JBAE/JC JB close_mark Überspringen Sie, wenn unten
JBE/JNA JBE close_label Springe, wenn niedriger oder gleich
JCXZ JCXZ close_mark Gehe, wenn CX=0
JE/JZ JE close_mark Bei Gleichheit gehen
JG/JNLE JG close_mark Springen, wenn größer (nach Vergleich der vorzeichenbehafteten Operanden)
JGE/JNL LGE close_label Springe, wenn größer oder gleich
JL/JNGE JL close_mark Überspringen, wenn weniger
JLE/JNG JLE close_label Springe, wenn kleiner oder gleich
J.N.C. JNC close_label Gehen Sie, wenn es keinen Transfer gibt
JNE/JNZ JNE close_label Gehen Sie, wenn nicht gleich
JNO JNO close_label Springe, wenn kein Überlauf vorliegt
JNP/JPO JNP close_label Springe, wenn es seltsam ist
JO JO close_mark Gehen Sie, wenn Sie umsteigen
JP/JPE JP close_mark Gehen Sie, wenn sogar
JS JS close_label Gehen Sie, wenn negativ
Zyklussteuerungsbefehle
SCHLEIFE LOOP close_label Zyklus wiederholen
LOOPE/LOOPZ LOOPE close_label Wiederholen, bis der Gleichstand erreicht ist
LOOPNE/LOOPNZ LOOPNE close_label Wiederholen, bis der Gleichstand erreicht ist

Unbedingte Sprungbefehle CALL, RET, JMP können ein Fern- oder Nahspeichermodell verwenden, während bedingte Sprungbefehle nur ein kleines verwenden können (innerhalb von -128...+127 Bytes). Mit dem fernen Speichermodell (festgelegt durch die Option „Optionen/Compiler/Far-Aufrufe erzwingen“ der Turbo Pascal-Umgebung oder der Compiler-Direktive (F+)) wird sowohl die Intrasegment- als auch die Intersegment-Steuerungsübertragung durchgeführt, mit dem nahen Modell nur intrasegment.

Die CALL-Anweisung funktioniert wie folgt. Zuerst wird die Adresse des Befehls, der dem CALL folgt (Rückgabeadresse), auf dem Stapel platziert, dann wird die Adresse des Prozedureinstiegspunkts im IP-Register (oder im CS:IP-Paar) platziert, also unmittelbar nach dem CALL-Befehl Der erste Befehl der Prozedur wird ausgeführt. Bei beiden Adressen (Eingabe- und Rückkehrpunkte) handelt es sich um 16-Bit-Offsets für einen Aufruf innerhalb eines Segments bzw. um vollständige 32-Bit-Adressen für einen Aufruf zwischen Segmenten. Alle Pascal-Prozeduren (Funktionen), die im (F+)-Modus übersetzt werden oder das reservierte Wort FAR im Header enthalten, müssen als Far-Prozeduren aufgerufen werden. Dazu muss der CALL-Anweisung das Speichermodell folgen:

Prozedur MyProc; Weit;

Rufen Sie FAR MyProc auf (Aufruf einer Fernprozedur)

Alle Bibliotheksroutinen (also diejenigen, die in den Schnittstellenteilen von Modulen deklariert sind) müssen auf die gleiche Weise aufgerufen werden. Bei einem Fernaufruf wird zuerst der Inhalt des CS-Codesegments auf den Stapel geschoben, gefolgt vom Rückgabeoffset.

Beim Verlassen der Far-Prozedur entfernt der RET-Befehl beide 16-Bit-Wörter vom Stapel und platziert das erste in IP und das zweite in CS. Beim Verlassen der Near-Anweisung entfernt er nur den Offset vom Stapel und platziert ihn in IP .

Bedingte Verzweigungsbefehle sind in der Lage, die Steuerung an ein Etikett zu übertragen, das sich innerhalb der nächsten plus oder minus 128 Bytes von der Anweisung selbst befindet. Wenn Sie die Steuerung an ein Label weiter im selben Segment oder an ein Label in einem anderen Segment übertragen müssen, wird unmittelbar nach dem bedingten Übertragungsbefehl ein bedingungsloser JMP- oder CAL-Befehl platziert, zum Beispiel:

Angst,0 (Überprüfen von AH)

jne@NotZero (AX=0 ?)

jmp IsZero (Ja – gehen Sie zur Fernmarkierung)

....... (Nein – wir arbeiten weiter)

In der Tabelle wird der Begriff „oben/unten“ für den Vergleich von vorzeichenlosen Operanden und „mehr als/weniger“ für den Vergleich von vorzeichenbehafteten Operanden verwendet.

Da bedingte Sprünge eine Programmverzweigung basierend auf Prüfflags implementieren, gehen ihnen normalerweise unmittelbar Befehle voraus, die diese Flags ändern, am häufigsten der CMP-Vergleichsbefehl. Nachfolgend sind die CMP-Conditional_Jump-Kombinationen für verschiedene Ziel- und Quellbeziehungen (erster und zweiter Operand) des CMP-Befehls aufgeführt:

Zum Beispiel:

pp ah,5 (AX>5 ?)

ja @AboveS (Ja, mehr – weiter geht’s)

Seite bx, - 3 (VH<=-3 ?}

jle @LessM3 (Ja, kleiner oder gleich)

Die Befehle LOOP/LOOPE/LOOPNE werden zum Organisieren von Schleifen verwendet. Sie alle nutzen den Inhalt des CX-Registers als Wiederholungszähler. Der LOOP-Befehl dekrementiert CX um eins und übergibt die Steuerung an die Schleifenstartmarke, wenn der Inhalt dieses Registers ungleich Null ist. Die LOOPE/LOOPNE-Anweisungen dekrementieren auch den CX-Zähler, übertragen die Steuerung jedoch unter der kombinierten Bedingung an den Anfang der Schleife, dass das ZF-Flag gesetzt (oder zurückgesetzt) ​​ist und der CX-Zähler nicht gleich Null ist.

So finden Sie beispielsweise das Null-Byte im AOB-Array:

AOB: Array von Byte;

mov ex,It)00 (Wir initiieren den CX-Zähler)

lea bx,AOB (Platzieren Sie die AOB-Adresse in BX)

dez bx (Vorbereitung des Zyklus)

(Hier ist der Beginn des Überprüfungszyklus)

@@Test: inc bx (Adresse des nächsten Bytes)

cmp BYTE PTR ,0 (Prüfung des Bytes)

loopne ©Test (Schließen Sie die Schleife)

jnz ©NotZero (Wenn kein Nullbyte gefunden wird)

....... (Es wurde ein Nullbyte gefunden)

String-Befehle

Mnemonik Format
Hat Ihnen der Artikel gefallen? Mit Freunden teilen: