1 // license:BSD-3-Clause
2 // copyright-holders:Andrew Gardner
3 /*
4 
5     TODO:
6 
7     - LDV1000 mode
8     - PR7820 INT/_EXT line
9     - coin counter
10     - convert SSI-263 to a sound device
11     - dump laserdisc
12 
13 */
14 
15 #include "emu.h"
16 #include "cpu/cop400/cop400.h"
17 #include "cpu/z80/z80.h"
18 #include "machine/ldstub.h"
19 #include "machine/ldv1000.h"
20 #include "speaker.h"
21 
22 #include "thayers.lh"
23 
24 
25 #define LOG 0
26 
27 struct ssi263_t
28 {
29 	uint8_t dr;
30 	uint8_t p;
31 	uint16_t i;
32 	uint8_t r;
33 	uint8_t t;
34 	uint8_t c;
35 	uint8_t a;
36 	uint8_t f;
37 	uint8_t mode;
38 };
39 
40 class thayers_state : public driver_device
41 {
42 public:
thayers_state(const machine_config & mconfig,device_type type,const char * tag)43 	thayers_state(const machine_config &mconfig, device_type type, const char *tag)
44 		: driver_device(mconfig, type, tag)
45 		, m_pr7820(*this, "laserdisc")
46 		, m_ldv1000(*this, "ldv1000")
47 		, m_maincpu(*this, "maincpu")
48 		, m_row(*this, "ROW.%u", 0)
49 		, m_digits(*this, "digit%u", 0U)
50 	{
51 	}
52 
53 	void thayers(machine_config &config);
54 
55 	DECLARE_READ_LINE_MEMBER(laserdisc_enter_r);
56 	DECLARE_READ_LINE_MEMBER(laserdisc_ready_r);
57 
58 private:
59 	enum
60 	{
61 		TIMER_INTRQ_TICK,
62 		TIMER_SSI263_PHONEME_TICK
63 	};
64 
65 	optional_device<pioneer_pr7820_device> m_pr7820;
66 	optional_device<pioneer_ldv1000_device> m_ldv1000;
67 	uint8_t m_laserdisc_data;
68 	int m_rx_bit;
69 	int m_keylatch;
70 	uint8_t m_keydata;
71 	bool m_kbdata;
72 	bool m_kbclk;
73 	uint8_t m_cop_data_latch;
74 	int m_cop_data_latch_enable;
75 	uint8_t m_cop_l;
76 	uint8_t m_cop_cmd_latch;
77 	int m_timer_int;
78 	int m_data_rdy_int;
79 	int m_ssi_data_request;
80 	int m_cart_present;
81 	int m_pr7820_enter;
82 	struct ssi263_t m_ssi263;
83 	void intrq_w(uint8_t data);
84 	uint8_t irqstate_r();
85 	void timer_int_ack_w(uint8_t data);
86 	void data_rdy_int_ack_w(uint8_t data);
87 	void cop_d_w(uint8_t data);
88 	uint8_t cop_data_r();
89 	void cop_data_w(uint8_t data);
90 	uint8_t cop_l_r();
91 	void cop_l_w(uint8_t data);
92 	uint8_t cop_g_r();
93 	void control_w(uint8_t data);
94 	void cop_g_w(uint8_t data);
95 	DECLARE_READ_LINE_MEMBER(kbdata_r);
96 	DECLARE_WRITE_LINE_MEMBER(kbclk_w);
97 	void control2_w(uint8_t data);
98 	uint8_t dsw_b_r();
99 	uint8_t laserdsc_data_r();
100 	void laserdsc_data_w(uint8_t data);
101 	void laserdsc_control_w(uint8_t data);
102 	void den1_w(uint8_t data);
103 	void den2_w(uint8_t data);
104 	void ssi263_register_w(offs_t offset, uint8_t data);
105 	uint8_t ssi263_register_r();
106 
107 	virtual void machine_start() override;
108 	virtual void machine_reset() override;
109 	void check_interrupt();
110 	required_device<cpu_device> m_maincpu;
111 	required_ioport_array<10> m_row;
112 	output_finder<16> m_digits;
113 
114 	void thayers_io_map(address_map &map);
115 	void thayers_map(address_map &map);
116 
117 	virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
118 };
119 
120 static const uint8_t led_map[16] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7c, 0x07, 0x7f, 0x67, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x00 };
121 
122 /* Interrupts */
123 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)124 void thayers_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
125 {
126 	switch (id)
127 	{
128 	case TIMER_INTRQ_TICK:
129 		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
130 		break;
131 	case TIMER_SSI263_PHONEME_TICK:
132 		m_ssi_data_request = 0;
133 		check_interrupt();
134 		break;
135 	default:
136 		throw emu_fatalerror("Unknown id in thayers_state::device_timer");
137 	}
138 }
139 
check_interrupt()140 void thayers_state::check_interrupt()
141 {
142 	if (!m_timer_int || !m_data_rdy_int || !m_ssi_data_request)
143 	{
144 		m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
145 	}
146 	else
147 	{
148 		m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE);
149 	}
150 }
151 
intrq_w(uint8_t data)152 void thayers_state::intrq_w(uint8_t data)
153 {
154 	// T = 1.1 * R30 * C53 = 1.1 * 750K * 0.01uF = 8.25 ms
155 
156 	m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE);
157 
158 	timer_set(attotime::from_usec(8250), TIMER_INTRQ_TICK);
159 }
160 
irqstate_r()161 uint8_t thayers_state::irqstate_r()
162 {
163 	/*
164 
165 	    bit     description
166 
167 	    0
168 	    1
169 	    2       SSI263 A/_R
170 	    3       tied to +5V
171 	    4       _TIMER INT
172 	    5       _DATA RDY INT
173 	    6       _CART PRES
174 	    7
175 
176 	*/
177 
178 	return m_cart_present << 6 | (m_data_rdy_int << 5) | (m_timer_int << 4) | 0x08 | (m_ssi_data_request << 2);
179 }
180 
timer_int_ack_w(uint8_t data)181 void thayers_state::timer_int_ack_w(uint8_t data)
182 {
183 	if (LOG) logerror("%s %s TIMER INT ACK\n", machine().time().as_string(), machine().describe_context());
184 
185 	m_timer_int = 1;
186 
187 	check_interrupt();
188 }
189 
data_rdy_int_ack_w(uint8_t data)190 void thayers_state::data_rdy_int_ack_w(uint8_t data)
191 {
192 	if (LOG) logerror("%s %s DATA RDY INT ACK\n", machine().time().as_string(), machine().describe_context());
193 
194 	m_data_rdy_int = 1;
195 
196 	check_interrupt();
197 }
198 
cop_d_w(uint8_t data)199 void thayers_state::cop_d_w(uint8_t data)
200 {
201 	/*
202 
203 	    bit     description
204 
205 	    D0      _TIMER INT
206 	    D1      _DATA RDY INT
207 	    D2
208 	    D3
209 
210 	*/
211 
212 	if (!BIT(data, 0))
213 	{
214 		if (LOG) logerror("%s %s TIMER INT\n", machine().time().as_string(), machine().describe_context());
215 		m_timer_int = 0;
216 	}
217 
218 	if (!BIT(data, 1))
219 	{
220 		if (LOG) logerror("%s %s DATA RDY INT\n", machine().time().as_string(), machine().describe_context());
221 		m_data_rdy_int = 0;
222 	}
223 
224 	check_interrupt();
225 }
226 
227 /* COP Communication */
228 
cop_data_r()229 uint8_t thayers_state::cop_data_r()
230 {
231 	if (!m_cop_data_latch_enable)
232 	{
233 		return m_cop_data_latch;
234 	}
235 	else
236 	{
237 		return m_cop_l;
238 	}
239 }
240 
cop_data_w(uint8_t data)241 void thayers_state::cop_data_w(uint8_t data)
242 {
243 	m_cop_data_latch = data;
244 	if (LOG) logerror("COP DATA %02x\n", m_cop_data_latch);
245 }
246 
cop_l_r()247 uint8_t thayers_state::cop_l_r()
248 {
249 	if (!m_cop_data_latch_enable)
250 	{
251 		return m_cop_data_latch;
252 	}
253 	else
254 	{
255 		return 0;
256 	}
257 }
258 
cop_l_w(uint8_t data)259 void thayers_state::cop_l_w(uint8_t data)
260 {
261 	m_cop_l = data;
262 	if (LOG) logerror("COP L %02x\n", m_cop_l);
263 }
264 
cop_g_r()265 uint8_t thayers_state::cop_g_r()
266 {
267 	/*
268 
269 	    bit     description
270 
271 	    G0      U16 Q0
272 	    G1      U16 Q1
273 	    G2      U16 Q2
274 	    G3
275 
276 	*/
277 
278 	return m_cop_cmd_latch;
279 }
280 
control_w(uint8_t data)281 void thayers_state::control_w(uint8_t data)
282 {
283 	/*
284 
285 	    bit     description
286 
287 	    0
288 	    1       _CS128A
289 	    2       _BANKSEL1
290 	    3
291 	    4
292 	    5       COP G0
293 	    6       COP G1
294 	    7       COP G2
295 
296 	*/
297 
298 	m_cop_cmd_latch = (data >> 5) & 0x07;
299 	if (LOG) logerror("COP G0..2 %u\n", m_cop_cmd_latch);
300 }
301 
cop_g_w(uint8_t data)302 void thayers_state::cop_g_w(uint8_t data)
303 {
304 	/*
305 
306 	    bit     description
307 
308 	    G0
309 	    G1
310 	    G2
311 	    G3      U17 enable
312 
313 	*/
314 
315 	m_cop_data_latch_enable = BIT(data, 3);
316 	if (LOG) logerror("U17 enable %u\n", m_cop_data_latch_enable);
317 }
318 
319 /* Keyboard */
320 
READ_LINE_MEMBER(thayers_state::kbdata_r)321 READ_LINE_MEMBER(thayers_state::kbdata_r)
322 {
323 	if (LOG) logerror("%s KBDATA %u BIT %u\n",machine().time().as_string(),m_kbdata,m_rx_bit);
324 	return m_kbdata;
325 }
326 
WRITE_LINE_MEMBER(thayers_state::kbclk_w)327 WRITE_LINE_MEMBER(thayers_state::kbclk_w)
328 {
329 	if (m_kbclk != state) {
330 		if (LOG) logerror("%s %s KBCLK %u\n", machine().time().as_string(), machine().describe_context(),state);
331 	}
332 
333 	if (!m_kbclk && state) {
334 		m_rx_bit++;
335 
336 		// 1, 1, 0, 1, Q9, P3, P2, P1, P0, 0
337 		switch (m_rx_bit)
338 		{
339 		case 0: case 1: case 3:
340 			m_kbdata = 1;
341 			break;
342 
343 		case 2: case 9:
344 			m_kbdata = 0;
345 			break;
346 
347 		case 10:
348 			m_rx_bit = 0;
349 			m_kbdata = 1;
350 
351 			m_keylatch++;
352 
353 			if (m_keylatch == 10) {
354 				m_keylatch = 0;
355 			}
356 
357 			m_keydata = m_row[m_keylatch]->read();
358 
359 			if (LOG) logerror("keylatch %u\n",m_keylatch);
360 			break;
361 
362 		case 4:
363 			m_kbdata = (m_keylatch == 9);
364 			break;
365 
366 		default:
367 			m_kbdata = BIT(m_keydata, 3);
368 			m_keydata <<= 1;
369 
370 			if (LOG) logerror("keydata %02x shift\n",m_keydata);
371 			break;
372 		}
373 	}
374 
375 	m_kbclk = state;
376 }
377 
378 /* I/O Board */
379 
control2_w(uint8_t data)380 void thayers_state::control2_w(uint8_t data)
381 {
382 	/*
383 
384 	    bit     description
385 
386 	    0
387 	    1       _RESOI (?)
388 	    2       _ENCARTDET
389 	    3
390 	    4
391 	    5
392 	    6
393 	    7
394 
395 	*/
396 
397 	if ((!BIT(data, 2)) & m_cart_present)
398 	{
399 		m_maincpu->set_input_line(INPUT_LINE_NMI, HOLD_LINE);
400 	}
401 }
402 
dsw_b_r()403 uint8_t thayers_state::dsw_b_r()
404 {
405 	return (ioport("COIN")->read() & 0xf0) | (ioport("DSWB")->read() & 0x0f);
406 }
407 
laserdsc_data_r()408 uint8_t thayers_state::laserdsc_data_r()
409 {
410 	if (m_ldv1000 != nullptr) return m_ldv1000->status_r();
411 	if (m_pr7820 != nullptr) return m_pr7820->data_r();
412 	return 0;
413 }
414 
laserdsc_data_w(uint8_t data)415 void thayers_state::laserdsc_data_w(uint8_t data)
416 {
417 	m_laserdisc_data = data;
418 }
419 
laserdsc_control_w(uint8_t data)420 void thayers_state::laserdsc_control_w(uint8_t data)
421 {
422 	/*
423 
424 	    bit     description
425 
426 	    0
427 	    1
428 	    2
429 	    3
430 	    4       coin counter
431 	    5       U16 output enable
432 	    6       ENTER if switch B5 closed
433 	    7       INT/_EXT
434 
435 	*/
436 
437 	machine().bookkeeping().coin_counter_w(0, BIT(data, 4));
438 
439 	if (BIT(data, 5))
440 	{
441 		if (m_ldv1000 != nullptr)
442 		{
443 			m_ldv1000->data_w(m_laserdisc_data);
444 			m_ldv1000->enter_w(BIT(data, 7) ? CLEAR_LINE : ASSERT_LINE);
445 		}
446 		if (m_pr7820 != nullptr)
447 		{
448 			m_pr7820->data_w(m_laserdisc_data);
449 			m_pr7820_enter = BIT(data, 6) ? CLEAR_LINE : ASSERT_LINE;
450 			m_pr7820->enter_w(m_pr7820_enter);
451 			// BIT(data, 7) is INT/_EXT, but there is no such input line in laserdsc.h
452 		}
453 	}
454 }
455 
den1_w(uint8_t data)456 void thayers_state::den1_w(uint8_t data)
457 {
458 	/*
459 
460 	    bit     description
461 
462 	    0       DD0
463 	    1       DD1
464 	    2       DD2
465 	    3       DD3
466 	    4       DA0
467 	    5       DA1
468 	    6       DA2
469 	    7       DA3
470 
471 	*/
472 
473 	m_digits[data >> 4] = led_map[data & 0x0f];
474 }
475 
den2_w(uint8_t data)476 void thayers_state::den2_w(uint8_t data)
477 {
478 	/*
479 
480 	    bit     description
481 
482 	    0       DD0
483 	    1       DD1
484 	    2       DD2
485 	    3       DD3
486 	    4       DA0
487 	    5       DA1
488 	    6       DA2
489 	    7       DA3
490 
491 	*/
492 
493 	m_digits[8 + (data >> 4)] = led_map[data & 0x0f];
494 }
495 
496 /* SSI-263 */
497 
498 /*
499 
500     The following information is from the SSI-263A data sheet.
501 
502     Thayer's Quest uses an SSI-263, so this might be inaccurate, but it works for now
503 
504 */
505 
506 #define SSI263_CLOCK (XTAL(4'000'000)/2)
507 
508 static const char SSI263_PHONEMES[0x40][5] =
509 {
510 	"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",
511 	"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"
512 };
513 
ssi263_register_w(offs_t offset,uint8_t data)514 void thayers_state::ssi263_register_w(offs_t offset, uint8_t data)
515 {
516 	struct ssi263_t &ssi263 = m_ssi263;
517 	switch (offset)
518 	{
519 	case 0:
520 		{
521 		int frame_time = ((4096 * (16 - ssi263.r)) / 2); // us, /2 should actually be /SSI263_CLOCK, but this way we get microseconds directly
522 		int phoneme_time = frame_time * (4 - ssi263.dr); // us
523 
524 		// duration/phoneme register
525 		ssi263.dr = (data >> 5) & 0x03;
526 		ssi263.p = data & 0x3f;
527 
528 		m_ssi_data_request = 1;
529 		check_interrupt();
530 
531 		switch (ssi263.mode)
532 		{
533 		case 0:
534 		case 1:
535 			// phoneme timing response
536 			timer_set(attotime::from_usec(phoneme_time), TIMER_SSI263_PHONEME_TICK);
537 			break;
538 		case 2:
539 			// frame timing response
540 			timer_set(attotime::from_usec(frame_time), TIMER_SSI263_PHONEME_TICK);
541 			break;
542 		case 3:
543 			// disable A/_R output
544 			break;
545 		}
546 
547 		//logerror("SSI263 Phoneme Duration: %u\n", ssi263.dr);
548 		//logerror("SSI263 Phoneme: %02x %s\n", ssi263.p, SSI263_PHONEMES[ssi263.p]);
549 		if (LOG && ssi263.p) printf("%s ", SSI263_PHONEMES[ssi263.p]);
550 		}
551 		break;
552 
553 	case 1:
554 		// inflection register
555 		ssi263.i = (data << 3) | (ssi263.i & 0x403);
556 
557 		//logerror("SSI263 Inflection: %u\n", ssi263.i);
558 		break;
559 
560 	case 2:
561 		// rate/inflection register
562 		ssi263.i = (BIT(data, 4) << 11) | (ssi263.i & 0x7f8) | (data & 0x07);
563 		ssi263.r = data >> 4;
564 
565 		//logerror("SSI263 Inflection: %u\n", ssi263.i);
566 		//logerror("SSI263 Rate: %u\n", ssi263.r);
567 		break;
568 
569 	case 3:
570 		// control/articulation/amplitude register
571 		if (ssi263.c && !BIT(data, 7))
572 		{
573 			ssi263.mode = ssi263.dr;
574 
575 			switch (ssi263.mode)
576 			{
577 			case 0:
578 				//logerror("SSI263 Phoneme Timing Response, Transitioned Inflection\n");
579 				break;
580 
581 			case 1:
582 				//logerror("SSI263 Phoneme Timing Response, Immediate Inflection\n");
583 				break;
584 
585 			case 2:
586 				// activate A/_R
587 				//logerror("SSI263 Frame Timing Response, Immediate Inflection\n");
588 				break;
589 
590 			case 3:
591 				// disable A/_R output
592 				//logerror("SSI263 A/R Output Disabled\n");
593 				break;
594 			}
595 		}
596 
597 		ssi263.c = BIT(data, 7);
598 		ssi263.t = (data >> 4) & 0x07;
599 		ssi263.a = data & 0x0f;
600 
601 		//logerror("SSI263 Control: %u\n", ssi263.c);
602 		//logerror("SSI263 Articulation: %u\n", ssi263.t);
603 		//logerror("SSI263 Amplitude: %u\n", ssi263.a);
604 		break;
605 
606 	case 4:
607 	case 5:
608 	case 6:
609 	case 7:
610 		// filter frequency register
611 		ssi263.f = data;
612 		//logerror("SSI263 Filter Frequency: %u\n", ssi263.f);
613 		break;
614 	}
615 }
616 
ssi263_register_r()617 uint8_t thayers_state::ssi263_register_r()
618 {
619 	// D7 becomes an output, as the inverted state of A/_R. The register address bits are ignored.
620 
621 	return !m_ssi_data_request << 7;
622 }
623 
624 /* Memory Maps */
625 
thayers_map(address_map & map)626 void thayers_state::thayers_map(address_map &map)
627 {
628 	map(0x0000, 0x7fff).rom();
629 	map(0x8000, 0xbfff).ram();
630 	map(0xc000, 0xdfff).rom();
631 }
632 
thayers_io_map(address_map & map)633 void thayers_state::thayers_io_map(address_map &map)
634 {
635 	map.global_mask(0xff);
636 	map(0x00, 0x07).rw(FUNC(thayers_state::ssi263_register_r), FUNC(thayers_state::ssi263_register_w));
637 	map(0x20, 0x20).w(FUNC(thayers_state::control_w));
638 	map(0x40, 0x40).rw(FUNC(thayers_state::irqstate_r), FUNC(thayers_state::control2_w));
639 	map(0x80, 0x80).rw(FUNC(thayers_state::cop_data_r), FUNC(thayers_state::cop_data_w));
640 	map(0xa0, 0xa0).w(FUNC(thayers_state::timer_int_ack_w));
641 	map(0xc0, 0xc0).w(FUNC(thayers_state::data_rdy_int_ack_w));
642 	map(0xf0, 0xf0).r(FUNC(thayers_state::laserdsc_data_r));
643 	map(0xf1, 0xf1).r(FUNC(thayers_state::dsw_b_r));
644 	map(0xf2, 0xf2).portr("DSWA");
645 	map(0xf3, 0xf3).w(FUNC(thayers_state::intrq_w));
646 	map(0xf4, 0xf4).w(FUNC(thayers_state::laserdsc_data_w));
647 	map(0xf5, 0xf5).w(FUNC(thayers_state::laserdsc_control_w));
648 	map(0xf6, 0xf6).w(FUNC(thayers_state::den1_w));
649 	map(0xf7, 0xf7).w(FUNC(thayers_state::den2_w));
650 }
651 
652 /* Input Ports */
653 
READ_LINE_MEMBER(thayers_state::laserdisc_enter_r)654 READ_LINE_MEMBER(thayers_state::laserdisc_enter_r)
655 {
656 	if (m_pr7820 != nullptr) return m_pr7820_enter;
657 	if (m_ldv1000 != nullptr) return (m_ldv1000->status_strobe_r() == ASSERT_LINE) ? 0 : 1;
658 	return 0;
659 }
660 
READ_LINE_MEMBER(thayers_state::laserdisc_ready_r)661 READ_LINE_MEMBER(thayers_state::laserdisc_ready_r)
662 {
663 	if (m_pr7820 != nullptr) return (m_pr7820->ready_r() == ASSERT_LINE) ? 0 : 1;
664 	if (m_ldv1000 != nullptr) return (m_ldv1000->command_strobe_r() == ASSERT_LINE) ? 0 : 1;
665 	return 0;
666 }
667 
668 static INPUT_PORTS_START( thayers )
669 	PORT_START("DSWA")
670 	PORT_DIPNAME( 0x07, 0x07, "Time Per Coin" ) PORT_DIPLOCATION( "A:3,2,1" )
671 	PORT_DIPSETTING(    0x07, "110 Seconds" )
672 	PORT_DIPSETTING(    0x06, "95 Seconds" )
673 	PORT_DIPSETTING(    0x05, "80 Seconds" )
674 	PORT_DIPSETTING(    0x04, "70 Seconds" )
675 	PORT_DIPSETTING(    0x03, "60 Seconds" )
676 	PORT_DIPSETTING(    0x02, "45 Seconds" )
677 	PORT_DIPSETTING(    0x01, "30 Seconds" )
DEF_STR(Free_Play)678 	PORT_DIPSETTING(    0x00, DEF_STR ( Free_Play ) )
679 	PORT_DIPNAME( 0x08, 0x08, DEF_STR( Coinage ) ) PORT_DIPLOCATION( "A:4" )
680 	PORT_DIPSETTING(    0x00, DEF_STR( 2C_1C ) )
681 	PORT_DIPSETTING(    0x08, DEF_STR( 1C_1C ) )
682 	PORT_DIPNAME( 0x10, 0x10, DEF_STR( Lives ) ) PORT_DIPLOCATION( "A:5" )
683 	PORT_DIPSETTING(    0x10, "3" )
684 	PORT_DIPSETTING(    0x00, "5" )
685 	PORT_DIPNAME( 0x20, 0x20, DEF_STR( Demo_Sounds ) ) PORT_DIPLOCATION( "A:6" )
686 	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
687 	PORT_DIPSETTING(    0x20, DEF_STR( On ) )
688 	PORT_DIPNAME( 0x40, 0x40, "Attract Mode Audio" ) PORT_DIPLOCATION( "A:7" )
689 	PORT_DIPSETTING(    0x40, "Always Playing" )
690 	PORT_DIPSETTING(    0x00, "One Out of 8 Times" )
691 	PORT_DIPUNUSED_DIPLOC( 0x80, IP_ACTIVE_LOW, "A:8" )
692 
693 	PORT_START("DSWB")
694 	PORT_SERVICE_DIPLOC( 0x01, 0x01, "B:1" )
695 	PORT_DIPUNUSED_DIPLOC( 0x02, IP_ACTIVE_LOW, "B:2" )
696 	PORT_DIPUNUSED_DIPLOC( 0x04, IP_ACTIVE_LOW, "B:3" )
697 	PORT_DIPNAME( 0x18, 0x00, "LD Player" ) PORT_DIPLOCATION( "B:5,4" )
698 	PORT_DIPSETTING(    0x18, "LDV-1000" )
699 	PORT_DIPSETTING(    0x00, "PR-7820" )
700 	PORT_DIPUNUSED_DIPLOC( 0xe0, IP_ACTIVE_LOW, "B:8,7,6" )
701 
702 	PORT_START("COIN")
703 	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_COIN1 )
704 	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_COIN2 )
705 	PORT_BIT( 0x40, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(thayers_state, laserdisc_enter_r)
706 	PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_MEMBER(thayers_state, laserdisc_ready_r)
707 
708 	PORT_START("ROW.0")
709 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("1 YES") PORT_CODE(KEYCODE_1)
710 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_Q)
711 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F1 CLEAR") PORT_CODE(KEYCODE_F1) PORT_CODE(KEYCODE_BACKSPACE)
712 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_F2)
713 
714 	PORT_START("ROW.1")
715 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("2 ITEMS") PORT_CODE(KEYCODE_2)
716 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("W AMULET") PORT_CODE(KEYCODE_W)
717 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_A)
718 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Z SPELL OF RELEASE") PORT_CODE(KEYCODE_Z)
719 
720 	PORT_START("ROW.2")
721 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("3 DROP ITEM") PORT_CODE(KEYCODE_3)
722 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("E BLACK MACE") PORT_CODE(KEYCODE_E)
723 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("S DAGGER") PORT_CODE(KEYCODE_S)
724 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("X SCEPTER") PORT_CODE(KEYCODE_X)
725 
726 	PORT_START("ROW.3")
727 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("4 GIVE SCORE") PORT_CODE(KEYCODE_4)
728 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("R BLOOD SWORD") PORT_CODE(KEYCODE_R)
729 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("D GREAT CIRCLET") PORT_CODE(KEYCODE_D)
730 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("C SPELL OF SEEING") PORT_CODE(KEYCODE_C)
731 
732 	PORT_START("ROW.4")
733 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("5 REPLAY") PORT_CODE(KEYCODE_5)
734 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("T CHALICE") PORT_CODE(KEYCODE_T)
735 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F HUNTING HORN") PORT_CODE(KEYCODE_F)
736 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("V SHIELD") PORT_CODE(KEYCODE_V)
737 
738 	PORT_START("ROW.5")
739 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("6 COMBINE ACTION") PORT_CODE(KEYCODE_6)
740 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("Y COINS") PORT_CODE(KEYCODE_Y)
741 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("G LONG BOW") PORT_CODE(KEYCODE_G)
742 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("B SILVER WHEAT") PORT_CODE(KEYCODE_B)
743 
744 	PORT_START("ROW.6")
745 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("7 SAVE GAME") PORT_CODE(KEYCODE_7)
746 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("U COLD FIRE") PORT_CODE(KEYCODE_U)
747 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("H MEDALLION") PORT_CODE(KEYCODE_H)
748 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("N STAFF") PORT_CODE(KEYCODE_N)
749 
750 	PORT_START("ROW.7")
751 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("8 UPDATE") PORT_CODE(KEYCODE_8)
752 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("I CROWN") PORT_CODE(KEYCODE_I)
753 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("J ONYX SEAL") PORT_CODE(KEYCODE_J)
754 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("M SPELL OF UNDERSTANDING") PORT_CODE(KEYCODE_M)
755 
756 	PORT_START("ROW.8")
757 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("9 HINT") PORT_CODE(KEYCODE_9)
758 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("O CRYSTAL") PORT_CODE(KEYCODE_O)
759 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("K ORB OF QUOID") PORT_CODE(KEYCODE_K)
760 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F4 SPACE") PORT_CODE(KEYCODE_F4) PORT_CODE(KEYCODE_SPACE)
761 
762 	PORT_START("ROW.9")
763 	PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("0 NO") PORT_CODE(KEYCODE_0)
764 	PORT_BIT( 0x02, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_P)
765 	PORT_BIT( 0x04, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_CODE(KEYCODE_L)
766 	PORT_BIT( 0x08, IP_ACTIVE_HIGH, IPT_KEYBOARD ) PORT_NAME("F3 ENTER") PORT_CODE(KEYCODE_F3) PORT_CODE(KEYCODE_ENTER)
767 INPUT_PORTS_END
768 
769 /* Machine Initialization */
770 
771 void thayers_state::machine_start()
772 {
773 	m_digits.resolve();
774 	memset(&m_ssi263, 0, sizeof(m_ssi263));
775 }
776 
machine_reset()777 void thayers_state::machine_reset()
778 {
779 	m_laserdisc_data = 0;
780 
781 	m_rx_bit = 0;
782 	m_kbdata = 1;
783 	m_keylatch = 0;
784 	m_keydata = m_row[m_keylatch]->read();
785 
786 	m_cop_data_latch = 0;
787 	m_cop_data_latch_enable = 0;
788 	m_cop_l = 0;
789 	m_cop_cmd_latch = 0;
790 
791 	m_timer_int = 1;
792 	m_data_rdy_int = 1;
793 	m_ssi_data_request = 1;
794 
795 	m_cart_present = 0;
796 	m_pr7820_enter = 0;
797 }
798 
799 /* Machine Driver */
800 
thayers(machine_config & config)801 void thayers_state::thayers(machine_config &config)
802 {
803 	/* basic machine hardware */
804 	Z80(config, m_maincpu, XTAL(4'000'000));
805 	m_maincpu->set_addrmap(AS_PROGRAM, &thayers_state::thayers_map);
806 	m_maincpu->set_addrmap(AS_IO, &thayers_state::thayers_io_map);
807 
808 	cop421_cpu_device &mcu(COP421(config, "mcu", XTAL(4'000'000)/2)); // COP421L-PCA/N
809 	mcu.set_config(COP400_CKI_DIVISOR_16, COP400_CKO_OSCILLATOR_OUTPUT, false);
810 	mcu.read_l().set(FUNC(thayers_state::cop_l_r));
811 	mcu.write_l().set(FUNC(thayers_state::cop_l_w));
812 	mcu.read_g().set(FUNC(thayers_state::cop_g_r));
813 	mcu.write_g().set(FUNC(thayers_state::cop_g_w));
814 	mcu.write_d().set(FUNC(thayers_state::cop_d_w));
815 	mcu.read_si().set(FUNC(thayers_state::kbdata_r));
816 	mcu.write_so().set(FUNC(thayers_state::kbclk_w));
817 
818 	/* video hardware */
819 	PIONEER_PR7820(config, m_pr7820, 0);
820 	m_pr7820->set_screen("screen");
821 
822 	screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
823 	screen.set_video_attributes(VIDEO_SELF_RENDER);
824 	screen.set_raw(XTAL(14'318'181)*2, 910, 0, 704, 525, 44, 524);
825 	screen.set_screen_update("laserdisc", FUNC(laserdisc_device::screen_update));
826 
827 	PALETTE(config, "palette").set_entries(256);
828 
829 	/* sound hardware */
830 	SPEAKER(config, "lspeaker").front_left();
831 	SPEAKER(config, "rspeaker").front_right();
832 	// SSI 263 @ 2MHz
833 
834 	m_pr7820->add_route(0, "lspeaker", 1.0);
835 	m_pr7820->add_route(1, "rspeaker", 1.0);
836 }
837 
838 /* ROMs */
839 
840 ROM_START( thayers )
841 	ROM_REGION( 0xe000, "maincpu", 0 )
842 	ROM_LOAD( "tq_u33.bin", 0x0000, 0x8000, CRC(82df5d89) SHA1(58dfd62bf8c5a55d1eba397d2c284e99a4685a3f) )
843 	ROM_LOAD( "tq_u1.bin",  0xc000, 0x2000, CRC(e8e7f566) SHA1(df7b83ef465c65446c8418bc6007447693b75021) )
844 
845 	ROM_REGION( 0x400, "mcu", 0 )
846 	ROM_LOAD( "tq_cop.bin", 0x000, 0x400, CRC(6748e6b3) SHA1(5d7d1ecb57c1501ef6a2d9691eecc9970586606b) )
847 
848 	DISK_REGION( "laserdisc" )
849 	DISK_IMAGE_READONLY( "thayers", 0, NO_DUMP )
850 ROM_END
851 
852 ROM_START( thayersa )
853 	ROM_REGION( 0xe000, "maincpu", 0 )
854 	ROM_LOAD( "tq_u33.bin", 0x0000, 0x8000, CRC(82df5d89) SHA1(58dfd62bf8c5a55d1eba397d2c284e99a4685a3f) )
855 	ROM_LOAD( "tq_u1.bin",  0xc000, 0x2000, CRC(33817e25) SHA1(f9750da863dd57fe2f5b6e8fce9c6695dc5c9adc) ) // sldh
856 
857 	ROM_REGION( 0x400, "mcu", 0 )
858 	ROM_LOAD( "tq_cop.bin", 0x000, 0x400, CRC(6748e6b3) SHA1(5d7d1ecb57c1501ef6a2d9691eecc9970586606b) )
859 
860 	DISK_REGION( "laserdisc" )
861 	DISK_IMAGE_READONLY( "thayers", 0, NO_DUMP )
862 ROM_END
863 
864 /* Game Drivers */
865 
866 //     YEAR  NAME      PARENT   MACHINE  INPUT    CLASS          INIT        MONITOR  COMPANY               FULLNAME                   FLAGS                                   LAYOUT
867 GAMEL( 1984, thayers,  0,       thayers, thayers, thayers_state, empty_init, ROT0,    "RDI Video Systems",  "Thayer's Quest (set 1)",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_thayers)
868 GAMEL( 1984, thayersa, thayers, thayers, thayers, thayers_state, empty_init, ROT0,    "RDI Video Systems",  "Thayer's Quest (set 2)",  MACHINE_NOT_WORKING | MACHINE_NO_SOUND, layout_thayers)
869