// license:BSD-3-Clause
// copyright-holders:AJR
// thanks-to:Aaron Giles
/**********************************************************************
Sony CXD1095 CMOS I/O Port Expander
Based on Sega X Board driver by Aaron Giles
This device provides four 8-bit ports (PA-PD) and one 4-bit port
(PE or PX). All these ports can be configured for input or for
output, entirely or in parts. The upper and lower halves of ports
A-D can be separately configured by the first of the two
write-only control registers. The second control register
determines the direction of individual PE/PX bits.
**********************************************************************/
#include "emu.h"
#include "machine/cxd1095.h"
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
DEFINE_DEVICE_TYPE(CXD1095, cxd1095_device, "cxd1095", "CXD1095 I/O Expander")
//**************************************************************************
// DEVICE DEFINITION
//**************************************************************************
//-------------------------------------------------
// cxd1095_device - constructor
//-------------------------------------------------
cxd1095_device::cxd1095_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, CXD1095, tag, owner, clock)
, m_input_cb{{*this}, {*this}, {*this}, {*this}, {*this}}
, m_output_cb{{*this}, {*this}, {*this}, {*this}, {*this}}
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void cxd1095_device::device_start()
{
// resolve callbacks
for (auto &cb : m_input_cb)
cb.resolve();
for (auto &cb : m_output_cb)
cb.resolve();
std::fill(std::begin(m_data_latch), std::end(m_data_latch), 0);
// save state
save_item(NAME(m_data_latch));
save_item(NAME(m_data_dir));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void cxd1095_device::device_reset()
{
std::fill(std::begin(m_data_dir), std::end(m_data_dir), 0xff);
}
//-------------------------------------------------
// read - read from an input port
//-------------------------------------------------
READ8_MEMBER(cxd1095_device::read)
{
if (offset < 5)
{
u8 input_data = 0;
u8 input_mask = m_data_dir[offset];
if (offset == 4)
input_mask &= 0x0f;
// read through callback if port not configured entirely for output
if (input_mask != 0 && !m_input_cb[offset].isnull())
input_data = m_input_cb[offset](0, input_mask) & input_mask;
else if (m_data_dir[offset] == 0xff)
logerror("Reading from undefined input port %c\n", 'A' + offset);
// combine live inputs with latched data
return input_data | (m_data_latch[offset] & ~m_data_dir[offset]);
}
logerror("Reading from nonexistent port %c\n", 'A' + offset);
return 0;
}
//-------------------------------------------------
// write - write to an output port or one of two
// control registers
//-------------------------------------------------
WRITE8_MEMBER(cxd1095_device::write)
{
if (offset < 5)
{
// port E is only 4 bits wide
if (offset == 4)
data &= 0x0f;
// update our latched data
m_data_latch[offset] = data;
// send output through callback
u8 dataout = data & ~m_data_dir[offset];
if (!m_output_cb[offset].isnull())
m_output_cb[offset](0, dataout, ~m_data_dir[offset]);
else
logerror("Writing %02X to undefined output port %c\n", dataout, 'A' + offset);
}
else if (offset == 6)
{
// control register 1 determines direction of each half of ports A-D
for (int port = 0; port < 4; port++)
{
m_data_dir[port] = (BIT(data, 0) ? 0x0f : 0) | (BIT(data, 1) ? 0xf0 : 0);
if (m_data_dir[port] != 0)
logerror("Port %c & %02X configured for input\n", 'A' + port, m_data_dir[port]);
if (m_data_dir[port] != 0xff)
logerror("Port %c & %02X configured for output\n", 'A' + port, 0xff ^ m_data_dir[port]);
data >>= 2;
}
}
else if (offset == 7)
{
// control register 2 determines direction of the four port E bits
m_data_dir[4] = (data & 0x0f) | 0xf0;
if (m_data_dir[4] != 0xf0)
logerror("Port E & %X configured for input\n", 0x0f & m_data_dir[4]);
if (m_data_dir[4] != 0xff)
logerror("Port E & %X configured for output\n", 0xff ^ m_data_dir[4]);
}
}