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