1 // license:BSD-3-Clause
2 // copyright-holders:Acho A. Tang, Nicola Salmoria
3 /***************************************************************************
4
5 Functions to emulate the Alpha Denshi "59MC07" audio board
6
7 |----------------------|
8 | Fully boxed = socket |
9 |----------------------|
10
11
12 | separation = solder
13
14
15 -----------------------------------------------------------------------|
16 |ALPHA DENSHI CO, LTD. SOUND BOARD NO.59MC07 |
17 | |
18 | |--------------------| |
19 | P | \/ | JAPAN 84250C | SN74LS232J | SN74LS74AN | DISSIPATOR |
20 | | /\ | M5l8085AP | 8131BJ | 8314A | |
21 | |--------------------| |
22 | |
23 | N | SN74LS74A |
24 | | 8122AG |
25 | |
26 | |------------| |
27 | M | HV1VR OKI | SN74LS138J | SN74LS74AJ | LM324N | LM324N |
28 | | 2764-45 | 8044AG | F8048 | J423A2 | 98509 |- (+12V)
29 | |------------| |
30 | |- (+5V)
31 | |------------| |
32 | L | HV2VR OKI | T74LS14B1 | SN74LS08N |- (OUT)
33 | | 2764-45 | 88442 | K8208 |
34 | |------------| |- (
35 | | (GND)
36 | |------------| |- (
37 | K | HV3VR OKI | SN74LS08J | SN74LS04N | LM324N |
38 | | 2764-45 | K8208 | I8313 | 98509 |
39 | |------------| |
40 | |
41 | J | SN74LS74A | SN74LS232J | | LM324N |
42 | | 8122AG | 8131BG | | 436A |
43 | |
44 | |------------| |------------| |
45 | H | HV4VR OKI | SN74LS393N | TBP18S030N | | LM324N |
46 | | 2764-45 | K8208 | J419X | | 436A |
47 | |------------| |------------| |
48 | |
49 | F | SN74LS138J | HCF40174BE | | CD4066BCN |
50 | | 8044AG | MSM40174 | | MM5666BN |
51 | |
52 | E | M74LS123P | SN74LS138J | CD4066BCN | CD4066BCN |
53 | | 84A6C1 | 8044AG | MM5666BN | MM5666BN |
54 | MUSIC |
55 | |-------------------------| |
56 | D | SN74LS373N | JAPAN 841903 | | CD4066BCN |
57 | | 2764-45 | M5L8155P | | MM5666BN |
58 | |-------------------------| VOICE |
59 | |
60 |C |-------------------------| |
61 |O C | 74LS173 PC | MSM5232RS | |
62 |N | 8247 | OKI 4342 | |
63 |N |-------------------------| |
64 | |
65 |T B |
66 |O |
67 | |-------------------------| |
68 |M A | 74LS173 PC | SOUND AY-3-8910 | CD4066BCN | CD4066BCN |
69 |A | 8247 | 8046 CDA | MM5666BN | MM5666BN |
70 |I |-------------------------| |
71 |N |
72 | | 1 | 2 | 3 | 4 | 5 |
73 -----------------------------------------------------------------------|
74
75 ***************************************************************************/
76
77 #include "emu.h"
78 #include "audio/ad_sound.h"
79
80 #include "cpu/z80/z80.h"
81 #include "sound/ay8910.h"
82 #include "speaker.h"
83
84
85 //**************************************************************************
86 // GLOBAL VARIABLES
87 //**************************************************************************
88
89 DEFINE_DEVICE_TYPE(AD_59MC07, ad_59mc07_device, "ad_59mc07", "Alpha Denshi 59MC07 sound board")
90
91
92
93 //**************************************************************************
94 // MEMORY MAPS
95 //**************************************************************************
96
sound_map(address_map & map)97 void ad_59mc07_device::sound_map(address_map &map)
98 {
99 map(0x0000, 0xbfff).rom();
100 map(0xc000, 0xc000).r(m_soundlatch, FUNC(generic_latch_8_device::read));
101 map(0xc080, 0xc08d).w(m_msm, FUNC(msm5232_device::write));
102 map(0xc0a0, 0xc0a1).w("aysnd", FUNC(ay8910_device::data_address_w));
103 map(0xc0b0, 0xc0b0).nopw(); // n.c.
104 map(0xc0c0, 0xc0c0).w(FUNC(ad_59mc07_device::cymbal_ctrl_w));
105 map(0xc0d0, 0xc0d0).w(FUNC(ad_59mc07_device::dac_latch_w)); // followed by 1 (and usually 0) on 8155 port B
106 map(0xc0e0, 0xc0e0).w(FUNC(ad_59mc07_device::dac_latch_w)); // followed by 2 (and usually 0) on 8155 port B
107 map(0xc0f8, 0xc0ff).w(FUNC(ad_59mc07_device::c0f8_w));
108 map(0xe000, 0xe0ff).rw(m_audio8155, FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w));
109 }
110
sound_portmap(address_map & map)111 void ad_59mc07_device::sound_portmap(address_map &map)
112 {
113 map(0x00e0, 0x00e7).rw(m_audio8155, FUNC(i8155_device::io_r), FUNC(i8155_device::io_w));
114 }
115
116 //**************************************************************************
117 // SPECIFIC IMPLEMENTATION
118 //**************************************************************************
119
120 //-------------------------------------------------
121 // ad_59mc07_device: Constructor
122 //-------------------------------------------------
123
ad_59mc07_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)124 ad_59mc07_device::ad_59mc07_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
125 : device_t(mconfig, AD_59MC07, tag, owner, clock),
126 m_audiocpu(*this, "audiocpu"),
127 m_audio8155(*this, "audio8155"),
128 m_samples(*this, "samples"),
129 m_msm(*this, "msm"),
130 m_dac_1(*this, "dac1"),
131 m_dac_2(*this, "dac2"),
132 m_soundlatch(*this, "soundlatch"),
133 m_frq_adjuster(*this, ":FRQ") // TODO: this should be moved here, but I don't think it's possible to set the default per-game value with a variable
134 {}
135
136 // MSM5232 clock is generated by a transistor oscillator circuit, not by the pcb xtal
137 // The circuit is controlled by a pot to allow adjusting the frequency. All games
138 // have been hand-tuned to a recording claiming to be an original recording from the
139 // boards. You can adjust this in the Slider Controls while using -cheat if needed.
140
141 #define MSM5232_MAX_CLOCK 6144000
142 #define MSM5232_MIN_CLOCK 214000 // unstable
143
144
145
146 /******************************************************************************/
147 // Sound
148
WRITE_LINE_MEMBER(ad_59mc07_device::i8155_timer_pulse)149 WRITE_LINE_MEMBER(ad_59mc07_device::i8155_timer_pulse)
150 {
151 if (!state)
152 m_audiocpu->set_input_line(I8085_TRAP_LINE, ASSERT_LINE);
153 }
154
TIMER_CALLBACK_MEMBER(ad_59mc07_device::frq_adjuster_callback)155 TIMER_CALLBACK_MEMBER(ad_59mc07_device::frq_adjuster_callback)
156 {
157 uint8_t frq = m_frq_adjuster->read();
158
159 m_msm->set_clock(MSM5232_MIN_CLOCK + frq * (MSM5232_MAX_CLOCK - MSM5232_MIN_CLOCK) / 100);
160 //popmessage("8155: C %02x A %02x AY: A %02x B %02x Unk:%x", m_8155_port_c, m_8155_port_a, m_ay_port_a, m_ay_port_b, m_cymbal_ctrl & 15);
161
162 m_cymvol *= 0.94f;
163 m_hihatvol *= 0.94f;
164
165 m_msm->set_output_gain(10, m_hihatvol + m_cymvol * (m_ay_port_b & 3) * 0.33f); /* NO from msm5232 */
166 }
167
c0f8_w(offs_t offset,uint8_t data)168 void ad_59mc07_device::c0f8_w(offs_t offset, uint8_t data)
169 {
170 switch (offset)
171 {
172 case 0: // c0f8: NMI ack (written by NMI handler)
173 m_audiocpu->set_input_line(I8085_TRAP_LINE, CLEAR_LINE);
174 break;
175
176 case 1: // c0f9: RST75 trigger (written by NMI handler)
177 // Note: solder pad CP3 on the pcb would allow to disable this
178 m_audiocpu->pulse_input_line(I8085_RST75_LINE, attotime::zero);
179 break;
180
181 case 2: // c0fa: INTR trigger (written by NMI handler)
182 // verified on PCB:
183 m_audiocpu->set_input_line(I8085_INTR_LINE, HOLD_LINE);
184 break;
185
186 case 3: // c0fb: n.c.
187 // Note: this is written at end of the INTR handler, however LS138@E3 pin 12 is definitely unconnected
188 break;
189
190 case 4: // c0fc: increment PROM address (written by NMI handler)
191 m_sound_prom_address = (m_sound_prom_address + 1) & 0x1f;
192 // at this point, the 5-bit value
193 // goes to an op-amp and to the base of a transistor. The transistor is part
194 // of a resonator that is used to generate the M5232 clock. The PROM doesn't
195 // actually seem to be important, since even removing it the M5232 clock
196 // continues to come out normally.
197 break;
198
199 case 5: // c0fd: n.c.
200 // Note: this is written at end of the RST75 handler, however LS138@E3 pin 10 is definitely unconnected
201 break;
202
203 case 6: // c0fe: 4-bit answer for main CPU (unused)
204 // Note: this would go to the LS173@B1, which is however unpopulated on the pcb
205 break;
206
207 case 7: // c0ff: sound command latch clear
208 // Note: solder pad CP1 on the pcb would allow to disable this
209 m_soundlatch->clear_w();
210 break;
211 }
212 }
213
214
ay8910_porta_w(uint8_t data)215 void ad_59mc07_device::ay8910_porta_w(uint8_t data)
216 {
217 // bongo 1
218 m_samples->set_volume(0, ((data & 0x30) >> 4) * 0.33);
219 if (data & ~m_ay_port_a & 0x80)
220 m_samples->start(0, 0);
221
222 // bongo 2
223 m_samples->set_volume(1, (data & 0x03) * 0.33);
224 if (data & ~m_ay_port_a & 0x08)
225 m_samples->start(1, 1);
226
227 m_ay_port_a = data;
228 }
229
ay8910_portb_w(uint8_t data)230 void ad_59mc07_device::ay8910_portb_w(uint8_t data)
231 {
232 // bongo 3
233 m_samples->set_volume(2, ((data & 0x30)>>4) * 0.33);
234 if (data & ~m_ay_port_b & 0x80)
235 m_samples->start(2, 2);
236
237 // FIXME I'm just enabling the MSM5232 Noise Output for now. Proper emulation
238 // of the analog circuitry should be done instead.
239 // if (data & ~m_ay_port_b & 0x08) cymbal hit trigger
240 // if (data & ~m_ay_port_b & 0x04) hi-hat hit trigger
241 // data & 3 cymbal volume
242 // data & 0x40 hi-hat enable
243
244 if (data & ~m_ay_port_b & 0x08)
245 m_cymvol = 1.0f;
246
247 if (data & ~m_ay_port_b & 0x04)
248 m_hihatvol = 0.8f;
249
250 if (~data & 0x40)
251 m_hihatvol = 0.0f;
252
253 m_ay_port_b = data;
254 }
255
cymbal_ctrl_w(uint8_t data)256 void ad_59mc07_device::cymbal_ctrl_w(uint8_t data)
257 {
258 m_cymbal_ctrl++;
259 }
260
261
update_dac()262 void ad_59mc07_device::update_dac()
263 {
264 // there is only one latch, which is used to drive two DAC channels.
265 // When the channel is enabled in the 4066, it goes to a series of
266 // low-pass filters. The channel is kept enabled only for a short time,
267 // then it's disabled again.
268 // Note that PB0 goes through three filters while PB1 only goes through one.
269
270 if (m_8155_port_b & 1)
271 m_dac_1->write(m_dac_latch);
272
273 if (m_8155_port_b & 2)
274 m_dac_2->write(m_dac_latch);
275 }
276
dac_latch_w(uint8_t data)277 void ad_59mc07_device::dac_latch_w(uint8_t data)
278 {
279 m_dac_latch = data;
280 update_dac();
281 }
282
i8155_porta_w(uint8_t data)283 void ad_59mc07_device::i8155_porta_w(uint8_t data)
284 {
285 m_8155_port_a = data;
286 m_msm->set_output_gain(0, (data >> 4) / 15.0); // group1 from msm5232
287 m_msm->set_output_gain(1, (data >> 4) / 15.0); // group1 from msm5232
288 m_msm->set_output_gain(2, (data >> 4) / 15.0); // group1 from msm5232
289 m_msm->set_output_gain(3, (data >> 4) / 15.0); // group1 from msm5232
290 m_msm->set_output_gain(4, (data & 0x0f) / 15.0); // group2 from msm5232
291 m_msm->set_output_gain(5, (data & 0x0f) / 15.0); // group2 from msm5232
292 m_msm->set_output_gain(6, (data & 0x0f) / 15.0); // group2 from msm5232
293 m_msm->set_output_gain(7, (data & 0x0f) / 15.0); // group2 from msm5232
294 }
295
i8155_portb_w(uint8_t data)296 void ad_59mc07_device::i8155_portb_w(uint8_t data)
297 {
298 m_8155_port_b = data;
299 update_dac();
300 }
301
i8155_portc_w(uint8_t data)302 void ad_59mc07_device::i8155_portc_w(uint8_t data)
303 {
304 m_8155_port_c = data;
305 m_msm->set_output_gain(8, (data & 0x0f) / 15.0); // SOLO 8' from msm5232
306 if (data & 0x20)
307 m_msm->set_output_gain(9, (data & 0x0f) / 15.0); // SOLO 16' from msm5232
308 else
309 m_msm->set_output_gain(9, 0); // SOLO 16' from msm5232
310 }
311
WRITE_LINE_MEMBER(ad_59mc07_device::msm5232_gate)312 WRITE_LINE_MEMBER(ad_59mc07_device::msm5232_gate)
313 {
314 }
315
316 static const char *const alphamc07_sample_names[] =
317 {
318 "*equites",
319 "bongo1",
320 "bongo2",
321 "bongo3",
322 nullptr
323 };
324
325
326 #define MSM5232_BASE_VOLUME 1.0
327
328 //-------------------------------------------------
329 // device_add_mconfig - add device configuration
330 //-------------------------------------------------
331
device_add_mconfig(machine_config & config)332 void ad_59mc07_device::device_add_mconfig(machine_config &config)
333 {
334 I8085A(config, m_audiocpu, 6.144_MHz_XTAL); // verified on pcb
335 m_audiocpu->set_addrmap(AS_PROGRAM, &ad_59mc07_device::sound_map);
336 m_audiocpu->set_addrmap(AS_IO, &ad_59mc07_device::sound_portmap);
337 m_audiocpu->set_clk_out(m_audio8155, FUNC(i8155_device::set_unscaled_clock_int));
338
339 I8155(config, m_audio8155, 0);
340 m_audio8155->out_pa_callback().set(FUNC(ad_59mc07_device::i8155_porta_w));
341 m_audio8155->out_pb_callback().set(FUNC(ad_59mc07_device::i8155_portb_w));
342 m_audio8155->out_pc_callback().set(FUNC(ad_59mc07_device::i8155_portc_w));
343 m_audio8155->out_to_callback().set(FUNC(ad_59mc07_device::i8155_timer_pulse));
344
345 // sound hardware
346 SPEAKER(config, "speaker").front_center();
347
348 GENERIC_LATCH_8(config, m_soundlatch);
349
350 MSM5232(config, m_msm, MSM5232_MAX_CLOCK); // will be adjusted at runtime through PORT_ADJUSTER
351 m_msm->set_capacitors(0.47e-6, 0.47e-6, 0.47e-6, 0.47e-6, 0.47e-6, 0.47e-6, 0.47e-6, 0.47e-6); // verified
352 m_msm->gate().set(FUNC(ad_59mc07_device::msm5232_gate));
353 m_msm->add_route(0, "speaker", MSM5232_BASE_VOLUME/2.2); // pin 28 2'-1 : 22k resistor
354 m_msm->add_route(1, "speaker", MSM5232_BASE_VOLUME/1.5); // pin 29 4'-1 : 15k resistor
355 m_msm->add_route(2, "speaker", MSM5232_BASE_VOLUME); // pin 30 8'-1 : 10k resistor
356 m_msm->add_route(3, "speaker", MSM5232_BASE_VOLUME); // pin 31 16'-1 : 10k resistor
357 m_msm->add_route(4, "speaker", MSM5232_BASE_VOLUME/2.2); // pin 36 2'-2 : 22k resistor
358 m_msm->add_route(5, "speaker", MSM5232_BASE_VOLUME/1.5); // pin 35 4'-2 : 15k resistor
359 m_msm->add_route(6, "speaker", MSM5232_BASE_VOLUME); // pin 34 8'-2 : 10k resistor
360 m_msm->add_route(7, "speaker", MSM5232_BASE_VOLUME); // pin 33 16'-2 : 10k resistor
361 m_msm->add_route(8, "speaker", 1.0); // pin 1 SOLO 8' (this actually feeds an analog section)
362 m_msm->add_route(9, "speaker", 1.0); // pin 2 SOLO 16' (this actually feeds an analog section)
363 m_msm->add_route(10,"speaker", 0.12); // pin 22 Noise Output (this actually feeds an analog section)
364
365 ay8910_device &aysnd(AY8910(config, "aysnd", 6.144_MHz_XTAL/4)); // verified on pcb
366 aysnd.port_a_write_callback().set(FUNC(ad_59mc07_device::ay8910_porta_w));
367 aysnd.port_b_write_callback().set(FUNC(ad_59mc07_device::ay8910_portb_w));
368 aysnd.add_route(ALL_OUTPUTS, "speaker", 0.15);
369
370 DAC_6BIT_R2R(config, m_dac_1, 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // unknown DAC
371 DAC_6BIT_R2R(config, m_dac_2, 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // unknown DAC
372
373 SAMPLES(config, m_samples);
374 m_samples->set_channels(3);
375 m_samples->set_samples_names(alphamc07_sample_names);
376 m_samples->add_route(ALL_OUTPUTS, "speaker", 0.3);
377 }
378
379
380 //-------------------------------------------------
381 // device_start: Start up the device
382 //-------------------------------------------------
383
device_start()384 void ad_59mc07_device::device_start()
385 {
386 // zerofill
387 m_sound_prom_address = 0;
388 m_dac_latch = 0;
389 m_8155_port_b = 0;
390 m_8155_port_a = 0;
391 m_8155_port_c = 0;
392 m_ay_port_a = 0;
393 m_ay_port_b = 0;
394 m_cymbal_ctrl = 0;
395 m_cymvol = 0.0;
396 m_hihatvol = 0.0;
397
398 // register for savestates
399 save_item(NAME(m_sound_prom_address));
400 save_item(NAME(m_dac_latch));
401 save_item(NAME(m_8155_port_b));
402 save_item(NAME(m_8155_port_a));
403 save_item(NAME(m_8155_port_c));
404 save_item(NAME(m_ay_port_a));
405 save_item(NAME(m_ay_port_b));
406 save_item(NAME(m_cymbal_ctrl));
407 save_item(NAME(m_cymvol));
408 save_item(NAME(m_hihatvol));
409
410 m_adjuster_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(ad_59mc07_device::frq_adjuster_callback), this));
411 m_adjuster_timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60));
412 }
413
414
415 //-------------------------------------------------
416 // device_reset: Reset the device
417 //-------------------------------------------------
418
device_reset()419 void ad_59mc07_device::device_reset()
420 {
421 m_audiocpu->set_input_line(I8085_INTR_LINE, CLEAR_LINE);
422 m_audiocpu->set_input_line(I8085_TRAP_LINE, CLEAR_LINE);
423 }
424
425
426 /*
427 Functions to emulate the Alpha Denshi "60MC01" audio board
428
429 CPU : Z80A
430 Sound: AY-3-8910A (unpopulated: another 8910 and a YM2203)
431 OSC : 16.000MHz
432
433 TODO: This bears a lot of similarities with the Super Stingray audio board. Verify if PCB codes match and if so merge implementations;
434 fix interrupts;
435 is there really no music?
436 */
437
438 //**************************************************************************
439 // GLOBAL VARIABLES
440 //**************************************************************************
441
442 DEFINE_DEVICE_TYPE(AD_60MC01, ad_60mc01_device, "ad_60mc01", "Alpha Denshi 60MC01 sound board")
443
444
445
446 //**************************************************************************
447 // MEMORY MAPS
448 //**************************************************************************
449
sound_map(address_map & map)450 void ad_60mc01_device::sound_map(address_map &map)
451 {
452 map(0x0000, 0x1fff).rom();
453 map(0x8000, 0x87ff).ram();
454 map(0xc100, 0xc100).r(m_soundlatch, FUNC(generic_latch_8_device::read));
455 map(0xc102, 0xc102).w(m_soundlatch, FUNC(generic_latch_8_device::clear_w));
456 map(0xc104, 0xc104).nopw(); // written at start up, would be DAC if it were populated
457 map(0xc106, 0xc10e).nopw(); // written continuously, it's audio board I/O according to Super Stingray's emulation
458 }
459
sound_portmap(address_map & map)460 void ad_60mc01_device::sound_portmap(address_map &map)
461 {
462 map.global_mask(0xff);
463 map(0x10, 0x11).nopw(); // written at start up, would be the YM2203 if it were populated
464 map(0x80, 0x81).w("aysnd", FUNC(ay8910_device::data_address_w));
465 }
466
467 //**************************************************************************
468 // SPECIFIC IMPLEMENTATION
469 //**************************************************************************
470
471 //-------------------------------------------------
472 // ad_60mc01_device: Constructor
473 //-------------------------------------------------
474
ad_60mc01_device(const machine_config & mconfig,const char * tag,device_t * owner,u32 clock)475 ad_60mc01_device::ad_60mc01_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
476 : device_t(mconfig, AD_60MC01, tag, owner, clock),
477 m_audiocpu(*this, "audiocpu"),
478 m_soundlatch(*this, "soundlatch")
479 {}
480
481 //-------------------------------------------------
482 // device_add_mconfig - add device configuration
483 //-------------------------------------------------
484
device_add_mconfig(machine_config & config)485 void ad_60mc01_device::device_add_mconfig(machine_config &config)
486 {
487 Z80(config, m_audiocpu, 16_MHz_XTAL / 4); // divider not verified
488 m_audiocpu->set_addrmap(AS_PROGRAM, &ad_60mc01_device::sound_map);
489 m_audiocpu->set_addrmap(AS_IO, &ad_60mc01_device::sound_portmap);
490 m_audiocpu->set_periodic_int(FUNC(ad_60mc01_device::sound_irq), attotime::from_hz(128));
491
492 // sound hardware
493 SPEAKER(config, "speaker").front_center();
494
495 GENERIC_LATCH_8(config, m_soundlatch);
496
497 ay8910_device &aysnd(AY8910(config, "aysnd", 16_MHz_XTAL / 8)); // divider not verified
498 aysnd.add_route(ALL_OUTPUTS, "speaker", 0.50);
499 }
500
501
502 //-------------------------------------------------
503 // device_start: Start up the device
504 //-------------------------------------------------
505
device_start()506 void ad_60mc01_device::device_start()
507 {
508 }
509
510
511 //-------------------------------------------------
512 // device_reset: Reset the device
513 //-------------------------------------------------
514
device_reset()515 void ad_60mc01_device::device_reset()
516 {
517 }
518