====== Befehlssatz ======
Die Registermaschine der Online-IDE orientiert sich sehr start an der [[https://schule.awiedemann.de/minimaschine.html|Minimaschine von Herrn Wiedemann]] und der Assemblersprache, wie sie im bayerischen Abitur Verwendung findt. Sie besitzt neben dem Programmzähler (//engl. program counter, kurz: PC//) und dem Stack Pointer (SP) nur ein einziges Arbeitsregister, den **Akkumulator** (AC). Jeder Speicherzelle der Registermaschine ist 16 Bit breit und wird von den Befehlen vorzeichenbehaftet interpretiert. \\ \\
Neben den gleich nachfolgenden Maschinensprachebefehlen gibt es noch [[.pseudobefehle|Pseudobefehle zur Steuerung des Compilers]], siehe unten.
==== Laden und Speichern ====
^Assemblerbefehl (Opcode mit Beispiel-Operanden)^Bedeutung^
|**LOAD** 100 | Lade den Wert der Speicherzelle 100 in den Akkumulator |
|**LOADI** 5 | Lädt den Wert 5 in den Akkumulator ("Load immediate") |
|**STORE** 102 | Schreibe eine Kopie des Akkumulatorwertes in die Speicherzelle 102 |
==== Arithmetische Befehle (direkte Adressierung) ====
|**ADD** 101 | Addiere den Wert der Speicherzelle 101 zum Akkumulator |
|**SUB** 101 | Subtrahiere den Wert der Speicherzelle 101 vom Akkumulator |
|**MUL** 101 | Multipliziere den Akkumulator mit dem Wert der Speicherzelle 101 |
|**DIV** 101 | Dividiere den Akkumulator durch den Wert der Speicherzelle 101 |
|**MOD** 101 | Berechne den Rest der Division des Akkumulators durch den Wert der Speicherzelle 101 und lege ihn dann im Akkumulator ab. |
==== Arithmetische Befehle (Immediate-Adressierung) ====
|**ADDI** 12 | Addiere den Wert 12 zum Akkumulator |
|**SUBI** 12 | Subtrahiere den Wert 12 vom Akkumulator |
|**MULI** 12 | Multipliziere den Akkumulator mit dem Wert 12 |
|**DIVI** 12 | Dividiere den Akkumulator durch den Wert 12 |
|**MODI** 12 | Berechne den Rest der Division des Akkumulators durch den Wert 12 und lege ihn dann im Akkumulator ab. |
"An operand that is directly encoded as part of a machine instruction is called an **immediate operand**." \\
Der (wenig gebräuchliche) deutsche Begriff dazu lautet **"unmittelbare Adressierung"**.
==== Logische Befehle (direkte Adressierung) ====
|**AND** 125| Logische Und-Verknüpfung des Wertes im Akkumulator mit dem Wert der Speicherzelle 125 |
|**OR** 125| Logische Oder-Verknüpfung des Wertes im Akkumulator mit dem Wert der Speicherzelle 125 |
|**XOR** 125| Logische XOR-Verknüpfung des Wertes im Akkumulator mit dem Wert der Speicherzelle 125 |
|**NOT** | Bitweise logische Invertierung des Wertes im Akkumulator |
==== Logische Befehle (immediate-Adressierung) ====
|**ANDI** 12| Logische Und-Verknüpfung des Wertes im Akkumulator mit dem Wert 12 |
|**ORI** 12| Logische Oder-Verknüpfung des Wertes im Akkumulator mit dem Wert 12 |
|**XORI** 12| Logische XOR-Verknüpfung des Wertes im Akkumulator mit dem Wert 12 |
==== Vergleichsbefehle ====
|**CMP** 125 | Vergleicht den Akkumulator mit dem Wert **der Speicherzelle** 125 und setzt Null- und Negativflag entsprechend |
|**CMPI** 12 | Vergleicht den Akkumulator mit dem Wert 12 und setzt Null- und Negativflag entsprechend |
==== Sprungbefehle ====
|**JMP** Adresse | Springt **immer** zur Adresse ("unbedingter Sprungbefehl") |
|**JEQ** Adresse \\ //("Jump if equal")// | Springt zur Adresse, wenn das Z-Flag gesetzt ist, d.h. wenn das Ergebnis der letzten Operation 0 war \\ bzw. **wenn die letzten beiden mit CMP verglichenen Werte gleich waren.** |
|**JNE** Adresse \\ //("Jump if not equal")// | Springt zur Adresse, wenn das Z-Flag nicht gesetzt ist, d.h. wenn das Ergebnis der letzten Operation nicht 0 war. \\ bzw. ** wenn beim letzten Vergleich mit CMP der Wert des Akkumulators und der damit verglichene Wert nicht gleich waren.** |
|**JGT** Adresse \\ //("Jump if greater")// | Springt zur Adresse, wenn das Ergebnis der letzen Operation positiv (> 0) war, d. h. weder N noch Z-Flag sind gesetzt \\ bzw. **wenn beim letzten Vergleich mit CMP der Wert im Akkumulator größer war als der damit verglichene Wert** |
|**JGE** Adresse \\ //("Jump if greater or equal")// | Springt zur Adresse, wenn das Ergebnis der letzen Operation nicht negativ war, d. h. das N-Flag nicht gesetzt ist \\ bzw. **wenn beim letzten Vergleich mit CMP der Wert im Akkumulator größer oder gleich war als der damit verglichene Wert** |
|**JLT** Adresse \\ //("Jump if lower")// | Springt zur Adresse, wenn das Ergebnis der letzen Operation negativ (< 0) war, d. h. das N-Flag ist gesetzt \\ bzw. **wenn beim letzten Vergleich mit CMP der Wert im Akkumulator kleiner war als der damit verglichene Wert** |
|**JLE** Adresse \\ //("Jump if lower or equal")// | Springt zur Adresse, wenn das Ergebnis der letzen Operation nicht positiv war, d. h. das N-Flag oder das Z-Flag ist gesetzt \\ bzw. **wenn beim letzten Vergleich mit CMP der Wert im Akkumulator kleiner oder gleich war als der damit verglichene Wert** |
Für die bedingten Sprungbefehle gibt es noch einen weiteren Satz von Mnemonics, deren Bedeutung direkt auf die maßgeblichen Flags im Statusregister abzielen. Sie sind aber beim Programmieren weniger intuitiv, z.B. sind folgende Befehle identisch:
^Befehl^...ist identisch mit^Bedeutung^
| **JMPZ** | **JEQ** | Jump if zero-flag is set |
| **JMPNZ** | **JEQ** | Jump if zero-flag is not set |
| **JMPN** | **JLT** | Jump if negative-flag is set |
| **JMPP** | **JGT** | Jump if negative-flag is not set/"jump if positive" |
| **JMPC** | | Jump if carry-flag is set |
| **JMPNC** | | Jump if carry-flag is not set |
| **JMPV** | | Jump if overflow-flag is set |
| **JMPNV** | | Jump if overflow-flag is not set |
==== Bitshift-Befehle ====
|**shr** Adresse \\ //("**Sh**ift **r**ight")// | Schiebt die Bits im Akkumulator nach rechts. Die Anzahl der Stellen, um die geschoben wird, ergibt sich aus dem Wert an der angegebenen Speicheradresse. Der Wert des letzten "herausgeschobenen" bits wird im Carry-Flag abgelegt. |
|**shl** Adresse \\ //("**Sh**ift **l**eft")// | Schiebt die Bits im Akkumulator nach rechts. Die Anzahl der Stellen, um die geschoben wird, ergibt sich aus dem Wert an der angegebenen Speicheradresse. Der Wert des letzten "herausgeschobenen" bits wird im Carry-Flag abgelegt. |
|**shri** Wert \\ //("**Sh**ift **r**ight")// | Schiebt die Bits im Akkumulator nach rechts. Die Anzahl der Stellen, um die geschoben wird, ergibt sich aus dem Operanden. Der Wert des letzten "herausgeschobenen" bits wird im Carry-Flag abgelegt. |
|**shli** Wert \\ //("**Sh**ift **l**eft")// | Schiebt die Bits im Akkumulator nach rechts. Die Anzahl der Stellen, um die geschoben wird, ergibt sich aus dem Operanden. Der Wert des letzten "herausgeschobenen" bits wird im Carry-Flag abgelegt. |
==== Befehlen in Zusammenhang mit dem Stack ====
Der Stackpointer zeigt immer auf den zuletzt auf dem Stack abgelegten Wert. Der Stack "wächst" nach unten.
| **push** | Erniedrigt den Stackpointer um 1 und speichert dann den Wert des Akkus an der Speicherzelle, auf die der Stackpointer zeigt |
| **pop** | Lädt den Wert der Speicherzelle, auf die der Stackpointer zeigt, in den Akku, und erhöht dann den Wert des Stackpointers um 1 |
| **jsr** Adresse \\ //("**J**ump to **s**ub**r**outine")// | Legt die Adresse des nächstfolgenden Befehls auf dem Stack ab und springt dann zu der Adresse, die im Operanden angegeben ist. |
| **rts** \\ //("**R**e**t**urn from **s**ubroutine")// | Nimmt eine Adresse vom Stack und springt dann an diese Adresse. |
| **return** | Wie rts. |
| **rsv** Wert | Reserviert Platz auf dem Stack, indem der Stackpointer um den Wert im Operanden erniedrigt wird. |
| **rel** Wert | Gibt Platz auf dem Stack frei, indem der Stackpointer um den Wert im Operanden erhöht wird. |
| **load** Offset(sp) | Lädt den Wert an der Adresse Stackpointer + Offset in den Akkumulator. |
| **store** Offset(sp) | Speichert den Wert des Akkumulators an der Stelle Stackpointer + Offset. |
==== Anhalten des Programms ====
| **halt** | Hält die Ausführung des Programms an. |
| **hold** | Wie halt. |
===== Sprungmarken statt Speicheradressen =====
Es ist müßig, die Zieladressen der Sprungbefehle jeweils "von Hand" zu ermitteln. Daher nutzen wir im folgenden Sprungmarken, deren Bezeichner wir uns frei ausdenken dürfen. Der Assembler bestimmt beim Übersetzen des Programms dann automatisch die korrekte Zieladresse. \\ \\ **Beispiel:**
LOADI 0
s1: ADDI 1
CMP 100
JLT s1
HOLD
Der Befehl ''JLT s1'' bewirkt hier, dass genau dann zur Sprungmarke ''s1'' gesprungen wird, wenn der Wert des Akkumulators kleiner als 100 ist.
===== Symbolische Adressierung =====
Das folgende Programm quadriert den Wert 12, der in Speicherstelle 100 abgelegt ist, und legt das Ergebnis in Speicherstelle 101 ab:
LOAD 100
MUL 100
Store 101
HOLD
Um das Programm zu verstehen, muss man sich merken, dass in Speicherstelle 100 der zu quadrierende Wert steht und in Speicherstelle 101 das Ergebnis der Berechnung abgelegt wird. Bei größeren Programmen gibt es sehr viele solcher Speicherstellen, deren Bedeutung man kennen muss, um das Programm zu verstehen. Um das zu erleichtern, ermöglicht uns der Compiler, Arbeitsspeicher zu reservieren und mit leicht eingängigen Bezeichnern zu benennen. Beim Übersetzen des Programms setzt er dann anstelle der Bezeichner die korrekten Adressen ein. \\
Unser Beispielprogramm kann mithilfe der symbolischen Adressierung folgendermaßen formuliert werden:
LOAD a
MUL a
STORE quadrat
HOLD
a: WORD 12
quadrat: WORD 0
Die Zeile ''a: WORD 12'' weist den Compiler an, Speicherplatz in der Größe eines WORD (== 16 Bit) zu reservieren und fortan statt ''a'' die Adresse dieses Speicherplatzes einzusetzen.
Symbolische Adressierung ist sehr praktisch, weil
* Speicherplätze mit sprechenden Bezeichnern versehen werden können,
* sich der Compiler um die Reservierung des Speicherplatzes kümmert und
* der Compiler beim Kompilieren auch gleich den angegebenen Startwert in den Speicherplatz schreibt.
===Bemerkung:===
In den [[https://www.isb.bayern.de/fileadmin/user_upload/Gymnasium/IlluPA/Inf/IlluPA_Informatik_Hinweise_zur_schriftlichen_Pruefung.pdf|Hinweisen zur schriftlichen Abiturprüfung (Seite 5)]] heißt es: \\
//Bei Registermaschinenprogrammen werden Speicherzellen und Sprungmarken bevorzugt symbolisch adressiert. Dies verbessert die Lesbarkeit und erleichtert das Erstellen und Korrigieren von
Registermaschinenprogrammen. //