#include "toaplan.h"
#include "eeprom_93cxx.h"

// Battle Bakraid.

static unsigned char nEEPROMData[0x42] = {
																						0xC2, 0x49,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xCF, 0x00, 0x04, 0x00, 0x00, 0x27, 0x14,
	0x00, 0x05, 0x06, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC2, 0x49,
};

static unsigned char DrvButton[8] = {0, 0, 0, 0, 0, 0, 0, 0};
static unsigned char DrvJoy1[8] = {0, 0, 0, 0, 0, 0, 0, 0};
static unsigned char DrvJoy2[8] = {0, 0, 0, 0, 0, 0, 0, 0};
static unsigned char DrvInput[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static unsigned char DrvReset = 0;
static unsigned char bDrawScreen;
static bool bVBlank;

static int nSoundData[4];

static int Z80BusRQ = 0;

static int nTextROMStatus;

static struct BurnRomInfo bbakraidRomDesc[] = {
	{"prg0u022.new", 0x080000, 0xFA8D38D3, 0x10}, //  0 CPU #0 code (even)
	{"prg2u021.bin", 0x080000, 0xFFBA8656, 0x10}, //  1
	{"prg1u023.new", 0x080000, 0x4AE9AA64, 0x10}, //  2				(odd)
	{"prg3u024.bin", 0x080000, 0x834B8AD6, 0x10}, //  3

	{"gfxu0510.bin", 0x400000, 0x9CCA3446,    1}, //  4 GP9001 Tile data
	{"gfxu0512.bin", 0x400000, 0xA2A281D5,    1}, //  5
	{"gfxu0511.bin", 0x400000, 0xE16472C0,    1}, //  6
	{"gfxu0513.bin", 0x400000, 0x8BB635A0,    1}, //  7

	{"sndu0720.bin", 0x020000, 0xE62AB246, 0x10}, //  8 Z80 program

	{"rom6.829.bin", 0x400000, 0x8848B4A0,    2}, //  9 YMZ280B (AD)PCM data
	{"rom8.830.bin", 0x400000, 0xD6224267,    2}, // 10
	{"rom8.831.bin", 0x400000, 0xA101DFB0,    2}, // 11
};

STD_ROM_PICK(bbakraid);
STD_ROM_FN(bbakraid);

// Rom information
static struct BurnRomInfo bbakradaRomDesc[] = {
	{"prg0u022.bin", 0x080000, 0x0DD59512, 0x10}, //  0 CPU #0 code (even)
	{"prg2u021.bin", 0x080000, 0xFFBA8656, 0x10}, //  1
	{"prg1u023.bin", 0x080000, 0xFECDE223, 0x10}, //  2				(odd)
	{"prg3u024.bin", 0x080000, 0x834B8AD6, 0x10}, //  3

	{"gfxu0510.bin", 0x400000, 0x9CCA3446,    1}, //  4 GP9001 Tile data
	{"gfxu0512.bin", 0x400000, 0xA2A281D5,    1}, //  5
	{"gfxu0511.bin", 0x400000, 0xE16472C0,    1}, //  6
	{"gfxu0513.bin", 0x400000, 0x8BB635A0,    1}, //  7

	{"sndu0720.bin", 0x020000, 0xE62AB246, 0x10}, //  8 Z80 program

	{"rom6.829.bin", 0x400000, 0x8848B4A0,    2}, //  9 YMZ280B (AD)PCM data
	{"rom8.830.bin", 0x400000, 0xD6224267,    2}, // 10
	{"rom8.831.bin", 0x400000, 0xA101DFB0,    2}, // 11
};

STD_ROM_PICK(bbakrada);
STD_ROM_FN(bbakrada);

static struct BurnInputInfo bbakraidInputList[] = {
	{"P1 Coin",		0, DrvButton + 3,	"p1 coin"},
	{"P1 Start",	0, DrvButton + 5,	"p1 start"},

	{"P1 Up",		0, DrvJoy1 + 0, 	"p1 up"},
	{"P1 Down",		0, DrvJoy1 + 1, 	"p1 down"},
	{"P1 Left",		0, DrvJoy1 + 2, 	"p1 left"},
	{"P1 Right",	0, DrvJoy1 + 3, 	"p1 right"},
	{"P1 Button 1",	0, DrvJoy1 + 4,		"p1 fire 1"},
	{"P1 Button 2",	0, DrvJoy1 + 5,		"p1 fire 2"},
	{"P1 Button 3",	0, DrvJoy1 + 6,		"p1 fire 3"},

	{"P2 Coin",		0, DrvButton + 4,	"p2 coin"},
	{"P2 Start",	0, DrvButton + 6,	"p2 start"},
	
	{"P2 Up",		0, DrvJoy2 + 0, 	"p2 up"},
	{"P2 Down",		0, DrvJoy2 + 1, 	"p2 down"},
	{"P2 Left",		0, DrvJoy2 + 2, 	"p2 left"},
	{"P2 Right",	0, DrvJoy2 + 3, 	"p2 right"},
	{"P2 Button 1",	0, DrvJoy2 + 4,		"p2 fire 1"},
	{"P2 Button 2",	0, DrvJoy2 + 5,		"p2 fire 2"},
	{"P2 Button 3",	0, DrvJoy2 + 6,		"p2 fire 3"},

	{"Reset",		0, &DrvReset,		"reset"},
	{"Diagnostics",	0, DrvButton + 0,	"diag"},
	{"Dip 1",		2, DrvInput + 3,	"dip"},
	{"Dip 2",		2, DrvInput + 4,	"dip"},
	{"Dip 3",		2, DrvInput + 5,	"dip"},
};

STDINPUTINFO(bbakraid);

static struct BurnDIPInfo bbakraidDIPList[] = {
	
	// Defaults
	{0x14,	0xFF, 0xFF,	0x00, NULL},
	{0x15,	0xFF, 0xFF,	0x00, NULL},
	{0x16,	0xFF, 0x3F,	0x00, NULL},
	
	// DIP 1
	{0,		0xFE, 0,	2,	  "Test mode"},
	{0x14,	0x01, 0x01,	0x00, "Normal"},
	{0x14,	0x01, 0x01,	0x01, "Test"},
	// Normal
	{0,		0xFE, 0,	2,	  "Starting coin"},
	{0x14,	0x82, 0x02,	0x00, "1 credit"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0x02,	0x02, "2 credits"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	// Free play
	{0,		0xFE, 0,	2,	  "Stick mode"},
	{0x14,	0x02, 0x02,	0x00, "Normal"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x02, 0x02,	0x02, "Special"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	// Normal
	{0,		0xFE, 0,	8,	  "Coin 1"},
	{0x14,	0x01, 0x1C, 0x00, "1 coin 1 credit"},
	{0x14,	0x01, 0x1C, 0x04, "1 coin 2 credits"},
	{0x14,	0x01, 0x1C, 0x08, "1 coin 3 credits"},
	{0x14,	0x01, 0x1C, 0x0C, "1 coin 3 credits"},
	{0x14,	0x01, 0x1C, 0x10, "2 coins 1 credit"},
	{0x14,	0x01, 0x1C, 0x14, "3 coins 1 credit"},
	{0x14,	0x01, 0x1C, 0x18, "4 coins 1 credit"},
	{0x14,	0x01, 0x1C, 0x1C, "Free Play"},
	// 0x1C: Free play settings active
	// Normal
	{0,		0xFE, 0,	7,	  "Coin 2"},
	{0x14,	0x82, 0xE0,	0x00, "1 coin 1 credit"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0xE0,	0x20, "1 coin 2 credits"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0xE0,	0x40, "1 coin 3 credits"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0xE0,	0x60, "1 coin 3 credits"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0xE0,	0x80, "2 coins 1 credit"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0xE0,	0xA0, "3 coins 1 credit"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0xE0,	0xC0, "4 coins 1 credit"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x82, 0xE0,	0x04, "1 coin 1 credit"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	// Free play
	{0,		0xFE, 0,	2,	  "Hit score"},
	{0x14,	0x02, 0x20, 0x00, "Off"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x02, 0x20, 0x20, "On"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0,		0xFE, 0,	2,	  "Sound effect"},
	{0x14,	0x02, 0x40,	0x00, "Off"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x02, 0x40,	0x40, "On"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0,		0xFE, 0,	2,	  "Music"},
	{0x14,	0x02, 0x80,	0x00, "Off"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	{0x14,	0x02, 0x80,	0x80, "On"},
	{0x14,	0x00, 0x1C, 0x1C, NULL},
	
	// DIP 2
	{0,		0xFE, 0,	4,	  "Start rank"},
	{0x15,	0x01, 0x03, 0x00, "Normal"},
	{0x15,	0x01, 0x03, 0x01, "Easy"},
	{0x15,	0x01, 0x03, 0x02, "Hard"},
	{0x15,	0x01, 0x03, 0x03, "Very hard"},
	{0,		0xFE, 0,	4,	  "Timer rank"},
	{0x15,	0x01, 0x0C, 0x00, "Normal"},
	{0x15,	0x01, 0x0C, 0x04, "Low"},
	{0x15,	0x01, 0x0C, 0x08, "High"},
	{0x15,	0x01, 0x0C, 0x0C, "Highest"},
	{0,		0xFE, 0,	4,	  "Player counts"},
	{0x15,	0x01, 0x30, 0x00, "3"},
	{0x15,	0x01, 0x30, 0x01, "4"},
	{0x15,	0x01, 0x30, 0x02, "2"},
	{0x15,	0x01, 0x30, 0x03, "1"},
	{0,		0xFE, 0,	4,	  "Extra player"},
    {0x15,	0x01, 0xC0, 0x00, "2000000 each"},
    {0x15,	0x01, 0xC0, 0x40, "3000000 each"},
    {0x15,	0x01, 0xC0, 0x80, "4000000 each"},
    {0x15,	0x01, 0xC0, 0xC0, "No extra player"},

	// DIP 3
	{0,		0xFE, 0,	2,	  "Screen flip"},
	{0x16,	0x01, 0x01, 0x00, "Off"},
	{0x16,	0x01, 0x01, 0x01, "On"},
	{0,		0xFE, 0,	2,	  "Demo sound"},
	{0x16,	0x01, 0x02, 0x00, "On"},
	{0x16,	0x01, 0x02, 0x02, "Off"},
	{0,		0xFE, 0,	2,	  "Stage edit"},
	{0x16,	0x01, 0x04, 0x00, "Disable"},
	{0x16,	0x01, 0x04, 0x04, "Enable"},
	{0,		0xFE, 0,	2,	  "Continue play"},
	{0x16,	0x01, 0x08, 0x00, "Enable"},
	{0x16,	0x01, 0x08, 0x08, "Disable"},
	{0,		0xFE, 0,	2,	  "Invincible"},
	{0x16,	0x01, 0x10, 0x00, "Off"},
	{0x16,	0x01, 0x10, 0x10, "On"},	
	{0,		0xFE, 0,	2,	  "Score ranking"},
	{0x16,	0x01, 0x20, 0x00, "Save"},
	{0x16,	0x01, 0x20, 0x20, "No save"},	
};

STDDIPINFO(bbakraid);

static int __fastcall DrvResetCallback()
{
	// Reset instruction on 68000
	ZetReset();						// Reset Z80 (CPU #1)

	return 0;
}

static unsigned char *Mem = NULL, *MemEnd = NULL;
static unsigned char *RamStart, *RamEnd;
static unsigned char *Rom01;
static unsigned char *Ram01, *Ram02, *RamPal;

static int nColCount = 0x0800;

static int MemIndex()
{
	unsigned char *Next; Next = Mem;
	Rom01		= Next; Next += 0x200000;			//
	RomZ80		= Next; Next += 0x020000;			// Z80 ROM
	GP9001ROM[0]= Next; Next += nGP9001ROMSize[0];	// GP9001 tile data
	YMZ280BROM	= Next;	Next += 0xC00000;
	RamStart	= Next;
	ExtraTROM	= Next; Next += 0x008000;			// Extra Text layer tile data
	ExtraTRAM	= Next; Next += 0x002000;			// Extra tile layer
	Ram01		= Next; Next += 0x005000;			// RAM + Extra text layer scroll/offset
	Ram02		= Next; Next += 0x008000;			//
	RamPal		= Next; Next += 0x001000;			// palette
	RamZ80		= Next; Next += 0x004000;			// Z80 RAM
	GP9001RAM[0]= Next; Next += 0x004000;
	GP9001Reg[0]= (unsigned short*)Next; Next += 0x0100 * sizeof(short);
	RamEnd		= Next;
	ToaPalette	= (unsigned int *)Next; Next += nColCount * sizeof(unsigned int);
	MemEnd		= Next;

 	ExtraTScroll= Ram01;							// Extra text layer scroll
	ExtraTOffset= Ram01 + 0x000200;					// Extra text layer offset
	return 0;
}

// Scan ram
static int DrvScan(int nAction, int *pnMin)
{
	struct BurnArea ba;

	if (nAction & 4) {					// Scan volatile ram
		if (pnMin) {					// Return minimum compatible version
			*pnMin = 0x020901;
		}

		memset(&ba, 0, sizeof(ba));
    	ba.Data		= RamStart;
		ba.nLen		= RamEnd - RamStart;
		ba.szName	= "RAM";
		BurnAcb(&ba);

		SekScan(nAction & 3);			// scan 68000 states
		ZetScan(nAction);				// Scan Z80

		SCAN_VAR(GP9001TileBank);		
		SCAN_VAR(DrvInput);
		SCAN_VAR(nSoundData);
		SCAN_VAR(Z80BusRQ);		
		
	}

//	EEPROMScan(nAction);				// Scan EEPROM

	return 0;
}

static int LoadRoms()
{
	// Load 68000 ROM
	if (ToaLoadCode(Rom01, 0, 4)) {
		return 1;
	}

	// Load GP9001 tile data
	ToaLoadGP9001Tiles(GP9001ROM[0], 4, 4, nGP9001ROMSize[0]);

	// Load the z80 Rom
	BurnLoadRom(RomZ80, 8, 1);

	// Load YMZ280B (AD)PCM data
	BurnLoadRom(YMZ280BROM + 0x000000, 9, 1);
	BurnLoadRom(YMZ280BROM + 0x400000, 10, 1);
	BurnLoadRom(YMZ280BROM + 0x800000, 11, 1);

	return 0;
}

static unsigned char ToaZIn(unsigned short nAddress)
{
	nAddress &= 0xFF;
	switch (nAddress) {
		case 0x48:
//			printf("Z80 read nSoundData[0].\n");
			return nSoundData[0];
		case 0x4A:
//			printf("Z80 read nSoundData[1].\n");
			return nSoundData[1];
		
		case 0x81:
			// YMZ280B status port.
//			printf("Z80 read YMZ280B status port.\n");
			return YMZ280BReadStatus();

//		default:
//			printf("Z80 read port #%02X\n", nAddress & 0xFF);
	}
	return 0;
}

static void ToaZOut(unsigned short nAddress, unsigned char nValue)
{
	nAddress &= 0xFF;
	switch (nAddress) {
		case 0x40:
//			printf("Z80 wrote nSoundData[2] ->%02X.\n", nValue);
			nSoundData[2] = nValue;
			break;
		case 0x42:
//			printf("Z80 wrote nSoundData[3] ->%02X.\n", nValue);
			nSoundData[3] = nValue;
			break;
		
//		case 0x44:				// ???
//		case 0x46:				// Acknowledge interrupt
//			break;
		
		case 0x80:
			// YMZ280B register select
//			printf("Z80 wrote YMZ280B register select -> %02X.\n", nValue);
			YMZ280BSelectRegister(nValue);
			break;
		case 0x81:
			// YMZ280B register write
//			printf("Z80 wrote YMZ280B register write -> %02X.\n", nValue);
			YMZ280BWriteRegister(nValue);
			break;
		
//		default:
//			printf("Z80 wrote value %02X to port #%02x.\n", nValue, nAddress);
	}
}

static int DrvZ80Init()
{
	// Init the z80
	ZetIn = ToaZIn;
	ZetOut = ToaZOut;

	ZetInit();
	// ROM
	ZetMapArea(0x0000, 0xBFFF, 0, RomZ80);	// Direct Read from ROM
	ZetMapArea(0x0000, 0xBFFF, 2, RomZ80);	// Direct Fetch from ROM
	// RAM
	ZetMapArea(0xC000, 0xFFFF, 0, RamZ80);	// Direct Read from RAM
	ZetMapArea(0xC000, 0xFFFF, 1, RamZ80);	// Direct Write to RAM
	ZetMapArea(0xC000, 0xFFFF, 2, RamZ80);	//
	
	ZetMemEnd();

	return 0;
}

static void Map68KTextROM(bool bMapTextROM)
{
	if (bMapTextROM) {
		if (nTextROMStatus != 1) {
			SekMemory(ExtraTROM,  0x200000, 0x207FFF, SM_RAM);	// Extra text tile memory
			
			nTextROMStatus = 1;
		}
	} else {
		if (nTextROMStatus != 0) {
			SekMemory(ExtraTRAM,  0x200000, 0x201FFF, SM_RAM);	// Extra text tilemap RAM
			SekMemory(RamPal,	  0x202000, 0x202FFF, SM_RAM);	// Palette RAM
			SekMemory(Ram01,	  0x203000, 0x207FFF, SM_RAM);  // Extra text Scroll & offset; RAM
		
			nTextROMStatus = 0;
		}
	}
}

static unsigned char __fastcall Drv1ReadByte(unsigned int sekAddress)
{
	switch (sekAddress) {

		case 0x500000:								// Player 2 Inputs
			return DrvInput[1];
		case 0x500001:								// Player 1 Inputs
			return DrvInput[0];
		case 0x500002:								// Other inputs
			return DrvInput[5];
		case 0x500003:								// Dipswitch 3
			return DrvInput[2];
		case 0x500004:								// Dipswitch 2
			return DrvInput[4];
		case 0x500005:								// Dipswitch 1
			return DrvInput[3];

		// These addresses contain the response of the Z80 to the (sound) commands	
		case 0x500011:
//			printf("Read sound command [2] (%02X).\n", nSoundData[2]);
			return nSoundData[2];
		case 0x500013:
//			printf("Read sound command [3] (%02X).\n", nSoundData[3]);
			return nSoundData[3];

		case 0x500019:
			return (EEPROMRead() << 4) | (Z80BusRQ >> 4);

		default: {
			// Read Z80 ROM
			if ((sekAddress & 0x00FC0000) == 0x00300000) {
				return RomZ80[(sekAddress & 0x3FFFF) >> 1];
			}
			
//			printf("Attempt to read byte value of location %x\n", sekAddress);
		}
	}
		
	return 0;
}

static unsigned short __fastcall Drv1ReadWord(unsigned int sekAddress)
{
	switch (sekAddress) {

		case 0x400008:
			return ToaGP9001ReadRAM_Hi(0);
		case 0x40000A:
			return ToaGP9001ReadRAM_Lo(0);

		// Video status register
		case 0x500006:
			return ToaScanlineRegister();

		// These addresses contain the response of the Z80 to the (sound) commands	
		case 0x500010:
//			printf("Read sound command [2] (%02X).\n", nSoundData[2]);
			return nSoundData[2];
		case 0x500012:
//			printf("Read sound command [3] (%02X).\n", nSoundData[3]);
			return nSoundData[3];

		case 0x500018:
			return (EEPROMRead() << 4) | (Z80BusRQ >> 4);

		default: {
			// Read Z80 ROM
			if ((sekAddress & 0x00FC0000) == 0x00300000) {
				return RomZ80[(sekAddress & 0x3FFFF) >> 1];
			}

//			printf("Attempt to read word value of location %x\n", sekAddress);			
		}
	}
		
	return 0;
}

static void __fastcall Drv1WriteByte(unsigned int sekAddress, unsigned char byteValue)
{
	switch (sekAddress) {

		case 0x500009:								// Coin control
			return;

		case 0x50001F:
			Z80BusRQ = byteValue & 0x10;
			EEPROMWrite(byteValue & 8, byteValue & 1, byteValue & 4);
			return;

//		default:
//			printf("Attempt to write byte value %x to location %x\n", byteValue, sekAddress);
	}
}

static void __fastcall Drv1WriteWord(unsigned int sekAddress, unsigned short wordValue)
{
	switch (sekAddress) {
		case 0x400000:
			ToaGP9001WriteRegister(wordValue);
			return;

		case 0x400004:
			ToaGP9001SelectRegister(wordValue);
			return;

		case 0x400008:
		case 0x40000A:
			ToaGP9001WriteRAM(wordValue, 0);
			return;

		case 0x40000C:								// Set GP9001 VRAM address-pointer
			ToaGP9001SetRAMPointer(wordValue);
			return;
		
		case 0x500014:
//			printf("Sound command [0] -> %02X.\n", wordValue);
			nSoundData[0] = wordValue;
			return;
		case 0x500016:
//			printf("Sound command [1] -> %02X.\n", wordValue);
			nSoundData[1] = wordValue;
			return;
		
		// This register is always written to after writing (sound) commands for the Z80
		case 0x50001A:
//			printf("Z80 NMI triggered.\n");
			ZetNmi();
			ZetRun(0x0200);
			nCyclesDone[1] += 0x0200;
			return;
		
		// Serial EEPROM command
		case 0x50001E:
			Z80BusRQ = wordValue & 0x10;
			EEPROMWrite(wordValue & 8, wordValue & 1, wordValue & 4);
			return;

		case 0x500080:
			Map68KTextROM(false);
			return;
		
		case 0x50001C:		// ??? Acknowledge interrupt ???
		case 0x500082:		//
			return;

		case 0x5000C0:
		case 0x5000C1:
		case 0x5000C2:
		case 0x5000C3:
		case 0x5000C4:
		case 0x5000C5:
		case 0x5000C6:
		case 0x5000C7:
		case 0x5000C8:
		case 0x5000C9:
		case 0x5000CA:
		case 0x5000CB:
		case 0x5000CC:
		case 0x5000CD:
		case 0x5000CE:
			GP9001TileBank[(sekAddress & 0x0F) >> 1] = ((wordValue & 0x0F) << 15);
			return;

//		default:
//			printf("Attempt to write word value %x to location %x\n", wordValue, sekAddress);
	}
}

static int DrvDoReset()
{
	SekOpen(0);
	SekReset();
	SekClose();

	ZetReset();

	EEPROMReset();

	Z80BusRQ = 0;

	memset(nSoundData, 0, sizeof(nSoundData));

	return 0;
}

static int bbakraidInit()
{
	int nLen;

	bRotatedScreen = true;
	nGP9001ROMSize[0] = 0x1000000;

	// Find out how much memory is needed
	Mem = NULL;
	MemIndex();
	nLen = MemEnd - (unsigned char *)0;
	if ((Mem = (unsigned char *)malloc(nLen)) == NULL) {
		return 1;
	}
	memset(Mem, 0, nLen);										// blank all memory
	MemIndex();													// Index the allocated memory
	
	EEPROMInit(9, 8);											// EEPROM has 512 8-bit words

	if (strcmp("bbakraid", BurnDrvText(0)) == 0) {
		EEPROMFill(nEEPROMData, 0x0200 - 0x42, 0x42);
	}
	
	// Load the roms into memory
	if (LoadRoms()) {
		return 1;
	}

	{
		SekInit(1);												// Allocate 68000
		SekExt[0].ResetCallback = DrvResetCallback;				// Get cpu 1 reset requests

		// Map 68000 memory:
	    SekOpen(0);
		SekMemory(Rom01,		0x000000, 0x1FFFFF, SM_ROM);	// CPU 0 ROM
		SekMemory(Ram02,		0x208000, 0x20FFFF, SM_RAM);

		Map68KTextROM(true);

		SekExt[0].ReadWord	= Drv1ReadWord;
		SekExt[0].ReadByte	= Drv1ReadByte;
		SekExt[0].WriteWord	= Drv1WriteWord;
		SekExt[0].WriteByte	= Drv1WriteByte;

		SekClose();
	}

	nSpriteYOffset = 0x0001;

	nLayer0XOffset = -0x01D6;
	nLayer1XOffset = -0x01D8;
	nLayer2XOffset = -0x01DA;

	ToaInitGP9001();

	nExtraTXOffset = 0x2C;
	ToaExtraTextInit();

	DrvZ80Init();				// Initialize Z80

	YMZ280BInit(16934400);

	nToaPalLen = nColCount;
	ToaPalSrc = RamPal;
	ToaPalInit();

	nTextROMStatus = -1;
	bDrawScreen = true;

	DrvDoReset(); 				// Reset machine
	
	return 0;
}

static int DrvExit()
{
	ToaPalExit();
	YMZ280BExit();
	ToaExitGP9001();
	ToaExtraTextExit();
	ToaZExit();				// Z80 exit
	SekExit();				// Deallocate 68000s

	EEPROMExit();

	// Deallocate all used memory
	free(Mem);
	Mem = NULL;

	return 0;
}

static int DrvDraw()
{
	ToaClearScreen();
	
	if (bDrawScreen) {
		ToaGetBitmap();		
		ToaRenderGP9001();					// Render GP9001 graphics
		ToaExtraTextLayer();				// Render extra text layer
	}
	
	ToaPalUpdate();							// Update the palette

	return 0;
}

inline static int CheckSleep(int)
{
	return 0;
}

static int DrvFrame()
{
	int nCyclesVBlank;
	int nInterleave = 4;

	if (DrvReset) {														// Reset machine
		DrvDoReset();
	}

	// Compile digital inputs
	DrvInput[0] = 0x00;													// Buttons
	DrvInput[1] = 0x00;													// Player 1
	DrvInput[2] = 0x00;													// Player 2
	for (int i = 0; i < 8; i++) {
		DrvInput[0] |= (DrvJoy1[i] & 1) << i;
		DrvInput[1] |= (DrvJoy2[i] & 1) << i;
		DrvInput[2] |= (DrvButton[i] & 1) << i;
	}
	ToaClearOpposites(&DrvInput[0]);
	ToaClearOpposites(&DrvInput[1]);

	nCyclesTotal[0] = TOA_68K_SPEED / 60;
	nCyclesTotal[1] = TOA_Z80_SPEED / 60;
	nCyclesDone[0] = nCyclesDone[1] = 0;

	nCyclesVBlank = nCyclesTotal[0] - ((nCyclesTotal[0] * 2) / 0x0106);
	bVBlank = false;

	int nSoundBufferPos = 0;

	SekOpen(0);
	for (int i = 0; i < nInterleave; i++) {
    	int nCurrentCPU;
		int nNext;

		// Run 68000

		nCurrentCPU = 0;
		nNext = (i + 1) * nCyclesTotal[nCurrentCPU] / nInterleave;

		// Trigger VBlank interrupt
		if (!bVBlank && nNext > nCyclesVBlank) {
			if (nCyclesDone[nCurrentCPU] < nCyclesVBlank) {
				nCyclesSegment = nCyclesVBlank - nCyclesDone[nCurrentCPU];
				nCyclesDone[nCurrentCPU] += SekRun(nCyclesSegment);
			}

			ToaBufferGP9001Sprites();
			
			bVBlank = true;

			ZetRaiseIrq(0xFF);
			nCyclesSegment = 0x0100;
			ZetRun(nCyclesSegment);
			nCyclesDone[1] += nCyclesSegment;

			SekInterrupt(3);
		}

		nCyclesSegment = nNext - nCyclesDone[nCurrentCPU];
		if (bVBlank || (!CheckSleep(nCurrentCPU))) {					// See if this CPU is busywaiting
			nCyclesDone[nCurrentCPU] += SekRun(nCyclesSegment);
		} else {
			nCyclesDone[nCurrentCPU] += nCyclesSegment;
		}

		// Run Z80
		nCurrentCPU = 1;
		nNext = (i + 1) * nCyclesTotal[nCurrentCPU] / nInterleave;
		nCyclesSegment = nNext - nCyclesDone[nCurrentCPU];
		nCyclesSegment = ZetRun(nCyclesSegment);
		nCyclesDone[nCurrentCPU] += nCyclesSegment;

		{
			// Render sound segment
			if (pBurnSoundOut) {
				int nSegmentLength = nBurnSoundLen / nInterleave;
				short* pSoundBuf = pBurnSoundOut + (nSoundBufferPos << 1);
				YMZ280BRender(pSoundBuf, nSegmentLength);
				nSoundBufferPos += nSegmentLength;
			}
		}
	}

	SekClose();

	{
		// Make sure the buffer is entirely filled.
		if (pBurnSoundOut) {
			int nSegmentLength = nBurnSoundLen - nSoundBufferPos;
			short* pSoundBuf = pBurnSoundOut + (nSoundBufferPos << 1);
			if (nSegmentLength) {
				YMZ280BRender(pSoundBuf, nSegmentLength);
			}
		}
	}

	if (pBurnDraw) {
		DrvDraw();																// Draw screen if needed
	}

	return 0;
}

struct BurnDriver BurnDrvBattleBakraidU = {
	{"bbakraid", "Battle Bakraid (ver. Tue Jun 8 1999)", "Preliminary sound", "Eighting", "Toaplan", "1999", NULL, NULL},
	1, 2, HARDWARE_TOAPLAN_RAIZING,
	NULL, bbakraidRomInfo, bbakraidRomName, bbakraidInputInfo, bbakraidDIPInfo,
	bbakraidInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &ToaRecalcPalette,
	240, 320, 3, 4
};

struct BurnDriver BurnDrvBattleBakraid = {
	{"bbakrada", "Battle Bakraid (ver. Wed Apr 7 1999)", "Preliminary sound", "Eighting", "Toaplan", "1999", "bbakraid", NULL},
	1, 2, HARDWARE_TOAPLAN_RAIZING,
	NULL, bbakradaRomInfo, bbakradaRomName, bbakraidInputInfo, bbakraidDIPInfo,
	bbakraidInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &ToaRecalcPalette,
	240, 320, 3, 4
};

