/*	
 *	clipn2.c
 *
 *	P/ECE YM2612(OPN2) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Dec 05 03:03:00 JST 2003 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/*
 *	* NTSC版MegaDriveのクロック構成
 *
 *		源振クロック = 53693100[Hz]
 *		|
 *		+--( 7分周)--+--> 68000  マスタクロック = 7.67[MHz]
 *		|	     +--> YM2612 マスタクロック = 7.67[MHz]
 *		+--(15分周)--+--> Z80    マスタクロック = 3.58[MHz]
 *			     +--> SN76789マスタクロック = 3.58[MHz]
 *
 *	* YM2612のマスタクロックと発声周波数の関係
 *
 *		マスタクロック		fM = 7.67[MHz]
 *		サンプリング周波数	fSam = fM÷(12×12) = 53267[Hz]
 *		発声周波数		fMus = (fSam×Fnumber×2^(Block-1))÷(2^20)
 *
 *		YM2203と比較すると、マスタクロックが約二倍な分、サンプリング周波数の分周比も二倍です。
 *		YM2203の通常構成では、fM=4MHz, fSam=fM÷(12×6) でした。(分周比はレジスタ設定で変更可)
 *		結局、YM2612もYM2203も、サンプリング周波数以降の計算には同じ式が使えるみたいです。
 *		YM2612の公式資料が入手できないため、YM2203の公式資料を参考にするとき意識してください。
 *		(YM2203の公式資料もデータシートだけで、アプリケーションマニュアルが見当たらないけど…)
 *
 *		<例> Fnumber = 1083, Block = 4
 *			fMus = (fSam×Fnumber×2^(Block-1))÷(2^20)
 *			     = ((53693100÷7÷(12×12))×1083×2^(4-1))÷(2^20)
 *			     = 440[Hz] (A-4)
 *
 *	* 発声周波数の実装について
 *
 *		fOut=SPEAKER_FREQUENCY=16000[Hz]とする。
 *		PhaseGeneratorTableを2^8エントリとすると、fMus=fOutのときに進行速度が2^ 8になればいい。
 *		進行速度の固定小数部16bitとすると、       fMus=fOutのときに進行速度が2^24になればいい。
 *
 *			進行速度 = fMus×2^24÷16000
 *				 = (fSam×Fnumber×2^(Block-1))÷2^20×2^24 ÷16000
 *				 = (fSam×Fnumber×2^(Block-1))      ×2^ 4 ÷16000
 *				 = (fSam×Fnumber×2^(      4))÷2^(1-Block)÷16000
 *				 = (fSam×Fnumber×2^(     10))÷2^(7-Block)÷16000
 *				 = (fSam×Fnumber<<10         )÷( 16000    <<(7-Block))
 *				 = (fSam×Fnumber<< 3         )÷((16000>>7)<<(7-Block))
 *				    ~~~~  ~~~~~~~   ~              ~~~~~~~~    ~~~~~~~
 *				    16bit×11bit×3bit = 30bit     7bit×7bit = 14bit
 *
 *		最後にMULを考慮して、
 *
 *			If MUL = 0 Then
 *				進行速度 = 進行速度 / 2
 *			Else
 *				進行速度 = 進行速度 * MUL
 *			End If
 */

const short ym2612op_pg_table[256] = { /* 符号付き9bit */
	+(  0),+(  6),+( 12),+( 18),+( 25),+( 31),+( 37),+( 43),+( 49),+( 55),+( 62),+( 68),+( 74),+( 80),+( 86),+( 91),
	+( 97),+(103),+(109),+(114),+(120),+(125),+(131),+(136),+(141),+(147),+(152),+(157),+(162),+(166),+(171),+(176),
	+(180),+(185),+(189),+(193),+(197),+(201),+(205),+(208),+(212),+(215),+(219),+(222),+(225),+(228),+(230),+(233),
	+(236),+(238),+(240),+(242),+(244),+(246),+(247),+(249),+(250),+(251),+(252),+(253),+(254),+(254),+(255),+(255),
	+(255),+(255),+(255),+(254),+(254),+(253),+(252),+(251),+(250),+(249),+(247),+(246),+(244),+(242),+(240),+(238),
	+(236),+(233),+(230),+(228),+(225),+(222),+(219),+(215),+(212),+(208),+(205),+(201),+(197),+(193),+(189),+(185),
	+(180),+(176),+(171),+(166),+(162),+(157),+(152),+(147),+(141),+(136),+(131),+(125),+(120),+(114),+(109),+(103),
	+( 97),+( 91),+( 86),+( 80),+( 74),+( 68),+( 62),+( 55),+( 49),+( 43),+( 37),+( 31),+( 25),+( 18),+( 12),+(  6),
	-(  0),-(  6),-( 12),-( 18),-( 25),-( 31),-( 37),-( 43),-( 49),-( 55),-( 62),-( 68),-( 74),-( 80),-( 86),-( 91),
	-( 97),-(103),-(109),-(114),-(120),-(125),-(131),-(136),-(141),-(147),-(152),-(157),-(162),-(166),-(171),-(176),
	-(180),-(185),-(189),-(193),-(197),-(201),-(205),-(208),-(212),-(215),-(219),-(222),-(225),-(228),-(230),-(233),
	-(236),-(238),-(240),-(242),-(244),-(246),-(247),-(249),-(250),-(251),-(252),-(253),-(254),-(254),-(255),-(255),
	-(255),-(255),-(255),-(254),-(254),-(253),-(252),-(251),-(250),-(249),-(247),-(246),-(244),-(242),-(240),-(238),
	-(236),-(233),-(230),-(228),-(225),-(222),-(219),-(215),-(212),-(208),-(205),-(201),-(197),-(193),-(189),-(185),
	-(180),-(176),-(171),-(166),-(162),-(157),-(152),-(147),-(141),-(136),-(131),-(125),-(120),-(114),-(109),-(103),
	-( 97),-( 91),-( 86),-( 80),-( 74),-( 68),-( 62),-( 55),-( 49),-( 43),-( 37),-( 31),-( 25),-( 18),-( 12),-(  6),
};

YM2612CH_MIX* const ym2612ch_mix_table[8] = {
	ym2612ch_mix0,
	ym2612ch_mix1,
	ym2612ch_mix2,
	ym2612ch_mix3,
	ym2612ch_mix4,
	ym2612ch_mix5,
	ym2612ch_mix6,
	ym2612ch_mix7,
};

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

void
ym2612_init(YM2612* ym2612, int f_m)
{
	int i_part;
	int i_reg;
	int i_ch;
	int i_op;

	/* まずクリアします。 */
	memset(ym2612, 0, sizeof(YM2612));

	/* サンプリング周波数を求めます。 */
	ym2612->f_sam = f_m / (12 * 12);
	if(ym2612->f_sam <= 0) {
		DIE();
	}

	/* 全レジスタを初期化します。 */
	for(i_part = 0; i_part < 2; i_part++) {
		for(i_reg = 0; i_reg < 256; i_reg++) {
			switch(i_reg) {
			case 0x2a: /* DAC */
				/* 重要!!
				 * GYMプレイヤーはDAC enは書き込みますが、DACは横取りします。
				 * DAC出力を中央値に初期化しておかないと、DACを使うGYMファイルは常に-128のバイアスがかかってしまいます。
				 * (DAC設定値は符号無し8bitで最小:0..128..255:最大 だから、0で初期化すると-128と見なされてしまう。)
				 */
				ym2612_write_reg(ym2612, i_part, i_reg, 128); /* 128が中央値です */
				break;
			case 0xb4: /* L, R, AMS, FMS */
			case 0xb5: /* L, R, AMS, FMS */
			case 0xb6: /* L, R, AMS, FMS */
				ym2612_write_reg(ym2612, i_part, i_reg, 0xc0); /* L,R=On */
				break;
			default:
				ym2612_write_reg(ym2612, i_part, i_reg, 0);
				break;
			}
		}
	}

	/* エンベロープ関数を初期化します。
	 * * レジスタの初期化だけではKey off判定されないため、エンベロープ関数が初期化されません。
	 *   初回のみ明示的にエンベロープ関数を初期化する必要があります。
	 */
	for(i_part = 0; i_part < 2; i_part++) {
		for(i_ch = 0; i_ch < 3; i_ch++) {
			for(i_op = 0; i_op < 4; i_op++) {
				ym2612->ch[i_part][i_ch].op[i_op].eg_env = ym2612op_env_silent;
			}
		}
	}
}

int
ym2612_read(YM2612* ym2612, int addr)
{
	//int i_part;
	//int i_reg;
	//
	///* A0ラインによるステータス読み出し/レジスタ値読み出しの切り替えは、YM2203の仕様に従いました。
	// * 既存のYM2612エミュレータソースは、A0ラインが0か1かにかかわらず、常にステータスを読み出すようです。
	// * MegaDrive固有の仕様か、それとも本当にYM2612はYM2203と違ってレジスタ値が読み出せないのか判断できないため、
	// * とりあえずYM2203と同じく、A0ラインによってステータス読み出し/レジスタ値読み出しを切り替えることにしました。
	// */
	//↓2004/12/09 Naoyuki Sawa 訂正
	//YM2203で読み出せるのは、0x00..0x0fのPSG互換部分レジスタだけです。FM音源部分のレジスタ値は読み出せません。
	//YM2612はPSG互換部分を持っていないので、読み出せるレジスタはありません。常にステータス読み出しとなります。
	//
	///* A0 = 0:ステータス読み出し */
	//if(!(addr & 1)) {
	//↓2004/12/09 Naoyuki Sawa 訂正
	//A0が0か1にかかわらず、ステータス読み出しです。

		/* * ステータスの構成は次の通りです。
		 *
		 *	Bit 76543210
		 *	    |||||||+- Overflow B
		 *	    ||||||+-- Overflow A
		 *	    |+++++--- Unknown
		 *	    +-------- Busy
		 *
		 * - 2004/12/05現在、P/ECE YM2612 EmulatorはYM2612内蔵タイマをサポートしないので、Overflow A,Bは常に0とします。
		 *   また実機では、レジスタデータ書き込み後Busy=1になり、一定時間後次のデータ受付可能時にBusy=0に戻るのですが、
		 *   エミュレーションプログラムではこの挙動を再現する必要がないため、Busyは常に0とします。
		 *   以上より、YM2612からの読み出しは、ステータスとして常に0を返すことにします。
		 */
		return 0;

	//};
	//
	///* A0 = 1:レジスタ値読み出し */
	//i_part = (addr >> 1) & 1; /* A1 = Part I/II 選択 */
	//i_reg = ym2612->i_reg[i_part];
	//return ym2612->reg[i_part][i_reg];
	//↓2004/12/09 Naoyuki Sawa 訂正
	//A0が0か1にかかわらず、ステータス読み出しです。
}

/* エンベロープ時間換算用の標準fSAM(=fM/(12*12))
 * * OPNのタイミング資料が見つからないので、OPMのタイミング資料を参考にしました。
 *   OPMの標準fMは4MHzですが、OPN2は分周比が2倍みたいなので8MHzを基準にしました。
 */
#define EG_FSAM_BASE	(8000000 / (12 * 12))
//↓理論上は上述のとおりのはずなのですが、なぜか2〜4倍速ぐらいの方が良い感じに聴こえます。
//↓たぶん、本物のFM音源は音量カーブが指数関数的で減衰が急であるのに対して、
//↓本ドライバの音量カーブは直線なので、減衰を速めた方が良い感じに聴こえるのだと思います。
//↓2004/12/9現在の実装は1倍のままとですが、今後、次の2〜4倍の定義に変えるかも知れません。
//#define EG_FSAM_BASE	(4000000 / (12 * 12)) // 2倍
//#define EG_FSAM_BASE	(2000000 / (12 * 12)) // 4倍

/* トータルレベル、サスティンレベル設定値から、ボリュームを計算します。
 * [in]
 *	tl		トータルレベル設定値
 *				(0..127)×0.75[dB]
 *				最大レベルからの減衰量
 *	sl		サスティンレベル設定値 (0..15)
 *				(0..14)×3[dB]、sl=15の場合のみ(15)×3+48[dB]
 *				トータルレベルからの減衰量
 * [out]
 *	戻り値		ボリューム
 *				音量小:(0.0)..(127.0):音量大
 *				(符号無し7.16bit固定小数)
 */
static int
calc_volume(int tl, int sl)
{
#if 0
//
//	/* * 『Inside X68000』p.288「オペレータを直列接続したときの出力波形」より引用(OPMの資料)
//	 *	------------------------------------------------------------------------------------
//	 *	　オペレータを2つ直列に接続したときのSIN波形テーブルの出力Aは、
//	 *
//	 *		A = SIN(ωt + αSIN(ψt))
//	 *
//	 *	のように表されます。ψt、ωtは、要するに周波数ですから、これは簡単に求められますが、
//	 *	αの求め方が公開されていないので、このままでは出力波形がわかりません。実験の結果、各
//	 *	スロットの出力がTLだけで決まるとき(ARが0、D1R、D2Rなどが最大値のとき)には、
//	 *
//	 *		α = 10^(-0.75/20×TL + e/2)
//	 *		     ここで、eは自然対数の底(=2.71828...)、'^'はべき乗を示す
//	 *
//	 *	とし、αSIN(ψt)の計算結果の数値をラジアン単位の角度データとみなしてωtと足すと、か
//	 *	なりよい感じになることがわかりました。
//	 *	　M1のフィードバック量はよくわかりませんでしたが、TLが0dBのときに1(ラジアン)
//	 *	とみなしてフィードバックするとよいようです。たとえばFL=3なら0.7853(=3.14159/4)
//	 *	をかけ算した値をフィードバックすると、近い波形が得られます。
//	 *	------------------------------------------------------------------------------------
//	 */
//
//	double db;
//	double a;
//
//	db = tl * 0.75;
//	if(sl < 15) {
//		db += sl * 3;
//	} else {
//		db += sl * 3 + 48;
//	}
//	a = pow(10, -db / 20 + exp(1) / 2);
//
//	a = a * (127 << 16) / (2 * acos(-1));
//	if(a > 127 << 16) {
//		a = 127 << 16;
//	}
//
//	return (int)a;
//
#else	//↑理論上はこうなのですが、その通りやってもあまりいい感じに聞こえないのと、計算時間がかかりすぎるため、
	//↓下のように簡略化しました。

	/* * 本物のFM音源は、エンベロープ出力が指数関数的に変化します。
	 *   本当は1サンプルづつけ計算しなければいけない(と思う)のですが、P/ECEで処理するには重過ぎます。
	 *   高速化のために、音量設定の段階で指数関数的に変換するだけでも、まあまあそれっぽく聴こえます。
	 *
	 *		実効音量 = (音量設定値 × 音量設定最大値)^6 ×音量設定最大値
	 *
	 *   音量設定値を6乗すると、だいたい良い感じになるみたいです。(調整可)
	 *   べき乗計算の途中で32bitを超えてしまわないよう、注意してください。
	 */
	int v;

	tl ^= 127;			/* 小:127..0:大 => 小:0..127:大 */
	tl = tl * tl;			/*  7bit× 7bit =>   14bit */
	v  = tl * tl;			/* 14bit×14bit =>   28bit */
	v >>= 14;			/* 28bit        =>   14bit */
	v  *= tl;			/* 14bit×14bit =>   28bit */
	v >>= 28 - (7 + 16);		/* 28bit        => 7.16bit */

	//if(sl < 15) {
	//	sl = sl * 3;
	//} else {
	//	sl = sl * 3 + 48;	/* SL=15の特例 */
	//}
	//sl = (15 * 3 + 48) - sl;	/* 小:93..0:大 => 小:0..93:大 */
	//sl = v * sl / (15 * 3 + 48);
	//↓SL=15の特例を考慮しない方が良い感じに聴こえるみたいです。
	sl ^= 15;			/* 小:15..0:大 => 小:0..15:大 */
	v = v * sl / 15;

	return v;

#endif
}

/* 直線的変化の音量設定値を、指数関数的変化の実効音量に変換します。
 * [in]
 *	v		音量設定値 (MIN:0..127:MAX)
 * [out]
 *	戻り値		実行音量 (MIN:(0.0)..(127.0):MAX、7.16bit固定小数)
 * [note]
 */

void
ym2612_write(YM2612* ym2612, int addr, int data)
{
	int i_part;
	int i_reg;
	int i_ch;
	int i_op;
	YM2612CH* ch;
	YM2612OP* op;
	int mask;
	int rate;
	int block;
	int f_number;
	int pg_spd;

	/* 不正な書き込み値を検出します。(プログラムエラー) */
	if((data < 0x00) || (data > 0xff)) {
		DIE();
	}

	/* A1 = Part I/II 選択 */
	i_part = (addr >> 1) & 1;

	/* A0 = 0:レジスタアドレス書き込み */
	if(!(addr & 1)) {
		ym2612->i_reg[i_part] = data;
		return;
	}

	/* 書き込むレジスタアドレスを取得します。 */
	i_reg = ym2612->i_reg[i_part];

	/* レジスタ0x00..0x2f */
	if(i_reg < 0x30) {

		/* Part IIにはレジスタ0x00..0x2fはありません。 */
		if(i_part) {
			return; /* レジスタ不正 */
		}

		switch(i_reg) {
		//case 0x22: /* LFO enable, LFO frequency */
		//case 0x24: /* Timer A MSBs */
		//case 0x25: /* Timer A LSBs */
		//case 0x26: /* Timer B */

		case 0x27: /* Ch3 mode, Reset B, Reset A, Enable B, Enable A, Load B, Load A */

			/* Channel 3 has 4 separate frequencyを格納します。
			 * * ホントはCh3 mode変更の直後もOperator i_opのpg_spdを更新すべきなのですけれど、
			 *   たぶんCh3 mode変更より後にF-numberとBlockが変更されると思うので、省略します。
			 * * bit7〜6がチャネル3のモード指定で、{00:通常,10:効果音:01:音声合成}となります。
			 *   (FM TOWNSテクニカルデータブック改訂2版 p.201 より)
			 *   しかし、既存のエミュレータソースやWeb上の資料では、{00:通常,01:効果音:10:音声合成}と
			 *   記載されているものが多く、どちらが正しいのか判断できません。
			 *   いずれにせよ、{00:通常}以外ならばチャネル3の各オペレータは個別の周波数設定となりますので、
			 *   00以外ならばChannel 3 has 4 separate frequency見なすことにします。
			 *   なお、音声合成モードはTimerと連動してKey on/offを行うモードで、2004/12/10現在は未実装です。
			 */
			ym2612->ch3_mode = (data & 0xc0) != 0;

			break;

		case 0x28: /* Operator, Channel */

			/* チャネルを取得します。 */
			i_part = (data >> 2) & 1; /* bit  2 = Part I/II 選択 */
			i_ch = data & 3;          /* bit1-0 = Channel 0..2   */
			if(i_ch == 3) {
				return; /* チャネル不正 */
			}
			ch = &ym2612->ch[i_part][i_ch];

			/* オペレータ1..4について... */
			mask = data >> 4;
			for(i_op = 0, op = ch->op;
			    i_op < 4;
			    i_op++, op++, mask >>= 1) {
				if(mask & 1) { /* Key on */

					/* {Attack,Decay,Sustain}ならば何もしません。 */
					if((op->eg_env != ym2612op_env_release) &&
					   (op->eg_env != ym2612op_env_silent)) {
						continue;
					}
					op->eg_env = ym2612op_env_attack;
					op->eg_cnt = 0;

				} else { /* Key off */

					/* {Release,Silent}ならば何もしません。 */
					if((op->eg_env == ym2612op_env_release) ||
					   (op->eg_env == ym2612op_env_silent)) {
						continue;
					}
					op->eg_env = ym2612op_env_release;
					if(op->eg_cnt > op->eg_sl) {
						op->eg_cnt = op->eg_sl;
					}
				}
			}

			break;

		case 0x2a: /* DAC */

			/* DAC出力値を格納します。((-127.0)..(127.0)×調整) */
			ym2612->dac = (data - 128) << (16 + 2/*DAC音量調整*/);

			break;

		case 0x2b: /* DAC en */

			/* DAC enを格納します。 */
			ym2612->dac_en = (data >> 7) & 1;

			break;
		}

		return;
	}

	/* チャネルを選択します。 */
	i_ch = i_reg & 3;
	if(i_ch == 3) {
		return; /* チャネル不正。レジスタ0xa8..0xafの場合はオペレータ不正 */
	}
	ch = &ym2612->ch[i_part][i_ch];

	/* レジスタ0x30..0x9f */
	if(i_reg < 0xa0) {

		/* オペレータを選択します。
		 * * レジスタ0x30..0x9eは、オペレータの順番が{1,2,3,4}ではなく{1,3,2,4}です。
		 *   (FM TOWNSテクニカルデータブック改訂2版 p.199 より)
		 */
		switch((i_reg >> 2) & 3) {
		case 0:       i_op = 0; break; /* 0x?0, 0x?1, 0x?2 = Op1 */
		case 1:       i_op = 2; break; /* 0x?4, 0x?5, 0x?6 = Op3 */
		case 2:       i_op = 1; break; /* 0x?8, 0x?9, 0x?a = Op2 */
		default/*3*/: i_op = 3; break; /* 0x?c, 0x?d, 0x?e = Op4 */
		}
		op = &ch->op[i_op];

		switch(i_reg >> 4) {
		case 0x30 >> 4: /* DT1, MUL */

			/* マルチプルを格納します。
			 * * ホントはMUL変更の直後もOperator i_opのpg_spdを更新すべきなのですけれど、
			 *   たぶんMUL変更より後にF-numberとBlockが変更されると思うので、省略します。
			 */
			op->mul = data & 15;

			break;

		case 0x40 >> 4: /* TL */

			/* トータルレベル設定値を保存します。 */
			op->tl = data & 127;

			/* トータルレベルを設定します。((0.0)..(127.0)) */
			op->eg_tl = calc_volume(op->tl, 0);

			/* トータルレベルの変更に追従して、サスティンレベルも更新します。((0.0)..(127.0)) */
			op->eg_sl = calc_volume(op->tl, op->sl);
			if((op->eg_env == ym2612op_env_sustain) || (op->eg_env == ym2612op_env_release)) {
				if(op->eg_cnt > op->eg_sl) {
					op->eg_cnt = op->eg_sl;
				}
			}

			break;

		case 0x50 >> 4: /* RS, AR */

			/* アタックレートを設定します。
			 * * AR={30:0.5[ms], 28:1.0[ms], 26:2.0[ms], ..., 0:INF} @標準fSAM から換算します。
			 */
			rate = data & 31;
			if(rate) {
				op->eg_ar = (((127 * ym2612->f_sam / EG_FSAM_BASE) << 16) / (int)(SPEAKER_FREQUENCY * 0.5 / 1000)) >> (15 - (rate >> 1));
				//            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
				//                 ボリューム最大値×実クロック÷標準クロック             0.5[ms]当りの出力サンプル数     
				if(rate & 1) { /* {31,29,27,...}は{30,28,26,...}の50%UP */
					op->eg_ar += op->eg_ar >> 1;
				}
			} else {
				op->eg_ar = 0;
			}

			break;

		case 0x60 >> 4: /* AM, D1R */

			/* ディケイレートを設定します。
			 * * DR={30:6.0[ms], 28:12.0[ms], 26:24.0[ms], ..., 0:INF} @標準fSAM から換算します。
			 */
			rate = data & 31;
			if(rate) {
				op->eg_dr = (((127 * ym2612->f_sam / EG_FSAM_BASE) << 16) / (int)(SPEAKER_FREQUENCY * 6.0 / 1000)) >> (15 - (rate >> 1));
				//            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
				//            ボリューム最大値×実クロック÷標準クロック    6.0[ms]当りの出力サンプル数
				if(rate & 1) { /* {31,29,27,...}は{30,28,26,...}の50%UP */
					op->eg_dr += op->eg_dr >> 1;
				}
			} else {
				op->eg_dr = 0;
			}

			break;

		case 0x70 >> 4: /* D2R */

			/* サスティンレートを設定します。
			 * * SR={30:6.0[ms], 28:12.0[ms], 26:24.0[ms], ..., 0:INF} @標準fSAM から換算します。
			 */
			rate = data & 31;
			if(rate) {
				op->eg_sr = (((127 * ym2612->f_sam / EG_FSAM_BASE) << 16) / (int)(SPEAKER_FREQUENCY * 6.0 / 1000)) >> (15 - (rate >> 1));
				//            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
				//            ボリューム最大値×実クロック÷標準クロック    6.0[ms]当りの出力サンプル数
				if(rate & 1) { /* {31,29,27,...}は{30,28,26,...}の50%UP */
					op->eg_sr += op->eg_sr >> 1;
				}
			} else {
				op->eg_sr = 0;
			}

			break;

		case 0x80 >> 4: /* D1L, RR */

			/* リリースレートを設定します。
			 * * RR={15:6.0[ms], 14:12.0[ms], 13:24.0[ms], ..., 0:INF} @標準fSAM から換算します。
			 */
			rate = data & 15;
			if(rate) {
				op->eg_rr = (((127 * ym2612->f_sam / EG_FSAM_BASE) << 16) / (int)(SPEAKER_FREQUENCY * 6.0 / 1000)) >> (15 - rate);
				//            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
				//            ボリューム最大値×実クロック÷標準クロック    6.0[ms]当りの出力サンプル数
			} else {
				op->eg_rr = 0;
			}

			/* サスティンレベル設定値を保存します。 */
			op->sl = (data >> 4) & 15;

			/* サスティンレベルを更新します。((0.0)..(127.0)) */
			op->eg_sl = calc_volume(op->tl, op->sl);
			if((op->eg_env == ym2612op_env_sustain) || (op->eg_env == ym2612op_env_release)) {
				if(op->eg_cnt > op->eg_sl) {
					op->eg_cnt = op->eg_sl;
				}
			}

			break;

		//case 0x90 >> 4: /* SSG-EG */
		}

		return;
	}

	/* レジスタ0xa0..0xff */
	switch(i_reg >> 2) {
	case 0xa0 >> 2: /* Frequency number LSB */

		/* F-number[7..0]を格納します。 */
		ch->f_number_lsb = data;
		data = -1; /* すぐ下でのレジスタ格納を抑制 */

		/* FALLTHRU */

	case 0xa4 >> 2: /* Block, Frequency number MSB */

		/* 上からのFALLTHRUでなければ、F-number[10..8]とBlockを格納します。 */
		if(data != -1) {
			ch->f_number_msb = data & 7;
			ch->block = (data >> 3) & 7;
		}

		/* 基準速度を求めます。 */
		f_number = ch->f_number_lsb | (ch->f_number_msb << 8);
		block    = ch->block;
		pg_spd   = (ym2612->f_sam * (f_number << 3)) / ((SPEAKER_FREQUENCY >> 7) << (7 - block));

		/* オペレータ1..4について... */
		for(i_op = 0, op = ch->op;
		    i_op < 4;
		    i_op++, op++) {

			/* Channel 3 has 4 separate frequencyならばオペレータ1..3はレジスタ0xa8..0xafによって設定されるので、ここでは設定しません。
			 * * 「Sega Genesis Technical Manual - YM2612 section」には、Channel 6も4 separate frequencyモードを持つと書かれていますが、
			 *   既存のYM2612エミュレータソースを参考にすると、4 separate frequencyモードを持っているのはChannel 3だけです。
			 *   たぶんYM2612では、Channel 6の特殊機能として、4 separate frequencyの代りに、ADPCMモードが実装されているのだと思います。
			 * * 「Sega Genesis Technical Manual - YM2612 section」には、Channel 3 has 4 separate frequencyの場合のレジスタ割り当ては、
			 *	0xa2, 0xa6 = Ch3 Op1
			 *	0xa8, 0xac = Ch3 Op2
			 *	0xa9, 0xad = Ch3 Op3
			 *	0xaa, 0xae = Ch3 Op4
			 *   と記載されていますが、既存のYM2612エミュレータソースを参考にすると、正しくは次のような割り当てになっているようです。
			 *	0xa2, 0xa6 = Ch3 Op4
			 *	0xa8, 0xac = Ch3 Op3
			 *	0xa9, 0xad = Ch3 Op1
			 *	0xaa, 0xae = Ch3 Op2
			 */
			if(ym2612->ch3_mode) {			/* Channel 3 has 4 separate frequencyで */
				if(!i_part && (i_ch == 2)) {	/* チャネル3ならば                      */
					if(i_op != 3) {		/* オペレータ4以外は設定しない。        */
						continue;
					}
				}
			}

			/* マルチプルを適用し、速度を設定します。 */
			if(op->mul) {
				op->pg_spd = pg_spd * op->mul;
			} else {
				op->pg_spd = pg_spd / 2;
			}
		}

		break;

	case 0xa8 >> 2: /* Ch3 supplementary frequency number LSB */

		/* Channel 6は4 separate frequencyモードを持っていません。(すぐ上のコメント参照)
		 * 従って、Part IIにはレジスタ0xa8..0xafはありません。
		 */
		if(i_part) {
			return;
		}

		/* F-number[7..0]を格納します。 */
		ym2612->ch3_f_number_lsb[i_reg & 3] = data;
		data = -1; /* すぐ下でのレジスタ格納を抑制 */

		/* FALLTHRU */

	case 0xac >> 2: /* Ch3 supplementary block, Ch3 supplementary frequency number MSB */

		/* Channel 6は4 separate frequencyモードを持っていません。(すぐ上のコメント参照)
		 * 従って、Part IIにはレジスタ0xa8..0xafはありません。
		 */
		if(i_part) {
			return;
		}

		/* 上からのFALLTHRUでなければ、F-number[10..8]とBlockを格納します。 */
		if(data != -1) {
			ym2612->ch3_f_number_msb[i_reg & 3] = data & 7;
			ym2612->ch3_block[i_reg & 3] = (data >> 3) & 7;
		}

		/* Channel 3 has 4 separate frequencyでなければ何もしません。 */
		if(!ym2612->ch3_mode) {
			return;
		}

		/* チャネル3、オペレータ1..3を選択します。(2..4ではない!!すぐ上のコメント参照) */
		i_ch = 2;
		ch = &ym2612->ch[i_part][i_ch];
		switch(i_reg & 3) { /* ここだけレジスタ並びが変則的です (すぐ上のコメント参照) */
		case 0:       i_op = 2; break; /* 0xa8, 0xac = Ch3 Op3 */
		case 1:       i_op = 0; break; /* 0xa9, 0xad = Ch3 Op1 */
		default/*2*/: i_op = 1; break; /* 0xaa, 0xae = Ch3 Op2 */
		}
		op = &ch->op[i_op];

		/* 基準速度を求めます。 */
		f_number = ym2612->ch3_f_number_lsb[i_reg & 3] | (ym2612->ch3_f_number_msb[i_reg & 3] << 8);
		block    = ym2612->ch3_block[i_reg & 3];
		pg_spd   = (ym2612->f_sam * (f_number << 3)) / ((SPEAKER_FREQUENCY >> 7) << (7 - block));

		/* マルチプルを適用し、速度を設定します。 */
		if(op->mul) {
			op->pg_spd = pg_spd * op->mul;
		} else {
			op->pg_spd = pg_spd / 2;
		}

		break;

	case 0xb0 >> 2: /* Feedback, Algorithm */

		/* アルゴリズムに応じたミキシング関数を設定します。 */
		ch->mix = ym2612ch_mix_table[data & 7];

		/* フィードバックを設定します。 */
		ch->fb = (data >> 3) & 7;

		break;

	case 0xb4 >> 2: /* L, R, AMS, FMS */

		/* パンを設定します。 */
		ch->lr = (data >> 6) & 3;

		break;
	}
}

void
ym2612_mix(YM2612* ym2612, short wbuff[/*count*/], int count)
{
#define MIXBUFLEN 64 /* 調整可 */

	static int mixbuf[MIXBUFLEN];
	//
	int dac;
	int dac_en;
	int len;
	int n;
	int* p;
	YM2612CH* ch;

	/* DAC en、DACを取得します。 */
	dac_en = ym2612->dac_en;
	if(dac_en) {
		dac = ym2612->dac; /* 格納時にスケーリング済み */
	} else {
		dac = 0;
	}

	/* 残りサンプル数がなくなるまで... */
	while(count > 0) {

		/* 残りサンプル数をMIXBUFLEN毎に分割します。 */
		len = count;
		if(len > MIXBUFLEN) {
			len = MIXBUFLEN;
		}
		count -= len;

		/* ミキシングバッファを0またはDAC出力値でクリアします。 */
		n = len;
		p = mixbuf;
		do {
			*p++ = dac;
		} while(--n);

		/* ミキシングを行います。 */
		/* * LRの少なくとも一方がOnならばミキシングを行います。
		 *   片方Onでも両方Onでもボリュームに違いはありません。
		 */
		ch = &ym2612->ch[0][0];
		if(ch->lr) ch->mix(ch, mixbuf, len);	/* Ch1 */
		ch++;
		if(ch->lr) ch->mix(ch, mixbuf, len);	/* Ch2 */
		ch++;
		if(ch->lr) ch->mix(ch, mixbuf, len);	/* Ch3 */
		ch++;
		if(ch->lr) ch->mix(ch, mixbuf, len);	/* Ch4 */
		ch++;
		if(ch->lr) ch->mix(ch, mixbuf, len);	/* Ch5 */
		ch++;
		if(!dac_en) {				/* Ch6 */
			if(ch->lr) ch->mix(ch, mixbuf, len);
		}

		/* ミキシングバッファから出力バッファへ。 */
		wbuff = ym2612_copy(wbuff, mixbuf, len);
	}

#undef MIXBUFLEN
}

