// license:BSD-3-Clause
// copyright-holders:hap
// thanks-to:Sean Riddle
/***************************************************************************

National Semiconductor COPS(COP400 MCU series) handhelds or other simple
devices, mostly LED electronic games/toys.

TODO:
- why does h2hbaskbc(and clones) need a workaround on writing L pins?
- plus1: which sensor position is which colour?
- vidchal: Add screen and gun cursor with brightness detection callback,
  and softwarelist for the video tapes. We'd also need a VHS player device.
  The emulated lightgun itself appears to be working fine(eg. add a 30hz
  timer to IN.3 to score +100)

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

#include "emu.h"

#include "cpu/cop400/cop400.h"
#include "video/pwm.h"
#include "machine/timer.h"
#include "sound/spkrdev.h"
#include "sound/dac.h"

#include "screen.h"
#include "speaker.h"

// internal artwork
#include "bship82.lh" // clickable
#include "ctstein.lh" // clickable
#include "einvaderc.lh"
#include "funjacks.lh" // clickable
#include "funrlgl.lh" // clickable
#include "funtag.lh" // clickable
#include "h2hbaskbc.lh"
#include "h2hhockeyc.lh"
#include "h2hsoccerc.lh"
#include "lchicken.lh" // clickable
#include "lightfgt.lh" // clickable
#include "mdallas.lh"
#include "qkracer.lh"
#include "unkeinv.lh"
#include "vidchal.lh"

//#include "hh_cop400_test.lh" // common test-layout - use external artwork


class hh_cop400_state : public driver_device
{
public:
	hh_cop400_state(const machine_config &mconfig, device_type type, const char *tag) :
		driver_device(mconfig, type, tag),
		m_maincpu(*this, "maincpu"),
		m_display(*this, "display"),
		m_speaker(*this, "speaker"),
		m_inputs(*this, "IN.%u", 0)
	{ }

	// devices
	required_device<cop400_cpu_device> m_maincpu;
	optional_device<pwm_display_device> m_display;
	optional_device<speaker_sound_device> m_speaker;
	optional_ioport_array<6> m_inputs; // max 6

	// misc common
	u8 m_l;                         // MCU port L write data
	u8 m_g;                         // MCU port G write data
	u8 m_d;                         // MCU port D write data
	int m_so;                       // MCU SO line state
	int m_sk;                       // MCU SK line state
	u16 m_inp_mux;                  // multiplexed inputs mask

	u16 read_inputs(int columns, u16 colmask = ~0);
	virtual DECLARE_INPUT_CHANGED_MEMBER(reset_button);

protected:
	virtual void machine_start() override;
	virtual void machine_reset() override;
};


// machine start/reset

void hh_cop400_state::machine_start()
{
	// zerofill
	m_l = 0;
	m_g = 0;
	m_d = 0;
	m_so = 0;
	m_sk = 0;
	m_inp_mux = ~0;

	// register for savestates
	save_item(NAME(m_l));
	save_item(NAME(m_g));
	save_item(NAME(m_d));
	save_item(NAME(m_so));
	save_item(NAME(m_sk));
	save_item(NAME(m_inp_mux));
}

void hh_cop400_state::machine_reset()
{
}



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

  Helper Functions

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

// generic input handlers

u16 hh_cop400_state::read_inputs(int columns, u16 colmask)
{
	// active low
	u16 ret = ~0 & colmask;

	// read selected input rows
	for (int i = 0; i < columns; i++)
		if (~m_inp_mux >> i & 1)
			ret &= m_inputs[i]->read();

	return ret;
}

INPUT_CHANGED_MEMBER(hh_cop400_state::reset_button)
{
	// when an input is directly wired to MCU reset pin
	m_maincpu->set_input_line(INPUT_LINE_RESET, newval ? ASSERT_LINE : CLEAR_LINE);
}



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

  Minidrivers (subclass, I/O, Inputs, Machine Config, ROM Defs)

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

namespace {

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

  Castle Toy Einstein
  * COP421 MCU label ~/927 COP421-NEZ/N
  * 4 lamps, 1-bit sound

  This is a Simon clone, the tones are not harmonic. Two models exist, each
  with a different batteries setup, assume they're same otherwise.

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

class ctstein_state : public hh_cop400_state
{
public:
	ctstein_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void write_g(u8 data);
	void write_l(u8 data);
	u8 read_l();
	void ctstein(machine_config &config);
};

// handlers

void ctstein_state::write_g(u8 data)
{
	// G0-G2: input mux
	m_inp_mux = data & 7;
}

void ctstein_state::write_l(u8 data)
{
	// L0-L3: button lamps
	m_display->matrix(1, data & 0xf);
}

u8 ctstein_state::read_l()
{
	// L4-L7: multiplexed inputs
	return read_inputs(3, 0xf) << 4 | 0xf;
}

// config

static INPUT_PORTS_START( ctstein )
	PORT_START("IN.0") // G0 port L
	PORT_CONFNAME( 0x0f, 0x01^0x0f, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01^0x0f, "1" )
	PORT_CONFSETTING(    0x02^0x0f, "2" )
	PORT_CONFSETTING(    0x04^0x0f, "3" )
	PORT_CONFSETTING(    0x08^0x0f, "4" )

	PORT_START("IN.1") // G1 port L
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_SELECT ) PORT_NAME("Best Score")
	PORT_BIT( 0x0c, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.2") // G2 port L
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Red Button")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Yellow Button")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Green Button")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Blue Button")
INPUT_PORTS_END

void ctstein_state::ctstein(machine_config &config)
{
	/* basic machine hardware */
	COP421(config, m_maincpu, 850000); // approximation - RC osc. R=12K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_g().set(FUNC(ctstein_state::write_g));
	m_maincpu->write_l().set(FUNC(ctstein_state::write_l));
	m_maincpu->write_sk().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_maincpu->read_l().set(FUNC(ctstein_state::read_l));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(1, 4);
	config.set_default_layout(layout_ctstein);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( ctstein )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421-nez_n", 0x0000, 0x0400, CRC(16148e03) SHA1(b2b74891d36813d9a1eefd56a925054997c4b7f7) ) // 2nd half empty
ROM_END





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

  Coleco Head to Head: Electronic Basketball/Hockey/Soccer (model 2150/2160/2170)
  * COP420 MCU label COP420L-NEZ/N
  * 2-digit 7seg display, 41 other leds, 1-bit sound

  3 Head to Head games were released using this MCU/ROM. They play very much
  the same, only differing on game time. The PCB is pre-configured on G1+IN2
  and IN3 to select the game.

  An earlier revision of this runs on TMS1000, see hh_tms1k.cpp driver. Model
  numbers are the same. From the outside, an easy way to spot the difference is
  the Start/Display button: TMS1000 version button label is D, COP420 one is a *.
  The COP420 version also plays much slower.

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

class h2hbaskbc_state : public hh_cop400_state
{
public:
	h2hbaskbc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void write_d(u8 data);
	void write_g(u8 data);
	void write_l(u8 data);
	u8 read_in();
	void h2hsoccerc(machine_config &config);
	void h2hbaskbc(machine_config &config);
	void h2hhockeyc(machine_config &config);
};

// handlers

void h2hbaskbc_state::write_d(u8 data)
{
	// D: led select
	m_d = data & 0xf;
}

void h2hbaskbc_state::write_g(u8 data)
{
	// G: led select, input mux
	m_inp_mux = data;
	m_g = data & 0xf;
}

void h2hbaskbc_state::write_l(u8 data)
{
	// D2,D3 double as multiplexer
	u16 mask = ((m_d >> 2 & 1) * 0x00ff) | ((m_d >> 3 & 1) * 0xff00);
	u16 sel = (m_g | m_d << 4 | m_g << 8 | m_d << 12) & mask;

	// D2+G0,G1 are 7segs
	// L0-L6: digit segments A-G, L0-L4: led data
	m_display->matrix(sel, data);
}

u8 h2hbaskbc_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(4, 7) | (m_inputs[4]->read() & 8);
}

// config

static INPUT_PORTS_START( h2hbaskbc )
	PORT_START("IN.0") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY PORT_NAME("P1 Pass CW") // clockwise
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY PORT_NAME("P1 Pass CCW") // counter-clockwise
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Players ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.1") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("P1 Shoot")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START ) PORT_NAME("Start/Display")
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_CUSTOM ) // factory set

	PORT_START("IN.2") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Defense Right")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_COCKTAIL PORT_16WAY PORT_NAME("P2 Defense Left")
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )

	PORT_START("IN.3") // G3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_CONFNAME( 0x04, 0x04, "Factory Test" )
	PORT_CONFSETTING(    0x04, DEF_STR( Off ) )
	PORT_CONFSETTING(    0x00, DEF_STR( On ) )

	PORT_START("IN.4") // IN3 (factory set)
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( h2hhockeyc )
	PORT_INCLUDE( h2hbaskbc )

	PORT_MODIFY("IN.3")
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_COCKTAIL PORT_NAME("P2 Goalie Right")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL PORT_NAME("P2 Goalie Left")

	PORT_MODIFY("IN.4")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

static INPUT_PORTS_START( h2hsoccerc )
	PORT_INCLUDE( h2hhockeyc )

	PORT_MODIFY("IN.1")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_CUSTOM )
INPUT_PORTS_END

void h2hbaskbc_state::h2hbaskbc(machine_config &config)
{
	/* basic machine hardware */
	COP420(config, m_maincpu, 1000000); // approximation - RC osc. R=43K, C=101pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(h2hbaskbc_state::write_d));
	m_maincpu->write_g().set(FUNC(h2hbaskbc_state::write_g));
	m_maincpu->write_l().set(FUNC(h2hbaskbc_state::write_l));
	m_maincpu->read_in().set(FUNC(h2hbaskbc_state::read_in));
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(16, 7);
	m_display->set_segmask(3, 0x7f);
	config.set_default_layout(layout_h2hbaskbc);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

void h2hbaskbc_state::h2hhockeyc(machine_config &config)
{
	h2hbaskbc(config);
	config.set_default_layout(layout_h2hhockeyc);
}

void h2hbaskbc_state::h2hsoccerc(machine_config &config)
{
	h2hbaskbc(config);
	config.set_default_layout(layout_h2hsoccerc);
}

// roms

ROM_START( h2hbaskbc )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420l-nmy", 0x0000, 0x0400, CRC(87152509) SHA1(acdb869b65d49b3b9855a557ed671cbbb0f61e2c) )
ROM_END

#define rom_h2hhockeyc rom_h2hbaskbc // dumped from Basketball
#define rom_h2hsoccerc rom_h2hbaskbc // "





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

  Entex Space Invader
  * COP444L MCU label /B138 COPL444-HRZ/N INV II (die label HRZ COP 444L/A)
  * 3 7seg LEDs, LED matrix and overlay mask, 1-bit sound

  The first version was on TMS1100 (see hh_tms1k.c), this is the reprogrammed
  second release with a gray case instead of black.

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

class einvaderc_state : public hh_cop400_state
{
public:
	einvaderc_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_d(u8 data);
	void write_g(u8 data);
	DECLARE_WRITE_LINE_MEMBER(write_sk);
	DECLARE_WRITE_LINE_MEMBER(write_so);
	void write_l(u8 data);
	void einvaderc(machine_config &config);
};

// handlers

void einvaderc_state::update_display()
{
	u8 l = bitswap<8>(m_l,7,6,0,1,2,3,4,5);
	u16 grid = (m_d | m_g << 4 | m_sk << 8 | m_so << 9) ^ 0x0ff;

	m_display->matrix(grid, l);
}

void einvaderc_state::write_d(u8 data)
{
	// D: led grid 0-3 (D0-D2 are 7segs)
	m_d = data;
	update_display();
}

void einvaderc_state::write_g(u8 data)
{
	// G: led grid 4-7
	m_g = data;
	update_display();
}

WRITE_LINE_MEMBER(einvaderc_state::write_sk)
{
	// SK: speaker out + led grid 8
	m_speaker->level_w(state);
	m_sk = state;
	update_display();
}

WRITE_LINE_MEMBER(einvaderc_state::write_so)
{
	// SO: led grid 9
	m_so = state;
	update_display();
}

void einvaderc_state::write_l(u8 data)
{
	// L: led state/segment
	m_l = data;
	update_display();
}

// config

static INPUT_PORTS_START( einvaderc )
	PORT_START("IN.0") // port IN
	PORT_CONFNAME( 0x01, 0x01, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x01, "Amateur" )
	PORT_CONFSETTING(    0x00, "Professional" )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_16WAY
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_16WAY
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON1 )
INPUT_PORTS_END

void einvaderc_state::einvaderc(machine_config &config)
{
	/* basic machine hardware */
	COP444L(config, m_maincpu, 850000); // approximation - RC osc. R=47K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->read_in().set_ioport("IN.0");
	m_maincpu->write_d().set(FUNC(einvaderc_state::write_d));
	m_maincpu->write_g().set(FUNC(einvaderc_state::write_g));
	m_maincpu->write_sk().set(FUNC(einvaderc_state::write_sk));
	m_maincpu->write_so().set(FUNC(einvaderc_state::write_so));
	m_maincpu->write_l().set(FUNC(einvaderc_state::write_l));

	/* video hardware */
	screen_device &mask(SCREEN(config, "mask", SCREEN_TYPE_SVG));
	mask.set_refresh_hz(60);
	mask.set_size(919, 1080);
	mask.set_visarea_full();

	PWM_DISPLAY(config, m_display).set_size(10, 8);
	m_display->set_segmask(7, 0x7f);
	config.set_default_layout(layout_einvaderc);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( einvaderc )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "copl444-hrz_n_inv_ii", 0x0000, 0x0800, CRC(76400f38) SHA1(0e92ab0517f7b7687293b189d30d57110df20fe0) )

	ROM_REGION( 82104, "mask", 0)
	ROM_LOAD( "einvaderc.svg", 0, 82104, CRC(0013227f) SHA1(44a3ac48c947369231f010559331ad16fcbef7be) )
ROM_END





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

  Gordon Barlow Design electronic Space Invaders game (unreleased, from patent US4345764)
  * COP421 (likely a development chip)
  * 36+9 LEDs, 1-bit sound

  This game is presumedly unreleased. The title is unknown, the patent simply names
  it "Hand-held electronic game". There is no mass-manufacture company assigned
  to it either. The game seems unfinished(no scorekeeping, some bugs), and the design
  is very complex. Player ship and bullets are on a moving "wand", a 2-way mirror
  makes it appear on the same plane as the enemies and barriers.

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

class unkeinv_state : public hh_cop400_state
{
public:
	unkeinv_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_g(u8 data);
	void write_d(u8 data);
	void write_l(u8 data);
	u8 read_l();

	void unkeinv(machine_config &config);
};

// handlers

void unkeinv_state::update_display()
{
	m_display->matrix(m_g << 4 | m_d, m_l);
}

void unkeinv_state::write_g(u8 data)
{
	// G0,G1: led select part
	// G2,G3: input mux
	m_g = ~data & 0xf;
	update_display();
}

void unkeinv_state::write_d(u8 data)
{
	// D0-D3: led select part
	m_d = ~data & 0xf;
	update_display();
}

void unkeinv_state::write_l(u8 data)
{
	// L0-L7: led data
	m_l = ~data & 0xff;
	update_display();
}

u8 unkeinv_state::read_l()
{
	u8 ret = 0xff;

	// L0-L5+G2: positional odd
	// L0-L5+G3: positional even
	u8 pos = m_inputs[1]->read();
	if (m_g & 4 && pos & 1)
		ret ^= (1 << (pos >> 1));
	if (m_g & 8 && ~pos & 1)
		ret ^= (1 << (pos >> 1));

	// L7+G3: fire button
	if (m_g & 8 && m_inputs[0]->read())
		ret ^= 0x80;

	return ret & ~m_l;
}

// config

static INPUT_PORTS_START( unkeinv )
	PORT_START("IN.0")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_BUTTON1 )

	PORT_START("IN.1")
	PORT_BIT( 0x0f, 0x00, IPT_POSITIONAL ) PORT_POSITIONS(12) PORT_SENSITIVITY(10) PORT_KEYDELTA(1) PORT_CENTERDELTA(0)
INPUT_PORTS_END

void unkeinv_state::unkeinv(machine_config &config)
{
	/* basic machine hardware */
	COP421(config, m_maincpu, 850000); // frequency guessed
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_g().set(FUNC(unkeinv_state::write_g));
	m_maincpu->write_d().set(FUNC(unkeinv_state::write_d));
	m_maincpu->write_l().set(FUNC(unkeinv_state::write_l));
	m_maincpu->read_l().set(FUNC(unkeinv_state::read_l));
	m_maincpu->read_l_tristate().set_constant(0xff);
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(6, 8);
	config.set_default_layout(layout_unkeinv);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( unkeinv )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421_us4345764", 0x0000, 0x0400, CRC(0068c3a3) SHA1(4e5fd566a5a26c066cc14623a9bd01e109ebf797) ) // typed in from patent US4345764, good print quality
ROM_END





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

  LJN I Took a Lickin' From a Chicken
  * COP421 MCU label ~/005 COP421-NJC/N
  * 11 leds, 1-bit sound, motor to a chicken on a spring

  This toy includes 4 games: Tic Tac Toe, Chicken Sez, and Total Recall I/II.

  known releases:
  - USA: I Took a Lickin' From a Chicken, published by LJN
  - Japan: Professor Chicken's Genius Classroom 「にわとり博士の天才教室」, published by Bandai
    (not sure if it's the same ROM, or just licensed the outer shell)

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

class lchicken_state : public hh_cop400_state
{
public:
	lchicken_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	u8 m_motor_pos;
	TIMER_DEVICE_CALLBACK_MEMBER(motor_sim_tick);
	DECLARE_READ_LINE_MEMBER(motor_switch_r);

	void write_l(u8 data);
	void write_d(u8 data);
	void write_g(u8 data);
	u8 read_g();
	DECLARE_WRITE_LINE_MEMBER(write_so);
	DECLARE_READ_LINE_MEMBER(read_si);
	void lchicken(machine_config &config);

protected:
	virtual void machine_start() override;
};

void lchicken_state::machine_start()
{
	hh_cop400_state::machine_start();

	// zerofill, register for savestates
	m_motor_pos = 0;
	save_item(NAME(m_motor_pos));
}

// handlers

READ_LINE_MEMBER(lchicken_state::motor_switch_r)
{
	return m_motor_pos > 0xe8; // approximation
}

TIMER_DEVICE_CALLBACK_MEMBER(lchicken_state::motor_sim_tick)
{
	if (~m_inp_mux & 8)
	{
		m_motor_pos++;
		output().set_value("motor_pos", 100 * (m_motor_pos / (float)0x100));
	}
}

void lchicken_state::write_l(u8 data)
{
	// L0-L3: led data
	// L4-L6: led select
	// L7: N/C
	m_display->matrix(data >> 4 & 7, ~data & 0xf);
}

void lchicken_state::write_d(u8 data)
{
	// D0-D3: input mux
	// D3: motor on
	m_inp_mux = data & 0xf;
	output().set_value("motor_on", ~data >> 3 & 1);
}

void lchicken_state::write_g(u8 data)
{
	m_g = data;
}

u8 lchicken_state::read_g()
{
	// G0-G3: multiplexed inputs
	return read_inputs(4, m_g);
}

WRITE_LINE_MEMBER(lchicken_state::write_so)
{
	// SO: speaker out
	m_speaker->level_w(state);
	m_so = state;
}

READ_LINE_MEMBER(lchicken_state::read_si)
{
	// SI: SO
	return m_so;
}

// config

static INPUT_PORTS_START( lchicken )
	PORT_START("IN.0") // D0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // D1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.2") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.3") // D3 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(lchicken_state, motor_switch_r)
INPUT_PORTS_END

void lchicken_state::lchicken(machine_config &config)
{
	/* basic machine hardware */
	COP421(config, m_maincpu, 850000); // approximation - RC osc. R=12K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_l().set(FUNC(lchicken_state::write_l));
	m_maincpu->write_d().set(FUNC(lchicken_state::write_d));
	m_maincpu->write_g().set(FUNC(lchicken_state::write_g));
	m_maincpu->read_g().set(FUNC(lchicken_state::read_g));
	m_maincpu->write_so().set(FUNC(lchicken_state::write_so));
	m_maincpu->read_si().set(FUNC(lchicken_state::read_si));

	TIMER(config, "chicken_motor").configure_periodic(FUNC(lchicken_state::motor_sim_tick), attotime::from_msec(6000/0x100)); // ~6sec for a full rotation

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(3, 4);
	config.set_default_layout(layout_lchicken);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( lchicken )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421-njc_n", 0x0000, 0x0400, CRC(319e7985) SHA1(9714327518f65ebefe38ac7911bed2b9b9c77307) )
ROM_END





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

  Mattel Funtronics: Jacks (model 1603)
  * COP410L MCU bonded directly to PCB (die label COP410L/B NGS)
  * 8 LEDs, 1-bit sound

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

class funjacks_state : public hh_cop400_state
{
public:
	funjacks_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_g(u8 data);
	u8 read_l();
	u8 read_g();
	void funjacks(machine_config &config);
};

// handlers

void funjacks_state::update_display()
{
	m_display->matrix(m_d, m_l);
}

void funjacks_state::write_d(u8 data)
{
	// D: led grid + input mux
	m_inp_mux = data;
	m_d = ~data & 0xf;
	update_display();
}

void funjacks_state::write_l(u8 data)
{
	// L0,L1: led state
	m_l = data & 3;
	update_display();
}

void funjacks_state::write_g(u8 data)
{
	// G1: speaker out
	m_speaker->level_w(data >> 1 & 1);
	m_g = data;
}

u8 funjacks_state::read_l()
{
	// L4,L5: multiplexed inputs
	return read_inputs(3, 0x30) | m_l;
}

u8 funjacks_state::read_g()
{
	// G1: speaker out state
	// G2,G3: inputs
	return m_inputs[3]->read() | (m_g & 2);
}

// config

static INPUT_PORTS_START( funjacks )
	PORT_START("IN.0") // D0 port L
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON3 )

	PORT_START("IN.1") // D1 port L
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON4 )

	PORT_START("IN.2") // D2 port L
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 ) // positioned at 1 o'clock on panel, increment clockwise
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON6 )

	PORT_START("IN.3") // port G
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_CUSTOM ) // speaker
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START )
	PORT_CONFNAME( 0x08, 0x08, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x08, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

void funjacks_state::funjacks(machine_config &config)
{
	/* basic machine hardware */
	COP410(config, m_maincpu, 850000); // approximation - RC osc. R=47K, C=56pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(funjacks_state::write_d));
	m_maincpu->write_l().set(FUNC(funjacks_state::write_l));
	m_maincpu->write_g().set(FUNC(funjacks_state::write_g));
	m_maincpu->read_l().set(FUNC(funjacks_state::read_l));
	m_maincpu->read_g().set(FUNC(funjacks_state::read_g));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(4, 2);
	config.set_default_layout(layout_funjacks);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( funjacks )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_ngs", 0x0000, 0x0200, CRC(863368ea) SHA1(f116cc27ae721b3a3e178fa13765808bdc275663) )
ROM_END





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

  Mattel Funtronics: Red Light Green Light (model 1604)
  * COP410L MCU bonded directly to PCB (die label COP410L/B NHZ)
  * 14 LEDs, 1-bit sound

  known releases:
  - USA: Funtronics: Red Light Green Light, published by Mattel
  - USA(rerelease): Funtronics: Hot Wheels Drag Race, published by Mattel

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

class funrlgl_state : public hh_cop400_state
{
public:
	funrlgl_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_g(u8 data);
	void funrlgl(machine_config &config);
};

// handlers

void funrlgl_state::update_display()
{
	m_display->matrix(m_d, m_l);
}

void funrlgl_state::write_d(u8 data)
{
	// D: led grid
	m_d = ~data & 0xf;
	update_display();
}

void funrlgl_state::write_l(u8 data)
{
	// L0-L3: led state
	// L4-L7: N/C
	m_l = ~data & 0xf;
	update_display();
}

void funrlgl_state::write_g(u8 data)
{
	// G3: speaker out
	m_speaker->level_w(data >> 3 & 1);
}

// config

static INPUT_PORTS_START( funrlgl )
	PORT_START("IN.0") // port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1)
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2)
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("RESET")
	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_START ) PORT_CHANGED_MEMBER(DEVICE_SELF, hh_cop400_state, reset_button, 0)
INPUT_PORTS_END

void funrlgl_state::funrlgl(machine_config &config)
{
	/* basic machine hardware */
	COP410(config, m_maincpu, 800000); // approximation - RC osc. R=51K, C=91pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(funrlgl_state::write_d));
	m_maincpu->write_l().set(FUNC(funrlgl_state::write_l));
	m_maincpu->read_l_tristate().set_constant(0xff);
	m_maincpu->write_g().set(FUNC(funrlgl_state::write_g));
	m_maincpu->read_g().set_ioport("IN.0");

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(4, 4);
	m_display->set_bri_levels(0.005, 0.1); // top led is brighter
	config.set_default_layout(layout_funrlgl);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( funrlgl )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_nhz", 0x0000, 0x0200, CRC(4065c3ce) SHA1(f0bc8125d922949e0d7ab1ba89c805a836d20e09) )
ROM_END





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

  Mattel Funtronics: Tag (model 1497)
  * COP410L MCU bonded directly to PCB (die label COP410L/B GTJ)
  * 7 LEDs, 7 buttons, 1-bit sound

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

class funtag_state : public hh_cop400_state
{
public:
	funtag_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	void write_g(u8 data);
	u8 read_l();
	u8 read_g();
	void funtag(machine_config &config);
};

// handlers

void funtag_state::update_display()
{
	m_display->matrix(m_d, m_l);
}

void funtag_state::write_d(u8 data)
{
	// D: led grid + input mux
	m_inp_mux = data;
	m_d = ~data & 0xf;
	update_display();
}

void funtag_state::write_l(u8 data)
{
	// L0,L1: led state
	m_l = data & 3;
	update_display();
}

void funtag_state::write_g(u8 data)
{
	// G2: speaker out
	m_speaker->level_w(data >> 2 & 1);
}

u8 funtag_state::read_l()
{
	// L2: difficulty switch
	return m_inputs[4]->read() | 8;
}

u8 funtag_state::read_g()
{
	// G0,G1: multiplexed inputs
	// G3: start button
	return read_inputs(3, 3) | m_inputs[3]->read() | 4;
}

// config

static INPUT_PORTS_START( funtag )
	PORT_START("IN.0") // D0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 )

	PORT_START("IN.1") // D1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 )

	PORT_START("IN.2") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON6 )

	PORT_START("IN.3") // port G
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START )

	PORT_START("IN.4") // port L
	PORT_CONFNAME( 0x04, 0x04, DEF_STR( Difficulty ) )
	PORT_CONFSETTING(    0x04, "1" )
	PORT_CONFSETTING(    0x00, "2" )
INPUT_PORTS_END

void funtag_state::funtag(machine_config &config)
{
	/* basic machine hardware */
	COP410(config, m_maincpu, 1000000); // approximation - RC osc. R=47K, C=91pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(funtag_state::write_d));
	m_maincpu->write_l().set(FUNC(funtag_state::write_l));
	m_maincpu->write_g().set(FUNC(funtag_state::write_g));
	m_maincpu->read_l().set(FUNC(funtag_state::read_l));
	m_maincpu->read_g().set(FUNC(funtag_state::read_g));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(4, 2);
	config.set_default_layout(layout_funtag);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( funtag )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_gtj", 0x0000, 0x0200, CRC(ce565da6) SHA1(34e5f39e32f220007d353c93787c1a6d117592c1) )
ROM_END





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

  Mattel Dalla$ (J.R. handheld)
  * COP444 MCU label COP444L-HYN/N
  * 8-digit 7seg display, 1-bit sound

  This is a board game, only the handheld device is emulated here.

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

class mdallas_state : public hh_cop400_state
{
public:
	mdallas_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_l(u8 data);
	void write_d(u8 data);
	void write_g(u8 data);
	u8 read_in();
	void mdallas(machine_config &config);
};

// handlers

void mdallas_state::update_display()
{
	m_display->matrix(~(m_d << 4 | m_g), m_l);
}

void mdallas_state::write_l(u8 data)
{
	// L: digit segment data
	m_l = data;
	update_display();
}

void mdallas_state::write_d(u8 data)
{
	// D: select digit, input mux high
	m_inp_mux = (m_inp_mux & 0xf) | (data << 4 & 0x30);
	m_d = data & 0xf;
	update_display();
}

void mdallas_state::write_g(u8 data)
{
	// G: select digit, input mux low
	m_inp_mux = (m_inp_mux & 0x30) | (data & 0xf);
	m_g = data & 0xf;
	update_display();
}

u8 mdallas_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(6, 0xf);
}

// config

/* physical button layout and labels is like this:

    <  ON>  [YES]   [NO]   [NEXT]
    [<W]    [^N]    [Sv]   [E>]
    [7]     [8]     [9]    [STATUS]
    [4]     [5]     [6]    [ASSETS]
    [1]     [2]     [3]    [START]
    [CLEAR] [0]     [MOVE] [ENTER]
*/

static INPUT_PORTS_START( mdallas )
	PORT_START("IN.0") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_CODE(KEYCODE_DEL) PORT_NAME("Clear")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_M) PORT_NAME("Move")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("Enter")

	PORT_START("IN.1") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_S) PORT_NAME("Start")

	PORT_START("IN.2") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("Assets")

	PORT_START("IN.3") // G3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_T) PORT_NAME("Status")

	PORT_START("IN.4") // D0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("West") // W
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_X) PORT_NAME("Next")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("East") // E

	PORT_START("IN.5") // D1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_N) PORT_NAME("No")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Y) PORT_NAME("Yes")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("South") // S
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("North") // N
INPUT_PORTS_END

void mdallas_state::mdallas(machine_config &config)
{
	/* basic machine hardware */
	COP444L(config, m_maincpu, 1000000); // approximation - RC osc. R=57K, C=101pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_l().set(FUNC(mdallas_state::write_l));
	m_maincpu->write_d().set(FUNC(mdallas_state::write_d));
	m_maincpu->write_g().set(FUNC(mdallas_state::write_g));
	m_maincpu->read_in().set(FUNC(mdallas_state::read_in));
	m_maincpu->write_so().set(m_speaker, FUNC(speaker_sound_device::level_w));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(8, 8);
	m_display->set_segmask(0xff, 0xff);
	config.set_default_layout(layout_mdallas);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( mdallas )
	ROM_REGION( 0x0800, "maincpu", 0 )
	ROM_LOAD( "copl444l-hyn_n", 0x0000, 0x0800, CRC(7848b78c) SHA1(778d24512180892f58c49df3c72ca77b2618d63b) )
ROM_END





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

  Milton Bradley Plus One
  * COP410L MCU in 8-pin DIP, label ~/029 MM 57405 (die label COP410L/B NNE)
  * orientation sensor(4 directions), 1-bit sound

  This is a board game, each player needs to rotate a triangular pyramid
  shaped piece the same as the previous player, plus 1.

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

class plus1_state : public hh_cop400_state
{
public:
	plus1_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void write_d(u8 data);
	void write_l(u8 data);
	u8 read_l();
	void plus1(machine_config &config);
};

// handlers

void plus1_state::write_d(u8 data)
{
	// D0?: speaker out
	m_speaker->level_w(data & 1);
}

void plus1_state::write_l(u8 data)
{
	m_l = data;
}

u8 plus1_state::read_l()
{
	// L: IN.1, mask with output
	return m_inputs[1]->read() & m_l;
}

// config

static INPUT_PORTS_START( plus1 )
	PORT_START("IN.0") // port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Sensor Position 3")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_NAME("Sensor Position 1")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // port L
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_NAME("Sensor Position 4")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_NAME("Sensor Position 2")
	PORT_BIT( 0xf0, IP_ACTIVE_LOW, IPT_UNUSED )
INPUT_PORTS_END

void plus1_state::plus1(machine_config &config)
{
	/* basic machine hardware */
	COP410(config, m_maincpu, 1000000); // approximation - RC osc. R=51K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(plus1_state::write_d));
	m_maincpu->read_g().set_ioport("IN.0");
	m_maincpu->write_l().set(FUNC(plus1_state::write_l));
	m_maincpu->read_l().set(FUNC(plus1_state::read_l));

	/* no visual feedback! */

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( plus1 )
	ROM_REGION( 0x0200, "maincpu", 0 )
	ROM_LOAD( "cop410l_b_nne", 0x0000, 0x0200, CRC(d861b80c) SHA1(4652f8ee0dd4c3c48b625285bb4f094d96434071) )
ROM_END





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

  Milton Bradley Electronic Lightfight
  * COP421L MCU label /B119 COP421L-HLA/N
  * LED matrix, 1-bit sound

  Xbox-shaped electronic game for 2 or more players, with long diagonal buttons
  next to each outer LED. The main object of the game is to pinpoint a light
  by pressing 2 buttons. To start, press a skill-level button(P2 button 7/8/9)
  after selecting a game mode(P1 button 6-10).

  The game variations are:
  1: LightFight
  2: NightFight
  3: RiteSite
  4: QuiteBrite
  5: RightLight

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

class lightfgt_state : public hh_cop400_state
{
public:
	lightfgt_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	DECLARE_WRITE_LINE_MEMBER(write_so);
	void write_d(u8 data);
	void write_l(u8 data);
	u8 read_g();
	void lightfgt(machine_config &config);
};

// handlers

void lightfgt_state::update_display()
{
	u8 grid = (m_so | m_d << 1) ^ 0x1f;
	m_display->matrix(grid, m_l);
}

WRITE_LINE_MEMBER(lightfgt_state::write_so)
{
	// SO: led grid 0 (and input mux)
	m_so = state;
	update_display();
}

void lightfgt_state::write_d(u8 data)
{
	// D: led grid 1-4 (and input mux)
	m_d = data;
	update_display();
}

void lightfgt_state::write_l(u8 data)
{
	// L0-L4: led state
	// L5-L7: N/C
	m_l = data & 0x1f;
	update_display();
}

u8 lightfgt_state::read_g()
{
	// G: multiplexed inputs
	m_inp_mux = m_d << 1 | m_so;
	return read_inputs(5, 0xf);
}

// config

static INPUT_PORTS_START( lightfgt )
	PORT_START("IN.0") // SO port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON6 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) // note: button 1 is on the left side from player perspective
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON5 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON10 ) PORT_COCKTAIL

	PORT_START("IN.1") // D0 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON7 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON4 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON9 ) PORT_COCKTAIL

	PORT_START("IN.2") // D1 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON8 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON3 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON8 ) PORT_COCKTAIL

	PORT_START("IN.3") // D2 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON9 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON4 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON7 ) PORT_COCKTAIL

	PORT_START("IN.4") // D3 port G
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON10 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON5 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_BUTTON6 ) PORT_COCKTAIL
INPUT_PORTS_END

void lightfgt_state::lightfgt(machine_config &config)
{
	/* basic machine hardware */
	COP421(config, m_maincpu, 950000); // approximation - RC osc. R=82K, C=56pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_so().set(FUNC(lightfgt_state::write_so));
	m_maincpu->write_d().set(FUNC(lightfgt_state::write_d));
	m_maincpu->write_l().set(FUNC(lightfgt_state::write_l));
	m_maincpu->write_sk().set(m_speaker, FUNC(speaker_sound_device::level_w));
	m_maincpu->read_g().set(FUNC(lightfgt_state::read_g));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(5, 5);
	config.set_default_layout(layout_lightfgt);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker).add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( lightfgt )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop421l-hla_n", 0x0000, 0x0400, CRC(aceb2d65) SHA1(2328cbb195faf93c575f3afa3a1fe0079180edd7) )
ROM_END





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

  Milton Bradley Electronic Battleship (1982 version)
  * COP420 MCU label COP420-JWE/N

  see hh_tms1k.cpp bship driver for more information

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

class bship82_state : public hh_cop400_state
{
public:
	bship82_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void write_d(u8 data);
	void write_g(u8 data);
	u8 read_l();
	u8 read_in();
	DECLARE_WRITE_LINE_MEMBER(write_so);
	void bship82(machine_config &config);
};

// handlers

void bship82_state::write_d(u8 data)
{
	// D: input mux
	m_inp_mux = data;
}

void bship82_state::write_g(u8 data)
{
	// G0-G2: speaker out via 3.9K, 2.2K, 1.0K resistor
	// G3: enable speaker
	m_speaker->level_w((data & 8) ? (data & 7) : 0);
}

u8 bship82_state::read_l()
{
	// L: multiplexed inputs
	return read_inputs(4, 0xff);
}

u8 bship82_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(4, 0xf00) >> 8;
}

WRITE_LINE_MEMBER(bship82_state::write_so)
{
	// SO: led
	m_display->matrix(1, state);
}

// config

static INPUT_PORTS_START( bship82 )
	PORT_START("IN.0") // D0 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_BACKSPACE) PORT_NAME("P1 Clear Last Entry") // CLE
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_A) PORT_NAME("P1 A")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_B) PORT_NAME("P1 B")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_C) PORT_NAME("P1 C")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_D) PORT_NAME("P1 D")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("P1 E")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_F) PORT_NAME("P1 F")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_G) PORT_NAME("P1 G")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_H) PORT_NAME("P1 H")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_I) PORT_NAME("P1 I")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_J) PORT_NAME("P1 J")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.1") // D1 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_DEL) PORT_NAME("P1 Clear Memory") // CM
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("P1 1")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("P1 2")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("P1 3")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("P1 4")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("P1 5")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("P1 6")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("P1 7")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("P1 8")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("P1 9")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("P1 10")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ENTER) PORT_CODE(KEYCODE_ENTER_PAD) PORT_NAME("P1 Fire")

	PORT_START("IN.2") // D2 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Clear Last Entry") // CLE
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 A")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 B")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 C")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 D")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 E")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 F")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 G")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 H")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 I")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 J")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_UNUSED )

	PORT_START("IN.3") // D3 ports L,IN
	PORT_BIT( 0x001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Clear Memory") // CM
	PORT_BIT( 0x002, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 1")
	PORT_BIT( 0x004, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 2")
	PORT_BIT( 0x008, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 3")
	PORT_BIT( 0x010, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 4")
	PORT_BIT( 0x020, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 5")
	PORT_BIT( 0x040, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 6")
	PORT_BIT( 0x080, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 7")
	PORT_BIT( 0x100, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 8")
	PORT_BIT( 0x200, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 9")
	PORT_BIT( 0x400, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 10")
	PORT_BIT( 0x800, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_NAME("P2 Fire")

	PORT_START("IN.4") // SI
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_TOGGLE PORT_CODE(KEYCODE_F1) PORT_NAME("Load/Go") // switch
INPUT_PORTS_END

void bship82_state::bship82(machine_config &config)
{
	/* basic machine hardware */
	COP420(config, m_maincpu, 750000); // approximation - RC osc. R=14K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(bship82_state::write_d));
	m_maincpu->write_g().set(FUNC(bship82_state::write_g));
	m_maincpu->read_l().set(FUNC(bship82_state::read_l));
	m_maincpu->read_in().set(FUNC(bship82_state::read_in));
	m_maincpu->write_so().set(FUNC(bship82_state::write_so));
	m_maincpu->read_si().set_ioport("IN.4");

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(1, 1);
	config.set_default_layout(layout_bship82);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	SPEAKER_SOUND(config, m_speaker);
	static const double speaker_levels[8] = { 0.0, 1.0/7.0, 2.0/7.0, 3.0/7.0, 4.0/7.0, 5.0/7.0, 6.0/7.0, 1.0 };
	m_speaker->set_levels(8, speaker_levels);
	m_speaker->add_route(ALL_OUTPUTS, "mono", 0.25);
}

// roms

ROM_START( bship82 )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420-jwe_n", 0x0000, 0x0400, CRC(5ea8111a) SHA1(34931463b806b48dce4f8ae2361512510bae0ebf) )
ROM_END





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

  National Semiconductor QuizKid Racer (COP420 version)
  * COP420 MCU label COP420-NPG/N
  * 8-digit 7seg led display(1 custom digit), 1 green led, no sound

  This is the COP420 version, they removed support for the link cable.
  The first release was on a MM5799 MCU, see hh_cops1.cpp.

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

class qkracer_state : public hh_cop400_state
{
public:
	qkracer_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_d(u8 data);
	void write_g(u8 data);
	void write_l(u8 data);
	u8 read_in();
	DECLARE_WRITE_LINE_MEMBER(write_sk);
	void qkracer(machine_config &config);
};

// handlers

void qkracer_state::update_display()
{
	m_display->matrix(~(m_d | m_g << 4 | m_sk << 8), m_l);
}

void qkracer_state::write_d(u8 data)
{
	// D: select digit, D3: input mux low bit
	m_inp_mux = (m_inp_mux & ~1) | (data >> 3 & 1);
	m_d = data & 0xf;
	update_display();
}

void qkracer_state::write_g(u8 data)
{
	// G: select digit, input mux
	m_inp_mux = (m_inp_mux & 1) | (data << 1 & 0x1e);
	m_g = data & 0xf;
	update_display();
}

void qkracer_state::write_l(u8 data)
{
	// L0-L6: digit segment data
	m_l = data & 0x7f;
	update_display();
}

u8 qkracer_state::read_in()
{
	// IN: multiplexed inputs
	return read_inputs(5, 0xf);
}

WRITE_LINE_MEMBER(qkracer_state::write_sk)
{
	// SK: green led
	m_sk = state;
	update_display();
}

// config

static INPUT_PORTS_START( qkracer )
	PORT_START("IN.0") // D3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_Q) PORT_NAME("Amateur")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_W) PORT_NAME("Pro")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_E) PORT_NAME("Complex")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_R) PORT_NAME("Tables")

	PORT_START("IN.1") // G0 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_7) PORT_CODE(KEYCODE_7_PAD) PORT_NAME("7")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_8) PORT_CODE(KEYCODE_8_PAD) PORT_NAME("8")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_9) PORT_CODE(KEYCODE_9_PAD) PORT_NAME("9")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_SLASH_PAD) PORT_NAME(UTF8_DIVIDE)

	PORT_START("IN.2") // G1 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_4) PORT_CODE(KEYCODE_4_PAD) PORT_NAME("4")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_5) PORT_CODE(KEYCODE_5_PAD) PORT_NAME("5")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_6) PORT_CODE(KEYCODE_6_PAD) PORT_NAME("6")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_ASTERISK) PORT_NAME(UTF8_MULTIPLY)

	PORT_START("IN.3") // G2 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_1) PORT_CODE(KEYCODE_1_PAD) PORT_NAME("1")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_2) PORT_CODE(KEYCODE_2_PAD) PORT_NAME("2")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_3) PORT_CODE(KEYCODE_3_PAD) PORT_NAME("3")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS_PAD) PORT_NAME("-")

	PORT_START("IN.4") // G3 port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_MINUS) PORT_NAME("Slow")
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_EQUALS) PORT_NAME("Fast")
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_0) PORT_CODE(KEYCODE_0_PAD) PORT_NAME("0")
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_CODE(KEYCODE_PLUS_PAD) PORT_NAME("+")
INPUT_PORTS_END

void qkracer_state::qkracer(machine_config &config)
{
	/* basic machine hardware */
	COP420(config, m_maincpu, 950000); // approximation - RC osc. R=47K, C=100pF
	m_maincpu->set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(qkracer_state::write_d));
	m_maincpu->write_g().set(FUNC(qkracer_state::write_g));
	m_maincpu->write_l().set(FUNC(qkracer_state::write_l));
	m_maincpu->read_in().set(FUNC(qkracer_state::read_in));
	m_maincpu->write_sk().set(FUNC(qkracer_state::write_sk));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(9, 7);
	m_display->set_segmask(0xdf, 0x7f);
	m_display->set_segmask(0x20, 0x41); // equals sign
	config.set_default_layout(layout_qkracer);

	/* no sound! */
}

// roms

ROM_START( qkracer )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420-npg_n", 0x0000, 0x0400, CRC(17f8e538) SHA1(23d1a1819e6ba552d8da83da2948af1cf5b13d5b) )
ROM_END





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

  Select Merchandise Video Challenger
  * COP420 MCU label COP420-TDX/N
  * 6-digit 7seg led display, 3 other leds, 4-bit sound

  This is a lightgun with scorekeeping. The "games" themselves were released
  on VHS tapes. To determine scoring, the lightgun detects strobe lighting
  from objects in the video.

  known releases:
  - Japan: Video Challenger, published by Takara
  - UK: Video Challenger, published by Bandai
  - Canada: Video Challenger, published by Irwin

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

class vidchal_state : public hh_cop400_state
{
public:
	vidchal_state(const machine_config &mconfig, device_type type, const char *tag) :
		hh_cop400_state(mconfig, type, tag)
	{ }

	void update_display();
	void write_d(u8 data);
	void write_l(u8 data);
	DECLARE_WRITE_LINE_MEMBER(write_sk);
	void vidchal(machine_config &config);
};

// handlers

void vidchal_state::update_display()
{
	m_display->matrix(m_d | m_sk << 6, m_l);
}

void vidchal_state::write_d(u8 data)
{
	// D: CD4028BE to digit select
	m_d = 1 << data & 0x3f;
	update_display();
}

void vidchal_state::write_l(u8 data)
{
	// L: digit segment data
	m_l = bitswap<8>(data,0,3,1,5,4,7,2,6);
	update_display();
}

WRITE_LINE_MEMBER(vidchal_state::write_sk)
{
	// SK: hit led
	m_sk = state;
	update_display();
}

// config

static INPUT_PORTS_START( vidchal )
	PORT_START("IN.0") // port IN
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_UNUSED )
	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER) PORT_CODE(KEYCODE_F1) PORT_NAME("Light Sensor")
INPUT_PORTS_END

void vidchal_state::vidchal(machine_config &config)
{
	/* basic machine hardware */
	COP420(config, m_maincpu, 900000); // approximation
	m_maincpu->set_config(COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, false); // guessed
	m_maincpu->write_d().set(FUNC(vidchal_state::write_d));
	m_maincpu->write_g().set("dac", FUNC(dac_byte_interface::data_w));
	m_maincpu->write_l().set(FUNC(vidchal_state::write_l));
	m_maincpu->read_in().set_ioport("IN.0");
	m_maincpu->write_sk().set(FUNC(vidchal_state::write_sk));

	/* video hardware */
	PWM_DISPLAY(config, m_display).set_size(6+1, 8);
	m_display->set_segmask(0x3f, 0xff);
	config.set_default_layout(layout_vidchal);

	/* sound hardware */
	SPEAKER(config, "mono").front_center();
	DAC_4BIT_BINARY_WEIGHTED_SIGN_MAGNITUDE(config, "dac").add_route(ALL_OUTPUTS, "mono", 0.125); // unknown DAC
}

// roms

ROM_START( vidchal )
	ROM_REGION( 0x0400, "maincpu", 0 )
	ROM_LOAD( "cop420-tdx_n", 0x0000, 0x0400, CRC(c9bd041c) SHA1(ab0dcaf4741620fa4c28ab75337a23d646af7626) )
ROM_END



} // anonymous namespace

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

  Game driver(s)

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

//    YEAR  NAME        PARENT    CMP MACHINE     INPUT       CLASS            INIT        COMPANY, FULLNAME, FLAGS
CONS( 1979, ctstein,    0,         0, ctstein,    ctstein,    ctstein_state,   empty_init, "Castle Toy", "Einstein (Castle Toy)", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )

CONS( 1980, h2hbaskbc,  h2hbaskb,  0, h2hbaskbc,  h2hbaskbc,  h2hbaskbc_state, empty_init, "Coleco", "Head to Head: Electronic Basketball (COP420L version)", MACHINE_SUPPORTS_SAVE )
CONS( 1980, h2hhockeyc, h2hhockey, 0, h2hhockeyc, h2hhockeyc, h2hbaskbc_state, empty_init, "Coleco", "Head to Head: Electronic Hockey (COP420L version)", MACHINE_SUPPORTS_SAVE )
CONS( 1980, h2hsoccerc, 0,         0, h2hsoccerc, h2hsoccerc, h2hbaskbc_state, empty_init, "Coleco", "Head to Head: Electronic Soccer (COP420L version)", MACHINE_SUPPORTS_SAVE )

CONS( 1981, einvaderc,  einvader,  0, einvaderc,  einvaderc,  einvaderc_state, empty_init, "Entex", "Space Invader (Entex, COP444L version)", MACHINE_SUPPORTS_SAVE )

CONS( 1980, unkeinv,    0,         0, unkeinv,    unkeinv,    unkeinv_state,   empty_init, "Gordon Barlow Design", "unknown electronic Space Invaders game (patent)", MACHINE_SUPPORTS_SAVE )

CONS( 1980, lchicken,   0,         0, lchicken,   lchicken,   lchicken_state,  empty_init, "LJN", "I Took a Lickin' From a Chicken", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK | MACHINE_MECHANICAL )

CONS( 1979, funjacks,   0,         0, funjacks,   funjacks,   funjacks_state,  empty_init, "Mattel", "Funtronics: Jacks", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
CONS( 1979, funrlgl,    0,         0, funrlgl,    funrlgl,    funrlgl_state,   empty_init, "Mattel", "Funtronics: Red Light Green Light", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
CONS( 1980, funtag,     0,         0, funtag,     funtag,     funtag_state,    empty_init, "Mattel", "Funtronics: Tag", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
CONS( 1981, mdallas,    0,         0, mdallas,    mdallas,    mdallas_state,   empty_init, "Mattel", "Dalla$ (J.R. handheld)", MACHINE_SUPPORTS_SAVE ) // ***

CONS( 1980, plus1,      0,         0, plus1,      plus1,      plus1_state,     empty_init, "Milton Bradley", "Plus One", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_CONTROLS ) // ***
CONS( 1981, lightfgt,   0,         0, lightfgt,   lightfgt,   lightfgt_state,  empty_init, "Milton Bradley", "Electronic Lightfight - The Games of Dueling Lights", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK )
CONS( 1982, bship82,    bship,     0, bship82,    bship82,    bship82_state,   empty_init, "Milton Bradley", "Electronic Battleship (1982 version)", MACHINE_SUPPORTS_SAVE | MACHINE_CLICKABLE_ARTWORK ) // ***

CONS( 1979, qkracer,    0,         0, qkracer,    qkracer,    qkracer_state,   empty_init, "National Semiconductor", "QuizKid Racer (COP420 version)", MACHINE_SUPPORTS_SAVE | MACHINE_NO_SOUND_HW )

CONS( 1987, vidchal,    0,         0, vidchal,    vidchal,    vidchal_state,   empty_init, "Select Merchandise", "Video Challenger", MACHINE_SUPPORTS_SAVE | MACHINE_NOT_WORKING )

// ***: As far as MAME is concerned, the game is emulated fine. But for it to be playable, it requires interaction
// with other, unemulatable, things eg. game board/pieces, book, playing cards, pen & paper, etc.
