summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/z80pio.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/z80pio.c')
-rw-r--r--trunk/src/emu/machine/z80pio.c844
1 files changed, 844 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/z80pio.c b/trunk/src/emu/machine/z80pio.c
new file mode 100644
index 00000000000..51e53b06219
--- /dev/null
+++ b/trunk/src/emu/machine/z80pio.c
@@ -0,0 +1,844 @@
+/***************************************************************************
+
+ Zilog Z80 Parallel Input/Output Controller implementation
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "z80pio.h"
+#include "cpu/z80/z80daisy.h"
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+#define LOG 0
+
+enum
+{
+ MODE_OUTPUT = 0,
+ MODE_INPUT,
+ MODE_BIDIRECTIONAL,
+ MODE_BIT_CONTROL
+};
+
+enum
+{
+ ANY = 0,
+ IOR,
+ MASK
+};
+
+const int ICW_ENABLE_INT = 0x80;
+const int ICW_AND_OR = 0x40;
+const int ICW_AND = 0x40;
+const int ICW_OR = 0x00;
+const int ICW_HIGH_LOW = 0x20;
+const int ICW_HIGH = 0x20;
+const int ICW_LOW = 0x00;
+const int ICW_MASK_FOLLOWS = 0x10;
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+// device type definition
+const device_type Z80PIO = &device_creator<z80pio_device>;
+
+//-------------------------------------------------
+// z80pio_device - constructor
+//-------------------------------------------------
+
+z80pio_device::z80pio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, Z80PIO, "Z8420", tag, owner, clock),
+ device_z80daisy_interface(mconfig, *this)
+{
+}
+
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void z80pio_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const z80pio_interface *intf = reinterpret_cast<const z80pio_interface *>(static_config());
+ if (intf != NULL)
+ *static_cast<z80pio_interface *>(this) = *intf;
+
+ // or initialize to defaults if none provided
+ else
+ {
+ memset(&m_out_int_cb, 0, sizeof(m_out_int_cb));
+ memset(&m_in_pa_cb, 0, sizeof(m_in_pa_cb));
+ memset(&m_out_pa_cb, 0, sizeof(m_out_pa_cb));
+ memset(&m_out_ardy_cb, 0, sizeof(m_out_ardy_cb));
+ memset(&m_in_pb_cb, 0, sizeof(m_in_pb_cb));
+ memset(&m_out_pb_cb, 0, sizeof(m_out_pb_cb));
+ memset(&m_out_brdy_cb, 0, sizeof(m_out_brdy_cb));
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void z80pio_device::device_start()
+{
+ m_port[PORT_A].start(this, PORT_A, m_in_pa_cb, m_out_pa_cb, m_out_ardy_cb);
+ m_port[PORT_B].start(this, PORT_B, m_in_pb_cb, m_out_pb_cb, m_out_brdy_cb);
+
+ // resolve callbacks
+ m_out_int_func.resolve(m_out_int_cb, *this);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void z80pio_device::device_reset()
+{
+ // loop over ports
+ for (int index = PORT_A; index < PORT_COUNT; index++)
+ m_port[index].reset();
+}
+
+
+
+//**************************************************************************
+// DAISY CHAIN INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// z80daisy_irq_state - return the overall IRQ
+// state for this device
+//-------------------------------------------------
+
+int z80pio_device::z80daisy_irq_state()
+{
+ int state = 0;
+
+ for (int index = PORT_A; index < PORT_COUNT; index++)
+ {
+ pio_port &port = m_port[index];
+
+ if (port.m_ius)
+ {
+ // interrupt under service
+ return Z80_DAISY_IEO;
+ }
+ else if (port.m_ie && port.m_ip)
+ {
+ // interrupt pending
+ state = Z80_DAISY_INT;
+ }
+ }
+
+ return state;
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_ack - acknowledge an IRQ and
+// return the appropriate vector
+//-------------------------------------------------
+
+int z80pio_device::z80daisy_irq_ack()
+{
+ for (int index = PORT_A; index < PORT_COUNT; index++)
+ {
+ pio_port &port = m_port[index];
+
+ if (port.m_ip)
+ {
+ if (LOG) logerror("Z80PIO '%s' Interrupt Acknowledge\n", tag());
+
+ // clear interrupt pending flag
+ port.m_ip = false;
+
+ // set interrupt under service flag
+ port.m_ius = true;
+
+ check_interrupts();
+
+ return port.m_vector;
+ }
+ }
+
+ logerror("z80pio_irq_ack: failed to find an interrupt to ack!\n");
+
+ return 0;
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_reti - clear the interrupt
+// pending state to allow other interrupts through
+//-------------------------------------------------
+
+void z80pio_device::z80daisy_irq_reti()
+{
+ for (int index = PORT_A; index < PORT_COUNT; index++)
+ {
+ pio_port &port = m_port[index];
+
+ if (port.m_ius)
+ {
+ if (LOG) logerror("Z80PIO '%s' Return from Interrupt\n", tag());
+
+ // clear interrupt under service flag
+ port.m_ius = false;
+ check_interrupts();
+
+ return;
+ }
+ }
+
+ logerror("z80pio_irq_reti: failed to find an interrupt to clear IEO on!\n");
+}
+
+
+
+//**************************************************************************
+// DEVICE-LEVEL IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// control_read - control register read
+//-------------------------------------------------
+
+UINT8 z80pio_device::control_read()
+{
+ return (m_port[PORT_A].m_icw & 0xc0) | (m_port[PORT_B].m_icw >> 4);
+}
+
+
+//-------------------------------------------------
+// check_interrupts - update the interrupt state
+// over all ports
+//-------------------------------------------------
+
+void z80pio_device::check_interrupts()
+{
+ int state = CLEAR_LINE;
+
+ for (int index = PORT_A; index < PORT_COUNT; index++)
+ if (m_port[index].interrupt_signalled())
+ state = ASSERT_LINE;
+
+ m_out_int_func(state);
+}
+
+
+
+//**************************************************************************
+// PORT-LEVEL IMPLEMENTATION
+//**************************************************************************
+
+//-------------------------------------------------
+// pio_port - constructor
+//-------------------------------------------------
+
+z80pio_device::pio_port::pio_port()
+ : m_device(NULL),
+ m_index(0),
+ m_mode(0),
+ m_next_control_word(0),
+ m_input(0),
+ m_output(0),
+ m_ior(0),
+ m_rdy(false),
+ m_stb(false),
+ m_ie(false),
+ m_ip(false),
+ m_ius(false),
+ m_icw(0),
+ m_vector(0),
+ m_mask(0),
+ m_match(false)
+{
+ memset(&m_in_p_func, 0, sizeof(m_in_p_func));
+ memset(&m_out_p_func, 0, sizeof(m_out_p_func));
+ memset(&m_out_rdy_func, 0, sizeof(m_out_rdy_func));
+}
+
+
+//-------------------------------------------------
+// start - set up a port during device startup
+//-------------------------------------------------
+
+void z80pio_device::pio_port::start(z80pio_device *device, int index, const devcb_read8 &infunc, const devcb_write8 &outfunc, const devcb_write_line &rdyfunc)
+{
+ m_device = device;
+ m_index = index;
+
+ // resolve callbacks
+ m_in_p_func.resolve(infunc, *m_device);
+ m_out_p_func.resolve(outfunc, *m_device);
+ m_out_rdy_func.resolve(rdyfunc, *m_device);
+
+ // register for state saving
+ m_device->save_item(NAME(m_mode), m_index);
+ m_device->save_item(NAME(m_next_control_word), m_index);
+ m_device->save_item(NAME(m_input), m_index);
+ m_device->save_item(NAME(m_output), m_index);
+ m_device->save_item(NAME(m_ior), m_index);
+ m_device->save_item(NAME(m_rdy), m_index);
+ m_device->save_item(NAME(m_stb), m_index);
+ m_device->save_item(NAME(m_ie), m_index);
+ m_device->save_item(NAME(m_ip), m_index);
+ m_device->save_item(NAME(m_ius), m_index);
+ m_device->save_item(NAME(m_icw), m_index);
+ m_device->save_item(NAME(m_vector), m_index);
+ m_device->save_item(NAME(m_mask), m_index);
+ m_device->save_item(NAME(m_match), m_index);
+}
+
+
+//-------------------------------------------------
+// reset - reset a port during device reset
+//-------------------------------------------------
+
+void z80pio_device::pio_port::reset()
+{
+ // set mode 1
+ set_mode(MODE_INPUT);
+
+ // reset interrupt enable flip-flops
+ m_icw &= ~ICW_ENABLE_INT;
+ m_ie = false;
+ m_ip = false;
+ m_ius = false;
+ m_match = false;
+
+ // reset all bits of the data I/O register
+ m_ior = 0;
+
+ // set all bits of the mask control register
+ m_mask = 0xff;
+
+ // reset output register
+ m_output = 0;
+
+ // clear ready line
+ set_rdy(false);
+}
+
+
+//-------------------------------------------------
+// interrupt_signalled - return true if an
+// interrupt is signalled
+//-------------------------------------------------
+
+bool z80pio_device::pio_port::interrupt_signalled()
+{
+ if (m_mode == MODE_BIT_CONTROL)
+ {
+ // fetch input data (ignore output lines)
+ UINT8 data = (m_input & m_ior) | (m_output & ~m_ior);
+ UINT8 mask = ~m_mask;
+ bool match = false;
+
+ data &= mask;
+
+ if ((m_icw & 0x60) == 0 && data != mask) match = true;
+ else if ((m_icw & 0x60) == 0x20 && data != 0) match = true;
+ else if ((m_icw & 0x60) == 0x40 && data == 0) match = true;
+ else if ((m_icw & 0x60) == 0x60 && data == mask) match = true;
+
+ if (!m_match && match)
+ {
+ // trigger interrupt
+ m_ip = true;
+ if (LOG) logerror("Z80PIO '%s' Port %c Interrupt Pending\n", m_device->tag(), 'A' + m_index);
+ }
+
+ m_match = match;
+ }
+
+ return (m_ie && m_ip && !m_ius);
+}
+
+
+//-------------------------------------------------
+// trigger_interrupt - trigger an interrupt from
+// this port
+//-------------------------------------------------
+
+void z80pio_device::pio_port::trigger_interrupt()
+{
+ m_ip = true;
+ if (LOG) logerror("Z80PIO '%s' Port %c Interrupt Pending\n", m_device->tag(), 'A' + m_index);
+
+ check_interrupts();
+}
+
+
+//-------------------------------------------------
+// set_rdy - set the port's RDY line
+//-------------------------------------------------
+
+void z80pio_device::pio_port::set_rdy(bool state)
+{
+ if (m_rdy == state) return;
+
+ if (LOG) logerror("Z80PIO '%s' Port %c Ready: %u\n", m_device->tag(), 'A' + m_index, state);
+
+ m_rdy = state;
+ m_out_rdy_func(state);
+}
+
+
+//-------------------------------------------------
+// set_mode - set the port's mode
+//-------------------------------------------------
+
+void z80pio_device::pio_port::set_mode(int mode)
+{
+ if (LOG) logerror("Z80PIO '%s' Port %c Mode: %u\n", m_device->tag(), 'A' + m_index, mode);
+
+ switch (mode)
+ {
+ case MODE_OUTPUT:
+ // enable data output
+ m_out_p_func(0, m_output);
+
+ // assert ready line
+ set_rdy(true);
+
+ // set mode register
+ m_mode = mode;
+ break;
+
+ case MODE_INPUT:
+ // set mode register
+ m_mode = mode;
+ break;
+
+ case MODE_BIDIRECTIONAL:
+ if (m_index == PORT_B)
+ {
+ logerror("Z80PIO '%s' Port %c Invalid Mode: %u!\n", m_device->tag(), 'A' + m_index, mode);
+ }
+ else
+ {
+ // set mode register
+ m_mode = mode;
+ }
+ break;
+
+ case MODE_BIT_CONTROL:
+ if ((m_index == PORT_A) || (m_device->m_port[PORT_A].m_mode != MODE_BIDIRECTIONAL))
+ {
+ // clear ready line
+ set_rdy(false);
+ }
+
+ // disable interrupts until IOR is written
+ m_ie = false;
+ check_interrupts();
+
+ // set logic equation to false
+ m_match = false;
+
+ // next word is I/O register
+ m_next_control_word = IOR;
+
+ // set mode register
+ m_mode = mode;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// strobe - strobe data in/out of the port
+//-------------------------------------------------
+
+void z80pio_device::pio_port::strobe(bool state)
+{
+ if (LOG) logerror("Z80PIO '%s' Port %c Strobe: %u\n", m_device->tag(), 'A' + m_index, state);
+
+ if (m_device->m_port[PORT_A].m_mode == MODE_BIDIRECTIONAL)
+ {
+ if (m_rdy) // port ready
+ {
+ if (m_stb && !state) // falling edge
+ {
+ if (m_index == PORT_A)
+ m_out_p_func(0, m_output);
+ else
+ m_device->m_port[PORT_A].m_input = m_device->m_port[PORT_A].m_in_p_func(0);
+ }
+ else if (!m_stb && state) // rising edge
+ {
+ trigger_interrupt();
+
+ // clear ready line
+ set_rdy(false);
+ }
+ }
+ }
+ else
+ {
+ switch (m_mode)
+ {
+ case MODE_OUTPUT:
+ if (m_rdy)
+ {
+ if (!m_stb && state) // rising edge
+ {
+ trigger_interrupt();
+
+ // clear ready line
+ set_rdy(false);
+ }
+ }
+ break;
+
+ case MODE_INPUT:
+ if (!state)
+ {
+ // input port data
+ m_input = m_in_p_func(0);
+ }
+ else if (!m_stb && state) // rising edge
+ {
+ trigger_interrupt();
+
+ // clear ready line
+ set_rdy(false);
+ }
+ break;
+ }
+ }
+
+ m_stb = state;
+}
+
+
+//-------------------------------------------------
+// read - port I/O read
+//-------------------------------------------------
+
+UINT8 z80pio_device::pio_port::read()
+{
+ UINT8 data = 0xff;
+
+ switch (m_mode)
+ {
+ case MODE_OUTPUT:
+ data = m_output;
+ break;
+
+ case MODE_BIDIRECTIONAL:
+ if (m_index == PORT_A)
+ data = m_output;
+ break;
+
+ case MODE_BIT_CONTROL:
+ data = m_ior | (m_output & (m_ior ^ 0xff));
+ break;
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// write - port I/O write
+//-------------------------------------------------
+
+void z80pio_device::pio_port::write(UINT8 data)
+{
+ if (m_mode == MODE_BIT_CONTROL)
+ {
+ // latch data
+ m_input = data;
+ check_interrupts();
+ }
+}
+
+
+//-------------------------------------------------
+// control_write - control register write
+//-------------------------------------------------
+
+void z80pio_device::pio_port::control_write(UINT8 data)
+{
+ switch (m_next_control_word)
+ {
+ case ANY:
+ if (!BIT(data, 0))
+ {
+ // load interrupt vector
+ m_vector = data;
+ if (LOG) logerror("Z80PIO '%s' Port %c Interrupt Vector: %02x\n", m_device->tag(), 'A' + m_index, data);
+
+ // set interrupt enable
+ m_icw |= ICW_ENABLE_INT;
+ m_ie = true;
+ check_interrupts();
+ }
+ else
+ {
+ switch (data & 0x0f)
+ {
+ case 0x0f: // select operating mode
+ set_mode(data >> 6);
+ break;
+
+ case 0x07: // set interrupt control word
+ m_icw = data;
+
+ if (LOG)
+ {
+ logerror("Z80PIO '%s' Port %c Interrupt Enable: %u\n", m_device->tag(), 'A' + m_index, BIT(data, 7));
+ logerror("Z80PIO '%s' Port %c Logic: %s\n", m_device->tag(), 'A' + m_index, BIT(data, 6) ? "AND" : "OR");
+ logerror("Z80PIO '%s' Port %c Active %s\n", m_device->tag(), 'A' + m_index, BIT(data, 5) ? "High" : "Low");
+ logerror("Z80PIO '%s' Port %c Mask Follows: %u\n", m_device->tag(), 'A' + m_index, BIT(data, 4));
+ }
+
+ if (m_icw & ICW_MASK_FOLLOWS)
+ {
+ // disable interrupts until mask is written
+ m_ie = false;
+
+ // reset pending interrupts
+ m_ip = false;
+ check_interrupts();
+
+ // set logic equation to false
+ m_match = false;
+
+ // next word is mask control
+ m_next_control_word = MASK;
+ }
+ break;
+
+ case 0x03: // set interrupt enable flip-flop
+ m_icw = (data & 0x80) | (m_icw & 0x7f);
+ if (LOG) logerror("Z80PIO '%s' Port %c Interrupt Enable: %u\n", m_device->tag(), 'A' + m_index, BIT(data, 7));
+
+ // set interrupt enable
+ m_ie = BIT(m_icw, 7) ? true : false;
+ check_interrupts();
+ break;
+
+ default:
+ logerror("Z80PIO '%s' Port %c Invalid Control Word: %02x!\n", m_device->tag(), 'A' + m_index, data);
+ }
+ }
+ break;
+
+ case IOR: // data direction register
+ m_ior = data;
+ if (LOG) logerror("Z80PIO '%s' Port %c IOR: %02x\n", m_device->tag(), 'A' + m_index, data);
+
+ // set interrupt enable
+ m_ie = BIT(m_icw, 7) ? true : false;
+ check_interrupts();
+
+ // next word is any
+ m_next_control_word = ANY;
+ break;
+
+ case MASK: // interrupt mask
+ m_mask = data;
+ if (LOG) logerror("Z80PIO '%s' Port %c Mask: %02x\n", m_device->tag(), 'A' + m_index, data);
+
+ // set interrupt enable
+ m_ie = BIT(m_icw, 7) ? true : false;
+ check_interrupts();
+
+ // next word is any
+ m_next_control_word = ANY;
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// data_read - data register read
+//-------------------------------------------------
+
+UINT8 z80pio_device::pio_port::data_read()
+{
+ UINT8 data = 0;
+
+ switch (m_mode)
+ {
+ case MODE_OUTPUT:
+ data = m_output;
+ break;
+
+ case MODE_INPUT:
+ if (!m_stb)
+ {
+ // input port data
+ m_input = m_in_p_func(0);
+ }
+
+ data = m_input;
+
+ // clear ready line
+ set_rdy(false);
+
+ // assert ready line
+ set_rdy(true);
+ break;
+
+ case MODE_BIDIRECTIONAL:
+ data = m_input;
+
+ // clear ready line
+ m_device->m_port[PORT_B].set_rdy(false);
+
+ // assert ready line
+ m_device->m_port[PORT_B].set_rdy(true);
+ break;
+
+ case MODE_BIT_CONTROL:
+ // input port data
+ m_input = m_in_p_func(0);
+
+ data = (m_input & m_ior) | (m_output & (m_ior ^ 0xff));
+ break;
+ }
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// data_write - data register write
+//-------------------------------------------------
+
+void z80pio_device::pio_port::data_write(UINT8 data)
+{
+ switch (m_mode)
+ {
+ case MODE_OUTPUT:
+ // clear ready line
+ set_rdy(false);
+
+ // latch output data
+ m_output = data;
+
+ // output data to port
+ m_out_p_func(0, data);
+
+ // assert ready line
+ set_rdy(true);
+ break;
+
+ case MODE_INPUT:
+ // latch output data
+ m_output = data;
+ break;
+
+ case MODE_BIDIRECTIONAL:
+ // clear ready line
+ set_rdy(false);
+
+ // latch output data
+ m_output = data;
+
+ if (!m_stb)
+ {
+ // output data to port
+ m_out_p_func(0, data);
+ }
+
+ // assert ready line
+ set_rdy(true);
+ break;
+
+ case MODE_BIT_CONTROL:
+ // latch output data
+ m_output = data;
+
+ // output data to port
+ m_out_p_func(0, m_ior | (m_output & (m_ior ^ 0xff)));
+ break;
+ }
+}
+
+
+
+//**************************************************************************
+// GLOBAL STUBS
+//**************************************************************************
+
+READ8_DEVICE_HANDLER( z80pio_c_r ) { return downcast<z80pio_device *>(device)->control_read(); }
+WRITE8_DEVICE_HANDLER( z80pio_c_w ) { downcast<z80pio_device *>(device)->control_write(offset & 1, data); }
+
+READ8_DEVICE_HANDLER( z80pio_d_r ) { return downcast<z80pio_device *>(device)->data_read(offset & 1); }
+WRITE8_DEVICE_HANDLER( z80pio_d_w ) { downcast<z80pio_device *>(device)->data_write(offset & 1, data); }
+
+READ_LINE_DEVICE_HANDLER( z80pio_ardy_r ) { return downcast<z80pio_device *>(device)->rdy(z80pio_device::PORT_A); }
+READ_LINE_DEVICE_HANDLER( z80pio_brdy_r ) { return downcast<z80pio_device *>(device)->rdy(z80pio_device::PORT_B); }
+
+WRITE_LINE_DEVICE_HANDLER( z80pio_astb_w ) { downcast<z80pio_device *>(device)->strobe(z80pio_device::PORT_A, state ? true : false); }
+WRITE_LINE_DEVICE_HANDLER( z80pio_bstb_w ) { downcast<z80pio_device *>(device)->strobe(z80pio_device::PORT_B, state ? true : false); }
+
+READ8_DEVICE_HANDLER( z80pio_pa_r ) { return downcast<z80pio_device *>(device)->port_read(z80pio_device::PORT_A); }
+READ8_DEVICE_HANDLER( z80pio_pb_r ) { return downcast<z80pio_device *>(device)->port_read(z80pio_device::PORT_B); }
+
+WRITE8_DEVICE_HANDLER( z80pio_pa_w ) { downcast<z80pio_device *>(device)->port_write(z80pio_device::PORT_A, data); }
+WRITE8_DEVICE_HANDLER( z80pio_pb_w ) { downcast<z80pio_device *>(device)->port_write(z80pio_device::PORT_B, data); }
+
+//-------------------------------------------------
+// z80pio_cd_ba_r - register read
+//-------------------------------------------------
+
+READ8_DEVICE_HANDLER( z80pio_cd_ba_r )
+{
+ int index = BIT(offset, 0);
+
+ return BIT(offset, 1) ? z80pio_c_r(device, index) : z80pio_d_r(device, index);
+}
+
+//-------------------------------------------------
+// z80pio_cd_ba_w - register write
+//-------------------------------------------------
+
+WRITE8_DEVICE_HANDLER( z80pio_cd_ba_w )
+{
+ int index = BIT(offset, 0);
+
+ BIT(offset, 1) ? z80pio_c_w(device, index, data) : z80pio_d_w(device, index, data);
+}
+
+//-------------------------------------------------
+// z80pio_ba_cd_r - register read
+//-------------------------------------------------
+
+READ8_DEVICE_HANDLER( z80pio_ba_cd_r )
+{
+ int index = BIT(offset, 1);
+
+ return BIT(offset, 0) ? z80pio_c_r(device, index) : z80pio_d_r(device, index);
+}
+
+//-------------------------------------------------
+// z80pio_ba_cd_w - register write
+//-------------------------------------------------
+
+WRITE8_DEVICE_HANDLER( z80pio_ba_cd_w )
+{
+ int index = BIT(offset, 1);
+
+ BIT(offset, 0) ? z80pio_c_w(device, index, data) : z80pio_d_w(device, index, data);
+}