#include <stdio.h>
#include <windows.h>
#include "misc.h"
#include "lc89510.h"
#include "cd_aspi.h"
#include "Star_68k.h"
#include "mem_S68k.h"
#include "pcm.h"

#define CDC_DMA_SPEED 256

int CDC_Decode_Reg_Read;

void CDD_Reset(void)
{
	int i;

	// Reseting CDD
	
	CDD.Fader = 0;
	CDD.Control = 0;
	CDD.Cur_Comm = 0;
	CDD.Status = 0;
	CDD.Minute = 0;
	CDD.Seconde = 0;
	CDD.Frame = 0;
	CDD.Ext = 0;

	for(i = 0; i < 10; i++)
	{
		CDD.Rcv_Status[i] = 0;
		CDD.Trans_Comm[i] = 0;
	}

	CDD.Rcv_Status[8] = 0xF;		// Default checksum

	SCD.Cur_Track = 0;
	SCD.Cur_LBA = -150;
	SCD.Status_CDD = READY;
}


void CDC_Reset(void)
{
	int i;

	// Reseting CDC
	
	memset(CDC.Buffer, 0, (16 * 1024 * 2) + 2352);
	CDC_Update_Header();

	CDC.COMIN = 0;
	CDC.IFSTAT = 0xFF;
	CDC.DAC.N = 0;
	CDC.DBC.N = 0;
	CDC.HEAD.N = 0x01000000;
	CDC.PT.N = 0;
	CDC.WA.N = 2352 * 2;
	CDC.STAT.N = 0x00000080;
	CDC.SBOUT = 0;
	CDC.IFCTRL = 0;
	CDC.CTRL.N = 0;

	CDC_Decode_Reg_Read = 0;
}


void LC89510_Reset(void)
{
	CDD_Reset();
	CDC_Reset();

	CDC.RS0 = 0;
	CDC.RS1 = 0;
	CDC.Host_Data = 0;
	CDC.DMA_Adr = 0;
	CDC.Stop_Watch = 0;

	SCD.Status_CDC = 0;
	CDD_Complete = 0;
}

void Update_CDC_TRansfert(void)
{
	unsigned int i, dep, lenght, add_dest;
	unsigned char *dest;
	
	if ((SCD.Status_CDC & 0x08) == 0) return;
	
	switch(CDC.RS0 & 0x0700)
	{
		case 0x0200:				// MAIN CPU
		case 0x0300:				// SUB CPU
			CDC.RS0 |= 0x4000;		// Data ready in host port
			return;
			break;

		case 0x0400:		// PCM RAM
			dest = (unsigned char *) Ram_PCM;
			dep = ((CDC.DMA_Adr & 0x03FF) << 2) + PCM_Chip.Bank;
			add_dest = 2;
			break;

		case 0x0500:		// PRG RAM
			dest = (unsigned char *) Ram_Prg;
			dep = (CDC.DMA_Adr & 0xFFFF) << 3;
			add_dest = 2;
#ifdef DEBUG_CD
//			fprintf(debug_SCD_file, "DMA transfert PRG RAM : adr = %.8X  ", dep);
#endif
			break;

		case 0x0700:		// WORD RAM
			if (Ram_Word_State >= 2)
			{
				dest = (unsigned char *) Ram_Word_1M;
				add_dest = 2;
				if (Ram_Word_State & 1) dep = ((CDC.DMA_Adr & 0x3FFF) << 3);
				else dep = ((CDC.DMA_Adr & 0x3FFF) << 3) + 0x20000;
			}
			else
			{
				dest = (unsigned char *) Ram_Word_2M;
				dep = ((CDC.DMA_Adr & 0x7FFF) << 3);
				add_dest = 2;
			}
			break;

		default:
			return;
	}

	if (CDC.DBC.N <= (CDC_DMA_SPEED * 2))
	{
		lenght = (CDC.DBC.N + 1) >> 1;
		SCD.Status_CDC &= ~0x08;	// Last transfert
		CDC.RS0 |= 0x8000;			// End data transfert
		CDC.RS0 &= ~0x4000;			// no more data ready
		CDC.IFSTAT |= 0x08;			// No more data transfert in progress

		if (CDC.IFCTRL & 0x40)		// DTEIEN = Data Trasnfert End Interrupt Enable ?
		{
			CDC.IFSTAT &= ~0x40;

			if (Int_Mask_S68K & 0x20) sub68k_interrupt(5, -1);

#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "CDC - DTE interrupt\n");
#endif
		}
	}
	else lenght = CDC_DMA_SPEED;

#ifdef DEBUG_CD
//	fprintf(debug_SCD_file, "DMA lenght = %.4X\n", lenght);
#endif


	if ((CDC.RS0 & 0x0700) == 0x0400)		// PCM DMA
	{
	__asm
		{
			mov ecx, lenght
			mov edi, dest
			lea esi, CDC.Buffer
			add edi, dep
			add esi, CDC.DAC.N
			mov ebx, add_dest

		Loop_DMA_PCM:
			mov ax, [esi]
			add esi, 2
			mov [edi], ax
			add edi, ebx
			dec ecx
			jnz Loop_DMA_PCM
		}

		lenght <<= 1;
		CDC.DMA_Adr += lenght >> 2;
	}
	else									// OTHER DMA
	{
		__asm
		{
			mov ecx, lenght
			mov edi, dest
			lea esi, CDC.Buffer
			add edi, dep
			add esi, CDC.DAC.N
			mov ebx, add_dest

		Loop_DMA:
			mov ax, [esi]
			add esi, 2
			rol ax, 8
			mov [edi], ax
			add edi, ebx
			dec ecx
			jnz Loop_DMA
		}

		lenght <<= 1;
		CDC.DMA_Adr += lenght >> 3;
	}

	CDC.DAC.N = (CDC.DAC.N + lenght) & 0xFFFF;
	if (SCD.Status_CDC & 0x08) CDC.DBC.N -= lenght;
	else CDC.DBC.N = 0;
}


unsigned short Read_CDC_Host_SUB(void)
{		
	unsigned short val;

	if (SCD.Status_CDC & 0x08)				// Transfert data
	{
		if ((CDC.RS0 & 0x0700) == 0x0300)	// SUB CPU
		{
			CDC.DBC.N -= 2;
			
			if (CDC.DBC.N <= 0)
			{
				CDC.DBC.N = 0;
				SCD.Status_CDC &= ~0x08;	// Last transfert
				CDC.RS0 |= 0x8000;			// End data transfert
				CDC.RS0 &= ~0x4000;			// no more data ready
				CDC.IFSTAT |= 0x08;			// No more data transfert in progress

				if (CDC.IFCTRL & 0x40)		// DTEIEN = Data Trasnfert End Interrupt Enable ?
				{
					CDC.IFSTAT &= ~0x40;

					if (Int_Mask_S68K & 0x20) sub68k_interrupt(5, -1);

#ifdef DEBUG_CD
					fprintf(debug_SCD_file, "CDC - DTE interrupt\n");
#endif
				}
			}

			__asm
			{
				mov esi, CDC.DAC.N
				lea ebx, CDC.Buffer
//				and esi, 0x3FFF
				mov ax, [ebx + esi]
				add esi, 2
				rol ax, 8
				mov CDC.DAC.N, esi
				mov val, ax
			}

#ifdef DEBUG_CD
//	fprintf(debug_SCD_file, "Host READ on SUB CPU side : %.4X  DBA = %.4X  DBC = %.4X\n", val, CDC.DBC.N, CDC.DAC.N);
#endif

			return val;
		}
	}

	return 0;
}


unsigned short Read_CDC_Host_MAIN(void)
{		
	unsigned short val;

	if (SCD.Status_CDC & 0x08)				// Transfert data
	{
		if ((CDC.RS0 & 0x0700) == 0x0200)	// MAIN CPU
		{
			CDC.DBC.N -= 2;
			
			if (CDC.DBC.N <= 0)
			{
				SCD.Status_CDC &= ~0x08;	// Last transfert
				CDC.RS0 |= 0x8000;			// End data transfert
				CDC.RS0 &= ~0x4000;			// no more data ready
				CDC.IFSTAT |= 0x08;			// No more data transfert in progress

				if (CDC.IFCTRL & 0x40)		// DTEIEN = Data Trasnfert End Interrupt Enable ?
				{
					CDC.IFSTAT &= ~0x40;

					if (Int_Mask_S68K & 0x20) sub68k_interrupt(5, -1);

#ifdef DEBUG_CD
					fprintf(debug_SCD_file, "CDC - DTE interrupt\n");
#endif
				}
			}

			__asm
			{
				mov esi, CDC.DAC.N
				lea ebx, CDC.Buffer
//				and esi, 0x3FFF
				mov ax, [ebx + esi]
				add esi, 2
				rol ax, 8
				mov CDC.DAC.N, esi
				mov val, ax
			}

#ifdef DEBUG_CD
//	fprintf(debug_SCD_file, "Host READ on SUB CPU side : %.4X  DBA = %.4X  DBC = %.4X\n", val, CDC.DBC.N, CDC.DAC.N);
#endif

			return val;
		}
	}

	return 0;
}


void CDC_Update_Header(void)
{
	if (CDC.CTRL.B.B1 & 0x01)		// Sub-Hearder wanted ?
	{
		CDC.HEAD.B.B0 = 0;
		CDC.HEAD.B.B1 = 0;
		CDC.HEAD.B.B2 = 0;
		CDC.HEAD.B.B3 = 0;
	}
	else
	{
		_msf MSF;

		LBA_to_MSF(SCD.Cur_LBA, &MSF);
		
		CDC.HEAD.B.B0 = INT_TO_BCDB(MSF.M);
		CDC.HEAD.B.B1 = INT_TO_BCDB(MSF.S);
		CDC.HEAD.B.B2 = INT_TO_BCDB(MSF.F);
		CDC.HEAD.B.B3 = 0x01;
	}
}


unsigned char CDC_Read_Reg(void)
{
	unsigned char ret;

#ifdef DEBUG_CD
	fprintf(debug_SCD_file, "CDC read reg %.2d = ", CDC.RS0 & 0xF);
#endif

	switch(CDC.RS0 & 0xF)
	{
		case 0x0: // COMIN
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.COMIN);
#endif

			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x1;
			return CDC.COMIN;

		case 0x1: // IFSTAT
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.IFSTAT);
#endif

			CDC_Decode_Reg_Read |= (1 << 1);		// Reg 1 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x2;
			return CDC.IFSTAT;

		case 0x2: // DBCL
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.DBC.B.L);
#endif

			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x3;
			return CDC.DBC.B.L;

		case 0x3: // DBCH
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.DBC.B.H);
#endif

			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x4;
			return CDC.DBC.B.H;

		case 0x4: // HEAD0
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.HEAD.B.B0);
#endif

			CDC_Decode_Reg_Read |= (1 << 4);		// Reg 4 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x5;
			return CDC.HEAD.B.B0;

		case 0x5: // HEAD1
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.HEAD.B.B1);
#endif

			CDC_Decode_Reg_Read |= (1 << 5);		// Reg 5 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x6;
			return CDC.HEAD.B.B1;

		case 0x6: // HEAD2
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.HEAD.B.B2);
#endif

			CDC_Decode_Reg_Read |= (1 << 6);		// Reg 6 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x7;
			return CDC.HEAD.B.B2;

		case 0x7: // HEAD3
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.HEAD.B.B3);
#endif

			CDC_Decode_Reg_Read |= (1 << 7);		// Reg 7 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x8;
			return CDC.HEAD.B.B3;

		case 0x8: // PTL
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.PT.B.L);
#endif

			CDC_Decode_Reg_Read |= (1 << 8);		// Reg 8 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x9;
			return CDC.PT.B.L;

		case 0x9: // PTH
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.PT.B.H);
#endif

			CDC_Decode_Reg_Read |= (1 << 9);		// Reg 9 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xA;
			return CDC.PT.B.H;

		case 0xA: // WAL
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.WA.B.L);
#endif

			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xB;
			return CDC.WA.B.L;

		case 0xB: // WAH
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.WA.B.H);
#endif

			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xC;
			return CDC.WA.B.H;

		case 0xC: // STAT0
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.STAT.B.B0);
#endif

			CDC_Decode_Reg_Read |= (1 << 12);		// Reg 12 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xD;
			return CDC.STAT.B.B0;

		case 0xD: // STAT1
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.STAT.B.B1);
#endif

			CDC_Decode_Reg_Read |= (1 << 13);		// Reg 13 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xE;
			return CDC.STAT.B.B1;

		case 0xE: // STAT2
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.STAT.B.B2);
#endif

			CDC_Decode_Reg_Read |= (1 << 14);		// Reg 14 (decoding)
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xF;
			return CDC.STAT.B.B2;

		case 0xF: // STAT3
#ifdef DEBUG_CD
			fprintf(debug_SCD_file, "%.2X\n", CDC.STAT.B.B3);
#endif

			ret = CDC.STAT.B.B3;
			CDC.IFSTAT |= 0x20;			// decoding interrupt flag cleared
			if ((CDC.CTRL.B.B0 & 0x80) && (CDC.IFCTRL & 0x20))
			{
				if ((CDC_Decode_Reg_Read & 0x73F2) == 0x73F2)
					CDC.STAT.B.B3 = 0x80;
			}
			return ret;
	}

	return 0;
}


void CDC_Write_Reg(unsigned char Data)
{

#ifdef DEBUG_CD
	fprintf(debug_SCD_file, "CDC write reg%d = %.2X\n", CDC.RS0 & 0xF, Data);
#endif

	switch(CDC.RS0 & 0xF)
	{
		case 0x0: // SBOUT
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x1;
			CDC.SBOUT = Data;

			break;

		case 0x1: // IFCTRL
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x2;
			CDC.IFCTRL = Data;

			if ((CDC.IFCTRL & 0x02) == 0)		// Stop data transfert
			{
				CDC.DBC.N = 0;
				SCD.Status_CDC &= ~0x08;
				CDC.IFSTAT |= 0x08;				// No more data transfert in progress

/*
if (CDD.Trans_Comm[1] != 0)
{
	SCD.Status_CDD = READY;
	SCD.Status_CDC &= 0;
}*/

			}
			break;

		case 0x2: // DBCL
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x3;
			CDC.DBC.B.L = Data;

			break;

		case 0x3: // DBCH
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x4;
			CDC.DBC.B.H = Data;

			break;

		case 0x4: // DACL
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x5;
			CDC.DAC.B.L = Data;

			break;

		case 0x5: // DACH
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x6;
			CDC.DAC.B.H = Data;

			break;

		case 0x6: // DTTRG
			if (CDC.IFCTRL & 0x02)		// Data transfert enable ?
			{
				CDC.IFSTAT &= ~0x08;		// Data transfert in progress
				SCD.Status_CDC |= 0x08;		// Data transfert in progress
				CDC.RS0 &= 0x7FFF;			// A data transfert start

#ifdef DEBUG_CD
				fprintf(debug_SCD_file, "\n************** Starting Data Transfert ***********\n");
				fprintf(debug_SCD_file, "RS0 = %.4X  DAC = %.4X  DBC = %.4X  DMA adr = %.4X\n\n", CDC.RS0, CDC.DAC.N, CDC.DBC.N, CDC.DMA_Adr);
#endif
			}
			break;

		case 0x7: // DTACK
			CDC.IFSTAT |= 0x40;			// end data transfert interrupt flag cleared
			break;

		case 0x8: // WAL
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0x9;
			CDC.WA.B.L = Data;

			break;

		case 0x9: // WAH
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xA;
			CDC.WA.B.H = Data;

			break;

		case 0xA: // CTRL0
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xB;
			CDC.CTRL.B.B0 = Data;

			break;

		case 0xB: // CTRL1
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xC;
			CDC.CTRL.B.B1 = Data;

			break;

		case 0xC: // PTL
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xD;
			CDC.PT.B.L = Data;

			break;

		case 0xD: // PTH
			CDC.RS0 = (CDC.RS0 & 0xFFF0) | 0xE;
			CDC.PT.B.H = Data;

			break;

		case 0xE: // CTRL2
			CDC.CTRL.B.B2 = Data;
			break;

		case 0xF: // RESET
			CDC_Reset();
			break;
	}
}


void CDD_Processing(void)
{

#ifdef DEBUG_CD
	fprintf(debug_SCD_file, "CDD exporting status\n");
	fprintf(debug_SCD_file, "Status=%.4X, Minute=%.4X, Seconde=%.4X, Frame=%.4X, Ext=%.4X\n", CDD.Status, CDD.Minute, CDD.Seconde, CDD.Frame, CDD.Ext);
#endif

	CDD_Export_Status();

	sub68k_interrupt(4, -1);
}


void CDD_Import_Command(void)
{

#ifdef DEBUG_CD
	fprintf(debug_SCD_file, "CDD importing command\n");
	fprintf(debug_SCD_file, "Commande=%.4X, Minute=%.4X, Seconde=%.4X, Frame=%.4X  Checksum=%.4X\n", (CDD.Trans_Comm[0] & 0xFF) + ((CDD.Trans_Comm[1] & 0xFF) << 8), (CDD.Trans_Comm[2] & 0xFF) + ((CDD.Trans_Comm[3] & 0xFF) << 8), (CDD.Trans_Comm[4] & 0xFF) + ((CDD.Trans_Comm[5] & 0xFF) << 8), (CDD.Trans_Comm[6] & 0xFF) + ((CDD.Trans_Comm[7] & 0xFF) << 8), (CDD.Trans_Comm[8] & 0xFF) + ((CDD.Trans_Comm[9] & 0xFF) << 8));
#endif

	switch (CDD.Trans_Comm[1])
	{
		case 0x0:	// STATUS (?)
			Get_Status_CDD_c0();
			break;

		case 0x1:	// STOP ALL (?)
			Stop_CDD_c1();
			break;

		case 0x2:	// GET TOC INFORMATIONS
			switch(CDD.Trans_Comm[2])
			{
				case 0x0:	// get current position (MSF format)
					CDD.Status = (CDD.Status & 0xFF00);
					Get_Pos_CDD_c20();
					break;

				case 0x1:	// get elapsed time of current track played/scanned (relative MSF format)
					CDD.Status = (CDD.Status & 0xFF00) | 1;
					Get_Track_Pos_CDD_c21();
					break;

				case 0x2:	// get current track in RS2-RS3
					CDD.Status =  (CDD.Status & 0xFF00) | 2;
					Get_Current_Track_CDD_c22();
					break;

				case 0x3:	// get total lenght (MSF format)
					CDD.Status = (CDD.Status & 0xFF00) | 3;
					Get_Total_Lenght_CDD_c23();
					break;

				case 0x4:	// first & last track number
					CDD.Status = (CDD.Status & 0xFF00) | 4;
					Get_First_Last_Track_CDD_c24();
					break;

				case 0x5:	// get track addresse (MSF format)
					CDD.Status = (CDD.Status & 0xFF00) | 5;
					Get_Track_Adr_CDD_c25();
					break;

				default :	// invalid, then we return status
					CDD.Status = (CDD.Status & 0xFF00) | 0xF;
					Get_Status_CDD_c0();
					break;
			}
			break;

		case 0x3:	// READ
			Play_CDD_c3();
			break;

		case 0x4:	// SEEK
			Seek_CDD_c4();
			break;

		case 0x6:	// PAUSE/STOP
			Pause_CDD_c6();
			break;

		case 0x7:	// RESUME
			Resume_CDD_c7();
			break;

		case 0x8:	// FAST FOWARD
			Fast_Foward_CDD_c8();
			break;

		case 0x9:	// FAST REWIND
			Fast_Rewind_CDD_c9();
			break;

		case 0xA:	// RECOVER INITIAL STATE (?)
			CDD_cA();
			break;

		case 0xC:	// CLOSE TRAY
			Close_Tray_CDD_cC();
			break;

		case 0xD:	// OPEN TRAY
			Open_Tray_CDD_cD();
			break;

		default:
					// UNKNOW
			CDD_Def();
			break;
	}
}


unsigned char SCD_Read_Byte(unsigned int Adr)
{

#ifdef DEBUG_CD
	fprintf(debug_SCD_file, "SCD read (B), address = %.8X\n", Adr);
#endif

	return 0;
}


unsigned short SCD_Read_Word(unsigned int Adr)
{

#ifdef DEBUG_CD
	fprintf(debug_SCD_file, "SCD read (W), address = %.8X\n", Adr);
#endif

	return 0;
}




void debug_truc(unsigned int val)
{
#ifdef DEBUG_CD
	fprintf(debug_SCD_file, "ebx = %.8X\n", val);
#endif
}
