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

unsigned int CpsMProt[4];
unsigned int CpsBID[3];

static unsigned char *CpsMem=NULL,*CpsMemEnd=NULL;
unsigned char *CpsRam90=NULL;
unsigned char *CpsZRamC0=NULL,*CpsZRamF0=NULL;
unsigned char *CpsSavePal=NULL;
unsigned char *CpsSaveReg[MAX_RASTER + 1];
unsigned char *CpsSaveFrg[MAX_RASTER + 1];
static unsigned char *CpsSaveRegData = NULL;
static unsigned char *CpsSaveFrgData = NULL;
unsigned char *CpsRam708=NULL,*CpsReg=NULL,*CpsFrg=NULL;
unsigned char *CpsRamFF=NULL;

// This routine is called first to determine how much memory is needed
// and then to set up all the pointers.
static int CpsMemIndex()
{
	unsigned char*  Next; Next =  CpsMem;

	CpsRam90	  = Next; Next += 0x030000;							// Video Ram
	CpsRamFF	  = Next; Next += 0x010000;							// Work Ram
	CpsReg		  = Next; Next += 0x000100;							// Registers

	CpsSavePal    = Next; Next += 0x002000;							// Draw Copy of Correct Palette

	if (Cps == 2 || Cps1Qs == 1) {
		CpsZRamC0 = Next; Next += 0x001000;							// Z80 c000-cfff
		CpsZRamF0 = Next; Next += 0x001000;							// Z80 f000-ffff
	}

	if (Cps == 2) {
		CpsRam708 = Next; Next += 0x010000;							// Obj Ram
		CpsFrg    = Next; Next += 0x000010;							// 'Four' Registers (Registers at 0x400000)
	
		ZBuf      = (unsigned short*)Next; Next += 384 * 224 * 2;	// Sprite Masking Z buffer

		CpsSaveRegData = Next; Next += 0x0100 * (MAX_RASTER + 1);	// Draw Copy of registers
		CpsSaveFrgData = Next; Next += 0x0010 * (MAX_RASTER + 1);	// Draw Copy of 'Four' Registers
		
		for (int i = 0; i < MAX_RASTER + 1; i++) {
			CpsSaveReg[i] = CpsSaveRegData + i * 0x0100;
			CpsSaveFrg[i] = CpsSaveFrgData + i * 0x0010;
		}
	
	} else {
		CpsSaveRegData = Next; Next += 0x0100;						// Draw Copy of registers
		CpsSaveFrgData = Next; Next += 0x0010;						// Draw Copy of 'Four' Registers

		CpsSaveReg[0] = CpsSaveRegData;
		CpsSaveFrg[0] = CpsSaveFrgData;
	}

	CpsMemEnd = Next;

	return 0;
}

static int AllocateMemory()
{
	int nLen;

	CpsMem = NULL;													// Find out how much memory is needed
	CpsMemIndex();
	nLen = CpsMemEnd - (unsigned char*)0;
	
	if ((CpsMem = (unsigned char*)malloc(nLen)) == NULL) {
		return 1;
	}
	
	memset(CpsMem, 0, nLen);										// blank all memory
	CpsMemIndex();													// Index the allocated memory
	
	return 0;
}

// Map the correct bank of obj memory to the 68000 address space (including mirrors).
void CpsMapObjectBanks(int nBank)
{
	if (nBank != nCpsObjectBank) {
		nCpsObjectBank = nBank;
		
		if (nCpsObjectBank) {
			SekMemory(CpsRam708, 0x708000, 0x709FFF, SM_RAM);
			SekMemory(CpsRam708, 0x70A000, 0x70BFFF, SM_RAM);
			SekMemory(CpsRam708, 0x70C000, 0x70DFFF, SM_RAM);
			SekMemory(CpsRam708, 0x70E000, 0x70FFFF, SM_RAM);
		} else {
			SekMemory(CpsRam708 + 0x8000, 0x708000, 0x709FFF, SM_RAM);
			SekMemory(CpsRam708 + 0x8000, 0x70A000, 0x70BFFF, SM_RAM);
			SekMemory(CpsRam708 + 0x8000, 0x70C000, 0x70DFFF, SM_RAM);
			SekMemory(CpsRam708 + 0x8000, 0x70E000, 0x70FFFF, SM_RAM);
		}
	}
}

static int __fastcall CpsCallback()
{
	// Reset instruction on 68000
	ZetReset();						// Reset Z80 (CPU #1)
	
	return 0;
}

int CpsMemInit()
{
	int nRet=0;
	nRet = AllocateMemory();  if (nRet!=0) return 1;

	SekOpen(0);
	
	SekExt[0].ResetCallback = CpsCallback;

	// Map in memory:
	// 68000 Rom (as seen as is, through read)
	SekMemory(CpsRom, 0, nCpsRomLen - 1, SM_READ);

	// 68000 Rom (as seen decrypted, through fetch)
	if (nCpsCodeLen > 0) {
		// Decoded part (up to nCpsCodeLen)
		SekMemory(CpsCode, 0, nCpsCodeLen - 1, SM_FETCH);
	}
	if (nCpsRomLen > nCpsCodeLen) {
		// The rest (up to nCpsRomLen)
		SekMemory(CpsRom + nCpsCodeLen, nCpsCodeLen, nCpsRomLen - 1 ,SM_FETCH);
	}

	if (slamPro) {
		for (int i = 0x7FFF; i >= 0; i--) {
			CpsEncZRom[(i << 1)] = CpsEncZRom[i];
			CpsEncZRom[(i << 1) + 1] = 0xFF;
		}
		SekMemory(CpsEncZRom, 0xF00000, 0xF0FFFF, SM_ROM);	// QSound ROM
	}

	if (Cps == 2) {
		nCpsObjectBank = -1;
		CpsMapObjectBanks(0);
	}

	SekMemory(CpsRam90, 0x900000, 0x92FFFF, SM_RAM);		// Gfx Ram
	SekMemory(CpsRamFF, 0xFF0000, 0xFFFFFF, SM_RAM);		// Work Ram
	
	SekExt[0].ReadByte  = CpsReadByte;
	SekExt[0].WriteByte = CpsWriteByte;
	SekExt[0].ReadWord  = CpsReadWord;
	SekExt[0].WriteWord = CpsWriteWord;
	
	SekClose();

	return 0;
}

int CpsMemExit()
{
	// Deallocate all used memory
	free(CpsMem);
	CpsMem = NULL;
	
	return 0;
}

static int ScanRam()
{
	// scan ram:
	struct BurnArea ba;
	memset(&ba, 0, sizeof(ba));

	ba.Data = CpsRam90;  ba.nLen = 0x030000; ba.szName = "CpsRam90";  BurnAcb(&ba);
	ba.Data = CpsRamFF;  ba.nLen = 0x010000; ba.szName = "CpsRamFF";  BurnAcb(&ba);
	ba.Data = CpsReg;    ba.nLen = 0x000100; ba.szName = "CpsReg";    BurnAcb(&ba);

	if (Cps == 2 || Cps1Qs == 1) {
		ba.Data = CpsZRamC0; ba.nLen = 0x001000; ba.szName = "CpsZRamC0"; BurnAcb(&ba);
		ba.Data = CpsZRamF0; ba.nLen = 0x001000; ba.szName = "CpsZRamF0"; BurnAcb(&ba);
	}
	
	if (Cps == 2) {
		ba.Data = CpsRam708; ba.nLen = 0x008000; ba.szName = "CpsRam708"; BurnAcb(&ba);
		ba.Data = CpsFrg;    ba.nLen = 0x000010; ba.szName = "CpsFrg";    BurnAcb(&ba);
	}

	return 0;
}

// Scan the current state of the CPS1/2 machine
int CpsAreaScan(int nAction, int *pnMin)
{
	if (CpsMem == NULL) {
		return 1;
	}

	if (pnMin) {										// Return minimum compatible version
		*pnMin = 0x020922;
	}

	if (Cps == 2 || Cps1Qs == 1 || PangEEP == 1) {		// Scan EEPROM
		EEPROMScan(nAction);
	}

	if (nAction & 4) {									// Scan volatile variables/reisters/RAM
		
		ScanRam();

		if ((Cps & 1) && Cps1Qs == 0) {					// Scan PSound chips
			PsndScan(nAction & 3);
		}
		if (Cps == 2 || Cps1Qs == 1) {					// Scan QSound chips
			QsndScan(nAction & 3);
		}
		SekScan(nAction & 3);							// Scan 68000 state

		if (nAction & 2) {								// Palette could have changed
			CpsRecalcPal = 1;
		}
	}

	return 0;
}


// CPS2 sound codes in Z80 memory:
int CpsScodePoll()
{
	int nCode;

	if ((Cps & 1) && Cps1Qs == 0) {
		return 1;
	}

	if (CpsZRamC0[0x00F]) {
		return 0;
	}

	nCode = (CpsZRamC0[0x000] << 8) | CpsZRamC0[0x001];

	// Assume nothing is being played:
	CpsZRamC0[0x00F] = 0xFF;

	if (nCode >= 0) {
		CpsZRamC0[0x000] = (unsigned char)(nCode >> 8);
		CpsZRamC0[0x001] = (unsigned char)(nCode & 255);
		CpsZRamC0[0x00F] = 0x00;
	}

	return 0;
}
