6309エミュレータを書いたのでMB-S1に載せてCMOCでクロス開発してみた
では、自作の6309エミュレータの検証を試みたのですが、実質6809としての検証に終わりました。
そこで、以前から興味があったNitrOS9で検証することにしました。
NitrOS9は、6809/6309でマルチタスクをサポートするOS-9に由来するOSです。

NitrOS9/Level2/6309を試せるのは日本では馴染みのないTandy Color Computer 3だけのようなので、
Macで使えるエミュレータとして以下のOVCCを選び、自作6309エミュレータに載せ替えます。

https://github.com/WallyZambotti/OVCC

ここのREADMEにも書いてありますが、macOSの場合は
普通にmakeして実行すると、キーフォーカスがOVCCのウィンドウではなくターミナルに残ったままになります。
そこでXcodeプロジェクトを作る際、Command Line ToolではなくAppを選ぶ、但しNSApplicationMain()は呼ばないという変則的な対応をしました。

困ったのはそれくらいで、自作コアは6809としては動いていたので、上記MB-S1のときと同様にデバッグを進め、いくつかのバグを修正してまともに動くようになりました。
最適化もしたのでソースとしてはだいぶ変わりました。

インストール

まずlibagarをインストールします。

https://github.com/JulNadeauCA/libagar

から(ReleasesではなくCodeの)ZIPファイルをダウンロードし、
./configure
make depend all
sudo make install
とします。
SDL2もインストールします。
brew install sdl2
OVCCをcloneします。
git clone --recursive https://github.com/kwhr0/OVCC
今回は、いろいろなアプリが入ったハードディスクイメージで提供されていて、すぐに使えるNitrOS9 Easy Of Useで試します。

http://www.lcurtisboyle.com/nitros9/nitros9.html
の下の方の
Version 1.0.0/6309 (released December 3, 2022) を展開した中から63EMU.dskと、
Version 1.0.0/6309 with GIME-X (released December 3, 2022) を展開した中から63GIMEX.VHDを
OVCC/copy下にコピーします。また、

https://github.com/WallyZambotti/OVCC
のREADME.mdに示されているサイトのバイナリに含まれるcoco3.romとdisk11.romをOVCC/copy下にコピーします。

実行

OVCC.xcodeprojを開き、Product->Scheme->Choose Scheme->Edit Scheme->Info->Build ConfigurationでReleaseを選んでビルドします。
RunしてBASICの画面が出たら
DOS[return]
と入力するとNitrOS9が立ち上がります。
Time ?
で[return]を押すと
{Term|02}/DD:
とプロンプトが出ます。

クロス開発

6809のクロスコンパイラCMOC

http://perso.b2b2c.ca/~sarrazip/dev/cmoc.html

をインストールします。
また、OS-9フォーマットのディスクを読み書きするために

https://github.com/hathaway3/toolshed

をインストールします。

まず、cmoc_os9ライブラリをビルドします。
cd OVCC/cmoc_os9
make
その後、OVCC/test下でmakeすると、ブートフロッピーに実行ファイルgを書き込みます。
/d0/g[return]
で実行します。
以下の三種類のデモが選べます。

星とバキュラ
色とりどりのドットと金属っぽい板が画面奥から飛んでくるデモです。
簡易的な射影変換、光源処理、Zソート、ラスタライズをしています。
また、ウィンドウデバイスを2つ開き、ダブルバッファリングしています。
ウィンドウの切り替えはVSyncまでブロックされ、ちらつかないと同時にスピードを制限されます。
starbacura.cのDOUBLE_BUFFERの定義をコメントアウトすると描画と表示が同じウィンドウになり、ちらつきますが最大のスピードが出ます。
また、CMOCが吐く6809のライブラリ呼び出しで特に時間がかかる除算を6309のDIVQ命令で高速化してみました。

MIDIプレイヤー
エミュレータに矩形波、三角波、鋸波を選択可能な32音ポリPSGを実装しました。
/DD/MIDI下にSMF0のMIDIファイルを置いておけば、実行時に選択して再生できます。
カーソルキー左右で選択、[return]で演奏開始(ディレクトリの場合は下の階層へ)です。
[esc]で演奏中なら停止、停止中でトップディレクトリでなければ上の階層へ、さもなくば終了します。
左端の文字を押すと、そのトラックをON/OFFできます。

マンデルブロ
6309のアセンブラを書くのが楽しいので、低精度浮動小数点数ライブラリを作ってマンデルブロ集合を描いてみました。
低精度とは、乗除算でMULD,DIVQ一回で済むよう、仮数部のビット数を減らしたという意味です。
CMOCは便利で、最初はCで書き、少しずつインラインアセンブラに置き換えていって、最終的にはほぼアセンブラのみまで持っていけます。
変数や引数は:を付けることで簡単にオペランドに指定できます。
# この指定をすると実際にはindexed addressingに変換されます
ライブラリは、四則演算と整数のロードを実装し、512バイト以内に収まりました。
浮動小数点数のロードは無いので、内部表現に変換するldf.cを用意しました。
ホスト側であらかじめ変換し、ゲスト側のソースに埋め込みます。

テキスト版は以下のページのソースと等価で、結果も同様になります。

http://haserin09.la.coocan.jp/asciiart.html

グラフィック版は320x192の各ドットで計算します。
M1 Macで6309/600MHz相当の速度が出るので、画面全体で4秒以内で済みます。

NitrOS9メモ

サンプルを作る過程で分かったことを記します。
ページ表記は以下のマニュアルの該当ページを表します。

NitrOS-9 Technical Reference Manual v0.1 (Boisy Pitre)

実行ディレクトリ
NitrOS9は、実行ディレクトリを指定するコマンドがあります。サンプルの場合、
chx /d0[return]
で実行ディレクトリを変更しておくと、
g[return]
だけで実行できます。
実際に開発するときはソースを編集してmakeし、OVCCに切り替えて
[esc]を押して終了、[home]で元の端末に戻し、g[return]で再度実行できます。
# [home]を押す必要があるのはプロンプトが{W1|05}/DD:のように、{Term|02}でない場合だけです

キーボード入力
デフォルトでは、/TERMを開いてI$Readすると、エコーバックされます。
# I$やF$ではじまる文字列はシステムコールを表します
エコーバックなしにするには、I$GetSttして、該当バイトを0にしてI$SetSttします。
I$GetSttで得られるデータはp.47のPD.OPTの32バイトで、Sequential Character Fileの場合の内訳はpp.71-73にあります。
PD.EKOが該当バイトで、Relative Addressは$24、PD.OPTは$20からなのでI$GetSttしたときのオフセットは4です。
また、I$Readを呼ぶと、入力されるまで返ってこないので、入力がないとき待たないようにするにはI$Readする前にI$GetSttにSS.RDYを渡して調べます。
/W(ウィンドウデバイス)を開いた場合も同様の操作ができます。

ディレクトリエントリ
NitrOS9のディレクトリエントリは32バイトで、29バイトが名前、3バイトがLSN(Logical Sector Number)です。
名前は最後の文字のMSBを立てることで終端を表すので、MSBが1の文字コードは使えません。
また、エントリの属性(ファイルかディレクトリか等)はありません。
各ファイルの先頭セクタがfile descriptorで、そこに書かれているのですが(p.51)、
ふつうにI$Openするとそれは読み飛ばされてファイル本体から読まれるので、どうやってアクセスするのか不明です。
MIDIプレイヤーでは指定した名前でchdirできるかどうかで判定しています。

シグナルの補足
F$Icptでできます。
NitrOS9では、ESCキーでkeyboard terminateシグナルが発出されるので、サンプルのトップメニュー以外では終了しないようにしてみました。

I/O
今時のOSでは、I/Oはユーザからは直接見えないのですが、CoCo3/NitrOS9では、$ff00〜$ffffに見えています。
なので、I/Oの増設は簡単にできます。
MIDIプレイヤーでは、PSGを実装してみました。

sleep
NitrOS9では、きめ細かいスリープができないようです。
次のVSyncまでスリープしたいとき、F$Sleepに1tick(VSyncの間隔)を渡せばいいのかなと思ったのですが、これだと即座にactive process queueに入ります。
他に実行するプロセスがないとVSyncを待たずに実行順が回ってきます。
かといって2ticksを渡すと多くの場合2つ先のVSyncまで待たされます。
そこで、VSyncカウンタを作って読めるようにし、MIDIプレイヤーではyieldしながらのループでそれを監視し、正確なタイミングを取るようにしました。
ただし、ほぼビジーループなのでホストの負荷が高くなります。
MIDIプレイヤーでは6309のクロックを30MHz程度に落とすといいでしょう。

snd.hでLOW_LOADを定義するとF$Sleepを2ticksとし、クロック設定が高くても負荷が高くならないオプションも用意しました。
ただし、MIDIの処理が2VSync間隔になるのでちょっと大雑把になります。

メモリ
cmoc_os9ライブラリで提供されているmallocは3回くらい呼ぶと返ってこなくなるようです。
そこで、簡易なallocを作ってメモリ確保しています。
これを使って調べたところ、ユーザが使えるのは0xdeffまでのようです。
ウィンドウはタイプ8だと2枚で64kバイト使うのですが、これは別空間に確保されます。
こういうところはMMUありのOSのありがたいところです。反面、直アクセスはできないですが。

エミュレータの変更点

DisplayのAllow Resize
OFFにするとウィンドウをリサイズできなくなる代わりに少し軽くなるようにしました。

クロック設定
オリジナルの標準クロックはカラーバーストを元にしているので端数がありますが、1MHzにしました。

キーボード
シフトキーが効かない不具合を修正しました。NitrOS9お試し用なので、専用キーマップ以外は削除しました。
ホストのキー配列はUSを想定しています。

マウス
gshellでマウスカーソルがずれる不具合を修正しました。
CoCo3の標準マウスはX/Y方向ともに64段階しかないようなので細かい場所は指定できません。

MMUを自動選択に
Intelは高速なハードウェアMMUに、Apple SiliconはNitrOS9のページサイズ8kバイトより大きいのでソフトウェアMMUを選択します。

既存の6809/6309を削除
自作6309エミュレータ検証用なので、それ以外は削除しました。

DisplayのThrottleSpeedについて
ON: ホストのVSync間隔で処理落ちしないクロックに自動調整します。
OFF: 指定したクロックで実行し、処理落ちした場合はフレームレートが低下します。

https://github.com/kwhr0/OVCC

macOS10.13以上
Xcode10以上
Intel/Apple Silicon