Raspberry Pi Pico でレトロPCのエミュレータを作るメモ
勝手に Pasocom mini シリーズの製作メモ
実装方針
市販の Pasocom mini が 1/4 スケールで製作されているので、 同縮尺のミニチュア筐体に組み込むのを目標とする。
筐体についてはいくつかネットで STL データが公開されているので、それを利用して加工することとする。
- 安価なマイコンを使用する(総額 1000円めど)
- 外付けハードウェアは最小限とする
- PS/2 ないし USB キーボードで操作する
- 映像出力はコンポジットビデオまたは VGA 出力とする
- エミュレータとして完璧な動作は目指さない。有名どころが動作すればOKとする。
初期は CH32V203 をベースとしたモノクロコンポジットビデオ対応、 最近は Raspberry Pi Pico をベースとした VGA 対応を基本としている。
ターゲットの選定
勝手に Pasocom mini のターゲットについては、以下の要素を考慮している。
8 bit CPU
Pico の処理能力的に Z80 4MHz 程度が処理限界なので、現実的には 8 bit マシンが限度となる。
デジタル RGB 出力
1 pixel あたり 4bit を使う場合、フレームバッファに 125KiB を使用する。 RGB に各 1bit を割り当てたデジタルRGB 出力を持った機種がターゲットとなる。
64KiB 程度のメモリ
Pico の RAM 容量は 262KiB であるので、125KiB のフレームバッファを確保すると、残りは 137KiB である。 640x200 で RGB 3 プレーンの VRAM が 48KiB 使用するので、エミュレータが使用できるメモリは 64KiB 程度となる。
以上のことから、1984 年ごろのレトロ PC がターゲットとなる。 Pico 2 になるともう少し性能的に余裕ができるので、もっといろいろできるかもしれない。
以下各コンポーネントの実装について…
コンポジット出力
CH32V203 では、映像出力にモノクロコンポジットビデオを採用した。 これは、スキャンライン毎のデータをリアルタイムで生成して、DMA+SPI で映像出力を行うものである。
リアルタイム合成するので、複雑な処理は不可能である。
VGA 出力
Raspberry Pi Pico の PIO は CPU に依存しないで、いろいろな処理が行えるので非常に強力な機能である。
Pico を使った VGA 信号生成にはサンプルの scanvideo を含めて、いくつか実装があるがここでは この実装 を使用している。
この実装では PIO を一個 VGA 出力のために使用していて、
画像データは 1pixel あたり 4bit を用いて、DMA でデータを一気に送信するようになっている。 また、DMA の再設定にも DMA Chain 機能を使っているので、VGA の処理に一切 CPU を使用しないという特徴がある。
元々の仕様では、VGA 解像度(640x480)を出力するようになっていたが、国産レトロPCでは縦 200 pixel の場合が多いので、 640x400 pixel で出力するように改変している。
scanvideo では動的にスキャンライン毎のデータを生成しているが、 レトロPCにありがちが、テキスト+グラフィック合成などを行うと処理が間に合わない。 フレームバッファ方式はこの点で有利である。
しかしながら、カラーパレットの変更などで全画面のデータを書き換える必要が出てくると、実機よりかなり遅くなってしまう。 また HSYNC 毎にデータを書き換える処理(ラスタースクロールなど)が実現できなくなる。
CPU
CPU 自体のエミュレータは一番肝な部分であるが、開発するのは大変なので出来合いの物を使用することとする。 しかしながら、github などで公開されているものでも、バグが含まれているものが多く結構難儀である。
実際 6800 や Z80 では CPU エミュレータ由来のバグが発生して、別のコードに入れ替えている。
I/O など
このころのレトロPC では、各CPUに対応した周辺 IC を使っていることが多い。 全機能をまともに実装すると大変なので、一部の機能のみ実装することで手抜きをする。
入出力のモード設定などは、一度設定したら決め打ちなので、その辺は省略できたりする。
割り込みなど
一部の割り込みは真面目に実装しないと動かない。 キー入力やタイマーで割り込み処理が行われているものは多い。
CPU のエミュレータによって割り込みのやり方がまちまちだったり、そもそも実装がまずかったりすることもある。
キーボード
CH32V203 も Pico も USB ホスト機能が使えるので、基本的に USB キーボード対応としている。 USB HID では、同時に5つまで押されているキーの情報が入手できるので、これをエミュレータ上のキーボード入力に変換する。
レトロPCにおけるキー入力の方法は主に、キーマトリックス方式とキーコード方式の2つがある。
キーマトリックス方式はパラレルI/Oに直接キーボードの信号が接続されている方式である。 この場合は USB のキーコードと、キーマトリックスの変換テーブルを使って変換を行う。 物理キーの変換ができれば、SHIFT や Control などの装飾やキーリピートなども、エミュレートされたマシンが処理してくれるので楽である。
BML3 のキー入力はかなり変態ではあるが、キーマトリックス方式の一種とみなせば同様に処理できる。
一方のキーコード方式は、キー入力用にサブCPUなどを使って、押されているキーの情報を入手する方式である。 この場合は、SHIFT などの装飾を含めてエミュレータで処理を行う必要がある。 またキーリピートが存在する場合にもエミュレータで実装する必要がある。
メモリマップ
CPU のエミュレータはメモリ空間に(Z80 なら I/O 空間も)アクセスするための関数を用意しているので、 その関数に実際のメモリを割り当てる。
バンク切り替えなど複雑な処理が行われている場合も多い。
VRAM
メモリマップと合わせて重要なのが VRAM である。 レトロ PC では変態構成のマシンが多いのでいろいろ面倒である。
- 直接 VRAM にアクセスできない (Pasopia/MSX など)
- 色情報などを持ったアトリビュートに直接アクセスできない (BasicMaster シリーズ)
- アトリビュートとデータが一対一対応していない (PC-8001 など)
- テキストとグラフィックの優先順位が変えられる (MZ シリーズ)
- スクロールレジスタによって物理メモリの位置が変わる (FM シリーズ)
初期のレトロPCでは、VRAM アクセスするのに水平同期信号を待ってる場合も多いが、 この辺の処理は省略する。
テープ
レトロPCにおける主要な外部記憶装置はテープなので、テープの入出力は実装しないとどうしようもない。
テープ入出力に UART を使っている場合は単純であるが、一般的にはソフトウェアで波形を制御しているので、 CPU の消費クロックを計算してエンコード&デコードの処理を行う必要がある。
ファイル
エミュレータに対して、データを渡す方法として、フラッシュの空き容量を使う方式を採用した。 Pico の場合、エミュレータに 300KiB くらい使うので、残り 1.5MiB くらいが空いており、 代表的なソフトをいくつか保存する程度であれば十分すな容量がある。
フラッシュをファイルシステムとして使うために LittleFS を使用した。 ただ、フラッシュの空き部分に書き込むために、openocd を使って SWD 経由で書き込む必要がある。
サウンド
初期のレトロPCだと beep が標準である。 単に PIO のポートをたたいているものは、GPIO の出力に変換する。 タイマーで出力しているものは、PWM 出力などに変換する方がきれいな音になる。
PSGについては前に CH32V 用に作ったコードがあるので、それを流用する。 DAC出力した方がきれいな音になるが、部品数削減のために PWM で出力する。
FM 音源になると、Pico のフルパワーを消費することになりそうなので、現状は見合わせている。