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