// 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.
**********************************************************************************************/
#define TC0140SYT_PORT01_FULL (0x01)
#define TC0140SYT_PORT23_FULL (0x02)
#define TC0140SYT_PORT01_FULL_MASTER (0x04)
#define 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, uint32_t 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)
{
memset(m_slavedata, 0, sizeof(uint8_t)*4);
memset(m_masterdata, 0, sizeof(uint8_t)*4);
}
tc0140syt_device::tc0140syt_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t 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 (uint32_t i = 0; i < 4; i++)
{
m_slavedata[i] = 0;
m_masterdata[i] = 0;
}
}
//-------------------------------------------------
// DEVICE HANDLERS
//-------------------------------------------------
void tc0140syt_device::update_nmi()
{
uint32_t nmi_pending = m_status & (TC0140SYT_PORT23_FULL | TC0140SYT_PORT01_FULL);
uint32_t state = (nmi_pending && m_nmi_enabled) ? ASSERT_LINE : CLEAR_LINE;
m_slavecpu->set_input_line(INPUT_LINE_NMI, state);
}
//-------------------------------------------------
// MASTER SIDE
//-------------------------------------------------
WRITE8_MEMBER( tc0140syt_device::master_port_w )
{
data &= 0x0f;
m_mainmode = data;
if (data > 4)
{
logerror("tc0140syt : error Master entering unknown mode[%02x]\n", data);
}
}
WRITE8_MEMBER( tc0140syt_device::master_comm_w )
{
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;
}
}
READ8_MEMBER( tc0140syt_device::master_comm_r )
{
machine().scheduler().synchronize(); // let slavecpu catch up (after we return and the main cpu finishes what it's doing)
uint8_t 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
//-------------------------------------------------
WRITE8_MEMBER( tc0140syt_device::slave_port_w )
{
data &= 0x0f;
m_submode = data;
if (data > 6)
{
logerror("tc0140syt error : Slave cpu unknown mode[%02x]\n", data);
}
}
WRITE8_MEMBER( tc0140syt_device::slave_comm_w )
{
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;
}
}
READ8_MEMBER( tc0140syt_device::slave_comm_r )
{
uint8_t 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, uint32_t clock)
: tc0140syt_device(mconfig, PC060HA, tag, owner, clock)
{
}