summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/z80sio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/z80sio.cpp')
-rw-r--r--src/devices/machine/z80sio.cpp344
1 files changed, 215 insertions, 129 deletions
diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp
index a18be27d9c0..ef4fa943116 100644
--- a/src/devices/machine/z80sio.cpp
+++ b/src/devices/machine/z80sio.cpp
@@ -122,10 +122,6 @@ z80sio_device::z80sio_device(const machine_config &mconfig, device_type type, co
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),
@@ -164,11 +160,12 @@ i8274_new_device::i8274_new_device(const machine_config &mconfig, const char *ta
}
//-------------------------------------------------
-// device_start - device-specific startup
+// device_resolve_objects - device-specific setup
//-------------------------------------------------
-void z80sio_device::device_start()
+void z80sio_device::device_resolve_objects()
{
LOG("%s\n", FUNCNAME);
+
// resolve callbacks
m_out_txda_cb.resolve_safe();
m_out_dtra_cb.resolve_safe();
@@ -185,14 +182,14 @@ void z80sio_device::device_start()
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;
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+void z80sio_device::device_start()
+{
+ LOG("%s\n", FUNCNAME);
// state saving
save_item(NAME(m_int_state));
@@ -514,8 +511,13 @@ WRITE8_MEMBER( z80sio_device::ba_cd_w )
z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, Z80SIO_CHANNEL, tag, owner, clock)
, device_serial_interface(mconfig, *this)
- , m_rx_error(0)
+ , m_rx_fifo_depth(0)
+ , m_rx_data_fifo(0)
+ , m_rx_error_fifo(0)
, m_rx_clock(0)
+ , m_rx_count(0)
+ , m_rx_bit(0)
+ , m_rx_sr(0)
, m_rx_first(0)
, m_rx_break(0)
, m_rx_rr0_latch(0)
@@ -530,21 +532,30 @@ z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, d
, 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;
+
+ // 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;
}
//-------------------------------------------------
-// start - channel startup
+// resove_objects - channel setup
//-------------------------------------------------
-void z80sio_channel::device_start()
+void z80sio_channel::device_resolve_objects()
{
LOG("%s\n",FUNCNAME);
m_uart = downcast<z80sio_device *>(owner());
m_index = m_uart->get_channel_index(this);
- m_variant = ((z80sio_device *)owner())->m_variant;
+ m_variant = m_uart->m_variant;
+}
+
+//-------------------------------------------------
+// start - channel startup
+//-------------------------------------------------
+void z80sio_channel::device_start()
+{
+ LOG("%s\n",FUNCNAME);
// state saving
save_item(NAME(m_rr0));
@@ -558,8 +569,13 @@ void z80sio_channel::device_start()
save_item(NAME(m_wr5));
save_item(NAME(m_wr6));
save_item(NAME(m_wr7));
- save_item(NAME(m_rx_error));
+ save_item(NAME(m_rx_fifo_depth));
+ save_item(NAME(m_rx_data_fifo));
+ save_item(NAME(m_rx_error_fifo));
save_item(NAME(m_rx_clock));
+ save_item(NAME(m_rx_count));
+ save_item(NAME(m_rx_bit));
+ save_item(NAME(m_rx_sr));
save_item(NAME(m_rx_first));
save_item(NAME(m_rx_break));
save_item(NAME(m_rx_rr0_latch));
@@ -583,7 +599,9 @@ void z80sio_channel::device_reset()
LOG("%s\n", FUNCNAME);
// Reset RS232 emulation
- receive_register_reset();
+ m_rx_fifo_depth = 0;
+ m_rx_data_fifo = m_rx_error_fifo = 0U;
+ receive_reset();
transmit_register_reset();
// disable receiver
@@ -661,7 +679,8 @@ void z80sio_channel::tra_complete()
// empty transmit buffer
m_rr0 |= RR0_TX_BUFFER_EMPTY;
-
+ if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(true);
if (m_wr1 & WR1_TX_INT_ENABLE)
m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
}
@@ -699,33 +718,6 @@ void z80sio_channel::tra_complete()
//-------------------------------------------------
-// rcv_callback -
-//-------------------------------------------------
-void z80sio_channel::rcv_callback()
-{
- if (m_wr3 & WR3_RX_ENABLE)
- {
- LOGBIT("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd);
- receive_register_update_bit(m_rxd);
- }
-}
-
-
-//-------------------------------------------------
-// rcv_complete -
-//-------------------------------------------------
-void z80sio_channel::rcv_complete()
-{
- uint8_t data;
-
- receive_register_extract();
- data = get_received_char();
- LOGRCV("%s() \"%s \"Channel %c Received Data %02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, data);
- receive_data(data);
-}
-
-
-//-------------------------------------------------
// get_clock_mode - get clock divisor
//-------------------------------------------------
int z80sio_channel::get_clock_mode()
@@ -783,6 +775,20 @@ void z80sio_channel::update_rts()
set_dtr((m_wr5 & WR5_DTR) ? 0 : 1);
}
+void z80sio_channel::set_ready(bool ready)
+{
+ // WAIT mode not supported yet
+ if (!(m_wr1 & WR1_WRDY_FUNCTION))
+ return;
+
+ logerror("Channel %sready\n", ready ? "" : "not ");
+
+ if (m_index == z80sio_device::CHANNEL_A)
+ m_uart->m_out_wrdya_cb(ready ? 0 : 1);
+ else
+ m_uart->m_out_wrdyb_cb(ready ? 0 : 1);
+}
+
//-------------------------------------------------
// get_stop_bits - get number of stop bits
//-------------------------------------------------
@@ -1036,7 +1042,16 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data)
case WR0_ERROR_RESET:
// error reset
LOGCMD("%s %s Ch:%c : Error Reset\n", FUNCNAME, tag(), 'A' + m_index);
- m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
+ if ((WR1_RX_INT_FIRST == (m_wr1 & WR1_RX_INT_MODE_MASK)) && (m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR)))
+ {
+ // clearing framing and overrun errors advances the FIFO
+ m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
+ advance_rx_fifo();
+ }
+ else
+ {
+ m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
+ }
break;
case WR0_RETURN_FROM_INT:
LOGINT("%s %s Ch:%c : Return from interrupt\n", FUNCNAME, tag(), 'A' + m_index);
@@ -1094,6 +1109,16 @@ void z80sio_channel::do_sioreg_wr1(uint8_t data)
LOG("Z80SIO \"%s\" Channel %c : Receiver Interrupt on All Characters\n", owner()->tag(), 'A' + m_index);
break;
}
+
+ if (data & WR1_WRDY_ENABLE)
+ {
+ if (data & WR1_WRDY_ON_RX_TX)
+ set_ready((m_rr0 & RR0_RX_CHAR_AVAILABLE) != 0);
+ else
+ set_ready((m_rr0 & RR0_TX_BUFFER_EMPTY) != 0);
+ }
+ else
+ set_ready(false);
}
void z80sio_channel::do_sioreg_wr2(uint8_t data)
@@ -1179,22 +1204,11 @@ void z80sio_channel::control_write(uint8_t data)
//-------------------------------------------------
uint8_t z80sio_channel::data_read()
{
- uint8_t data = 0;
+ uint8_t const data = uint8_t(m_rx_data_fifo & 0x000000ffU);
- if (!m_rx_data_fifo.empty())
- {
- // load data from the FIFO
- data = m_rx_data_fifo.dequeue();
-
- // 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.dequeue();
-
- if (m_rx_data_fifo.empty())
- {
- // no more characters available in the FIFO
- m_rr0 &= ~ RR0_RX_CHAR_AVAILABLE;
- }
- }
+ // framing and overrun errors need to be cleared to advance the FIFO in interrupt-on-first mode
+ if ((WR1_RX_INT_FIRST != (m_wr1 & WR1_RX_INT_MODE_MASK)) || !(m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR)))
+ advance_rx_fifo();
LOG("Z80SIO \"%s\" Channel %c : Data Register Read '%02x'\n", owner()->tag(), 'A' + m_index, data);
@@ -1217,14 +1231,17 @@ void z80sio_channel::data_write(uint8_t data)
// empty transmit buffer
m_rr0 |= RR0_TX_BUFFER_EMPTY;
-
+ if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(true);
if (m_wr1 & WR1_TX_INT_ENABLE)
m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
}
else
{
- LOGTX(" Transmitter %s, data byte dropped\n", m_wr5 & WR5_TX_ENABLE ? "not enabled" : "not emptied");
+ LOGTX(" Transmitter not %s, data byte %02x pending\n", m_wr5 & WR5_TX_ENABLE ? "emptied" : "enabled", m_tx_data);
m_rr0 &= ~RR0_TX_BUFFER_EMPTY;
+ if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(false);
}
m_rr1 &= ~RR1_ALL_SENT;
@@ -1232,60 +1249,108 @@ void z80sio_channel::data_write(uint8_t data)
//-------------------------------------------------
+// receive_reset - reset receive state
+//-------------------------------------------------
+void z80sio_channel::receive_reset()
+{
+ m_rx_count = (get_clock_mode() - 1) / 2;
+ m_rx_bit = 0;
+}
+
+
+//-------------------------------------------------
+// advance_rx_fifo - move to next received byte
+//-------------------------------------------------
+void z80sio_channel::advance_rx_fifo()
+{
+ if (m_rx_fifo_depth)
+ {
+ if (--m_rx_fifo_depth)
+ {
+ // shift the FIFO
+ m_rx_data_fifo >>= 8;
+ m_rx_error_fifo >>= 8;
+
+ // load error status from the FIFO
+ m_rr1 = (m_rr1 & ~RR1_CRC_FRAMING_ERROR) | uint8_t(m_rx_error_fifo & 0x000000ffU);
+ }
+ else
+ {
+ // no more characters available in the FIFO
+ m_rr0 &= ~RR0_RX_CHAR_AVAILABLE;
+ if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(false);
+ }
+ }
+}
+
+
+//-------------------------------------------------
// receive_data - receive data word
//-------------------------------------------------
-void z80sio_channel::receive_data(uint8_t data)
+void z80sio_channel::receive_data()
{
- LOGRCV("%s(%02x) %s:%c\n",FUNCNAME, data, tag(), 'A' + m_index);
+ LOGRCV("%s(%04x) %s:%c\n",FUNCNAME, m_rx_sr, tag(), 'A' + m_index);
+
+ // check for parity and framing errors
+ int const word_length = get_rx_word_length();
+ bool const parity = 0U != (m_wr4 & WR4_PARITY_ENABLE);
+ uint16_t const stop_bit = uint16_t(1U) << (word_length + (parity ? 1 : 0));
+ uint32_t rx_error = (m_rx_sr & stop_bit) ? 0U : RR1_CRC_FRAMING_ERROR;
+ m_rx_sr |= stop_bit;
+ if (parity)
+ {
+ uint16_t par(m_rx_sr);
+ for (int i = 1; word_length >= i; ++i)
+ par ^= BIT(par, i);
+ if (bool(BIT(par, 0)) == bool(m_wr4 & WR4_PARITY_EVEN))
+ rx_error |= RR1_PARITY_ERROR;
+ }
- if (m_rx_data_fifo.full())
+ if (3 == m_rx_fifo_depth)
{
LOG(" Overrun detected\n");
// receive overrun error detected
- m_rx_error |= RR1_RX_OVERRUN_ERROR;
+ 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;
- }
+ m_rx_data_fifo = (m_rx_data_fifo & 0x0000ffffU) | (uint32_t(m_rx_sr & 0x00ffU) << 16);
+ m_rx_error_fifo = (m_rx_error_fifo & 0x0000ffffU) | (rx_error << 16);
}
else
{
// store received character and error status into FIFO
- m_rx_data_fifo.enqueue(data);
- m_rx_error_fifo.enqueue(m_rx_error);
+ if (!m_rx_fifo_depth)
+ m_rx_data_fifo = m_rx_error_fifo = 0U;
+ m_rx_data_fifo |= uint32_t(m_rx_sr & 0x00ffU) << (8 * m_rx_fifo_depth);
+ m_rx_error_fifo |= rx_error << (8 * m_rx_fifo_depth);
+ ++m_rx_fifo_depth;
}
m_rr0 |= RR0_RX_CHAR_AVAILABLE;
+ if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(true);
+ if (!m_rx_fifo_depth)
+ m_rr1 |= uint8_t(rx_error);
// receive interrupt
switch (m_wr1 & WR1_RX_INT_MODE_MASK)
{
case WR1_RX_INT_FIRST:
- if (m_rx_first)
- {
+ if (m_rx_first || (rx_error & (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR)))
m_uart->trigger_interrupt(m_index, INT_RECEIVE);
- m_rx_first = 0;
- }
+ m_rx_first = 0;
break;
case WR1_RX_INT_ALL_PARITY:
+ m_uart->trigger_interrupt(m_index, (rx_error & (RR1_CRC_FRAMING_ERROR | RR1_PARITY_ERROR)) ? INT_SPECIAL : INT_RECEIVE);
+ break;
+
case WR1_RX_INT_ALL:
- m_uart->trigger_interrupt(m_index, INT_RECEIVE);
+ m_uart->trigger_interrupt(m_index, (rx_error & RR1_CRC_FRAMING_ERROR) ? INT_SPECIAL : INT_RECEIVE);
break;
+
default:
LOG("No interrupt triggered\n");
-
}
}
@@ -1403,18 +1468,63 @@ WRITE_LINE_MEMBER( z80sio_channel::sync_w )
WRITE_LINE_MEMBER( z80sio_channel::rxc_w )
{
//LOG("Z80SIO \"%s\" Channel %c : Receiver Clock Pulse\n", owner()->tag(), m_index + 'A');
- int clocks = get_clock_mode();
- if (clocks == 1)
- rx_clock_w(state);
- else if(state)
+ if ((m_wr3 & WR3_RX_ENABLE) && state && !m_rx_clock)
{
- rx_clock_w(m_rx_clock < clocks/2);
+ // RxD sampled on rising edge
+ int const clocks = get_clock_mode() - 1;
- m_rx_clock++;
- if (m_rx_clock == clocks)
- m_rx_clock = 0;
+ if (!m_rx_bit)
+ {
+ // look for start bit
+ if (m_rxd)
+ {
+ // line idle
+ m_rx_count = (std::max)(m_rx_count, (clocks / 2) + 1) - 1;
+ }
+ else if (!m_rx_count)
+ {
+ // half a bit period expired, start shifting bits
+ m_rx_count = clocks;
+ ++m_rx_bit;
+ m_rx_sr = ~uint16_t(0U);
+ }
+ else
+ {
+ // ensure start bit lasts long enough
+ --m_rx_count;
+ }
+ }
+ else if (!m_rx_count)
+ {
+ // sample a data/parity/stop bit
+ if (!m_rxd)
+ m_rx_sr &= ~uint16_t(1U << (m_rx_bit - 1));
+
+ if ((get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0) + 1) == m_rx_bit)
+ {
+ // this is the stop bit - framing error adds a half bit period
+ m_rx_count = m_rxd ? 0 : clocks;
+ m_rx_bit = 0;
+
+ LOGRCV("%s() \"%s \"Channel %c Received Data %02x\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rx_sr & 0xff);
+ receive_data();
+ }
+ else
+ {
+ LOGBIT("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd);
+ // wait a whole bit period for the next bit
+ m_rx_count = clocks;
+ ++m_rx_bit;
+ }
+ }
+ else
+ {
+ // bit period hasn't expired
+ --m_rx_count;
+ }
}
+ m_rx_clock = state;
}
@@ -1444,8 +1554,8 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w )
//-------------------------------------------------
void z80sio_channel::update_serial()
{
- int data_bit_count = get_rx_word_length();
- stop_bits_t stop_bits = get_stop_bits();
+ int const data_bit_count = get_rx_word_length();
+ stop_bits_t const stop_bits = get_stop_bits();
parity_t parity;
LOG("%s\n", FUNCNAME);
@@ -1463,20 +1573,7 @@ void z80sio_channel::update_serial()
set_data_frame(1, data_bit_count, parity, stop_bits);
- int clocks = get_clock_mode();
-
- if (m_rxc > 0)
- {
- LOG("- RxC:%d/%d = %d\n", m_rxc, clocks, m_rxc / clocks);
- set_rcv_rate(m_rxc / clocks);
- }
-
- if (m_txc > 0)
- {
- LOG("- TxC:%d/%d = %d\n", m_txc, clocks, m_txc / clocks);
- set_tra_rate(m_txc / clocks);
- }
- receive_register_reset(); // if stop bits is changed from 0, receive register has to be reset
+ receive_reset(); // if stop bits is changed from 0, receive register has to be reset
}
@@ -1493,14 +1590,3 @@ void z80sio_channel::set_dtr(int state)
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);
-}