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

	Space Chaser (MisfitMAME version)

	August, 2008 by Robbbert

	Known errors in schematic diagram:
	1. Cocktail / Upright switch is said to be switch 7 (in 2 places), but diagram
	   shows it as switch 6.

	2. In the "effects" circuit, the connections of sx2 and sx4 are swapped.

	I've played this game many times back in 1980, and it seems apparent that these
	roms are not the same as the ones in our machines - perhaps they are prototypes.

	Differences:
	1. The missile sound should go off and stay off when an explosion occurs, or you
	   win the stage. With these roms, sometimes the sound comes back on for a few
	   moments.

	2. On the Hard level, you start with 9000 points (instead of 0). On the screen
	   where you are awarded your Bonus, you will instead get (or sometimes lose)
	   a random amount. At the end, if you got the High Score, it could be changed
	   to something else. This is a programming bug, which I've patched below.

	These observations are from memory, unfortunately very few of these machines
	exist, KLOV only records 1. Certainly there are none that I know of.


	What happens when you choose the Hard Difficulty?
	1. Your starting level becomes 3,4,5 or 6 depending on the dipswitches.
	2. [Possible] the missile speeds up sooner
	3. [Possible] the fuel gets used faster


	Changes for MisfitMAME:
	1. Created this one-off driver
	2. Swapped sx2 and sx4 to fix the effect sound
	3. Patched the scoring bug in the Hard level
	4. Identified unknown dipswitches
	5. Fixed cocktail mode

******************************************************************************************

	Space Chaser (CV version) for MisfitMAME

	August, 2008 by Robbbert

	Unlike Space Chaser (above), I don't have a diagram, therefore this is a Hack Job.


	These cheats exist in this game:
	1. Hold down 2P DOWN (the F key) while it says INSERT COIN. Then insert a coin
	   and play. You will have 2 extra ships.
	2. In the Hard difficulty setting, you normally start at level 4. Hold down the
	   1P START (the 1 key) while it says INSERT COIN. Then insert a coin and play.
	   You will start at level 5.


	Changes for MisfitMAME:
	1. Created this one-off driver
	2. Used the existing Discrete System for all sounds
	3. Removed IN0 as it is never referenced
	4. Identified and documented input cheats
	5. Added watchdog
	6. Added coin lockout
	7. Fixed cocktail mode

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

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

/* this is to stop stray pixels accumulating at the edge of the screen in cocktail mode */
#define SCHASER_HPIXCOUNT (INVADERS_HPIXCOUNT - 4)

static UINT8 schaser_background_disable;
static UINT8 schaser_background_select;
static UINT8 *schaser_colorram;

INLINE void set_pixel(bitmap_t *bitmap, UINT8 y, UINT8 x, pen_t *pens, UINT8 color)
{
	if (y >= INVADERS_VCOUNTER_START_NO_VBLANK)
	{
		if (flipscreen)
			*BITMAP_ADDR32(bitmap, INVADERS_VBSTART - 1 - (y - INVADERS_VCOUNTER_START_NO_VBLANK), SCHASER_HPIXCOUNT - 1 - x) = pens[color&0x07];
		else
			*BITMAP_ADDR32(bitmap, y - INVADERS_VCOUNTER_START_NO_VBLANK, x) = pens[color&0x07];
	}
}

static VIDEO_UPDATE( schaser )
{
	UINT8 i,x,y,data,color,back_color,back_data;
	pen_t pens[8];
	offs_t offs;
	UINT8 *color_map_base = memory_region(screen->machine, "proms");

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

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

		if (!schaser_background_disable)
		{
			back_data = color_map_base[(offs >> 8 << 5) | (offs & 0x1f)];

			/* the equations derived from the schematics don't appear to produce
		               the right colors, but this one does, at least for this PROM */
			back_color = (((back_data & 0x0c) == 0x0c) && schaser_background_select) ? 4 : 2;
		}

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

	return 0;
}

static VIDEO_UPDATE( schasrcv )
{
	UINT8 i,x,y,data,color,back_color;
	pen_t pens[8];
	offs_t offs;

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

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

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

	return 0;
}


/*************************************
 *
 *  Discrete Sound
 *
 *************************************/

static emu_timer *schaser_effect_555_timer;
static attotime schaser_effect_555_time_remain;
static int schaser_effect_555_is_low;
static int schaser_explosion;
static UINT8 global_sound_enabled = 0;

/*
 *  The dot sound is a square wave clocked by either the
 *  the 8V or 4V signals
 *
 *  The frequencies are (for the 8V signal):
 *
 *  19.968 MHz crystal / 2 (Qa of 74160 #10) -> 9.984MHz
 *                     / 2 (7474 #14) -> 4.992MHz
 *                     / 256+16 (74161 #5 and #8) -> 18352.94Hz
 *                     / 8 (8V) -> 2294.12 Hz
 *                     / 2 the final freq. is 2 toggles -> 1147.06Hz
 *
 *  for 4V, it's double at 2294.12Hz
 */
#define INVADERS_HSYNC	18352.94
#define INVADERS_4V		INVADERS_HSYNC /2 /4
#define INVADERS_8V		INVADERS_HSYNC /2 /8

static const sn76477_interface schaser_sn76477_interface =
{
	RES_K( 47),	/*  4 noise_res         */
	RES_K(330),	/*  5 filter_res        */
	CAP_P(470),	/*  6 filter_cap        */
	RES_M(2.2),	/*  7 decay_res         */
	CAP_U(1.0),	/*  8 attack_decay_cap  */
	RES_K(4.7),	/* 10 attack_res        */
	0,			/* 11 amplitude_res (variable)  */
	RES_K(33),	/* 12 feedback_res      */
	0,			/* 16 vco_voltage       */
	CAP_U(0.1),	/* 17 vco_cap           */
	RES_K(39),	/* 18 vco_res           */
	5.0,		/* 19 pitch_voltage     */
	RES_K(120),	/* 20 slf_res           */
	CAP_U(1.0),	/* 21 slf_cap           */
	CAP_U(0.1),	/* 23 oneshot_cap       */
	RES_K(220), /* 24 oneshot_res       */
	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) */
};

/* Nodes - Inputs */
#define INVADERS_DOT_EN		NODE_01
#define INVADERS_DOT_SEL		NODE_02
#define INVADERS_EXP_STREAM	NODE_03
#define INVADERS_MUSIC_BIT	NODE_04
#define INVADERS_SND_EN		NODE_05
/* Nodes - Adjusters */
#define INVADERS_VR1			NODE_07
#define INVADERS_VR2			NODE_08
#define INVADERS_VR3			NODE_09
/* Nodes - Sounds */
#define INVADERS_DOT_SND		NODE_10
#define INVADERS_EXP_SND		NODE_11
#define INVADERS_MUSIC_SND	NODE_12

static DISCRETE_SOUND_START(schaser)
	/************************************************/
	/* Input register mapping for schaser           */
	/************************************************/
	DISCRETE_INPUT_LOGIC  (INVADERS_DOT_EN)
	DISCRETE_INPUT_LOGIC  (INVADERS_DOT_SEL)
	// scale to 0-3.5V
	DISCRETE_INPUTX_STREAM(INVADERS_EXP_STREAM, 0, 1.0/14100, 2.323)
	DISCRETE_INPUTX_LOGIC (INVADERS_MUSIC_BIT, DEFAULT_TTL_V_LOGIC_1, 0, 0.0)
	DISCRETE_INPUT_LOGIC  (INVADERS_SND_EN)

	/************************************************/
	/* Volume adjusters.                            */
	/* We will set them to adjust the relative      */
	/* gains.                                       */
	/************************************************/
	DISCRETE_ADJUSTMENT_TAG(INVADERS_VR1, 0, RES_K(50)/(RES_K(50) + RES_K(470)), DISC_LINADJ, "VR1")
	DISCRETE_ADJUSTMENT_TAG(INVADERS_VR2, 0, RES_K(50)/(RES_K(50) + 560 + RES_K(6.8) + RES_K(2)), DISC_LINADJ, "VR2")
	DISCRETE_ADJUSTMENT_TAG(INVADERS_VR3, 0, RES_K(50)/(RES_K(50) + 560 + RES_K(6.8) + RES_K(10)), DISC_LINADJ, "VR3")

	/************************************************/
	/* Dot selection just selects between 4V and 8V */
	/************************************************/
	DISCRETE_SQUAREWFIX(NODE_20, 1, INVADERS_4V, DEFAULT_TTL_V_LOGIC_1, 50, 0, 0)
	DISCRETE_SQUAREWFIX(NODE_21, 1, INVADERS_8V, DEFAULT_TTL_V_LOGIC_1, 50, 0, 0)
	DISCRETE_SWITCH(NODE_22, INVADERS_DOT_EN, INVADERS_DOT_SEL, NODE_20, NODE_21)
	DISCRETE_RCFILTER(NODE_23, 1, NODE_22, 560, CAP_U(.1))
	DISCRETE_RCFILTER(NODE_24, 1, NODE_23, RES_K(6.8) + 560, CAP_U(.1))
	DISCRETE_MULTIPLY(INVADERS_DOT_SND, 1, NODE_24, INVADERS_VR3)

	/************************************************/
	/* Explosion/Effect filtering                   */
	/************************************************/
	DISCRETE_RCFILTER(NODE_30, 1, INVADERS_EXP_STREAM, 560, CAP_U(.1))
	DISCRETE_RCFILTER(NODE_31, 1, NODE_30, RES_K(6.8) + 560, CAP_U(.1))
	DISCRETE_CRFILTER(NODE_32, 1, NODE_31, RES_K(6.8) + 560 + RES_K(2) + RES_K(50), CAP_U(1))
	DISCRETE_MULTIPLY(INVADERS_EXP_SND, 1, NODE_32, INVADERS_VR2)

	/************************************************/
	/* Music is just a 1 bit DAC                    */
	/************************************************/
	DISCRETE_CRFILTER(NODE_40, 1, INVADERS_MUSIC_BIT, RES_K(470) + RES_K(50), CAP_U(.01))
	DISCRETE_MULTIPLY(INVADERS_MUSIC_SND, 1, NODE_40, INVADERS_VR1)

	/************************************************/
	/* Final mix with gain                          */
	/************************************************/
	DISCRETE_ADDER3(NODE_90, INVADERS_SND_EN, INVADERS_DOT_SND, INVADERS_EXP_SND, INVADERS_MUSIC_SND)

	DISCRETE_OUTPUT(NODE_90, 33080)
DISCRETE_SOUND_END

#if 0
/* This has all the possible combinations as shown in the diagram.
	However, in reality, only one effect line is activated at a time,
	and the diagram has sx2 and sx4 switched around. */
static const double schaser_effect_rc[8] =
{
	0,
	(RES_K(82) + RES_K(20)) * CAP_U(1),
	(RES_K(39) + RES_K(20)) * CAP_U(1),
	(1.0/ (1.0/RES_K(82) + 1.0/RES_K(39)) + RES_K(20)) * CAP_U(1),
	(RES_K(15) + RES_K(20)) * CAP_U(1),
	(1.0/ (1.0/RES_K(82) + 1.0/RES_K(15)) + RES_K(20)) * CAP_U(1),
	(1.0/ (1.0/RES_K(39) + 1.0/RES_K(15)) + RES_K(20)) * CAP_U(1),
	(1.0/ (1.0/RES_K(82) + 1.0/RES_K(39) + 1.0/RES_K(15)) + RES_K(20)) * CAP_U(1)
};
#endif

static const double schaser_effect_rc[8] =
{
	0,
	(RES_K(15) + RES_K(20)) * CAP_U(1),
	(RES_K(39) + RES_K(20)) * CAP_U(1),
	0,
	(RES_K(82) + RES_K(20)) * CAP_U(1),
	0, 0, 0
};

static TIMER_CALLBACK( schaser_effect_555_cb )
{
	attotime new_time;
	/* Toggle 555 output */
	schaser_effect_555_is_low ^= 1;
	schaser_effect_555_time_remain = attotime_zero;

	if (schaser_effect_555_is_low)
		new_time = attotime_div(PERIOD_OF_555_ASTABLE(0, RES_K(20), CAP_U(1)), 2);
	else
	{
		if (param)
			new_time = attotime_make(0, ATTOSECONDS_PER_SECOND * .8873 * schaser_effect_rc[param]);
		else
			new_time = attotime_never;
	}

	timer_adjust_oneshot(schaser_effect_555_timer, new_time, param);
	sn76477_enable_w(0, !(schaser_effect_555_is_low || schaser_explosion));
	sn76477_one_shot_cap_voltage_w(0, !(schaser_effect_555_is_low || schaser_explosion) ? 0 : SN76477_EXTERNAL_VOLTAGE_DISCONNECT);
}

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

static WRITE8_HANDLER( schaser_03_w )
{
	UINT8 effect;

    /* bit 0 - Dot Sound Enable (SX0)
       bit 1 - Dot Sound Pitch (SX1)
       bit 2 - Effect Sound A (SX2)
       bit 3 - Effect Sound B (SX3)
       bit 4 - Effect Sound C (SX4)
       bit 5 - Explosion (SX5) */

	schaser_explosion = data & 0x20 ? 1 : 0;
	discrete_sound_w(machine, INVADERS_DOT_EN, data & 0x01);
	discrete_sound_w(machine, INVADERS_DOT_SEL, data & 0x02);	/* changes if you are using fuel */

	effect = (data >> 2) & 0x07;
	if (port_1_last != effect)
	{
		if (effect)
		{
			if (attotime_compare(schaser_effect_555_time_remain, attotime_zero) != 0)
			{
				/* timer re-enabled, use up remaining 555 high time */
				timer_adjust_oneshot(schaser_effect_555_timer, schaser_effect_555_time_remain, effect);
			}
			else if (!schaser_effect_555_is_low)
			{
				/* set 555 high time */
				attotime new_time = attotime_make(0, ATTOSECONDS_PER_SECOND * .8873 * schaser_effect_rc[effect]);
				timer_adjust_oneshot(schaser_effect_555_timer, new_time, effect);
			}
		}
		else
		{
			/* disable effect - stops at end of low cycle */
			if (!schaser_effect_555_is_low)
			{
				schaser_effect_555_time_remain = timer_timeleft(schaser_effect_555_timer);
				timer_adjust_oneshot(schaser_effect_555_timer, attotime_never, 0);
			}
		}
		port_1_last = effect;
	}

	if (schaser_explosion)
		sn76477_amplitude_res_w(0, 1.0 / (1.0/RES_K(200) + 1.0/RES_K(68)));
	else
		sn76477_amplitude_res_w(0, RES_K(200));

	sn76477_enable_w(0, !(schaser_effect_555_is_low || schaser_explosion));
	sn76477_one_shot_cap_voltage_w(0, !(schaser_effect_555_is_low || schaser_explosion) ? 0 : SN76477_EXTERNAL_VOLTAGE_DISCONNECT);
	sn76477_mixer_b_w(0, schaser_explosion);

	if (data & 0xc0) printf("3=%X ",data);
}

static WRITE8_HANDLER( schaser_05_w )
{
    /* bit 0 - Music (DAC) (SX6)
       bit 1 - Sound Enable (SX7)
       bit 2 - Coin Lockout (SX8)
       bit 3 - Field Control A (SX9)
       bit 4 - Field Control B (SX10)
       bit 5 - Flip Screen */

	discrete_sound_w(machine, INVADERS_MUSIC_BIT, data & 0x01);

	discrete_sound_w(machine, INVADERS_SND_EN, data & 0x02);
	sound_global_enable(data & 0x02);

	coin_lockout_global_w(data & 0x04);

	schaser_background_disable = data & 0x08;
	schaser_background_select = data & 0x10;

	player = data & 0x20;
	flipscreen = (player<<1) & (input_port_read(machine, "IN2") & 0x40);

	if (data & 0xc0) printf("5=%X ",data);
}


static WRITE8_HANDLER( schasrcv_03_w )
{
    /* bit 1 - Dot Sound Enable
       bit 2 - Effect Sound A (a stream of pulses)
       bit 3 - Effect Sound B (a stream of pulses)
       bit 4 - Explosion (a stream of ff's with some fe's thrown in)
       bit 5 - Mostly high - may be another sound enable */

	UINT8 effect, byte;
	static UINT8 seq = 0;

	/* If you use fuel, the dot sound turns into a continuous beep.

	The effect sequence is really bizarre, however a pattern exists. */

	schaser_explosion = data & 0x10 ? 1 : 0;
	if (!schaser_explosion) discrete_sound_w(machine, INVADERS_DOT_EN, data & 0x02);	/* prevents beep during explosion */

	byte = data & 0x2c;

	if (!global_sound_enabled)				/* sound disabled */
		seq=0;
	else
	if ((seq == 0) && (global_sound_enabled))		/* normal play */
		seq=1;
	else
	if ((seq == 1) && (byte == 0x28))			/* going faster... */
		seq=2;
	else
	if ((seq == 2) && (byte == 0x24))			/* and faster... */
		seq=3;
	else
	if ((seq < 4) && (byte == 0x2c))			/* died */
		seq=4;
	else
	if ((seq > 2) && (byte == 0))				/* finished stage, and the pause before a stage starts */
		seq=5;

	if (seq == 1) effect=4;					/* normal speed */
	else
	if (seq == 2) effect=2;					/* going faster... */
	else
	if (seq == 3) effect=1;					/* and faster... */
	else          effect=0;					/* no missile on screen, or in attract mode */

	if (port_1_last != effect)
	{
		if (effect)
		{
			/* timer re-enabled, use up remaining 555 high time */
			timer_adjust_oneshot(schaser_effect_555_timer, schaser_effect_555_time_remain, effect);
		}
		else
		{
			/* disable effect - stops at end of low cycle */
			if (!schaser_effect_555_is_low)
			{
				schaser_effect_555_time_remain = timer_timeleft(schaser_effect_555_timer);
				timer_adjust_oneshot(schaser_effect_555_timer, attotime_never, 0);
			}
		}
		port_1_last = effect;
	}

	if (schaser_explosion)
		sn76477_amplitude_res_w(0, 1.0 / (1.0/RES_K(200) + 1.0/RES_K(68)));
	else
		sn76477_amplitude_res_w(0, RES_K(200));

	sn76477_enable_w(0, !(schaser_effect_555_is_low || schaser_explosion));
	sn76477_one_shot_cap_voltage_w(0, !(schaser_effect_555_is_low || schaser_explosion) ? 0 : SN76477_EXTERNAL_VOLTAGE_DISCONNECT);
	sn76477_mixer_b_w(0, schaser_explosion);

	if ((data & 0xc0) && (data < 0xfe)) printf("3=%X ",data);
}

static WRITE8_HANDLER( schasrcv_05_w )
{
    /* bit 0 - Music (DAC)
       bit 2 - Coin Lockout
       bit 4 - Sound Enable
       bit 5 - Flip Screen

	There are no field control bits. Therefore the green mask can never appear. */

	discrete_sound_w(machine, INVADERS_MUSIC_BIT, data & 0x01);
	coin_lockout_global_w(data & 0x04);

	discrete_sound_w(machine, INVADERS_SND_EN, data & 0x10);
	sound_global_enable(data & 0x10);
	global_sound_enabled = data & 0x10;

	player = data & 0x20;
	flipscreen = player & input_port_read(machine, "CAB");

	if (data & 0xca) printf("5=%X ",data);
}


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

static MACHINE_START( schaser )
{
	schaser_effect_555_timer = timer_alloc(schaser_effect_555_cb, NULL);
	MACHINE_START_CALL(invaders);
}

static MACHINE_RESET( schaser )
{
	schaser_effect_555_is_low = 0;
	timer_adjust_oneshot(schaser_effect_555_timer, attotime_never, 0);
	schaser_effect_555_time_remain = attotime_zero;
	MACHINE_RESET_CALL(invaders_common);
}

static MACHINE_RESET( schasrcv )
{
	schaser_effect_555_is_low = 1;
	timer_adjust_oneshot(schaser_effect_555_timer, attotime_never, 0);
	schaser_effect_555_time_remain = attotime_zero;
	MACHINE_RESET_CALL(invaders_common);
}


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

static READ8_HANDLER( schasrcv_02_r )
{
	UINT8 data,other;
	data = input_port_read(machine, "IN2");

	if (flipscreen) return data;

	other = input_port_read(machine, "IN1");

	return (data & 0x89) | (other & 0x70) | ((other & 0x08) >> 2) | ((other & 0x80) >> 5);
}


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

static ADDRESS_MAP_START( schaser_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_WRITENOP
	AM_RANGE(0xc000, 0xdfff) AM_MIRROR(0x00e0) AM_RAM AM_BASE(&schaser_colorram)
ADDRESS_MAP_END


static ADDRESS_MAP_START( schaser_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(0x02, 0x02) AM_WRITE(mb14241_0_shift_count_w)
	AM_RANGE(0x03, 0x03) AM_WRITE(schaser_03_w)
	AM_RANGE(0x04, 0x04) AM_WRITE(mb14241_0_shift_data_w)
	AM_RANGE(0x05, 0x05) AM_WRITE(schaser_05_w)
	AM_RANGE(0x06, 0x06) AM_WRITE(watchdog_reset_w)
ADDRESS_MAP_END

static ADDRESS_MAP_START( schasrcv_io_map, ADDRESS_SPACE_IO, 8 )
	AM_RANGE(0x01, 0x01) AM_READ_PORT("IN1")
	AM_RANGE(0x02, 0x02) AM_READ(schasrcv_02_r)
	AM_RANGE(0x03, 0x03) AM_READ(mb14241_0_shift_result_r)

	AM_RANGE(0x02, 0x02) AM_WRITE(mb14241_0_shift_count_w)
	AM_RANGE(0x03, 0x03) AM_WRITE(schasrcv_03_w)
	AM_RANGE(0x04, 0x04) AM_WRITE(mb14241_0_shift_data_w)
	AM_RANGE(0x05, 0x05) AM_WRITE(schasrcv_05_w)
	AM_RANGE(0x06, 0x06) AM_WRITE(watchdog_reset_w)
ADDRESS_MAP_END

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

static INPUT_PORTS_START( schaser )
/* The manual (in 2 places) refers to switch 7 being for upright or cocktail, yet the diagram shows switch 6. */
	PORT_START("IN0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_DIPNAME( 0x60, 0x00, "Hard Starting Level" )		PORT_DIPLOCATION("SW1:5,6")
	PORT_DIPSETTING(    0x00, "3" )
	PORT_DIPSETTING(    0x20, "4" )
	PORT_DIPSETTING(    0x40, "5" )
	PORT_DIPSETTING(    0x60, "6" )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNUSED )

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

	PORT_START("IN2")	// port 2
	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_DIPUNKNOWN_DIPLOC( 0x04, 0x00, "SW1:3" )		// does nothing, see code at 0x003A
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Difficulty ) )		PORT_DIPLOCATION("SW1:4")
	PORT_DIPSETTING(    0x00, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )
	/* Name Reset - if name of high scorer was rude, owner can press this button */
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME("Name Reset") PORT_CODE(KEYCODE_F1)
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_TILT )
	PORT_DIPNAME( 0x40, 0x00, DEF_STR( Cabinet ) )			PORT_DIPLOCATION("SW1:7")
	PORT_DIPSETTING(    0x00, DEF_STR( Upright ) )
	PORT_DIPSETTING(    0x40, DEF_STR( Cocktail ) )
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_UNKNOWN )

	PORT_START("VR1")
	PORT_ADJUSTER( 70, "VR1 - Music Volume" )

	PORT_START("VR2")
	PORT_ADJUSTER( 90, "VR2 - Explosion/Effect Volume" )

	PORT_START("VR3")
	PORT_ADJUSTER( 70, "VR3 - Dot Volume" )
INPUT_PORTS_END

static INPUT_PORTS_START( schasrcv )
/* The code reads bit 0 and 1 of IN2 when determining lives. It also reads bit 4 of IN1 when determining
	the start level of Hard Difficulty. */	
	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_JOYSTICK_DOWN ) PORT_4WAY
	PORT_BIT( 0x10, IP_ACTIVE_HIGH, IPT_BUTTON1 )
	PORT_BIT( 0x20, IP_ACTIVE_HIGH, IPT_JOYSTICK_LEFT ) PORT_4WAY
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_4WAY
	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_4WAY

	PORT_START("IN2")
	PORT_DIPNAME( 0x01, 0x00, DEF_STR( Lives ) )					PORT_DIPLOCATION("SW1:1")
	PORT_DIPSETTING(    0x00, "3" )
	PORT_DIPSETTING(    0x01, "4" )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_JOYSTICK_DOWN ) PORT_4WAY PORT_PLAYER(2)
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_JOYSTICK_UP ) PORT_4WAY PORT_PLAYER(2)
	PORT_DIPNAME( 0x08, 0x00, DEF_STR( Difficulty ) )				PORT_DIPLOCATION("SW1:2")
	PORT_DIPSETTING(    0x00, DEF_STR( Easy ) )
	PORT_DIPSETTING(    0x08, DEF_STR( Hard ) )
	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_4WAY PORT_PLAYER(2)	PORT_DIPLOCATION("SW1:6")
	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_JOYSTICK_RIGHT ) PORT_4WAY PORT_PLAYER(2)	PORT_DIPLOCATION("SW1:7")
	PORT_DIPUNKNOWN_DIPLOC( 0x80, 0x00, "SW1:3" )			/* doesn't do anything */

	PORT_START("VR1")
	PORT_ADJUSTER( 70, "VR1 - Music Volume" )

	PORT_START("VR2")
	PORT_ADJUSTER( 90, "VR2 - Explosion/Effect Volume" )

	PORT_START("VR3")
	PORT_ADJUSTER( 70, "VR3 - Dot Volume" )

	INVADERS_CAB(0x20)
INPUT_PORTS_END

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

static MACHINE_DRIVER_START( schaser )

	/* basic machine hardware */
	MDRV_CPU_ADD("main",8080,INVADERS_CPU_CLOCK)
	MDRV_CPU_PROGRAM_MAP(schaser_map,0)
	MDRV_CPU_IO_MAP(schaser_io_map,0)
	MDRV_WATCHDOG_VBLANK_INIT(255)
	MDRV_MACHINE_START(schaser)
	MDRV_MACHINE_RESET(schaser)

	/* video hardware */
	MDRV_VIDEO_UPDATE(schaser)
	MDRV_SCREEN_ADD("main", RASTER)
	MDRV_SCREEN_FORMAT(BITMAP_FORMAT_RGB32)
	MDRV_SCREEN_RAW_PARAMS(INVADERS_PIXEL_CLOCK, INVADERS_HTOTAL, INVADERS_HBEND, SCHASER_HPIXCOUNT, INVADERS_VTOTAL, INVADERS_VBEND, INVADERS_VBSTART)

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

	MDRV_SOUND_ADD("sn", SN76477, 0)
	MDRV_SOUND_CONFIG(schaser_sn76477_interface)
	MDRV_SOUND_ROUTE_EX(0, "discrete", 1.0, 0)

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

static MACHINE_DRIVER_START( schasrcv )
	MDRV_IMPORT_FROM(schaser)
	MDRV_CPU_MODIFY("main")
	MDRV_CPU_IO_MAP(schasrcv_io_map,0)
	MDRV_MACHINE_RESET(schasrcv)
	MDRV_VIDEO_UPDATE(schasrcv)
MACHINE_DRIVER_END

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

ROM_START( schaser )
	ROM_REGION( 0x10000, "main", 0 )
	ROM_LOAD( "rt13.bin",     0x0000, 0x0400, CRC(0dfbde68) SHA1(7367b138ad8448aba9222fed632a892df65cecbd) )
	ROM_LOAD( "rt14.bin",     0x0400, 0x0400, CRC(5a508a25) SHA1(c681d0bbf49317e79b596fb094e66b8912f0e409) )
	ROM_LOAD( "rt15.bin",     0x0800, 0x0400, CRC(2ac43a93) SHA1(d364f0940681a888c0147e06bcb01f8a0d4a24c8) )
	ROM_LOAD( "rt16.bin",     0x0c00, 0x0400, CRC(f5583afc) SHA1(5e8edb43ccb138fd47ac8f3da1af79b4444a4a82) )
	ROM_LOAD( "rt17.bin",     0x1000, 0x0400, CRC(51cf1155) SHA1(fd8c82d951602fd7e0ada65fc7cdee9f277c70db) )
	ROM_LOAD( "rt18.bin",     0x1400, 0x0400, CRC(3f0fc73a) SHA1(b801c3f1e8e6e41c564432db7c5891f6b27293b2) )
	ROM_LOAD( "rt19.bin",     0x1800, 0x0400, CRC(b66ea369) SHA1(d277f572f9c7c4301518546cf60671a6539326ee) )
	ROM_LOAD( "rt20.bin",     0x1c00, 0x0400, CRC(e3a7466a) SHA1(2378970f38b0cec066ef853a6540500e468e4ab4) )
	ROM_LOAD( "rt21.bin",     0x4000, 0x0400, CRC(b368ac98) SHA1(6860efe0496955db67611183be0efecda92c9c98) )
	ROM_LOAD( "rt22.bin",     0x4400, 0x0400, CRC(6e060dfb) SHA1(614e2ecf676c3ea2f9ea869125cfffef2f713684) )
	ROM_FILL( 0x47e5, 1, 0xc3)	// fix scoring issues on Hard Difficulty

	ROM_REGION( 0x0400, "proms", 0 )		/* background color map */
	ROM_LOAD( "rt06.ic2",     0x0000, 0x0400, CRC(950cf973) SHA1(d22df09b325835a0057ccd0d54f827b374254ac6) )
ROM_END

ROM_START( schasrcv )
	ROM_REGION( 0x10000, "main", 0 )
	ROM_LOAD( "schasrcv.1",	  0x0000, 0x0400, CRC(bec2b16b) SHA1(c62210ecb64d7c38e5b63481d7fe04eb59bb1068) )
	ROM_LOAD( "schasrcv.2",	  0x0400, 0x0400, CRC(9d25e608) SHA1(4cc52a93a3ab96a0ec1d07593e17832fa59b30a1) )
	ROM_LOAD( "schasrcv.3",	  0x0800, 0x0400, CRC(113d0635) SHA1(ab5e98d0b5fc37d7d69bb5c541681a0f66460440) )
	ROM_LOAD( "schasrcv.4",	  0x0c00, 0x0400, CRC(f3a43c8d) SHA1(29a7a8b7d1de763a255cfec79157fd95e7bff551) )
	ROM_LOAD( "schasrcv.5",	  0x1000, 0x0400, CRC(47c84f23) SHA1(61b475fa92b8335f8edd3a128d8ac8561658e464) )
	ROM_LOAD( "schasrcv.6",	  0x1400, 0x0400, CRC(02ff2199) SHA1(e12c235b2064cb4bb426145172e523256e3c6358) )
	ROM_LOAD( "schasrcv.7",	  0x1800, 0x0400, CRC(87d06b88) SHA1(2d743161f85e47cb8ee2a600cbee790b1ad7ad99) )
	ROM_LOAD( "schasrcv.8",	  0x1c00, 0x0400, CRC(6dfaad08) SHA1(2184c4e2f4b6bffdc4fe13e178134331fcd43253) )
	ROM_LOAD( "schasrcv.9",	  0x4000, 0x0400, CRC(3d1a2ae3) SHA1(672ad6590aebdfebc2748455fa638107f3934c41) )
	ROM_LOAD( "schasrcv.10",  0x4400, 0x0400, CRC(037edb99) SHA1(f2fc5e61f962666e7f6bb81753ac24ea0b97e581) )

	/* the pcb had proms for some other game - the correct one(s) have not been found */
ROM_END

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

/* board #  rom       parent    machine   inp */

/* Taito games */
GAME( 1979, schaser,  0,        schaser,  schaser,  0, ROT270, "Taito", "Space Chaser", 0 )
GAME( 1979, schasrcv, schaser,  schasrcv, schasrcv, 0, ROT270, "Taito", "Space Chaser (CV version)", 0 )
