1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5     Hard Drivin' sound hardware
6 
7 ****************************************************************************/
8 
9 #include "emu.h"
10 #include "includes/harddriv.h"
11 
12 #include "cpu/tms32010/tms32010.h"
13 #include "speaker.h"
14 
15 
16 #define BIO_FREQUENCY       (1000000 / 50)
17 #define CYCLES_PER_BIO      (5000000 / BIO_FREQUENCY)
18 
19 
20 //**************************************************************************
21 //  LIVE DEVICE
22 //**************************************************************************
23 
24 //-------------------------------------------------
25 //  harddriv_sound_board_device - constructor
26 //-------------------------------------------------
27 
28 DEFINE_DEVICE_TYPE(HARDDRIV_SOUND_BOARD, harddriv_sound_board_device, "harddriv_sound", "Hard Drivin' Sound Board")
29 
harddriv_sound_board_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)30 harddriv_sound_board_device::harddriv_sound_board_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
31 	device_t(mconfig, HARDDRIV_SOUND_BOARD, tag, owner, clock),
32 	m_soundcpu(*this, "soundcpu"),
33 	m_latch(*this, "latch"),
34 	m_dac(*this, "dac"),
35 	m_sounddsp(*this, "sounddsp"),
36 	m_sounddsp_ram(*this, "sounddsp_ram"),
37 	m_sound_rom(*this, "serialroms"),
38 	m_soundflag(0),
39 	m_mainflag(0),
40 	m_sounddata(0),
41 	m_maindata(0),
42 	m_cramen(0),
43 	m_irq68k(0),
44 	m_sound_rom_offs(0),
45 	m_last_bio_cycles(0)
46 {
47 	memset(m_comram, 0 , sizeof(m_comram));
48 }
49 
50 
51 //-------------------------------------------------
52 //  device_start - device-specific startup
53 //-------------------------------------------------
54 
device_start()55 void harddriv_sound_board_device::device_start()
56 {
57 }
58 
59 //-------------------------------------------------
60 //  device_reset - device-specific reset
61 //-------------------------------------------------
62 
device_reset()63 void harddriv_sound_board_device::device_reset()
64 {
65 	m_last_bio_cycles = 0;
66 	m_sounddsp->set_input_line(INPUT_LINE_HALT, ASSERT_LINE);
67 }
68 
69 /*************************************
70  *
71  *  Update flags
72  *
73  *************************************/
74 
update_68k_interrupts()75 void harddriv_sound_board_device::update_68k_interrupts()
76 {
77 	m_soundcpu->set_input_line(1, m_mainflag ? ASSERT_LINE : CLEAR_LINE);
78 	m_soundcpu->set_input_line(3, m_irq68k   ? ASSERT_LINE : CLEAR_LINE);
79 }
80 
81 
82 
83 /*************************************
84  *
85  *  I/O from main CPU side
86  *
87  *************************************/
88 
hd68k_snd_data_r()89 uint16_t harddriv_sound_board_device::hd68k_snd_data_r()
90 {
91 	m_soundflag = 0;
92 	logerror("%s:main read from sound=%04X\n", machine().describe_context(), m_sounddata);
93 	return m_sounddata;
94 }
95 
96 
hd68k_snd_status_r()97 uint16_t harddriv_sound_board_device::hd68k_snd_status_r()
98 {
99 	return (m_mainflag << 15) | (m_soundflag << 14) | 0x1fff;
100 }
101 
102 
TIMER_CALLBACK_MEMBER(harddriv_sound_board_device::delayed_68k_w)103 TIMER_CALLBACK_MEMBER( harddriv_sound_board_device::delayed_68k_w )
104 {
105 	m_maindata = param;
106 	m_mainflag = 1;
107 	update_68k_interrupts();
108 }
109 
110 
hd68k_snd_data_w(uint16_t data)111 void harddriv_sound_board_device::hd68k_snd_data_w(uint16_t data)
112 {
113 	machine().scheduler().synchronize(timer_expired_delegate(FUNC(harddriv_sound_board_device::delayed_68k_w), this), data);
114 	logerror("%s:main write to sound=%04X\n", machine().describe_context(), data);
115 }
116 
117 
hd68k_snd_reset_w(uint16_t data)118 void harddriv_sound_board_device::hd68k_snd_reset_w(uint16_t data)
119 {
120 	m_soundcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE);
121 	m_soundcpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE);
122 	m_mainflag = m_soundflag = 0;
123 	update_68k_interrupts();
124 	logerror("%s:Reset sound\n", machine().describe_context());
125 }
126 
127 
128 
129 /*************************************
130  *
131  *  I/O from sound CPU side
132  *
133  *************************************/
134 
hdsnd68k_data_r()135 uint16_t harddriv_sound_board_device::hdsnd68k_data_r()
136 {
137 	m_mainflag = 0;
138 	update_68k_interrupts();
139 	logerror("%s:sound read from main=%04X\n", machine().describe_context(), m_maindata);
140 	return m_maindata;
141 }
142 
143 
hdsnd68k_data_w(offs_t offset,uint16_t data,uint16_t mem_mask)144 void harddriv_sound_board_device::hdsnd68k_data_w(offs_t offset, uint16_t data, uint16_t mem_mask)
145 {
146 	COMBINE_DATA(&m_sounddata);
147 	m_soundflag = 1;
148 	logerror("%s:sound write to main=%04X\n", machine().describe_context(), data);
149 }
150 
151 
152 
153 /*************************************
154  *
155  *  Misc. 68k inputs
156  *
157  *************************************/
158 
hdsnd68k_switches_r(offs_t offset)159 uint16_t harddriv_sound_board_device::hdsnd68k_switches_r(offs_t offset)
160 {
161 	logerror("%s:hdsnd68k_switches_r(%04X)\n", machine().describe_context(), offset);
162 	return 0;
163 }
164 
165 
hdsnd68k_320port_r(offs_t offset)166 uint16_t harddriv_sound_board_device::hdsnd68k_320port_r(offs_t offset)
167 {
168 	logerror("%s:hdsnd68k_320port_r(%04X)\n", machine().describe_context(), offset);
169 	return 0;
170 }
171 
172 
hdsnd68k_status_r()173 uint16_t harddriv_sound_board_device::hdsnd68k_status_r()
174 {
175 //FFFF 3000 R   READSTAT    Read Status
176 //            D15 = 'Main Flag'
177 //            D14 = 'Sound Flag'
178 //            D13 = Test Switch
179 //            D12 = 5220 Ready Flag (0=Ready)
180 	//logerror("%s:hdsnd68k_status_r(%04X)\n", machine().describe_context(), offset);
181 	return (m_mainflag << 15) | (m_soundflag << 14) | 0x2000 | 0;//((ioport("IN0")->read() & 0x0020) << 8) | 0;
182 }
183 
184 
185 
186 /*************************************
187  *
188  *  Misc. 68k outputs
189  *
190  *************************************/
191 
hdsnd68k_latches_w(offs_t offset,uint16_t data)192 void harddriv_sound_board_device::hdsnd68k_latches_w(offs_t offset, uint16_t data)
193 {
194 	// bit 3 selects the value; data is ignored
195 	// low 3 bits select the function
196 	m_latch->write_bit(offset & 7, (offset >> 3) & 1);
197 }
198 
199 
WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_write_w)200 WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_write_w)
201 {
202 	// data == 0 means high, 1 means low
203 	logerror("%06X:SPWR=%d\n", m_soundcpu->pcbase(), state);
204 }
205 
206 
WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_reset_w)207 WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_reset_w)
208 {
209 	// data == 0 means low, 1 means high
210 	logerror("%06X:SPRES=%d\n", m_soundcpu->pcbase(), state);
211 }
212 
213 
WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_rate_w)214 WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_rate_w)
215 {
216 	// data == 0 means 8kHz, 1 means 10kHz
217 	logerror("%06X:SPRATE=%d\n", m_soundcpu->pcbase(), state);
218 }
219 
220 
WRITE_LINE_MEMBER(harddriv_sound_board_device::cram_enable_w)221 WRITE_LINE_MEMBER(harddriv_sound_board_device::cram_enable_w)
222 {
223 	// data == 0 means disable 68k access to COM320, 1 means enable
224 	m_cramen = state;
225 }
226 
227 
WRITE_LINE_MEMBER(harddriv_sound_board_device::led_w)228 WRITE_LINE_MEMBER(harddriv_sound_board_device::led_w)
229 {
230 }
231 
232 
hdsnd68k_speech_w(offs_t offset,uint16_t data)233 void harddriv_sound_board_device::hdsnd68k_speech_w(offs_t offset, uint16_t data)
234 {
235 	logerror("%s:hdsnd68k_speech_w(%04X)=%04X\n", machine().describe_context(), offset, data);
236 }
237 
238 
hdsnd68k_irqclr_w(uint16_t data)239 void harddriv_sound_board_device::hdsnd68k_irqclr_w(uint16_t data)
240 {
241 	m_irq68k = 0;
242 	update_68k_interrupts();
243 }
244 
245 
246 
247 /*************************************
248  *
249  *  TMS32010 access
250  *
251  *************************************/
252 
hdsnd68k_320ram_r(offs_t offset)253 uint16_t harddriv_sound_board_device::hdsnd68k_320ram_r(offs_t offset)
254 {
255 	return m_sounddsp_ram[offset & 0xfff];
256 }
257 
258 
hdsnd68k_320ram_w(offs_t offset,uint16_t data,uint16_t mem_mask)259 void harddriv_sound_board_device::hdsnd68k_320ram_w(offs_t offset, uint16_t data, uint16_t mem_mask)
260 {
261 	COMBINE_DATA(&m_sounddsp_ram[offset & 0xfff]);
262 }
263 
264 
hdsnd68k_320ports_r(offs_t offset)265 uint16_t harddriv_sound_board_device::hdsnd68k_320ports_r(offs_t offset)
266 {
267 	return m_sounddsp->space(AS_IO).read_word(offset & 7);
268 }
269 
270 
hdsnd68k_320ports_w(offs_t offset,uint16_t data)271 void harddriv_sound_board_device::hdsnd68k_320ports_w(offs_t offset, uint16_t data)
272 {
273 	m_sounddsp->space(AS_IO).write_word(offset & 7, data);
274 }
275 
276 
hdsnd68k_320com_r(offs_t offset)277 uint16_t harddriv_sound_board_device::hdsnd68k_320com_r(offs_t offset)
278 {
279 	if (m_cramen)
280 		return m_comram[offset & 0x1ff];
281 
282 	logerror("%s:hdsnd68k_320com_r(%04X) -- not allowed\n", machine().describe_context(), offset);
283 	return 0xffff;
284 }
285 
286 
hdsnd68k_320com_w(offs_t offset,uint16_t data,uint16_t mem_mask)287 void harddriv_sound_board_device::hdsnd68k_320com_w(offs_t offset, uint16_t data, uint16_t mem_mask)
288 {
289 	if (m_cramen)
290 		COMBINE_DATA(&m_comram[offset & 0x1ff]);
291 	else
292 		logerror("%s:hdsnd68k_320com_w(%04X)=%04X -- not allowed\n", machine().describe_context(), offset, data);
293 }
294 
295 
296 
297 /*************************************
298  *
299  *  TMS32010 interrupts
300  *
301  *************************************/
302 
READ_LINE_MEMBER(harddriv_sound_board_device::hdsnddsp_get_bio)303 READ_LINE_MEMBER(harddriv_sound_board_device::hdsnddsp_get_bio)
304 {
305 	uint64_t cycles_since_last_bio = m_sounddsp->total_cycles() - m_last_bio_cycles;
306 	int32_t cycles_until_bio = CYCLES_PER_BIO - cycles_since_last_bio;
307 
308 	/* if we're not at the next BIO yet, advance us there */
309 	if (cycles_until_bio > 0)
310 	{
311 		m_sounddsp->adjust_icount(-cycles_until_bio);
312 		m_last_bio_cycles += CYCLES_PER_BIO;
313 	}
314 	else
315 		m_last_bio_cycles = m_sounddsp->total_cycles();
316 	return ASSERT_LINE;
317 }
318 
319 
320 
321 /*************************************
322  *
323  *  TMS32010 ports
324  *
325  *************************************/
326 
hdsnddsp_dac_w(uint16_t data)327 void harddriv_sound_board_device::hdsnddsp_dac_w(uint16_t data)
328 {
329 	/* /DACL */
330 	m_dac->write((data >> 4) ^ 0x800); // schematics show d0-3 are ignored & the msb is inverted
331 }
332 
333 
hdsnddsp_comport_w(uint16_t data)334 void harddriv_sound_board_device::hdsnddsp_comport_w(uint16_t data)
335 {
336 	/* COM port TD0-7 */
337 	logerror("%s:hdsnddsp_comport_w=%d\n", machine().describe_context(), data);
338 }
339 
340 
hdsnddsp_mute_w(uint16_t data)341 void harddriv_sound_board_device::hdsnddsp_mute_w(uint16_t data)
342 {
343 	/* mute DAC audio, D0=1 */
344 	logerror("%s:mute DAC=%d\n", machine().describe_context(), data);
345 }
346 
347 
hdsnddsp_gen68kirq_w(uint16_t data)348 void harddriv_sound_board_device::hdsnddsp_gen68kirq_w(uint16_t data)
349 {
350 	/* generate 68k IRQ */
351 	m_irq68k = 1;
352 	update_68k_interrupts();
353 }
354 
355 
hdsnddsp_soundaddr_w(offs_t offset,uint16_t data)356 void harddriv_sound_board_device::hdsnddsp_soundaddr_w(offs_t offset, uint16_t data)
357 {
358 	if (offset == 0)
359 	{
360 		/* select sound ROM block */
361 		m_sound_rom_offs = (m_sound_rom_offs & 0xffff) | ((data & 15) << 16);
362 	}
363 	else
364 	{
365 		/* sound memory address */
366 		m_sound_rom_offs = (m_sound_rom_offs & ~0xffff) | (data & 0xffff);
367 	}
368 }
369 
370 
hdsnddsp_rom_r()371 uint16_t harddriv_sound_board_device::hdsnddsp_rom_r()
372 {
373 	if (m_sound_rom_offs < m_sound_rom.length())
374 		return m_sound_rom[m_sound_rom_offs++] << 7;
375 	m_sound_rom_offs++;
376 	return 0;
377 }
378 
379 
hdsnddsp_comram_r()380 uint16_t harddriv_sound_board_device::hdsnddsp_comram_r()
381 {
382 	return m_comram[m_sound_rom_offs++ & 0x1ff];
383 }
384 
385 
hdsnddsp_compare_r(offs_t offset)386 uint16_t harddriv_sound_board_device::hdsnddsp_compare_r(offs_t offset)
387 {
388 	logerror("%s:hdsnddsp_compare_r(%04X)\n", machine().describe_context(), offset);
389 	return 0;
390 }
391 
driversnd_68k_map(address_map & map)392 void harddriv_sound_board_device::driversnd_68k_map(address_map &map)
393 {
394 	map.unmap_value_high();
395 	map(0x000000, 0x01ffff).rom();
396 	map(0xff0000, 0xff0fff).rw(FUNC(harddriv_sound_board_device::hdsnd68k_data_r), FUNC(harddriv_sound_board_device::hdsnd68k_data_w));
397 	map(0xff1000, 0xff1fff).rw(FUNC(harddriv_sound_board_device::hdsnd68k_switches_r), FUNC(harddriv_sound_board_device::hdsnd68k_latches_w));
398 	map(0xff2000, 0xff2fff).rw(FUNC(harddriv_sound_board_device::hdsnd68k_320port_r), FUNC(harddriv_sound_board_device::hdsnd68k_speech_w));
399 	map(0xff3000, 0xff3fff).rw(FUNC(harddriv_sound_board_device::hdsnd68k_status_r), FUNC(harddriv_sound_board_device::hdsnd68k_irqclr_w));
400 	map(0xff4000, 0xff5fff).rw(FUNC(harddriv_sound_board_device::hdsnd68k_320ram_r), FUNC(harddriv_sound_board_device::hdsnd68k_320ram_w));
401 	map(0xff6000, 0xff7fff).rw(FUNC(harddriv_sound_board_device::hdsnd68k_320ports_r), FUNC(harddriv_sound_board_device::hdsnd68k_320ports_w));
402 	map(0xff8000, 0xffbfff).rw(FUNC(harddriv_sound_board_device::hdsnd68k_320com_r), FUNC(harddriv_sound_board_device::hdsnd68k_320com_w));
403 	map(0xffc000, 0xffffff).ram();
404 }
405 
406 
driversnd_dsp_program_map(address_map & map)407 void harddriv_sound_board_device::driversnd_dsp_program_map(address_map &map)
408 {
409 	map.unmap_value_high();
410 	map(0x000, 0xfff).ram().share("sounddsp_ram");
411 }
412 
413 
414 /* $000 - 08F  TMS32010 Internal Data RAM in Data Address Space */
415 
driversnd_dsp_io_map(address_map & map)416 void harddriv_sound_board_device::driversnd_dsp_io_map(address_map &map)
417 {
418 	map(0, 0).r(FUNC(harddriv_sound_board_device::hdsnddsp_rom_r)).w(FUNC(harddriv_sound_board_device::hdsnddsp_dac_w));
419 	map(1, 1).r(FUNC(harddriv_sound_board_device::hdsnddsp_comram_r));
420 	map(2, 2).r(FUNC(harddriv_sound_board_device::hdsnddsp_compare_r));
421 	map(1, 2).nopw();
422 	map(3, 3).w(FUNC(harddriv_sound_board_device::hdsnddsp_comport_w));
423 	map(4, 4).w(FUNC(harddriv_sound_board_device::hdsnddsp_mute_w));
424 	map(5, 5).w(FUNC(harddriv_sound_board_device::hdsnddsp_gen68kirq_w));
425 	map(6, 7).w(FUNC(harddriv_sound_board_device::hdsnddsp_soundaddr_w));
426 }
427 
428 
429 //-------------------------------------------------
430 // device_add_mconfig - add device configuration
431 //-------------------------------------------------
432 
device_add_mconfig(machine_config & config)433 void harddriv_sound_board_device::device_add_mconfig(machine_config &config)
434 {
435 	/* basic machine hardware */
436 	M68000(config, m_soundcpu, 16_MHz_XTAL/2);
437 	m_soundcpu->set_addrmap(AS_PROGRAM, &harddriv_sound_board_device::driversnd_68k_map);
438 
439 	LS259(config, m_latch, 0); // 80R
440 	m_latch->q_out_cb<0>().set(FUNC(harddriv_sound_board_device::speech_write_w)); // SPWR - 5220 write strobe
441 	m_latch->q_out_cb<1>().set(FUNC(harddriv_sound_board_device::speech_reset_w)); // SPRES - 5220 hard reset
442 	m_latch->q_out_cb<2>().set(FUNC(harddriv_sound_board_device::speech_rate_w)); // SPRATE
443 	m_latch->q_out_cb<3>().set(FUNC(harddriv_sound_board_device::cram_enable_w)); // CRAMEN
444 	m_latch->q_out_cb<4>().set_inputline(m_sounddsp, INPUT_LINE_HALT).invert(); // RES320
445 	m_latch->q_out_cb<7>().set(FUNC(harddriv_sound_board_device::led_w));
446 
447 	TMS32010(config, m_sounddsp, XTAL(20'000'000));
448 	m_sounddsp->set_addrmap(AS_PROGRAM, &harddriv_sound_board_device::driversnd_dsp_program_map);
449 	/* Data Map is internal to the CPU */
450 	m_sounddsp->set_addrmap(AS_IO, &harddriv_sound_board_device::driversnd_dsp_io_map);
451 	m_sounddsp->bio().set(FUNC(harddriv_sound_board_device::hdsnddsp_get_bio));
452 
453 	/* sound hardware */
454 	SPEAKER(config, "speaker").front_center();
455 
456 	AM6012(config, m_dac, 0).add_route(ALL_OUTPUTS, "speaker", 0.5); // ls374d.75e + ls374d.90e + am6012
457 }
458