#include "cps.h"

extern "C" {
 #include "driver.h"
 #include "ym2151.h"
}

// PSound - Z80
static unsigned char FmReg[0x100];			// Our copy of the FM registers
static unsigned char nFmSel = 0;			// The FM register selected
static int nPsndZBank = 0;
static unsigned char *PsndZRam = NULL;

int Kodb = 0;

// Map in the memory for the current 0x8000-0xc000 bank
static int PsndZBankMap()
{
	unsigned char *Bank;
	unsigned int nOff = (nPsndZBank << 14) + 0x8000;

	if (nOff + 0x4000 > nCpsZRomLen) {		// End of bank in out of range
		nOff = 0;
	}

	Bank = CpsZRom + nOff;

	// Read and fetch the bank
	ZetMapArea(0x8000, 0xBFFF, 0, Bank);
	ZetMapArea(0x8000, 0xBFFF, 2, Bank);
	return 0;
}

// PSound Z80 memory write
static void PsndZWrite(unsigned short a, unsigned char d)
{
	switch (a) {
		case 0xF000:
			nFmSel = d;
			break;
		case 0xF001:
			// FM Register write
			FmReg[nFmSel] = d;
			// Write the register to the chip emu
			if (bPsmOkay) {
				YM2151WriteReg(0, nFmSel, d);
			}
			break;
		case 0xF002:
			OKIM6295Command(0, d);
			break;
		case 0xF004: {
			int nNewBank = d & 0x0f;
			if (nPsndZBank != nNewBank) {
				nPsndZBank = nNewBank;
				PsndZBankMap();
			}
			break;
		}
		case 0xF006:						// ??? Enable interrupt ???
			break;

#ifdef _DEBUG
		default:
			printf("Z80 address %04X -> %02X.\n", a, d);
#endif

	}
	return;
}

static void kodbZWrite(unsigned short a, unsigned char d)
{
	switch (a) {
		case 0xE000:
			nFmSel = d;
			break;
		case 0xE001:
			// FM Register write
			FmReg[nFmSel] = d;
			// Write the register to the chip emu
			if (bPsmOkay) {
				YM2151WriteReg(0, nFmSel, d);
			}
			break;
		case 0xE400:
			OKIM6295Command(0, d);
			break;

#ifdef _DEBUG
		default:
			printf("Z80 address %04X -> %02X.\n", a, d);
#endif
	
	}
	return;
}

// PSound Z80 memory read
static unsigned char PsndZRead(unsigned short a)
{
	switch (a) {
		case 0xF001:
			return YM2151ReadStatus(0);
		case 0xF002:
			return OKIM6295ReadStatus(0);
		case 0xF008:
			return PsndCode;
		case 0xF00A:
			return PsndFade;

#ifdef _DEBUG
		default:
			printf("Z80 address %04X read.\n", a);
#endif
	
	}
	return 0;
}

static unsigned char kodbZRead(unsigned short a)
{
	switch (a) {
		case 0xE001:
			return YM2151ReadStatus(0);
		case 0xE400:
			return OKIM6295ReadStatus(0);
		case 0xE800:
			return PsndCode;

#ifdef _DEBUG
		default:
			printf("Z80 address %04X read.\n", a);
#endif
	
	}
	return 0;
}

int PsndZInit()
{
	if (nCpsZRomLen < 0x8000) {					// Not enough Z80 Data
		return 1;
	}
	if (CpsZRom == NULL) {
		return 1;
	}
	
	PsndZRam = (unsigned char *)malloc(0x800);
	if (PsndZRam == NULL) {
		return 1;
	}

	if (Kodb) {
		ZetRead = kodbZRead;
		ZetWrite = kodbZWrite;
	} else {
		ZetRead = PsndZRead;
		ZetWrite = PsndZWrite;
	}
	ZetInit();

	// Read and fetch first 0x8000 of Rom
	ZetMapArea(0x0000,0x7fff,0,CpsZRom);
	ZetMapArea(0x0000,0x7fff,2,CpsZRom);

	// Map first Bank of Rom to 0x8000-0xc000
	nPsndZBank=0;
	PsndZBankMap();

	// Ram
	ZetMapArea(0xd000,0xd7ff,0,PsndZRam);
	ZetMapArea(0xd000,0xd7ff,1,PsndZRam);
	ZetMapArea(0xd000,0xd7ff,2,PsndZRam);

	// Sound chip interfaces
	ZetMemCallback(0xf000,0xffff,0);
	ZetMemCallback(0xf000,0xffff,1);

	// In case it tries to fetch other areas
	ZetMapArea(0xc000,0xcfff,2,CpsZRom);
	ZetMapArea(0xd800,0xffff,2,CpsZRom);
	ZetMemEnd();

	return 0;
}

int PsndZExit()
{
	if (PsndZRam!=NULL)	free(PsndZRam);PsndZRam=NULL;

	ZetExit();
	return 0;
}

int PsndZRun(int nWant)
{
	int nDid;
	if (nWant <= 0) {
		return 0;
	}
	nDid = ZetRun(nWant);
	return nDid;
}

// Scan the current PSound z80 state
int PsndZScan(int nAction)
{
	struct BurnArea ba;
	ZetScan(nAction);
	
	OKIM6295Scan(0);

	SCAN_VAR(FmReg)
	SCAN_VAR(nFmSel)
	SCAN_VAR(nPsndZBank)

	// Scan Ram
	memset(&ba, 0, sizeof(ba));
	ba.szName = "PsndZRam";
	ba.Data = PsndZRam;
	ba.nLen = 0x800;
	BurnAcb(&ba);

	if (nAction & 2) {
		PsndZBankMap();			// If write, bank could have changed
		if (bPsmOkay) {			// Resend all the registers to the ym2151
			for (int i = 0; i < 0x100; i++) {
				YM2151WriteReg(0, i, FmReg[i]);
			}
		}
	}
	return 0;
}
