68561mpcc: Detect framing and parity errors and include them in the receive FIFO; improve handling of interrupts and status register writes

mc2661: Detect framing and parity errors; allow disabling of side effects of reads
This commit is contained in:
AJR 2018-12-28 19:48:33 -05:00
parent b49825bf25
commit a67dd2df3c
3 changed files with 58 additions and 19 deletions

View File

@ -609,6 +609,12 @@ void mpcc_device::rcv_complete()
data = get_received_char(); data = get_received_char();
LOGRX("%s %02x [%c]\n", FUNCNAME, isascii(data) ? data : ' ', data); LOGRX("%s %02x [%c]\n", FUNCNAME, isascii(data) ? data : ' ', data);
uint8_t errors = 0;
if (is_receive_parity_error())
errors |= REG_RSR_CPERR;
if (is_receive_framing_error())
errors |= REG_RSR_FRERR;
// receive_data(data); // receive_data(data);
if (m_rx_data_fifo.full()) if (m_rx_data_fifo.full())
{ {
@ -622,7 +628,12 @@ void mpcc_device::rcv_complete()
} }
else else
{ {
m_rx_data_fifo.enqueue(data); if (m_rx_data_fifo.empty())
{
m_rsr |= errors;
update_interrupts(INT_RX);
}
m_rx_data_fifo.enqueue(data | errors << 8);
m_rsr |= REG_RSR_RDA; m_rsr |= REG_RSR_RDA;
// interrupt if rx data availble is enabled // interrupt if rx data availble is enabled
if (m_rier & REG_RIER_RDA) if (m_rier & REG_RIER_RDA)
@ -747,13 +758,14 @@ void mpcc_device::update_interrupts(int source)
switch(source) switch(source)
{ {
case INT_TX:
case INT_TX_TDRA: case INT_TX_TDRA:
case INT_TX_TFC: case INT_TX_TFC:
case INT_TX_TUNRN: case INT_TX_TUNRN:
case INT_TX_TFERR: case INT_TX_TFERR:
if ( m_tsr & (REG_TSR_TDRA | REG_TSR_TFC | REG_TSR_TUNRN | REG_TSR_TFERR) ) if (m_tsr & m_tier & (REG_TSR_TDRA | REG_TSR_TFC | REG_TSR_TUNRN | REG_TSR_TFERR))
{ {
LOGINT(" - Found unserved TX interrupt %02x\n", m_tsr); LOGINT(" - Found unserved TX interrupt %02x\n", m_tsr & m_tier);
m_int_state[TX_INT_PRIO] = INT_REQ; // Still TX interrupts to serve m_int_state[TX_INT_PRIO] = INT_REQ; // Still TX interrupts to serve
} }
else else
@ -761,15 +773,16 @@ void mpcc_device::update_interrupts(int source)
m_int_state[TX_INT_PRIO] = INT_NONE; // No more TX interrupts to serve m_int_state[TX_INT_PRIO] = INT_NONE; // No more TX interrupts to serve
} }
break; break;
case INT_RX:
case INT_RX_RDA: case INT_RX_RDA:
case INT_RX_EOF: case INT_RX_EOF:
case INT_RX_CPERR: case INT_RX_CPERR:
case INT_RX_FRERR: case INT_RX_FRERR:
case INT_RX_ROVRN: case INT_RX_ROVRN:
case INT_RX_RAB: case INT_RX_RAB:
if ( m_rsr & (REG_RSR_RDA | REG_RSR_EOF | REG_RSR_CPERR | REG_RSR_FRERR | REG_RSR_ROVRN | REG_RSR_RAB)) if (m_rsr & m_rier & (REG_RSR_RDA | REG_RSR_EOF | REG_RSR_CPERR | REG_RSR_FRERR | REG_RSR_ROVRN | REG_RSR_RAB))
{ {
LOGINT(" - Found unserved RX interrupt %02x\n", m_rsr); LOGINT(" - Found unserved RX interrupt %02x\n", m_rsr & m_rier);
m_int_state[RX_INT_PRIO] = INT_REQ; // Still RX interrupts to serve m_int_state[RX_INT_PRIO] = INT_REQ; // Still RX interrupts to serve
} }
else else
@ -777,12 +790,13 @@ void mpcc_device::update_interrupts(int source)
m_int_state[RX_INT_PRIO] = INT_NONE; // No more RX interrupts to serve m_int_state[RX_INT_PRIO] = INT_NONE; // No more RX interrupts to serve
} }
break; break;
case INT_SR:
case INT_SR_CTS: case INT_SR_CTS:
case INT_SR_DSR: case INT_SR_DSR:
case INT_SR_DCD: case INT_SR_DCD:
if ( m_sisr & (REG_SISR_CTST | REG_SISR_DSRT | REG_SISR_DCDT ) ) if (m_sisr & m_sier & (REG_SISR_CTST | REG_SISR_DSRT | REG_SISR_DCDT))
{ {
LOGINT(" - Found unserved SR interrupt %02x\n", m_sisr); LOGINT(" - Found unserved SR interrupt %02x\n", m_sisr & m_sier);
m_int_state[SR_INT_PRIO] = INT_REQ; // Still SR interrupts to serve m_int_state[SR_INT_PRIO] = INT_REQ; // Still SR interrupts to serve
} }
else else
@ -865,12 +879,15 @@ WRITE8_MEMBER( mpcc_device::write )
} }
} }
// TODO: Sync clear of error bits with readout from fifo
// TODO: implement Idle bit // TODO: implement Idle bit
void mpcc_device::do_rsr(uint8_t data) void mpcc_device::do_rsr(uint8_t data)
{ {
LOG("%s -> %02x\n", FUNCNAME, data); LOG("%s -> %02x\n", FUNCNAME, data);
m_rsr = data; // writing 1 resets status bits except for RDA which is read-only
m_rsr &= ~data | REG_RSR_RDA;
// status belonging to data at the head of the FIFO cannot be cleared
if (!m_rx_data_fifo.empty())
m_rsr |= m_rx_data_fifo.peek() >> 8;
update_interrupts(INT_RX); update_interrupts(INT_RX);
} }
@ -909,7 +926,7 @@ uint8_t mpcc_device::do_rdr()
if (!m_rx_data_fifo.empty()) if (!m_rx_data_fifo.empty())
{ {
// load data from the FIFO // load data from the FIFO
data = m_rx_data_fifo.dequeue(); data = m_rx_data_fifo.dequeue() & 0xff;
// Check if this was the last data and reset the interrupt and status register accordingly // Check if this was the last data and reset the interrupt and status register accordingly
if (m_rx_data_fifo.empty()) if (m_rx_data_fifo.empty())
@ -917,6 +934,11 @@ uint8_t mpcc_device::do_rdr()
m_rsr &= ~REG_RSR_RDA; m_rsr &= ~REG_RSR_RDA;
update_interrupts(INT_RX_RDA); update_interrupts(INT_RX_RDA);
} }
else
{
m_rsr |= m_rx_data_fifo.peek() >> 8;
update_interrupts(INT_RX);
}
} }
else else
{ {
@ -952,6 +974,7 @@ void mpcc_device::do_rier(uint8_t data)
LOGSETUP(" - Rx INT on Frame error : %s\n", (m_rier & REG_RIER_FRERR) ? "enabled" : "disabled"); LOGSETUP(" - Rx INT on Frame error : %s\n", (m_rier & REG_RIER_FRERR) ? "enabled" : "disabled");
LOGSETUP(" - Rx INT on Receiver overrun : %s\n", (m_rier & REG_RIER_ROVRN) ? "enabled" : "disabled"); LOGSETUP(" - Rx INT on Receiver overrun : %s\n", (m_rier & REG_RIER_ROVRN) ? "enabled" : "disabled");
LOGSETUP(" - Rx INT on Abort/Break : %s\n", (m_rier & REG_RIER_RAB) ? "enabled" : "disabled"); LOGSETUP(" - Rx INT on Abort/Break : %s\n", (m_rier & REG_RIER_RAB) ? "enabled" : "disabled");
update_interrupts(INT_RX);
} }
uint8_t mpcc_device::do_rier() uint8_t mpcc_device::do_rier()
@ -1004,7 +1027,8 @@ void mpcc_device::do_tdr(uint8_t data)
void mpcc_device::do_tsr(uint8_t data) void mpcc_device::do_tsr(uint8_t data)
{ {
LOGINT("%s -> %02x\n", FUNCNAME, data); LOGINT("%s -> %02x\n", FUNCNAME, data);
m_tsr = data; // writing 1 resets status bits except for TDRA which is read-only
m_tsr &= ~data | REG_TSR_TDRA;
update_interrupts(INT_TX); update_interrupts(INT_TX);
} }
@ -1060,6 +1084,7 @@ void mpcc_device::do_tier(uint8_t data)
LOGSETUP(" - Tx INT on Frame complete : %s\n", (m_tier & REG_TIER_TFC ) ? "enabled" : "disabled"); LOGSETUP(" - Tx INT on Frame complete : %s\n", (m_tier & REG_TIER_TFC ) ? "enabled" : "disabled");
LOGSETUP(" - Tx INT on Underrun : %s\n", (m_tier & REG_TIER_TUNRN) ? "enabled" : "disabled"); LOGSETUP(" - Tx INT on Underrun : %s\n", (m_tier & REG_TIER_TUNRN) ? "enabled" : "disabled");
LOGSETUP(" - Tx INT on Frame error : %s\n", (m_tier & REG_TIER_TFERR) ? "enabled" : "disabled"); LOGSETUP(" - Tx INT on Frame error : %s\n", (m_tier & REG_TIER_TFERR) ? "enabled" : "disabled");
update_interrupts(INT_TX);
} }
uint8_t mpcc_device::do_tier() uint8_t mpcc_device::do_tier()
@ -1131,6 +1156,7 @@ void mpcc_device::do_sier(uint8_t data)
LOGSETUP(" - Serial interface INT on CTS: %s\n", (m_sier & REG_SIER_CTS) ? "enabled" : "disabled"); LOGSETUP(" - Serial interface INT on CTS: %s\n", (m_sier & REG_SIER_CTS) ? "enabled" : "disabled");
LOGSETUP(" - Serial interface INT on DSR: %s\n", (m_sier & REG_SIER_DSR) ? "enabled" : "disabled"); LOGSETUP(" - Serial interface INT on DSR: %s\n", (m_sier & REG_SIER_DSR) ? "enabled" : "disabled");
LOGSETUP(" - Serial interface INT on DCD: %s\n", (m_sier & REG_SIER_DCD) ? "enabled" : "disabled"); LOGSETUP(" - Serial interface INT on DCD: %s\n", (m_sier & REG_SIER_DCD) ? "enabled" : "disabled");
update_interrupts(INT_SR);
} }
uint8_t mpcc_device::do_sier() uint8_t mpcc_device::do_sier()

View File

@ -207,7 +207,7 @@ protected:
uint8_t m_rdr; uint8_t m_rdr;
uint8_t do_rdr(); uint8_t do_rdr();
// TODO: investigate if 4 x 16 bit wide FIFO is needed for 16 bit mode // TODO: investigate if 4 x 16 bit wide FIFO is needed for 16 bit mode
util::fifo<uint8_t, 8> m_rx_data_fifo; util::fifo<uint16_t, 8> m_rx_data_fifo;
// RIVNR - Rx Interrupt Vector Number Register // RIVNR - Rx Interrupt Vector Number Register
uint8_t m_rivnr; uint8_t m_rivnr;

View File

@ -212,6 +212,10 @@ void mc2661_device::rcv_complete()
receive_register_extract(); receive_register_extract();
m_rhr = get_received_char(); m_rhr = get_received_char();
m_sr |= STATUS_RXRDY; m_sr |= STATUS_RXRDY;
if (is_receive_parity_error())
m_sr |= STATUS_PE;
if (is_receive_framing_error())
m_sr |= STATUS_FE;
m_write_rxrdy(ASSERT_LINE); m_write_rxrdy(ASSERT_LINE);
} }
@ -228,8 +232,11 @@ uint8_t mc2661_device::read(offs_t offset)
{ {
case REGISTER_HOLDING: case REGISTER_HOLDING:
data = m_rhr; data = m_rhr;
if (!machine().side_effects_disabled())
{
m_sr &= ~STATUS_RXRDY; m_sr &= ~STATUS_RXRDY;
m_write_rxrdy(CLEAR_LINE); m_write_rxrdy(CLEAR_LINE);
}
break; break;
case REGISTER_STATUS: case REGISTER_STATUS:
@ -239,14 +246,20 @@ uint8_t mc2661_device::read(offs_t offset)
case REGISTER_MODE: case REGISTER_MODE:
data = m_mr[m_mode_index]; data = m_mr[m_mode_index];
if (!machine().side_effects_disabled())
{
m_mode_index++; m_mode_index++;
m_mode_index &= 0x01; m_mode_index &= 0x01;
}
break; break;
case REGISTER_COMMAND: case REGISTER_COMMAND:
if (!machine().side_effects_disabled())
{
m_mode_index = 0; m_mode_index = 0;
m_sync_index = 0; m_sync_index = 0;
}
data = m_cr; data = m_cr;
break; break;
@ -415,7 +428,7 @@ void mc2661_device::write(offs_t offset, uint8_t data)
} }
if (!COMMAND_RXEN) if (!COMMAND_RXEN)
{ {
m_sr &= ~STATUS_RXRDY; m_sr &= ~(STATUS_RXRDY | STATUS_FE | STATUS_OVERRUN | STATUS_PE);
m_write_rxrdy(CLEAR_LINE); m_write_rxrdy(CLEAR_LINE);
} }
if (COMMAND_RESET) if (COMMAND_RESET)