Befehlsübersicht RISC Simulator
Kompakte Referenz zu den Befehlen des RISC Simulators von Peter Higginson, die für die Aufgaben im Assembler-Dossier gebraucht werden. Offizielle Quellen: Manual und Instruction Set (PDF).
1. Die Maschine
Register: Es gibt acht frei verwendbare Register R0 bis R7 sowie drei Spezialregister:
| Register | Name | Bedeutung |
|---|---|---|
| R0 … R7 | General Purpose | freie Arbeitsregister, je 16 Bit |
| SP | Stack Pointer | zeigt auf das oberste Element des Stacks |
| LR | Link Register | merkt sich die Rücksprungadresse bei JMS |
| PC | Program Counter | Adresse des nächsten Befehls |
Flags: Das Flags-Register hält das Ergebnis der letzten ALU-Operation fest. Die Sprungbefehle werten genau diese vier Bits aus.
| Flag | Name | gesetzt, wenn … |
|---|---|---|
| N | Negative | das Resultat negativ ist (oberstes Bit = 1) |
| Z | Zero | das Resultat genau 0 ist |
| C | Carry | ein Übertrag über die 16 Bit hinaus entstanden ist |
| V | oVerflow | das Resultat als vorzeichenbehaftete Zahl überläuft |
Speicher: 1 Wort = 16 Bit. Das Programm wird ab Adresse 000 geladen, der Stack beginnt bei 400 und wächst nach unten. Der Bereich dazwischen ist die Heap.
2. Operanden: die vier Adressierungsarten
Fast jeder Befehl gibt es in mehreren Varianten. Entscheidend ist, woher der zweite Operand kommt:
| Schreibweise | Art | Bedeutung | Beispiel |
|---|---|---|---|
#42 | immediate | die Zahl selbst, direkt im Befehl | MOV R1,#42 |
R2 | Register | der Inhalt des Registers | ADD R1,R2 |
100 bzw. marke | direkt | der Inhalt der Speicherzelle 100 | LDR R0,100 |
4(R2) | indiziert | Inhalt der Zelle mit Adresse R2+4 | LDR R0,4(R2) |
Zahlen können überall auch hexadezimal als 0xnnnn geschrieben werden. Ein Immediate ist auf 8 Bit begrenzt, also auf Werte von 0 bis 255 – MOV R1,#1000 funktioniert nicht, dafür braucht es den Umweg über den Speicher.
Kurzform mit zwei Operanden: Der Assembler ergänzt fehlende Register automatisch. ADD R1,R2 bedeutet ADD R1,R1,R2 und LSR R1,#3 bedeutet LSR R1,R1,#3. Ebenso ist (R2) dasselbe wie 0(R2).
3. Daten bewegen
| Befehl | Bedeutung |
|---|---|
MOV Rd,#42 | schreibt den Wert 42 in Rd |
MOV Rd,Rs | kopiert Rs nach Rd |
MOV Rd,SP / MOV SP,Rs | liest bzw. schreibt ein Spezialregister (auch LR, PC, flags) |
LDR Rd,100 | LoaD Register: lädt den Inhalt der Speicherzelle 100 in Rd |
LDR Rd,4(Rn) | lädt aus der Zelle, deren Adresse in Rn steht, plus Offset 4 |
STR Rs,100 | STore Register: schreibt Rs in die Speicherzelle 100 |
STR Rs,4(Rn) | schreibt Rs in die Zelle Rn+4 |
MVN Rd,Rs | MoVe Not: kopiert Rs bitweise invertiert nach Rd |
LDR, STR und MOV verändern die Flags nicht (MVN schon).
4. Ein- und Ausgabe
Die Gerätenummer bestimmt das Format der Ausgabe:
| Befehl | Bedeutung |
|---|---|
INP Rd,2 | liest eine Zahl vom Benutzer nach Rd |
OUT Rs,4 | gibt Rs als Zahl mit Vorzeichen aus |
OUT Rs,5 | gibt Rs als Zahl ohne Vorzeichen aus |
OUT Rs,6 | gibt Rs hexadezimal aus |
OUT Rs,7 | gibt Rs als ASCII-Zeichen aus |
5. Direktiven und Sonstiges
| Befehl | Bedeutung |
|---|---|
DAT | reserviert eine Speicherzelle (mit Wert 0) |
DAT 48801 | reserviert eine Speicherzelle und legt den Wert hinein |
X DAT 42 | reserviert eine Speicherzelle mit dem Wert 42 und gibt ihr den Namen X |
marke: bzw. marke | definiert eine Marke für diese Adresse; der Doppelpunkt ist optional |
HLT | beendet das Programm |
NOP | tut nichts (erzeugt intern MOV R0,R0) |
| Kommentar bis zum Zeilenende |
5.1 Variablen mit DAT
Eine Marke ist nichts anderes als ein Name für eine Adresse. Schreibt man sie vor ein DAT, bekommt die reservierte Speicherzelle einen Namen – und damit hat man das, was in Python oder C# eine Variable heisst:
X DAT 42
Ab jetzt darf überall, wo eine Speicheradresse erwartet wird, X statt der Zahl stehen. Der Assembler setzt beim Übersetzen automatisch die richtige Adresse ein. Es gibt drei Arten, eine solche Variable zu benutzen:
| Befehl | Bedeutung |
|---|---|
LDR R0,X | Variable lesen: holt den Wert aus X in R0 |
STR R0,X | Variable schreiben: speichert R0 in X |
ADD R0,X / SUB R0,X | direkt mit der Variablen rechnen, ohne sie vorher zu laden |
Ein vollständiges Beispiel – es addiert zwei Variablen und legt das Resultat in einer dritten ab:
LDR R0,zahl1 // R0 = 42
ADD R0,zahl2 // R0 = 42 + 8
STR R0,summe // summe = 50
OUT R0,4
HLT
zahl1 DAT 42
zahl2 DAT 8
summe DAT // leere Zelle für das Resultat
Drei Dinge sind dabei wichtig:
- Die Daten gehören ans Ende, nach dem
HLT. Die CPU arbeitet den Speicher stur von oben nach unten ab und würde einDATsonst als Befehl auszuführen versuchen. DATohne Wert ergibt eine leere Zelle – genau richtig für Resultate, die das Programm erst später berechnet.- Der Name ersetzt nur die Adresse, nicht den Wert.
LDR R0,Xholt den Inhalt von X. Eine Verwechslung mitMOV R0,#X(das die Adresse von X ins Register schreibt) ist ein beliebter Fehler.
6. Rechnen
| Befehl | Bedeutung |
|---|---|
ADD Rd,Rs,Rb | Rd = Rs + Rb (auch ADD Rd,Rs, ADD Rd,#5, ADD Rd,100) |
SUB Rd,Rs,Rb | Rd = Rs - Rb (gleiche Varianten wie ADD) |
ADC Rd,Rs | Add with Carry: Rd = Rd + Rs + Carry |
SBC Rd,Rs | SuBtract with Carry: Subtraktion mit Übertrag |
MUL Rd,Rs | Multiplikation, nur die unteren 16 Bit des Resultats |
MLX Rd,Rs | MuLtiply eXtended: 32-Bit-Resultat, unteres Word in Rd, oberes in R(d+1) |
DIV Rd,Rs | Division mit Vorzeichen |
UDV Rd,Rs | Division ohne Vorzeichen |
MOD Rd,#5 | Rest der Division (Modulo) |
NEG Rd,Rs | Vorzeichenwechsel: Rd = -Rs |
Die beiden wichtigsten Tricks für 32-Bit-Zahlen:
ADC addiert den Übertrag der vorherigen Addition mit. Damit rechnet man Word für Word: zuerst ADD für das untere Word, dann ADC für das obere.
ADC Rd,R7 addiert nur den Übertrag (0 oder 1), sofern R7 noch den Wert 0 hat. So fängt man den letzten Übertrag in einem eigenen Register auf. Achtung: R7 ist kein echtes Null-Register, es ist nach dem Reset bloss zufällig 0. Wer R7 vorher beschreibt, zerstört den Trick.
MLX liefert das volle 32-Bit-Produkt auf zwei Register verteilt: MLX R1,R0 legt das untere Word in R1 und das obere in R2. MLX rechnet vorzeichenlos und löscht alle Flags ausser Z – ein ADC direkt danach addiert also garantiert eine 0.
7. Logik und Schieben
| Befehl | Bedeutung |
|---|---|
AND Rd,Rs,Rb | bitweises Und |
ORR Rd,Rs,Rb | bitweises Oder (auch BIS geschrieben) |
XOR Rd,Rs,Rb | bitweises Exklusiv-Oder (auch EOR) |
BIC Rd,Rs | Bit Clear: löscht in Rd alle Bits, die in Rs gesetzt sind |
LSL Rd,Rs,#3 | Logical Shift Left: 3 Stellen nach links (= Multiplikation mit $2^3$) |
LSR Rd,Rs,#3 | Logical Shift Right: 3 Stellen nach rechts, links wird mit 0 aufgefüllt |
ASR Rd,#3 | Arithmetic Shift Right: wie LSR, aber das Vorzeichenbit bleibt erhalten |
ROR Rd,#3 | ROtate Right: rechts herausgeschobene Bits kommen links wieder herein |
8. Vergleichen und Springen
Ein Vergleich und der zugehörige Sprung sind immer zwei Befehle: CMP rechnet im Stillen Rb - Operand und setzt nur die Flags, der Branch wertet sie aus.
| Befehl | Bedeutung |
|---|---|
CMP Rb,#10 / CMP Rb,Rs | vergleicht, indem subtrahiert und nur das Flag-Ergebnis behalten wird |
TST Rb,Rs | bitweises Und, ebenfalls nur für die Flags |
Sprungziel ist eine Marke (Label), z.B. BGE LOOP.
| Befehl | Sprung, wenn … |
|---|---|
BRA / B | immer (unbedingter Sprung) |
BEQ | gleich (Z=1) |
BNE | ungleich (Z=0) |
BGT | grösser als |
BGE | grösser oder gleich |
BLT | kleiner als |
BLE | kleiner oder gleich |
BMI | Resultat negativ (Minus) |
BPL | Resultat positiv (Plus) |
BCS / BHS | Carry gesetzt / grösser oder gleich (vorzeichenlos) |
BCC / BLO | Carry gelöscht / kleiner (vorzeichenlos) |
BHI | grösser (vorzeichenlos) |
BLS | kleiner oder gleich (vorzeichenlos) |
BVS / BVC | Overflow gesetzt / gelöscht |
BGT BGE BLT BLE vergleichen mit Vorzeichen, BHI BHS BLO BLS ohne. BRA Rs springt an die Adresse, die im Register Rs steht.
So sieht eine Zählschleife aus:
MOV R1,#10
LOOP: OUT R1,4
SUB R1,#1
CMP R1,#0
BGE LOOP
HLT
9. Stack und Funktionen
| Befehl | Bedeutung |
|---|---|
PSH Rs | legt Rs oben auf den Stack, SP wandert nach unten |
POP Rd | holt das oberste Element vom Stack nach Rd |
PSH {R0,R1,LR} | legt mehrere Register auf einmal ab (Liste oder Bereich {R0-R3}) |
POP {R7,R0} | holt mehrere Register zurück |
JMS marke | JuMp to Subroutine: Rücksprungadresse nach LR, dann Sprung |
RET | Rücksprung an die Adresse in LR |
LDR Rd,4(SP) / STR Rs,4(SP) | Zugriff auf den Stack ohne POP/PSH |
Der Stack arbeitet nach LIFO: zuletzt abgelegt, zuerst geholt. Wer zwei Werte mit PSH R0 und PSH R1 übergibt, muss sie in der Funktion mit POP R1 und POP R0 – in dieser Reihenfolge – wieder abholen.
Bei Rekursion aufpassen: JMS überschreibt LR. Ruft sich eine Funktion selbst auf, ist die ursprüngliche Rücksprungadresse verloren. Deshalb rettet man LR zu Beginn auf den Stack (PSH {R0,LR}) und holt ihn am Ende wieder zurück.
10. Stolpersteine
- Flags werden nur von ALU-Operationen gesetzt.
LDR,STRundMOVlassen sie unberührt. EinCMPgehört deshalb unmittelbar vor den Branch – einLDRdazwischen ist harmlos, einADDzerstört den Vergleich. - Immediates gehen nur bis 255. Grössere Konstanten kommen über
DATin den Speicher und werden mitLDRgeholt. MULverliert die oberen 16 Bit. Wer das ganze Produkt braucht, nimmtMLX.MLXlöscht die Flags (ausser Z). Ein Übertrag aus einer vorherigen Addition ist danach weg.R7ist nur nach dem Reset 0. DerADC Rd,R7-Trick setzt das voraus.JMSüberschreibt LR. Ohne Rettung auf den Stack scheitert jede Rekursion.- Der Stack läuft nach unten und wird nicht umgebrochen. Zu viele
POPerzeugen einen Fehler, danach hilft nurReset.
11. Alle Befehle auf einen Blick
| Gruppe | Befehle |
|---|---|
| Daten | MOV MVN LDR STR |
| Arithmetik | ADD ADC SUB SBC MUL MLX DIV UDV MOD NEG |
| Logik | AND ORR (BIS) XOR (EOR) BIC |
| Schieben | LSL LSR ASR ROR |
| Vergleichen | CMP TST |
| Springen | BRA/B BEQ BNE BGT BGE BLT BLE BMI BPL BHI BLS BHS/BCS BLO/BCC BVS BVC |
| Funktionen | JMS RET PSH POP |
| Ein-/Ausgabe | INP OUT |
| Sonstiges | HLT NOP DAT |