(.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.s の startF / endF マクロを使用して、ra、s0、s1、s2 をスタックに保存・復元します:
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/を除く)。
