/*	
 *	clipay3.c
 *
 *	P/ECE AY-3-8910 Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sat Feb 07 19:08:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Sun Mar 06 19:52:00 JST 2004 Naoyuki Sawa
 *	- 音量カーブの調整を行いました。
 *	  これまで、amplitude_level,envelope_levelは0〜15で、変化はリニアでしたが、
 *	  これではエンベロープ減衰がゆるやかすぎます。
 *	  そこで、それぞれ2乗して 0〜15^2 の値を格納することにしました。
 */
#include "clip.h"

/****************************************************************************
 *	内部関数
 ****************************************************************************/

unsigned char
ay38910_internal_in(AY38910* ay, int port)
{
	return -1;
}

void
ay38910_internal_out(AY38910* ay, int port, unsigned char data)
{
	/** no job **/
}

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

void
ay38910_reset(AY38910* ay, int clock, AY38910INPROC* in, AY38910OUTPROC* out)
{
	int i;

	memset(ay, 0, sizeof(AY38910));

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

	/* ポート入出力外部関数を格納。 */
	ay->in  = in  ? in  : ay38910_internal_in;
	ay->out = out ? out : ay38910_internal_out;

	/* ノイズLFSR初期化。(これ忘れるとノイズ音が鳴らなくなります!) */
	ay->noise_lfsr = 1;

	/* period!=0を保証するため、全レジスタを明示的に初期化します。 */
	for(i = 0; i < 16; i++) {
		ay38910_address(ay, i);
		ay38910_write(ay, 0);
	}
}

void
ay38910_address(AY38910* ay, int address)
{
	/* レジスタアドレス格納。 */
	ay->address = address & 0xf;
}

unsigned
char ay38910_read(AY38910* ay)
{
	unsigned char* reg = ay->reg;
	int address = ay->address;

	switch(address) {
	case 14: /* I/O Port A Data Store */
		reg[address] = ay->in(ay, 0);
		break;
	case 15: /* I/O Port B Data Store */
		reg[address] = ay->in(ay, 1);
		break;
	}

	return reg[address];
}

void
ay38910_write(AY38910* ay, unsigned char data)
{
	unsigned char* reg = ay->reg;
	int address = ay->address;

	//if(reg[address] == data) return; /* 変化なしなら、何もしません */
	//reg[address] = data; /* 変化ありなら、まず格納 */
	//↓修正。変化なしでも常に処理します。
	//↓Reg#13への書き込みは、変化の有無に関わらずエンベロープリセットの効果があるからです。
	//↓Reg#13だけ特別扱いする方法もありますが、全レジスタを無条件に処理することにしました。
	reg[address] = data;

	switch(address) {
	case 0: /* Channel A Tone Period (Fine Tune) */
	case 1: /* Channel A Tone Period (Coarse Tune) */
		ay->channel[0].tone_period = reg[0] | (reg[1] & 0xf) << 8;
		if(!ay->channel[0].tone_period) ay->channel[0].tone_period = 1;
		ay->channel[0].tone_period <<= 4 - 1;
		break;
	case 2: /* Channel B Tone Period (Fine Tune) */
	case 3: /* Channel B Tone Period (Coarse Tune) */
		ay->channel[1].tone_period = reg[2] | (reg[3] & 0xf) << 8;
		if(!ay->channel[1].tone_period) ay->channel[1].tone_period = 1;
		ay->channel[1].tone_period <<= 4 - 1;
		break;
	case 4: /* Channel C Tone Period (Fine Tune) */
	case 5: /* Channel C Tone Period (Coarse Tune) */
		ay->channel[2].tone_period = reg[4] | (reg[5] & 0xf) << 8;
		if(!ay->channel[2].tone_period) ay->channel[2].tone_period = 1;
		ay->channel[2].tone_period <<= 4 - 1;
		break;
	case 6: /* Noise Period */
		ay->noise_period = reg[6] & 0x1f;
		if(!ay->noise_period) ay->noise_period = 1; /* "S.T.U.N.Runner.ay"対策 */
		ay->noise_period <<= 4;
		break;
	case 7: /* #Enable */
		ay->channel[0].tone_enable  = !(reg[7] & (1 << 0));
		ay->channel[0].noise_enable = !(reg[7] & (1 << 3));
		ay->channel[1].tone_enable  = !(reg[7] & (1 << 1));
		ay->channel[1].noise_enable = !(reg[7] & (1 << 4));
		ay->channel[2].tone_enable  = !(reg[7] & (1 << 2));
		ay->channel[2].noise_enable = !(reg[7] & (1 << 5));
		ay->noise_enable = ay->channel[0].noise_enable ||
				   ay->channel[1].noise_enable ||
				   ay->channel[2].noise_enable;
		break;
	case 8: /* Channel A Amplitude */
		ay->channel[0].amplitude_mode = (reg[8] >> 4) & 1;
		ay->channel[0].amplitude_level = reg[8] & 0xf;
		ay->channel[0].amplitude_level *= ay->channel[0].amplitude_level; /* 音量カーブの調整 */
		ay->envelope_enable = ay->channel[0].amplitude_mode ||
				      ay->channel[1].amplitude_mode ||
				      ay->channel[2].amplitude_mode;
		break;
	case 9: /* Channel B Amplitude */
		ay->channel[1].amplitude_mode = (reg[9] >> 4) & 1;
		ay->channel[1].amplitude_level = reg[9] & 0xf;
		ay->channel[1].amplitude_level *= ay->channel[1].amplitude_level;  /* 音量カーブの調整 */
		ay->envelope_enable = ay->channel[0].amplitude_mode ||
				      ay->channel[1].amplitude_mode ||
				      ay->channel[2].amplitude_mode;
		break;
	case 10: /* Channel C Amplitude */
		ay->channel[2].amplitude_mode = (reg[10] >> 4) & 1;
		ay->channel[2].amplitude_level = reg[10] & 0xf;
		ay->channel[2].amplitude_level *= ay->channel[2].amplitude_level;  /* 音量カーブの調整 */
		ay->envelope_enable = ay->channel[0].amplitude_mode ||
				      ay->channel[1].amplitude_mode ||
				      ay->channel[2].amplitude_mode;
		break;
	case 11: /* Envelope Period (Fine Tune) */
	case 12: /* Envelope Period (Coarse Tune) */
		ay->envelope_period = reg[11] | reg[12] << 8;
		if(!ay->envelope_period) ay->envelope_period = 1;
		ay->envelope_period <<= 4;
		break;
	case 13: /* Envelope Shape Cycle */
		ay->envelope_shape_cycle = reg[13] & 0xf;
		/*{{エンベロープリセット。忘れないで!!*/
		ay->envelope_progress = 0; /* リセット直後、初回のay38910_process()でenvelope_progressがアンダーフローして... */
		ay->envelope_counter = -1; /* envelope_counterが0になります。そして、初回のenvelope_level設定が行われます。   */
		/*}}エンベロープリセット。忘れないで!!*/
		break;
	case 14: /* I/O Port A Data Store */
		ay->out(ay, 0, reg[14]);
		break;
	case 15: /* I/O Port B Data Store */
		ay->out(ay, 1, reg[15]);
		break;
	}
}

