6309を書いてみると、6502ってどんなだったろうと気になります。
以下のマニュアルと65C02で拡張された部分の資料を見ながら書いてみました。

http://archive.6502.org/books/mcs6500_family_programming_manual.pdf

http://6502.org/tutorials/65c02opcodes.html

登場時期が古い割に、アドレシングと実行内容の組み合わせだけで表現できる命令が多い、つまりは直交性が高いことに感心しました。

6502には有名なクロスコンパイラがあります。

https://github.com/cc65/cc65

ざっとテストコードを書いて、このコンパイラが吐くコードでデバッグしました。

そして、載せ替えてみるエミュレータを探したのですが、そういえば国産8ビットパソコンには採用例がないような。
# ファミコンを除く
ということで今回はAppleIIエミュレータに載せます。
ソースが公開されていてmacOSで走った実績があるCatakigを選びました。

http://catakig.sourceforge.net

これをOS X Lion対応にした以下のものをベースにします。

https://github.com/aufflick/Catakig

まずはIntelマシンで試します。
Architecturesのi386をx86_64にして、Header Search PathsにSource/LibAppleIIを追加することで無事走りました。
次はApple Siliconです。
まず、オーディオ周りの古いコードが通らなくなっていたので書き換えます(main.m)。
これで走るには走ったのですがブートしません。
デバッグを進めると、ディスクイメージが読めていないことがわかりました。
ふとログを見て、
Warning: VM page size = 16384 (> 0x2000)
と出ているのが気になりました。

さらに調べると、1トラック分8192バイトごとにmmap()する仕組みでした。
4kバイト単位のIntelマシンでは動いていましたが、Apple Siliconは16kバイト単位なのでエラーになっていました。ああ、だからあのwarning...
対策として、フロッピー1枚分まとめてmmap()するようにしました。
これでApple Siliconでもちゃんと走るようになりました。

では、6502を入れ替えます。
C++ Language DialectをC++11に変更し、W65C02.cppをプロジェクトに追加してビルドしてみると、エラーが出ます。
エラーを見ると、どうもC++としてコンパイルされていません。
原因はCompile Sources AsがObjective-Cになっていることでした。
ソースが何であってもObjective-Cとしてコンパイルする。こんなオプションがあったなんて。
これをAccording to File Typeに変更すると、今度はNSObjCRuntime.hなど、システムヘッダでエラーが出ます。
インクルード元がPrinting.l、lexのソースですね。
ログを見ると、lexの出力がCのソースになるためでした。
だからどんなソースでもObjective-Cとしてコンパイルしてたんだ。
それなら拡張子を.mにすれば...ってどうやるんだ?
色々検索すると、拡張子を.lmにすればいいことが判明。これはちょっと難易度高めでした。

次に自作6502の方を呼ぶように変更していきます。
CPU.mで命令の処理をしていたのですが、この中でCPU-RW.hをリードとライト別々に2回インクルードし、I/Oと一体化したコードになっていました。
これをRW.mmに分離しました。CPU.mm(C++のコードを呼ぶので拡張子を変更)から自作6502を呼び、そこからのアクセスはRW.mmの方を呼ぶ仕組みです。

あとはMB-S1のときのようにProcessCompare.hを使って1命令ずつ比較して違いがあればトレースダンプ、デバッグを繰り返します。

この作業はもう慣れたので割とすぐまともに走るようになりました。
山場は一体になったMPUとI/Oを分離するところでした。

cc65によるクロス開発

今回初めて知ったのですが、AppleIIってカラー画面が出せるのにモノクロのVRAMしか無かったんですね(HiResグラフィック)。
1ドットごとにON/OFFするとカラーバーストと同じ周波数になり、4種類の位相を作れるようになっていました。
NTSCの仕様を巧みに使っています。
また、古い設計なのにVRAMダブルバッファが使えるんです。
画面下部4行だけテキスト、残りはグラフィックという変わったモードもあります。
cc65のライブラリがどこまでサポートしているか不明で、AppleIIの仕様を把握したかったというのもあってI/OやVRAMに直アクセスするデモを作りました。

まず、cc65をインストールします。
$ brew install cc65
逆アセンブラも入れておきます。cc65をインストールすると逆アセンブラda65も入るのですが、サイクル数が表示される以下のものを使いました。

https://github.com/tcarmelveilleux/dcc6502

ダウンロード、makeしてできたdcc6502をパスの通ったところに置いてください。
次に、

https://prodos8.com

からProDOSのディスクイメージを取得します。
ディスクイメージの中で必要なのはPRODOS(SYS)だけなので、それ以外を削除します。
MB-S1エミュレータのページにあるL3ディスクエクスプローラが、さまざまなレトロパソコンのディスクイメージを扱えて便利です。

http://s-sasaji.ddo.jp/bml3mk5/download.htm#l3diskex

そして、/opt/homebrew/share/cc65/target/apple2enh/util/loader.system
(Intelマシンだと/usr/local/share/...)
をT.SYSTEM(SYS)という名前でコピーし、ディスクエクスプローラを終了します。
このディスクイメージをa.dskという名前にしておきます。

あと、makeするときにコマンドラインからディスクイメージを書き換えるツール、Apple Commanderが必要です。

https://github.com/AppleCommander/AppleCommander/releases

からAppleCommander-ac-*.jarをダウンロードし、ac.jarという名前にしておきます。
これ、Javaで書かれているので
$ brew install openjdk@11
でインストールします。

以上で準備ができました。

https://github.com/kwhr0/Catakig-sample

からソースをダウンロード、展開、makeするとバイナリがディスクイメージに書き込まれるのでエミュレータで読んで実行します。
このとき、機種はApple//eにしてください。
HiResデモは、標準速度用にパーティクルが少なめになっています。
#define STAR_N 256
にしてスピードを上げると大量に飛んできます。試してみてください。

cc65の注意点

volatileが効かない
キーワード自体はエラーにならないのですが、効力がありません。
上記サンプルのclockpos()が不自然に関数になっているのはその対策です。

複雑な式は危険
*p = *p & ~m | ctbl[gattr & 7][o & 1] & m;
では動かず、
d = ctbl[gattr & 7][o & 1];
*p = *p & ~m | d & m;
にしたら動いた事例がありました。
volatileの件もそうですが、怪しいと思ったらアセンブルリストまで確認したほうがいいかもしれません。

エミュレータの変更点

今回も、自分の用途に合わせてエミュレータに手を加えました。

ホストのfpsに合わせた
オリジナルは30fpsだったので、ホストのフレームレートに合わせました。
標準的なモニターだと60fpsになります。

バックグラウンドでも実行
オリジナルはウィンドウにキーフォーカスがないとき一時停止します。
これを、実行を続けるように変更しました。サウンドの生成はキーフォーカスがあるウィンドウだけです。

起動シーケンスの省略
オリジナルは起動してcommand+Nを押す、モデルとRAM容量を選ぶ、ディスクイメージを選ぶ、何かキーを押す
という操作で動き出しますが、クロス開発時に面倒なので
モデル、RAM容量、ディスクイメージパス、ついでにスピードを保存することでアプリを起動したらディスクをロードしてキー入力なしで動き出すようにしました。
動かなくなった場合に困るのでshiftキーを押しながらだとモデル/RAM選択ダイアログからになります。
また、ウィンドウがキーフォーカスを得たときディスクイメージが更新されていたら読み直してリブートします。
クロス開発時にmakeしてCatakigに切り替えるだけで新しいバイナリを試すことができます。

400倍速まで追加
40倍速、80倍速、200倍速、400倍速を追加しました。
高速だとサウンドがノイズにしかならないので、新たに追加した速度ではサウンド以外のスレッドで実行します。
ウィンドウごとに別スレッドにしているので、Intelでは物理コア数、Apple Siliconでは高性能コア数までは複数ウィンドウでも1枚のときの最高速のまま動かせます。

https://github.com/kwhr0/Catakig

表層にROMsフォルダを置き、Catakig.xcodeprojを開いてビルド、実行してください。