今まで各種のCPUエミュレータを書いてきたのですが、Z80だけは90年代に書き始めた古いままです。
他のソースと雰囲気を合わせたいなぁ。
しかし、PC-8001の色々なゲームで検証済みだし、これはこれで残しておきたい。
あるとき、MSXは今でも色々な新規格が考えられていることを知り、中でもR800の700MHzというのが目に止まりました。
そういえばR800ってあったなぁ、
ということで、他のソースと雰囲気を合わせたR800エミュレータを作りました。
といってもクロックカウントをR800にして乗算命令を追加しただけで、DMAやMMUは入っていません。
アプリ例として、レトロCPUには辛いJPEG展開を試します。
V9990には192x240ピクセルの画面モード(B0)があるようです。アスペクト比の関係で2ラインずつ同じデータを表示すると仮定し、192x120ピクセルで試します。
ffmpegで生成した連番JPEGファイルを、ChaNさんのfatfsで読み込み、picojpegでデコード、連続的に表示するプログラムを作りました。
書き上げて最初に走らせたときは、白っぽいブロックノイズだけのような画面が出ました。
まあ、書いただけで正常に動作することなんて滅多にありません。
ソースレベルデバッガは無いので、とりあえずprintfデバッグをしていきます。
picojpeg.cのhuffCreateという関数が怪しい、というところまでデバッグが進み、そこにprintfを入れたとき...
なんと正常な画像が出ました。
え?
printfを削除すると元の壊れた画像に...
あ、これ面倒なやつだ
OKの場合とNGの場合でどう違うか、アセンブルリストを調べます。
call _printfと、その周辺だけ違うと思いきや
NGの場合はmMaxCodeを計算する部分が全然違うものになっていることが判明。
これコンパイラのバグでは?
SDCCのR800ターゲットは比較的最近追加されたので、十分あり得ます。
huffCreate関数だけ取り出して、テストプログラムを作ったところ、明らかにおかしい出力を得たので、バグレポートを提出しました。
https://sourceforge.net/p/sdcc/bugs/3873/
#15630で修正が反映されました。
実行
現行のSDCC4.5.0では正しくコンパイルできないので、
スナップショットから#15630以降のバージョンをインストールします。
ソースからビルドする場合は、以下のスクリプトでconfigureすれば最小限のインストールができます。
https://github.com/kwhr0/sdcc-config-script
FATファイルシステムのディスクイメージを作成し、ルートディレクトリにMJPEGディレクトリを作成しておきます。
ffmpeg -i 動画ファイル -vf "scale=192:120:force_original_aspect_ratio=increase,crop=192:120" -q:v 1 MJPEG/img%03d.jpg
でファイルを作り、ホームディレクトリにfat.dmgという名前で置きます。
https://github.com/kwhr0/movieplay-R800
からCodeのZIPファイルをダウンロードします。
unzip movieplay-R800-master.zip
cd movieplay-R800-master
make
./emu800 c/a.bin
結果は、18fpsでした。再生にかかった実時間とクロックカウントから計算したR800のクロックは1GHzちょっとでした。
# Mac mini M1をMac mini M4 Proに買い替え、どのくらいのスピードが出るか試したくてR800エミュレータを作ったというのもあります...
タイムプロファイラの結果です。
21.1% _mullong (SDCC)
11.5% idctCols (picojpeg.c)
11.0% huffDecode (picojpeg.c)
7.9% idctRows (picojpeg.c)
7.1% decodeNextMCU (picojpeg.c)
5.8% pjpeg_decompress (jpegdec.c)
5.7% getBits (picojpeg.c)
5.7% upsampleCb (picojpeg.c)
5.4% upsampleCr (picojpeg.c)
5.3% getBit (picojpeg.c)
2.1% subAndClamp (picojpeg.c)
2.0% copyY (picojpeg.c)
1.8% addAndClamp (picojpeg.c)
1.3% huffExtend (picojpeg.c)
1.0% clamp (picojpeg.c)
1.0% imul_b1_b3 (picojpeg.c)
_mullongが異常に重い。
ライブラリのソースsdcc/device/lib/_mullong.cを見てみると、R800はZ80と同じ扱いで、新設された乗算命令が使われていません。
そこでIDCTで使う乗算をアセンブラで書き、再度実行しました。
結果、23fpsに向上しました。
14.2% idctCols (picojpeg.c)
13.9% huffDecode (picojpeg.c)
10.0% idctRows (picojpeg.c)
8.9% decodeNextMCU (picojpeg.c)
7.6% pjpeg_decompress (jpegdec.c)
7.0% getBit (picojpeg.c)
6.9% upsampleCb (picojpeg.c)
6.6% getBits (picojpeg.c)
6.5% upsampleCr (picojpeg.c)
4.6% imul_b5 (sjpeg.s)
2.6% copyY (picojpeg.c)
2.4% addAndClamp (picojpeg.c)
2.3% subAndClamp (picojpeg.c)
1.5% huffExtend (picojpeg.c)
1.1% clamp (picojpeg.c)
負荷配分も納得がいくものです。