// license:BSD-3-Clause // copyright-holders:Aaron Giles /*************************************************************************** segaspeech.cpp Sega Speech board for early G-80 games. ***************************************************************************/ #include "emu.h" #include "segaspeech.h" #include "sound/sp0250.h" #include "includes/segag80r.h" #include "includes/segag80v.h" #include "audio/nl_segaspeech.h" #define VERBOSE 0 #include "logmacro.h" #define ENABLE_NETLIST_FILTERING (0) /*************************************************************************** CONSTANTS ***************************************************************************/ #define SPEECH_MASTER_CLOCK 3120000 /*************************************************************************** SPEECH BOARD ***************************************************************************/ DEFINE_DEVICE_TYPE(SEGA_SPEECH_BOARD, sega_speech_device, "sega_speech_device", "Sega Speech Sound Board") sega_speech_device::sega_speech_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) : device_t(mconfig, SEGA_SPEECH_BOARD, tag, owner, clock), device_mixer_interface(mconfig, *this), m_speech(*this, "data"), m_cpu(*this, "cpu"), m_drq(0), m_latch(0), m_t0(0), m_p2(0) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void sega_speech_device::device_start() { save_item(NAME(m_latch)); save_item(NAME(m_t0)); save_item(NAME(m_p2)); save_item(NAME(m_drq)); } /************************************* * * i8035 port accessors * *************************************/ READ_LINE_MEMBER( sega_speech_device::t0_r ) { return m_t0; } READ_LINE_MEMBER( sega_speech_device::t1_r ) { //printf("%s: t1_r=%d\n", machine().scheduler().time().as_string(), m_drq); return m_drq; } uint8_t sega_speech_device::p1_r() { return m_latch & 0x7f; } uint8_t sega_speech_device::rom_r(offs_t offset) { return m_speech->base()[0x100 * (m_p2 & 0x3f) + offset]; } void sega_speech_device::p1_w(uint8_t data) { if (!(data & 0x80)) m_t0 = 0; } void sega_speech_device::p2_w(uint8_t data) { m_p2 = data; } /************************************* * * i8035 port accessors * *************************************/ WRITE_LINE_MEMBER(sega_speech_device::drq_w) { //printf("%s: DRQ=%d\n", machine().scheduler().time().as_string(), state); m_drq = (state == ASSERT_LINE); } /************************************* * * External access * *************************************/ TIMER_CALLBACK_MEMBER( sega_speech_device::delayed_speech_w ) { int data = param; u8 old = m_latch; /* all 8 bits are latched */ m_latch = data; /* the high bit goes directly to the INT line */ m_cpu->set_input_line(INPUT_LINE_IRQ0, (data & 0x80) ? CLEAR_LINE : ASSERT_LINE); /* a clock on the high bit clocks a 1 into T0 */ if (!(old & 0x80) && (data & 0x80)) m_t0 = 1; } void sega_speech_device::data_w(uint8_t data) { machine().scheduler().synchronize(timer_expired_delegate(FUNC(sega_speech_device::delayed_speech_w), this), data); } void sega_speech_device::control_w(uint8_t data) { // bit 3 controls channel #1 of a CD4053 to enable/disable // speech output this->set_input_gain(0, BIT(data, 3) ? 1.0 : 0.0); // bit 4 controls channel #2, but the source is unconnected // bit 5 controls channel #3, which comes from off-board // and has a pot to control the incoming volume; at least on // Star Trek, this is the audio from the Universal Sound Board this->set_input_gain(1, BIT(data, 5) ? 1.0 : 0.0); LOG("Speech control = %X\n", data); } /************************************* * * Speech board address maps * *************************************/ void sega_speech_device::speech_map(address_map &map) { map(0x0000, 0x07ff).mirror(0x0800).rom(); } void sega_speech_device::speech_portmap(address_map &map) { map(0x00, 0xff).r(FUNC(sega_speech_device::rom_r)); map(0x00, 0xff).w("sp0250", FUNC(sp0250_device::write)); } /************************************* * * Speech board machine drivers * *************************************/ void sega_speech_device::device_add_mconfig(machine_config &config) { // CPU for the speech board i8035_device &audiocpu(I8035(config, "cpu", SPEECH_MASTER_CLOCK)); // divide by 15 in CPU audiocpu.set_addrmap(AS_PROGRAM, &sega_speech_device::speech_map); audiocpu.set_addrmap(AS_IO, &sega_speech_device::speech_portmap); audiocpu.p1_in_cb().set(FUNC(sega_speech_device::p1_r)); audiocpu.p1_out_cb().set(FUNC(sega_speech_device::p1_w)); audiocpu.p2_out_cb().set(FUNC(sega_speech_device::p2_w)); audiocpu.t0_in_cb().set(FUNC(sega_speech_device::t0_r)); audiocpu.t1_in_cb().set(FUNC(sega_speech_device::t1_r)); // speech chip sp0250_device &speech(SP0250(config, "sp0250", SPEECH_MASTER_CLOCK)); speech.drq().set(FUNC(sega_speech_device::drq_w)); #if (ENABLE_NETLIST_FILTERING) speech.set_pwm_mode(); speech.add_route(ALL_OUTPUTS, "sound_nl", 1.0, 0); // netlist filtering NETLIST_SOUND(config, "sound_nl", SPEECH_MASTER_CLOCK/2) .set_source(NETLIST_NAME(segaspeech)) .add_route(ALL_OUTPUTS, *this, 1.0); NETLIST_STREAM_INPUT(config, "sound_nl:cin0", 0, "I_SP0250.IN").set_mult_offset(1.0 / 32767.0, 0); NETLIST_STREAM_OUTPUT(config, "sound_nl:cout0", 0, "OUTPUT").set_mult_offset(75000.0, 0.0); #else speech.add_route(ALL_OUTPUTS, *this, 1.0); #endif }