(dios, ¿quién programó a .iris para que no pudiera ser comprometido?)
Un RPG basado en texto escrito en ensamblador RISC-V de 32 bits, simulado mediante QEMU.
"El universo decidió que no quería que hiciera nada mejor, como quizás conseguir una vida."
Dependencias
riscv32-elf-as— Ensamblador RISC-Vriscv32-elf-ld— Enlazador RISC-Vqemu-riscv32— Emulador de modo usuario RISC-V
En Arch Linux:
sudo pacman -S riscv64-elf-binutils qemu-user
Compilar y Ejecutar
make build # ensamblar y enlazar en main.bin
make run # compilar + ejecutar mediante qemu-riscv32
make debug # ejecutar con -strace para inspeccionar llamadas al sistema
Estructura del Proyecto
Extremis/
├── main.s # Punto de entrada (_start), llama a run_chapter_1
├── src/
│ ├── include/
│ │ └── function.s # Macros de marco de pila startF / endF
│ ├── engine/
│ │ ├── print.s # print — imprime una cadena terminada en nulo (a0)
│ │ └── utils.s # printl — imprime una lista de cadenas (a0=dir tabla, a1=contador)
│ ├── chapters/
│ │ └── chapter_1.s # Lógica del Capítulo 1; carga cadenas de introducción y llama a printl
│ └── dialogue/
│ └── intro.s # Datos de cadena para la secuencia de introducción
└── build/ # Archivos .o compilados (refleja la estructura de src/)
Arquitectura
El motor es ensamblador RISC-V puro de 32 bits que utiliza la ABI de Linux mediante la emulación de modo usuario de QEMU.
Convención de Marco de Pila
Cada función utiliza las macros startF / endF de src/include/function.s para guardar y restaurar ra, s0, s1, s2 en la pila:
startF # push: asignar 16 bytes, guardar ra/s0/s1/s2
...
endF # pop: restaurar ra/s0/s1/s2, desasignar 16 bytes
ret
Llamadas al Sistema
Advertencia: Los números de llamada de entorno RARS/MARS no funcionan bajo
qemu-riscv32. Este proyecto utiliza números de llamada al sistema de la ABI Linux RISC-V.
El número de llamada al sistema va en a7, se invoca con ecall. El valor de retorno vuelve en a0.
Salida
| Intención | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Notas |
|---|---|---|---|---|---|---|
| Imprimir cadena | 4 |
64 |
1 (stdout fd) |
dir buf | longitud | Linux NO se detiene en nulo — pasar longitud en bytes |
| Imprimir carácter | 11 |
64 |
1 |
buf char | 1 |
almacenar char en memoria, pasar su dirección |
| Imprimir entero | 1 |
— | entero | — | — | sin equivalente en Linux; convertir a cadena primero |
| Imprimir hex | 34 |
— | entero | — | — | sin equivalente en Linux; convertir manualmente |
| Imprimir sin signo | 36 |
— | entero | — | — | sin equivalente en Linux; convertir manualmente |
Entrada
| Intención | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
Notas |
|---|---|---|---|---|---|---|
| Leer cadena | 8 |
63 |
0 (stdin fd) |
dir buf | bytes máx | Linux devuelve bytes crudos incluyendo nueva línea |
| Leer carácter | 12 |
63 |
0 |
dir buf | 1 |
devuelve a0 = bytes leídos; char está en buf |
| Leer entero | 5 |
— | — | — | — | sin equivalente en Linux; leer cadena, analizarla |
Proceso
| Intención | RARS a7 |
Linux a7 |
a0 |
Notas |
|---|---|---|---|---|
| Salir | 10 |
93 |
— | RARS ignora a0; Linux lo lee como estado de salida |
| Salir(código) | 17 |
93 |
código salida | Linux exit_group |
Memoria
| Intención | RARS a7 |
Linux a7 |
a0 |
Devuelve | Notas |
|---|---|---|---|---|---|
| Asignar (sbrk) | 9 |
214 |
bytes (RARS) / nueva dir brk (Linux) | a0 = inicio del bloque asignado |
Linux brk funciona diferente — se establece el nuevo tope, no un tamaño |
E/S de Archivos
| Intención | RARS a7 |
Linux a7 |
a0 |
a1 |
a2 |
a3 |
Notas |
|---|---|---|---|---|---|---|---|
| Abrir archivo | 13 |
56 |
dirfd (-100=CWD) |
dir nombre | banderas | modo | Linux usa openat |
| Leer archivo | 14 |
63 |
fd | dir buf | bytes máx | — | devuelve bytes leídos |
| Escribir archivo | 15 |
64 |
fd | dir buf | conteo bytes | — | devuelve bytes escritos |
| Cerrar archivo | 16 |
57 |
fd | — | — | — | devuelve 0 en éxito |
Tiempo
| Intención | RARS a7 |
Linux a7 |
a0 |
a1 |
Notas |
|---|---|---|---|---|---|
| Tiempo | 30 |
113 |
id reloj (1=TIEMPO_REAL) |
timespec* buf |
RARS devuelve lo/hi dividido en a0/a1; Linux escribe struct en buf |
| Dormir | 32 |
115 |
id reloj | timespec* |
RARS toma milisegundos en a0; Linux usa clock_nanosleep con un struct |
Agregar Contenido
- Las nuevas cadenas de diálogo van en
src/dialogue/. - Los nuevos capítulos van en
src/chapters/y se incluyen con.includeen el archivo del capítulo que los necesita. - El Makefile descubre automáticamente todos los archivos
.sbajosrc/(excluyendosrc/include/).
