summaryrefslogblamecommitdiffstatshomepage
path: root/src/devices/machine/cxd1095.cpp
blob: bb39a440bed47844f5239afdcb7c342bb8fa609f (plain) (tree)
























                                                                            
                                                                              









                                                                                                          


                                                                  















                                                                       






                                                   








                                                                      










































































                                                                                                                        
// 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]);
	}
}