/*	
 *	clipdms.c
 *
 *	P/ECE DMG-Sound Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Thu Jan 15 20:09:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Mon Feb 14 20:30:00 JST 2005 Naoyuki Sawa
 *	- 正常なレジスタ書き込みに対してDIE()してしまっていたのを削除。
 *	* Fri Feb 25 20:40:00 JST 2005 Naoyuki Sawa
 *	- 発声開始時以外にもチャネルパラメータを変更できるように修正しました。
 *	  これまで、NRx4への書き込みによる発生開始時のみチャネルパラメータを設定し、
 *	  発生終了までパラメータを変更しないように実装していましたが、誤りでした。
 *	  NRx0-NRx3およびD7=0でNRx4へ書き込むことで、発声中でもパラメータ変更可能\です。
 *	- dmgsound_read()関数を追加しました。
 *	  これまで、アプリケーションプログラムからサウンドレジスタの読み出しは、
 *	  メモリを直接読み出していましたが、今後はdmgsound_read()関数を呼んでください。
 */
#include "clip.h"

/****************************************************************************
 *	メモ
 ****************************************************************************/

#if 0

========
スイープ
========

SweepTime設定値は0〜7です。
SweepTime=0の場合、スイープなしです。
SweepTime=1の場合、128/1[Hz]で出力周波数を更新します。
SweepTime=2の場合、128/2[Hz]で出力周波数を更新します。
...
SweepTime=7の場合、128/7[Hz]で出力周波数を更新します。

128[Hz]は基本入力クロック4194304[Hz]の1/32768です。
スイープ回路の入力クロックは、基本入力クロックの1/32768分周と考えます。

--------
実装方法
--------

経過サイクル数を蓄積し、SweepTime*32768を超えたら出力周波数を更新します。
毎回SweepTime*32768と比較するので、sweep_timeにはあらかじめSweepTimeを32768倍(15ビット左シフト)した値を格納しておきます。
こうしておけば、経過サイクル蓄積値とsweep_timeをそのまま比較するだけで済みます。
↓
スイープ進行カウンタはダウンカウンタとしました。

============
エンベロープ
============

EnvelopeTimeの設定値は、0〜7です。
EnvelopeTime=0の場合、スイープなしです。
EnvelopeTime=1の場合、64/1[Hz]でエンエロープ値を更新します。
EnvelopeTime=2の場合、64/2[Hz]でエンエロープ値を更新します。
...
EnvelopeTime=7の場合、64/7[Hz]で出力周波数を更新します。

64[Hz]は基本入力クロック4194304[Hz]の1/65536です。
エンベロープ回路の入力クロックは、基本入力クロックの1/65536分周と考えます。

--------
実装方法
--------

経過サイクル数を蓄積し、EnvelopeTime*65536を超えたら出力周波数を更新します。
毎回EnvelopeTime*65536と比較するので、envelope_timeにはあらかじめEnvelopeTimeを65536倍(16ビット左シフト)した値を格納しておきます。
こうしておけば、経過サイクル蓄積値とenvelope_timeをそのまま比較するだけで済みます。
↓
エンベロープ進行カウンタはダウンカウンタとしました。

==================
出力周波数(トーン)
==================

出力周波数Fout、基本入力クロックFin、出力周波数設定値fの関係は次の通りです。

	Fout = Fin / 32 / (2048 - f)

トーンのデューティ比設定は{1/8,2/8,4/8,6/8}なので、1波形8ステップと考えます。
トーンの再生を8サンプルPCMの再生と見なすと、再生速度Fstepは次の通りです。

	Fstep = Fin / (32 / 8) / (2048 - f)
	      = Fin / 4 / (2048 - f)
	      = Fin / (2048*4 - f*4)
	      = Fin / (8192 - f*4)

--------
実装方法
--------

frequencyにはあらかじめf*4(左2ビットシフト)の値を設定しておきます。
※(8192-f*4)を設定しておきたいところですが、スイープによってfが更新されるのでダメです。
　スイープは率で変化するので、あらかじめ4倍しておくのは大丈夫です。

f*4をfrequencyに置き換えると、

	Fstep = Fin / (8192 - frequency)

経過サイクル数を蓄積し、(8192-frequency)を超えたら波形ステップを進めます。

	経過サイクル数蓄積: 初期値=0
	...
	経過サイクル数蓄積 += 経過サイクル数;
	while(経過サイクル数蓄積 >= 8192 - frequency) {
		経過サイクル数蓄積 -= 8192 - frequency;
		波形ステップ++;
	}

これは次のように置き換えられます。

	経過サイクル数蓄積: 初期値=0
	...
	経過サイクル数蓄積 += 経過サイクル数;
	while(経過サイクル数蓄積 - 8192 >= -frequency) {
		経過サイクル数蓄積 -= 8192 - frequency;
		波形ステップ++;
	}

さらに、

	経過サイクル数蓄積: 初期値=0
	...
	経過サイクル数蓄積 += 経過サイクル数;
	while(8192 - 経過サイクル数蓄積 <= frequency) {
		経過サイクル数蓄積 -= 8192 - frequency;
		波形ステップ++;
	}

ここまでの処理を図示します。

	           8192-frequency
	 |                | frequency |
	0|----------------|-----------|8192
	 *====*====*====*====>
	    *====*====*====>
	  *====*====*====*====>

さて、(8192-経過サイクル数蓄積)を、初期値8192からのダウンカウンタと見なすと、

	ダウンカウンタ: 初期値=8192
	...
	ダウンカウンタ -= 経過サイクル数;
	while(ダウンカウンタ <= frequency) {
		ダウンカウンタ += 8192 - frequency;
		波形ステップ++;
	}

	         frequency
	 |           | 8192-frequency |
	0|-----------|----------------|8192
	          <====*====*====*====*
	            <====*====*====*
	         <====*====*====*====*

ダウンカウンタの初期値を(8192-frequency)とすると、

	ダウンカウンタ: 初期値=8192-frequency
	...
	ダウンカウンタ -= 経過サイクル数;
	while(ダウンカウンタ <= 0) {
		ダウンカウンタ += 8192 - frequency;
		波形ステップ++;
	}

	             0         8192-frequency
	             |                |
	-------------|----------------|
	          <====*====*====*====*
	            <====*====*====*
	         <====*====*====*====*

矩形波の位相は無視できるので、ダウンカウンタの初期値を(8192-frequency)とする必要はありません。
単純に、ダウンカウンタの初期値を0とすると、

	ダウンカウンタ: 初期値=0
	...
	ダウンカウンタ -= 経過サイクル数;
	while(ダウンカウンタ <= 0) {
		ダウンカウンタ += 8192 - frequency;
		波形ステップ++;
	}

	             0         8192-frequency
	             |                |
	-------------|----------------|
	        <====*
	          <====*====*====*
	            <====*====*====*
	         <====*====*====*====*

===============
出力周波数(PCM)
===============

出力周波数Fout、基本入力クロックFin、出力周波数設定値fの関係は次の通りです。

	Fout = Fin / 64 / (2048 - f)

波形メモリは1サンプル4ビットで全16バイトなので、1波形32ステップと考えます。
トーンの再生を32サンプルPCMの再生と見なすと、再生速度Fstepは次の通りです。

	Fstep = Fin / (64 / 32) / (2048 - f)
	      = Fin / 2 / (2048 - f)
	      = Fin / (2048*2 - f*2)
	      = Fin / (4096 - f*2)

--------
実装方法
--------

考え方はトーンと同じなので省略します。
トーンとの相違点だけを挙げておきます。

			トーン		PCM
frequency設定値		f*4		f*2
ダウンカウンタ初期値	8192		4096

==================
出力周波数(ノイズ)
==================

プリスケーラ出力周波数Fdivと基本入力周波数Fin、および分周比設定値との関係は、次の通りです。

	分周比設定(NR43[2:0])
	0	Fdiv = Fin / 8 * 2 = Fin / 4 /  1
	1	Fdiv = Fin / 8 * 1 = Fin / 4 /  2
	2	Fdiv = Fin / 8 / 2 = Fin / 4 /  4
	3	Fdiv = Fin / 8 / 3 = Fin / 4 /  6
	4	Fdiv = Fin / 8 / 4 = Fin / 4 /  8
	5	Fdiv = Fin / 8 / 5 = Fin / 4 / 10
	6	Fdiv = Fin / 8 / 6 = Fin / 4 / 12
	7	Fdiv = Fin / 8 / 7 = Fin / 4 / 14

これらの式を

	Fdiv = Fin / 4 / A

	A = 分周比設定値 * 2	/* 分周比設定値!=0の場合 */
	A = 1			/* 分周比設定値==0の場合 */

と表\すことにします。

シフトカウンタ入力周波数FshiftとFdiv、およびシフトクロック周波数設定値との関係は、次の通りです。

	シフトクロック周波数設定(NR43[7:4])
	 0	Fshift = Fdiv / (2^ 1) = Fdiv / 2 / 2^ 0
	 1	Fshift = Fdiv / (2^ 2) = Fdiv / 2 / 2^ 1
	 2	Fshift = Fdiv / (2^ 3) = Fdiv / 2 / 2^ 2
	...
	13	Fshift = Fdiv / (2^13) = Fdiv / 2 / 2^13
	14	*無効*
	15	*無効*

これらの式を

	Fshift = Fdiv / 2 / 2^B

	B = シフトクロック周波数設定値

と表\すことにします。

先ほどの式とまとめると、

	Fshift = (Fin / 4 / A) / 2 / 2^B
	       = Fin / A / 2^B / 8
	       = Fin / A / 2^(B+3)
	       = Fin / (A * 2^(B+3))

つまり、(A * 2^(B+3))[サイクル]経過する毎に、シフトカウンタが1ステップ進行します。

--------
実装方法
--------

あらかじめ、(A * 2^(B+3))を計算し、DMGSOUNDNOISE.periodに格納しておきます。

	frequency = A * 2^(B+3)

	A = 分周比設定値 * 2	/* 分周比設定値!=0の場合 */
	A = 1			/* 分周比設定値==0の場合 */

	B = シフトクロック周波数設定値

経過サイクル数を蓄積し、frequencyを超える度にシフトカウンタを1ステップ進めます。

	アップカウンタ: 初期値=0

	アップカウンタ += 経過サイクル数;
	while(アップカウンタ >= frequency) {
		アップカウンタ -= frequency;
		シフトカウンタ進行処理;
	}

比較を簡単にするため、カウンタ進行方向を逆にします。

	ダウンカウンタ: 初期値=frequency

	ダウンカウンタ -= 経過サイクル数;
	while(ダウンカウンタ <= 0) {
		ダウンカウンタ += frequency;
		シフトカウンタ進行処理;
	}

ノイズ波形の位相は無視できるので、ダウンカウンタの初期値を(2^(B+3))とする必要はありません。

	ダウンカウンタ: 初期値=0

	ダウンカウンタ -= 経過サイクル数;
	while(ダウンカウンタ <= 0) {
		ダウンカウンタ += frequency;
		シフトカウンタ進行処理;
	}

#endif

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

void
dmgsound_reset(DMGSOUND* ds, unsigned char* reg, int clock)
{
	/* まずクリア。 */
	memset(ds, 0, sizeof(DMGSOUND));

	/* レジスタメモリ格納。 */
	ds->reg = reg;

	/* クロック進行DDAセットアップ。 */
	ds->progress.u = clock / SPEAKER_FREQUENCY;	/* 毎回の変化量 */
	ds->progress.n = clock % SPEAKER_FREQUENCY;	/* 端数の増加量 */
	ds->progress.d =        -SPEAKER_FREQUENCY;	/* 端数 */
}

//void
//dmgsound_write(DMGSOUND* ds, unsigned char addr, unsigned char data)
//{
//	unsigned char* p;
//
//	/* まず格納します。 */
//	ASSERT(addr >= 0x10 && addr <= 0x3f);
//	ds->reg[addr] = data;
//
//	/* レジスタ書き込み値のデコード。 */
//	switch(addr) {
//	/*========== Ch#1 ==========*/
//#define ch	(ds->ch1)
//#define reg(i)	(ds->reg[0x10 + (i)])
//	//case 0x10: /* NR10 */
//	//case 0x11: /* NR11 */
//	//case 0x12: /* NR12 */
//	//case 0x13: /* NR13 */
//	case 0x14: /* NR14 */
//		if(data & 0x80) {
//			/* Sound Length */
//			ch.sound_length = reg(4) & 0x40 ? (0x40 - (reg(1) & 0x3f)) << 14
//							: -1; /* INFINITE */
//			/* Sweep */
//			ch.sweep_time = (reg(0) & 0x70) << 11;
//			ch.sweep_up_down = (reg(0) >> 3) & 1;
//			ch.sweep_shift = reg(0) & 7;
//			ch.sweep_progress = ch.sweep_time;
//			/* Envelope */
//			ch.envelope = (reg(2) >> 4) & 0xf;
//			ch.envelope_time = (reg(2) & 7) << 16;
//			ch.envelope_up_down = (reg(2) >> 3) & 1;
//			ch.envelope_progress = ch.envelope_time;
//			/* Wave Pattern */
//			ch.wave_pattern = 0;
//			ch.wave_pattern_duty = (reg(1) >> 5) & 6;           /* 1=>2/8,2=>4/8,3=>6/8 */
//			if(!ch.wave_pattern_duty) ch.wave_pattern_duty = 1; /* 0=>1/8 */
//			/* Frequency */
//			ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 2;
//			ch.frequency_progress = 0;
//		}
//		return;
//#undef ch
//#undef reg
//	/*========== Ch#2 ==========*/
//#define ch	(ds->ch2)
//#define reg(i)	(ds->reg[0x15 + (i)])
//	//0x15:NR20はありません
//	//case 0x16: /* NR21 */
//	//case 0x17: /* NR22 */
//	//case 0x18: /* NR23 */
//	case 0x19: /* NR24 */
//		if(data & 0x80) {
//			/* Sound Length */
//			ch.sound_length = reg(4) & 0x40 ? (0x40 - (reg(1) & 0x3f)) << 14
//							: -1; /* INFINITE */
//			/* Envelope */
//			ch.envelope = (reg(2) >> 4) & 0xf;
//			ch.envelope_time = (reg(2) & 7) << 16;
//			ch.envelope_up_down = (reg(2) >> 3) & 1;
//			ch.envelope_progress = ch.envelope_time;
//			/* Wave Pattern */
//			ch.wave_pattern = 0;
//			ch.wave_pattern_duty = (reg(1) >> 5) & 6;           /* 1=>2/8,2=>4/8,3=>6/8 */
//			if(!ch.wave_pattern_duty) ch.wave_pattern_duty = 1; /* 0=>1/8 */
//			/* Frequency */
//			ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 2;
//			ch.frequency_progress = 0;
//		}
//		return;
//#undef ch
//#undef reg
//	/*========== Ch#3 ==========*/
//#define ch	(ds->ch3)
//#define reg(i)	(ds->reg[0x1a + (i)])
//	case 0x1a: /* NR30 */
//		ch.sound_on_off = (data >> 7) & 1; /* KeyOnと非同期にON/OFF可能\(?) */
//		return;
//	//case 0x1b: /* NR31 */
//	//case 0x1c: /* NR32 */
//	//case 0x1d: /* NR33 */
//	case 0x1e: /* NR34 */
//		if(data & 0x80) {
//			/* Sound Length */
//			ch.sound_length = reg(4) & 0x40 ? (0x100 - reg(1)) << 21
//							: -1; /* INFINITE */
//			/* Output Level */
//			ch.output_level = (reg(2) >> 5) & 3;
//			/* Wave Pattern */
//			ch.wave_pattern = 0;
//			/* Frequency */
//			ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 1;
//			ch.frequency_progress = 0;
//		}
//		return;
//	case 0x30: /* AUD3W[0] */
//	case 0x31: /* AUD3W[1] */
//	case 0x32: /* AUD3W[2] */
//	case 0x33: /* AUD3W[3] */
//	case 0x34: /* AUD3W[4] */
//	case 0x35: /* AUD3W[5] */
//	case 0x36: /* AUD3W[6] */
//	case 0x37: /* AUD3W[7] */
//	case 0x38: /* AUD3W[8] */
//	case 0x39: /* AUD3W[9] */
//	case 0x3a: /* AUD3W[a] */
//	case 0x3b: /* AUD3W[b] */
//	case 0x3c: /* AUD3W[c] */
//	case 0x3d: /* AUD3W[d] */
//	case 0x3e: /* AUD3W[e] */
//	case 0x3f: /* AUD3W[f] */
//		p = &ch.wave_pattern_ram[(addr - 0x30) << 1];
//		p[0] = ((data >> 3) & 0x1e) - 15; /* 上位ニブルが先 */
//		p[1] = ((data << 1) & 0x1e) - 15; /* 下位ニブルが後 */
//		return;
//#undef ch
//#undef reg
//	/*========== Ch#4 ==========*/
//#define ch	(ds->ch4)
//#define reg(i)	(ds->reg[0x1f + (i)])
//	//0x1f:NR40はありません
//	//case 0x20: /* NR41 */
//	//case 0x21: /* NR42 */
//	//case 0x22: /* NR43 */
//	case 0x23: /* NR44 */
//		if(data & 0x80) {
//			/* Sound Length */
//			ch.sound_length = reg(4) & 0x40 ? (0x40 - (reg(1) & 0x3f)) << 14
//							: -1; /* INFINITE */
//			/* Envelope */
//			ch.envelope = (reg(2) >> 4) & 0xf;
//			ch.envelope_time = (reg(2) & 7) << 16;
//			ch.envelope_up_down = (reg(2) >> 3) & 1;
//			ch.envelope_progress = ch.envelope_time;
//			/* LFSR */
//			ch.lfsr = 1;
//			ch.lfsr_bits = (reg(3) >> 3) & 1;
//			/* Frequency */
//			ch.frequency = (reg(3) & 7) << 1;
//			if(!ch.frequency) ch.frequency = 1;
//			ch.frequency <<= (((reg(3) >> 4) & 0xf) + 3);
//			ch.frequency_progress = 0;
//		}
//		return;
//#undef ch
//#undef reg
//	/*========== Misc ==========*/
//	case 0x24: /* NR50 */
//		ds->volume[0] =  data       & 7;
//		ds->volume[1] = (data >> 4) & 7;
//		return;
//	case 0x25: /* NR51 */
//		ds->sound_output = data;
//		return;
//	case 0x26: /* NR52 */
//		ds->all_sound_on_off = (data >> 7) & 1;
//		/* bit3-0の「Sound # ON flag」は、読み出し専用のステータスフラグです。
//		 * 書き込んだ値によって各チャネルのON/OFFを制御するものではありません。
//		 */
//		return;
//	}
//
////#ifndef PIECE
////	/* 存在しないレジスタへの書き込み? */
////	DIE();
////#endif /*PIECE*/
////↓2005/02/14 削除
//// * Mon Feb 14 20:30:00 JST 2005 Naoyuki Sawa
//// - NR10-13やNR21-23などは、値を格納するだけで、switch()で処理していないので、
////   存在するレジスタへの書き込みでも、ここへ来ることあるのでした。
////   P/ECE環境では、上のブロックはコンパイルされないので、気付きませんでした。
////   Win32環境だと、実行後すぐに上のブロックのDIE()に引っかかります。
////   一年経っていまごろ気付いたので(^^;、削除しました。
//}
//↓ * Fri Feb 25 20:40:00 JST 2005 Naoyuki Sawa
//↓ - 発声開始時以外にもチャネルパラメータを変更できるように修正しました。
//↓ これまで、NRx4への書き込みによる発生開始時のみチャネルパラメータを設定し、
//↓ 発生終了までパラメータを変更しないように実装していましたが、誤りでした。
//↓ NRx0-NRx3およびD7=0でNRx4へ書き込むことで、発声中でもパラメータ変更可能\です。
void
dmgsound_write(DMGSOUND* ds, unsigned char addr, unsigned char data)
{
	unsigned char* p;

	/* まず格納します。 */
	ASSERT(addr >= 0x10 && addr <= 0x3f);
	ds->reg[addr] = data;

	/* レジスタ書き込み値のデコード。 */
	switch(addr) {
	/*========== Ch#1 ==========*/
#define ch	(ds->ch1)
#define reg(i)	(ds->reg[0x10 + (i)])
	case 0x10: /* NR10 */
		/* Sweep */
		ch.sweep_time = (reg(0) & 0x70) << 11;
		ch.sweep_up_down = (reg(0) >> 3) & 1;
		ch.sweep_shift = reg(0) & 7;
		ch.sweep_progress = ch.sweep_time;
		return;
	case 0x11: /* NR11 */
		/* Wave Pattern */
		ch.wave_pattern_duty = (reg(1) >> 5) & 6;           /* 1=>2/8,2=>4/8,3=>6/8 */
		if(!ch.wave_pattern_duty) ch.wave_pattern_duty = 1; /* 0=>1/8 */
		ch.wave_pattern = 0;
		return;
	case 0x12: /* NR12 */
		/* Envelope */
		ch.envelope = (reg(2) >> 4) & 0xf;
		ch.envelope_time = (reg(2) & 7) << 16;
		ch.envelope_up_down = (reg(2) >> 3) & 1;
		ch.envelope_progress = ch.envelope_time;
		return;
	case 0x13: /* NR13 */
		/* Frequency */
		ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 2;
		ch.frequency_progress = 0;
		return;
	case 0x14: /* NR14 */
		/* Frequency */
		ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 2;
		ch.frequency_progress = 0;
		if(data & 0x80) {
			/* Sound Length */
			ch.sound_length = reg(4) & 0x40 ? (0x40 - (reg(1) & 0x3f)) << 14
							: -1; /* INFINITE */
		}
		return;
#undef ch
#undef reg
	/*========== Ch#2 ==========*/
#define ch	(ds->ch2)
#define reg(i)	(ds->reg[0x15 + (i)])
	//0x15:NR20はありません
	case 0x16: /* NR21 */
		/* Wave Pattern */
		ch.wave_pattern_duty = (reg(1) >> 5) & 6;           /* 1=>2/8,2=>4/8,3=>6/8 */
		if(!ch.wave_pattern_duty) ch.wave_pattern_duty = 1; /* 0=>1/8 */
		ch.wave_pattern = 0;
		return;
	case 0x17: /* NR22 */
		/* Envelope */
		ch.envelope = (reg(2) >> 4) & 0xf;
		ch.envelope_time = (reg(2) & 7) << 16;
		ch.envelope_up_down = (reg(2) >> 3) & 1;
		ch.envelope_progress = ch.envelope_time;
		return;
	case 0x18: /* NR23 */
		/* Frequency */
		ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 2;
		ch.frequency_progress = 0;
		return;
	case 0x19: /* NR24 */
		/* Frequency */
		ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 2;
		ch.frequency_progress = 0;
		if(data & 0x80) {
			/* Sound Length */
			ch.sound_length = reg(4) & 0x40 ? (0x40 - (reg(1) & 0x3f)) << 14
							: -1; /* INFINITE */
		}
		return;
#undef ch
#undef reg
	/*========== Ch#3 ==========*/
#define ch	(ds->ch3)
#define reg(i)	(ds->reg[0x1a + (i)])
	case 0x1a: /* NR30 */
		ch.sound_on_off = (data >> 7) & 1; /* KeyOnと非同期にON/OFF可能\(?) */
		return;
	//case 0x1b: /* NR31 */
	case 0x1c: /* NR32 */
		/* Output Level */
		ch.output_level = (reg(2) >> 5) & 3;
		return;
	case 0x1d: /* NR33 */
		/* Frequency */
		ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 1;
		ch.frequency_progress = 0;
		return;
	case 0x1e: /* NR34 */
		/* Frequency */
		ch.frequency = (reg(3) | ((reg(4) & 0x7) << 8)) << 1;
		ch.frequency_progress = 0;
		if(data & 0x80) {
			/* Sound Length */
			ch.sound_length = reg(4) & 0x40 ? (0x100 - reg(1)) << 21
							: -1; /* INFINITE */
			/* Wave Pattern */
			ch.wave_pattern = 0;
		}
		return;
	case 0x30: /* AUD3W[0] */
	case 0x31: /* AUD3W[1] */
	case 0x32: /* AUD3W[2] */
	case 0x33: /* AUD3W[3] */
	case 0x34: /* AUD3W[4] */
	case 0x35: /* AUD3W[5] */
	case 0x36: /* AUD3W[6] */
	case 0x37: /* AUD3W[7] */
	case 0x38: /* AUD3W[8] */
	case 0x39: /* AUD3W[9] */
	case 0x3a: /* AUD3W[a] */
	case 0x3b: /* AUD3W[b] */
	case 0x3c: /* AUD3W[c] */
	case 0x3d: /* AUD3W[d] */
	case 0x3e: /* AUD3W[e] */
	case 0x3f: /* AUD3W[f] */
		p = &ch.wave_pattern_ram[(addr - 0x30) << 1];
		p[0] = ((data >> 3) & 0x1e) - 15; /* 上位ニブルが先 */
		p[1] = ((data << 1) & 0x1e) - 15; /* 下位ニブルが後 */
		return;
#undef ch
#undef reg
	/*========== Ch#4 ==========*/
#define ch	(ds->ch4)
#define reg(i)	(ds->reg[0x1f + (i)])
	//0x1f:NR40はありません
	//case 0x20: /* NR41 */
	case 0x21: /* NR42 */
		/* Envelope */
		ch.envelope = (reg(2) >> 4) & 0xf;
		ch.envelope_time = (reg(2) & 7) << 16;
		ch.envelope_up_down = (reg(2) >> 3) & 1;
		ch.envelope_progress = ch.envelope_time;
		return;
	case 0x22: /* NR43 */
		/* LFSR */
		ch.lfsr_bits = (reg(3) >> 3) & 1;
		ch.lfsr = 1;
		/* Frequency */
		ch.frequency = (reg(3) & 7) << 1;
		if(!ch.frequency) ch.frequency = 1;
		ch.frequency <<= (((reg(3) >> 4) & 0xf) + 3);
		ch.frequency_progress = 0;
		return;
	case 0x23: /* NR44 */
		if(data & 0x80) {
			/* Sound Length */
			ch.sound_length = reg(4) & 0x40 ? (0x40 - (reg(1) & 0x3f)) << 14
							: -1; /* INFINITE */
		}
		return;
#undef ch
#undef reg
	/*========== Misc ==========*/
	case 0x24: /* NR50 */
		ds->volume[0] =  data       & 7;
		ds->volume[1] = (data >> 4) & 7;
		return;
	case 0x25: /* NR51 */
		ds->sound_output = data;
		return;
	case 0x26: /* NR52 */
		ds->all_sound_on_off = (data >> 7) & 1;
		/* bit3-0の「Sound # ON flag」は、読み出し専用のステータスフラグです。
		 * 書き込んだ値によって各チャネルのON/OFFを制御するものではありません。
		 */
		return;
	}

//#ifndef PIECE
//	/* 存在しないレジスタへの書き込み? */
//	DIE();
//#endif /*PIECE*/
//↓2005/02/14 削除
// * Mon Feb 14 20:30:00 JST 2005 Naoyuki Sawa
// - NR10-13やNR21-23などは、値を格納するだけで、switch()で処理していないので、
//   存在するレジスタへの書き込みでも、ここへ来ることあるのでした。
//   P/ECE環境では、上のブロックはコンパイルされないので、気付きませんでした。
//   Win32環境だと、実行後すぐに上のブロックのDIE()に引っかかります。
//   一年経っていまごろ気付いたので(^^;、削除しました。
}

unsigned char
dmgsound_read(DMGSOUND* ds, unsigned char addr)
{
	int data;

	ASSERT(addr >= 0x10 && addr <= 0x3f);

	switch(addr) {
	case 0x26: /* NR52 */
		data = 0;
		if(ds->all_sound_on_off) data |= (1<<7);
		if(ds->ch1.sound_length) data |= (1<<0);
		if(ds->ch2.sound_length) data |= (1<<1);
		if(ds->ch3.sound_length) data |= (1<<2);
		if(ds->ch4.sound_length) data |= (1<<3);
		return data;
	}

	return ds->reg[addr];
}
