diff options
author | 2019-05-26 18:00:33 +0200 | |
---|---|---|
committer | 2019-05-26 12:00:33 -0400 | |
commit | 41c456c3a61b49d71f75e3c90bff3044c621b5f1 (patch) | |
tree | 9f888378b58c4ed6e825065642f1e54797c42d6b /src/devices/machine/z80sio.cpp | |
parent | ecf043eca1bc1144ac4b9766cfb4ea7b113cc57c (diff) |
Hp9845: added 98046 module emulation (#5115)
* hp9845: fixed handling of optional ROMs (nw)
* z80sio: massive enhancement to Z80 SIO driven by HP98046 test sw
* hp9845: implemented the HP98046 serial I/O module
Diffstat (limited to 'src/devices/machine/z80sio.cpp')
-rw-r--r-- | src/devices/machine/z80sio.cpp | 1007 |
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; |