NS8250 Fixes [smf]

Loopback: tx goes high and data is clocked at the correct rate instead of appearing instantly
Modem status register: don't lose track of external signals when starting, resetting, switching loopback off, writing to register
Handshaking: active low for consistency (RS232 port now defaults handshaking lines high and serial mouse dtr/rts handling has been adjusted).
This commit is contained in:
smf- 2014-09-17 13:58:02 +00:00
parent 13537ab5f1
commit f1d0477489
5 changed files with 143 additions and 102 deletions

View File

@ -50,16 +50,16 @@ void rs232_port_device::device_start()
save_item(NAME(m_cts)); save_item(NAME(m_cts));
m_rxd = 1; m_rxd = 1;
m_dcd = 0; m_dcd = 1;
m_dsr = 0; m_dsr = 1;
m_ri = 0; m_ri = 1;
m_cts = 0; m_cts = 1;
m_rxd_handler(1); m_rxd_handler(1);
m_dcd_handler(0); m_dcd_handler(1);
m_dsr_handler(0); m_dsr_handler(1);
m_ri_handler(0); m_ri_handler(1);
m_cts_handler(0); m_cts_handler(1);
} }
WRITE_LINE_MEMBER( rs232_port_device::write_txd ) WRITE_LINE_MEMBER( rs232_port_device::write_txd )

View File

@ -13,6 +13,8 @@ serial_mouse_device::serial_mouse_device(const machine_config &mconfig, device_t
: device_t(mconfig, type, name, tag, owner, clock, shortname, source), : device_t(mconfig, type, name, tag, owner, clock, shortname, source),
device_rs232_port_interface(mconfig, *this), device_rs232_port_interface(mconfig, *this),
device_serial_interface(mconfig, *this), device_serial_interface(mconfig, *this),
m_dtr(1),
m_rts(1),
m_x(*this, "ser_mouse_x"), m_x(*this, "ser_mouse_x"),
m_y(*this, "ser_mouse_y"), m_y(*this, "ser_mouse_y"),
m_btn(*this, "ser_mouse_btn") m_btn(*this, "ser_mouse_btn")
@ -23,7 +25,6 @@ const device_type MSFT_SERIAL_MOUSE = &device_creator<microsoft_mouse_device>;
microsoft_mouse_device::microsoft_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) microsoft_mouse_device::microsoft_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: serial_mouse_device(mconfig, MSFT_SERIAL_MOUSE, "Microsoft Serial Mouse", tag, owner, clock, "microsoft_mouse", __FILE__) : serial_mouse_device(mconfig, MSFT_SERIAL_MOUSE, "Microsoft Serial Mouse", tag, owner, clock, "microsoft_mouse", __FILE__)
, m_dtr(0)
{ {
} }
@ -40,9 +41,18 @@ void serial_mouse_device::device_start()
m_enabled = false; m_enabled = false;
set_frame(); set_frame();
set_tra_rate(1200); set_tra_rate(1200);
reset_mouse();
save_item(NAME(m_dtr));
save_item(NAME(m_rts));
save_item(NAME(m_queue));
save_item(NAME(m_head));
save_item(NAME(m_tail));
save_item(NAME(m_mb));
save_item(NAME(m_enabled));
} }
void serial_mouse_device::device_reset() void serial_mouse_device::reset_mouse()
{ {
m_head = m_tail = 0; m_head = m_tail = 0;
output_rxd(1); output_rxd(1);
@ -202,24 +212,30 @@ void serial_mouse_device::set_mouse_enable(bool state)
} }
void microsoft_mouse_device::check_state()
WRITE_LINE_MEMBER(serial_mouse_device::input_dtr)
{ {
if (m_dtr && m_rts) m_dtr = state;
check_state();
}
WRITE_LINE_MEMBER(serial_mouse_device::input_rts)
{
m_rts = state;
check_state();
}
WRITE_LINE_MEMBER(microsoft_mouse_device::input_rts)
{
if (!m_dtr && m_rts && !state)
{ {
/* RTS toggled */ reset_mouse();
if (!m_old_rts && m_rts) /* Identify as Microsoft 3 Button Mouse */
{ queue_data('M');
/* reset mouse */ queue_data('3');
serial_mouse_device::device_reset();
/* Identify as Microsoft 3 Button Mouse */
queue_data('M');
queue_data('3');
}
/* start a timer to scan the mouse input */
set_mouse_enable(true);
} }
else
set_mouse_enable(false); serial_mouse_device::input_rts(state);
} }

View File

@ -21,7 +21,6 @@ public:
protected: protected:
virtual void device_start(); virtual void device_start();
virtual void device_reset();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
virtual void mouse_trans(int dx, int dy, int nb, int mbc) = 0; virtual void mouse_trans(int dx, int dy, int nb, int mbc) = 0;
virtual void set_frame() = 0; virtual void set_frame() = 0;
@ -30,6 +29,13 @@ protected:
UINT8 unqueue_data() {UINT8 ret = m_queue[m_tail]; ++m_tail %= 256; return ret;} UINT8 unqueue_data() {UINT8 ret = m_queue[m_tail]; ++m_tail %= 256; return ret;}
virtual void tra_complete(); virtual void tra_complete();
virtual void tra_callback(); virtual void tra_callback();
void reset_mouse();
virtual WRITE_LINE_MEMBER(input_dtr);
virtual WRITE_LINE_MEMBER(input_rts);
int m_dtr;
int m_rts;
private: private:
UINT8 m_queue[256]; UINT8 m_queue[256];
UINT8 m_head, m_tail, m_mb; UINT8 m_head, m_tail, m_mb;
@ -40,25 +46,20 @@ private:
required_ioport m_x; required_ioport m_x;
required_ioport m_y; required_ioport m_y;
required_ioport m_btn; required_ioport m_btn;
void check_state() { set_mouse_enable(!m_dtr && !m_rts); }
}; };
class microsoft_mouse_device : public serial_mouse_device class microsoft_mouse_device : public serial_mouse_device
{ {
public: public:
microsoft_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); microsoft_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual DECLARE_WRITE_LINE_MEMBER( input_dtr ) { m_dtr = state; check_state(); }
virtual DECLARE_WRITE_LINE_MEMBER( input_rts ) { m_rts = state; check_state(); m_old_rts = state; }
protected: protected:
virtual WRITE_LINE_MEMBER(input_rts);
virtual void set_frame() { set_data_frame(1, 7, PARITY_NONE, STOP_BITS_2); } virtual void set_frame() { set_data_frame(1, 7, PARITY_NONE, STOP_BITS_2); }
virtual void mouse_trans(int dx, int dy, int nb, int mbc); virtual void mouse_trans(int dx, int dy, int nb, int mbc);
virtual void device_reset() {m_old_rts = 0; serial_mouse_device::device_reset();}
private:
void check_state();
int m_dtr;
int m_rts;
int m_old_rts;
}; };
extern const device_type MSFT_SERIAL_MOUSE; extern const device_type MSFT_SERIAL_MOUSE;
@ -67,17 +68,10 @@ class mouse_systems_mouse_device : public serial_mouse_device
{ {
public: public:
mouse_systems_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); mouse_systems_mouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual DECLARE_WRITE_LINE_MEMBER( input_dtr ) { m_dtr = state; check_state(); }
virtual DECLARE_WRITE_LINE_MEMBER( input_rts ) { m_rts = state; check_state(); }
protected: protected:
virtual void set_frame() { set_data_frame(1, 8, PARITY_NONE, STOP_BITS_2); } virtual void set_frame() { set_data_frame(1, 8, PARITY_NONE, STOP_BITS_2); }
virtual void mouse_trans(int dx, int dy, int nb, int mbc); virtual void mouse_trans(int dx, int dy, int nb, int mbc);
private:
void check_state() { set_mouse_enable((m_dtr && m_rts)?true:false); }
int m_dtr;
int m_rts;
}; };
extern const device_type MSYSTEM_SERIAL_MOUSE; extern const device_type MSYSTEM_SERIAL_MOUSE;

View File

@ -97,7 +97,12 @@ ins8250_uart_device::ins8250_uart_device(const machine_config &mconfig, device_t
m_out_rts_cb(*this), m_out_rts_cb(*this),
m_out_int_cb(*this), m_out_int_cb(*this),
m_out_out1_cb(*this), m_out_out1_cb(*this),
m_out_out2_cb(*this) m_out_out2_cb(*this),
m_rxd(1),
m_dcd(1),
m_dsr(1),
m_ri(1),
m_cts(1)
{ {
} }
@ -212,20 +217,11 @@ WRITE8_MEMBER( ins8250_uart_device::ins8250_w )
{ {
m_regs.thr = data; m_regs.thr = data;
m_regs.lsr &= ~0x20; m_regs.lsr &= ~0x20;
if ( m_regs.mcr & 0x10 ) if((m_device_type >= TYPE_NS16550) && (m_regs.fcr & 1))
{ push_tx(data);
m_regs.lsr |= 0x61; clear_int(COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY);
m_regs.rbr = data; if(m_regs.lsr & 0x40)
trigger_int(COM_INT_PENDING_RECEIVED_DATA_AVAILABLE); tra_complete();
}
else
{
if((m_device_type >= TYPE_NS16550) && (m_regs.fcr & 1))
push_tx(data);
clear_int(COM_INT_PENDING_TRANSMITTER_HOLDING_REGISTER_EMPTY);
if(m_regs.lsr & 0x40)
tra_complete();
}
} }
break; break;
case 1: case 1:
@ -289,25 +285,25 @@ WRITE8_MEMBER( ins8250_uart_device::ins8250_w )
{ {
m_regs.mcr = data & 0x1f; m_regs.mcr = data & 0x1f;
if ( m_regs.mcr & 0x10 ) /* loopback test */ update_msr();
if (m_regs.mcr & 0x10) /* loopback test */
{ {
data = ( ( m_regs.mcr & 0x0c ) << 4 ) | ( ( m_regs.mcr & 0x01 ) << 5 ) | ( ( m_regs.mcr & 0x02 ) << 3 ); m_out_tx_cb(1);
if ( ( m_regs.msr & 0x20 ) != ( data & 0x20 ) ) device_serial_interface::rx_w(m_txd);
data |= 0x02; m_out_dtr_cb(1);
if ( ( m_regs.msr & 0x10 ) != ( data & 0x10 ) ) m_out_rts_cb(1);
data |= 0x01; m_out_out1_cb(1);
if ( ( m_regs.msr & 0x40 ) && ! ( data & 0x40 ) ) m_out_out2_cb(1);
data |= 0x04;
if ( ( m_regs.msr & 0x80 ) != ( data & 0x80 ) )
data |= 0x08;
m_regs.msr = data;
} }
else else
{ {
m_out_dtr_cb((m_regs.mcr & 1) ? 1 : 0); m_out_tx_cb(m_txd);
m_out_rts_cb((m_regs.mcr & 2) ? 1 : 0); device_serial_interface::rx_w(m_rxd);
m_out_out1_cb((m_regs.mcr & 4) ? 1 : 0); m_out_dtr_cb((m_regs.mcr & 1) ? 0 : 1);
m_out_out2_cb((m_regs.mcr & 8) ? 1 : 0); m_out_rts_cb((m_regs.mcr & 2) ? 0 : 1);
m_out_out1_cb((m_regs.mcr & 4) ? 0 : 1);
m_out_out2_cb((m_regs.mcr & 8) ? 0 : 1);
} }
} }
break; break;
@ -327,15 +323,7 @@ WRITE8_MEMBER( ins8250_uart_device::ins8250_w )
break; break;
case 6: case 6:
/* // modem status register is read only
This register can be written, but if you write a 1 bit into any of
bits 3 - 0, you could cause an interrupt if the appropriate IER bit
is set.
*/
m_regs.msr = data;
if ( m_regs.msr & 0x0f )
trigger_int(COM_INT_PENDING_MODEM_STATUS_REGISTER);
break; break;
case 7: case 7:
m_regs.scr = data; m_regs.scr = data;
@ -354,7 +342,7 @@ READ8_MEMBER( ins8250_uart_device::ins8250_r )
data = (m_regs.dl & 0xff); data = (m_regs.dl & 0xff);
else else
{ {
if((m_device_type >= TYPE_NS16550) && (m_regs.fcr & 1) && !(m_regs.mcr & 0x10)) if((m_device_type >= TYPE_NS16550) && (m_regs.fcr & 1))
m_regs.rbr = pop_rx(); m_regs.rbr = pop_rx();
else else
{ {
@ -482,37 +470,66 @@ void ins8250_uart_device::tra_complete()
void ins8250_uart_device::tra_callback() void ins8250_uart_device::tra_callback()
{ {
m_out_tx_cb(transmit_register_get_data_bit()); m_txd = transmit_register_get_data_bit();
if (m_regs.mcr & 0x10)
{
device_serial_interface::rx_w(m_txd);
}
else
{
m_out_tx_cb(m_txd);
}
} }
void ins8250_uart_device::update_msr(int bit, UINT8 state) void ins8250_uart_device::update_msr()
{ {
UINT8 mask = (1<<bit); UINT8 data;
if((m_regs.msr & mask) == (state<<bit))
return; if (m_regs.mcr & 0x10)
m_regs.msr |= mask; data = ((m_regs.mcr & 0x0c) << 4) | ((m_regs.mcr & 0x01) << 5) | ((m_regs.mcr & 0x02) << 3);
m_regs.msr = (m_regs.msr & ~(mask << 4)) | (state<<(bit+4)); else
trigger_int(COM_INT_PENDING_MODEM_STATUS_REGISTER); data = (!m_dcd << 7) | (!m_ri << 6) | (!m_dsr << 5) | (!m_cts << 4);
int change = (m_regs.msr ^ data) >> 4;
if (change)
{
m_regs.msr = data | (m_regs.msr & 0xf) | change;
trigger_int(COM_INT_PENDING_MODEM_STATUS_REGISTER);
}
} }
WRITE_LINE_MEMBER(ins8250_uart_device::dcd_w) WRITE_LINE_MEMBER(ins8250_uart_device::dcd_w)
{ {
update_msr(3, (state ? 1 : 0)); m_dcd = state;
update_msr();
} }
WRITE_LINE_MEMBER(ins8250_uart_device::dsr_w) WRITE_LINE_MEMBER(ins8250_uart_device::dsr_w)
{ {
update_msr(1, (state ? 1 : 0)); m_dsr = state;
update_msr();
} }
WRITE_LINE_MEMBER(ins8250_uart_device::ri_w) WRITE_LINE_MEMBER(ins8250_uart_device::ri_w)
{ {
update_msr(2, (state ? 1 : 0)); m_ri = state;
update_msr();
} }
WRITE_LINE_MEMBER(ins8250_uart_device::cts_w) WRITE_LINE_MEMBER(ins8250_uart_device::cts_w)
{ {
update_msr(0, (state ? 1 : 0)); m_cts = state;
update_msr();
}
WRITE_LINE_MEMBER(ins8250_uart_device::rx_w)
{
m_rxd = state;
if (!(m_regs.mcr & 0x10))
device_serial_interface::rx_w(m_rxd);
} }
void ins8250_uart_device::device_start() void ins8250_uart_device::device_start()
@ -525,7 +542,6 @@ void ins8250_uart_device::device_start()
m_out_out2_cb.resolve_safe(); m_out_out2_cb.resolve_safe();
set_tra_rate(0); set_tra_rate(0);
set_rcv_rate(0); set_rcv_rate(0);
memset(&m_regs, 0x00, sizeof(m_regs));
device_serial_interface::register_save_state(machine().save(), name(), tag()); device_serial_interface::register_save_state(machine().save(), name(), tag());
save_item(NAME(m_regs.thr)); save_item(NAME(m_regs.thr));
@ -540,24 +556,32 @@ void ins8250_uart_device::device_start()
save_item(NAME(m_regs.msr)); save_item(NAME(m_regs.msr));
save_item(NAME(m_regs.scr)); save_item(NAME(m_regs.scr));
save_item(NAME(m_int_pending)); save_item(NAME(m_int_pending));
save_item(NAME(m_txd));
save_item(NAME(m_rxd));
save_item(NAME(m_dcd));
save_item(NAME(m_dsr));
save_item(NAME(m_ri));
save_item(NAME(m_cts));
} }
void ins8250_uart_device::device_reset() void ins8250_uart_device::device_reset()
{ {
memset(&m_regs, 0x00, sizeof(m_regs));
m_regs.ier = 0; m_regs.ier = 0;
m_regs.iir = 1; m_regs.iir = 1;
m_regs.lcr = 0; m_regs.lcr = 0;
m_regs.mcr = 0; m_regs.mcr = 0;
m_regs.lsr = (1<<5) | (1<<6); m_regs.lsr = (1<<5) | (1<<6);
update_msr();
m_regs.msr &= 0xf0;
m_int_pending = 0; m_int_pending = 0;
receive_register_reset(); receive_register_reset();
transmit_register_reset(); transmit_register_reset();
m_out_rts_cb(0); m_txd = 1;
m_out_dtr_cb(0);
m_out_out1_cb(0);
m_out_out2_cb(0);
m_out_tx_cb(1); m_out_tx_cb(1);
m_out_rts_cb(1);
m_out_dtr_cb(1);
m_out_out1_cb(1);
m_out_out2_cb(1);
} }
void ins8250_uart_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) void ins8250_uart_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)

View File

@ -32,7 +32,7 @@ public:
DECLARE_WRITE_LINE_MEMBER( dsr_w ); DECLARE_WRITE_LINE_MEMBER( dsr_w );
DECLARE_WRITE_LINE_MEMBER( ri_w ); DECLARE_WRITE_LINE_MEMBER( ri_w );
DECLARE_WRITE_LINE_MEMBER( cts_w ); DECLARE_WRITE_LINE_MEMBER( cts_w );
DECLARE_WRITE_LINE_MEMBER( rx_w ) { device_serial_interface::rx_w(state); } DECLARE_WRITE_LINE_MEMBER( rx_w );
protected: protected:
virtual void device_start(); virtual void device_start();
@ -81,7 +81,14 @@ private:
devcb_write_line m_out_out2_cb; devcb_write_line m_out_out2_cb;
void update_interrupt(); void update_interrupt();
void update_msr(int bit, UINT8 state); void update_msr();
int m_txd;
int m_rxd;
int m_dcd;
int m_dsr;
int m_ri;
int m_cts;
}; };
class ins8250_device : public ins8250_uart_device class ins8250_device : public ins8250_uart_device