(dio, chi ha programmato .iris per non poter essere committato?)
Un RPG testuale scritto in assembly RISC-V a 32 bit, simulato tramite QEMU.
"L'universo ha deciso che non voleva che facessi niente di meglio, tipo magari farmi una vita."
Dipendenze
riscv32-elf-as— Assemblatore RISC-Vriscv32-elf-ld— Linker RISC-Vqemu-riscv32— Emulatore RISC-V in modalità utente
Su Arch Linux:
sudo pacman -S riscv64-elf-binutils qemu-user
Compilazione ed Esecuzione
make build # assembla e linka in main.bin
make run # compila + esegui tramite qemu-riscv32
make debug # esegui con -strace per ispezionare le syscall
Struttura del Progetto
Extremis/
├── main.s # Punto di ingresso (_start), chiama run_chapter_1
├── src/
│ ├── include/
│ │ └── function.s # Macro startF / endF per stack frame
│ ├── engine/
│ │ ├── print.s # print — stampa una singola stringa terminata da null (a0)
│ │ └── utils.s # printl — stampa una lista di stringhe (a0=indirizzo tabella, a1=conteggio)
│ ├── chapters/
│ │ └── chapter_1.s # Logica del Capitolo 1; carica le stringhe introduttive e chiama printl
│ └── dialogue/
│ └── intro.s # Dati delle stringhe per la sequenza introduttiva
└── build/ # File .o compilati (rispecchia la struttura di src/)
Architettura
Il motore è puro assembly RISC-V a 32 bit che utilizza l'ABI Linux tramite l'emulazione QEMU in modalità utente.
Convenzione dello Stack Frame
Ogni funzione utilizza le macro startF / endF da src/include/function.s per salvare e ripristinare ra, s0, s1, s2 sullo stack:
startF # push: alloca 16 byte, salva ra/s0/s1/s2
...
endF # pop: ripristina ra/s0/s1/s2, dealloca 16 byte
ret
Syscall
Attenzione: I numeri delle chiamate d'ambiente RARS/MARS non funzionano sotto
qemu-riscv32. Questo progetto utilizza i numeri di syscall dell'ABI Linux RISC-V.
Il numero di syscall va in a7, si invoca con ecall. Il valore di ritorno torna in a0.
Output
| Intento | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Note |
|---|---|---|---|---|---|---|
| Stampa stringa | 4 |
64 |
1 (stdout fd) |
indirizzo buf | lunghezza | Linux NON si ferma al null — passa la lunghezza in byte |
| Stampa carattere | 11 |
64 |
1 |
buf carattere | 1 |
memorizza il carattere in memoria, passa il suo indirizzo |
| Stampa intero | 1 |
— | intero | — | — | nessun equivalente Linux; convertire in stringa prima |
| Stampa intero hex | 34 |
— | intero | — | — | nessun equivalente Linux; convertire manualmente |
| Stampa intero unsigned | 36 |
— | intero | — | — | nessun equivalente Linux; convertire manualmente |
Input
| Intento | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Note |
|---|---|---|---|---|---|---|
| Leggi stringa | 8 |
63 |
0 (stdin fd) |
indirizzo buf | byte massimi | Linux restituisce byte grezzi inclusa la newline |
| Leggi carattere | 12 |
63 |
0 |
indirizzo buf | 1 |
restituisce a0 = byte letti; il carattere è in buf |
| Leggi intero | 5 |
— | — | — | — | nessun equivalente Linux; leggere stringa, analizzarla |
Processo
| Intento | RARS a7 |
Linux a7 |
a0 |
Note |
|---|---|---|---|---|
| Esci | 10 |
93 |
— | RARS ignora a0; Linux lo legge come stato di uscita |
| Esci(codice) | 17 |
93 |
codice uscita | Linux exit_group |
Memoria
| Intento | RARS a7 |
Linux a7 |
a0 |
Restituisce | Note |
|---|---|---|---|---|---|
| Alloca (sbrk) | 9 |
214 |
byte (RARS) / nuovo indirizzo brk (Linux) | a0 = inizio blocco allocato |
Linux brk funziona diversamente — imposti il nuovo top, non una dimensione |
I/O su File
| Intento | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
a3 |
Note |
|---|---|---|---|---|---|---|---|
| Apri file | 13 |
56 |
dirfd (-100=CWD) |
indirizzo nome file | flag | modalità | Linux usa openat |
| Leggi file | 14 |
63 |
fd | indirizzo buf | byte massimi | — | restituisce byte letti |
| Scrivi file | 15 |
64 |
fd | indirizzo buf | conteggio byte | — | restituisce byte scritti |
| Chiudi file | 16 |
57 |
fd | — | — | — | restituisce 0 in caso di successo |
Tempo
| Intento | RARS a7 |
Linux a7 |
a0 |
a1 |
Note |
|---|---|---|---|---|---|
| Tempo | 30 |
113 |
id clock (1=REALTIME) |
timespec* buf |
RARS restituisce lo/hi divisi in a0/a1; Linux scrive la struct in buf |
| Sleep | 32 |
115 |
id clock | timespec* |
RARS prende millisecondi in a0; Linux usa clock_nanosleep con una struct |
Aggiungere Contenuti
- Nuove stringhe di dialogo vanno in
src/dialogue/. - Nuovi capitoli vanno in
src/chapters/e vengono.include'd nel file del capitolo che li necessita. - Il Makefile scopre automaticamente tutti i file
.ssottosrc/(escludendosrc/include/).
