Manchmal fällt mir eine Idee ein, sie loszuwerden, was sehr schwierig ist. Das ist mir passiert.
Ich entschied mich für die Erstellung einer virtuellen Maschine (VM), da ich zu diesem Zeitpunkt keine Ideen hatte, schien mir dies eine großartige Idee zu sein. Wenn Sie interessiert sind, dann gehen Sie zum Schnitt!
Theorie
Zunächst eine kleine Theorie. Was ist eine virtuelle Maschine im Allgemeinen? Dies ist ein Programm oder eine Reihe von Programmen, mit denen Sie eine Art Hardwareplattform emulieren können, dh einen Computeremulator.
Virtuelle Maschinen selbst sind unterschiedlich. Beispielsweise ist Virtual Box eine klassische virtuelle Maschine, mit der Sie einen realen Computer emulieren können. JVM (Java Virtual Machine) kann dies jedoch nicht.
Meine VM-Version wird der JVM etwas ähnlich sein, einfach weil es sich um ein Schulungsprojekt handelt, das nicht auf die Erstellung einer leistungsstarken VM abzielt.
Die Erinnerung
Lassen Sie uns nun die Erinnerung herausfinden. Um Speicher zu erstellen, habe ich mich für ein vorzeichenloses int-Array entschieden. Die Größe des Arrays wird mithilfe eines Makros bestimmt. In meiner Version beträgt die Speichergröße 4096 Byte (es gibt 1024 Elemente im Array, und da auf den meisten Plattformen 4 Bytes für vorzeichenlose int-Daten zugewiesen sind, dann 1024 * 4 = 4096), definieren wir unter anderem 8 Register durch 8 Zellen in jeder sind bereits 256 Bytes (8 * 8 * 4 = 256). Es sieht so aus:
#define MEMSIZE 1024 unsigned int memory[MEMSIZE]; unsigned int reg[8][8];
Programmierung
Wir haben Speicher, aber wie schreibt man jetzt Code für unsere VM? Jetzt werden wir uns mit diesem Problem befassen und zunächst die Befehle bestimmen, die unsere Maschine ausführen wird:
enum commands { CRG = 1, CRC, PRG, PRC };
Jedes Team hat ein eigenes Flag, das einige zusätzliche Parameter definiert.
Wir werden Flaggen beschreiben:
enum flags { STDI = 1, STDA };
Der Standardbefehl hat die Form: [Befehl] [Flag] [Daten] (das Erscheinungsbild einiger Befehle kann abweichen). Auf dieser Grundlage schreiben wir einen einfachen Interpreter:
if (memory[cell] == CRG && memory[cell + 1] == STDI) { indxX = memory[cell + 2]; cell++; } else if (memory[cell] == CRC && memory[cell + 1] == STDI) { indxY = memory[cell + 2]; cell++; } else if (memory[cell] == PRG && memory[cell + 1] == STDI) { reg[indxX][0] = memory[cell + 2]; cell++; } else if (memory[cell] == PRC && memory[cell + 1] == STDI) { reg[indxX][indxY] = memory[cell + 2]; cell++; }
indxX & indxY sind Variablen, die die aktuelle Cursorposition im Register speichern.
Zelle ist eine Variable, die die aktuelle Cursorposition im Speicherarray speichert.
Das Programmieren mit Zahlen ist jedoch nicht sehr praktisch. Mit dem C-Präprozessor werden wir unseren Assembler beschreiben. Ich verstehe, dass das Schreiben von Asm mit Makros nicht sehr gut ist, aber diese Lösung ist vorübergehend.
Unser ASM-Code sieht folgendermaßen aus:
#define $CRG {memory[memIndx++] = CRG;} #define $CRC {memory[memIndx++] = CRC;} #define $PRG {memory[memIndx++] = PRG;} #define $PRC {memory[memIndx++] = PRC;} #define _$STDI {memory[memIndx++] = STDI;} #define _$STDA {memory[memIndx++] = STDA;} #define _$DATA memory[memIndx++] =
memIndx ist eine Variable, die die aktuelle Cursorposition im Speicherarray speichert.
Und hier ist der Code auf unserem ASM, der 123 unter der Adresse [1] [0] (erstes Register, Nullzelle) in das Register einfügt:
$CRG _$STDI _$DATA 1; $CRC _$STDI _$DATA 0; $PRC _$STDI _$DATA 123;
Herzlichen Glückwunsch, wir haben jetzt einen Anschein von Asm für unser Auto!
Programme starten
Wir haben es geschafft, unsere Maschine dazu zu bringen, Programme auszuführen, aber der Code ist von einer Maschine zur anderen nicht portierbar. Deshalb werden wir jetzt einen Maschinencodegenerator von asm erstellen (und ich erinnere Sie daran, dass unsere Maschine im Gegensatz zu echten Computern einen Maschinencode hat, der nicht in binärer Form dargestellt wird). und Dezimalzahlen) ist im Prinzip nicht so schwierig, aber lassen Sie uns zunächst über die Implementierung nachdenken.
Zuerst haben wir asm-Code, jetzt müssen wir ihn in Zahlen übersetzen und dann den resultierenden Maschinencode in eine .ncp-Datei schreiben (numerisches Code-Programm, eigentlich ist es eine Textdatei, aber um ihn von allem anderen zu unterscheiden, habe ich mir meine eigene Erweiterung ausgedacht) Wir müssen die .ncp-Datei ausführen. Dies ist einfach, da der zuvor geschriebene Interpreter die Zahlen erkennt. Wir müssen nur Daten aus der Datei extrahieren und sie mit atoi () in Zahlen umwandeln.
Gehen wir von Worten zu Taten:
Lesen Sie den Code und schreiben Sie ihn in eine Datei:
if (memory[i] == CRG && memory[i + 1] == STDI) { fprintf(code, "%d %d ", CRG, STDI); i++; } else if (memory[i] == CRC && memory[i + 1] == STDI) { fprintf(code, "%d %d ", CRC, STDI); i++; } else if (memory[i] == PRG && memory[i + 1] == STDI) { fprintf(code, "%d %d ", PRG, STDI); i++; } else if (memory[i] == PRC && memory[i + 1] == STDI) { fprintf(code, "%d %d ", PRC, STDI); i++; }
Der Code ist Teil des Hauptteils der Funktion ncpGen ().
Lesen einer Datei und ihrer Ausführung:
if (prog != NULL) { fread(txt, 1, len, prog); tok = strtok(txt, " "); while (tok != NULL) { memory[i] = atoi(tok); tok = strtok(NULL, " "); if (argc == 3 && strcmp(argv[2], "-m") == 0) { printf("%d\n", memory[i]); } i++; } memInter(); } else { perror("Fail"); }
Definieren wir nun ein Makro, sodass der Code anstelle der Interpretation von asm in .ncp umgewandelt wird:
#define _toNCP(name) {strcpy(filename, name);} {ncpGen();}
Wenn überhaupt, dann enthält der Artikel nicht den gesamten Code, sondern nur einen kleinen Teil davon!
Der vollständige Code befindet sich im Projekt-
Repository .
Vielen Dank fürs Lesen!