( god, die .iris programmeerde om niet opgenomen te kunnen worden? )
Een tekstgebaseerd rollenspel geschreven in RISC-V 32-bit assembly, gesimuleerd via QEMU.
"Het universum besloot dat het niet wilde dat ik iets beters deed, zoals misschien een leven krijgen."
Afhankelijkheden
riscv32-elf-as— RISC-V assemblerriscv32-elf-ld— RISC-V linkerqemu-riscv32— RISC-V user-mode emulator
Op Arch Linux:
sudo pacman -S riscv64-elf-binutils qemu-user
Bouwen & Uitvoeren
make build # assembleren en linken naar main.bin
make run # bouwen + uitvoeren via qemu-riscv32
make debug # uitvoeren met -strace om syscalls te inspecteren
Projectstructuur
Extremis/
├── main.s # Entry point (_start), roept run_chapter_1 aan
├── src/
│ ├── include/
│ │ └── function.s # startF / endF stack frame macros
│ ├── engine/
│ │ ├── print.s # print — print een enkele null-terminated string (a0)
│ │ └── utils.s # printl — print een lijst strings (a0=addr tabel, a1=aantal)
│ ├── chapters/
│ │ └── chapter_1.s # Hoofdstuk 1 logica; laadt intro strings en roept printl aan
│ └── dialogue/
│ └── intro.s # String data voor de introductie
└── build/ # Gecompileerde .o bestanden (spiegelt src/ structuur)
Architectuur
De engine is pure RISC-V 32-bit assembly gericht op de Linux ABI via QEMU user-mode emulatie.
Stack Frame Conventie
Elke functie gebruikt de startF / endF macros uit src/include/function.s om ra, s0, s1, s2 op de stack op te slaan en te herstellen:
startF # push: wijs 16 bytes toe, sla ra/s0/s1/s2 op
...
endF # pop: herstel ra/s0/s1/s2, dewijs 16 bytes vrij
ret
Syscalls
Waarschuwing: RARS/MARS omgevingscallnummers werken niet onder
qemu-riscv32. Dit project gebruikt Linux RISC-V ABI syscallnummers.
Syscallnummer gaat in a7, aanroepen met ecall. Retourwaarde komt terug in a0.
Uitvoer
| Doel | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Opmerkingen |
|---|---|---|---|---|---|---|
| Print string | 4 |
64 |
1 (stdout fd) |
buf addr | lengte | Linux stopt NIET bij null — geef byte lengte |
| Print char | 11 |
64 |
1 |
char buf | 1 |
sla char op in geheugen, geef adres door |
| Print integer | 1 |
— | integer | — | — | geen Linux equivalent; eerst naar string |
| Print hex integer | 34 |
— | integer | — | — | geen Linux equivalent; handmatig converteren |
| Print unsigned int | 36 |
— | integer | — | — | geen Linux equivalent; handmatig converteren |
Invoer
| Doel | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Opmerkingen |
|---|---|---|---|---|---|---|
| Lees string | 8 |
63 |
0 (stdin fd) |
buf addr | max bytes | Linux retourneert ruwe bytes incl. newline |
| Lees char | 12 |
63 |
0 |
buf addr | 1 |
retourneert a0 = gelezen bytes; char in buf |
| Lees integer | 5 |
— | — | — | — | geen Linux equivalent; lees string, parseer |
Proces
| Doel | RARS a7 |
Linux a7 |
a0 |
Opmerkingen |
|---|---|---|---|---|
| Afsluiten | 10 |
93 |
— | RARS negeert a0; Linux leest als exitstatus |
| Afsluiten(code) | 17 |
93 |
exitcode | Linux exit_group |
Geheugen
| Doel | RARS a7 |
Linux a7 |
a0 |
Retourneert | Opmerkingen |
|---|---|---|---|---|---|
| Toewijzen (sbrk) | 9 |
214 |
bytes (RARS) / nieuw brk adres (Linux) | a0 = begin toegewezen blok |
Linux brk werkt anders — je stelt nieuwe top in, geen grootte |
Bestands-I/O
| Doel | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
a3 |
Opmerkingen |
|---|---|---|---|---|---|---|---|
| Bestand openen | 13 |
56 |
dirfd (-100=CWD) |
bestandsnaam addr | vlaggen | modus | Linux gebruikt openat |
| Bestand lezen | 14 |
63 |
fd | buf addr | max bytes | — | retourneert gelezen bytes |
| Bestand schrijven | 15 |
64 |
fd | buf addr | byte aantal | — | retourneert geschreven bytes |
| Bestand sluiten | 16 |
57 |
fd | — | — | — | retourneert 0 bij succes |
Tijd
| Doel | RARS a7 |
Linux a7 |
a0 |
a1 |
Opmerkingen |
|---|---|---|---|---|---|
| Tijd | 30 |
113 |
clock id (1=REALTIME) |
timespec* buf |
RARS retourneert lo/hi in a0/a1; Linux schrijft struct naar buf |
| Slaap | 32 |
115 |
clock id | timespec* |
RARS neemt milliseconden in a0; Linux gebruikt clock_nanosleep met struct |
Inhoud Toevoegen
- Nieuwe dialoogstrings gaan in
src/dialogue/. - Nieuwe hoofdstukken gaan in
src/chapters/en worden.include'd in het hoofdstukbestand dat ze nodig heeft. - Het Makefile ontdekt automatisch alle
.sbestanden ondersrc/(exclusiefsrc/include/).
