VGMフォーマットを解析する部分は簡単ですが、問題なのはVGMデータをどのように持つか 、ということになります。デバッグ時のテストでは
const unsigned char vgmdata[]= {0x56, 0x67, 0x6d, 0x20,・・・・・・};
という具合に配列としてコードに埋め込んでしまいましたが、曲の差し替えができるようにBSS領域にバッファを確保して、シリアルからダウンロードするようにしました。
メモリ配分は以下のように仮置きし、バッファ領域はできるだけ大きく0x08000~0x1FFFFの96kbyteとしています。
VGMプレーヤのメインルーチンはこのような感じになります。核になるパーサ処理vgmplay()を終了コードを検出するまで常時コールし続けるだけです。vgmplay()はイベントごとにgetByte()でバッファから1バイトコマンドを取り出すと同時にポインタを進めます。
今回はVGMの解析もFM音源の制御も既存のものを参考にしているので難しいところはないのですが、悩み、つまづいたのがまたしてもx86CPUにおけるセグメントの壁でした。
このプログラムはMSCのスモールモデルでコンパイルしていますが大きいVGMデータをアクセスするためにメモリ上の特定位置に流し込んでポインタでキャストした実アドレスで参照しています。このポインタを最初、
unsigned char far *vgmaddr;
というようにfarポインタで宣言していましたが、どうも動作がおかしいのです。64kバイトを超えるようなファイルを転送すると1周してバッファ先頭部分を壊してしまいます。farポインタはセグメントとオフセットの両方の情報を保持しているので1Mbyeまで連続アクセスできると思い込んでいたのですが、インクリメントする場合はオフセット部分しか評価せず、セグメント境界を超えるアクセス(セグメント部分への桁上げ)はできないようです。まあ考えてみれば当たり前なのですが。ROMに焼き付けているモニタプログラムのloadコマンドもfarポインタを使っていますがスモールモデルでコンパイルする限りコード・データ合わせて64kbyteの制約があるので問題にならなかったわけです。
MSC/C++6.00Aでは幸いヒュージポインタをサポートしているのでこれを使えばリニアなアクセスができます。ポインタは
unsigned char huge *vgmaddr;
と書き換えるだけです。