本作には音楽が 1 つしかない (音楽ID 0)。
音楽仮想マシンはバイトコード方式で、以下の状態を持つ:
また、音楽仮想マシンは APU レジスタ $4000-$400F
に書きたい値を専用のバッファ $0790-$079F
に書き込むことができる。オーディオドライバは効果音との調停や APU レジスタ値キャッシュの内容を考慮してバッファ内の値を実際に書くかどうかを決める。
オーディオドライバが(再)初期化されると、音楽仮想マシンのウェイトカウンタは 0 になる (つまり、仮想マシンが停止する)。
オーディオドライバに対して音楽の再生要求が行われると、音楽仮想マシンは以下のように初期化される:
オーディオドライバにより音楽仮想マシンが tick されると、以下のことが起こる:
音楽バイトコード命令は 1 バイトのオペコードと 0..=2
バイトのオペランドからなる。つまり、命令長は 1..=3
バイトである。
仮想マシンの 1 ステップ実行はウェイトカウンタ設定命令が実行されるまで続く。
命令セットは以下の通り:
オペコード | オペランド | 概要 |
---|---|---|
0..=0x1F | ウェイトカウンタをオペコードの値に設定する。 1 ステップ実行を完了する。 オペコード 0 は音楽の終了を意味する (が、これは実際には現れず、音楽は無限ループする)。 | |
0x20..=0x2F | value: u8 | APU レジスタへ value を書き込み要求する。書き込み要求対象アドレスは 0x4000 + (オペコード下位 4bit) 。 |
0x33 | dst: ptr | アドレス dst へジャンプする。 |
0x34 | func: ptr | サブルーチン func を呼ぶ。 |
0x35 | サブルーチンから呼び出し元へ戻る。 | |
0x36 | n: u8 | n 回のループを開始する。 |
0x37 | ループ継続/終了。 | |
0x40..=0x7B, 0x80..=0xBB, 0xC0..=0xFB | 矩形波/三角波の発音/消音要求。 |
以下の命令は未使用で、実装もスタブ的なものとなっている:
オペコード | オペランド | 概要 |
---|---|---|
0x30..=0x32, 0x38..=0x3E | 値 (オペコード) & 7 をアドレス $C3 に書き込む (write-only)。実質 nop。 | |
0x3F | 全ての矩形波/三角波チャンネルの周波数レジスタ $4002, $4006, $400A (u16le ) に対して値 0xFF00 を書き込み要求する。 |
オペコード 0x7C..=0x7F, 0xBC..=0xBF, 0xFC..=0xFF
は不正である。実装上は矩形波/三角波の発音/消音要求として扱われるが、実行すると周波数タイマー値テーブルの範囲外を参照してしまう。
0x34 <func:ptr>
: サブルーチン呼び出し次の命令のアドレスを戻りアドレスとしてスタックに push し、アドレス func へジャンプする。
0x35
: サブルーチンからの復帰スタックから戻りアドレスを pop し、そのアドレスへジャンプする。
0x36 <n:count>
: n
回のループを開始スタックに n
をループカウンタとして push し、さらに次の命令のアドレスをループ開始アドレスとして push し、実行を継続する。
なお、n == 0
の場合は 256 回のループと解釈される (が、実際にはそのようなケースは現れない)。
0x37
: ループ継続/終了スタックトップにあるループカウンタをデクリメントし、
0x40..=0x7B, 0x80..=0xBB, 0xC0..=0xFB
: 矩形波/三角波の発音/消音要求まず対象 APU チャンネルを決める。オペコード 0x40..=0x7B
は矩形波A, 0x80..=0xBB
は矩形波B, 0xC0..=0xFB
は三角波が対象となる (オペコードの bit6-7 を見ている)。
そして、オペコードの bit0-5 が発音/消音指示となる。値 59 は消音を意味し、値 0..=58
は発音すべき音名を意味する。これは 12 平均律の F2 から半音ずつ高くなる構成であり、値 0, 1, 2, ... は音名 F2, F#2, G2, ... に対応する (ただし APU の特性上、高音域になるほど精度は悪くなる)。
消音指示の場合、対応する APU チャンネルに周波数タイマー値 0 を書き込み要求する。発音指示の場合は音名に対応する周波数タイマー値を書き込み要求する。
NOTE: 三角波を消音する際も周波数タイマー値 0 を書き込むため、プチノイズが発生しうる。
NOTE: 実装コードを見ると、発音/消音の際に書き込む length counter 値を NMI カウンタ $C4
に基づいてランダムに決めているようだが、これは特に意味を持たないと思われる (本作の音楽は全ての矩形波/三角波チャンネルが音長無限設定になっているため)。