/*	
 *	frampky.c
 *
 *	P/ECE POKEY(CO12294) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Mon Jan 26 20:20:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

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

short
pokey_process(POKEY* pokey, int cycle)
{
	int step, v;

	while(cycle) {
		/* いずれかのチャネルが出力反転するまでの最短サイクル数を取得します。 */
		                                      step = pokey->channel[0].progress;
		if(pokey->channel[1].progress < step) step = pokey->channel[1].progress;
		if(pokey->channel[2].progress < step) step = pokey->channel[2].progress;
		if(pokey->channel[3].progress < step) step = pokey->channel[3].progress;

		if(cycle < step) {
			/* 残りサイクル数以内でなければ、反転処理は不要です。 */
			step = cycle; /* 残りサイクルを全て消費 */
			//pokey->lfsr_progress += step;
			//pokey_channel_process(pokey, &pokey->channel[0], step);
			//pokey_channel_process(pokey, &pokey->channel[1], step);
			//pokey_channel_process(pokey, &pokey->channel[2], step);
			//pokey_channel_process(pokey, &pokey->channel[3], step);
			//↑こうやっても結果は同じですが、
			//↓処理時間短縮のためにこうします。
			pokey->lfsr_progress += step;
			pokey->channel[0].progress -= step;
			pokey->channel[1].progress -= step;
			pokey->channel[2].progress -= step;
			pokey->channel[3].progress -= step;

		} else { /* cycle >= step */
			/* 残りサイクル数以内ならば、出力反転処理を行います。 */
			pokey->lfsr_progress += step;
			pokey_channel_process(pokey, &pokey->channel[0], step);
			pokey_channel_process(pokey, &pokey->channel[1], step);
			pokey_channel_process(pokey, &pokey->channel[2], step);
			pokey_channel_process(pokey, &pokey->channel[3], step);
		}

		/* ※注意:
		 * 「pokey->lfsr_progress += step;」の処理はここにまとめても同じように思うのですが、
		 * なぜかここへ移すだけで音色が変わり、ノイズっぽい間違った音色になってしまいます。
		 * pokey_channel_process()の先にやるか後でやるかだけの違いなのですが・・・謎です。
		 */

		/* 残りサイクル数を減らします。 */
		cycle -= step;
	}

	/* チャネル1〜4の出力レベルを合計します。 */
	v = pokey->channel[0].output	/*  符号付き5bit */
	  + pokey->channel[1].output	/* +符号付き5bit */
	  + pokey->channel[2].output	/* +符号付き5bit */
	  + pokey->channel[3].output;	/* +符号付き5bit */
					/* =符号付き7bit */

	/* スケーリング、クリッピングします。 */
	v <<= (16 - 7) + 1/*調整可*/;
	if(v < -0x7fff) v = -0x7fff;
	if(v >  0x7fff) v =  0x7fff;

	return v;
}

void
pokey_channel_process(POKEY* pokey, POKEYCHANNEL* channel, int cycle)
{
	if(channel->volume_only) return;
	channel->progress -= cycle;
	while(channel->progress <= 0) {
		channel->progress += channel->period;
		switch(channel->distotion) {
		case 0: /* 5bit & 17(9)bit */
			if(pokey_lfsr_signal5(pokey) && pokey_lfsr_signal17_9(pokey)) channel->signal = !channel->signal;
			break;
		case 1:
		case 3: /* 5bit */
			if(pokey_lfsr_signal5(pokey)                                ) channel->signal = !channel->signal;
			break;
		case 2: /* 5bit & 4bit */
			if(pokey_lfsr_signal5(pokey) && pokey_lfsr_signal4(pokey)   ) channel->signal = !channel->signal;
			break;
		case 4: /* 17(9)bit */
			if(pokey_lfsr_signal17_9(pokey)                             ) channel->signal = !channel->signal;
			break;
		case 6: /* 4bit */
			if(pokey_lfsr_signal4(pokey)                                ) channel->signal = !channel->signal;
			break;
		default:/* case 5,7: PureTone */
			channel->signal = !channel->signal;
			break;
		}
		channel->output = channel->signal ?  channel->volume
						  : -channel->volume;
	}
}

/* 4bit LFSRの出力値を取得します。 */
int
pokey_lfsr_signal4(POKEY* pokey)
{
	unsigned u = pokey->lfsr_progress;
	u %= (1 << 4) - 1;
	u = pokey_lfsr_table4[u / 8] >> (u & 7) & 1;
	return u;
}

/* 5bit LFSRの出力値を取得します。 */
int
pokey_lfsr_signal5(POKEY* pokey)
{
	unsigned u = pokey->lfsr_progress;
	u %= (1 << 5) - 1;
	u = pokey_lfsr_table5[u / 8] >> (u & 7) & 1;
	return u;
}

/* 17or9bit LFSRの出力値を取得します。 */
int
pokey_lfsr_signal17_9(POKEY* pokey)
{
	unsigned u = pokey->lfsr_progress;
	if(pokey->reg[8] & 0x80) { /* 9bit */
		u %= (1 << 9) - 1;
		u = pokey_lfsr_table9[u / 8] >> (u & 7) & 1;
	} else { /* 17bit */
		u %= (1 << 17) - 1;
		u = pokey_lfsr_table17[u / 8] >> (u & 7) & 1;
	}
	return u;
}

/* * pokey_lfsr_signal*()が剰余を多用するので、剰余ライブラリを高速RAMに置きます。
 *   また、__umodsi3がpokey_lfsr_signal*()の前後256バイト以内にあれば、call命令がext不要になります。
 *   できるだけpokey_lfsr_signal*()の「直後」に置いてください。
 *   「直前」でなく「直後」とした理由は、__umodsi3のサイズが距離に加算されない分、
 *   「直後」の方が距離が近くなる可能性が高いからです。
 *   全ての__umodsi3呼び出しが、256バイト以内となっていることを確認しました。(2004/01/28現在)
 * * ここで定義した__umodsi3は、グローバルの__umodsi3を★置き換えません★。
 *   「.global __umodsi3」を宣言していないので、このモジュール内でのみ有効です。
 *   「.global __umodsi3」を宣言すると、EPSONライブラリとぶつかってしまいます。
 */
#ifdef PIECE
asm("
	.code
	.align 2
	; .global 宣言しちゃダメ!
__umodsi3:
	ld.w %alr, %r12
	div0u %r13
	div1 %r13		;  1
	div1 %r13		;  2
	div1 %r13		;  3
	div1 %r13		;  4
	div1 %r13		;  5
	div1 %r13		;  6
	div1 %r13		;  7
	div1 %r13		;  8
	div1 %r13		;  9
	div1 %r13		; 10
	div1 %r13		; 11
	div1 %r13		; 12
	div1 %r13		; 13
	div1 %r13		; 14
	div1 %r13		; 15
	div1 %r13		; 16
	div1 %r13		; 17
	div1 %r13		; 18
	div1 %r13		; 19
	div1 %r13		; 20
	div1 %r13		; 21
	div1 %r13		; 22
	div1 %r13		; 23
	div1 %r13		; 24
	div1 %r13		; 25
	div1 %r13		; 26
	div1 %r13		; 27
	div1 %r13		; 28
	div1 %r13		; 29
	div1 %r13		; 30
	div1 %r13		; 31
	div1 %r13		; 32
	;div2u %r13		; 符号無し除算の後処理はありません
	;div3u			; 符号無し除算の後処理はありません
	ret.d
	ld.w %r10, %ahr		; *delay*(undoc'd)
");
#endif /*PIECE*/

