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

	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

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

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

#define NUM_PENS	(8)
#define LUPIN3_MASTER_CLOCK				(19968000)
#define LUPIN3_CPU_CLOCK					(LUPIN3_MASTER_CLOCK / 10)
#define LUPIN3_PIXEL_CLOCK				(LUPIN3_MASTER_CLOCK / 4)
#define LUPIN3_HTOTAL						(0x140)
#define LUPIN3_HBEND						(0x000)
#define LUPIN3_HBSTART					(0x100)
#define LUPIN3_VTOTAL						(0x106)
#define LUPIN3_VBEND						(0x000)
#define LUPIN3_VBSTART					(0x0e0)
#define LUPIN3_VCOUNTER_START_NO_VBLANK	(0x020)
#define LUPIN3_VCOUNTER_START_VBLANK		(0x0da)
#define LUPIN3_INT_TRIGGER_COUNT_1		(0x080)
#define LUPIN3_INT_TRIGGER_VBLANK_1		(0)
#define LUPIN3_INT_TRIGGER_COUNT_2		LUPIN3_VCOUNTER_START_VBLANK
#define LUPIN3_INT_TRIGGER_VBLANK_2		(1)

/* +4 is added to HBSTART because the hardware displays that many pixels after
   setting HBLANK */
#define LUPIN3_HPIXCOUNT		(LUPIN3_HBSTART + 4)

static UINT8 lupin3_flip_screen;
static UINT8 *lupin3_ram;
static size_t lupin3_ram_size;
static UINT8 *lupin3_colorram;
static UINT8 port_1_last = 0;
static UINT8 port_2_last = 0;


/*************************************
 *
 *  Interrupt generation
 *
 *************************************/

static emu_timer *interrupt_timer;


static UINT8 vpos_to_vysnc_chain_counter(int vpos)
{
	/* convert from a vertical position to the actual values on the vertical sync counters */
	UINT8 counter;
	int vblank = (vpos >= LUPIN3_VBSTART);

	if (vblank)
		counter = vpos - LUPIN3_VBSTART + LUPIN3_VCOUNTER_START_VBLANK;
	else
		counter = vpos + LUPIN3_VCOUNTER_START_NO_VBLANK;

	return counter;
}


static int vysnc_chain_counter_to_vpos(UINT8 counter, int vblank)
{
	/* convert from the vertical sync counters to an actual vertical position */
	int vpos;

	if (vblank)
		vpos = counter - LUPIN3_VCOUNTER_START_VBLANK + LUPIN3_VBSTART;
	else
		vpos = counter - LUPIN3_VCOUNTER_START_NO_VBLANK;

	return vpos;
}


static TIMER_CALLBACK( lupin3_interrupt_callback )
{
	UINT8 next_counter;
	int next_vpos;
	int next_vblank;

	/* compute vector and set the interrupt line */
	int vpos = video_screen_get_vpos(machine->primary_screen);
	UINT8 counter = vpos_to_vysnc_chain_counter(vpos);
	UINT8 vector = 0xc7 | ((counter & 0x40) >> 2) | ((~counter & 0x40) >> 3);
	cpunum_set_input_line_and_vector(machine, 0, 0, HOLD_LINE, vector);

	/* set up for next interrupt */
	if (counter == LUPIN3_INT_TRIGGER_COUNT_1)
	{
		next_counter = LUPIN3_INT_TRIGGER_COUNT_2;
		next_vblank = LUPIN3_INT_TRIGGER_VBLANK_2;
	}
	else
	{
		next_counter = LUPIN3_INT_TRIGGER_COUNT_1;
		next_vblank = LUPIN3_INT_TRIGGER_VBLANK_1;
	}

	next_vpos = vysnc_chain_counter_to_vpos(next_counter, next_vblank);
	timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, next_vpos, 0), 0);
}


static void lupin3_create_interrupt_timer(void)
{
	interrupt_timer = timer_alloc(lupin3_interrupt_callback, NULL);
}


static void lupin3_start_interrupt_timer(running_machine *machine)
{
	int vpos = vysnc_chain_counter_to_vpos(LUPIN3_INT_TRIGGER_COUNT_1, LUPIN3_INT_TRIGGER_VBLANK_1);
	timer_adjust_oneshot(interrupt_timer, video_screen_get_time_until_pos(machine->primary_screen, vpos, 0), 0);
}

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

INLINE void set_pixel(bitmap_t *bitmap, UINT8 y, UINT8 x, pen_t *pens, UINT8 color)
{
	if (y >= LUPIN3_VCOUNTER_START_NO_VBLANK)
	{
		if (lupin3_flip_screen)
			*BITMAP_ADDR32(bitmap, LUPIN3_VBSTART - 1 - (y - LUPIN3_VCOUNTER_START_NO_VBLANK), LUPIN3_HPIXCOUNT - 1 - x) = pens[color];
		else
			*BITMAP_ADDR32(bitmap, y - LUPIN3_VCOUNTER_START_NO_VBLANK, x) = pens[color];
	}
}

static VIDEO_UPDATE( lupin3 )
{
	int i;
	pen_t pens[NUM_PENS];
	offs_t offs;

	for (i = 0; i < NUM_PENS; i++)
		pens[i] = MAKE_RGB(pal1bit(i >> 0), pal1bit(i >> 2), pal1bit(i >> 1));

	for (offs = 0; offs < lupin3_ram_size; offs++)
	{
		UINT8 y = offs >> 5;
		UINT8 x = offs << 3;

		UINT8 data = lupin3_ram[offs];
		UINT8 fore_color = ~lupin3_colorram[offs & 0x1f1f] & 0x07;

		for (i = 0; i < 8; i++)
		{
			set_pixel(bitmap, y, x, pens, (data & 1) ? fore_color : 0);
			x++;
			data = 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
};

static SOUND_START( samples )
{
	/* setup for save states */
	state_save_register_global(port_1_last);
	state_save_register_global(port_2_last);
}

/*************************************
 *
 *  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 LUPIN3_MUSIC_CLK		(7680.0*2*2*2)

/* Nodes - Inputs */
#define LUPIN3_MUSIC_DATA		NODE_01
/* Nodes - Sounds */
#define LUPIN3_MUSIC			NODE_11

static DISCRETE_SOUND_START(lupin3)

	DISCRETE_INPUT_DATA (LUPIN3_MUSIC_DATA)

/******************************************************************************
 *
 * Music Generator
 *
 ******************************************************************************/
	DISCRETE_NOTE(NODE_20, 1, LUPIN3_MUSIC_CLK, LUPIN3_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_port_00_w )
{
	discrete_sound_w(machine, LUPIN3_MUSIC_DATA, data);
}

static WRITE8_HANDLER( lupin3_port_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, get money */
		lupin3_step++;
		if (lupin3_step > 3) lupin3_step = 2;
	}

	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);

	port_1_last = data;
}

/* bit 7 goes high when game starts - purpose? */
static WRITE8_HANDLER( lupin3_port_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 */

	lupin3_flip_screen = ((data & 0x20)>>3) & (input_port_read(machine, "IN2") & 4);

	port_2_last = data;
}

/*************************************
 *
 *  Machine setup
 *
 *************************************/
static MACHINE_START( lupin3 )
{
	lupin3_create_interrupt_timer();
	mb14241_init(0);
}

static MACHINE_RESET( lupin3 )
{
	lupin3_start_interrupt_timer(machine);
}


/*************************************
 *
 *  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(&lupin3_ram) AM_SIZE(&lupin3_ram_size)
	AM_RANGE(0x4000, 0x5fff) AM_ROM
	AM_RANGE(0xc000, 0xdfff) AM_MIRROR(0x00e0) AM_RAM AM_BASE(&lupin3_colorram)
ADDRESS_MAP_END

static ADDRESS_MAP_START( lupin3_io_map, ADDRESS_SPACE_IO, 8 )
	AM_RANGE(0x00, 0x00) AM_READ_PORT("IN0") AM_WRITE(lupin3_port_00_w)
	AM_RANGE(0x01, 0x01) AM_READ_PORT("IN1")
	AM_RANGE(0x02, 0x02) AM_READ_PORT("IN2") AM_WRITE(mb14241_0_shift_count_w)
	AM_RANGE(0x03, 0x03) AM_READWRITE(mb14241_0_shift_result_r, lupin3_port_03_w)
	AM_RANGE(0x04, 0x04) AM_WRITE(mb14241_0_shift_data_w)
	AM_RANGE(0x05, 0x05) AM_WRITE(lupin3_port_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,LUPIN3_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(lupin3)
	MDRV_MACHINE_RESET(lupin3)

	/* video hardware */
	MDRV_VIDEO_UPDATE(lupin3)

	MDRV_SCREEN_ADD("main", RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
	MDRV_SCREEN_RAW_PARAMS(LUPIN3_PIXEL_CLOCK, LUPIN3_HTOTAL, LUPIN3_HBEND, LUPIN3_HPIXCOUNT, LUPIN3_VTOTAL, LUPIN3_VBEND, LUPIN3_VBSTART)

	/* 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( "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", GAME_SUPPORTS_SAVE )
