PCEエミュレータを高速化しよう

iアプリPCエンジンエミュレータであるixpceのCPUエミュレーションコアは、xpceのソースコードに含まれていたM6502という有名な6502コアをJavaへ移植したものです。
短期間でそれなりの速度と品質でC言語からJavaへ移植するために、Cソースを機械的Javaへ変換していきました。
例えば、C言語のマクロで書かれた部分は、Visual C++プリプロセッサを一旦通して、すべて展開してしまう等です。

結果、まあまあPCEのソフトが動くiアプリ用のエミュレータがそれほど労力をかけず、1ヶ月ぐらいでできたわけです。

しかし、その機械的に移植したCPUコアは、Java向けに最適化されたコードになっているかというと、いまいちなわけです。

まだまだ最適化して、実行速度を向上させる余地があることは、移植の作業中からわかっていました。
そのときは、とにかくそれなりの品質で移植しちゃうことを優先させたわけです。
もっと高速にできるよなーとは思ったのですが、具体的にどんなコードにすれば、もっと速くなるか、具体的なプランが見えていたわけではありませんでした。

今になって、しばらくぼんやりとixpceのM6502.javaを眺めていたら、最適化のプランが見えてきました。
現状の問題点は、PCEのメインメモリにアクセスするためのコストがかなり高いということです。
プログラムカウンタが指す位置のメインメモリから1バイト読み出すためのコードは、以下のような感じです。

ram[((page[(wk1 = (pc++)) >> 13][wk1 - (wk1 & msk)] & 0xFF))] & 0xFF

PCEはバンク切り替えでアクセスできるメモリを増やしているため、メモリマッピングレジスタ(pageという変数がそれ)を介してアクセスするアドレスを計算してやる必要があります。
これが見ての通り、結構複雑な処理となっております。
メモリアクセスは、全ての種類の命令実行で発生することなので、上のようなコードがixpceのCPUエミュレータにはかなり大量に書いてあります。
速度のため、全部をインライン展開したからです。

これをどうにかできないか?というところから考え始めました。
毎回、メモリマッピングレジスタを介してアクセスするのはしんどいです。
ならば、リニアにメモリアクセスできるように、バンク切り替えに従って、メモリの内容をコピーしてしまえばいいかもしれません。
ひとつのバンクサイズは8KBなので、バンク切り替えのたびに、8KBのメモリコピーを行うわけです。
そうすれば、

ram[pc++] & 0xFF

というコードで、メモリアクセスできるようになり、かなり単純化されます。
反面、バンク切り替え時のオーバーヘッドが大きくなりますが、バンク切り替えの頻度がそれほどではないなら、そのオーバーヘッドは無視できるかなーと思っています。


こんな感じで、CPUコアを書き直そうと思います。


ついでに、CPUコアのバグも、キレイにFIXしたいですね。
今考えているのは、品質的に信頼できるPCエンジンエミュレータのソースから、バリデーションコードを起こすことです。
Ootakeが良さそうです。
OotakeのCPUコアのソースをちょっと改造して、レジスタの取りうる値の全ての組み合わせに対して、その演算結果をデータとして出力できるようにします。

ixpceも同様のデータを出力できるようにして、それがOotakeの出力データと一致すれば、バグが無いことが確認できます。
けっこういい作戦だと思います。