
#include "pgm.h"
#include "bitswap.h"

static UINT16 *sharedprotram; // shared by several simulations

//-------------------------------------------------------------------------------------------------------------------
// Proper emulation
//-------------------------------------------------------------------------------------------------------------------

//-----------------------------------
// kov2, kov2p, Martmast, ddp2, dw2001, dwpc

static UINT8 asic27a_to_arm = 0;
static UINT8 asic27a_to_68k = 0;

static inline void pgm_cpu_sync()
{
	INT32 nCycles = SekTotalCycles() - Arm7TotalCycles();

	if (nCycles > 0) {
		Arm7Run(nCycles);
	}
}

static void __fastcall asic27a_write_byte(UINT32 address, UINT8 data)
{
	if ((address & 0xfffffe) == 0xd10000) {	// ddp2
		pgm_cpu_sync();
		asic27a_to_arm = data;
		Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_ASSERT_LINE);
		return;
	}
}

static void __fastcall asic27a_write_word(UINT32 address, UINT16 data)
{
	if ((address & 0xfffffe) == 0xd10000) {
		pgm_cpu_sync();
		asic27a_to_arm = data & 0xff;
		Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_ASSERT_LINE);
		return;
	}
}

static UINT8 __fastcall asic27a_read_byte(UINT32 address)
{
	if ((address & 0xfffffc) == 0xd10000) {
		pgm_cpu_sync();
		return asic27a_to_68k;
	}

	return 0;
}

static UINT16 __fastcall asic27a_read_word(UINT32 address)
{
	if ((address & 0xfffffc) == 0xd10000) {
		pgm_cpu_sync();
		return asic27a_to_68k;
	}

	return 0;
}

static void asic27a_arm7_write_byte(UINT32 address, UINT8 data)
{
	switch (address)
	{
		case 0x38000000:
			asic27a_to_68k = data;
		return;
	}
}

static UINT8 asic27a_arm7_read_byte(UINT32 address)
{
	switch (address)
	{
		case 0x38000000:
			Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_CLEAR_LINE);
			return asic27a_to_arm;
	}

	return 0;
}

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

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMARMShareRAM;
		ba.nLen		= 0x0010000;
		ba.nAddress	= 0xd00000;
		ba.szName	= "ARM SHARE RAM";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM0;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 0";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM1;
		ba.nLen		= 0x0010000;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 1";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM2;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 2";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		Arm7Scan(nAction);

		SCAN_VAR(asic27a_to_arm);
		SCAN_VAR(asic27a_to_68k);
	}

 	return 0;
}

void install_protection_asic27a_martmast()
{
	nPGMArm7Type = 2;
	pPgmScanCallback = asic27aScan;

	SekOpen(0);

	SekMapMemory(PGMARMShareRAM,	0xd00000, 0xd0ffff, SM_RAM);

	SekMapHandler(4,		0xd10000, 0xd10003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, asic27a_read_word);
	SekSetReadByteHandler(4, asic27a_read_byte);
	SekSetWriteWordHandler(4, asic27a_write_word);
	SekSetWriteByteHandler(4, asic27a_write_byte);
	SekClose();

	Arm7Init(1);
	Arm7Open(0);
	Arm7MapMemory(PGMARMROM,	0x00000000, 0x00003fff, ARM7_ROM);
	Arm7MapMemory(PGMUSER0,		0x08000000, 0x08000000+(nPGMExternalARMLen-1), ARM7_ROM);
	Arm7MapMemory(PGMARMRAM0,	0x10000000, 0x100003ff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM1,	0x18000000, 0x1800ffff, ARM7_RAM);
	Arm7MapMemory(PGMARMShareRAM,	0x48000000, 0x4800ffff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM2,	0x50000000, 0x500003ff, ARM7_RAM);
	Arm7SetWriteByteHandler(asic27a_arm7_write_byte);
	Arm7SetReadByteHandler(asic27a_arm7_read_byte);
	Arm7Close();
}


//---------------------------------------------------------------
// kovsh / photoy2k / photoy2k2 (XingXing)

static UINT16 kovsh_highlatch_arm_w;
static UINT16 kovsh_lowlatch_arm_w;
static UINT16 kovsh_highlatch_68k_w;
static UINT16 kovsh_lowlatch_68k_w ;
static UINT32 kovsh_counter;

static void __fastcall kovsh_asic27a_write_word(UINT32 address, UINT16 data)
{
	switch (address)
	{
		case 0x500000:
		case 0x600000:
			kovsh_lowlatch_68k_w = data;
		return;

		case 0x500002:
		case 0x600002:
			kovsh_highlatch_68k_w = data;
		return;
	}
}

static UINT16 __fastcall kovsh_asic27a_read_word(UINT32 address)
{
	if ((address & 0xffffc0) == 0x4f0000) {
		return BURN_ENDIAN_SWAP_INT16(*((UINT16*)(PGMARMShareRAM + (address & 0x3e))));
	}

	switch (address)
	{
		case 0x500000:
		case 0x600000:
			pgm_cpu_sync();
			return kovsh_lowlatch_arm_w;

		case 0x500002:
		case 0x600002:
			pgm_cpu_sync();
			return kovsh_highlatch_arm_w;
	}

	return 0;
}

static void kovsh_asic27a_arm7_write_word(UINT32 address, UINT16 data)
{
	// written... but never read?
	if ((address & 0xffffff80) == 0x50800000) {
		*((UINT16*)(PGMARMShareRAM + ((address>>1) & 0x3e))) = BURN_ENDIAN_SWAP_INT16(data);
		return;
	}
}

static void kovsh_asic27a_arm7_write_long(UINT32 address, UINT32 data)
{
	switch (address)
	{
		case 0x40000000:
		{
			kovsh_highlatch_arm_w = data >> 16;
			kovsh_lowlatch_arm_w = data;

			kovsh_highlatch_68k_w = 0;
			kovsh_lowlatch_68k_w = 0;
		}
		return;
	}
}

static UINT32 kovsh_asic27a_arm7_read_long(UINT32 address)
{
	switch (address)
	{
		case 0x40000000:
			return (kovsh_highlatch_68k_w << 16) | (kovsh_lowlatch_68k_w);

		case 0x4000000c:
			return kovsh_counter++;
	}

	return 0;
}

static void reset_kovsh_asic27a()
{
	kovsh_highlatch_arm_w = 0;
	kovsh_lowlatch_arm_w = 0;
	kovsh_highlatch_68k_w = 0;
	kovsh_lowlatch_68k_w = 0;
	kovsh_counter = 1;
}

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

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMARMShareRAM;
		ba.nLen		= 0x0000040;
		ba.nAddress	= 0x400000;
		ba.szName	= "ARM SHARE RAM";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM0;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 0";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM2;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 1";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		Arm7Scan(nAction);

		SCAN_VAR(kovsh_highlatch_arm_w);
		SCAN_VAR(kovsh_lowlatch_arm_w);
		SCAN_VAR(kovsh_highlatch_68k_w);
		SCAN_VAR(kovsh_lowlatch_68k_w);
		SCAN_VAR(kovsh_counter);
	}

 	return 0;
}

void install_protection_asic27a_kovsh()
{
	nPGMArm7Type = 1;
	pPgmScanCallback = kovsh_asic27aScan;
	pPgmResetCallback = reset_kovsh_asic27a;

	SekOpen(0);
	SekMapMemory(PGMARMShareRAM,	0x4f0000, 0x4f003f, SM_RAM);

	SekMapHandler(4,		0x500000, 0x600005, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	kovsh_asic27a_read_word);
	SekSetWriteWordHandler(4, 	kovsh_asic27a_write_word);
	SekClose();

	Arm7Init(1);
	Arm7Open(0);
	Arm7MapMemory(PGMARMROM,	0x00000000, 0x00003fff, ARM7_ROM);
	Arm7MapMemory(PGMARMRAM0,	0x10000000, 0x100003ff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM2,	0x50000000, 0x500003ff, ARM7_RAM);
	Arm7SetWriteWordHandler(kovsh_asic27a_arm7_write_word);
	Arm7SetWriteLongHandler(kovsh_asic27a_arm7_write_long);
	Arm7SetReadLongHandler(kovsh_asic27a_arm7_read_long);
	Arm7Close();
}

//-------------------------------
// Kovshp hack

void __fastcall kovshp_asic27a_write_word(UINT32 address, UINT16 data)
{
	switch (address & 6)
	{
		case 0:
			kovsh_lowlatch_68k_w = data;
		return;

		case 2:
		{
			unsigned char asic_key = data >> 8;
			unsigned char asic_cmd = (data & 0xff) ^ asic_key;

			switch (asic_cmd) // Intercept commands and translate them to those used by kovsh
			{
				case 0x9a: asic_cmd = 0x99; break; // kovassga
				case 0xa6: asic_cmd = 0xa9; break; // kovassga
				case 0xaa: asic_cmd = 0x56; break; // kovassga
				case 0xf8: asic_cmd = 0xf3; break; // kovassga

		                case 0x38: asic_cmd = 0xad; break;
		                case 0x43: asic_cmd = 0xca; break;
		                case 0x56: asic_cmd = 0xac; break;
		                case 0x73: asic_cmd = 0x93; break;
		                case 0x84: asic_cmd = 0xb3; break;
		                case 0x87: asic_cmd = 0xb1; break;
		                case 0x89: asic_cmd = 0xb6; break;
		                case 0x93: asic_cmd = 0x73; break;
		                case 0xa5: asic_cmd = 0xa9; break;
		                case 0xac: asic_cmd = 0x56; break;
		                case 0xad: asic_cmd = 0x38; break;
		                case 0xb1: asic_cmd = 0x87; break;
		                case 0xb3: asic_cmd = 0x84; break;
		                case 0xb4: asic_cmd = 0x90; break;
		                case 0xb6: asic_cmd = 0x89; break;
		                case 0xc5: asic_cmd = 0x8c; break;
		                case 0xca: asic_cmd = 0x43; break;
		                case 0xcc: asic_cmd = 0xf0; break;
		                case 0xd0: asic_cmd = 0xe0; break;
		                case 0xe0: asic_cmd = 0xd0; break;
		                case 0xe7: asic_cmd = 0x70; break;
		                case 0xed: asic_cmd = 0xcb; break;
		                case 0xf0: asic_cmd = 0xcc; break;
		                case 0xf1: asic_cmd = 0xf5; break;
		                case 0xf2: asic_cmd = 0xf1; break;
		                case 0xf4: asic_cmd = 0xf2; break;
		                case 0xf5: asic_cmd = 0xf4; break;
		                case 0xfc: asic_cmd = 0xc0; break;
		                case 0xfe: asic_cmd = 0xc3; break;
			}

			kovsh_highlatch_68k_w = asic_cmd ^ (asic_key | (asic_key << 8));
		}
		return;
	}
}

void install_protection_asic27a_kovshp()
{
	nPGMArm7Type = 1;
	pPgmScanCallback = kovsh_asic27aScan;

	SekOpen(0);
	SekMapMemory(PGMARMShareRAM,	0x4f0000, 0x4f003f, SM_RAM);

	SekMapHandler(4,		0x500000, 0x600005, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	kovsh_asic27a_read_word);
	SekSetWriteWordHandler(4, 	kovshp_asic27a_write_word);
	SekClose();

	Arm7Init(1);
	Arm7Open(0);
	Arm7MapMemory(PGMARMROM,	0x00000000, 0x00003fff, ARM7_ROM);
	Arm7MapMemory(PGMARMRAM0,	0x10000000, 0x100003ff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM2,	0x50000000, 0x500003ff, ARM7_RAM);
	Arm7SetWriteWordHandler(kovsh_asic27a_arm7_write_word);
	Arm7SetWriteLongHandler(kovsh_asic27a_arm7_write_long);
	Arm7SetReadLongHandler(kovsh_asic27a_arm7_read_long);
	Arm7Close();
}

//-------------------------------------------------------------------------------------------
// svg / dmnfrnt / theglad / killbld / Happy6

static UINT8 svg_ram_sel = 0;
static UINT8 *svg_ram[2];

static void svg_set_ram_bank(INT32 data)
{
	svg_ram_sel = data & 1;
	Arm7MapMemory(svg_ram[svg_ram_sel],	0x38000000, 0x3801ffff, ARM7_RAM);
	SekMapMemory(svg_ram[svg_ram_sel^1],	0x500000, 0x51ffff, SM_FETCH);
}

static void __fastcall svg_write_byte(UINT32 address, UINT8 data)
{
	pgm_cpu_sync();

	if ((address & 0xffe0000) == 0x0500000) {
		svg_ram[svg_ram_sel^1][(address & 0x1ffff)^1] = data;
		return;
	}

	switch (address)
	{
		case 0x5c0000:
		case 0x5c0001:
			Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_HOLD_LINE);
		return;
	}
}

static void __fastcall svg_write_word(UINT32 address, UINT16 data)
{
	pgm_cpu_sync();

	if ((address & 0xffe0000) == 0x0500000) {
		*((UINT16*)(svg_ram[svg_ram_sel^1] + (address & 0x1fffe))) = BURN_ENDIAN_SWAP_INT16(data);
		
		return;
	}

	switch (address)
	{
		case 0x5c0000:
			Arm7SetIRQLine(ARM7_FIRQ_LINE, ARM7_HOLD_LINE);
		return;

		case 0x5c0300:
			asic27a_to_arm = data;
		return;
	}
}

static UINT8 __fastcall svg_read_byte(UINT32 address)
{
	if ((address & 0xffe0000) == 0x0500000) {
		pgm_cpu_sync();

		INT32 d = svg_ram[svg_ram_sel^1][(address & 0x1ffff)^1];
		return d;
	}

	switch (address)
	{
		case 0x5c0000:
		case 0x5c0001:
			return 0;
	}

	return 0;
}

static UINT16 __fastcall svg_read_word(UINT32 address)
{
	if ((address & 0xffe0000) == 0x0500000) {
		pgm_cpu_sync();

		return BURN_ENDIAN_SWAP_INT16(*((UINT16*)(svg_ram[svg_ram_sel^1] + (address & 0x1fffe))));
	}

	switch (address)
	{
		case 0x5c0000:
		case 0x5c0001:
			return 0;

		case 0x5c0300:
			pgm_cpu_sync();
			return asic27a_to_68k;
	}

	return 0;
}

static void svg_arm7_write_byte(UINT32 address, UINT8 data)
{
	switch (address)
	{
		case 0x40000018:
			svg_set_ram_bank(data);
		return;

		case 0x48000000:
			asic27a_to_68k = data;
		return;
	}
}

static void svg_arm7_write_word(UINT32 /*address*/, UINT16 /*data*/)
{

}

static void svg_arm7_write_long(UINT32 address, UINT32 data)
{
	switch (address)
	{
		case 0x40000018:
			svg_set_ram_bank(data);
		return;

		case 0x48000000:
			asic27a_to_68k = data;
		return;
	}
}

static UINT8 svg_arm7_read_byte(UINT32 address)
{
	switch (address)
	{
		case 0x48000000:
		case 0x48000001:
		case 0x48000002:
		case 0x48000003:
			return asic27a_to_arm;
	}

	return 0;
}

static UINT16 svg_arm7_read_word(UINT32 address)
{
	switch (address)
	{
		case 0x48000000:
		case 0x48000002:
			return asic27a_to_arm;
	}

	return 0;
}

static UINT32 svg_arm7_read_long(UINT32 address)
{
	switch (address)
	{
		case 0x48000000:
			return asic27a_to_arm;
	}

	return 0;
}

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

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMARMShareRAM;
		ba.nLen		= 0x0020000;
		ba.nAddress	= 0x400000;
		ba.szName	= "ARM SHARE RAM #0 (address 500000)";
		BurnAcb(&ba);

		ba.Data		= PGMARMShareRAM2;
		ba.nLen		= 0x0020000;
		ba.nAddress	= 0x500000;
		ba.szName	= "ARM SHARE RAM #1";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM0;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 0";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM1;
		ba.nLen		= 0x0040000;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 1";
		BurnAcb(&ba);

		ba.Data		= PGMARMRAM2;
		ba.nLen		= 0x0000400;
		ba.nAddress	= 0;
		ba.szName	= "ARM RAM 2";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		Arm7Scan(nAction);
		SCAN_VAR(asic27a_to_arm);
		SCAN_VAR(asic27a_to_68k);

		SCAN_VAR(svg_ram_sel);
		svg_set_ram_bank(svg_ram_sel);
	}

 	return 0;
}

void install_protection_asic27a_svg()
{
	nPGMArm7Type = 3;

	pPgmScanCallback = svg_asic27aScan;

	svg_ram_sel = 0;
	svg_ram[0] = PGMARMShareRAM;
	svg_ram[1] = PGMARMShareRAM2;

	SekOpen(0);
	SekMapHandler(5,		0x500000, 0x5fffff, SM_RAM);
	SekSetReadWordHandler(5, 	svg_read_word);
	SekSetReadByteHandler(5, 	svg_read_byte);
	SekSetWriteWordHandler(5, 	svg_write_word);
	SekSetWriteByteHandler(5, 	svg_write_byte);
	SekClose();

	Arm7Init(1);
	Arm7Open(0);
	Arm7MapMemory(PGMARMROM,	0x00000000, 0x00003fff, ARM7_ROM);
	Arm7MapMemory(PGMUSER0,		0x08000000, 0x08000000 | (nPGMExternalARMLen-1), ARM7_ROM);
	Arm7MapMemory(PGMARMRAM0,	0x10000000, 0x100003ff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM1,	0x18000000, 0x1803ffff, ARM7_RAM);
	Arm7MapMemory(svg_ram[1],	0x38000000, 0x3801ffff, ARM7_RAM);
	Arm7MapMemory(PGMARMRAM2,	0x50000000, 0x500003ff, ARM7_RAM);
	Arm7SetWriteByteHandler(svg_arm7_write_byte);
	Arm7SetWriteWordHandler(svg_arm7_write_word);
	Arm7SetWriteLongHandler(svg_arm7_write_long);
	Arm7SetReadByteHandler(svg_arm7_read_byte);
	Arm7SetReadWordHandler(svg_arm7_read_word);
	Arm7SetReadLongHandler(svg_arm7_read_long);
	Arm7Close();
}


//-------------------------------------------------------------------------------------------------------------------
// Simulations
//-------------------------------------------------------------------------------------------------------------------


//---------------------
// Oriental Legends

static UINT8 asic3_latch[3];
static UINT8 asic3_x;
static UINT8 asic3_y;
static UINT8 asic3_z;
static UINT8 asic3_h1;
static UINT8 asic3_h2;
static UINT8 asic3_reg;
static UINT16 asic3_hold;

static UINT32 bt(UINT32 v, INT32 bit)
{
	return (v & (1<<bit)) != 0;
}

static void asic3_compute_hold()
{
	// The mode is dependant on the region
	static INT32 modes[4] = { 1, 1, 3, 2 };
	INT32 mode = modes[PgmInput[7] & 3];

	switch(mode) {
	case 1:
		asic3_hold =
			(asic3_hold << 1)
			^0x2bad
			^bt(asic3_hold, 15)^bt(asic3_hold, 10)^bt(asic3_hold, 8)^bt(asic3_hold, 5)
			^bt(asic3_z, asic3_y)
			^(bt(asic3_x, 0) << 1)^(bt(asic3_x, 1) << 6)^(bt(asic3_x, 2) << 10)^(bt(asic3_x, 3) << 14);
		break;
	case 2:
		asic3_hold =
			(asic3_hold << 1)
			^0x2bad
			^bt(asic3_hold, 15)^bt(asic3_hold, 7)^bt(asic3_hold, 6)^bt(asic3_hold, 5)
			^bt(asic3_z, asic3_y)
			^(bt(asic3_x, 0) << 4)^(bt(asic3_x, 1) << 6)^(bt(asic3_x, 2) << 10)^(bt(asic3_x, 3) << 12);
		break;
	case 3:
		asic3_hold =
			(asic3_hold << 1)
			^0x2bad
			^bt(asic3_hold, 15)^bt(asic3_hold, 10)^bt(asic3_hold, 8)^bt(asic3_hold, 5)
			^bt(asic3_z, asic3_y)
			^(bt(asic3_x, 0) << 4)^(bt(asic3_x, 1) << 6)^(bt(asic3_x, 2) << 10)^(bt(asic3_x, 3) << 12);
		break;
	}
}

static UINT8 pgm_asic3_r()
{
	UINT8 res = 0;

	switch(asic3_reg)
	{
		case 0x00: res = (asic3_latch[0] & 0xf7) | ((PgmInput[7] << 3) & 0x08); break;
		case 0x01: res =  asic3_latch[1]; break;
		case 0x02: res = (asic3_latch[2] & 0x7f) | ((PgmInput[7] << 6) & 0x80); break;
		case 0x03:
			res = (bt(asic3_hold, 15) << 0)	| (bt(asic3_hold, 12) << 1) | (bt(asic3_hold, 13) << 2) | (bt(asic3_hold, 10) << 3) | 
				(bt(asic3_hold,  7) << 4) | (bt(asic3_hold,  9) << 5) | (bt(asic3_hold,  2) << 6) | (bt(asic3_hold,  5) << 7);
			break;
		case 0x20: res = 0x49; break;
		case 0x21: res = 0x47; break;
		case 0x22: res = 0x53; break;
		case 0x24: res = 0x41; break;
		case 0x25: res = 0x41; break;
		case 0x26: res = 0x7f; break;
		case 0x27: res = 0x41; break;
		case 0x28: res = 0x41; break;
		case 0x2a: res = 0x3e; break;
		case 0x2b: res = 0x41; break;
		case 0x2c: res = 0x49; break;
		case 0x2d: res = 0xf9; break;
		case 0x2e: res = 0x0a; break;
		case 0x30: res = 0x26; break;
		case 0x31: res = 0x49; break;
		case 0x32: res = 0x49; break;
		case 0x33: res = 0x49; break;
		case 0x34: res = 0x32; break;
	}

	return res;
}

static void pgm_asic3_w(UINT16 data)
{
	{
		if(asic3_reg < 3)
			asic3_latch[asic3_reg] = data << 1;
		else if(asic3_reg == 0xa0) {
			asic3_hold = 0;
		} else if(asic3_reg == 0x40) {
			asic3_h2 = asic3_h1;
			asic3_h1 = data;
		} else if(asic3_reg == 0x48) {
			asic3_x = 0;
			if(!(asic3_h2 & 0x0a)) asic3_x |= 8;
			if(!(asic3_h2 & 0x90)) asic3_x |= 4;
			if(!(asic3_h1 & 0x06)) asic3_x |= 2;
			if(!(asic3_h1 & 0x90)) asic3_x |= 1;
		} else if(asic3_reg >= 0x80 && asic3_reg <= 0x87) {
			asic3_y = asic3_reg & 7;
			asic3_z = data;
			asic3_compute_hold();
		}
	}
}

static void pgm_asic3_reg_w(UINT16 data)
{
	asic3_reg = data & 0xff;
}

static void __fastcall asic3_write_word(UINT32 address, UINT16 data)
{
	if (address == 0xc04000) {
		pgm_asic3_reg_w(data);
		return;
	}

	if (address == 0xc0400e) {
		pgm_asic3_w(data);
		return;
	}
}

static UINT16 __fastcall asic3_read_word(UINT32 address)
{
	if (address == 0xc0400e) {
		return pgm_asic3_r();
	}

	return 0;
}

static void reset_asic3()
{
	memset (asic3_latch, 0, 3 * sizeof(UINT8));

	asic3_hold = 0;
	asic3_reg = 0;
	asic3_x = 0;
	asic3_y = 0;
	asic3_z = 0;
	asic3_h1 = 0;
	asic3_h2 = 0;
}

static INT32 asic3Scan(INT32 nAction, INT32 *)
{
	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(asic3_reg);
		SCAN_VAR(asic3_latch[0]);
		SCAN_VAR(asic3_latch[1]);
		SCAN_VAR(asic3_latch[2]);
		SCAN_VAR(asic3_x);
		SCAN_VAR(asic3_y);
		SCAN_VAR(asic3_z);
		SCAN_VAR(asic3_h1);
		SCAN_VAR(asic3_h2);
		SCAN_VAR(asic3_hold);
	}

	return 0;
}

void install_protection_asic3_orlegend()
{
	pPgmScanCallback = asic3Scan;
	pPgmResetCallback = reset_asic3;

	SekOpen(0);
	SekMapHandler(4,	0xc04000, 0xc0400f, SM_READ | SM_WRITE);
		
	SekSetReadWordHandler(4, asic3_read_word);
	SekSetWriteWordHandler(4, asic3_write_word);
	SekClose();
}


//--------------------
// killblade

static const UINT8 (*m_kb_source_data)[0xec];
static INT32 m_kb_source_data_offset;
static UINT32 m_kb_game_id;
static UINT16 m_kb_prot_hold;
static UINT16 m_kb_prot_hilo;
static UINT16 m_kb_prot_hilo_select;
static int    m_kb_cmd;
static int    m_kb_reg;
static int    m_kb_ptr;
static UINT8  m_kb_swap;
static UINT32 m_kb_regs[0x10];

// these were all xored by a table at $178B2A
static const UINT8 killbld_source_data[0x0c][0xec] =  // offsets to these tables stored at $155ed0
{
	{ // region 16, $178772
		0x5e, 0x09, 0xb3, 0x39, 0x60, 0x71, 0x71, 0x53, 0x11, 0xe5, 0x26, 0x34, 0x4c, 0x8c, 0x90, 0xee,
		0xed, 0xb5, 0x05, 0x95, 0x9e, 0x6b, 0xdd, 0x87, 0x0e, 0x7b, 0xed, 0x33, 0xaf, 0xc2, 0x62, 0x98,
		0xec, 0xc8, 0x2c, 0x2b, 0x57, 0x3d, 0x00, 0xbd, 0x12, 0xac, 0xba, 0x64, 0x81, 0x99, 0x16, 0x29,
		0xb4, 0x63, 0xa8, 0xd9, 0xc9, 0x5f, 0xfe, 0x21, 0xbb, 0xbf, 0x9b, 0xd1, 0x7b, 0x93, 0xc4, 0x82,
		0xef, 0x2b, 0xe8, 0xa6, 0xdc, 0x68, 0x3a, 0xd9, 0xc9, 0x23, 0xc7, 0x7b, 0x98, 0x5b, 0xe1, 0xc7,
		0xa3, 0xd4, 0x51, 0x0a, 0x86, 0x30, 0x20, 0x51, 0x6e, 0x04, 0x1c, 0xd4, 0xfb, 0xf5, 0x22, 0x8f,
		0x16, 0x6f, 0xb9, 0x59, 0x30, 0xcf, 0xab, 0x32, 0x1d, 0x6c, 0x84, 0xab, 0x23, 0x90, 0x94, 0xb1,
		0xe7, 0x4b, 0x6d, 0xc1, 0x84, 0xba, 0x32, 0x68, 0xa3, 0xf2, 0x47, 0x28, 0xe5, 0xcb, 0xbb, 0x47,
		0x14, 0x2c, 0xad, 0x4d, 0xa1, 0xd7, 0x18, 0x53, 0xf7, 0x6f, 0x05, 0x81, 0x8f, 0xbb, 0x29, 0xdc,
		0xbd, 0x17, 0x61, 0x92, 0x9b, 0x1d, 0x4e, 0x7a, 0x83, 0x14, 0x9f, 0x7b, 0x7a, 0x6a, 0xe1, 0x27,
		0x62, 0x52, 0x7e, 0x82, 0x45, 0xda, 0xed, 0xf1, 0x0a, 0x3b, 0x6c, 0x02, 0x5b, 0x6e, 0x45, 0x4e,
		0xf2, 0x65, 0x87, 0x1d, 0x80, 0xed, 0x6a, 0xc3, 0x77, 0xcb, 0xe8, 0x8d, 0x5a, 0xb8, 0xda, 0x89,
		0x88, 0x4b, 0x27, 0xd5, 0x57, 0x29, 0x91, 0x86, 0x12, 0xbb, 0xd3, 0x8c, 0xc7, 0x49, 0x84, 0x9c,
		0x96, 0x59, 0x30, 0x93, 0x92, 0xeb, 0x59, 0x2b, 0x93, 0x5b, 0x5f, 0xf9, 0x67, 0xac, 0x97, 0x8c,
		0x04, 0xda, 0x1b, 0x65, 0xd7, 0xef, 0x44, 0xca, 0xc4, 0x87, 0x18, 0x2b
	},
	{ // region 17, $178a36
		0xd7, 0x49, 0xb3, 0x39, 0x60, 0x71, 0x70, 0x53, 0x11, 0x00, 0x27, 0xb2, 0x61, 0xd3, 0x8c, 0x8b,
		0xb2, 0xde, 0x6a, 0x78, 0x40, 0x5d, 0x4d, 0x88, 0xeb, 0x81, 0xd0, 0x2a, 0xbf, 0x8c, 0x22, 0x0d,
		0x89, 0x83, 0xc8, 0xef, 0x0d, 0x7a, 0xf6, 0xf0, 0x1d, 0x49, 0xa2, 0xd3, 0x1e, 0xef, 0x1c, 0xa2,
		0xce, 0x00, 0x5e, 0xa8, 0x7f, 0x4c, 0x41, 0x27, 0xa8, 0x6b, 0x92, 0x0a, 0xb8, 0x03, 0x2f, 0x7e,
		0xaf, 0x4a, 0xd0, 0x5c, 0xce, 0xeb, 0x0e, 0x8a, 0x4d, 0x0b, 0x73, 0xb3, 0xf3, 0x0c, 0x83, 0xaa,
		0xe5, 0xe4, 0x84, 0x06, 0xd7, 0xcc, 0xcb, 0x52, 0x8d, 0xbe, 0xa4, 0xdf, 0xd9, 0xab, 0x50, 0x59,
		0x53, 0x61, 0xa1, 0xc8, 0x6d, 0xbc, 0xde, 0xab, 0xaa, 0x5e, 0xc6, 0xf7, 0x83, 0xdc, 0x40, 0xcb,
		0x1b, 0xdd, 0x28, 0x3b, 0xee, 0xb1, 0x1f, 0x37, 0xdb, 0xe9, 0xbb, 0x74, 0x4b, 0xc2, 0x8a, 0xe8,
		0xec, 0x6e, 0x0e, 0x35, 0xe3, 0x2e, 0xbe, 0xef, 0xfd, 0x07, 0xbf, 0x8c, 0xfe, 0xf3, 0x5c, 0xbf,
		0x87, 0xe5, 0xbc, 0xcf, 0x60, 0xdc, 0x18, 0xf8, 0xfc, 0x51, 0x50, 0x86, 0xc6, 0x48, 0x3d, 0xb9,
		0x1d, 0x26, 0xf7, 0x7e, 0x87, 0x90, 0x12, 0xe8, 0x06, 0x0a, 0x45, 0xe9, 0xd9, 0xd8, 0x41, 0x68,
		0x21, 0x52, 0x92, 0x0f, 0xd6, 0xda, 0xa2, 0x97, 0xeb, 0x68, 0xd0, 0xb1, 0x15, 0x19, 0x8b, 0xd0,
		0x48, 0x1a, 0xeb, 0x90, 0x3f, 0x2a, 0x33, 0x1e, 0x5e, 0x30, 0x66, 0x01, 0x64, 0xef, 0x99, 0x52,
		0xba, 0x23, 0xbd, 0x53, 0xc0, 0x60, 0x87, 0x09, 0xcb, 0x4d, 0xd3, 0x87, 0x0e, 0x3a, 0x5c, 0x8d,
		0xc8, 0xb8, 0xb7, 0x34, 0x01, 0xeb, 0x72, 0x0d, 0xb1, 0x1f, 0x0f, 0xea
	},
	{ // region 18, $17dac4
		0x6a, 0x13, 0xb3, 0x09, 0x60, 0x79, 0x61, 0x53, 0x11, 0x33, 0x41, 0x31, 0x76, 0x34, 0x88, 0x0f,
		0x77, 0x08, 0xb6, 0x74, 0xc8, 0x36, 0xbc, 0x70, 0xe2, 0x87, 0x9a, 0x21, 0xe8, 0x56, 0xe1, 0x9a,
		0x26, 0x57, 0x7e, 0x9b, 0xdb, 0xb7, 0xd4, 0x3d, 0x0f, 0xfe, 0x8a, 0x2a, 0xba, 0x2d, 0x22, 0x03,
		0xcf, 0x9c, 0xfa, 0x77, 0x35, 0x39, 0x6a, 0x14, 0xae, 0x30, 0x89, 0x42, 0xdc, 0x59, 0xb2, 0x93,
		0x6f, 0x82, 0xd1, 0x12, 0xd9, 0x88, 0xfa, 0x3b, 0xb7, 0x0c, 0x1f, 0x05, 0x68, 0xa3, 0x0c, 0xa6,
		0x0f, 0xf4, 0x9e, 0x1b, 0x29, 0x82, 0x77, 0x3a, 0xac, 0x92, 0x2d, 0x04, 0xd0, 0x61, 0x65, 0x0a,
		0x77, 0x6c, 0x89, 0x38, 0xaa, 0xa9, 0xf8, 0x0c, 0x1f, 0x37, 0x09, 0x2b, 0xca, 0x29, 0x05, 0xe5,
		0x4e, 0x57, 0xfb, 0xcd, 0x40, 0xa8, 0x0c, 0x06, 0x2d, 0xe0, 0x30, 0xd9, 0x97, 0xb9, 0x59, 0x8a,
		0xde, 0xc9, 0x87, 0x1d, 0x3f, 0x84, 0x4c, 0x73, 0x04, 0x85, 0x61, 0xb0, 0x6e, 0x2c, 0x8f, 0xa2,
		0x6a, 0xcd, 0x31, 0xf3, 0x25, 0x83, 0xe1, 0x5e, 0x5d, 0xa7, 0xe7, 0xaa, 0x13, 0x26, 0xb1, 0x33,
		0xf0, 0x13, 0x58, 0x7a, 0xb0, 0x46, 0x1d, 0xdf, 0x02, 0xbf, 0x1e, 0xd1, 0x71, 0x43, 0x56, 0x82,
		0x4f, 0x58, 0x9d, 0x01, 0x2d, 0xc7, 0xda, 0x6b, 0x47, 0x05, 0xd1, 0xd5, 0xe8, 0x92, 0x3c, 0x18,
		0x21, 0xcf, 0xc9, 0x32, 0x0e, 0x12, 0xed, 0xb5, 0xaa, 0xa4, 0x12, 0x75, 0x01, 0x7d, 0xc7, 0x21,
		0xde, 0xec, 0x32, 0x13, 0xee, 0xd4, 0x9c, 0xe6, 0x04, 0x3f, 0x48, 0xfb, 0xb4, 0xc7, 0x21, 0x8e,
		0x8d, 0x7d, 0x54, 0x03, 0x11, 0xe7, 0xb9, 0x4f, 0x85, 0xb6, 0x1f, 0xaa
	},
	{ // region 19, $178eee
		0xe3, 0x53, 0xb3, 0x09, 0x60, 0x79, 0x60, 0x53, 0x11, 0x66, 0x5b, 0xc8, 0x8b, 0x94, 0x84, 0xab,
		0x3c, 0x18, 0x03, 0x57, 0x6a, 0x0f, 0x45, 0x58, 0xc0, 0x74, 0x64, 0x18, 0xf8, 0x39, 0xa1, 0x0f,
		0xc2, 0x2b, 0x1b, 0x60, 0xaa, 0x0e, 0xb2, 0x89, 0x01, 0x9b, 0x72, 0x80, 0x57, 0x83, 0x28, 0x63,
		0xe9, 0x39, 0x97, 0x46, 0xea, 0x3f, 0x93, 0x01, 0x9b, 0xf4, 0x80, 0x93, 0x01, 0xaf, 0x1d, 0x8f,
		0x16, 0xa1, 0xb9, 0xc7, 0xe4, 0x0c, 0xe7, 0xd2, 0x3b, 0xf3, 0xca, 0x3d, 0xc3, 0x54, 0xad, 0x89,
		0x51, 0x1e, 0xd1, 0x17, 0x7a, 0x1f, 0x23, 0x22, 0xcb, 0x4d, 0xce, 0x0f, 0xae, 0x30, 0x93, 0xd3,
		0x9b, 0x77, 0x71, 0xa7, 0xe7, 0x96, 0x2c, 0x85, 0xac, 0x29, 0x4b, 0x5e, 0x2b, 0x75, 0xb0, 0x00,
		0x81, 0xe9, 0xb6, 0x47, 0xaa, 0x9f, 0xdf, 0xd4, 0x7e, 0xd7, 0xa4, 0x3f, 0xe3, 0xb0, 0x41, 0x2c,
		0xb7, 0x0c, 0xe7, 0xeb, 0x9a, 0xda, 0xd9, 0x10, 0x23, 0x1d, 0x1c, 0xd4, 0xdd, 0x7d, 0xc2, 0x6c,
		0x4d, 0x9c, 0xa5, 0x18, 0xd0, 0x43, 0xab, 0xdc, 0xbd, 0xe4, 0x7f, 0xb5, 0x5f, 0x04, 0x0d, 0xac,
		0xab, 0xe6, 0xb8, 0x76, 0xf2, 0x15, 0x41, 0xef, 0x17, 0x8e, 0xf6, 0xb9, 0xef, 0x94, 0x52, 0x83,
		0x96, 0x45, 0x8f, 0xf2, 0x9c, 0xb4, 0x13, 0x3f, 0xbb, 0xa1, 0xd2, 0xf9, 0xa3, 0xf2, 0x06, 0x78,
		0xe0, 0x9e, 0xa7, 0xd3, 0xdc, 0x13, 0x8f, 0x4d, 0xf6, 0x19, 0xbd, 0x03, 0x9d, 0x24, 0xdc, 0xd6,
		0xe9, 0xcf, 0xa6, 0xd2, 0x1d, 0x49, 0xca, 0xc4, 0x55, 0x18, 0xbc, 0x70, 0x5b, 0x55, 0xfe, 0x8f,
		0x6b, 0x42, 0xf0, 0xd1, 0x21, 0xe3, 0xe7, 0x91, 0x59, 0x4e, 0x16, 0x83
	},
	{ 0, }, // unused region 1a
	{ 0, }, // unused region 1b
	{ 0, }, // unused region 1c
	{ 0, }, // unused region 1d
	{ 0, }, // unused region 1e
	{ 0, }, // unused region 1f
	{ // region 20, $17a322
		0xb3, 0x10, 0xf3, 0x0b, 0xe0, 0x71, 0x60, 0x53, 0x11, 0x9a, 0x12, 0x70, 0x1f, 0x1e, 0x81, 0xda,
		0x9d, 0x1f, 0x4b, 0xd6, 0x71, 0x48, 0x83, 0xe1, 0x04, 0x6c, 0x1b, 0xf1, 0xcd, 0x09, 0xdf, 0x3e,
		0x0b, 0xaa, 0x95, 0xc1, 0x07, 0xec, 0x0f, 0x54, 0xd0, 0x16, 0xb0, 0xdc, 0x86, 0x7b, 0x52, 0x38,
		0x3c, 0x68, 0x2b, 0xed, 0xe2, 0xeb, 0xb3, 0xc6, 0x48, 0x24, 0x41, 0x36, 0x17, 0x25, 0x1f, 0xa5,
		0x22, 0xc6, 0x5c, 0xa6, 0x19, 0xef, 0x17, 0x5c, 0x56, 0x4b, 0x4a, 0x2b, 0x75, 0xab, 0xe6, 0x22,
		0xd5, 0xc0, 0xd3, 0x46, 0xcc, 0xe4, 0xd4, 0xc4, 0x8c, 0x9a, 0x8a, 0x75, 0x24, 0x73, 0xa4, 0x26,
		0xca, 0x79, 0xaf, 0xb3, 0x94, 0x2a, 0x15, 0xbe, 0x40, 0x7b, 0x4d, 0xf6, 0xb4, 0xa4, 0x7b, 0xcf,
		0xce, 0xa0, 0x1d, 0xcb, 0x2f, 0x60, 0x28, 0x63, 0x85, 0x98, 0xd3, 0xd2, 0x45, 0x3f, 0x02, 0x65,
		0xd7, 0xf4, 0xbc, 0x2a, 0xe7, 0x50, 0xd1, 0x3f, 0x7f, 0xf6, 0x05, 0xb8, 0xe9, 0x39, 0x10, 0x6e,
		0x68, 0xa8, 0x89, 0x60, 0x00, 0x68, 0xfd, 0x20, 0xc4, 0xdc, 0xef, 0x67, 0x75, 0xfb, 0xbe, 0xfe,
		0x2b, 0x16, 0xa6, 0x5a, 0x77, 0x0d, 0x0c, 0xe2, 0x2d, 0xd1, 0xe4, 0x11, 0xc9, 0x4b, 0x81, 0x3a,
		0x0c, 0x24, 0xaa, 0x77, 0x2b, 0x2f, 0x83, 0x23, 0xd1, 0xe9, 0xa7, 0x29, 0x0a, 0xf9, 0x26, 0x9d,
		0x51, 0xc8, 0x6d, 0x71, 0x9d, 0xce, 0x46, 0x72, 0x26, 0x48, 0x3d, 0x64, 0xe5, 0x67, 0xbb, 0x1a,
		0xb4, 0x6d, 0x21, 0x11, 0x79, 0x78, 0xc2, 0xd5, 0x11, 0x6a, 0xd2, 0xea, 0x03, 0x4d, 0x92, 0xaf,
		0x18, 0xd5, 0x07, 0x79, 0xaa, 0xf9, 0x44, 0x93, 0x6f, 0x41, 0x22, 0x0d
	},
	{ // region 21, $17b3b4
		0x2d, 0x50, 0xf3, 0x0b, 0xe0, 0x71, 0x61, 0x53, 0x11, 0xb4, 0x2c, 0xee, 0x34, 0x7e, 0x7d, 0x5e,
		0x62, 0x48, 0x97, 0xd2, 0xf9, 0x3a, 0xf2, 0xc9, 0xfa, 0x59, 0xe4, 0xe8, 0xf6, 0xd2, 0x9f, 0xb2,
		0xa7, 0x7e, 0x32, 0x86, 0xbc, 0x43, 0xec, 0xa0, 0xc2, 0xcb, 0x98, 0x33, 0x23, 0xd1, 0x58, 0x98,
		0x56, 0x05, 0xc7, 0xbc, 0x98, 0xd8, 0xdc, 0xb3, 0x35, 0xe8, 0x51, 0x6e, 0x3b, 0x7b, 0x89, 0xba,
		0xe1, 0xe5, 0x44, 0x5c, 0x24, 0x73, 0x04, 0x0d, 0xd9, 0x33, 0xf5, 0x63, 0xe9, 0x5c, 0x88, 0x05,
		0x18, 0xd0, 0x07, 0x5b, 0x1e, 0x81, 0x80, 0xac, 0x92, 0x6e, 0x13, 0x80, 0x1b, 0x29, 0xd2, 0xef,
		0x08, 0x84, 0x97, 0x23, 0xd1, 0x17, 0x2f, 0x38, 0xb4, 0x6d, 0x8f, 0x2a, 0x15, 0xf0, 0x40, 0xe9,
		0x02, 0x33, 0xd7, 0x5e, 0x99, 0x57, 0x15, 0x32, 0xbd, 0x8f, 0x48, 0x38, 0x91, 0x36, 0xe9, 0x07,
		0xc9, 0x37, 0x1d, 0x12, 0x2a, 0xbf, 0x5f, 0xdb, 0x85, 0x75, 0xbf, 0xdc, 0x59, 0x8a, 0x43, 0x51,
		0x4b, 0x77, 0xfd, 0x84, 0xc4, 0x28, 0xc7, 0x85, 0x25, 0x1a, 0x87, 0x8b, 0xc1, 0xd9, 0x1a, 0x78,
		0xe5, 0x03, 0x20, 0x56, 0xa0, 0xc2, 0x17, 0xf2, 0x29, 0xa0, 0xbd, 0xf8, 0x61, 0x9c, 0x7d, 0x54,
		0x3a, 0x11, 0xb5, 0x69, 0x9a, 0x1c, 0xbb, 0xf6, 0x2d, 0x86, 0xa8, 0x4d, 0xdd, 0x5a, 0xd6, 0xe4,
		0x11, 0x7e, 0x4b, 0x13, 0x6c, 0xb6, 0x01, 0x0a, 0x72, 0xbc, 0xe8, 0xf1, 0x82, 0x0e, 0xd0, 0xcf,
		0xbf, 0x50, 0x95, 0xb7, 0xa7, 0xec, 0xd7, 0xb3, 0x49, 0x5c, 0x47, 0x5f, 0xa9, 0xda, 0x70, 0xb0,
		0xdc, 0x9a, 0xa3, 0x48, 0xd3, 0xf5, 0x72, 0xd5, 0x43, 0xd8, 0x19, 0xcc
	}
};

// all tables xored with data from $149c4c
static const UINT8 dw3_source_data[0x07][0xec] =
{
	{ // region 1, $14c21a
		0x67, 0x51, 0xf3, 0x19, 0xa0, 0x09, 0xb1, 0x21, 0xb0, 0xee, 0xe3, 0xf6, 0xbe, 0x81, 0x35, 0xe3,
		0xfb, 0xe6, 0xef, 0xdf, 0x61, 0x01, 0xfa, 0x22, 0x5d, 0x43, 0x01, 0xa5, 0x3b, 0x17, 0xd4, 0x74,
		0xf0, 0xf4, 0xf3, 0x43, 0xb5, 0x19, 0x04, 0xd5, 0x84, 0xce, 0x87, 0xfe, 0x35, 0x3e, 0xc4, 0x3c,
		0xc7, 0x85, 0x2a, 0x33, 0x00, 0x86, 0xd0, 0x4d, 0x65, 0x4b, 0xf9, 0xe9, 0xc0, 0xba, 0xaa, 0x77,
		0x9e, 0x66, 0xf6, 0x0f, 0x4f, 0x3a, 0xb6, 0xf1, 0x64, 0x9a, 0xe9, 0x25, 0x1a, 0x5f, 0x22, 0xa3,
		0xa2, 0xbf, 0x4b, 0x77, 0x3f, 0x34, 0xc9, 0x6e, 0xdb, 0x12, 0x5c, 0x33, 0xa5, 0x8b, 0x6c, 0xb1,
		0x74, 0xc8, 0x40, 0x4e, 0x2f, 0xe7, 0x46, 0xae, 0x99, 0xfc, 0xb0, 0x55, 0x54, 0xdf, 0xa7, 0xa1,
		0x0f, 0x5e, 0x49, 0xcf, 0x56, 0x3c, 0x90, 0x2b, 0xac, 0x65, 0x6e, 0xdb, 0x58, 0x3e, 0xc9, 0x00,
		0xae, 0x53, 0x4d, 0x92, 0xfa, 0x40, 0xb2, 0x6b, 0x65, 0x4b, 0x90, 0x8a, 0x0c, 0xe2, 0xa5, 0x9a,
		0xd0, 0x20, 0x29, 0x55, 0xa4, 0x44, 0xac, 0x51, 0x87, 0x54, 0x53, 0x34, 0x24, 0x4b, 0x81, 0x67,
		0x34, 0x4c, 0x5f, 0x31, 0x4e, 0xf2, 0xf1, 0x19, 0x18, 0x1c, 0x34, 0x38, 0xe1, 0x81, 0x17, 0xcf,
		0x24, 0xb9, 0x9a, 0xcb, 0x34, 0x51, 0x50, 0x59, 0x44, 0xb1, 0x0b, 0x50, 0x95, 0x6c, 0x48, 0x7e,
		0x14, 0xa4, 0xc6, 0xd9, 0xd3, 0xa5, 0xd6, 0xd0, 0xc5, 0x97, 0xf0, 0x45, 0xd0, 0x98, 0x51, 0x91,
		0x9f, 0xa3, 0x43, 0x51, 0x05, 0x90, 0xee, 0xca, 0x7e, 0x5f, 0x72, 0x53, 0xb1, 0xd3, 0xaf, 0x36,
		0x08, 0x75, 0xb0, 0x9b, 0xe0, 0x0d, 0x43, 0x88, 0xaa, 0x27, 0x44, 0x11
	},
	{ // region 2, $14c126
		0xf9, 0x19, 0xf3, 0x09, 0xa0, 0x09, 0xb0, 0x21, 0xb0, 0x22, 0xfd, 0x8e, 0xd3, 0xc8, 0x31, 0x67,
		0xc0, 0x10, 0x3c, 0xc2, 0x03, 0xf2, 0x6a, 0x0a, 0x54, 0x49, 0xca, 0xb5, 0x4b, 0xe0, 0x94, 0xe8,
		0x8d, 0xc8, 0x90, 0xee, 0x6b, 0x6f, 0xfa, 0x09, 0x76, 0x84, 0x6f, 0x55, 0xd1, 0x94, 0xca, 0x9c,
		0xe1, 0x22, 0xc6, 0x02, 0xb5, 0x8c, 0xf9, 0x3a, 0x52, 0x10, 0xf0, 0x22, 0xe4, 0x11, 0x15, 0x73,
		0x5e, 0x9e, 0xde, 0xc4, 0x5a, 0xbd, 0xa3, 0x89, 0xe7, 0x9b, 0x95, 0x5d, 0x75, 0xf6, 0xc3, 0x9f,
		0xe4, 0xcf, 0x65, 0x73, 0x90, 0xd0, 0x75, 0x56, 0xfa, 0xcc, 0xe4, 0x3e, 0x9c, 0x41, 0x81, 0x62,
		0xb1, 0xd3, 0x28, 0xbd, 0x6c, 0xed, 0x60, 0x28, 0x27, 0xee, 0xf2, 0xa1, 0xb4, 0x2c, 0x6c, 0xbb,
		0x42, 0xd7, 0x1d, 0x62, 0xc0, 0x33, 0x7d, 0xf9, 0xe4, 0x5c, 0xe2, 0x41, 0xa4, 0x1c, 0x98, 0xa1,
		0x87, 0x95, 0xad, 0x61, 0x56, 0x96, 0x40, 0x08, 0x6b, 0xe2, 0x4b, 0x95, 0x7b, 0x1b, 0xd8, 0x64,
		0xb3, 0xee, 0x9d, 0x79, 0x69, 0xea, 0x5d, 0xcf, 0x01, 0x91, 0xea, 0x3f, 0x70, 0x29, 0xdc, 0xe0,
		0x08, 0x20, 0xbf, 0x46, 0x90, 0xa8, 0xfc, 0x29, 0x14, 0xd1, 0x0d, 0x20, 0x79, 0xd2, 0x2c, 0xe9,
		0x52, 0xa6, 0x8c, 0xbd, 0xa3, 0x3e, 0x88, 0x2d, 0xb8, 0x4e, 0xf2, 0x74, 0x50, 0xcc, 0x12, 0xde,
		0xd3, 0x5a, 0xa4, 0x7b, 0xa2, 0x8d, 0x91, 0x68, 0x12, 0x0c, 0x9c, 0xb9, 0x6d, 0x26, 0x66, 0x60,
		0xc3, 0x6d, 0xd0, 0x11, 0x33, 0x05, 0x1d, 0xa8, 0xb6, 0x51, 0xe6, 0xe0, 0x58, 0x61, 0x74, 0x37,
		0xcc, 0x3a, 0x4d, 0x6a, 0x0a, 0x09, 0x71, 0xe3, 0x7e, 0xa5, 0x3b, 0xe9
	},
	{ // region 3, $14E5BE
		0x73, 0x59, 0xf3, 0x09, 0xa0, 0x09, 0xb1, 0x21, 0xb0, 0x55, 0x18, 0x0d, 0xe8, 0x29, 0x2d, 0x04,
		0x85, 0x39, 0x88, 0xbe, 0x8b, 0xcb, 0xd9, 0x0b, 0x32, 0x36, 0x94, 0xac, 0x74, 0xc3, 0x3b, 0x5d,
		0x2a, 0x83, 0x46, 0xb3, 0x3a, 0xac, 0xd8, 0x55, 0x68, 0x21, 0x57, 0xab, 0x6e, 0xd1, 0xd0, 0xfc,
		0xe2, 0xbe, 0x63, 0xd0, 0x6b, 0x79, 0x23, 0x40, 0x58, 0xd4, 0xe7, 0x73, 0x22, 0x67, 0x7f, 0x88,
		0x05, 0xbd, 0xdf, 0x7a, 0x65, 0x41, 0x90, 0x3a, 0x52, 0x83, 0x28, 0xae, 0xe9, 0x8e, 0x65, 0x82,
		0x0e, 0xdf, 0x98, 0x88, 0xe1, 0x86, 0x21, 0x3e, 0x1a, 0x87, 0x6d, 0x62, 0x7a, 0xf6, 0xaf, 0x2c,
		0xd5, 0xc5, 0x10, 0x2d, 0xa9, 0xda, 0x93, 0xa1, 0x9b, 0xc7, 0x35, 0xd4, 0x15, 0x78, 0x18, 0xd5,
		0x75, 0x6a, 0xd7, 0xdb, 0x12, 0x2a, 0x6a, 0xc8, 0x36, 0x53, 0x57, 0xa6, 0xf0, 0x13, 0x67, 0x43,
		0x79, 0xf0, 0x0e, 0x49, 0xb1, 0xec, 0xcd, 0xa4, 0x8a, 0x61, 0x06, 0xb9, 0xea, 0x53, 0xf2, 0x47,
		0x7d, 0xd6, 0xf8, 0x9d, 0x2e, 0xaa, 0x27, 0x35, 0x61, 0xce, 0x9b, 0x63, 0xbc, 0x07, 0x51, 0x5a,
		0xc2, 0x0d, 0x39, 0x42, 0xd2, 0x5e, 0x21, 0x20, 0x10, 0xa0, 0xe5, 0x08, 0xf7, 0x3d, 0x28, 0x04,
		0x99, 0x93, 0x97, 0xaf, 0xf9, 0x12, 0xc0, 0x01, 0x2d, 0xea, 0xf3, 0x98, 0x0b, 0x46, 0xc2, 0x26,
		0x93, 0x10, 0x69, 0x1d, 0x71, 0x8e, 0x33, 0x00, 0x5e, 0x80, 0x2f, 0x47, 0x0a, 0xcc, 0x94, 0x16,
		0xe7, 0x37, 0x45, 0xd0, 0x61, 0x79, 0x32, 0x86, 0x08, 0x2a, 0x5b, 0x55, 0xfe, 0xee, 0x52, 0x38,
		0xaa, 0x18, 0xe9, 0x39, 0x1a, 0x1e, 0xb8, 0x26, 0x6b, 0x3d, 0x4b, 0xa9
	},
	{ // region 4, $14d500
		0x06, 0x01, 0xf3, 0x39, 0xa0, 0x09, 0xa0, 0x21, 0xb0, 0x6f, 0x32, 0x8b, 0xfd, 0x89, 0x29, 0xa0,
		0x4a, 0x62, 0xed, 0xa1, 0x2d, 0xa4, 0x49, 0xf2, 0x10, 0x3c, 0x77, 0xa3, 0x84, 0x8d, 0xfa, 0xd1,
		0xc6, 0x57, 0xe2, 0x78, 0xef, 0xe9, 0xb6, 0xa1, 0x5a, 0xbd, 0x3f, 0x02, 0x0b, 0x28, 0xd6, 0x76,
		0xfc, 0x5b, 0x19, 0x9f, 0x21, 0x66, 0x4c, 0x2d, 0x45, 0x99, 0xde, 0xab, 0x46, 0xbd, 0xe9, 0x84,
		0xc4, 0xdc, 0xc7, 0x30, 0x70, 0xdd, 0x64, 0xea, 0xbc, 0x6b, 0xd3, 0xe6, 0x45, 0x3f, 0x07, 0x7e,
		0x50, 0xef, 0xb2, 0x84, 0x33, 0x3c, 0xcc, 0x3f, 0x39, 0x5b, 0xf5, 0x6d, 0x71, 0xc5, 0xdd, 0xf5,
		0xf9, 0xd0, 0xf7, 0x9c, 0xe6, 0xc7, 0xad, 0x1b, 0x29, 0xb9, 0x90, 0x08, 0x75, 0xc4, 0xc3, 0xef,
		0xa8, 0xfc, 0xab, 0x55, 0x7c, 0x21, 0x57, 0x97, 0x87, 0x4a, 0xcb, 0x0c, 0x56, 0x0a, 0x4f, 0xcb,
		0x52, 0x33, 0x87, 0x31, 0xf3, 0x43, 0x5b, 0x41, 0x90, 0xf8, 0xc0, 0xdd, 0x5a, 0xa4, 0x26, 0x2a,
		0x60, 0xa5, 0x6d, 0xda, 0xf2, 0x6a, 0xf0, 0xb3, 0xda, 0x25, 0x33, 0x87, 0x22, 0xe4, 0xac, 0xd3,
		0x96, 0xe0, 0x99, 0x3e, 0xfb, 0x14, 0x45, 0x17, 0x25, 0x56, 0xbe, 0xef, 0x8f, 0x8e, 0x3d, 0x1e,
		0xc7, 0x99, 0xa2, 0xa1, 0x50, 0xfe, 0xdf, 0xd4, 0xa1, 0x87, 0xf4, 0xd5, 0xde, 0xa6, 0x8c, 0x6d,
		0x6c, 0xde, 0x47, 0xbe, 0x59, 0x8f, 0xd4, 0x97, 0xc3, 0xf4, 0xda, 0xbb, 0xa6, 0x73, 0xa9, 0xcb,
		0xf2, 0x01, 0xb9, 0x90, 0x8f, 0xed, 0x60, 0x64, 0x40, 0x1c, 0xb6, 0xc9, 0xa5, 0x7c, 0x17, 0x52,
		0x6f, 0xdc, 0x6d, 0x08, 0x2a, 0x1a, 0xe6, 0x68, 0x3f, 0xd4, 0x42, 0x69
	},
	{ // region 5, $14bfb2
		0x7f, 0x41, 0xf3, 0x39, 0xa0, 0x09, 0xa1, 0x21, 0xb0, 0xa2, 0x4c, 0x23, 0x13, 0xe9, 0x25, 0x3d,
		0x0f, 0x72, 0x3a, 0x9d, 0xb5, 0x96, 0xd1, 0xda, 0x07, 0x29, 0x41, 0x9a, 0xad, 0x70, 0xba, 0x46,
		0x63, 0x2b, 0x7f, 0x3d, 0xbe, 0x40, 0xad, 0xd4, 0x4c, 0x73, 0x27, 0x58, 0xa7, 0x65, 0xdc, 0xd6,
		0xfd, 0xde, 0xb5, 0x6e, 0xd6, 0x6c, 0x75, 0x1a, 0x32, 0x45, 0xd5, 0xe3, 0x6a, 0x14, 0x6d, 0x80,
		0x84, 0x15, 0xaf, 0xcc, 0x7b, 0x61, 0x51, 0x82, 0x40, 0x53, 0x7f, 0x38, 0xa0, 0xd6, 0x8f, 0x61,
		0x79, 0x19, 0xe5, 0x99, 0x84, 0xd8, 0x78, 0x27, 0x3f, 0x16, 0x97, 0x78, 0x4f, 0x7b, 0x0c, 0xa6,
		0x37, 0xdb, 0xc6, 0x0c, 0x24, 0xb4, 0xc7, 0x94, 0x9d, 0x92, 0xd2, 0x3b, 0xd5, 0x11, 0x6f, 0x0a,
		0xdb, 0x76, 0x66, 0xe7, 0xcd, 0x18, 0x2b, 0x66, 0xd8, 0x41, 0x40, 0x58, 0xa2, 0x01, 0x1e, 0x6d,
		0x44, 0x75, 0xe7, 0x19, 0x4f, 0xb2, 0xe8, 0xc4, 0x96, 0x77, 0x62, 0x02, 0xc9, 0xdc, 0x59, 0xf3,
		0x43, 0x8d, 0xc8, 0xfe, 0x9e, 0x2a, 0xba, 0x32, 0x3b, 0x62, 0xe3, 0x92, 0x6e, 0xc2, 0x08, 0x4d,
		0x51, 0xcd, 0xf9, 0x3a, 0x3e, 0xc9, 0x50, 0x27, 0x21, 0x25, 0x97, 0xd7, 0x0e, 0xf8, 0x39, 0x38,
		0xf5, 0x86, 0x94, 0x93, 0xbf, 0xeb, 0x18, 0xa8, 0xfc, 0x24, 0xf5, 0xf9, 0x99, 0x20, 0x3d, 0xcd,
		0x2c, 0x94, 0x25, 0x79, 0x28, 0x77, 0x8f, 0x2f, 0x10, 0x69, 0x86, 0x30, 0x43, 0x01, 0xd7, 0x9a,
		0x17, 0xe3, 0x47, 0x37, 0xbd, 0x62, 0x75, 0x42, 0x78, 0xf4, 0x2b, 0x57, 0x4c, 0x0a, 0xdb, 0x53,
		0x4d, 0xa1, 0x0a, 0xd6, 0x3a, 0x16, 0x15, 0xaa, 0x2c, 0x6c, 0x39, 0x42
	},
	{ // region 6, $14cd82
		0x12, 0x09, 0xf3, 0x29, 0xa0, 0x09, 0xa0, 0x21, 0xb0, 0xd5, 0x66, 0xa1, 0x28, 0x4a, 0x21, 0xc0,
		0xd3, 0x9b, 0x86, 0x80, 0x57, 0x6f, 0x41, 0xc2, 0xe4, 0x2f, 0x0b, 0x91, 0xbd, 0x3a, 0x7a, 0xba,
		0x00, 0xe5, 0x35, 0x02, 0x74, 0x7d, 0x8b, 0x21, 0x57, 0x10, 0x0f, 0xae, 0x44, 0xbb, 0xe2, 0x37,
		0x18, 0x7b, 0x52, 0x3d, 0x8c, 0x59, 0x9e, 0x20, 0x1f, 0x0a, 0xcc, 0x1c, 0x8e, 0x6a, 0xd7, 0x95,
		0x2b, 0x34, 0xb0, 0x82, 0x6d, 0xfd, 0x25, 0x33, 0xaa, 0x3b, 0x2b, 0x70, 0x15, 0x87, 0x31, 0x5d,
		0xbb, 0x29, 0x19, 0x95, 0xd5, 0x8e, 0x24, 0x28, 0x5e, 0xd0, 0x20, 0x83, 0x46, 0x4a, 0x21, 0x70,
		0x5b, 0xcd, 0xae, 0x7b, 0x61, 0xa1, 0xfa, 0xf4, 0x2b, 0x84, 0x15, 0x6e, 0x36, 0x5d, 0x1b, 0x24,
		0x0f, 0x09, 0x3a, 0x61, 0x38, 0x0f, 0x18, 0x35, 0x11, 0x38, 0xb4, 0xbd, 0xee, 0xf7, 0xec, 0x0f,
		0x1d, 0xb7, 0x48, 0x01, 0xaa, 0x09, 0x8f, 0x61, 0xb5, 0x0f, 0x1d, 0x26, 0x39, 0x2e, 0x8c, 0xd6,
		0x26, 0x5c, 0x3d, 0x23, 0x63, 0xe9, 0x6b, 0x97, 0xb4, 0x9f, 0x7b, 0xb6, 0xba, 0xa0, 0x7c, 0xc6,
		0x25, 0xa1, 0x73, 0x36, 0x67, 0x7f, 0x74, 0x1e, 0x1d, 0xda, 0x70, 0xbf, 0xa5, 0x63, 0x35, 0x39,
		0x24, 0x8c, 0x9f, 0x85, 0x16, 0xd8, 0x50, 0x95, 0x71, 0xc0, 0xf6, 0x1e, 0x6d, 0x80, 0xed, 0x15,
		0xeb, 0x63, 0xe9, 0x1b, 0xf6, 0x78, 0x31, 0xc6, 0x5c, 0xdd, 0x19, 0xbd, 0xdf, 0xa7, 0xec, 0x50,
		0x22, 0xad, 0xbb, 0xf6, 0xeb, 0xd6, 0xa3, 0x20, 0xc9, 0xe6, 0x9f, 0xcb, 0xf2, 0x97, 0xb9, 0x54,
		0x12, 0x66, 0xa6, 0xbe, 0x4a, 0x12, 0x43, 0xec, 0x00, 0xea, 0x49, 0x02
	},
	{ // region 7, $14ce76
		0xa4, 0x49, 0xf3, 0x29, 0xa0, 0x09, 0xa1, 0x21, 0xb0, 0xef, 0x80, 0x20, 0x3d, 0xaa, 0x36, 0x5d,
		0x98, 0xc4, 0xd2, 0x63, 0xdf, 0x61, 0xb0, 0xc3, 0xc2, 0x35, 0xd4, 0x88, 0xe6, 0x1d, 0x3a, 0x2f,
		0x9c, 0xb9, 0xd1, 0xc6, 0x43, 0xba, 0x69, 0x6d, 0x49, 0xac, 0xdd, 0x05, 0xe0, 0xf8, 0xe8, 0x97,
		0x19, 0x18, 0x08, 0x0c, 0x42, 0x46, 0xc7, 0x0d, 0x25, 0xce, 0xc3, 0x54, 0xb2, 0xd9, 0x42, 0x91,
		0xea, 0x53, 0x98, 0x38, 0x78, 0x81, 0x12, 0xca, 0x15, 0x23, 0xbd, 0xc1, 0x70, 0x1f, 0xd2, 0x40,
		0xfd, 0x39, 0x33, 0xaa, 0x27, 0x2b, 0xe8, 0x10, 0x7d, 0xa4, 0xa8, 0x8e, 0x3d, 0x00, 0x4f, 0x3a,
		0x7f, 0xd8, 0x96, 0xea, 0x9e, 0x8e, 0x15, 0x6e, 0x9f, 0x76, 0x57, 0xba, 0x7d, 0xc2, 0xdf, 0x57,
		0x42, 0x82, 0xf4, 0xda, 0x89, 0x06, 0x05, 0x04, 0x62, 0x2f, 0x29, 0x23, 0x54, 0xd5, 0xbb, 0x97,
		0xf5, 0xf9, 0xc1, 0xcf, 0xec, 0x5f, 0x1d, 0xfd, 0xbb, 0xa6, 0xd7, 0x4a, 0xa8, 0x66, 0xbf, 0xb9,
		0x09, 0x44, 0xb1, 0x60, 0x28, 0xa9, 0x35, 0x16, 0x15, 0xf5, 0x13, 0xc1, 0x07, 0x7e, 0xd7, 0x40,
		0xdf, 0x8e, 0xd3, 0x32, 0xa9, 0x35, 0x98, 0x15, 0x32, 0xa9, 0x49, 0xc0, 0x24, 0xb4, 0x4a, 0x53,
		0x6b, 0x79, 0xaa, 0x77, 0x6c, 0xc5, 0x88, 0x69, 0xe5, 0x5d, 0xde, 0x42, 0x28, 0xf9, 0xb7, 0x5c,
		0xab, 0x19, 0xc7, 0xbc, 0xc5, 0x60, 0xeb, 0x5e, 0xa8, 0x52, 0xc4, 0x32, 0x7c, 0x35, 0x02, 0x06,
		0x46, 0x77, 0x30, 0xb6, 0x33, 0x4b, 0xb8, 0xfd, 0x02, 0xd8, 0x14, 0x40, 0x99, 0x25, 0x7e, 0x55,
		0xd6, 0x44, 0x43, 0x8d, 0x73, 0x0e, 0x71, 0x48, 0xd3, 0x82, 0x40, 0xda
	}
};

static void IGS022_do_dma(UINT16 src, UINT16 dst, UINT16 size, UINT16 mode)
{
	UINT16 param;

	param = mode >> 8;
	mode &=0xf;  // what are the other bits?

	if ((mode == 0) || (mode == 1) || (mode == 2) || (mode == 3))
	{
		int x;
		UINT16 *PROTROM = (UINT16*)(PGMUSER0 + 0x10000);

		for (x = 0; x < size; x++)
		{
			UINT16 dat2 = PROTROM[src + x];

			UINT8 extraoffset = param&0xff;
			UINT8* dectable = (UINT8*)PROTROM; // the basic decryption table is at the start of the mcu data rom! at least in killbld
			UINT8 taboff = ((x*2)+extraoffset) & 0xff; // must allow for overflow in instances of odd offsets
			UINT16 extraxor = ((dectable[taboff+0]) << 8) | (dectable[taboff+1] << 0);

			dat2 = ((dat2 & 0x00ff)<<8) | ((dat2 & 0xff00)>>8);

			//  mode==0 plain
			if (mode==3) dat2 ^= extraxor;
			if (mode==2) dat2 += extraxor;
			if (mode==1) dat2 -= extraxor;

			sharedprotram[dst + x] = dat2;
		}

		/* Killing Blade: hack, patches out some additional security checks... we need to emulate them instead! */
		// different region IGS025 devices supply different sequences - we currently only have the china sequence for Killing Blade
		//if ((mode==3) && (param==0x54) && (src*2==0x2120) && (dst*2==0x2600)) sharedprotram[0x2600 / 2] = 0x4e75;

	}
	if (mode == 4)
	{
		// not used by killing blade
		/* looks almost like a fixed value xor, but isn't */
	}
	else if (mode == 5)
	{
		/* mode 5 seems to be a straight copy */
		int x;
		UINT16 *PROTROM = (UINT16*)(PGMUSER0 + 0x10000);
		for (x = 0; x < size; x++)
		{
			UINT16 dat = PROTROM[src + x];

			sharedprotram[dst + x] = dat;
		}
	}
	else if (mode == 6)
	{
		/* mode 6 seems to swap bytes and nibbles */
		int x;
		UINT16 *PROTROM = (UINT16*)(PGMUSER0 + 0x10000);
		for (x = 0; x < size; x++)
		{
			UINT16 dat = PROTROM[src + x];

			dat = ((dat & 0xf000) >> 12)|
					((dat & 0x0f00) >> 4)|
					((dat & 0x00f0) << 4)|
					((dat & 0x000f) << 12);

			sharedprotram[dst + x] = dat;
		}
	}
	else if (mode == 7)
	{
		// not used by killing blade
		/* weird mode, the params get left in memory? - maybe it's a NOP? */
	}
	else
	{
		// not used by killing blade
		/* invalid? */

	}
}

// the internal MCU boot code automatically does this DMA
// and puts the version # of the data rom in ram
static void IGS022_reset()
{
	int i;
	UINT16 *PROTROM = (UINT16*)(PGMUSER0 + 0x10000);
	UINT16 tmp;

	// fill ram with A5 patern
	for (i = 0; i < 0x4000/2; i++)
		sharedprotram[i] = 0xa55a;

	// the auto-dma
	UINT16 src = PROTROM[0x100 / 2];
	UINT32 dst = PROTROM[0x102 / 2];
	UINT16 size = PROTROM[0x104/ 2];
	UINT16 mode = PROTROM[0x106 / 2];

	src = ((src & 0xff00) >> 8) | ((src & 0x00ff) << 8);
	dst = ((dst & 0xff00) >> 8) | ((dst & 0x00ff) << 8);
	size = ((size & 0xff00) >> 8) | ((size & 0x00ff) << 8);
	mode &= 0xff;

	src >>= 1;

	IGS022_do_dma(src,dst,size,mode);

	// there is also a version ID? (or is it some kind of checksum) that is stored in the data rom, and gets copied..
	// Dragon World 3 checks it
	tmp = PROTROM[0x114/2];
	tmp = ((tmp & 0xff00) >> 8) | ((tmp & 0x00ff) << 8);
	sharedprotram[0x2a2/2] = tmp;
}

void IGS022_handle_command()
{
	UINT16 cmd = sharedprotram[0x200/2];

	if (cmd == 0x6d)    // Store values to asic ram
	{
		UINT32 p1 = (sharedprotram[0x298/2] << 16) | sharedprotram[0x29a/2];
		UINT32 p2 = (sharedprotram[0x29c/2] << 16) | sharedprotram[0x29e/2];

		if ((p2 & 0xffff) == 0x9)   // Set value
		{
			int reg = (p2 >> 16) & 0xffff;

			if (reg & 0x200)
				m_kb_regs[reg & 0xff] = p1;
		}

		if ((p2 & 0xffff) == 0x6)   // Add value
		{
			int src1 = (p1 >> 16) & 0xff;
			int src2 = (p1 >> 0) & 0xff;
			int dst = (p2 >> 16) & 0xff;

			m_kb_regs[dst] = m_kb_regs[src2] - m_kb_regs[src1];
		}

		if ((p2 & 0xffff) == 0x1)   // Add Imm?
		{
			int reg = (p2 >> 16) & 0xff;
			int imm = (p1 >> 0) & 0xffff;

			m_kb_regs[reg] += imm;
		}

		if ((p2 & 0xffff) == 0xa)   // Get value
		{
			int reg = (p1 >> 16) & 0xFF;

			sharedprotram[0x29c/2] = (m_kb_regs[reg] >> 16) & 0xffff;
			sharedprotram[0x29e/2] = m_kb_regs[reg] & 0xffff;
		}
	}

	if(cmd == 0x4f) //memcpy with encryption / scrambling
	{
		UINT16 src = sharedprotram[0x290 / 2] >> 1; // ?
		UINT32 dst = sharedprotram[0x292 / 2];
		UINT16 size = sharedprotram[0x294 / 2];
		UINT16 mode = sharedprotram[0x296 / 2];

		IGS022_do_dma(src,dst,size,mode);
	}
}

void killbld_protection_calculate_hold(int y, int z)
{
	unsigned short old = m_kb_prot_hold;

	m_kb_prot_hold = ((old << 1) | (old >> 15));

	m_kb_prot_hold ^= 0x2bad;
	m_kb_prot_hold ^= BIT(z, y);
	m_kb_prot_hold ^= BIT( old,  7) <<  0;
	m_kb_prot_hold ^= BIT(~old, 13) <<  4;
	m_kb_prot_hold ^= BIT( old,  3) << 11;

	m_kb_prot_hold ^= (m_kb_prot_hilo & ~0x0408) << 1;
}

void killbld_protection_calculate_hilo()
{
	UINT8 source;

	m_kb_prot_hilo_select++;

	if (m_kb_prot_hilo_select > 0xeb) {
		m_kb_prot_hilo_select = 0;
	}

	source = m_kb_source_data[(PgmInput[7] - m_kb_source_data_offset)][m_kb_prot_hilo_select];

	if (m_kb_prot_hilo_select & 1)
	{
		m_kb_prot_hilo = (m_kb_prot_hilo & 0x00ff) | (source << 8);
	}
	else
	{
		m_kb_prot_hilo = (m_kb_prot_hilo & 0xff00) | (source << 0);
	}
}

static void __fastcall killbld_igs025_prot_w(UINT32 offset, UINT16 data)
{
	offset &= 0xf;

	if (offset == 0)
	{
		m_kb_cmd = data;
	}
	else
	{
		switch (m_kb_cmd)
		{
			case 0x00:
				m_kb_reg = data;
			break;

			//case 0x01: // ??
			//break;

			case 0x02:
			{
				if (data == 0x0001) {  // Execute cmd
					IGS022_handle_command();
					m_kb_reg++;
				}
			}
			break;

			case 0x03:
				m_kb_swap = data;
			break;

			case 0x04:
				m_kb_ptr = data;
			break;

			case 0x20:
			case 0x21:
			case 0x22:
			case 0x23:
			case 0x24:
			case 0x25:
			case 0x26:
			case 0x27:
				m_kb_ptr++;
				killbld_protection_calculate_hold(m_kb_cmd & 0x0f, data & 0xff);
			break;
		}
	}
}

static UINT16 __fastcall killbld_igs025_prot_r(UINT32 offset)
{
	offset &= 2;

	if (offset)
	{
		switch (m_kb_cmd)
		{
			case 0x00:
				return BITSWAP08((m_kb_swap+1)&0xff, 0,1,2,3,4,5,6,7); // dw3

			case 0x01:
				return m_kb_reg & 0x7f;

			case 0x05:
			{
				switch (m_kb_ptr)
				{
					case 1:
						return 0x3f00 | PgmInput[7];

					case 2:
						return 0x3f00 | ((m_kb_game_id >> 8) & 0xff);

					case 3:
						return 0x3f00 | ((m_kb_game_id >> 16) & 0xff);

					case 4:
						return 0x3f00 | ((m_kb_game_id >> 24) & 0xff);

					case 5:
					default: // >= 5
						return 0x3f00 | BITSWAP08(m_kb_prot_hold, 5,2,9,7,10,13,12,15);
				}

				return 0;
			}

			case 0x40:
				killbld_protection_calculate_hilo();
				return 0; // is this used?
		}
	}

	return 0;
}

static void killbld_reset()
{
	/* fill the protection ram with a5 + auto dma */
	IGS022_reset();

	// Reset IGS025 stuff
	m_kb_prot_hold = 0;
	m_kb_prot_hilo = 0;
	m_kb_prot_hilo_select = 0;
	m_kb_cmd = 0;
	m_kb_reg = 0;
	m_kb_ptr = 0;
	m_kb_swap = 0;

	memset(m_kb_regs, 0, 0x10 * sizeof(UINT32));
}

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

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMUSER0 + 0x000000;
		ba.nLen		= 0x0004000;
		ba.nAddress	= 0x300000;
		ba.szName	= "ProtRAM";
		BurnAcb(&ba);

		ba.Data		= (UINT8*)m_kb_regs;
		ba.nLen		= 0x00100 * sizeof(INT32);
		ba.nAddress	= 0xfffffc00;
		ba.szName	= "Protection Registers";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(m_kb_prot_hold);
		SCAN_VAR(m_kb_prot_hilo);
		SCAN_VAR(m_kb_prot_hilo_select);
		SCAN_VAR(m_kb_cmd);
		SCAN_VAR(m_kb_reg);
		SCAN_VAR(m_kb_ptr);
		SCAN_VAR(m_kb_swap);
	}

	return 0;
}


void install_protection_asic25_asic22_killbld()
{
	if (strcmp(BurnDrvGetTextA(DRV_NAME), "killbld") == 0) {
		BurnLoadRom(PGMUSER0 + 0x10000,	11, 1);
	} else {
		BurnLoadRom(PGMUSER0 + 0x10000,	14, 1); // killbld104
	}

	BurnByteswap(PGMUSER0 + 0x10000, 0x10000);

	pPgmScanCallback = killbldScan;
	pPgmResetCallback = killbld_reset;

	sharedprotram = (UINT16*)PGMUSER0;

	m_kb_source_data = killbld_source_data;
	m_kb_source_data_offset = 0x16;
	m_kb_game_id = 0x89911400;

	SekOpen(0);
	SekMapMemory(PGMUSER0,	0x300000, 0x303fff, SM_RAM);
	SekMapHandler(4,	0xd40000, 0xd40003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, killbld_igs025_prot_r);
	SekSetWriteWordHandler(4, killbld_igs025_prot_w);
	SekClose();
}


static void drgw3_reset()
{
	/* fill the protection ram with a5 + auto dma */
	IGS022_reset();

	/* game won't boot unless various values are in protection RAM
	 - these should almost certainly end up there as the result of executing the protection
	   commands are startup, but which, and how? */

	sharedprotram[0x200/2] = 0x006d;
	sharedprotram[0x202/2] = 0x007c; // it cares about this, operation status flag?

	sharedprotram[0x20c/2] = 0x0000;
	sharedprotram[0x20e/2] = 0x0007;
	sharedprotram[0x210/2] = 0x0000;
	sharedprotram[0x212/2] = 0x0004;
	sharedprotram[0x214/2] = 0x0000;
	sharedprotram[0x216/2] = 0x0007;
	sharedprotram[0x218/2] = 0x0000;
	sharedprotram[0x21a/2] = 0x0004;

	sharedprotram[0x288/2] = 0x0000;
	sharedprotram[0x28a/2] = 0x00c2;
	sharedprotram[0x28c/2] = 0x0000;
	sharedprotram[0x28e/2] = 0x00c2;
	sharedprotram[0x290/2] = 0x0500;
	sharedprotram[0x292/2] = 0x1000;
	sharedprotram[0x294/2] = 0x00c3;
	sharedprotram[0x296/2] = 0x7104;
	sharedprotram[0x298/2] = 0x0000;
	sharedprotram[0x29a/2] = 0x0003;
	sharedprotram[0x29c/2] = 0x0108;
	sharedprotram[0x29e/2] = 0x0009;

	sharedprotram[0x2a2/2] = 0x84f6; // it cares about this, it's the version number of the data rom, copied automatically!

	sharedprotram[0x2ac/2] = 0x006d;
	sharedprotram[0x2ae/2] = 0x0000;

	sharedprotram[0x2b0/2] = 0xaf56;

	// Reset IGS025 stuff
	m_kb_prot_hold = 0;
	m_kb_prot_hilo = 0;
	m_kb_prot_hilo_select = 0;
	m_kb_cmd = 0;
	m_kb_reg = 0;
	m_kb_ptr = 0;
	m_kb_swap = 0;
	memset(m_kb_regs, 0, 0x10 * sizeof(UINT32));

}

void install_protection_asic25_asic22_drgw3()
{
	BurnLoadRom(PGMUSER0 + 0x10000,	7, 1);
	BurnByteswap(PGMUSER0 + 0x10000, 0x10000);

	pPgmScanCallback = killbldScan;
	pPgmResetCallback = drgw3_reset;

	sharedprotram = (UINT16*)PGMUSER0;

	m_kb_source_data = dw3_source_data;
	m_kb_source_data_offset = 1;
	m_kb_game_id = 0x00060000;

	SekOpen(0);
	SekMapMemory(PGMUSER0,	0x300000, 0x303fff, SM_RAM);
	SekMapHandler(4,	0xd40000, 0xd40003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, killbld_igs025_prot_r);
	SekSetWriteWordHandler(4, killbld_igs025_prot_w);
	SekClose();
}



//------------------------------------------
// Common asic27a simulation functions


static UINT16 asic27a_sim_value;
static UINT16 asic27a_sim_key;
static UINT32 asic27a_sim_response;
static UINT32 asic27a_sim_slots[0x100];
static UINT16 asic27a_sim_regs[0x100];
static UINT8  asic27a_sim_internal_slot;

static void (*asic27a_sim_command)(UINT8);

void __fastcall asic27a_sim_write(UINT32 offset, UINT16 data)
{
	switch (offset & 0x06)
	{
		case 0: asic27a_sim_value = data; return;

		case 2:
		{
			if ((data >> 8) == 0xff) asic27a_sim_key = 0xffff;

			asic27a_sim_value ^= asic27a_sim_key;

			UINT8 command = (data ^ asic27a_sim_key) & 0xff;

			asic27a_sim_regs[command] = asic27a_sim_value;

		//	bprintf (0, _T("Command: %2.2x, Data: %2.2x\n"), command, asic27a_sim_value);

			asic27a_sim_command(command);

			asic27a_sim_key = (asic27a_sim_key + 0x0100) & 0xff00;
			if (asic27a_sim_key == 0xff00) asic27a_sim_key = 0x0100;
			asic27a_sim_key |= asic27a_sim_key >> 8;
		}
		return;

		case 4: return;
	}
}

static UINT16 __fastcall asic27a_sim_read(UINT32 offset)
{
	switch (offset & 0x02)
	{
		case 0: return (asic27a_sim_response >>  0) ^ asic27a_sim_key;
		case 2: return (asic27a_sim_response >> 16) ^ asic27a_sim_key;
	}

	return 0;
}

static void asic27a_sim_reset()
{
	// The ASIC27a writes this to shared RAM
	UINT8 ram_string[16] = {
		'I', 'G', 'S', 'P', 'G', 'M', 0, 0, 0, 0/*REGION*/, 'C', 'H', 'I', 'N', 'A', 0
	};

	memset (PGMUSER0, 0, 0x400);

	ram_string[9] = PgmInput[7]; // region

	memcpy (PGMUSER0, ram_string, 16);

	BurnByteswap(PGMUSER0, 0x10);

	memset (asic27a_sim_slots, 0, 0x100 * sizeof(INT32));
	memset (asic27a_sim_regs,  0, 0x100 * sizeof(INT16));

	asic27a_sim_value = 0;
	asic27a_sim_key = 0;
	asic27a_sim_response = 0;
	asic27a_sim_internal_slot = 0;
}

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

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= (UINT8*)asic27a_sim_slots;
		ba.nLen		= 0x0000100 * sizeof(INT32);
		ba.nAddress	= 0xff00000;
		ba.szName	= "ASIC27a Slots";
		BurnAcb(&ba);

		ba.Data		= (UINT8*)asic27a_sim_regs;
		ba.nLen		= 0x0000100 * sizeof(INT16);
		ba.nAddress	= 0xff01000;
		ba.szName	= "ASIC27a Regs";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(asic27a_sim_value);
		SCAN_VAR(asic27a_sim_key);
		SCAN_VAR(asic27a_sim_response);
		SCAN_VAR(asic27a_sim_internal_slot);
	}

	return 0;
}


//---------------------------------
// ketsui / espgaluda / ddp3

static void ddp3_asic27a_sim_command(UINT8 command)
{
	switch (command)
	{
		case 0x40: // Combine slot values
			asic27a_sim_slots[(asic27a_sim_value >> 10) & 0x1f] = (asic27a_sim_slots[(asic27a_sim_value >> 5) & 0x1f] + asic27a_sim_slots[(asic27a_sim_value >> 0) & 0x1f]) & 0xffffff;
			asic27a_sim_response = 0x880000;
		break;

		case 0x67: // Select slot & write (high)
			asic27a_sim_internal_slot = asic27a_sim_value >> 8;
			asic27a_sim_slots[asic27a_sim_internal_slot] = (asic27a_sim_value & 0x00ff) << 16;
			asic27a_sim_response = 0x880000;
		break;

		case 0xe5: // Write slot (low)
			asic27a_sim_slots[asic27a_sim_internal_slot] |= asic27a_sim_value;
			asic27a_sim_response = 0x880000;
		break;

		case 0x8e: // Read slot
			asic27a_sim_response = asic27a_sim_slots[asic27a_sim_value & 0xff];
		break;

		case 0x99: // Reset?
			asic27a_sim_key = 0;
			asic27a_sim_response = 0x880000 | (PgmInput[7] << 8);
		break;

		default:
			asic27a_sim_response = 0x880000;
		break;
	}
}

void install_protection_asic27a_ketsui()
{
	pPgmResetCallback = asic27a_sim_reset;
	pPgmScanCallback = asic27a_sim_scan;
	asic27a_sim_command = ddp3_asic27a_sim_command;

	SekOpen(0);
	SekMapHandler(4,		0x400000, 0x400005, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	asic27a_sim_read);
	SekSetWriteWordHandler(4, 	asic27a_sim_write);
	SekClose();
}

void install_protection_asic27a_ddp3()
{
	pPgmResetCallback = asic27a_sim_reset;
	pPgmScanCallback = asic27a_sim_scan;
	asic27a_sim_command = ddp3_asic27a_sim_command;

	SekOpen(0);
	SekMapHandler(4,		0x500000, 0x500005, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	asic27a_sim_read);
	SekSetWriteWordHandler(4, 	asic27a_sim_write);
	SekClose();
}


//--------------------------
// oldsplus

static const UINT8 oldsplus_fc[0x20]={
	0x00,0x00,0x0a,0x3a,0x4e,0x2e,0x03,0x40,0x33,0x43,0x26,0x2c,0x00,0x00,0x00,0x00,
	0x00,0x00,0x44,0x4d,0x0b,0x27,0x3d,0x0f,0x37,0x2b,0x02,0x2f,0x15,0x45,0x0e,0x30
};

static const UINT16 oldsplus_90[0x7]={
	0x50,0xa0,0xc8,0xf0,0x190,0x1f4,0x258
};

static const UINT8 oldsplus_5e[0x20]={
	0x04,0x04,0x04,0x04,0x04,0x03,0x03,0x03,0x02,0x02,0x02,0x01,0x01,0x01,0x01,0x01,
	0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

static const UINT8 oldsplus_b0[0xe0]={
	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,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,0x01,0x02,0x03,0x04,
	0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,
	0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,
	0x00,0x00,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,
	0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,
	0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
	0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
	0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,
	0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x1f,0x1f,0x1f,0x1f
};

static const UINT8 oldsplus_ae[0xe0]={
	0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,
	0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,
	0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,
	0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,0x50,
	0x1E,0x1F,0x20,0x21,0x22,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x1F,0x20,0x21,0x22,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x20,0x21,0x22,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x21,0x22,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,
	0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23,0x23
};

static const UINT16 oldsplus_ba[0x4]={
	0x3138,0x2328,0x1C20,0x1518
};

static const UINT16 oldsplus_8c[0x20]={
	0x0032,0x0032,0x0064,0x0096,0x0096,0x00fa,0x012c,0x015e,0x0032,0x0064,0x0096,0x00c8,0x00c8,0x012c,0x015e,0x0190,
	0x0064,0x0096,0x00c8,0x00fa,0x00fa,0x015e,0x0190,0x01c2,0x0096,0x00c8,0x00fa,0x012c,0x012c,0x0190,0x01c2,0x01f4
};

static inline UINT16 oldsplus_9d(UINT16 a)
{
	const UINT8 tab[8] = { 0x3c, 0x46, 0x5a, 0x6e, 0x8c, 0xc8, 0x50, };

	if ((a % 0x27) <= 0x07) return (a % 0x27) * 0x64;
	if ((a % 0x27) >= 0x17) return 0x6bc;
	
	return 0x2bc + (tab[a / 0x27] * ((a % 0x27) - 7));
}

static void oldsplus_asic27a_sim_command(UINT8 command)
{
	switch (command)
	{
		case 0x88: // Reset?
			asic27a_sim_key = 0;
			asic27a_sim_response = 0x990000 | (PgmInput[7] << 8);
		break;

		case 0xa0:
			asic27a_sim_response = ((asic27a_sim_value >= 0x0f) ? 0x0f : asic27a_sim_value) * 0x23;
		break;

		case 0xd0: // Text palette offset
			asic27a_sim_response = 0xa01000 + (asic27a_sim_value << 5);
		break;

		case 0xc0: // Sprite palette offset
			asic27a_sim_response = 0xa00000 + (asic27a_sim_value << 6);
		break;

		case 0xc3: // Background palette offset
			asic27a_sim_response = 0xa00800 + (asic27a_sim_value << 6);
		break;

		case 0x33: // Store regs
			asic27a_sim_response = 0x990000;
		break;

		case 0x35: // Add '36' reg
			asic27a_sim_regs[0x36] += asic27a_sim_value;
			asic27a_sim_response = 0x990000;
		break;

		case 0x36: // Store regs
			asic27a_sim_response = 0x990000;
		break;

		case 0x37: // Add '33' reg
			asic27a_sim_regs[0x33] += asic27a_sim_value;
			asic27a_sim_response = 0x990000;
		break;

		case 0x34: // Read '36' reg
			asic27a_sim_response = asic27a_sim_regs[0x36];
		break;

		case 0x38: // Read '33' reg
			asic27a_sim_response = asic27a_sim_regs[0x33];
		break;

		case 0xe7: // Select slot
		{
			asic27a_sim_response = 0x990000;

			asic27a_sim_internal_slot = (asic27a_sim_value >> 12) & 0x0f;
		}
		break;

		case 0xe5: // Write slot
		{
			asic27a_sim_response = 0x990000;

			asic27a_sim_slots[asic27a_sim_internal_slot] = asic27a_sim_value;

			if (asic27a_sim_internal_slot == 0x0b) asic27a_sim_slots[0xc] = 0; // ??
		}
		break;

		case 0xf8: // Read slot
			asic27a_sim_response = asic27a_sim_slots[asic27a_sim_value];
		break;

		case 0xc5: // Increment slot 'd'
			asic27a_sim_slots[0xd]--;
			asic27a_sim_response = 0x990000;
		break;

		case 0xd6: // Increment slot 'b'
			asic27a_sim_slots[0xb]++;
			asic27a_sim_response = 0x990000;
		break;

		case 0x3a: // Clear slot 'f'
			asic27a_sim_slots[0xf] = 0;
			asic27a_sim_response = 0x990000;
		break;

		case 0xf0: // Background layer 'x' select
			asic27a_sim_response = 0x990000;
		break;

		case 0xed: // Background layer offset
			if (asic27a_sim_value & 0x400) asic27a_sim_value = -(0x400 - (asic27a_sim_value & 0x3ff));
			asic27a_sim_response = 0x900000 + ((asic27a_sim_regs[0xf0] + (asic27a_sim_value * 0x40)) * 4);
		break;

		case 0xe0: // Text layer 'x' select
			asic27a_sim_response = 0x990000;
		break;

		case 0xdc: // Text layer offset
			asic27a_sim_response = 0x904000 + ((asic27a_sim_regs[0xe0] + (asic27a_sim_value * 0x40)) * 4);
		break;

		case 0xcb: // Some sort of status read?
			asic27a_sim_response = 0x00c000;
		break;

		case 0x5e: // Read from data table
			asic27a_sim_response = oldsplus_5e[asic27a_sim_value];
		break;

		case 0x80: // Read from data table
			asic27a_sim_response = (asic27a_sim_value < 4) ? ((asic27a_sim_value + 1) * 0xbb8) : 0xf4240;
		break;

		case 0x8c: // Read from data table
			asic27a_sim_response = oldsplus_8c[asic27a_sim_value];
		break;

		case 0x90: // Read from data table
			asic27a_sim_response = oldsplus_90[asic27a_sim_value];
		break;

		case 0x9d: // Read from data table
			asic27a_sim_response = oldsplus_9d(asic27a_sim_value);
		break;

		case 0xae: // Read from data table
			asic27a_sim_response = oldsplus_ae[asic27a_sim_value];
		break;

		case 0xb0: // Read from data table
			asic27a_sim_response = oldsplus_b0[asic27a_sim_value];
		break;

		case 0xba: // Read from data table
			asic27a_sim_response = oldsplus_ba[asic27a_sim_value];
		break;

		case 0xfc: // Read from data table
			asic27a_sim_response = oldsplus_fc[asic27a_sim_value];
		break;

		default:
			asic27a_sim_response = 0x990000;
		break;
	}
}

void install_protection_asic27a_oldsplus()
{
	pPgmResetCallback = asic27a_sim_reset;
	pPgmScanCallback = asic27a_sim_scan;
	asic27a_sim_command = oldsplus_asic27a_sim_command;

	SekOpen(0);
	SekMapMemory(PGMUSER0,		0x4f0000, 0x4f003f | 0x3ff, SM_READ); // ram

	SekMapHandler(4,		0x500000, 0x500003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	asic27a_sim_read);
	SekSetWriteWordHandler(4, 	asic27a_sim_write);
	SekClose();
}


//-------------------------------------
// Knights of Valour




//-----------------------------------------------------------------------------------------------------
// ASIC28 - Knights of Valour


static const UINT8 B0TABLE[8] = { 2, 0, 1, 4, 3 }; // Maps char portraits to tables

static const UINT8 BATABLE[0x40] = {
	0x00,0x29,0x2c,0x35,0x3a,0x41,0x4a,0x4e,0x57,0x5e,0x77,0x79,0x7a,0x7b,0x7c,0x7d,
	0x7e,0x7f,0x80,0x81,0x82,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x90,
	0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9e,0xa3,0xd4,0xa9,0xaf,0xb5,0xbb,0xc1
};

static void kov_asic27a_sim_command(UINT8 command)
{
	switch (command)
	{
		case 0x67: // unknown or status check?
		case 0x8e:
		case 0xa3:
		case 0x33: // kovsgqyz (a3)
		case 0x3a: // kovplus
		case 0xc5: // kovplus
			asic27a_sim_response = 0x880000;
		break;

		case 0x99: // Reset
			asic27a_sim_key = 0;
			asic27a_sim_response = 0x880000 | (PgmInput[7] << 8);
		break;

		case 0x9d: // Sprite palette offset
			asic27a_sim_response = 0xa00000 + ((asic27a_sim_value & 0x1f) * 0x40);
		break;

		case 0xb0: // Read from data table
			asic27a_sim_response = B0TABLE[asic27a_sim_value & 0x07];
		break;

		case 0xb4: // Copy slot 'a' to slot 'b'
		case 0xb7: // kovsgqyz (b4)
		{
			asic27a_sim_response = 0x880000;

			if (asic27a_sim_value == 0x0102) asic27a_sim_value = 0x0100; // why?

			asic27a_sim_slots[(asic27a_sim_value >> 8) & 0x0f] = asic27a_sim_slots[(asic27a_sim_value >> 0) & 0x0f];
		}
		break;

		case 0xba: // Read from data table
			asic27a_sim_response = BATABLE[asic27a_sim_value & 0x3f];
		break;

		case 0xc0: // Text layer 'x' select
			asic27a_sim_response = 0x880000;
		break;

		case 0xc3: // Text layer offset
			asic27a_sim_response = 0x904000 + ((asic27a_sim_regs[0xc0] + (asic27a_sim_value * 0x40)) * 4);
		break;

		case 0xcb: // Background layer 'x' select
			asic27a_sim_response = 0x880000;
		break;

		case 0xcc: // Background layer offset
		{
	   	 	INT32 y = asic27a_sim_value;
	    		if (y & 0x400) y = -(0x400 - (y & 0x3ff));
	    		asic27a_sim_response = 0x900000 + (((asic27a_sim_regs[0xcb] + y * 64) * 4));
   		}
		break;

		case 0xd0: // Text palette offset
		case 0xcd: // kovsgqyz (d0)
			asic27a_sim_response = 0xa01000 + (asic27a_sim_value * 0x20);
		break;

		case 0xd6: // Copy slot to slot 0
			asic27a_sim_response = 0x880000;
			asic27a_sim_slots[0] = asic27a_sim_slots[asic27a_sim_value & 0x0f];
		break;

		case 0xdc: // Background palette offset
		case 0x11: // kovsgqyz (dc)
			asic27a_sim_response = 0xa00800 + (asic27a_sim_value * 0x40);
		break;

		case 0xe0: // Sprite palette offset
		case 0x9e: // kovsgqyz (e0)
			asic27a_sim_response = 0xa00000 + ((asic27a_sim_value & 0x1f) * 0x40);
		break;

		case 0xe5: // Write slot (low)
		{
			asic27a_sim_response = 0x880000;

			asic27a_sim_slots[asic27a_sim_internal_slot] = (asic27a_sim_slots[asic27a_sim_internal_slot] & 0x00ff0000) | ((asic27a_sim_value & 0xffff) <<  0);
		}
		break;

		case 0xe7: // Write slot (and slot select) (high)
		{
			asic27a_sim_response = 0x880000;
			asic27a_sim_internal_slot = (asic27a_sim_value >> 12) & 0x0f;

			asic27a_sim_slots[asic27a_sim_internal_slot] = (asic27a_sim_slots[asic27a_sim_internal_slot] & 0x0000ffff) | ((asic27a_sim_value & 0x00ff) << 16);
		}
		break;

		case 0xf0: // Some sort of status read?
			asic27a_sim_response = 0x00c000;
		break;

		case 0xf8: // Read slot
		case 0xab: // kovsgqyz (f8)
			asic27a_sim_response = asic27a_sim_slots[asic27a_sim_value & 0x0f] & 0x00ffffff;
		break;

		case 0xfc: // Adjust damage level to char experience level
			asic27a_sim_response = (asic27a_sim_value * asic27a_sim_regs[0xfe]) >> 6;
		break;

		case 0xfe: // Damage level adjust
			asic27a_sim_response = 0x880000;
		break;

		default:
			asic27a_sim_response = 0x880000;
		break;
	}
}

void install_protection_asic27_kov()
{
	pPgmResetCallback = asic27a_sim_reset;
	pPgmScanCallback = asic27a_sim_scan;
	asic27a_sim_command = kov_asic27a_sim_command;

	SekOpen(0);
	SekMapMemory(PGMUSER0,		0x4f0000, 0x4f003f | 0x3ff, SM_READ);

	SekMapHandler(4,		0x500000, 0x500003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	asic27a_sim_read);
	SekSetWriteWordHandler(4, 	asic27a_sim_write);
	SekClose();
}

//-------------------------------------------------------------------------
// puzlstar

static const UINT8 Pstar_ba[0x1e]={
	0x02,0x00,0x00,0x01,0x00,0x03,0x00,0x00,0x02,0x00,0x06,0x00,0x22,0x04,0x00,0x03,
	0x00,0x00,0x06,0x00,0x20,0x07,0x00,0x03,0x00,0x21,0x01,0x00,0x00,0x63
};

static const UINT8 Pstar_b0[0x10]={
	0x09,0x0A,0x0B,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x00,0x00,0x00,0x00
};

static const UINT16 Pstar_ae[0x10]={
	0x5D,0x86,0x8C,0x8B,0xE0,0x8B,0x62,0xAF,0xB6,0xAF,0x10A,0xAF,0x00,0x00,0x00,0x00
};

static const UINT8 Pstar_a0[0x10]={
	0x02,0x03,0x04,0x05,0x06,0x01,0x0A,0x0B,0x0C,0x0D,0x0E,0x09,0x00,0x00,0x00,0x00
};

static const UINT8 Pstar_9d[0x10]={
	0x05,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

static const UINT8 Pstar_90[0x10]={
	0x0C,0x10,0x0E,0x0C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00
};

static const UINT8 Pstar_8c[0x23]={
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,
	0x01,0x01,0x01,0x01,0x02,0x02,0x02,0x02,0x02,0x02,0x03,0x03,0x03,0x04,0x04,0x04,
	0x03,0x03,0x03
};

static const UINT8 Pstar_80[0x1a3]={
	0x03,0x03,0x04,0x04,0x04,0x04,0x05,0x05,0x05,0x05,0x06,0x06,0x03,0x03,0x04,0x04,
	0x05,0x05,0x05,0x05,0x06,0x06,0x07,0x07,0x03,0x03,0x04,0x04,0x05,0x05,0x05,0x05,
	0x06,0x06,0x07,0x07,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x07,0x07,0x07,0x07,
	0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x07,0x07,0x07,0x08,0x08,0x05,0x05,0x05,0x05,
	0x05,0x05,0x05,0x06,0x06,0x06,0x07,0x07,0x06,0x06,0x06,0x07,0x07,0x07,0x08,0x08,
	0x09,0x09,0x09,0x09,0x07,0x07,0x07,0x07,0x07,0x08,0x08,0x08,0x08,0x09,0x09,0x09,
	0x06,0x06,0x07,0x07,0x07,0x08,0x08,0x08,0x08,0x08,0x09,0x09,0x05,0x05,0x06,0x06,
	0x06,0x07,0x07,0x08,0x08,0x08,0x08,0x09,0x07,0x07,0x07,0x07,0x07,0x08,0x08,0x08,
	0x08,0x09,0x09,0x09,0x06,0x06,0x07,0x03,0x07,0x06,0x07,0x07,0x08,0x07,0x05,0x04,
	0x03,0x03,0x04,0x04,0x05,0x05,0x06,0x06,0x06,0x06,0x06,0x06,0x03,0x04,0x04,0x04,
	0x04,0x05,0x05,0x06,0x06,0x06,0x06,0x07,0x04,0x04,0x05,0x05,0x06,0x06,0x06,0x06,
	0x06,0x07,0x07,0x08,0x05,0x05,0x06,0x07,0x07,0x08,0x08,0x08,0x08,0x08,0x08,0x08,
	0x05,0x05,0x05,0x07,0x07,0x07,0x07,0x07,0x07,0x08,0x08,0x08,0x08,0x08,0x09,0x09,
	0x09,0x09,0x03,0x04,0x04,0x05,0x05,0x05,0x06,0x06,0x07,0x07,0x07,0x07,0x08,0x08,
	0x08,0x09,0x09,0x09,0x03,0x04,0x05,0x05,0x04,0x03,0x04,0x04,0x04,0x05,0x05,0x04,
	0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x04,0x04,0x04,0x04,0x04,
	0x04,0x04,0x04,0x04,0x04,0x03,0x03,0x03,0x03,0x03,0x03,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,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,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,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,
	0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00
};

static UINT16 __fastcall puzlstar_protram_read_word(UINT32 offset)
{
	if ((offset & 0x3e) == 0x08) return PgmInput[7]; // Region
	if ((offset & 0x38) == 0x20) return asic27a_sim_slots[((offset & 0x06)/2)+0x10]--; // Timer

	return 0;
}

static UINT8 __fastcall puzlstar_protram_read_byte(UINT32 offset)
{
	if ((offset & 0x3e) == 0x08) return PgmInput[7]; // Region

	return 0;
}

static void puzlstar_asic27a_sim_command(UINT8 command)
{
	switch (command)
	{
		case 0x99: // Reset?
			asic27a_sim_key = 0;
			asic27a_sim_response = 0x890000 | (PgmInput[7] << 8);
		break;

		case 0xb1:
			asic27a_sim_response = 0x890000;
		break;

		case 0xbf:
			asic27a_sim_response = asic27a_sim_regs[0xb1] * asic27a_sim_value;
		break;

		case 0xc1: // TODO: TIMER  0,1,2,FIX TO 0 should be OK?
			asic27a_sim_response = 0;
		break;

		case 0xce: // TODO: TIMER  0,1,2
			asic27a_sim_response = 0x890000;
		break;

		case 0xcf: // TODO:TIMER  0,1,2
			asic27a_sim_slots[asic27a_sim_regs[0xce] + 0x10] = asic27a_sim_value;
			asic27a_sim_response = 0x890000;
		break;

		case 0xd0: // Text palette offset
			asic27a_sim_response = 0xa01000 + (asic27a_sim_value << 5);
		break;

		case 0xdc: // Background palette offset
			asic27a_sim_response = 0xa00800 + (asic27a_sim_value << 6);
		break;

		case 0xe0: // Sprite palette offset
			asic27a_sim_response = 0xa00000 + (asic27a_sim_value << 6);
		break;

		case 0xe5: // Write slot (low)
		{
			asic27a_sim_response = 0x890000;

			asic27a_sim_slots[asic27a_sim_internal_slot] = (asic27a_sim_slots[asic27a_sim_internal_slot] & 0xff0000) | (asic27a_sim_value);
		}
		break;

		case 0xe7: // Write slot (and slot select) (high)
		{
			asic27a_sim_response = 0x890000;

			asic27a_sim_internal_slot = (asic27a_sim_value >> 12) & 0xf;
			asic27a_sim_slots[asic27a_sim_internal_slot] = (asic27a_sim_slots[asic27a_sim_internal_slot] & 0x00ffff) | ((asic27a_sim_value & 0xff) << 16);
		}
		break;

		case 0xf8: // Read slot
			asic27a_sim_response = asic27a_sim_slots[asic27a_sim_value];
		break;

		case 0x80: // Read from data table
	    		asic27a_sim_response = Pstar_80[asic27a_sim_value];
	   	break;

		case 0x8c: // Read from data table
	    		asic27a_sim_response = Pstar_8c[asic27a_sim_value];
	   	break;

		case 0x90: // Read from data table
	    		asic27a_sim_response = Pstar_90[asic27a_sim_value];
	   	break;

		case 0x9d: // Read from data table
	    		asic27a_sim_response = Pstar_9d[asic27a_sim_value];
	   	break;

		case 0xa0: // Read from data table
	    		asic27a_sim_response = Pstar_a0[asic27a_sim_value];
	   	break;

		case 0xae: // Read from data table
	    		asic27a_sim_response = Pstar_ae[asic27a_sim_value];
	   	break;

		case 0xb0: // Read from data table
	    		asic27a_sim_response = Pstar_b0[asic27a_sim_value];
	   	break;

		case 0xba: // Read from data table
	    		asic27a_sim_response = Pstar_ba[asic27a_sim_value];
	   	break;

		default:
			asic27a_sim_response = 0x890000;
		break;
	}
}

void install_protection_asic27a_puzlstar()
{
	pPgmResetCallback = asic27a_sim_reset;
	pPgmScanCallback = asic27a_sim_scan;

	asic27a_sim_command = puzlstar_asic27a_sim_command;

	SekOpen(0);
	SekMapHandler(4,		0x500000, 0x500003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	asic27a_sim_read);
	SekSetWriteWordHandler(4, 	asic27a_sim_write);

	SekMapHandler(5,		0x4f0000, 0x4f03ff, SM_READ);
	SekSetReadWordHandler(5, 	puzlstar_protram_read_word);
	SekSetReadByteHandler(5, 	puzlstar_protram_read_byte);
	SekClose();
}


//--------------------
// puzzli2

static int stage;
static int tableoffs;
static int tableoffs2;
static int entries_left;
static int currentcolumn;
static int currentrow;
static int num_entries;
static int full_entry;
static int prev_tablloc;
static int numbercolumns;
static int depth;
static UINT16 m_row_bitmask;
static int hackcount;
static int hackcount2;
static int hack_47_value;
static int hack_31_table_offset;
static int p2_31_retcounter;
static int command_31_write_type;
static UINT16 level_structure[8][10];

static const UINT8 puzzli2_level_decode[256] = {
	0x32, 0x3e, 0xb2, 0x37, 0x31, 0x22, 0xd6, 0x0d, 0x35, 0x5c, 0x8d, 0x3c, 0x7a, 0x5f, 0xd7, 0xac, // 0x0
	0x53, 0xff, 0xeb, 0x44, 0xe8, 0x11, 0x69, 0x77, 0xd9, 0x34, 0x36, 0x45, 0xa6, 0xe9, 0x1c, 0xc6, // 0x1
	0x3b, 0xbd, 0xad, 0x2e, 0x18, 0xdf, 0xa1, 0xab, 0xdd, 0x52, 0x57, 0xc2, 0xe5, 0x0a, 0x00, 0x6d, // 0x2
 	0x67, 0x64, 0x15, 0x70, 0xb6, 0x39, 0x27, 0x78, 0x82, 0xd2, 0x71, 0xb9, 0x13, 0xf5, 0x93, 0x92, // 0x3
	0xfa, 0xe7, 0x5e, 0xb0, 0xf6, 0xaf, 0x95, 0x8a, 0x7c, 0x73, 0xf9, 0x63, 0x86, 0xcb, 0x1a, 0x56, // 0x4
	0xf1, 0x3a, 0xae, 0x61, 0x01, 0x29, 0x97, 0x23, 0x8e, 0x5d, 0x9a, 0x65, 0x74, 0x21, 0x20, 0x40, // 0x5
	0xd3, 0x05, 0xa2, 0xe1, 0xbc, 0x9e, 0x1e, 0x10, 0x14, 0x0c, 0x88, 0x9c, 0xec, 0x38, 0xb5, 0x9d, // 0x6
	0x2d, 0xf7, 0x17, 0x0e, 0x84, 0xc7, 0x7d, 0xce, 0x94, 0x16, 0x48, 0xa8, 0x81, 0x6e, 0x7b, 0xd8, // 0x7
	0xa7, 0x7f, 0x42, 0xe6, 0xa0, 0x2a, 0xef, 0xee, 0x24, 0xba, 0xb8, 0x7e, 0xc9, 0x2b, 0x90, 0xcc, // 0x8
	0x5b, 0xd1, 0xf3, 0xe2, 0x6f, 0xed, 0x9f, 0xf0, 0x4b, 0x54, 0x8c, 0x08, 0xf8, 0x51, 0x68, 0xc8, // 0x9
	0x03, 0x0b, 0xbb, 0xc1, 0xe3, 0x4d, 0x04, 0xc5, 0x8f, 0x09, 0x0f, 0xbf, 0x62, 0x49, 0x76, 0x59, // 0xa
	0x1d, 0x80, 0xde, 0x60, 0x07, 0xe0, 0x1b, 0x66, 0xa5, 0xbe, 0xcd, 0x87, 0xdc, 0xc3, 0x6b, 0x4e, // 0xb
	0xd0, 0xfd, 0xd4, 0x3f, 0x98, 0x96, 0x2f, 0x4c, 0xb3, 0xea, 0x2c, 0x75, 0xe4, 0xc0, 0x6c, 0x6a, // 0xc
	0x9b, 0xb7, 0x43, 0x8b, 0x41, 0x47, 0x02, 0xdb, 0x99, 0x3d, 0xa3, 0x79, 0x50, 0x4f, 0xb4, 0x55, // 0xd
	0x5a, 0x25, 0xf4, 0xca, 0x58, 0x30, 0xc4, 0x12, 0xa9, 0x46, 0xda, 0x91, 0xa4, 0xaa, 0xfc, 0x85, // 0xe
	0xfb, 0x89, 0x06, 0xcf, 0xfe, 0x33, 0xd5, 0x28, 0x1f, 0x19, 0x4a, 0xb1, 0x83, 0xf2, 0x72, 0x26, // 0xf
};

static int get_position_of_bit(UINT16 value, int bit_wanted)
{
	int count = 0;
	for (int i=0;i<16;i++)
	{
		int bit = (value >> i) & 1;

		if (bit) count++;

		if (count==(bit_wanted+1))
			return i;
	}

	return -1;
}

static int puzzli2_take_leveldata_value(UINT8 datvalue)
{
	if (stage==-1)
	{
		tableoffs = 0;
		tableoffs2 = 0;
		entries_left = 0;
		currentcolumn = 0;
		currentrow = 0;
		num_entries = 0;
		full_entry = 0;
		prev_tablloc = 0;
		numbercolumns = 0;
		depth = 0;
		m_row_bitmask = 0;

		tableoffs = datvalue;
		tableoffs2 = 0;
		stage = 0;
	}
	else
	{
		UINT8 rawvalue = datvalue;
		UINT8 tableloc = (tableoffs+tableoffs2)&0xff;
		rawvalue ^= puzzli2_level_decode[tableloc];

		tableoffs2++;
		tableoffs2&=0xf;

		if (stage==0)
		{
			stage = 1;
			depth = (rawvalue & 0xf0);
			numbercolumns = (rawvalue & 0x0f);
			numbercolumns++;
		}
		else if (stage==1)
		{
			stage = 2;
			entries_left = (rawvalue >> 4);
			m_row_bitmask = (rawvalue & 0x0f)<<8;
			full_entry = rawvalue;
			prev_tablloc = tableloc;
			num_entries = entries_left;
		}
		else if (stage==2)
		{	
			stage = 3;
			m_row_bitmask |= rawvalue;

			if (entries_left == 0)
			{
				stage = 1;
				currentcolumn++;
				currentrow = 0;
				m_row_bitmask = 0;

				if (currentcolumn==numbercolumns)
				{
					return 1;
				}
			}
			else
			{
				int num = (1 << ((num > 8) ? 8 : num)) - 1;
			}
		}
		else if (stage==3)
		{
			UINT16 object_value;

			     if (rawvalue<=0x10) object_value = 0x0100 + rawvalue; 
			else if (rawvalue<=0x21) object_value = 0x0120 + (rawvalue - 0x11); 
			else if (rawvalue<=0x32) object_value = 0x0140 + (rawvalue - 0x22); 
			else if (rawvalue<=0x43) object_value = 0x0180 + (rawvalue - 0x33); 
			else if (rawvalue==0xd0) object_value = 0x0200;
			else if (rawvalue==0xe0) object_value = 0x8000;
			else if (rawvalue==0xe1) object_value = 0x8020; // solid slant top down
			else if (rawvalue==0xe2) object_value = 0x8040; // solid slant top up
			else if (rawvalue==0xe3) object_value = 0x8060;
			else if (rawvalue==0xe4) object_value = 0x8080; // sold slant bottom up
			else                     object_value = 0x0110;

			int realrow = get_position_of_bit(m_row_bitmask, currentrow);

			if (realrow != -1)
				level_structure[currentcolumn][realrow] = object_value;

			currentrow++;

			entries_left--;
			if (entries_left == 0)
			{
				stage = 1;
				currentcolumn++;
				currentrow = 0;
				m_row_bitmask = 0;

				if (currentcolumn==numbercolumns)
				{
					return 1;
				}
			}
		}
	}

	return 0;
}
	
static void puzzli2_asic27a_sim_command(UINT8 command)
{
	switch (command)
	{
		case 0x31:
		{
			if (command_31_write_type==2)
			{
				if (hackcount2==0)
				{
					puzzli2_take_leveldata_value(asic27a_sim_value&0xff);

					hack_31_table_offset = asic27a_sim_value & 0xff;
					hackcount2++;
					asic27a_sim_response = 0x00d20000;
				}
				else
				{
					int end = puzzli2_take_leveldata_value(asic27a_sim_value&0xff);

					if (!end)
					{
						asic27a_sim_response = 0x00d20000;
						hackcount2++;
					}
					else
					{
						hackcount2=0;
						asic27a_sim_response = 0x00630000 | numbercolumns;
					}
				}
			}
			else
			{
				asic27a_sim_response = 0x00d20000 | p2_31_retcounter;
				p2_31_retcounter++;
			}
		}
		break;

		case 0x13:
		{
			UINT16* leveldata = &level_structure[0][0];
			if (hackcount==0)
			{
				asic27a_sim_response = 0x002d0000 | ((depth>>4)+1);
			}
			else if (hackcount<((10*numbercolumns)+1))
			{
				asic27a_sim_response = 0x002d0000 | leveldata[hackcount-1];
			}
			else
			{
				hackcount=0;
				asic27a_sim_response = 0x00740054;
			}

			hackcount++;
		}
		break;

		case 0x38: // Reset
		{
			asic27a_sim_response = 0x780000 | (PgmInput[7] << 8);
			asic27a_sim_key = 0x100;
		}	
		break;

		case 0x47:
			hack_47_value = asic27a_sim_value;
			asic27a_sim_response = 0x00740047;
		break;

		case 0x52:
		{
			int val = ((hack_47_value & 0x0f00)>>8) * 0x19;
			if (asic27a_sim_value!=0x0000)
			{
				val +=((hack_47_value & 0x000f)>>0) * 0x05;
			}
			val += asic27a_sim_value & 0x000f;
			asic27a_sim_response = 0x00740000 | (val & 0xffff);
		}
		break;

		case 0x61:
			command_31_write_type = 1;
			asic27a_sim_response = 0x360000;
			p2_31_retcounter = 0xc;
		break;

		case 0x41: // ASIC status?
			command_31_write_type = 0;
			asic27a_sim_response = 0x740061;
		break;

		case 0x54: // ??
		{
			command_31_write_type = 2;
			stage = -1;
			hackcount2 = 0;
			hackcount = 0;
			asic27a_sim_response = 0x360000;
			
			//  clear the return structure
			for (int columns=0;columns<8;columns++)
				for (int rows=0;rows<10;rows++)
					level_structure[columns][rows] = 0x0000;
		}
		break;

		case 0x63:
		{
			UINT32 z80table[2][8] = {
				{ 0x1694a8, 0x16cfae, 0x16ebf2, 0x16faa8, 0x174416, 0x600000, 0x600000, 0x600000 }, // puzzli2
				{ 0x19027a, 0x193D80, 0x1959c4, 0x19687a, 0x19b1e8, 0x600000, 0x600000, 0x600000 }  // puzzli2s
			};

			if (!strcmp(BurnDrvGetTextA(DRV_NAME),"puzzli2"))
			{
				asic27a_sim_response = z80table[0][asic27a_sim_value & 7];
			}
			else {// puzzli2 super
				asic27a_sim_response = z80table[1][asic27a_sim_value & 7];
			}
		}
		break;

		case 0x67:
		{
			UINT32 z80table[2][8] = {
				{ 0x166178, 0x166178, 0x166178, 0x166178, 0x166e72, 0x600000, 0x600000, 0x600000 }, // puzzli2
				{ 0x18cf4a, 0x18cf4a, 0x18cf4a, 0x18cf4a, 0x18dc44, 0x600000, 0x600000, 0x600000 } // puzzli2s
			};

			if (!strcmp(BurnDrvGetTextA(DRV_NAME),"puzzli2"))
			{
				asic27a_sim_response = z80table[0][asic27a_sim_value & 7];
			}
			else {// puzzli2 super
				asic27a_sim_response = z80table[1][asic27a_sim_value & 7];
			}
		}
		break;

		default:
			asic27a_sim_response = 0x740000;
		break;
	}
}

void install_protection_asic27a_puzzli2()
{
	pPgmResetCallback = asic27a_sim_reset;
	pPgmScanCallback = asic27a_sim_scan;
	asic27a_sim_command = puzzli2_asic27a_sim_command;

	SekOpen(0);
	SekMapMemory(PGMUSER0,		0x4f0000, 0x4f003f | 0x3ff, SM_READ);

	SekMapHandler(4,		0x500000, 0x500003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	asic27a_sim_read);
	SekSetWriteWordHandler(4, 	asic27a_sim_write);
	SekClose();
}


//-------------------------------------------------------------------------------------------
// py2k2

static void py2k2_asic27a_sim_command(UINT8 command)
{
	switch (command)
	{
		case 0x99: // Reset?
			asic27a_sim_key = 0x100;
			asic27a_sim_response = 0x880000 | (PgmInput[7] << 8);
		break;

		case 0x38: // ?
			asic27a_sim_response = 0x880000;
		break;

		case 0xc0:
			asic27a_sim_response = 0x880000;
		break;

		case 0xc3:
			asic27a_sim_response = 0x904000 + ((asic27a_sim_regs[0xc0] + (asic27a_sim_value * 0x40)) * 4);
		break;

		case 0xd0:
			asic27a_sim_response = 0xa01000 + (asic27a_sim_value * 0x20);
		break;

		case 0xdc:
			asic27a_sim_response = 0xa00800 + (asic27a_sim_value * 0x40);
		break;

		case 0xe0:
			asic27a_sim_response = 0xa00000 + ((asic27a_sim_value & 0x1f) * 0x40);
		break;

		case 0xcb: // Background layer 'x' select (pgm3in1, same as kov)
			asic27a_sim_response = 0x880000;
		break;

		case 0xcc: // Background layer offset (pgm3in1, same as kov)
		{
			INT32 y = asic27a_sim_value;
			if (y & 0x400) y = -(0x400 - (y & 0x3ff));
			asic27a_sim_response = 0x900000 + ((asic27a_sim_regs[0xcb] + (y * 0x40)) * 4);
		}
		break;

	//	case 0x32: // ?
	//	break;

	//	case 0xba: // almost definitely a table...
	//	break;

		default:
			asic27a_sim_response = 0x880000;
			bprintf (0, _T("Uknown ASIC Command %2.2x Value: %4.4x\n"), command, asic27a_sim_value);
		break;
	}
}

void install_protection_asic27a_py2k2()
{
	pPgmResetCallback = asic27a_sim_reset;
	pPgmScanCallback = asic27a_sim_scan;
	asic27a_sim_command = py2k2_asic27a_sim_command;

	SekOpen(0);
	SekMapMemory(PGMUSER0,		0x4f0000, 0x4f003f | 0x3ff, SM_READ);

	SekMapHandler(4,		0x500000, 0x500003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4, 	asic27a_sim_read);
	SekSetWriteWordHandler(4, 	asic27a_sim_write);
	SekClose();
}


//--------------------------
// drgw2

static const UINT8 drgw2_source_data[0x08][0xec] =
{
	{ 0, }, // Region 0, not used
	{   // Region 1, $13A886
		0x67, 0x51, 0xF3, 0x19, 0xA0, 0x11, 0xB1, 0x11, 0xB0, 0xEE, 0xE3, 0xF6, 0xBE, 0x81, 0x35, 0xE3,
		0xFB, 0xE6, 0xEF, 0xDF, 0x61, 0x01, 0xFA, 0x22, 0x5D, 0x43, 0x01, 0xA5, 0x3B, 0x17, 0xD4, 0x74,
		0xF0, 0xF4, 0xF3, 0x43, 0xB5, 0x19, 0x04, 0xD5, 0x84, 0xCE, 0x87, 0xFE, 0x35, 0x3E, 0xC4, 0x3C,
		0xC7, 0x85, 0x2A, 0x33, 0x00, 0x86, 0xD0, 0x4D, 0x65, 0x4B, 0xF9, 0xE9, 0xC0, 0xBA, 0xAA, 0x77,
		0x9E, 0x66, 0xF6, 0x0F, 0x4F, 0x3A, 0xB6, 0xF1, 0x64, 0x9A, 0xE9, 0x25, 0x1A, 0x5F, 0x22, 0xA3,
		0xA2, 0xBF, 0x4B, 0x77, 0x3F, 0x34, 0xC9, 0x6E, 0xDB, 0x12, 0x5C, 0x33, 0xA5, 0x8B, 0x6C, 0xB1,
		0x74, 0xC8, 0x40, 0x4E, 0x2F, 0xE7, 0x46, 0xAE, 0x99, 0xFC, 0xB0, 0x55, 0x54, 0xDF, 0xA7, 0xA1,
		0x0F, 0x5E, 0x49, 0xCF, 0x56, 0x3C, 0x90, 0x2B, 0xAC, 0x65, 0x6E, 0xDB, 0x58, 0x3E, 0xC9, 0x00,
		0xAE, 0x53, 0x4D, 0x92, 0xFA, 0x40, 0xB2, 0x6B, 0x65, 0x4B, 0x90, 0x8A, 0x0C, 0xE2, 0xA5, 0x9A,
		0xD0, 0x20, 0x29, 0x55, 0xA4, 0x44, 0xAC, 0x51, 0x87, 0x54, 0x53, 0x34, 0x24, 0x4B, 0x81, 0x67,
		0x34, 0x4C, 0x5F, 0x31, 0x4E, 0xF2, 0xF1, 0x19, 0x18, 0x1C, 0x34, 0x38, 0xE1, 0x81, 0x17, 0xCF,
		0x24, 0xB9, 0x9A, 0xCB, 0x34, 0x51, 0x50, 0x59, 0x44, 0xB1, 0x0B, 0x50, 0x95, 0x6C, 0x48, 0x7E,
		0x14, 0xA4, 0xC6, 0xD9, 0xD3, 0xA5, 0xD6, 0xD0, 0xC5, 0x97, 0xF0, 0x45, 0xD0, 0x98, 0x51, 0x91,
		0x9F, 0xA3, 0x43, 0x51, 0x05, 0x90, 0xEE, 0xCA, 0x7E, 0x5F, 0x72, 0x53, 0xB1, 0xD3, 0xAF, 0x36,
		0x08, 0x75, 0xB0, 0x9B, 0xE0, 0x0D, 0x43, 0x88, 0xAA, 0x27, 0x44, 0x11
	},
	{ 0, }, // Region 2, not used
	{ 0, }, // Region 3, not used
	{ 0, }, // Region 4, not used
	{   // Region 5, $13ab42 (drgw2c)
		0x7F, 0x41, 0xF3, 0x39, 0xA0, 0x11, 0xA1, 0x11, 0xB0, 0xA2, 0x4C, 0x23, 0x13, 0xE9, 0x25, 0x3D,
		0x0F, 0x72, 0x3A, 0x9D, 0xB5, 0x96, 0xD1, 0xDA, 0x07, 0x29, 0x41, 0x9A, 0xAD, 0x70, 0xBA, 0x46,
		0x63, 0x2B, 0x7F, 0x3D, 0xBE, 0x40, 0xAD, 0xD4, 0x4C, 0x73, 0x27, 0x58, 0xA7, 0x65, 0xDC, 0xD6,
		0xFD, 0xDE, 0xB5, 0x6E, 0xD6, 0x6C, 0x75, 0x1A, 0x32, 0x45, 0xD5, 0xE3, 0x6A, 0x14, 0x6D, 0x80,
		0x84, 0x15, 0xAF, 0xCC, 0x7B, 0x61, 0x51, 0x82, 0x40, 0x53, 0x7F, 0x38, 0xA0, 0xD6, 0x8F, 0x61,
		0x79, 0x19, 0xE5, 0x99, 0x84, 0xD8, 0x78, 0x27, 0x3F, 0x16, 0x97, 0x78, 0x4F, 0x7B, 0x0C, 0xA6,
		0x37, 0xDB, 0xC6, 0x0C, 0x24, 0xB4, 0xC7, 0x94, 0x9D, 0x92, 0xD2, 0x3B, 0xD5, 0x11, 0x6F, 0x0A,
		0xDB, 0x76, 0x66, 0xE7, 0xCD, 0x18, 0x2B, 0x66, 0xD8, 0x41, 0x40, 0x58, 0xA2, 0x01, 0x1E, 0x6D,
		0x44, 0x75, 0xE7, 0x19, 0x4F, 0xB2, 0xE8, 0xC4, 0x96, 0x77, 0x62, 0x02, 0xC9, 0xDC, 0x59, 0xF3,
		0x43, 0x8D, 0xC8, 0xFE, 0x9E, 0x2A, 0xBA, 0x32, 0x3B, 0x62, 0xE3, 0x92, 0x6E, 0xC2, 0x08, 0x4D,
		0x51, 0xCD, 0xF9, 0x3A, 0x3E, 0xC9, 0x50, 0x27, 0x21, 0x25, 0x97, 0xD7, 0x0E, 0xF8, 0x39, 0x38,
		0xF5, 0x86, 0x94, 0x93, 0xBF, 0xEB, 0x18, 0xA8, 0xFC, 0x24, 0xF5, 0xF9, 0x99, 0x20, 0x3D, 0xCD,
		0x2C, 0x94, 0x25, 0x79, 0x28, 0x77, 0x8F, 0x2F, 0x10, 0x69, 0x86, 0x30, 0x43, 0x01, 0xD7, 0x9A,
		0x17, 0xE3, 0x47, 0x37, 0xBD, 0x62, 0x75, 0x42, 0x78, 0xF4, 0x2B, 0x57, 0x4C, 0x0A, 0xDB, 0x53,
		0x4D, 0xA1, 0x0A, 0xD6, 0x3A, 0x16, 0x15, 0xAA, 0x2C, 0x6C, 0x39, 0x42
	},
	{   // Region 6, $13ab42 (drgw2), $13ab2e (dw2v100x)
		0x12, 0x09, 0xF3, 0x29, 0xA0, 0x11, 0xA0, 0x11, 0xB0, 0xD5, 0x66, 0xA1, 0x28, 0x4A, 0x21, 0xC0,
		0xD3, 0x9B, 0x86, 0x80, 0x57, 0x6F, 0x41, 0xC2, 0xE4, 0x2F, 0x0B, 0x91, 0xBD, 0x3A, 0x7A, 0xBA,
		0x00, 0xE5, 0x35, 0x02, 0x74, 0x7D, 0x8B, 0x21, 0x57, 0x10, 0x0F, 0xAE, 0x44, 0xBB, 0xE2, 0x37,
		0x18, 0x7B, 0x52, 0x3D, 0x8C, 0x59, 0x9E, 0x20, 0x1F, 0x0A, 0xCC, 0x1C, 0x8E, 0x6A, 0xD7, 0x95,
		0x2B, 0x34, 0xB0, 0x82, 0x6D, 0xFD, 0x25, 0x33, 0xAA, 0x3B, 0x2B, 0x70, 0x15, 0x87, 0x31, 0x5D,
		0xBB, 0x29, 0x19, 0x95, 0xD5, 0x8E, 0x24, 0x28, 0x5E, 0xD0, 0x20, 0x83, 0x46, 0x4A, 0x21, 0x70,
		0x5B, 0xCD, 0xAE, 0x7B, 0x61, 0xA1, 0xFA, 0xF4, 0x2B, 0x84, 0x15, 0x6E, 0x36, 0x5D, 0x1B, 0x24,
		0x0F, 0x09, 0x3A, 0x61, 0x38, 0x0F, 0x18, 0x35, 0x11, 0x38, 0xB4, 0xBD, 0xEE, 0xF7, 0xEC, 0x0F,
		0x1D, 0xB7, 0x48, 0x01, 0xAA, 0x09, 0x8F, 0x61, 0xB5, 0x0F, 0x1D, 0x26, 0x39, 0x2E, 0x8C, 0xD6,
		0x26, 0x5C, 0x3D, 0x23, 0x63, 0xE9, 0x6B, 0x97, 0xB4, 0x9F, 0x7B, 0xB6, 0xBA, 0xA0, 0x7C, 0xC6,
		0x25, 0xA1, 0x73, 0x36, 0x67, 0x7F, 0x74, 0x1E, 0x1D, 0xDA, 0x70, 0xBF, 0xA5, 0x63, 0x35, 0x39,
		0x24, 0x8C, 0x9F, 0x85, 0x16, 0xD8, 0x50, 0x95, 0x71, 0xC0, 0xF6, 0x1E, 0x6D, 0x80, 0xED, 0x15,
		0xEB, 0x63, 0xE9, 0x1B, 0xF6, 0x78, 0x31, 0xC6, 0x5C, 0xDD, 0x19, 0xBD, 0xDF, 0xA7, 0xEC, 0x50,
		0x22, 0xAD, 0xBB, 0xF6, 0xEB, 0xD6, 0xA3, 0x20, 0xC9, 0xE6, 0x9F, 0xCB, 0xF2, 0x97, 0xB9, 0x54,
		0x12, 0x66, 0xA6, 0xBE, 0x4A, 0x12, 0x43, 0xEC, 0x00, 0xEA, 0x49, 0x02
	},
	{ 0, }  // Region 7, not used
};

static UINT16 m_drgw2_prot_hold;
static UINT16 m_drgw2_prot_hilo;
static UINT16 m_drgw2_prot_hilo_select;
static UINT16 m_drgw2_ptr;
static UINT16 m_drgw2_cmd;
static UINT32 m_drgw2_protection_region;

static void drgw2_protection_calculate_hold(int y, int z)
{
	unsigned short old = m_drgw2_prot_hold;

	m_drgw2_prot_hold = ((old << 1) | (old >> 15));

	m_drgw2_prot_hold ^= 0x2bad;
	m_drgw2_prot_hold ^= BIT(z, y);
	m_drgw2_prot_hold ^= BIT( old,  7) <<  0;
	m_drgw2_prot_hold ^= BIT(~old, 13) <<  4;
	m_drgw2_prot_hold ^= BIT( old,  3) << 11;

	m_drgw2_prot_hold ^= (m_drgw2_prot_hilo & ~0x0408) << 1;
}

static void drgw2_protection_calculate_hilo()
{
	UINT8 source;

	m_drgw2_prot_hilo_select++;
	if (m_drgw2_prot_hilo_select > 0xeb) {
		m_drgw2_prot_hilo_select = 0;
	}

	source = drgw2_source_data[m_drgw2_protection_region][m_drgw2_prot_hilo_select];

	if (m_drgw2_prot_hilo_select & 1)
	{
		m_drgw2_prot_hilo = (m_drgw2_prot_hilo & 0x00ff) | (source << 8);
	}
	else
	{
		m_drgw2_prot_hilo = (m_drgw2_prot_hilo & 0xff00) | (source << 0);
	}
}

static UINT16 __fastcall drgw2_d80000_protection_r(UINT32 )
{
	switch (m_drgw2_cmd)
	{
		case 0x05:
		{
			switch (m_drgw2_ptr)
			{
				case 1: return 0x3f00 | ((m_drgw2_protection_region >> 0) & 0xff);

				case 2:
					return 0x3f00 | ((m_drgw2_protection_region >> 8) & 0xff);

				case 3:
					return 0x3f00 | ((m_drgw2_protection_region >> 16) & 0xff);

				case 4:
					return 0x3f00 | ((m_drgw2_protection_region >> 24) & 0xff);

				case 5:
				default:
					return 0x3f00 | BITSWAP08(m_drgw2_prot_hold, 5,2,9,7,10,13,12,15);
			}

			return 0x3f00;
		}

		case 0x40:
			drgw2_protection_calculate_hilo();
			return 0;
	}

	return 0;
}

static void __fastcall drgw2_d80000_protection_w(UINT32 offset, UINT16 data)
{
	offset &= 2;

	if (offset == 0)
	{
		m_drgw2_cmd = data;
		return;
	}

	switch (m_drgw2_cmd)
	{
		case 0x20:
		case 0x21:
		case 0x22:
		case 0x23:
		case 0x24:
		case 0x25:
		case 0x26:
		case 0x27:
			m_drgw2_ptr++;
			drgw2_protection_calculate_hold(m_drgw2_cmd & 0x0f, data & 0xff);
		break;
	}
}

static INT32 dw2Scan(INT32 nAction, INT32 *)
{
	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(m_drgw2_prot_hold);
		SCAN_VAR(m_drgw2_prot_hilo);
		SCAN_VAR(m_drgw2_prot_hilo_select);
		SCAN_VAR(m_drgw2_ptr);
		SCAN_VAR(m_drgw2_cmd);
	}

	return 0;
}

static void reset_asic25_asic12_dw2()
{
	m_drgw2_prot_hold = 0;
	m_drgw2_prot_hilo = 0;
	m_drgw2_prot_hilo_select = 0;
	m_drgw2_ptr = 0;
	m_drgw2_cmd = 0;
}

void install_protection_asic25_asic12_dw2()
{
	if (strcmp(BurnDrvGetTextA(DRV_NAME), "drgw2")    == 0) m_drgw2_protection_region = 6;
	if (strcmp(BurnDrvGetTextA(DRV_NAME), "dw2v100x") == 0) m_drgw2_protection_region = 6;
	if (strcmp(BurnDrvGetTextA(DRV_NAME), "drgw2c")   == 0) m_drgw2_protection_region = 5;
	if (strcmp(BurnDrvGetTextA(DRV_NAME), "drgw2j")   == 0) m_drgw2_protection_region = 1;

	pPgmScanCallback = dw2Scan;
	pPgmResetCallback = reset_asic25_asic12_dw2;

	SekOpen(0);
	SekMapHandler(4,		0xd80000, 0xd80003, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4,	drgw2_d80000_protection_r);
	SekSetWriteWordHandler(4,	drgw2_d80000_protection_w);
	SekClose();
}


//-------------------------
// olds

static int           m_olds_cmd;
static int           m_olds_reg;
static int           m_olds_ptr;
static UINT16        m_olds_bs;
static UINT16        m_olds_cmd3;
static UINT16        m_olds_prot_hold;
static UINT16        m_olds_prot_hilo;
static UINT16        m_olds_prot_hilo_select;

static const UINT8 olds_source_data[7][0xec] = // table addresses $2951CA
{
	{ // region 1, $1A669A
		0x67, 0x51, 0xf3, 0x19, 0xa0, 0x11, 0xe1, 0x11, 0x10, 0xee, 0xe3, 0xf6, 0xbe, 0x81, 0x35, 0xe3,
		0xfb, 0xe6, 0xef, 0xdf, 0x61, 0x01, 0xfa, 0x22, 0x5d, 0x43, 0x01, 0xa5, 0x3b, 0x17, 0xd4, 0x74,
		0xf0, 0xf4, 0xf3, 0x43, 0xb5, 0x19, 0x04, 0xd5, 0x84, 0xce, 0x87, 0xfe, 0x35, 0x3e, 0xc4, 0x3c,
		0xc7, 0x85, 0x2a, 0x33, 0x00, 0x86, 0xd0, 0x4d, 0x65, 0x4b, 0xf9, 0xe9, 0xc0, 0xba, 0xaa, 0x77,
		0x9e, 0x66, 0xf6, 0x0f, 0x4f, 0x3a, 0xb6, 0xf1, 0x64, 0x9a, 0xe9, 0x25, 0x1a, 0x5f, 0x22, 0xa3,
		0xa2, 0xbf, 0x4b, 0x77, 0x3f, 0x34, 0xc9, 0x6e, 0xdb, 0x12, 0x5c, 0x33, 0xa5, 0x8b, 0x6c, 0xb1,
		0x74, 0xc8, 0x40, 0x4e, 0x2f, 0xe7, 0x46, 0xae, 0x99, 0xfc, 0xb0, 0x55, 0x54, 0xdf, 0xa7, 0xa1,
		0x0f, 0x5e, 0x49, 0xcf, 0x56, 0x3c, 0x90, 0x2b, 0xac, 0x65, 0x6e, 0xdb, 0x58, 0x3e, 0xc9, 0x00,
		0xae, 0x53, 0x4d, 0x92, 0xfa, 0x40, 0xb2, 0x6b, 0x65, 0x4b, 0x90, 0x8a, 0x0c, 0xe2, 0xa5, 0x9a,
		0xd0, 0x20, 0x29, 0x55, 0xa4, 0x44, 0xac, 0x51, 0x87, 0x54, 0x53, 0x34, 0x24, 0x4b, 0x81, 0x67,
		0x34, 0x4c, 0x5f, 0x31, 0x4e, 0xf2, 0xf1, 0x19, 0x18, 0x1c, 0x34, 0x38, 0xe1, 0x81, 0x17, 0xcf,
		0x24, 0xb9, 0x9a, 0xcb, 0x34, 0x51, 0x50, 0x59, 0x44, 0xb1, 0x0b, 0x50, 0x95, 0x6c, 0x48, 0x7e,
		0x14, 0xa4, 0xc6, 0xd9, 0xd3, 0xa5, 0xd6, 0xd0, 0xc5, 0x97, 0xf0, 0x45, 0xd0, 0x98, 0x51, 0x91,
		0x9f, 0xa3, 0x43, 0x51, 0x05, 0x90, 0xee, 0xca, 0x7e, 0x5f, 0x72, 0x53, 0xb1, 0xd3, 0xaf, 0x36,
		0x08, 0x75, 0xb0, 0x9b, 0xe0, 0x0d, 0x43, 0x88, 0xaa, 0x27, 0x44, 0x11
	},
	{ // region 2, $19A5F8
		0xf9, 0x19, 0xf3, 0x09, 0xa0, 0x11, 0xe0, 0x11, 0x10, 0x22, 0xfd, 0x8e, 0xd3, 0xc8, 0x31, 0x67,
		0xc0, 0x10, 0x3c, 0xc2, 0x03, 0xf2, 0x6a, 0x0a, 0x54, 0x49, 0xca, 0xb5, 0x4b, 0xe0, 0x94, 0xe8,
		0x8d, 0xc8, 0x90, 0xee, 0x6b, 0x6f, 0xfa, 0x09, 0x76, 0x84, 0x6f, 0x55, 0xd1, 0x94, 0xca, 0x9c,
		0xe1, 0x22, 0xc6, 0x02, 0xb5, 0x8c, 0xf9, 0x3a, 0x52, 0x10, 0xf0, 0x22, 0xe4, 0x11, 0x15, 0x73,
		0x5e, 0x9e, 0xde, 0xc4, 0x5a, 0xbd, 0xa3, 0x89, 0xe7, 0x9b, 0x95, 0x5d, 0x75, 0xf6, 0xc3, 0x9f,
		0xe4, 0xcf, 0x65, 0x73, 0x90, 0xd0, 0x75, 0x56, 0xfa, 0xcc, 0xe4, 0x3e, 0x9c, 0x41, 0x81, 0x62,
		0xb1, 0xd3, 0x28, 0xbd, 0x6c, 0xed, 0x60, 0x28, 0x27, 0xee, 0xf2, 0xa1, 0xb4, 0x2c, 0x6c, 0xbb,
		0x42, 0xd7, 0x1d, 0x62, 0xc0, 0x33, 0x7d, 0xf9, 0xe4, 0x5c, 0xe2, 0x41, 0xa4, 0x1c, 0x98, 0xa1,
		0x87, 0x95, 0xad, 0x61, 0x56, 0x96, 0x40, 0x08, 0x6b, 0xe2, 0x4b, 0x95, 0x7b, 0x1b, 0xd8, 0x64,
		0xb3, 0xee, 0x9d, 0x79, 0x69, 0xea, 0x5d, 0xcf, 0x01, 0x91, 0xea, 0x3f, 0x70, 0x29, 0xdc, 0xe0,
		0x08, 0x20, 0xbf, 0x46, 0x90, 0xa8, 0xfc, 0x29, 0x14, 0xd1, 0x0d, 0x20, 0x79, 0xd2, 0x2c, 0xe9,
		0x52, 0xa6, 0x8c, 0xbd, 0xa3, 0x3e, 0x88, 0x2d, 0xb8, 0x4e, 0xf2, 0x74, 0x50, 0xcc, 0x12, 0xde,
		0xd3, 0x5a, 0xa4, 0x7b, 0xa2, 0x8d, 0x91, 0x68, 0x12, 0x0c, 0x9c, 0xb9, 0x6d, 0x26, 0x66, 0x60,
		0xc3, 0x6d, 0xd0, 0x11, 0x33, 0x05, 0x1d, 0xa8, 0xb6, 0x51, 0xe6, 0xe0, 0x58, 0x61, 0x74, 0x37,
		0xcc, 0x3a, 0x4d, 0x6a, 0x0a, 0x09, 0x71, 0xe3, 0x7e, 0xa5, 0x3b, 0xe9
	},
	{ // region 3, $1F9508
		0x73, 0x59, 0xf3, 0x09, 0xa0, 0x11, 0xe1, 0x11, 0x10, 0x55, 0x18, 0x0d, 0xe8, 0x29, 0x2d, 0x04,
		0x85, 0x39, 0x88, 0xbe, 0x8b, 0xcb, 0xd9, 0x0b, 0x32, 0x36, 0x94, 0xac, 0x74, 0xc3, 0x3b, 0x5d,
		0x2a, 0x83, 0x46, 0xb3, 0x3a, 0xac, 0xd8, 0x55, 0x68, 0x21, 0x57, 0xab, 0x6e, 0xd1, 0xd0, 0xfc,
		0xe2, 0xbe, 0x63, 0xd0, 0x6b, 0x79, 0x23, 0x40, 0x58, 0xd4, 0xe7, 0x73, 0x22, 0x67, 0x7f, 0x88,
		0x05, 0xbd, 0xdf, 0x7a, 0x65, 0x41, 0x90, 0x3a, 0x52, 0x83, 0x28, 0xae, 0xe9, 0x8e, 0x65, 0x82,
		0x0e, 0xdf, 0x98, 0x88, 0xe1, 0x86, 0x21, 0x3e, 0x1a, 0x87, 0x6d, 0x62, 0x7a, 0xf6, 0xaf, 0x2c,
		0xd5, 0xc5, 0x10, 0x2d, 0xa9, 0xda, 0x93, 0xa1, 0x9b, 0xc7, 0x35, 0xd4, 0x15, 0x78, 0x18, 0xd5,
		0x75, 0x6a, 0xd7, 0xdb, 0x12, 0x2a, 0x6a, 0xc8, 0x36, 0x53, 0x57, 0xa6, 0xf0, 0x13, 0x67, 0x43,
		0x79, 0xf0, 0x0e, 0x49, 0xb1, 0xec, 0xcd, 0xa4, 0x8a, 0x61, 0x06, 0xb9, 0xea, 0x53, 0xf2, 0x47,
		0x7d, 0xd6, 0xf8, 0x9d, 0x2e, 0xaa, 0x27, 0x35, 0x61, 0xce, 0x9b, 0x63, 0xbc, 0x07, 0x51, 0x5a,
		0xc2, 0x0d, 0x39, 0x42, 0xd2, 0x5e, 0x21, 0x20, 0x10, 0xa0, 0xe5, 0x08, 0xf7, 0x3d, 0x28, 0x04,
		0x99, 0x93, 0x97, 0xaf, 0xf9, 0x12, 0xc0, 0x01, 0x2d, 0xea, 0xf3, 0x98, 0x0b, 0x46, 0xc2, 0x26,
		0x93, 0x10, 0x69, 0x1d, 0x71, 0x8e, 0x33, 0x00, 0x5e, 0x80, 0x2f, 0x47, 0x0a, 0xcc, 0x94, 0x16,
		0xe7, 0x37, 0x45, 0xd0, 0x61, 0x79, 0x32, 0x86, 0x08, 0x2a, 0x5b, 0x55, 0xfe, 0xee, 0x52, 0x38,
		0xaa, 0x18, 0xe9, 0x39, 0x1a, 0x1e, 0xb8, 0x26, 0x6b, 0x3d, 0x4b, 0xa9
	},
	{ // region 4, $1CA7B8
		0x06, 0x01, 0xf3, 0x39, 0xa0, 0x11, 0xf0, 0x11, 0x10, 0x6f, 0x32, 0x8b, 0xfd, 0x89, 0x29, 0xa0,
		0x4a, 0x62, 0xed, 0xa1, 0x2d, 0xa4, 0x49, 0xf2, 0x10, 0x3c, 0x77, 0xa3, 0x84, 0x8d, 0xfa, 0xd1,
		0xc6, 0x57, 0xe2, 0x78, 0xef, 0xe9, 0xb6, 0xa1, 0x5a, 0xbd, 0x3f, 0x02, 0x0b, 0x28, 0xd6, 0x76,
		0xfc, 0x5b, 0x19, 0x9f, 0x21, 0x66, 0x4c, 0x2d, 0x45, 0x99, 0xde, 0xab, 0x46, 0xbd, 0xe9, 0x84,
		0xc4, 0xdc, 0xc7, 0x30, 0x70, 0xdd, 0x64, 0xea, 0xbc, 0x6b, 0xd3, 0xe6, 0x45, 0x3f, 0x07, 0x7e,
		0x50, 0xef, 0xb2, 0x84, 0x33, 0x3c, 0xcc, 0x3f, 0x39, 0x5b, 0xf5, 0x6d, 0x71, 0xc5, 0xdd, 0xf5,
		0xf9, 0xd0, 0xf7, 0x9c, 0xe6, 0xc7, 0xad, 0x1b, 0x29, 0xb9, 0x90, 0x08, 0x75, 0xc4, 0xc3, 0xef,
		0xa8, 0xfc, 0xab, 0x55, 0x7c, 0x21, 0x57, 0x97, 0x87, 0x4a, 0xcb, 0x0c, 0x56, 0x0a, 0x4f, 0xcb,
		0x52, 0x33, 0x87, 0x31, 0xf3, 0x43, 0x5b, 0x41, 0x90, 0xf8, 0xc0, 0xdd, 0x5a, 0xa4, 0x26, 0x2a,
		0x60, 0xa5, 0x6d, 0xda, 0xf2, 0x6a, 0xf0, 0xb3, 0xda, 0x25, 0x33, 0x87, 0x22, 0xe4, 0xac, 0xd3,
		0x96, 0xe0, 0x99, 0x3e, 0xfb, 0x14, 0x45, 0x17, 0x25, 0x56, 0xbe, 0xef, 0x8f, 0x8e, 0x3d, 0x1e,
		0xc7, 0x99, 0xa2, 0xa1, 0x50, 0xfe, 0xdf, 0xd4, 0xa1, 0x87, 0xf4, 0xd5, 0xde, 0xa6, 0x8c, 0x6d,
		0x6c, 0xde, 0x47, 0xbe, 0x59, 0x8f, 0xd4, 0x97, 0xc3, 0xf4, 0xda, 0xbb, 0xa6, 0x73, 0xa9, 0xcb,
		0xf2, 0x01, 0xb9, 0x90, 0x8f, 0xed, 0x60, 0x64, 0x40, 0x1c, 0xb6, 0xc9, 0xa5, 0x7c, 0x17, 0x52,
		0x6f, 0xdc, 0x6d, 0x08, 0x2a, 0x1a, 0xe6, 0x68, 0x3f, 0xd4, 0x42, 0x69
	},
	{ // region 5, $1A19FA
		0x7f, 0x41, 0xf3, 0x39, 0xa0, 0x11, 0xf1, 0x11, 0x10, 0xa2, 0x4c, 0x23, 0x13, 0xe9, 0x25, 0x3d,
		0x0f, 0x72, 0x3a, 0x9d, 0xb5, 0x96, 0xd1, 0xda, 0x07, 0x29, 0x41, 0x9a, 0xad, 0x70, 0xba, 0x46,
		0x63, 0x2b, 0x7f, 0x3d, 0xbe, 0x40, 0xad, 0xd4, 0x4c, 0x73, 0x27, 0x58, 0xa7, 0x65, 0xdc, 0xd6,
		0xfd, 0xde, 0xb5, 0x6e, 0xd6, 0x6c, 0x75, 0x1a, 0x32, 0x45, 0xd5, 0xe3, 0x6a, 0x14, 0x6d, 0x80,
		0x84, 0x15, 0xaf, 0xcc, 0x7b, 0x61, 0x51, 0x82, 0x40, 0x53, 0x7f, 0x38, 0xa0, 0xd6, 0x8f, 0x61,
		0x79, 0x19, 0xe5, 0x99, 0x84, 0xd8, 0x78, 0x27, 0x3f, 0x16, 0x97, 0x78, 0x4f, 0x7b, 0x0c, 0xa6,
		0x37, 0xdb, 0xc6, 0x0c, 0x24, 0xb4, 0xc7, 0x94, 0x9d, 0x92, 0xd2, 0x3b, 0xd5, 0x11, 0x6f, 0x0a,
		0xdb, 0x76, 0x66, 0xe7, 0xcd, 0x18, 0x2b, 0x66, 0xd8, 0x41, 0x40, 0x58, 0xa2, 0x01, 0x1e, 0x6d,
		0x44, 0x75, 0xe7, 0x19, 0x4f, 0xb2, 0xe8, 0xc4, 0x96, 0x77, 0x62, 0x02, 0xc9, 0xdc, 0x59, 0xf3,
		0x43, 0x8d, 0xc8, 0xfe, 0x9e, 0x2a, 0xba, 0x32, 0x3b, 0x62, 0xe3, 0x92, 0x6e, 0xc2, 0x08, 0x4d,
		0x51, 0xcd, 0xf9, 0x3a, 0x3e, 0xc9, 0x50, 0x27, 0x21, 0x25, 0x97, 0xd7, 0x0e, 0xf8, 0x39, 0x38,
		0xf5, 0x86, 0x94, 0x93, 0xbf, 0xeb, 0x18, 0xa8, 0xfc, 0x24, 0xf5, 0xf9, 0x99, 0x20, 0x3d, 0xcd,
		0x2c, 0x94, 0x25, 0x79, 0x28, 0x77, 0x8f, 0x2f, 0x10, 0x69, 0x86, 0x30, 0x43, 0x01, 0xd7, 0x9a,
		0x17, 0xe3, 0x47, 0x37, 0xbd, 0x62, 0x75, 0x42, 0x78, 0xf4, 0x2b, 0x57, 0x4c, 0x0a, 0xdb, 0x53,
		0x4d, 0xa1, 0x0a, 0xd6, 0x3a, 0x16, 0x15, 0xaa, 0x2c, 0x6c, 0x39, 0x42
	},
	{ // region 6, $2937EA
		0x12, 0x09, 0xf3, 0x29, 0xa0, 0x11, 0xf0, 0x11, 0x10, 0xd5, 0x66, 0xa1, 0x28, 0x4a, 0x21, 0xc0,
		0xd3, 0x9b, 0x86, 0x80, 0x57, 0x6f, 0x41, 0xc2, 0xe4, 0x2f, 0x0b, 0x91, 0xbd, 0x3a, 0x7a, 0xba,
		0x00, 0xe5, 0x35, 0x02, 0x74, 0x7d, 0x8b, 0x21, 0x57, 0x10, 0x0f, 0xae, 0x44, 0xbb, 0xe2, 0x37,
		0x18, 0x7b, 0x52, 0x3d, 0x8c, 0x59, 0x9e, 0x20, 0x1f, 0x0a, 0xcc, 0x1c, 0x8e, 0x6a, 0xd7, 0x95,
		0x2b, 0x34, 0xb0, 0x82, 0x6d, 0xfd, 0x25, 0x33, 0xaa, 0x3b, 0x2b, 0x70, 0x15, 0x87, 0x31, 0x5d,
		0xbb, 0x29, 0x19, 0x95, 0xd5, 0x8e, 0x24, 0x28, 0x5e, 0xd0, 0x20, 0x83, 0x46, 0x4a, 0x21, 0x70,
		0x5b, 0xcd, 0xae, 0x7b, 0x61, 0xa1, 0xfa, 0xf4, 0x2b, 0x84, 0x15, 0x6e, 0x36, 0x5d, 0x1b, 0x24,
		0x0f, 0x09, 0x3a, 0x61, 0x38, 0x0f, 0x18, 0x35, 0x11, 0x38, 0xb4, 0xbd, 0xee, 0xf7, 0xec, 0x0f,
		0x1d, 0xb7, 0x48, 0x01, 0xaa, 0x09, 0x8f, 0x61, 0xb5, 0x0f, 0x1d, 0x26, 0x39, 0x2e, 0x8c, 0xd6,
		0x26, 0x5c, 0x3d, 0x23, 0x63, 0xe9, 0x6b, 0x97, 0xb4, 0x9f, 0x7b, 0xb6, 0xba, 0xa0, 0x7c, 0xc6,
		0x25, 0xa1, 0x73, 0x36, 0x67, 0x7f, 0x74, 0x1e, 0x1d, 0xda, 0x70, 0xbf, 0xa5, 0x63, 0x35, 0x39,
		0x24, 0x8c, 0x9f, 0x85, 0x16, 0xd8, 0x50, 0x95, 0x71, 0xc0, 0xf6, 0x1e, 0x6d, 0x80, 0xed, 0x15,
		0xeb, 0x63, 0xe9, 0x1b, 0xf6, 0x78, 0x31, 0xc6, 0x5c, 0xdd, 0x19, 0xbd, 0xdf, 0xa7, 0xec, 0x50,
		0x22, 0xad, 0xbb, 0xf6, 0xeb, 0xd6, 0xa3, 0x20, 0xc9, 0xe6, 0x9f, 0xcb, 0xf2, 0x97, 0xb9, 0x54,
		0x12, 0x66, 0xa6, 0xbe, 0x4a, 0x12, 0x43, 0xec, 0x00, 0xea, 0x49, 0x02
	},
	{ // region 7, $255E8C
		0xa4, 0x49, 0xf3, 0x29, 0xa0, 0x11, 0xf1, 0x11, 0x10, 0xef, 0x80, 0x20, 0x3d, 0xaa, 0x36, 0x5d,
		0x98, 0xc4, 0xd2, 0x63, 0xdf, 0x61, 0xb0, 0xc3, 0xc2, 0x35, 0xd4, 0x88, 0xe6, 0x1d, 0x3a, 0x2f,
		0x9c, 0xb9, 0xd1, 0xc6, 0x43, 0xba, 0x69, 0x6d, 0x49, 0xac, 0xdd, 0x05, 0xe0, 0xf8, 0xe8, 0x97,
		0x19, 0x18, 0x08, 0x0c, 0x42, 0x46, 0xc7, 0x0d, 0x25, 0xce, 0xc3, 0x54, 0xb2, 0xd9, 0x42, 0x91,
		0xea, 0x53, 0x98, 0x38, 0x78, 0x81, 0x12, 0xca, 0x15, 0x23, 0xbd, 0xc1, 0x70, 0x1f, 0xd2, 0x40,
		0xfd, 0x39, 0x33, 0xaa, 0x27, 0x2b, 0xe8, 0x10, 0x7d, 0xa4, 0xa8, 0x8e, 0x3d, 0x00, 0x4f, 0x3a,
		0x7f, 0xd8, 0x96, 0xea, 0x9e, 0x8e, 0x15, 0x6e, 0x9f, 0x76, 0x57, 0xba, 0x7d, 0xc2, 0xdf, 0x57,
		0x42, 0x82, 0xf4, 0xda, 0x89, 0x06, 0x05, 0x04, 0x62, 0x2f, 0x29, 0x23, 0x54, 0xd5, 0xbb, 0x97,
		0xf5, 0xf9, 0xc1, 0xcf, 0xec, 0x5f, 0x1d, 0xfd, 0xbb, 0xa6, 0xd7, 0x4a, 0xa8, 0x66, 0xbf, 0xb9,
		0x09, 0x44, 0xb1, 0x60, 0x28, 0xa9, 0x35, 0x16, 0x15, 0xf5, 0x13, 0xc1, 0x07, 0x7e, 0xd7, 0x40,
		0xdf, 0x8e, 0xd3, 0x32, 0xa9, 0x35, 0x98, 0x15, 0x32, 0xa9, 0x49, 0xc0, 0x24, 0xb4, 0x4a, 0x53,
		0x6b, 0x79, 0xaa, 0x77, 0x6c, 0xc5, 0x88, 0x69, 0xe5, 0x5d, 0xde, 0x42, 0x28, 0xf9, 0xb7, 0x5c,
		0xab, 0x19, 0xc7, 0xbc, 0xc5, 0x60, 0xeb, 0x5e, 0xa8, 0x52, 0xc4, 0x32, 0x7c, 0x35, 0x02, 0x06,
		0x46, 0x77, 0x30, 0xb6, 0x33, 0x4b, 0xb8, 0xfd, 0x02, 0xd8, 0x14, 0x40, 0x99, 0x25, 0x7e, 0x55,
		0xd6, 0x44, 0x43, 0x8d, 0x73, 0x0e, 0x71, 0x48, 0xd3, 0x82, 0x40, 0xda
	}
};

static UINT32 olds_prot_addr(UINT16 addr)
{
	switch (addr & 0xff)
	{
		case 0x0:
		case 0x5:
		case 0xa: return 0x402a00 + ((addr >> 8) << 2);
		case 0x2:
		case 0x8: return 0x402e00 + ((addr >> 8) << 2);
		case 0x1: return 0x40307e;
		case 0x3: return 0x403090;
		case 0x4: return 0x40309a;
		case 0x6: return 0x4030a4;
		case 0x7: return 0x403000;
		case 0x9: return 0x40306e;
	}

	return 0;
}

static UINT32 olds_read_reg(UINT16 addr)
{
	UINT32 protaddr = (olds_prot_addr(addr) - 0x400000) / 2;
	return sharedprotram[protaddr] << 16 | sharedprotram[protaddr + 1];
}

static void olds_write_reg( UINT16 addr, UINT32 val )
{
	sharedprotram[(olds_prot_addr(addr) - 0x400000) / 2]     = val >> 16;
	sharedprotram[(olds_prot_addr(addr) - 0x400000) / 2 + 1] = val & 0xffff;
}

static void IGS028_do_dma(UINT16 src, UINT16 dst, UINT16 size, UINT16 mode)
{
	UINT16 param = mode >> 8;
	UINT16 *PROTROM = (UINT16*)(PGMUSER0 + 0x10000);

	mode &= 0x0f;

	switch (mode)
	{
		case 0x00: // This mode copies code later on in the game! Encrypted somehow?
		// src:2fc8, dst: 045a, size: 025e, mode: 0000
		// jumps from 12beb4 in unprotected set
		// jumps from 1313e0 in protected set
		case 0x01: // swap bytes and nibbles
		case 0x02: // ^= encryption
		case 0x05: // copy
		case 0x06: // += encryption (correct?)
		{
			UINT8 extraoffset = param & 0xff;
			UINT8 *dectable = (UINT8 *)(PROTROM + (0x100 / 2));

			for (INT32 x = 0; x < size; x++)
			{
				UINT16 dat2 = PROTROM[src + x];

				int taboff = ((x*2)+extraoffset) & 0xff; // must allow for overflow in instances of odd offsets
				unsigned short extraxor = ((dectable[taboff + 0]) << 0) | (dectable[taboff + 1] << 8);

				if (mode==0) dat2 = 0x4e75; // hack
				if (mode==1) dat2  = ((dat2 & 0xf000) >> 12) | ((dat2 & 0x0f00) >> 4) | ((dat2 & 0x00f0) << 4) | ((dat2 & 0x000f) << 12);
				if (mode==2) dat2 ^= extraxor;
				//if (mode==5) dat2  = dat2;
				if (mode==6) dat2 += extraxor;

				if (mode==2 || mode==6) dat2 = (dat2<<8)|(dat2>>8);

				sharedprotram[dst + x] = (dat2 << 8) | (dat2 >> 8);
			}
		}
	}
}

static void olds_protection_calculate_hold(int y, int z) // calculated in routine $12dbc2 in olds
{
	unsigned short old = m_olds_prot_hold;

	m_olds_prot_hold = ((old << 1) | (old >> 15));

	m_olds_prot_hold ^= 0x2bad;
	m_olds_prot_hold ^= BIT(z, y);
	m_olds_prot_hold ^= BIT( old,  7) <<  0;
	m_olds_prot_hold ^= BIT(~old, 13) <<  4;
	m_olds_prot_hold ^= BIT( old,  3) << 11;

	m_olds_prot_hold ^= (m_olds_prot_hilo & ~0x0408) << 1; // $81790c
}

static void olds_protection_calculate_hilo() // calculated in routine $12dbc2 in olds
{
	UINT8 source;

	m_olds_prot_hilo_select++;
	if (m_olds_prot_hilo_select > 0xeb) {
		m_olds_prot_hilo_select = 0;
	}

	source = olds_source_data[PgmInput[7] - 1][m_olds_prot_hilo_select];

	if (m_olds_prot_hilo_select & 1)    // $8178fa
	{
		m_olds_prot_hilo = (m_olds_prot_hilo & 0x00ff) | (source << 8);     // $8178d8
	}
	else
	{
		m_olds_prot_hilo = (m_olds_prot_hilo & 0xff00) | (source << 0);     // $8178d8
	}
}

static void __fastcall olds_protection_w(UINT32 offset, UINT16 data)
{
	offset &= 2;

	if (offset == 0)
	{
		m_olds_cmd = data;
	}
	else
	{
		switch (m_olds_cmd)
		{
			case 0x00:
				m_olds_reg = data;
			break;

			case 0x02:
				m_olds_bs = ((data & 0x03) << 6) | ((data & 0x04) << 3) | ((data & 0x08) << 1);
			break;

			case 0x03:
			{
				UINT16 cmd = sharedprotram[0x3026 / 2];

				switch (cmd)
				{
					case 0x12:
					{
						UINT16 mode = sharedprotram[0x303e / 2];  // ?
						UINT16 src  = sharedprotram[0x306a / 2] >> 1; // ?
						UINT16 dst  = sharedprotram[0x3084 / 2] & 0x1fff;
						UINT16 size = sharedprotram[0x30a2 / 2] & 0x1fff;

						IGS028_do_dma(src, dst, size, mode);
					}
					break;

					case 0x64: // incomplete...
					{
					        UINT16 p1 = sharedprotram[0x3050 / 2];
					        UINT16 p2 = sharedprotram[0x3082 / 2];
					        UINT16 p3 = sharedprotram[0x3054 / 2];
					        UINT16 p4 = sharedprotram[0x3088 / 2];

					        if (p2  == 0x02)
					                olds_write_reg(p1, olds_read_reg(p1) + 0x10000);

					        switch (p4)
					        {
					                case 0xd:
					                        olds_write_reg(p1,olds_read_reg(p3));
					                        break;
					                case 0x0:
					                        olds_write_reg(p3,(olds_read_reg(p2))^(olds_read_reg(p1)));
					                        break;
					                case 0xe:
					                        olds_write_reg(p3,olds_read_reg(p3)+0x10000);
					                        break;
					                case 0x2:
					                        olds_write_reg(p1,(olds_read_reg(p2))+(olds_read_reg(p3)));
					                        break;
					                case 0x6:
					                        olds_write_reg(p3,(olds_read_reg(p2))&(olds_read_reg(p1)));
					                        break;
					                case 0x1:
					                        olds_write_reg(p2,olds_read_reg(p1)+0x10000);
					                        break;
					                case 0x7:
					                        olds_write_reg(p3,olds_read_reg(p1));
					                        break;
					                default:
					                        break;
					        }
					}
				}

				m_olds_cmd3 = ((data >> 4) + 1) & 0x3;
			}
			break;

			case 0x04:
				m_olds_ptr = data;
			break;

			case 0x20:
			case 0x21:
			case 0x22:
			case 0x23:
			case 0x24:
			case 0x25:
			case 0x26:
			case 0x27:
				m_olds_ptr++;
				olds_protection_calculate_hold(m_olds_cmd & 0x0f, data & 0xff);
			break;
		}
	}
}

static UINT16 __fastcall olds_protection_r(UINT32 offset)
{
	offset &= 2;

	if (offset)
	{
		switch (m_olds_cmd)
		{
			case 0x01:
				return m_olds_reg & 0x7f;

			case 0x02:
				return m_olds_bs | 0x80;

			case 0x03:
				return m_olds_cmd3;

			case 0x05:
			{
				switch (m_olds_ptr)
				{
					case 1: return 0x3f00 | PgmInput[7];

					case 2:
						return 0x3f00 | 0x00;

					case 3:
						return 0x3f00 | 0x90;

					case 4:
						return 0x3f00 | 0x00;

					case 5:
					default: // >= 5
						return 0x3f00 | BITSWAP08(m_olds_prot_hold, 5,2,9,7,10,13,12,15);    // $817906
				}
			}

			case 0x40:
				olds_protection_calculate_hilo();
				return 0; // unused?
		}
	}

	return 0;
}

static void reset_olds()
{
//	written by protection device
//	there seems to be an auto-dma that writes from $401000-402573?
	sharedprotram[0x1000/2] = 0x4749; // 'IGS.28'
	sharedprotram[0x1002/2] = 0x2E53;
	sharedprotram[0x1004/2] = 0x3832;
	sharedprotram[0x3064/2] = 0xB315; // crc or status check?

//	Should these be written by command 64??
//	sharedprotram[0x2a00/2] = 0x0000; // ?
//	sharedprotram[0x2a02/2] = 0x0000; // ?
	sharedprotram[0x2a04/2] = 0x0002; // ?
//	sharedprotram[0x2a06/2] = 0x0000; // ?
//	sharedprotram[0x2ac0/2] = 0x0000; // ?
	sharedprotram[0x2ac2/2] = 0x0001; // ?
//	sharedprotram[0x2e00/2] = 0x0000; // ?
//	sharedprotram[0x2e02/2] = 0x0000; // ?
//	sharedprotram[0x2e04/2] = 0x0000; // ?
	sharedprotram[0x2e06/2] = 0x0009; // seconds on char. select timer
//	sharedprotram[0x2e08/2] = 0x0000; // ?
	sharedprotram[0x2e0a/2] = 0x0006; // ?

	m_olds_prot_hold = 0;
	m_olds_prot_hilo = 0;
	m_olds_prot_hilo_select = 0;
	m_olds_cmd = 0;
	m_olds_reg = 0;
	m_olds_ptr = 0;
	m_olds_bs = 0;
	m_olds_cmd3 = 0;
}

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

	if (nAction & ACB_MEMORY_RAM) {
		ba.Data		= PGMUSER0 + 0x000000;
		ba.nLen		= 0x0004000;
		ba.nAddress	= 0x400000;
		ba.szName	= "ProtRAM";
		BurnAcb(&ba);
	}

	if (nAction & ACB_DRIVER_DATA) {
		SCAN_VAR(m_olds_prot_hold);
		SCAN_VAR(m_olds_prot_hilo);
		SCAN_VAR(m_olds_prot_hilo_select);
		SCAN_VAR(m_olds_cmd);
		SCAN_VAR(m_olds_reg);
		SCAN_VAR(m_olds_ptr);
		SCAN_VAR(m_olds_bs);
		SCAN_VAR(m_olds_cmd3);
	}

	return 0;
}

void install_protection_asic25_asic28_olds()
{
	pPgmScanCallback = oldsScan;
	pPgmResetCallback = reset_olds;

	sharedprotram = (UINT16*)PGMUSER0;

	// no protection rom and different offset for olds100a
	if (strcmp(BurnDrvGetTextA(DRV_NAME), "olds100a") == 0) {
		BurnLoadRom(PGMUSER0 + 0x10000,	15, 1);
	} else {
		BurnLoadRom(PGMUSER0 + 0x10000,	19, 1);
	}

	SekOpen(0);

	SekMapMemory(PGMUSER0,	0x400000, 0x403fff, SM_RAM);

	SekMapHandler(4,	  0xdcb400, 0xdcb403, SM_READ | SM_WRITE);
	SekSetReadWordHandler(4,  olds_protection_r);
	SekSetWriteWordHandler(4, olds_protection_w);

	SekClose();
}
