// FB Alpha Alien Invaders driver module
// Based on MAME driver by David Haywood, Mariusz Wojcieszek

#include "tiles_generic.h"
#include "m6502_intf.h"
#include "dac.h"

static UINT8 *AllMem;
static UINT8 *MemEnd;
static UINT8 *AllRam;
static UINT8 *RamEnd;
static UINT8 *Drv6502ROM;
static UINT8 *DrvProtPROM;
static UINT8 *Drv6502RAM;
static UINT8 *DrvVidRAM;

static UINT32 *DrvPalette;
static UINT8 DrvRecalc;

static UINT8 previrqmask;
static UINT8 irqmask;

static UINT8 DrvJoy1[6];
static UINT8 DrvDips[1];
static UINT8 DrvReset;

static struct BurnInputInfo AlinvadeInputList[] = {
	{"P1 Coin",		BIT_DIGITAL,	DrvJoy1 + 0,	"p1 coin"	},
	{"P1 Start",		BIT_DIGITAL,	DrvJoy1 + 4,	"p1 start"	},
	{"P1 Left",		BIT_DIGITAL,	DrvJoy1 + 3,	"p1 left"	},
	{"P1 Right",		BIT_DIGITAL,	DrvJoy1 + 2,	"p1 right"	},
	{"P1 Button 1",		BIT_DIGITAL,	DrvJoy1 + 1,	"p1 fire 1"	},

	{"Reset",		BIT_DIGITAL,	&DrvReset,	"reset"		},
	{"Dips",		BIT_DIPSWITCH,	DrvDips + 0,	"dip"		},
};

STDINPUTINFO(Alinvade)

static struct BurnDIPInfo AlinvadeDIPList[]=
{
	{0x06, 0xff, 0xff, 0x00, NULL		},

	{0   , 0xfe, 0   ,    4, "Lives"	},
	{0x06, 0x01, 0x03, 0x00, "2"		},
	{0x06, 0x01, 0x03, 0x01, "3"		},
	{0x06, 0x01, 0x03, 0x02, "4"		},
	{0x06, 0x01, 0x03, 0x03, "5"		},

	{0   , 0xfe, 0   ,    2, "×"		},
	{0x06, 0x01, 0x04, 0x00, "Off"		},
	{0x06, 0x01, 0x04, 0x04, "On"		},
};

STDDIPINFO(Alinvade)

static UINT8 alinvade_read(UINT16 address)
{
	switch (address)
	{
		case 0x4000: return (~DrvJoy1[0] & 1) * 0x10;
		case 0x6000: return DrvDips[0];
		case 0x8000: return ( DrvJoy1[1] & 1) * 0x20;
		case 0x8001: return ( DrvJoy1[2] & 1) * 0x20;
		case 0x8002: return ( DrvJoy1[3] & 1) * 0x20;
		case 0x8003: return ( DrvJoy1[4] & 1) * 0x20;
		case 0x8004: return ( DrvJoy1[5] & 1) * 0x20;

		case 0xe800: return 0;
	}

	return 0;
}

static void alinvade_write(UINT16 address, UINT8 data)
{
	switch (address)
	{
		case 0x2000: // definitely sound writes of some sort
			DACWrite(0, data);
		return;

		case 0xa000:
		return;		// nop

		case 0xe400:
		return;		// nop

		case 0xe800:
			if (previrqmask == 0 && (data & 1) == 1)
				irqmask ^= 1;
			previrqmask = data & 1;
		return;
	}
}

static INT32 DrvSyncDAC()
{
	return (INT32)(float)(nBurnSoundLen * (M6502TotalCycles() / (2000000.000 / 60)));
}

static INT32 DrvDoReset()
{
	memset (AllRam, 0, RamEnd - AllRam);

	M6502Open(0);
	M6502Reset();
	M6502Close();

	DACReset();

	previrqmask = 0;
	irqmask = 1;

	return 0;
}

static INT32 MemIndex()
{
	UINT8 *Next; Next = AllMem;

	Drv6502ROM		= Next; Next += 0x002000;

	DrvProtPROM		= Next; Next += 0x000100;

	DrvPalette		= (UINT32*)Next; Next += 0x0002 * sizeof(UINT32);

	AllRam			= Next;

	Drv6502RAM		= Next; Next += 0x000200;
	DrvVidRAM		= Next; Next += 0x000c00;

	RamEnd			= Next;

	MemEnd			= Next;

	return 0;
}

static INT32 DrvInit()
{
	AllMem = NULL;
	MemIndex();
	INT32 nLen = MemEnd - (UINT8 *)0;
	if ((AllMem = (UINT8 *)BurnMalloc(nLen)) == NULL) return 1;
	memset(AllMem, 0, nLen);
	MemIndex();

	{
		if (BurnLoadRom(Drv6502ROM + 0x0000, 0, 1)) return 1;
		if (BurnLoadRom(Drv6502ROM + 0x0c00, 1, 1)) return 1;
		if (BurnLoadRom(Drv6502ROM + 0x1000, 2, 1)) return 1;
		if (BurnLoadRom(Drv6502ROM + 0x1400, 3, 1)) return 1;
		if (BurnLoadRom(Drv6502ROM + 0x1800, 4, 1)) return 1;
		if (BurnLoadRom(Drv6502ROM + 0x1c00, 5, 1)) return 1;
	}

	// Protection hack, since prom isn't dumped we replace it with this
	{
		for (INT32 i = 0; i < 0x100; i++) {
			DrvProtPROM[i] = ((i & 0xf) == 0x0f) ? 0x60 : 0xea;
		}
	}

	M6502Init(0, TYPE_M6502);
	M6502Open(0);
	M6502MapMemory(Drv6502RAM,	0x0000, 0x01ff, MAP_RAM);
	M6502MapMemory(DrvVidRAM,	0x0400, 0x0fff, MAP_RAM);
	for (INT32 i = 0; i < 0x1000; i+=0x100)
		M6502MapMemory(DrvProtPROM,	0xc000+i, 0xc0ff+i, MAP_ROM); // mirrored
	M6502MapMemory(Drv6502ROM,	0xe000, 0xffff, MAP_ROM);
	M6502SetWriteHandler(alinvade_write);
	M6502SetReadHandler(alinvade_read);
	M6502Close();

	DACInit(0, 0, 0, DrvSyncDAC);
	DACSetRoute(0, 1.00, BURN_SND_ROUTE_BOTH);

	GenericTilesInit();

	DrvDoReset();

	return 0;
}

static INT32 DrvExit()
{
	GenericTilesExit();

	DACExit();

	M6502Exit();

	BurnFree(AllMem);

	return 0;
}

static void draw_bitmap()
{
	for (INT32 offs = 0; offs < (128 * 128)/8; offs++)
	{
		UINT8 sx = (offs << 3) & 0x78;
		INT32 sy = (offs >> 4) & 0x7f;
		UINT8 data = DrvVidRAM[offs];
		UINT16 *dst = pTransDraw + (sy * nScreenWidth) + sx;

		for (INT32 x = 0; x < 8; x++)
		{
			dst[x] = data & 1;
			data >>= 1;
		}
	}
}

static INT32 DrvDraw()
{
	DrvPalette[0] = BurnHighCol(0x00,0x00,0x00, 0);
	DrvPalette[1] = BurnHighCol(0xff,0xff,0xff, 0);

	draw_bitmap();

	BurnTransferCopy(DrvPalette);

	return 0;
}

static INT32 DrvFrame()
{
	if (DrvReset) {
		DrvDoReset();
	}

	M6502NewFrame();

	INT32 nTotalCycles = 2000000 / 60;

	M6502Open(0);
	M6502Run(nTotalCycles);
	if (irqmask) M6502SetIRQLine(0, CPU_IRQSTATUS_AUTO);
	M6502Close();

	if (pBurnSoundOut) {
		DACUpdate(pBurnSoundOut, nBurnSoundLen);
	}

	if (pBurnDraw) {
		DrvDraw();
	}

	return 0;
}

static INT32 DrvScan(INT32 nAction,INT32 *pnMin)
{
	struct BurnArea ba;

	if (pnMin) {
		*pnMin = 0x029722;
	}

	if (nAction & ACB_VOLATILE) {
		memset(&ba, 0, sizeof(ba));
		ba.Data	  = AllRam;
		ba.nLen	  = RamEnd - AllRam;
		ba.szName = "All Ram";
		BurnAcb(&ba);

		M6502Scan(nAction);

		DACScan(nAction,pnMin);

		SCAN_VAR(previrqmask);
		SCAN_VAR(irqmask);
	}

	return 0;
}


// Alien Invaders

static struct BurnRomInfo alinvadeRomDesc[] = {
	{ "alien28.708",	0x0400, 0xde376295, 1 | BRF_PRG | BRF_ESS }, //  0 m6502 Code
	{ "alien29.708",	0x0400, 0x20212977, 1 | BRF_PRG | BRF_ESS }, //  1
	{ "alien30.708",	0x0400, 0x734b691c, 1 | BRF_PRG | BRF_ESS }, //  2
	{ "alien31.708",	0x0400, 0x5a70535c, 1 | BRF_PRG | BRF_ESS }, //  3
	{ "alien32.708",	0x0400, 0x332dd234, 1 | BRF_PRG | BRF_ESS }, //  4
	{ "alien33.708",	0x0400, 0xe0d57fc7, 1 | BRF_PRG | BRF_ESS }, //  5

	{ "prom",		0x0010, 0x00000000, 2 | BRF_NODUMP        }, //  6 Protection prom
};

STD_ROM_PICK(alinvade)
STD_ROM_FN(alinvade)

struct BurnDriver BurnDrvAlinvade = {
	"alinvade", NULL, NULL, NULL, "198?",
	"Alien Invaders\0", "preliminary sound", "Forbes?", "Miscellaneous",
	NULL, NULL, NULL, NULL,
	BDF_GAME_WORKING | BDF_ORIENTATION_VERTICAL | BDF_ORIENTATION_FLIPPED, 1, HARDWARE_MISC_PRE90S, GBF_VERSHOOT, 0,
	NULL, alinvadeRomInfo, alinvadeRomName, NULL, NULL, NULL, NULL, AlinvadeInputInfo, AlinvadeDIPInfo,
	DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 2,
	128, 128, 4, 3
};
