summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/audio/leland.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mame/audio/leland.cpp')
-rw-r--r--src/mame/audio/leland.cpp880
1 files changed, 880 insertions, 0 deletions
diff --git a/src/mame/audio/leland.cpp b/src/mame/audio/leland.cpp
new file mode 100644
index 00000000000..d860cf43a2a
--- /dev/null
+++ b/src/mame/audio/leland.cpp
@@ -0,0 +1,880 @@
+// license:BSD-3-Clause
+// copyright-holders:Aaron Giles
+/***************************************************************************
+
+ Cinemat/Leland driver
+
+ Leland sound hardware
+ driver by Aaron Giles and Paul Leaman
+
+ -------------------------------------------------------------------
+
+ 1st generation sound hardware was controlled by the master Z80.
+ It drove an AY-8910/AY-8912 pair for music. It also had two DACs
+ that were driven by the video refresh. At the end of each scanline
+ there are 8-bit DAC samples that can be enabled via the output
+ ports on the AY-8910. The DACs run at a fixed frequency of 15.3kHz,
+ since they are clocked once each scanline.
+
+ -------------------------------------------------------------------
+
+ 2nd generation sound hardware was used in Redline Racer. It
+ consisted of an 80186 microcontroller driving 8 8-bit DACs. The
+ frequency of the DACs were controlled by one of 3 Intel 8254
+ programmable interval timers (PITs):
+
+ DAC number Clock source
+ ---------- -----------------
+ 0 8254 PIT 1 output 0
+ 1 8254 PIT 1 output 1
+ 2 8254 PIT 1 output 2
+ 3 8254 PIT 2 output 0
+ 4 8254 PIT 2 output 1
+ 5-7 8254 PIT 3 output 0
+
+ The clock outputs for each DAC can be read, and are polled to
+ determine when data should be updated on the chips. The 80186's
+ two DMA channels are generally used to drive the first two DACs,
+ with the remaining 6 DACs being fed manually via polling.
+
+ -------------------------------------------------------------------
+
+ 3rd generation sound hardware appeared in the football games
+ (Quarterback, AAFB) and the later games up through Pigout. This
+ variant is closely based on the Redline Racer sound system, but
+ they took out two of the DACs and replaced them with a higher
+ resolution (10-bit) DAC. The driving clocks have been rearranged
+ a bit, and the number of PITs reduced from 3 to 2:
+
+ DAC number Clock source
+ ---------- -----------------
+ 0 8254 PIT 1 output 0
+ 1 8254 PIT 1 output 1
+ 2 8254 PIT 1 output 2
+ 3 8254 PIT 2 output 0
+ 4 8254 PIT 2 output 1
+ 5 8254 PIT 2 output 2
+ 10-bit 80186 timer 0
+
+ Like the 2nd generation board, the first two DACs are driven via
+ the DMA channels, and the remaining 5 DACs are polled.
+
+ -------------------------------------------------------------------
+
+ 4th generation sound hardware showed up in Ataxx, Indy Heat, and
+ World Soccer Finals. For this variant, they removed one more PIT
+ and 3 of the 8-bit DACs, and added a YM2151 music chip and an
+ externally-fed 8-bit DAC.
+
+ DAC number Clock source
+ ---------- -----------------
+ 0 8254 PIT 1 output 0
+ 1 8254 PIT 1 output 1
+ 2 8254 PIT 1 output 2
+ 10-bit 80186 timer 0
+ ext 80186 timer 1
+
+ The externally driven DACs have registers for a start/stop address
+ and triggers to control the clocking.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "cpu/z80/z80.h"
+#include "includes/leland.h"
+
+#define LOG_COMM 0
+#define LOG_EXTERN 0
+
+/*************************************
+ *
+ * 2nd-4th generation sound
+ *
+ *************************************/
+
+WRITE_LINE_MEMBER(leland_80186_sound_device::pit0_2_w)
+{
+ set_clock_line(2, state);
+}
+
+WRITE_LINE_MEMBER(leland_80186_sound_device::pit1_0_w)
+{
+ set_clock_line(3, state);
+}
+
+WRITE_LINE_MEMBER(leland_80186_sound_device::pit1_1_w)
+{
+ set_clock_line(4, state);
+}
+
+WRITE_LINE_MEMBER(leland_80186_sound_device::pit1_2_w)
+{
+ set_clock_line(5, state);
+}
+
+WRITE_LINE_MEMBER(leland_80186_sound_device::pit2_0_w)
+{
+ set_clock_line(5, state);
+}
+
+WRITE_LINE_MEMBER(leland_80186_sound_device::i80186_tmr0_w)
+{
+ set_clock_line(6, state);
+}
+
+WRITE_LINE_MEMBER(leland_80186_sound_device::i80186_tmr1_w)
+{
+ if (state)
+ {
+ if (m_ext_active && (m_ext_start < m_ext_stop))
+ {
+ m_dac4->write_signed8(m_ext_base[m_ext_start]);
+ m_ext_start++;
+ }
+ }
+ set_clock_line(7, state);
+}
+
+static MACHINE_CONFIG_FRAGMENT( leland_80186_sound )
+ MCFG_SPEAKER_STANDARD_MONO("speaker")
+ MCFG_SOUND_ADD("dac1", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac2", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac3", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac4", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac5", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac6", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac7", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 1.00)
+
+ MCFG_DEVICE_ADD("pit0", PIT8254, 0)
+ MCFG_PIT8253_CLK0(4000000)
+ MCFG_PIT8253_OUT0_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq0_w))
+ MCFG_PIT8253_CLK1(4000000)
+ MCFG_PIT8253_OUT1_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq1_w))
+ MCFG_PIT8253_CLK2(4000000)
+ MCFG_PIT8253_OUT2_HANDLER(WRITELINE(leland_80186_sound_device, pit0_2_w))
+
+ MCFG_DEVICE_ADD("pit1", PIT8254, 0)
+ MCFG_PIT8253_CLK0(4000000)
+ MCFG_PIT8253_OUT0_HANDLER(WRITELINE(leland_80186_sound_device, pit1_0_w))
+ MCFG_PIT8253_CLK1(4000000)
+ MCFG_PIT8253_OUT1_HANDLER(WRITELINE(leland_80186_sound_device, pit1_1_w))
+ MCFG_PIT8253_CLK2(4000000)
+ MCFG_PIT8253_OUT2_HANDLER(WRITELINE(leland_80186_sound_device, pit1_2_w))
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( redline_80186_sound )
+ MCFG_SPEAKER_STANDARD_MONO("speaker")
+ MCFG_SOUND_ADD("dac1", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac2", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac3", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac4", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac5", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac6", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac7", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac8", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+
+ MCFG_DEVICE_ADD("pit0", PIT8254, 0)
+ MCFG_PIT8253_CLK0(7000000)
+ MCFG_PIT8253_OUT0_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq0_w))
+ MCFG_PIT8253_CLK1(7000000)
+ MCFG_PIT8253_OUT1_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq1_w))
+ MCFG_PIT8253_CLK2(7000000)
+ MCFG_PIT8253_OUT2_HANDLER(WRITELINE(leland_80186_sound_device, pit0_2_w))
+
+ MCFG_DEVICE_ADD("pit1", PIT8254, 0)
+ MCFG_PIT8253_CLK0(7000000)
+ MCFG_PIT8253_OUT0_HANDLER(WRITELINE(leland_80186_sound_device, pit1_0_w))
+ MCFG_PIT8253_CLK1(7000000)
+ MCFG_PIT8253_OUT1_HANDLER(WRITELINE(leland_80186_sound_device, pit1_1_w))
+ MCFG_PIT8253_CLK2(7000000)
+
+ MCFG_DEVICE_ADD("pit2", PIT8254, 0)
+ MCFG_PIT8253_CLK0(7000000)
+ MCFG_PIT8253_OUT0_HANDLER(WRITELINE(leland_80186_sound_device, pit1_2_w))
+ MCFG_PIT8253_CLK1(7000000)
+ MCFG_PIT8253_CLK2(7000000)
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( ataxx_80186_sound )
+ MCFG_SPEAKER_STANDARD_MONO("speaker")
+ MCFG_SOUND_ADD("dac1", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac2", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac3", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac4", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac7", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 1.00)
+
+ MCFG_DEVICE_ADD("pit0", PIT8254, 0)
+ MCFG_PIT8253_CLK0(4000000)
+ MCFG_PIT8253_OUT0_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq0_w))
+ MCFG_PIT8253_CLK1(4000000)
+ MCFG_PIT8253_OUT1_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq1_w))
+ MCFG_PIT8253_CLK2(4000000)
+ MCFG_PIT8253_OUT2_HANDLER(WRITELINE(leland_80186_sound_device, pit0_2_w))
+MACHINE_CONFIG_END
+
+static MACHINE_CONFIG_FRAGMENT( wsf_80186_sound )
+ MCFG_SPEAKER_STANDARD_MONO("speaker")
+ MCFG_SOUND_ADD("dac1", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac2", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac3", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac4", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.40)
+ MCFG_SOUND_ADD("dac7", DAC, 0)
+ MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 1.00)
+
+ /* sound hardware */
+ MCFG_YM2151_ADD("ymsnd", 4000000)
+ MCFG_SOUND_ROUTE(0, "speaker", 0.40)
+ MCFG_SOUND_ROUTE(1, "speaker", 0.40)
+
+ MCFG_DEVICE_ADD("pit0", PIT8254, 0)
+ MCFG_PIT8253_CLK0(4000000)
+ MCFG_PIT8253_OUT0_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq0_w))
+ MCFG_PIT8253_CLK1(4000000)
+ MCFG_PIT8253_OUT1_HANDLER(DEVWRITELINE(":audiocpu", i80186_cpu_device, drq1_w))
+ MCFG_PIT8253_CLK2(4000000)
+ MCFG_PIT8253_OUT2_HANDLER(WRITELINE(leland_80186_sound_device, pit0_2_w))
+MACHINE_CONFIG_END
+
+machine_config_constructor leland_80186_sound_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( leland_80186_sound );
+}
+
+machine_config_constructor redline_80186_sound_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( redline_80186_sound );
+}
+
+machine_config_constructor ataxx_80186_sound_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( ataxx_80186_sound );
+}
+
+machine_config_constructor wsf_80186_sound_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( wsf_80186_sound );
+}
+
+/*************************************
+ *
+ * Sound initialization
+ *
+ *************************************/
+
+void leland_80186_sound_device::device_start()
+{
+ // register for savestates
+ save_item(NAME(m_peripheral));
+ save_item(NAME(m_last_control));
+ save_item(NAME(m_clock_active));
+ save_item(NAME(m_clock_tick));
+ save_item(NAME(m_sound_command));
+ save_item(NAME(m_sound_response));
+ save_item(NAME(m_ext_start));
+ save_item(NAME(m_ext_stop));
+ save_item(NAME(m_ext_active));
+
+ // zerofill
+ m_peripheral = 0;
+ m_last_control = 0;
+ m_clock_active = 0;
+ m_clock_tick = 0;
+ m_sound_command = 0;
+ m_sound_response = 0;
+ m_ext_start = 0;
+ m_ext_stop = 0;
+ m_ext_active = 0;
+ m_ext_base = NULL;
+
+ m_audiocpu = downcast<i80186_cpu_device *>(machine().device("audiocpu"));
+
+ /* determine which sound hardware is installed */
+ if (m_type == TYPE_WSF)
+ m_ext_base = machine().root_device().memregion("dac")->base();
+}
+
+void leland_80186_sound_device::device_reset()
+{
+ m_last_control = 0xf8;
+ m_clock_active = 0;
+ m_clock_tick = 0;
+ m_ext_start = 0;
+ m_ext_stop = 0;
+ m_ext_active = 0;
+}
+
+const device_type LELAND_80186 = &device_creator<leland_80186_sound_device>;
+
+leland_80186_sound_device::leland_80186_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, LELAND_80186, "80186 DAC (Leland)", tag, owner, clock, "leland_80186_sound", __FILE__),
+ m_dac1(*this, "dac1"),
+ m_dac2(*this, "dac2"),
+ m_dac3(*this, "dac3"),
+ m_dac4(*this, "dac4"),
+ m_dac5(*this, "dac5"),
+ m_dac6(*this, "dac6"),
+ m_dac7(*this, "dac7"),
+ m_dac8(*this, "dac8"),
+ m_pit0(*this, "pit0"),
+ m_pit1(*this, "pit1"),
+ m_pit2(*this, "pit2"),
+ m_ymsnd(*this, "ymsnd")
+{
+ m_type = TYPE_LELAND;
+}
+
+leland_80186_sound_device::leland_80186_sound_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_dac1(*this, "dac1"),
+ m_dac2(*this, "dac2"),
+ m_dac3(*this, "dac3"),
+ m_dac4(*this, "dac4"),
+ m_dac5(*this, "dac5"),
+ m_dac6(*this, "dac6"),
+ m_dac7(*this, "dac7"),
+ m_dac8(*this, "dac8"),
+ m_pit0(*this, "pit0"),
+ m_pit1(*this, "pit1"),
+ m_pit2(*this, "pit2"),
+ m_ymsnd(*this, "ymsnd")
+{
+}
+
+const device_type REDLINE_80186 = &device_creator<redline_80186_sound_device>;
+
+redline_80186_sound_device::redline_80186_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : leland_80186_sound_device(mconfig, REDLINE_80186, "80186 DAC (Redline Racer)", tag, owner, clock, "redline_80186_sound", __FILE__)
+{
+ m_type = TYPE_REDLINE;
+}
+
+const device_type ATAXX_80186 = &device_creator<ataxx_80186_sound_device>;
+
+ataxx_80186_sound_device::ataxx_80186_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : leland_80186_sound_device(mconfig, REDLINE_80186, "80186 DAC (Ataxx)", tag, owner, clock, "ataxx_80186_sound", __FILE__)
+{
+ m_type = TYPE_ATAXX;
+}
+
+const device_type WSF_80186 = &device_creator<wsf_80186_sound_device>;
+
+wsf_80186_sound_device::wsf_80186_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : leland_80186_sound_device(mconfig, REDLINE_80186, "80186 DAC (WSF)", tag, owner, clock, "wsf_80186_sound", __FILE__)
+{
+ m_type = TYPE_WSF;
+}
+
+WRITE16_MEMBER(leland_80186_sound_device::peripheral_ctrl)
+{
+ switch (offset)
+ {
+ case 2:
+ m_peripheral = data;
+ break;
+
+ case 4:
+ {
+ UINT32 temp = (m_peripheral & 0xffc0) << 4;
+ if (data & 0x0040)
+ {
+ m_audiocpu->device_t::memory().space(AS_PROGRAM).install_readwrite_handler(temp, temp + 0x2ff, read16_delegate(FUNC(leland_80186_sound_device::peripheral_r), this), write16_delegate(FUNC(leland_80186_sound_device::peripheral_w), this));
+ }
+ else
+ {
+ temp &= 0xffff;
+ m_audiocpu->device_t::memory().space(AS_IO).install_readwrite_handler(temp, temp + 0x2ff, read16_delegate(FUNC(leland_80186_sound_device::peripheral_r), this), write16_delegate(FUNC(leland_80186_sound_device::peripheral_w), this));
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+/*************************************
+ *
+ * External 80186 control
+ *
+ *************************************/
+
+WRITE8_MEMBER( leland_80186_sound_device::leland_80186_control_w )
+{
+ /* see if anything changed */
+ int diff = (m_last_control ^ data) & 0xf8;
+ if (diff == 0)
+ return;
+ m_last_control = data;
+
+ if (LOG_COMM)
+ {
+ logerror("%04X:80186 control = %02X", m_audiocpu->device_t::safe_pc(), data);
+ if (!(data & 0x80)) logerror(" /RESET");
+ if (!(data & 0x40)) logerror(" ZNMI");
+ if (!(data & 0x20)) logerror(" INT0");
+ if (!(data & 0x10)) logerror(" /TEST");
+ if (!(data & 0x08)) logerror(" INT1");
+ logerror("\n");
+ }
+
+ /* /RESET */
+ m_audiocpu->device_t::execute().set_input_line(INPUT_LINE_RESET, (data & 0x80) ? CLEAR_LINE : ASSERT_LINE);
+ m_audiocpu->device_t::execute().set_input_line(INPUT_LINE_TEST, (data & 0x10) ? CLEAR_LINE : ASSERT_LINE);
+
+ /* /NMI */
+/* If the master CPU doesn't get a response by the time it's ready to send
+ the next command, it uses an NMI to force the issue; unfortunately, this
+ seems to really screw up the sound system. It turns out it's better to
+ just wait for the original interrupt to occur naturally */
+/* space.machine().device("audiocpu")->execute().set_input_line(INPUT_LINE_NMI, (data & 0x40) ? CLEAR_LINE : ASSERT_LINE);*/
+
+ /* INT0 */
+ m_audiocpu->int0_w(data & 0x20);
+ /* INT1 */
+ m_audiocpu->int1_w(data & 0x08);
+ /* handle reset here */
+ if ((diff & 0x80) && (data & 0x80))
+ reset();
+}
+
+
+
+/*************************************
+ *
+ * Sound command handling
+ *
+ *************************************/
+
+void leland_80186_sound_device::command_lo_sync(void *ptr, int param)
+{
+ if (LOG_COMM) logerror("%s:Write sound command latch lo = %02X\n", machine().describe_context(), param);
+ m_sound_command = (m_sound_command & 0xff00) | param;
+}
+
+
+WRITE8_MEMBER( leland_80186_sound_device::leland_80186_command_lo_w )
+{
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(leland_80186_sound_device::command_lo_sync), this), data);
+}
+
+
+WRITE8_MEMBER( leland_80186_sound_device::leland_80186_command_hi_w )
+{
+ if (LOG_COMM) logerror("%04X:Write sound command latch hi = %02X\n", m_audiocpu->device_t::safe_pc(), data);
+ m_sound_command = (m_sound_command & 0x00ff) | (data << 8);
+}
+
+
+READ16_MEMBER( leland_80186_sound_device::main_to_sound_comm_r )
+{
+ if (LOG_COMM) logerror("%05X:Read sound command latch = %02X\n", m_audiocpu->device_t::safe_pc(), m_sound_command);
+ return m_sound_command;
+}
+
+
+
+
+/*************************************
+ *
+ * Sound response handling
+ *
+ *************************************/
+
+void leland_80186_sound_device::delayed_response_r(void *ptr, int param)
+{
+ cpu_device *master = machine().device<cpu_device>("master");
+ int checkpc = param;
+ int pc = master->pc();
+ int oldaf = master->state_int(Z80_AF);
+
+ /* This is pretty cheesy, but necessary. Since the CPUs run in round-robin order,
+ synchronizing on the write to this register from the slave side does nothing.
+ In order to make sure the master CPU get the real response, we synchronize on
+ the read. However, the value we returned the first time around may not be
+ accurate, so after the system has synced up, we go back into the master CPUs
+ state and put the proper value into the A register. */
+ if (pc == checkpc)
+ {
+ if (LOG_COMM) logerror("(Updated sound response latch to %02X)\n", m_sound_response);
+
+ oldaf = (oldaf & 0x00ff) | (m_sound_response << 8);
+ master->set_state_int(Z80_AF, oldaf);
+ }
+ else if(LOG_COMM)
+ logerror("ERROR: delayed_response_r - current PC = %04X, checkPC = %04X\n", pc, checkpc);
+}
+
+
+READ8_MEMBER( leland_80186_sound_device::leland_80186_response_r )
+{
+ cpu_device *master = machine().device<cpu_device>("master");
+ offs_t pc = master->device_t::safe_pcbase();
+
+ if (LOG_COMM) logerror("%04X:Read sound response latch = %02X\n", pc, m_sound_response);
+
+ /* synchronize the response */
+ machine().scheduler().synchronize(timer_expired_delegate(FUNC(leland_80186_sound_device::delayed_response_r), this), pc + 2);
+ return m_sound_response;
+}
+
+
+WRITE16_MEMBER( leland_80186_sound_device::sound_to_main_comm_w )
+{
+ if (LOG_COMM) logerror("%05X:Write sound response latch = %02X\n", m_audiocpu->device_t::safe_pc(), data);
+ m_sound_response = data;
+}
+
+
+
+/*************************************
+ *
+ * Low-level DAC I/O
+ *
+ *************************************/
+
+WRITE16_MEMBER( leland_80186_sound_device::dac_w )
+{
+ int which = offset & 7;
+
+ /* handle value changes */
+ if (ACCESSING_BITS_0_7)
+ {
+ switch(which)
+ {
+ case 0:
+ m_dac1->write_signed8(data & 0xff);
+ break;
+ case 1:
+ m_dac2->write_signed8(data & 0xff);
+ break;
+ case 2:
+ m_dac3->write_signed8(data & 0xff);
+ break;
+ case 3:
+ m_dac4->write_signed8(data & 0xff);
+ break;
+ case 4:
+ m_dac5->write_signed8(data & 0xff);
+ break;
+ case 5:
+ m_dac6->write_signed8(data & 0xff);
+ break;
+ case 6:
+ m_dac7->write_signed8(data & 0xff);
+ break;
+ case 7:
+ m_dac8->write_signed8(data & 0xff);
+ break;
+ }
+ m_clock_active &= ~(1<<which);
+ }
+
+ /* handle volume changes */
+ if (ACCESSING_BITS_8_15)
+ switch(which)
+ {
+ case 0:
+ m_dac1->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ case 1:
+ m_dac2->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ case 2:
+ m_dac3->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ case 3:
+ m_dac4->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ case 4:
+ m_dac5->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ case 5:
+ m_dac6->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ case 6:
+ m_dac7->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ case 7:
+ m_dac8->set_output_gain(ALL_OUTPUTS, (data >> 8)/255.0f);
+ break;
+ }
+
+}
+
+
+WRITE16_MEMBER( redline_80186_sound_device::redline_dac_w )
+{
+ dac_w(space, (offset >> 8) & 7, (data & 0xff) | (offset << 8), 0xffff);
+}
+
+WRITE16_MEMBER( leland_80186_sound_device::ataxx_dac_control )
+{
+ /* handle common offsets */
+ switch (offset)
+ {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ if (ACCESSING_BITS_0_7)
+ dac_w(space, offset, data, 0x00ff);
+ return;
+
+ case 0x03:
+ if(ACCESSING_BITS_0_7)
+ {
+ m_dac1->set_output_gain(ALL_OUTPUTS, (((data & 7) * 0x49) >> 1) / 255.0f);
+ m_dac2->set_output_gain(ALL_OUTPUTS, ((((data >> 3) & 7) * 0x49) >> 1) / 255.0f);
+ m_dac3->set_output_gain(ALL_OUTPUTS, (((data >> 6) & 3) * 0x55) / 255.0f);
+ }
+ return;
+
+ case 0x21:
+ if (ACCESSING_BITS_0_7)
+ dac_w(space, 1, data, mem_mask);
+ return;
+
+ default:
+ break;
+ }
+
+ /* if we have a YM2151 (and an external DAC), handle those offsets */
+ if (m_type == TYPE_WSF)
+ {
+ switch (offset)
+ {
+ case 0x04:
+ m_ext_active = 1;
+ if (LOG_EXTERN) logerror("External DAC active\n");
+ return;
+
+ case 0x05:
+ m_ext_active = 0;
+ if (LOG_EXTERN) logerror("External DAC inactive\n");
+ return;
+
+ case 0x06:
+ m_ext_start >>= 4;
+ COMBINE_DATA(&m_ext_start);
+ m_ext_start <<= 4;
+ if (LOG_EXTERN) logerror("External DAC start = %05X\n", m_ext_start);
+ return;
+
+ case 0x07:
+ m_ext_stop >>= 4;
+ COMBINE_DATA(&m_ext_stop);
+ m_ext_stop <<= 4;
+ if (LOG_EXTERN) logerror("External DAC stop = %05X\n", m_ext_stop);
+ return;
+
+ default:
+ break;
+ }
+ }
+ logerror("%05X:Unexpected peripheral write %d/%02X = %02X\n", m_audiocpu->device_t::safe_pc(), 5, offset, data);
+}
+
+
+
+/*************************************
+ *
+ * Peripheral chip dispatcher
+ *
+ *************************************/
+
+READ16_MEMBER( leland_80186_sound_device::peripheral_r )
+{
+ int select = offset / 0x40;
+ offset &= 0x3f;
+
+ switch (select)
+ {
+ case 0:
+ /* we have to return 0 periodically so that they handle interrupts */
+ //if ((++m_clock_tick & 7) == 0)
+ // return 0;
+
+ /* if we've filled up all the active channels, we can give this CPU a reset */
+ /* until the next interrupt */
+ if (m_type != TYPE_REDLINE)
+ return ((m_clock_active >> 1) & 0x3e);
+ else
+ return ((m_clock_active << 1) & 0x7e);
+
+ case 1:
+ return main_to_sound_comm_r(space, offset, mem_mask);
+
+ case 2:
+ if (mem_mask != 0xff00)
+ return m_pit0->read(space, offset & 3);
+ break;
+
+ case 3:
+ if (m_type <= TYPE_REDLINE)
+ {
+ if (mem_mask != 0xff00)
+ return m_pit1->read(space, offset & 3);
+ }
+ else if (m_type == TYPE_WSF)
+ return m_ymsnd->read(space, offset);
+ break;
+
+ case 4:
+ if (m_type == TYPE_REDLINE)
+ {
+ if (mem_mask != 0xff00)
+ return m_pit2->read(space, offset & 3);
+ }
+ else
+ logerror("%05X:Unexpected peripheral read %d/%02X\n", m_audiocpu->device_t::safe_pc(), select, offset*2);
+ break;
+
+ default:
+ logerror("%05X:Unexpected peripheral read %d/%02X\n", m_audiocpu->device_t::safe_pc(), select, offset*2);
+ break;
+ }
+ return 0xffff;
+}
+
+
+WRITE16_MEMBER( leland_80186_sound_device::peripheral_w )
+{
+ int select = offset / 0x40;
+ offset &= 0x3f;
+
+ switch (select)
+ {
+ case 1:
+ sound_to_main_comm_w(space, offset, data, mem_mask);
+ break;
+
+ case 2:
+ if (mem_mask != 0xff00)
+ m_pit0->write(space, offset & 3, data);
+ break;
+
+ case 3:
+ if (m_type <= TYPE_REDLINE)
+ {
+ if (mem_mask != 0xff00)
+ m_pit1->write(space, offset & 3, data);
+ }
+ else if(m_type == TYPE_WSF)
+ m_ymsnd->write(space, offset, data);
+ break;
+
+ case 4:
+ if (m_type == TYPE_REDLINE)
+ {
+ if (mem_mask != 0xff00)
+ m_pit2->write(space, offset & 3, data);
+ }
+ else if (mem_mask == 0xffff)
+ {
+ m_dac7->write_signed16(data << 6);
+ m_clock_active &= ~(1<<6);
+ }
+ break;
+
+ case 5: /* Ataxx/WSF/Indy Heat only */
+ if (m_type > TYPE_REDLINE)
+ ataxx_dac_control(space, offset, data, mem_mask);
+ break;
+
+ default:
+ logerror("%05X:Unexpected peripheral write %d/%02X = %02X\n", m_audiocpu->device_t::safe_pc(), select, offset, data);
+ break;
+ }
+}
+
+
+
+/*************************************
+ *
+ * Game-specific handlers
+ *
+ *************************************/
+
+WRITE8_MEMBER( leland_80186_sound_device::ataxx_80186_control_w )
+{
+ /* compute the bit-shuffled variants of the bits and then write them */
+ int modified = ((data & 0x01) << 7) |
+ ((data & 0x02) << 5) |
+ ((data & 0x04) << 3) |
+ ((data & 0x08) << 1);
+ leland_80186_control_w(space, offset, modified);
+}
+
+
+
+/*************************************
+ *
+ * Sound CPU memory handlers
+ *
+ *************************************/
+
+ADDRESS_MAP_START( leland_80186_map_program, AS_PROGRAM, 16, driver_device )
+ AM_RANGE(0x00000, 0x03fff) AM_MIRROR(0x1c000) AM_RAM
+ AM_RANGE(0x20000, 0xfffff) AM_ROM
+ADDRESS_MAP_END
+
+ADDRESS_MAP_START( ataxx_80186_map_io, AS_IO, 16, driver_device )
+ADDRESS_MAP_END
+
+ADDRESS_MAP_START( redline_80186_map_io, AS_IO, 16, driver_device )
+ AM_RANGE(0x0000, 0xffff) AM_DEVWRITE("custom", redline_80186_sound_device, redline_dac_w)
+ADDRESS_MAP_END
+
+
+ADDRESS_MAP_START( leland_80186_map_io, AS_IO, 16, driver_device )
+ AM_RANGE(0x0000, 0xffff) AM_DEVWRITE("custom", leland_80186_sound_device, dac_w)
+ADDRESS_MAP_END
+
+
+/************************************************************************
+
+Memory configurations:
+
+ Redline Racer:
+ FFDF7:80186 upper chip select = E03C -> E0000-FFFFF, 128k long
+ FFDF7:80186 lower chip select = 00FC -> 00000-00FFF, 4k long
+ FFDF7:80186 peripheral chip select = 013C -> 01000, 01080, 01100, 01180, 01200, 01280, 01300
+ FFDF7:80186 middle chip select = 81FC -> 80000-C0000, 64k chunks, 256k total
+ FFDF7:80186 middle P chip select = A0FC
+
+ Quarterback, Team Quarterback, AAFB, Super Offroad, Track Pack, Pigout, Viper:
+ FFDFA:80186 upper chip select = E03C -> E0000-FFFFF, 128k long
+ FFDFA:80186 peripheral chip select = 203C -> 20000, 20080, 20100, 20180, 20200, 20280, 20300
+ FFDFA:80186 middle chip select = 01FC -> 00000-7FFFF, 128k chunks, 512k total
+ FFDFA:80186 middle P chip select = C0FC
+
+ Ataxx, Indy Heat, World Soccer Finals:
+ FFD9D:80186 upper chip select = E03C -> E0000-FFFFF, 128k long
+ FFD9D:80186 peripheral chip select = 043C -> 04000, 04080, 04100, 04180, 04200, 04280, 04300
+ FFD9D:80186 middle chip select = 01FC -> 00000-7FFFF, 128k chunks, 512k total
+ FFD9D:80186 middle P chip select = C0BC
+
+************************************************************************/