8-Bit Turing kompletter Computer in Factorio



Ich möchte mein in Factorio erstelltes Projekt teilen, das auf der Logik dieses Spiels basiert. Dieses Projekt wurde von einem großen Geist inspiriert, der eine Schritt-für-Schritt-Anleitung aufschrieb, um fast das gleiche Auto zu bauen, aber in der realen Welt. Ich empfehle es anzuschauen, es wird Ihnen helfen, dieses Projekt zu verstehen und neu zu erstellen: 8-Bit-Computer



Ich neige meinen Kopf vor Ben Eater, der mir über seinen Kanal so viel beigebracht hat, und ich möchte ihm dieses kleine Projekt widmen. Großartige Arbeit, Ben!



Hier ist ein Computer, der die Fibonacci-Zahl berechnet. Nachdem er die 8-Bit-Grenze (255) überschritten hat, führt er eine bedingte Verzweigung durch und beginnt von vorne:



Bild


Mal sehen, wie dieser Computer funktioniert. Und keine Angst - ich bin sicher, dass Sie es mit den Grundlagen auch schaffen können! Beginnen wir mit dem allgemeinen Layout des Computers. Hier habe ich die wichtigen Bereiche hervorgehoben. Im Folgenden werde ich erklären, wie ich sie erstellt habe.



CLK ist ein Timer, der die Maschinensynchronisation ermöglicht. Moderne CPUs können mit 4-5 GHz (4-5.000.000.000 Hz) betrieben werden. Momentan kann meine Maschine aufgrund der Einschränkungen der Factorio-Logikgatter mit 2 Hz betrieben werden. Jeder Eingang muss für jeden Kombinator (Gate) berechnet werden. Wenn wir also 10 in einer Reihe haben, müssen wir 10 Spieltakte (fps) warten ), um den nächsten Zyklus des Systems zu starten. Andernfalls werden die Signale verwirrt und die Berechnung wird nicht durchgeführt.



PC (Programmzähler, Programmzähler) - Der Zähler gibt an, in welchem ​​Teil des Programms wir uns befinden. Programme werden aus dem 16-Byte-Speicher gelesen (ein Byte enthält 8 Bit). Der Zähler zählt bis zu 16 (4 Bit) im Binärformat (0000, 0001, 0010, 0011, 0100, 0101 ... 1111), sodass jede dieser Berechnungen eine Registeradresse ergibt, die wir später aus dem Speicher und abrufen können damit Aktionen ausführen. Es enthält auch einen Sprung, der den Zähler zurücksetzt. Wir können einen anderen Wert eingeben, um zu einem bestimmten Ort in unserem Speicher / Code zu gelangen.



BUS ist der Hauptverbindungspunkt für alle Computerkomponenten. Wir können Daten zu / von ihm übertragen. Dazu verwenden wir Steuersignale, die die Gates jeder Komponente öffnen / schließen, sodass nie mehr als zwei Gates geöffnet werden (daher werden die Daten nicht gemischt).



ALU ist unser "Taschenrechner", der Additions- und Subtraktionsoperationen ausführt (bei komplexeren CPUs kann er viel mehr!). Es empfängt sofort, was sich in den Registern A und B befindet, und führt dann logische Operationen aus (wir wählen die Operation durch den Befehlsdecoder aus). Diese Daten werden dann an den Bus (Bus) ausgegeben. ALU speichert auch Flags, die in bedingten Verzweigungsfunktionen verwendet werden können.



Register A und B - Sie können 8-Bit-Nummern speichern, die später in der ALU verkettet werden. Beide Register können Daten vom Bus senden und empfangen.



Registeradresse / Decoder (RAD) - Liest eine 4-Bit-Adresse vom Bus und decodiert, wie viel RAM wir lesen sollen. Beispielsweise enthält die Adresse 1110 den Wert 0000 0011 (siehe Bild).



RAM - Moderne PCs verfügen normalerweise über ca. 16 GB Arbeitsspeicher (16.000.000.000 Byte). Wir haben nur 16 Bytes ... Dies lässt uns Platz für 16 Anweisungen oder weniger Anweisungen, damit wir Daten / Variablen in anderen Teilen des Speichers speichern können. Grundsätzlich besteht der RAM hier aus 16 verschiedenen Registern, wie wir sie an anderer Stelle verwenden. Auf sie wird nur über einen Registerdecoder zugegriffen.



Befehlsregister (IR) / Decoder (DC) - Wir können Daten vom BUS in das Befehlsregister einfügen und dann dekodieren, um zu bestimmen, wie sich das Programm verhalten soll. Es werden nur 4 Bits verwendet (türkis hervorgehoben), wodurch wir 16 Arten von Befehlen erhalten, die programmiert werden können. Angenommen, wir haben einen OUT-Befehl, der ausgibt, was in Register A gespeichert ist. Er ist als 1110 codiert. Wenn ein solcher Befehl auf das Register trifft, können wir ihn decodieren und dem Computer mitteilen, wie er vorgehen soll.



Mikrocode-Zähler (MC) - Ähnlich wie der Programmzähler, befindet sich jedoch im Befehlsdecoder. Gibt uns die Möglichkeit, jeden Befehl zu durchlaufen.



LCD / Bildschirm ist eigentlich ein Register, aber komplizierter, da es seinen Inhalt auf einem LCD-Bildschirm druckt (Lamp-Combinator-Display, "Anzeige von Laternen und Kombinatoren").



Schalttafel (SB) - In diesem Bereich sehen Sie, welche Schaltfunktionen wir zur Steuerung der einzelnen Komponenten des Computers senden. Im Moment gibt es 17 verschiedene Schalter, die verschiedene Dinge steuern. Zum Beispiel, wenn wir aus dem BUS lesen möchten, um A zu registrieren, oder in das Speicher- / Befehlsregister usw. schreiben möchten. Die unten beschriebenen Schalter können zur manuellen Steuerung der Maschine verwendet werden.



Flags (F) - ein Register zum Speichern von Flags (Carry [T] - wenn wir beim Addieren 8-Bit-Werte überschreiten, [O] auf Null setzen - wenn die Summe / Differenz 0 ist). Sie helfen uns bei den bedingten Sprungbefehlen.



Bild


Lassen Sie mich zunächst näher auf jede Komponente eingehen, und am Ende werden wir sehen, wie ein Computer programmiert wird, da der Prozess klarer wird. Wenn Sie nur an der Programmierung interessiert sind, fahren Sie mit dem letzten Teil des Artikels fort.



CLK ist unser Timing-Generator, das Wichtigste bei jeder Berechnung. Ich wollte einen Oszillator erstellen, der gleichzeitig ein hohes [C = 1] und ein niedriges [C = 0] Signal hat.



(1) Dies ist ein grundlegender Konstantenkombinator, der einem Generator Signale zuführt. Es springt zu (2), wo Eingabe und Ausgabe zusammengeführt werden. Dank dieser Konfiguration wird der Wert von [C] mit jeder Spieluhr (UPS) um 1 erhöht. Wenn er [Z] erreicht, wird er auf 0 zurückgesetzt. Das heißt, Z gibt an, wie viele Spieluhren benötigt werden Generator zurücksetzen. Es gibt auch einen einfachen Teiler durch 2 unten, der den Generator für die Hälfte der Zeit hoch und für die Hälfte der Zeit niedrig hält. Wenn C kleiner als [Y] ist (was halb [Z] ist), ist der Generator hoch, andernfalls ist er niedrig.



Der Inserter (4) wird als sekundärer Synchronisationsgenerator verwendet, falls wir mehr Kontrolle über die Ticks benötigen. Wenn Sie etwas in die erste Truhe stecken, passiert ein Schlag. Wenn wir 5 Balken benötigen, müssen wir fünf Objekte darin platzieren.



(5) ist das erste Steuersignal. [H] steht für den Befehl HALT {HLT}. Wenn es einen niedrigen Wert [H = 0] hat, arbeitet der Generator normal und wenn er hoch ist, geht er in den manuellen Modus. Dies wird durch die Steuergatter erleichtert, sie (5a) werden für den normalen Betrieb verwendet, und wenn das Signal [H] nicht 0 ist, wird der manuelle Modus aktiviert und [C] (unser CLK) ausgegeben.



Ich habe auch ein invertiertes Signal mit dem Gate (6) erzeugt - wenn der Ausgang niedrig ist, ist das invertierte Signal hoch. Ich benutze es nicht in einem Auto, aber es ist eine gute Idee, es sich zum späteren Nachschlagen zu merken.



Das Signal [C] durchläuft das System durch das grüne Kabel. Ich wollte es auf einem völlig separaten Kabel isolieren (zum Beispiel befindet sich unser BUS auf dem roten Kabel), damit es leicht verfolgt und nicht mit anderen Signalen verwechselt werden kann.



Bild


Register - lassen Sie sich nicht von ihnen einschüchtern. Dies ist wahrscheinlich der komplexeste Teil des gesamten Systems, aber es ist wichtig zu verstehen, wie die gesamte Maschine funktioniert.



Register enthalten Werte. Im normalen Leben müssten wir für jedes der 8 Bits und andere Signale ein Register erstellen. Glücklicherweise können Sie mit Factorio mehrere Signale über eine einzige Leitung senden. Im Wesentlichen sind dies JK-Trigger.



Kurz darüber, wie sie funktionieren. Bei jedem Synchronisationsimpuls spülen sie das Innere und behalten den eingehenden Wert bei. Wenn keine eingehenden Werte (alle Nullen) vorhanden sind, werden diese im Synchronisierungszyklus gelöscht. Natürlich wollen wir nicht, dass sie immer leer sind, schließlich müssen wir Werte in ihnen speichern. Daher verwenden wir die Steuerlogik, über die ich jetzt sprechen werde, und wir werden uns mit der schwarzen Magie befassen, später einen Trigger zu erstellen.



Gespeicherte Werte (1) werden mit Taschenlampen angezeigt. Wenn das Licht an ist, bedeutet es 1 und aus bedeutet es 0. Wie Sie sehen können, speichern wir derzeit den Wert 1110 1001.



Um den Wert an den Bus auszugeben, verwenden wir die Gate-Steuerlogik (2). Wenn das [K] -Signal niedrig ist, gibt dieses Gate alles, was sich im Register befindet, an den Hauptbus aus.



Warum verwenden wir es, wenn das Signal niedrig und nicht hoch ist? Da Logikgatter alles ausgeben, was ihnen zugeführt wird (rot *), hat der Bus ein Signal [K], und wir brauchen dieses nicht, wir brauchen nur [7, 6, 5, 4, 3 2, 10]. Aus dem gleichen Grund müssen wir die Steuersignale mit dem Gate (3) herausfiltern, damit wir [K] nur dann empfangen, wenn wir es brauchen.



Das Gate (4) ist sowohl mit dem Bus (rotes Kabel) als auch mit den Steuersignalen (grünes Kabel) verbunden. Wie im vorherigen Fall empfängt das Register einen Eingang, wenn das Signal [A] niedrig ist. Um alle anderen Signale herauszufiltern, verwenden wir ein Logikgatter (4a). Tatsächlich nimmt es alle Eingänge vom Bus und unerwünschte Steuersignale und fügt sie dann dem Kombinator (4b) hinzu, dessen Eingänge immer Signale [7, 6, ... 0] = 1 sind. Dann, wenn eines der Signale ist 0, dann gibt es jedes dieser Signale aus = 1. Es ist einfach, oder? In diesem Fall gelangen nur die für uns wichtigen Werte vom Bus in die Register (Werte 0 bleiben weiterhin 1, sie blinken für einen Taktzyklus und bleiben dann während des gesamten Zyklus mit hohem CLK deaktiviert).



In einer solchen Situation gibt der Verschluss (6) das Signal [SCHWARZ] aus, wenn [C] hoch geht, und der Verschluss (6a) hebt [C] auf. Da jedoch 1 weitere USV erforderlich ist, um auf Null zu reduzieren, gibt das Gate (6) in so kurzer Zeit ein Signal aus.



Dieses Signal wird dann an das Gate (7) übertragen, das sich ebenfalls für kurze Zeit öffnet. Das Gatter (7b) macht das [SCHWARZ] -Signal ungültig, so dass es nicht im Gatter (8) gespeichert ist, das als Verwalter unseres Signals verwendet wird. Dies ähnelt dem CLK-Netz, da sowohl der Eingang als auch der Ausgang miteinander verbunden sind. Wenn keine Eingabe vorhanden ist, bleibt diese unverändert. Wenn wir die Uhr erneut ändern, ohne neue Daten einzugeben, gibt das Gate (7a) ein Signal ein, das in Bezug auf den im Register gespeicherten Wert invertiert ist, um es zu löschen.



Jetzt, da wir wissen, wie Änderungserkennung und -register funktionieren, wissen wir fast alles.



Bild


ALU - Addiere / subtrahiere ständig, was sich in den Registern (A) und (B) befindet. Wir steuern nur, ob es an den [Z] -Bus ausgegeben oder der Modus geändert werden soll, um [S] zu subtrahieren.



Wie es funktioniert? Um ein vollständiges Bild zu erhalten, empfehle ich, einige Videos von Ben Iter anzusehen, da die Erklärung dreimal länger ist als mein Artikel.



Ich werde nur erklären, wie man einen solchen Addierer in Factorio erstellt.



Dazu benötigen wir drei Arten von Gates: XOR (1), AND (2) und OR (3). Glücklicherweise sind sie einfach zu erstellen. Da wir mehrere Signale auf derselben Leitung verwenden können, können unser erstes XOR- und UND-Gatter auf nur zwei vereinfacht werden, und wir müssen sie nicht für alle 8 Bits ausführen. Dies ermöglicht es uns, (4) Teil der Kette zu machen und sie für jedes Bit zu duplizieren.



Die Subtraktion wird mit dem Signal [S] durchgeführt, das die vom Register (B) kommenden Signale invertiert.



Die ALU gibt auch Übertrag (wenn die Summe 8 Bit überschreitet) aus, setzt sie auf Null und speichert sie im Register rechts (F im Bild mit dem Hauptcomputer).



Bild


LCD / Bildschirm - Sieht einschüchternd aus, aber ehrlich gesagt war es am einfachsten zu machen. Es braucht nur Zeit, um alles richtig anzuschließen.



Zuerst erstellen wir ein Register, dessen Eingang vom [P] -Signal gesteuert wird. Dann multiplizieren wir jedes Bit mit seinem Wert und wandeln es in einen Dezimalwert um, um dasselbe Signal mit einem Dezimalwert zu erhalten (dies ist eine Art Betrug in Factorio, aber das Fehlen programmierbarer EEPROMs erlaubt es uns nicht, zu viel umzudrehen). Zum Konvertieren müssen wir nur das erste Bit [0] nehmen und mit * 1 multiplizieren, dann das zweite Bit [1] nehmen und mit * 2, das dritte [2] mit * 4 usw. multiplizieren. Dabei geben wir einen beliebigen Wert aus, um die resultierende Zahl zu bestimmen (in diesem Fall ist es [ein Wassertropfen]).



Das LCD wird in 9 Schritten für Zahlen (3) eingeschaltet. Wir müssen nur die Lichter einstellen, die den Schritten (1) entsprechen, und dann die Tore (2) verwenden, um den Wert genau dort auszugeben, wo wir ihn brauchen. Sie müssen nur daran denken, einen separaten Konstantenkombinator (3) hinzuzufügen und ihn nur mit einem speziellen Tor (2) zu verbinden. Dann verbinden wir einfach alle Lichter miteinander und geben ihnen Anweisungen, in welchem ​​Schritt sie sich befinden (1).



Bild


RAM / Speicherregister (RAD) - Hier werde ich erklären, wie RAM ungefähr funktioniert.



Wir kennen bereits Register, die Synchronisationsimpulse zum Speichern von Werten verwenden. RAM ist nur ein Gitter von 16 (in unserem Fall) verschiedenen Registern (2). Ihre Eingänge werden von einem anderen Register (1) gesteuert, das 4 Bits [0, 1, 2, 3] speichert, das uns sagt, auf welchen Speicherort wir zeigen. Dies wird unter Verwendung eines Adressdecoders (3) implementiert, der ähnlich wie LED / Bildschirm funktioniert. Jedes Gate empfängt einen Wert vom Konstantenkombinator (in unserem Fall 1100 bin = 10 dec) und gibt dann den Signalnamen des entsprechenden Registers (in unserem Fall [M]) aus, damit auf den Wert zugegriffen werden kann (in unserem Fall 00110) 0011).



Erwähnenswert ist hier auch die manuelle Speicherprogrammierung. Dies kann mit dem Signal [W] erfolgen, das mit dem Konstantenkombinator (4) aktiviert / deaktiviert wird. Ein anderer Kombinator (5) ermöglicht es uns, die Adresse zu ändern, und wir verwenden einen anderen Kombinator (6), um den Wert einzugeben. Am Ende legen wir einfach alles in die Truhe (7), damit Sie beim Synchronisieren die Werte manuell in den Arbeitsspeicher übertragen, ohne die Haupt-CLK des Computers zu berühren.



Bild


Programmzähler (PC) - seine Aufgabe ist es zu berechnen, in welchem ​​Programmschritt wir uns befinden (1). Beim Start hat es den Wert 0000, diese Adresse wird aus dem RAM gelesen und zur Interpretation in das Befehlsregister übertragen. Nachdem der Befehl abgeschlossen ist, können wir den Zähler mit dem Signal [X] inkrementieren, dann wird es gleich 0001, und bei der nächsten Iteration wird diese Adresse aus dem Speicher genommen, und die Schleife wird fortgesetzt.



Natürlich müssen wir manchmal bedingungslose oder bedingte Verzweigungen zu anderen Teilen des Programms durchführen. Wir können dies mit dem Signal [J] tun. Wenn es niedrig ist (in unserem Fall bedeutet niedrig aktiv), wird es zurückgesetzt, liest vom Bus die Adresse, zu der es springen soll, und speichert sie in Register (2). Wenn [J] wieder hoch geht, signalisiert es dem PC den Änderungsdetektor (direkt unter 2).



Der Zähler selbst funktioniert ähnlich wie CLK, zählt jedoch nicht ständig Taktzyklen, sondern Taktzyklen, wenn Änderungen an CLK erkannt werden (tatsächlich nur, wenn X und CLK aktiv sind). Dies ist direkt im Bild zu sehen (1).



Das Signal kann dann mit dem Steuersignal [C] an den Bus angelegt werden.



Bild


Schalttafel (SB) - Dies ist der richtige Zeitpunkt, um jedes im Programm verwendete Steuersignal zu erläutern.



Die Signale sind in zwei Farben unterteilt, grüne nach links und rote nach rechts. Jedes Signal von konstanten Kombinatoren wird tatsächlich als Werte [-1] übergeben. Das heißt, wenn die Kombinatoren auf *! = 0 gesetzt sind, können sie Signal 1 ausgeben. Wenn die Steuerlogik das Signal [1] sendet, werden sie aufgrund dessen gelöscht, und wir erhalten [0] und in allen Fällen wir brauche nur das (Sie können in dem Teil lesen, in dem ich Register erkläre).



[H] - stoppt den Taktgenerator (schaltet in den manuellen Modus), ein hohes Signal bedeutet, dass CLK nicht geschaltet wird.



[Q] - Die Adresse des RAM, in dem sich das Register befindet, mit einem hohen Signal. Das RAM-Adressregister speichert den Wert vom Bus im nächsten CLK-Zyklus.



[Y] - RAM-Speichereingang, wenn das RAM-Signal hoch ist, speichert es den Wert vom Bus im nächsten CLK-Zyklus (an der im Adressregister gespeicherten Adresse).



[R] - RAM-Ausgang, wenn das RAM-Signal hoch ist, gibt es den Wert im nächsten CLK-Zyklus an den Bus aus (von der im Adressregister gespeicherten Adresse).



[V] - Eingang des Befehlsregisters Wenn das Signal hoch ist, speichert das Befehlsregister den Wert vom Bus im nächsten CLK-Zyklus.



[U] - Ausgabe des Befehlsregisters Wenn das Signal hoch ist, gibt das Befehlsregister den Wert im nächsten CLK-Zyklus an den Bus aus (nur die letzten 4 Bits [3, 2, 1, 0]).



[C] - Ausgabe des Programmzählers Wenn das Signal hoch ist, gibt der Programmzähler den Wert im nächsten CLK-Zyklus an den Bus aus (nur die ersten 4 Bits [7, 6, 5, 4]).



[J] - Eingabe der Übergangsadresse, wenn das Signal hoch ist, setzt der Programmzähler den Wert vom Bus im nächsten CLK-Zyklus (nur die letzten 4 Bits [3, 2, 1, 0]).



[X] - Erhöhen des Werts des Befehlszählers, wenn das Signal hoch ist, führt der Programmzähler im nächsten CLK-Zyklus ein Inkrement durch.



[A] - Eingang von Register A mit einem hohen Signal in Register A, der Wert vom Bus wird im nächsten CLK-Taktzyklus gespeichert.



[K] - Ausgabe von Register A mit einem hohen Signal von Register A, der Wert wird im nächsten CLK-Taktzyklus an den Bus ausgegeben.



[Z] - ALU-Pin, wenn das ALU-Signal hoch ist, gibt es den Wert im nächsten CLK-Zyklus an den Bus aus.



[S] - Subtraktion (ALU) Wenn das Signal hoch ist, ändert die ALU ihren Modus von Addition zu Subtraktion.



[B] - Eingang von Register B mit einem hohen Signal in Register B, der Wert vom Bus wird im nächsten CLK-Taktzyklus gespeichert.



[L] - Ausgabe von Register B mit einem hohen Signal von Register B, der Wert wird im nächsten CLK-Taktzyklus an den Bus ausgegeben.



[P] - Eingabe des LCD / Bildschirm-Registers, wenn das Signal hoch ist, wird der Wert vom Bus im nächsten CLK-Zyklus im LCD / Bildschirm-Register gespeichert und dieser Wert wird angezeigt.



[W] - Eingang des Flags-Registers, wenn das Signal hoch ist, speichert das Flags-Register Übertrag von ALU (wenn 8 Bits überschritten werden), Null (wenn ALU-Betrieb = 0000 0000).



[rosa Signal] - Übertragsflag angehoben [T]



[türkisfarbenes Signal] - Null-Flag angehoben [O]



Nehmen wir nun an, wir müssen eine OUT-Aktion ausführen: Nehmen Sie das, was sich in Register A befindet, und drucken Sie es auf LCD / Bildschirm (Register). .. Um dies manuell zu tun, müssen wir nur das [K] -Signal (Ausgang von Register A -> Bus) und das Signal [P] (Bus -> Registereingang lcd) einschalten (indem wir den konstanten Kombinator für einen bestimmten Buchstaben ausschalten) / screen), dann führen Sie die CLK-Uhr aus.



Bild


Befehlsregister / Decoder / Mikrocode-Zähler - hier beginnt die Magie. Jetzt, da wir wissen, wie man einen Computer manuell steuert, hilft uns dies zu verstehen. was getan werden muss, damit er sich selbst verwalten kann.



(1) Der Mikrocode-Zähler zählt bis 8 (die Anzahl kann reduziert werden, wenn wir nicht so viel benötigen), dh wir können 8 verschiedene Ein / Aus-Befehle ausführen, um eine Aktion in einem Befehl auszuführen.



(2) Befehle werden vom Bus in ein Register eingelesen, dazu müssen wir die Signale [C] (Ausgang des Befehlszählers -> Bus) und [Q] (Bus -> Eingangsspeicheradresse) einschalten und dann Lesen Sie RAM [R] (Ausgangs-RAM -> Bus) in das Befehlsregister [V] (Bus -> Befehlsregister) und erhöhen Sie den Zähler [X].



Da alle oben genannten Aktionen jedes Mal ausgeführt werden müssen, habe ich all dies (4) direkt mit dem Mikrocode-Zähler verbunden, sodass dies jedes Mal geschieht, wenn der Zähler die Schritte 0 und 1 durchläuft.



Wenn sich etwas im Register befindet, können wir es verwenden Wahrheitstabellen ähnlich denen, die wir für das RAM-Adressregister erstellt und auf dem LCD / Bildschirm ausgegeben haben.



Die [D] -Werte aus dem Befehlsregister (es ist immer größer als 8) und dem Mikrocode-Zähler (immer gleich oder kleiner als 8) können addiert werden, und unter Verwendung der resultierenden Zahl können wir Logikgatter erstellen. Dies geschieht durch Tore (3).



Das Beispiel zeigt den Befehl 0110 XXXX (48 + X im Dezember, für den ich den JMP-Befehl programmiert habe), der dann zu Schritt 2 des Mikrocode-Zählers hinzugefügt wird, der 50 ergibt.



Ein weiteres Beispiel: Befehl ADD (0010 XXXX - 16 + X im Dezember); Nach den Schritten 0 und 1 ist der Mikrocode 2, dh die Register 18-24 können für einen anderen Teil des Codes verwendet werden (in diesem Fall benötigen wir nur 18-20, da ADD ein dreistufiger Prozess ist).



(5) Übertragsflags werden von einfachen Logikgattern verarbeitet, deren Eingabe nur deaktiviert wird, wenn kein Übertrag [T] oder Null [O] an die Logikgatter angelegt wird.



Unten ist meine vollständige Liste der implementierten Befehle (Sie können sie ändern oder Ihre eigenen hinzufügen!):



0 NOP - 0000 XXXX - macht nichts.



1 LDA X - 0001 XXXX - Lädt den Wert von der X-RAM-Adresse in Register A.



2 ADD X - 0010 XXXX - lädt den Wert von der X-RAM-Adresse in Register B, gibt dann die Addition aus und legt sie in Register A ab.



3 ADD X - 0011 XXXX - lädt den Wert von der X-RAM-Adresse in Register B, und gibt dann die Subtraktion aus und legt sie in Register A ab.



4 STA X - 0100 XXXX - lädt den Wert aus Register A und speichert ihn im RAM unter der Adresse



X.5 LDI X - 0101 XXXX - lädt den Wert schnell aus dem Befehlsregister ( nur 4-Bit-Wert) in Register A.



6 JMP X - 0110 XXXX - bedingungslos (tritt immer auf), der Übergang zum Wert X (PC weist den Wert von X zu).



7 JC X - 0111 XXXX - Wenn der Übertragswert [T] wahr ist, springt er zum Wert X (weist PC X zu).



8 JO X - 1000 XXXX - Wenn die Übertragung von Null [O] wahr ist, geht sie auf den Wert X (weist X den PC-Wert zu).



9 UNSER X - 1001 XXXX - zeigt den Wert aus der X-RAM-Adresse an.

...

...

...

14 OUT - 1110 XXXX - Führt die Anzeige von Register A aus (X macht nichts).



15 HLT - 1111 XXXX - stoppt den Synchronisationsgenerator (X macht nichts).



Lassen Sie uns ein einfaches Programm schreiben und sehen, wie es funktioniert!



0 LDA 3 - Laden Sie den Wert aus der Speicheradresse in Register A. 3



1 OUT - Zeigen Sie den Wert aus Register A an.



2 HLT - Stopp CLK, dh die gesamte Maschine.



3 42 - gespeicherter Wert



Das heißt, dieses Programm gibt den an Adresse 3 RAM (0011 in binär) gespeicherten Wert aus.



Konvertieren wir es in eine Binärdatei:



0 Adresse: 0000, Wert: 0001 0011



1 Adresse: 0001, Wert: 1110 0000



2 Adresse: 0010, Wert: 1111 0000



3 Adresse: 0011, Wert: 0010 1010



Das heißt, um ein Programm zu schreiben, Wir müssen in den Speicher schreiben (W auf dem Speicherfeld; siehe den Teil mit dem RAM-Image), beginnend mit der Adresse 0000, und den Wert 0001 0011 eingeben (0001 bedeutet den LDA-Befehl, wobei 0011 X ist, dh Adresse 3 im Speicher) ...



Dann machen wir dasselbe für die anderen Teams.



Vergessen Sie nicht, [W] wieder grün zu färben und den Zähler anzuhalten.



Sie können den PC auch zurücksetzen, indem Sie mit J springen (der CLK-Beat muss nicht geändert werden).



All Articles