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