summaryrefslogtreecommitdiffstatshomepage
path: root/trunk/src/emu/machine/z80sio.c
diff options
context:
space:
mode:
Diffstat (limited to 'trunk/src/emu/machine/z80sio.c')
-rw-r--r--trunk/src/emu/machine/z80sio.c865
1 files changed, 865 insertions, 0 deletions
diff --git a/trunk/src/emu/machine/z80sio.c b/trunk/src/emu/machine/z80sio.c
new file mode 100644
index 00000000000..8ddcd54806a
--- /dev/null
+++ b/trunk/src/emu/machine/z80sio.c
@@ -0,0 +1,865 @@
+/***************************************************************************
+
+ Z80 SIO (Z8440) implementation
+
+ Copyright Nicola Salmoria and the MAME Team.
+ Visit http://mamedev.org for licensing and usage restrictions.
+
+***************************************************************************/
+
+#include "emu.h"
+#include "z80sio.h"
+#include "cpu/z80/z80.h"
+#include "cpu/z80/z80daisy.h"
+
+
+// device type definition
+const device_type Z80SIO = &device_creator<z80sio_device>;
+
+
+//**************************************************************************
+// DEBUGGING
+//**************************************************************************
+
+#define VERBOSE 0
+
+#define VPRINTF(x) do { if (VERBOSE) logerror x; } while (0)
+
+
+
+//**************************************************************************
+// CONSTANTS
+//**************************************************************************
+
+// interrupt states
+const int INT_TRANSMIT = 0x00; // not confirmed
+const int INT_STATUS = 0x01;
+const int INT_RECEIVE = 0x02;
+const int INT_ERROR = 0x03;
+
+const int INT_CHB_TRANSMIT = 0 + INT_TRANSMIT;
+const int INT_CHB_STATUS = 0 + INT_STATUS;
+const int INT_CHB_RECEIVE = 0 + INT_RECEIVE;
+const int INT_CHB_ERROR = 0 + INT_ERROR;
+const int INT_CHA_TRANSMIT = 4 + INT_TRANSMIT;
+const int INT_CHA_STATUS = 4 + INT_STATUS;
+const int INT_CHA_RECEIVE = 4 + INT_RECEIVE;
+const int INT_CHA_ERROR = 4 + INT_ERROR;
+
+// SIO write register 0
+const int SIO_WR0_RESET_MASK = 0xc0; // D7-D6: Reset control
+const int SIO_WR0_RESET_NULL = 0x00; // 00 = NULL code
+const int SIO_WR0_RESET_RX_CRC = 0x40; // 01 = Reset Rx CRC checker
+const int SIO_WR0_RESET_TX_CRC = 0x80; // 10 = Reset Tx CRC generator
+const int SIO_WR0_RESET_TX_LATCH = 0xc0; // 11 = Reset Tx Underrun/EOM latch
+const int SIO_WR0_COMMAND_MASK = 0x38; // D5-D3: Command
+const int SIO_WR0_COMMAND_NULL = 0x00; // 000 = NULL code
+const int SIO_WR0_COMMAND_SET_ABORT = 0x08; // 001 = Set abort (SDLC)
+const int SIO_WR0_COMMAND_RES_STATUS_INT = 0x10; // 010 = reset ext/status interrupts
+const int SIO_WR0_COMMAND_CH_RESET = 0x18; // 011 = Channel reset
+const int SIO_WR0_COMMAND_ENA_RX_INT = 0x20; // 100 = Enable int on next Rx character
+const int SIO_WR0_COMMAND_RES_TX_INT = 0x28; // 101 = Reset Tx int pending
+const int SIO_WR0_COMMAND_RES_ERROR = 0x30; // 110 = Error reset
+const int SIO_WR0_COMMAND_RETI = 0x38; // 111 = Return from int (CH-A only)
+const int SIO_WR0_REGISTER_MASK = 0x07; // D2-D0: Register select (0-7)
+
+// SIO write register 1
+const int SIO_WR1_READY_WAIT_ENA = 0x80; // D7 = READY/WAIT enable
+const int SIO_WR1_READY_WAIT_FUNCTION = 0x40; // D6 = READY/WAIT function
+const int SIO_WR1_READY_WAIT_ON_RT = 0x20; // D5 = READY/WAIT on R/T
+const int SIO_WR1_RXINT_MASK = 0x18; // D4-D3 = Rx int control
+const int SIO_WR1_RXINT_DISABLE = 0x00; // 00 = Rx int disable
+const int SIO_WR1_RXINT_FIRST = 0x08; // 01 = Rx int on first character
+const int SIO_WR1_RXINT_ALL_PARITY = 0x10; // 10 = int on all Rx characters (parity affects vector)
+const int SIO_WR1_RXINT_ALL_NOPARITY = 0x18; // 11 = int on all Rx characters (parity ignored)
+const int SIO_WR1_STATUS_AFFECTS_VECTOR = 0x04; // D2 = Status affects vector (CH-B only)
+const int SIO_WR1_TXINT_ENABLE = 0x02; // D1 = Tx int enable
+const int SIO_WR1_STATUSINT_ENABLE = 0x01; // D0 = Ext int enable
+
+// SIO write register 2 (CH-B only)
+const int SIO_WR2_INT_VECTOR_MASK = 0xff; // D7-D0 = interrupt vector
+
+// SIO write register 3
+const int SIO_WR3_RX_DATABITS_MASK = 0xc0; // D7-D6 = Rx Data bits
+const int SIO_WR3_RX_DATABITS_5 = 0x00; // 00 = Rx 5 bits/character
+const int SIO_WR3_RX_DATABITS_7 = 0x40; // 01 = Rx 7 bits/character
+const int SIO_WR3_RX_DATABITS_6 = 0x80; // 10 = Rx 6 bits/character
+const int SIO_WR3_RX_DATABITS_8 = 0xc0; // 11 = Rx 8 bits/character
+const int SIO_WR3_AUTO_ENABLES = 0x20; // D5 = Auto enables
+const int SIO_WR3_ENTER_HUNT_PHASE = 0x10; // D4 = Enter hunt phase
+const int SIO_WR3_RX_CRC_ENABLE = 0x08; // D3 = Rx CRC enable
+const int SIO_WR3_ADDR_SEARCH_MODE = 0x04; // D2 = Address search mode (SDLC)
+const int SIO_WR3_SYNC_LOAD_INHIBIT = 0x02; // D1 = Sync character load inhibit
+const int SIO_WR3_RX_ENABLE = 0x01; // D0 = Rx enable
+
+// SIO write register 4
+const int SIO_WR4_CLOCK_MODE_MASK = 0xc0; // D7-D6 = Clock mode
+const int SIO_WR4_CLOCK_MODE_x1 = 0x00; // 00 = x1 clock mode
+const int SIO_WR4_CLOCK_MODE_x16 = 0x40; // 01 = x16 clock mode
+const int SIO_WR4_CLOCK_MODE_x32 = 0x80; // 10 = x32 clock mode
+const int SIO_WR4_CLOCK_MODE_x64 = 0xc0; // 11 = x64 clock mode
+const int SIO_WR4_SYNC_MODE_MASK = 0x30; // D5-D4 = Sync mode
+const int SIO_WR4_SYNC_MODE_8BIT = 0x00; // 00 = 8 bit sync character
+const int SIO_WR4_SYNC_MODE_16BIT = 0x10; // 01 = 16 bit sync character
+const int SIO_WR4_SYNC_MODE_SDLC = 0x20; // 10 = SDLC mode (01111110 flag)
+const int SIO_WR4_SYNC_MODE_EXTERNAL = 0x30; // 11 = External sync mode
+const int SIO_WR4_STOPBITS_MASK = 0x0c; // D3-D2 = Stop bits
+const int SIO_WR4_STOPBITS_SYNC = 0x00; // 00 = Sync modes enable
+const int SIO_WR4_STOPBITS_1 = 0x04; // 01 = 1 stop bit/character
+const int SIO_WR4_STOPBITS_15 = 0x08; // 10 = 1.5 stop bits/character
+const int SIO_WR4_STOPBITS_2 = 0x0c; // 11 = 2 stop bits/character
+const int SIO_WR4_PARITY_EVEN = 0x02; // D1 = Parity even/odd
+const int SIO_WR4_PARITY_ENABLE = 0x01; // D0 = Parity enable
+
+// SIO write register 5
+const int SIO_WR5_DTR = 0x80; // D7 = DTR
+const int SIO_WR5_TX_DATABITS_MASK = 0x60; // D6-D5 = Tx Data bits
+const int SIO_WR5_TX_DATABITS_5 = 0x00; // 00 = Tx 5 bits/character
+const int SIO_WR5_TX_DATABITS_7 = 0x20; // 01 = Tx 7 bits/character
+const int SIO_WR5_TX_DATABITS_6 = 0x40; // 10 = Tx 6 bits/character
+const int SIO_WR5_TX_DATABITS_8 = 0x60; // 11 = Tx 8 bits/character
+const int SIO_WR5_SEND_BREAK = 0x10; // D4 = Send break
+const int SIO_WR5_TX_ENABLE = 0x08; // D3 = Tx Enable
+const int SIO_WR5_CRC16_SDLC = 0x04; // D2 = CRC-16/SDLC
+const int SIO_WR5_RTS = 0x02; // D1 = RTS
+const int SIO_WR5_TX_CRC_ENABLE = 0x01; // D0 = Tx CRC enable
+
+// SIO write register 6
+const int SIO_WR6_SYNC_7_0_MASK = 0xff; // D7-D0 = Sync bits 7-0
+
+// SIO write register 7
+const int SIO_WR7_SYNC_15_8_MASK = 0xff; // D7-D0 = Sync bits 15-8
+
+// SIO read register 0
+const int SIO_RR0_BREAK_ABORT = 0x80; // D7 = Break/abort
+const int SIO_RR0_TX_UNDERRUN = 0x40; // D6 = Tx underrun/EOM
+const int SIO_RR0_CTS = 0x20; // D5 = CTS
+const int SIO_RR0_SYNC_HUNT = 0x10; // D4 = Sync/hunt
+const int SIO_RR0_DCD = 0x08; // D3 = DCD
+const int SIO_RR0_TX_BUFFER_EMPTY = 0x04; // D2 = Tx buffer empty
+const int SIO_RR0_INT_PENDING = 0x02; // D1 = int pending (CH-A only)
+const int SIO_RR0_RX_CHAR_AVAILABLE = 0x01; // D0 = Rx character available
+
+// SIO read register 1
+const int SIO_RR1_END_OF_FRAME = 0x80; // D7 = End of frame (SDLC)
+const int SIO_RR1_CRC_FRAMING_ERROR = 0x40; // D6 = CRC/Framing error
+const int SIO_RR1_RX_OVERRUN_ERROR = 0x20; // D5 = Rx overrun error
+const int SIO_RR1_PARITY_ERROR = 0x10; // D4 = Parity error
+const int SIO_RR1_IFIELD_BITS_MASK = 0x0e; // D3-D1 = I field bits
+ // 100 = 0 in prev, 3 in 2nd prev
+ // 010 = 0 in prev, 4 in 2nd prev
+ // 110 = 0 in prev, 5 in 2nd prev
+ // 001 = 0 in prev, 6 in 2nd prev
+ // 101 = 0 in prev, 7 in 2nd prev
+ // 011 = 0 in prev, 8 in 2nd prev
+ // 111 = 1 in prev, 8 in 2nd prev
+ // 000 = 2 in prev, 8 in 2nd prev
+const int SIO_RR1_ALL_SENT = 0x01; // D0 = All sent
+
+// SIO read register 2 (CH-B only)
+const int SIO_RR2_VECTOR_MASK = 0xff; // D7-D0 = Interrupt vector
+
+
+const UINT8 z80sio_device::k_int_priority[] =
+{
+ INT_CHA_RECEIVE,
+ INT_CHA_TRANSMIT,
+ INT_CHA_STATUS,
+ INT_CHA_ERROR,
+ INT_CHB_RECEIVE,
+ INT_CHB_TRANSMIT,
+ INT_CHB_STATUS,
+ INT_CHB_ERROR
+};
+
+
+
+/***************************************************************************
+ FUNCTION PROTOTYPES
+***************************************************************************/
+
+
+/*
+
+ Interrupt priorities:
+ Ch A receive
+ Ch A transmit
+ Ch A external/status
+ Ch B receive
+ Ch B transmit
+ Ch B external/status
+
+
+ Initial configuration (both channels):
+ 005D:sio_reg_w(0,4) = 44
+ 01 = x16 clock mode
+ 00 = 8 bit sync character
+ 01 = 1 stop bit/character
+ Parity odd
+ Parity disabled
+
+ 005D:sio_reg_w(0,3) = C1
+ 11 = Rx 8 bits/character
+ No auto enables
+ No enter hunt phase
+ No Rx CRC enable
+ No address search mode
+ No sync character load inhibit
+ Rx enable
+
+ 005D:sio_reg_w(0,5) = 68
+ DTR = 0
+ 11 = Tx 8 bits/character
+ No send break
+ Tx enable
+ SDLC
+ No RTS
+ No CRC enable
+
+ 005D:sio_reg_w(0,2) = 40
+ Vector = 0x40
+
+ 005D:sio_reg_w(0,1) = 1D
+ No READY/WAIT
+ No READY/WAIT function
+ No READY/WAIT on R/T
+ 11 = int on all Rx characters (parity ignored)
+ Status affects vector
+ No Tx int enable
+ Ext int enable
+
+*/
+
+
+//**************************************************************************
+// INLINE FUNCTIONS
+//**************************************************************************
+
+//-------------------------------------------------
+// update_interrupt_state - update the interrupt
+// state to the external world
+//-------------------------------------------------
+
+inline void z80sio_device::update_interrupt_state()
+{
+ // if we have a callback, update it with the current state
+ if (m_irq_cb != NULL)
+ (*m_irq_cb)(this, (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE);
+}
+
+
+//-------------------------------------------------
+// set_interrupt - set the given interrupt state
+// on this channel and update the overall device
+// state
+//-------------------------------------------------
+
+inline void z80sio_device::sio_channel::set_interrupt(int type)
+{
+ int inum = (this == &m_device->m_channel[0] ? 4 : 0) + type;
+ m_device->m_int_state[inum] = Z80_DAISY_INT;
+ m_device->update_interrupt_state();
+}
+
+
+//-------------------------------------------------
+// clear_interrupt - clear the given interrupt
+// state on this channel and update the overall
+// device state
+//-------------------------------------------------
+
+inline void z80sio_device::sio_channel::clear_interrupt(int type)
+{
+ int inum = (this == &m_device->m_channel[0] ? 4 : 0) + type;
+ m_device->m_int_state[inum] &= ~Z80_DAISY_INT;
+ m_device->update_interrupt_state();
+}
+
+
+//-------------------------------------------------
+// compute_time_per_character - compute the
+// serial clocking period
+//-------------------------------------------------
+
+inline attotime z80sio_device::sio_channel::compute_time_per_character()
+{
+ // fix me -- should compute properly and include data, stop, parity bit
+ return attotime::from_hz(9600) * 10;
+}
+
+
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// z80sio_device - constructor
+//-------------------------------------------------
+
+z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, Z80SIO, "Zilog Z80 SIO", tag, owner, clock),
+ device_z80daisy_interface(mconfig, *this)
+{
+ for (int i = 0; i < 8; i++)
+ m_int_state[i] = 0;
+}
+
+
+//-------------------------------------------------
+// device_config_complete - perform any
+// operations now that the configuration is
+// complete
+//-------------------------------------------------
+
+void z80sio_device::device_config_complete()
+{
+ // inherit a copy of the static data
+ const z80sio_interface *intf = reinterpret_cast<const z80sio_interface *>(static_config());
+ if (intf != NULL)
+ *static_cast<z80sio_interface *>(this) = *intf;
+
+ // or initialize to defaults if none provided
+ else
+ {
+ m_irq_cb = NULL;
+ m_dtr_changed_cb = NULL;
+ m_rts_changed_cb = NULL;
+ m_break_changed_cb = NULL;
+ m_transmit_cb = NULL;
+ m_receive_poll_cb = NULL;
+ }
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void z80sio_device::device_start()
+{
+ m_channel[0].start(this, 0);
+ m_channel[1].start(this, 1);
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void z80sio_device::device_reset()
+{
+ // loop over channels
+ for (int ch = 0; ch < 2; ch++)
+ m_channel[ch].reset();
+}
+
+
+
+//**************************************************************************
+// DAISY CHAIN INTERFACE
+//**************************************************************************
+
+//-------------------------------------------------
+// z80daisy_irq_state - return the overall IRQ
+// state for this device
+//-------------------------------------------------
+
+int z80sio_device::z80daisy_irq_state()
+{
+ int state = 0;
+
+ VPRINTF(("sio IRQ state = B:%d%d%d%d A:%d%d%d%d\n",
+ m_int_state[0], m_int_state[1], m_int_state[2], m_int_state[3],
+ m_int_state[4], m_int_state[5], m_int_state[6], m_int_state[7]));
+
+ // loop over all interrupt sources
+ for (int irqsource = 0; irqsource < 8; irqsource++)
+ {
+ int inum = k_int_priority[irqsource];
+
+ // if we're servicing a request, don't indicate more interrupts
+ if (m_int_state[inum] & Z80_DAISY_IEO)
+ {
+ state |= Z80_DAISY_IEO;
+ break;
+ }
+ state |= m_int_state[inum];
+ }
+
+ return state;
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_ack - acknowledge an IRQ and
+// return the appropriate vector
+//-------------------------------------------------
+
+int z80sio_device::z80daisy_irq_ack()
+{
+ // loop over all interrupt sources
+ for (int irqsource = 0; irqsource < 8; irqsource++)
+ {
+ int inum = k_int_priority[irqsource];
+
+ // find the first channel with an interrupt requested
+ if (m_int_state[inum] & Z80_DAISY_INT)
+ {
+ VPRINTF(("sio IRQAck %d\n", inum));
+
+ // clear interrupt, switch to the IEO state, and update the IRQs
+ m_int_state[inum] = Z80_DAISY_IEO;
+ update_interrupt_state();
+ return m_channel[1].m_regs[2] + inum * 2;
+ }
+ }
+
+ logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n");
+ return m_channel[1].m_regs[2];
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_reti - clear the interrupt
+// pending state to allow other interrupts through
+//-------------------------------------------------
+
+void z80sio_device::z80daisy_irq_reti()
+{
+ // loop over all interrupt sources
+ for (int irqsource = 0; irqsource < 8; irqsource++)
+ {
+ int inum = k_int_priority[irqsource];
+
+ // find the first channel with an IEO pending
+ if (m_int_state[inum] & Z80_DAISY_IEO)
+ {
+ VPRINTF(("sio IRQReti %d\n", inum));
+
+ // clear the IEO state and update the IRQs
+ m_int_state[inum] &= ~Z80_DAISY_IEO;
+ update_interrupt_state();
+ return;
+ }
+ }
+
+ logerror("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n");
+}
+
+
+
+//**************************************************************************
+// SIO CHANNEL
+//**************************************************************************
+
+//-------------------------------------------------
+// sio_channel - constructor
+//-------------------------------------------------
+
+z80sio_device::sio_channel::sio_channel()
+ : m_device(NULL),
+ m_index(0),
+ m_inbuf(-1),
+ m_outbuf(-1),
+ m_int_on_next_rx(false),
+ m_receive_timer(NULL),
+ m_receive_inptr(0),
+ m_receive_outptr(0)
+{
+ memset(m_regs, 0, sizeof(m_regs));
+ memset(m_status, 0, sizeof(m_status));
+ memset(m_receive_buffer, 0, sizeof(m_receive_buffer));
+}
+
+
+//-------------------------------------------------
+// start - channel-specific startup
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::start(z80sio_device *device, int index)
+{
+ m_device = device;
+ m_index = index;
+ m_receive_timer = device->machine().scheduler().timer_alloc(FUNC(static_serial_callback), this);
+}
+
+
+//-------------------------------------------------
+// reset - reset a single SIO channel
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::reset()
+{
+ m_status[0] = SIO_RR0_TX_BUFFER_EMPTY;
+ m_status[1] = 0x00;
+ m_status[2] = 0x00;
+ m_int_on_next_rx = false;
+ m_outbuf = -1;
+
+ // reset interrupts
+ clear_interrupt(INT_TRANSMIT);
+ clear_interrupt(INT_STATUS);
+ clear_interrupt(INT_RECEIVE);
+ clear_interrupt(INT_ERROR);
+
+ // start the receive timer running
+ attotime tpc = compute_time_per_character();
+ m_receive_timer->adjust(tpc, 0, tpc);
+}
+
+
+//-------------------------------------------------
+// control_write - write to a control register
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::control_write(UINT8 data)
+{
+ int regnum = m_regs[0] & 7;
+
+ if (regnum != 0 || (regnum & 0xf8) != 0)
+ VPRINTF(("%s:sio_reg_w(%c,%d) = %02X\n", m_device->machine().describe_context(), 'A' + m_index, regnum, data));
+
+ // write a new value to the selected register
+ UINT8 old = m_regs[regnum];
+ m_regs[regnum] = data;
+
+ // clear the register number for the next write
+ if (regnum != 0)
+ m_regs[0] &= ~7;
+
+ // switch off the register for live state changes
+ switch (regnum)
+ {
+ // SIO write register 0
+ case 0:
+ switch (data & SIO_WR0_COMMAND_MASK)
+ {
+ case SIO_WR0_COMMAND_CH_RESET:
+ VPRINTF(("%s:SIO reset channel %c\n", m_device->machine().describe_context(), 'A' + m_index));
+ reset();
+ break;
+
+ case SIO_WR0_COMMAND_RES_STATUS_INT:
+ clear_interrupt(INT_STATUS);
+ break;
+
+ case SIO_WR0_COMMAND_ENA_RX_INT:
+ m_int_on_next_rx = true;
+ m_device->update_interrupt_state();
+ break;
+
+ case SIO_WR0_COMMAND_RES_TX_INT:
+ clear_interrupt(INT_TRANSMIT);
+ break;
+
+ case SIO_WR0_COMMAND_RES_ERROR:
+ clear_interrupt(INT_ERROR);
+ break;
+ }
+ break;
+
+ // SIO write register 1
+ case 1:
+ m_device->update_interrupt_state();
+ break;
+
+ // SIO write register 5
+ case 5:
+ if (((old ^ data) & SIO_WR5_DTR) && m_device->m_dtr_changed_cb)
+ (*m_device->m_dtr_changed_cb)(m_device, m_index, (data & SIO_WR5_DTR) != 0);
+ if (((old ^ data) & SIO_WR5_SEND_BREAK) && m_device->m_break_changed_cb)
+ (*m_device->m_break_changed_cb)(m_device, m_index, (data & SIO_WR5_SEND_BREAK) != 0);
+ if (((old ^ data) & SIO_WR5_RTS) && m_device->m_rts_changed_cb)
+ (*m_device->m_rts_changed_cb)(m_device, m_index, (data & SIO_WR5_RTS) != 0);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// control_read - read from a control register
+//-------------------------------------------------
+
+UINT8 z80sio_device::sio_channel::control_read()
+{
+ int regnum = m_regs[0] & 7;
+ UINT8 result = m_status[regnum];
+
+ // switch off the register for live state changes
+ switch (regnum)
+ {
+ // SIO read register 0
+ case 0:
+ result &= ~SIO_RR0_INT_PENDING;
+ if (m_device->z80daisy_irq_state() & Z80_DAISY_INT)
+ result |= SIO_RR0_INT_PENDING;
+ break;
+ }
+
+ VPRINTF(("%s:sio_reg_r(%c,%d) = %02x\n", m_device->machine().describe_context(), 'A' + m_index, regnum, m_status[regnum]));
+
+ return m_status[regnum];
+}
+
+
+//-------------------------------------------------
+// data_write - write to a data register
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::data_write(UINT8 data)
+{
+ VPRINTF(("%s:sio_data_w(%c) = %02X\n", m_device->machine().describe_context(), 'A' + m_index, data));
+
+ // if tx not enabled, just ignore it
+ if (!(m_regs[5] & SIO_WR5_TX_ENABLE))
+ return;
+
+ // update the status register
+ m_status[0] &= ~SIO_RR0_TX_BUFFER_EMPTY;
+
+ // reset the transmit interrupt
+ clear_interrupt(INT_TRANSMIT);
+
+ // stash the character
+ m_outbuf = data;
+}
+
+
+//-------------------------------------------------
+// data_read - read from a data register
+//-------------------------------------------------
+
+UINT8 z80sio_device::sio_channel::data_read()
+{
+ // update the status register
+ m_status[0] &= ~SIO_RR0_RX_CHAR_AVAILABLE;
+
+ // reset the receive interrupt
+ clear_interrupt(INT_RECEIVE);
+
+ VPRINTF(("%s:sio_data_r(%c) = %02X\n", m_device->machine().describe_context(), 'A' + m_index, m_inbuf));
+
+ return m_inbuf;
+}
+
+
+//-------------------------------------------------
+// dtr - return the state of the DTR line
+//-------------------------------------------------
+
+int z80sio_device::sio_channel::dtr()
+{
+ return ((m_regs[5] & SIO_WR5_DTR) != 0);
+}
+
+
+//-------------------------------------------------
+// rts - return the state of the RTS line
+//-------------------------------------------------
+
+int z80sio_device::sio_channel::rts()
+{
+ return ((m_regs[5] & SIO_WR5_RTS) != 0);
+}
+
+
+//-------------------------------------------------
+// set_cts - set the state of the CTS line
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::set_cts(int state)
+{
+ m_device->machine().scheduler().synchronize(FUNC(static_change_input_line), (SIO_RR0_CTS << 1) + (state != 0), this);
+}
+
+
+//-------------------------------------------------
+// set_dcd - set the state of the DCD line
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::set_dcd(int state)
+{
+ m_device->machine().scheduler().synchronize(FUNC(static_change_input_line), (SIO_RR0_DCD << 1) + (state != 0), this);
+}
+
+
+//-------------------------------------------------
+// receive_data - receive data on the input lines
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::receive_data(int data)
+{
+ // put it on the queue
+ int newinptr = (m_receive_inptr + 1) % ARRAY_LENGTH(m_receive_buffer);
+ if (newinptr != m_receive_outptr)
+ {
+ m_receive_buffer[m_receive_inptr] = data;
+ m_receive_inptr = newinptr;
+ }
+ else
+ logerror("z80sio_receive_data: buffer overrun\n");
+}
+
+
+//-------------------------------------------------
+// change_input_line - generically change the
+// state of an input line; designed to be called
+// from a timer callback
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::change_input_line(int line, int state)
+{
+ VPRINTF(("sio_change_input_line(%c, %s) = %d\n", 'A' + m_index, (line == SIO_RR0_CTS) ? "CTS" : "DCD", state));
+
+ // remember the old value
+ UINT8 old = m_status[0];
+
+ // set the bit in the status register
+ m_status[0] &= ~line;
+ if (state)
+ m_status[0] |= line;
+
+ // if state change interrupts are enabled, signal
+ if (((old ^ m_status[0]) & line) && (m_regs[1] & SIO_WR1_STATUSINT_ENABLE))
+ set_interrupt(INT_STATUS);
+}
+
+
+//-------------------------------------------------
+// serial_callback - callback to pump
+// data through
+//-------------------------------------------------
+
+void z80sio_device::sio_channel::serial_callback()
+{
+ int data = -1;
+
+ // first perform any outstanding transmits
+ if (m_outbuf != -1)
+ {
+ VPRINTF(("serial_callback(%c): Transmitting %02x\n", 'A' + m_index, m_outbuf));
+
+ // actually transmit the character
+ if (m_device->m_transmit_cb != NULL)
+ (*m_device->m_transmit_cb)(m_device, m_index, m_outbuf);
+
+ // update the status register
+ m_status[0] |= SIO_RR0_TX_BUFFER_EMPTY;
+
+ // set the transmit buffer empty interrupt if enabled
+ if (m_regs[1] & SIO_WR1_TXINT_ENABLE)
+ set_interrupt(INT_TRANSMIT);
+
+ // reset the output buffer
+ m_outbuf = -1;
+ }
+
+ // ask the polling callback if there is data to receive
+ if (m_device->m_receive_poll_cb != NULL)
+ data = (*m_device->m_receive_poll_cb)(m_device, m_index);
+
+ // if we have buffered data, pull it
+ if (m_receive_inptr != m_receive_outptr)
+ {
+ data = m_receive_buffer[m_receive_outptr];
+ m_receive_outptr = (m_receive_outptr + 1) % ARRAY_LENGTH(m_receive_buffer);
+ }
+
+ // if we have data, receive it
+ if (data != -1)
+ {
+ VPRINTF(("serial_callback(%c): Receiving %02x\n", 'A' + m_index, data));
+
+ // if rx not enabled, just ignore it
+ if (!(m_regs[3] & SIO_WR3_RX_ENABLE))
+ {
+ VPRINTF((" (ignored because receive is disabled)\n"));
+ return;
+ }
+
+ // stash the data and update the status
+ m_inbuf = data;
+ m_status[0] |= SIO_RR0_RX_CHAR_AVAILABLE;
+
+ // update our interrupt state
+ switch (m_regs[1] & SIO_WR1_RXINT_MASK)
+ {
+ case SIO_WR1_RXINT_FIRST:
+ if (!m_int_on_next_rx)
+ break;
+
+ case SIO_WR1_RXINT_ALL_NOPARITY:
+ case SIO_WR1_RXINT_ALL_PARITY:
+ set_interrupt(INT_RECEIVE);
+ break;
+ }
+ m_int_on_next_rx = false;
+ }
+}
+
+
+
+//**************************************************************************
+// GLOBAL STUBS
+//**************************************************************************
+
+WRITE8_DEVICE_HANDLER( z80sio_c_w ) { downcast<z80sio_device *>(device)->control_write(offset & 1, data); }
+WRITE8_DEVICE_HANDLER( z80sio_d_w ) { downcast<z80sio_device *>(device)->data_write(offset & 1, data); }
+READ8_DEVICE_HANDLER( z80sio_c_r ) { return downcast<z80sio_device *>(device)->control_read(offset & 1); }
+READ8_DEVICE_HANDLER( z80sio_d_r ) { return downcast<z80sio_device *>(device)->data_read(offset & 1); }
+
+READ8_DEVICE_HANDLER( z80sio_get_dtr ) { return downcast<z80sio_device *>(device)->dtr(offset & 1); }
+READ8_DEVICE_HANDLER( z80sio_get_rts ) { return downcast<z80sio_device *>(device)->rts(offset & 1); }
+
+WRITE8_DEVICE_HANDLER( z80sio_set_cts ) { downcast<z80sio_device *>(device)->set_cts(offset & 1, data); }
+WRITE8_DEVICE_HANDLER( z80sio_set_dcd ) { downcast<z80sio_device *>(device)->set_dcd(offset & 1, data); }
+WRITE8_DEVICE_HANDLER( z80sio_receive_data ) { downcast<z80sio_device *>(device)->receive_data(offset & 1, data); }
+
+READ8_DEVICE_HANDLER( z80sio_cd_ba_r )
+{
+ switch (offset & 3)
+ {
+ case 0: return z80sio_d_r(device, 0);
+ case 1: return z80sio_d_r(device, 1);
+ case 2: return z80sio_c_r(device, 0);
+ case 3: return z80sio_c_r(device, 1);
+ }
+
+ return 0xff;
+}
+
+WRITE8_DEVICE_HANDLER( z80sio_cd_ba_w )
+{
+ switch (offset & 3)
+ {
+ case 0: z80sio_d_w(device, 0, data); break;
+ case 1: z80sio_d_w(device, 1, data); break;
+ case 2: z80sio_c_w(device, 0, data); break;
+ case 3: z80sio_c_w(device, 1, data); break;
+ }
+}
+
+READ8_DEVICE_HANDLER( z80sio_ba_cd_r )
+{
+ switch (offset & 3)
+ {
+ case 0: return z80sio_d_r(device, 0);
+ case 1: return z80sio_c_r(device, 0);
+ case 2: return z80sio_d_r(device, 1);
+ case 3: return z80sio_c_r(device, 1);
+ }
+
+ return 0xff;
+}
+
+WRITE8_DEVICE_HANDLER( z80sio_ba_cd_w )
+{
+ switch (offset & 3)
+ {
+ case 0: z80sio_d_w(device, 0, data); break;
+ case 1: z80sio_c_w(device, 0, data); break;
+ case 2: z80sio_d_w(device, 1, data); break;
+ case 3: z80sio_c_w(device, 1, data); break;
+ }
+}