mirror of
https://github.com/holub/mame
synced 2025-04-18 22:49:58 +03:00
ti99: Model the Speech Synthesizer as an own unit, with an adapter board for the PEB.
This commit is contained in:
parent
ea2e33ad19
commit
f769905a02
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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(); }
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
158
src/devices/bus/ti99/peb/speechadapter.cpp
Normal file
158
src/devices/bus/ti99/peb/speechadapter.cpp
Normal 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
|
||||
|
49
src/devices/bus/ti99/peb/speechadapter.h
Normal file
49
src/devices/bus/ti99/peb/speechadapter.h
Normal 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
|
273
src/devices/bus/ti99/sidecar/speechsyn.cpp
Normal file
273
src/devices/bus/ti99/sidecar/speechsyn.cpp
Normal 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
|
65
src/devices/bus/ti99/sidecar/speechsyn.h
Normal file
65
src/devices/bus/ti99/sidecar/speechsyn.h
Normal 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
|
Loading…
Reference in New Issue
Block a user