1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4
5 midway.cpp
6
7 Functions to emulate general the various Midway sound cards.
8
9 TODO: the "Turbo Cheap Squeak" and "Sounds Good" boards are nearly identical
10 in function, but use different CPUs and Memory maps (The PIA hookup, DAC
11 and Filter are all exactly the same), so these should probably be combined
12 into one base class with two subclass device implementations to reduce
13 duplicated code.
14 The "Cheap Squeak Deluxe" board in /audio/csd.cpp is also almost identical
15 to the "Sounds Good" board, and should also be a member of any future
16 combined class/device implementation.
17
18 ***************************************************************************/
19
20 #include "emu.h"
21 #include "includes/mcr.h"
22 #include "audio/midway.h"
23 #include "audio/williams.h"
24
25 #include <algorithm>
26
27
28 //**************************************************************************
29 // GLOBAL VARIABLES
30 //**************************************************************************
31
32 DEFINE_DEVICE_TYPE(MIDWAY_SSIO, midway_ssio_device, "midssio", "Midway SSIO Sound Board")
33 DEFINE_DEVICE_TYPE(MIDWAY_SOUNDS_GOOD, midway_sounds_good_device, "midsg", "Midway Sounds Good Sound Board")
34 DEFINE_DEVICE_TYPE(MIDWAY_TURBO_CHEAP_SQUEAK, midway_turbo_cheap_squeak_device, "midtcs", "Midway Turbo Cheap Squeak Sound Board")
35
36
37 //**************************************************************************
38 // SUPER SOUND I/O BOARD (SSIO)
39 //**************************************************************************
40
41 //-------------------------------------------------
42 // midway_ssio_device - constructor
43 //-------------------------------------------------
44
midway_ssio_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)45 midway_ssio_device::midway_ssio_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
46 : device_t(mconfig, MIDWAY_SSIO, tag, owner, clock)
47 , device_mixer_interface(mconfig, *this, 2)
48 , m_cpu(*this, "cpu")
49 , m_ay0(*this, "ay0")
50 , m_ay1(*this, "ay1")
51 , m_ports(*this, "IP%u", 0U)
52 , m_status(0)
53 , m_14024_count(0)
54 , m_mute(0)
55 , m_custom_input_mask{ 0U, 0U, 0U, 0U, 0U }
56 , m_custom_input(*this)
57 , m_custom_output_mask{ 0U, 0U }
58 , m_custom_output(*this)
59 {
60 std::fill(std::begin(m_data), std::end(m_data), 0);
61 std::fill(std::begin(m_overall), std::end(m_overall), 0);
62 for (auto &duty_cycle : m_duty_cycle)
63 std::fill(std::begin(duty_cycle), std::end(duty_cycle), 0);
64 std::fill(std::begin(m_ayvolume_lookup), std::end(m_ayvolume_lookup), 0);
65 }
66
67
68 //-------------------------------------------------
69 // suspend_cpu
70 //-------------------------------------------------
71
suspend_cpu()72 void midway_ssio_device::suspend_cpu()
73 {
74 m_cpu->suspend(SUSPEND_REASON_DISABLE, 1);
75 }
76
77 //-------------------------------------------------
78 // read - return the status value
79 //-------------------------------------------------
80
read()81 uint8_t midway_ssio_device::read()
82 {
83 return m_status;
84 }
85
86
87 //-------------------------------------------------
88 // write - handle an external write to one of the
89 // input latches
90 //-------------------------------------------------
91
write(offs_t offset,uint8_t data)92 void midway_ssio_device::write(offs_t offset, uint8_t data)
93 {
94 synchronize(0, (offset << 8) | (data & 0xff));
95 }
96
97
98 //-------------------------------------------------
99 // reset_write - write to the reset line
100 //-------------------------------------------------
101
WRITE_LINE_MEMBER(midway_ssio_device::reset_write)102 WRITE_LINE_MEMBER(midway_ssio_device::reset_write)
103 {
104 if (state)
105 {
106 // going high halts the CPU
107 device_reset();
108 m_cpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
109 }
110 else
111 {
112 // going low resets and reactivates the CPU
113 m_cpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
114 }
115 }
116
117
118 //-------------------------------------------------
119 // ioport_read - read from one of the I/O ports
120 // on the device
121 //-------------------------------------------------
122
ioport_read(offs_t offset)123 uint8_t midway_ssio_device::ioport_read(offs_t offset)
124 {
125 uint8_t result = m_ports[offset].read_safe(0xff);
126 if (!m_custom_input[offset].isnull())
127 result = (result & ~m_custom_input_mask[offset]) |
128 (m_custom_input[offset]() & m_custom_input_mask[offset]);
129 return result;
130 }
131
132
133 //-------------------------------------------------
134 // ioport_write - write to one of the I/O ports
135 // on the device
136 //-------------------------------------------------
137
ioport_write(offs_t offset,uint8_t data)138 void midway_ssio_device::ioport_write(offs_t offset, uint8_t data)
139 {
140 int which = offset >> 2;
141 if (!m_custom_output[which].isnull())
142 m_custom_output[which](data & m_custom_output_mask[which]);
143 }
144
145
146 //-------------------------------------------------
147 // compute_ay8910_modulation - precompute
148 // volume modulation tables based on the duty
149 // cycle described by the PROMs
150 //-------------------------------------------------
151
compute_ay8910_modulation()152 void midway_ssio_device::compute_ay8910_modulation()
153 {
154 //
155 // AY-8910 modulation:
156 //
157 // Starts with a 16MHz oscillator
158 // /2 via 7474 flip-flip @ F11
159 //
160 // This signal clocks the binary counter @ E11 which
161 // cascades into the decade counter @ D11. This combo
162 // effectively counts from 0-159 and then wraps. The
163 // value from these counters is input to an 82S123 PROM,
164 // which appears to be standard on all games.
165 //
166 // One bit at a time from this PROM is clocked at a time
167 // and the resulting inverted signal becomes a clock for
168 // the down counters at F3, F4, F5, F8, F9, and F10. The
169 // value in these down counters are reloaded after the 160
170 // counts from the binary/decade counter combination.
171 //
172 // When these down counters are loaded, the TC signal is
173 // clear, which mutes the voice. When the down counters
174 // cross through 0, the TC signal goes high and the 4016
175 // multiplexers allow the AY-8910 voice to go through.
176 // Thus, writing a 0 to the counters will enable the
177 // voice for the longest period of time, while writing
178 // a 15 enables it for the shortest period of time.
179 // This creates an effective duty cycle for the voice.
180 //
181 // Given that the down counters are reset 50000 times per
182 // second (SSIO_CLOCK/2/160), which is above the typical
183 // frequency of sound output. So we simply apply a volume
184 // adjustment to each voice according to the duty cycle.
185 //
186
187 // loop over all possible values of the duty cycle
188 uint8_t *prom = memregion("proms")->base();
189 for (int volval = 0; volval < 16; volval++)
190 {
191 // loop over all the clocks until we run out; look up in the PROM
192 // to find out when the next clock should fire
193 int remaining_clocks = volval;
194 int cur = 0, prev = 1;
195 int curclock;
196 for (curclock = 0; curclock < 160 && remaining_clocks; curclock++)
197 {
198 cur = prom[curclock / 8] & (0x80 >> (curclock % 8));
199
200 // check for a high -> low transition
201 if (cur == 0 && prev != 0)
202 remaining_clocks--;
203
204 prev = cur;
205 }
206
207 // treat the duty cycle as a volume
208 m_ayvolume_lookup[15 - volval] = curclock * 100 / 160;
209 }
210 }
211
212
213 //-------------------------------------------------
214 // clock_14024 - periodic timer to clock the
215 // 7-bit async counter at C12
216 //-------------------------------------------------
217
INTERRUPT_GEN_MEMBER(midway_ssio_device::clock_14024)218 INTERRUPT_GEN_MEMBER(midway_ssio_device::clock_14024)
219 {
220 //
221 // /SINT is generated as follows:
222 //
223 // Starts with a 16MHz oscillator
224 // /2 via 7474 flip-flop @ F11
225 // /16 via 74161 binary counter @ E11
226 // /10 via 74190 decade counter @ D11
227 //
228 // Bit 3 of the decade counter clocks a 14024 7-bit async counter @ C12.
229 // This routine is called to clock this 7-bit counter.
230 // Bit 6 of the output is inverted and connected to /SINT.
231 //
232 m_14024_count = (m_14024_count + 1) & 0x7f;
233
234 // if the low 5 bits clocked to 0, bit 6 has changed state
235 if ((m_14024_count & 0x3f) == 0)
236 m_cpu->set_input_line(0, (m_14024_count & 0x40) ? ASSERT_LINE : CLEAR_LINE);
237 }
238
239
240 //-------------------------------------------------
241 // irq_clear - reset the IRQ state and 14024 count
242 //-------------------------------------------------
243
irq_clear()244 uint8_t midway_ssio_device::irq_clear()
245 {
246 // a read here asynchronously resets the 14024 count, clearing /SINT
247 if (!machine().side_effects_disabled())
248 {
249 m_14024_count = 0;
250 m_cpu->set_input_line(0, CLEAR_LINE);
251 }
252 return 0xff;
253 }
254
255
256 //-------------------------------------------------
257 // status_w - set the outgoing status value
258 //-------------------------------------------------
259
status_w(uint8_t data)260 void midway_ssio_device::status_w(uint8_t data)
261 {
262 m_status = data;
263 }
264
265
266 //-------------------------------------------------
267 // data_r - read incoming data latches
268 //-------------------------------------------------
269
data_r(offs_t offset)270 uint8_t midway_ssio_device::data_r(offs_t offset)
271 {
272 return m_data[offset];
273 }
274
275
276 //-------------------------------------------------
277 // porta0_w - handle writes to AY-8910 #0 port A
278 //-------------------------------------------------
279
porta0_w(uint8_t data)280 void midway_ssio_device::porta0_w(uint8_t data)
281 {
282 m_duty_cycle[0][0] = data & 15;
283 m_duty_cycle[0][1] = data >> 4;
284 update_volumes();
285 }
286
287
288 //-------------------------------------------------
289 // portb0_w - handle writes to AY-8910 #0 port B
290 //-------------------------------------------------
291
portb0_w(uint8_t data)292 void midway_ssio_device::portb0_w(uint8_t data)
293 {
294 m_duty_cycle[0][2] = data & 15;
295 m_overall[0] = (data >> 4) & 7;
296 update_volumes();
297 }
298
299
300 //-------------------------------------------------
301 // porta1_w - handle writes to AY-8910 #1 port A
302 //-------------------------------------------------
303
porta1_w(uint8_t data)304 void midway_ssio_device::porta1_w(uint8_t data)
305 {
306 m_duty_cycle[1][0] = data & 15;
307 m_duty_cycle[1][1] = data >> 4;
308 update_volumes();
309 }
310
311
312 //-------------------------------------------------
313 // portb1_w - handle writes to AY-8910 #1 port B
314 //-------------------------------------------------
315
portb1_w(uint8_t data)316 void midway_ssio_device::portb1_w(uint8_t data)
317 {
318 m_duty_cycle[1][2] = data & 15;
319 m_overall[1] = (data >> 4) & 7;
320 m_mute = data & 0x80;
321 update_volumes();
322 }
323
324
325 //-------------------------------------------------
326 // update_volumes - update the volumes of each
327 // AY-8910 channel based on modulation and mute
328 //-------------------------------------------------
329
update_volumes()330 void midway_ssio_device::update_volumes()
331 {
332 m_ay0->set_volume(0, m_mute ? 0 : m_ayvolume_lookup[m_duty_cycle[0][0]]);
333 m_ay0->set_volume(1, m_mute ? 0 : m_ayvolume_lookup[m_duty_cycle[0][1]]);
334 m_ay0->set_volume(2, m_mute ? 0 : m_ayvolume_lookup[m_duty_cycle[0][2]]);
335 m_ay1->set_volume(0, m_mute ? 0 : m_ayvolume_lookup[m_duty_cycle[1][0]]);
336 m_ay1->set_volume(1, m_mute ? 0 : m_ayvolume_lookup[m_duty_cycle[1][1]]);
337 m_ay1->set_volume(2, m_mute ? 0 : m_ayvolume_lookup[m_duty_cycle[1][2]]);
338 }
339
340 //-------------------------------------------------
341 // audio CPU map
342 //-------------------------------------------------
343
344 // address map verified from schematics
ssio_map(address_map & map)345 void midway_ssio_device::ssio_map(address_map &map)
346 {
347 map.unmap_value_high();
348 map(0x0000, 0x3fff).rom();
349 map(0x8000, 0x83ff).mirror(0x0c00).ram();
350 map(0x9000, 0x9003).mirror(0x0ffc).r(FUNC(midway_ssio_device::data_r));
351 map(0xa000, 0xa000).mirror(0x0ffc).w("ay0", FUNC(ay8910_device::address_w));
352 map(0xa001, 0xa001).mirror(0x0ffc).r("ay0", FUNC(ay8910_device::data_r));
353 map(0xa002, 0xa002).mirror(0x0ffc).w("ay0", FUNC(ay8910_device::data_w));
354 map(0xb000, 0xb000).mirror(0x0ffc).w("ay1", FUNC(ay8910_device::address_w));
355 map(0xb001, 0xb001).mirror(0x0ffc).r("ay1", FUNC(ay8910_device::data_r));
356 map(0xb002, 0xb002).mirror(0x0ffc).w("ay1", FUNC(ay8910_device::data_w));
357 map(0xc000, 0xcfff).nopr().w(FUNC(midway_ssio_device::status_w));
358 map(0xd000, 0xdfff).nopw(); // low bit controls yellow LED
359 map(0xe000, 0xefff).r(FUNC(midway_ssio_device::irq_clear));
360 map(0xf000, 0xffff).portr("DIP"); // 6 DIP switches
361 }
362
363
364 //-------------------------------------------------
365 // default ports map
366 //-------------------------------------------------
367
ssio_input_ports(address_map & map,const char * ssio)368 void midway_ssio_device::ssio_input_ports(address_map &map, const char *ssio)
369 {
370 map(0x00, 0x04).mirror(0x18).r(ssio, FUNC(midway_ssio_device::ioport_read));
371 map(0x07, 0x07).mirror(0x18).r(ssio, FUNC(midway_ssio_device::read));
372 map(0x00, 0x07).w(ssio, FUNC(midway_ssio_device::ioport_write));
373 map(0x1c, 0x1f).w(ssio, FUNC(midway_ssio_device::write));
374 }
375
376
377 //-------------------------------------------------
378 // ROM definitions
379 //-------------------------------------------------
380
381 ROM_START( midway_ssio )
382 ROM_REGION( 0x0020, "proms", 0 )
CRC(e1281ee9)383 ROM_LOAD( "82s123.12d", 0x0000, 0x0020, CRC(e1281ee9) SHA1(9ac9b01d24affc0ee9227a4364c4fd8f8290343a) ) /* from shollow, assuming it's the same */
384 ROM_END
385
386
387 //-------------------------------------------------
388 // device_rom_region - return a pointer to the
389 // the device's ROM definitions
390 //-------------------------------------------------
391
392 const tiny_rom_entry *midway_ssio_device::device_rom_region() const
393 {
394 return ROM_NAME(midway_ssio);
395 }
396
397
398 //-------------------------------------------------
399 // device_add_mconfig - add device configuration
400 //-------------------------------------------------
401
device_add_mconfig(machine_config & config)402 void midway_ssio_device::device_add_mconfig(machine_config &config)
403 {
404 Z80(config, m_cpu, DERIVED_CLOCK(1, 2*4));
405 m_cpu->set_addrmap(AS_PROGRAM, &midway_ssio_device::ssio_map);
406 if (clock())
407 m_cpu->set_periodic_int(DEVICE_SELF, FUNC(midway_ssio_device::clock_14024), attotime::from_hz(clock() / (2*16*10)));
408
409 AY8910(config, m_ay0, DERIVED_CLOCK(1, 2*4));
410 m_ay0->port_a_write_callback().set(FUNC(midway_ssio_device::porta0_w));
411 m_ay0->port_b_write_callback().set(FUNC(midway_ssio_device::portb0_w));
412 m_ay0->add_route(ALL_OUTPUTS, *this, 0.33, AUTO_ALLOC_INPUT, 0);
413
414 AY8910(config, m_ay1, DERIVED_CLOCK(1, 2*4));
415 m_ay1->port_a_write_callback().set(FUNC(midway_ssio_device::porta1_w));
416 m_ay1->port_b_write_callback().set(FUNC(midway_ssio_device::portb1_w));
417 m_ay1->add_route(ALL_OUTPUTS, *this, 0.33, AUTO_ALLOC_INPUT, 1);
418 }
419
420
421 //-------------------------------------------------
422 // device_input_ports - return a pointer to
423 // the device's I/O ports
424 //-------------------------------------------------
425
device_input_ports() const426 ioport_constructor midway_ssio_device::device_input_ports() const
427 {
428 return nullptr;
429 // return INPUT_PORTS_NAME( midway_ssio );
430 }
431
432
433 //-------------------------------------------------
434 // device_start - device-specific startup
435 //-------------------------------------------------
436
device_start()437 void midway_ssio_device::device_start()
438 {
439 compute_ay8910_modulation();
440 save_item(NAME(m_data));
441 save_item(NAME(m_status));
442 save_item(NAME(m_14024_count));
443 save_item(NAME(m_mute));
444 save_item(NAME(m_overall));
445 save_item(NAME(m_duty_cycle));
446 }
447
448
449 //-------------------------------------------------
450 // device_reset - device-specific reset
451 //-------------------------------------------------
452
device_reset()453 void midway_ssio_device::device_reset()
454 {
455 // latches also get reset
456 memset(m_data, 0, sizeof(m_data));
457 m_status = 0;
458 m_14024_count = 0;
459 }
460
461
462 //-------------------------------------------------
463 // device_timer - timer callbacks
464 //-------------------------------------------------
465
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)466 void midway_ssio_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
467 {
468 m_data[param >> 8] = param & 0xff;
469 }
470
471
472
473 //**************************************************************************
474 // SOUNDS GOOD BOARD
475 //**************************************************************************
476
477 //-------------------------------------------------
478 // midway_sounds_good_device - constructor
479 //-------------------------------------------------
480
midway_sounds_good_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)481 midway_sounds_good_device::midway_sounds_good_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
482 : device_t(mconfig, MIDWAY_SOUNDS_GOOD, tag, owner, clock)
483 , device_mixer_interface(mconfig, *this)
484 , m_cpu(*this, "cpu")
485 , m_pia(*this, "pia")
486 , m_dac(*this, "dac")
487 , m_dac_filter(*this, "dac_filter%u", 0U)
488 , m_status(0)
489 , m_dacval(0)
490 {
491 }
492
493
494 //-------------------------------------------------
495 // read - return the status value
496 //-------------------------------------------------
497
read()498 uint8_t midway_sounds_good_device::read()
499 {
500 return m_status;
501 }
502
503
504 //-------------------------------------------------
505 // write - handle an external write to the input
506 // latch
507 //-------------------------------------------------
508
write(uint8_t data)509 void midway_sounds_good_device::write(uint8_t data)
510 {
511 synchronize(0, data);
512 }
513
514
515 //-------------------------------------------------
516 // reset_write - write to the reset line
517 //-------------------------------------------------
518
WRITE_LINE_MEMBER(midway_sounds_good_device::reset_write)519 WRITE_LINE_MEMBER(midway_sounds_good_device::reset_write)
520 {
521 //if (state) osd_printf_debug("SG Reset\n");
522 m_cpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
523 }
524
525
526 //-------------------------------------------------
527 // porta_w - PIA port A writes
528 //-------------------------------------------------
529
porta_w(uint8_t data)530 void midway_sounds_good_device::porta_w(uint8_t data)
531 {
532 m_dacval = (data << 2) | (m_dacval & 3);
533 m_dac->write(m_dacval);
534 }
535
536
537 //-------------------------------------------------
538 // portb_w - PIA port B writes
539 //-------------------------------------------------
540
portb_w(uint8_t data)541 void midway_sounds_good_device::portb_w(uint8_t data)
542 {
543 uint8_t z_mask = m_pia->port_b_z_mask();
544
545 m_dacval = (m_dacval & ~3) | (data >> 6);
546 m_dac->write(m_dacval);
547
548 if (~z_mask & 0x10) m_status = (m_status & ~1) | ((data >> 4) & 1);
549 if (~z_mask & 0x20) m_status = (m_status & ~2) | ((data >> 4) & 2);
550 }
551
552
553 //-------------------------------------------------
554 // irq_w - IRQ line state changes
555 //-------------------------------------------------
556
WRITE_LINE_MEMBER(midway_sounds_good_device::irq_w)557 WRITE_LINE_MEMBER(midway_sounds_good_device::irq_w)
558 {
559 int combined_state = m_pia->irq_a_state() | m_pia->irq_b_state();
560 m_cpu->set_input_line(4, combined_state ? ASSERT_LINE : CLEAR_LINE);
561 }
562
563
564 //-------------------------------------------------
565 // audio CPU map
566 //-------------------------------------------------
567
568 // address map determined by PAL20L10 @U15; not dumped/verified yet
569 //Address map (x = ignored; * = selects address within this range)
570 //68k address map known for certain:
571 //a23 a22 a21 a20 a19 a18 a17 a16 a15 a14 a13 a12 a11 a10 a9 a8 a7 a6 a5 a4 a3 a2 a1 (a0 via UDS/LDS)
572 //x x x x x * * * ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? RW PAL@
573 //BEGIN 68k map guesses (until we have a pal dump):
574 //x x x x x 0 [a] [b] * * * * * * * * * * * * * * * -> R ROM U7, U17
575 //x x x x x 0 [c] [d] * * * * * * * * * * * * * * * -> R ROM U8, U18
576 //x x x x x 1 1 0 x x x x x x x x x x x x 0 *e* *f* 1 RW PIA U9 (e goes to PIA RS0, f goes to PIA RS1, D8-D15 go to PIA D0-D7)
577 //x x x x x 1 1 1 x x x x * * * * * * * * * * * ? RW RAM U6, U16
578 //The ROMTYPE PAL line, is pulled low if JW1 is present, and this presumably controls [a] [b] [c] [d] as such:
579 //When ROMTYPE is HIGH/JW1 absent, 27256 roms: (JW3 should be present, JW2 absent)
580 //x x x x x 0 0? 0 * * * * * * * * * * * * * * * -> R ROM U7, U17
581 //x x x x x 0 0? 1 * * * * * * * * * * * * * * * -> R ROM U8, U18
582 //When ROMTYPE is LOW/JW1 present, 27512 roms: (JW3 should be absent, JW2 present)
583 //x x x x x 0 0 * * * * * * * * * * * * * * * * -> R ROM U7, U17
584 //x x x x x 0 1 * * * * * * * * * * * * * * * * -> R ROM U8, U18
585 //JW2 and JW3 are mutually exclusive, and control whether 68k a16 connects to the EPROM pin 1 line (JW2 present), or whether it is tied high (JW3 present).
586 //EPROM pin 1 is A15 on 27512, and VPP(usually an active high enable) on 27256
587 //END guesses
588
589
590
soundsgood_map(address_map & map)591 void midway_sounds_good_device::soundsgood_map(address_map &map)
592 {
593 map.unmap_value_high();
594 map.global_mask(0x7ffff);
595 map(0x000000, 0x03ffff).rom();
596 map(0x060000, 0x060007).mirror(0x00fff0).rw(m_pia, FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt)).umask16(0xff00); // RS0 connects to A2, RS1 connects to A1, hence the "alt" functions are used.
597 map(0x070000, 0x070fff).mirror(0x00f000).ram();
598 }
599
600
601 //-------------------------------------------------
602 // device_add_mconfig - add device configuration
603 //-------------------------------------------------
604
device_add_mconfig(machine_config & config)605 void midway_sounds_good_device::device_add_mconfig(machine_config &config)
606 {
607 M68000(config, m_cpu, DERIVED_CLOCK(1, 2));
608 m_cpu->set_addrmap(AS_PROGRAM, &midway_sounds_good_device::soundsgood_map);
609
610 PIA6821(config, m_pia, 0);
611 m_pia->writepa_handler().set(FUNC(midway_sounds_good_device::porta_w));
612 m_pia->writepb_handler().set(FUNC(midway_sounds_good_device::portb_w));
613 m_pia->irqa_handler().set(FUNC(midway_sounds_good_device::irq_w));
614 m_pia->irqb_handler().set(FUNC(midway_sounds_good_device::irq_w));
615
616 AD7533(config, m_dac, 0); /// ad7533jn.u10
617
618 // The DAC filters here are identical to those on the "Turbo Cheap Squeak" and "Cheap Squeak Deluxe" boards.
619 //LM359 @U2.2, 2nd order MFB low-pass (fc = 5404.717733, Q = 0.625210, gain = -1.000000)
620 FILTER_BIQUAD(config, m_dac_filter[2]).opamp_mfb_lowpass_setup(RES_K(150), RES_K(82), RES_K(150), CAP_P(470), CAP_P(150)); // R115, R109, R108, C112, C111
621 m_dac_filter[2]->add_route(ALL_OUTPUTS, *this, 1.0);
622 //LM359 @U3.2, 2nd order MFB low-pass (fc = 5310.690763, Q = 1.608630, gain = -1.000000)
623 FILTER_BIQUAD(config, m_dac_filter[1]).opamp_mfb_lowpass_setup(RES_K(33), RES_K(18), RES_K(33), CAP_P(5600), CAP_P(270)); // R113, R117, R116, C115, C118
624 m_dac_filter[1]->add_route(ALL_OUTPUTS, m_dac_filter[2], 1.0);
625 //LM359 @U3.1, 1st order MFB low-pass (fc = 4912.189602, Q = 0.707107(ignored), gain = -1.000000)
626 FILTER_BIQUAD(config, m_dac_filter[0]).opamp_mfb_lowpass_setup(RES_K(120), RES_K(0), RES_K(120), CAP_P(0), CAP_P(270)); // R112, <short>, R111, <nonexistent>, C113
627 m_dac_filter[0]->add_route(ALL_OUTPUTS, m_dac_filter[1], 1.0);
628 m_dac->add_route(ALL_OUTPUTS, m_dac_filter[0], 1.0);
629 }
630
631
632 //-------------------------------------------------
633 // device_start - device-specific startup
634 //-------------------------------------------------
635
device_start()636 void midway_sounds_good_device::device_start()
637 {
638 save_item(NAME(m_status));
639 save_item(NAME(m_dacval));
640 }
641
642
643 //-------------------------------------------------
644 // device_reset - device-specific reset
645 //-------------------------------------------------
646
device_reset()647 void midway_sounds_good_device::device_reset()
648 {
649 }
650
651
652 //-------------------------------------------------
653 // device_timer - timer callbacks
654 //-------------------------------------------------
655
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)656 void midway_sounds_good_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
657 {
658 m_pia->portb_w((param >> 1) & 0x0f);
659 m_pia->ca1_w(~param & 0x01);
660
661 // oftentimes games will write one nibble at a time; the sync on this is very
662 // important, so we boost the interleave briefly while this happens
663 machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(250));
664 }
665
666
667
668 //**************************************************************************
669 // TURBO CHEAP SQUEAK BOARD
670 //**************************************************************************
671
672 //-------------------------------------------------
673 // midway_turbo_cheap_squeak_device - constructor
674 //-------------------------------------------------
675
midway_turbo_cheap_squeak_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)676 midway_turbo_cheap_squeak_device::midway_turbo_cheap_squeak_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
677 : device_t(mconfig, MIDWAY_TURBO_CHEAP_SQUEAK, tag, owner, clock),
678 device_mixer_interface(mconfig, *this)
679 , m_cpu(*this, "cpu")
680 , m_pia(*this, "pia")
681 , m_dac(*this, "dac")
682 , m_dac_filter(*this, "dac_filter%u", 0U)
683 , m_status(0)
684 , m_dacval(0)
685 {
686 }
687
688
689 //-------------------------------------------------
690 // read - return the status value
691 //-------------------------------------------------
692
read()693 uint8_t midway_turbo_cheap_squeak_device::read()
694 {
695 return m_status;
696 }
697
698
699 //-------------------------------------------------
700 // write - handle an external write to the input
701 // latch
702 //-------------------------------------------------
703
write(uint8_t data)704 void midway_turbo_cheap_squeak_device::write(uint8_t data)
705 {
706 synchronize(0, data);
707 }
708
709
710 //-------------------------------------------------
711 // reset_write - write to the reset line
712 //-------------------------------------------------
713
WRITE_LINE_MEMBER(midway_turbo_cheap_squeak_device::reset_write)714 WRITE_LINE_MEMBER(midway_turbo_cheap_squeak_device::reset_write)
715 {
716 m_cpu->set_input_line(INPUT_LINE_RESET, state ? ASSERT_LINE : CLEAR_LINE);
717 }
718
719
720 //-------------------------------------------------
721 // porta_w - PIA port A writes
722 //-------------------------------------------------
723
porta_w(uint8_t data)724 void midway_turbo_cheap_squeak_device::porta_w(uint8_t data)
725 {
726 m_dacval = (data << 2) | (m_dacval & 3);
727 m_dac->write(m_dacval);
728 }
729
730
731 //-------------------------------------------------
732 // portb_w - PIA port B writes
733 //-------------------------------------------------
734
portb_w(uint8_t data)735 void midway_turbo_cheap_squeak_device::portb_w(uint8_t data)
736 {
737 m_dacval = (m_dacval & ~3) | (data >> 6);
738 m_dac->write(m_dacval);
739 m_status = (data >> 4) & 3;
740 }
741
742
743 //-------------------------------------------------
744 // irq_w - IRQ line state changes
745 //-------------------------------------------------
746
WRITE_LINE_MEMBER(midway_turbo_cheap_squeak_device::irq_w)747 WRITE_LINE_MEMBER(midway_turbo_cheap_squeak_device::irq_w)
748 {
749 int combined_state = m_pia->irq_a_state() | m_pia->irq_b_state();
750 m_cpu->set_input_line(M6809_IRQ_LINE, combined_state ? ASSERT_LINE : CLEAR_LINE);
751 }
752
753
754 //-------------------------------------------------
755 // audio CPU map
756 //-------------------------------------------------
757
758 // address map verified from schematics
turbocs_map(address_map & map)759 void midway_turbo_cheap_squeak_device::turbocs_map(address_map &map)
760 {
761 map.unmap_value_high();
762 map(0x0000, 0x07ff).mirror(0x3800).ram();
763 map(0x4000, 0x4003).mirror(0x3ffc).rw("pia", FUNC(pia6821_device::read_alt), FUNC(pia6821_device::write_alt));
764 map(0x8000, 0xffff).rom();
765 }
766
767
768 //-------------------------------------------------
769 // device_add_mconfig - add device configuration
770 //-------------------------------------------------
771
device_add_mconfig(machine_config & config)772 void midway_turbo_cheap_squeak_device::device_add_mconfig(machine_config &config)
773 {
774 MC6809E(config, m_cpu, DERIVED_CLOCK(1, 4));
775 m_cpu->set_addrmap(AS_PROGRAM, &midway_turbo_cheap_squeak_device::turbocs_map);
776
777 PIA6821(config, m_pia, 0);
778 m_pia->writepa_handler().set(FUNC(midway_turbo_cheap_squeak_device::porta_w));
779 m_pia->writepb_handler().set(FUNC(midway_turbo_cheap_squeak_device::portb_w));
780 m_pia->irqa_handler().set(FUNC(midway_turbo_cheap_squeak_device::irq_w));
781 m_pia->irqb_handler().set(FUNC(midway_turbo_cheap_squeak_device::irq_w));
782
783 AD7533(config, m_dac, 0); /// ad7533jn.u11
784
785 // The DAC filters here are identical to those on the "Sounds Good" and "Cheap Squeak Deluxe" boards.
786 //LM359 @U14.2, 2nd order MFB low-pass (fc = 5404.717733, Q = 0.625210, gain = -1.000000)
787 FILTER_BIQUAD(config, m_dac_filter[2]).opamp_mfb_lowpass_setup(RES_K(150), RES_K(82), RES_K(150), CAP_P(470), CAP_P(150)); // R36, R37, R39, C19, C20
788 m_dac_filter[2]->add_route(ALL_OUTPUTS, *this, 1.0);
789 //LM359 @U13.2, 2nd order MFB low-pass (fc = 5310.690763, Q = 1.608630, gain = -1.000000)
790 FILTER_BIQUAD(config, m_dac_filter[1]).opamp_mfb_lowpass_setup(RES_K(33), RES_K(18), RES_K(33), CAP_P(5600), CAP_P(270)); // R32, R33, R35, C16, C17
791 m_dac_filter[1]->add_route(ALL_OUTPUTS, m_dac_filter[2], 1.0);
792 //LM359 @U13.1, 1st order MFB low-pass (fc = 4912.189602, Q = 0.707107(ignored), gain = -1.000000)
793 FILTER_BIQUAD(config, m_dac_filter[0]).opamp_mfb_lowpass_setup(RES_K(120), RES_K(0), RES_K(120), CAP_P(0), CAP_P(270)); // R27, <short>, R31, <nonexistent>, C14
794 m_dac_filter[0]->add_route(ALL_OUTPUTS, m_dac_filter[1], 1.0);
795 m_dac->add_route(ALL_OUTPUTS, m_dac_filter[0], 1.0);
796 }
797
798
799 //-------------------------------------------------
800 // device_start - device-specific startup
801 //-------------------------------------------------
802
device_start()803 void midway_turbo_cheap_squeak_device::device_start()
804 {
805 save_item(NAME(m_status));
806 save_item(NAME(m_dacval));
807 }
808
809
810 //-------------------------------------------------
811 // device_reset - device-specific reset
812 //-------------------------------------------------
813
device_reset()814 void midway_turbo_cheap_squeak_device::device_reset()
815 {
816 }
817
818
819 //-------------------------------------------------
820 // device_timer - timer callbacks
821 //-------------------------------------------------
822
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)823 void midway_turbo_cheap_squeak_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
824 {
825 m_pia->portb_w((param >> 1) & 0x0f);
826 m_pia->ca1_w(~param & 0x01);
827
828 // oftentimes games will write one nibble at a time; the sync on this is very
829 // important, so we boost the interleave briefly while this happens
830 machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(100));
831 }
832