#include "cps.h"
// CPS - Run

// Inputs:
unsigned char CpsReset = 0;
unsigned char Cpi01A = 0, Cpi01C = 0, Cpi01E = 0;

static int nInterrupt;
static int nIrqLine, nIrqCycles;
static bool bEnableAutoIrq50, bEnableAutoIrq52;				// Trigger an interrupt every 32 scanlines

static const int nFirstLine = 0x08;							// The first scanline of the display
static const int nVBlank = 0x0106 - 0x0C;					// The scanline at which the vblank interrupt is triggered

int nIrqLine50, nIrqLine52;
int nCpsCyclesSegment;

int nGigawing;

static int DrvReset()
{
	// Reset machine
	EEPROMReset();

	SekOpen(0);
	SekReset();
	SekClose();

	ZetReset();	// Reset z80

	if (Cps == 2) {
		// Disable beam-synchronized interrupts
		*((unsigned short*)(CpsReg + 0x4E)) = 0x0200;
		*((unsigned short*)(CpsReg + 0x50)) = 0x0106;
		*((unsigned short*)(CpsReg + 0x52)) = 0x0106;
	}

	CpsMapObjectBanks(0);

	return 0;
}

int CpsRunInit()
{
	nGigawing = 0;
	nLagObjectPalettes = 0;
	
	// Giga Wing is different from other CPS2 games (new revision of CPS2 hardware?)
	if (strcmp(BurnDrvText(0), "gigawing") == 0 || strcmp(BurnDrvText(0), "gwingj") == 0) {
		nGigawing = 1;
	#ifndef _XBOX
	#endif
	#ifndef _XBOX
		nLagObjectPalettes = 1;
	#endif
	}
	
	SekInit(1);								// Allocate 68000

	if (CpsMemInit()) {						// Memory init
		return 1;
	}

	if (Cps == 2 || PangEEP) {				// EEPROM init (64 16bit words)
		EEPROMInit(6, 16);
	} else {
		if (Cps1Qs == 1) {
			EEPROMInit(7, 8);				// EEPROM init (128 bytes)
		}
	}
	
	CpsRwInit();							// Registers setup

	if (CpsPalInit()) {						// Palette init
		return 1;
	}
	if (CpsObjInit()) {						// Sprite init
		return 1;
	}

	if ((Cps & 1) && Cps1Qs == 0) {			// Sound init (OKIM6295 + YM2151)
		if (PsndInit()) {
			return 1;
		}
	}
	if (Cps == 2 || Cps1Qs == 1) {			// Sound init (QSound)
		if (QsndInit()) {
			return 1;
		}
	}

	EEPROMReset();
	DrvReset();

	//Init Draw Function
	DrawFnInit();

	return 0;
}

int CpsRunExit()
{
	EEPROMExit();

	// Sound exit
	if (Cps == 2 || Cps1Qs == 1) QsndExit();
	if (Cps != 2 && Cps1Qs == 0) PsndExit();

	// Graphics exit
	CpsObjExit();
	CpsPalExit();

	// Sprite Masking exit
	ZBuf = NULL;

	// Memory exit
	CpsRwExit();
	CpsMemExit();

	SekExit();

	return 0;
}

// nStart = 0-3, nCount=1-4
inline static void GetPalette(int nStart, int nCount)
{
	// Update Palette (Ghouls points to the wrong place on boot up I think)
	int nPal = (*((unsigned short*)(CpsReg + 0x0A)) << 8) & 0xFFF800;

	unsigned char* Find = CpsFindGfxRam(nPal, 0x1000);
	if (Find) {
		memcpy(CpsSavePal + (nStart << 10), Find + (nStart << 10), nCount << 10);
	}
}

static void GetStarPalette()
{
	int nPal = (*((unsigned short*)(CpsReg + 0x0A)) << 8) & 0xFFF800;

	unsigned char* Find = CpsFindGfxRam(nPal, 256);
	if (Find) {
		memcpy(CpsSavePal + 4096, Find + 4096, 256);
		memcpy(CpsSavePal + 5120, Find + 5120, 256);
	}
}

inline static void CopyCpsReg(int i)
{
	memcpy(CpsSaveReg[i], CpsReg, 0x0100);
}

inline static void CopyCpsFrg(int i)
{
	memcpy(CpsSaveFrg[i], CpsFrg, 0x0010);
}

// Schedule a beam-synchronized interrupt
static void ScheduleIRQ()
{
	int nLine = 0x0106;

	if (nIrqLine50 <= nLine) {
		nLine = nIrqLine50;
	}
	if (nIrqLine52 < nLine) {
		nLine = nIrqLine52;
	}
	
	if (nLine < 0x0106) {
		nIrqLine = nLine;
		nIrqCycles = (nLine * nCpsCycles / 0x0106) + 1;
	} else {
		nIrqCycles = nCpsCycles + 1;
	}

	return;
}

// Execute a beam-synchronised interrupt and schedule the next one
static void DoIRQ(int* nDone)
{
	// 0x4E - bit 9 = 1: Beam Synchronized interrupts disabled
	// 0x50 - Beam synchronized interrupt #1 occurs at raster line.
	// 0x52 - Beam synchronized interrupt #2 occurs at raster line.

	// Trigger IRQ and copy registers.
	if (nIrqLine >= nFirstLine) {

		nInterrupt++;
		nRasterline[nInterrupt] = nIrqLine - nFirstLine;
	}

	SekInterrupt(4);
	nCpsCyclesSegment += nCpsCycles * 0x01 / 0x0106;
	*nDone += SekRun(nCpsCycles * 0x01 / 0x0106);
	if (nRasterline[nInterrupt] < 224) {
		CopyCpsReg(nInterrupt);
		CopyCpsFrg(nInterrupt);
	} else {
		nRasterline[nInterrupt] = 0;
	}

	// Schedule next interrupt
	if (!bEnableAutoIrq50) {
		if (nIrqLine >= nIrqLine50) {
			nIrqLine50 = 0x0106;
		}
	} else {
		if (bEnableAutoIrq50 && nIrqLine == nIrqLine50) {
			nIrqLine50 += 32;
		}
	}
	if (!bEnableAutoIrq52 && nIrqLine >= nIrqLine52) {
		nIrqLine52 = 0x0106;
	} else {
		if (bEnableAutoIrq52 && nIrqLine == nIrqLine52) {
			nIrqLine52 += 32;
		}
	}
	ScheduleIRQ();
	if (nIrqCycles < nCpsCyclesSegment) {
		nIrqCycles = nCpsCyclesSegment + 1;
	}

	return;
}

int Cps1Frame()
{
	int nDone, nDisplayEnd, nNext, i;

	if (CpsReset) {
		DrvReset();
	}

	CpsRwGetInp();										// Update the input port values

	nDone = 0;
	nDisplayEnd = (nCpsCycles * (nFirstLine + 224)) / 0x0106;// Account for VBlank

	GetPalette(0, 4);									// Get palette
	if (CpsStar) {
		GetStarPalette();
	}
	
	if (!Sf2Hack) {
		CpsObjGet();									// Get objects
	}

	SekOpen(0);

	for (i = 0; i < 4; i++) {
		int nStart, nEnd;
		nNext = ((i + 1) * nDisplayEnd) >> 2;			// find out next cycle count to run to
		nDone += SekRun(nNext - nDone);					// run cpu

		// Run sound chips for this section
		nStart = i << 8;
		nEnd = (i + 1) << 8;

		if (Cps1Qs == 1) {
			CpsScodePoll();								// See which code has been passed
			QsndSectRun(nStart,nEnd);
		} else {
			PsndSectRun(nStart,nEnd);
		}
	}

	memcpy(CpsSaveReg[0], CpsReg, 0x100);				// Registers correct now
	
	if (Sf2Hack) {
		CpsObjGet();   									// Get objects
	}
	
	if (pBurnDraw) {
		CpsDraw();										// Draw frame
	}

	SekInterrupt(2);									// Trigger VBlank interrupt
	SekRun(nCpsCycles - nDisplayEnd);
	SekClose();

	return 0;
}

int Cps2Frame()
{
	int nDone, nDisplayEnd, nNext;						// variables to keep track of executed 68K cyles
	int i;

	if (CpsReset) {
		DrvReset();
	}

	CpsRwGetInp();										// Update the input port values

	nDisplayEnd = (nCpsCycles * (nFirstLine + 224)) / 0x0106;// Account for VBlank
	nDone = 0;

	nInterrupt = 0;
	for (i = 0; i < MAX_RASTER + 2; i++) {
		nRasterline[i] = 0;
	}

	// Determine which (if any) of the line counters generates the first IRQ
	bEnableAutoIrq50 = bEnableAutoIrq52 = false;	
	nIrqLine50 = nIrqLine52 = 0x0106;
	if (*((unsigned short*)(CpsReg + 0x50)) & 0x8000) {
		bEnableAutoIrq50 = true;
	}
	if (bEnableAutoIrq50 || (*((unsigned short*)(CpsReg + 0x4E)) & 0x0200) == 0) {
		nIrqLine50 = (*((unsigned short*)(CpsReg + 0x50)) & 0x01FF);
	}	
	if (*((unsigned short*)(CpsReg + 0x52)) & 0x8000) {
		bEnableAutoIrq52 = true;
	}
	if (bEnableAutoIrq52 || (*((unsigned short*)(CpsReg + 0x4E)) & 0x0200) == 0) {
		nIrqLine52 = (*((unsigned short*)(CpsReg + 0x52)) & 0x01FF);
	}	
	ScheduleIRQ();	
	
	SekOpen(0);

	if (nGigawing) {
		GetPalette(0, 1);								// Get object palettes
		CpsObjGet();									// Get objects
	}

	if (nIrqCycles < nCpsCycles * nFirstLine / 0x0106) {
		nCpsCyclesSegment = nIrqCycles;
		nDone = SekRun(nIrqCycles);
		DoIRQ(&nDone);
	}
	nCpsCyclesSegment = nCpsCycles * nFirstLine / 0x0106;
	if (nDone < nCpsCyclesSegment) {
		nDone += SekRun(nCpsCyclesSegment - nDone);
	}
	
	GetPalette(1, 3);									// Get tile palettes
	CopyCpsReg(0);										// Get inititial copy of registers
	CopyCpsFrg(0);										//
	
	for (i = 0; i < 3; i++) {
		int nStart, nEnd;

		nNext = ((i + 1) * nDisplayEnd) >> 2;			// find out next cycle count to run to

		while (nNext > nIrqCycles && nInterrupt < MAX_RASTER) {
			nCpsCyclesSegment = nIrqCycles;
			nDone += SekRun(nIrqCycles - nDone);
			DoIRQ(&nDone);
		}
		nCpsCyclesSegment = nNext;
		nDone += SekRun(nNext - nDone);					// run cpu

		CpsScodePoll();									// See which code has been passed

		// Run sound chips for this section
		nStart = i << 8;
		nEnd = (i + 1) << 8;
		QsndSectRun(nStart, nEnd);
	}

	if (pBurnDraw) {
		CpsDraw();
	}
	
	nCpsCyclesSegment = (nCpsCycles * nVBlank) / 0x0106;
	nDone += SekRun(nCpsCyclesSegment - nDone);
	
	if (!nGigawing) {
		GetPalette(0, 1);								// Get object palettes
		CpsObjGet();									// Get objects
	}

	SekInterrupt(2);									// VBlank
	nCpsCyclesSegment = nCpsCycles;
	nDone += SekRun(nCpsCyclesSegment - nDone);
	
	CpsScodePoll();										// See which code has been passed
	QsndSectRun(3 << 8, (3 + 1) << 8);					// Run sound chips for this section
	
	SekClose();

#if 0
 #ifdef _DEBUG
	if (nInterrupt) {
		printf("Beam synchronized interrupt at line %2X.\r", nRasterline[nInterrupt]);
	} else {
		printf("Beam synchronized interrupt disabled.   \r");
	}

	extern int counter;
	if (counter) {
		printf("\n\nSlices start at: ");
		for (i = 0; i < MAX_RASTER + 2; i++) {
			printf("%2X ", nRasterline[i]);
		}
		printf("\n");
		for (i = 0; i < 0x80; i++) {
			if (*((unsigned short*)(CpsSaveReg[0] + i * 2)) != *((unsigned short*)(CpsSaveReg[nInterrupt] + i * 2))) {
				printf("Register %2X: %4X -> %4X\n", i * 2, *((unsigned short*)(CpsSaveReg[0] + i * 2)), *((unsigned short*)(CpsSaveReg[nInterrupt] + i * 2)));
			}
		}
		printf("\n");
		for (i = 0; i < 0x010; i++) {
			if (CpsSaveFrg[0][i] != CpsSaveFrg[nInterrupt][i]) {
				printf("FRG %X: %02X -> %02X\n", i, CpsSaveFrg[0][i], CpsSaveFrg[nInterrupt][i]);
			}
		}
		printf("\n");
		if (((CpsSaveFrg[0][4] << 8) | CpsSaveFrg[0][5]) != ((CpsSaveFrg[nInterrupt][4] << 8) | CpsSaveFrg[nInterrupt][5])) {
			printf("Layer-sprite priority: %04X -> %04X\n", ((CpsSaveFrg[0][4] << 8) | CpsSaveFrg[0][5]), ((CpsSaveFrg[nInterrupt][4] << 8) | CpsSaveFrg[nInterrupt][5]));
		}

		printf("\n");
		for (int j = 0; j <= nInterrupt; j++) {
			if (j) {
				printf("IRQ : %i (triggered at line %3i)\n\n", j, nRasterline[j]);
			} else {
				printf("Initial register status\n\n");
			}
			
			for (i = 0; i < 0x080; i+= 8) {
				printf("%2X: %4X %4X %4X %4X %4X %4X %4X %4X\n", i * 2, *((unsigned short*)(CpsSaveReg[j] + 0 + i * 2)), *((unsigned short*)(CpsSaveReg[j] + 2 + i * 2)), *((unsigned short*)(CpsSaveReg[j] + 4 + i * 2)), *((unsigned short*)(CpsSaveReg[j] + 6 + i * 2)), *((unsigned short*)(CpsSaveReg[j] + 8 + i * 2)), *((unsigned short*)(CpsSaveReg[j] + 10 + i * 2)), *((unsigned short*)(CpsSaveReg[j] + 12 + i * 2)), *((unsigned short*)(CpsSaveReg[j] + 14 + i * 2)));
			}
			
			printf("\nFRG: ");
			for (i = 0; i < 0x010; i++) {
				printf("%02X ", CpsSaveFrg[j][i]);
			}
			printf("\n\n");

		}
		
		extern int bRunPause;
		bRunPause = 1;
		counter = 0;
	}
 #endif
#endif

	return 0;
}

