#include "driver.h"
#include "machine/scudsp.h"

/*SCU DSP functions */
/*V0.03*/

/* 
031211: (Mariusz Wojcieszek changes)
- result of ALU command is stored into ALU register 
- X-Bus command: MOV [s],X can be executed in parallel to other X-Bus commands
- Y-Bus command: MOV [s],Y can be executed in parallel to other Y-Bus commands
- Jump and LPS/BTM support: 
	jump addresses are absolute, 
	prefetched instructions are executed before jump is taken
- after each instruction, X and Y is multiplied and contents are loaded into MUL register
- fixed RL8
- fixed MVI
- flags computation in MVI and JMP is partly guessed (because of errors in docs)
- added reading DSP mem from SH2 side
- overworked disassembler

 TODO:
- make vfremix work...
- ALU commands: complete the flags
- ALU commands: rewrite it using signed arithmetic ?
- DMA: find out how this should really work (especially add number)
- Disassembler: complete it
*/

/*DSP macros*/
#define PRF ((stv_scu[32] & 0x04000000) >> 26)
#define EPF ((stv_scu[32] & 0x02000000) >> 25)
#define T0F ((stv_scu[32] & 0x00800000) >> 23)
#define SF  ((stv_scu[32] & 0x00400000) >> 22)
#define ZF  ((stv_scu[32] & 0x00200000) >> 21)
#define CF  ((stv_scu[32] & 0x00100000) >> 20)
#define VF  ((stv_scu[32] & 0x00080000) >> 19)
#define EF  ((stv_scu[32] & 0x00040000) >> 18)
#define ESF ((stv_scu[32] & 0x00020000) >> 17)
#define EXF ((stv_scu[32] & 0x00010000) >> 16)
#define LEF ((stv_scu[32] & 0x00008000) >> 15)
#define SF_1 if(!(stv_scu[32] & 0x00400000)) stv_scu[32]^=0x00400000
#define ZF_1 if(!(stv_scu[32] & 0x00200000)) stv_scu[32]^=0x00200000
#define CF_1 if(!(stv_scu[32] & 0x00100000)) stv_scu[32]^=0x00100000
#define VF_1 if(!(stv_scu[32] & 0x00080000)) stv_scu[32]^=0x00080000
#define SF_0 if(stv_scu[32] & 0x00400000) stv_scu[32]^=0x00400000
#define ZF_0 if(stv_scu[32] & 0x00200000) stv_scu[32]^=0x00200000
#define CF_0 if(stv_scu[32] & 0x00100000) stv_scu[32]^=0x00100000
#define VF_0 if(stv_scu[32] & 0x00080000) stv_scu[32]^=0x00080000
#define T0F_1 if(!(stv_scu[32] & 0x00800000)) stv_scu[32]^=0x00800000
#define T0F_0 if(stv_scu[32] & 0x00800000) stv_scu[32]^=0x00800000

struct {
	   UINT8 pc; 							           /*Program Counter*/
	   UINT8 delay;									   /* Delay */
	   UINT8 top;							   		   /*Jump Command memory*/
	   UINT16 lop;								       /*Counter Register*/	  /*12-bits*/
	   UINT8  ct0,ct1,ct2,ct3;					       /*Index for RAM*/	  /*6-bits */
	   UINT32 md0[0x40],md1[0x40],md2[0x40],md3[0x40]; /*RAM memory*/
	   UINT8  ra;									   /*RAM selector*/
	   UINT32 rx;									   /*X-Bus register*/
	   UINT64 mul;                                     /*Multiplier register* //*48-bits/*/
	   UINT32 ry;									   /*Y-Bus register*/
	   UINT64 alu;                                     /*ALU register*/       /*48-bits*/
	   UINT16 ph;									   /*ALU high register*/
	   UINT32 pl;									   /*ALU low register*/
	   UINT16 ach;									   /*ALU external high register*/
	   UINT32 acl;									   /*ALU external low register*/
	   UINT32 ra0,wa0;								   /*DSP DMA registers*/
	   UINT32 internal_prg[0x100];
} dsp_reg;

extern data32_t* stv_scu;

static UINT32 opcode;
static UINT8 update_mul = 0;

#define DEBUG_DSP

static	char dasm_buffer[100];
static FILE *log_file = NULL;
void dsp_dasm_opcode( UINT32 op, char *buffer );



static UINT32 dsp_get_source_mem_value( UINT32 mode )
{
	UINT32 value = 0;

	switch( mode )
	{
		case 0x0:
			value = dsp_reg.md0[ dsp_reg.ct0 ];
			break;
		case 0x1:
			value = dsp_reg.md1[ dsp_reg.ct1 ];
			break;
		case 0x2:
			value = dsp_reg.md2[ dsp_reg.ct2 ];
			break;
		case 0x3:
			value = dsp_reg.md3[ dsp_reg.ct3 ];
			break;
		case 0x4:
			value = dsp_reg.md0[ dsp_reg.ct0++ ];
			break;
		case 0x5:
			value = dsp_reg.md1[ dsp_reg.ct1++ ];
			break;
		case 0x6:
			value = dsp_reg.md2[ dsp_reg.ct2++ ];
			break;
		case 0x7:
			value = dsp_reg.md3[ dsp_reg.ct3++ ];
			break;
	}
	return value;
}

static UINT32 dsp_get_source_mem_reg_value( UINT32 mode )
{
	if ( mode < 0x8 )
	{
		return dsp_get_source_mem_value( mode );
	}
	else
	{
		switch( mode )
		{
			case 0x9:
				return (dsp_reg.alu & 0x00000000ffffffff) >> 0;
			case 0xA:
				return (dsp_reg.alu & 0x0000ffff00000000) >> 32;
		}
	}
	return 0;
}

static void dsp_set_dest_mem_reg( UINT32 mode, UINT32 value )
{
	switch( mode )
	{
		case 0x0:	/* MC0 */
			dsp_reg.md0[ dsp_reg.ct0++ ] = value;
			break;
		case 0x1:	/* MC1 */
			dsp_reg.md1[ dsp_reg.ct1++ ] = value;
			break;
		case 0x2:	/* MC2 */
			dsp_reg.md2[ dsp_reg.ct2++ ] = value;
			break;
		case 0x3:	/* MC3 */
			dsp_reg.md3[ dsp_reg.ct3++ ] = value;
			break;
		case 0x4:	/* RX */
			dsp_reg.rx = value;
			update_mul = 1;
			break;
		case 0x5:	/* PL */
			dsp_reg.pl = value;
			break;
		case 0x6:	/* RA0 */
			dsp_reg.ra0 = value;
			break;
		case 0x7:	/* WA0 */
			dsp_reg.wa0 = value;
			break;
		case 0x8:
		case 0x9:
			/* ??? */
			break;
		case 0xa:	/* LOP */
			dsp_reg.lop = value;
			break;
		case 0xb:	/* TOP */
			dsp_reg.top = value;
			break;
		case 0xc:	/* CT0 */
			dsp_reg.ct0 = value;
			break;
		case 0xd:	/* CT1 */
			dsp_reg.ct1 = value;
			break;
		case 0xe:	/* CT2 */
			dsp_reg.ct2 = value;
			break;
		case 0xf:	/* CT3 */
			dsp_reg.ct3 = value;
			break;
	}
}

static void dsp_set_dest_mem_reg_2( UINT32 mode, UINT32 value )
{
	if ( mode < 0xb )
	{
		dsp_set_dest_mem_reg( mode, value );
	}
	else
	{
		switch( mode )
		{
			case 0xc:	/* PC */
			    dsp_reg.delay = dsp_reg.pc;  /* address next after this command will be executed twice */
				dsp_reg.top = dsp_reg.pc;
				dsp_reg.pc = value;
				break;
		}
	}
}

static UINT32 dsp_compute_condition( UINT32 condition )
{
	UINT32 result = 0;

	switch( condition & 0xf )
	{
		case 0x1:	/* Z */
			result = ZF;
			break;
		case 0x2:	/* S */
			result = SF;
			break;
		case 0x3:	/* ZS */
			result = ZF | SF;
			break;
		case  0x4:	/* C */
			result = CF;
			break;
		case 0x8:	/* T0 */
			result = T0F;
			break;
	}
	if ( !(condition & 0x20) )
	{
		result = !result;
	}
	return result;
}

static void dsp_set_dest_dma_mem( UINT32 memcode, UINT32 value, UINT32 counter )
{
	if ( memcode < 4 )
	{
		dsp_set_dest_mem_reg( memcode, value );
	}
	else if ( memcode == 4 )
	{
		dsp_reg.internal_prg[ counter & 0x100 ] = value;
	}
}

static UINT32 dsp_get_mem_source_dma( UINT32 memcode, UINT32 counter )
{
	switch( memcode & 0x3 )
	{
		case 0x0:
			return dsp_reg.md0[ (dsp_reg.ct0 + counter) & 0x3f ];
		case 0x1:
			return dsp_reg.md1[ (dsp_reg.ct1 + counter) & 0x3f ];
		case 0x2:
			return dsp_reg.md2[ (dsp_reg.ct2 + counter) & 0x3f ];
		case 0x3:
			return dsp_reg.md3[ (dsp_reg.ct3 + counter) & 0x3f ];
	}
	return 0;
}

void dsp_prg_ctrl(UINT32 data)
{
	if(LEF) dsp_reg.pc = (data & 0xff);
	if(EXF) dsp_execute_program();
	if(EF && (!(stv_scu[40] & 0x0020)))
		cpu_set_irq_line_and_vector(0, 0xa, HOLD_LINE , 0x45);
}

void dsp_prg_data(UINT32 data)
{
	dsp_reg.internal_prg[dsp_reg.pc] = data;
	dsp_reg.pc++;
}

void dsp_ram_addr_ctrl(UINT32 data)
{
	dsp_reg.ra = data & 0xff;

	switch((dsp_reg.ra & 0xc0) >> 6)
	{
		case 0: dsp_reg.ct0 = (dsp_reg.ra & 0x3f); break;
		case 1: dsp_reg.ct1 = (dsp_reg.ra & 0x3f); break;
		case 2: dsp_reg.ct2 = (dsp_reg.ra & 0x3f); break;
		case 3: dsp_reg.ct3 = (dsp_reg.ra & 0x3f); break;
	}
}

void dsp_ram_addr_w(UINT32 data)
{
#ifdef DEBUG_DSP
    if ( log_file == NULL ) 
    {
        log_file = fopen( "dsp.log", "a" );
    }
    fprintf( log_file, "DSP: Writing mem %02X %04X\n", dsp_reg.ra & 0xff, data );
#endif    
	dsp_set_dest_mem_reg( (dsp_reg.ra & 0xc0) >> 6, data );
}

UINT32 dsp_ram_addr_r()
{
    UINT32 data;
    
    data = dsp_get_source_mem_value( ((dsp_reg.ra & 0xc0) >> 6) + 4 );
#ifdef DEBUG_DSP
    if ( log_file == NULL ) 
    {
        log_file = fopen( "dsp.log", "a" );
    }
    fprintf( log_file, "DSP: Reading mem at %02X %04X\n", dsp_reg.ra & 0xff, data );
#endif
	return data;
}

static void dsp_operation(void)
{
	/* ALU */
	switch( (opcode & 0x3c000000) >> 26 )
	{
		case 0x0:	/* NOP */
			break;
		case 0x1:	/* AND */
			dsp_reg.alu = dsp_reg.acl & dsp_reg.pl;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			{CF_0;}
			break;
		case 0x2:	/* OR */
			dsp_reg.alu = dsp_reg.acl | dsp_reg.pl;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			{CF_0;}
			break;
		case 0x3:	/* XOR */
			dsp_reg.alu = dsp_reg.acl ^ dsp_reg.pl;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			{CF_0;}
			break;
		case 0x4:	/* ADD */
			dsp_reg.alu = dsp_reg.acl + dsp_reg.pl;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x100000000 ) { CF_1; } else { CF_0; };
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			/* TODO: V flag */
			break;
		case 0x5:	/* SUB */
			dsp_reg.alu = dsp_reg.acl - dsp_reg.pl;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x100000000 ) { CF_1; } else { CF_0; };
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			/* TODO: V flag */
			break;
		case 0x6:	/* AD2 */
			dsp_reg.alu = dsp_reg.acl + dsp_reg.pl;
			dsp_reg.alu += ((UINT64)(dsp_reg.ach + dsp_reg.ph)) << 32;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x800000000000 ) {SF_1;} else {SF_0;}
			if ( dsp_reg.alu & 0x1000000000000 ) {CF_1; } else {CF_0;}
			/* TODO: V flag */
			break;
		case 0x7:	/* ??? */
			/* Unrecognized opcode */
			break;
		case 0x8:	/* SR */
			if ( dsp_reg.acl & 0x1 ) {CF_1;} else {CF_0;}
			dsp_reg.alu = dsp_reg.acl >> 1;
			if ( dsp_reg.alu & 0x40000000 ) dsp_reg.alu |= 0x80000000;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			break;
		case 0x9:	/* RR */
			if ( dsp_reg.acl & 0x1 ) {CF_1;} else {CF_0;}
			dsp_reg.alu = dsp_reg.acl >> 1;
			if ( CF ) dsp_reg.alu |= 0x80000000;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			break;
		case 0xA:	/* SL */
			if ( dsp_reg.acl & 0x80000000 ) {CF_1;} else {CF_0;}
			dsp_reg.alu = dsp_reg.acl << 1;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			break;
		case 0xB:	/* RL */
			if ( dsp_reg.acl & 0x80000000 ) {CF_1;} else {CF_0;}
			dsp_reg.alu = dsp_reg.acl << 1;
			if ( CF ) dsp_reg.alu |= 0x1;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			break;
		case 0xC:
		case 0xD:
		case 0xE:
			/* Unrecognized opcode */
			break;
		case 0xF:	/* RL8 */
			if ( dsp_reg.acl & 0x01000000 ) {CF_1;} else {CF_0;}
			dsp_reg.alu = (dsp_reg.acl >> 24) | (dsp_reg.acl << 8);
			if ( CF ) dsp_reg.alu |= 0x1;
			if ( dsp_reg.alu == 0 ) {ZF_1;} else {ZF_0;}
			if ( dsp_reg.alu & 0x80000000 )
			{
				SF_1;
				dsp_reg.alu |= 0xffff00000000;	/* sign extension */
			}
			else {SF_0;}
			break;
	}

	/* X-Bus */
	if ( opcode & 0x2000000 )
	{
		/* MOV [s],X */
		dsp_reg.rx = dsp_get_source_mem_value( (opcode & 0x700000) >> 20 );
		update_mul = 1;
	}
	switch( (opcode & 0x1800000) >> 23 )
	{
		case 0x0:	/* NOP */
		case 0x1:	/* NOP ? */
			break;
		case 0x2:	/* MOV MUL,P */
			dsp_reg.ph = ((dsp_reg.mul & 0x0000ffff00000000) >> 32);
			dsp_reg.pl = ((dsp_reg.mul & 0x00000000ffffffff) >> 0);
			break;
		case 0x3:	/* MOV [s],P */
			dsp_reg.pl = dsp_get_source_mem_value(  (opcode & 0x700000) >> 20 );
			if ( dsp_reg.pl & 0x80000000 ) 
			{
				dsp_reg.ph = 0xffff;
			}
			else
			{
				dsp_reg.ph = 0x0;
			}
			break;
	}

	/* Y-Bus */
	if ( opcode & 0x80000 )
	{
		/* MOV [s],Y */
		dsp_reg.ry = dsp_get_source_mem_value( (opcode & 0x1C000 ) >> 14 );
		update_mul = 1;
	}
	switch( (opcode & 0x60000) >> 17 )
	{
		case 0x0:	/* NOP */
			break;
		case 0x1:	/* CLR A */
			dsp_reg.acl = 0;
			dsp_reg.ach = 0;
			break;
		case 0x2:	/* MOV ALU,A */
			dsp_reg.ach = ((dsp_reg.alu & 0x0000ffff00000000) >> 32);
			dsp_reg.acl = ((dsp_reg.alu & 0x00000000ffffffff) >> 0);
			break;
		case 0x3:	/* MOV [s], A */
			dsp_reg.acl = dsp_get_source_mem_value( (opcode & 0x1C000 ) >> 14 );
			if ( dsp_reg.acl & 0x80000000 )
			{
				dsp_reg.ach = 0xffff;
			}
			else
			{
				dsp_reg.ach = 0x0;
			}
			break;
	}

	/* D1-Bus */
	switch( (opcode & 0x3000) >> 12 )
	{
		case 0x0:	/* NOP */
			break;
		case 0x1:	/* MOV SImm,[d] */
			dsp_set_dest_mem_reg( (opcode & 0xF00) >> 8, (INT32)(INT8)(opcode & 0xFF) );
			break;
		case 0x2:
			/* ??? */
			break;
		case 0x3:	/* MOV [s],[d] */
			dsp_set_dest_mem_reg( (opcode & 0xF00) >> 8, dsp_get_source_mem_reg_value( opcode & 0xF ) );
			break;
	}

}

static void dsp_move_immediate( void )
{
	UINT32 value;

	if ( opcode & 0x2000000 )
	{
		if ( dsp_compute_condition( (opcode & 0x3F80000 ) >> 18 ) )
		{
			value = opcode & 0x7ffff;
			if ( value & 0x40000 ) value |= 0xfff80000;
			dsp_set_dest_mem_reg_2( (opcode & 0x3C000000) >> 26, value );
		}
	}
	else
	{
		value = opcode & 0x1ffffff;
		if ( value & 0x1000000 ) value |= 0xfe000000;
		dsp_set_dest_mem_reg_2( (opcode & 0x3C000000) >> 26, value );
	}
}


static void dsp_dma( void )
{
	UINT8 hold = (opcode &  0x4000) >> 14;
	UINT32 add = (opcode & 0x38000) >> 15;
	UINT32 dir_from_D0 = (opcode & 0x1000 ) >> 12;
	UINT32 transfer_cnt = 0;
	UINT32 source = 0, dest = 0;
	UINT32 dsp_mem = (opcode & 0x700) >> 8;
	UINT32 counter = 0;
	UINT32 data;

	if ( opcode & 0x2000 )
	{
		transfer_cnt = dsp_get_source_mem_value( opcode & 0xf );
		switch ( add & 0x1 )
		{
		  case 0: add = 2; break;
		  case 1: add = 4; break;
		}
	}
	else
	{
		transfer_cnt = opcode & 0xff;
		switch( add )
		{
		  case 0: add = 0; break;  /* 0 */
		  case 1: add = 1; break;  /* 1 */
		  case 2: add = 2; break;  /* 2 */
		  case 3: add = 4; break; /* 4 */
		  case 4: add = 8; break; /* 8 */
  		  case 5: add = 16; break; /* 16 */
  		  case 6: add = 32; break; /* 32 */
  		  case 7: add = 64; break; /* 64 */
        }
	}

	if ( dir_from_D0 == 0 )
	{
		/* DMA D0,[RAM] */
		source = dsp_reg.ra0 << 2;
#ifdef DEBUG_DSP
        fprintf( log_file, "/*DSP DMA D0,[RAM%d],%d add=%d*/\n", dsp_mem, transfer_cnt, add );
#endif			
		for ( counter = 0; counter < transfer_cnt ; counter++ )
		{
		    data = cpu_readmem32bedw_dword( source );
#ifdef DEBUG_DSP
            fprintf( log_file, "%08X, %08X,\n", source, data );
#endif            		    
			dsp_set_dest_dma_mem( dsp_mem, data, counter );
			source += add;
		}

		if ( hold == 0 )
		{
			dsp_reg.ra0 += counter * add;
		}
	}
	else
	{
		/* DMA [RAM],D0 */
		dest = dsp_reg.wa0 << 2;
#ifdef DEBUG_DSP
    fprintf( log_file, "/*DSP DMA [RAM%d],D0,%d\tadd=%d,source=%08X*/\n", dsp_mem, transfer_cnt, add, source );
#endif					
		for ( counter = 0; counter < transfer_cnt; counter++ )
		{
			cpu_writemem32bedw_dword( dest, cpu_readmem32bew_word( dsp_get_mem_source_dma( dsp_mem, counter ) ) );
			dest += add;
		}

		if ( hold == 0 )
		{
			dsp_reg.wa0 += counter * add;
		}
	}
	T0F_1;
}

static void dsp_jump( void )
{
	if ( opcode & 0x3f80000 )
	{
		if ( dsp_compute_condition( (opcode & 0x3f80000) >> 19 ) )
		{
			dsp_reg.delay = dsp_reg.pc;
			dsp_reg.pc = opcode & 0xff;
		}
	}
	else
	{
		dsp_reg.delay = dsp_reg.pc;
		dsp_reg.pc = opcode & 0xff;
	}
}

static void dsp_end( void )
{
	if(opcode & 0x08000000)
	{
		/*ENDI*/
		if(!EF) stv_scu[32]^=0x00040000;
		if(EXF) stv_scu[32]^=0x00100000;
	}
	else
	{
		/*END*/
		if(EXF) stv_scu[32]^=0x00100000;
	}

};

static void dsp_loop( void )
{
	if ( opcode & 0x8000000 )
	{
		/* LPS */
		if ( dsp_reg.lop != 0 )
		{
			dsp_reg.lop--;
			dsp_reg.delay = dsp_reg.pc;
			dsp_reg.pc--;
		}
	}
	else
	{
		/* BTM */
		if ( dsp_reg.lop != 0 )
		{
			dsp_reg.lop--;
			dsp_reg.delay = dsp_reg.pc;
			dsp_reg.pc = dsp_reg.top;
		}
	}
}

static void dsp_dump_mem( FILE *f )
{
    UINT16 i;
    
	fprintf( f, "\n/*MEM 0*/\n{" );
	for ( i = 0; i < 0x40; i++ )
	{
        fprintf( f, "%08X, ", dsp_reg.md0[ i ] );
	}
	fprintf( f, "}\n/*MEM 1*/\n{" );
	for ( i = 0; i < 0x40; i++ )
	{
        fprintf( f, "%08X,", dsp_reg.md1[ i ] );
	}
	fprintf( f, "}\n/*MEM 2*/\n{" );
	for ( i = 0; i < 0x40; i++ )
	{
        fprintf( f, "%08X,", dsp_reg.md2[ i ] );
	}
	fprintf( f, "}\n/*MEM 3*/\n{" );
	for ( i = 0; i < 0x40; i++ )
	{
        fprintf( f, "%08X,", dsp_reg.md3[ i ] );
	}
	fprintf( f, "}\n" );
}

void dsp_execute_program()
{
	UINT32 cycles_run = 0;
	UINT8 cont = 1;
	UINT16 i;

#ifdef DEBUG_DSP
    if ( log_file == NULL )
    {
    	log_file = fopen("dsp.log", "a");
   	}
	for ( i = 0; i < 0x100; i++ )
	{
        dsp_dasm_opcode( dsp_reg.internal_prg[ i ], dasm_buffer );
        fprintf( log_file, "%02X\t%08X\t%s\n", i, dsp_reg.internal_prg[ i ], dasm_buffer );
	}
	dsp_dump_mem( log_file );
#endif	
	
	update_mul = 0;
	do
	{
		if ( dsp_reg.delay )
		{
			opcode = dsp_reg.internal_prg[ dsp_reg.delay ];
			dsp_reg.delay = 0;
		}
		else
		{
			opcode = dsp_reg.internal_prg[ dsp_reg.pc ];
			dsp_reg.pc++;
		}

		dsp_dasm_opcode( opcode, dasm_buffer );

		switch( (opcode & 0xc0000000) >> 30 ) 
		{
		case 0x00: /* 00 */
			dsp_operation();
			break;
		case 0x01: /* 01 */
			/* unrecognized opcode */
			break;
		case 0x02: /* 10 */
			dsp_move_immediate( );
			break;
		case 0x03: /* 11 */
			switch( (opcode & 0x30000000) >> 28 )
			{
				case 0x00:
					dsp_dma();
					break;
				case 0x01:
					dsp_jump();
					break;
				case 0x02:
					dsp_loop();
					break;
				case 0x03:
					dsp_end();
					cont = 0;
					break;
			}
			break;
		}
		if ( update_mul == 1 )
		{
			dsp_reg.mul = ((UINT64)dsp_reg.rx) * ((UINT64)dsp_reg.ry);
			update_mul = 0;
		}
		
		cycles_run++;

	} while( cont );
#ifdef DEBUG_DSP	
	dsp_dump_mem( log_file );
	fprintf( log_file, "\nRun %d cycles\n\n", cycles_run );	
	fclose( log_file );
	log_file = NULL;
#endif	
}



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

    SCU DSP Disassembler
    
************************************************************************/
const char* ALU_Commands[] =
{
	"",		/* 0000 */
	"AND",	/* 0001 */
	"OR",	/* 0010 */
	"XOR",	/* 0011 */
	"ADD",	/* 0100 */
	"SUB",	/* 0101 */
	"AD2",	/* 0110 */
	"???",	/* 0111 */
	"SR",	/* 1000 */
	"RR",	/* 1001 */
	"SL",	/* 1010 */
	"RL",	/* 1011 */
	"???",	/* 1100 */
	"???",	/* 1101 */
	"???",	/* 1110 */
	"RL8",	/* 1111 */	
};

const char* X_Commands[] =
{
	"",				/* 000 */
	"",				/* 001 */	/* NOP? check instruction @ 0x0B */
	"MOV MUL,P",	/* 010 */
	"MOV %s,P",		/* 011 */
	"MOV %s,X",		/* 100 */
};

const char* Y_Commands[] =
{
	"",				/* 000 */
	"CLR A",		/* 001 */
	"MOV ALU,A",	/* 010 */
	"MOV %s,A",		/* 011 */
	"MOV %s,Y",		/* 100 */
};

const char* D1_Commands[] = 
{
	"",					/* 00 */
	"MOV %I8,%d",		/* 01 */
	"???",				/* 10 */
	"MOV %S,%d",		/* 11 */
};

const char* SourceMemory[] =
{
	"M0",			/* 000 */
	"M1",			/* 001 */
	"M2",			/* 010 */
	"M3",			/* 011 */
	"MC0",			/* 100 */
	"MC1",			/* 101 */
	"MC2",			/* 110 */
	"MC3",			/* 111 */
};

const char* SourceMemory2[] =
{
	"M0",			/* 0000 */
	"M1",			/* 0001 */
	"M2",			/* 0010 */
	"M3",			/* 0011 */
	"MC0",			/* 0100 */
	"MC1",			/* 0101 */
	"MC2",			/* 0110 */
	"MC3",			/* 0111 */
	"???",			/* 1000 */
	"ALL",			/* 1001 */
	"ALH",			/* 1010 */
	"???",			/* 1011 */
	"???",			/* 1100 */
	"???",			/* 1101 */
	"???",			/* 1110 */
	"???",			/* 1111 */
};

const char* DestMemory[] =
{
	"MC0",			/* 0000 */
	"MC1",			/* 0001 */
	"MC2",			/* 0010 */
	"MC3",			/* 0011 */
	"RX",			/* 0100 */
	"PL",			/* 0101 */
	"RA0",			/* 0110 */
	"WA0",			/* 0111 */
	"???",			/* 1000 */
	"???",			/* 1001 */
	"LOP",			/* 1010 */
	"TOP",			/* 1011 */
	"CT0",			/* 1100 */
	"CT1",			/* 1101 */
	"CT2",			/* 1110 */
	"CT3",			/* 1111 */
};

const char* DestDMAMemory[] =
{
	"M0",			/* 000 */
	"M1",			/* 001 */
	"M2",			/* 010 */
	"M3",			/* 011 */
	"PRG",			/* 100 */
	"???",			/* 101 */
	"???",			/* 110 */
	"???",			/* 111 */
};

const char* MVI_Command[] =
{
	"MVI %I,%d",	/* 0 */
	"MVI %I,%d,%f",	/* 1 */
};

const char* JMP_Command[] =
{
	"JMP %IA",
	"JMP %f,%IA",
};

const char* DMA_Command[] =
{
	"DMA%H%A D0,%M,%I",
	"DMA%H%A %s,D0,%I",
	"DMA%H%A D0,%M,%s",
	"DMA%H%A %s,D0,%s",
};


void	dsp_dasm_prefix( const char* format, char* buffer, UINT32 *data )
{
	
	for ( ; *format; format++ )
	{
		if ( *format == '%' )
		{
			switch( *++format )
			{
				case 'H':
					if ( *data )
					{
						strcpy( buffer, "H" );
					}
					else
					{
						*buffer = 0;
					}
					break;
				case 'A':
					if ( *data == 0 )
					{
						strcpy( buffer, "0" );
					}
					else if ( *data == 1 )
					{
						*buffer = 0;
					}
					else
					{
						sprintf( buffer, "%d", 1 << (*data - 1) );
					}
					break;
				case 's':
					strcpy( buffer, SourceMemory[ *data & 0x7 ] );
					break;
				case 'd':
					strcpy( buffer, DestMemory[ *data & 0xf ] );
					break;
				case 'S':
					strcpy( buffer, SourceMemory2[ *data & 0xf ] );
					break;
				case 'I':
					++format;
					if ( *format == '8' )
					{
						sprintf( buffer, "#$%x", *data );
					}
					else if ( *format == 'A' )
					{
						sprintf( buffer, "$%X", *data );
					}
					else
					{
						--format;
						sprintf( buffer, "#$%X", *data );
					}
					break;
				case 'f':
					if ( !(*data & 0x20) )
					{
						strcpy( buffer, "N" );
						buffer++;
					}
					switch( *data & 0xf )
					{
						case 0x3:
							strcpy( buffer, "ZS" );
							break;
						case 0x2:
							strcpy( buffer, "S" );
							break;
						case 0x4:
							strcpy( buffer, "C" );
							break;
						case 0x8:
							strcpy( buffer, "T0" );
							break;
						case 0x1:
							strcpy( buffer, "Z" );
							break;
						default:
							strcpy( buffer, "?" );
							break;
					}
					break;
				case 'M':
					strcpy( buffer, DestDMAMemory[ *data ] );
					break;
					
			}
			data++;
			buffer += strlen( buffer );
		}
		else
		{
			*buffer++ = *format;
		}
	}
	*buffer = 0;
}
void	dsp_dasm_operation( UINT32 op, char *buffer )
{
	char *my_buffer = buffer;
	char temp_buffer[64];
	UINT32 data[2 ];

	/* NOP */
	if ( (op & 0x3F8E3000) == 0 )
	{
		sprintf( buffer, "%-10s", "NOP" );
		return;
	}
	/* ALU */
	sprintf( my_buffer, "%-10s", ALU_Commands[ (op & 0x3c000000) >> 26] );
	my_buffer += strlen( my_buffer );

	/* X-Bus */
	data[0] = (op & 0x700000) >> 20;
	if ( op & 0x2000000 )
	{
		dsp_dasm_prefix( X_Commands[ 4 ], temp_buffer, data );
	}
	else
	{
		*temp_buffer = 0;
	}
	sprintf( my_buffer, "%-10s", temp_buffer );
	my_buffer += strlen( my_buffer );

	dsp_dasm_prefix( X_Commands[ (op & 0x1800000) >> 23 ], temp_buffer,  data );
	sprintf( my_buffer, "%-10s", temp_buffer );
	my_buffer += strlen( my_buffer );
	
	/* Y-Bus */
	data[0] = (op & 0x1C000 ) >> 14 ;
	if ( op & 0x80000 )
	{
		dsp_dasm_prefix( Y_Commands[4], temp_buffer, data );
	}
	else
	{
		*temp_buffer = 0;
	}
	sprintf( my_buffer, "%-10s", temp_buffer );
	my_buffer += strlen( my_buffer );
	
	dsp_dasm_prefix( Y_Commands[ (op & 0x60000) >> 17 ], temp_buffer,  data );
	sprintf( my_buffer, "%-10s", temp_buffer );
	my_buffer += strlen( my_buffer );

	/* D1-Bus */
	switch( (op & 0x3000) >> 12 ) 
	{
	case 0x1:
		data[0] = (op & 0xFF);
		data[1] = ((op & 0xF00) >> 8);
		break;
	case 0x3:
		data[0] = (op & 0xF);
		data[1] = ((op & 0xF00) >> 8);
		break;
	};
	dsp_dasm_prefix( D1_Commands[ (op & 0x3000) >> 12 ], temp_buffer, data );
	sprintf( my_buffer, "%-10s", temp_buffer );
}

void dsp_dasm_move_immediate( UINT32 op, char *buffer )
{
	UINT32 data[3];
	if ( (op & 0x2000000) )
	{
		data[0] = op & 0x7FFFF;
		data[1] = (op & 0x3C000000) >> 26;
		data[2] = (op & 0x3F80000 ) >> 18;
		dsp_dasm_prefix( MVI_Command[1], buffer, data ); /* TODO: bad mem*/
	}
	else
	{
		data[0] = op & 0x1FFFFFF;
		data[1] = (op & 0x3C000000) >> 26;
		dsp_dasm_prefix( MVI_Command[0], buffer, data ); /* TODO: bad mem*/
	}
}

void dsp_dasm_jump( UINT32 op, char *buffer )
{
	UINT32 data[2];
	if ( op & 0x3F80000 )
	{
		data[0] = (op & 0x3F80000) >> 19;
		data[1] = op & 0xff;
		dsp_dasm_prefix( JMP_Command[1], buffer, data );
	}
	else
	{
		data[0] = op & 0xff;
		dsp_dasm_prefix( JMP_Command[0], buffer, data );
	}
}

void dsp_dasm_loop( UINT32 op, char* buffer )
{
	if ( op & 0x8000000 )
	{
		strcpy( buffer, "LPS" );
	}
	else
	{
		strcpy( buffer, "BTM" );
	}
}

void dsp_dasm_end( UINT32 op, char* buffer )
{
	if ( op & 0x8000000 )
	{
		strcpy( buffer, "ENDI" );
	}
	else
	{
		strcpy( buffer, "END" );
	}
}

void dsp_dasm_dma( UINT32 op, char* buffer )
{
	UINT32 data[4];
	data[0] = (op &  0x4000) >> 14; /* H */
	data[1] = (op & 0x38000) >> 15; /* A */
	data[2] = (op & 0x700) >> 8; /* Mem */
	data[3] = (op & 0xff);
	dsp_dasm_prefix( DMA_Command[(op & 0x3000) >> 12], buffer, data );
}

void dsp_dasm_opcode( UINT32 op, char *buffer )
{
	switch( (op & 0xc0000000) >> 30 ) 
	{
	case 0x00: /* 00 */
		dsp_dasm_operation( op, buffer );
		break;
	case 0x01: /* 01 */
		strcpy( buffer, "???"  );
		break;
	case 0x02: /* 10 */
		dsp_dasm_move_immediate( op, buffer );
		break;
	case 0x03: /* 11 */
		switch( (op & 0x30000000) >> 28 )
		{
			case 0x00:
				dsp_dasm_dma( op, buffer );
				break;
			case 0x01:
				dsp_dasm_jump( op, buffer );
				break;
			case 0x02:
				dsp_dasm_loop( op, buffer );
				break;
			case 0x03:
				dsp_dasm_end( op, buffer );
				break;
		}
		break;
	}
};

