diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 407757e6371..7826a2f84ea 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -4176,8 +4176,8 @@ if (BUSES["TI99"]~=null) then MAME_DIR .. "src/devices/bus/ti99/peb/scsicard.h", MAME_DIR .. "src/devices/bus/ti99/peb/sidmaster.cpp", MAME_DIR .. "src/devices/bus/ti99/peb/sidmaster.h", - MAME_DIR .. "src/devices/bus/ti99/peb/spchsyn.cpp", - MAME_DIR .. "src/devices/bus/ti99/peb/spchsyn.h", + MAME_DIR .. "src/devices/bus/ti99/peb/speechadapter.cpp", + MAME_DIR .. "src/devices/bus/ti99/peb/speechadapter.h", MAME_DIR .. "src/devices/bus/ti99/peb/ti_32kmem.cpp", MAME_DIR .. "src/devices/bus/ti99/peb/ti_32kmem.h", MAME_DIR .. "src/devices/bus/ti99/peb/ti_fdc.cpp", @@ -4192,6 +4192,8 @@ if (BUSES["TI99"]~=null) then MAME_DIR .. "src/devices/bus/ti99/peb/tn_usbsm.h", MAME_DIR .. "src/devices/bus/ti99/sidecar/arcturus.cpp", MAME_DIR .. "src/devices/bus/ti99/sidecar/arcturus.h", + MAME_DIR .. "src/devices/bus/ti99/sidecar/speechsyn.cpp", + MAME_DIR .. "src/devices/bus/ti99/sidecar/speechsyn.h", } end diff --git a/src/devices/bus/ti99/internal/datamux.cpp b/src/devices/bus/ti99/internal/datamux.cpp index d87b2df75e6..8359e213d8d 100644 --- a/src/devices/bus/ti99/internal/datamux.cpp +++ b/src/devices/bus/ti99/internal/datamux.cpp @@ -205,6 +205,9 @@ void datamux_device::setaddress_all(uint16_t addr) // Cartridge ROM bool iscartrom = ((addr & 0xe000)==0x6000); + // Speech (1001 0wxx xxxx xxx0) + bool sbe = ((addr & 0xf801)==0x9000) && validaccess; + // Always deliver to GROM so that the select line may be cleared line_state gsq = isgrom? ASSERT_LINE : CLEAR_LINE; if (isgrom) m_grom_idle = false; @@ -228,6 +231,7 @@ void datamux_device::setaddress_all(uint16_t addr) // I/O port gets all accesses m_memen_state = ASSERT_LINE; m_ioport->memen_in(m_memen_state); + m_ioport->sbe(sbe); m_ioport->setaddress_dbin(addr, m_dbin); } diff --git a/src/devices/bus/ti99/internal/ioport.cpp b/src/devices/bus/ti99/internal/ioport.cpp index 5ca4f680e0a..43d083c5797 100644 --- a/src/devices/bus/ti99/internal/ioport.cpp +++ b/src/devices/bus/ti99/internal/ioport.cpp @@ -81,6 +81,7 @@ #include "splitter.h" #include "bus/ti99/peb/peribox.h" #include "bus/ti99/sidecar/arcturus.h" +#include "bus/ti99/sidecar/speechsyn.h" DEFINE_DEVICE_TYPE(TI99_IOPORT, bus::ti99::internal::ioport_device, "ti99_ioport", "TI-99 I/O Port") @@ -149,6 +150,12 @@ void ioport_device::reset_in(int state) m_connected->reset_in(state); } +void ioport_device::sbe(int state) +{ + if (m_connected != nullptr) + m_connected->sbe(state); +} + void ioport_device::device_start() { if (m_connected != nullptr) @@ -178,6 +185,7 @@ void ti99_ioport_options_plain(device_slot_interface &device) device.option_add("peb", TI99_PERIBOX); device.option_add("splitter", TI99_IOSPLIT); device.option_add("arcturus", TI99_ARCTURUS); + device.option_add("speechsyn", TI99_SPEECHSYN); } void ti99_ioport_options_evpc(device_slot_interface &device) @@ -185,6 +193,7 @@ void ti99_ioport_options_evpc(device_slot_interface &device) device.option_add("peb", TI99_PERIBOX_EV); device.option_add("splitter", TI99_IOSPLIT); device.option_add("arcturus", TI99_ARCTURUS); + device.option_add("speechsyn", TI99_SPEECHSYN); } // Used for the splitter (to avoid getting multiple EVPCs in the system) diff --git a/src/devices/bus/ti99/internal/ioport.h b/src/devices/bus/ti99/internal/ioport.h index 867ff6dccf4..0da1d977621 100644 --- a/src/devices/bus/ti99/internal/ioport.h +++ b/src/devices/bus/ti99/internal/ioport.h @@ -38,6 +38,7 @@ public: virtual void msast_in(int state) { } virtual void clock_in(int state) { } virtual void reset_in(int state) { } + virtual void sbe(int state) { } void set_ioport(ioport_device* ioport) { m_ioport = ioport; } @@ -80,6 +81,7 @@ public: void msast_in(int state); void clock_in(int state); void reset_in(int state); + void sbe(int state); // Callbacks auto extint_cb() { return m_console_extint.bind(); } diff --git a/src/devices/bus/ti99/peb/peribox.cpp b/src/devices/bus/ti99/peb/peribox.cpp index d89293c3b6d..55de5eccc2c 100644 --- a/src/devices/bus/ti99/peb/peribox.cpp +++ b/src/devices/bus/ti99/peb/peribox.cpp @@ -192,7 +192,7 @@ CRUCLK* 51||52 DBIN #include "evpc.h" #include "hsgpl.h" #include "ti_rs232.h" -#include "spchsyn.h" +#include "speechadapter.h" #include "memex.h" #include "horizon.h" #include "forti.h" @@ -484,7 +484,7 @@ void peribox_device::device_config_complete() void peribox_common_slots(device_slot_interface &device) { device.option_add("tirs232", TI99_RS232); - device.option_add("speech", TI99_SPEECH); + device.option_add("speechadapter", TI99_SPEECHADAPTER); device.option_add("horizon", TI99_HORIZON); device.option_add("ide", TI99_IDE); device.option_add("usbsm", TI99_USBSM); diff --git a/src/devices/bus/ti99/peb/spchsyn.cpp b/src/devices/bus/ti99/peb/spchsyn.cpp deleted file mode 100644 index 0cf577dad86..00000000000 --- a/src/devices/bus/ti99/peb/spchsyn.cpp +++ /dev/null @@ -1,188 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/**************************************************************************** - - TI-99 Speech synthesizer - - We emulate the Speech Synthesizer plugged onto a P-Box adapter. The original - Speech Synthesizer device was provided as a box to be plugged into the - right side of the console. In order to be used with Geneve and SGCPU, the - speech synthesizer must be moved into the Peripheral Box. - - The Speech Synthesizer used for the TI was the CD2501E, AKA TMS5200, - (internal name TMC0285), a predecessor of the TMS5220 which was used in - other commercial products. - - Note that this adapter also contains the speech roms. - - Michael Zapf - - February 2012: Rewritten as class - -*****************************************************************************/ - -#include "emu.h" -#include "spchsyn.h" - -#include "speaker.h" - -#define LOG_WARN (1U << 1) // Warnings -#define LOG_CONFIG (1U << 2) -#define LOG_MEM (1U << 3) -#define LOG_ADDR (1U << 4) -#define LOG_READY (1U << 5) - -#define VERBOSE (LOG_CONFIG | LOG_WARN) -#include "logmacro.h" - -DEFINE_DEVICE_TYPE(TI99_SPEECH, bus::ti99::peb::ti_speech_synthesizer_device, "ti99_speech", "TI-99 Speech synthesizer (on adapter card)") - -namespace bus::ti99::peb { - -/****************************************************************************/ - -ti_speech_synthesizer_device::ti_speech_synthesizer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : - device_t(mconfig, TI99_SPEECH, tag, owner, clock), - device_ti99_peribox_card_interface(mconfig, *this), - m_vsp(*this, "vsp"), - m_reading(false), - m_sbe(false), - m_dec_high(false) -{ -} - -/* - Memory read -*/ - -void ti_speech_synthesizer_device::readz(offs_t offset, uint8_t *value) -{ - if (machine().side_effects_disabled()) return; - - if (m_sbe) - { - *value = m_vsp->status_r() & 0xff; - LOGMASKED(LOG_MEM, "read value = %02x\n", *value); - // We should clear the lines at this point. The TI-99/4A clears the - // lines by setting the address bus to a different value, but the - // Geneve may behave differently. This may not 100% reflect the real - // situation, but it ensures a safe processing. - m_vsp->combined_rsq_wsq_w(~0); - } -} - -/* - Memory write -*/ -void ti_speech_synthesizer_device::write(offs_t offset, uint8_t data) -{ - if (machine().side_effects_disabled()) return; - - if (m_sbe) - { - LOGMASKED(LOG_MEM, "write value = %02x\n", data); - m_vsp->data_w(data); - // Note that we must NOT clear the lines here. Find the lines in the - // READY callback below. - } -} - -void ti_speech_synthesizer_device::setaddress_dbin(offs_t offset, int state) -{ - // 1001 00xx xxxx xxx0 DBIN=1 - // 1001 01xx xxxx xxx0 DBIN=0 - // 1111 1000 0000 0001 mask - m_reading = (state==ASSERT_LINE); - - bool valid = (((offset & 0x0400)==0) == m_reading); - - if (m_dec_high) - m_sbe = ((offset & 0x7f801)==0x79000) && valid; - else - m_sbe = ((offset & 0x0f801)==0x09000) && valid; - - if (m_sbe) - { - LOGMASKED(LOG_ADDR, "set address = %04x, dbin = %d\n", offset, state); - - // Caution: In the current tms5220 emulation, care must be taken - // to clear one line before asserting the other line, or otherwise - // both RS* and WS* are active, which is illegal. - // Alternatively, we'll use the combined settings method - - m_vsp->combined_rsq_wsq_w(m_reading ? ~tms5220_device::RS : ~tms5220_device::WS); - } - else - // If other address, turn off RS* and WS* (negative logic!) - m_vsp->combined_rsq_wsq_w(~0); -} - -/****************************************************************************/ - -void ti_speech_synthesizer_device::speech_ready(int state) -{ - // The TMS5200 implementation uses true/false, not ASSERT/CLEAR semantics - // and we have to adapt a /READY to a READY line. - // The real synthesizer board uses a transistor for that purpose. - m_slot->set_ready((state==0)? ASSERT_LINE : CLEAR_LINE); - LOGMASKED(LOG_READY, "READY = %d\n", (state==0)); - - if ((state==0) && !m_reading) - // Clear the lines only when we are done with writing. - m_vsp->combined_rsq_wsq_w(~0); -} - -void ti_speech_synthesizer_device::device_start() -{ - save_item(NAME(m_reading)); - save_item(NAME(m_sbe)); -} - -void ti_speech_synthesizer_device::device_reset() -{ - m_reading = false; - m_sbe = false; - m_dec_high = (ioport("AMADECODE")->read()!=0); -} - -ROM_START( ti99_speech ) - ROM_REGION(0x8000, "vsm", 0) - ROM_LOAD("cd2325a.u2a", 0x0000, 0x4000, CRC(1f58b571) SHA1(0ef4f178716b575a1c0c970c56af8a8d97561ffe)) // at location u2, bottom of stack - ROM_LOAD("cd2326a.u2b", 0x4000, 0x4000, CRC(65d00401) SHA1(a367242c2c96cebf0e2bf21862f3f6734b2b3020)) // at location u2, top of stack -ROM_END - -void ti_speech_synthesizer_device::device_add_mconfig(machine_config& config) -{ - SPEAKER(config, "speech_out").front_center(); - CD2501E(config, m_vsp, 640000L); - - m_vsp->ready_cb().set(FUNC(ti_speech_synthesizer_device::speech_ready)); - m_vsp->add_route(ALL_OUTPUTS, "speech_out", 0.50); - - TMS6100(config, "vsm", 0); - m_vsp->m0_cb().set("vsm", FUNC(tms6100_device::m0_w)); - m_vsp->m1_cb().set("vsm", FUNC(tms6100_device::m1_w)); - m_vsp->addr_cb().set("vsm", FUNC(tms6100_device::add_w)); - m_vsp->data_cb().set("vsm", FUNC(tms6100_device::data_line_r)); - m_vsp->romclk_cb().set("vsm", FUNC(tms6100_device::clk_w)); -} - -INPUT_PORTS_START( ti99_speech ) - PORT_START( "AMADECODE" ) - PORT_CONFNAME( 0x01, 0x01, "Decode AMA/AMB/AMC lines" ) - PORT_CONFSETTING( 0x00, DEF_STR( Off )) - PORT_CONFSETTING( 0x01, DEF_STR( On )) -INPUT_PORTS_END - -ioport_constructor ti_speech_synthesizer_device::device_input_ports() const -{ - return INPUT_PORTS_NAME(ti99_speech); -} - -const tiny_rom_entry *ti_speech_synthesizer_device::device_rom_region() const -{ - return ROM_NAME( ti99_speech ); -} - -} // end namespace bus::ti99::peb - diff --git a/src/devices/bus/ti99/peb/spchsyn.h b/src/devices/bus/ti99/peb/spchsyn.h deleted file mode 100644 index ff9c3f237e6..00000000000 --- a/src/devices/bus/ti99/peb/spchsyn.h +++ /dev/null @@ -1,55 +0,0 @@ -// license:LGPL-2.1+ -// copyright-holders:Michael Zapf -/**************************************************************************** - - TI-99 Speech Synthesizer - See spchsyn.c for documentation - - Michael Zapf, October 2010 - February 2012: Rewritten as class - -*****************************************************************************/ - -#ifndef MAME_BUS_TI99_PEB_SPCHSYN_H -#define MAME_BUS_TI99_PEB_SPCHSYN_H - -#pragma once - -#include "peribox.h" -#include "sound/tms5220.h" -#include "machine/tms6100.h" - -namespace bus::ti99::peb { - -class ti_speech_synthesizer_device : public device_t, public device_ti99_peribox_card_interface -{ -public: - ti_speech_synthesizer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); - void readz(offs_t offset, uint8_t *value) override; - void write(offs_t offset, uint8_t data) override; - void setaddress_dbin(offs_t offset, int state) override; - - void crureadz(offs_t offset, uint8_t *value) override { } - void cruwrite(offs_t offset, uint8_t data) override { } - -protected: - virtual void device_start() override; - virtual void device_reset() override; - virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD; - virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; - virtual ioport_constructor device_input_ports() const override ATTR_COLD; - -private: - void speech_ready(int state); - - required_device m_vsp; - bool m_reading; - bool m_sbe; // Signal "Speech block enable" - bool m_dec_high; // Decode the AMA/AMB/ABC address lines -}; - -} // end namespace bus::ti99::peb - -DECLARE_DEVICE_TYPE_NS(TI99_SPEECH, bus::ti99::peb, ti_speech_synthesizer_device) - -#endif // MAME_BUS_TI99_PEB_SPCHSYN_H diff --git a/src/devices/bus/ti99/peb/speechadapter.cpp b/src/devices/bus/ti99/peb/speechadapter.cpp new file mode 100644 index 00000000000..778465d0eb3 --- /dev/null +++ b/src/devices/bus/ti99/peb/speechadapter.cpp @@ -0,0 +1,158 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + TI-99 Speech synthesizer adapter device + + This adapter card made the sidecar Speech Synthesizer unit available to + systems without a TI-99/4A console, like the SGCPU and the Geneve. The + board must be removed from the sidecar unit and plugged on this simple + adapter board, which is then put in a slot of the Peripheral Expansion Box. + + +-------+ + +-----|-------|---+ + | Speech syn | + | board | + | _________ | + +-----+++++++++---+ + +----------------------------+|||||||+------------+ + | ------- | + | Adapter board | + | | + (((o LED PEB slot connector | + +--------------|||||||||||||||||||||||||----------+ + ||||||||||||||||||||||||| + + Technical detail: + + The SBE signal (Speech Block Enable), which is generated in the TI console, + is not forwarded to the PEB. One of the tasks of this board is thus to + decode the mapped addresses once more and to activate the synthesizer + accordingly. + + A second issue for Geneve users is that the address extension bits (AMA, + AMB, AMC) need to be decoded as well to avoid the synthesizer interfering + with other memory access. This can be activated in the configuration. The + default is on. + + Michael Zapf + March 2025 + +*****************************************************************************/ + +#include "emu.h" +#include "speechadapter.h" + +#include "speaker.h" +#include "bus/ti99/sidecar/speechsyn.h" + +#define LOG_WARN (1U << 1) // Warnings +#define LOG_CONFIG (1U << 2) +#define LOG_MEM (1U << 3) +#define LOG_ADDR (1U << 4) +#define LOG_READY (1U << 5) + +#define VERBOSE (LOG_CONFIG | LOG_WARN) +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(TI99_SPEECHADAPTER, bus::ti99::peb::ti_speechsyn_adapter_device, "ti99_speechconn", "TI-99 Speech synthesizer adapter card") + +namespace bus::ti99::peb { + +#define PORT "conn" + +/****************************************************************************/ + +ti_speechsyn_adapter_device::ti_speechsyn_adapter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, TI99_SPEECHADAPTER, tag, owner, clock), + device_ti99_peribox_card_interface(mconfig, *this), + m_port(*this, PORT), + m_dec_high(false) +{ +} + +void ti_speechsyn_adapter_device::readz(offs_t offset, uint8_t *value) +{ + if (m_port != nullptr) + { + LOGMASKED(LOG_MEM, "read %06x\n", offset); + m_port->readz(offset, value); + } +} + +void ti_speechsyn_adapter_device::write(offs_t offset, uint8_t data) +{ + if (m_port != nullptr) + { + LOGMASKED(LOG_MEM, "write %06x\n", offset); + m_port->write(offset, data); + } +} + +void ti_speechsyn_adapter_device::setaddress_dbin(offs_t offset, int state) +{ + // Valid access = not(DBIN and A5) + bool reading = (state==ASSERT_LINE); + + // An access is valid when reading from 9000 and writing to 9400. + bool valid = (((offset & 0x0400)==0) == reading); + bool sbe = false; + + // Recreate the SBE signal that is only available at the I/O port of the console + if (m_dec_high) + // We need to decode the AMA/AMB/AMC address extension lines + sbe = ((offset & 0x7f801)==0x79000) && valid; + else + // No need to decode the extension lines + sbe = ((offset & 0x0f801)==0x09000) && valid; + + if (sbe) LOGMASKED(LOG_ADDR, "set address = %04x, dbin = %d\n", offset, state); + + if (m_port != nullptr) + { + m_port->sbe(sbe); + m_port->setaddress_dbin(offset, state); + } +} + +void ti_speechsyn_adapter_device::ready(int state) +{ + LOGMASKED(LOG_READY, "Incoming READY=%d from synthesizer %d\n", state); + m_slot->set_ready(state); +} + +void ti_speechsyn_adapter_device::device_start() +{ +} + +void ti_speechsyn_adapter_device::device_reset() +{ + m_dec_high = (ioport("AMADECODE")->read()!=0); + LOGMASKED(LOG_CONFIG, "Speech adapter%s decoding the AMA/B/C lines.\n", m_dec_high? "" : " not"); +} + +void ti_speechsyn_adapter_options(device_slot_interface &device) +{ + device.option_add("speechsyn", TI99_SPEECHSYN); +} + +void ti_speechsyn_adapter_device::device_add_mconfig(machine_config& config) +{ + TI99_IOPORT(config, m_port, 0, ti_speechsyn_adapter_options, "speechsyn"); + m_port->ready_cb().set(FUNC(ti_speechsyn_adapter_device::ready)); +} + +INPUT_PORTS_START( ti99_speechadapter ) + PORT_START( "AMADECODE" ) + PORT_CONFNAME( 0x01, 0x01, "Decode AMA/AMB/AMC lines" ) + PORT_CONFSETTING( 0x00, DEF_STR( Off )) + PORT_CONFSETTING( 0x01, DEF_STR( On )) +INPUT_PORTS_END + +ioport_constructor ti_speechsyn_adapter_device::device_input_ports() const +{ + return INPUT_PORTS_NAME(ti99_speechadapter); +} + +} // end namespace bus::ti99::peb + diff --git a/src/devices/bus/ti99/peb/speechadapter.h b/src/devices/bus/ti99/peb/speechadapter.h new file mode 100644 index 00000000000..5846b595c5d --- /dev/null +++ b/src/devices/bus/ti99/peb/speechadapter.h @@ -0,0 +1,49 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + TI-99 Speech Synthesizer connector adapter + for the Peripheral Expansion Box + + Michael Zapf, March 2025 + +*****************************************************************************/ + +#ifndef MAME_BUS_TI99_PEB_SPCHADPT_H +#define MAME_BUS_TI99_PEB_SPCHADPT_H + +#pragma once + +#include "peribox.h" +#include "bus/ti99/internal/ioport.h" + +namespace bus::ti99::peb { + +class ti_speechsyn_adapter_device : public device_t, public device_ti99_peribox_card_interface +{ +public: + ti_speechsyn_adapter_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void readz(offs_t offset, uint8_t *value) override; + void write(offs_t offset, uint8_t data) override; + void setaddress_dbin(offs_t offset, int state) override; + void crureadz(offs_t offset, uint8_t *value) override { } + void cruwrite(offs_t offset, uint8_t data) override { } + +protected: + virtual void device_start() override; + virtual void device_reset() override; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + virtual ioport_constructor device_input_ports() const override ATTR_COLD; + +private: + required_device m_port; + bool m_dec_high; + + void ready(int state); +}; + +} // end namespace bus::ti99::peb + +DECLARE_DEVICE_TYPE_NS(TI99_SPEECHADAPTER, bus::ti99::peb, ti_speechsyn_adapter_device) + +#endif // MAME_BUS_TI99_PEB_SPCHADPT_H diff --git a/src/devices/bus/ti99/sidecar/speechsyn.cpp b/src/devices/bus/ti99/sidecar/speechsyn.cpp new file mode 100644 index 00000000000..1f55bcc348e --- /dev/null +++ b/src/devices/bus/ti99/sidecar/speechsyn.cpp @@ -0,0 +1,273 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + TI-99 Speech synthesizer + + This is the emulation Speech Synthesizer, which plugs into the I/O port of + the TI console. Even though the sidecar expansion concept was largely + abandoned by the introduction of the Peripheral Expansion Box with the 4A, + the Speech Synthesizer was sold as a sidecar expansion until the end. + + Typical setup: + Speech Synthesizer + sidecar + v + +----------------+---+---------- + | TI-99/4(A) | | PEB connection cable + +------------+---+ +---------- + | oooooooooo | |---+ + | oooooooooo | | + +----------------- + + Also as a common modification, users removed the board inside the sidecar + and placed it on an adapter to go into the PEB and thus to become + available for the Geneve or SGCPU. See bus/ti99/peb/speechadapter.cpp. + + The sidecar offers a flippable lid where vocabulary expansion modules were + supposed to be plugged in, but those have never seen daylight. In most of + the units, including mine, no connector can be found under the lid, so the + decision to drop this idea must have come early. + + Technical details: + + The Voice Synthesis Processor (VSP) used for the TI Speech Synthesizer + is the CD2501E, aka TMS5200 (internal name TMC0285), a predecessor of the + TMS5220 which was used in many other commercial products. + Two TMS6100 circuits hold the standard vocabulary, mainly used from Extended + Basic programs. + + The interaction with the TMS5200 relies completely on the READY* + line; the INT* line is not connected. + + The VSP delivers a READY* signal, which needs to be inverted for the rest + of the TI system. The board of the Speech Synthesizer uses a simple + transistor for this purpose. + + Michael Zapf + March 2025 + +*****************************************************************************/ + +#include "emu.h" +#include "speechsyn.h" +#include "speaker.h" + +#define LOG_WARN (1U << 1) // Warnings +#define LOG_CONFIG (1U << 2) +#define LOG_MEM (1U << 3) +#define LOG_ADDR (1U << 4) +#define LOG_READY (1U << 5) + +#define VERBOSE (LOG_GENERAL | LOG_WARN) + +#include "logmacro.h" + +DEFINE_DEVICE_TYPE(TI99_SPEECHSYN, bus::ti99::sidecar::ti_speech_synthesizer_device, "ti99_speech", "TI-99 Speech Synthesizer") + +namespace bus::ti99::sidecar { + +#define VSP "tms5200" +#define PORT "extport" + +/* + Constructor called from subclasses. +*/ +ti_speech_synthesizer_device::ti_speech_synthesizer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : bus::ti99::internal::ioport_attached_device(mconfig, TI99_SPEECHSYN, tag, owner, clock), + m_vsp(*this, VSP), + m_port(*this, PORT), + m_reading(false), + m_sbe(CLEAR_LINE), + m_ext_ready(ASSERT_LINE), + m_ssyn_ready(ASSERT_LINE) +{ +} + +/* + Memory read +*/ + +void ti_speech_synthesizer_device::readz(offs_t offset, uint8_t *value) +{ + if (m_sbe) + { + if (machine().side_effects_disabled()) return; + + *value = m_vsp->status_r() & 0xff; + LOGMASKED(LOG_MEM, "read value = %02x\n", *value); + // We should clear the lines at this point. The TI-99/4A clears the + // lines by setting the address bus to a different value, but the + // Geneve may behave differently. This may not 100% reflect the real + // situation, but it ensures a safe processing. + m_vsp->combined_rsq_wsq_w(~0); + } + + // Pass through to the external port + if (m_port != nullptr) + m_port->readz(offset, value); +} +/* + Memory write +*/ +void ti_speech_synthesizer_device::write(offs_t offset, uint8_t data) +{ + if (m_sbe) + { + if (machine().side_effects_disabled()) return; + LOGMASKED(LOG_MEM, "write value = %02x\n", data); + m_vsp->data_w(data); + // Note that we must NOT clear the lines here. Find the lines in the + // READY callback below. + } + + // Pass through to the external port + if (m_port != nullptr) + m_port->write(offset, data); +} + +void ti_speech_synthesizer_device::setaddress_dbin(offs_t offset, int state) +{ + if (m_sbe) + { + m_reading = (state==ASSERT_LINE); + LOGMASKED(LOG_ADDR, "set address = %04x, dbin = %d\n", offset, state); + + // Caution: In the current tms5220 emulation, care must be taken + // to clear one line before asserting the other line, or otherwise + // both RS* and WS* are active, which is illegal. + // Alternatively, we'll use the combined settings method + + m_vsp->combined_rsq_wsq_w(m_reading ? ~tms5220_device::RS : ~tms5220_device::WS); + } + else + { + // If other address, turn off RS* and WS* (negative logic!) + m_vsp->combined_rsq_wsq_w(~0); + + // Pass through to the external port + if (m_port != nullptr) + m_port->setaddress_dbin(offset, state); + } +} + +void ti_speech_synthesizer_device::sbe(int state) +{ + m_sbe = (state==ASSERT_LINE); + // Not forwarded to the external port +} + +void ti_speech_synthesizer_device::crureadz(offs_t offset, uint8_t *value) +{ + // Pass through to the external port + if (m_port != nullptr) + m_port->crureadz(offset, value); +} + +void ti_speech_synthesizer_device::cruwrite(offs_t offset, uint8_t data) +{ + // Pass through to the external port + if (m_port != nullptr) + m_port->cruwrite(offset, data); +} + +void ti_speech_synthesizer_device::memen_in(int state) +{ + // Pass through to the external port + if (m_port != nullptr) + m_port->memen_in(state); +} + +void ti_speech_synthesizer_device::msast_in(int state) +{ + // Pass through to the external port + if (m_port != nullptr) + m_port->msast_in(state); +} + +void ti_speech_synthesizer_device::clock_in(int state) +{ + // Pass through to the external port + if (m_port != nullptr) + m_port->clock_in(state); +} + +void ti_speech_synthesizer_device::reset_in(int state) +{ + // Pass through to the external port + if (m_port != nullptr) + m_port->reset_in(state); +} + +/* + Forward the incoming interrupt to the console +*/ +void ti_speech_synthesizer_device::extint(int state) +{ + set_extint(state); +} + +void ti_speech_synthesizer_device::extready(int state) +{ + LOGMASKED(LOG_READY, "Incoming READY=%d from external port\n", state); + m_ext_ready = (line_state)state; + set_ready(m_ext_ready & m_ssyn_ready); +} + +void ti_speech_synthesizer_device::speech_ready(int state) +{ + // Invert the READY* signal + m_ssyn_ready = (state==0)? ASSERT_LINE : CLEAR_LINE; + LOGMASKED(LOG_READY, "SSyn READY = %d\n", (state==0)); + + if ((state==0) && !m_reading) + // Clear the lines only when we are done with writing. + m_vsp->combined_rsq_wsq_w(~0); + + set_ready(m_ext_ready & m_ssyn_ready); +} + +void ti_speech_synthesizer_device::device_start() +{ + save_item(NAME(m_reading)); + save_item(NAME(m_sbe)); +} + +void ti_speech_synthesizer_device::device_reset() +{ + m_reading = false; + m_sbe = CLEAR_LINE; +} + +ROM_START( ti99_speech ) + ROM_REGION(0x8000, "vsm", 0) + ROM_LOAD("cd2325a.u2a", 0x0000, 0x4000, CRC(1f58b571) SHA1(0ef4f178716b575a1c0c970c56af8a8d97561ffe)) // at location u2, bottom of stack + ROM_LOAD("cd2326a.u2b", 0x4000, 0x4000, CRC(65d00401) SHA1(a367242c2c96cebf0e2bf21862f3f6734b2b3020)) // at location u2, top of stack +ROM_END + +void ti_speech_synthesizer_device::device_add_mconfig(machine_config& config) +{ + SPEAKER(config, "speech_out").front_center(); + CD2501E(config, m_vsp, 640000L); + + m_vsp->ready_cb().set(FUNC(ti_speech_synthesizer_device::speech_ready)); + m_vsp->add_route(ALL_OUTPUTS, "speech_out", 0.50); + + TMS6100(config, "vsm", 0); + m_vsp->m0_cb().set("vsm", FUNC(tms6100_device::m0_w)); + m_vsp->m1_cb().set("vsm", FUNC(tms6100_device::m1_w)); + m_vsp->addr_cb().set("vsm", FUNC(tms6100_device::add_w)); + m_vsp->data_cb().set("vsm", FUNC(tms6100_device::data_line_r)); + m_vsp->romclk_cb().set("vsm", FUNC(tms6100_device::clk_w)); + + TI99_IOPORT(config, m_port, 0, ti99_ioport_options_evpc1, nullptr); + m_port->extint_cb().set(FUNC(ti_speech_synthesizer_device::extint)); + m_port->ready_cb().set(FUNC(ti_speech_synthesizer_device::extready)); +} + +const tiny_rom_entry *ti_speech_synthesizer_device::device_rom_region() const +{ + return ROM_NAME( ti99_speech ); +} + +} // end namespace bus::ti99::sidecar \ No newline at end of file diff --git a/src/devices/bus/ti99/sidecar/speechsyn.h b/src/devices/bus/ti99/sidecar/speechsyn.h new file mode 100644 index 00000000000..fab42d09aa0 --- /dev/null +++ b/src/devices/bus/ti99/sidecar/speechsyn.h @@ -0,0 +1,65 @@ +// license:LGPL-2.1+ +// copyright-holders:Michael Zapf +/**************************************************************************** + + Speech Synthesizer sidecar device + Michael Zapf + +*****************************************************************************/ + +#ifndef MAME_BUS_TI99_SIDECAR_SPEECHSYN_H +#define MAME_BUS_TI99_SIDECAR_SPEECHSYN_H + +#pragma once + +#include "bus/ti99/internal/ioport.h" +#include "sound/tms5220.h" +#include "machine/tms6100.h" + +namespace bus::ti99::sidecar { + +class ti_speech_synthesizer_device : public bus::ti99::internal::ioport_attached_device +{ +public: + ti_speech_synthesizer_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + void readz(offs_t offset, uint8_t *value) override; + void write(offs_t offset, uint8_t data) override; + void setaddress_dbin(offs_t offset, int state) override; + void sbe(int state) override; + + void crureadz(offs_t offset, uint8_t *value) override; + void cruwrite(offs_t offset, uint8_t data) override; + + void memen_in(int state) override; + void msast_in(int state) override; + + void clock_in(int state) override; + void reset_in(int state) override; + +protected: + virtual void device_start() override ATTR_COLD; + virtual void device_reset() override ATTR_COLD; + virtual const tiny_rom_entry *device_rom_region() const override ATTR_COLD; + virtual void device_add_mconfig(machine_config &config) override ATTR_COLD; + + // Callbacks from the external port + void extint(int state); + void extready(int state); + +private: + required_device m_vsp; + required_device m_port; + + void speech_ready(int state); + + bool m_reading; + bool m_sbe; + line_state m_ext_ready; + line_state m_ssyn_ready; +}; + +} // end namespace bus::ti99::internal + +DECLARE_DEVICE_TYPE_NS(TI99_SPEECHSYN, bus::ti99::sidecar, ti_speech_synthesizer_device) + +#endif // MAME_BUS_TI99_SIDECAR_SPEECHSYN_H