summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/z80sio.cpp
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2017-10-30 13:41:43 +1100
committer Vas Crabb <vas@vastheman.com>2017-10-30 13:41:43 +1100
commit519b14e33c614b7065c2bc561728394253c08db1 (patch)
tree6d4b9c95eed01d867a5f1bfca9249917e49e87f7 /src/devices/machine/z80sio.cpp
parent77118012900e7f5b860530da827427290eb65bc9 (diff)
z80sio: Made async receive behave more like real device.
* Check that start bit persists for half a clock interval. * Sample data bits mid-interval. * Handle invalid stop bit as described in Zilog manual. * Check parity and latch overrun and parity errors. (nw) This fixes the issues with corrupt characters being typed on the Kaypro. Synchronous modes are still broken. The channels are still using device_serial_interface to transmit frames, but receiving is handled entirely in the device class itself. Overruns still aren't handled properly.
Diffstat (limited to 'src/devices/machine/z80sio.cpp')
-rw-r--r--src/devices/machine/z80sio.cpp179
1 files changed, 95 insertions, 84 deletions
diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp
index 8cf9169c6b2..e17d7c83fa9 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),
@@ -195,14 +191,6 @@ void z80sio_device::device_start()
{
LOG("%s\n", FUNCNAME);
- // 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));
save_item(NAME(m_int_source));
@@ -523,8 +511,10 @@ 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_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)
@@ -576,8 +566,10 @@ 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_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));
@@ -601,7 +593,7 @@ void z80sio_channel::device_reset()
LOG("%s\n", FUNCNAME);
// Reset RS232 emulation
- receive_register_reset();
+ receive_reset();
transmit_register_reset();
// disable receiver
@@ -717,33 +709,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()
@@ -1205,7 +1170,7 @@ uint8_t z80sio_channel::data_read()
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();
+ m_rr1 = (m_rr1 & ~RR1_CRC_FRAMING_ERROR) | m_rx_error_fifo.dequeue();
if (m_rx_data_fifo.empty())
{
@@ -1250,17 +1215,28 @@ 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;
+}
+
+//-------------------------------------------------
// 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);
if (m_rx_data_fifo.full())
{
+ // FIXME: this should overwrite the tail of the FIFO and store the overrun error
+
LOG(" Overrun detected\n");
// receive overrun error detected
- m_rx_error |= RR1_RX_OVERRUN_ERROR;
+ //uint8_t rx_error |= RR1_RX_OVERRUN_ERROR;
switch (m_wr1 & WR1_RX_INT_MODE_MASK)
{
@@ -1279,9 +1255,24 @@ void z80sio_channel::receive_data(uint8_t data)
}
else
{
+ // 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));
+ uint8_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;
+ }
+
// store received character and error status into FIFO
- m_rx_data_fifo.enqueue(data);
- m_rx_error_fifo.enqueue(m_rx_error);
+ m_rx_data_fifo.enqueue(uint8_t(m_rx_sr & 0xffU));
+ m_rx_error_fifo.enqueue(rx_error);
}
m_rr0 |= RR0_RX_CHAR_AVAILABLE;
@@ -1301,9 +1292,9 @@ void z80sio_channel::receive_data(uint8_t data)
case WR1_RX_INT_ALL:
m_uart->trigger_interrupt(m_index, INT_RECEIVE);
break;
+
default:
LOG("No interrupt triggered\n");
-
}
}
@@ -1421,18 +1412,62 @@ 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
+ LOGBIT("%s() \"%s \"Channel %c Received Data Bit %d\n", FUNCNAME, owner()->tag(), 'A' + m_index, m_rxd);
+ 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
+ {
+ // 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;
}
@@ -1462,8 +1497,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);
@@ -1481,20 +1516,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
}
@@ -1511,14 +1533,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);
-}