#include "driver.h"
#include "machine/eeprom.h"
#include "sound/okim6295.h"
#include "sound/3812intf.h"
#include "sound/2413intf.h"

/* in machine/kabuki.c */
void mgakuen2_decode(void);
void pang_decode(void);
void cworld_decode(void);
void hatena_decode(void);
void spang_decode(void);
void spangj_decode(void);
void sbbros_decode(void);
void marukin_decode(void);
void qtono1_decode(void);
void qsangoku_decode(void);
void block_decode(void);


VIDEO_START( pang );
VIDEO_UPDATE( pang );

WRITE8_HANDLER( pang_video_bank_w );
WRITE8_HANDLER( pang_videoram_w );
READ8_HANDLER( pang_videoram_r );
WRITE8_HANDLER( pang_colorram_w );
READ8_HANDLER( pang_colorram_r );
WRITE8_HANDLER( pang_gfxctrl_w );
WRITE8_HANDLER( pang_paletteram_w );
READ8_HANDLER( pang_paletteram_r );

extern unsigned char *pang_videoram;
extern unsigned char *pang_colorram;

extern size_t pang_videoram_size;

static WRITE8_HANDLER( pang_bankswitch_w )
{
	memory_set_bank(1, data & 0x0f);
}



/***************************************************************************

  EEPROM

***************************************************************************/

static struct EEPROM_interface eeprom_interface =
{
	6,		/* address bits */
	16,		/* data bits */
	"0110",	/*  read command */
	"0101",	/* write command */
	"0111"	/* erase command */
};

static unsigned char *nvram;
static size_t nvram_size;
static int init_eeprom_count;

static NVRAM_HANDLER( mitchell )
{
	if (read_or_write)
	{
		EEPROM_save(file);					/* EEPROM */
		if (nvram_size)	/* Super Pang, Block Block */
			mame_fwrite(file,nvram,nvram_size);	/* NVRAM */
	}
	else
	{
		EEPROM_init(&eeprom_interface);

		if (file)
		{
			init_eeprom_count = 0;
			EEPROM_load(file);					/* EEPROM */
			if (nvram_size)	/* Super Pang, Block Block */
				mame_fread(file,nvram,nvram_size);	/* NVRAM */
		}
		else
			init_eeprom_count = 1000;	/* for Super Pang */
	}
}

static READ8_HANDLER( pang_port5_r )
{
	int bit;

	bit = EEPROM_read_bit() << 7;

	/* bits 0 and (sometimes) 3 are checked in the interrupt handler. */
	/* Maybe they are vblank related, but I'm not sure. */
	/* bit 3 is checked before updating the palette so it really seems to be vblank. */
	/* Many games require two interrupts per frame and for these bits to toggle, */
	/* otherwise music doesn't work. */
	if (cpu_getiloops() & 1) bit |= 0x01;
	else bit |= 0x08;

	{
		static const game_driver *mitchell_driver = NULL;
		static int pang_port5_kludge = 0;

		/* Only compute pang_port5_kludge when confronted with a new gamedrv */
		if (mitchell_driver != Machine->gamedrv)
		{
			mitchell_driver = Machine->gamedrv;
			if (strcmp(mitchell_driver->name, "mgakuen2") == 0)
				pang_port5_kludge = 0;
			else
				pang_port5_kludge = 1;
		}
		if (pang_port5_kludge)	/* hack... music doesn't work otherwise */
			bit ^= 0x08;
	}

	return (input_port_0_r(0) & 0x76) | bit;
}

static WRITE8_HANDLER( eeprom_cs_w )
{
	EEPROM_set_cs_line(data ? CLEAR_LINE : ASSERT_LINE);
}

static WRITE8_HANDLER( eeprom_clock_w )
{
	EEPROM_set_clock_line(data ? CLEAR_LINE : ASSERT_LINE);
}

static WRITE8_HANDLER( eeprom_serial_w )
{
	EEPROM_write_bit(data);
}



/***************************************************************************

  Input handling

***************************************************************************/

static int dial[2],dial_selected;

static READ8_HANDLER( block_input_r )
{
	static int dir[2];

	if (dial_selected)
	{
		int delta;

		delta = (readinputport(4 + offset) - dial[offset]) & 0xff;
		if (delta & 0x80)
		{
			delta = (-delta) & 0xff;
			if (dir[offset])
			{
			/* don't report movement on a direction change, otherwise it will stutter */
				dir[offset] = 0;
				delta = 0;
			}
		}
		else if (delta > 0)
		{
			if (dir[offset] == 0)
			{
			/* don't report movement on a direction change, otherwise it will stutter */
				dir[offset] = 1;
				delta = 0;
			}
		}
		if (delta > 0x3f) delta = 0x3f;
		return delta << 2;
	}
	else
	{
		int res;

		res = readinputport(2 + offset) & 0xf7;
		if (dir[offset]) res |= 0x08;

		return res;
	}
}

static WRITE8_HANDLER( block_dial_control_w )
{
	if (data == 0x08)
	{
		/* reset the dial counters */
		dial[0] = readinputport(4);
		dial[1] = readinputport(5);
	}
	else if (data == 0x80)
		dial_selected = 0;
	else
		dial_selected = 1;
}


static int keymatrix;

static READ8_HANDLER( mahjong_input_r )
{
	int i;

	for (i = 0;i < 5;i++)
		if (keymatrix & (0x80 >> i)) return readinputport(2 + 5 * offset + i);

	return 0xff;
}

static WRITE8_HANDLER( mahjong_input_select_w )
{
	keymatrix = data;
}


static int input_type;

static READ8_HANDLER( input_r )
{
	switch (input_type)
	{
		case 0:
		default:
			return readinputport(1 + offset);
			break;
		case 1:	/* Mahjong games */
			if (offset) return mahjong_input_r(offset-1);
			else return readinputport(1);
			break;
		case 2:	/* Block Block - dial control */
			if (offset) return block_input_r(offset-1);
			else return readinputport(1);
			break;
		case 3:	/* Super Pang - simulate START 1 press to initialize EEPROM */
			if (offset || init_eeprom_count == 0) return readinputport(1 + offset);
			else
			{
				init_eeprom_count--;
				return readinputport(1) & ~0x08;
			}
			break;
	}
}


static WRITE8_HANDLER( input_w )
{
	switch (input_type)
	{
		case 0:
		default:
logerror("PC %04x: write %02x to port 01\n",activecpu_get_pc(),data);
			break;
		case 1:
			mahjong_input_select_w(offset,data);
			break;
		case 2:
			block_dial_control_w(offset,data);
			break;
	}
}



/***************************************************************************

  Memory handlers

***************************************************************************/

static ADDRESS_MAP_START( readmem, ADDRESS_SPACE_PROGRAM, 8 )
	AM_RANGE(0x0000, 0x7fff) AM_READ(MRA8_ROM)
	AM_RANGE(0x8000, 0xbfff) AM_READ(MRA8_BANK1)
	AM_RANGE(0xc000, 0xc7ff) AM_READ(pang_paletteram_r)	/* Banked palette RAM */
	AM_RANGE(0xc800, 0xcfff) AM_READ(pang_colorram_r)	/* Attribute RAM */
	AM_RANGE(0xd000, 0xdfff) AM_READ(pang_videoram_r)	/* Banked char / OBJ RAM */
	AM_RANGE(0xe000, 0xffff) AM_READ(MRA8_RAM)	/* Work RAM */
ADDRESS_MAP_END

static ADDRESS_MAP_START( writemem, ADDRESS_SPACE_PROGRAM, 8 )
	AM_RANGE(0x0000, 0xbfff) AM_WRITE(MWA8_ROM)
	AM_RANGE(0xc000, 0xc7ff) AM_WRITE(pang_paletteram_w)
	AM_RANGE(0xc800, 0xcfff) AM_WRITE(pang_colorram_w) AM_BASE(&pang_colorram)
	AM_RANGE(0xd000, 0xdfff) AM_WRITE(pang_videoram_w) AM_BASE(&pang_videoram) AM_SIZE(&pang_videoram_size)
	AM_RANGE(0xe000, 0xffff) AM_WRITE(MWA8_RAM)
ADDRESS_MAP_END

static ADDRESS_MAP_START( readport, ADDRESS_SPACE_IO, 8 )
	ADDRESS_MAP_FLAGS( AMEF_ABITS(8) )
	AM_RANGE(0x00, 0x02) AM_READ(input_r)	/* Super Pang needs a kludge to initialize EEPROM.
                        The Mahjong games and Block Block need special input treatment */
	AM_RANGE(0x03, 0x03) AM_READ(input_port_12_r)	/* mgakuen only */
	AM_RANGE(0x04, 0x04) AM_READ(input_port_13_r)	/* mgakuen only */
	AM_RANGE(0x05, 0x05) AM_READ(pang_port5_r)
ADDRESS_MAP_END

static ADDRESS_MAP_START( writeport, ADDRESS_SPACE_IO, 8 )
	ADDRESS_MAP_FLAGS( AMEF_ABITS(8) )
	AM_RANGE(0x00, 0x00) AM_WRITE(pang_gfxctrl_w)    /* Palette bank, layer enable, coin counters, more */
	AM_RANGE(0x01, 0x01) AM_WRITE(input_w)
	AM_RANGE(0x02, 0x02) AM_WRITE(pang_bankswitch_w)      /* Code bank register */
	AM_RANGE(0x03, 0x03) AM_WRITE(YM2413_data_port_0_w)
	AM_RANGE(0x04, 0x04) AM_WRITE(YM2413_register_port_0_w)
	AM_RANGE(0x05, 0x05) AM_WRITE(OKIM6295_data_0_w)
	AM_RANGE(0x06, 0x06) AM_WRITE(MWA8_NOP)	/* watchdog? irq ack? */
	AM_RANGE(0x07, 0x07) AM_WRITE(pang_video_bank_w)      /* Video RAM bank register */
	AM_RANGE(0x08, 0x08) AM_WRITE(eeprom_cs_w)
	AM_RANGE(0x10, 0x10) AM_WRITE(eeprom_clock_w)
	AM_RANGE(0x18, 0x18) AM_WRITE(eeprom_serial_w)
ADDRESS_MAP_END



INPUT_PORTS_START( pang )
	PORT_START      /* DSW */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )	/* USED - handled in port5_r */
	PORT_BIT(0x02, 0x02, IPT_SERVICE ) PORT_NAME( DEF_STR( Service_Mode )) PORT_CODE(KEYCODE_F2)
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )	/* unused? */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNKNOWN )	/* USED - handled in port5_r */
	PORT_BIT( 0x70, IP_ACTIVE_LOW, IPT_UNKNOWN )	/* unused? */
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )	/* data from EEPROM */

	PORT_START      /* IN0 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNKNOWN )    /* probably unused */
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )    /* probably unused */
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )    /* probably unused */
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_COIN1 )

	PORT_START      /* IN1 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY

	PORT_START      /* IN2 */
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(2)
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(2)
INPUT_PORTS_END



static const gfx_layout charlayout =
{
	8,8,	/* 8*8 characters */
	32768,	/* 32768 characters */
	4,		/* 4 bits per pixel */
	{ 32768*16*8+4, 32768*16*8+0,4, 0 },
	{ 0, 1, 2, 3, 8+0, 8+1, 8+2, 8+3 },
	{ 0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16 },
	16*8    /* every char takes 16 consecutive bytes */
};

static const gfx_layout spritelayout =
{
	16,16,  /* 16*16 sprites */
	2048,   /* 2048 sprites */
	4,      /* 4 bits per pixel */
	{ 2048*64*8+4, 2048*64*8+0, 4, 0 },
	{ 0, 1, 2, 3, 8+0, 8+1, 8+2, 8+3,
			32*8+0, 32*8+1, 32*8+2, 32*8+3, 33*8+0, 33*8+1, 33*8+2, 33*8+3 },
	{ 0*16, 1*16, 2*16, 3*16, 4*16, 5*16, 6*16, 7*16,
			8*16, 9*16, 10*16, 11*16, 12*16, 13*16, 14*16, 15*16 },
	64*8    /* every sprite takes 64 consecutive bytes */
};

static GFXDECODE_START( mitchell )
	GFXDECODE_ENTRY( REGION_GFX1, 0, charlayout,     0, 128 ) /* colors 0-2047 */
	GFXDECODE_ENTRY( REGION_GFX2, 0, spritelayout,   0,  16 ) /* colors 0- 255 */
GFXDECODE_END


static MACHINE_DRIVER_START( pang )

	/* basic machine hardware */
	MDRV_CPU_ADD(Z80, 8000000)	/* Super Pang says 8MHZ ORIGINAL BOARD */
	MDRV_CPU_PROGRAM_MAP(readmem,writemem)
	MDRV_CPU_IO_MAP(readport,writeport)
	MDRV_CPU_VBLANK_INT(irq0_line_hold,2)	/* ??? one extra irq seems to be needed for music (see input5_r) */

	MDRV_SCREEN_REFRESH_RATE(60)
	MDRV_SCREEN_VBLANK_TIME(DEFAULT_60HZ_VBLANK_DURATION)

	MDRV_NVRAM_HANDLER(mitchell)

	/* video hardware */
	MDRV_VIDEO_ATTRIBUTES(VIDEO_TYPE_RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
	MDRV_SCREEN_SIZE(64*8, 32*8)
	MDRV_SCREEN_VISIBLE_AREA(8*8, (64-8)*8-1, 1*8, 31*8-1 )
	MDRV_GFXDECODE(mitchell)
	MDRV_PALETTE_LENGTH(2048)

	MDRV_VIDEO_START(pang)
	MDRV_VIDEO_UPDATE(pang)

	/* sound hardware */
	MDRV_SPEAKER_STANDARD_MONO("mono")

	MDRV_SOUND_ADD(OKIM6295, 990000)
	MDRV_SOUND_CONFIG(okim6295_interface_region_1_pin7high) // clock frequency & pin 7 not verified
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.50)

	MDRV_SOUND_ADD(YM2413, 4000000)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
MACHINE_DRIVER_END


static void bootleg_decode(void)
{
	memory_set_decrypted_region(0, 0x0000, 0x7fff, memory_region(REGION_CPU1) + 0x50000);
	memory_configure_bank_decrypted(1, 0, 16, memory_region(REGION_CPU1) + 0x60000, 0x4000);
}


static void configure_banks(void)
{
	memory_configure_bank(1, 0, 16, memory_region(REGION_CPU1) + 0x10000, 0x4000);
}

static DRIVER_INIT( pangb )
{
	input_type = 0;
	nvram_size = 0;
	bootleg_decode();
	configure_banks();
}

/* Misfitmame start */

ROM_START( mitcdemo )
	ROM_REGION( 2*0x50000, REGION_CPU1, 0 )	/* 320k for code */
	ROM_LOAD( "md_04.bin",    0x50000, 0x08000, CRC(f0ed5c22) SHA1(b8e26a09fca279d627bd700c31a82ffc18483192) )   /* Decrypted opcode + data */
	ROM_CONTINUE(             0x00000, 0x08000 )
	ROM_LOAD_OPTIONAL( "pang_02.bin",  0x60000, 0x20000, CRC(3f15bb61) SHA1(4f74ee25f32a201482840158b4d4c7aca1cda684) )   /* Decrypted op codes */
	ROM_LOAD_OPTIONAL( "pang_03.bin",  0x10000, 0x20000, CRC(0c8477ae) SHA1(a31a8c00407dfc3017d56e29fac6114b73248030) )   /* Decrypted data */

	ROM_REGION( 0x100000, REGION_GFX1, ROMREGION_DISPOSE )
	ROM_LOAD( "md_09.bin",    0x000000, 0x20000, CRC(f76acc9a) SHA1(31004f441fc56efb692e2e52c4ecc44748185d0a) )	/* chars */
	/* 40000-7ffff empty */
	ROM_LOAD( "md_11.bin",    0x080000, 0x20000, CRC(48f92e6f) SHA1(6c51cccf5bcf7b3bfe6579a95516a850e99c9b76) )
	/* c0000-fffff empty */

	ROM_REGION( 0x040000, REGION_GFX2, ROMREGION_DISPOSE )
	ROM_LOAD( "md10.bin",     0x000000, 0x20000, CRC(709a27b6) SHA1(b7e8d124ee207e75c420762d336a97f4cf4054f6) )	/* sprites */
	ROM_LOAD( "md9.bin",      0x020000, 0x20000, CRC(47de1ec7) SHA1(45bff13a6cdf58bc425fdc3cd2ae22d40d69a2c2) )

	ROM_REGION( 0x80000, REGION_SOUND1, 0 )	/* OKIM */
	ROM_LOAD_OPTIONAL( "bb1.bin",      0x00000, 0x20000, CRC(c52e5b8e) SHA1(933b954bfdd2d67e28b032ffabde192531249c1f) )
ROM_END

GAME( 1989, mitcdemo, 0,     pang,    pang,     pangb,    ROT0,   "Charles Doty", "Demo - Mitchell", 0 )
/* Misfitmame end */
