diff options
Diffstat (limited to 'src/mame/audio/segausb.cpp')
-rw-r--r-- | src/mame/audio/segausb.cpp | 859 |
1 files changed, 859 insertions, 0 deletions
diff --git a/src/mame/audio/segausb.cpp b/src/mame/audio/segausb.cpp new file mode 100644 index 00000000000..59e1c226854 --- /dev/null +++ b/src/mame/audio/segausb.cpp @@ -0,0 +1,859 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles +/*************************************************************************** + + segausb.cpp + + Sega Universal Sound Board. + +***************************************************************************/ + +#include "emu.h" +#include "segausb.h" +#include "nl_segausb.h" + +#include "includes/segag80r.h" +#include "includes/segag80v.h" + +#include <cmath> + + +#define VERBOSE 0 +#include "logmacro.h" + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define USB_MASTER_CLOCK 6000000 +#define USB_2MHZ_CLOCK (USB_MASTER_CLOCK/3) +#define USB_PCS_CLOCK (USB_2MHZ_CLOCK/2) +#define USB_GOS_CLOCK (USB_2MHZ_CLOCK/16/4) +#define MM5837_CLOCK 100000 + + +/*************************************************************************** + UNIVERSAL SOUND BOARD +***************************************************************************/ + +DEFINE_DEVICE_TYPE(SEGAUSB, usb_sound_device, "segausb", "Sega Universal Sound Board") + +usb_sound_device::usb_sound_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) + : device_t(mconfig, type, tag, owner, clock), + device_mixer_interface(mconfig, *this), + m_in_latch(0), + m_out_latch(0), + m_last_p2_value(0), + m_program_ram(*this, "pgmram"), + m_work_ram(*this, "workram"), + m_work_ram_bank(0), + m_t1_clock(0), + m_t1_clock_mask(0), + m_ourcpu(*this, "ourcpu"), + m_maincpu(*this, finder_base::DUMMY_TAG), +#if (ENABLE_SEGAUSB_NETLIST) + m_pit(*this, "pit_%u", 0), + m_nl_dac0(*this, "sound_nl:dac0_%u", 0), + m_nl_sel0(*this, "sound_nl:sel0"), + m_nl_pit0_out(*this, "sound_nl:pit0_out%u", 0), + m_nl_dac1(*this, "sound_nl:dac1_%u", 0), + m_nl_sel1(*this, "sound_nl:sel1"), + m_nl_pit1_out(*this, "sound_nl:pit1_out%u", 0), + m_nl_dac2(*this, "sound_nl:dac2_%u", 0), + m_nl_sel2(*this, "sound_nl:sel2"), + m_nl_pit2_out(*this, "sound_nl:pit2_out%u", 0), + m_gos_clock(0) +#else + m_stream(nullptr), + m_noise_shift(0), + m_noise_state(0), + m_noise_subcount(0) +#endif +{ +} + +usb_sound_device::usb_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : usb_sound_device(mconfig, SEGAUSB, tag, owner, clock) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void usb_sound_device::device_start() +{ + // register for save states + save_item(NAME(m_in_latch)); + save_item(NAME(m_out_latch)); + save_item(NAME(m_last_p2_value)); + save_item(NAME(m_work_ram_bank)); + save_item(NAME(m_t1_clock)); + +#if (ENABLE_SEGAUSB_NETLIST) + + for (int index = 0; index < 3; index++) + { + m_pit[index]->write_gate0(1); + m_pit[index]->write_gate1(1); + m_pit[index]->write_gate2(1); + } + save_item(NAME(m_gos_clock)); + +#else + + m_stream = stream_alloc(0, 1, USB_2MHZ_CLOCK); + + m_noise_shift = 0x15555; + + for (timer8253 &group : m_timer_group) + { + group.chan_filter[0].configure(10e3, 1e-6); + group.chan_filter[1].configure(10e3, 1e-6); + group.gate1.configure(100e3, 0.01e-6); + group.gate2.configure(2 * 100e3, 0.01e-6); + } + + g80_filter_state temp; + temp.configure(100e3, 0.01e-6); + m_gate_rc1_exp[0] = temp.exponent; + temp.configure(1e3, 0.01e-6); + m_gate_rc1_exp[1] = temp.exponent; + temp.configure(2 * 100e3, 0.01e-6); + m_gate_rc2_exp[0] = temp.exponent; + temp.configure(2 * 1e3, 0.01e-6); + m_gate_rc2_exp[1] = temp.exponent; + + m_noise_filters[0].configure(2.7e3 + 2.7e3, 1.0e-6); + m_noise_filters[1].configure(2.7e3 + 1e3, 0.30e-6); + m_noise_filters[2].configure(2.7e3 + 270, 0.15e-6); + m_noise_filters[3].configure(2.7e3 + 0, 0.082e-6); + m_noise_filters[4].configure(33e3, 0.1e-6); + + m_final_filter.configure(100e3, 4.7e-6); + + for (int tgroup = 0; tgroup < 3; tgroup++) + { + timer8253 &group = m_timer_group[tgroup]; + for (int tchan = 0; tchan < 3; tchan++) + { + timer8253::channel &channel = group.chan[tchan]; + save_item(NAME(channel.holding), tgroup * 3 + tchan); + save_item(NAME(channel.latchmode), tgroup * 3 + tchan); + save_item(NAME(channel.latchtoggle), tgroup * 3 + tchan); + save_item(NAME(channel.clockmode), tgroup * 3 + tchan); + save_item(NAME(channel.bcdmode), tgroup * 3 + tchan); + save_item(NAME(channel.output), tgroup * 3 + tchan); + save_item(NAME(channel.lastgate), tgroup * 3 + tchan); + save_item(NAME(channel.gate), tgroup * 3 + tchan); + save_item(NAME(channel.subcount), tgroup * 3 + tchan); + save_item(NAME(channel.count), tgroup * 3 + tchan); + save_item(NAME(channel.remain), tgroup * 3 + tchan); + } + save_item(NAME(group.env), tgroup); + save_item(NAME(group.chan_filter[0].capval), tgroup); + save_item(NAME(group.chan_filter[1].capval), tgroup); + save_item(NAME(group.gate1.capval), tgroup); + save_item(NAME(group.gate2.capval), tgroup); + save_item(NAME(group.config), tgroup); + } + + save_item(NAME(m_timer_mode)); + save_item(NAME(m_noise_shift)); + save_item(NAME(m_noise_state)); + save_item(NAME(m_noise_subcount)); + save_item(NAME(m_final_filter.capval)); + save_item(NAME(m_noise_filters[0].capval)); + save_item(NAME(m_noise_filters[1].capval)); + save_item(NAME(m_noise_filters[2].capval)); + save_item(NAME(m_noise_filters[3].capval)); + save_item(NAME(m_noise_filters[4].capval)); + +#endif +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void usb_sound_device::device_reset() +{ + // halt the USB CPU at reset time + m_ourcpu->set_input_line(INPUT_LINE_RESET, ASSERT_LINE); + + // start the clock timer + m_t1_clock_mask = 0x10; +} + + +/************************************* + * + * Initialization/reset + * + *************************************/ + +TIMER_DEVICE_CALLBACK_MEMBER( usb_sound_device::increment_t1_clock_timer_cb ) +{ + // only increment if it is not being forced clear + if (!(m_last_p2_value & 0x80)) + m_t1_clock++; +} + + +/************************************* + * + * External access + * + *************************************/ + +u8 usb_sound_device::status_r() +{ + LOG("%s:usb_data_r = %02X\n", machine().describe_context(), (m_out_latch & 0x81) | (m_in_latch & 0x7e)); + + m_maincpu->adjust_icount(-200); + + // only bits 0 and 7 are controlled by the I8035; the remaining + // bits 1-6 reflect the current input latch values + return (m_out_latch & 0x81) | (m_in_latch & 0x7e); +} + + +TIMER_CALLBACK_MEMBER( usb_sound_device::delayed_usb_data_w ) +{ + // look for rising/falling edges of bit 7 to control the RESET line + int data = param; + m_ourcpu->set_input_line(INPUT_LINE_RESET, (data & 0x80) ? ASSERT_LINE : CLEAR_LINE); + + // if the CLEAR line is set, the low 7 bits of the input are ignored + if ((m_last_p2_value & 0x40) == 0) + data &= ~0x7f; + + // update the effective input latch + m_in_latch = data; +} + + +void usb_sound_device::data_w(u8 data) +{ + LOG("%s:usb_data_w = %02X\n", machine().describe_context(), data); + machine().scheduler().synchronize(timer_expired_delegate(FUNC(usb_sound_device::delayed_usb_data_w), this), data); + + // boost the interleave so that sequences can be sent + machine().scheduler().boost_interleave(attotime::zero, attotime::from_usec(250)); +} + + +u8 usb_sound_device::ram_r(offs_t offset) +{ + return m_program_ram[offset]; +} + + +void usb_sound_device::ram_w(offs_t offset, u8 data) +{ + if (m_in_latch & 0x80) + m_program_ram[offset] = data; + else + LOG("%s:sega_usb_ram_w(%03X) = %02X while /LOAD disabled\n", machine().describe_context(), offset, data); +} + + + +/************************************* + * + * I8035 port accesses + * + *************************************/ + +u8 usb_sound_device::p1_r() +{ + // bits 0-6 are inputs and map to bits 0-6 of the input latch + if ((m_in_latch & 0x7f) != 0) + LOG("%s: P1 read = %02X\n", machine().describe_context(), m_in_latch & 0x7f); + return m_in_latch & 0x7f; +} + + +void usb_sound_device::p1_w(u8 data) +{ + // bit 7 maps to bit 0 on the output latch + m_out_latch = (m_out_latch & 0xfe) | (data >> 7); + LOG("%s: P1 write = %02X\n", machine().describe_context(), data); +} + + +void usb_sound_device::p2_w(u8 data) +{ + u8 old = m_last_p2_value; + m_last_p2_value = data; + + // low 2 bits control the bank of work RAM we are addressing + m_work_ram_bank = data & 3; + + // bit 6 controls the "ready" bit output to the host + // it also clears the input latch from the host (active low) + m_out_latch = ((data & 0x40) << 1) | (m_out_latch & 0x7f); + if ((data & 0x40) == 0) + m_in_latch = 0; + + // bit 7 controls the reset on the upper counter at U33 + if ((old & 0x80) && !(data & 0x80)) + m_t1_clock = 0; + + LOG("%s: P2 write -> bank=%d ready=%d clock=%d\n", machine().describe_context(), data & 3, (data >> 6) & 1, (data >> 7) & 1); +} + + +READ_LINE_MEMBER( usb_sound_device::t1_r ) +{ + // T1 returns 1 based on the value of the T1 clock; the exact + // pattern is determined by one or more jumpers on the board. + return (m_t1_clock & m_t1_clock_mask) != 0; +} + + + +/************************************* + * + * Sound generation + * + *************************************/ + +#if (!ENABLE_SEGAUSB_NETLIST) + +inline void usb_sound_device::g80_filter_state::configure(double r, double c) +{ + capval = 0.0; + exponent = 1.0 - std::exp(-1.0 / (r * c * USB_2MHZ_CLOCK)); +} + +inline void usb_sound_device::timer8253::channel::clock() +{ + u8 const old_lastgate = lastgate; + + // update the gate + lastgate = gate; + + // if we're holding, skip + if (holding) + return; + + // switch off the clock mode + switch (clockmode) + { + // oneshot; waits for trigger to restart + case 1: + if (!old_lastgate && gate) + { + output = 0; + remain = count; + } + else + { + if (--remain == 0) + output = 1; + } + break; + + // square wave: counts down by 2 and toggles output + case 3: + remain = (remain - 1) & ~1; + if (remain == 0) + { + output ^= 1; + remain = count; + } + break; + } +} + + +/************************************* + * + * USB timer and envelope controls + * + *************************************/ + +void usb_sound_device::timer_w(int which, u8 offset, u8 data) +{ + timer8253 &group = m_timer_group[which]; + + m_stream->update(); + + // switch off the offset + switch (offset) + { + case 0: + case 1: + case 2: + { + timer8253::channel &ch = group.chan[offset]; + bool was_holding = ch.holding; + + // based on the latching mode + switch (ch.latchmode) + { + case 1: // low word only + ch.count = data; + ch.holding = false; + break; + + case 2: // high word only + ch.count = data << 8; + ch.holding = false; + break; + + case 3: // low word followed by high word + if (ch.latchtoggle == 0) + { + ch.count = (ch.count & 0xff00) | (data & 0x00ff); + ch.latchtoggle = 1; + } + else + { + ch.count = (ch.count & 0x00ff) | (data << 8); + ch.holding = false; + ch.latchtoggle = 0; + } + break; + } + + // if we're not holding, load the initial count for some modes + if (was_holding && !ch.holding) + ch.remain = 1; + break; + } + + case 3: + // break out the components + if (((data & 0xc0) >> 6) < 3) + { + timer8253::channel &ch = group.chan[(data & 0xc0) >> 6]; + + // extract the bits + ch.holding = true; + ch.latchmode = (data >> 4) & 3; + ch.clockmode = (data >> 1) & 7; + ch.bcdmode = (data >> 0) & 1; + ch.latchtoggle = 0; + ch.output = (ch.clockmode == 1); + } + break; + } +} + + +void usb_sound_device::env_w(int which, u8 offset, u8 data) +{ + timer8253 &group = m_timer_group[which]; + + m_stream->update(); + + if (offset < 3) + group.env[offset] = double(data); + else + group.config = data & 1; +} + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void usb_sound_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + stream_sample_t *dest = outputs[0]; + + // iterate over samples + while (samples--) + { + /*---------------- + Noise Source + ---------------- + + RC + MM5837 ---> FILTER ---> CR FILTER ---> 3.2x AMP ---> NOISE + LADDER + */ + + // update the noise source + if (m_noise_subcount-- == 0) + { + m_noise_shift = (m_noise_shift << 1) | (((m_noise_shift >> 13) ^ (m_noise_shift >> 16)) & 1); + m_noise_state = (m_noise_shift >> 16) & 1; + m_noise_subcount += USB_2MHZ_CLOCK / MM5837_CLOCK; + } + + // update the filtered noise value -- this is just an approximation to the pink noise filter + // being applied on the PCB, but it sounds pretty close + m_noise_filters[0].capval = 0.99765 * m_noise_filters[0].capval + m_noise_state * 0.0990460; + m_noise_filters[1].capval = 0.96300 * m_noise_filters[1].capval + m_noise_state * 0.2965164; + m_noise_filters[2].capval = 0.57000 * m_noise_filters[2].capval + m_noise_state * 1.0526913; + double noiseval = m_noise_filters[0].capval + m_noise_filters[1].capval + m_noise_filters[2].capval + m_noise_state * 0.1848; + + // final output goes through a CR filter; the scaling factor is arbitrary to get the noise to the + // correct relative volume + noiseval = m_noise_filters[4].step_cr(noiseval); + noiseval *= 0.075; + + // there are 3 identical groups of circuits, each with its own 8253 + double sample = 0; + for (int groupnum = 0; groupnum < 3; groupnum++) + { + timer8253 &group = m_timer_group[groupnum]; + + /*------------- + Channel 0 + ------------- + + 8253 CR AD7524 + OUT0 ---> FILTER ---> BUFFER---> VRef ---> 100k ---> mix + */ + + // channel 0 clocks with the PCS clock + if (group.chan[0].subcount-- == 0) + { + group.chan[0].subcount += USB_2MHZ_CLOCK / USB_PCS_CLOCK; + group.chan[0].gate = 1; + group.chan[0].clock(); + } + + // channel 0 is mixed in with a resistance of 100k + double chan0 = group.chan_filter[0].step_cr(group.chan[0].output) * group.env[0] * (1.0/100.0); + + /*------------- + Channel 1 + ------------- + + 8253 CR AD7524 + OUT1 ---> FILTER ---> BUFFER---> VRef ---> 100k ---> mix + */ + + // channel 1 clocks with the PCS clock + if (group.chan[1].subcount-- == 0) + { + group.chan[1].subcount += USB_2MHZ_CLOCK / USB_PCS_CLOCK; + group.chan[1].gate = 1; + group.chan[1].clock(); + } + + // channel 1 is mixed in with a resistance of 100k + double chan1 = group.chan_filter[1].step_cr(group.chan[1].output) * group.env[1] * (1.0/100.0); + + /*------------- + Channel 2 + ------------- + + If timer_mode == 0: + + SWITCHED AD7524 + NOISE ---> RC ---> 1.56x AMP ---> INVERTER ---> VRef ---> 33k ---> mix + FILTERS + + If timer mode == 1: + + AD7524 SWITCHED + NOISE ---> INVERTER ---> VRef ---> 33k ---> mix ---> INVERTER ---> RC ---> 1.56x AMP ---> finalmix + FILTERS + */ + + // channel 2 clocks with the 2MHZ clock and triggers with the GOS clock + if (group.chan[2].subcount-- == 0) + { + group.chan[2].subcount += USB_2MHZ_CLOCK / USB_GOS_CLOCK / 2; + group.chan[2].gate = !group.chan[2].gate; + } + group.chan[2].clock(); + + // the exponents for the gate filters are determined by channel 2's output + group.gate1.exponent = m_gate_rc1_exp[group.chan[2].output]; + group.gate2.exponent = m_gate_rc2_exp[group.chan[2].output]; + + // based on the envelope mode, we do one of two things with source 2 + double chan2, mix; + if (group.config == 0) + { + chan2 = group.gate2.step_rc(group.gate1.step_rc(noiseval)) * -1.56 * group.env[2] * (1.0/33.0); + mix = chan0 + chan1 + chan2; + } + else + { + chan2 = -noiseval * group.env[2] * (1.0/33.0); + mix = chan0 + chan1 + chan2; + mix = group.gate2.step_rc(group.gate1.step_rc(-mix)) * 1.56; + } + + // accumulate the sample + sample += mix; + } + + /*------------- + Final mix + ------------- + + INPUTS + EQUAL ---> 1.2x INVERTER ---> CR FILTER ---> out + WEIGHT + + */ + *dest++ = 3000 * m_final_filter.step_cr(sample); + } +} + +#endif + + + +/************************************* + * + * USB work RAM access + * + *************************************/ + +u8 usb_sound_device::workram_r(offs_t offset) +{ + offset += 256 * m_work_ram_bank; + return m_work_ram[offset]; +} + + +void usb_sound_device::workram_w(offs_t offset, u8 data) +{ + offset += 256 * m_work_ram_bank; + m_work_ram[offset] = data; + + // writes to the low 32 bytes go to various controls +#if (ENABLE_SEGAUSB_NETLIST) + + switch (offset) + { + case 0x00: // 8253 U41 + case 0x01: // 8253 U41 + case 0x02: // 8253 U41 + case 0x03: // 8253 U41 + if ((offset & 3) != 3) + printf("%s: 2.%d count=%d\n", machine().scheduler().time().as_string(), offset & 3, data); + m_pit[0]->write(offset & 3, data); + break; + + case 0x04: // ENV0 U26 + case 0x05: // ENV0 U25 + case 0x06: // ENV0 U24 + m_nl_dac0[offset & 3]->write(double(data) / 255.0); + break; + + case 0x07: // ENV0 U38B + m_nl_sel0->write(data & 1); + break; + + case 0x08: // 8253 U42 + case 0x09: // 8253 U42 + case 0x0a: // 8253 U42 + case 0x0b: // 8253 U42 + if ((offset & 3) != 3) + printf("%s: 2.%d count=%d\n", machine().scheduler().time().as_string(), offset & 3, data); + m_pit[1]->write(offset & 3, data); + break; + + case 0x0c: // ENV1 U12 + case 0x0d: // ENV1 U13 + case 0x0e: // ENV1 U14 + m_nl_dac1[offset & 3]->write(double(data) / 255.0); + break; + + case 0x0f: // ENV1 U2B + m_nl_sel1->write(data & 1); + break; + + case 0x10: // 8253 U43 + case 0x11: // 8253 U43 + case 0x12: // 8253 U43 + case 0x13: // 8253 U43 + if ((offset & 3) != 3) + printf("%s: 2.%d count=%d\n", machine().scheduler().time().as_string(), offset & 3, data); + m_pit[2]->write(offset & 3, data); + break; + + case 0x14: // ENV2 U27 + case 0x15: // ENV2 U28 + case 0x16: // ENV2 U29 + m_nl_dac2[offset & 3]->write(double(data) / 255.0); + break; + + case 0x17: // ENV2 U38B + m_nl_sel2->write(data & 1); + break; + } + +#else + + switch (offset & ~3) + { + case 0x00: // CTC0 + timer_w(0, offset & 3, data); + break; + + case 0x04: // ENV0 + env_w(0, offset & 3, data); + break; + + case 0x08: // CTC1 + timer_w(1, offset & 3, data); + break; + + case 0x0c: // ENV1 + env_w(1, offset & 3, data); + break; + + case 0x10: // CTC2 + timer_w(2, offset & 3, data); + break; + + case 0x14: // ENV2 + env_w(2, offset & 3, data); + break; + } + +#endif +} + + + +/************************************* + * + * USB address maps + * + *************************************/ + +void usb_sound_device::usb_map(address_map &map) +{ + map(0x0000, 0x0fff).ram().share("pgmram"); +} + +void usb_sound_device::usb_portmap(address_map &map) +{ + map(0x00, 0xff).rw(FUNC(usb_sound_device::workram_r), FUNC(usb_sound_device::workram_w)).share("workram"); +} + + +//------------------------------------------------- +// device_add_mconfig - add device configuration +//------------------------------------------------- + +#if (ENABLE_SEGAUSB_NETLIST) + +TIMER_DEVICE_CALLBACK_MEMBER( usb_sound_device::gos_timer ) +{ + // technically we should clock at 2x and toggle between states + // however, in practice this is just used to trigger a oneshot + // so we can halve the high frequency rate and just toggle both + // ways to initiate the countdown + m_pit[0]->write_gate2(0); + m_pit[1]->write_gate2(0); + m_pit[2]->write_gate2(0); + m_pit[0]->write_gate2(1); + m_pit[1]->write_gate2(1); + m_pit[2]->write_gate2(1); +} + +#endif + + +void usb_sound_device::device_add_mconfig(machine_config &config) +{ + // CPU for the usb board + I8035(config, m_ourcpu, USB_MASTER_CLOCK); // divide by 15 in CPU + m_ourcpu->set_addrmap(AS_PROGRAM, &usb_sound_device::usb_map); + m_ourcpu->set_addrmap(AS_IO, &usb_sound_device::usb_portmap); + m_ourcpu->p1_in_cb().set(FUNC(usb_sound_device::p1_r)); + m_ourcpu->p1_out_cb().set(FUNC(usb_sound_device::p1_w)); + m_ourcpu->p2_out_cb().set(FUNC(usb_sound_device::p2_w)); + m_ourcpu->t1_in_cb().set(FUNC(usb_sound_device::t1_r)); + + TIMER(config, "usb_timer", 0).configure_periodic( + FUNC(usb_sound_device::increment_t1_clock_timer_cb), + attotime::from_hz(USB_2MHZ_CLOCK / 256)); + +#if (ENABLE_SEGAUSB_NETLIST) + + TIMER(config, "gos_timer", 0).configure_periodic( + FUNC(usb_sound_device::gos_timer), attotime::from_hz(USB_GOS_CLOCK)); + + NETLIST_SOUND(config, "sound_nl", 48000) + .set_source(NETLIST_NAME(segausb)) + .add_route(ALL_OUTPUTS, *this, 1.0); + + // channel 0 inputs + NETLIST_ANALOG_INPUT(config, m_nl_dac0[0], "I_U26_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac0[1], "I_U25_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac0[2], "I_U24_DAC.IN"); + NETLIST_LOGIC_INPUT(config, m_nl_sel0, "I_U38B_SEL.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit0_out[0], "I_U41_OUT0.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit0_out[1], "I_U41_OUT1.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit0_out[2], "I_U41_OUT2.IN", 0); + + // channel 1 inputs + NETLIST_ANALOG_INPUT(config, m_nl_dac1[0], "I_U12_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac1[1], "I_U13_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac1[2], "I_U14_DAC.IN"); + NETLIST_LOGIC_INPUT(config, m_nl_sel1, "I_U2B_SEL.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit1_out[0], "I_U42_OUT0.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit1_out[1], "I_U42_OUT1.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit1_out[2], "I_U42_OUT2.IN", 0); + + // channel 2 inputs + NETLIST_ANALOG_INPUT(config, m_nl_dac2[0], "I_U27_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac2[1], "I_U28_DAC.IN"); + NETLIST_ANALOG_INPUT(config, m_nl_dac2[2], "I_U29_DAC.IN"); + NETLIST_LOGIC_INPUT(config, m_nl_sel2, "I_U2A_SEL.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit2_out[0], "I_U43_OUT0.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit2_out[1], "I_U43_OUT1.IN", 0); + NETLIST_LOGIC_INPUT(config, m_nl_pit2_out[2], "I_U43_OUT2.IN", 0); + + // final output + NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "OUTPUT").set_mult_offset(5000.0, 0.0); + + // configure the PIT clocks and gates + for (int index = 0; index < 3; index++) + { + PIT8253(config, m_pit[index], 0); + m_pit[index]->set_clk<0>(USB_PCS_CLOCK); + m_pit[index]->set_clk<1>(USB_PCS_CLOCK); + m_pit[index]->set_clk<2>(USB_2MHZ_CLOCK); + } + + // connect the PIT outputs to the netlist + m_pit[0]->out_handler<0>().set(m_nl_pit0_out[0], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[0]->out_handler<1>().set(m_nl_pit0_out[1], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[0]->out_handler<2>().set(m_nl_pit0_out[2], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[1]->out_handler<0>().set(m_nl_pit1_out[0], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[1]->out_handler<1>().set(m_nl_pit1_out[1], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[1]->out_handler<2>().set(m_nl_pit1_out[2], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[2]->out_handler<0>().set(m_nl_pit2_out[0], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[2]->out_handler<1>().set(m_nl_pit2_out[1], FUNC(netlist_mame_logic_input_device::write_line)); + m_pit[2]->out_handler<2>().set(m_nl_pit2_out[2], FUNC(netlist_mame_logic_input_device::write_line)); + +#endif +} + + +DEFINE_DEVICE_TYPE(SEGAUSBROM, usb_rom_sound_device, "segausbrom", "Sega Universal Sound Board with ROM") + +usb_rom_sound_device::usb_rom_sound_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) + : usb_sound_device(mconfig, SEGAUSBROM, tag, owner, clock) +{ +} + +void usb_sound_device::usb_map_rom(address_map &map) +{ + map(0x0000, 0x0fff).rom().region(":usbcpu", 0); +} + +void usb_rom_sound_device::device_add_mconfig(machine_config &config) +{ + usb_sound_device::device_add_mconfig(config); + + // CPU for the usb board + m_ourcpu->set_addrmap(AS_PROGRAM, &usb_rom_sound_device::usb_map_rom); +} |