/*	
 *	clipsds.c
 *
 *	P/ECE S-DSP Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Jan 06 06:00:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

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

void
sdsp_reset(SDSP* sdsp, const void* mem)
{
	memset(sdsp, 0, sizeof(SDSP));
	sdsp->mem = (const unsigned char*)mem;
}

void
sdsp_addr_write(SDSP* sdsp, unsigned char addr)
{
//#ifdef SPC_DEBUG
//	if(addr > 0x7f) {
//		TRACE("sdsp_addr_write: Invalid register address $%02x\n", addr);
//	}
//#endif /*SPC_DEBUG*/

	sdsp->addr = addr & 0x7f;
}

unsigned char
sdsp_addr_read(SDSP* sdsp)
{
	return sdsp->addr;
}

void
sdsp_data_write(SDSP* sdsp, unsigned char data)
{
	int addr = sdsp->addr;
	int addr_lo = addr & 0xf;
	int addr_hi = addr >> 4;
	unsigned char* preg = &sdsp->reg[addr];
	//
	int i_voice;
	SDSPVOICE* voice;

//#ifdef SPC_DEBUG
//	TRACE("sdsp_data_write: %02x %02x\n", addr, data);
//#endif /*SPC_DEBUG*/

	/* READされた場合のために格納しておきます。 */
	*preg = data;

	if(addr_lo <= 9) {
		/* 0x?0〜0x?9: Voice-? 設定レジスタ */
		voice = &sdsp->voice[addr_hi];
		switch(addr_lo) {
		case 0: /* VOL(L) */
			/* VOL(L,R)=-128(逆相MAX)〜0(MIN)〜128(順相MAX) => VOL(L+R)=0(MIN)〜256(MAX) */
			voice->vol = abs(data) + abs(*(preg + 1));
			break;
		case 1: /* VOL(R) */
			/* VOL(L,R)=-128(逆相MAX)〜0(MIN)〜128(順相MAX) => VOL(L+R)=0(MIN)〜256(MAX) */
			voice->vol = abs(data) + abs(*(preg - 1));
			break;
		case 2: /* P(L) */
			voice->p = (data | *(preg + 1) << 8) & 0x3fff;
			break;
		case 3: /* P(H) */
			voice->p = (data << 8 | *(preg - 1)) & 0x3fff;
			break;
		case 4: /* SRCN */
			voice->srcn = data << 2;	/* &(Source Directory)＝(DIR)×0x100＋(SRCN)×4 */
			break;
		case 5: /* ADSR(1) */
			voice->ar = data & 0xf;		/* bit3-0: Attack Rate */
			voice->dr = (data >> 4) & 7;	/* bit6-4: Decay Rate */
			voice->adsr_gain = data >> 7;	/* bit7  : ADSR/GAIN */
			break;
		case 6: /* ADSR(2) */
			voice->sr = data & 0x1f;	/* bit4-0: Sustain Rate */
			voice->sl = data >> 5;		/* bit7-5: Sustain Level */
			break;
		case 7: /* GAIN */
			voice->gain = data;
			break;
		//case 8: /* ENVX */ READ ONLY?
		//case 9: /* OUTX */ READ ONLY?
		}
	} else {
		/* 0x?c,0x?d,0x?f: 共通設定レジスタ */
		switch(addr_lo) {
		case 0xc:
			switch(addr_hi) {
			case 0: /* MVOL(L) */
				/* MVOL(L,R)=-128(逆相MAX)〜0(MIN)〜128(順相MAX) => MVOL(L+R)=0(MIN)〜256(MAX) */
				sdsp->mvol = abs(data) + abs(*(preg + 0x10/*1じゃないよ!*/));
				break;
			case 1: /* MVOL(R) */
				/* MVOL(L,R)=-128(逆相MAX)〜0(MIN)〜128(順相MAX) => MVOL(L+R)=0(MIN)〜256(MAX) */
				sdsp->mvol = abs(data) + abs(*(preg - 0x10/*1じゃないよ!*/));
				break;
			//case 2: /* EVOL(L) */
			//case 3: /* EVOL(R) */
			case 4: /* KON */
				for(i_voice = 0, voice = sdsp->voice; i_voice < 8; i_voice++, voice++) {
					if(data & 1) {
						voice->state = SDSVOICE_ATTACK;
						voice->progress = 0;
					}
					data >>= 1;
				}
				break;
			case 5: /* KOF */
				for(i_voice = 0, voice = sdsp->voice; i_voice < 8; i_voice++, voice++) {
					if(data & 1) {
						if(voice->state > SDSVOICE_RELEASE) {
							voice->state = SDSVOICE_RELEASE;
						}
					}
					data >>= 1;
				}
				break;
			case 6: /* FLG */
				if(data & 0x80) {		/* bit7  : RES */
					for(i_voice = 0, voice = sdsp->voice; i_voice < 8; i_voice++, voice++) {
						voice->state = 0;
					}
				}
				sdsp->mute = (data >> 6) & 1;	/* bit6  : MUTE */
				//				/* bit5  : ECEN (未対応) */
				sdsp->nck = data & 0x1f;	/* bit4-0: NCK */
				break;
			//case 7: /* ENDX */ READ ONLY?
			}
			break;
		case 0xd:
			switch(addr_hi) {
			//case 0: /* EFB */
			//case 1: /* (Not Used) */
			//case 2: /* PMON */
			case 3: /* NON */
				for(i_voice = 0, voice = sdsp->voice; i_voice < 8; i_voice++, voice++) {
					voice->non = data & 1;
					data >>= 1;
				}
				break;
			//case 4: /* EON */
			case 5: /* DIR */
				sdsp->dir = data << 8;	/* &(Source Directory)＝(DIR)×0x100＋(SRCN)×4 */
				break;
			//case 6: /* ESA */
			//case 7: /* EDL */
			}
			break;
		//case 0xf: /* C0-7 */
		}
	}
}

unsigned char
sdsp_data_read(SDSP* sdsp)
{
	return sdsp->reg[sdsp->addr];
}

