/*	
 *	clipsn7.c
 *
 *	P/ECE SN76489/SN76496(DCSG) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Mon Jan 24 18:30:00 JST 2005 Naoyuki Sawa
 *	- 高速化のために大幅な改造を行いました。
 *	  VGM Player全体で2.5倍程度速くなったので、単体ではもっと速くなっていると思います。
 *	- 動作実績のある旧コードは keep/SN76489改造前保存-20050124.zip に保存してあります。
 */
#include "clip.h"

/****************************************************************************
 *	
 ****************************************************************************/

void
sn76489_reset(SN76489* sn, int clock)
{
	memset(sn, 0, sizeof(SN76489));

	/* 入力クロックを格納します。 */
	sn->clock = clock;
}

void
sn76489_write(SN76489* sn, unsigned char data)
{
	int i_channel;
	SN76489TONE* tone;
	SN76489NOISE* noise;

	if(data & 0x80) {
		// data: 76543210
		//       ||||++++- 周期[3:0] or 音量
		//       |||+----- 0=周期[3:0]設定,1=音量設定
		//       |++------ チャネル番号(0..2:Tone,3:Noise)
		//       +-------- 1
		i_channel = sn->i_channel = (data >> 5) & 3;
		if(i_channel <= 2) {
			tone = &sn->tone[i_channel];
			if(data & 0x10) {
				/* 音量を格納します。
				 * 設定値は(小:15..0:大)なので、あらかじめ反転しておきます。
				 */
				tone->volume = ~data & 0xf;
			} else {
				/* 周期の下位ビットを格納します。 */
				tone->period = (tone->period & ~0xf) | (data & 0xf);
			}
		} else {
			noise = &sn->noise;
			if(data & 0x10) {
				/* 音量を格納します。
				 * 設定値は(小:15..0:大)なので、あらかじめ反転しておきます。
				 */
				noise->volume = ~data & 0xf;
			} else {
				/* 周期を格納します。 */
				switch(data & 3) {
				case 0:  noise->period = 0x20; break;
				case 1:  noise->period = 0x40; break;
				case 2:  noise->period = 0x80; break;
				default: noise->period = 0x00; break; /* Tone2の周期を使用 */
				}
				/* フィードバックタイプを格納します。 */
				noise->fbtype = (data >> 2) & 1;
				/* LFSRを初期化します。
				 * ホワイトノイズの場合、0以外ならば何でも構いません。
				 * 周期ノイズの場合、デューティー比を1:15にするため1必須です。
				 * 従って、1に初期化することにします。
				 */
				noise->lfsr = 1; /* 必要!! */
			}
		}
	} else {
		// data: 76543210
		//       ||++++++- 周期[9:4]
		//       |+------- (未使用)
		//       +-------- 0
		i_channel = sn->i_channel;
		if(i_channel <= 2) {
			tone = &sn->tone[i_channel];
			/* 周期の上位ビットを格納します。 */
			tone->period = (tone->period & 0xf) | ((data & 0x3f) << 4);
		}
	}
}

void
sn76489_mix(SN76489* sn, short wbuff[/*count*/], int count)
{
	/* まず出力バッファをクリアします。 */
	memset(wbuff, 0, sizeof(short) * count);

	/* 要求サンプル数が0以下ならば、何もしません。 */
	if(count <= 0) {
		return;
	}

	/* 各チャネルの出力波形を合成します。 */
	sn76489_tone_mix (sn, &sn->tone[0], wbuff, count);
	sn76489_tone_mix (sn, &sn->tone[1], wbuff, count);
	sn76489_tone_mix (sn, &sn->tone[2], wbuff, count);
	sn76489_noise_mix(sn, &sn->noise  , wbuff, count);

	/* 合成された出力波形をスケーリングします。 */
	sn76489_volume_shift(wbuff, count);
}
