Hinweis: Einige Antworten sind eingetroffen. Erwägen Sie auch, neuere Antworten zu aktualisieren.
- Common Lisp von happy5214
- C von Luser Droog
- Java von NeatMonster
- Javascript von Crempp
- C von Mike C
- C ++ von Darius Goad
- Postscript von luser droog
- C ++ von JoeFish
- Javascript aus rein subjektiv
- C von RichTX
- C ++ von Dave C
- Haskell von JB
- Python von ja
Der 8086 ist der erste x86-Mikroprozessor von Intel. Ihre Aufgabe ist es, einen Emulator dafür zu schreiben. Da dies relativ weit fortgeschritten ist, möchte ich es einschränken:
- Es müssen nur die folgenden Opcodes implementiert werden:
- mov, push, pop, xchg
- add, adc, sub, sbb, cmp und oder xor
- inc, dec
- call, ret, jmp
- jb, jz, jbe, js, jnb, jnz, jnbe, jns
- stc, clc
- hlt, nein
- Infolgedessen müssen Sie nur die Übertrags-, Null- und Vorzeichenflags berechnen
- Implementieren Sie keine Segmente. Angenommen
cs = ds = ss = 0
. - Keine Präfixe
- Keine Arten von Interrupts oder Port-E / A
- Keine Stringfunktionen
- Keine Zwei-Byte-Opcodes (0F ..)
- Keine Gleitkomma-Arithmetik
- (offensichtlich) keine 32-Bit-Dinge, sse, mmx, ... was 1979 noch nicht erfunden wurde
- Sie müssen weder Zyklen zählen noch Timing durchführen
Beginnen Sie mit ip = 0
und sp = 100h
.
Eingabe: Ihr Emulator sollte ein Binärprogramm in einem beliebigen Format als Eingabe verwenden (aus Datei lesen, vordefiniertes Array, ...) und es unter Adresse 0 in den Speicher laden.
Ausgabe: Der Video-RAM beginnt an der Adresse 8000h, jedes Byte ist ein (ASCII-) Zeichen. Emulieren Sie einen 80x25-Bildschirm zur Konsole. Behandle null Bytes wie Leerzeichen.
Beispiel:
08000 2E 2E 2E 2E 2E 2E 2E 2E 2E 00 00 00 00 00 00 00 ................
08010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
08050 48 65 6C 6C 6F 2C 20 77 6F 72 6C 64 21 00 00 00 Hello,.world!...
Hinweis: Dies ähnelt sehr dem Real Video-Modus, der normalerweise bei 0xB8000 liegt und für Farben ein weiteres Byte pro Zeichen enthält.
Gewinnkriterien:
- Alle genannten Anweisungen müssen implementiert werden
Ich habe ein unkommentiertes Testprogramm ( Link , nasm source ) erstellt, das ordnungsgemäß ausgeführt werden sollte. Es gibt aus
......... Hello, world! 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ################################################################################ ## ## ## 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 ## ## ## ## 0 1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 ## ## ## ## 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ################################################################################
Ich bin mir nicht ganz sicher, ob das Codegolf sein soll. Es ist eine schwierige Aufgabe, daher wird jede Einsendung auf jeden Fall viele positive Stimmen erhalten. Bitte kommentieren.
Hier sind einige Links, die Ihnen bei dieser Aufgabe helfen:
- Anweisungsformat , mehr
- Opcode-Tabelle
- Opcode-Beschreibungen
- 16-Bit-Mod-R / M-Byte-Decodierung
- Register , Flaggenregister
- 1979 Manual
Dies ist mein erster Eintrag auf dieser Plattform. Wenn es Fehler gibt, machen Sie sie bitte darauf aufmerksam. Wenn ich ein Detail verpasst habe, frag einfach.
quelle
Antworten:
Fühlen Sie sich frei, es zu gabeln und Golf zu spielen: https://github.com/julienaubert/py8086
Ich habe auch einen interaktiven Debugger eingebaut.
Es gibt drei Dateien: emu8086.py (erforderlich) console.py (optional für die Anzeigeausgabe), disasm.py (optional, um eine Auflistung des Asms im Codegolf zu erhalten).
Mit der Anzeige laufen (Notiz benutzt Flüche):
So führen Sie einen interaktiven Debugger aus:
So führen Sie den nicht interaktiven "Debugger" aus:
Das Programm " codegolf " sollte sich im selben Verzeichnis befinden.
emu8086.py
console.py
disasm.py
Auf Github
quelle
Haskell,
256234196 ZeilenIch habe dieses Work-in-Progress für einige Zeit gehabt, ich wollte es ein bisschen mehr polieren, bevor es veröffentlicht wird, aber jetzt hat der Spaß offiziell begonnen, es hat keinen Sinn mehr, es versteckt zu halten. Beim Extrahieren ist mir aufgefallen, dass es genau 256 Zeilen lang ist. Ich nehme an, es befindet sich an einem "bemerkenswerten" Punkt seiner Existenz.
Was ist drin ? Kaum genug des 8086-Befehlssatzes, um die Beispiel-Binärdatei fehlerfrei auszuführen. Selbstmodifizierender Code wird unterstützt. (Prefetch: null Bytes)
Ironischerweise waren die ersten ausreichenden Iterationen des Codes länger und unterstützten weniger die Opcode-Spanne. Refactoring hat sich sowohl für die Codelänge als auch für die Opcode-Abdeckung als vorteilhaft erwiesen.
Was ist heraus: offensichtlich, Segmente, Präfixe und Multibyte-Opcodes, Interrupts, I / O-Ports, String-Operationen und FP. Ich habe anfangs das ursprüngliche
PUSH SP
Verhalten verfolgt, musste es aber nach einigen Iterationen fallen lassen.Die Ergebnisse von Carry Flags sind wahrscheinlich in einigen Fällen von
ADC
/ sehr durcheinanderSBB
.Wie auch immer, hier ist der Code:
Die Ausgabe für die bereitgestellte Beispielbinärdatei stimmt perfekt mit der Spezifikation überein. Probieren Sie es mit einem Aufruf wie:
Die meisten nicht implementierten Operationen führen einfach zu einem Mustervergleichsfehler.
Ich habe immer noch vor, einiges mehr zu berücksichtigen und die eigentliche Live-Ausgabe mit Flüchen umzusetzen.
Update 1: auf 234 Zeilen reduziert. Besser organisiert den Code durch Funktionalität, neu ausgerichtet, was sein könnte, versucht, bei 80 Spalten zu bleiben. Und die ALU mehrfach überarbeitet.
Update 2: Es sind fünf Jahre vergangen, ich habe mir ein Update ausgedacht, um es fehlerfrei auf den neuesten GHC zu kompilieren, der in Ordnung sein könnte. Nach dem Weg:
<$>
und<*>
im Präludium.Wie aus den Codekommentaren hervorgeht, sind 5 Zeilen (der Data.Char-Import, die 8-Bit-Registerzuordnungen und der Bildschirmauszug) nicht in der Spezifikation. Sie können sie daher gerne rabattieren, wenn Sie dies wünschen :-)
quelle
.|.
? / 10charC - 7143 Zeilen (CPU selbst 3162 Zeilen)
BEARBEITEN: Der Windows-Build verfügt jetzt über Dropdown-Menüs zum Austauschen von virtuellen Laufwerken.
Ich habe einen vollständigen 80186 / V20-PC-Emulator geschrieben (mit CGA / MCGA / VGA, Sound Blaster, Adlib, Maus usw.), es ist keine triviale Sache, einen 8086 auf irgendeine Weise zu emulieren. Es hat viele Monate gedauert, bis die Daten vollständig korrekt waren. Hier ist das CPU-Modul nur aus meinem Emulator.
http://sourceforge.net/p/fake86/code/ci/master/tree/src/fake86/cpu.c
Ich gebe als Erster zu, dass ich in diesem Emulator viel zu viele globale Variablen verwende. Ich fing an, dies zu schreiben, als ich noch ziemlich neu in C war, und es zeigt. Ich muss eines Tages einiges davon aufräumen. Die meisten anderen Quelldateien darin sehen nicht so hässlich aus.
Sie können den gesamten Code (und einige Screenshots, davon einen unten) hier einsehen: http://sourceforge.net/p/fake86
Ich würde mich sehr freuen, allen anderen helfen zu können, die ihre eigenen schreiben wollen, weil es eine Menge Spaß macht und Sie viel über die CPU lernen! Haftungsausschluss: Ich habe die 8080-Emulation des V20 nicht hinzugefügt, da sie in einem PC-Programm so gut wie nie verwendet wurde. Es scheint, als würde viel Arbeit keinen Gewinn bringen.
quelle
Postscript (
130200367517531222246 Zeilen)Noch in Arbeit, aberIch wollte Code zeigen, um andere zu ermutigen, Code zu zeigen .Der Registersatz wird als eine Zeichenfolge dargestellt, sodass sich die verschiedenen Register in Byte- und Wortgröße natürlich überlappen können, wenn auf Teilzeichenfolgen verwiesen wird. Teilzeichenfolgen werden durchgehend als Zeiger verwendet, damit ein Register und eine Speicherstelle (Teilzeichenfolge der Speicherzeichenfolge) in den Operatorfunktionen einheitlich behandelt werden können.
Dann gibt es eine Handvoll Wörter, um Daten (Byte oder Wort) von einem "Zeiger", aus dem Speicher, aus mem [(IP)] (inkrementierende IP) abzurufen und zu speichern. Dann gibt es ein paar Funktionen, um das MOD-REG-R / M-Byte abzurufen und die REG- und R / M- und MOD-Variablen zu setzen und sie unter Verwendung von Tabellen zu decodieren. Dann funktioniert der Operator, der auf das Opcode-Byte getastet ist. Die Ausführungsschleife ist also einfach
fetchb load exec
.Ich habe nur eine Handvoll Opcodes implementiert, aber gDas Entschlüsseln der Operanden war ein Meilenstein, den ich mit anderen teilen wollte.Bearbeiten: Wörter hinzugefügt, um negative Zahlen mit Vorzeichen zu verlängern. Weitere Opcodes. Bugfix in den Registerbelegungen. Bemerkungen. Arbeite immer noch an Fahnen und fülle die Operatoren aus. Bei der Ausgabe stehen einige Optionen zur Auswahl: Text bei Beendigung auf Standardausgabe ausgeben, fortlaufend mit VT100-Codes ausgeben und mit der Schriftart CP437 in das Bildfenster ausgeben.
edit: Schreibvorgang beendet, Debugging gestartet. Es werden die ersten vier Punkte ausgegeben! Dann geht der Carry schief. Schläfrig.
edit: Ich glaube ich habe die Carry Flag sortiert. Ein Teil der Geschichte geschah auf comp.lang.postscript . Ich habe einige Debugging-Geräte hinzugefügt, und die Ausgabe erfolgt im Grafikfenster (mit meiner zuvor geschriebenen Schriftart Code-Page 437 Type-3 ), sodass die Textausgabe voller Spuren und Speicherauszüge sein kann. Es schreibt "Hallo Welt!" und dann ist da noch das verdächtige Caret. Dann eine ganze Menge nichts. :( Wir werden dort ankommen. Danke für all die Ermutigung!
Bearbeiten: Führt den Test vollständig aus. Die letzten paar Fehler waren: XCHG macht 2 {read store} Wiederholungen, die natürlich eher kopiert als ausgetauscht werden, UND setzt keine Flags (FE) INC, um ein Wort von einem Byte-Zeiger zu erhalten.
Bearbeiten: Vollständiges Neuschreiben von Grund auf anhand der übersichtlichen Tabelle aus dem Handbuch ( blätterte eine neue Seite um! ). Ich fange an zu denken, dass das Ausklammern des Geschäfts aus den Opcodes eine schlechte Idee war, aber es half, das Optab hübsch zu halten. Diesmal kein Screenshot. Ich habe einen Befehlszähler und einen Mod-Trigger hinzugefügt, um den Videospeicher zu entleeren, damit er leicht mit den Debug-Informationen verschachtelt werden kann.
edit: Führt das Testprogramm erneut aus! Die letzten paar Fehler für das kürzere Neuschreiben haben es versäumt, das unmittelbare Byte in den Opcodes 83 (die "Sofort" -Gruppe) und EB (kurzes JMP) mit Vorzeichen zu erweitern. Die Erhöhung um 24 Zeilen deckt zusätzliche Debugging-Routinen ab, die erforderlich sind, um diese letzten Fehler aufzuspüren.
Und die Ausgabe (mit dem Ende der abgekürzten Debugging-Ausgabe).
quelle
Javascript
Ich schreibe einen 486-Emulator in Javascript, inspiriert von jslinux. Wenn ich gewusst hätte, wie viel Arbeit es sein würde, hätte ich wahrscheinlich nie angefangen, aber jetzt möchte ich es beenden.
Dann bin ich auf Ihre Herausforderung gestoßen und war sehr froh, ein 8086-Programm zum Testen zu haben.
Sie können es hier "sehen": http://codinguncut.com/jsmachine/
Ich hatte ein Problem beim Ausdrucken des Grafikpuffers. Wo Leerzeichen sein sollen, enthält der Speicher "00" -Elemente. Ist es richtig, "0x00" als Leerzeichen zu interpretieren oder habe ich einen Fehler in meinem Emulator?
Prost,
Johannes
quelle
C ++
Ich möchte unseren Eintrag für diese Code-Abfrage einreichen. Es wurde in c ++ geschrieben und führt das Testprogramm perfekt aus. Wir haben 90% der One-Byte-Op-Codes und der Basissegmentierung implementiert (einige sind deaktiviert, weil sie mit dem Testprogramm nicht funktionieren).
Program Write Up: http://davecarruth.com/index.php/2012/04/15/creating-an-8086-emulator
Sie finden den Code in einer Zip-Datei am Ende des Blog-Beitrags.
Screenshot Testprogramm ausführen:
Dies hat einige Zeit in Anspruch genommen. Wenn Sie Fragen oder Kommentare haben, können Sie mir gerne eine Nachricht senden. Es war sicherlich eine großartige Übung in der Partnerprogrammierung.
quelle
ret imm
Anweisung falsch ist (siehe hier ), und Sie vermissen die0xff
Gruppe. Ihre Fehlermeldungen gefallen mir jedoch: throw "Sofortiger Wert kann keinen Wert speichern, verzögern.";cs
RegisterC
Große Herausforderung und meine erste. Ich habe ein Konto erstellt, nur weil mich die Herausforderung so fasziniert hat. Der Nachteil ist, dass ich nicht aufhören konnte, an die Herausforderung zu denken, wenn ich echte, bezahlte Programmierarbeit zu erledigen hatte.
Ich fühle mich gezwungen, eine fertige 8086-Emulation zum Laufen zu bringen, aber das ist eine andere Herausforderung ;-)
Der Code ist in ANSI-C geschrieben, also kompilieren / verknüpfen Sie einfach die .c-Dateien, übergeben Sie die Codegolf-Binärdatei und los geht's.
Quelle gezippt
quelle
C ++ 1064 Zeilen
Fantastisches Projekt. Ich habe vor vielen Jahren einen Intellivision-Emulator gemacht , daher war es großartig, meine bissigen Muskeln wieder zu beugen.
Nach ungefähr einer Woche Arbeit hätte ich nicht aufgeregter sein können, als dies geschah:
Ein wenig späteres Debuggen und ... SHAZAM!
Außerdem habe ich das ursprüngliche Testprogramm ohne die 80386-Erweiterungen neu erstellt, da ich meinen Emulator so erstellen wollte, dass er dem 8086 entspricht und keine zusätzlichen Anweisungen enthält. Direkter Link zum Code hier: Zip-Datei .
Ok, ich habe zu viel Spaß damit. Ich habe die Speicher- und Bildschirmverwaltung aufgeschlüsselt, und jetzt wird der Bildschirm aktualisiert, wenn in den Bildschirmpuffer geschrieben wird. Ich habe ein Video gemacht :)
http://www.youtube.com/watch?v=qnAssaTpmnA
Aktualisierungen: Der erste Durchgang der Segmentierung ist in. Es sind nur sehr wenige Anweisungen implementiert, aber ich habe sie getestet, indem ich CS / DS und SS verschoben habe, und alles läuft noch einwandfrei.
Es wurde auch eine rudimentäre Interrupt-Behandlung hinzugefügt. Sehr rudimentär. Aber ich habe int 21h implementiert, um einen String zu drucken. Der Testquelle wurden ein paar Zeilen hinzugefügt und diese ebenfalls hochgeladen.
Wenn jemand einen recht einfachen Assembler-Code hat, der die Segmente testen würde, würde ich gerne damit spielen.
Ich versuche herauszufinden, wie weit ich das bringen möchte. Volle CPU-Emulation? VGA-Modus? Jetzt schreibe ich DOSBox.
12/6: Probieren Sie es aus, VGA-Modus!
quelle
C ++ - 4455 Zeilen
Und nein, ich habe nicht nur die Anforderungen der Frage erfüllt. Ich habe die GESAMTE 8086 gemacht, einschließlich 16 nie zuvor gekannter Opcodes. Reenigne half beim Herausfinden dieser Opcodes.
https://github.com/Alegend45/IBM5150
quelle
#include "cpu.h"
ist schwer zu sehen.Javascript - 4.404 Zeilen
Ich bin auf diesen Beitrag gestoßen, als ich nach Informationen für meinen eigenen Emulator gesucht habe. Dieser Codegolf-Beitrag war für mich von unschätzbarem Wert. Das Beispielprogramm und die zugehörige Assembly ermöglichten ein einfaches Debuggen und Anzeigen der aktuellen Ereignisse.
Danke!!!
Und hier ist die erste Version meines JavaScript 8086-Emulators.
Eigenschaften:
Demo
Ich habe eine Demo online, zögern Sie nicht, damit zu spielen und lassen Sie mich wissen, wenn Sie Fehler finden :)
http://js86emu.chadrempp.com/
So führen Sie das Codegolf-Programm aus
1) Klicken Sie auf die Schaltfläche Einstellungen
2) Klicken Sie dann einfach auf Laden (Sie können hier mit den Debug-Optionen spielen, z. B. durch das Programm gehen). Das Codegolf-Programm ist das einzige, das derzeit verfügbar ist. Ich arbeite daran, mehr online zu bekommen.
Quelle
Vollständige Quelle hier. https://github.com/crempp/js86emu
Ich habe versucht, die Eingeweide der 8086-Emulation hier einzufügen (wie von Doorknob vorgeschlagen), aber die Zeichenbeschränkung wurde überschritten ("Body ist auf 30000 Zeichen beschränkt; Sie haben 158.272 eingegeben").
Hier ist ein schneller Link zu dem Code, den ich hier einfügen wollte - https://github.com/crempp/js86emu/blob/39dbcb7106a0aaf59e003cd7f722acb4b6923d87/src/js/emu/cpus/8086.js
*Edit - updated for new demo and repo location
quelle
Java
Ich hatte diese Herausforderung so lange gewollt und mir endlich die Zeit dafür genommen. Bisher war es eine wundervolle Erfahrung und ich bin stolz zu verkünden, dass ich sie endlich abgeschlossen habe.
Quelle
Der Quellcode ist auf GitHub unter NeatMonster / Intel8086 verfügbar . Ich habe versucht, so ziemlich alles mit Hilfe des Benutzerhandbuchs der Holly 8086-Familie zu dokumentieren .
Ich beabsichtige, alle fehlenden Opcodes und Funktionen zu implementieren, daher möchten Sie möglicherweise Release 1.0 auf eine Version überprüfen, die nur die für diese Herausforderung erforderlichen enthält.
Vielen Dank an @copy!
quelle
Common Lisp - 580 loc (442 ohne Leerzeilen und Kommentare)
Ich nutzte diese Herausforderung als Ausrede, um Common Lisp zu lernen. Hier ist das Ergebnis:
Hier ist die Ausgabe in Emacs:
Ich möchte drei Hauptmerkmale hervorheben. Dieser Code macht intensiven Gebrauch von Makros , wenn Befehle Umsetzung wie
mov
,xchg
und die artithmetic Operationen. Jede Anweisung enthält einendisasm-instr
Makroaufruf. Dies implementiert die Disassemblierung neben dem eigentlichen Code unter Verwendung einer if-over-globalen Variablen, die zur Laufzeit festgelegt wird. Ich bin besonders stolz auf den zielunabhängigen Ansatz, mit dem Werte in Register und indirekte Adressen geschrieben werden. Die Makros, die die Anweisungen implementieren, interessieren sich nicht für das Ziel, da die Formulare, die für beide Möglichkeiten zusammengefügt wurden, mit dem allgemeinensetf
Common-Lisp-Makro funktionieren .Der Code ist in meinem GitHub-Repo zu finden . Suchen Sie nach dem "Codegolf" -Zweig, da ich bereits damit begonnen habe, andere Funktionen des 8086 in master zu implementieren. Ich habe bereits die Überlauf- und Paritätsflags zusammen mit dem FLAGS-Register implementiert.
Es gibt drei Operationen in dieser nicht in der 8086, die
0x82
und0x83
Versionen der logischen Operatoren. Dies wurde sehr spät festgestellt, und es wäre ziemlich chaotisch, diese Operationen zu entfernen.Ich möchte @ja für seine Python-Version danken, die mich schon früh in diesem Unternehmen inspiriert hat.
quelle
C -
319348 ZeilenDies ist eine mehr oder weniger direkte Übersetzung meines Postscript-Programms nach C. Natürlich wird die Stapelverwendung durch explizite Variablen ersetzt. Die Felder eines Befehls werden in die Variablen
o
- Befehls-Opcode-Byted
- Richtungsfeldw
- Breitenfeld aufgeteilt. Wenn es sich um einen "mod-reg-r / m" -Befehl handelt, wird das mr-rm- Byte eingelesenstruct rm r
. Die Dekodierung der Felder reg und r / m erfolgt in zwei Schritten: Berechnung des Zeigers auf die Daten und Laden der Daten, Wiederverwendung derselben Variablen. Für etwas wieADD AX,BX
ist also zuerst x ein Zeiger auf ax und y ein Zeiger auf bx, dann ist x der Inhalt (ax) und y der Inhalt (bx). Es ist viel Casting erforderlich, um die Variable für verschiedene Typen wie diesen wiederzuverwenden.Das Operationscode-Byte wird mit einer Tabelle von Funktionszeigern decodiert. Jeder Funktionskörper besteht aus Makros für wiederverwendbare Teile. Das
DW
Makro ist in allen Opcode-Funktionen vorhanden und decodiert died
undw
-Variablen aus demo
Opcode-Byte. DasRMP
Makro führt die erste Stufe des Decodierens des "mr-rm" -Bytes aus undLDXY
führt die zweite Stufe aus. Opcodes, die ein Ergebnis speichern, verwenden diep
Variable, um den Zeiger auf die Ergebnispositionz
zu halten, und die Variable, um den Ergebniswert zu halten. Flags werden berechnet, nachdem derz
Wert berechnet wurde. Die OperationenINC
undDEC
speichern das Übertragsflag, bevor die generischeMATHFLAGS
Funktion (als Teil des Submakros) verwendet wird, und stellen die Nachwörter wieder her, um den Übertrag zu erhalten.ADD
oder) verwendet wirdSUB
Edit: Fehler behoben!
Bearbeiten: erweitert und kommentiert. Wenn
trace==0
jetzt ein ANSI-Befehl zum Verschieben nach 0,0 ausgegeben wird, wenn das Video ausgegeben wird. So wird eine tatsächliche Anzeige besser simuliert. DasBIGENDIAN
Ding (das nicht einmal funktioniert hat) wurde entfernt. Es basiert an einigen Stellen auf der Little-Endian-Bytereihenfolge, aber ich plane, dies in der nächsten Revision zu korrigieren. Grundsätzlich muss jeder Zeigerzugriff die Funktionenget_
und durchlaufen, dieput_
die Bytes explizit in LE-Reihenfolge (de) zusammensetzen.Die Verwendung von Makros für die Stufen der verschiedenen Operationen führt zu einer sehr engen semantischen Übereinstimmung mit der rein sequentiellen Funktionsweise des Postscript-Codes. Beispielsweise sind die ersten vier Operationscodes 0x00-0x03 alle ADD-Anweisungen mit unterschiedlicher Richtung (REG -> REG / MOD, REG <- REG / MOD) und Byte- / Wortgröße, sodass sie in der Funktionstabelle genau gleich dargestellt werden .
Die Funktionstabelle wird mit diesem Makro instanziiert:
OPF()
Dies gilt für jede Opcode-Darstellung.OPF()
ist definiert als:Also erweitern sich die ersten vier Opcodes (einmal) zu:
Diese Funktionen zeichnen sich durch das Ergebnis des
DW
Makros aus, das die Richtung und die Byte / Wort-Bits direkt aus dem Opcode-Byte bestimmt. Wenn Sie den Body einer dieser Funktionen (einmal) erweitern, erhalten Sie:Wo die Hauptschleife die
o
Variable bereits gesetzt hat :Nochmaliges Erweitern ergibt das ganze "Fleisch" des Opcodes:
Und die vollständig vorverarbeitete Funktion durchlief
indent
:Nicht der beste C-Stil für den täglichen Gebrauch, aber die Verwendung von Makros auf diese Weise scheint ziemlich perfekt zu sein, um die Implementierung hier sehr kurz und direkt zu gestalten.
Testprogrammausgabe mit Ende der Trace-Ausgabe:
Ich habe einige frühere Versionen in comp.lang.c geteilt, aber sie waren nicht sehr interessiert.
quelle
indent
ed 5810 Zeilen.