Raspberry Pi Pico VGA 出力について
現在レトロPCエミュレータで使っている VGA 出力ライブラリは、 Hunter Adams さんの VGAライブラリ を使っているのですが、いろいろ改変しているので、ここでまとめます。
基本原理
基本原理はオリジナルの解説を読んでください。
大まかには PIO 部分と DMA 部分に分かれています。
- H-SYNC 出力用 PIO
すべての基本。H-SYNC 信号を生成して、定期的に IRQ を発生させます
- V-SYNC 出力用 PIO
H-SYNC からの IRQ をカウントして、定期的に V-SYNC 信号を発生させます。表示期間の間は、RGB 信号出力をスタートさせます。
- RGB 出力用 PIO
V-SYNC からの表示開始信号をうけて、1ライン分のデータを転送します。 8bit のデータを FIFO から拾って、4bit づつ 2 回出力します。(8色モードの場合)
- データ転送用 DMA
フレームバッファから全画面分のデータを転送します。 データ転送が終わったら、DMA Chain で再設定用 DMA を起動します。
- 再設定用の DMA
データ転送用 DMA のコントロールブロックを書き換えて、再設定します。 終わったら DMA Chain でデータ転送用 DMA を起動します。
これによって、PIO と DMA だけで VGA 信号の出力が可能になります。
変更その1
国産レトロPC の場合、大抵 640x400 なり 640x200 なりの解像度が使われています。 そのままでは 640x80/2 = 25KiB の非表示部分のデータが無駄になってしまいます。
ということで、縦の解像度を 400 ラインとする改造を行いました。
V-SYNC 出力用 PIO を改造すればいいのですが、
- 1 PIO ユニットで使用可能な命令数は 32
- PIO ではイミディエイトは最大 31
という制限が PIO にはあります。元のコードではすでに 32命令使用しているので、何らかの方法で命令数を削減しないといけません。
H-SYNC PIO でクロック調整している部分が以下のようになっています。
; SYNC PULSE
pulse:
set pins, 0 [31] ; Low for hsync pulse (32 cycles)
set pins, 0 [31] ; Low for hsync pulse (64 cycles)
set pins, 0 [31] ; Low for hsync pulse (96 cycles)
; BACKPORCH
backporch:
set pins, 1 [31] ; High for back porch (32 cycles)
set pins, 1 [12] ; High for back porch (45 cycles)
irq 0 [1] ; Set IRQ to signal end of line (47 cycles)
.wrap
ここを削減するために、H-SYNC PIO の動作クロックを半分にします。それによってウエイト用の命令を2個削減できます。
いっぽう V-SYNC PIO で追加しないといけない数字は 80 です、フロントポーチ・バックポーチに分けても 2命令でおさまりません。 ここは FIFO でパラメータとして渡して、PIO 内部のレジスタに保存することにしました。 FIFO からレジスタに保存するのに 2命令使うので、ちょうど収まります。
変更その2
最初に作ったのが 400 ライン必要な BasicMaster Level3 だったので、これでよかったのですが、 以降 200 ラインしかない機種ばかり作っていました。
同じデータを 2ライン分描画することで対応していたのですが、 PC-6001mk2 で拡張メモリ分の RAM が確保できないという問題が出てきました。
今度は 200 ライン専用にして、フレームバッファの使用メモリを削減することにしました。 幸い PIO の構造をあまり変更しなくても、DMA の変更でなんとかなりそうなことがわかりました。
- データ転送用 DMA のデータ転送を、全画面ではなく 1 ライン分で終了させる
- 再設定用 DMA で、各ラインの先頭アドレスを設定する
- 空白部分には空データを、表示部分には 2ラインごと同じデータを設定する
- 再設定用 DMA は ring モードにすることで、フレームが変わっても常に同じ内容が表示されるようにする
DMA のリングモードは 2 のべき乗である必要があるので、データ転送期間を 512 ライン分とするように V-SYNC PIO を変更しています。(というか元に戻しているのか?)
これによって、フレームバッファの使用量は半分になり、表示アドレスデータの分を引いても 60KiB の節約が可能になりました。