8ビットの時代はZ80しか書いたことがなく、6809ってどんなMPUだったのか今更ながら興味を持ちました。
6809には、日立が魔改造した6309というバリエーションがあります。
以下の資料を見ながら書いてみました。
https://github.com/ChetSimpson/KAOSTools/blob/master/Reference/6x09_Instruction_Sets.pdf
とりあえず、CMOCという(ほぼ)Cコンパイラが吐くコードでざっとデバッグします。
http://perso.b2b2c.ca/~sarrazip/dev/cmoc.html
6809アセンブラの流儀も学べます。
例えば、サブルーチンから帰るときにレジスタを復帰するPULSでPCも復帰することでRTSを使わないとか。
CPU/MPUごとに異なるテクニックがあって面白いです。
そして、px68kのときのように試しに載せ替えてみるエミュレータを探しました。
国産パソコンで6809といえばまずはFM-7でしょう。
ところが、ソースが公開されていてmacOSで走った実績があるものは見つかりませんでした。
国産でFM-7系以外だとベーシックマスターだな、と思って探すと、以下のページが見つかりました。
http://s-sasaji.ddo.jp/bml3mk5/
S1のエミュレータもあったので、こちらに載せてみることにしました。
El CapitanとHigh Sierraで動作確認済みのようなので、まずはIntelマシンで試します。
Homebrewで必要なライブラリをインストールしていったのですが、ffmpegでエラーが出ました。
画面録画をしなければ不要らしいのでその辺りをコメントアウトし、無事走りました。
次に、Apple Siliconのマシンで試したところ、描画されません。
まずSDL_RenderPresent()を呼ぶところを調べると、OpenGLを使う場合は呼ばれないようになっていました。
ということでUSE_OPENGLの定義を削除し、エラーが出る箇所を修正すると画面も出ました。
その後自前の6309に入れ替えます。
px68kのときと同じようにプロンプトが出るまでトレース結果を比較して修正していきます。
ここまでは割と早かったのですが、下記のゲームを試したところ、キーを押すとエラーで止まってしまう現象が発生しました。
http://blog-s1.sblo.jp/article/107647174.html
px68kのときは異常があるアプリをautoexec.batに登録してブートから異常が出るところまでトレースを取って比較することでまともに走る状態に持っていけました。
ところが、今回は(割込みで処理される)キー入力が必要で、単純なトレースではデバッグできません。
そこで、次のような仕組みを作りました。
従来のMPUに1命令実行させ、メモリアクセスのログをとります(R/Wそれそれ複数回対応)。
次に載せ替えるMPUでも1命令実行します。
リードは先程のログと順に同じアドレスにアクセスしていることを確認して先ほど読んだデータを返します。
再びリード動作をするとそれがI/Oだった場合異常動作につながるかもしれないからです。
ライトも同様、アドレスとデータの比較だけで実際には書き込みません。
これを実装したのがCompareProcess.hです。
これで不一致を検出したらそこまでのトレースを出力して終了します。
この仕組みで楽々デバッグできました。
ところで、残念ながら6309のネイティブモードでは走りませんでした。
ネイティブモードでは(一部を除く)割込みとSWI/SWI2/SWI3命令でWレジスタもスタックに積みます。
これによりPCを積んだ場所のオフセットが変わるのでシステムコールのSWI2命令で正しいパラメータを読めなくなるのです。
CMOCによるクロス開発
ただ走っただけでは物足りないので、今回はCMOCでクロス開発してみました。
お題はMIDIプレイヤー。
S1は拡張含め6音ポリなので、一般のMIDIファイルを演奏すると音数が足りません。
また、単音色/モノラルであることから、キーオン時に(ピッチベンドを含む)音程が同じでプログラムナンバやパンが異なるノートは最大音量のものだけ残すように調整しました。
まず前掲のサイトからCMOCと、アセンブラが別になっているのでリンク先からLWTOOLSをダウンロードします。
まずLWTOOLSを展開し、
$ make
$ sudo make install
します。
次にCMOCを展開します。ライブラリは、特定の機種向けのものではなく、USIM(シミュレータ)用のものを使います。
EXECコマンドで実行して帰ってくるために、src/stdlib/crt.asmにパッチを当てる必要があります。
IFDEF USIM
LEAS 4,S for MB-S1
RTS for MB-S1
SYNC to leave usim
また、macOSでエラーを回避するためにsrc/Makefile.inの
AM_YFLAGS = -d -Wno-conflicts-sr
を
AM_YFLAGS = -d
に変更します。
(2022/03/20更新)
Makefile.inのパス名を修正
そして
$ ./configure
$ make
$ sudo make install
します。
逆アセンブラも入れておきます。
https://github.com/Arakula/f9dasm
準備ができたら
https://github.com/kwhr0/mid2l3
をダウンロード、makeして
$ ./mid2l3 <midi file>
でl3ファイルを生成します。
これをエミュレータで読むと演奏が始まります。
使用できるMIDIファイルはSMF0のみです。
MIDIトラック10は非対応です。
エンベロープは非対応ですが、発音中のボリューム、エクスプレッション、ピッチベンドの変化は対応しています。
1倍速ではちょっともたつきます。
処理が間に合わなかったときは+マークが表示されるので、あまり表示されなくなるくらいにスピードを上げます。
高速にしてもテンポを保つために、「CPUスピードと同期」はオフにします。
終わりまで演奏するか、何かキーを押すと終了します。
参考資料:
http://s-sasaji.ddo.jp/bml3mk5/tape.htm
http://s-sasaji.ddo.jp/bml3mk5/s1basicwork.htm
https://seesaawiki.jp/mb-s1/
CMOCのライブラリにはprintfなども含まれていますが、うまく表示できなかったので以下のxprintfを使っています。
最終的には1文字表示にしか使っていませんが、デバッグ中は有用です。
http://elm-chan.org/fsw/strf/xprintf_j.html
エミュレータでは好きな時にテープのデータを読みに行けるので成り立っていますが、実機ではディスク版に移植するなどしないと正常に動かないと思われます。
しかし、CMOCによるクロス開発は実機をお持ちの方にも参考になると思います。
CMOCは"C like"なのでいくつかクセがあります。
式の評価中、intに昇格しない
char a, b;
int c = a * b;
C言語ではaとbはintに昇格してから乗算ですが、CMOCでは昇格しないので明示的にキャストする必要があります。
関数内でstatic変数を定義できない
仕方がないので関数外に出します。
(2022/03/20更新)
CMOC0.1.76で可能になりました。
switchの中でcontinueを使えない
C言語ではswitchの外側のループにcontinueできますが、CMOCではエラーになるのでラベルを定義してgotoします。
他に、エミュレータに加えた変更は以下の通り。
CPUスピードに32,64,128倍速を追加
M1 Macでは64倍速でもCPU負荷100%に達することなく60fps出ているので128倍速までメニューに追加しました。
負荷が100%に達すると急激にフレームレートが落ちますが、画面->フレームレート->60fpsにすると改善します。
終了のキーボードショートカット
command+Qで終了するように変更しました。
負荷の軽減
インストラクションフェッチは(I/Oなどではない)通常のメモリに限定し、負荷を軽減しました。
MEMORY::read_data8w_fetch()の部分です。
テープの自動更新
最初だけl3ファイルをドラッグ&ドロップすると、ウィンドウがアクティブになったときファイルが更新されていたら読み直します。
クロス開発時、外部で更新したらウィンドウをアクティブにしてF1,F5で実行できるようになります。
https://github.com/kwhr0/MB-S1
Homebrewでsdl2とsdl2_ttfをインストールし、
source/Xcode/mbs1_cocoa2/mbs1.xcodeprojを開いてビルドしてください。