// FinalBurn Neo Dr. Tomy driver module
// Based on MAME driver by Pierpaolo Prazzoli

#include "tiles_generic.h"
#include "m68000_intf.h"
#include "msm6295.h"
#include "burn_pal.h"

static UINT8 *AllMem;
static UINT8 *MemEnd;
static UINT8 *RamStart;
static UINT8 *RamEnd;
static UINT8 *Drv68KROM;
static UINT8 *DrvGfxROM[2];
static UINT8 *DrvSndROM;
static UINT8 *DrvSprRAM;
static UINT8 *Drv68KRAM;
static UINT8 *DrvVidRAM;

static UINT8 DrvRecalc;

static INT32 okibank;

static UINT8 DrvJoy1[8];
static UINT8 DrvJoy2[8];
static UINT8 DrvDips[2];
static UINT8 DrvInputs[2];
static UINT8 DrvReset;

static struct BurnInputInfo DrvInputList[] = {
	{"Coin 1", 		BIT_DIGITAL  , DrvJoy1 + 6, "p1 coin"	},
	{"Coin 2", 		BIT_DIGITAL  , DrvJoy1 + 7, "p2 coin"	},

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

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

	{"Reset",	  	BIT_DIGITAL  , &DrvReset,   "reset"		},
	{"Dip 1",	  	BIT_DIPSWITCH, DrvDips + 0, "dip"		},
	{"Dip 2",	  	BIT_DIPSWITCH, DrvDips + 1, "dip"		},
};

STDINPUTINFO(Drv)

static struct BurnDIPInfo DrvDIPList[]=
{
	{0x11, 0xff, 0xff, 0xff, NULL 					},
	{0x12, 0xff, 0xff, 0xaf, NULL 					},

	{0   , 0xfe, 0   , 11  , "Coin A" 				},
	{0x11, 0x01, 0x0f, 0x0a, "2 Coins 1 Credit"		},
	{0x11, 0x01, 0x0f, 0x07, "3 Coins 2 Credits"	},
	{0x11, 0x01, 0x0f, 0x00, "5 Coins 4 Credits"	},
	{0x11, 0x01, 0x0f, 0x0f, "1 Coin  1 Credit"		},
	{0x11, 0x01, 0x0f, 0x06, "3 Coins 4 Credits"	},
	{0x11, 0x01, 0x0f, 0x09, "2 Coins 3 Credits"	},
	{0x11, 0x01, 0x0f, 0x0e, "1 Coin  2 Credits"	},
	{0x11, 0x01, 0x0f, 0x08, "2 Coins 5 Credits"	},
	{0x11, 0x01, 0x0f, 0x0d, "1 Coin  3 Credits"	},
	{0x11, 0x01, 0x0f, 0x0c, "1 Coin  4 Credits"	},
	{0x11, 0x01, 0x0f, 0x0b, "1 Coin  5 Credits"	},

	{0   , 0xfe, 0   , 11  , "Coin B" },
	{0x11, 0x01, 0xf0, 0xa0, "2 Coins 1 Credit" 	},
	{0x11, 0x01, 0xf0, 0x70, "3 Coins 2 Credits"	},
	{0x11, 0x01, 0xf0, 0x00, "5 Coins 4 Credits"	},
	{0x11, 0x01, 0xf0, 0xf0, "1 Coin  1 Credit"		},
	{0x11, 0x01, 0xf0, 0x60, "3 Coins 4 Credits"	},
	{0x11, 0x01, 0xf0, 0x90, "2 Coins 3 Credits"	},
	{0x11, 0x01, 0xf0, 0xe0, "1 Coin  2 Credits"	},
	{0x11, 0x01, 0xf0, 0x80, "2 Coins 5 Credits"	},
	{0x11, 0x01, 0xf0, 0xd0, "1 Coin  3 Credits"	},
	{0x11, 0x01, 0xf0, 0xc0, "1 Coin  4 Credits"	},
	{0x11, 0x01, 0xf0, 0xb0, "1 Coin  5 Credits"	},

	{0   , 0xfe, 0   , 2   , "Time"					},
	{0x12, 0x01, 0x01, 0x00, "Less"					},
	{0x12, 0x01, 0x01, 0x01, "More"					},

	{0   , 0xfe, 0   , 2   , "Number of Virus"		},
	{0x12, 0x01, 0x02, 0x02, "Less"					},	
	{0x12, 0x01, 0x02, 0x00, "More"					},

	{0   , 0xfe, 0   , 2   , "Test Mode"			},
	{0x12, 0x01, 0x08, 0x08, "Off"					},
	{0x12, 0x01, 0x08, 0x00, "On"					},

	{0   , 0xfe, 0   , 2   , "Demo Sounds"			},
	{0x12, 0x01, 0x10, 0x10, "Off"					},
	{0x12, 0x01, 0x10, 0x00, "On"					},

	{0   , 0xfe, 0   , 2   , "Language"				},
	{0x12, 0x01, 0x20, 0x20, "English"				},
	{0x12, 0x01, 0x20, 0x00, "Italian"				},

	{0   , 0xfe, 0   , 2   , "Allow Continue"		},
	{0x12, 0x01, 0x40, 0x40, "No"					},
	{0x12, 0x01, 0x40, 0x00, "Yes"					},
};

STDDIPINFO(Drv)

static inline void set_okibank(UINT8 data)
{
	okibank = data & 3;

	MSM6295SetBank(0, DrvSndROM + (okibank * 0x20000), 0x20000, 0x3ffff);
}

static UINT8 __fastcall drtomy_read_byte(UINT32 address)
{
	switch (address)
	{
		case 0x700001:
			return DrvDips[0]; 

		case 0x700003:
			return DrvDips[1]; 

		case 0x700005:
			return DrvInputs[0]; 

		case 0x700007:
			return DrvInputs[1];

		case 0x70000f:
			return MSM6295Read(0);
	}

	return 0;
}

static void __fastcall drtomy_write_byte(UINT32 address, UINT8 data)
{
	switch (address)
	{
		case 0x70000d:
			set_okibank(data);
		return;

		case 0x70000f:
			MSM6295Write(0, data);
		return;
	}
}

static void __fastcall drtomy_write_word(UINT32 address, UINT16 data)
{
	if ((address & 0xfff800) == 0x200000) {
		*((UINT16*)(BurnPalRAM + (address & 0x7fe))) = data;
		if (address < 0x200600) BurnPaletteWrite_xRRRRRGGGGGBBBBB(address & 0x7fe);
		return;
	}
}

static tilemap_callback( background )
{
	UINT16 *ram = (UINT16*)(DrvVidRAM + 0x1000);

	INT32 attr = BURN_ENDIAN_SWAP_INT16(ram[offs]);

	TILE_SET_INFO(0, attr, attr >> 12, 0);
}

static tilemap_callback( foreground )
{
	UINT16 *ram = (UINT16*)(DrvVidRAM + 0x0000);

	INT32 attr = BURN_ENDIAN_SWAP_INT16(ram[offs]);

	INT32 flags = ((attr & 0xfff) == 0) ? TILE_SKIP : 0;

	TILE_SET_INFO(1, attr, attr >> 12, flags);
}

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

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

	MSM6295Reset(0);

	set_okibank(0);

	HiscoreReset();

	return 0;
}

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

	Drv68KROM		= Next; Next += 0x040000;
	DrvGfxROM[0]	= Next; Next += 0x200000;
	DrvGfxROM[1]	= Next; Next += 0x100000;

	MSM6295ROM		= Next;
	DrvSndROM		= Next; Next += 0x080000;

	BurnPalette		= (UINT32*)Next; Next += 0x00300 * sizeof(UINT32);

	RamStart		= Next;

	DrvVidRAM		= Next; Next += 0x002000;
	BurnPalRAM		= Next; Next += 0x000800;
	DrvSprRAM		= Next; Next += 0x001000;
	Drv68KRAM		= Next; Next += 0x004000;

	RamEnd			= Next;

	MemEnd			= Next;

	return 0;
}

static INT32 DrvGfxDecode()
{
	static INT32 Planes[4] = { STEP4(0,0x200000) };
	static INT32 XOffs[16] = { STEP8(0,1), STEP8(128,1) };
	static INT32 YOffs[16] = { STEP16(0,8) };

	UINT8 *tmp = (UINT8*)BurnMalloc(0x100000);
	if (tmp == NULL) {
		return 1;
	}

	memcpy (tmp, DrvGfxROM[0], 0x100000);

	GfxDecode(0x8000, 4,  8,  8, Planes, XOffs, YOffs, 0x040, tmp, DrvGfxROM[0]);
	GfxDecode(0x1000, 4, 16, 16, Planes, XOffs, YOffs, 0x100, tmp, DrvGfxROM[1]);

	BurnFree (tmp);

	return 0;
}

static INT32 DrvInit()
{
	BurnAllocMemIndex();

	{
		INT32 k = 0;
		if (BurnLoadRom(Drv68KROM    + 0x000000, k++, 2)) return 1;
		if (BurnLoadRom(Drv68KROM    + 0x000001, k++, 2)) return 1;

		if (BurnLoadRom(DrvGfxROM[0] + 0x000000, k++, 1)) return 1;
		if (BurnLoadRom(DrvGfxROM[0] + 0x040000, k++, 1)) return 1;
		if (BurnLoadRom(DrvGfxROM[0] + 0x080000, k++, 1)) return 1;
		if (BurnLoadRom(DrvGfxROM[0] + 0x0c0000, k++, 1)) return 1;

		if (BurnLoadRom(DrvSndROM    + 0x000000, k++, 1)) return 1;

		DrvGfxDecode();
	}

	SekInit(0, 0x68000);
	SekOpen(0);
	SekMapMemory(Drv68KROM,		0x000000, 0x03ffff, MAP_ROM);
	SekMapMemory(DrvVidRAM,		0x100000, 0x101fff, MAP_RAM);
	SekMapMemory(BurnPalRAM,	0x200000, 0x1007ff, MAP_ROM);
	SekMapMemory(DrvSprRAM,		0x440000, 0x440fff, MAP_RAM);
	SekMapMemory(Drv68KRAM,		0xffc000, 0xffffff, MAP_RAM);
	SekSetWriteByteHandler(0, 	drtomy_write_byte);
	SekSetWriteWordHandler(0, 	drtomy_write_word);
	SekSetReadByteHandler(0, 	drtomy_read_byte);
	SekClose();

	MSM6295Init(0, 1625000 / 132, 0);
	MSM6295SetBank(0, DrvSndROM, 0, 0x3ffff);
	MSM6295SetRoute(0, 0.80, BURN_SND_ROUTE_BOTH);

	GenericTilesInit();
	GenericTilemapInit(0, TILEMAP_SCAN_ROWS, background_map_callback, 16, 16, 32, 32);
	GenericTilemapInit(1, TILEMAP_SCAN_ROWS, foreground_map_callback, 16, 16, 32, 32);
	GenericTilemapSetGfx(0, DrvGfxROM[1], 4, 16, 16, 0x100000, 0x000, 0xf);
	GenericTilemapSetGfx(1, DrvGfxROM[1], 4, 16, 16, 0x100000, 0x200, 0xf);
	GenericTilemapSetGfx(2, DrvGfxROM[0], 4,  8,  8, 0x200000, 0x100, 0xf);
	GenericTilemapSetOffsets(TMAP_GLOBAL, 0, -16);
	GenericTilemapSetTransparent(1, 0);

	DrvDoReset();

	return 0;
}

static INT32 DrvExit()
{
	SekExit();
	GenericTilesExit();
	MSM6295Exit(0);

	BurnFreeMemIndex();
	
	MSM6295ROM = NULL;

	return 0;
}

static void draw_sprites()
{
	UINT16 *spriteram = (UINT16*)DrvSprRAM;

	for (INT32 i = 3; i < 0x1000/2; i+=4)
	{
		INT32 sx    = BURN_ENDIAN_SWAP_INT16(spriteram[i+2]) & 0x01ff;
		INT32 sy    = (240 - (BURN_ENDIAN_SWAP_INT16(spriteram[i]) & 0x00ff)) & 0x00ff;
		INT32 code  = BURN_ENDIAN_SWAP_INT16(spriteram[i+3]);
		INT32 color = (BURN_ENDIAN_SWAP_INT16(spriteram[i+2]) & 0x1e00) >> 9;
		INT32 attr  = (BURN_ENDIAN_SWAP_INT16(spriteram[i]) & 0xfe00) >> 9;

		INT32 xflip = attr & 0x20;
		INT32 yflip = attr & 0x40;
		INT32 spr_size;

		if (attr & 0x04){
			spr_size = 1;
		} else {
			spr_size = 2;
			code &= (~3);
		}

		for (INT32 y = 0; y < spr_size; y++)
		{
			for (INT32 x = 0; x < spr_size; x++)
			{
				INT32 ex = xflip ? (spr_size-1-x) : x;
				INT32 ey = yflip ? (spr_size-1-y) : y;

				DrawGfxMaskTile(0, 2, code + (ex * 2) + ey, sx-0x09+x*8, sy+y*8 - 16, xflip, yflip, color, 0);
			}
		}
	}
}

static INT32 DrvDraw()
{
	if (DrvRecalc) {
		BurnPaletteUpdate_xRRRRRGGGGGBBBBB();
		DrvRecalc = 0;
	}

	GenericTilemapDraw(0, pTransDraw, 0);
	GenericTilemapDraw(1, pTransDraw, 0);

	draw_sprites();

	BurnTransferCopy(BurnPalette);

	return 0;
}

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

	{
		memset (DrvInputs, 0xff, sizeof(DrvInputs));

		for (INT32 i = 0; i < 8; i++) {
			DrvInputs[0] ^= (DrvJoy1[i] & 1) << i;
			DrvInputs[1] ^= (DrvJoy2[i] & 1) << i;
		}
	}

	SekOpen(0);
	SekRun(12000000 / 60);
	SekSetIRQLine(6, CPU_IRQSTATUS_AUTO);
	SekClose();

	if (pBurnSoundOut) {
		MSM6295Render(0, pBurnSoundOut, nBurnSoundLen);
	}

	if (pBurnDraw) {
		BurnDrvRedraw();
	}

	return 0;
}

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

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

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

	if (nAction & ACB_DRIVER_DATA)
	{
		SekScan(nAction);
		MSM6295Scan(nAction, pnMin);

		SCAN_VAR(okibank);
	}

	if (nAction & ACB_WRITE) {
		set_okibank(okibank);
	}

	return 0;
}


// Dr. Tomy

static struct BurnRomInfo drtomyRomDesc[] = {
	{ "15.u21", 0x20000, 0x0b8d763b, 1 | BRF_PRG | BRF_ESS }, // 0 68k code
	{ "16.u22", 0x20000, 0x206f4d65, 1 | BRF_PRG | BRF_ESS }, // 1

	{ "20.u80", 0x40000, 0x4d4d86ff, 2 | BRF_GRA },           // 2 Graphics
	{ "19.u81", 0x40000, 0x49ecbfe2, 2 | BRF_GRA },           // 3
	{ "18.u82", 0x40000, 0x8ee5c921, 2 | BRF_GRA },           // 4
	{ "17.u83", 0x40000, 0x42044b1c, 2 | BRF_GRA },           // 5

	{ "14.u23", 0x80000, 0x479614ec, 3 | BRF_SND },           // 6 Samples
};

STD_ROM_PICK(drtomy)
STD_ROM_FN(drtomy)

struct BurnDriver BurnDrvDrtomy = {
	"drtomy", NULL, NULL, NULL, "1993",
	"Dr. Tomy\0", NULL, "Playmark", "Miscellaneous",
	NULL, NULL, NULL, NULL,
	BDF_GAME_WORKING | BDF_HISCORE_SUPPORTED, 2, HARDWARE_MISC_POST90S, GBF_PUZZLE, 0,
	NULL, drtomyRomInfo, drtomyRomName, NULL, NULL, NULL, NULL, DrvInputInfo, DrvDIPInfo,
	DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x300,
	320, 240, 4, 3
};
