カスタムRISC-Vエミュレータ上でDOOMを実行する
ほぼすべてのコンピューティングデバイスでDOOMを動かすことができるという能力は、ハードウェアとソフトウェア開発の世界で称賛されるベンチマークとなっています。スマートウォッチから妊娠検査薬まで、象徴的なこのゲームは開発者の創意工夫と現代コンピューティングの柔軟性を示す証です。この伝統は、最近のプロジェクトであるカスタム構築のRISC‑Vエミュレータ上でDOOMを動かすデモにより継承され、オープンソースの命令セットアーキテクチャの力とアクセスしやすさを示しています。
このプロジェクトは単なるミームの実現にとどまらず、エミュレータ開発の細部、システムコール実装、そして新しい最小限環境へのアプリケーション移植に伴う課題について貴重な洞察を提供します。"hello world" から完全にプレイ可能なゲームに至るまでの旅路は、ゼロから機能的なコンピューティングプラットフォームを構築するために必要な基礎的ステップを示しています。
RISC-Vエミュレータの構築
この取り組みの中心は、RV32IM 命令セットを実装することを目的とした RISC‑V エミュレータ rvcore です。この命令セットの選択は、組み込みシステムや教育プロジェクトに適した、コンパクトでありながら十分な機能を持つアーキテクチャに焦点を当てていることを意味します。エミュレータは最小限のシステムコールインターフェースを提供し、プログラムが仮想ハードウェアとやり取りできるように、入出力やメモリ管理といった操作を処理します。
ELF ローディングとメモリ管理
コンパイル済みプログラムを実行するために、エミュレータは ELF(Executable and Linkable Format)ローディング機能を組み込んでいます。現在は単一の PT_LOAD セグメントのみをサポートしており、初期実装を簡素化しつつ多くのアプリケーションに十分対応できるようにしています。カスタムリンカスクリプトを使用して、C プログラムのエントリーポイントである _start が仮想アドレス 0 に常に配置されるようにし、ELF ローダーの設計をさらに合理化しました。
メモリ構成はシステムにとって重要な要素であり、rvcore エミュレータは DOOM を収容するための特定のメモリマップを定義しています:
0x7FFFFF +-------------------------------------+
| |
| QUEUE_SIZE (32 bytes) |
| |
0x7FFFDF +-------------------------------------+ <-- QUEUE_START
0x7FFFDE | QUEUE_READ_IDX |
0x7FFFDD | QUEUE_WRITE_IDX |
+-------------------------------------+
| |
| |
| VRAM (1,024,000 bytes) |
| |
| |
0x705FDD +-------------------------------------+ <-- STACK_START
| Stack |
| |
| v |
| |
| ^ |
| |
| Program data + Heap |
| |
0x000000 +-------------------------------------+
このレイアウトは、DOOM がフレームを描画する VRAM(1,024,000 バイト)を 0x705FDD から開始する領域に大きく割り当てています。メモリ上部の小さなキュー領域は入力を処理し、読み書き用のインデックスが専用に用意されています。
DOOM 移植:ステップバイステップのアプローチ
DOOM を動かすまでの道のりは、よりシンプルなタスクから始め、必要なインフラを徐々に構築していく形で進められました。
"Hello World" から C プログラムへ
当初、エミュレータは基本的な "hello world" アセンブリプログラムでテストされました。次の大きな壁は C プログラムの実行を可能にすることでした。これは組み込みシステム向けに最適化された C 標準ライブラリ newlib を統合することで達成されました。newlib により、開発者はシステムコールスタブを段階的に実装でき、必要な関数を一つずつ提供しながら、ターゲットプログラムが正常に動作するまで環境を整えていくことができます。
doomgeneric の活用
移植作業を簡素化するために、プロジェクトは doomgeneric を利用しました。このプロジェクトは DOOM 用の最小限かつポータブルなレイヤーを提供し、プラットフォーム固有の詳細を抽象化します。基本的なシステムコールと描画メカニズムが整った時点で、ゲームを動かすために必要なスタブ実装に集中できるようになります。
入出力の取り扱い
DOOM は出力を固定領域(VRAM 領域 0x705FDD から開始)に描画します。エミュレータはこのメモリ領域をディスプレイ出力として解釈する役割を担います。キーボード入力などは、メモリ上の指定キューに書き込むことで処理されます。エミュレータ内で動作する DOOM インスタンスはこれらの入力を取得し、対話的なループを完結させます。
DOOM 移植の永続的な遺産
このような RISC‑V エミュレータ上で DOOM を動かすプロジェクトは、技術コミュニティの中で深く共鳴します。熱意に満ちた反応が寄せられ、探求心と技術的熟練の精神を体現しています。Hacker News のコメント投稿者 @patrickhener が的確に述べたように:
There is nothing, that can not run DOOM. I love those type of projects. Thanks for showing.
この言葉は、DOOM の移植が多くの開発者にとって新しいハードウェアやソフトウェアプラットフォームを探求する際の通過儀礼となっている文化的意義を強調しています。別のユーザー @ani_k47 はシンプルに「Things like this are really cool」と述べ、こうした創造的かつ挑戦的な取り組みに対する広範な評価を端的に表しています。
この RISC‑V DOOM エミュレータは、オープンソースハードウェアアーキテクチャと熱心な開発によって何が可能になるかを示す説得力のある例です。古典的なゲームのための機能的プラットフォームを提供するだけでなく、システムエミュレーションやアプリケーション移植の低レベルな詳細に関心のあるすべての人にとって教育的リソースともなります。