// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** Hard Drivin' sound hardware ****************************************************************************/ #include "emu.h" #include "includes/harddriv.h" #include "machine/atarigen.h" #include "cpu/tms32010/tms32010.h" #include "sound/volt_reg.h" #include "speaker.h" #define BIO_FREQUENCY (1000000 / 50) #define CYCLES_PER_BIO (5000000 / BIO_FREQUENCY) //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // harddriv_sound_board_device - constructor //------------------------------------------------- DEFINE_DEVICE_TYPE(HARDDRIV_SOUND_BOARD, harddriv_sound_board_device, "harddriv_sound", "Hard Drivin' Sound Board") harddriv_sound_board_device::harddriv_sound_board_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, HARDDRIV_SOUND_BOARD, tag, owner, clock), m_soundcpu(*this, "soundcpu"), m_latch(*this, "latch"), m_dac(*this, "dac"), m_sounddsp(*this, "sounddsp"), m_sounddsp_ram(*this, "sounddsp_ram"), m_sound_rom(*this, "serialroms"), m_soundflag(0), m_mainflag(0), m_sounddata(0), m_maindata(0), m_cramen(0), m_irq68k(0), m_sound_rom_offs(0), m_last_bio_cycles(0) { memset(m_comram, 0 , sizeof(m_comram)); } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void harddriv_sound_board_device::device_start() { } //------------------------------------------------- // device_reset - device-specific reset //------------------------------------------------- void harddriv_sound_board_device::device_reset() { m_last_bio_cycles = 0; m_sounddsp->set_input_line(INPUT_LINE_HALT, ASSERT_LINE); } /************************************* * * Update flags * *************************************/ void harddriv_sound_board_device::update_68k_interrupts() { m_soundcpu->set_input_line(1, m_mainflag ? ASSERT_LINE : CLEAR_LINE); m_soundcpu->set_input_line(3, m_irq68k ? ASSERT_LINE : CLEAR_LINE); } /************************************* * * I/O from main CPU side * *************************************/ READ16_MEMBER(harddriv_sound_board_device::hd68k_snd_data_r) { m_soundflag = 0; logerror("%s:main read from sound=%04X\n", machine().describe_context(), m_sounddata); return m_sounddata; } READ16_MEMBER(harddriv_sound_board_device::hd68k_snd_status_r) { return (m_mainflag << 15) | (m_soundflag << 14) | 0x1fff; } TIMER_CALLBACK_MEMBER( harddriv_sound_board_device::delayed_68k_w ) { m_maindata = param; m_mainflag = 1; update_68k_interrupts(); } WRITE16_MEMBER(harddriv_sound_board_device::hd68k_snd_data_w) { machine().scheduler().synchronize(timer_expired_delegate(FUNC(harddriv_sound_board_device::delayed_68k_w), this), data); logerror("%s:main write to sound=%04X\n", machine().describe_context(), data); } WRITE16_MEMBER(harddriv_sound_board_device::hd68k_snd_reset_w) { m_soundcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); m_soundcpu->set_input_line(INPUT_LINE_RESET, CLEAR_LINE); m_mainflag = m_soundflag = 0; update_68k_interrupts(); logerror("%s:Reset sound\n", machine().describe_context()); } /************************************* * * I/O from sound CPU side * *************************************/ READ16_MEMBER(harddriv_sound_board_device::hdsnd68k_data_r) { m_mainflag = 0; update_68k_interrupts(); logerror("%s:sound read from main=%04X\n", machine().describe_context(), m_maindata); return m_maindata; } WRITE16_MEMBER(harddriv_sound_board_device::hdsnd68k_data_w) { COMBINE_DATA(&m_sounddata); m_soundflag = 1; logerror("%s:sound write to main=%04X\n", machine().describe_context(), data); } /************************************* * * Misc. 68k inputs * *************************************/ READ16_MEMBER(harddriv_sound_board_device::hdsnd68k_switches_r) { logerror("%s:hdsnd68k_switches_r(%04X)\n", machine().describe_context(), offset); return 0; } READ16_MEMBER(harddriv_sound_board_device::hdsnd68k_320port_r) { logerror("%s:hdsnd68k_320port_r(%04X)\n", machine().describe_context(), offset); return 0; } READ16_MEMBER(harddriv_sound_board_device::hdsnd68k_status_r) { //FFFF 3000 R READSTAT Read Status // D15 = 'Main Flag' // D14 = 'Sound Flag' // D13 = Test Switch // D12 = 5220 Ready Flag (0=Ready) logerror("%s:hdsnd68k_status_r(%04X)\n", machine().describe_context(), offset); return (m_mainflag << 15) | (m_soundflag << 14) | 0x2000 | 0;//((ioport("IN0")->read() & 0x0020) << 8) | 0; } /************************************* * * Misc. 68k outputs * *************************************/ WRITE16_MEMBER(harddriv_sound_board_device::hdsnd68k_latches_w) { // bit 3 selects the value; data is ignored // low 3 bits select the function m_latch->write_bit(offset & 7, (offset >> 3) & 1); } WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_write_w) { // data == 0 means high, 1 means low logerror("%06X:SPWR=%d\n", m_soundcpu->pcbase(), state); } WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_reset_w) { // data == 0 means low, 1 means high logerror("%06X:SPRES=%d\n", m_soundcpu->pcbase(), state); } WRITE_LINE_MEMBER(harddriv_sound_board_device::speech_rate_w) { // data == 0 means 8kHz, 1 means 10kHz logerror("%06X:SPRATE=%d\n", m_soundcpu->pcbase(), state); } WRITE_LINE_MEMBER(harddriv_sound_board_device::cram_enable_w) { // data == 0 means disable 68k access to COM320, 1 means enable m_cramen = state; } WRITE_LINE_MEMBER(harddriv_sound_board_device::led_w) { } WRITE16_MEMBER(harddriv_sound_board_device::hdsnd68k_speech_w) { logerror("%s:hdsnd68k_speech_w(%04X)=%04X\n", machine().describe_context(), offset, data); } WRITE16_MEMBER(harddriv_sound_board_device::hdsnd68k_irqclr_w) { m_irq68k = 0; update_68k_interrupts(); } /************************************* * * TMS32010 access * *************************************/ READ16_MEMBER(harddriv_sound_board_device::hdsnd68k_320ram_r) { return m_sounddsp_ram[offset & 0xfff]; } WRITE16_MEMBER(harddriv_sound_board_device::hdsnd68k_320ram_w) { COMBINE_DATA(&m_sounddsp_ram[offset & 0xfff]); } READ16_MEMBER(harddriv_sound_board_device::hdsnd68k_320ports_r) { return m_sounddsp->space(AS_IO).read_word(offset & 7); } WRITE16_MEMBER(harddriv_sound_board_device::hdsnd68k_320ports_w) { m_sounddsp->space(AS_IO).write_word(offset & 7, data); } READ16_MEMBER(harddriv_sound_board_device::hdsnd68k_320com_r) { if (m_cramen) return m_comram[offset & 0x1ff]; logerror("%s:hdsnd68k_320com_r(%04X) -- not allowed\n", machine().describe_context(), offset); return 0xffff; } WRITE16_MEMBER(harddriv_sound_board_device::hdsnd68k_320com_w) { if (m_cramen) COMBINE_DATA(&m_comram[offset & 0x1ff]); else logerror("%s:hdsnd68k_320com_w(%04X)=%04X -- not allowed\n", machine().describe_context(), offset, data); } /************************************* * * TMS32010 interrupts * *************************************/ READ_LINE_MEMBER(harddriv_sound_board_device::hdsnddsp_get_bio) { uint64_t cycles_since_last_bio = m_sounddsp->total_cycles() - m_last_bio_cycles; int32_t cycles_until_bio = CYCLES_PER_BIO - cycles_since_last_bio; /* if we're not at the next BIO yet, advance us there */ if (cycles_until_bio > 0) { m_sounddsp->adjust_icount(-cycles_until_bio); m_last_bio_cycles += CYCLES_PER_BIO; } else m_last_bio_cycles = m_sounddsp->total_cycles(); return ASSERT_LINE; } /************************************* * * TMS32010 ports * *************************************/ WRITE16_MEMBER(harddriv_sound_board_device::hdsnddsp_dac_w) { /* /DACL */ m_dac->write((data >> 4) ^ 0x800); // schematics show d0-3 are ignored & the msb is inverted } WRITE16_MEMBER(harddriv_sound_board_device::hdsnddsp_comport_w) { /* COM port TD0-7 */ logerror("%s:hdsnddsp_comport_w=%d\n", machine().describe_context(), data); } WRITE16_MEMBER(harddriv_sound_board_device::hdsnddsp_mute_w) { /* mute DAC audio, D0=1 */ logerror("%s:mute DAC=%d\n", machine().describe_context(), data); } WRITE16_MEMBER(harddriv_sound_board_device::hdsnddsp_gen68kirq_w) { /* generate 68k IRQ */ m_irq68k = 1; update_68k_interrupts(); } WRITE16_MEMBER(harddriv_sound_board_device::hdsnddsp_soundaddr_w) { if (offset == 0) { /* select sound ROM block */ m_sound_rom_offs = (m_sound_rom_offs & 0xffff) | ((data & 15) << 16); } else { /* sound memory address */ m_sound_rom_offs = (m_sound_rom_offs & ~0xffff) | (data & 0xffff); } } READ16_MEMBER(harddriv_sound_board_device::hdsnddsp_rom_r) { if (m_sound_rom_offs < m_sound_rom.length()) return m_sound_rom[m_sound_rom_offs++] << 7; m_sound_rom_offs++; return 0; } READ16_MEMBER(harddriv_sound_board_device::hdsnddsp_comram_r) { return m_comram[m_sound_rom_offs++ & 0x1ff]; } READ16_MEMBER(harddriv_sound_board_device::hdsnddsp_compare_r) { logerror("%s:hdsnddsp_compare_r(%04X)\n", machine().describe_context(), offset); return 0; } void harddriv_sound_board_device::driversnd_68k_map(address_map &map) { map.unmap_value_high(); map(0x000000, 0x01ffff).rom(); map(0xff0000, 0xff0fff).rw(this, FUNC(harddriv_sound_board_device::hdsnd68k_data_r), FUNC(harddriv_sound_board_device::hdsnd68k_data_w)); map(0xff1000, 0xff1fff).rw(this, FUNC(harddriv_sound_board_device::hdsnd68k_switches_r), FUNC(harddriv_sound_board_device::hdsnd68k_latches_w)); map(0xff2000, 0xff2fff).rw(this, FUNC(harddriv_sound_board_device::hdsnd68k_320port_r), FUNC(harddriv_sound_board_device::hdsnd68k_speech_w)); map(0xff3000, 0xff3fff).rw(this, FUNC(harddriv_sound_board_device::hdsnd68k_status_r), FUNC(harddriv_sound_board_device::hdsnd68k_irqclr_w)); map(0xff4000, 0xff5fff).rw(this, FUNC(harddriv_sound_board_device::hdsnd68k_320ram_r), FUNC(harddriv_sound_board_device::hdsnd68k_320ram_w)); map(0xff6000, 0xff7fff).rw(this, FUNC(harddriv_sound_board_device::hdsnd68k_320ports_r), FUNC(harddriv_sound_board_device::hdsnd68k_320ports_w)); map(0xff8000, 0xffbfff).rw(this, FUNC(harddriv_sound_board_device::hdsnd68k_320com_r), FUNC(harddriv_sound_board_device::hdsnd68k_320com_w)); map(0xffc000, 0xffffff).ram(); } void harddriv_sound_board_device::driversnd_dsp_program_map(address_map &map) { map.unmap_value_high(); map(0x000, 0xfff).ram().share("sounddsp_ram"); } /* $000 - 08F TMS32010 Internal Data RAM in Data Address Space */ void harddriv_sound_board_device::driversnd_dsp_io_map(address_map &map) { map(0, 0).r(this, FUNC(harddriv_sound_board_device::hdsnddsp_rom_r)).w(this, FUNC(harddriv_sound_board_device::hdsnddsp_dac_w)); map(1, 1).r(this, FUNC(harddriv_sound_board_device::hdsnddsp_comram_r)); map(2, 2).r(this, FUNC(harddriv_sound_board_device::hdsnddsp_compare_r)); map(1, 2).nopw(); map(3, 3).w(this, FUNC(harddriv_sound_board_device::hdsnddsp_comport_w)); map(4, 4).w(this, FUNC(harddriv_sound_board_device::hdsnddsp_mute_w)); map(5, 5).w(this, FUNC(harddriv_sound_board_device::hdsnddsp_gen68kirq_w)); map(6, 7).w(this, FUNC(harddriv_sound_board_device::hdsnddsp_soundaddr_w)); } //------------------------------------------------- // device_add_mconfig - add device configuration //------------------------------------------------- MACHINE_CONFIG_START(harddriv_sound_board_device::device_add_mconfig) /* basic machine hardware */ MCFG_DEVICE_ADD("soundcpu", M68000, XTAL(16'000'000)/2) MCFG_DEVICE_PROGRAM_MAP(driversnd_68k_map) MCFG_DEVICE_ADD("latch", LS259, 0) // 80R MCFG_ADDRESSABLE_LATCH_Q0_OUT_CB(WRITELINE(*this, harddriv_sound_board_device, speech_write_w)) // SPWR - 5220 write strobe MCFG_ADDRESSABLE_LATCH_Q1_OUT_CB(WRITELINE(*this, harddriv_sound_board_device, speech_reset_w)) // SPRES - 5220 hard reset MCFG_ADDRESSABLE_LATCH_Q2_OUT_CB(WRITELINE(*this, harddriv_sound_board_device, speech_rate_w)) // SPRATE MCFG_ADDRESSABLE_LATCH_Q3_OUT_CB(WRITELINE(*this, harddriv_sound_board_device, cram_enable_w)) // CRAMEN MCFG_ADDRESSABLE_LATCH_Q4_OUT_CB(INPUTLINE("sounddsp", INPUT_LINE_HALT)) MCFG_DEVCB_INVERT // RES320 MCFG_ADDRESSABLE_LATCH_Q7_OUT_CB(WRITELINE(*this, harddriv_sound_board_device, led_w)) MCFG_DEVICE_ADD("sounddsp", TMS32010, XTAL(20'000'000)) MCFG_DEVICE_PROGRAM_MAP(driversnd_dsp_program_map) /* Data Map is internal to the CPU */ MCFG_DEVICE_IO_MAP(driversnd_dsp_io_map) MCFG_TMS32010_BIO_IN_CB(READLINE(*this, harddriv_sound_board_device, hdsnddsp_get_bio)) /* sound hardware */ SPEAKER(config, "speaker").front_center(); MCFG_DEVICE_ADD("dac", AM6012, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.5) // ls374d.75e + ls374d.90e + am6012 MCFG_DEVICE_ADD("vref", VOLTAGE_REGULATOR, 0) MCFG_VOLTAGE_REGULATOR_OUTPUT(5.0) MCFG_SOUND_ROUTE(0, "dac", 1.0, DAC_VREF_POS_INPUT) MCFG_SOUND_ROUTE(0, "dac", -1.0, DAC_VREF_NEG_INPUT) MACHINE_CONFIG_END