Raspberry Pi Pico で MSX1 のエミュレータを作る
MSX1 エミュレータを作ったのでメモ
MSX とは
レトロ PC 界隈の方には説明不要だと思いますが、MSX (MSX1) とは 1983 年に発表されたレトロPCの規格で、 当時複数のメーカーからいろんな機種が発売されたものです。
特徴は以下の通りです。
- VDP として TMS9918 を使用
- 音源に PSG (AY-3-8910)を使用
- カートリッジによって、ソフトウェアを供給したり、ハードウェアを拡張することが可能
のちに、VDP の増強や、メモリの増量、FM 音源の搭載などによって、MSX2、MSX2+、MSX TurboR と進化していきました。
Raasperry Pi Pico で MSX1 をエミュレートする
いつものように 1/4 筐体にエミュレータを仕込んで、実際に動く MiniMSX を作るのが目標です。 できるだけ楽に作るのがモットーなので、
- Z80 のエミュレーションには今まで使ってきた Z80 エミュレータを使う
- TMS9918 のエミュレーションには vrEmuTMS9918 という出来の良いエミュレータがあるので、そのまま採用する
- 音源系のエミュレーションには DSA さんの音源エミュレータを使用する
というのを基本方針としました。
スロットとメガROMとメモリーマッパー
MSX を語るうえで一番厄介なのが、メモリー周りです。 8bit パソコンでは、メモリ空間が 64KiB しかないので、大量の RAM や ROM を搭載しようとすると何らかの切替機構が必要になります。
MSX ではこの辺りを標準化していて、64KiB のメモリ空間を 16KiB 毎の Page に4分割して、それぞれに4個あるスロットの中のどのメモリを割り当てるか切り替えできるようになっています。 これが「標準スロット」になります。
さらに、「標準スロット」を 4 つに拡張して、「拡張したスロット」のどのスロットをメモリに割り当てるか切り替えできるようになっています。
なので、一般的にメモリの割り当ては「Page 1 に Slot 3-0 を割り当てる」などという表現がされます。 原理的には 4x4 の 16 スロットまで持てますが、Slot 0 と Slot 3 を内蔵して拡張スロットにしている機種が多いようです。 ここでは Slot 3 を拡張スロットとして、RAM と DISKBIOS と FMBIOS を置くこととします。
次に問題になるのが「メガROM」です。 MSX の各スロットには最大 64KiB のメモリしかないのに対して、ソフトウェアのサイズが大きくなってきて 128KiB を超えるものが出てきました。 そこで、「スロットの中を Bank に分割して、Bank に割り当てるメモリを変更できるようにする」のがメガROMマッパーです。 しかも標準的な仕様が複数あって、ROM イメージだけではどの方式を使っているのか判別できないという素敵仕様です。
マッパーの仕様により「どのアドレスへ書き込みが行われるか」に差異が出るので、ROMファイルをスキャンして統計情報でどの仕様のマッパーなのか推測しています。
MSX には MSX-DOS という OS があって、最低 64KiB の RAM で動作します。 このため、最初の実装では 64KiB で実装していたのですが、MSX-DOS 互換の Nextor というシステムでは、最低 128KiB の RAM が必要になっています。 この辺りを拡張するのがメモリーマッパーです。
メモリーマッパーはスロット内のメモリーの割り当てを拡張して、16KiB のセグメントのどこから Page に割り当てるかを自由に変えることができるシステムです。
もう、わけがわからないよ…
フレームバッファを廃止してメモリを稼ぐ
いままでのレトロPCエミュレータには、フレームバッファ方式を用いていて、画面の描画に一切 CPU を使っていないのを特徴としていましたが、 その分メモリの消費量が多いという問題がありました。
今回 MSX は解像度が低い(256x192)ので、リアルタイムで画面を生成しても十分に間に合うので、フレームバッファを使わずに描画するテストとしてみました。 ただ、メニュー表示ができなくなるので、TMS9918 のインスタンスをもうひとつ使って、メニュー表示時にインスタンスを切り替えることで、対応しました。
ROM をロードする
MSX のソフトウェア供給方式として一般的なのは ROM カートリッジなので、ROM に対応しないといけません。 ファイルシステム上から読み出すととても遅いですし、RAM 上にロードするにはメモリが足りないので、フラッシュ上に ROM の領域を確保して、 ロードの際に書き込みが必要かどうかチェックしてから、フラッシュに書き込むことにしました。
Joystick を実装する
これまた初めての試みとして、USB Gamepad の対応に挑戦しました。 TinyUSB に DualShock4 から情報を取る例があったので、簡単にできるかと思ったのですが、そう簡単ではありませんでした。 DualShock4 の実装を見てみると、レポートのどこにデータが入っているか決め打ちしていたので、まったく使えないことがわかりました。
なので、HID のレポートデスクプリタを解釈するツールを探す必要がありました。 (この辺のライセンスがちょっと怪しいのだよね…)
FD を実装する
MSX では FD は BIOS を通して使うのが決められているだけで、FDC の標準仕様は定義されていません。 世の中の実装的には MB8877(WD2793) と uPD765A(TC8566AF) の二つが主流のようなので、今回は MB8877 の方を採用しました。 ぶっちゃけ FM-7 の実装をそのまま流用しています。
ただ、MSX の標準のファイルフォーマットはべたイメージの DSK 形式なので、512バイト/セクターの処理に特化させています。
OPLL で苦しむ
MSX の拡張音源として一般的なのが MSX-MUSIC です。のちに MSX2+ などの標準仕様に取り込まれています。 MSX-MUSIC で使用されている音源が YM2413 通称 OPLL です。
PC-6001mk2 エミュレータの FM 音源で使っていた fmgen では OPLL は対応していなかったので、 代わりの実装を探していたのですが、DSA さんの emu2413 が一番処理が軽そうなのでつかってみることにしました。
しかし、そのまま使ったのでは処理が重すぎてまともに音が出ないという結果になりました。
- サンプリングレート変換をオフにする
- ノイズの処理を減らす
- テストレジスタの処理を削除する
- OPLL 処理ルーチンをRAM上に配置する
- データも RAM 上に配置する
- Pico の動作クロックを 250MHz まで上げる
- ほかの処理を全部 Core0 に寄せて、Core1 を OPLL の処理に専念させる
などの対応でやっとそれなりの音が出せるようになりました。
まとめ
MSX1 のエミュレータとしてはこんな感じとなります。
今回の新機構については、以下の判断となりました。
- リアルタイム描画が高解像度で使えるかどうかはわからない
- USB Gamepad 対応はほかのエミュレータでも使える