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).

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.

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).

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).

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
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 Kommentar bis zum Zeilenende

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 ein DAT sonst als Befehl auszuführen versuchen.
  • DAT ohne 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,X holt den Inhalt von X. Eine Verwechslung mit MOV R0,#X (das die Adresse von X ins Register schreibt) ist ein beliebter Fehler.
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.

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

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
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.

  • Flags werden nur von ALU-Operationen gesetzt. LDR, STR und MOV lassen sie unberührt. Ein CMP gehört deshalb unmittelbar vor den Branch – ein LDR dazwischen ist harmlos, ein ADD zerstört den Vergleich.
  • Immediates gehen nur bis 255. Grössere Konstanten kommen über DAT in den Speicher und werden mit LDR geholt.
  • MUL verliert die oberen 16 Bit. Wer das ganze Produkt braucht, nimmt MLX.
  • MLX löscht die Flags (ausser Z). Ein Übertrag aus einer vorherigen Addition ist danach weg.
  • R7 ist nur nach dem Reset 0. Der ADC 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 POP erzeugen einen Fehler, danach hilft nur Reset.
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
  • ef_informatik/assembler_risc.txt
  • Zuletzt geändert: 2026-09-15 09:14
  • von sca