/*	
 *	framsid.c
 *
 *	P/ECE SID(MOS6581) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2003 Naoyuki Sawa
 *
 *	* Sat Dec 28 21:25:00 JST 2003 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

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

/* 出力: 符号なし8bit */
int
sidto_process(SIDTO* to, int step)
{
	/* SID仕様より
	 *	Fout = Fn * Fclk / 16777216 [Hz]
	 *	     = Fn * Fclk >> 24
	 * トーンオシレータ出力を24bitとすると、
	 *	TOout = Fn * Fclk
	 * トーンオシレータ出力を8bitとすると、
	 *	TOout = Fn * Fclk >> 16
	 * Fn = freq, Fclk増分 = step なので、
	 *	progress += freq * step
	 *	TOout = (short)(progress >> 16)
	 */
	to->progress += to->freq/*16bit*/ * step/*7bit(1M/16K=62前後)*/;
	return (unsigned char)(to->progress >> 16);
}

/* 出力: 符号付き8bit */
int
sidwg_process(SIDWG* wg, int value)
{
	int wf = wg->wf;

	/* ※TODO: 複数Waveformの組み合わせは未対応です。 */
	if(wf & SIDWG_TRIANGLE) {
		//					/*    0〜127,128〜 255 */
		if(value > 127) value = 255 - value;	/*    0〜127,127〜   0 */
		value -= 64;				/*  -64〜 63, 63〜 -64 */
		value <<= 1;				/* -128〜126,126〜-128 */
		value++;				/* -127〜127,127〜-127 */
	} else if(wf & SIDWG_SAWTOOTH) {
		//					/* 0〜127, 128〜 255 */
		value = (char)value;			/* 0〜127,-128〜  -1 */
	} else if(wf & SIDWG_PULSE) {
		//					/*   0〜 255 */
		value <<= 4;				/*   0〜4080 */
		value = value < wg->pw ? 127 : -127;	/* 127〜-127 */
	} else if(wf & SIDWG_NOISE) {
		value = sid_noise_table[value];
	} else {
		value = 0;
	}

	return value;
}

/* 出力: 符号なし4bit */
int
sideg_process(SIDEG* eg, int step)
{
	int value;
	int progress;
	int rate;

	/* ちょっと手抜きな実装で、毎回除算が入ってしまってます… */

	progress = eg->progress + step;
	switch(eg->cycle) {
	case SIDEG_ATTACK:
		rate = sid_attack_table[eg->atk];
		if(progress < rate) {
			value = 16/*15じゃないよ!*/ * progress / rate;
			break;
		}
		progress -= rate;
		eg->cycle = SIDEG_DECAY;
		/* FALL THRU */
	case SIDEG_DECAY:
		rate = sid_decay_release_table[eg->dcy];
		if(progress < rate) {
			value = 15 - (15 - eg->stn) * progress / rate;
			break;
		}
		progress -= rate;
		eg->cycle = SIDEG_SUSTAIN;
		/* FALL THRU */
	case SIDEG_SUSTAIN:
		value = eg->stn;
		break;
	case SIDEG_RELEASE:
		rate = sid_decay_release_table[eg->rls];
		if(progress < rate) {
			value = eg->stn - eg->stn * progress / rate;
			break;
		}
		eg->cycle = 0;
		/* FALL THRU */
	default:
		value = 0;
		break;
	}
	eg->progress = progress;

	return value;
}

/* * sideg_process()が除算を多用するので、除算ライブラリを高速RAMに置きます。
 *   また、__divsi3がsideg_process()の前後256バイト以内にあれば、call命令がext不要になります。
 *   できるだけsideg_process()の「直後」に置いてください。
 *   「直前」でなく「直後」とした理由は、__divsi3のサイズが距離に加算されない分、
 *   「直後」の方が距離が近くなる可能性が高いからです。
 *   全ての__divsi3呼び出しが、256バイト以内となっていることを確認しました。(2003/12/28現在)
 * * ここで定義した__divsi3は、グローバルの__divsi3を★置き換えません★。
 *   「.global __divsi3」を宣言していないので、このモジュール内でのみ有効です。
 *   「.global __divsi3」を宣言すると、EPSONライブラリとぶつかってしまいます。
 */
#ifdef PIECE
asm("
	.code
	.align 2
	; .global 宣言しちゃダメ!
__divsi3:
	ld.w %alr, %r12
	div0s %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
	div2s %r13
	div3s
	ret.d
	ld.w %r10, %alr		; *delay*(undoc'd)
");
#endif /*PIECE*/

/* 出力: 符号付き12bit */
int
sid_voice_process(SIDVOICE* voice, int step)
{
	int value;

	value = sidto_process(&voice->to, step);	/* 符号なし 8bit */
	value = sidwg_process(&voice->wg, value);	/* 符号付き 8bit */
	value *= sideg_process(&voice->eg, step);	/* 符号付き12bit */

	return value;
}

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

int
sid_mix1(SID* sid, int* pstep)
{
	int step;
	int value;
	int i_voice;
	SIDVOICE* voice;

	/* 何クロック進むか? */
	step = sid->clock.u;
	sid->clock.d += sid->clock.n;
	if(sid->clock.d >= 0) {
		sid->clock.d -= SPEAKER_FREQUENCY;
		step++;
	}
	*pstep = step; /* 経過クロック数を格納 */

	/* VOICE1〜3の出力を合成します。 */
	value = 0;
	voice = sid->voice;
	for(i_voice = 0; i_voice < 3; i_voice++) {
		value += sid_voice_process(voice, step);
		voice++;
	}
	/* value: 符号付き12bit × 3 = 符号付き13.5bit */
	/* vol  : 符号なし 4bit */
	value *= sid->vol; /* 符号付き17.5bit */

	return value;
}

