1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*************************************************************************
4
5 Exidy 6502 hardware
6
7 *************************************************************************/
8
9 #include "emu.h"
10 #include "audio/exidy.h"
11
12 #include "cpu/z80/z80.h"
13 #include "machine/input_merger.h"
14 #include "machine/rescap.h"
15 #include "cpu/m6502/m6502.h"
16 #include "speaker.h"
17
18
19
20 /*************************************
21 *
22 * Constants
23 *
24 *************************************/
25
26 #define CRYSTAL_OSC (XTAL(3'579'545))
27 #define SH8253_CLOCK (CRYSTAL_OSC / 2)
28 #define SH6840_CLOCK (CRYSTAL_OSC / 4)
29 #define SH6532_CLOCK (CRYSTAL_OSC / 4)
30 #define CVSD_CLOCK (1.0 / (0.693 * (RES_K(2.4) + 2.0 * RES_K(20)) * CAP_P(2200)))
31 #define CVSD_Z80_CLOCK (CRYSTAL_OSC / 2)
32 #define BASE_VOLUME (32767 / 6)
33
34
35 /*************************************
36 *
37 * 6840 clock counting helper
38 *
39 *************************************/
40
sh6840_apply_clock(exidy_sound_device::sh6840_timer_channel * t,int clocks)41 inline void exidy_sound_device::sh6840_apply_clock(exidy_sound_device::sh6840_timer_channel *t, int clocks)
42 {
43 /* dual 8-bit case */
44 if (t->cr & 0x04)
45 {
46 /* handle full decrements */
47 while (clocks > t->counter.b.l)
48 {
49 clocks -= t->counter.b.l + 1;
50 t->counter.b.l = t->timer;
51
52 /* decrement MSB */
53 if (!t->counter.b.h--)
54 {
55 t->state = 0;
56 t->counter.w = t->timer;
57 }
58
59 /* state goes high when MSB is 0 */
60 else if (!t->counter.b.h)
61 {
62 t->state = 1;
63 t->clocks++;
64 }
65 }
66
67 /* subtract off the remainder */
68 t->counter.b.l -= clocks;
69 }
70
71 /* 16-bit case */
72 else
73 {
74 /* handle full decrements */
75 while (clocks > t->counter.w)
76 {
77 clocks -= t->counter.w + 1;
78 t->state ^= 1;
79 t->clocks += t->state;
80 t->counter.w = t->timer;
81 }
82
83 /* subtract off the remainder */
84 t->counter.w -= clocks;
85 }
86 }
87
88
89
90 /*************************************
91 *
92 * Noise generation helper
93 *
94 *************************************/
95
sh6840_update_noise(int clocks)96 inline int exidy_sound_device::sh6840_update_noise(int clocks)
97 {
98 uint32_t newxor;
99 int noise_clocks = 0;
100 int i;
101
102 /* loop over clocks */
103 for (i = 0; i < clocks; i++)
104 {
105 /* shift the LFSR. its a LOOOONG LFSR, so we need
106 * four longs to hold it all!
107 * first we grab new sample, then shift the high bits,
108 * then the low ones; finally or in the result and see if we've
109 * had a 0->1 transition */
110 newxor = (m_sh6840_LFSR_3 ^ m_sh6840_LFSR_2) >> 31; /* high bits of 3 and 2 xored is new xor */
111 m_sh6840_LFSR_3 <<= 1;
112 m_sh6840_LFSR_3 |= m_sh6840_LFSR_2 >> 31;
113 m_sh6840_LFSR_2 <<= 1;
114 m_sh6840_LFSR_2 |= m_sh6840_LFSR_1 >> 31;
115 m_sh6840_LFSR_1 <<= 1;
116 m_sh6840_LFSR_1 |= m_sh6840_LFSR_0 >> 31;
117 m_sh6840_LFSR_0 <<= 1;
118 m_sh6840_LFSR_0 |= newxor ^ m_sh6840_LFSR_oldxor;
119 m_sh6840_LFSR_oldxor = newxor;
120 /*printf("LFSR: %4x, %4x, %4x, %4x\n", sh6840_LFSR_3, sh6840_LFSR_2, sh6840_LFSR_1, sh6840_LFSR_0);*/
121 /* if we clocked 0->1, that will serve as an external clock */
122 if ((m_sh6840_LFSR_2 & 0x03) == 0x01) /* tap is at 96th bit */
123 {
124 noise_clocks++;
125 }
126 }
127 return noise_clocks;
128 }
129
130
131
132 /*************************************
133 *
134 * 6840 state saving
135 *
136 *************************************/
137
sh6840_register_state_globals()138 void exidy_sound_device::sh6840_register_state_globals()
139 {
140 save_item(NAME(m_sh6840_volume));
141 save_item(NAME(m_sh6840_MSB_latch));
142 save_item(NAME(m_sh6840_LSB_latch));
143 save_item(NAME(m_sh6840_LFSR_oldxor));
144 save_item(NAME(m_sh6840_LFSR_0));
145 save_item(NAME(m_sh6840_LFSR_1));
146 save_item(NAME(m_sh6840_LFSR_2));
147 save_item(NAME(m_sh6840_LFSR_3));
148 save_item(NAME(m_sh6840_clock_count));
149 save_item(NAME(m_sfxctrl));
150 save_item(NAME(m_sh6840_timer[0].cr));
151 save_item(NAME(m_sh6840_timer[0].state));
152 save_item(NAME(m_sh6840_timer[0].leftovers));
153 save_item(NAME(m_sh6840_timer[0].timer));
154 save_item(NAME(m_sh6840_timer[0].clocks));
155 save_item(NAME(m_sh6840_timer[0].counter.w));
156 save_item(NAME(m_sh6840_timer[1].cr));
157 save_item(NAME(m_sh6840_timer[1].state));
158 save_item(NAME(m_sh6840_timer[1].leftovers));
159 save_item(NAME(m_sh6840_timer[1].timer));
160 save_item(NAME(m_sh6840_timer[1].clocks));
161 save_item(NAME(m_sh6840_timer[1].counter.w));
162 save_item(NAME(m_sh6840_timer[2].cr));
163 save_item(NAME(m_sh6840_timer[2].state));
164 save_item(NAME(m_sh6840_timer[2].leftovers));
165 save_item(NAME(m_sh6840_timer[2].timer));
166 save_item(NAME(m_sh6840_timer[2].clocks));
167 save_item(NAME(m_sh6840_timer[2].counter.w));
168 }
169
170 /*************************************
171 *
172 * Audio startup routines
173 *
174 *************************************/
175
common_sh_start()176 void exidy_sound_device::common_sh_start()
177 {
178 int sample_rate = SH8253_CLOCK.value();
179
180 m_sh6840_clocks_per_sample = (int)(SH6840_CLOCK.dvalue() / (double)sample_rate * (double)(1 << 24));
181
182 /* allocate the stream */
183 m_stream = stream_alloc(0, 1, sample_rate);
184
185 sh6840_register_state_globals();
186 }
187
188 DEFINE_DEVICE_TYPE(EXIDY, exidy_sound_device, "exidy_sfx", "Exidy SFX")
189
exidy_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)190 exidy_sound_device::exidy_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
191 : exidy_sound_device(mconfig, EXIDY, tag, owner, clock)
192 {
193 }
194
exidy_sh8253_sound_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)195 exidy_sh8253_sound_device::exidy_sh8253_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
196 : exidy_sound_device(mconfig, type, tag, owner, clock),
197 m_riot(*this, "riot"),
198 m_cvsd(*this, "cvsd"),
199 m_cvsd_filter(*this, "cvsd_filter"),
200 m_cvsd_filter2(*this, "cvsd_filter2"),
201 m_cvsdcpu(*this, "cvsdcpu"),
202 m_tms(*this, "tms"),
203 m_pia(*this, "pia")
204 {
205 }
206
exidy_sound_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)207 exidy_sound_device::exidy_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
208 : device_t(mconfig, type, tag, owner, clock),
209 device_sound_interface(mconfig, *this),
210 m_stream(nullptr),
211 m_freq_to_step(0),
212 m_sh6840_MSB_latch(0),
213 m_sh6840_LSB_latch(0),
214 m_sh6840_LFSR_oldxor(0),
215 m_sh6840_LFSR_0(0xffffffff),
216 m_sh6840_LFSR_1(0xffffffff),
217 m_sh6840_LFSR_2(0xffffffff),
218 m_sh6840_LFSR_3(0xffffffff),
219 m_sh6840_clocks_per_sample(0),
220 m_sh6840_clock_count(0),
221 m_sfxctrl(0)
222 {
223 }
224
225 //-------------------------------------------------
226 // device_start - device-specific startup
227 //-------------------------------------------------
228
device_start()229 void exidy_sound_device::device_start()
230 {
231 common_sh_start();
232 }
233
234 //-------------------------------------------------
235 // device_reset - device-specific reset
236 //-------------------------------------------------
237
device_reset()238 void exidy_sound_device::device_reset()
239 {
240 common_sh_reset();
241 }
242
243 //-------------------------------------------------
244 // sound_stream_update - handle a stream update
245 //-------------------------------------------------
246
sound_stream_update(sound_stream & stream,std::vector<read_stream_view> const & inputs,std::vector<write_stream_view> & outputs)247 void exidy_sound_device::sound_stream_update(sound_stream &stream, std::vector<read_stream_view> const &inputs, std::vector<write_stream_view> &outputs)
248 {
249 sh6840_timer_channel *sh6840_timer = m_sh6840_timer;
250
251 /* hack to skip the expensive lfsr noise generation unless at least one of the 3 channels actually depends on it being generated */
252 int noisy = ((sh6840_timer[0].cr & sh6840_timer[1].cr & sh6840_timer[2].cr & 0x02) == 0);
253 auto &buffer = outputs[0];
254
255 /* loop over samples */
256 for (int sampindex = 0; sampindex < buffer.samples(); sampindex++)
257 {
258 sh6840_timer_channel *t;
259 int clocks;
260 s32 sample = 0;
261
262 /* determine how many 6840 clocks this sample */
263 m_sh6840_clock_count += m_sh6840_clocks_per_sample;
264 int clocks_this_sample = m_sh6840_clock_count >> 24;
265 m_sh6840_clock_count &= (1 << 24) - 1;
266
267 /* skip if nothing enabled */
268 if ((sh6840_timer[0].cr & 0x01) == 0)
269 {
270 int noise_clocks_this_sample = 0;
271 uint32_t chan0_clocks;
272
273 /* generate E-clocked noise if configured to do so */
274 if (noisy && !(m_sfxctrl & 0x01))
275 noise_clocks_this_sample = sh6840_update_noise(clocks_this_sample);
276
277 /* handle timer 0 if enabled */
278 t = &sh6840_timer[0];
279 chan0_clocks = t->clocks;
280 clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample;
281 sh6840_apply_clock(t, clocks);
282 if (t->state && !(m_sfxctrl & 0x02) && (t->cr & 0x80))
283 sample += m_sh6840_volume[0];
284
285 /* generate channel 0-clocked noise if configured to do so */
286 if (noisy && (m_sfxctrl & 0x01))
287 noise_clocks_this_sample = sh6840_update_noise(t->clocks - chan0_clocks);
288
289 /* handle timer 1 if enabled */
290 t = &sh6840_timer[1];
291 clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample;
292 sh6840_apply_clock(t, clocks);
293 if (t->state && (t->cr & 0x80))
294 sample += m_sh6840_volume[1];
295
296 /* handle timer 2 if enabled */
297 t = &sh6840_timer[2];
298 clocks = (t->cr & 0x02) ? clocks_this_sample : noise_clocks_this_sample;
299 /* prescale */
300 if (t->cr & 0x01)
301 {
302 clocks += t->leftovers;
303 t->leftovers = clocks % 8;
304 clocks /= 8;
305 }
306 sh6840_apply_clock(t, clocks);
307 if (t->state && (t->cr & 0x80))
308 sample += m_sh6840_volume[2];
309 }
310
311 /* music (if present) */
312 sample += generate_music_sample();
313
314 /* stash */
315 buffer.put_int(sampindex, sample, 32768);
316 }
317 }
318
generate_music_sample()319 s32 exidy_sound_device::generate_music_sample()
320 {
321 return 0;
322 }
323
generate_music_sample()324 s32 exidy_sh8253_sound_device::generate_music_sample()
325 {
326 sh8253_timer_channel *c;
327 s32 sample = 0;
328
329 /* music channel 0 */
330 c = &m_sh8253_timer[0];
331 if (c->enable)
332 {
333 c->fraction += c->step;
334 if (c->fraction & 0x0800000)
335 sample += BASE_VOLUME;
336 }
337
338 /* music channel 1 */
339 c = &m_sh8253_timer[1];
340 if (c->enable)
341 {
342 c->fraction += c->step;
343 if (c->fraction & 0x0800000)
344 sample += BASE_VOLUME;
345 }
346
347 /* music channel 2 */
348 c = &m_sh8253_timer[2];
349 if (c->enable)
350 {
351 c->fraction += c->step;
352 if (c->fraction & 0x0800000)
353 sample += BASE_VOLUME;
354 }
355
356 return sample;
357 }
358
359
360
361 /*************************************
362 *
363 * Audio reset routines
364 *
365 *************************************/
366
common_sh_reset()367 void exidy_sound_device::common_sh_reset()
368 {
369 /* 6840 */
370 memset(m_sh6840_timer, 0, sizeof(m_sh6840_timer));
371 m_sh6840_MSB_latch = 0;
372 m_sh6840_LSB_latch = 0;
373 m_sh6840_volume[0] = 0;
374 m_sh6840_volume[1] = 0;
375 m_sh6840_volume[2] = 0;
376 m_sh6840_clock_count = 0;
377 m_sfxctrl = 0;
378
379 /* LFSR */
380 m_sh6840_LFSR_oldxor = 0;
381 m_sh6840_LFSR_0 = 0xffffffff;
382 m_sh6840_LFSR_1 = 0xffffffff;
383 m_sh6840_LFSR_2 = 0xffffffff;
384 m_sh6840_LFSR_3 = 0xffffffff;
385 }
386
387
388 /*************************************
389 *
390 * 6532 interface
391 *
392 *************************************/
393
r6532_porta_w(uint8_t data)394 void exidy_sh8253_sound_device::r6532_porta_w(uint8_t data)
395 {
396 if (m_cvsd.found())
397 m_cvsdcpu->set_input_line(INPUT_LINE_RESET, (data & 0x10) ? CLEAR_LINE : ASSERT_LINE);
398
399 if (m_tms.found())
400 {
401 logerror("(%f)%s:TMS5220 data write = %02X\n", machine().time().as_double(), machine().describe_context(), m_riot->porta_out_get());
402 m_tms->data_w(data);
403 }
404 }
405
r6532_porta_r()406 uint8_t exidy_sh8253_sound_device::r6532_porta_r()
407 {
408 uint8_t status = 0xff;
409 if (m_tms.found())
410 {
411 status = m_tms->status_r();
412 logerror("(%f)%s:TMS5220 status read = %02X\n", machine().time().as_double(), machine().describe_context(), status);
413 }
414 return status;
415 }
416
r6532_portb_w(uint8_t data)417 void exidy_sh8253_sound_device::r6532_portb_w(uint8_t data)
418 {
419 if (m_tms.found())
420 {
421 m_tms->rsq_w(BIT(data, 0));
422 m_tms->wsq_w(BIT(data, 1));
423 }
424 }
425
426
r6532_portb_r()427 uint8_t exidy_sh8253_sound_device::r6532_portb_r()
428 {
429 uint8_t newdata = m_riot->portb_in_get();
430 if (m_tms.found())
431 {
432 newdata &= ~0x0c;
433 if (m_tms->readyq_r()) newdata |= 0x04;
434 if (m_tms->intq_r()) newdata |= 0x08;
435 }
436 return newdata;
437 }
438
439
440 /*************************************
441 *
442 * 8253 state saving
443 *
444 *************************************/
445
446
sh8253_register_state_globals()447 void exidy_sh8253_sound_device::sh8253_register_state_globals()
448 {
449 save_item(NAME(m_sh8253_timer[0].clstate));
450 save_item(NAME(m_sh8253_timer[0].enable));
451 save_item(NAME(m_sh8253_timer[0].count));
452 save_item(NAME(m_sh8253_timer[0].step));
453 save_item(NAME(m_sh8253_timer[0].fraction));
454 save_item(NAME(m_sh8253_timer[1].clstate));
455 save_item(NAME(m_sh8253_timer[1].enable));
456 save_item(NAME(m_sh8253_timer[1].count));
457 save_item(NAME(m_sh8253_timer[1].step));
458 save_item(NAME(m_sh8253_timer[1].fraction));
459 save_item(NAME(m_sh8253_timer[2].clstate));
460 save_item(NAME(m_sh8253_timer[2].enable));
461 save_item(NAME(m_sh8253_timer[2].count));
462 save_item(NAME(m_sh8253_timer[2].step));
463 save_item(NAME(m_sh8253_timer[2].fraction));
464 }
465
466 /*************************************
467 *
468 * 8253 timer handlers
469 *
470 *************************************/
471
sh8253_w(offs_t offset,uint8_t data)472 void exidy_sh8253_sound_device::sh8253_w(offs_t offset, uint8_t data)
473 {
474 int chan;
475
476 m_stream->update();
477
478 switch (offset)
479 {
480 case 0:
481 case 1:
482 case 2:
483 chan = offset;
484 if (!m_sh8253_timer[chan].clstate)
485 {
486 m_sh8253_timer[chan].clstate = 1;
487 m_sh8253_timer[chan].count = (m_sh8253_timer[chan].count & 0xff00) | (data & 0x00ff);
488 }
489 else
490 {
491 m_sh8253_timer[chan].clstate = 0;
492 m_sh8253_timer[chan].count = (m_sh8253_timer[chan].count & 0x00ff) | ((data << 8) & 0xff00);
493 if (m_sh8253_timer[chan].count)
494 m_sh8253_timer[chan].step = m_freq_to_step * SH8253_CLOCK.dvalue() / m_sh8253_timer[chan].count;
495 else
496 m_sh8253_timer[chan].step = 0;
497 }
498 break;
499
500 case 3:
501 chan = (data & 0xc0) >> 6;
502 m_sh8253_timer[chan].enable = ((data & 0x0e) != 0);
503 break;
504 }
505 }
506
507
508
509 /*************************************
510 *
511 * 6840 timer handlers
512 *
513 *************************************/
514
sh6840_r(offs_t offset)515 uint8_t exidy_sound_device::sh6840_r(offs_t offset)
516 {
517 /* force an update of the stream */
518 m_stream->update();
519
520 switch (offset)
521 {
522 /* offset 0: Motorola datasheet says it isn't used, Hitachi datasheet says it reads as 0s always*/
523 case 0:
524 return 0;
525 /* offset 1 reads the status register: bits 2 1 0 correspond to ints on channels 2,1,0, and bit 7 is an 'OR' of bits 2,1,0 */
526 case 1:
527 logerror("%s:exidy_sh6840_r - unexpected read, status register is TODO!\n", machine().describe_context());
528 return 0;
529 /* offsets 2,4,6 read channel 0,1,2 MSBs and latch the LSB*/
530 case 2: case 4: case 6:
531 m_sh6840_LSB_latch = m_sh6840_timer[((offset>>1)-1)].counter.b.l;
532 return m_sh6840_timer[((offset>>1)-1)].counter.b.h;
533 /* offsets 3,5,7 read the LSB latch*/
534 default: /* case 3,5,7 */
535 return m_sh6840_LSB_latch;
536 }
537 }
538
539
sh6840_w(offs_t offset,uint8_t data)540 void exidy_sound_device::sh6840_w(offs_t offset, uint8_t data)
541 {
542 sh6840_timer_channel *sh6840_timer = m_sh6840_timer;
543
544 /* force an update of the stream */
545 m_stream->update();
546
547 switch (offset)
548 {
549 /* offset 0 writes to either channel 0 control or channel 2 control */
550 case 0:
551 if (sh6840_timer[1].cr & 0x01)
552 sh6840_timer[0].cr = data;
553 else
554 sh6840_timer[2].cr = data;
555
556 /* only support mode 0 and 2 */
557 if (((data >> 3) & 5) != 0)
558 fatalerror("exidy_sh6840_w - channel %d configured for mode %d\n", (sh6840_timer[1].cr & 0x01) ? 0 : 2, (data >> 3) & 7);
559 break;
560
561 /* offset 1 writes to channel 1 control */
562 case 1:
563 sh6840_timer[1].cr = data;
564
565 /* only support mode 0 and 2 */
566 if (((data >> 3) & 5) != 0)
567 fatalerror("exidy_sh6840_w - channel 1 configured for mode %d\n", (data >> 3) & 7);
568 break;
569
570 /* offsets 2/4/6 write to the common MSB latch */
571 case 2:
572 case 4:
573 case 6:
574 m_sh6840_MSB_latch = data;
575 break;
576
577 /* offsets 3/5/7 write to the LSB controls */
578 case 3:
579 case 5:
580 case 7:
581 {
582 /* latch the timer value */
583 int ch = (offset - 3) / 2;
584 sh6840_timer[ch].timer = (m_sh6840_MSB_latch << 8) | (data & 0xff);
585
586 /* if CR4 is clear, the value is loaded immediately */
587 if (!(sh6840_timer[ch].cr & 0x10))
588 sh6840_timer[ch].counter.w = sh6840_timer[ch].timer;
589 break;
590 }
591 }
592 }
593
594
595
596 /*************************************
597 *
598 * External sound effect controls
599 *
600 *************************************/
601
sfxctrl_w(offs_t offset,uint8_t data)602 void exidy_sound_device::sfxctrl_w(offs_t offset, uint8_t data)
603 {
604 m_stream->update();
605
606 switch (offset)
607 {
608 case 0:
609 m_sfxctrl = data;
610 break;
611
612 case 1:
613 case 2:
614 case 3:
615 m_sh6840_volume[offset - 1] = ((data & 7) * BASE_VOLUME) / 7;
616 break;
617 }
618 }
619
620
621
622 /*************************************
623 *
624 * Sound filter control
625 *
626 *************************************/
627
filter_w(uint8_t data)628 void venture_sound_device::filter_w(uint8_t data)
629 {
630 logerror("exidy_sound_filter_w = %02X\n", data);
631 }
632
633
634
635 /*************************************
636 *
637 * Venture, etc.
638 *
639 *************************************/
640
641
642 DEFINE_DEVICE_TYPE(EXIDY_VENTURE, venture_sound_device, "venture_sound", "Exidy SFX+PSG")
643
venture_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)644 venture_sound_device::venture_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
645 : venture_sound_device(mconfig, EXIDY_VENTURE, tag, owner, clock)
646 {
647 }
648
venture_sound_device(const machine_config & mconfig,device_type type,const char * tag,device_t * owner,uint32_t clock)649 venture_sound_device::venture_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
650 : exidy_sh8253_sound_device(mconfig, type, tag, owner, clock)
651 , m_pa_callback(*this)
652 , m_pb_callback(*this)
653 , m_ca2_callback(*this)
654 , m_cb2_callback(*this)
655 {
656 }
657
device_resolve_objects()658 void venture_sound_device::device_resolve_objects()
659 {
660 m_pa_callback.resolve_safe();
661 m_pb_callback.resolve_safe();
662 m_ca2_callback.resolve_safe();
663 m_cb2_callback.resolve_safe();
664 }
665
666 //-------------------------------------------------
667 // device_start - device-specific startup
668 //-------------------------------------------------
669
device_start()670 void exidy_sh8253_sound_device::device_start()
671 {
672 common_sh_start();
673
674 /* 8253 */
675 m_freq_to_step = (1 << 24) / SH8253_CLOCK;
676
677 sh8253_register_state_globals();
678 }
679
680 //-------------------------------------------------
681 // device_reset - device-specific reset
682 //-------------------------------------------------
683
device_reset()684 void exidy_sh8253_sound_device::device_reset()
685 {
686 common_sh_reset();
687
688 /* 8253 */
689 memset(m_sh8253_timer, 0, sizeof(m_sh8253_timer));
690 }
691
692
pa_w(uint8_t data)693 void venture_sound_device::pa_w(uint8_t data)
694 {
695 m_pia->porta_w(data);
696 }
697
698
pb_w(uint8_t data)699 void venture_sound_device::pb_w(uint8_t data)
700 {
701 m_pia->portb_w(data);
702 }
703
704
WRITE_LINE_MEMBER(venture_sound_device::ca_w)705 WRITE_LINE_MEMBER(venture_sound_device::ca_w)
706 {
707 m_pia->ca1_w(state);
708 }
709
710
WRITE_LINE_MEMBER(venture_sound_device::cb_w)711 WRITE_LINE_MEMBER(venture_sound_device::cb_w)
712 {
713 m_pia->cb1_w(state);
714 }
715
716
pia_pa_w(uint8_t data)717 void venture_sound_device::pia_pa_w(uint8_t data)
718 {
719 m_pa_callback(data);
720 }
721
722
pia_pb_w(uint8_t data)723 void venture_sound_device::pia_pb_w(uint8_t data)
724 {
725 m_pb_callback(data);
726 }
727
728
WRITE_LINE_MEMBER(venture_sound_device::pia_ca2_w)729 WRITE_LINE_MEMBER(venture_sound_device::pia_ca2_w)
730 {
731 m_ca2_callback(state);
732 }
733
734
WRITE_LINE_MEMBER(venture_sound_device::pia_cb2_w)735 WRITE_LINE_MEMBER(venture_sound_device::pia_cb2_w)
736 {
737 m_cb2_callback(state);
738 }
739
740
venture_audio_map(address_map & map)741 void venture_sound_device::venture_audio_map(address_map &map)
742 {
743 map.global_mask(0x7fff);
744 map(0x0000, 0x007f).mirror(0x0780).ram();
745 map(0x0800, 0x087f).mirror(0x0780).rw("riot", FUNC(riot6532_device::read), FUNC(riot6532_device::write));
746 map(0x1000, 0x1003).mirror(0x07fc).rw("pia", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
747 map(0x1800, 0x1803).mirror(0x07fc).w(FUNC(venture_sound_device::sh8253_w));
748 map(0x2000, 0x27ff).w(FUNC(venture_sound_device::filter_w));
749 map(0x2800, 0x2807).mirror(0x07f8).rw(FUNC(venture_sound_device::sh6840_r), FUNC(venture_sound_device::sh6840_w));
750 map(0x3000, 0x3003).mirror(0x07fc).w(FUNC(venture_sound_device::sfxctrl_w));
751 map(0x5800, 0x7fff).rom();
752 }
753
754
device_add_mconfig(machine_config & config)755 void venture_sound_device::device_add_mconfig(machine_config &config)
756 {
757 m6502_device &audiocpu(M6502(config, "audiocpu", 3579545/4));
758 audiocpu.set_addrmap(AS_PROGRAM, &venture_sound_device::venture_audio_map);
759
760 RIOT6532(config, m_riot, SH6532_CLOCK);
761 m_riot->in_pa_callback().set(FUNC(venture_sound_device::r6532_porta_r));
762 m_riot->out_pa_callback().set(FUNC(venture_sound_device::r6532_porta_w));
763 m_riot->in_pb_callback().set(FUNC(venture_sound_device::r6532_portb_r));
764 m_riot->out_pb_callback().set(FUNC(venture_sound_device::r6532_portb_w));
765 m_riot->irq_callback().set("audioirq", FUNC(input_merger_device::in_w<0>));
766
767 PIA6821(config, m_pia, 0);
768 m_pia->writepa_handler().set(FUNC(venture_sound_device::pia_pa_w));
769 m_pia->writepb_handler().set(FUNC(venture_sound_device::pia_pb_w));
770 m_pia->ca2_handler().set(FUNC(venture_sound_device::pia_ca2_w));
771 m_pia->cb2_handler().set(FUNC(venture_sound_device::pia_cb2_w));
772 m_pia->irqb_handler().set("audioirq", FUNC(input_merger_device::in_w<1>));
773
774 INPUT_MERGER_ANY_HIGH(config, "audioirq").output_handler().set_inputline("audiocpu", m6502_device::IRQ_LINE); // open collector
775
776 SPEAKER(config, "mono").front_center();
777
778 this->add_route(ALL_OUTPUTS, "mono", 0.50);
779 }
780
781
782
783 /*************************************
784 *
785 * CVSD sound for Mouse Trap
786 *
787 *************************************/
788
789 DEFINE_DEVICE_TYPE(EXIDY_MTRAP, mtrap_sound_device, "mtrap_sound", "Exidy SFX+PSG+CVSD")
790
mtrap_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)791 mtrap_sound_device::mtrap_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
792 : venture_sound_device(mconfig, EXIDY_MTRAP, tag, owner, clock)
793 , m_cvsd_timer(*this,"cvsd_timer")
794 , m_cvsd_clk(false)
795 {
796 }
797
798 //-------------------------------------------------
799 // device_start - device-specific startup
800 //-------------------------------------------------
801
device_start()802 void mtrap_sound_device::device_start()
803 {
804 common_sh_start();
805
806 /* 8253 */
807 m_freq_to_step = (1 << 24) / SH8253_CLOCK;
808
809 sh8253_register_state_globals();
810
811 save_item(NAME(m_cvsd_clk));
812 }
813
TIMER_DEVICE_CALLBACK_MEMBER(mtrap_sound_device::cvsd_timer)814 TIMER_DEVICE_CALLBACK_MEMBER(mtrap_sound_device::cvsd_timer)
815 {
816 m_cvsd_clk = !m_cvsd_clk;
817 m_cvsd->clock_w(m_cvsd_clk);
818 }
819
voiceio_w(offs_t offset,uint8_t data)820 void mtrap_sound_device::voiceio_w(offs_t offset, uint8_t data)
821 {
822 if (!(offset & 0x10))
823 m_cvsd->digit_w(data & 1);
824
825 if (!(offset & 0x20))
826 m_riot->portb_in_set(data & 1, 0xff);
827 }
828
829
voiceio_r(offs_t offset)830 uint8_t mtrap_sound_device::voiceio_r(offs_t offset)
831 {
832 uint8_t retval = 0xff; // this should probably be open bus
833 if (!(offset & 0x80))
834 {
835 retval &= 0xf0;
836 uint8_t porta = m_riot->porta_out_get();
837 uint8_t data = (porta & 0x06) >> 1;
838 data |= (porta & 0x01) << 2;
839 data |= (porta & 0x08);
840 retval |= data;
841 }
842
843 if (!(offset & 0x40))
844 {
845 retval &= 0x7f;
846 retval |= (m_cvsd_clk << 7);
847 }
848
849 return retval;
850 }
851
852
cvsd_map(address_map & map)853 void mtrap_sound_device::cvsd_map(address_map &map)
854 {
855 map.global_mask(0x3fff);
856 map(0x0000, 0x3fff).rom().region("cvsdcpu", 0);
857 }
858
859
cvsd_iomap(address_map & map)860 void mtrap_sound_device::cvsd_iomap(address_map &map)
861 {
862 map.global_mask(0xff);
863 map(0x00, 0xff).rw(FUNC(mtrap_sound_device::voiceio_r), FUNC(mtrap_sound_device::voiceio_w));
864 }
865
866
device_add_mconfig(machine_config & config)867 void mtrap_sound_device::device_add_mconfig(machine_config &config)
868 {
869 venture_sound_device::device_add_mconfig(config);
870
871 Z80(config, m_cvsdcpu, CVSD_Z80_CLOCK);
872 m_cvsdcpu->set_addrmap(AS_PROGRAM, &mtrap_sound_device::cvsd_map);
873 m_cvsdcpu->set_addrmap(AS_IO, &mtrap_sound_device::cvsd_iomap);
874
875 TIMER(config, m_cvsd_timer).configure_periodic(FUNC(mtrap_sound_device::cvsd_timer), attotime::from_hz(CVSD_CLOCK*2.0)); // this is a 555 timer with 53% duty cycle, within margin of error of 50% duty cycle; the handler clocks on both clock edges, hence * 2.0
876
877 /* audio hardware */
878 FILTER_BIQUAD(config, m_cvsd_filter2).opamp_mfb_lowpass_setup(RES_K(10), RES_K(3.9), RES_K(18), CAP_N(20), CAP_N(2.2));
879 m_cvsd_filter2->add_route(ALL_OUTPUTS, "mono", 1.0);
880 FILTER_BIQUAD(config, m_cvsd_filter).opamp_mfb_lowpass_setup(RES_K(10), RES_K(3.9), RES_K(18), CAP_N(20), CAP_N(2.2));
881 m_cvsd_filter->add_route(ALL_OUTPUTS, m_cvsd_filter2, 1.0);
882 MC3417(config, m_cvsd, 0).add_route(ALL_OUTPUTS, m_cvsd_filter, 0.3086); // each filter has gain of 1.8 for total gain of 3.24, 0.3086 cancels this out. was 0.8
883
884 }
885
886
887 /*************************************
888 *
889 * Victory
890 *
891 *************************************/
892
893 #define VICTORY_AUDIO_CPU_CLOCK (XTAL(3'579'545) / 4)
894 #define VICTORY_LOG_SOUND 0
895
896
897
response_r()898 uint8_t victory_sound_device::response_r()
899 {
900 uint8_t ret = m_pia->b_output();
901
902 if (!machine().side_effects_disabled())
903 {
904 if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound response read = %02X\n", machine().describe_context(), ret);
905
906 m_pia_cb1 = 0;
907 m_pia->cb1_w(m_pia_cb1);
908 }
909
910 return ret;
911 }
912
913
status_r()914 uint8_t victory_sound_device::status_r()
915 {
916 uint8_t ret = (m_pia_ca1 << 7) | (m_pia_cb1 << 6);
917
918 if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound status read = %02X\n", machine().describe_context(), ret);
919
920 return ret;
921 }
922
923
TIMER_CALLBACK_MEMBER(victory_sound_device::delayed_command_w)924 TIMER_CALLBACK_MEMBER(victory_sound_device::delayed_command_w)
925 {
926 m_pia->porta_w(param);
927 m_pia_ca1 = 0;
928 m_pia->ca1_w(m_pia_ca1);
929 }
930
command_w(uint8_t data)931 void victory_sound_device::command_w(uint8_t data)
932 {
933 if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound command = %02X\n", machine().describe_context(), data);
934
935 machine().scheduler().synchronize(timer_expired_delegate(FUNC(victory_sound_device::delayed_command_w), this), data);
936 }
937
938
WRITE_LINE_MEMBER(victory_sound_device::irq_clear_w)939 WRITE_LINE_MEMBER(victory_sound_device::irq_clear_w)
940 {
941 if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound IRQ clear = %02X\n", machine().describe_context(), state);
942
943 if (!state)
944 {
945 m_pia_ca1 = 1;
946 m_pia->ca1_w(m_pia_ca1);
947 }
948 }
949
950
WRITE_LINE_MEMBER(victory_sound_device::main_ack_w)951 WRITE_LINE_MEMBER(victory_sound_device::main_ack_w)
952 {
953 if (VICTORY_LOG_SOUND) logerror("%s:!!!! Sound Main ACK W = %02X\n", machine().describe_context(), state);
954
955 if (m_victory_sound_response_ack_clk && !state)
956 {
957 m_pia_cb1 = 1;
958 m_pia->cb1_w(m_pia_cb1);
959 }
960
961 m_victory_sound_response_ack_clk = state;
962 }
963
964
965 DEFINE_DEVICE_TYPE(EXIDY_VICTORY, victory_sound_device, "victory_sound", "Exidy SFX+PSG+Speech")
966
victory_sound_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)967 victory_sound_device::victory_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
968 : exidy_sh8253_sound_device(mconfig, EXIDY_VICTORY, tag, owner, clock)
969 , m_victory_sound_response_ack_clk(0)
970 {
971 }
972
973 //-------------------------------------------------
974 // device_start - device-specific startup
975 //-------------------------------------------------
976
device_start()977 void victory_sound_device::device_start()
978 {
979 save_item(NAME(m_victory_sound_response_ack_clk));
980 save_item(NAME(m_pia_ca1));
981 save_item(NAME(m_pia_cb1));
982
983 exidy_sh8253_sound_device::device_start();
984 }
985
986 //-------------------------------------------------
987 // device_reset - device-specific reset
988 //-------------------------------------------------
989
device_reset()990 void victory_sound_device::device_reset()
991 {
992 exidy_sh8253_sound_device::device_reset();
993
994 /* the flip-flop @ F4 is reset */
995 m_victory_sound_response_ack_clk = 0;
996 m_pia_cb1 = 1;
997 m_pia->cb1_w(m_pia_cb1);
998
999 /* these two lines shouldn't be needed, but it avoids the log entry
1000 as the sound CPU checks port A before the main CPU ever writes to it */
1001 m_pia->porta_w(0);
1002 m_pia_ca1 = 1;
1003 m_pia->ca1_w(m_pia_ca1);
1004 }
1005
1006
victory_audio_map(address_map & map)1007 void victory_sound_device::victory_audio_map(address_map &map)
1008 {
1009 map(0x0000, 0x00ff).mirror(0x0f00).ram();
1010 map(0x1000, 0x107f).mirror(0x0f80).rw("riot", FUNC(riot6532_device::read), FUNC(riot6532_device::write));
1011 map(0x2000, 0x2003).mirror(0x0ffc).rw("pia", FUNC(pia6821_device::read), FUNC(pia6821_device::write));
1012 map(0x3000, 0x3003).mirror(0x0ffc).w(FUNC(victory_sound_device::sh8253_w));
1013 map(0x4000, 0x4fff).noprw();
1014 map(0x5000, 0x5007).mirror(0x0ff8).rw(FUNC(victory_sound_device::sh6840_r), FUNC(victory_sound_device::sh6840_w));
1015 map(0x6000, 0x6003).mirror(0x0ffc).w(FUNC(victory_sound_device::sfxctrl_w));
1016 map(0x7000, 0xafff).noprw();
1017 map(0xb000, 0xffff).rom();
1018 }
1019
1020
device_add_mconfig(machine_config & config)1021 void victory_sound_device::device_add_mconfig(machine_config &config)
1022 {
1023 m6502_device &audiocpu(M6502(config, "audiocpu", VICTORY_AUDIO_CPU_CLOCK));
1024 audiocpu.set_addrmap(AS_PROGRAM, &victory_sound_device::victory_audio_map);
1025
1026 RIOT6532(config, m_riot, SH6532_CLOCK);
1027 m_riot->in_pa_callback().set(FUNC(victory_sound_device::r6532_porta_r));
1028 m_riot->out_pa_callback().set(FUNC(victory_sound_device::r6532_porta_w));
1029 m_riot->in_pb_callback().set(FUNC(victory_sound_device::r6532_portb_r));
1030 m_riot->out_pb_callback().set(FUNC(victory_sound_device::r6532_portb_w));
1031 m_riot->irq_callback().set("audioirq", FUNC(input_merger_device::in_w<0>));
1032
1033 PIA6821(config, m_pia, 0);
1034 m_pia->ca2_handler().set(FUNC(victory_sound_device::irq_clear_w));
1035 m_pia->cb2_handler().set(FUNC(victory_sound_device::main_ack_w));
1036 m_pia->irqb_handler().set("audioirq", FUNC(input_merger_device::in_w<1>));
1037
1038 INPUT_MERGER_ANY_HIGH(config, "audioirq").output_handler().set_inputline("audiocpu", m6502_device::IRQ_LINE); // open collector
1039
1040 SPEAKER(config, "mono").front_center();
1041
1042 this->add_route(ALL_OUTPUTS, "mono", 1.0);
1043
1044 TMS5220(config, m_tms, 640000).add_route(ALL_OUTPUTS, "mono", 1.0);
1045 }
1046