Extremis
Assembly

Extremis

用RISC-V汇编编写的RPG冒险游戏

(天啊,谁把.iris编程成无法提交的?)

一款基于RISC-V 32位汇编编写的文本角色扮演游戏,通过QEMU模拟运行。

"宇宙决定不让我做任何更好的事,比如或许找点生活。"

依赖项

  • riscv32-elf-as — RISC-V 汇编器
  • riscv32-elf-ld — RISC-V 链接器
  • qemu-riscv32 — RISC-V 用户模式模拟器

在 Arch Linux 上:

sudo pacman -S riscv64-elf-binutils qemu-user

构建与运行

make build   # 汇编并链接成 main.bin
make run     # 构建 + 通过 qemu-riscv32 运行
make debug   # 使用 -strace 运行以检查系统调用

项目结构

Extremis/
├── main.s                      # 入口点 (_start),调用 run_chapter_1
├── src/
│   ├── include/
│   │   └── function.s          # startF / endF 栈帧宏
│   ├── engine/
│   │   ├── print.s             # print  — 打印单个以空字符结尾的字符串 (a0)
│   │   └── utils.s             # printl — 打印字符串列表 (a0=地址表, a1=数量)
│   ├── chapters/
│   │   └── chapter_1.s         # 第一章逻辑;加载开场字符串并调用 printl
│   └── dialogue/
│       └── intro.s             # 开场序列的字符串数据
└── build/                      # 编译后的 .o 文件(镜像 src/ 布局)

架构

引擎是纯 RISC-V 32 位汇编,通过 QEMU 用户模式模拟针对 Linux ABI。

栈帧约定

每个函数使用 src/include/function.s 中的 startF / endF 宏在栈上保存和恢复 ras0s1s2

startF    # 压栈:分配 16 字节,保存 ra/s0/s1/s2
...
endF      # 出栈:恢复 ra/s0/s1/s2,释放 16 字节
ret

系统调用

警告: RARS/MARS 环境调用号在 qemu-riscv32适用。本项目使用 Linux RISC-V ABI 系统调用号。

系统调用号放入 a7,通过 ecall 调用。返回值在 a0 中。

输出

意图 RARS a7 Linux a7 a0 a1 a2 说明
打印字符串 4 64 1 (stdout 文件描述符) 缓冲区地址 长度 Linux 不会在空字符处停止 — 需传递字节长度
打印字符 11 64 1 字符缓冲区 1 将字符存入内存,传递其地址
打印整数 1 整数 无 Linux 等效项;需先转换为字符串
打印十六进制整数 34 整数 无 Linux 等效项;需手动转换
打印无符号整数 36 整数 无 Linux 等效项;需手动转换

输入

意图 RARS a7 Linux a7 a0 a1 a2 说明
读取字符串 8 63 0 (stdin 文件描述符) 缓冲区地址 最大字节数 Linux 返回原始字节,包括换行符
读取字符 12 63 0 缓冲区地址 1 a0 返回读取的字节数;字符在缓冲区中
读取整数 5 无 Linux 等效项;读取字符串,解析它

进程

意图 RARS a7 Linux a7 a0 说明
退出 10 93 RARS 忽略 a0;Linux 将其作为退出状态读取
退出(代码) 17 93 退出代码 Linux exit_group

内存

意图 RARS a7 Linux a7 a0 返回 说明
分配 (sbrk) 9 214 字节数 (RARS) / 新 brk 地址 (Linux) a0 = 分配块的起始地址 Linux brk 工作方式不同 — 你设置新的顶部,而不是大小

文件 I/O

意图 RARS a7 Linux a7 a0 a1 a2 a3 说明
打开文件 13 56 目录文件描述符 (-100=当前工作目录) 文件名地址 标志 模式 Linux 使用 openat
读取文件 14 63 文件描述符 缓冲区地址 最大字节数 返回读取的字节数
写入文件 15 64 文件描述符 缓冲区地址 字节数 返回写入的字节数
关闭文件 16 57 文件描述符 成功时返回 0

时间

意图 RARS a7 Linux a7 a0 a1 说明
时间 30 113 时钟 ID (1=实时时钟) timespec* 缓冲区 RARS 在 a0/a1 中返回分离的低/高位;Linux 将结构体写入缓冲区
睡眠 32 115 时钟 ID timespec* RARS 在 a0 中接收毫秒数;Linux 使用带结构体的 clock_nanosleep

添加内容

  • 新的对话字符串放在 src/dialogue/ 中。
  • 新的章节放在 src/chapters/ 中,并通过 .include 引入到需要它们的章节文件中。
  • Makefile 自动发现 src/ 下所有 .s 文件(排除 src/include/)。