summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/drivers/thayers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/drivers/thayers.cpp')
-rw-r--r--src/mame/drivers/thayers.cpp838
1 files changed, 838 insertions, 0 deletions
diff --git a/src/mame/drivers/thayers.cpp b/src/mame/drivers/thayers.cpp
new file mode 100644
index 00000000000..26db8693aef
--- /dev/null
+++ b/src/mame/drivers/thayers.cpp
@@ -0,0 +1,838 @@
+// license:BSD-3-Clause
+// copyright-holders:Andrew Gardner
+/*
+
+ TODO:
+
+ - LDV1000 mode
+ - PR7820 INT/_EXT line
+ - coin counter
+ - convert SSI-263 to a sound device
+ - dump laserdisc
+
+*/
+
+#include "emu.h"
+#include "cpu/z80/z80.h"
+#include "machine/ldstub.h"
+#include "machine/ldv1000.h"
+#include "cpu/cop400/cop400.h"
+//#include "dlair.lh"
+
+
+struct ssi263_t
+{
+ UINT8 dr;
+ UINT8 p;
+ UINT16 i;
+ UINT8 r;
+ UINT8 t;
+ UINT8 c;
+ UINT8 a;
+ UINT8 f;
+ UINT8 mode;
+};
+
+class thayers_state : public driver_device
+{
+public:
+ enum
+ {
+ TIMER_INTRQ_TICK,
+ TIMER_SSI263_PHONEME_TICK
+ };
+
+ thayers_state(const machine_config &mconfig, device_type type, const char *tag)
+ : driver_device(mconfig, type, tag),
+ m_pr7820(*this, "laserdisc"),
+ m_ldv1000(*this, "ldv1000"),
+ m_maincpu(*this, "maincpu"),
+ m_row(*this, "ROW") { }
+
+ optional_device<pioneer_pr7820_device> m_pr7820;
+ optional_device<pioneer_ldv1000_device> m_ldv1000;
+ UINT8 m_laserdisc_data;
+ int m_rx_bit;
+ int m_keylatch;
+ UINT8 m_cop_data_latch;
+ int m_cop_data_latch_enable;
+ UINT8 m_cop_l;
+ UINT8 m_cop_cmd_latch;
+ int m_timer_int;
+ int m_data_rdy_int;
+ int m_ssi_data_request;
+ int m_cart_present;
+ int m_pr7820_enter;
+ struct ssi263_t m_ssi263;
+ DECLARE_WRITE8_MEMBER(intrq_w);
+ DECLARE_READ8_MEMBER(irqstate_r);
+ DECLARE_WRITE8_MEMBER(timer_int_ack_w);
+ DECLARE_WRITE8_MEMBER(data_rdy_int_ack_w);
+ DECLARE_WRITE8_MEMBER(cop_d_w);
+ DECLARE_READ8_MEMBER(cop_data_r);
+ DECLARE_WRITE8_MEMBER(cop_data_w);
+ DECLARE_READ8_MEMBER(cop_l_r);
+ DECLARE_WRITE8_MEMBER(cop_l_w);
+ DECLARE_READ8_MEMBER(cop_g_r);
+ DECLARE_WRITE8_MEMBER(control_w);
+ DECLARE_WRITE8_MEMBER(cop_g_w);
+ DECLARE_READ_LINE_MEMBER(cop_si_r);
+ DECLARE_WRITE_LINE_MEMBER(cop_so_w);
+ DECLARE_WRITE8_MEMBER(control2_w);
+ DECLARE_READ8_MEMBER(dsw_b_r);
+ DECLARE_READ8_MEMBER(laserdsc_data_r);
+ DECLARE_WRITE8_MEMBER(laserdsc_data_w);
+ DECLARE_WRITE8_MEMBER(laserdsc_control_w);
+ DECLARE_WRITE8_MEMBER(den1_w);
+ DECLARE_WRITE8_MEMBER(den2_w);
+ DECLARE_WRITE8_MEMBER(ssi263_register_w);
+ DECLARE_READ8_MEMBER(ssi263_register_r);
+ DECLARE_CUSTOM_INPUT_MEMBER(laserdisc_enter_r);
+ DECLARE_CUSTOM_INPUT_MEMBER(laserdisc_ready_r);
+ virtual void machine_start();
+ virtual void machine_reset();
+ void check_interrupt();
+ required_device<cpu_device> m_maincpu;
+ required_ioport_array<10> m_row;
+
+protected:
+ virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
+};
+
+
+extern const char layout_dlair[];
+
+
+
+static const UINT8 led_map[16] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x00 };
+
+/* Interrupts */
+
+void thayers_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_INTRQ_TICK:
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
+ break;
+ case TIMER_SSI263_PHONEME_TICK:
+ m_ssi_data_request = 0;
+ check_interrupt();
+ break;
+ default:
+ assert_always(FALSE, "Unknown id in thayers_state::device_timer");
+ }
+}
+
+void thayers_state::check_interrupt()
+{
+ if (!m_timer_int || !m_data_rdy_int || !m_ssi_data_request)
+ {
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, HOLD_LINE);
+ }
+ else
+ {
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
+ }
+}
+
+WRITE8_MEMBER(thayers_state::intrq_w)
+{
+ // T = 1.1 * R30 * C53 = 1.1 * 750K * 0.01uF = 8.25 ms
+
+ m_maincpu->set_input_line(INPUT_LINE_IRQ0, HOLD_LINE);
+
+ timer_set(attotime::from_usec(8250), TIMER_INTRQ_TICK);
+}
+
+READ8_MEMBER(thayers_state::irqstate_r)
+{
+ /*
+
+ bit description
+
+ 0
+ 1
+ 2 SSI263 A/_R
+ 3 tied to +5V
+ 4 _TIMER INT
+ 5 _DATA RDY INT
+ 6 _CART PRES
+ 7
+
+ */
+
+ return (m_data_rdy_int << 5) | (m_timer_int << 4) | 0x08 | (m_ssi_data_request << 2);
+}
+
+WRITE8_MEMBER(thayers_state::timer_int_ack_w)
+{
+ m_timer_int = 1;
+
+ check_interrupt();
+}
+
+WRITE8_MEMBER(thayers_state::data_rdy_int_ack_w)
+{
+ m_data_rdy_int = 1;
+
+ check_interrupt();
+}
+
+WRITE8_MEMBER(thayers_state::cop_d_w)
+{
+ /*
+
+ bit description
+
+ D0 _TIMER INT
+ D1 _DATA RDY INT
+ D2
+ D3
+
+ */
+
+ if (!BIT(data, 0))
+ {
+ m_timer_int = 0;
+ }
+
+ if (!BIT(data, 1))
+ {
+ m_data_rdy_int = 0;
+ }
+
+ check_interrupt();
+}
+
+/* COP Communication */
+
+READ8_MEMBER(thayers_state::cop_data_r)
+{
+ if (!m_cop_data_latch_enable)
+ {
+ return m_cop_data_latch;
+ }
+ else
+ {
+ return m_cop_l;
+ }
+}
+
+WRITE8_MEMBER(thayers_state::cop_data_w)
+{
+ m_cop_data_latch = data;
+}
+
+READ8_MEMBER(thayers_state::cop_l_r)
+{
+ if (!m_cop_data_latch_enable)
+ {
+ return m_cop_data_latch;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+WRITE8_MEMBER(thayers_state::cop_l_w)
+{
+ m_cop_l = data;
+}
+
+READ8_MEMBER(thayers_state::cop_g_r)
+{
+ /*
+
+ bit description
+
+ G0 U16 Q0
+ G1 U16 Q1
+ G2 U16 Q2
+ G3
+
+ */
+
+ return m_cop_cmd_latch;
+}
+
+WRITE8_MEMBER(thayers_state::control_w)
+{
+ /*
+
+ bit description
+
+ 0
+ 1 _CS128A
+ 2 _BANKSEL1
+ 3
+ 4
+ 5 COP G0
+ 6 COP G1
+ 7 COP G2
+
+ */
+
+ m_cop_cmd_latch = (data >> 5) & 0x07;
+}
+
+WRITE8_MEMBER(thayers_state::cop_g_w)
+{
+ /*
+
+ bit description
+
+ G0
+ G1
+ G2
+ G3 U17 enable
+
+ */
+
+ m_cop_data_latch_enable = BIT(data, 3);
+}
+
+/* Keyboard */
+
+READ_LINE_MEMBER(thayers_state::cop_si_r)
+{
+ /* keyboard data */
+
+ /*
+
+ Serial communications format
+
+ 1, 1, 0, 1, Q8, P0, P1, P2, P3, 0
+
+ */
+
+ switch (m_rx_bit)
+ {
+ case 0:
+ case 1:
+ case 3:
+ return 1;
+
+ case 4:
+ return (m_keylatch == 9);
+
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ return BIT(m_row[m_keylatch]->read(), m_rx_bit - 5);
+
+ default:
+ return 0;
+ }
+}
+
+WRITE_LINE_MEMBER(thayers_state::cop_so_w)
+{
+ /* keyboard clock */
+
+ if (state)
+ {
+ m_rx_bit++;
+
+ if (m_rx_bit == 10)
+ {
+ m_rx_bit = 0;
+
+ m_keylatch++;
+
+ if (m_keylatch == 10)
+ {
+ m_keylatch = 0;
+ }
+ }
+ }
+}
+
+/* I/O Board */
+
+WRITE8_MEMBER(thayers_state::control2_w)
+{
+ /*
+
+ bit description
+
+ 0
+ 1 _RESOI (?)
+ 2 _ENCARTDET
+ 3
+ 4
+ 5
+ 6
+ 7
+
+ */
+
+ if ((!BIT(data, 2)) & m_cart_present)
+ {
+ m_maincpu->set_input_line(INPUT_LINE_NMI, HOLD_LINE);
+ }
+}
+
+READ8_MEMBER(thayers_state::dsw_b_r)
+{
+ return (ioport("COIN")->read() & 0xf0) | (ioport("DSWB")->read() & 0x0f);
+}
+
+READ8_MEMBER(thayers_state::laserdsc_data_r)
+{
+ if (m_ldv1000 != NULL) return m_ldv1000->status_r();
+ if (m_pr7820 != NULL) return m_pr7820->data_r();
+ return 0;
+}
+
+WRITE8_MEMBER(thayers_state::laserdsc_data_w)
+{
+ m_laserdisc_data = data;
+}
+
+WRITE8_MEMBER(thayers_state::laserdsc_control_w)
+{
+ /*
+
+ bit description
+
+ 0
+ 1
+ 2
+ 3
+ 4 coin counter
+ 5 U16 output enable
+ 6 ENTER if switch B5 closed
+ 7 INT/_EXT
+
+ */
+
+ coin_counter_w(machine(), 0, BIT(data, 4));
+
+ if (BIT(data, 5))
+ {
+ if (m_ldv1000 != NULL)
+ {
+ m_ldv1000->data_w(m_laserdisc_data);
+ m_ldv1000->enter_w(BIT(data, 7) ? CLEAR_LINE : ASSERT_LINE);
+ }
+ if (m_pr7820 != NULL)
+ {
+ m_pr7820->data_w(m_laserdisc_data);
+ m_pr7820_enter = BIT(data, 6) ? CLEAR_LINE : ASSERT_LINE;
+ m_pr7820->enter_w(m_pr7820_enter);
+ // BIT(data, 7) is INT/_EXT, but there is no such input line in laserdsc.h
+ }
+ }
+}
+
+WRITE8_MEMBER(thayers_state::den1_w)
+{
+ /*
+
+ bit description
+
+ 0 DD0
+ 1 DD1
+ 2 DD2
+ 3 DD3
+ 4 DA0
+ 5 DA1
+ 6 DA2
+ 7 DA3
+
+ */
+
+ output_set_digit_value(data >> 4, led_map[data & 0x0f]);
+}
+
+WRITE8_MEMBER(thayers_state::den2_w)
+{
+ /*
+
+ bit description
+
+ 0 DD0
+ 1 DD1
+ 2 DD2
+ 3 DD3
+ 4 DA0
+ 5 DA1
+ 6 DA2
+ 7 DA3
+
+ */
+
+ output_set_digit_value(8 + (data >> 4), led_map[data & 0x0f]);
+}
+
+/* SSI-263 */
+
+/*
+
+ The following information is from the SSI-263A data sheet.
+
+ Thayer's Quest uses an SSI-263, so this might be inaccurate, but it works for now
+
+*/
+
+#define SSI263_CLOCK (XTAL_4MHz/2)
+
+#if 0
+static const char SSI263_PHONEMES[0x40][5] =
+{
+ "PA", "E", "E1", "Y", "YI", "AY", "IE", "I", "A", "AI", "EH", "EH1", "AE", "AE1", "AH", "AH1", "W", "O", "OU", "OO", "IU", "IU1", "U", "U1", "UH", "UH1", "UH2", "UH3", "ER", "R", "R1", "R2",
+ "L", "L1", "LF", "W", "B", "D", "KV", "P", "T", "K", "HV", "HVC", "HF", "HFC", "HN", "Z", "S", "J", "SCH", "V", "F", "THV", "TH", "M", "N", "NG", ":A", ":OH", ":U", ":UH", "E2", "LB"
+};
+#endif
+
+WRITE8_MEMBER(thayers_state::ssi263_register_w)
+{
+ struct ssi263_t &ssi263 = m_ssi263;
+ switch (offset)
+ {
+ case 0:
+ {
+ int frame_time = ((4096 * (16 - ssi263.r)) / 2); // us, /2 should actually be /SSI263_CLOCK, but this way we get microseconds directly
+ int phoneme_time = frame_time * (4 - ssi263.dr); // us
+
+ // duration/phoneme register
+ ssi263.dr = (data >> 5) & 0x03;
+ ssi263.p = data & 0x3f;
+
+ m_ssi_data_request = 1;
+ check_interrupt();
+
+ switch (ssi263.mode)
+ {
+ case 0:
+ case 1:
+ // phoneme timing response
+ timer_set(attotime::from_usec(phoneme_time), TIMER_SSI263_PHONEME_TICK);
+ break;
+ case 2:
+ // frame timing response
+ timer_set(attotime::from_usec(frame_time), TIMER_SSI263_PHONEME_TICK);
+ break;
+ case 3:
+ // disable A/_R output
+ break;
+ }
+
+ //logerror("SSI263 Phoneme Duration: %u\n", ssi263.dr);
+ //logerror("SSI263 Phoneme: %02x %s\n", ssi263.p, SSI263_PHONEMES[ssi263.p]);
+ }
+ break;
+
+ case 1:
+ // inflection register
+ ssi263.i = (data << 3) | (ssi263.i & 0x403);
+
+ //logerror("SSI263 Inflection: %u\n", ssi263.i);
+ break;
+
+ case 2:
+ // rate/inflection register
+ ssi263.i = (BIT(data, 4) << 11) | (ssi263.i & 0x7f8) | (data & 0x07);
+ ssi263.r = data >> 4;
+
+ //logerror("SSI263 Inflection: %u\n", ssi263.i);
+ //logerror("SSI263 Rate: %u\n", ssi263.r);
+ break;
+
+ case 3:
+ // control/articulation/amplitude register
+ if (ssi263.c && !BIT(data, 7))
+ {
+ ssi263.mode = ssi263.dr;
+
+ switch (ssi263.mode)
+ {
+ case 0:
+ //logerror("SSI263 Phoneme Timing Response, Transitioned Inflection\n");
+ break;
+
+ case 1:
+ //logerror("SSI263 Phoneme Timing Response, Immediate Inflection\n");
+ break;
+
+ case 2:
+ // activate A/_R
+ //logerror("SSI263 Frame Timing Response, Immediate Inflection\n");
+ break;
+
+ case 3:
+ // disable A/_R output
+ //logerror("SSI263 A/R Output Disabled\n");
+ break;
+ }
+ }
+
+ ssi263.c = BIT(data, 7);
+ ssi263.t = (data >> 4) & 0x07;
+ ssi263.a = data & 0x0f;
+
+ //logerror("SSI263 Control: %u\n", ssi263.c);
+ //logerror("SSI263 Articulation: %u\n", ssi263.t);
+ //logerror("SSI263 Amplitude: %u\n", ssi263.a);
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ // filter frequency register
+ ssi263.f = data;
+ //logerror("SSI263 Filter Frequency: %u\n", ssi263.f);
+ break;
+ }
+}
+
+READ8_MEMBER(thayers_state::ssi263_register_r)
+{
+ // D7 becomes an output, as the inverted state of A/_R. The register address bits are ignored.
+
+ return !m_ssi_data_request << 7;
+}
+
+/* Memory Maps */
+
+static ADDRESS_MAP_START( thayers_map, AS_PROGRAM, 8, thayers_state )
+ AM_RANGE(0x0000, 0x7fff) AM_ROM
+ AM_RANGE(0x8000, 0xbfff) AM_RAM
+ AM_RANGE(0xc000, 0xdfff) AM_ROM
+ADDRESS_MAP_END
+
+static ADDRESS_MAP_START( thayers_io_map, AS_IO, 8, thayers_state )
+ ADDRESS_MAP_GLOBAL_MASK(0xff)
+ AM_RANGE(0x00, 0x07) AM_READWRITE(ssi263_register_r, ssi263_register_w)
+ AM_RANGE(0x20, 0x20) AM_WRITE(control_w)
+ AM_RANGE(0x40, 0x40) AM_READWRITE(irqstate_r, control2_w)
+ AM_RANGE(0x80, 0x80) AM_READWRITE(cop_data_r, cop_data_w)
+ AM_RANGE(0xa0, 0xa0) AM_WRITE(timer_int_ack_w)
+ AM_RANGE(0xc0, 0xc0) AM_WRITE(data_rdy_int_ack_w)
+ AM_RANGE(0xf0, 0xf0) AM_READ(laserdsc_data_r)
+ AM_RANGE(0xf1, 0xf1) AM_READ(dsw_b_r)
+ AM_RANGE(0xf2, 0xf2) AM_READ_PORT("DSWA")
+ AM_RANGE(0xf3, 0xf3) AM_WRITE(intrq_w)
+ AM_RANGE(0xf4, 0xf4) AM_WRITE(laserdsc_data_w)
+ AM_RANGE(0xf5, 0xf5) AM_WRITE(laserdsc_control_w)
+ AM_RANGE(0xf6, 0xf6) AM_WRITE(den1_w)
+ AM_RANGE(0xf7, 0xf7) AM_WRITE(den2_w)
+ADDRESS_MAP_END
+
+/* Input Ports */
+
+CUSTOM_INPUT_MEMBER(thayers_state::laserdisc_enter_r)
+{
+ if (m_pr7820 != NULL) return m_pr7820_enter;
+ if (m_ldv1000 != NULL) return (m_ldv1000->status_strobe_r() == ASSERT_LINE) ? 0 : 1;
+ return 0;
+}
+
+CUSTOM_INPUT_MEMBER(thayers_state::laserdisc_ready_r)
+{
+ if (m_pr7820 != NULL) return (m_pr7820->ready_r() == ASSERT_LINE) ? 0 : 1;
+ if (m_ldv1000 != NULL) return (m_ldv1000->command_strobe_r() == ASSERT_LINE) ? 0 : 1;
+ return 0;
+}
+
+static INPUT_PORTS_START( thayers )
+ PORT_START("DSWA")
+ PORT_DIPNAME( 0x07, 0x07, "Time Per Coin" ) PORT_DIPLOCATION( "A:3,2,1" )
+ PORT_DIPSETTING( 0x07, "110 Seconds" )
+ PORT_DIPSETTING( 0x06, "95 Seconds" )
+ PORT_DIPSETTING( 0x05, "80 Seconds" )
+ PORT_DIPSETTING( 0x04, "70 Seconds" )
+ PORT_DIPSETTING( 0x03, "60 Seconds" )
+ PORT_DIPSETTING( 0x02, "45 Seconds" )
+ PORT_DIPSETTING( 0x01, "30 Seconds" )
+ PORT_DIPSETTING( 0x00, DEF_STR ( Free_Play ) )
+ PORT_DIPNAME( 0x08, 0x08, DEF_STR( Coinage ) ) PORT_DIPLOCATION( "A:4" )
+ PORT_DIPSETTING( 0x00, DEF_STR( 2C_1C ) )
+ PORT_DIPSETTING( 0x08, DEF_STR( 1C_1C ) )
+ PORT_DIPNAME( 0x10, 0x10, DEF_STR( Lives ) ) PORT_DIPLOCATION( "A:5" )
+ PORT_DIPSETTING( 0x10, "3" )
+ PORT_DIPSETTING( 0x00, "5" )
+ PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION( "A:6" )
+ PORT_DIPSETTING( 0x00, DEF_STR( Off ) )
+ PORT_DIPSETTING( 0x20, DEF_STR( On ) )
+ PORT_DIPNAME( 0x40, 0x40, "Attract Mode Audio" ) PORT_DIPLOCATION( "A:7" )
+ PORT_DIPSETTING( 0x40, "Always Playing" )
+ PORT_DIPSETTING( 0x00, "One Out of 8 Times" )
+ PORT_DIPUNUSED_DIPLOC( 0x80, IP_ACTIVE_LOW, "A:8" )
+
+ PORT_START("DSWB")
+ PORT_SERVICE_DIPLOC( 0x01, 0x01, "B:1" )
+ PORT_DIPUNUSED_DIPLOC( 0x02, IP_ACTIVE_LOW, "B:2" )
+ PORT_DIPUNUSED_DIPLOC( 0x04, IP_ACTIVE_LOW, "B:3" )
+ PORT_DIPNAME( 0x18, 0x00, "LD Player" ) PORT_DIPLOCATION( "B:5,4" )
+ PORT_DIPSETTING( 0x18, "LDV-1000" )
+ PORT_DIPSETTING( 0x00, "PR-7820" )
+ PORT_DIPUNUSED_DIPLOC( 0xe0, IP_ACTIVE_LOW, "B:8,7,6" )
+
+ PORT_START("COIN")
+ PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN1 )
+ PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN2 )
+ PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, thayers_state,laserdisc_enter_r, NULL)
+ PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, thayers_state,laserdisc_ready_r, NULL)
+
+ PORT_START("ROW.0")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "2" ) PORT_CODE( KEYCODE_F2 )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "1 - Clear" ) PORT_CODE( KEYCODE_BACKSPACE )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Q" ) PORT_CODE( KEYCODE_Q )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( DEF_STR( Yes ) ) PORT_CODE( KEYCODE_0_PAD )
+
+ PORT_START("ROW.1")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Z - Spell of Release" ) PORT_CODE( KEYCODE_Z )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "A" ) PORT_CODE( KEYCODE_A )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "W - Amulet" ) PORT_CODE( KEYCODE_W )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Items" ) PORT_CODE( KEYCODE_1_PAD )
+
+ PORT_START("ROW.2")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "X - Scepter" ) PORT_CODE( KEYCODE_X )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "S - Dagger" ) PORT_CODE( KEYCODE_S )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "E - Black Mace" ) PORT_CODE( KEYCODE_E )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Drop Item" ) PORT_CODE( KEYCODE_2_PAD )
+
+ PORT_START("ROW.3")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "C - Spell of Seeing" ) PORT_CODE( KEYCODE_C )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "D - Great Circlet" ) PORT_CODE( KEYCODE_D )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "R - Blood Sword" ) PORT_CODE( KEYCODE_R )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Give Score" ) PORT_CODE( KEYCODE_3_PAD )
+
+ PORT_START("ROW.4")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "V - Shield" ) PORT_CODE( KEYCODE_V )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "F - Hunting Horn" ) PORT_CODE( KEYCODE_F )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "T - Chalice" ) PORT_CODE( KEYCODE_T )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Replay" ) PORT_CODE( KEYCODE_4_PAD )
+
+ PORT_START("ROW.5")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "B - Silver Wheat" ) PORT_CODE( KEYCODE_B )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "G - Long Bow" ) PORT_CODE( KEYCODE_G )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Y - Coins" ) PORT_CODE( KEYCODE_Y )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Combine Action" ) PORT_CODE( KEYCODE_6_PAD )
+
+ PORT_START("ROW.6")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "N - Staff" ) PORT_CODE( KEYCODE_N )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "H - Medallion" ) PORT_CODE( KEYCODE_H )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "U - Cold Fire" ) PORT_CODE( KEYCODE_U )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Save Game" ) PORT_CODE( KEYCODE_7_PAD )
+
+ PORT_START("ROW.7")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "M - Spell of Understanding" ) PORT_CODE( KEYCODE_M )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "J - Onyx Seal" ) PORT_CODE( KEYCODE_J )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "I - Crown" ) PORT_CODE( KEYCODE_I )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Update" ) PORT_CODE( KEYCODE_8_PAD )
+
+ PORT_START("ROW.8")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "3 - Enter" ) PORT_CODE( KEYCODE_ENTER )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "K - Orb of Quoid" ) PORT_CODE( KEYCODE_K )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "O - Crystal" ) PORT_CODE( KEYCODE_O )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "Hint" ) PORT_CODE( KEYCODE_9_PAD )
+
+ PORT_START("ROW.9")
+ PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "4 - Space" ) PORT_CODE( KEYCODE_SPACE )
+ PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "L" ) PORT_CODE( KEYCODE_L )
+ PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( "P" ) PORT_CODE( KEYCODE_P )
+ PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_OTHER ) PORT_NAME( DEF_STR( No ) ) PORT_CODE( KEYCODE_0_PAD )
+INPUT_PORTS_END
+
+/* Machine Initialization */
+
+void thayers_state::machine_start()
+{
+ memset(&m_ssi263, 0, sizeof(m_ssi263));
+}
+
+void thayers_state::machine_reset()
+{
+ m_laserdisc_data = 0;
+
+ m_rx_bit = 0;
+ m_keylatch = 0;
+
+ m_cop_data_latch = 0;
+ m_cop_data_latch_enable = 0;
+ m_cop_l = 0;
+ m_cop_cmd_latch = 0;
+
+ m_timer_int = 1;
+ m_data_rdy_int = 1;
+ m_ssi_data_request = 1;
+
+ m_cart_present = 0;
+ m_pr7820_enter = 0;
+
+// newtype = (ioport("DSWB")->read() & 0x18) ? LASERDISC_TYPE_PIONEER_LDV1000 : LASERDISC_TYPE_PIONEER_PR7820;
+// laserdisc_set_type(m_laserdisc, newtype);
+}
+
+/* Machine Driver */
+
+static MACHINE_CONFIG_START( thayers, thayers_state )
+
+ /* basic machine hardware */
+ MCFG_CPU_ADD("maincpu", Z80, XTAL_4MHz)
+ MCFG_CPU_PROGRAM_MAP(thayers_map)
+ MCFG_CPU_IO_MAP(thayers_io_map)
+
+ MCFG_CPU_ADD("mcu", COP421, XTAL_4MHz/2) // COP421L-PCA/N
+ MCFG_COP400_CONFIG( COP400_CKI_DIVISOR_4, COP400_CKO_OSCILLATOR_OUTPUT, COP400_MICROBUS_DISABLED )
+ MCFG_COP400_READ_L_CB(READ8(thayers_state, cop_l_r))
+ MCFG_COP400_WRITE_L_CB(WRITE8(thayers_state, cop_l_w))
+ MCFG_COP400_READ_G_CB(READ8(thayers_state, cop_g_r))
+ MCFG_COP400_WRITE_G_CB(WRITE8(thayers_state, cop_g_w))
+ MCFG_COP400_WRITE_D_CB(WRITE8(thayers_state, cop_d_w))
+ MCFG_COP400_READ_SI_CB(READLINE(thayers_state, cop_si_r))
+ MCFG_COP400_WRITE_SO_CB(WRITELINE(thayers_state, cop_so_w))
+
+ MCFG_LASERDISC_PR7820_ADD("laserdisc")
+
+ /* video hardware */
+ MCFG_LASERDISC_SCREEN_ADD_NTSC("screen", "laserdisc")
+
+ MCFG_PALETTE_ADD("palette", 256)
+
+ /* sound hardware */
+ MCFG_SPEAKER_STANDARD_STEREO("lspeaker", "rspeaker")
+ // SSI 263 @ 2MHz
+
+ MCFG_SOUND_MODIFY("laserdisc")
+ MCFG_SOUND_ROUTE(0, "lspeaker", 1.0)
+ MCFG_SOUND_ROUTE(1, "rspeaker", 1.0)
+MACHINE_CONFIG_END
+
+/* ROMs */
+
+ROM_START( thayers )
+ ROM_REGION( 0xe000, "maincpu", 0 )
+ ROM_LOAD( "tq_u33.bin", 0x0000, 0x8000, CRC(82df5d89) SHA1(58dfd62bf8c5a55d1eba397d2c284e99a4685a3f) )
+ ROM_LOAD( "tq_u1.bin", 0xc000, 0x2000, CRC(e8e7f566) SHA1(df7b83ef465c65446c8418bc6007447693b75021) )
+
+ ROM_REGION( 0x400, "mcu", 0 )
+ ROM_LOAD( "tq_cop.bin", 0x000, 0x400, CRC(6748e6b3) SHA1(5d7d1ecb57c1501ef6a2d9691eecc9970586606b) )
+
+ DISK_REGION( "laserdisc" )
+ DISK_IMAGE_READONLY( "thayers", 0, NO_DUMP )
+ROM_END
+
+ROM_START( thayersa )
+ ROM_REGION( 0xe000, "maincpu", 0 )
+ ROM_LOAD( "tq_u33.bin", 0x0000, 0x8000, CRC(82df5d89) SHA1(58dfd62bf8c5a55d1eba397d2c284e99a4685a3f) )
+ ROM_LOAD( "tq_u1.bin", 0xc000, 0x2000, CRC(33817e25) SHA1(f9750da863dd57fe2f5b6e8fce9c6695dc5c9adc) ) // sldh
+
+ ROM_REGION( 0x400, "mcu", 0 )
+ ROM_LOAD( "tq_cop.bin", 0x000, 0x400, CRC(6748e6b3) SHA1(5d7d1ecb57c1501ef6a2d9691eecc9970586606b) )
+
+ DISK_REGION( "laserdisc" )
+ DISK_IMAGE_READONLY( "thayers", 0, NO_DUMP )
+ROM_END
+
+/* Game Drivers */
+
+/* YEAR NAME PARENT MACHINE INPUT INIT MONITOR COMPANY FULLNAME FLAGS LAYOUT */
+GAMEL( 1984, thayers, 0, thayers, thayers, driver_device, 0, ROT0, "RDI Video Systems", "Thayer's Quest (set 1)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_dlair)
+GAMEL( 1984, thayersa, thayers, thayers, thayers, driver_device, 0, ROT0, "RDI Video Systems", "Thayer's Quest (set 2)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_dlair)