ti99: Model the Speech Synthesizer as an own unit, with an adapter board for the PEB.

This commit is contained in:
Michael Zapf 2025-03-06 17:13:22 +01:00
parent ea2e33ad19
commit f769905a02
11 changed files with 566 additions and 247 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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)

View File

@ -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(); }

View File

@ -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);

View File

@ -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

View File

@ -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<cd2501e_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

View File

@ -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

View File

@ -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<bus::ti99::internal::ioport_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

View File

@ -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

View File

@ -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<cd2501e_device> m_vsp;
required_device<bus::ti99::internal::ioport_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