BasiliskII

68k独自コアも68020に近いところまで実装したし、68kMacに載せてみたい。
ということで、FPUなしで68020だけを設定可能なBasiliskIIに載せてみることにしました。

まず、メモリとI/Oのアクセス周りを調べます。
デバッガで追ってみると、不思議なことにロード・ストアの全てがメモリアクセスで、I/Oにアクセスしている節がありません。
さらに調べると、I/Oデバイスのエミュレートは実装されておらず、
68kに独自の擬似命令EMULOP #<imm>を追加し、それが実行されるとROM Toolboxに相当するさまざまな処理を行っていることがわかりました。
その命令は起動時にROMにパッチを当てることで使われるようになっていました。

アクセスの全てがメモリ扱いならかえって楽かと思ったら、ちょっと厄介なことをしていました。
EMULOP実行中に、現在のステートを保存してサブルーチン的な68kコードを実行したりしていたのです。
従来のMPUと比較しながら実行するとき、EMULOPの中身は二度実行すると正しい動作ができません。
そこで、EMULOP実行中(ネストすることもある)は比較をしないことにしました。
EMULOP外で十分にデバッグできれば、EMULOP内のコードは正常に実行できるだろうと踏んだのです。

デバッグを進めていって、初めて画面が出るとちょっと感動するものです。
X68000のHumanのプロンプトまでは約一千万命令でしたが、MacOSの起動完了までは一億命令程度でした。

M1 Mac上でMacBench2によりProcessorテストをすると、68LC040/33MHzを基準として元のUAEコアでは20.9倍速、独自コアでは16.4倍速でした。
昔仕事で書いたプログラムのビルド時間を調べると、元のUAEコアでは16秒、独自コアでは19秒でした。
コンパクトさでは優っていますがスピードは敵いませんね。

SheepShaver

こちらはPowerMacintoshのエミュレータです。
そういえばPowerPCのアセンブラって馴染みがないなーと思って以下のマニュアルを見てみました。

https://www.nxp.com/files-static/product/doc/MPCFPE32B.pdf

ユーザレベルのレジスタ構成だけ見ても、ちょっと変わっています。

MSB側がビット0
整数のコンディションコード4ビットを8組持っている
他にXERレジスタに3ビットのコンディションとストリング命令用のループ値
ループカウント用のCTRレジスタ

BasiliskIIを独自コアに置き換えたので、PowerPCの勉強がてらこっちも置き換えてみようかなーと、書き始めました。
PowerPCの仕様をフル実装するのは大変そうですが、
SheepShaverはMMUを使わず、FPUも演算は標準の演算子まかせ、発生する例外は実装しなくて大丈夫そうなので、それほど難しくなさそうです。

SheepShaverも基本はBasiliskIIと同じで、I/Oアクセスはなくメモリアクセスのみ、EMULOP命令と同様のことをSHEEP命令が行なっていました。
ちょっとびっくりしたのは、PowerPCの割込みやトラップなどの例外処理を使っていないことです。
以前からSheepShaver上ではCodeWarriorのデバッガが使えないなーと思っていたのですが、例外処理をエミュレートしないのでは無理もありません。

実装を終えてデバッグを進め、System7.5.3が起動するところまできました。
しかし、MacOS8.1以上だと起動中に落ちてしまいます。
現行のKPXコアとの違いが全く検出されないのに、新プロセッサ単独に切り替えると落ちるのです。
BasiliskII同様、SHEEP命令実行中は比較をしていなかったのですが、
SHEEP命令の中から呼ばれるPPCルーチンの比較も追加してみました。それでも、不一致は検出されません。
落ちるまでのトレース結果を見ると、浮動小数点レジスタに入っている値をストアし、そこから+2バイトのデータをロード(結果的に16ビット右シフト)したものを実効アドレスとしてアクセスして落ちるという、訳のわからなさです。
自分が書いたプログラムのデバッグであれば正しい動作を想定できますが、OSの起動中という、正しい動作が不明な状態のデバッグは難度が高いです。

途方に暮れてマニュアルとSheepShaverのソースをじっくり読み返したところ、Dキャッシュの扱いに問題を発見しました。
Dキャッシュは実装しないので、NOP扱いにしていたのですが、dcbz命令はそれではダメだったのです。
マニュアルに従い、算出した実効アドレスを含むキャッシュブロックに相当するメモリを0クリアすることで、MacOS8.1以上でも無事起動するようになりました。
このバグだけで4日かかりました。PowerPCのマニュアルを見ながらざっくり書き上げるまでも4日でしたから、なかなか歯応えがあるバグでした。

M1 Mac上でMacBench3によりProcessorテストをすると、PowerMacintosh6100/60MHzを基準として元のKPXコアでは11.9倍速、独自コアでは12.7倍速でした。
拙作StillMovie(Carbon版)のビルド時間は元のKPXコアでは85秒、独自コアでは91秒でした。
処理内容により速い/遅いがありますがほぼ互角といってもいいのではないでしょうか。

1000行足らずのプロセッサエミュレータでクラシックMacOSが動くというのもちょっと不思議な感じがします。

https://github.com/kwhr0/macemu