summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/z80sio.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/z80sio.c')
-rw-r--r--src/devices/machine/z80sio.c1429
1 files changed, 1429 insertions, 0 deletions
diff --git a/src/devices/machine/z80sio.c b/src/devices/machine/z80sio.c
new file mode 100644
index 00000000000..09c0e46663f
--- /dev/null
+++ b/src/devices/machine/z80sio.c
@@ -0,0 +1,1429 @@
+// license:BSD-3-Clause
+// copyright-holders:Curt Coder, Joakim Larsson Edstrom
+/***************************************************************************
+
+ Z80-SIO Serial Input/Output emulation
+
+ The variants in the SIO family are only different in the packaging
+ but has the same register features. However, since some signals are
+ not connected to the pins on the package or share a pin with another
+ signal the functionality is limited. However, this driver does not
+ check that an operation is invalid because of package type but relies
+ on the software to be adapated for the particular version.
+
+ Package: DIP40 SIO/0, SIO/1, SIO/2, SIO/9
+ QFP44 SIO/3
+ PLCC44 SIO/4
+ -------------------------------------------------------------------
+ Channels / Full Duplex 2 / Y
+ Synch data rates 2Mhz 500Kbps
+ 4MHz 800Kbps
+ 6MHz 1200Kbps
+ 10MHz 2500Kbps
+ -- Asynchrounous features -------------------------------------------
+ 5-8 bit per char Y
+ 1,1.5,2 stop bits Y
+ odd/even parity Y
+ x1,x16,x32,x64 Y
+ break det/gen Y
+ parity, framing & Y
+ overrun error det Y
+ -- Byte oriented synchrounous features -------------------------------
+ Int/ext char sync Y
+ 1/2 synch chars Y
+ Aut synch char insertion Y
+ Aut CRC gen/det Y
+ -- SDLC/HDLC capabilities --------------------------------------------
+ Abort seq gen/chk Y
+ Aut zero ins/det Y
+ Aut flag insert Y
+ Addr field rec Y
+ 1-fld resid hand Y
+ Valid rec msg protection Y
+ --
+ Receiver FIFO 3
+ Transmitter FIFO 1
+ -------------------------------------------------------------------------
+ * = Features that has been implemented n/a = features that will not
+***************************************************************************/
+
+#include "z80sio.h"
+
+//**************************************************************************
+// MACROS / CONSTANTS
+//**************************************************************************
+
+#define VERBOSE 0
+
+#define LOG(x) do { if (VERBOSE) logerror x; } while (0)
+#if VERBOSE == 2
+#define logerror printf
+#endif
+
+#ifdef _MSC_VER
+#define LLFORMAT "%I64%"
+#define FUNCNAME __func__
+#else
+#define LLFORMAT "%lld"
+#define FUNCNAME __PRETTY_FUNCTION__
+#endif
+
+#define CHANA_TAG "cha"
+#define CHANB_TAG "chb"
+
+
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+// device type definition
+const device_type Z80SIO = &device_creator<z80sio_device>;
+const device_type Z80SIO_CHANNEL = &device_creator<z80sio_channel>;
+
+//-------------------------------------------------
+// device_mconfig_additions -
+//-------------------------------------------------
+
+MACHINE_CONFIG_FRAGMENT( z80sio )
+MCFG_DEVICE_ADD(CHANA_TAG, Z80SIO_CHANNEL, 0)
+MCFG_DEVICE_ADD(CHANB_TAG, Z80SIO_CHANNEL, 0)
+MACHINE_CONFIG_END
+
+machine_config_constructor z80sio_device::device_mconfig_additions() const
+{
+ return MACHINE_CONFIG_NAME( z80sio );
+}
+
+//**************************************************************************
+// LIVE DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// z80sio_device - constructor
+//-------------------------------------------------
+
+z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 variant, const char *shortname, const char *source)
+ : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
+ device_z80daisy_interface(mconfig, *this),
+ m_chanA(*this, CHANA_TAG),
+ m_chanB(*this, CHANB_TAG),
+ m_rxca(0),
+ m_txca(0),
+ m_rxcb(0),
+ m_txcb(0),
+ m_out_txda_cb(*this),
+ m_out_dtra_cb(*this),
+ m_out_rtsa_cb(*this),
+ m_out_wrdya_cb(*this),
+ m_out_synca_cb(*this),
+ m_out_txdb_cb(*this),
+ m_out_dtrb_cb(*this),
+ m_out_rtsb_cb(*this),
+ m_out_wrdyb_cb(*this),
+ m_out_syncb_cb(*this),
+ m_out_int_cb(*this),
+ m_out_rxdrqa_cb(*this),
+ m_out_txdrqa_cb(*this),
+ m_out_rxdrqb_cb(*this),
+ m_out_txdrqb_cb(*this),
+ m_variant(variant)
+{
+ for (int i = 0; i < 8; i++)
+ m_int_state[i] = 0;
+}
+
+z80sio_device::z80sio_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, Z80SIO, "Z80 SIO", tag, owner, clock, "z80sio", __FILE__),
+ device_z80daisy_interface(mconfig, *this),
+ m_chanA(*this, CHANA_TAG),
+ m_chanB(*this, CHANB_TAG),
+ m_rxca(0),
+ m_txca(0),
+ m_rxcb(0),
+ m_txcb(0),
+ m_out_txda_cb(*this),
+ m_out_dtra_cb(*this),
+ m_out_rtsa_cb(*this),
+ m_out_wrdya_cb(*this),
+ m_out_synca_cb(*this),
+ m_out_txdb_cb(*this),
+ m_out_dtrb_cb(*this),
+ m_out_rtsb_cb(*this),
+ m_out_wrdyb_cb(*this),
+ m_out_syncb_cb(*this),
+ m_out_int_cb(*this),
+ m_out_rxdrqa_cb(*this),
+ m_out_txdrqa_cb(*this),
+ m_out_rxdrqb_cb(*this),
+ m_out_txdrqb_cb(*this),
+ m_variant(TYPE_Z80SIO)
+{
+ for (int i = 0; i < 8; i++)
+ m_int_state[i] = 0;
+}
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void z80sio_device::device_start()
+{
+ LOG(("%s\n", FUNCNAME));
+ // resolve callbacks
+ m_out_txda_cb.resolve_safe();
+ m_out_dtra_cb.resolve_safe();
+ m_out_rtsa_cb.resolve_safe();
+ m_out_wrdya_cb.resolve_safe();
+ m_out_synca_cb.resolve_safe();
+ m_out_txdb_cb.resolve_safe();
+ m_out_dtrb_cb.resolve_safe();
+ m_out_rtsb_cb.resolve_safe();
+ m_out_wrdyb_cb.resolve_safe();
+ m_out_syncb_cb.resolve_safe();
+ m_out_int_cb.resolve_safe();
+ m_out_rxdrqa_cb.resolve_safe();
+ m_out_txdrqa_cb.resolve_safe();
+ m_out_rxdrqb_cb.resolve_safe();
+ m_out_txdrqb_cb.resolve_safe();
+
+ // configure channel A
+ m_chanA->m_rxc = m_rxca;
+ m_chanA->m_txc = m_txca;
+
+ // configure channel B
+ m_chanB->m_rxc = m_rxcb;
+ m_chanB->m_txc = m_txcb;
+
+ // state saving
+ save_item(NAME(m_int_state));
+}
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+void z80sio_device::device_reset()
+{
+ LOG(("%s \"%s\" \n", FUNCNAME, tag()));
+
+ m_chanA->reset();
+ m_chanB->reset();
+}
+
+//-------------------------------------------------
+// z80daisy_irq_state - get interrupt status
+//-------------------------------------------------
+int z80sio_device::z80daisy_irq_state()
+{
+ int state = 0;
+ int i;
+
+
+ LOG(("%s %s A:%d%d%d%d B:%d%d%d%d ",FUNCNAME, tag(),
+ 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 (i = 0; i < 8; i++)
+ {
+ // if we're servicing a request, don't indicate more interrupts
+ if (m_int_state[i] & Z80_DAISY_IEO)
+ {
+ state |= Z80_DAISY_IEO;
+ break;
+ }
+ state |= m_int_state[i];
+ }
+
+ LOG(("Interrupt State %u\n", state));
+
+ return state;
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_ack - interrupt acknowledge
+//-------------------------------------------------
+int z80sio_device::z80daisy_irq_ack()
+{
+ int i;
+
+ LOG(("%s %s \n",FUNCNAME, tag()));
+
+ // loop over all interrupt sources
+ for (i = 0; i < 8; i++)
+ {
+ // find the first channel with an interrupt requested
+ if (m_int_state[i] & Z80_DAISY_INT)
+ {
+ // clear interrupt, switch to the IEO state, and update the IRQs
+ m_int_state[i] = Z80_DAISY_IEO;
+ m_chanA->m_rr0 &= ~z80sio_channel::RR0_INTERRUPT_PENDING;
+ check_interrupts();
+
+ //LOG(("%s %s \n",FUNCNAME, tag(), m_chanB->m_rr2));
+
+ return m_chanB->m_rr2;
+ }
+ }
+
+ //logerror("z80sio_irq_ack: failed to find an interrupt to ack!\n");
+
+ return m_chanB->m_rr2;
+}
+
+
+//-------------------------------------------------
+// z80daisy_irq_reti - return from interrupt
+//-------------------------------------------------
+void z80sio_device::z80daisy_irq_reti()
+{
+ int i;
+
+ LOG(("%s %s \n",FUNCNAME, tag()));
+
+ // loop over all interrupt sources
+ for (i = 0; i < 8; i++)
+ {
+ // find the first channel with an IEO pending
+ if (m_int_state[i] & Z80_DAISY_IEO)
+ {
+ // clear the IEO state and update the IRQs
+ m_int_state[i] &= ~Z80_DAISY_IEO;
+ check_interrupts();
+ return;
+ }
+ }
+
+ //logerror("z80sio_irq_reti: failed to find an interrupt to clear IEO on!\n");
+}
+
+
+//-------------------------------------------------
+// check_interrupts -
+//-------------------------------------------------
+void z80sio_device::check_interrupts()
+{
+ LOG(("%s %s \n",FUNCNAME, tag()));
+ int state = (z80daisy_irq_state() & Z80_DAISY_INT) ? ASSERT_LINE : CLEAR_LINE;
+ m_out_int_cb(state);
+}
+
+
+//-------------------------------------------------
+// reset_interrupts -
+//-------------------------------------------------
+void z80sio_device::reset_interrupts()
+{
+ LOG(("%s %s \n",FUNCNAME, tag()));
+ // reset internal interrupt sources
+ for (int i = 0; i < 8; i++)
+ {
+ m_int_state[i] = 0;
+ }
+
+ check_interrupts();
+}
+
+
+//-------------------------------------------------
+// trigger_interrupt - TODO: needs attention for SIO
+//-------------------------------------------------
+void z80sio_device::trigger_interrupt(int index, int state)
+{
+ UINT8 vector = m_chanB->m_wr2;
+ int priority;
+
+ LOG(("%s %s \n",FUNCNAME, tag()));
+
+#if 0
+ if((m_variant == TYPE_I8274) || (m_variant == TYPE_UPD7201))
+ {
+ int prio_level = 0;
+ switch(state)
+ {
+ case z80sio_channel::INT_TRANSMIT:
+ prio_level = 1;
+ break;
+ case z80sio_channel::INT_RECEIVE:
+ case z80sio_channel::INT_SPECIAL:
+ prio_level = 0;
+ break;
+ case z80sio_channel::INT_EXTERNAL:
+ prio_level = 2;
+ break;
+ }
+
+ if(m_chanA->m_wr2 & z80sio_channel::WR2_PRIORITY)
+ {
+ priority = (prio_level * 2) + index;
+ }
+ else
+ {
+ priority = (prio_level == 2) ? index + 4 : ((index * 2) + prio_level);
+ }
+ if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
+ {
+ vector = (!index << 2) | state;
+ if((m_chanA->m_wr1 & 0x18) == z80sio_channel::WR2_MODE_8086_8088)
+ {
+ vector = (m_chanB->m_wr2 & 0xf8) | vector;
+ }
+ else
+ {
+ vector = (m_chanB->m_wr2 & 0xe3) | (vector << 2);
+ }
+ }
+ }
+ else
+ {
+#endif
+ priority = (index << 2) | state;
+ if (m_chanB->m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
+ {
+ // status affects vector
+ vector = (m_chanB->m_wr2 & 0xf1) | (!index << 3) | (state << 1);
+ }
+// }
+ // update vector register
+ m_chanB->m_rr2 = vector;
+
+ // trigger interrupt
+ m_int_state[priority] |= Z80_DAISY_INT;
+ m_chanA->m_rr0 |= z80sio_channel::RR0_INTERRUPT_PENDING;
+
+ // check for interrupt
+ check_interrupts();
+}
+
+
+//-------------------------------------------------
+// m1_r - interrupt acknowledge
+//-------------------------------------------------
+int z80sio_device::m1_r()
+{
+ LOG(("%s %s \n",FUNCNAME, tag()));
+ return z80daisy_irq_ack();
+}
+
+
+//-------------------------------------------------
+// cd_ba_r -
+//-------------------------------------------------
+READ8_MEMBER( z80sio_device::cd_ba_r )
+{
+ int ba = BIT(offset, 0);
+ int cd = BIT(offset, 1);
+ z80sio_channel *channel = ba ? m_chanB : m_chanA;
+
+ //LOG(("%s %s %c %s read\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" ));
+
+ return cd ? channel->control_read() : channel->data_read();
+}
+
+
+//-------------------------------------------------
+// cd_ba_w -
+//-------------------------------------------------
+WRITE8_MEMBER( z80sio_device::cd_ba_w )
+{
+ int ba = BIT(offset, 0);
+ int cd = BIT(offset, 1);
+ z80sio_channel *channel = ba ? m_chanB : m_chanA;
+
+ LOG(("%s %s %c %s write\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" ));
+
+ if (cd)
+ channel->control_write(data);
+ else
+ channel->data_write(data);
+}
+
+
+//-------------------------------------------------
+// ba_cd_r -
+//-------------------------------------------------
+READ8_MEMBER( z80sio_device::ba_cd_r )
+{
+ int ba = BIT(offset, 1);
+ int cd = BIT(offset, 0);
+ z80sio_channel *channel = ba ? m_chanB : m_chanA;
+
+ //LOG(("%s %s %c %s read\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" ));
+
+ return cd ? channel->control_read() : channel->data_read();
+}
+
+
+//-------------------------------------------------
+// ba_cd_w -
+//-------------------------------------------------
+WRITE8_MEMBER( z80sio_device::ba_cd_w )
+{
+ int ba = BIT(offset, 1);
+ int cd = BIT(offset, 0);
+ z80sio_channel *channel = ba ? m_chanB : m_chanA;
+
+ LOG(("%s %s %c %s write\n",FUNCNAME, tag(), 'A' + ba ? 1 : 0 , cd ? "control" : "data" ));
+
+ if (cd)
+ channel->control_write(data);
+ else
+ channel->data_write(data);
+}
+
+//**************************************************************************
+// SIO CHANNEL
+//**************************************************************************
+
+//-------------------------------------------------
+// z80sio_channel - constructor
+//-------------------------------------------------
+z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : device_t(mconfig, Z80SIO_CHANNEL, "Z80 SIO channel", tag, owner, clock, "z80sio_channel", __FILE__),
+ device_serial_interface(mconfig, *this),
+ m_rx_error(0),
+ m_rx_fifo(-1),
+ m_rx_clock(0),
+ m_rx_first(0),
+ m_rx_break(0),
+ m_rx_rr0_latch(0),
+ m_rxd(0),
+ m_sh(0),
+ m_cts(0),
+ m_dcd(0),
+ m_tx_data(0),
+ m_tx_clock(0),
+ m_dtr(0),
+ m_rts(0),
+ m_sync(0)
+{
+ LOG(("%s\n",FUNCNAME));
+ // Reset all registers
+ m_rr0 = m_rr1 = m_rr2 = 0;
+ m_wr0 = m_wr1 = m_wr2 = m_wr3 = m_wr4 = m_wr5 = m_wr6 = m_wr7 = 0;
+
+ for (int i = 0; i < 3; i++)
+ {
+ m_rx_data_fifo[i] = 0;
+ m_rx_error_fifo[i] = 0;
+ }
+}
+
+
+//-------------------------------------------------
+// start - channel startup
+//-------------------------------------------------
+void z80sio_channel::device_start()
+{
+ LOG(("%s\n",FUNCNAME));
+ m_uart = downcast<z80sio_device *>(owner());
+ m_index = m_uart->get_channel_index(this);
+ m_variant = ((z80sio_device *)m_owner)->m_variant;
+
+ // state saving
+ save_item(NAME(m_rr0));
+ save_item(NAME(m_rr1));
+ save_item(NAME(m_rr2));
+ save_item(NAME(m_wr0));
+ save_item(NAME(m_wr1));
+ save_item(NAME(m_wr2));
+ save_item(NAME(m_wr3));
+ save_item(NAME(m_wr4));
+ save_item(NAME(m_wr5));
+ save_item(NAME(m_wr6));
+ save_item(NAME(m_wr7));
+ save_item(NAME(m_rx_data_fifo));
+ save_item(NAME(m_rx_error_fifo));
+ save_item(NAME(m_rx_error));
+ save_item(NAME(m_rx_fifo));
+ save_item(NAME(m_rx_clock));
+ save_item(NAME(m_rx_first));
+ save_item(NAME(m_rx_break));
+ save_item(NAME(m_rx_rr0_latch));
+ save_item(NAME(m_sh));
+ save_item(NAME(m_cts));
+ save_item(NAME(m_dcd));
+ save_item(NAME(m_tx_data));
+ save_item(NAME(m_tx_clock));
+ save_item(NAME(m_dtr));
+ save_item(NAME(m_rts));
+ save_item(NAME(m_sync));
+ save_item(NAME(m_variant));
+ device_serial_interface::register_save_state(machine().save(), this);
+}
+
+
+//-------------------------------------------------
+// reset - reset channel status
+//-------------------------------------------------
+void z80sio_channel::device_reset()
+{
+ LOG(("%s\n", FUNCNAME));
+
+ // Reset RS232 emulation
+ receive_register_reset();
+ transmit_register_reset();
+
+ // disable receiver
+ m_wr3 &= ~WR3_RX_ENABLE;
+
+ // disable transmitter
+ m_wr5 &= ~WR5_TX_ENABLE;
+ m_rr0 |= RR0_TX_BUFFER_EMPTY;
+ m_rr1 |= RR1_ALL_SENT;
+
+ // reset external lines
+ set_rts(1);
+ set_dtr(1);
+
+ // reset interrupts
+ if (m_index == z80sio_device::CHANNEL_A)
+ {
+ m_uart->reset_interrupts();
+ }
+}
+
+void z80sio_channel::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ device_serial_interface::device_timer(timer, id, param, ptr);
+}
+
+//-------------------------------------------------
+// tra_callback -
+//-------------------------------------------------
+void z80sio_channel::tra_callback()
+{
+ if (!(m_wr5 & WR5_TX_ENABLE))
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c transmit mark 1 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5));
+ // transmit mark
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_txda_cb(1);
+ else
+ m_uart->m_out_txdb_cb(1);
+ }
+ else if (m_wr5 & WR5_SEND_BREAK)
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c send break 1 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5));
+ // transmit break
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_txda_cb(0);
+ else
+ m_uart->m_out_txdb_cb(0);
+ }
+ else if (!is_transmit_register_empty())
+ {
+ int db = transmit_register_get_data_bit();
+
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c transmit data bit %d m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, db, m_wr5));
+ // transmit data
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_txda_cb(db);
+ else
+ m_uart->m_out_txdb_cb(db);
+ }
+ else
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Failed to transmit m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5));
+ logerror("%s \"%s \"Channel %c Failed to transmit\n", FUNCNAME, m_owner->tag(), 'A' + m_index);
+ }
+}
+
+
+//-------------------------------------------------
+// tra_complete -
+//-------------------------------------------------
+void z80sio_channel::tra_complete()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ if ((m_wr5 & WR5_TX_ENABLE) && !(m_wr5 & WR5_SEND_BREAK) && !(m_rr0 & RR0_TX_BUFFER_EMPTY))
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_tx_data, m_wr5));
+
+ transmit_register_setup(m_tx_data);
+
+ // empty transmit buffer
+ m_rr0 |= RR0_TX_BUFFER_EMPTY;
+
+ if (m_wr1 & WR1_TX_INT_ENABLE)
+ m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
+ }
+ else if (m_wr5 & WR5_SEND_BREAK)
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Transmit Break 0 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5));
+ // transmit break
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_txda_cb(0);
+ else
+ m_uart->m_out_txdb_cb(0);
+ }
+ else
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Transmit Mark 1 m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5));
+ // transmit mark
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_txda_cb(1);
+ else
+ m_uart->m_out_txdb_cb(1);
+ }
+
+ // if transmit buffer is empty
+ if (m_rr0 & RR0_TX_BUFFER_EMPTY)
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Transmit buffer empty m_wr5:%02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_wr5));
+ // then all characters have been sent
+ m_rr1 |= RR1_ALL_SENT;
+
+ // when the RTS bit is reset, the _RTS output goes high after the transmitter empties
+ if (!m_rts)
+ set_rts(1);
+ }
+}
+
+
+//-------------------------------------------------
+// rcv_callback -
+//-------------------------------------------------
+void z80sio_channel::rcv_callback()
+{
+ if (m_wr3 & WR3_RX_ENABLE)
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Received Data Bit %d\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, m_rxd));
+ receive_register_update_bit(m_rxd);
+ }
+#if 0
+ else
+ {
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Received Data Bit but receiver is disabled\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index));
+ logerror("Z80SIO %s() \"%s \"Channel %c Received data dit but receiver is disabled\n", __func__, m_owner->tag(), 'A' + m_index);
+ }
+#endif
+}
+
+
+//-------------------------------------------------
+// rcv_complete -
+//-------------------------------------------------
+void z80sio_channel::rcv_complete()
+{
+ UINT8 data;
+
+ receive_register_extract();
+ data = get_received_char();
+ LOG(("%" I64FMT "d %s() \"%s \"Channel %c Received Data %02x\n", machine().firstcpu->total_cycles(), FUNCNAME, m_owner->tag(), 'A' + m_index, data));
+ receive_data(data);
+}
+
+
+//-------------------------------------------------
+// get_clock_mode - get clock divisor
+//-------------------------------------------------
+
+int z80sio_channel::get_clock_mode()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ int clocks = 1;
+
+ switch (m_wr4 & WR4_CLOCK_RATE_MASK)
+ {
+ case WR4_CLOCK_RATE_X1: clocks = 1; break;
+ case WR4_CLOCK_RATE_X16: clocks = 16; break;
+ case WR4_CLOCK_RATE_X32: clocks = 32; break;
+ case WR4_CLOCK_RATE_X64: clocks = 64; break;
+ }
+
+ return clocks;
+}
+
+/* From "uPD7201/7201A MULTI PROTOCOL SERIAL COMMUNICATION CONTROLLER" by NEC:
+"RTSA (Request to Send A): The state of the RTS bit (01 of the CR5 register) controls this pin. If
+the RTS bit is reset in the asynchronous mode, a high level will not be output on the RTS pin until
+all transmit characters are written and the all sent bit (D0 of the SR1 register) is set. In the
+synchronous mode, the state of the RTS bit is used as is. That is, when the RTS bit is 0, the RTS
+pin is 1. When the RTS bit is 1, the RTS pin is O."
+
+CR5 = m_wr5 and SR1 = m_rr1
+
+*/
+
+void z80sio_channel::set_rts(int state)
+{
+ LOG(("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, m_owner->tag(), 'A' + m_index));
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_rtsa_cb(state);
+ else
+ m_uart->m_out_rtsb_cb(state);
+}
+
+void z80sio_channel::update_rts()
+{
+ // LOG(("%s(%d) \"%s\" Channel %c \n", FUNCNAME, state, m_owner->tag(), 'A' + m_index));
+ LOG(("%s() \"%s\" Channel %c \n", FUNCNAME, m_owner->tag(), 'A' + m_index));
+ if (m_wr5 & WR5_RTS)
+ {
+ // when the RTS bit is set, the _RTS output goes low
+ set_rts(0);
+ m_rts = 1;
+ }
+ else
+ {
+ // when the RTS bit is reset, the _RTS output goes high after the transmitter empties
+ m_rts = 0;
+ }
+
+ // data terminal ready output follows the state programmed into the DTR bit*/
+ set_dtr((m_wr5 & WR5_DTR) ? 0 : 1);
+}
+
+//-------------------------------------------------
+// get_stop_bits - get number of stop bits
+//-------------------------------------------------
+
+device_serial_interface::stop_bits_t z80sio_channel::get_stop_bits()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ switch (m_wr4 & WR4_STOP_BITS_MASK)
+ {
+ case WR4_STOP_BITS_1: return STOP_BITS_1;
+ case WR4_STOP_BITS_1_5: return STOP_BITS_1_5;
+ case WR4_STOP_BITS_2: return STOP_BITS_2;
+ }
+
+ return STOP_BITS_0;
+}
+
+
+//-------------------------------------------------
+// get_rx_word_length - get receive word length
+//-------------------------------------------------
+
+int z80sio_channel::get_rx_word_length()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ int bits = 5;
+
+ switch (m_wr3 & WR3_RX_WORD_LENGTH_MASK)
+ {
+ case WR3_RX_WORD_LENGTH_5: bits = 5; break;
+ case WR3_RX_WORD_LENGTH_6: bits = 6; break;
+ case WR3_RX_WORD_LENGTH_7: bits = 7; break;
+ case WR3_RX_WORD_LENGTH_8: bits = 8; break;
+ }
+
+ return bits;
+}
+
+
+//-------------------------------------------------
+// get_tx_word_length - get transmit word length
+//-------------------------------------------------
+
+int z80sio_channel::get_tx_word_length()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ int bits = 5;
+
+ switch (m_wr5 & WR5_TX_WORD_LENGTH_MASK)
+ {
+ case WR5_TX_WORD_LENGTH_5: bits = 5; break;
+ case WR5_TX_WORD_LENGTH_6: bits = 6; break;
+ case WR5_TX_WORD_LENGTH_7: bits = 7; break;
+ case WR5_TX_WORD_LENGTH_8: bits = 8; break;
+ }
+
+ return bits;
+}
+
+/*
+ * This register contains the status of the receive and transmit buffers; the
+ * DCD, CTS, and SYNC inputs; the Transmit Underrun/EOM latch; and the
+ * Break/Abort latch. */
+UINT8 z80sio_channel::do_sioreg_rr0()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ return m_rr0;
+}
+/*
+ * This register contains the Special Receive condition status bits and Residue
+ * codes for the I-Field in the SDLC Receive Mode. */
+UINT8 z80sio_channel::do_sioreg_rr1()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ return m_rr1;
+}
+/*
+ * This register contains the interrupt vector written into WR2 if the Status
+Affects Vector control bit is not set. If the control bit is set, it contains the
+modified vector listed in the Status Affects Vector paragraph of the Write
+Register 1 section. When this register is read, the vector returned is modi-
+fied by the highest priority interrupting condition at the time of the read. If
+no interrupts are pending, the vector is modified with V3 = 0, V2 = 1, and
+V1 = 1. This register is read only through Channel B. */
+UINT8 z80sio_channel::do_sioreg_rr2()
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ // channel B only
+ return m_index == z80sio_device::CHANNEL_B ? m_rr2 : 0;
+}
+
+
+//-------------------------------------------------
+// control_read - read control register
+//-------------------------------------------------
+
+UINT8 z80sio_channel::control_read()
+{
+ UINT8 data = 0;
+ UINT8 reg = m_wr0 & WR0_REGISTER_MASK;
+
+ //LOG(("%s %s\n",FUNCNAME, tag()));
+ if (reg != 0)
+ {
+ // mask out register index
+ m_wr0 &= ~WR0_REGISTER_MASK;
+ }
+
+ switch (reg)
+ {
+ case REG_RR0_STATUS: data = do_sioreg_rr0(); break;
+ case REG_RR1_SPEC_RCV_COND: data = do_sioreg_rr1(); break;
+ case REG_RR2_INTERRUPT_VECT: data = do_sioreg_rr2(); break;
+ default:
+ logerror("Z80SIO \"%s\" Channel %c : Unsupported RRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg);
+ LOG(("%s %s unsupported register:%02x\n",FUNCNAME, tag(), reg));
+ }
+
+ return data;
+}
+
+/* SIO CRC Initialization Code handling - candidate for breaking out in a z80sio_base class
+ Handle the WR0 CRC Reset/Init bits separatelly, needed by derived devices separatelly from the commands */
+void z80sio_channel::do_sioreg_wr0_resets(UINT8 data)
+{
+ LOG(("%s %s\n",FUNCNAME, tag()));
+ switch (data & WR0_CRC_RESET_CODE_MASK)
+ {
+ case WR0_CRC_RESET_NULL:
+ LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_NULL\n", m_owner->tag(), 'A' + m_index));
+ break;
+ case WR0_CRC_RESET_RX: /* In Synchronous mode: all Os (zeros) (CCITT-O CRC-16) */
+ LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_RX - not implemented\n", m_owner->tag(), 'A' + m_index));
+ break;
+ case WR0_CRC_RESET_TX: /* In HDLC mode: all 1s (ones) (CCITT-1) */
+ LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_TX - not implemented\n", m_owner->tag(), 'A' + m_index));
+ break;
+ case WR0_CRC_RESET_TX_UNDERRUN: /* Resets Tx underrun/EOM bit (D6 of the SRO register) */
+ LOG(("Z80SIO \"%s\" Channel %c : CRC_RESET_TX_UNDERRUN - not implemented\n", m_owner->tag(), 'A' + m_index));
+ break;
+ default: /* Will not happen unless someone messes with the mask */
+ logerror("Z80SIO \"%s\" Channel %c : %s Wrong CRC reset/init command:%02x\n", m_owner->tag(), 'A' + m_index, FUNCNAME, data & WR0_CRC_RESET_CODE_MASK);
+ }
+}
+
+void z80sio_channel::do_sioreg_wr0(UINT8 data)
+{
+ m_wr0 = data;
+ switch (data & WR0_COMMAND_MASK)
+ {
+ case WR0_NULL:
+ LOG(("Z80SIO \"%s\" Channel %c : Null\n", m_owner->tag(), 'A' + m_index));
+ break;
+ case WR0_RESET_EXT_STATUS:
+ // reset external/status interrupt
+ m_rr0 &= ~(RR0_DCD | RR0_SYNC_HUNT | RR0_CTS | RR0_BREAK_ABORT);
+ // release the latch
+ m_rx_rr0_latch = 0;
+ // update register to reflect wire values TODO: Check if this will fire new interrupts
+ if (!m_dcd) m_rr0 |= RR0_DCD;
+ if (m_sync) m_rr0 |= RR0_SYNC_HUNT;
+ if (m_cts) m_rr0 |= RR0_CTS;
+
+ LOG(("Z80SIO \"%s\" Channel %c : Reset External/Status Interrupt\n", m_owner->tag(), 'A' + m_index));
+ break;
+ case WR0_CHANNEL_RESET:
+ // channel reset
+ LOG(("Z80SIO \"%s\" Channel %c : Channel Reset\n", m_owner->tag(), 'A' + m_index));
+ device_reset();
+ break;
+ case WR0_ENABLE_INT_NEXT_RX:
+ // enable interrupt on next receive character
+ LOG(("Z80SIO \"%s\" Channel %c : Enable Interrupt on Next Received Character\n", m_owner->tag(), 'A' + m_index));
+ m_rx_first = 1;
+ break;
+ case WR0_RESET_TX_INT:
+ // reset transmitter interrupt pending
+ LOG(("Z80SIO \"%s\" Channel %c : Reset Transmitter Interrupt Pending\n", m_owner->tag(), 'A' + m_index));
+ logerror("Z80SIO \"%s\" Channel %c : unsupported command: Reset Transmitter Interrupt Pending\n", m_owner->tag(), 'A' + m_index);
+ break;
+ case WR0_ERROR_RESET:
+ // error reset
+ LOG(("Z80SIO \"%s\" Channel %c : Error Reset\n", m_owner->tag(), 'A' + m_index));
+ m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
+ break;
+ case WR0_RETURN_FROM_INT:
+ // return from interrupt
+ LOG(("Z80SIO \"%s\" Channel %c : Return from Interrupt\n", m_owner->tag(), 'A' + m_index));
+ m_uart->z80daisy_irq_reti();
+ break;
+ default:
+ LOG(("Z80SIO \"%s\" Channel %c : Unsupported WR0 command %02x mask %02x\n", m_owner->tag(), 'A' + m_index, data, WR0_REGISTER_MASK));
+
+ }
+ do_sioreg_wr0_resets(data);
+}
+
+void z80sio_channel::do_sioreg_wr1(UINT8 data)
+{
+/* TODO: implement vector modifications when WR1 bit D2 is changed */
+ m_wr1 = data;
+ LOG(("Z80SIO \"%s\" Channel %c : External Interrupt Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_EXT_INT_ENABLE) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Transmit Interrupt Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_TX_INT_ENABLE) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Status Affects Vector %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_STATUS_VECTOR) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Wait/Ready Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR1_WRDY_ENABLE) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Wait/Ready Function %s\n", m_owner->tag(), 'A' + m_index, (data & WR1_WRDY_FUNCTION) ? "Ready" : "Wait"));
+ LOG(("Z80SIO \"%s\" Channel %c : Wait/Ready on %s\n", m_owner->tag(), 'A' + m_index, (data & WR1_WRDY_ON_RX_TX) ? "Receive" : "Transmit"));
+
+ switch (data & WR1_RX_INT_MODE_MASK)
+ {
+ case WR1_RX_INT_DISABLE:
+ LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt Disabled\n", m_owner->tag(), 'A' + m_index));
+ break;
+
+ case WR1_RX_INT_FIRST:
+ LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt on First Character\n", m_owner->tag(), 'A' + m_index));
+ break;
+
+ case WR1_RX_INT_ALL_PARITY:
+ LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters, Parity Affects Vector\n", m_owner->tag(), 'A' + m_index));
+ break;
+
+ case WR1_RX_INT_ALL:
+ LOG(("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters\n", m_owner->tag(), 'A' + m_index));
+ break;
+ }
+}
+
+void z80sio_channel::do_sioreg_wr2(UINT8 data)
+{
+ m_wr2 = data;
+ if (m_index == z80sio_device::CHANNEL_B)
+ {
+ if (m_wr1 & z80sio_channel::WR1_STATUS_VECTOR)
+ m_rr2 = ( m_rr2 & 0x0e ) | ( m_wr2 & 0xF1);
+ else
+ m_rr2 = m_wr2;
+ }
+ m_uart->check_interrupts();
+ LOG(("Z80SIO \"%s\" Channel %c : Interrupt Vector %02x\n", m_owner->tag(), 'A' + m_index, data));
+}
+
+void z80sio_channel::do_sioreg_wr3(UINT8 data)
+{
+ m_wr3 = data;
+ LOG(("Z80SIO \"%s\" Channel %c : Receiver Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR3_RX_ENABLE) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Auto Enables %u\n", m_owner->tag(), 'A' + m_index, (data & WR3_AUTO_ENABLES) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Receiver Bits/Character %u\n", m_owner->tag(), 'A' + m_index, get_rx_word_length()));
+}
+
+void z80sio_channel::do_sioreg_wr4(UINT8 data)
+{
+ m_wr4 = data;
+ LOG(("Z80SIO \"%s\" Channel %c : Parity Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR4_PARITY_ENABLE) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Parity %s\n", m_owner->tag(), 'A' + m_index, (data & WR4_PARITY_EVEN) ? "Even" : "Odd"));
+ LOG(("Z80SIO \"%s\" Channel %c : Stop Bits %s\n", m_owner->tag(), 'A' + m_index, stop_bits_tostring(get_stop_bits())));
+ LOG(("Z80SIO \"%s\" Channel %c : Clock Mode %uX\n", m_owner->tag(), 'A' + m_index, get_clock_mode()));
+}
+
+void z80sio_channel::do_sioreg_wr5(UINT8 data)
+{
+ m_wr5 = data;
+ LOG(("Z80SIO \"%s\" Channel %c : Transmitter Enable %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_TX_ENABLE) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Transmitter Bits/Character %u\n", m_owner->tag(), 'A' + m_index, get_tx_word_length()));
+ LOG(("Z80SIO \"%s\" Channel %c : Send Break %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_SEND_BREAK) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Request to Send %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_RTS) ? 1 : 0));
+ LOG(("Z80SIO \"%s\" Channel %c : Data Terminal Ready %u\n", m_owner->tag(), 'A' + m_index, (data & WR5_DTR) ? 1 : 0));
+}
+
+void z80sio_channel::do_sioreg_wr6(UINT8 data)
+{
+ LOG(("Z80SIO \"%s\" Channel %c : Transmit Sync %02x\n", m_owner->tag(), 'A' + m_index, data));
+ m_sync = (m_sync & 0xff00) | data;
+}
+
+void z80sio_channel::do_sioreg_wr7(UINT8 data)
+{
+ LOG(("Z80SIO \"%s\" Channel %c : Receive Sync %02x\n", m_owner->tag(), 'A' + m_index, data));
+ m_sync = (data << 8) | (m_sync & 0xff);
+}
+
+//-------------------------------------------------
+// control_write - write control register
+//-------------------------------------------------
+void z80sio_channel::control_write(UINT8 data)
+{
+ UINT8 reg = m_wr0 & WR0_REGISTER_MASK;
+
+ if (reg != 0)
+ {
+ // mask out register index
+ m_wr0 &= ~WR0_REGISTER_MASK;
+ }
+
+ LOG(("\n%s(%02x) reg %02x\n", FUNCNAME, data, reg));
+
+ switch (reg)
+ {
+ case REG_WR0_COMMAND_REGPT: do_sioreg_wr0(data); break;
+ case REG_WR1_INT_DMA_ENABLE: do_sioreg_wr1(data); m_uart->check_interrupts(); break;
+ case REG_WR2_INT_VECTOR: do_sioreg_wr2(data); break;
+ case REG_WR3_RX_CONTROL: do_sioreg_wr3(data); update_serial(); break;
+ case REG_WR4_RX_TX_MODES: do_sioreg_wr4(data); update_serial(); break;
+ case REG_WR5_TX_CONTROL: do_sioreg_wr5(data); update_serial(); update_rts(); break;
+ case REG_WR6_SYNC_OR_SDLC_A: do_sioreg_wr6(data); break;
+ case REG_WR7_SYNC_OR_SDLC_F: do_sioreg_wr7(data); break;
+ default:
+ logerror("Z80SIO \"%s\" Channel %c : Unsupported WRx register:%02x\n", m_owner->tag(), 'A' + m_index, reg);
+ }
+}
+
+
+//-------------------------------------------------
+// data_read - read data register
+//-------------------------------------------------
+UINT8 z80sio_channel::data_read()
+{
+ UINT8 data = 0;
+
+ if (m_rx_fifo >= 0)
+ {
+ // load data from the FIFO
+ data = m_rx_data_fifo[m_rx_fifo];
+
+ // load error status from the FIFO
+ m_rr1 = (m_rr1 & ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR)) | m_rx_error_fifo[m_rx_fifo];
+
+ // decrease FIFO pointer
+ m_rx_fifo--;
+
+ if (m_rx_fifo < 0)
+ {
+ // no more characters available in the FIFO
+ m_rr0 &= ~ RR0_RX_CHAR_AVAILABLE;
+ }
+ }
+
+ LOG(("Z80SIO \"%s\" Channel %c : Data Register Read '%02x'\n", m_owner->tag(), 'A' + m_index, data));
+
+ return data;
+}
+
+
+//-------------------------------------------------
+// data_write - write data register
+//-------------------------------------------------
+void z80sio_channel::data_write(UINT8 data)
+{
+ m_tx_data = data;
+
+ if ((m_wr5 & WR5_TX_ENABLE) && is_transmit_register_empty())
+ {
+ LOG(("Z80SIO \"%s\" Channel %c : Transmit Data Byte '%02x'\n", m_owner->tag(), 'A' + m_index, m_tx_data));
+
+ transmit_register_setup(m_tx_data);
+
+ // empty transmit buffer
+ m_rr0 |= RR0_TX_BUFFER_EMPTY;
+
+ if (m_wr1 & WR1_TX_INT_ENABLE)
+ m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
+ }
+ else
+ {
+ LOG((" Transmitter %s, data byte dropped\n", m_wr5 & WR5_TX_ENABLE ? "not enabled" : "not emptied"));
+ m_rr0 &= ~RR0_TX_BUFFER_EMPTY;
+ }
+
+ m_rr1 &= ~RR1_ALL_SENT;
+}
+
+
+//-------------------------------------------------
+// receive_data - receive data word
+//-------------------------------------------------
+void z80sio_channel::receive_data(UINT8 data)
+{
+ LOG(("%s(%02x) %s:%c\n",FUNCNAME, data, tag(), 'A' + m_index));
+
+ if (m_rx_fifo == 2)
+ {
+ LOG((" Overrun detected\n"));
+ // receive overrun error detected
+ m_rx_error |= RR1_RX_OVERRUN_ERROR;
+
+ switch (m_wr1 & WR1_RX_INT_MODE_MASK)
+ {
+ case WR1_RX_INT_FIRST:
+ if (!m_rx_first)
+ {
+ m_uart->trigger_interrupt(m_index, INT_SPECIAL);
+ }
+ break;
+
+ case WR1_RX_INT_ALL_PARITY:
+ case WR1_RX_INT_ALL:
+ m_uart->trigger_interrupt(m_index, INT_SPECIAL);
+ break;
+ }
+ }
+ else
+ {
+ m_rx_fifo++;
+ }
+
+ // store received character and error status into FIFO
+ m_rx_data_fifo[m_rx_fifo] = data;
+ m_rx_error_fifo[m_rx_fifo] = m_rx_error;
+
+ m_rr0 |= RR0_RX_CHAR_AVAILABLE;
+
+ // receive interrupt
+ switch (m_wr1 & WR1_RX_INT_MODE_MASK)
+ {
+ case WR1_RX_INT_FIRST:
+ if (m_rx_first)
+ {
+ m_uart->trigger_interrupt(m_index, INT_RECEIVE);
+ m_rx_first = 0;
+ }
+ break;
+
+ case WR1_RX_INT_ALL_PARITY:
+ case WR1_RX_INT_ALL:
+ m_uart->trigger_interrupt(m_index, INT_RECEIVE);
+ break;
+ default:
+ LOG(("No interrupt triggered\n"));
+
+ }
+}
+
+
+//-------------------------------------------------
+// cts_w - clear to send handler
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( z80sio_channel::cts_w )
+{
+ LOG(("%s(%02x) %s:%c\n",FUNCNAME, state, tag(), 'A' + m_index));
+
+ if (m_cts != state)
+ {
+ // enable transmitter if in auto enables mode
+ if (!state)
+ if (m_wr3 & WR3_AUTO_ENABLES)
+ m_wr5 |= WR5_TX_ENABLE;
+
+ // set clear to send
+ m_cts = state;
+
+ if (!m_rx_rr0_latch)
+ {
+ if (!m_cts)
+ m_rr0 |= RR0_CTS;
+ else
+ m_rr0 &= ~RR0_CTS;
+
+ // trigger interrupt
+ if (m_wr1 & WR1_EXT_INT_ENABLE)
+ {
+ // trigger interrupt
+ m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
+
+ // latch read register 0
+ m_rx_rr0_latch = 1;
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// dcd_w - data carrier detected handler
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( z80sio_channel::dcd_w )
+{
+ LOG(("Z80SIO \"%s\" Channel %c : DCD %u\n", m_owner->tag(), 'A' + m_index, state));
+
+ if (m_dcd != state)
+ {
+ // enable receiver if in auto enables mode
+ if (!state)
+ if (m_wr3 & WR3_AUTO_ENABLES)
+ m_wr3 |= WR3_RX_ENABLE;
+
+ // set data carrier detect
+ m_dcd = state;
+
+ if (!m_rx_rr0_latch)
+ {
+ if (m_dcd)
+ m_rr0 |= RR0_DCD;
+ else
+ m_rr0 &= ~RR0_DCD;
+
+ if (m_wr1 & WR1_EXT_INT_ENABLE)
+ {
+ // trigger interrupt
+ m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
+
+ // latch read register 0
+ m_rx_rr0_latch = 1;
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// sh_w - Sync Hunt handler
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( z80sio_channel::sync_w )
+{
+ LOG(("Z80SIO \"%s\" Channel %c : Sync %u\n", m_owner->tag(), 'A' + m_index, state));
+
+ if (m_sh != state)
+ {
+ // set ring indicator state
+ m_sh = state;
+
+ if (!m_rx_rr0_latch)
+ {
+ if (m_sh)
+ m_rr0 |= RR0_SYNC_HUNT;
+ else
+ m_rr0 &= ~RR0_SYNC_HUNT;
+
+ if (m_wr1 & WR1_EXT_INT_ENABLE)
+ {
+ // trigger interrupt
+ m_uart->trigger_interrupt(m_index, INT_EXTERNAL);
+
+ // latch read register 0
+ m_rx_rr0_latch = 1;
+ }
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// rxc_w - receive clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( z80sio_channel::rxc_w )
+{
+ //LOG(("Z80SIO \"%s\" Channel %c : Receiver Clock Pulse\n", m_owner->tag(), m_index + 'A'));
+ int clocks = get_clock_mode();
+ if (clocks == 1)
+ rx_clock_w(state);
+ else if(state)
+ {
+ rx_clock_w(m_rx_clock < clocks/2);
+
+ m_rx_clock++;
+ if (m_rx_clock == clocks)
+ m_rx_clock = 0;
+
+ }
+}
+
+
+//-------------------------------------------------
+// txc_w - transmit clock
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER( z80sio_channel::txc_w )
+{
+ //LOG(("Z80SIO \"%s\" Channel %c : Transmitter Clock Pulse\n", m_owner->tag(), m_index + 'A'));
+ int clocks = get_clock_mode();
+ if (clocks == 1)
+ tx_clock_w(state);
+ else if(state)
+ {
+ tx_clock_w(m_tx_clock < clocks/2);
+
+ m_tx_clock++;
+ if (m_tx_clock == clocks)
+ m_tx_clock = 0;
+
+ }
+}
+
+
+//-------------------------------------------------
+// update_serial -
+//-------------------------------------------------
+void z80sio_channel::update_serial()
+{
+ int data_bit_count = get_rx_word_length();
+ stop_bits_t stop_bits = get_stop_bits();
+ parity_t parity;
+
+ LOG(("Z80SIO update_serial\n"));
+
+ if (m_wr4 & WR4_PARITY_ENABLE)
+ {
+ if (m_wr4 & WR4_PARITY_EVEN)
+ parity = PARITY_EVEN;
+ else
+ parity = PARITY_ODD;
+ }
+ else
+ parity = PARITY_NONE;
+
+ set_data_frame(1, data_bit_count, parity, stop_bits);
+
+ int clocks = get_clock_mode();
+
+ if (m_rxc > 0)
+ {
+ set_rcv_rate(m_rxc / clocks);
+ }
+
+ if (m_txc > 0)
+ {
+ set_tra_rate(m_txc / clocks);
+ }
+ receive_register_reset(); // if stop bits is changed from 0, receive register has to be reset
+}
+
+
+//-------------------------------------------------
+// set_dtr -
+//-------------------------------------------------
+
+void z80sio_channel::set_dtr(int state)
+{
+ LOG(("%s(%d)\n", FUNCNAME, state));
+ m_dtr = state;
+
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_dtra_cb(m_dtr);
+ else
+ m_uart->m_out_dtrb_cb(m_dtr);
+}
+
+//-------------------------------------------------
+// write_rx -
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER(z80sio_channel::write_rx)
+{
+ m_rxd = state;
+ //only use rx_w when self-clocked
+ if(m_rxc)
+ device_serial_interface::rx_w(state);
+}