Extremis
Assembly

Extremis

RISC-Vアセンブリで書かれたRPGアドベンチャー

(.iris をコミットできないようにプログラムした神よ?)

RISC-V 32ビットアセンブリで書かれたテキストベースのRPGで、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=addr table, a1=count)
│   ├── chapters/
│   │   └── chapter_1.s         # 第1章のロジック; イントロ文字列をロードして printl を呼び出す
│   └── dialogue/
│       └── intro.s             # イントロシーケンスの文字列データ
└── build/                      # コンパイルされた .o ファイル (src/ の構造を反映)

アーキテクチャ

エンジンは純粋なRISC-V 32ビットアセンブリで、QEMUユーザーモードエミュレーションを介してLinux ABIをターゲットとしています。

スタックフレーム規約

すべての関数は src/include/function.sstartF / endF マクロを使用して、ras0s1s2 をスタックに保存・復元します:

startF    # push: 16バイト確保、ra/s0/s1/s2 を保存
...
endF      # pop:  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 fd) buf addr length Linux はヌル文字で停止しない — バイト長を渡す
文字出力 11 64 1 char buf 1 文字をメモリに格納し、そのアドレスを渡す
整数出力 1 integer Linux に相当するものなし; 文字列に変換してから
16進整数出力 34 integer Linux に相当するものなし; 手動で変換
符号なし整数出力 36 integer Linux に相当するものなし; 手動で変換

入力

目的 RARS a7 Linux a7 a0 a1 a2 備考
文字列入力 8 63 0 (stdin fd) buf addr max bytes Linux は改行を含む生のバイトを返す
文字入力 12 63 0 buf addr 1 a0 = 読み取ったバイト数; 文字は buf 内
整数入力 5 Linux に相当するものなし; 文字列を読み取り、解析

プロセス

目的 RARS a7 Linux a7 a0 備考
終了 10 93 RARS は a0 を無視; Linux は終了ステータスとして読み取る
終了(コード) 17 93 exit code Linux exit_group

メモリ

目的 RARS a7 Linux a7 a0 戻り値 備考
割り当て (sbrk) 9 214 bytes (RARS) / new brk addr (Linux) a0 = 割り当てられたブロックの先頭 Linux brk は動作が異なる — サイズではなく新しい上限を設定

ファイルI/O

目的 RARS a7 Linux a7 a0 a1 a2 a3 備考
ファイル開く 13 56 dirfd (-100=CWD) filename addr flags mode Linux は openat を使用
ファイル読む 14 63 fd buf addr max bytes 読み取ったバイト数を返す
ファイル書く 15 64 fd buf addr byte count 書き込んだバイト数を返す
ファイル閉じる 16 57 fd 成功時は 0 を返す

時間

目的 RARS a7 Linux a7 a0 a1 備考
時刻 30 113 clock id (1=REALTIME) timespec* buf RARS は a0/a1 で分割された lo/hi を返す; Linux は構造体を buf に書き込む
スリープ 32 115 clock id timespec* RARS は a0 でミリ秒を受け取る; Linux は構造体で clock_nanosleep を使用

コンテンツの追加

  • 新しいダイアログ文字列は src/dialogue/ に配置します。
  • 新しいチャプターは src/chapters/ に配置し、必要なチャプターファイルに .include で取り込みます。
  • Makefile は src/ 以下のすべての .s ファイルを自動検出します(src/include/ を除く)。