====== Maschinensprache (Assembler) ======
Wir lernen im Folgenden eine Programmiersprache, die sehr ähnlich ist zur **Maschinensprache** realer Prozessoren. Die Befehle dieser Sprache sind als Abfolge von Byte-Werten im Arbeitsspeicher des Rechners abgelegt. Jedem Befehl entspricht ein bestimmter Zahlencode (**"Opcode"**). Da es für uns Menschen recht mühselig ist, auswendigzulernen dass z.B. die Zahl 276 bedeutet "Lade den Wert der nachfolgenden Speicherstelle", schreiben wir Maschinensprachebefehle nicht als Zahl, sondern als sogenannte **Mnemonics**. Der Mnemonic für 276 ist beispielsweise "LOAD". \\ \\
Wir brauchen dann einen Compiler, der die mit Mnemonics notierten Programme in die Zahlenfolgen übersetzt, die der Prozessor versteht. Solche Compiler nennt man **Assembler**. Die mithilfe von Mnemonics notierten Programme nennt man **Assemblerprogramme**, zu einer Assemblersprache sagt man umgangssprachlich auch kurz "Assembler".
==== Hier ein Beispielprogramm: ====
^Assemblerbefehl (Opcode mit Operanden)^Werte im Arbeitsspeicher^Bedeutung^
|LOAD 100 | 276 100 |Lade den Wert der Speicherzelle 100 in den Akkumulator |
|ADD 101 | 266 101 |Addiere den Wert der Speicherzelle 101 zum Akkumulator |
|STORE 102 | 277 102 | Schreibe eine Kopie des Akkumulatorwertes in die Speicherzelle 102 |
|HOLD | 99 |Stoppe den Prozessor |
Für die Zahldarstellung der Befehle werden wir uns im Folgenden nicht mehr interessieren. \\ => **Nicht auswendiglernen!**
====== Die Minimaschine ======
Wir arbeiten im Folgenden mit einem Simulator einer Registermaschine, der **Minimaschine**.
[[https://schule.awiedemann.de/minimaschine.html|Hier finden Sie den Downloadlink und auch die Dokumentation dazu]].
{{ .:pasted:20251029-102223.png?700 }}
Hier der [[https://schule.awiedemann.de/manualmini/assembler.html|Deeplink zur Dokumentation der Assemblersprache der Minimaschine.]] \\ \\
**Speicherzellen der Minimaschine** \\
Ungewöhlicherweise enthält jede Speicherzelle der Minimaschine **16 Bit** und nicht (wie allgemein üblich) 8 Bit.
===== Die wichtigsten Befehle =====
Nachfolgend eine Auswahl der für Sie wichtigsten Befehle der Minimaschine. [[https://www.schule.awiedemann.de/manualmini/assembler.html|Die gesamte Befehlsliste finden Sie hier.]]
==== 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 (2. Operand im Speicher) ====
|**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 (2. Operand "immediate") ====
|**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 (2. Operand im Speicher) ====
|**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 (2. Operand "immediate") ====
|**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 |
| **JMPN** | **JLT** | Jump if negative-flag is set |
| **JMPP** | **JGT** | Jump if negative-flag is not set/"jump if positive" |
usw.
===== 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 [[file:///C:/Users/asvpa/Downloads/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. //