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.cpp190
1 files changed, 114 insertions, 76 deletions
diff --git a/src/devices/machine/z80sio.cpp b/src/devices/machine/z80sio.cpp
index 11d2f4e61d0..15561da267c 100644
--- a/src/devices/machine/z80sio.cpp
+++ b/src/devices/machine/z80sio.cpp
@@ -128,6 +128,12 @@ enum : uint8_t
enum : uint8_t
{
RR1_ALL_SENT = 0x01,
+ // This bit is not actually present in RR1 register
+ // It's enqueued in the rx error FIFO to mark the spot
+ // where "interrupt on 1st rx character" should occur.
+ // It's stripped off before head of error FIFO is dequeued
+ // into RR1
+ RR1_HIDDEN_1ST_MARKER = 0x01,
RR1_RESIDUE_CODE_MASK = 0x0e,
RR1_PARITY_ERROR = 0x10,
RR1_RX_OVERRUN_ERROR = 0x20,
@@ -173,7 +179,7 @@ enum : uint8_t
WR1_RX_INT_ALL_PARITY = 0x10,
WR1_RX_INT_ALL = 0x18,
WR1_WRDY_ON_RX_TX = 0x20,
- WR1_WRDY_FUNCTION = 0x40, // WAIT not supported
+ WR1_WRDY_FUNCTION = 0x40,
WR1_WRDY_ENABLE = 0x80
};
@@ -310,11 +316,31 @@ inline void z80sio_channel::out_dtr_cb(int state)
m_uart->m_out_dtr_cb[m_index](state);
}
-inline void z80sio_channel::set_ready(bool ready)
+inline void z80sio_channel::update_wait_ready()
{
- // WAIT mode not supported yet
- if (m_wr1 & WR1_WRDY_FUNCTION)
- m_uart->m_out_wrdy_cb[m_index](ready ? 0 : 1);
+ bool ready = false;
+
+ if (m_wr1 & WR1_WRDY_ENABLE)
+ {
+ if (m_wr1 & WR1_WRDY_ON_RX_TX)
+ {
+ // Monitor rx
+ ready = bool(m_rr0 & RR0_RX_CHAR_AVAILABLE);
+ }
+ else
+ {
+ // Monitor tx
+ ready = get_tx_empty();
+ }
+ if (!(m_wr1 & WR1_WRDY_FUNCTION))
+ {
+ // Ready function has opposite polarity of wait function
+ ready = !ready;
+ }
+ }
+
+ // ready/wait is active low
+ m_uart->m_out_wrdy_cb[m_index](ready ? 0 : 1);
}
inline bool z80sio_channel::receive_allowed() const
@@ -658,6 +684,8 @@ void z80sio_device::trigger_interrupt(int index, int type)
{{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE"}}[type]);
// trigger interrupt
+ if (m_int_state[(index * 3) + type] & Z80_DAISY_INT)
+ return;
m_int_state[(index * 3) + type] |= Z80_DAISY_INT;
m_chanA->m_rr0 |= RR0_INTERRUPT_PENDING;
@@ -675,6 +703,8 @@ void z80sio_device::clear_interrupt(int index, int type)
{{"INT_TRANSMIT", "INT_EXTERNAL", "INT_RECEIVE"}}[type]);
// clear interrupt
+ if (!(m_int_state[(index * 3) + type] & Z80_DAISY_INT))
+ return;
m_int_state[(index * 3) + type] &= ~Z80_DAISY_INT;
if (std::find_if(std::begin(m_int_state), std::end(m_int_state), [] (int state) { return bool(state & Z80_DAISY_INT); }) == std::end(m_int_state))
m_chanA->m_rr0 &= ~RR0_INTERRUPT_PENDING;
@@ -733,9 +763,7 @@ 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 & (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 & m_chanA->get_special_rx_mask()))
+ if (m_chanA->m_rr1 & m_chanA->get_special_rx_mask())
return vec | 0x0eU;
else
return vec | 0x0cU;
@@ -744,9 +772,7 @@ 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 & (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 & m_chanB->get_special_rx_mask()))
+ if (m_chanB->m_rr1 & m_chanB->get_special_rx_mask())
return vec | 0x06U;
else
return vec | 0x04U;
@@ -960,7 +986,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_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK)
+ : z80sio_channel(mconfig, Z80SIO_CHANNEL, tag, owner, clock, RR1_CRC_FRAMING_ERROR | RR1_RESIDUE_CODE_MASK)
{
}
@@ -1093,7 +1119,7 @@ void z80sio_channel::device_reset()
m_tx_count = 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);
+ m_rr1 &= ~(RR1_PARITY_ERROR | RR1_RX_OVERRUN_ERROR | RR1_CRC_FRAMING_ERROR | RR1_END_OF_FRAME);
// disable receiver
m_wr3 &= ~WR3_RX_ENABLE;
@@ -1111,7 +1137,9 @@ void z80sio_channel::device_reset()
out_txd_cb(1);
m_tx_sr = ~0;
- // TODO: what happens to WAIT/READY?
+ // Disable wait & ready
+ m_wr1 &= ~WR1_WRDY_ENABLE;
+ update_wait_ready();
// reset external lines
out_rts_cb(m_rts = 1);
@@ -1159,8 +1187,7 @@ void z80sio_channel::transmit_enable()
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);
+ update_wait_ready();
}
else if (!(m_rr0 & RR0_TX_BUFFER_EMPTY))
async_tx_setup();
@@ -1294,19 +1321,13 @@ void z80sio_channel::set_tx_empty(bool prev_state, bool new_state)
else
m_rr0 &= ~RR0_TX_BUFFER_EMPTY;
+ update_wait_ready();
+
bool curr_tx_empty = get_tx_empty();
- if (!prev_state && curr_tx_empty)
+ if (!prev_state && curr_tx_empty && (m_wr1 & WR1_TX_INT_ENABLE))
{
- 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);
+ m_uart->trigger_interrupt(m_index, INT_TRANSMIT);
}
}
@@ -1657,7 +1678,7 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data)
case WR0_ERROR_RESET:
// error reset
LOGCMD("%s Ch:%c : Error Reset\n", FUNCNAME, 'A' + m_index);
- if ((WR1_RX_INT_FIRST == (m_wr1 & WR1_RX_INT_MODE_MASK)) && (m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR)))
+ if ((WR1_RX_INT_FIRST == (m_wr1 & WR1_RX_INT_MODE_MASK)) && (m_rr1 & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_END_OF_FRAME)))
{
// clearing framing and overrun errors advances the FIFO
// TODO: Intel 8274 manual doesn't mention this behaviour - is it specific to Z80 SIO?
@@ -1667,6 +1688,7 @@ void z80sio_channel::do_sioreg_wr0(uint8_t data)
else
{
m_rr1 &= ~(RR1_END_OF_FRAME | RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR | RR1_PARITY_ERROR);
+ update_rx_int();
}
break;
case WR0_RETURN_FROM_INT:
@@ -1695,12 +1717,7 @@ void z80sio_channel::do_sioreg_wr1(uint8_t data)
LOGSETUP(" - Receiver Interrupt %s\n", std::array<char const *, 4>
{{"Disabled", "on First Character", "on All Characters, Parity Affects Vector", "on All Characters"}}[(m_wr2 >> 3) & 0x03]);
- if (!(data & WR1_WRDY_ENABLE))
- set_ready(false);
- 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);
+ update_wait_ready();
}
void z80sio_channel::do_sioreg_wr2(uint8_t data)
@@ -1891,37 +1908,59 @@ void z80sio_channel::advance_rx_fifo()
m_rx_error_fifo >>= 8;
// load error status from the FIFO
- m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(m_rx_error_fifo & 0x000000ffU);
-
- // if we're in interrupt-on-first mode, clear interrupt if there's no pending error condition
- if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_FIRST)
- {
- for (int i = 0; m_rx_fifo_depth > i; ++i)
- {
- if (uint8_t(m_rx_error_fifo >> (i * 8)) & (RR1_CRC_FRAMING_ERROR | RR1_RX_OVERRUN_ERROR))
- return;
- }
- m_uart->clear_interrupt(m_index, INT_RECEIVE);
- }
+ m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(m_rx_error_fifo & 0x000000ffU & ~RR1_HIDDEN_1ST_MARKER);
}
else
{
// no more characters available in the FIFO
m_rr0 &= ~RR0_RX_CHAR_AVAILABLE;
- if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX))
- set_ready(false);
- m_uart->clear_interrupt(m_index, INT_RECEIVE);
+ update_wait_ready();
}
}
+ update_rx_int();
}
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);
+ if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_DISABLE)
+ {
+ return 0;
+ }
+ else
+ {
+ uint8_t mask = ((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);
+ if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_ALL_PARITY)
+ mask |= RR1_PARITY_ERROR;
+ return mask;
+ }
}
+void z80sio_channel::update_rx_int()
+{
+ bool state = false;
+
+ auto rx_int_mode = m_wr1 & WR1_RX_INT_MODE_MASK;
+ if (rx_int_mode != WR1_RX_INT_DISABLE)
+ {
+ if (m_rr1 & get_special_rx_mask())
+ state = true;
+ else if (m_rx_fifo_depth)
+ {
+ // FIFO not empty
+ if (rx_int_mode != WR1_RX_INT_FIRST)
+ state = true;
+ else if (m_rx_error_fifo & RR1_HIDDEN_1ST_MARKER)
+ state = true;
+ }
+ }
+ LOGINT("rx %d wr1 %02x rr1 %02x fd %u ref %06x\n", state, m_wr1, m_rr1, m_rx_fifo_depth, m_rx_error_fifo);
+ if (state)
+ m_uart->trigger_interrupt(m_index, INT_RECEIVE);
+ else
+ m_uart->clear_interrupt(m_index, INT_RECEIVE);
+}
//-------------------------------------------------
// receive_enabled - conditions have changed
@@ -2209,7 +2248,13 @@ void z80sio_channel::sdlc_receive()
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;
+ if (m_rx_sync_fsm == SYNC_FSM_1ST_CHAR)
+ {
+ // reception of 1st char clears END-OF-FRAME
+ m_rr1 &= ~RR1_END_OF_FRAME;
+ update_rx_int();
+ m_rx_sync_fsm = SYNC_FSM_IN_FRAME;
+ }
}
}
}
@@ -2240,6 +2285,13 @@ void z80sio_channel::receive_data()
void z80sio_channel::queue_received(uint16_t data, uint32_t error)
{
+ if ((m_wr1 & WR1_RX_INT_MODE_MASK) == WR1_RX_INT_FIRST && m_rx_first)
+ {
+ // insert a hidden marker for 1st received character
+ error |= RR1_HIDDEN_1ST_MARKER;
+ m_rx_first = false;
+ }
+
if (3 == m_rx_fifo_depth)
{
LOG(" Receive FIFO overrun detected\n");
@@ -2257,31 +2309,15 @@ 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 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(error);
+ m_rr1 = (m_rr1 & ~m_rr1_auto_reset) | uint8_t(error & ~RR1_HIDDEN_1ST_MARKER);
++m_rx_fifo_depth;
}
m_rr0 |= RR0_RX_CHAR_AVAILABLE;
- if ((m_wr1 & WR1_WRDY_ENABLE) && (m_wr1 & WR1_WRDY_ON_RX_TX))
- set_ready(true);
+ update_wait_ready();
// receive interrupt
- switch (m_wr1 & WR1_RX_INT_MODE_MASK)
- {
- case WR1_RX_INT_FIRST:
- if (m_rx_first || (error & get_special_rx_mask()))
- m_uart->trigger_interrupt(m_index, INT_RECEIVE);
- m_rx_first = false;
- break;
-
- case WR1_RX_INT_ALL_PARITY:
- case WR1_RX_INT_ALL:
- m_uart->trigger_interrupt(m_index, INT_RECEIVE);
- break;
-
- default:
- LOG("No receive interrupt triggered\n");
- }
+ update_rx_int();
}
@@ -2515,9 +2551,11 @@ void z80sio_channel::txc_w(int state)
// 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
+ (m_tx_flags & (TX_FLAG_DATA_TX | TX_FLAG_CRC_TX)) && (m_tx_hist & 0x1f) == 0x1f)
+ {
+ // SDLC, sending data/CRC & 5 ones in a row: do zero insertion
new_bit = false;
+ }
else
{
bool get_out = false;
@@ -2605,10 +2643,10 @@ void z80sio_channel::txc_w(int state)
else
m_all_sent_delay = 0;
}
- if (m_tx_flags & TX_FLAG_FRAMING)
- m_tx_hist = 0;
- else
+ if (m_tx_flags & (TX_FLAG_DATA_TX | TX_FLAG_CRC_TX))
m_tx_hist = (m_tx_hist << 1) | new_bit;
+ else
+ m_tx_hist = 0;
// Insert new bit in delay register
m_tx_delay = (m_tx_delay & ~1U) | new_bit;
}