diff options
Diffstat (limited to 'src/devices/bus/coco/coco_ssc.cpp')
-rw-r--r-- | src/devices/bus/coco/coco_ssc.cpp | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/src/devices/bus/coco/coco_ssc.cpp b/src/devices/bus/coco/coco_ssc.cpp new file mode 100644 index 00000000000..d3bd20fd9af --- /dev/null +++ b/src/devices/bus/coco/coco_ssc.cpp @@ -0,0 +1,562 @@ +// license:BSD-3-Clause +// copyright-holders:tim lindner +/*************************************************************************** + + coco_ssc.cpp + + Code for emulating the CoCo Speech / Sound Cartridge + + The SSC was a complex sound cartridge. A TMS-7040 microcontroller, + SPO256-AL2 speech processor, AY-3-8913 programable sound generator, and + 2K of RAM. + + All four ports of the microcontroller are under software control. + + Port A is input from the host CPU. + Port B is A0-A7 for the 2k of RAM. + Port C is the internal bus controller: + bit 7 6 5 4 3 2 1 0 + | | | | | | | | + | | | | | | | + A8 for RAM and BC1 of AY3-8913 + | | | | | | +-- A9 for RAM + | | | | | +---- A10 for RAM + | | | | +------ R/W* for RAM and BDIR for AY3-8913 + | | | +-------- CS* for RAM + | | +---------- ALD* for SP0256 + | +------------ CS* for AY3-8913 + +-------------- BUSY* for host CPU (connects to a latch) + * – Active low + Port D is the 8-bit data bus. + +***************************************************************************/ + +#include "emu.h" +#include "sound/ay8910.h" +#include "sound/sp0256.h" +#include "machine/netlist.h" +#include "netlist/devices/net_lib.h" +#include "cpu/tms7000/tms7000.h" +#include "speaker.h" +#include "cococart.h" +#include "machine/ram.h" + +#define LOG_SSC 0 +#define PIC_TAG "pic7040" +#define AY_TAG "cocossc_ay" +#define SP0256_TAG "sp0256" + +#define SP0256_GAIN 1.75 +#define AY8913_GAIN 2.0 + +#define C_A8 0x01 +#define C_BC1 0x01 +#define C_A9 0x02 +#define C_A10 0x04 +#define C_RRW 0x08 +#define C_BDR 0x08 +#define C_RCS 0x10 +#define C_ALD 0x20 +#define C_ACS 0x40 +#define C_BSY 0x80 + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +namespace +{ + class cocossc_sac_device; + + // ======================> coco_ssc_device + + class coco_ssc_device : + public device_t, + public device_cococart_interface + { + public: + // construction/destruction + coco_ssc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + + // optional information overrides + virtual const tiny_rom_entry *device_rom_region() const override; + virtual void device_add_mconfig(machine_config &config) override; + virtual void device_reset() override; + + DECLARE_READ8_MEMBER(ssc_port_a_r); + DECLARE_WRITE8_MEMBER(ssc_port_b_w); + DECLARE_READ8_MEMBER(ssc_port_c_r); + DECLARE_WRITE8_MEMBER(ssc_port_c_w); + DECLARE_READ8_MEMBER(ssc_port_d_r); + DECLARE_WRITE8_MEMBER(ssc_port_d_w); + + protected: + // device-level overrides + virtual void device_start() override; + virtual DECLARE_READ8_MEMBER(ff7d_read); + virtual DECLARE_WRITE8_MEMBER(ff7d_write); + virtual void set_sound_enable(bool sound_enable) override; + private: + uint8_t m_reset_line; + bool m_host_busy; + uint8_t m_tms7000_porta; + uint8_t m_tms7000_portb; + uint8_t m_tms7000_portc; + uint8_t m_tms7000_portd; + required_device<cpu_device> m_tms7040; + required_device<ram_device> m_staticram; + required_device<ay8910_device> m_ay; + required_device<sp0256_device> m_spo; + required_device<cocossc_sac_device> m_sac; + }; + + // ======================> Color Computer Sound Activity Circuit filter + + class cocossc_sac_device : public device_t, + public device_sound_interface + { + public: + cocossc_sac_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + ~cocossc_sac_device() { } + bool sound_activity_circuit_output(); + + protected: + // device-level overrides + virtual void device_start() override; + + // sound stream update overrides + virtual void sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) override; + + private: + sound_stream* m_stream; + double m_rms[16]; + int m_index; + }; +}; + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +DEFINE_DEVICE_TYPE(COCO_SSC, coco_ssc_device, "coco_ssc", "CoCo S/SC PAK"); +DEFINE_DEVICE_TYPE(COCOSSC_SAC, cocossc_sac_device, "cocossc_sac", "CoCo SSC Sound Activity Circuit"); + + +//************************************************************************** +// MACHINE FRAGMENTS AND ADDRESS MAPS +//************************************************************************** + +static ADDRESS_MAP_START(ssc_io_map, AS_IO, 8, coco_ssc_device) + AM_RANGE(TMS7000_PORTA, TMS7000_PORTA) AM_READ(ssc_port_a_r) + AM_RANGE(TMS7000_PORTB, TMS7000_PORTB) AM_WRITE(ssc_port_b_w) + AM_RANGE(TMS7000_PORTC, TMS7000_PORTC) AM_READWRITE(ssc_port_c_r, ssc_port_c_w) + AM_RANGE(TMS7000_PORTD, TMS7000_PORTD) AM_READWRITE(ssc_port_d_r, ssc_port_d_w) +ADDRESS_MAP_END + +static ADDRESS_MAP_START(ssc_rom, AS_PROGRAM, 8, coco_ssc_device) + AM_RANGE(0xf000, 0xffff) AM_ROM AM_REGION(PIC_TAG, 0) +ADDRESS_MAP_END + +MACHINE_CONFIG_MEMBER(coco_ssc_device::device_add_mconfig) + MCFG_CPU_ADD(PIC_TAG, TMS7040, DERIVED_CLOCK(1, 2)) + MCFG_CPU_PROGRAM_MAP(ssc_rom) + MCFG_CPU_IO_MAP(ssc_io_map) + + MCFG_RAM_ADD("staticram") + MCFG_RAM_DEFAULT_SIZE("2K") + MCFG_RAM_DEFAULT_VALUE(0x00) + + MCFG_SPEAKER_STANDARD_MONO("ssc_audio") + + MCFG_SOUND_ADD(SP0256_TAG, SP0256, XTAL_3_12MHz) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "ssc_audio", SP0256_GAIN) + MCFG_SP0256_DATA_REQUEST_CB(INPUTLINE(PIC_TAG, TMS7000_INT1_LINE)) + + MCFG_SOUND_ADD(AY_TAG, AY8913, DERIVED_CLOCK(1, 2)) + MCFG_AY8910_OUTPUT_TYPE(AY8910_SINGLE_OUTPUT) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "coco_sac_tag", AY8913_GAIN) + + MCFG_DEVICE_ADD("coco_sac_tag", COCOSSC_SAC, DERIVED_CLOCK(1, 2)) + MCFG_SOUND_ROUTE(ALL_OUTPUTS, "ssc_audio", 1.0) +MACHINE_CONFIG_END + +ROM_START(coco_ssc) + ROM_REGION(0x1000, PIC_TAG, 0) + ROM_LOAD("pic-7040-510.bin", 0x0000, 0x1000, CRC(a8e2eb98) SHA1(7c17dcbc21757535ce0b3a9e1ce5ca61319d3606)) // pic7040 cpu rom + ROM_REGION(0x10000, SP0256_TAG, 0) + ROM_LOAD("sp0256-al2.bin", 0x1000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc)) +ROM_END + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// coco_ssc_device - constructor +//------------------------------------------------- + +coco_ssc_device::coco_ssc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, COCO_SSC, tag, owner, clock), + device_cococart_interface(mconfig, *this ), + m_tms7040(*this, PIC_TAG), + m_staticram(*this, "staticram"), + m_ay(*this, AY_TAG), + m_spo(*this, SP0256_TAG), + m_sac(*this, "coco_sac_tag") +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void coco_ssc_device::device_start() +{ + // install $FF7D-E handler + write8_delegate wh = write8_delegate(FUNC(coco_ssc_device::ff7d_write), this); + read8_delegate rh = read8_delegate(FUNC(coco_ssc_device::ff7d_read), this); + install_readwrite_handler(0xFF7D, 0xFF7E, rh, wh); + + save_item(NAME(m_reset_line)); + save_item(NAME(m_host_busy)); + save_item(NAME(m_tms7000_porta)); + save_item(NAME(m_tms7000_portb)); + save_item(NAME(m_tms7000_portc)); + save_item(NAME(m_tms7000_portd)); +} + + +//------------------------------------------------- +// device_reset - device-specific reset +//------------------------------------------------- + +void coco_ssc_device::device_reset() +{ + m_reset_line = 0; + m_host_busy = false; +} + + +//------------------------------------------------- +// rom_region - device-specific ROM region +//------------------------------------------------- + +const tiny_rom_entry *coco_ssc_device::device_rom_region() const +{ + return ROM_NAME( coco_ssc ); +} + + +//------------------------------------------------- +// set_sound_enable +//------------------------------------------------- + +void coco_ssc_device::set_sound_enable(bool sound_enable) +{ + if( sound_enable ) + { + m_sac->set_output_gain(0, 1.0); + m_spo->set_output_gain(0, 1.0); + } + else + { + m_sac->set_output_gain(0, 0.0); + m_spo->set_output_gain(0, 0.0); + } +} + +//------------------------------------------------- +// ff7d_read +//------------------------------------------------- + +READ8_MEMBER(coco_ssc_device::ff7d_read) +{ + uint8_t data = 0xff; + + switch(offset) + { + case 0x00: + data = 0xff; + + if (LOG_SSC) + { + logerror( "[%s] ff7d read: %02x\n", machine().describe_context(), data ); + } + break; + + case 0x01: + data = 0x1f; + + if( m_host_busy == false ) + { + data |= 0x80; + } + + if( m_spo->sby_r() ) + { + data |= 0x40; + } + + if( ! m_sac->sound_activity_circuit_output() ) + { + data |= 0x20; + } + + if (LOG_SSC) + { + logerror( "[%s] ff7e read: %c%c%c%c %c%c%c%c (%02x)\n", + machine().describe_context(), + data & 0x80 ? '.' : 'B', + data & 0x40 ? '.' : 'S', + data & 0x20 ? '.' : 'P', + data & 0x10 ? '.' : '1', + data & 0x08 ? '.' : '1', + data & 0x04 ? '.' : '1', + data & 0x02 ? '.' : '1', + data & 0x01 ? '.' : '1', + data ); + } + break; + } + + return data; +} + + +//------------------------------------------------- +// ff7d_write +//------------------------------------------------- + +WRITE8_MEMBER(coco_ssc_device::ff7d_write) +{ + switch(offset) + { + case 0x00: + if (LOG_SSC) + { + logerror( "[%s] ff7d write: %02x\n", machine().describe_context(), data ); + } + + if( (m_reset_line & 1) == 1 ) + { + if( (data & 1) == 0 ) + { + m_tms7040->reset(); + m_ay->reset(); + m_spo->reset(); + m_host_busy = false; + } + } + + m_reset_line = data; + break; + + case 0x01: + + if (LOG_SSC) + { + logerror( "[%s] ff7e write: %02x\n", machine().describe_context(), data ); + } + + m_tms7000_porta = data; + m_host_busy = true; + m_tms7040->set_input_line(TMS7000_INT3_LINE, ASSERT_LINE); + break; + } +} + + +//------------------------------------------------- +// Handlers for secondary CPU ports +//------------------------------------------------- + +READ8_MEMBER(coco_ssc_device::ssc_port_a_r) +{ + if (LOG_SSC) + { + logerror( "[%s] port a read: %02x\n", machine().describe_context(), m_tms7000_porta ); + } + + m_tms7040->set_input_line(TMS7000_INT3_LINE, CLEAR_LINE); + + return m_tms7000_porta; +} + +WRITE8_MEMBER(coco_ssc_device::ssc_port_b_w) +{ + if (LOG_SSC) + { + logerror( "[%s] port b write: %02x\n", machine().describe_context(), data ); + } + + m_tms7000_portb = data; +} + +READ8_MEMBER(coco_ssc_device::ssc_port_c_r) +{ + if (LOG_SSC) + { + logerror( "[%s] port c read: %02x\n", machine().describe_context(), m_tms7000_portc ); + } + + return m_tms7000_portc; +} + +WRITE8_MEMBER(coco_ssc_device::ssc_port_c_w) +{ + if( (data & C_RCS) == 0 && (data & C_RRW) == 0) /* static RAM write */ + { + uint16_t address = (uint16_t)data << 8; + address += m_tms7000_portb; + address &= 0x7ff; + + m_staticram->write(address, m_tms7000_portd); + } + + if( (data & C_ACS) == 0 ) /* chip select for AY-3-8913 */ + { + if( (data & (C_BDR|C_BC1)) == (C_BDR|C_BC1) ) /* BDIR = 1, BC1 = 1: latch address */ + { + m_ay->address_w(space, 0, m_tms7000_portd); + } + + if( ((data & C_BDR) == C_BDR) && ((data & C_BC1) == 0) ) /* BDIR = 1, BC1 = 0: write data */ + { + m_ay->data_w(space, 0, m_tms7000_portd); + } + } + + if( (data & C_ALD) == 0 ) + { + m_spo->ald_w(space, 0, m_tms7000_portd); + } + + if( (data & C_BSY) == 0 ) + { + m_host_busy = false; + } + + if (LOG_SSC) + { + logerror( "[%s] port c write: %c%c%c%c %c%c%c%c (%02x)\n", + machine().describe_context(), + data & 0x80 ? '.' : 'B', + data & 0x40 ? '.' : 'P', + data & 0x20 ? '.' : 'V', + data & 0x10 ? '.' : 'R', + data & 0x40 ? (data & 0x08 ? 'R' : 'W') : (data & 0x08 ? 'D' : '.'), + data & 0x04 ? '1' : '0', + data & 0x02 ? '1' : '0', + data & 0x40 ? (data & 0x01 ? '1' : '0') : (data & 0x01 ? 'C' : '.'), + data ); + } + + m_tms7000_portc = data; +} + +READ8_MEMBER(coco_ssc_device::ssc_port_d_r) +{ + if( ((m_tms7000_portc & C_RCS) == 0) && ((m_tms7000_portc & C_ACS) == 0)) + logerror( "[%s] Warning: Reading RAM and PSG at the same time!\n", machine().describe_context() ); + + if( ((m_tms7000_portc & C_RCS) == 0) && ((m_tms7000_portc & C_RRW) == C_RRW)) /* static ram chip select (low) and static ram chip read (high) */ + { + uint16_t address = (uint16_t)m_tms7000_portc << 8; + address += m_tms7000_portb; + address &= 0x7ff; + + m_tms7000_portd = m_staticram->read(address); + } + + if( (m_tms7000_portc & C_ACS) == 0 ) /* chip select for AY-3-8913 */ + { + if( ((m_tms7000_portc & C_BDR) == 0) && ((m_tms7000_portc & C_BC1) == C_BC1) ) /* psg read data */ + { + m_tms7000_portd = m_ay->data_r(space, 0); + } + } + + if (LOG_SSC) + { + logerror( "[%s] port d read: %02x\n", machine().describe_context(), m_tms7000_portd ); + } + + return m_tms7000_portd; +} + +WRITE8_MEMBER(coco_ssc_device::ssc_port_d_w) +{ + if (LOG_SSC) + { + logerror( "[%s] port d write: %02x\n", machine().describe_context(), data ); + } + + m_tms7000_portd = data; +} + + +//------------------------------------------------- +// cocossc_sac_device - constructor +//------------------------------------------------- + +cocossc_sac_device::cocossc_sac_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, COCOSSC_SAC, tag, owner, clock), + device_sound_interface(mconfig, *this), + m_stream(nullptr), + m_index(0) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void cocossc_sac_device::device_start() +{ + m_stream = stream_alloc(1, 1, machine().sample_rate()); +} + + +//------------------------------------------------- +// sound_stream_update - handle a stream update +//------------------------------------------------- + +void cocossc_sac_device::sound_stream_update(sound_stream &stream, stream_sample_t **inputs, stream_sample_t **outputs, int samples) +{ + stream_sample_t *src = inputs[0]; + stream_sample_t *dst = outputs[0]; + + double n = samples; + + while (samples--) + { + m_rms[m_index] += ( (double)*src * (double)*src ); + *dst++ = (*src++); + } + + m_rms[m_index] = m_rms[m_index] / n; + m_rms[m_index] = sqrt(m_rms[m_index]); + + m_index++; + m_index &= 0x0f; +} + + +//------------------------------------------------- +// sound_activity_circuit_output - making sound +//------------------------------------------------- + +bool cocossc_sac_device::sound_activity_circuit_output() +{ + double average = m_rms[0] + m_rms[1] + m_rms[2] + m_rms[3] + m_rms[4] + + m_rms[5] + m_rms[6] + m_rms[7] + m_rms[8] + m_rms[9] + m_rms[10] + + m_rms[11] + m_rms[12] + m_rms[13] + m_rms[14] + m_rms[15]; + + average /= 16.0; + + if( average > 10400.0 ) + return true; + + return false; +} |