tsutsuiの作業記録置き場

NetBSDとかPC-6001とかの作業記録のうち、Twitterの140字では収まらない内容や記事としてまとめるべき内容をとりあえず置いてみる予定

「Tinyみずいろ」にBGMを追加するまで(1)


前回の予告通り Tinyみずいろに BGMを追加するまでの作業についてぼちぼち書いてみます。

Tinyみずいろ実行時メモリマップ

BGM を追加するには BASIC の PLAY文では正直無理で、何かしらの PSG 演奏ドライバを足してやる必要があるだろうと思われました。ということで、まずは Tinyみずいろ実行時の空きメモリがどれくらいあるかを調べてみました。

wav データ修復作業の過程でざっくりとした構成は把握していたので、あとは実際に未使用の領域がどれくらいのオーダーで残っているのかというところなのですが、ざっとまとめると左の図のような感じでした。

BASIC プログラム本体も 6k バイトと結構なサイズがあります。これは、マシン語ルーチンのデータのほか、デモ中のテキストメッセージもすべて DATA 文として入っているためです。

グラフィック表示とテープロードのマシン語ルーチンが置かれる 9F00h のアドレスは BASIC の CLEAR 文で定義されているアドレスです。それより前の空き領域はスタック使用量や BASIC 文字列領域等々で変動しますし、 BGM 追加のために BASIC プログラム本体を修正した場合も使用量が変わるので、とりあえずは見積もりからは除外します。

グラフィックデータは SCREEN 4 の 256×192 モノクロの VRAM データとして 1枚あたり 6k バイト必要で、無圧縮の状態では 6k×5枚 = 30k バイトとなりそのままでは到底入りません。が、 以前のエントリ で説明したようなランレングス圧縮により 1枚あたり 3k バイト前後まで小さくなっていて、実際のメモリ上のサイズは合計 15k バイト程度と約半分になっています。

グラフィックデータの最後から VRAM までの領域がまとまった空き領域ですが、数字としては 992 バイトで 1k バイトもありません。グラフィックデータとマシン語ルーチンの間のすき間のデータを加えても +179バイトです。BASIC 使用領域の空き部分を CLEAR 文をいじって詰めるとしてもせいぜいあと 256バイト程度で、がんばっても 1400バイトくらいが未使用領域として残っているメモリ容量でした。

もっとも、残り容量がギリギリなのはよく考えれば当たり前ともいえます。もともとのデモ作成の過程において 5枚のグラフィックをオンメモリに載せる必要があったわけで、そのためには圧縮展開処理だけでなくグラフィックデータに対するモノクロ化ディザやトリミング等の加工においても様々な試行錯誤が行われたであろうことは想像に難くありません。

オリジナルのデモに BGM が無かったというのはこのメモリ制約も要因の1つと思われました。

音源ドライバ

次に、初代P6 でも使える PSG音源ドライバについて調べてみました。

まず思いつくのは、私自身が P6 をいじりだすきっかけとなった TINY野郎さんのイース2オープニングデモ で使われていた「よっしゅさんのPSG音源ドライバ」です。

よっしゅさんのPSG音源ドライバ

よっしゅさんのページ の P6ページ How many pages? 内の PSG音源ドライバの説明を読むと、

となかなかにハードルが高かったりします。

しかし、「完全に割り込みにする」というコンセプトと、 BASIC の EXEC文による演奏開始停止ルーチンのコールだけで使える、というところは
「既存の BASIC プログラムに BGM を追加する」
という今回の目的にはぴったりという感じでした。

よっしゅさんのPSGドライバ用MMLコンパイラ

「TINY野郎さんはよっしゅさんの PSG 音源ドライバをどうやって P6初代で動かしたのだろう」ということで TINY野郎さんのページ を見ると、 DEEP! P6! DEEP! MSX! のページに
「【Download】よっしゅさんのPSGドライバ用MMLコンパイラ
というダウンロードリンクがありました。

実は、以前からこのリンクは気づいていたものの、独立した解説ページがなかったので中身を見るのをサボっていたのですが、前項に書いた
MML を BASIC のコメント文として記述する」
MML → 演奏データのコンパイルも P6 上で実行」
の部分を Windows 上で実行するためのプログラムです。

以下配布の readme.txt から引用:

よっしゅさん作成「PC-6001用PSG音源ドライバ」用のデータを、PCで作成するツールです。

PSG音源ドライバを使う場合、

1)PSGドライバをディスクから読み込む
2)REM文でMMLを書く
3)exec&He000でコンパイルする
4)exec&hf100で演奏
5)exec&hf103で演奏停止

という流れでしたが、この過程の2)3)の部分をPCで行うものです。

「どうやって P6初代で実行するのか」というところですが、同じく readme.txt を見ると MMLコンパイラには以下の機能もあることがわかります。

◆「ドライバー+音楽データ」ボタンをクリックすると、MMLコンパイル後のバイナリーデータに加えてPSG音源ドライバ本体のバイナリーデータまでBASICのDATA文にしたものを出力します。
・よっしゅさん作成のPSG音源ドライバのディスクイメージが必要です。
・ドライバ本体もDATA文になっているので、N60-BASICや、ディスクドライブの無い環境でも演奏できます。

つまり、

  • よっしゅさんの PSG音源ドライバ自体は P6初代でも動作する
  • 配布形態が mk2 以降でサポートされる 1D フロッピーのイメージであるため
    「対象機種は mk2 以降」と表記されている

ということだと思われます。

P6初代でもオプションの拡張 BASIC ROM があればフロッピーも使えるようですが、 1S のみなのか 1D も使用可能なのかそのへんの仕様をよくわかっていません……

PSG音源ドライバテスト

MMLコンパイラの配布 zip にはサンプルとしてイース2オープニングデモで採用されていた「TO MAKE THE END OF BATTLE」の MML データも付属していたので、早速よっしゅさんのページからディスクイメージをダウンロードした上でサンプル MML から BASIC プログラムを作って P6実機で実行してみました。 操作としては

という手順で行いました。

が、"MUSIC DATA READ" が終わった後もなぜか音が鳴ってくれません。

とりあえず調べるには実機ではなにかと手間がかかるので、エミュレータで詳細を確認することにします。最初からエミュを使えと怒られる

ちなみに、 PC6001V や PC6001VW には「打込み代行」の機能があり、 BASIC プログラムのテキストファイルをドラッグ・アンド・ドロップすればそのままエミュレータ上でファイルの内容をキーボード入力してくれるので、特に txt2bas での変換操作をせずとも実行が可能です。

打ち込み機能はリアル(?)にゆっくり入力されるので、 設定をウェイト無効の最速状態に変更 (PC6001V であればメニューの「設定」→「ウェイト無効」をチェック) したほうがよいです


が、PC6001V 上でもやはり鳴ってくれません。

ドライバのソースを調べようかと思ったのですが、せっかちに PC6001V のトレース機能を使って動作を見ると、 EXEC &HF100 で演奏ルーチンにエントリ飛んだ後、実際の演奏ルーチンの先頭で F00Ch のアドレスの値をチェックしてそれがゼロでなければリターンするようになっていました。


実際に よっしゅさんの PSG音源ドライバのソース を見ると確かに F00Ch をチェックしているのですが、それ以外に F00Ch を操作しているところがありません。

うーん、と MMLコンパイラ側のソース を見ると、こちらは F00Ch に対する書き込み (ソース上の記載は "(MAINWK+12)") があります。さらに ドライバの詳細資料 の方を見るとワークエリアの F00Ch は「コンパイルラーフラグ」であることがわかりました。


音源ドライバマニュアルページ の「演奏開始」項には「コンパイルでエラーが発生した場合は演奏されません。」とあります。つまり、コンパイルを実行せずにいきなり演奏データを直接読み込んだ場合には、明示的に F00Ch に 0 を書いて「エラーなし」の状態にしないと起動時の RAM の初期値によってはエラーと判定されて動かない、ということのようでした。


というわけで、とりあえず MML2P6PSGDRV により生成された BASIC プログラムのデータロードから演奏開始までの間に F00Ch の 0 を書き込むコードを足してやると無事演奏が開始されるようになりました。

YS2OP曲 MML改造

MMLコンパイラにサンプルとして付属している YS2OP の MML は、実際のデモ同様に 1回の演奏が終わるとそこで終了するようになっています。が、「せっかくだから P6実機を作業用 BGM演奏マシンにしよう」ということで MMLコンパイラの練習がてらサンプルデータをいじって OP曲を繰り返し再生するようにデータを修正してみました。


よっしゅさんの PSG音源ドライバの MML では "J" が「演奏データが最終まできたらこの地点まで戻る」なので、とりあえず 3ch の各パートの先頭に J を追加してみたのですが、これだけではうまくいかないという罠が。

というわけで、 SAMPLEMML.txt の以下の行を赤字部分のように修正すればとりあえずループ再生可能になります。

1010 "D Jt11,3v15s-3,1,-3,0,0M12,2,1,4l16r4
1400 "D l16o5drrdrrdd8&[d48&u-8(]8u%0r1
2010 "E Jt11,3v15s-3,1,-3,0,0M12,2,1,4l16r4u%-1
2400 "E l16o4a<d>da<ga>aa8&[a48&u-8(]8u%0r1
3010 "F Jt11,3v14p2s-10,1,-2,0,0l16w15rc32c32w8c8s-3,1,-1,0,0p1w18
3400 "F p2l16p2w15cw8ccw15cw8ccs-10,2,-1,0,0p1o4cp2w15c8.r16r24r1

PSG音源ドライバ組み込みへの道

だいぶ話が脱線しましたが、「BASIC プログラム実行中でも Ok プロンプトが出ている最中でも文字通りバックグラウンドで BGM再生可能」ということは確認できたので、Tinyみずいろへの PSG音源ドライバの組み込みについて考えてみました。

MMLコンパイラにより生成される DATA 文中の PSGドライバは F100h〜F77Fh までを使用しています。また、 F000h〜F0FFh までがドライバのワークエリアです。よって、ドライバ全体のサイズとしては 1919バイト、つまり 2kバイト弱が必要ということになります。


BGM再生のためには MML コンパイルにより生成された曲データそのものを置く領域も必要です。そこでサンプルの YS2OP 曲のデータサイズを見てみると、あれだけの各種効果が盛り込まれているにもかかわらず C000h〜C551h つまり 1361バイトと、思ったよりは小さなサイズに収まっていました。ただしこれは YS2OP曲がループを効果的に組み合わせて構成されているということも考慮に入れる必要があるかも

Tinyみずいろの BGM については、都合よく PSG化された MML データが落ちているわけもなく「自分で耳コピして MML 化するしかないかなあ」とぼんやり考えていた段階でした。なのでサイズの見積もりをする術もなかったのですが、とりあえずえいやで 1〜2k バイト必要だとすると、ドライバと合わせて 3〜4k バイトは必要ということになります。

今回のエントリの始めの「Tinyみずいろ実行時メモリマップ」で確認したとおり、現状の Tinyみずいろ実行時の空きメモリ は 1k バイト強しかありません。曲データを 1kバイトで仕上げたとしても 2kバイトの不足です。


削ると行ってもなかなか簡単ではありません。とりあえず思いつくところでは

  • BASIC の DATA 文について複数行の記述を 1行にまとめる
  • その他の行も極力マルチステートメントを使う

といった案がありましたが、作業やデバッグが面倒そうな割に、キロバイト単位で空きができるとも思えませんでした。

32kバイトの RAMのうち、ワークエリアや VRAM を除いたフリーエリアは 23kバイトで、 その 1割に相当する 2kバイトを空けるとなると、「現状 15kバイトを占めている画像データを (ランレングス圧縮よりも効率の良い方法で) 圧縮する」というくらいしか手がなさそうでした。

が、データ圧縮をしたら当然展開ルーチンも必要です。ランレングス圧縮の場合は展開ルーチンは10数バイトで実装できますが、新たな展開ルーチンのサイズが圧縮効率改善により空いたサイズより十分小さくないとそもそも別の圧縮ルーチンを持ってくる意味がありません。

というわけで、MMLコンパイラの一連のツイートをしていた 6月18日の時点では「BGM 組み込みはなかなか厳しそうだ」という気持ちになりつつありました。

実際には動画の説明にもあるとおり圧縮ルーチンを差し替えることで PSGドライバを組み込むことができたわけですが、次回はその圧縮ルーチン組み込みについて書こうと思います。