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.cpp1007
1 files changed, 680 insertions, 327 deletions
diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp
index b985340e576..dfa5a13ad5e 100644
--- a/src/devices/machine/z80sio.cpp
+++ b/src/devices/machine/z80sio.cpp
@@ -179,9 +179,9 @@ enum : uint8_t
enum : uint8_t
{
WR3_RX_ENABLE = 0x01,
- WR3_SYNC_CHAR_LOAD_INHIBIT= 0x02, // not supported
- WR3_ADDRESS_SEARCH_MODE = 0x04, // not supported
- WR3_RX_CRC_ENABLE = 0x08, // not supported
+ WR3_SYNC_CHAR_LOAD_INHIBIT= 0x02,
+ WR3_ADDRESS_SEARCH_MODE = 0x04,
+ WR3_RX_CRC_ENABLE = 0x08,
WR3_ENTER_HUNT_PHASE = 0x10,
WR3_AUTO_ENABLES = 0x20,
WR3_RX_WORD_LENGTH_MASK = 0xc0,
@@ -196,14 +196,14 @@ enum : uint8_t
WR4_PARITY_ENABLE = 0x01,
WR4_PARITY_EVEN = 0x02,
WR4_STOP_BITS_MASK = 0x0c,
- WR4_STOP_BITS_SYNC = 0x00, // partially supported
+ WR4_STOP_BITS_SYNC = 0x00,
WR4_STOP_BITS_1 = 0x04,
WR4_STOP_BITS_1_5 = 0x08,
WR4_STOP_BITS_2 = 0x0c,
- WR4_SYNC_MODE_MASK = 0x30, // partially supported
- WR4_SYNC_MODE_8_BIT = 0x00, // partially supported
- WR4_SYNC_MODE_16_BIT = 0x10, // partially supported
- WR4_SYNC_MODE_SDLC = 0x20, // partially supported
+ WR4_SYNC_MODE_MASK = 0x30,
+ WR4_SYNC_MODE_8_BIT = 0x00,
+ WR4_SYNC_MODE_16_BIT = 0x10,
+ WR4_SYNC_MODE_SDLC = 0x20,
WR4_SYNC_MODE_EXT = 0x30, // partially supported
WR4_CLOCK_RATE_MASK = 0xc0,
WR4_CLOCK_RATE_X1 = 0x00,
@@ -227,6 +227,8 @@ enum : uint8_t
WR5_DTR = 0x80
};
+constexpr uint32_t TX_SR_MASK = 0xfffffU;
+constexpr uint16_t SDLC_RESIDUAL = 0x1d0f;
//**************************************************************************
// DEVICE DEFINITIONS
@@ -309,21 +311,20 @@ inline void z80sio_channel::set_dtr(int state)
}
}
-inline void z80sio_channel::tx_setup(uint16_t data, int bits, int parity, bool framing, bool special)
+inline void z80sio_channel::tx_setup(uint16_t data, int bits, bool framing, bool crc_tx, bool abort_tx)
{
- m_tx_bits = bits;
- m_tx_parity = parity;
- m_tx_sr = data | (~uint16_t(0) << bits);
- if (parity)
- {
- if (m_wr4 & WR4_PARITY_EVEN)
- m_tx_sr &= ~(uint16_t(1) << m_tx_bits);
- ++m_tx_bits;
- }
+ m_rr1 |= RR1_ALL_SENT;
+ m_tx_parity = false;
+ m_tx_sr = data;
+ m_tx_sr &= ~(~uint32_t(0) << bits);
+ m_tx_sr |= ~uint32_t(0) << (bits + 3);
m_tx_flags =
((!framing && (m_wr5 & WR5_TX_CRC_ENABLE)) ? TX_FLAG_CRC : 0U) |
(framing ? TX_FLAG_FRAMING : 0U) |
- (special ? TX_FLAG_SPECIAL : 0U);
+ (abort_tx ? TX_FLAG_ABORT_TX : 0U) |
+ (crc_tx ? TX_FLAG_CRC_TX : 0U) |
+ (!framing && !crc_tx && !abort_tx ? TX_FLAG_DATA_TX : 0U);
+ LOGBIT("%.6f TX_SR %05x data %04x flags %x\n" , machine().time().as_double() , m_tx_sr & TX_SR_MASK , data , m_tx_flags);
}
inline void z80sio_channel::tx_setup_idle()
@@ -331,24 +332,21 @@ inline void z80sio_channel::tx_setup_idle()
switch (m_wr4 & WR4_SYNC_MODE_MASK)
{
case WR4_SYNC_MODE_8_BIT:
- tx_setup(m_wr6, 8, 0, true, false);
+ case WR4_SYNC_MODE_EXT:
+ // External sync mode sends a single sync byte
+ tx_setup(m_wr6, 8, true, false, false);
break;
case WR4_SYNC_MODE_16_BIT:
- tx_setup(uint16_t(m_wr6) | (uint16_t(m_wr7) << 8), 16, 0, true, false);
+ tx_setup(uint16_t(m_wr6) | (uint16_t(m_wr7) << 8), 16, true, false, false);
break;
case WR4_SYNC_MODE_SDLC:
// SDLC transmit examples don't show flag being loaded, implying it's hard-coded on the transmit side
- tx_setup(0x7e, 8, 0, true, false);
- break;
- case WR4_SYNC_MODE_EXT:
- // TODO: what does a real chip do for sync idle in external sync mode?
- // This is based on the assumption that bit 4 controls 8-/16-bit idle pattern (fits for monosync/bisync/SDLC).
- tx_setup(uint16_t(m_wr6) | (uint16_t(m_wr7) << 8), 16, 0, true, false);
+ tx_setup(0x7e, 8, true, false, false);
break;
}
+ m_tx_in_pkt = false;
}
-
//-------------------------------------------------
// z80sio_device - constructor
//-------------------------------------------------
@@ -697,7 +695,6 @@ uint8_t z80sio_device::read_vector()
{
if (m_int_state[prio[i]] & Z80_DAISY_INT)
{
- constexpr uint8_t RR1_SPECIAL(RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR | RR1_END_OF_FRAME);
switch (prio[i])
{
case 0 + z80sio_channel::INT_TRANSMIT:
@@ -705,9 +702,9 @@ uint8_t z80sio_device::read_vector()
case 0 + z80sio_channel::INT_EXTERNAL:
return vec | 0x0aU;
case 0 + z80sio_channel::INT_RECEIVE:
- if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanA->m_rr1 & (RR1_SPECIAL | RR1_PARITY_ERROR)))
+ if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanA->m_rr1 & (m_chanA->get_special_rx_mask() | RR1_PARITY_ERROR)))
return vec | 0x0eU;
- else if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanA->m_rr1 & RR1_SPECIAL))
+ else if (((m_chanA->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanA->m_rr1 & m_chanA->get_special_rx_mask()))
return vec | 0x0eU;
else
return vec | 0x0cU;
@@ -716,9 +713,9 @@ uint8_t z80sio_device::read_vector()
case 3 + z80sio_channel::INT_EXTERNAL:
return vec | 0x02U;
case 3 + z80sio_channel::INT_RECEIVE:
- if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanB->m_rr1 & (RR1_SPECIAL | RR1_PARITY_ERROR)))
+ if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY) && (m_chanB->m_rr1 & (m_chanB->get_special_rx_mask() | RR1_PARITY_ERROR)))
return vec | 0x06U;
- else if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanB->m_rr1 & RR1_SPECIAL))
+ else if (((m_chanB->m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL) && (m_chanB->m_rr1 & m_chanB->get_special_rx_mask()))
return vec | 0x06U;
else
return vec | 0x04U;
@@ -922,10 +919,9 @@ z80sio_channel::z80sio_channel(
, m_rx_bit(0)
, m_rx_sr(0)
, m_rx_first(0)
- , m_rx_break(0)
, m_rxd(1)
, m_tx_data(0)
- , m_tx_clock(0), m_tx_count(0), m_tx_bits(0), m_tx_parity(0), m_tx_sr(0), m_tx_crc(0), m_tx_hist(0), m_tx_flags(0)
+ , m_tx_clock(0), m_tx_count(0), m_tx_parity(0), m_tx_sr(0), m_tx_crc(0), m_tx_hist(0), m_tx_flags(0)
, m_txd(1), m_dtr(0), m_rts(0)
, m_ext_latched(0), m_brk_latched(0), m_cts(0), m_dcd(0), m_sync(0)
, m_rr1_auto_reset(rr1_auto_reset)
@@ -938,7 +934,7 @@ z80sio_channel::z80sio_channel(
}
z80sio_channel::z80sio_channel(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
- : z80sio_channel(mconfig, Z80SIO_CHANNEL, tag, owner, clock, RR1_CRC_FRAMING_ERROR)
+ : z80sio_channel(mconfig, Z80SIO_CHANNEL, tag, owner, clock, RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK)
{
}
@@ -981,19 +977,31 @@ void z80sio_channel::device_start()
save_item(NAME(m_rx_error_fifo));
save_item(NAME(m_rx_clock));
save_item(NAME(m_rx_count));
+ save_item(NAME(m_dlyd_rxd));
save_item(NAME(m_rx_bit));
+ save_item(NAME(m_rx_bit_limit));
+ save_item(NAME(m_rx_sync_fsm));
+ save_item(NAME(m_rx_one_cnt));
save_item(NAME(m_rx_sr));
+ save_item(NAME(m_rx_sync_sr));
+ save_item(NAME(m_rx_crc_delay));
+ save_item(NAME(m_rx_crc));
+ save_item(NAME(m_rx_crc_en));
+ save_item(NAME(m_rx_parity));
save_item(NAME(m_rx_first));
- save_item(NAME(m_rx_break));
save_item(NAME(m_tx_data));
save_item(NAME(m_tx_clock));
save_item(NAME(m_tx_count));
- save_item(NAME(m_tx_bits));
+ save_item(NAME(m_tx_phase));
save_item(NAME(m_tx_parity));
+ save_item(NAME(m_tx_in_pkt));
+ save_item(NAME(m_tx_forced_sync));
save_item(NAME(m_tx_sr));
save_item(NAME(m_tx_crc));
save_item(NAME(m_tx_hist));
save_item(NAME(m_tx_flags));
+ save_item(NAME(m_tx_delay));
+ save_item(NAME(m_all_sent_delay));
save_item(NAME(m_txd));
save_item(NAME(m_dtr));
save_item(NAME(m_rts));
@@ -1016,9 +1024,11 @@ void z80sio_channel::device_reset()
m_rx_fifo_depth = 0;
m_rx_data_fifo = m_rx_error_fifo = 0U;
m_rx_bit = 0;
+ m_rx_one_cnt = 0;
+ m_rx_sync_fsm = SYNC_FSM_HUNT;
m_tx_count = 0;
- m_tx_bits = 0;
m_rr0 &= ~RR0_RX_CHAR_AVAILABLE;
+ m_rr0 |= RR0_SYNC_HUNT;
m_rr1 &= ~(RR1_PARITY_ERROR | RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR);
// disable receiver
@@ -1029,6 +1039,10 @@ void z80sio_channel::device_reset()
m_rr0 |= RR0_TX_BUFFER_EMPTY | RR0_TX_UNDERRUN;
m_rr1 |= RR1_ALL_SENT;
m_tx_flags = 0U;
+ m_tx_delay = ~0;
+ m_all_sent_delay = 0;
+ m_tx_in_pkt = false;
+ m_tx_forced_sync = true;
// TODO: what happens to WAIT/READY?
@@ -1045,6 +1059,10 @@ void z80sio_channel::device_reset()
m_uart->reset_interrupts();
}
+bool z80sio_channel::is_tx_idle() const
+{
+ return (m_tx_sr & TX_SR_MASK) == TX_SR_MASK;
+}
//-------------------------------------------------
// transmit_enable - start transmission if
@@ -1052,19 +1070,37 @@ void z80sio_channel::device_reset()
//-------------------------------------------------
void z80sio_channel::transmit_enable()
{
- if (!m_tx_bits && transmit_allowed())
+ if (transmit_allowed())
{
- if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)
+ if (is_tx_idle())
{
- LOGTX("Channel %c synchronous transmit enabled - load sync pattern\n", 'A' + m_index);
- tx_setup_idle();
- if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
- set_ready(true);
+ if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)
+ {
+ LOGTX("Channel %c synchronous transmit enabled - load sync pattern\n", 'A' + m_index);
+ tx_setup_idle();
+ m_tx_forced_sync = false;
+ if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(true);
+ }
+ else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY))
+ async_tx_setup();
}
- else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY))
+ }
+ else
+ {
+ // Send at least 1 sync once tx is re-enabled
+ m_tx_forced_sync = true;
+ LOGBIT("tx forced set 1\n");
+
+ // If tx is disabled during CRC transmission, flag/sync is sent for the remaining bits
+ if (m_tx_flags & TX_FLAG_CRC_TX)
{
- async_tx_setup();
+ m_tx_flags = TX_FLAG_FRAMING;
+ set_tx_empty(false , (m_rr0 & RR0_TX_BUFFER_EMPTY) != 0);
}
+ m_tx_in_pkt = false;
+ // Not sure if RR0_TX_UNDERRUN is set when tx is disabled. It certainly makes sense to be that way.
+ m_rr0 |= RR0_TX_UNDERRUN;
}
}
@@ -1093,52 +1129,65 @@ void z80sio_channel::sync_tx_sr_empty()
if (!transmit_allowed())
{
LOGTX("%s() Channel %c Transmitter Disabled m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_wr5);
+ m_tx_flags = 0;
+ }
+ else if (m_tx_forced_sync ||
+ ((m_rr0 & RR0_TX_BUFFER_EMPTY) && ((m_rr0 & RR0_TX_UNDERRUN) || !(m_wr5 & WR5_TX_CRC_ENABLE))))
+ {
+ LOGBIT("tx forced = %d\n" , m_tx_forced_sync);
+ m_tx_forced_sync = false;
- // transmit disabled, set flag if nothing pending
- m_tx_flags &= ~TX_FLAG_SPECIAL;
- if (m_rr0 & RR0_TX_BUFFER_EMPTY)
- m_rr1 |= RR1_ALL_SENT;
+ if (!(m_rr0 & RR0_TX_UNDERRUN))
+ {
+ m_rr0 |= RR0_TX_UNDERRUN;
+ trigger_ext_int();
+ }
+ // TODO: Check
+ // if ((m_tx_flags & (TX_FLAG_CRC_TX | TX_FLAG_DATA_TX)) && (m_wr1 & WR1_TX_INT_ENABLE))
+ // // At the beginning of the sync/flag sequence that closes a frame, send tx interrupt
+ // m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
+ if (m_tx_flags & TX_FLAG_CRC_TX)
+ {
+ // At the end of CRC transmission, set tx empty
+ m_tx_flags = 0;
+ set_tx_empty (false , (m_rr0 & RR0_TX_BUFFER_EMPTY) != 0);
+ }
+ tx_setup_idle();
}
else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY))
{
LOGTX("%s() Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_tx_data, m_wr5);
- tx_setup(m_tx_data, get_tx_word_length(m_tx_data), (m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0, false, false);
-
+ tx_setup(m_tx_data, get_tx_word_length(), false, false, false);
// 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 if ((m_rr0 & RR0_TX_UNDERRUN) || ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_8_BIT))
- {
- // uts20 always resets the underrun/end-of-message flag if it sees it set, but wants to see sync (not CRC) on the loopback.
- // It seems odd that automatic CRC transmission would be disabled by certain modes, but this at least allows the test to pass.
-
- LOGTX("%s() Channel %c Underrun - load sync pattern m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_wr5);
- bool const first_idle((m_tx_flags & TX_FLAG_SPECIAL) || !(m_tx_flags & TX_FLAG_FRAMING));
- tx_setup_idle();
-
- if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
- set_ready(true);
- m_rr1 |= RR1_ALL_SENT;
-
- // if this is the first sync pattern, generate an interrupt indicating that the next frame can be sent
- // FIXME: uts20 definitely doesn't want a Tx interrupt here, but what does SDLC mode want?
- // In that case, it would seem that the underrun flag would already be set when the CRC was loaded.
- if (!(m_rr0 & RR0_TX_UNDERRUN))
- trigger_ext_int();
- else if (first_idle && (m_wr1 & WR1_TX_INT_ENABLE))
- m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
+ set_tx_empty(false , true);
}
else
{
LOGTX("%s() Channel %c Transmit FCS '%04x' m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_tx_crc, m_wr5);
- // just for fun, SDLC sends the FCS inverted in reverse bit order
- uint16_t const fcs(bitswap<16>(m_tx_crc, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
- tx_setup(((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) ? ~fcs : fcs, 16, 0, false, true);
+ // Send CRC. 16 bits are counted by loading 2 flag/sync bytes into tx SR (these bits
+ // are actually sent out when tx is disabled during CRC transmission)
+ uint16_t flags = 0;
+ switch (m_wr4 & WR4_SYNC_MODE_MASK)
+ {
+ case WR4_SYNC_MODE_8_BIT:
+ case WR4_SYNC_MODE_EXT:
+ flags = (uint16_t(m_wr6) << 8) | m_wr6;
+ break;
+ case WR4_SYNC_MODE_16_BIT:
+ flags = uint16_t(m_wr6) | (uint16_t(m_wr7) << 8);
+ break;
+ case WR4_SYNC_MODE_SDLC:
+ flags = 0x7e7e;
+ // In SDLC mode, invert CRC before sending it out
+ m_tx_crc = ~m_tx_crc;
+ // In addition, ensure at least 1 flag is sent out before next frame
+ m_tx_forced_sync = true;
+ break;
+ }
+ tx_setup(flags, 16, false, true, false);
+ set_tx_empty(true , true);
+ LOGBIT("Send CRC=%04x\n" , m_tx_crc);
// set the underrun flag so it will send sync next time
m_rr0 |= RR0_TX_UNDERRUN;
@@ -1146,6 +1195,43 @@ void z80sio_channel::sync_tx_sr_empty()
}
}
+bool z80sio_channel::get_tx_empty() const
+{
+ // During CRC transmission, tx buffer is shown as full
+ return (m_rr0 & RR0_TX_BUFFER_EMPTY) &&
+ (m_tx_flags & TX_FLAG_CRC_TX) == 0;
+}
+
+void z80sio_channel::set_tx_empty(bool prev_state, bool new_state)
+{
+ if (new_state)
+ m_rr0 |= RR0_TX_BUFFER_EMPTY;
+ else
+ m_rr0 &= ~RR0_TX_BUFFER_EMPTY;
+
+ bool curr_tx_empty = get_tx_empty();
+
+ if (!prev_state && curr_tx_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 if (prev_state && !curr_tx_empty)
+ {
+ if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
+ set_ready(false);
+ }
+}
+
+void z80sio_channel::update_crc(uint16_t& crc , bool bit)
+{
+ if (BIT(crc , 15) ^ bit)
+ crc = (crc << 1) ^ ((m_wr5 & WR5_CRC16) ? 0x8005U : 0x1021U);
+ else
+ crc <<= 1;
+}
//-------------------------------------------------
// async_tx_setup - set up for asynchronous
@@ -1155,15 +1241,27 @@ void z80sio_channel::async_tx_setup()
{
LOGTX("%s() Channel %c Transmit Data Byte '%02x' m_wr5:%02x\n", FUNCNAME, 'A' + m_index, m_tx_data, m_wr5);
- tx_setup(uint16_t(m_tx_data) << 1, get_tx_word_length(m_tx_data) + 1, (m_wr4 & WR4_PARITY_ENABLE) ? 2 : 0, false, false);
- ++m_tx_bits; // stop bit
+ // 5 bits: | 11x 1 | tx_data (8 bits) | 0 |
+ // 6 bits: | 10x 1 | 000 | tx_data (6 bits) | 0 |
+ // 7 bits: | 9x 1 | 000 | tx_data (7 bits) | 0 |
+ // 8 bits: | 8x 1 | 000 | tx_data (8 bits) | 0 |
+ // Add start bit on the right
+ m_tx_sr = uint32_t(m_tx_data) << 1;
+ auto wl = get_tx_word_length();
+ if (wl != 5)
+ // Filter out bits to be ignored in m_tx_data
+ m_tx_sr &= ~(~uint32_t(0) << (wl + 1));
+ // Add 1s on the left
+ m_tx_sr |= ~uint32_t(0) << (wl + 4);
+ LOGBIT("TX_SR %05x TX_D %02x\n" , m_tx_sr & TX_SR_MASK , m_tx_data);
+ m_tx_parity = false;
+
+ m_tx_flags = TX_FLAG_DATA_TX;
// 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);
+ set_tx_empty(false , true);
+ m_rr1 &= ~RR1_ALL_SENT;
+ m_all_sent_delay = 0;
}
@@ -1264,10 +1362,8 @@ void z80sio_channel::update_dtr_rts_break()
// RTS is affected by transmit queue state in asynchronous mode
if (m_wr5 & WR5_RTS)
set_rts(0); // when the RTS bit is set, the _RTS output goes low
- else if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)
+ else if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC || (m_rr1 & RR1_ALL_SENT))
set_rts(1); // in synchronous mode, there's no automatic RTS
- else
- set_rts(((m_rr0 & RR0_TX_BUFFER_EMPTY) && !m_tx_count) ? 1 : 0); // TODO: is this affected by transmit enable?
// break immediately forces spacing condition on TxD output
out_txd_cb((m_wr5 & WR5_SEND_BREAK) ? 0 : m_txd);
@@ -1315,20 +1411,6 @@ int z80sio_channel::get_tx_word_length() const
return 5;
}
-int z80sio_channel::get_tx_word_length(uint8_t data) const
-{
- LOG("%s(%02x)\n", FUNCNAME, data);
-
- // deal with "five bits or less" mode (the actual chips probably detect a sentinel pattern in the transmit shift register)
- int bits = get_tx_word_length();
- if (5 == bits)
- {
- for (int b = 7; (b >= 4) && BIT(data, b); --b)
- --bits;
- }
- 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
@@ -1336,10 +1418,10 @@ int z80sio_channel::get_tx_word_length(uint8_t data) const
uint8_t z80sio_channel::do_sioreg_rr0()
{
LOGR("%s\n", FUNCNAME);
- if (m_tx_flags & TX_FLAG_SPECIAL)
- return m_rr0 & ~RR0_TX_BUFFER_EMPTY;
- else
- return m_rr0;
+ uint8_t tmp = m_rr0 & ~RR0_TX_BUFFER_EMPTY;
+ if (get_tx_empty())
+ tmp |= RR0_TX_BUFFER_EMPTY;
+ return tmp;
}
/*
@@ -1423,6 +1505,8 @@ void z80sio_channel::do_sioreg_wr0_resets(uint8_t data)
break;
case WR0_CRC_RESET_RX: /* In Synchronous mode: all Os (zeros) (CCITT-O CRC-16) */
LOGCMD("Z80SIO Channel %c : CRC_RESET_RX - not implemented\n", 'A' + m_index);
+ m_rx_crc = ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC) ? ~uint16_t(0U) : uint16_t(0U);
+ m_rx_crc_en = false;
break;
case WR0_CRC_RESET_TX: /* In HDLC mode: all 1s (ones) (CCITT-1) */
LOGCMD("Z80SIO Channel %c : CRC_RESET_TX\n", 'A' + m_index);
@@ -1430,7 +1514,9 @@ void z80sio_channel::do_sioreg_wr0_resets(uint8_t data)
break;
case WR0_CRC_RESET_TX_UNDERRUN: /* Resets Tx underrun/EOM bit (D6 of the SRO register) */
LOGCMD("Z80SIO Channel %c : CRC_RESET_TX_UNDERRUN\n", 'A' + m_index);
- m_rr0 &= ~RR0_TX_UNDERRUN;
+ // Command is accepted in active part of packet only
+ if (m_tx_in_pkt)
+ m_rr0 &= ~RR0_TX_UNDERRUN;
break;
default: /* Will not happen unless someone messes with the mask */
logerror("Z80SIO Channel %c : %s Wrong CRC reset/init command:%02x\n", 'A' + m_index, FUNCNAME, data & WR0_CRC_RESET_CODE_MASK);
@@ -1457,13 +1543,9 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data)
else
{
LOGCMD("%s Ch:%c : Send abort command\n", FUNCNAME, 'A' + m_index);
- // FIXME: how does this interact with interrupts?
- // For now assume it behaves like automatically sending CRC and generates a transmit interrupt when a new frame can be sent.
- tx_setup(0xff, 8, 0, true, true);
- m_rr0 |= RR0_TX_BUFFER_EMPTY;
- m_rr1 &= ~RR1_ALL_SENT;
- if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
- set_ready(false);
+ bool prev_tx_empty = get_tx_empty();
+ tx_setup(0xff, 8, true, false, true);
+ set_tx_empty(prev_tx_empty , true);
}
break;
case WR0_RESET_EXT_STATUS:
@@ -1492,12 +1574,12 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data)
{
// clearing framing and overrun errors advances the FIFO
// TODO: Intel 8274 manual doesn't mention this behaviour - is it specific to Z80 SIO?
- m_rr1 &= ~(RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
+ m_rr1 &= ~(RR1_END_OF_FRAME | 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);
+ m_rr1 &= ~(RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
}
break;
case WR0_RETURN_FROM_INT:
@@ -1547,7 +1629,7 @@ void z80sio_channel::do_sioreg_wr1(uint8_t data)
else if (data & WR1_WRDY_ON_RX_TX)
set_ready(bool(m_rr0 & RR0_RX_CHAR_AVAILABLE));
else
- set_ready((m_rr0 & RR0_TX_BUFFER_EMPTY) && !(m_tx_flags & TX_FLAG_SPECIAL));
+ set_ready(m_rr0 & RR0_TX_BUFFER_EMPTY);
}
void z80sio_channel::do_sioreg_wr2(uint8_t data)
@@ -1576,16 +1658,7 @@ void z80sio_channel::do_sioreg_wr3(uint8_t data)
else if ((data & WR3_ENTER_HUNT_PHASE) && ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC))
{
// TODO: should this re-initialise hunt logic if already in hunt phase for 8-bit/16-bit/SDLC sync?
- if ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_EXT)
- {
- m_rx_bit = 0;
- }
- else if (!(m_rr0 & RR0_SYNC_HUNT))
- {
- m_rx_bit = 0;
- m_rr0 |= RR0_SYNC_HUNT;
- trigger_ext_int();
- }
+ enter_hunt_mode();
}
}
@@ -1693,21 +1766,23 @@ void z80sio_channel::data_write(uint8_t data)
// fill transmit buffer
m_tx_data = data;
- m_rr0 &= ~RR0_TX_BUFFER_EMPTY;
- m_rr1 &= ~RR1_ALL_SENT;
- if ((m_wr1 & WR1_WRDY_ENABLE) && !(m_wr1 & WR1_WRDY_ON_RX_TX))
- set_ready(false);
+ set_tx_empty(get_tx_empty() , false);
+ if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)
+ m_tx_in_pkt = true;
+ else
+ {
+ // ALL_SENT is only meaningful in async mode, in sync mode it's always 1
+ m_rr1 &= ~RR1_ALL_SENT;
+ m_all_sent_delay = 0;
+ }
- // handle automatic RTS
bool const async((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC);
- if (async && !(m_wr5 & WR5_RTS))
- set_rts(0); // TODO: if transmission is disabled when the data buffer is full, is this still asserted?
// clear transmit interrupt
m_uart->clear_interrupt(m_index, INT_TRANSMIT);
// may be possible to transmit immediately (synchronous mode will load when sync pattern completes)
- if (async && !m_tx_bits && transmit_allowed())
+ if (async && is_tx_idle() && transmit_allowed())
async_tx_setup();
}
@@ -1750,6 +1825,13 @@ void z80sio_channel::advance_rx_fifo()
}
}
+uint8_t z80sio_channel::get_special_rx_mask() const
+{
+ return ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC) ?
+ (RR1_RX_OVERRUN_ERROR | RR1_END_OF_FRAME) :
+ (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR);
+}
+
//-------------------------------------------------
// receive_enabled - conditions have changed
@@ -1761,73 +1843,282 @@ void z80sio_channel::receive_enabled()
bool const sync_mode((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC);
m_rx_count = sync_mode ? 0 : ((get_clock_mode() - 1) / 2);
m_rx_bit = 0;
- if (sync_mode && ((m_wr4 & WR4_SYNC_MODE_MASK) != WR4_SYNC_MODE_EXT))
- m_rr0 |= RR0_SYNC_HUNT;
+ if (sync_mode)
+ enter_hunt_mode();
}
+void z80sio_channel::enter_hunt_mode()
+{
+ if (!(m_rr0 & RR0_SYNC_HUNT))
+ {
+ m_rx_sync_fsm = SYNC_FSM_HUNT;
+ m_rr0 |= RR0_SYNC_HUNT;
+ trigger_ext_int();
+ }
+}
//-------------------------------------------------
// sync_receive - synchronous reception handler
//-------------------------------------------------
-
void z80sio_channel::sync_receive()
{
- // TODO: this is a fundamentally flawed approach - it's just the quickest way to get uts20 to pass some tests
- // Sync acquisition works, but sync load suppression doesn't work right.
- // Assembled data needs to be separated from the receive shift register for SDLC.
- // Supporting receive checksum for modes other than SDLC is going to be very complicated due to all the bit delays involved.
+ LOGBIT("%.6f Channel %c Sync Received Bit %d, sync=%02x, sr=%03x, crc_dly=%02x, crc=%04x, FSM=%d, bit=%d, limit=%d\n" , machine().time().as_double(), 'A' + m_index, m_dlyd_rxd, m_rx_sync_sr, m_rx_sr, m_rx_crc_delay, m_rx_crc, m_rx_sync_fsm, m_rx_bit, m_rx_bit_limit);
- bool const ext_sync((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_EXT);
- bool const hunt_phase(ext_sync ? m_sync : (m_rr0 & RR0_SYNC_HUNT));
- if (hunt_phase)
+ bool sync_sr_out = BIT(m_rx_sync_sr , 0);
+ m_rx_sync_sr = (m_rx_sync_sr >> 1) & 0x7f;
+ if (m_dlyd_rxd)
+ m_rx_sync_sr |= 0x80;
+
+ bool wr7_matched = m_rx_sync_sr == m_wr7;
+
+ switch (m_rx_sync_fsm)
{
- // check for sync detection
- bool acquired(false);
- int limit(16);
+ case SYNC_FSM_HUNT:
+ {
+ bool got_sync = false;
switch (m_wr4 & WR4_SYNC_MODE_MASK)
{
case WR4_SYNC_MODE_8_BIT:
- case WR4_SYNC_MODE_SDLC:
- acquired = (m_rx_bit >= 8) && ((m_rx_sr & 0xff00U) == (uint16_t(m_wr7) << 8));
- limit = 8;
+ if (wr7_matched)
+ {
+ LOGRCV("Channel %c 8-bit Sync Acquired\n", 'A' + m_index);
+ got_sync = true;
+ }
break;
+
case WR4_SYNC_MODE_16_BIT:
- acquired = (m_rx_bit >= 16) && (m_rx_sr == ((uint16_t(m_wr7) << 8) | uint16_t(m_wr6)));
+ {
+ m_rx_sr = (m_rx_sr >> 1) & 0x7f;
+ if (sync_sr_out)
+ m_rx_sr |= 0x80;
+ if ((m_rx_sr & 0xff) == m_wr6 && wr7_matched)
+ {
+ LOGRCV("Channel %c 16-bit Sync Acquired\n", 'A' + m_index);
+ got_sync = true;
+ }
+ break;
+ }
+
+ case WR4_SYNC_MODE_EXT:
+ // Not entirely correct: sync input is synchronized 2 bits off in the real hw
+ got_sync = m_sync;
+ break;
+
+ default:
break;
}
- if (acquired)
+ if (got_sync)
{
- // TODO: make this do something sensible in SDLC mode
- // FIXME: set sync output for one receive bit cycle
- // FIXME: what if sync load isn't suppressed?
- LOGRCV("%s() Channel %c Character Sync Acquired\n", FUNCNAME, 'A' + m_index);
- m_rr0 &= ~RR0_SYNC_HUNT;
+ if (m_rr0 & RR0_SYNC_HUNT)
+ {
+ m_rr0 &= ~RR0_SYNC_HUNT;
+ trigger_ext_int();
+ }
+ m_rx_sync_fsm = SYNC_FSM_1ST_CHAR;
+ m_rx_crc_en = false;
m_rx_bit = 0;
- trigger_ext_int();
+ m_rx_bit_limit = get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0);
+ m_rx_parity = false;
}
- else
+ }
+ break;
+
+ case SYNC_FSM_1ST_CHAR:
+ case SYNC_FSM_IN_FRAME:
+ {
+ bool rx_sr_out = BIT(m_rx_sr , 0);
+ bool rx_crc_delay_out = BIT(m_rx_crc_delay , 0);
+ m_rx_crc_delay = (m_rx_crc_delay >> 1);
+ if (rx_sr_out)
+ m_rx_crc_delay |= 0x80;
+ if (m_rx_crc_en)
+ update_crc(m_rx_crc , rx_crc_delay_out);
+ m_rx_sr = (m_rx_sr >> 1) & ((1U << (m_rx_bit_limit - 1)) - 1);
+ if (m_dlyd_rxd)
{
- // track number of bits we have
- m_rx_bit = (std::min)(m_rx_bit + 1, limit);
+ m_rx_sr |= (1U << (m_rx_bit_limit - 1));
+ m_rx_parity = !m_rx_parity;
}
+ if (++m_rx_bit == m_rx_bit_limit)
+ {
+ if (!(m_wr3 & WR3_SYNC_CHAR_LOAD_INHIBIT) ||
+ ((m_rx_sr & 0xff) != m_wr6 && !wr7_matched))
+ {
+ uint8_t status_byte = 0;
+ if (m_rx_crc != 0)
+ status_byte |= RR1_CRC_FRAMING_ERROR;
+ if (m_wr4 & WR4_PARITY_EVEN)
+ m_rx_parity = !m_rx_parity;
+ if (!m_rx_parity && (m_wr4 & WR4_PARITY_ENABLE))
+ status_byte |= RR1_PARITY_ERROR;
+ uint8_t data = m_rx_sr & 0xff;
+ if (m_rx_bit_limit < 8)
+ // Fill the unused part of character with ones
+ data |= ~((1U << m_rx_bit_limit) - 1);
+ queue_received(data , status_byte);
+ }
+ m_rx_bit = 0;
+ m_rx_bit_limit = get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0);
+ m_rx_parity = false;
+ m_rx_crc_en = (m_rx_sync_fsm == SYNC_FSM_IN_FRAME) && (m_wr3 & WR3_RX_CRC_ENABLE);
+ m_rx_sync_fsm = SYNC_FSM_IN_FRAME;
+ }
+ break;
+ }
+
+ default:
+ LOG("Invalid Sync FSM state (%d)\n" , m_rx_sync_fsm);
+ m_rx_sync_fsm = SYNC_FSM_HUNT;
+ }
+
+ m_dlyd_rxd = m_rxd;
+}
+
+//-------------------------------------------------
+// sdlc_receive - SDLC reception handler
+//-------------------------------------------------
+void z80sio_channel::sdlc_receive()
+{
+ LOGBIT("Channel %c SDLC Received Bit %d, sync=%02x, sr=%03x, FSM=%d, bit=%d, limit=%d\n", 'A' + m_index, m_rxd, m_rx_sync_sr, m_rx_sr, m_rx_sync_fsm, m_rx_bit, m_rx_bit_limit);
+
+ // Check for flag
+ bool flag_matched = m_rx_sync_sr == m_wr7;
+
+ // Shift RxD into sync SR
+ bool sync_sr_out = BIT(m_rx_sync_sr , 0);
+ m_rx_sync_sr >>= 1;
+ if (m_rxd)
+ m_rx_sync_sr |= 0x80;
+
+ // Zero deletion & abort detection
+ bool zero_deleted = false;
+ if (sync_sr_out)
+ {
+ m_rx_sr = (m_rx_sr >> 1) | (1U << 10);
+ if (m_rx_one_cnt < 7 && ++m_rx_one_cnt == 7)
+ {
+ LOGRCV("SDLC Abort detected\n");
+ m_rr0 |= RR0_BREAK_ABORT;
+ if (!m_brk_latched) {
+ m_brk_latched = 1;
+ trigger_ext_int();
+ }
+ enter_hunt_mode();
+ }
+ }
+ else if (m_rx_one_cnt == 5)
+ {
+ m_rx_one_cnt = 0;
+ // Ignore the zero
+ zero_deleted = true;
}
else
{
- // FIXME: SDLC needs to monitor for flag/abort
- // FIXME: what if sync load is suppressed?
- // FIXME: what about receive checksum and the nasty internal shift register delays?
- int const word_length(get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0));
- if (++m_rx_bit == word_length)
+ m_rx_sr >>= 1;
+ m_rx_one_cnt = 0;
+ if (m_rr0 & RR0_BREAK_ABORT)
{
- uint16_t const data((m_rx_sr >> (16 - word_length)) | (~uint16_t(0) << word_length));
- m_rx_bit = 0;
- LOGRCV("%s() Channel %c Received Data %02x\n", FUNCNAME, 'A' + m_index, data & 0xff);
- queue_received(data, 0U);
+ m_rr0 &= ~RR0_BREAK_ABORT;
+ if (!m_brk_latched) {
+ m_brk_latched = 1;
+ trigger_ext_int();
+ }
}
}
- LOGBIT("%s() Channel %c Read Bit %d\n", FUNCNAME, 'A' + m_index, m_rxd);
- m_rx_sr = (m_rx_sr >> 1) | (m_rxd ? 0x8000U : 0x0000U);
+ switch (m_rx_sync_fsm)
+ {
+ case SYNC_FSM_HUNT:
+ case SYNC_FSM_EVICT:
+ if (flag_matched)
+ {
+ // Got sync
+ m_rx_sync_fsm = SYNC_FSM_EVICT;
+ m_rx_bit = 0;
+ m_rx_bit_limit = 7;
+ LOGRCV("Channel %c SDLC Sync Acquired\n", 'A' + m_index);
+ if (m_rr0 & RR0_SYNC_HUNT)
+ {
+ m_rr0 &= ~RR0_SYNC_HUNT;
+ trigger_ext_int();
+ }
+ }
+ else if (m_rx_sync_fsm == SYNC_FSM_EVICT && ++m_rx_bit == m_rx_bit_limit)
+ {
+ m_rx_sync_fsm = SYNC_FSM_1ST_CHAR;
+ m_rx_crc = ~0;
+ m_rx_bit = 0;
+ m_rx_bit_limit = 11;
+ }
+ break;
+
+ case SYNC_FSM_1ST_CHAR:
+ case SYNC_FSM_IN_FRAME:
+ if (zero_deleted)
+ break;
+ if (++m_rx_bit == m_rx_bit_limit)
+ m_rx_bit = 0;
+ if (flag_matched)
+ {
+ // Got closing flag
+ if (m_rx_sync_fsm != SYNC_FSM_1ST_CHAR)
+ {
+ // Frame ended
+ LOGRCV("SDLC frame ended, CRC=%04x, residual=%d\n" , m_rx_crc , m_rx_bit);
+ uint8_t status_byte = RR1_END_OF_FRAME;
+ if (m_rx_crc != SDLC_RESIDUAL)
+ status_byte |= RR1_CRC_FRAMING_ERROR;
+ // The residue code is nothing but the bit-reversed accumulated bit count
+ if (BIT(m_rx_bit , 0))
+ status_byte |= 0x08;
+ if (BIT(m_rx_bit , 1))
+ status_byte |= 0x04;
+ if (BIT(m_rx_bit , 2))
+ status_byte |= 0x02;
+ // Is the last character masked according to rx word length?
+ // We don't mask it here, after all it just holds a (useless) part of CRC
+ queue_received(m_rx_sr & 0xff , status_byte);
+ }
+ // else: frame ended before 11 bits are received, discard it
+ m_rx_sync_fsm = SYNC_FSM_EVICT;
+ m_rx_bit = 0;
+ m_rx_bit_limit = 7;
+ }
+ else
+ {
+ // Update rx CRC
+ update_crc(m_rx_crc , sync_sr_out);
+ LOGBIT("SDLC CRC=%04x/%d\n" , m_rx_crc , sync_sr_out);
+
+ if (m_rx_bit == 0)
+ {
+ // Check for address byte
+ if (m_rx_sync_fsm == SYNC_FSM_1ST_CHAR && (m_wr3 & WR3_ADDRESS_SEARCH_MODE) &&
+ (m_rx_sr & 0xff) != 0xff && (m_rx_sr & 0xff) != m_wr6)
+ {
+ LOGRCV("Channel %c SDLC Address %02x not matching %02x\n" , 'A' + m_index , m_rx_sr & 0xff , m_wr6);
+ // Address not matching, ignore this frame
+ m_rx_sync_fsm = SYNC_FSM_HUNT;
+ }
+ else
+ {
+ m_rx_bit_limit = get_rx_word_length();
+ uint8_t data = m_rx_sr & 0xff;
+ if (m_rx_bit_limit != 8)
+ // Fill the unused part of character with ones
+ data |= ~((1U << m_rx_bit_limit) - 1);
+ LOGRCV("SDLC rx data=%02x (%d bits)\n" , data , m_rx_bit_limit);
+ queue_received(data , 0);
+ m_rx_sync_fsm = SYNC_FSM_IN_FRAME;
+ }
+ }
+ }
+ break;
+
+ default:
+ LOG("Invalid SDLC FSM state (%d)\n" , m_rx_sync_fsm);
+ m_rx_sync_fsm = SYNC_FSM_HUNT;
+ }
}
//-------------------------------------------------
@@ -1844,20 +2135,6 @@ void z80sio_channel::receive_data()
void z80sio_channel::queue_received(uint16_t data, uint32_t error)
{
- if (m_wr4 & WR4_PARITY_ENABLE)
- {
- int const word_length = get_rx_word_length();
- uint16_t par(data);
- for (int i = 1; word_length >= i; ++i)
- par ^= BIT(par, i);
-
- if (bool(BIT(par, 0)) == bool(m_wr4 & WR4_PARITY_EVEN))
- {
- LOGRCV(" Parity error detected\n");
- error |= RR1_PARITY_ERROR;
- }
- }
-
if (3 == m_rx_fifo_depth)
{
LOG(" Receive FIFO overrun detected\n");
@@ -1875,7 +2152,7 @@ void z80sio_channel::queue_received(uint16_t data, uint32_t error)
m_rx_data_fifo |= uint32_t(data & 0x00ffU) << (8 * m_rx_fifo_depth);
m_rx_error_fifo |= error << (8 * m_rx_fifo_depth);
if (!m_rx_fifo_depth)
- m_rr1 |= uint8_t(error);
+ m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(error);
++m_rx_fifo_depth;
}
@@ -1887,7 +2164,7 @@ void z80sio_channel::queue_received(uint16_t data, uint32_t error)
switch (m_wr1 & WR1_RX_INT_MODE_MASK)
{
case WR1_RX_INT_FIRST:
- if (m_rx_first || (error & (RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR)))
+ if (m_rx_first || (error & get_special_rx_mask()))
m_uart->trigger_interrupt(m_index, INT_RECEIVE);
m_rx_first = 0;
break;
@@ -1915,9 +2192,8 @@ WRITE_LINE_MEMBER( z80sio_channel::cts_w )
m_cts = state;
trigger_ext_int();
- // this may enable transmission
- if (!state)
- transmit_enable();
+ // this may enable/disable transmission
+ transmit_enable();
}
}
@@ -1966,27 +2242,21 @@ 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');
+ //if ((receive_allowed() || m_rx_bit != 0) && state && !m_rx_clock)
if (receive_allowed() && state && !m_rx_clock)
{
// RxD sampled on rising edge
int const clocks = get_clock_mode() - 1;
- // break termination detection
- // TODO: how does this interact with receiver being disable or synchronous modes?
- if (m_rxd && !m_brk_latched && (m_rr0 & RR0_BREAK_ABORT))
- {
- LOGRCV("Break termination detected\n");
- m_rr0 &= ~RR0_BREAK_ABORT;
- m_brk_latched = 1;
- trigger_ext_int();
- }
-
if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_SYNC)
{
// synchronous receive is a different beast
if (!m_rx_count)
{
- sync_receive();
+ if ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC)
+ sdlc_receive();
+ else
+ sync_receive();
m_rx_count = clocks;
}
else
@@ -1994,70 +2264,98 @@ WRITE_LINE_MEMBER( z80sio_channel::rxc_w )
--m_rx_count;
}
}
- else if (!m_rx_bit)
+ else if (!(m_rr0 & RR0_BREAK_ABORT) || m_rxd)
{
- // 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)
+ // break termination detection
+ if ((m_rr0 & RR0_BREAK_ABORT) && m_rxd)
{
- // half a bit period expired, start shifting bits
- m_rx_count = clocks;
- ++m_rx_bit;
- m_rx_sr = ~uint16_t(0U);
+ LOGRCV("Break termination detected\n");
+ m_rr0 &= ~RR0_BREAK_ABORT;
+ if (!m_brk_latched) {
+ m_brk_latched = 1;
+ trigger_ext_int();
+ }
}
- else
+ if (!m_rx_bit)
{
- // ensure start bit lasts long enough
- --m_rx_count;
+ // 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));
- int const word_length(get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0));
- bool const stop_reached((word_length + 1) == m_rx_bit);
- LOGBIT("%s() Channel %c Received %s Bit %d\n", FUNCNAME, 'A' + m_index, stop_reached ? "Stop" : "Data", m_rxd);
-
- if (stop_reached)
+ else if (!m_rx_count)
{
- // this is the stop bit - framing error adds a half bit period
- m_rx_count = m_rxd ? (clocks / 2) : clocks;
- m_rx_bit = 0;
-
- LOGRCV("%s() Channel %c Received Data %02x\n", FUNCNAME, 'A' + m_index, m_rx_sr & 0xff);
-
- // check framing errors and break condition
- uint16_t const stop_bit = uint16_t(1U) << word_length;
- bool const brk(!(m_rx_sr & ((stop_bit << 1) - 1)));
- queue_received(m_rx_sr | stop_bit, (m_rx_sr & stop_bit) ? 0U : RR1_CRC_FRAMING_ERROR);
-
- // break interrupt
- if (brk && !m_brk_latched && !(m_rr0 & RR0_BREAK_ABORT))
+ // sample a data/parity/stop bit
+ if (!m_rxd)
+ m_rx_sr &= ~uint16_t(1U << (m_rx_bit - 1));
+ int const word_length(get_rx_word_length() + ((m_wr4 & WR4_PARITY_ENABLE) ? 1 : 0));
+ bool const stop_reached((word_length + 1) == m_rx_bit);
+ LOGBIT("%s() Channel %c Received %s Bit %d\n", FUNCNAME, 'A' + m_index, stop_reached ? "Stop" : "Data", m_rxd);
+
+ if (stop_reached)
{
- LOGRCV("Break detected\n");
- m_rr0 |= RR0_BREAK_ABORT;
- m_brk_latched = 1;
- trigger_ext_int();
+ // this is the stop bit - framing error adds a half bit period
+ m_rx_count = m_rxd ? (clocks / 2) : clocks;
+ m_rx_bit = 0;
+
+ LOGRCV("%s() Channel %c Received Data %02x\n", FUNCNAME, 'A' + m_index, m_rx_sr & 0xff);
+
+ // check framing errors and break condition
+ uint16_t const stop_bit = uint16_t(1U) << word_length;
+ bool const brk(!(m_rx_sr & ((stop_bit << 1) - 1)));
+ uint8_t error = brk || (m_rx_sr & stop_bit) ? 0U : RR1_CRC_FRAMING_ERROR;
+ if (m_wr4 & WR4_PARITY_ENABLE)
+ {
+ int const word_length = get_rx_word_length();
+ 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))
+ {
+ LOGRCV(" Parity error detected\n");
+ error |= RR1_PARITY_ERROR;
+ }
+ }
+
+ queue_received(m_rx_sr | stop_bit, error);
+
+ // break interrupt
+ if (brk && !m_brk_latched && !(m_rr0 & RR0_BREAK_ABORT))
+ {
+ LOGRCV("Break detected\n");
+ m_rr0 |= RR0_BREAK_ABORT;
+ m_brk_latched = 1;
+ trigger_ext_int();
+ }
+ }
+ else
+ {
+ // wait a whole bit period for the next bit
+ m_rx_count = clocks;
+ ++m_rx_bit;
}
}
else
{
- // wait a whole bit period for the next bit
- m_rx_count = clocks;
- ++m_rx_bit;
+ // bit period hasn't expired
+ --m_rx_count;
}
}
- else
- {
- // bit period hasn't expired
- --m_rx_count;
- }
}
m_rx_clock = state;
}
@@ -2072,87 +2370,142 @@ WRITE_LINE_MEMBER( z80sio_channel::txc_w )
if (!state && m_tx_clock)
{
// falling edge active
- if (m_tx_count)
+ if (m_tx_count == 0)
{
- // divide transmit clock
- --m_tx_count;
- }
- else if (!m_tx_bits)
- {
- // idle marking line
- if (!m_txd)
- {
- m_txd = 1;
- if (!(m_wr5 & WR5_SEND_BREAK))
- out_txd_cb(1);
- }
-
- if (((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC) && (m_rr0 & RR0_TX_BUFFER_EMPTY))
- {
- // when the RTS bit is reset in asynchronous mode, the _RTS output goes high after the transmitter empties
- if (!(m_wr5 & WR5_RTS) && !m_rts)
- set_rts(1); // TODO: if transmission is disabled when the data buffer is full, is this still asserted?
-
- // if transmit buffer is empty in asynchronous mode then all characters have been sent
- m_rr1 |= RR1_ALL_SENT;
- }
+ // x1 clock
+ m_tx_phase = true;
+ // Shift delay by a half bit and duplicate last input bit
+ m_tx_delay = (m_tx_delay << 1) | (m_tx_delay & 1);
}
else
+ m_tx_count--;
+ if (m_tx_count == 0)
{
- bool const sdlc_mode((m_wr4 & (WR4_STOP_BITS_MASK | WR4_SYNC_MODE_MASK)) == (WR4_STOP_BITS_SYNC | WR4_SYNC_MODE_SDLC));
- bool const framing(m_tx_flags & TX_FLAG_FRAMING);
- bool const stuff_zero(sdlc_mode && !framing && ((m_tx_hist & 0x1fU) == 0x1fU));
-
- // have bits, shift out
- int const db(stuff_zero ? 0 : BIT(m_tx_sr, 0));
- if (!stuff_zero)
- {
- LOGBIT("%s() Channel %c transmit %s bit %d m_wr5:%02x\n", FUNCNAME, 'A' + m_index, framing ? "framing" : "data", db, m_wr5);
- if (m_tx_parity >= m_tx_bits)
- m_tx_parity = 0;
- else if (m_tx_parity)
- m_tx_sr ^= uint16_t(db) << (m_tx_bits - m_tx_parity);
- m_tx_sr >>= 1;
-
- if (m_tx_flags & TX_FLAG_CRC)
- {
- uint16_t const poly((m_wr5 & WR5_CRC16) ? 0x8005U : device_sdlc_consumer_interface::POLY_SDLC);
- m_tx_crc = device_sdlc_consumer_interface::update_frame_check(poly, m_tx_crc, db);
- }
- }
- else
+ m_tx_phase = !m_tx_phase;
+ // Load delay for half bit
+ m_tx_count = get_clock_mode() / 2;
+ // Send out a delayed half bit
+ bool new_txd = BIT(m_tx_delay , 3);
+ if (new_txd != m_txd && !(m_wr5 & WR5_SEND_BREAK))
{
- LOGBIT("%s() Channel %c stuff bit %d m_wr5:%02x\n", FUNCNAME, 'A' + m_index, db, m_wr5);
+ LOGBIT("%.6f TX %d DLY %x\n" , machine().time().as_double() , new_txd , m_tx_delay & 0xf);
+ out_txd_cb(new_txd);
}
- m_tx_hist = (m_tx_hist << 1) | db;
-
- // update output line state
- if (bool(m_txd) != bool(db))
+ m_txd = new_txd;
+ // Check for ALL SENT condition
+ if (!(m_rr1 & RR1_ALL_SENT) && BIT(m_all_sent_delay , 3))
{
- m_txd = db;
- if (!(m_wr5 & WR5_SEND_BREAK))
- out_txd_cb(m_txd);
+ LOGBIT("%.6f ALL_SENT\n" , machine().time().as_double());
+ m_rr1 |= RR1_ALL_SENT;
+ if (!(m_wr5 & WR5_RTS))
+ set_rts(1);
}
-
- // calculate next bit time
- m_tx_count = get_clock_mode();
- if (!stuff_zero && !--m_tx_bits)
+ // Shift delay by a half bit and duplicate last input bit
+ // When m_tx_phase is false, LSB is replaced by new bit (see below)
+ m_tx_delay = (m_tx_delay << 1) | (m_tx_delay & 1);
+ m_all_sent_delay <<= 1;
+ if (!m_tx_phase)
{
- switch (m_wr4 & WR4_STOP_BITS_MASK)
+ // Generate a new bit
+ bool new_bit = false;
+ if ((m_wr4 & (WR4_SYNC_MODE_MASK | WR4_STOP_BITS_MASK)) == (WR4_SYNC_MODE_SDLC | WR4_STOP_BITS_SYNC) &&
+ !(m_tx_flags & TX_FLAG_FRAMING) && (m_tx_hist & 0x1f) == 0x1f)
+ // SDLC, not sending framing & 5 ones in a row: do zero insertion
+ new_bit = false;
+ else
{
- case WR4_STOP_BITS_SYNC:
- case WR4_STOP_BITS_1:
- break;
- case WR4_STOP_BITS_1_5:
- m_tx_count = ((m_tx_count * 3) + 1) / 2; // TODO: what does 1.5 stop bits do in TxC/1 mode? the +1 here rounds it up
- break;
- case WR4_STOP_BITS_2:
- m_tx_count *= 2;
- break;
+ bool get_out = false;
+ while (!get_out)
+ {
+ // Pattern for parity bit in SR?
+ // 17x 1 || 000
+ if ((m_tx_sr & TX_SR_MASK) == 0xffff8)
+ {
+ if ((m_wr4 & WR4_PARITY_ENABLE) != 0 &&
+ (m_tx_flags & TX_FLAG_DATA_TX))
+ {
+ new_bit = m_tx_parity;
+ if (!(m_wr4 & WR4_PARITY_EVEN))
+ new_bit = !new_bit;
+ get_out = true;
+ }
+ }
+ // Pattern for 1st stop bit?
+ // 18x 1 || 00
+ else if ((m_tx_sr & TX_SR_MASK) == 0xffffc)
+ {
+ if ((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC)
+ {
+ new_bit = true;
+ get_out = true;
+ }
+ }
+ // Pattern for 2nd stop bit?
+ // 19x 1 || 0
+ else if ((m_tx_sr & TX_SR_MASK) == 0xffffe)
+ {
+ if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_1_5 ||
+ (m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_2)
+ {
+ new_bit = true;
+ if ((m_wr4 & WR4_STOP_BITS_MASK) == WR4_STOP_BITS_1_5)
+ // Force current stop bit to last for 1/2 bit time
+ m_tx_phase = true;
+ get_out = true;
+ }
+ }
+ // Pattern for idle tx?
+ // 20x 1
+ else if (is_tx_idle())
+ {
+ transmit_complete();
+ if (is_tx_idle())
+ {
+ new_bit = true;
+ get_out = true;
+ }
+ else
+ continue;
+ }
+ else if (m_tx_flags & TX_FLAG_CRC_TX)
+ {
+ // CRC bits (from MSB to LSB)
+ new_bit = BIT(m_tx_crc , 15);
+ m_tx_crc <<= 1;
+ if ((m_wr4 & WR4_SYNC_MODE_MASK) == WR4_SYNC_MODE_SDLC)
+ m_tx_crc |= 1;
+ get_out = true;
+ }
+ else
+ {
+ // Start bit or data bits
+ new_bit = BIT(m_tx_sr , 0);
+ // Update parity
+ if (new_bit)
+ m_tx_parity = !m_tx_parity;
+ // Update CRC
+ if (m_tx_flags & TX_FLAG_CRC)
+ {
+ LOGBIT("CRC %04x/%d\n" , m_tx_crc , new_bit);
+ update_crc(m_tx_crc , new_bit);
+ }
+ get_out = true;
+ }
+ // Shift right 1 bit && insert 1 at MSB
+ m_tx_sr = (m_tx_sr >> 1) | 0x80000;
+ }
+ if ((m_wr4 & WR4_STOP_BITS_MASK) != WR4_STOP_BITS_SYNC && is_tx_idle() && (m_rr0 & RR0_TX_BUFFER_EMPTY))
+ m_all_sent_delay |= 1U;
+ else
+ m_all_sent_delay = 0;
}
- transmit_complete();
+ if (m_tx_flags & TX_FLAG_FRAMING)
+ m_tx_hist = 0;
+ else
+ m_tx_hist = (m_tx_hist << 1) | new_bit;
+ // Insert new bit in delay register
+ m_tx_delay = (m_tx_delay & ~1U) | new_bit;
}
- --m_tx_count;
}
}
m_tx_clock = state;