• サウンドドライバに求められる機能
これから数回に分けて、Z80等のアセンブラでのサウンドドライバの作り方について解説していきます。最初は理屈から、…という事で、まずはゲーム内で演奏されるサウンドドライバに求められる機能ですが、私は以下の優先度で考えています。※ 異論は認めます。
  1. 高速動作
  2. 省メモリ
  3. 音質
音楽再生でゲームの実行はどうしても阻害されます。その影響範囲を少しでも少なくするため、ドライバは高速動作が最重要だと考えています。昔作成したゲームでは、ADPCM等もフルサポートした結果、CPUを20%近くも使ってしまいました。これは本末転倒であると今では考えています。音質は良くて当然としても、実行速度の向上に励むのが大事だと思います。

続いて省メモリ。Z80のメモリ空間は64KBしかありません、ROMカセットなら、割り込み発生時に切り替えてしまえばこの問題はある程度解決するものの、テープロードが大半でゲーム稼働中はオンメモリが前提となる PC-8001やMZ-80K等では、サウンドドライバとデータが閉める割合は決して小さくありません。そこで、ドライバそのもののサイズを小さくする工夫に加えて、データサイズも小さくする必要があります。

そして音質です。これは往々にしてドライバの機能アップに比例して、音質は向上していきますが、残念ながら実行速度や省メモリが犠牲になっていきます。どこまでの音で許容するのか、この線引きが後々まで尾を引くことになります。そのため、なるべくゲーム制作の早い段階で仕様を決めておくべきでしょう。


  • サウンド再生対象のハードウェア調査
ドライバを動かすハードウェアの調査は重要です。当然、ハードにより出来ることと出来ないことがあるためです。PC-8001 + PCG8100 の場合は、音量変更が出来ません。故にトレモロの実装は不可能です(擬似的に出来ない事はないですがゲーム中に鳴らすのは辛すぎます)。また PCG8100 の初期型は再生チャンネルが1つしかありません。初期型もなるべく対応したいです。

PCG8100 の音は PSG ではなく ビープ音(チップは 8253)です。音階の変更が出来るビープです。ハードウェア要件を確認すると

 * 音程
 * キーのオン/オフ
 * 3チャンネル(1チャンネルも考慮)

このような単純な内容となります。MSX や PC-6001 等に搭載されている PSG AY-3-8910 の場合は上記に加えて

 * 音量
 * エンベロープ
 * ノイズ

と、設定できることが多くなります。FM音源はさらに多くなりますが、趣旨から外れていきますのでこの辺りで(苦笑)。以下、3チャンネルがあるハードウェアという前提で話を進めます。


  • 呼び出し頻度
サウンドドライバの発音処理が呼び出される速度を確認します。ハードウェア割り込みがある機種だと、比較的自由に呼び出し速度を決められると思いますが、画面の垂直帰線割り込みを利用する場合は 1/60 が最高速となります。PC-8001 の場合は割り込みがないので、さらに厄介です。タイミングを合わせて自分で呼び出してやらねば鳴りません。


PC-8001 では垂直基線割り込みの状態変化を確認して、裏から表に行ったタイミングで処理をするようにすると良いでしょう。垂直帰線が裏側にある瞬間だけ、CPUはクロック通りの速度で動作しますので、もし、高速に処理するとかノイズが出ないように配慮する処理が無ければ。サウンドドライバの呼び出しを裏に回しても宜しいかと思います。

ハードウェア割り込みがある機種の場合、調子に乗って 1/1000 で処理する等と割り込み速度を上げると、ゲーム本体の進行速度に影響が出ます。この辺りはバランスですので、ご自身が判断する必要があります。



  • サウンドドライバの全体の流れ
まずはフローチャートをご覧ください。
SNDTimer
私の場合、サウンドドライバは全体的な処理部 SNDTimer と、チャンネル毎の処理である SNDPlayer に分けて設計します。これは、チャンネルが増えても呼び出しを増やすだけで済みますし、ワークが連続していなくても対応出来る等のメリットがあります。

フローチャート内の汎用カウンタとは、ゲームの全体進行やビブラートやトレモロ等の時系列管理のために使用します。さほど処理時間を食うワケでもないので、このように単純にインクリメントしておきます。SNDPlayer 呼び出しの際は、各チャンネル毎のワークエリアの先頭を引数としてレジスタ渡ししています。

全部でハードウェア的には3チャンネルしかありませんが、仮想4チャンネル目としてSE再生専用を用意しています。実際の発音処理では、効果音が鳴っていれば、3ch(または2ch)を SEチャンネルに置き換えて再生する事で、フルチャンネルで音楽演奏しながら効果音も鳴らせるのです。
SNDPlayer
実際の処理部であるSNDPlayerの簡易的な構造説明用フローです。ワークエリアとして、現在処理しているコマンドデータアドレス(以下ポインタと略す)があり、それが 0x0000 なら再生していないと判断しています。続いて、今演奏されている音がいつまで鳴り続けるのかという音長カウンタを参照して、ゼロ以外であればデクリメントして現状を維持します。

音長がゼロなら次の発音のための処理に入ります。ポインタから1バイトデータを取得します。同時にポインタは+1更新します。終了コマンドであれば、データアドレスなどを 0x0000 クリアして終了します。コマンドが音階であれば、発音カウント値の取得、設定、音長カウンタ値の初期化を行い、ポインタもワークに格納して終了します。そして、コマンドがそれ以外であれば、その他コマンドの種別判定と処理を行い、再びコマンド取得ループに戻ります。このサンプルフローでは、終了と音階以外には音長しか用意していませんが、ここに音楽性に拘るのなら、ビブラート等のコマンド拡張を行います。

 第1回:ASM サウンドドライバの構造