// license:BSD-3-Clause
// copyright-holders:Philip Bennett
/**********************************************************************************************
Taito TC0140SYT
TODO:
- Add pinout and description
- Create a separate implementation for the PC060HA
General rule seems to be that TC0140SYT supports a YM2610,
whereas PC060HA goes with a YM2203 or YM2151.
**********************************************************************************************/
#include "emu.h"
#include "taitosnd.h"
#include "cpu/z80/z80.h"
/**********************************************************************************************
It seems like 1 nibble commands are only for control purposes.
2 nibble commands are the real messages passed from one board to the other.
**********************************************************************************************/
static constexpr u8 TC0140SYT_PORT01_FULL = 0x01;
static constexpr u8 TC0140SYT_PORT23_FULL = 0x02;
static constexpr u8 TC0140SYT_PORT01_FULL_MASTER = 0x04;
static constexpr u8 TC0140SYT_PORT23_FULL_MASTER = 0x08;
// device type definition
DEFINE_DEVICE_TYPE(TC0140SYT, tc0140syt_device, "tc0140syt", "Taito TC0140SYT")
DEFINE_DEVICE_TYPE(PC060HA, pc060ha_device, "pc060ha", "Taito PC060HA CIU")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// tc0140syt_device - constructor
//-------------------------------------------------
tc0140syt_device::tc0140syt_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, type, tag, owner, clock)
, m_mainmode(0)
, m_submode(0)
, m_status(0)
, m_nmi_enabled(0)
, m_mastercpu(*this, finder_base::DUMMY_TAG)
, m_slavecpu(*this, finder_base::DUMMY_TAG)
{
std::fill(std::begin(m_slavedata), std::end(m_slavedata), 0);
std::fill(std::begin(m_masterdata), std::end(m_masterdata), 0);
}
tc0140syt_device::tc0140syt_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: tc0140syt_device(mconfig, TC0140SYT, tag, owner, clock)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void tc0140syt_device::device_start()
{
save_item(NAME(m_mainmode));
save_item(NAME(m_submode));
save_item(NAME(m_status));
save_item(NAME(m_nmi_enabled));
save_item(NAME(m_slavedata));
save_item(NAME(m_masterdata));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void tc0140syt_device::device_reset()
{
m_mainmode = 0;
m_submode = 0;
m_status = 0;
m_nmi_enabled = 0;
for (u8 i = 0; i < 4; i++)
{
m_slavedata[i] = 0;
m_masterdata[i] = 0;
}
}
//-------------------------------------------------
// DEVICE HANDLERS
//-------------------------------------------------
void tc0140syt_device::update_nmi()
{
u32 nmi_pending = m_status & (TC0140SYT_PORT23_FULL | TC0140SYT_PORT01_FULL);
u32 state = (nmi_pending && m_nmi_enabled) ? ASSERT_LINE : CLEAR_LINE;
m_slavecpu->set_input_line(INPUT_LINE_NMI, state);
}
//-------------------------------------------------
// MASTER SIDE
//-------------------------------------------------
void tc0140syt_device::master_port_w(u8 data)
{
data &= 0x0f;
m_mainmode = data;
if (data > 4)
{
logerror("tc0140syt : error Master entering unknown mode[%02x]\n", data);
}
}
void tc0140syt_device::master_comm_w(u8 data)
{
machine().scheduler().synchronize(); // let slavecpu catch up (after we return and the main cpu finishes what it's doing)
data &= 0x0f; /* this is important, otherwise ballbros won't work */
switch (m_mainmode)
{
case 0x00: // mode #0
m_slavedata[m_mainmode++] = data;
break;
case 0x01: // mode #1
m_slavedata[m_mainmode++] = data;
m_status |= TC0140SYT_PORT01_FULL;
update_nmi();
break;
case 0x02: // mode #2
m_slavedata[m_mainmode++] = data;
break;
case 0x03: // mode #3
m_slavedata[m_mainmode++] = data;
m_status |= TC0140SYT_PORT23_FULL;
update_nmi();
break;
case 0x04: // port status
/* this does a hi-lo transition to reset the sound cpu */
m_slavecpu->set_input_line(INPUT_LINE_RESET, data ? ASSERT_LINE : CLEAR_LINE);
break;
default:
break;
}
}
u8 tc0140syt_device::master_comm_r()
{
machine().scheduler().synchronize(); // let slavecpu catch up (after we return and the main cpu finishes what it's doing)
u8 res = 0;
switch (m_mainmode)
{
case 0x00: // mode #0
res = m_masterdata[m_mainmode++];
break;
case 0x01: // mode #1
m_status &= ~TC0140SYT_PORT01_FULL_MASTER;
res = m_masterdata[m_mainmode++];
break;
case 0x02: // mode #2
res = m_masterdata[m_mainmode++];
break;
case 0x03: // mode #3
m_status &= ~TC0140SYT_PORT23_FULL_MASTER;
res = m_masterdata[m_mainmode++];
break;
case 0x04: // port status
res = m_status;
break;
default:
break;
}
return res;
}
//-------------------------------------------------
// SLAVE SIDE
//-------------------------------------------------
void tc0140syt_device::slave_port_w(u8 data)
{
data &= 0x0f;
m_submode = data;
if (data > 6)
{
logerror("tc0140syt error : Slave cpu unknown mode[%02x]\n", data);
}
}
void tc0140syt_device::slave_comm_w(u8 data)
{
data &= 0x0f;
switch (m_submode)
{
case 0x00: // mode #0
m_masterdata[m_submode++] = data;
break;
case 0x01: // mode #1
m_masterdata[m_submode++] = data;
m_status |= TC0140SYT_PORT01_FULL_MASTER;
break;
case 0x02: // mode #2
m_masterdata[m_submode++] = data;
break;
case 0x03: // mode #3
m_masterdata[m_submode++] = data;
m_status |= TC0140SYT_PORT23_FULL_MASTER;
break;
case 0x04: // port status
//m_status = TC0140SYT_SET_OK;
break;
case 0x05: // NMI disable
m_nmi_enabled = 0;
update_nmi();
break;
case 0x06: // NMI enable
m_nmi_enabled = 1;
update_nmi();
break;
default:
break;
}
}
u8 tc0140syt_device::slave_comm_r()
{
u8 res = 0;
switch (m_submode)
{
case 0x00: // mode #0
res = m_slavedata[m_submode++];
break;
case 0x01: // mode #1
m_status &= ~TC0140SYT_PORT01_FULL;
res = m_slavedata[m_submode++];
update_nmi();
break;
case 0x02: // mode #2
res = m_slavedata[m_submode++];
break;
case 0x03: // mode #3
m_status &= ~TC0140SYT_PORT23_FULL;
res = m_slavedata[m_submode++];
update_nmi();
break;
case 0x04: // port status
res = m_status;
break;
default:
break;
}
return res;
}
//-------------------------------------------------
// pc060ha_device - constructor
//-------------------------------------------------
pc060ha_device::pc060ha_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: tc0140syt_device(mconfig, PC060HA, tag, owner, clock)
{
}