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

	Star Wars (MisfitMAME version)

	December, 2008 by Robbbert

	This is a real dump, which came with colour proms.

	Differences between starw and starw1:
	1. The audio port is 4 instead of 3
	2. Extra (unknown) write to ports 3 and 7 at boot time


	There is a minor bug in this game:
	If the dips are set for 5 ships, AND you scored enough to get an extra ship,
	then when your game ends, the attract mode will award itself an extra ship. This
	will overlay the word CREDIT, causing corrupt graphics.

	Changes for MisfitMAME:

	1. Created this one-off driver
	2. Moved subroutines that are only called once, into the calling routine
	3. Removed mb14241, as it is not used by this game
	4. Added colour
	5. Fixed coin (1 coin 2 credits instead of 2 coins 1 credit)

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

#include "driver.h"
#include "sound/samples.h"
#include "sound/sn76477.h"

#define NUM_PENS	(8)
#define STARW1_MASTER_CLOCK				(19968000)
#define STARW1_CPU_CLOCK					(STARW1_MASTER_CLOCK / 10)
#define STARW1_PIXEL_CLOCK				(STARW1_MASTER_CLOCK / 4)
#define STARW1_HTOTAL						(0x140)
#define STARW1_HBEND						(0x000)
#define STARW1_HBSTART					(0x100)
#define STARW1_VTOTAL						(0x106)
#define STARW1_VBEND						(0x000)
#define STARW1_VBSTART					(0x0e0)
#define STARW1_VCOUNTER_START_NO_VBLANK	(0x020)
#define STARW1_VCOUNTER_START_VBLANK		(0x0da)
#define STARW1_INT_TRIGGER_COUNT_1		(0x080)
#define STARW1_INT_TRIGGER_VBLANK_1		(0)
#define STARW1_INT_TRIGGER_COUNT_2		STARW1_VCOUNTER_START_VBLANK
#define STARW1_INT_TRIGGER_VBLANK_2		(1)

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

static UINT8 starw1_flip_screen;
static UINT8 starw1_screen_red;
static UINT8 *starw1_ram;
static size_t starw1_ram_size;
static UINT8 starw1_player;

/*************************************
 *
 *  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 >= STARW1_VBSTART);

	if (vblank)
		counter = vpos - STARW1_VBSTART + STARW1_VCOUNTER_START_VBLANK;
	else
		counter = vpos + STARW1_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 - STARW1_VCOUNTER_START_VBLANK + STARW1_VBSTART;
	else
		vpos = counter - STARW1_VCOUNTER_START_NO_VBLANK;

	return vpos;
}


static TIMER_CALLBACK( starw1_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 == STARW1_INT_TRIGGER_COUNT_1)
	{
		next_counter = STARW1_INT_TRIGGER_COUNT_2;
		next_vblank = STARW1_INT_TRIGGER_VBLANK_2;
	}
	else
	{
		next_counter = STARW1_INT_TRIGGER_COUNT_1;
		next_vblank = STARW1_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 starw1_create_interrupt_timer(void)
{
	interrupt_timer = timer_alloc(starw1_interrupt_callback, NULL);
}


static void starw1_start_interrupt_timer(running_machine *machine)
{
	int vpos = vysnc_chain_counter_to_vpos(STARW1_INT_TRIGGER_COUNT_1, STARW1_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 >= STARW1_VCOUNTER_START_NO_VBLANK)
	{
		if (starw1_flip_screen)
			*BITMAP_ADDR32(bitmap, STARW1_VBSTART - 1 - (y - STARW1_VCOUNTER_START_NO_VBLANK), STARW1_HPIXCOUNT - 1 - x) = pens[color];
		else
			*BITMAP_ADDR32(bitmap, y - STARW1_VCOUNTER_START_NO_VBLANK, x) = pens[color];
	}
}

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

	UINT8 *prom = memory_region(screen->machine, "proms");
	UINT8 *color_map_base = starw1_player ? &prom[0x0800] : &prom[0];

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

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

		UINT8 data = starw1_ram[offs];

		offs_t color_address = (offs >> 8 << 5) | (offs & 0x1f);

		UINT8 fore_color = starw1_screen_red ? 1 : color_map_base[color_address] & 0x07;

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

	return 0;
}

/**********************************************************************************
 *
 *  Special Input Handler
 *	if in upright mode, we need to read players 1's controls for both players
 *
 **********************************************************************************/

static READ8_HANDLER( starw1_in2_r )
{
	if (input_port_read(machine, "CAB"))
		return input_port_read(machine, "IN2");
	else
		return (input_port_read(machine, "IN2") & 0x8F) | (input_port_read(machine, "IN1") & 0x70);
}

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

static const sn76477_interface invaders_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(0.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 invaders_sample_names[] =
{
	"*invaders",
	"2.wav",		/* missile destroyed */
	"8.wav",		/* UFO/saucer hit */
	"9.wav",		/* bonus base */
	0
};


static const samples_interface invaders_samples_interface =
{
	2,	/* 2 channels */
	invaders_sample_names
};

static WRITE8_HANDLER( starw1_port_04_w )
{
	static UINT8 port_04_last = 0;
	UINT8 rising_bits = data & ~port_04_last;
	port_04_last = data;

	sn76477_enable_w(0, !(data & 0x01));				/* thrust sound */

	if (rising_bits & 0x04) sample_start_n(0, 0, 0, 0);		/* EXPLOSION */
	if (rising_bits & 0x10) sample_start_n(0, 1, 2, 0);		/* BONUS MISSILE BASE */
	starw1_screen_red = data & 4;

	sound_global_enable(data & 0x20);

	if (data & 0xca) printf("Unknown code to port 4 = %X\n",data);
}

static WRITE8_HANDLER( starw1_port_05_w )
{
	/*
	00 - normal play
	10 - killed an invader
	20 - flip screen */

	static UINT8 port_05_last = 0;
	UINT8 rising_bits = data & ~port_05_last;
	port_05_last = data;

	if (rising_bits & 0x10) sample_start_n(0, 0, 1, 0);		/* invader hit */
	starw1_flip_screen = data & 0x20 & input_port_read(machine, "CAB");
	starw1_player = data & 0x20;

	if (data & 0xcf) printf("Unknown code to port 5 = %X\n",data);
}


/*************************************
 *
 *  Machine setup
 *
 *************************************/

static MACHINE_START( starw1 )
{
	starw1_create_interrupt_timer();
}

static MACHINE_RESET( starw1 )
{
	starw1_start_interrupt_timer(machine);
}

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

static ADDRESS_MAP_START( main_map, ADDRESS_SPACE_PROGRAM, 8 )
	ADDRESS_MAP_GLOBAL_MASK(0x7fff)
	AM_RANGE(0x0000, 0x1fff) AM_ROM
	AM_RANGE(0x2000, 0x3fff) AM_MIRROR(0x4000) AM_RAM AM_BASE(&starw1_ram) AM_SIZE(&starw1_ram_size)
ADDRESS_MAP_END

static ADDRESS_MAP_START( starw1_io_map, ADDRESS_SPACE_IO, 8 )
	AM_RANGE(0x01, 0x01) AM_READ_PORT("IN1")
	AM_RANGE(0x02, 0x02) AM_READ(starw1_in2_r)
	AM_RANGE(0x04, 0x04) AM_WRITE(starw1_port_04_w)
	AM_RANGE(0x05, 0x05) AM_WRITE(starw1_port_05_w)
	AM_RANGE(0x06, 0x06) AM_WRITE(watchdog_reset_w)
ADDRESS_MAP_END

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

static INPUT_PORTS_START( starw1 )
	PORT_START("IN1")
	PORT_BIT( 0x01, IP_ACTIVE_LOW,  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_UNKNOWN )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_2WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_2WAY
	PORT_BIT( 0x80, IP_ACTIVE_LOW,  IPT_UNKNOWN )

	PORT_START("IN2")
	PORT_DIPNAME( 0x03, 0x00, DEF_STR( Lives ) )		PORT_DIPLOCATION("SW1:1,2")
	PORT_DIPSETTING(    0x00, "2" )
	PORT_DIPSETTING(    0x01, "3" )
	PORT_DIPSETTING(    0x02, "4" )
	PORT_DIPSETTING(    0x03, "5" )
	PORT_DIPUNKNOWN_DIPLOC( 0x04, 0x00, "SW1:3" )
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Bonus_Life ) )	PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x00, "3000" )
	PORT_DIPSETTING(    0x08, "5000" )
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)			PORT_DIPLOCATION("SW1:5")
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_2WAY PORT_PLAYER(2)	PORT_DIPLOCATION("SW1:6")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_2WAY PORT_PLAYER(2)	PORT_DIPLOCATION("SW1:7")
	PORT_DIPNAME( 0x80, 0x00, DEF_STR( Coinage ) )					PORT_DIPLOCATION("SW1:8")
	PORT_DIPSETTING(    0x00, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x80, DEF_STR( 1C_2C ) )

	/* Dummy port for cocktail mode - equivalent of the jumpers etc on the real machine */
	PORT_START("CAB")
	PORT_CONFNAME( 0x20, 0x00, DEF_STR( Cabinet ) )
	PORT_CONFSETTING(    0x00, DEF_STR( Upright ) )
	PORT_CONFSETTING(    0x20, DEF_STR( Cocktail ) )
INPUT_PORTS_END

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

static MACHINE_DRIVER_START( starw1 )

	/* basic machine hardware */
	MDRV_CPU_ADD("main",8080,STARW1_CPU_CLOCK)
	MDRV_CPU_PROGRAM_MAP(main_map,0)
	MDRV_CPU_IO_MAP(starw1_io_map,0)
	MDRV_WATCHDOG_VBLANK_INIT(255)
	MDRV_MACHINE_START(starw1)
	MDRV_MACHINE_RESET(starw1)

	/* video hardware */
	MDRV_VIDEO_UPDATE(starw1)

	MDRV_SCREEN_ADD("main", RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
	MDRV_SCREEN_RAW_PARAMS(STARW1_PIXEL_CLOCK, STARW1_HTOTAL, STARW1_HBEND, STARW1_HPIXCOUNT, STARW1_VTOTAL, STARW1_VBEND, STARW1_VBSTART)

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

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

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

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

ROM_START( starw1 )
	ROM_REGION( 0x10000, "main", 0 )
	ROM_LOAD( "gc.75",   0x0000, 0x0400, CRC(ad10c128) SHA1(c30ff9ff5cf8dedf7654c8e2799a4bb79a30104a) )
	ROM_LOAD( "gc.77",   0x0400, 0x0400, CRC(ab77c474) SHA1(eb07dcad1f265834b93a8108298d4441d6a74b2e) )
	ROM_LOAD( "gc.76",   0x0800, 0x0400, CRC(3638aed4) SHA1(1426c9270f248fd2ab134dc35526599c02051ccd) )
	ROM_LOAD( "gc.80",   0x1400, 0x0400, CRC(4c67957b) SHA1(dda7bbd54e7395dea80d224e487318fb4429f027) )
	ROM_LOAD( "gc.81",   0x1800, 0x0400, CRC(246621ef) SHA1(bddc5253f735fa81266d725a24b1c14faabe0c6a) )
	ROM_LOAD( "gc.82",   0x1c00, 0x0400, CRC(19bd32ee) SHA1(a1718a6a6300c3d7df469793cb0d590c4a966aff) )

	ROM_REGION( 0x1000, "proms", 0 )		/* color maps player 1/player 2 */
	ROM_LOAD( "cv01",    0x0000, 0x0800, CRC(8d892ef3) SHA1(c471dd6197a3c779d89c33fcb425cf3bbdf4fc15) )
	ROM_LOAD( "cv02",    0x0800, 0x0800, CRC(b44ddde8) SHA1(8793f370526c072e645d8d0b9794b1b64a7701ef) )
ROM_END

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

/* board #  rom       parent    machine   inp */

GAME(1979, starw1, 0, starw1, starw1, 0, ROT270, "Yamashita", "Star Wars (colour)", 0 )
