Extremis
Assembly

Extremis

Aventura RPG escrita en ensamblador RISC-V

(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-V
  • riscv32-elf-ld — Enlazador RISC-V
  • qemu-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 .include en el archivo del capítulo que los necesita.
  • El Makefile descubre automáticamente todos los archivos .s bajo src/ (excluyendo src/include/).