1 // license:BSD-3-Clause
2 // copyright-holders:Mike Balfour
3 /***************************************************************************
4
5 Irem M27 hardware
6
7 If you have any questions about how this driver works, don't hesitate to
8 ask. - Mike Balfour (mab22@po.cwru.edu)
9
10 ****************************************************************************/
11
12 #include "emu.h"
13 #include "includes/redalert.h"
14
15 #include "cpu/m6800/m6800.h"
16 #include "cpu/m6502/m6502.h"
17 #include "machine/rescap.h"
18 #include "speaker.h"
19
20
21
22 #define REDALERT_AUDIO_PCB_CLOCK (XTAL(12'500'000))
23 #define REDALERT_AUDIO_CPU_CLOCK (REDALERT_AUDIO_PCB_CLOCK / 12)
24 #define REDALERT_AY8910_CLOCK (REDALERT_AUDIO_PCB_CLOCK / 6)
25 #define REDALERT_AUDIO_CPU_IRQ_FREQ (PERIOD_OF_555_ASTABLE(RES_K(120), RES_K(2.7), CAP_U(0.01)))
26 #define REDALERT_AUDIO_CPU_IRQ_TIME (PERIOD_OF_555_ASTABLE(RES_K(2.7), 0, CAP_U(0.01))) /* 555 discharge time */
27
28 #define REDALERT_VOICE_PCB_CLOCK (XTAL(6'000'000))
29 #define REDALERT_VOICE_CPU_CLOCK (REDALERT_VOICE_PCB_CLOCK)
30 #define REDALERT_HC55516_CLOCK (REDALERT_VOICE_PCB_CLOCK / 256)
31
32 #define DEMONEYE_AUDIO_PCB_CLOCK (XTAL(3'579'545))
33 #define DEMONEYE_AUDIO_CPU_CLOCK (DEMONEYE_AUDIO_PCB_CLOCK) /* what's the real divisor? */
34 #define DEMONEYE_AY8910_CLOCK (DEMONEYE_AUDIO_PCB_CLOCK / 2) /* what's the real divisor? */
35
36
TIMER_CALLBACK_MEMBER(redalert_state::audio_irq_on)37 TIMER_CALLBACK_MEMBER(redalert_state::audio_irq_on)
38 {
39 if (m_sndpia.found())
40 m_sndpia->cb1_w(0); // guess
41 else
42 m_audiocpu->set_input_line(m6502_device::IRQ_LINE, ASSERT_LINE);
43
44 m_audio_irq_off_timer->adjust(REDALERT_AUDIO_CPU_IRQ_TIME);
45 }
46
47
TIMER_CALLBACK_MEMBER(redalert_state::audio_irq_off)48 TIMER_CALLBACK_MEMBER(redalert_state::audio_irq_off)
49 {
50 if (m_sndpia.found())
51 m_sndpia->cb1_w(1); // guess
52 else
53 m_audiocpu->set_input_line(m6502_device::IRQ_LINE, CLEAR_LINE);
54 }
55
56
57 /*************************************
58 *
59 * Red Alert analog sounds
60 *
61 *************************************/
62
redalert_analog_w(uint8_t data)63 void redalert_state::redalert_analog_w(uint8_t data)
64 {
65 /* this port triggers analog sounds
66 D0 = Formation Aircraft?
67 D1 = Dive bombers?
68 D2 = Helicopters?
69 D3 = Launcher firing?
70 D4 = Explosion #1?
71 D5 = Explosion #2?
72 D6 = Explosion #3? */
73
74 logerror("Analog: %02X\n",data);
75 }
76
77
78
79 /*************************************
80 *
81 * Red Alert audio board
82 *
83 *************************************/
84
redalert_audio_command_w(uint8_t data)85 void redalert_state::redalert_audio_command_w(uint8_t data)
86 {
87 /* the byte is connected to port A of the AY8910 */
88 m_soundlatch->write(data);
89 m_sound_hs = 1;
90
91 /* D7 is also connected to the NMI input of the CPU -
92 the NMI is actually toggled by a 74121 (R1=27K, C10=330p) */
93 if ((data & 0x80) == 0x00)
94 m_audiocpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
95 }
96
97
redalert_AY8910_w(uint8_t data)98 void redalert_state::redalert_AY8910_w(uint8_t data)
99 {
100 /* BC2 is connected to a pull-up resistor, so BC2=1 always */
101 switch (data & 0x03)
102 {
103 /* BC1=0, BDIR=0 : inactive */
104 case 0x00:
105 break;
106
107 /* BC1=1, BDIR=0 : read from PSG */
108 case 0x01:
109 m_sound_hs = 0;
110 m_ay8910_latch_1 = m_ay8910->data_r();
111 break;
112
113 /* BC1=0, BDIR=1 : write to PSG */
114 /* BC1=1, BDIR=1 : latch address */
115 case 0x02:
116 case 0x03:
117 default:
118 m_ay8910->data_address_w(data, m_ay8910_latch_2);
119 break;
120 }
121 }
122
123
redalert_ay8910_latch_1_r()124 uint8_t redalert_state::redalert_ay8910_latch_1_r()
125 {
126 return m_ay8910_latch_1;
127 }
128
129
redalert_ay8910_latch_2_w(uint8_t data)130 void redalert_state::redalert_ay8910_latch_2_w(uint8_t data)
131 {
132 m_ay8910_latch_2 = data;
133 }
134
redalert_audio_map(address_map & map)135 void redalert_state::redalert_audio_map(address_map &map)
136 {
137 map.global_mask(0x7fff);
138 map(0x0000, 0x03ff).mirror(0x0c00).ram();
139 map(0x1000, 0x1000).mirror(0x0ffe).nopr().w(FUNC(redalert_state::redalert_AY8910_w));
140 map(0x1001, 0x1001).mirror(0x0ffe).rw(FUNC(redalert_state::redalert_ay8910_latch_1_r), FUNC(redalert_state::redalert_ay8910_latch_2_w));
141 map(0x2000, 0x6fff).noprw();
142 map(0x7000, 0x77ff).mirror(0x0800).rom();
143 }
144
panther_audio_map(address_map & map)145 void redalert_state::panther_audio_map(address_map &map)
146 {
147 redalert_audio_map(map);
148 // Panther maps these two to $2000 while Red Alert to $1000, different PAL addressing?
149 map(0x1000, 0x1fff).unmaprw();
150 map(0x2000, 0x2000).mirror(0x0ffe).nopr().w(FUNC(redalert_state::redalert_AY8910_w));
151 map(0x2001, 0x2001).mirror(0x0ffe).rw(FUNC(redalert_state::redalert_ay8910_latch_1_r), FUNC(redalert_state::redalert_ay8910_latch_2_w));
152 }
153
154 /*************************************
155 *
156 * Red Alert audio board
157 *
158 *************************************/
159
sound_start()160 void redalert_state::sound_start()
161 {
162 m_audio_irq_on_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(redalert_state::audio_irq_on), this));
163 m_audio_irq_off_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(redalert_state::audio_irq_off), this));
164
165 m_audio_irq_on_timer->adjust(REDALERT_AUDIO_CPU_IRQ_FREQ, 0, REDALERT_AUDIO_CPU_IRQ_FREQ);
166
167 save_item(NAME(m_sound_hs));
168 save_item(NAME(m_ay8910_latch_1));
169 save_item(NAME(m_ay8910_latch_2));
170 }
171
172
173 /*************************************
174 *
175 * Red Alert voice board
176 *
177 *************************************/
178
redalert_voice_command_w(uint8_t data)179 void redalert_state::redalert_voice_command_w(uint8_t data)
180 {
181 m_soundlatch2->write((data & 0x78) >> 3);
182 m_voicecpu->set_input_line(I8085_RST75_LINE, (~data & 0x80) ? ASSERT_LINE : CLEAR_LINE);
183 }
184
185
WRITE_LINE_MEMBER(redalert_state::sod_callback)186 WRITE_LINE_MEMBER(redalert_state::sod_callback)
187 {
188 m_cvsd->digit_w(state);
189 }
190
191
READ_LINE_MEMBER(redalert_state::sid_callback)192 READ_LINE_MEMBER(redalert_state::sid_callback)
193 {
194 return m_cvsd->clock_state_r();
195 }
196
197
redalert_voice_map(address_map & map)198 void redalert_state::redalert_voice_map(address_map &map)
199 {
200 map(0x0000, 0x3fff).rom();
201 map(0x4000, 0x7fff).noprw();
202 map(0x8000, 0x83ff).mirror(0x3c00).ram();
203 map(0xc000, 0xc000).mirror(0x3fff).r(m_soundlatch2, FUNC(generic_latch_8_device::read)).nopw();
204 }
205
206
207
208 /*************************************
209 *
210 * Red Alert audio board (m37b)
211 *
212 *************************************/
213
redalert_audio_m37b(machine_config & config)214 void redalert_state::redalert_audio_m37b(machine_config &config)
215 {
216 M6502(config, m_audiocpu, REDALERT_AUDIO_CPU_CLOCK);
217 m_audiocpu->set_addrmap(AS_PROGRAM, &redalert_state::redalert_audio_map);
218
219 GENERIC_LATCH_8(config, m_soundlatch);
220
221 AY8910(config, m_ay8910, REDALERT_AY8910_CLOCK);
222 m_ay8910->port_a_read_callback().set(m_soundlatch, FUNC(generic_latch_8_device::read));
223 m_ay8910->port_b_write_callback().set(FUNC(redalert_state::redalert_analog_w));
224 m_ay8910->add_route(0, "mono", 0.50);
225 m_ay8910->add_route(1, "mono", 0.50);
226 /* channel C is used a noise source and is not connected to a speaker */
227 }
228
229 /*************************************
230 *
231 * Red Alert voice board (ue17b)
232 *
233 *************************************/
234
redalert_audio_voice(machine_config & config)235 void redalert_state::redalert_audio_voice(machine_config &config)
236 {
237 I8085A(config, m_voicecpu, REDALERT_VOICE_CPU_CLOCK);
238 m_voicecpu->set_addrmap(AS_PROGRAM, &redalert_state::redalert_voice_map);
239 m_voicecpu->in_sid_func().set(FUNC(redalert_state::sid_callback));
240 m_voicecpu->out_sod_func().set(FUNC(redalert_state::sod_callback));
241
242 GENERIC_LATCH_8(config, m_soundlatch2);
243
244 HC55516(config, m_cvsd, REDALERT_HC55516_CLOCK).add_route(ALL_OUTPUTS, "mono", 0.50);
245 }
246
247 /*************************************
248 *
249 * Red Alert
250 *
251 *************************************/
252
redalert_audio(machine_config & config)253 void redalert_state::redalert_audio(machine_config &config)
254 {
255 SPEAKER(config, "mono").front_center();
256
257 redalert_audio_m37b(config);
258 redalert_audio_voice(config);
259 }
260
261 /*************************************
262 *
263 * Red Alert
264 *
265 *************************************/
266
ww3_audio(machine_config & config)267 void redalert_state::ww3_audio(machine_config &config)
268 {
269 SPEAKER(config, "mono").front_center();
270
271 redalert_audio_m37b(config);
272 }
273
panther_audio(machine_config & config)274 void redalert_state::panther_audio(machine_config &config)
275 {
276 SPEAKER(config, "mono").front_center();
277
278 redalert_audio_m37b(config);
279 m_audiocpu->set_addrmap(AS_PROGRAM, &redalert_state::panther_audio_map);
280 }
281
282 /*************************************
283 *
284 * Demoneye-X audio board
285 *
286 *************************************/
287
288
demoneye_audio_command_w(uint8_t data)289 void redalert_state::demoneye_audio_command_w(uint8_t data)
290 {
291 /* the byte is connected to port A of the AY8910 */
292 m_soundlatch->write(data);
293 m_audiocpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
294 }
295
296
demoneye_ay8910_latch_1_w(uint8_t data)297 void redalert_state::demoneye_ay8910_latch_1_w(uint8_t data)
298 {
299 m_ay8910_latch_1 = data;
300 }
301
302
demoneye_ay8910_latch_2_r()303 uint8_t redalert_state::demoneye_ay8910_latch_2_r()
304 {
305 return m_ay8910_latch_2;
306 }
307
308
demoneye_ay8910_data_w(uint8_t data)309 void redalert_state::demoneye_ay8910_data_w(uint8_t data)
310 {
311 switch (m_ay8910_latch_1 & 0x03)
312 {
313 case 0x00:
314 if (m_ay8910_latch_1 & 0x10)
315 m_ay[0]->data_w(data);
316
317 if (m_ay8910_latch_1 & 0x20)
318 m_ay[1]->data_w(data);
319
320 break;
321
322 case 0x01:
323 if (m_ay8910_latch_1 & 0x10)
324 m_ay8910_latch_2 = m_ay[0]->data_r();
325
326 if (m_ay8910_latch_1 & 0x20)
327 m_ay8910_latch_2 = m_ay[1]->data_r();
328
329 break;
330
331 case 0x03:
332 if (m_ay8910_latch_1 & 0x10)
333 m_ay[0]->address_w(data);
334
335 if (m_ay8910_latch_1 & 0x20)
336 m_ay[1]->address_w(data);
337
338 break;
339
340 default:
341 logerror("demoneye_ay8910_data_w called with latch %02X data %02X\n", m_ay8910_latch_1, data);
342 break;
343 }
344 }
345
346
demoneye_audio_map(address_map & map)347 void redalert_state::demoneye_audio_map(address_map &map)
348 {
349 map(0x0500, 0x0503).mirror(0xc000).rw(m_sndpia, FUNC(pia6821_device::read), FUNC(pia6821_device::write));
350 map(0x2000, 0x3fff).mirror(0xc000).rom();
351 }
352
353
354
355 /*************************************
356 *
357 * Demoneye-X machine driver
358 *
359 *************************************/
360
demoneye_audio(machine_config & config)361 void redalert_state::demoneye_audio(machine_config &config)
362 {
363 M6802(config, m_audiocpu, DEMONEYE_AUDIO_CPU_CLOCK);
364 m_audiocpu->set_addrmap(AS_PROGRAM, &redalert_state::demoneye_audio_map);
365
366 PIA6821(config, m_sndpia);
367 m_sndpia->readpa_handler().set(FUNC(redalert_state::demoneye_ay8910_latch_2_r));
368 m_sndpia->writepa_handler().set(FUNC(redalert_state::demoneye_ay8910_data_w));
369 m_sndpia->writepb_handler().set(FUNC(redalert_state::demoneye_ay8910_latch_1_w));
370 m_sndpia->irqb_handler().set_inputline(m_audiocpu, M6802_IRQ_LINE);
371
372 SPEAKER(config, "mono").front_center();
373
374 GENERIC_LATCH_8(config, m_soundlatch);
375
376 AY8910(config, m_ay[0], DEMONEYE_AY8910_CLOCK);
377 m_ay[0]->add_route(ALL_OUTPUTS, "mono", 0.50);
378
379 AY8910(config, m_ay[1], DEMONEYE_AY8910_CLOCK);
380 m_ay[1]->port_a_read_callback().set(m_soundlatch, FUNC(generic_latch_8_device::read));
381 m_ay[1]->add_route(ALL_OUTPUTS, "mono", 0.50);
382 }
383