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