MSXは標準で3チャンネルのPSGを発生させることが出来ます。この制御はLSIのレジスタに値を書き込むことによって行ないます。BIOSにそのためのルーチンが用意されています。
【参考】1章 PSGと音声出力 - MSX Datapack wiki化計画(外部サイト)
void wrtpsg( unsigned char reg, unsigned char val )
{
#asm
LD IX, 2
ADD IX, SP
LD E, (IX) ; val
LD A, (IX+2) ; reg
CALL 0093h ; WRTPSG
#endasm
}
void main()
{
wrtpsg( 8, 10 ); // 音量
wrtpsg( 0, 0xac ); // 音階「ド」(L)
wrtpsg( 1, 0x01 ); // 音階「ド」(H)
wrtpsg( 7, 0b00111110 ); // 出力開始
for( int i = 0; i < 10000; i++ ){} // 音の長さ
wrtpsg( 0, 0x7d ); // 音階「レ」(L)
wrtpsg( 1, 0x01 ); // 音階「レ」(H)
for( int i = 0; i < 10000; i++ ){} // 音の長さ
wrtpsg( 0, 0x53 ); // 音階「ミ」(L)
wrtpsg( 1, 0x01 ); // 音階「ミ」(H)
for( int i = 0; i < 10000; i++ ){} // 音の長さ
wrtpsg( 7, 0b00111111 ); // 出力停止
}
PSGを鳴らすためには、PSGレジスタへの書き込みを複数回行なう必要があります。ですので、まずはその処理を関数化しておきます。
wrtpsg()関数は、PSGレジスタへ値を書き込む関数です。引数にはレジスタ番号と値を渡します。中身は全てアセンブリ言語で、BIOSコールのためのコードのみとなっています。
Z88DKのC言語でインラインアセンブラを使用する場合、関数の引数はスタックから取得することが出来ます。関数の引数が複数ある場合、後ろから格納されます。
AレジスタにPSGレジスタ番号、Eレジスタに書き込みたい値をセットして0093hをCALLすると、PSGレジスタへ値が書き込まれます。
PSGレジスタ8でチャンネルAの音量を制御します。bit0-3で音量を指定します。bit4はエンベロープ使用フラグで、0:使用しない、1:使用する、です。
PSGレジスタ0~5が、PSGチャンネルA~Cの音階に割り当てられています。
分周比を12bitで指定することによって音階を変化させます。例えばチャンネルAであれば、PSGレジスタ0に下位8bit、レジスタ1に上位4bitを指定します。
書き込むべき値の計算式は、以下の通りです。
111860.78125 / 周波数
例えば、「ラ」の音(440Hz)を鳴らしたい場合、111860.78125 / 440 = 254(0FEh)ですので、PSGレジスタ0に0xfe、PSGレジスタ1に0x00を書き込みます。
PSGレジスタ7で各チャンネルの出力を選択します。bit0-2で、チャンネルA-Cそれぞれのトーン出力を指定します。0:ON、1:OFFになります。bit3-5はノイズ出力の指定です。
#025 【MSX,Z88DK】C言語でBeep音