/*	
 *	clipwsg.c
 *
 *	P/ECE Namco WSG Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Feb 15 04:29:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

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

void
wsg_reset(WSG* wsg, int clock, const void* wavetable/*[8][0x20]*/)
{
	int i;
	const char* src;
	      char* dst;

	memset(wsg, 0, sizeof(WSG));

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

	/* 波形テーブルROMがあれば、波形テーブルを初期化します。 */
	if(wavetable) {
		src = (const char*)wavetable;
		dst = &wsg->wavetable[0][0];
		for(i = 0; i < 0x20 * 8/*WSG.wavetable[16][0x20]の前半8音色分のみ*/; i++) {
			*dst++ = (*src++ & 0xf) - 8; /* 0〜15 => -8〜+7 */
		}
	}

	/* 各チャネルのWavetable選択を有効な状態に初期化します。
	 * これをやらないと、Wavetable選択レジスタに書き込む前にミキシングを行った場合に、
	 * NULL参照が発生してしまいます。
	 */
	for(i = 0; i < 8; i++) {
		wsg->channel[i].wave = wsg->wavetable[0]; /* 仮に、音色0を選択 */
	}
}

void
wsg_write_mappy(WSG* wsg, int addr, unsigned char data)
{
	int i_channel;
	WSGCHANNEL* channel;
	unsigned char* reg;

	if(addr < 0 || addr > 0x3f) DIE();

	/* レジスタ値格納。 */
	reg = wsg->reg;
	if(reg[addr] == data) return;	/* 変化無しなら何もしません */
	reg[addr] = data;

	/* チャネル設定の準備。 */
	i_channel = addr >> 3;			/* チャネル番号 (0〜7) */
	channel = &wsg->channel[i_channel];	/* チャネル */
	reg += i_channel << 3;			/* このチャネルのレジスタベース */
	addr &= 7;				/* レジスタベースからのオフセット (0〜7) */

	/* チャネル設定。 */
	switch(addr) {
	case 6:	/* ?wwwffff (f:Frequency[19:16], w=WavetableSelect) */
		channel->wave = wsg->wavetable[(reg[6] >> 4) & 7];
		/* fall thru */
	case 5:	/* ffffffff (f:Frequency[15: 8]) */
	case 4:	/* ffffffff (f:Frequency[ 7: 0]) */
		channel->frequency = ((reg[6] & 0xf) << 8 | reg[5]) << 8 | reg[4];
		break;
	case 3:	/* ????vvvv (v:Volume) */
		/* 最終出力が符号付き16bit(+調整分)となるよう、あらかじめボリュームをスケーリングしておきます。 */
		channel->volume = (reg[3] & 0xf) << WSG_VOLUME_SHIFT;
		break;
	}
}

/* ※未検証 */
void
wsg_write_system1(WSG* wsg, int addr, unsigned char data)
{
	int i_channel;
	WSGCHANNEL* channel;
	WSGCHANNEL* next_channel;
	unsigned char* reg;

	if(addr < 0 || addr > 0x13f) DIE();

	/* レジスタ値格納。 */
	reg = wsg->reg;
	if(reg[addr] == data) return;	/* 変化無しなら何もしません */
	reg[addr] = data;

	if(addr <= 0xff) { /* 0x000-0x0ff: Wavetable */
		/* 波形テーブル値を2サンプルに分解、(0〜15)→(-8〜+7)に変換して格納します。 */
		reg = &wsg->wavetable[0][0] + (addr << 1);
		reg[0] = (data >>  4) - 8; /* 上位4bit、   */
		reg[1] = (data & 0xf) - 8; /* 下位4bitの順 */

	} else { /* 0x100-0x13f: Register */
		/* チャネル設定の準備。 */
		addr -= 0x100;
		i_channel = addr >> 3;			/* チャネル番号 (0〜7) */
		channel = &wsg->channel[i_channel];	/* チャネル */
		reg += i_channel << 3;			/* このチャネルのレジスタベース */
		addr &= 7;				/* レジスタベースからのオフセット (0〜7) */

		/* チャネル設定。 */
		switch(addr) {
		case 1:	/* wwwwffff (w:WavetableSelect, f:Frequency[19:16]) */
			channel->wave = wsg->wavetable[reg[1] >> 4];
			/* fall thru */
		case 2:	/* ffffffff (f:Frequency[15: 8]) */
		case 3:	/* ffffffff (f:Frequency[ 7: 0]) */
			channel->frequency = ((reg[1] & 0xf) << 8 | reg[2]) << 8 | reg[3];
			break;
		case 4:	/* n???vvvv (v:VolumeRight, n:NextChannel's NoiseSelect) */
			next_channel = &wsg->channel[(i_channel + 1) & 7];
			if(reg[4] & 0x80) { /* Noise */
				/* Tone->Noiseに変化した場合のみ、LFSR初期化。 */
				if(!next_channel->noise) next_channel->noise = 1;
			} else { /* Tone */
				next_channel->noise = 0;
			}
			/* fall thru */
		case 0:	/* ????vvvv (v:VolumeLeft) */
			/* 最終出力が符号付き16bit(+調整分)となるよう、あらかじめボリュームをスケーリングしておきます。 */
			/* 左右のボリュームを足すので、倍率は半分(シフト数-1)とします。 */
			channel->volume = ((reg[0] & 0xf) + (reg[4] & 0xf)) << (WSG_VOLUME_SHIFT - 1);
			break;
		}
	}
}

/* ※未検証 */
void
wsg_write_xevious(WSG* wsg, int addr, unsigned char data)
{
	unsigned char* reg;

	if(addr < 0 || addr > 0x1f) DIE();

	/* 全レジスタ上位4bitゼロ固定。 */
	data &= 0xf;

	/* レジスタ値格納。 */
	reg = wsg->reg;
	if(reg[addr] == data) return;	/* 変化無しなら何もしません */
	reg[addr] = data;

	switch(addr) {
	/* [Ch0] */
	case 0x05: /* 0www (w:WavetableSelect) */
		wsg->channel[0].wave = wsg->wavetable[reg[0x05]];
		break;
	case 0x10: /* ffff (f:Frequency[ 3: 0]) */
	case 0x11: /* ffff (f:Frequency[ 7: 4]) */
	case 0x12: /* ffff (f:Frequency[11: 8]) */
	case 0x13: /* ffff (f:Frequency[15:12]) */
	case 0x14: /* ffff (f:Frequency[19:16]) */
		wsg->channel[0].frequency = (((reg[0x14] << 4 | reg[0x13]) << 4 | reg[0x12]) << 4 | reg[0x11]) << 4 | reg[0x10];
		break;
	case 0x15: /* vvvv (v:Volume) */
		/* 最終出力が符号付き16bit(+調整分)となるよう、あらかじめボリュームをスケーリングしておきます。 */
		wsg->channel[0].volume = reg[0x15] << WSG_VOLUME_SHIFT;
		break;
	/* [Ch1] */
	case 0x0a: /* 0www (w:WavetableSelect) */
		wsg->channel[1].wave = wsg->wavetable[reg[0x0a]];
		break;
	           /*   (Ch1のFrequency[ 3: 0]は0固定) */
	case 0x16: /* ffff (f:Frequency[ 7: 4]) */
	case 0x17: /* ffff (f:Frequency[11: 8]) */
	case 0x18: /* ffff (f:Frequency[15:12]) */
	case 0x19: /* ffff (f:Frequency[19:16]) */
		wsg->channel[1].frequency = (((reg[0x19] << 4 | reg[0x18]) << 4 | reg[0x17]) << 4 | reg[0x16]) << 4;
		break;
	case 0x1a: /* vvvv (v:Volume) */
		/* 最終出力が符号付き16bit(+調整分)となるよう、あらかじめボリュームをスケーリングしておきます。 */
		wsg->channel[1].volume = reg[0x1a] << WSG_VOLUME_SHIFT;
		break;
	/* [Ch2] */
	case 0x0f: /* 0www (w:WavetableSelect) */
		wsg->channel[2].wave = wsg->wavetable[reg[0x0f]];
		break;
	           /*   (Ch2のFrequency[ 3: 0]は0固定) */
	case 0x1b: /* ffff (f:Frequency[ 7: 4]) */
	case 0x1c: /* ffff (f:Frequency[11: 8]) */
	case 0x1d: /* ffff (f:Frequency[15:12]) */
	case 0x1e: /* ffff (f:Frequency[19:16]) */
		wsg->channel[2].frequency = (((reg[0x1e] << 4 | reg[0x1d]) << 4 | reg[0x1c]) << 4 | reg[0x1b]) << 4;
		break;
	case 0x1f: /* vvvv (v:Volume) */
		/* 最終出力が符号付き16bit(+調整分)となるよう、あらかじめボリュームをスケーリングしておきます。 */
		/* Xevious typeは同時発声数3なので、他のtype(同時発声数8)よりも倍率2倍(シフト数+1)とすることにします。 */
		wsg->channel[2].volume = reg[0x1f] << (WSG_VOLUME_SHIFT + 1);
		break;
	}
}

