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

	Lupin III (MisfitMAME version)

	September, 2008 by Robbbert

	No diagram available, but I believe the sound is as accurate as possible

	Changes for MisfitMAME:
	1. Created this one-off driver
	2. Moved subroutines that are only called once, into the calling routine
	3. Added music using "indianbt" discrete sound
	4. Added watchdog
	5. Added global sound enable
	6. Added coin lockout
	7. Fixed cocktail mode

	I highly suspect the "You must be Lupin" screen has incorrect colours.


	2009-07-05 new information

	The rom at 5000 is optional. It contains code for colour ram, and the tune that plays
		in the "wife vs man" scene. If you remove this rom, bits 0 and 1 of port 0 must
		be set High. The colour will then be determined by the 2 proms instead. Bit 7
		of port 5 will select which prom to use.

	Differences between the 2 sets:
		- Set 2 has a language selection switch. In the Japanese position, it looks
		  the same as set 1. Set 1 is always in Japanese.
		- Set 1, bit 6 of port 3 activates when the wife is kicking the man.
		- The dogs and policemen are yellow in set 1, and different colours in set 2.

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

#include "driver.h"
#include "invaders.h"
#include "machine/mb14241.h"
#include "sound/samples.h"
#include "sound/sn76477.h"
#include "sound/discrete.h"

static UINT8 *lupin3_colorram;

/*************************************
 *
 *  Video Routines
 *
 *************************************/


static PALETTE_INIT( lupin3 )
{
	UINT8 i;
	for (i = 0; i < 8; i++)
		palette_set_color(machine, i, MAKE_RGB(pal1bit(i >> 0), pal1bit(i >> 2), pal1bit(i >> 1)));
}

INLINE void set_pixel(bitmap_t *bitmap, UINT8 y, UINT8 x, UINT8 color)
{
	if (y >= INVADERS_VCOUNTER_START_NO_VBLANK)
	{
		if (flipscreen)
			*BITMAP_ADDR16(bitmap, INVADERS_VBSTART - 1 - (y - INVADERS_VCOUNTER_START_NO_VBLANK), INVADERS_HPIXCOUNT - 1 - x) = color;
		else
			*BITMAP_ADDR16(bitmap, y - INVADERS_VCOUNTER_START_NO_VBLANK, x) = color;
	}
}

static VIDEO_UPDATE( lupin3 )
{
	UINT8 i,x,y,data,color;
	offs_t offs;

	for (offs = 0; offs < invaders_ram_size; offs++)
	{
		y = offs >> 5;
		x = offs << 3;
		data = invaders_ram[offs];
		color = lupin3_colorram[offs & 0x1f1f]^7;

		for (i = 0; i < 8; i++)
		{
			set_pixel(bitmap, y, x|i, (data & 1) ? color : 0);
			data >>= 1;
		}
	}
	return 0;
}


/*************************************
 *
 *  Sound
 *
 *************************************/

/* copied from invaders, cap on pin 17 changed to sound more like a helicopter */
static const sn76477_interface lupin3_sn76477_interface =
{
	0,			/*  4 noise_res (N/C)        */
	0,			/*  5 filter_res (N/C)       */
	0,			/*  6 filter_cap (N/C)       */
	0,			/*  7 decay_res (N/C)        */
	0,			/*  8 attack_decay_cap (N/C) */
	RES_K(100),		/* 10 attack_res             */
	RES_K(56),		/* 11 amplitude_res          */
	RES_K(10),		/* 12 feedback_res           */
	0,			/* 16 vco_voltage (N/C)      */
	CAP_U(1),		/* 17 vco_cap                */
	RES_K(8.2),		/* 18 vco_res                */
	5.0,			/* 19 pitch_voltage          */
	RES_K(120),		/* 20 slf_res                */
	CAP_U(1.0),		/* 21 slf_cap                */
	0,			/* 23 oneshot_cap (N/C)      */
	0,			/* 24 oneshot_res (N/C)      */
	1,			/* 22 vco                    */
	0,			/* 26 mixer A                */
	0,			/* 25 mixer B                */
	0,			/* 27 mixer C                */
	1,			/* 1  envelope 1             */
	0,			/* 28 envelope 2             */
	1			/* 9  enable (variable)      */
};

static const char *const lupin3_sample_names[] =
{
	"*lupin3",
	"cap.wav",		/* go to jail */
	"bark.wav",		/* dog barking */
	"walk1.wav",		/* walk, get money */
	"walk2.wav",		/* walk, get money */
	"warp.wav",		/* translocate, deposit money */
	"extend.wav",		/* bonus man */
	"kick.wav",		/* lands on top of building, wife kicks man */
	0
};


static const samples_interface lupin3_samples_interface =
{
	4,	/* 4 channels */
	lupin3_sample_names
};


/*************************************
 *
 *  Discrete Sound
 * (copied from indianbt)
 *
 *************************************/

static const discrete_dac_r1_ladder lupin3_music_dac =
	{3, {0, RES_K(47), RES_K(12)}, 0, 0, 0, CAP_U(0.1)};

#define INVADERS_MUSIC_CLK		(7680.0*2*2*2)

/* Nodes - Inputs */
#define INVADERS_MUSIC_DATA		NODE_01
/* Nodes - Sounds */
#define INVADERS_MUSIC			NODE_11

static DISCRETE_SOUND_START(lupin3)

	DISCRETE_INPUT_DATA (INVADERS_MUSIC_DATA)

/******************************************************************************
 *
 * Music Generator
 *
 ******************************************************************************/
	DISCRETE_NOTE(NODE_20, 1, INVADERS_MUSIC_CLK, INVADERS_MUSIC_DATA, 255, 5, DISC_CLK_IS_FREQ)

	// Convert count to 7492 output
	DISCRETE_TRANSFORM2(NODE_21, 1, NODE_20, 2, "01>0+")

	DISCRETE_DAC_R1(NODE_22, 1, NODE_21, DEFAULT_TTL_V_LOGIC_1, &lupin3_music_dac)

/******************************************************************************
 *
 * Final Mixing and Output
 *
 ******************************************************************************/
	DISCRETE_CRFILTER(NODE_90, 1, NODE_22, RES_K(10), CAP_U(0.1))

	DISCRETE_OUTPUT(NODE_90, 21000)

DISCRETE_SOUND_END

/*************************************
 *
 *  Output Ports
 *
 *************************************/

static WRITE8_HANDLER( lupin3_00_w )
{
	discrete_sound_w(machine, INVADERS_MUSIC_DATA, data);
}

static WRITE8_HANDLER( lupin3_03_w )
{
	UINT8 rising_bits = data & ~port_1_last;
	static UINT8 lupin3_step = 2;

	if (rising_bits & 0x01)
	{
		sample_start(0, lupin3_step, 0);		/* Walking, steal money */
		lupin3_step ^= 1;
	}

	sn76477_enable_w(0, data & 0x02 ? 0:1);			/* Helicopter */

	if (rising_bits & 0x04) sample_start(1, 4, 0);		/* Translocate, deposit money */
	if (rising_bits & 0x08) sample_start(0, 0, 0);		/* Jail */
	if (rising_bits & 0x10) sample_start(2, 5, 0);		/* Bonus Man */

	sound_global_enable(data & 0x20);

	coin_lockout_global_w(data & 0x80);

	/*	if (data & 0x40) printf("3=%X ",data);	*/

	port_1_last = data;
}

/* bit 7 goes high when game starts - purpose? */
static WRITE8_HANDLER( lupin3_05_w )
{
	UINT8 rising_bits = data & ~port_2_last;

	if (rising_bits & 0x01) sample_start(0, 6, 0);		/* Lands on top of building, wife kicks man */
//	if (rising_bits & 0x02) sample_start(3, 7, 0);		/* deposit money, start intermission, end game */
//	if (rising_bits & 0x04) sample_start(4, 7, 0);		/* deposit money, start intermission, Slides down rope */
//	if (rising_bits & 0x08) sample_start(5, 7, 0);		/* start intermission, end game */
	if (rising_bits & 0x10) sample_start(3, 1, 0);		/* Dog barking */

	player = data & 0x20;
	flipscreen = (player>>3) & (input_port_read(machine, "IN2") & 4);
	color_map = data & 0x40;

//	if (data & 0x40) printf("5=%X ",data);

	port_2_last = data;
}



/*************************************
 *
 *  Address Maps
 *
 *************************************/

static ADDRESS_MAP_START( lupin3_map, ADDRESS_SPACE_PROGRAM, 8 )
	AM_RANGE(0x0000, 0x1fff) AM_ROM
	AM_RANGE(0x2000, 0x3fff) AM_RAM AM_BASE(&invaders_ram) AM_SIZE(&invaders_ram_size)
	AM_RANGE(0x4000, 0x5fff) AM_ROM
	AM_RANGE(0xc000, 0xdfff) AM_RAM AM_BASE(&lupin3_colorram) AM_MIRROR(0x00e0)
ADDRESS_MAP_END

static ADDRESS_MAP_START( lupin3_io_map, ADDRESS_SPACE_IO, 8 )
	AM_RANGE(0x00, 0x00) AM_READ_PORT("IN0")
	AM_RANGE(0x01, 0x01) AM_READ_PORT("IN1")
	AM_RANGE(0x02, 0x02) AM_READ_PORT("IN2")
	AM_RANGE(0x03, 0x03) AM_READ(mb14241_0_shift_result_r)

	AM_RANGE(0x00, 0x00) AM_WRITE(lupin3_00_w)
	AM_RANGE(0x02, 0x02) AM_WRITE(mb14241_0_shift_count_w)
	AM_RANGE(0x03, 0x03) AM_WRITE(lupin3_03_w)
	AM_RANGE(0x04, 0x04) AM_WRITE(mb14241_0_shift_data_w)
	AM_RANGE(0x05, 0x05) AM_WRITE(lupin3_05_w)
	AM_RANGE(0x06, 0x06) AM_WRITE(watchdog_reset_w)
ADDRESS_MAP_END

/*************************************
 *
 *  Inputs
 *
 *************************************/

static INPUT_PORTS_START( lupin3 )
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNKNOWN )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_TILT )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1) PORT_COCKTAIL
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_4WAY PORT_COCKTAIL
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_4WAY PORT_COCKTAIL
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_4WAY PORT_COCKTAIL
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_4WAY PORT_COCKTAIL

	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_COIN1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_START2 )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_START1 )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_4WAY
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_4WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_4WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_4WAY

	PORT_START("IN2")
	PORT_DIPNAME( 0x03, 0x00, DEF_STR( Lives ) )		PORT_DIPLOCATION("SW1:1,2")
	PORT_DIPSETTING(    0x00, "3" )
	PORT_DIPSETTING(    0x01, "4" )
	PORT_DIPSETTING(    0x02, "5" )
	PORT_DIPSETTING(    0x03, "6" )
	PORT_DIPNAME( 0x04, 0x00, DEF_STR( Cabinet ) )		PORT_DIPLOCATION("SW1:3")
	PORT_DIPSETTING(    0x00, DEF_STR( Upright ) )
	PORT_DIPSETTING(    0x04, DEF_STR( Cocktail ) )
	PORT_DIPNAME( 0x08, 0x00, "Bags To Collect" )		PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x08, "2" )
	PORT_DIPSETTING(    0x00, "8" )
	PORT_DIPNAME( 0x10, 0x00, DEF_STR( Language ) )		PORT_DIPLOCATION("SW1:5")
	PORT_DIPSETTING(    0x00, DEF_STR( English ) )
	PORT_DIPSETTING(    0x10, DEF_STR( Japanese ) )
	PORT_DIPUNUSED_DIPLOC( 0x20, 0x00, "SW1:6" )
	PORT_DIPUNUSED_DIPLOC( 0x40, 0x00, "SW1:7" )
	PORT_DIPNAME(0x80,  0x00, "Invulnerability (Cheat)")	PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x80, DEF_STR( On ) )
INPUT_PORTS_END

/*************************************
 *
 *  Machine Drivers
 *
 *************************************/

static MACHINE_DRIVER_START( lupin3 )

	/* basic machine hardware */
	MDRV_CPU_ADD("main",8080,INVADERS_CPU_CLOCK)
	MDRV_CPU_PROGRAM_MAP(lupin3_map,0)
	MDRV_CPU_IO_MAP(lupin3_io_map,0)
	MDRV_WATCHDOG_VBLANK_INIT(255)
	MDRV_MACHINE_START(invaders)
	MDRV_MACHINE_RESET(invaders_common)

	/* video hardware */
	MDRV_VIDEO_UPDATE(lupin3)

	MDRV_SCREEN_ADD("main", RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_INDEXED16)
	MDRV_SCREEN_RAW_PARAMS(INVADERS_PIXEL_CLOCK, INVADERS_HTOTAL, INVADERS_HBEND, INVADERS_HPIXCOUNT, INVADERS_VTOTAL, INVADERS_VBEND, INVADERS_VBSTART)

	MDRV_PALETTE_LENGTH(8)
	MDRV_PALETTE_INIT(lupin3)

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

	MDRV_SOUND_ADD("sn", SN76477, 0)
	MDRV_SOUND_CONFIG(lupin3_sn76477_interface)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 0.5)

	MDRV_SOUND_ADD("samples", SAMPLES, 0)
	MDRV_SOUND_CONFIG(lupin3_samples_interface)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)

	MDRV_SOUND_ADD("discrete", DISCRETE, 0)
	MDRV_SOUND_CONFIG_DISCRETE(lupin3)
	MDRV_SOUND_ROUTE(ALL_OUTPUTS, "mono", 1.0)
MACHINE_DRIVER_END


/*************************************
 *
 *  Roms
 *
 *************************************/

ROM_START( lupin3 )
	ROM_REGION( 0x10000, "main", 0 )
	ROM_LOAD( "lp01.36",      0x0000, 0x0800, CRC(fd506ee8) SHA1(67ce62f24892f0eddf3e47913dff541f41493a17) )
	ROM_LOAD( "lp02.35",      0x0800, 0x0800, CRC(ec4225f8) SHA1(cd7360b3b339e5050075b498226070914fb7a031) )
	ROM_LOAD( "lp03.34",      0x1000, 0x0800, CRC(9307d377) SHA1(081f6c63ff2dcc549e44ab5ff5f5ddf99d544640) )
	ROM_LOAD( "lp04.33",      0x1800, 0x0800, CRC(e41e8b2b) SHA1(e67eaa8aeaf13f706afc17074fbbde3ad2cc9548) )
	ROM_LOAD( "lp05.32",      0x4000, 0x0800, CRC(f5c2faf4) SHA1(8d056f8c630e4659c02dd5da759dd497e4734292) )
	ROM_LOAD( "lp06.31",      0x4800, 0x0800, CRC(66289ab2) SHA1(fc9b4a7b7a08d43f34beaf1a8e68ed0ff6148534) )
	ROM_LOAD( "lp18.bin",     0x5000, 0x0800, CRC(2f07b4ba) SHA1(982e4c437b39b45e23d15af1b2fc8c7aa3034559) )

	ROM_REGION( 0x0800, "proms", 0 )		/* color map */
	ROM_LOAD( "lp08.1",       0x0000, 0x0400, CRC(33dbd03a) SHA1(1e0ae1cad1e9a90642886ae2ef726d3f383dd6cf) )
	ROM_LOAD( "lp09.2",       0x0400, 0x0400, CRC(9eaee652) SHA1(a4d2d8282ba825f3a8c0cc9bca16e1d36a0d0796) )
ROM_END

ROM_START( lupin3a )
	ROM_REGION( 0x10000, "main", 0 )
	ROM_LOAD( "lp12.bin",     0x0000, 0x0800, CRC(68a7f47a) SHA1(dce99b3810331d7603fa468f1dea984e571f709b) )
	ROM_LOAD( "lp13.bin",     0x0800, 0x0800, CRC(cae9a17b) SHA1(a333ba7db45325996e3254ab36162bb7577e8a38) )
	ROM_LOAD( "lp14.bin",     0x1000, 0x0800, CRC(3553b9e4) SHA1(6affb5b6caf08f365c0dce669e44046295c3df91) )
	ROM_LOAD( "lp15.bin",     0x1800, 0x0800, CRC(acbeef64) SHA1(50d78cdc9938285b6bf9fa81fa0f6c30b23e0756) )
	ROM_LOAD( "lp16.bin",     0x4000, 0x0800, CRC(19fcdc54) SHA1(2f18ee8158321fff68886ffe793724001e8b18c2) )
	ROM_LOAD( "lp17.bin",     0x4800, 0x0800, CRC(66289ab2) SHA1(fc9b4a7b7a08d43f34beaf1a8e68ed0ff6148534) )
	ROM_LOAD( "lp18.bin",     0x5000, 0x0800, CRC(2f07b4ba) SHA1(982e4c437b39b45e23d15af1b2fc8c7aa3034559) )
ROM_END

/*************************************
 *
 *  Game Drivers
 *
 *************************************/

/* board #  rom       parent    machine   inp */

GAME( 1980, lupin3,   0,        lupin3,   lupin3,   0, ROT270, "Taito", "Lupin III (set 1)", GAME_SUPPORTS_SAVE )
GAME( 1980, lupin3a,  lupin3,   lupin3,   lupin3,   0, ROT270, "Taito", "Lupin III (set 2)", GAME_SUPPORTS_SAVE )
