#include <windows.h>
#include "ao.h"
#include "cpuintrf.h"
#include "../scsp.h"
#include "../scspdsp.h"

#define PACK_TEST 0

#if PACK_TEST

static UINT16 PACK(INT32 val)
{
	UINT32 temp;
	int sign,exponent,k;

	sign = (val >> 23) & 0x1;
	temp = (val ^ (val << 1)) & 0xFFFFFF;
	exponent = 0;
	for (k=0; k<12; k++)
	{
		if (temp & 0x800000)
			break;
		temp <<= 1;
		exponent += 1;
	}
	if (exponent < 12)
		val = (val << exponent) & 0x3FFFFF;
	else
		val <<= 11;
	val >>= 11;
	val &= 0x7FF;
	val |= sign << 15;
	val |= exponent << 11;

	return (UINT16)val;
}

static INT32 UNPACK(UINT16 val)
{
	int sign,exponent,mantissa;
	INT32 uval;

	sign = (val >> 15) & 0x1;
	exponent = (val >> 11) & 0xF;
	mantissa = val & 0x7FF;
	uval = mantissa << 11;
	if (exponent > 11)
	{
		exponent = 11;
		uval |= sign << 22;
	}
	else
		uval |= (sign ^ 1) << 22;
	uval |= sign << 23;
	uval <<= 8;
	uval >>= 8;
	uval >>= exponent;

	return uval;
}

extern UINT16 DSPX86_PACK(INT32 val);
extern INT32 DSPX86_UNPACKC(UINT16 val);
extern INT32 DSPX86_UNPACK(UINT16 val);

void pack_test()
{
	while (1)
	{
		INT32 iv = (rand() ^ (rand () << 16)) & 0xffffff;
		UINT16 p0 = PACK(iv);
		UINT16 p1 = DSPX86_PACK(iv);
		INT32 u0 = UNPACK(p0);
		INT32 u1 = DSPX86_UNPACK(p0);
		INT32 uc = DSPX86_UNPACKC(p0);
		if (p0 != p1 || u0 != u1 || u0 != uc)
		{
			OutputDebugStringA("failed");
		}
	}
}

#endif


typedef void (__cdecl * lpcodex86)(struct _SCSPDSP *DSP);

static void __cdecl gen_nullstep(struct _SCSPDSP *DSP)
{
	memset(DSP->EFREG,0,2*16);
	--DSP->DEC;
	memset(DSP->MIXS,0,4*16);
}

static lpcodex86 pcodex86 = gen_nullstep;
static unsigned lcodebuf = 0;
static void *pcodebuf = 0;
static int have_cmov = -1;
static int dsp_update = 0;

extern int DSPX86_CHECK_CMOV(void);

static void *code_alloc(unsigned l)
{
	return VirtualAlloc(0, l, MEM_COMMIT, PAGE_READWRITE);
}
static void code_writable(void *p, unsigned l)
{
	DWORD old;
	VirtualProtect(p, l, PAGE_READWRITE, &old);
}
static void code_executable(void *p, unsigned l1, unsigned l2)
{
	DWORD old;
	VirtualProtect(p, l1, PAGE_EXECUTE_READ, &old);
	FlushInstructionCache(GetCurrentProcess(), p, l2);

}
static void code_free(void *p, unsigned l)
{
	VirtualFree(p, 0, MEM_RELEASE);
}

void SCSPDSP_Init(struct _SCSPDSP *DSP)
{
#if PACK_TEST
	pack_test();
#endif
	SCSPDSP_Term(DSP);
	
	if (have_cmov < 0)
		have_cmov = DSPX86_CHECK_CMOV();

	memset(DSP,0,sizeof(struct _SCSPDSP));
	DSP->RBL=0x8000;
	DSP->Stopped=1;
}

static void gen_free()
{
	if (pcodebuf)
	{
		code_free(pcodebuf, lcodebuf);
		pcodebuf = 0;
		lcodebuf = 0;
	}
	pcodex86 = gen_nullstep;
}

void SCSPDSP_Term(struct _SCSPDSP *DSP)
{
	gen_free();
}

static INLINE unsigned getdwordle(void *ptr)
{
	return *(unsigned *)ptr;
}
static INLINE void setdwordle(void *ptr, unsigned dw)
{
	((unsigned *)ptr)[0] = dw;
}
static void adddwordle(void *ptr, unsigned dw)
{
	setdwordle(ptr, getdwordle(ptr) + dw);
}


#define DSPFN(x) \
extern unsigned DSPX86_S_##x; \
extern char DSPX86_C_##x[];

#define DSPFNP(x) \
extern unsigned DSPX86_S_##x; \
extern unsigned DSPX86_P_##x; \
extern char DSPX86_C_##x[];

DSPFN(ENTRY)
DSPFNP(IN_0) /* 1 */
DSPFNP(IN_1) /* 1 */
DSPFN(IN_2)
DSPFN(IN_3)
DSPFN(IN_4)
DSPFNP(IW_0) /* 1 */
DSPFN(IW_1)
DSPFN(OB_0)
DSPFNP(OB_1) /* 1 */
DSPFNP(OB_1O) /* 1 */
DSPFN(OB_2)
DSPFN(OB_3)
DSPFN(OX_0)
DSPFNP(OX_1) /* 1 */
DSPFNP(OX_1O) /* 1 */
DSPFN(OY_0)
DSPFNP(OY_1) /* 1 */
DSPFN(OY_2)
DSPFN(OY_3)
DSPFN(OY_4)
DSPFN(OS_0)
DSPFN(OS_0C)
DSPFN(OS_1)
DSPFN(OS_1C)
DSPFN(OS_2)
DSPFN(OS_3)
DSPFN(OA_0)
DSPFNP(TW_0) /* 1 */
DSPFNP(TW_0O) /* 1 */
DSPFN(OF_0)
DSPFN(OF_1)
DSPFNP(OM_0) /* 1 */
DSPFN(OM_1)
DSPFN(OM_2)
DSPFN(OM_3)
DSPFN(OM_4)
DSPFN(OM_5)
DSPFN(OM_6)
DSPFN(OM_6AICA)
DSPFN(OM_7)
DSPFN(OM_8)
DSPFN(OM_8C)
DSPFN(OM_9)
DSPFN(OM_A)
DSPFN(OR_0)
DSPFN(OR_1)
DSPFNP(EW_0) /* 1 */
DSPFN(EXIT)

#define DSPGEN(x) { \
	if (gen) \
	{ \
		memcpy(gen, DSPX86_C_##x, DSPX86_S_##x); \
		gen += DSPX86_S_##x; \
	} \
	size += DSPX86_S_##x; \
}
#define DSPGENP(x,p) { \
	if (gen) \
	{ \
		memcpy(gen, DSPX86_C_##x, DSPX86_S_##x); \
		adddwordle(gen + DSPX86_P_##x, p); \
		gen += DSPX86_S_##x; \
	} \
	size += DSPX86_S_##x; \
}

#if 1
#define DSPDUMP(x) {}
#else
#include <stdio.h>
static char dbuf[1024];
static void dump(const char *x)
{
	FILE *dfp = fopen("c:\\x\\dspdump.txt","a");
	if (dfp)
	{
		fputs(x, dfp);
		fputs("\n", dfp);
		fclose(dfp);
	}
}
#define DSPDUMP(x) { sprintf x ; dump(dbuf); }
#endif

static unsigned gen_build(struct _SCSPDSP *DSP, UINT16 *MPRO, int LastStep, char *gen)
{
	unsigned size = 0;
	int step;

	DSPDUMP((dbuf,">"))
	DSPGEN(ENTRY)

	for(step = 0; step < LastStep; ++step)
	{
		UINT16 *IPtr= MPRO + step*4;

//      if(IPtr[0]==0 && IPtr[1]==0 && IPtr[2]==0 && IPtr[3]==0)
//          break;

		UINT32 TRA=(IPtr[0]>>8)&0x7F;
		UINT32 TWT=(IPtr[0]>>7)&0x01;
		UINT32 TWA=(IPtr[0]>>0)&0x7F;

		UINT32 XSEL=(IPtr[1]>>15)&0x01;
		UINT32 YSEL=(IPtr[1]>>13)&0x03;
		UINT32 IRA=(IPtr[1]>>6)&0x3F;
		UINT32 IWT=(IPtr[1]>>5)&0x01;
		UINT32 IWA=(IPtr[1]>>0)&0x1F;

		UINT32 TABLE=(IPtr[2]>>15)&0x01;
		UINT32 MWT=(IPtr[2]>>14)&0x01;
		UINT32 MRD=(IPtr[2]>>13)&0x01;
		UINT32 EWT=(IPtr[2]>>12)&0x01;
		UINT32 EWA=(IPtr[2]>>8)&0x0F;
		UINT32 ADRL=(IPtr[2]>>7)&0x01;
		UINT32 FRCL=(IPtr[2]>>6)&0x01;
		UINT32 SHIFT=(IPtr[2]>>4)&0x03;
		UINT32 YRL=(IPtr[2]>>3)&0x01;
		UINT32 NEGB=(IPtr[2]>>2)&0x01;
		UINT32 ZERO=(IPtr[2]>>1)&0x01;
		UINT32 BSEL=(IPtr[2]>>0)&0x01;

		UINT32 NOFL=(IPtr[3]>>15)&1;		//????
		UINT32 COEF=(IPtr[3]>>9)&0x3f;

		UINT32 MASA=(IPtr[3]>>2)&0x1f;	//???
		UINT32 ADREB=(IPtr[3]>>1)&0x1;
		UINT32 NXADR=(IPtr[3]>>0)&0x1;

		//operations are done at 24 bit precision
		DSPDUMP((dbuf,"STEP %02x",step))

		//INPUTS RW
		if(IRA<=0x1f)
		{
			DSPDUMP((dbuf,"INPUTS=MEMS[%02x]",IRA))
			DSPGENP(IN_0, IRA)
		}
		else if(IRA<=0x2F)
		{
			DSPDUMP((dbuf,"INPUTS=MIXS[%02x]<<4",IRA-0x20))
			DSPGENP(IN_1, (IRA - 0x20))
		}
		else if(IRA<=0x31)
		{
			DSPDUMP((dbuf,"INPUTS=0"))
			DSPGEN(IN_2)
		}
		else
			DSPGEN(IN_3)
		DSPGEN(IN_4)

		if(IWT)
		{
			DSPDUMP((dbuf,"MEMS[%02x]=MEMVAL",IWA))
			DSPGENP(IW_0, IWA)
			if(IRA==IWA)
			{
				DSPDUMP((dbuf,"INPUTS=MEMVAL"))
				DSPGEN(IW_1)
			}
		}

		//Operand sel
		//B
		if(!ZERO)
		{
			if(BSEL)
			{
				if(NEGB)
					DSPDUMP((dbuf,"B=-ACC"))
				else
					DSPDUMP((dbuf,"B=ACC"))
				DSPGEN(OB_0)
			}
			else
			{
				if(NEGB)
					DSPDUMP((dbuf,"B=-TEMP[%02x]",TRA))
				else
					DSPDUMP((dbuf,"B=TEMP[%02x]",TRA))
#if ENABLE_XSF_TEST_DSP_DEC
				DSPGENP(OB_1, TRA)
#else
				DSPGENP(OB_1O, TRA)
#endif
			}
			if(NEGB)
				DSPGEN(OB_2)
		}
		else
		{
			DSPDUMP((dbuf,"B=0"))
			DSPGEN(OB_3)
		}

		//X
		if(XSEL)
		{
			DSPDUMP((dbuf,"X=INPUTS"))
			DSPGEN(OX_0)
		}
		else
		{
			DSPDUMP((dbuf,"X=TEMP[%02x]",TRA))
#if ENABLE_XSF_TEST_DSP_DEC
			DSPGENP(OX_1, TRA)
#else
			DSPGENP(OX_1O, TRA)
#endif
		}

		//Y
		if(YSEL==0)
		{
			DSPDUMP((dbuf,"Y=FRC_REG"))
			DSPGEN(OY_0)
		}
		else if(YSEL==1)
		{
			DSPDUMP((dbuf,"Y=(COEF[%02x]=%04x)>>3",COEF,DSP->COEF[COEF]))
			DSPGENP(OY_1, COEF)
		}
		else if(YSEL==2)
		{
			DSPDUMP((dbuf,"Y=Y_REG>>11"))
			DSPGEN(OY_2)
		}
		else if(YSEL==3)
		{
			DSPDUMP((dbuf,"Y=Y_REG>>4"))
			DSPGEN(OY_3)
		}

		if(YRL)
		{
			DSPDUMP((dbuf,"Y_REG=INPUTS"))
			DSPGEN(OY_4)
		}

		//Shifter
		if(SHIFT==0)
		{
			DSPDUMP((dbuf,"SHIFTED=S(ACC)"))
			if (have_cmov > 0)
				DSPGEN(OS_0C)
			else
				DSPGEN(OS_0)
		}
		else if(SHIFT==1)
		{
			DSPDUMP((dbuf,"SHIFTED=S(ACC*2)"))
			if (have_cmov > 0)
				DSPGEN(OS_1C)
			else
				DSPGEN(OS_1)
		}
		else if(SHIFT==2)
		{
			DSPDUMP((dbuf,"SHIFTED=ACC*2"))
			DSPGEN(OS_2)
		}
		else if(SHIFT==3)
		{
			DSPDUMP((dbuf,"SHIFTED=ACC"))
			DSPGEN(OS_3)
		}

		DSPDUMP((dbuf,"ACC=((X*Y) >> 12) + B"))
		DSPGEN(OA_0)

		if(TWT)
		{
			DSPDUMP((dbuf,"TEMP[%02x]=SHIFTED",TWA))
#if ENABLE_XSF_TEST_DSP_DEC
			DSPGENP(TW_0, TWA)
#else
			DSPGENP(TW_0O, TWA)
#endif
		}

		if(FRCL)
		{
			if(SHIFT==3)
			{
				DSPDUMP((dbuf,"FRC_REG=SHIFTED"))
				DSPGEN(OF_0)
			}
			else
			{
				DSPDUMP((dbuf,"FRC_REG=SHIFTED>11"))
				DSPGEN(OF_1)
			}
		}

		if(MRD || MWT)
		{
			DSPDUMP((dbuf,"ADDR=MADRS[%02x]=%04x",MASA,DSP->MADRS[MASA]))
			DSPGENP(OM_0, MASA)
			if(!TABLE)
			{
				DSPDUMP((dbuf,"ADDR+=DEC"))
				DSPGEN(OM_1)
			}
			if(ADREB)
			{
				DSPDUMP((dbuf,"ADDR+=ADRS_REG"))
				DSPGEN(OM_2)
			}
			if(NXADR)
			{
				DSPDUMP((dbuf,"ADDR++"))
				DSPGEN(OM_3)
			}
			if(!TABLE)
				DSPGEN(OM_4)
			else
				DSPGEN(OM_5)
			DSPGEN(OM_6)
			if(MRD && (step&1))	//memory only allowed on odd? DoA inserts NOPs on even
			{
				if(NOFL)
				{
					DSPDUMP((dbuf,"MEMVAL=DSPRAM[ADDR]"))
					DSPGEN(OM_7)
				}
				else if (have_cmov > 0)
				{
					DSPDUMP((dbuf,"MEMVAL=UNPACK(DSPRAM[ADDR])"))
					DSPGEN(OM_8C)
				}
				else
				{
					DSPDUMP((dbuf,"MEMVAL=UNPACK(DSPRAM[ADDR])"))
					DSPGEN(OM_8)
				}
			}
			if(MWT && (step&1))
			{
				if(NOFL)
				{
					DSPDUMP((dbuf,"DSPRAM[ADDR]=MEMVAL"))
					DSPGEN(OM_9)
				}
				else
				{
					DSPDUMP((dbuf,"DSPRAM[ADDR]=PACK(MEMVAL)"))
					DSPGEN(OM_A)
				}
			}
		}

		if(ADRL)
		{
			if(SHIFT==3)
			{
				DSPDUMP((dbuf,"ADRS_REG=SHIFTED>>12"))
				DSPGEN(OR_0)
			}
			else
			{
				DSPDUMP((dbuf,"ADRS_REG=INPUTS>>16"))
				DSPGEN(OR_1)
			}
		}

		if(EWT)
		{
			DSPDUMP((dbuf,"EFREG[%02x]+=SHIFTED>>8",EWA))
			DSPGENP(EW_0, EWA)
		}

	}

	DSPGEN(EXIT)

	return size;

}

void SCSPDSP_SetSample(struct _SCSPDSP *DSP,INT32 sample,int SEL,int MXL)
{
	DSP->MIXS[SEL]+=sample;
}

static void gen_step(struct _SCSPDSP *DSP)
{
	int i;
	for(i=127;i>=0;--i)
	{
		UINT16 *IPtr=DSP->MPRO+i*4;

		if(IPtr[0]!=0 || IPtr[1]!=0 || IPtr[2]!=0 || IPtr[3]!=0)
			break;
	}
	DSP->LastStep=i+1;
}

static void gen_run(struct _SCSPDSP *DSP)
{
	unsigned l;


	pcodex86 = gen_nullstep;

	if (DSP->LastStep > 0)
	{
		l = gen_build(DSP, DSP->MPRO, DSP->LastStep, 0);
		l = ((l + ((1 << 12) - 1)) >> 12) << 12;
		if (l > lcodebuf)
		{
			gen_free();
			pcodebuf = code_alloc(l);
			if (pcodebuf) lcodebuf = l;
		}

		if (pcodebuf)
		{
			code_writable(pcodebuf, lcodebuf);
			code_executable(pcodebuf, lcodebuf, gen_build(DSP, DSP->MPRO, DSP->LastStep, pcodebuf));
			pcodex86 = (lpcodex86)pcodebuf;
		}
	}
}

void SCSPDSP_Step(struct _SCSPDSP *DSP)
{
	if (DSP->Stopped)
		return;

	if (dsp_update)
	{
		dsp_update = 0;
#if DISABLE_XSF_TESTS
#else
		gen_step(DSP);
#endif
		gen_run(DSP);
	}

	if (pcodex86)
		pcodex86(DSP);
}

void SCSPDSP_Start(struct _SCSPDSP *DSP)
{
#if DISABLE_XSF_TESTS
	DSP->Stopped=0;
	gen_step(DSP);
#else
	gen_step(DSP);
#endif
}

void SCSPDSP_UpdateProgram(struct _SCSPDSP *DSP)
{
#if DISABLE_XSF_TESTS
#else
	DSP->Stopped=0;
#endif
	dsp_update = 1;
}
