ins8250: Detect framing and parity errors; allow side effects of reads to be disabled

This commit is contained in:
AJR 2018-12-28 20:32:25 -05:00
parent a67dd2df3c
commit 7b8ae74be1
2 changed files with 59 additions and 27 deletions

View File

@ -97,6 +97,8 @@ History:
#include "emu.h" #include "emu.h"
#include "machine/ins8250.h" #include "machine/ins8250.h"
#include <algorithm>
//#define VERBOSE 1 //#define VERBOSE 1
#include "logmacro.h" #include "logmacro.h"
@ -159,9 +161,9 @@ void pc16552_device::device_start()
static constexpr uint8_t INS8250_LSR_TSRE = 0x40; static constexpr uint8_t INS8250_LSR_TSRE = 0x40;
static constexpr uint8_t INS8250_LSR_THRE = 0x20; static constexpr uint8_t INS8250_LSR_THRE = 0x20;
//static constexpr uint8_t INS8250_LSR_BI = 0x10; static constexpr uint8_t INS8250_LSR_BI = 0x10;
//static constexpr uint8_t INS8250_LSR_FE = 0x08; static constexpr uint8_t INS8250_LSR_FE = 0x08;
//static constexpr uint8_t INS8250_LSR_PE = 0x04; static constexpr uint8_t INS8250_LSR_PE = 0x04;
static constexpr uint8_t INS8250_LSR_OE = 0x02; static constexpr uint8_t INS8250_LSR_OE = 0x02;
static constexpr uint8_t INS8250_LSR_DR = 0x01; static constexpr uint8_t INS8250_LSR_DR = 0x01;
@ -406,13 +408,16 @@ READ8_MEMBER( ins8250_uart_device::ins8250_r )
data = (m_regs.dl & 0xff); data = (m_regs.dl & 0xff);
else else
{ {
if((m_device_type >= dev_type::NS16550) && (m_regs.fcr & 1)) if (!machine().side_effects_disabled())
m_regs.rbr = pop_rx();
else
{ {
clear_int(COM_INT_PENDING_RECEIVED_DATA_AVAILABLE); if ((m_device_type >= dev_type::NS16550) && (m_regs.fcr & 1))
if( m_regs.lsr & INS8250_LSR_DR ) m_regs.rbr = pop_rx();
m_regs.lsr &= ~INS8250_LSR_DR; else
{
clear_int(COM_INT_PENDING_RECEIVED_DATA_AVAILABLE);
if (m_regs.lsr & INS8250_LSR_DR)
m_regs.lsr &= ~INS8250_LSR_DR;
}
} }
data = m_regs.rbr; data = m_regs.rbr;
} }
@ -427,7 +432,7 @@ READ8_MEMBER( ins8250_uart_device::ins8250_r )
data = m_regs.iir; data = m_regs.iir;
/* The documentation says that reading this register will /* The documentation says that reading this register will
clear the int if this is the source of the int */ clear the int if this is the source of the int */
if ( m_regs.ier & COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY ) if (!machine().side_effects_disabled() && (m_regs.ier & COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY))
clear_int(COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY); clear_int(COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY);
break; break;
case 3: case 3:
@ -438,19 +443,23 @@ READ8_MEMBER( ins8250_uart_device::ins8250_r )
break; break;
case 5: case 5:
data = m_regs.lsr; data = m_regs.lsr;
if( m_regs.lsr & 0x1f ) if (!machine().side_effects_disabled() && (m_regs.lsr & (INS8250_LSR_BI | INS8250_LSR_FE | INS8250_LSR_PE | INS8250_LSR_OE)) != 0)
{
m_regs.lsr &= 0xe1; /* clear FE, PE and OE and BREAK bits */ m_regs.lsr &= 0xe1; /* clear FE, PE and OE and BREAK bits */
/* reading line status register clears int */ /* reading line status register clears int */
clear_int(COM_INT_PENDING_RECEIVER_LINE_STATUS); clear_int(COM_INT_PENDING_RECEIVER_LINE_STATUS);
}
break; break;
case 6: case 6:
data = m_regs.msr; data = m_regs.msr;
m_regs.msr &= 0xf0; /* reset delta values */ if (!machine().side_effects_disabled())
{
/* reading msr clears int */ m_regs.msr &= 0xf0; /* reset delta values */
clear_int(COM_INT_PENDING_MODEM_STATUS_REGISTER);
/* reading msr clears int */
clear_int(COM_INT_PENDING_MODEM_STATUS_REGISTER);
}
break; break;
case 7: case 7:
data = m_regs.scr; data = m_regs.scr;
@ -473,8 +482,20 @@ void ns16550_device::rcv_complete()
return; return;
} }
uint8_t errors = 0;
if (is_receive_framing_error())
errors |= INS8250_LSR_FE;
if (is_receive_parity_error())
errors |= INS8250_LSR_PE;
if (m_rnum == 0 && errors != 0)
{
m_regs.lsr |= errors;
trigger_int(COM_INT_PENDING_RECEIVER_LINE_STATUS);
}
m_regs.lsr |= INS8250_LSR_DR; m_regs.lsr |= INS8250_LSR_DR;
m_rfifo[m_rhead] = get_received_char(); m_rfifo[m_rhead] = get_received_char();
m_efifo[m_rhead] = errors;
++m_rhead &= 0x0f; ++m_rhead &= 0x0f;
m_rnum++; m_rnum++;
if(m_rnum >= m_rintlvl) if(m_rnum >= m_rintlvl)
@ -514,6 +535,14 @@ void ins8250_uart_device::rcv_complete()
{ {
m_regs.lsr |= INS8250_LSR_DR; m_regs.lsr |= INS8250_LSR_DR;
receive_register_extract(); receive_register_extract();
if (is_receive_framing_error())
m_regs.lsr |= INS8250_LSR_FE;
if (is_receive_parity_error())
m_regs.lsr |= INS8250_LSR_PE;
if ((m_regs.lsr & (INS8250_LSR_BI | INS8250_LSR_PE | INS8250_LSR_FE)) != 0)
trigger_int(COM_INT_PENDING_RECEIVER_LINE_STATUS);
m_regs.rbr = get_received_char(); m_regs.rbr = get_received_char();
trigger_int(COM_INT_PENDING_RECEIVED_DATA_AVAILABLE); trigger_int(COM_INT_PENDING_RECEIVED_DATA_AVAILABLE);
} }
@ -660,6 +689,7 @@ void ns16550_device::device_start()
ins8250_uart_device::device_start(); ins8250_uart_device::device_start();
save_item(NAME(m_rintlvl)); save_item(NAME(m_rintlvl));
save_item(NAME(m_rfifo)); save_item(NAME(m_rfifo));
save_item(NAME(m_efifo));
save_item(NAME(m_tfifo)); save_item(NAME(m_tfifo));
save_item(NAME(m_rhead)); save_item(NAME(m_rhead));
save_item(NAME(m_rtail)); save_item(NAME(m_rtail));
@ -670,8 +700,9 @@ void ns16550_device::device_start()
void ns16550_device::device_reset() void ns16550_device::device_reset()
{ {
memset(&m_rfifo, '\0', sizeof(m_rfifo)); std::fill(std::begin(m_rfifo), std::end(m_rfifo), 0);
memset(&m_tfifo, '\0', sizeof(m_tfifo)); std::fill(std::begin(m_efifo), std::end(m_efifo), 0);
std::fill(std::begin(m_tfifo), std::end(m_tfifo), 0);
m_rhead = m_rtail = m_rnum = 0; m_rhead = m_rtail = m_rnum = 0;
m_thead = m_ttail = 0; m_thead = m_ttail = 0;
m_timeout->adjust(attotime::never); m_timeout->adjust(attotime::never);
@ -702,6 +733,11 @@ uint8_t ns16550_device::pop_rx()
{ {
++m_rtail &= 0x0f; ++m_rtail &= 0x0f;
m_rnum--; m_rnum--;
if (m_rnum > 0 && m_efifo[m_rtail] != 0)
{
m_regs.lsr |= m_efifo[m_rtail];
trigger_int(COM_INT_PENDING_RECEIVER_LINE_STATUS);
}
} }
else else
data = 0; data = 0;
@ -733,14 +769,15 @@ void ns16550_device::set_fcr(uint8_t data)
data |= 0x06; data |= 0x06;
if(data & 2) if(data & 2)
{ {
memset(&m_rfifo, '\0', sizeof(m_rfifo)); std::fill(std::begin(m_rfifo), std::end(m_rfifo), 0);
std::fill(std::begin(m_efifo), std::end(m_efifo), 0);
m_rhead = m_rtail = m_rnum = 0; m_rhead = m_rtail = m_rnum = 0;
clear_int(COM_INT_PENDING_CHAR_TIMEOUT | COM_INT_PENDING_RECEIVED_DATA_AVAILABLE); clear_int(COM_INT_PENDING_CHAR_TIMEOUT | COM_INT_PENDING_RECEIVED_DATA_AVAILABLE);
m_timeout->adjust(attotime::never); m_timeout->adjust(attotime::never);
} }
if(data & 4) if(data & 4)
{ {
memset(&m_tfifo, '\0', sizeof(m_tfifo)); std::fill(std::begin(m_tfifo), std::end(m_tfifo), 0);
m_thead = m_ttail = 0; m_thead = m_ttail = 0;
m_regs.lsr |= INS8250_LSR_THRE; m_regs.lsr |= INS8250_LSR_THRE;
trigger_int(COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY); trigger_int(COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY);

View File

@ -20,12 +20,6 @@
class ins8250_uart_device : public device_t, public device_serial_interface class ins8250_uart_device : public device_t, public device_serial_interface
{ {
public: public:
template <class Object> devcb_base &set_out_tx_callback(Object &&cb) { return m_out_tx_cb.set_callback(std::forward<Object>(cb)); }
template <class Object> devcb_base &set_out_dtr_callback(Object &&cb) { return m_out_dtr_cb.set_callback(std::forward<Object>(cb)); }
template <class Object> devcb_base &set_out_rts_callback(Object &&cb) { return m_out_rts_cb.set_callback(std::forward<Object>(cb)); }
template <class Object> devcb_base &set_out_int_callback(Object &&cb) { return m_out_int_cb.set_callback(std::forward<Object>(cb)); }
template <class Object> devcb_base &set_out_out1_callback(Object &&cb) { return m_out_out1_cb.set_callback(std::forward<Object>(cb)); }
template <class Object> devcb_base &set_out_out2_callback(Object &&cb) { return m_out_out2_cb.set_callback(std::forward<Object>(cb)); }
auto out_tx_callback() { return m_out_tx_cb.bind(); } auto out_tx_callback() { return m_out_tx_cb.bind(); }
auto out_dtr_callback() { return m_out_dtr_cb.bind(); } auto out_dtr_callback() { return m_out_dtr_cb.bind(); }
auto out_rts_callback() { return m_out_rts_cb.bind(); } auto out_rts_callback() { return m_out_rts_cb.bind(); }
@ -133,6 +127,7 @@ private:
void set_timer() { m_timeout->adjust(attotime::from_hz((clock()*4*8)/(m_regs.dl*16))); } void set_timer() { m_timeout->adjust(attotime::from_hz((clock()*4*8)/(m_regs.dl*16))); }
int m_rintlvl; int m_rintlvl;
uint8_t m_rfifo[16]; uint8_t m_rfifo[16];
uint8_t m_efifo[16];
uint8_t m_tfifo[16]; uint8_t m_tfifo[16];
int m_rhead, m_rtail, m_rnum; int m_rhead, m_rtail, m_rnum;
int m_thead, m_ttail; int m_thead, m_ttail;