Heute mal eine Neuigkeit: in der Reihe „Wir wissen es besser als die Industrie“ heute ein Konzept für einen Prozessor. Ich habe das mal angefangen für einen 32 Bit Risc Prozessor, aber in der Klasse habe wenig Erfahrung in Bezug auf Befehlssatz. Die habe ich in der 8 und 16 Bit Klasse und inspiriert durch die Unzulänglichkeiten der frühen 8 und 16 Bitter von Intel habe mich an das Design eines eigenen Prozessors gemacht. es sollte ein 8 Bitter sein, aber es ist ein 16/24 Bitter geworden. Dazu später mehr. Ich will den Artikel auch nutzen ein paar Dinge zu erklären die vielleicht den einen oder anderen interessieren.
Der Grundgedanke war das der Befehlssatz symmetrisch sein Darunter versteht man dass die Register gleichberechtigt sind. Also z.B. jedes Ziel und Quelle einer Rechenoperation sein kann. Das war beim 8080/Z80 nicht der Fall. Alle 8 Bit Rechenoperationen nutzten dort als einen der Operanden den Akkumulator und dort landete auch das Ergebnis. Ein solches „unsymmetrisches“ Modell macht dann zahlreiche Kopieraktionen in den Registern notwendig, wenn man das Ergebnis z. B. noch braucht. Wenn man weiß wie Befehle dekodiert werden, dann ergibt sich beim symmetrischen Modell eine Problematik. Nehmen wir 16 Register bei einem unsymmetrischen Prozessor könnte der Opcode des Befehls ADD A,Reg z.B. so aussehen:
0100.RRRR
Das Befehlswort wird eingelesen, der Prozessor stellt anhand der Maske 0100 in den obersten 4 Bit fest das es ein ADD-Befehl ist und in den unteren 4 Bits steht das Quellregister. Für 16 Register braucht man 4 Bits (ein Nibble). Wenn der Befehlssatz symmetrisch ist sähe der Befehl so aus : Add Reg1,Reg2 und man braucht 8 Bits für die Angabe der beiden Quellregister (wenn man die Dreiadressadressierung nimmt sogar 12 Bits). Damit braucht man zwei Byte für einen Befehl. 16 Register ist auch die Zahl die ich vorhergesehen habe.
Nun haben 8 Bitter mit 64 Kbyte maximal adressierbarem Speicher keinen sehr großen Adressbereich. Wer wie ich seine ersten Erfahrungen mit einem 8-Bit Rechner gemacht hat wie einem C64, Armstrad oder Sincailr Spectrum. Der weiß – man kann damit einiges machen. Das geht aber nur weil viele Befehle nur ein Byte lang sind. Hier wären sie nun zwei Byte lang und das ist schon ein großes Manko. der Speicher eines 8-Bit Systems sollte eher größer als kleiner sein. Da kam ich auf die zweite Änderung: es gibt einen getrennten Daten- und Codebereich, ebenso zwei Adressbereiche. Das macht in meinem Fall zwar ein 64-poliges Gehäuse notwendig, aber es gibt einige Vorteile:
- Mindestens doppelt so großer Speicher
- Paralleler Transfer (zwei Datenbusse) erhöht die Geschwindigkeit
- Besserer Prefetch möglich
- Keine Hardware notwendig zum Trennen von Daten und Codezugriffen.
In meinem Fall habe ich jeweils 16 Bit Adressen für Daten und Code. Als ich mich an das Aufstellen der Codes machte (siehe unten) stellte sich raus, das die meisten Befehle nun zwei oder drei Bytes lang sind, die Ein-Byte Befehle sind in der Minderheit denn sie kommen nur bei keinem Parameter vor oder nur einem Parameter. Ich habe mich für eine Maßnahme entscheiden die den Prozessor kräftig auf Trab bringt: Anstatt einzelner Bytes sind Befehle immer drei Bytes lang. Benötigt man nur zwei Bytes, dann enthält das letzte Byte den NOP Befehl, das Bitmuster für 0. Die Befehle ohne Parameter die eigentlich in ein Byte reinpassen habe ich in zwei geschoben so sind zum einen viele Opcodes frei und zum anderen ist das Befehlsformat einheitlicher.
Anders als bei anderen Prozessoren hat NOP aber nicht die Bedeutung einige Takte nichts zu tun, sondern ist ein Füllbyte das bei der Ausführung ignoriert wird also keine Zeitverzögerung generiert, dekodiert werden immer die gesamten 3 Byte. Der Vorteil wird deutlich wenn man sich ansieht, wie viele Takte bestimmte 8080 Befehle haben:
Aktion | Mnemonic 8080 | Takte 8080 | Takte Mein Prozessor |
---|---|---|---|
Lade Register mit Register | Mov Reg,Reg | 4 | 4 |
Lade Register mit 16 Bit Konstante | LXI Reg | 10 | 4 |
Lade Register mit 8 Bit Inhalt der Adresse die in einem zweiten Register steht | Mov Reg,M | 7 | nicht verfügbar |
Lade Register mit 16 Bit Inhalt der Adresse die in einem zweiten Register steht | LHLD | 16 | 10 |
Lade Adresse mit dem Inhalt des Registers | STAX | 10 | 7 |
Diese Unterschiede kommen dadurch zustande:
Bei einem Befehl der nur interne Register beim 8080 benutzt, gibt es folgenden Taktablauf:
- Lege Adresse auf den Adressbus
- Prüfe ob Daten auf dem Datenbus anliegen
- Lese Daten ein
- Führe Operation aus
Bei dem zweiten Befehl LXI muss nun nochmals die Adresse auf den Bus gelegt werden, erneut gewartet werden bis die Daten anliegen und dann kann man das erste niedrigwertige Byte holen, nach einem erneuten Anlegen der Adresse+1 wiederholt sich das Spiel. Das sind pro Lesezyklus immer 3 Takte mehr, also zehn Takte.
Pro Lesezyklus eines Bytes oder Schreiben eines Bytes verlängert sich die Ausführung um 3 Takte. Bei meinem Prozessor ist nun aber das ganze Befehlswort schon im Prozessor, er transferiert nicht 8 Bit sondern 24 Bit über den Code-Datenbus. Das bedeutet beim zweiten Befehl entfallen die Zugriffe auf den Speicher. Beim Befehl LHLD braucht man einen weiteren Zugriff, diesmal auf den Code-Datenbus. Da dieser aber 16 Bit auf einmal transferiert, braucht man nur 3 weitere Takte anstatt 6.
Das ist die erste Maßnahme um Geschwindigkeit aufzunehmen. Die zweite ist, dass man bei dem starren Befehlsformat sehr einfach eine Pipeline implementieren kann. Zusammen mit einem kleinen Buffer von zwei Bytes für Daten und sechs für Code kann man die Geschwindigkeit deutlich erhöhen. Dier mal ein kleines Zeitdiagramm für den Fall eines Speicherzugriffs auf den Datenbereich (7 Takte ohne Beschleunigung)
Takt | 1 | 2 | 3 | 4: Zyklus 2 beginnt | 5 | 6 | 7: Zyklus 3 beginnt | 8 |
---|---|---|---|---|---|---|---|---|
Buseinheit Code | Adresse auf Bus | Warte auf Daten | Hole Daten | Adresse auf Bus | Warte Auf Daten | Hole Daten | Adresse auf Bus | Warte auf Daten |
Ausführungseinheit | Führe aus | Führe aus | ||||||
Buseinheit Daten | Adresse auf Bus | Warte auf Daten | Hole Daten |
Der allererste Zyklus auf einer neuen Adresse (bei einer Verzweigung oder Sprung dauert immer 4 Takte, der folgende unabhängig von der Anzahl der Speicherzugriffe immer 3 Takte. Ausnahme sind einige Befehle, die intern sehr lange brauchen. Das sind Multiplikation und Division diese profitieren von der integrierten dreistufigen Pipeline. Als man diese im 80286 einführte sank die Ausführungszeit der DIV/Mul Operationen stark, denn die werden intern als Mikroprogramm ausgeführt und brauchen keine Speicherzugriffe außer beim Start so von 80-168 je nach Adressierung bei 8086 auf 17-28 beim 286. Dies war der Grund warum der Norton -Sysinfo einen so hohen SI-Wert für IBM-AT kompatible Rechner ausgab, denn dort steckten genau diese Operationen in einer Schleife (ein IBM-AT wurde z.B. 8,8-fach schneller als ein 8088 mit 5 MHz angezeigt anstatt rund 2,5 mal wie es richtig war).
Eine ähnliche Strategie gab es auch beim 8086 der eine 6 Byte Qeue hatte und einen automatischen Fetch d.H. während der Ausführungszeit las der Prozessor schon mal vorrauschauend die nächsten Bytes. Gerade diese Eigenschaft bremste den 8088 stark aus, weil durch den 8-Bit-Bus der Fetch nur noch selten angewandt werden konnte.
Kurzum: Dieser Prozessor ist deutlich schneller als andere 8-Bitter aber auch einfache 16-Bitter wie der 8086. (Würde man synchrones DRAM unterstützen, so wäre die Regelzeit eines Zyklus sogar nur 2 Takte und bei aufeinderfolgendem Code/Daten sogar nur noch 1 Takt.
Adressiert wird der Code und die Daten wortweise. Bei dem Code ist ein Wort 3 Bytes lang wobei drei denkbare Fälle möglich sind:
- Drei Byte Wort (enthält Adresse oder drei Register)
- Zwei Byte Wort (die meisten Befehle) + NOP
Drei NOP Bytes (Speicherzustand beim Reset, da der den Code 0 hat).
Nimmt man an das normaler 8 Bit Code im Mittel zwei Byte lang ist (es gibt viele ein-Byte-Befehle aber auch zwei und drei Byte Befehle) so bietet diese Lösung 100% mehr effektiven Speicher obwohl der Codebereich 192 KByte beträgt. Man kann auch Befehle einsparen weil mehr interne Register zur Verfügung stehen und alle mit 16 Bit rechnen.
Als ich den Instruktionssatz aufgestellt hatte, fiel mir was auf – es gibt gar keine 8 Bit Befehle. Alle Register sind, weil sie auch Adressen aufnehmen 16 Bit breit. Die Operationen bisher also auch immer 8 Bit. Wenn ich mir die arithmetischen Operationen ansehe, so sehe ich keine echte Notwendigkeit für 8 Bit. Bei Rechnungen reichen sie meist nicht aus, sie sind wegen dem 16-Bit Datenbus auch nicht schneller. Es gibt noch zwei Operationen bei denen könnte byteweise Operationen notwendig sein. Das eine sind Ein/Ausgabebefehle, das zweite sind Vergleiche, die braucht man z.B. bei einer Stringsuche. Dafür gibt es mehrere Lösungen.
Man könnte einen Befehl Compare Byte / Output / Input Byte einführen. Das zweite ist es bei Ein/Ausgabeoperationen einfach die oberen 8 Bit nicht zu verdrahten und bei Strings hilft einfach eine Konvention: man speichert immer zwei Bytes ab, nutzt also Unicode, das ist allerdings doch sehr platzverschwendend. So wäre ein Compare Byte Befehl nicht schlecht. Den Platz gibt es: im ersten Byte sind noch 164 Opcodes unbelegt, das ist so viel, dass man sogar alle arithmetischen Befehle mit 8 Bit integrieren könnte.
Ansonsten habe ich mich am 8080/Z80 Befehlssatz orientiert. Ich habe nicht alle Befehle übernommen, nur die die ich für sinnvoll hielt. Anstatt dem Befehl PUSH Flags könnte man natürlich auch den EXX Befehl des Z80 nehmen. Ich bin bei dem PUSH geblieben bei so eine Interruptroutine höhere Priorität eine nieder priore unterbrechen kann. Stackoperationen dauernd leider relativ lange. Man könnte auch einen 256 Wort Stack auf dem Chip nutzen. Alternativ, das weiter unten auch ein kleiner Cahce angedacht ist einen externen Speicher der für Cache und Stack gedacht ist.
Weiter ging ich bei den Adressierungsoperationen. Da gibt es ja eine Menge wie ich seit dem Studium der Befehlssätze von MicroVAX, NS32032 und Z8000 weiß. Ich habe mich auf einige sinnvolle Erweiterungen der drei Basisadressierungen des 8080 entschieden:
Allgemein gilt: Die Schreibweise Lad Reg1,Reg2 heißt: Lade das Register1 mit dem Inhalt von Register2. Folgende Adressierungsarten gibt es:
- Register zu Register: Ld Reg,Reg – Kopieraktionen in Registern. In meinem Prozessor weil alle Register gleichberechtigt sind weniger oft nötig als im 8080, da dieser oft Ergebnisse aus dem Akku in andere Register übernehmen musste.
- Direkt: Eine konstante folgt in Byte 2+3. Das kann eine konstante für eine Rechnung sein oder eine Adresse auf die man im folgenden einfach über das register zugreifen kann. Eine direkte Adressierung ist nur mit einer konstante als Quelle nicht als Ziel möglich.
- Indirekt: Es gibt zwei Möglichkeiten, beide haben ein Klammerpaar als Symbol (): Indrekt aus einem Register oder indirekt aus einer Adresse.
- Ld Reg1,(Reg2): Lade das Register1 mit dem Wert den Du an der Adresse findest die im Register2 steht. Ist dort z.B. die Adresse 4000, dann wird das Wort aus Speicherzelle 4000 geholt.
- Ld (Adr),Reg: Speichere in der Adresse die angegeben ist den Inhalt des Registers (hier Adresse als Ziel).
Das wars beim 8080. Der Z80 führte noch die indizierte Adressierung ein. Die Variante hier gibt es auch: Mit einer Konstante die zu einem Basisregister addiert wird. Dies ist ganz nützlich wenn man Datenstrukturen hat bei denen Teile immer an einem bestimmten Offset beginnen. Bei einem CP/M Verzeichniseintrag beginnt z.B. die FAT immer bei Offset 16. Dies ist die Instruktion Ld Reg, (Reg)+C beziehungsweise Ld (reg)+C,Reg, Die Indizierte Adressierung ist auf 12 Bits Offset beschränkt, das sind 4096 Bytes.
Flexibler ist die mit einem Basisregister und einem Offsetregister. Das erste bleibt konstant, das zweite wird erhöht. Der Vorteil ein Offsetregister anstatt dem Basisregister direkt zuer hohen ist dass man bei so bei einem erneuten Durchlauf das Basisregister nicht laden muss und vor allem bei einer Kopieraktion man nur ein Offsetregister erhöhen muss. Das ist ist die Instruktion Ld reg, (reg+reg) bzw. Ld (reg+reg),reg.
Speziell für Pointer braucht man die doppelte indirekte Adressierung: ld reg,((reg)). Dabei steht in Reg eine Adresse. Anstatt den Wert nun aus der Adresse zu holen wird dieser erneut als Adresse angesehen wo sich der Wert befindet. Zeigervariablen haben als Wert die Adresse der Variablen auf die sie zeigen.
Verbesserungsmöglichkeiten: Sinnvoll beim Einsatz einer Pipeline ist ein kleiner Cache. Zwar kenne ich keinen 8-Bitter der einen hat, doch die TMS 9995 CPU hat einen 128 Byte Workspace auf dem Chip (die Architektur des Vorgängers TMS 9900 war ausgelegt auf nur wenige Register in der CPU aber eines war ein verschiebbarer Zeiger auf 256 Byte im RAM die dann als Register genutzt werden konnten, da RAM nicht so schnell wurde wie man das bei TI annahm bekam der Nachfolger daher sein internes schnelles RAM spendiert). 256 Worte, das sind 768 Byte belegen bei 3 Transistoren (so bei der 8080) pro Bit rund 18432 Transistoren – das ist eine Menge gemessen an den 8200 die eine einfache Z80 CPU hat, doch vergleichen mut den 68000 einer MC68000 oder 134.000 eines 80286 ist es wenig. Der Vorteil ist dass man im Cache dekodierte Anweisungen speichern kann. Ohne Speicherzugriff sinkt dann die Ausführungszeit auf einen Takt pro Befehl.
So nun noch zur Titelzeile. Früher hat man sich sehr um Bezeichnungen herumgestritten, also wann eine CPU ein 8 Bitter oder 16 Bitter ist. Folgende Kriterien gab es:
- Die Breite der internen Register – die Menge an Daten die man auf einmal verarbeiten konnte
- Die Breite des Datenbusses – wie viele Bits man pro Takt einladen konnte
- Wie breit waren Adressen – gibt den adressierbaren Speicher an.
Alle Angaben konnte man diskutieren, vor allem weil bis zur 32 Bit Generation es so war, das die Breite von Registern für normale Operationen nicht für Adressen ausreichte, sonst wäre der Arbeitsspeicher zu klein gewesen. Einige Beispiele:
- Die Intel 4004 hatte Operationen nur für 4 Bit. Der Datenbereich wurde aber über 8 Bit Adressiert und dann noch über Bankswitching 5 Stück davon = 1280 Adressen zu je 4 Bit. Das Rom wurde über 12 Bit adressiert und war byteorientiert.
- Der 8008 war ein 8 Bit Prozessor mit nur 8 Bit Operationen auf den Registern. Der Adressbereich war aber 14 Bit = 16 KByte
- Der 8080 hatte einen 64 Kbyte Adressbereich und konnte erstmals Adressen auch in Registern berechnen, das bedeutet einige Rechenoperationen (Addition, Subtraktion, Erhöhen und erniedrigen) gingen mit 16 Bit.
- Der TMS 9900 ist hier der einzige Prozessor mit sauberen Kennzahlen: 16 Bit breite Register, 16 Bit Datenbus, 16 Bit Adressbus – leider byteorientiert hätte man Worte genommen so wären es immer 128 kb gewesen.
- Der Intel 8086 hat Register von 16 Bit Breite, 16 Bit Datenbus, aber 20 Bit Adressbus, Die Adressierung erfolgt durch Addition von Segmentregistern zu Basisregistern. Damit ist der Adressbus segmentiert wie beim 4004. Beim 8088 war auch der Datenbus 8 Bit breit. Beide wurden von Intel als 8/16 Bitter bezeichnet weil sie 8- und 16-Bit Befehle ausführten, das gilt aber auch für den 8080.
- Der MC6800 hat Register von 32 Bit Breilte, 16 Bit Datenbus und 24 Bit Adressbus. Er ist am komplexesten. Er war schon für eine 32 Bit Erweiterung ausgelegt, so warne die Register 32 Bit breit, ebenso konnte der Assembler 32 Bit lange Zahlen als Operanden akzeptieren. Der Adressbus war aber auf 24 Bit beschnitten. Die ALU rechnet mit nur 16 Bit- Motorola bezeichnet die CPU daher als 16/2 Bit CPU.
Heute könnte man nach demselben System eine aktuelle Intel CPU als 64/256 Bit CPU bezeichnen mit AVX2 wird daraus eine 64/512 Bit CPU … Meine müsste man nach der Breite der Register, die man meistens als Kriterium nimmt als 16 Bit CPU bezeichnen. Dieses Kriterium ist auch meistens das beste, denn mit der Registerbreite stehen auch die Befehle, so haben 16 Bit CPUs eben Befehle um 16 Bit Daten zu verarbeiten und meistens noch weitergehende (Multiplikation, Division) als 8-Bitter. Die Ausnahme ist der MC68000 der weil der 68020 als Nachfolger geplant war breitere Register hat als er intern in einem Rutsch bearbeiten kann, so in etwa vergleichbar mit den 8080.
Für einen 16-Bitter wäre der Adressbereich von 192 KByte Code und 128 KByte Daten recht klein. Das könnte man durch Segmentregister und einige Befehle ändern. Nur würde ich dann einen linearen Adressbereich anstreben, indem man z.B. die 16 Bit breiten Segmentregister als oberste 16 Bit nimmt. Zusammen mit Befehlen die Segmentregister erhöhen und erniedrigen müsste man so auch durch über 64 KWorte große Datenstrukturen gehen können. Die Beschränkung auf 64 KByte pro Datenstruktur war das was ich bei der Programmierung unter DOS immer als größte Einschränkung empfand.
Bedingt durch Pipeline, RISC Datenformat und Prefetch-Buffer müsste mein Prozessor deutlich schneller als normale 9-Bitter aber auch 16-Bitter sein. Selbst ohne Cache hat er Features die ein 80296 noch nicht hat, in dieser Geschwindigkeitsklasse wäre er dann einzuordnen.
Befehlssatz:
Befehl | Byte 1 | Byte 2 | Byte 3 |
Arithmetrik | |||
Adc Reg,(Reg) | 0000.0001 | RRRR.RRRR | |
Adc Reg,Reg | 0000.0010 | RRRR.RRRR | |
Add Reg,(Reg) | 0000.0011 | RRRR.RRRR | |
Add Reg,Reg | 0000.0100 | RRRR.RRRR | |
And Reg,(Reg) | 0000.0101 | RRRR.RRRR | |
And Reg,Reg | 0000.0110 | RRRR.RRRR | |
Sbc reg,(Reg) | 0000.0111 | RRRR.RRRR | |
Sbc reg,Reg | 0000.1000 | RRRR.RRRR | |
Sub Reg,(Reg) | 0000.1001 | RRRR.RRRR | |
Sub Reg,Reg | 0000.1010 | RRRR.RRRR | |
Xor reg,(Reg) | 0000.1011 | RRRR.RRRR | |
Xor reg,Reg | 0000.1100 | RRRR.RRRR | |
Or Reg,(Reg) | 0000.1101 | RRRR.RRRR | |
Or Reg,Reg | 0000.1110 | RRRR.RRRR | |
Cmp reg,Reg | 0000.1111 | RRRR.RRRR | |
Cmp reg,(Reg) | 0001.0000 | RRRR.RRRR | |
Idiv Reg,Reg | 0001.0001 | RRRR.RRRR | |
Imul Reg,reg | 0001.0010 | RRRR.RRRR | |
Mul Reg,reg | 0001.0011 | RRRR.RRRR | |
Div Reg,Reg | 0001.0100 | RRRR.RRRR | |
Unäre Arithmetrik | |||
Dec (reg) | 0001.0101 | RRRR.0000 | |
Dec Reg | 0001.0101 | RRRR.0001 | |
Inc (reg) | 0001.0101 | RRRR.0010 | |
Inc Reg | 0001.0101 | RRRR.0011 | |
Neg Reg | 0001.0101 | RRRR.0100 | |
Not Reg | 0001.0101 | RRRR.0101 | |
Rotiere Links mit Carry | 0001.0101 | RRRR.0110 | |
Rotiere links ohne Carry | 0001.0101 | RRRR.0111 | |
Rotiere rechts mit Carry | 0001.0101 | RRRR.1000 | |
Rotiere rechts ohne Carry | 0001.0101 | RRRR.1001 | |
Schiebe links mit Carry | 0001.0101 | RRRR.1010 | |
Schiebe links ohne Carry | 0001.0101 | RRRR.1011 | |
Schiebe rechts mit Carry | 0001.0101 | RRRR.1100 | |
Schiebe rechts ohne Carry | 0001.0101 | RRRR.1101 | |
Sprünge | |||
Jp (Reg) | 0001.0101 | RRRR.1110 | |
Call (reg) | 0001.0101 | RRRR.1111 | |
Call Cond | 0001.0110 | AAAA.AAAA | AAAA.AAAA |
Call Short,Cond | 0001.0111 | AAAA.AAAA | |
Call Short | 0001.1000 | AAAA.AAAA | |
Call Adr | 0001.1001 | AAAA.AAAA | AAAA.AAAA |
Call (Adr) | 0001.1010 | AAAA.AAAA | AAAA.AAAA |
Interrupt Adr | 0001.1011 | AAAA.AAAA | |
Jp Cond,Adr | 0001.1100 | AAAA.AAAA | AAAA.AAAA |
Jp Short Cond,Adr | 0001.1101 | AAAA.AAAA | |
Jp Adr | 0001.1110 | AAAA.AAAA | AAAA.AAAA |
Jp (Adr) | 0001.1111 | AAAA.AAAA | AAAA.AAAA |
Decrement Reg Jumpz,Short | 0010.RRRR | AAAA.AAAA | |
Ret | 0101.1010 | 0000.1011 | |
Ret Cond | 0101.1010 | 0000.1100 | |
IRet cond | 0101.1010 | 0000.1101 | |
Iret Cond | 0101.1010 | 0000.1110 | |
Ladebefehle | |||
Ld (Adr),Reg | 0011.RRRR | AAAA.AAAA | AAAA.AAAA |
Ld Reg,(Adr) | 0001.RRRR | AAAA.AAAA | AAAA.AAAA |
Ld Reg,Konstant | 0010.RRRR | AAAA.AAAA | AAAA.AAAA |
Ld (Reg+C),reg | 0011.RRRR | RRRR.CCCC | CCCC.CCCC |
Ld Reg,(Reg+C) | 0100.RRRR | RRRR.CCCC | CCCC.CCCC |
Ld Reg,Reg | 0101.0000 | RRRR.RRRR | |
Ld (reg),Reg | 0101.0001 | RRRR.RRRR | |
Ld Reg,(reg) | 0101.0010 | RRRR.RRRR | |
Ld Reg,(Reg)+Reg | 0101.0011 | RRRR.RRRR | RRRR.0000 |
Ld (Reg)+Reg,Reg | 0101.0011 | RRRR.RRRR | RRRR.0001 |
Ld Reg,((Reg)) | 0101.0100 | RRRR.RRRR | |
Ld ((Reg)),Reg | 0101.0101 | RRRR.RRRR | |
Stackoperationen | |||
Pop Flags | 0101.1010 | 0000.0000 | |
Pop Instruction Pointer | 0101.1010 | 0000.0001 | |
Pop Reg | 0101.1010 | 0001.RRRR | |
Pop Stackpoointer | 0101.1010 | 0000.0010 | |
Push Flags | 0101.1010 | 0000.0011 | |
Push Instruction Pointer | 0101.1010 | 0000.0100 | |
Push Reg | 0101.1010 | 0010.RRRR | |
Push Stackpointer | 0101.1010 | 0000.0101 | |
Ein/Ausgabe | |||
In Reg,Adr | 0110.RRRR | AAAA.AAAA | AAAA.AAAA |
Out Reg,Adr | 0111.RRRR | AAAA.AAAA | AAAA.AAAA |
In Rg,(Reg) | 0101.1011 | RRRR.RRRR | |
Out (reg),Reg | 0101.1100 | RRRR.RRRR | |
Sonstiges | |||
Di | 0101.1010 | 0000.0110 | |
EI | 0101.1010 | 0000.0111 | |
Halt | 0101.1010 | 0000.1000 | |
Nop | 0000.0000 | ||
Set InterruptMask,Reg | 0101.1010 | 0011.RRRR | |
Read Reg,InterruptMask | 0101.1010 | 0100.RRRR | |
Complement Carry | 0101.1010 | 0000.1001 | |
Set Carry Flag | 0101.1010 | 0000.1010 | |
Frei für Erweiterungen | 0101.1011 | 1111.1111 |