Ein Absturzbericht mit einer Zeile
Das Kartenterminal einer Bäckerei stürzt mitten am Morgen ab. Der Bericht, den es an den Hersteller schickt, nennt nur den letzten Befehl: sw a1, 12(sp).
Auf dem Terminal gibt es keinen Quelltext, nur Maschinenbefehle. Wer sie lesen kann, sieht, was das Programm beim Absturz gerade tat.
Am Ende dieser Lektion
Notiz
Ich kann ein kurzes RISC-V-Programm lesen.
Schlüsselwörter
| Begriff | Was er bedeutet | Beispiel |
|---|---|---|
| Register | ein kleiner, schneller Speicher im Prozessor | a0 |
| Direktwert | eine Zahl, die im Befehl selbst steht | 93 |
| Laden | ein Befehl, der einen Wert aus dem Speicher in ein Register kopiert | lw |
| Speichern | ein Befehl, der ein Register in den Speicher kopiert | sw |
| Verzweigung | ein Befehl, der an eine andere Stelle im Programm springen kann | bne |
| Marke | ein Name für eine Stelle im Programm | loop: |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Zweiunddreißig Register mit Aufgaben
Ein 64-Bit-Prozessor mit RISC-V hat 32 Register zu je 64 Bits, x0 bis x31. Im Assembler haben die meisten einen Namen für ihre übliche Aufgabe.
x0 heißt zero: Es liest sich immer als 0, und was man hineinschreibt, geht verloren. Die a-Register tragen die Eingaben einer Funktion und ihr Ergebnis. Die t-Register halten kurzlebige Zwischenwerte.
Beispiel: Register, die Ihnen begegnen
| Nummer | Name | Übliche Aufgabe |
|---|---|---|
| x0 | zero | Immer 0 |
| x2 | sp | Der Stack-Zeiger: wo der Stapel im Speicher liegt |
| x5 bis x7 | t0 bis t2 | Zwischenwerte |
| x10, x11 | a0, a1 | Eingaben einer Funktion, das Ergebnis in a0 |
| x17 | a7 | Welchen Systemaufruf ecall von Linux verlangt |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
In den Architektur-Ordner wechseln
cd ~/archHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Drei Formen von Befehlen
Die meisten Befehle nennen zuerst das Zielregister und dann, woher die Eingaben kommen.
add a0, a0, t0 addiert zwei Register. addi t0, t0, 2 addiert ein Register und einen Direktwert, eine Zahl im Befehl. li a0, 5 sieht aus wie eine dritte Art, ist aber eine Abkürzung. Für eine kleine Zahl schreibt der Assembler addi a0, zero, 5.
Laden und Speichern erreichen den Speicher
Register sind wenige, also liegen Daten im Speicher. RISC-V rechnet nur mit Registern. Ein Ladebefehl kopiert aus dem Speicher in ein Register, ein Speicherbefehl in die andere Richtung.
lw a0, 8(a1) lädt ein Wort von 4 Bytes von der Adresse in a1 plus 8. Ein int in C hat 4 Bytes. Das loop.c des Labs füllt eine Tabelle und summiert sie, also muss gcc laden und speichern.
loop.c in RISC-V-Assembler übersetzen
riscv64-linux-gnu-gcc -O2 -march=rv64gc -S loop.cHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Lade- und Speicherbefehle finden
grep -E 'lw|sw' loop.sHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Schleife Zeile für Zeile
grep -A 4 'L6:' loop.sHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Beispiel: C neben RISC-V
| C in loop.c | RISC-V | Was es tut |
|---|---|---|
| grid[r][c] = 1; | sw a3,0(a5) | Schreibt a3, das 1 enthält, an die Adresse in a5 |
| sum = sum + grid[r][c]; | lw a4,0(a5) | Liest 4 Bytes von der Adresse in a5 nach a4 |
| sum = sum + grid[r][c]; | add a3,a3,a4 | Zählt die Zahl zur Summe dazu, in Registern |
| das nächste c | addi a5,a5,4 | Rückt die Adresse um ein int weiter, 4 Bytes |
Die Zellen, die Lernende ausfüllen, bleiben in dieser Vorschau leer.
Ein häufiger Fehler
Warnung
Viele glauben, add könne eine Zahl direkt aus dem Speicher addieren. Bei RISC-V geht das nicht. Nur Lade- und Speicherbefehle berühren den Speicher. Ein Wert wird geladen, in Registern verarbeitet und zurückgespeichert. Deshalb braucht die Summe erst lw und dann add.
Eine Verzweigung baut eine Schleife
Eine Verzweigung vergleicht zwei Register und springt, wenn der Test stimmt. beq springt bei Gleichheit, bne bei Ungleichheit.
Eine Marke wie loop: benennt das Sprungziel. Das nächste Programm addiert die geraden Zahlen von 2 bis 20. t0 zählt in Zweierschritten, a0 hält die Summe, und bne t0, t1, loop springt zurück, bis t0 den Wert 22 erreicht.
Eine Schleife in RISC-V schreiben
printf '.globl _start\n_start:\n li a0, 0\n li t0, 2\n li t1, 22\nloop:\n add a0, a0, t0\n addi t0, t0, 2\n bne t0, t1, loop\n li a7, 93\n ecall\n' > gerade.sHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Assemblieren
riscv64-linux-gnu-as gerade.s -o gerade.oHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Linken
riscv64-linux-gnu-ld gerade.o -o geradeHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Im Emulator ausführen
qemu-riscv64 ./geradeHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Die Summe lesen
echo $?Hier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Ohne Abkürzungen zurücklesen
riscv64-linux-gnu-objdump -d -M no-aliases geradeHier angezeigt, nicht ausgeführt. Erstellen Sie ein kostenloses Konto, um „Ausführen“ zu drücken und ihn im Terminal Ihres eigenen Labs einzugeben.
Weiterführend: multiplizieren ohne mul
Schreiben Sie ein RISC-V-Programm, das 8 mal 9 nur mit li, add, addi und einer Verzweigung rechnet: neunmal 8 zur Summe addieren.
Führen Sie es zuerst in Ihrem Interpreter aus. Schreiben Sie es dann wie gerade.s mit printf in eine Datei, bauen Sie es und führen Sie es im Lab aus. Der Exit-Status sollte 72 sein.