- interrupt handling improved (only loopback works for now)
- interrupt reasons modem status change and receiver line status change added - number of data bits is considered now - REMARK: Windows 95 driver still makes trouble - TODO: real serial communication, FIFO, second serial port
This commit is contained in:
parent
a9c1b785f0
commit
d22d280571
@ -1,5 +1,5 @@
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// $Id: serial.cc,v 1.15 2002-01-08 20:31:14 vruppert Exp $
|
||||
// $Id: serial.cc,v 1.16 2002-01-20 16:35:32 vruppert Exp $
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2002 MandrakeSoft S.A.
|
||||
@ -135,6 +135,12 @@ bx_serial_c::init(bx_devices_c *d)
|
||||
/* internal state */
|
||||
BX_SER_THIS s[i].rx_empty = 1;
|
||||
BX_SER_THIS s[i].tx_empty = 1;
|
||||
BX_SER_THIS s[i].ls_ipending = 0;
|
||||
BX_SER_THIS s[i].ms_ipending = 0;
|
||||
BX_SER_THIS s[i].rx_ipending = 0;
|
||||
BX_SER_THIS s[i].tx_ipending = 0;
|
||||
BX_SER_THIS s[i].ls_interrupt = 0;
|
||||
BX_SER_THIS s[i].ms_interrupt = 0;
|
||||
BX_SER_THIS s[i].rx_interrupt = 0;
|
||||
BX_SER_THIS s[i].tx_interrupt = 0;
|
||||
|
||||
@ -256,24 +262,31 @@ bx_serial_c::read(Bit32u address, unsigned io_len)
|
||||
switch (address) {
|
||||
case 0x03F8: /* receive buffer, or divisor latch LSB if DLAB set */
|
||||
if (BX_SER_THIS s[0].line_cntl.dlab) {
|
||||
val = BX_SER_THIS s[0].divisor_lsb;
|
||||
val = BX_SER_THIS s[0].divisor_lsb;
|
||||
} else {
|
||||
val = BX_SER_THIS s[0].rxbuffer;
|
||||
BX_SER_THIS s[0].line_status.rxdata_ready = 0;
|
||||
BX_SER_THIS s[0].rx_empty = 1;
|
||||
val = BX_SER_THIS s[0].rxbuffer;
|
||||
BX_SER_THIS s[0].line_status.rxdata_ready = 0;
|
||||
BX_SER_THIS s[0].rx_empty = 1;
|
||||
|
||||
BX_SER_THIS s[0].rx_interrupt = 0;
|
||||
/* If there are no more ints pending, clear the irq */
|
||||
if ((BX_SER_THIS s[0].tx_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ls_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ms_interrupt == 0)) {
|
||||
BX_SER_THIS devices->pic->untrigger_irq(4);
|
||||
}
|
||||
BX_SER_THIS s[0].rx_interrupt = 0;
|
||||
BX_SER_THIS s[0].rx_ipending = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x03F9: /* interrupt enable register, or div. latch MSB */
|
||||
if (BX_SER_THIS s[0].line_cntl.dlab) {
|
||||
val = BX_SER_THIS s[0].divisor_msb;
|
||||
val = BX_SER_THIS s[0].divisor_msb;
|
||||
} else {
|
||||
val = BX_SER_THIS s[0].int_enable.rxdata_enable |
|
||||
(BX_SER_THIS s[0].int_enable.txhold_enable << 1) |
|
||||
(BX_SER_THIS s[0].int_enable.rxlstat_enable << 2) |
|
||||
(BX_SER_THIS s[0].int_enable.modstat_enable << 3);
|
||||
val = BX_SER_THIS s[0].int_enable.rxdata_enable |
|
||||
(BX_SER_THIS s[0].int_enable.txhold_enable << 1) |
|
||||
(BX_SER_THIS s[0].int_enable.rxlstat_enable << 2) |
|
||||
(BX_SER_THIS s[0].int_enable.modstat_enable << 3);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -281,22 +294,36 @@ bx_serial_c::read(Bit32u address, unsigned io_len)
|
||||
/*
|
||||
* Set the interrupt ID based on interrupt source
|
||||
*/
|
||||
if (BX_SER_THIS s[0].rx_interrupt) {
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x2;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 0;
|
||||
if (BX_SER_THIS s[0].ls_interrupt) {
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x3;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 0;
|
||||
} else if (BX_SER_THIS s[0].rx_interrupt) {
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x2;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 0;
|
||||
} else if (BX_SER_THIS s[0].tx_interrupt) {
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x1;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 0;
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x1;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 0;
|
||||
} else if (BX_SER_THIS s[0].ms_interrupt) {
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x0;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 0;
|
||||
} else {
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x0;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 1;
|
||||
BX_SER_THIS s[0].int_ident.int_ID = 0x0;
|
||||
BX_SER_THIS s[0].int_ident.ipending = 1;
|
||||
}
|
||||
|
||||
/* If there are no more ints pending, clear the irq */
|
||||
if ((BX_SER_THIS s[0].rx_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ls_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ms_interrupt == 0)) {
|
||||
BX_SER_THIS devices->pic->untrigger_irq(4);
|
||||
}
|
||||
|
||||
BX_SER_THIS s[0].tx_interrupt = 0;
|
||||
BX_SER_THIS s[0].tx_ipending = 0;
|
||||
|
||||
val = BX_SER_THIS s[0].int_ident.ipending |
|
||||
(BX_SER_THIS s[0].int_ident.int_ID << 1) |
|
||||
(BX_SER_THIS s[0].int_ident.fifo_enabled << 6);
|
||||
(BX_SER_THIS s[0].int_ident.int_ID << 1) |
|
||||
(BX_SER_THIS s[0].int_ident.fifo_enabled << 6);
|
||||
break;
|
||||
|
||||
case 0x03FB: /* Line control register */
|
||||
@ -326,7 +353,16 @@ bx_serial_c::read(Bit32u address, unsigned io_len)
|
||||
(BX_SER_THIS s[0].line_status.txhold_empty << 5) |
|
||||
(BX_SER_THIS s[0].line_status.txtransm_empty << 6) |
|
||||
(BX_SER_THIS s[0].line_status.fifo_error << 7);
|
||||
BX_SER_THIS s[0].line_status.overrun_error = 0;
|
||||
BX_SER_THIS s[0].line_status.break_int = 0;
|
||||
/* If there are no more ints pending, clear the irq */
|
||||
if ((BX_SER_THIS s[0].rx_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].tx_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ms_interrupt == 0)) {
|
||||
BX_SER_THIS devices->pic->untrigger_irq(4);
|
||||
}
|
||||
BX_SER_THIS s[0].ls_interrupt = 0;
|
||||
BX_SER_THIS s[0].ls_ipending = 0;
|
||||
break;
|
||||
|
||||
case 0x03FE: /* MODEM status register */
|
||||
@ -342,6 +378,14 @@ bx_serial_c::read(Bit32u address, unsigned io_len)
|
||||
BX_SER_THIS s[0].modem_status.delta_dsr = 0;
|
||||
BX_SER_THIS s[0].modem_status.ri_trailedge = 0;
|
||||
BX_SER_THIS s[0].modem_status.delta_dcd = 0;
|
||||
/* If there are no more ints pending, clear the irq */
|
||||
if ((BX_SER_THIS s[0].rx_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].tx_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ls_interrupt == 0)) {
|
||||
BX_SER_THIS devices->pic->untrigger_irq(4);
|
||||
}
|
||||
BX_SER_THIS s[0].ms_interrupt = 0;
|
||||
BX_SER_THIS s[0].ms_ipending = 0;
|
||||
break;
|
||||
|
||||
case 0x03FF: /* scratch register */
|
||||
@ -379,6 +423,9 @@ bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
||||
#else
|
||||
UNUSED(this_ptr);
|
||||
#endif // !BX_USE_SER_SMF
|
||||
Boolean prev_cts, prev_dsr, prev_ri, prev_dcd;
|
||||
Boolean gen_int = 0;
|
||||
|
||||
BX_DEBUG(("write: 0x%x <- %d",address,value));
|
||||
|
||||
/* SERIAL PORT 1 */
|
||||
@ -392,7 +439,7 @@ bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
||||
|
||||
switch (address) {
|
||||
case 0x03F8: /* transmit buffer, or divisor latch LSB if DLAB set */
|
||||
if (BX_SER_THIS s[0].line_cntl.dlab) {
|
||||
if (BX_SER_THIS s[0].line_cntl.dlab) {
|
||||
BX_SER_THIS s[0].divisor_lsb = value;
|
||||
|
||||
if (value != 0) {
|
||||
@ -404,18 +451,26 @@ bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
||||
#endif // USE_RAW_SERIAL
|
||||
}
|
||||
} else {
|
||||
if (BX_SER_THIS s[0].tx_empty) {
|
||||
BX_SER_THIS s[0].tx_empty = 0;
|
||||
BX_SER_THIS s[0].line_status.txtransm_empty = 0;
|
||||
BX_SER_THIS s[0].line_status.txhold_empty = 0;
|
||||
BX_SER_THIS s[0].tx_interrupt = 0;
|
||||
BX_SER_THIS s[0].txbuffer = value;
|
||||
bx_pc_system.activate_timer(BX_SER_THIS s[0].tx_timer_index,
|
||||
(int) (1000000.0 / (BX_SER_THIS s[0].baudrate / 8)),
|
||||
0); /* not continuous */
|
||||
} else {
|
||||
BX_ERROR(("write to tx hold register when not empty"));
|
||||
}
|
||||
if (BX_SER_THIS s[0].tx_empty) {
|
||||
BX_SER_THIS s[0].tx_empty = 0;
|
||||
BX_SER_THIS s[0].line_status.txtransm_empty = 0;
|
||||
BX_SER_THIS s[0].line_status.txhold_empty = 0;
|
||||
/* If there are no more ints pending, clear the irq */
|
||||
if ((BX_SER_THIS s[0].rx_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ls_interrupt == 0) &&
|
||||
(BX_SER_THIS s[0].ms_interrupt == 0)) {
|
||||
BX_SER_THIS devices->pic->untrigger_irq(4);
|
||||
}
|
||||
BX_SER_THIS s[0].tx_interrupt = 0;
|
||||
BX_SER_THIS s[0].tx_ipending = 0;
|
||||
Bit8u bitmask = 0xff >> (3 - BX_SER_THIS s[0].line_cntl.wordlen_sel);
|
||||
BX_SER_THIS s[0].txbuffer = value & bitmask;
|
||||
bx_pc_system.activate_timer(BX_SER_THIS s[0].tx_timer_index,
|
||||
(int) (1000000.0 / (BX_SER_THIS s[0].baudrate / 8)),
|
||||
0); /* not continuous */
|
||||
} else {
|
||||
BX_ERROR(("write to tx hold register when not empty"));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@ -436,6 +491,32 @@ bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
||||
BX_SER_THIS s[0].int_enable.txhold_enable = (value & 0x02) >> 1;
|
||||
BX_SER_THIS s[0].int_enable.rxlstat_enable = (value & 0x04) >> 2;
|
||||
BX_SER_THIS s[0].int_enable.modstat_enable = (value & 0x08) >> 3;
|
||||
if ((BX_SER_THIS s[0].ms_ipending == 1) &&
|
||||
(BX_SER_THIS s[0].int_enable.modstat_enable == 1)) {
|
||||
BX_SER_THIS s[0].ms_interrupt = 1;
|
||||
BX_SER_THIS s[0].ms_ipending = 0;
|
||||
gen_int = 1;
|
||||
}
|
||||
if ((BX_SER_THIS s[0].tx_ipending == 1) &&
|
||||
(BX_SER_THIS s[0].int_enable.txhold_enable == 1)) {
|
||||
BX_SER_THIS s[0].tx_interrupt = 1;
|
||||
BX_SER_THIS s[0].tx_ipending = 0;
|
||||
gen_int = 1;
|
||||
}
|
||||
if ((BX_SER_THIS s[0].rx_ipending == 1) &&
|
||||
(BX_SER_THIS s[0].int_enable.rxdata_enable == 1)) {
|
||||
BX_SER_THIS s[0].rx_interrupt = 1;
|
||||
BX_SER_THIS s[0].rx_ipending = 0;
|
||||
gen_int = 1;
|
||||
if ((BX_SER_THIS s[0].ls_ipending == 1) &&
|
||||
(BX_SER_THIS s[0].int_enable.rxlstat_enable == 1)) {
|
||||
BX_SER_THIS s[0].ls_interrupt = 1;
|
||||
BX_SER_THIS s[0].ls_ipending = 0;
|
||||
gen_int = 1;
|
||||
}
|
||||
}
|
||||
if (gen_int == 1)
|
||||
BX_SER_THIS devices->pic->trigger_irq(4);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -494,7 +575,7 @@ bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
||||
}
|
||||
BX_SER_THIS s[0].line_cntl.dlab = (value & 0x80) >> 7;
|
||||
break;
|
||||
|
||||
|
||||
case 0x03FC: /* MODEM control register */
|
||||
if ((value & 0x01) == 0) {
|
||||
#if USE_RAW_SERIAL
|
||||
@ -509,16 +590,41 @@ bx_serial_c::write(Bit32u address, Bit32u value, unsigned io_len)
|
||||
BX_SER_THIS s[0].modem_cntl.local_loopback = (value & 0x10) >> 4;
|
||||
|
||||
if (BX_SER_THIS s[0].modem_cntl.local_loopback) {
|
||||
BX_SER_THIS s[0].modem_status.cts = BX_SER_THIS s[0].modem_cntl.rts;
|
||||
BX_SER_THIS s[0].modem_status.dsr = BX_SER_THIS s[0].modem_cntl.dtr;
|
||||
BX_SER_THIS s[0].modem_status.ri = BX_SER_THIS s[0].modem_cntl.out1;
|
||||
BX_SER_THIS s[0].modem_status.dcd = BX_SER_THIS s[0].modem_cntl.out2;
|
||||
prev_cts = BX_SER_THIS s[0].modem_status.cts;
|
||||
prev_dsr = BX_SER_THIS s[0].modem_status.dsr;
|
||||
prev_ri = BX_SER_THIS s[0].modem_status.ri;
|
||||
prev_dcd = BX_SER_THIS s[0].modem_status.dcd;
|
||||
BX_SER_THIS s[0].modem_status.cts = BX_SER_THIS s[0].modem_cntl.rts;
|
||||
BX_SER_THIS s[0].modem_status.dsr = BX_SER_THIS s[0].modem_cntl.dtr;
|
||||
BX_SER_THIS s[0].modem_status.ri = BX_SER_THIS s[0].modem_cntl.out1;
|
||||
BX_SER_THIS s[0].modem_status.dcd = BX_SER_THIS s[0].modem_cntl.out2;
|
||||
if (BX_SER_THIS s[0].modem_status.cts != prev_cts) {
|
||||
BX_SER_THIS s[0].modem_status.delta_cts = 1;
|
||||
BX_SER_THIS s[0].ms_ipending = 1;
|
||||
}
|
||||
if (BX_SER_THIS s[0].modem_status.dsr != prev_dsr) {
|
||||
BX_SER_THIS s[0].modem_status.delta_dsr = 1;
|
||||
BX_SER_THIS s[0].ms_ipending = 1;
|
||||
}
|
||||
if (BX_SER_THIS s[0].modem_status.ri != prev_ri)
|
||||
BX_SER_THIS s[0].ms_ipending = 1;
|
||||
if ((BX_SER_THIS s[0].modem_status.ri == 0) && (prev_ri == 1))
|
||||
BX_SER_THIS s[0].modem_status.ri_trailedge = 1;
|
||||
if (BX_SER_THIS s[0].modem_status.dcd != prev_dcd) {
|
||||
BX_SER_THIS s[0].modem_status.delta_dcd = 1;
|
||||
BX_SER_THIS s[0].ms_ipending = 1;
|
||||
}
|
||||
if ((BX_SER_THIS s[0].ms_ipending == 1) &&
|
||||
(BX_SER_THIS s[0].int_enable.modstat_enable == 1)) {
|
||||
BX_SER_THIS s[0].ms_interrupt = 1;
|
||||
BX_SER_THIS s[0].ms_ipending = 0;
|
||||
}
|
||||
} else {
|
||||
/* set these to 0 for the time being */
|
||||
BX_SER_THIS s[0].modem_status.cts = 0;
|
||||
BX_SER_THIS s[0].modem_status.dsr = 0;
|
||||
BX_SER_THIS s[0].modem_status.ri = 0;
|
||||
BX_SER_THIS s[0].modem_status.dcd = 0;
|
||||
/* set these to 0 for the time being */
|
||||
BX_SER_THIS s[0].modem_status.cts = 0;
|
||||
BX_SER_THIS s[0].modem_status.dsr = 0;
|
||||
BX_SER_THIS s[0].modem_status.ri = 0;
|
||||
BX_SER_THIS s[0].modem_status.dcd = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -561,15 +667,29 @@ bx_serial_c::tx_timer(void)
|
||||
if (BX_SER_THIS s[0].int_enable.txhold_enable) {
|
||||
gen_int = 1;
|
||||
BX_SER_THIS s[0].tx_interrupt = 1;
|
||||
} else {
|
||||
BX_SER_THIS s[0].tx_ipending = 1;
|
||||
}
|
||||
|
||||
if (BX_SER_THIS s[0].modem_cntl.local_loopback) {
|
||||
if (BX_SER_THIS s[0].rx_empty == 0)
|
||||
BX_SER_THIS s[0].line_status.overrun_error = 1;
|
||||
BX_SER_THIS s[0].rxbuffer = BX_SER_THIS s[0].txbuffer;
|
||||
BX_SER_THIS s[0].line_status.rxdata_ready = 1;
|
||||
BX_SER_THIS s[0].rx_empty = 0;
|
||||
if (BX_SER_THIS s[0].int_enable.rxdata_enable) {
|
||||
gen_int = 1;
|
||||
BX_SER_THIS s[0].rx_interrupt = 1;
|
||||
if (BX_SER_THIS s[0].modem_cntl.out2) {
|
||||
if (BX_SER_THIS s[0].int_enable.rxdata_enable) {
|
||||
gen_int = 1;
|
||||
BX_SER_THIS s[0].rx_interrupt = 1;
|
||||
} else {
|
||||
BX_SER_THIS s[0].rx_ipending = 1;
|
||||
}
|
||||
if (BX_SER_THIS s[0].int_enable.rxlstat_enable) {
|
||||
gen_int = 1;
|
||||
BX_SER_THIS s[0].ls_interrupt = 1;
|
||||
} else {
|
||||
BX_SER_THIS s[0].ls_ipending = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#if defined (USE_TTY_HACK)
|
||||
@ -652,13 +772,15 @@ bx_serial_c::rx_timer(void)
|
||||
#endif
|
||||
if (!BX_SER_THIS s[0].modem_cntl.local_loopback) {
|
||||
|
||||
BX_SER_THIS s[0].rxbuffer = chbuf;
|
||||
BX_SER_THIS s[0].line_status.rxdata_ready = 1;
|
||||
BX_SER_THIS s[0].rx_empty = 0;
|
||||
if (BX_SER_THIS s[0].int_enable.rxdata_enable) {
|
||||
BX_SER_THIS s[0].rx_interrupt = 1;
|
||||
BX_SER_THIS devices->pic->trigger_irq(4);
|
||||
}
|
||||
BX_SER_THIS s[0].rxbuffer = chbuf;
|
||||
BX_SER_THIS s[0].line_status.rxdata_ready = 1;
|
||||
BX_SER_THIS s[0].rx_empty = 0;
|
||||
if (BX_SER_THIS s[0].int_enable.rxdata_enable) {
|
||||
BX_SER_THIS s[0].rx_interrupt = 1;
|
||||
BX_SER_THIS devices->pic->trigger_irq(4);
|
||||
} else {
|
||||
BX_SER_THIS s[0].rx_ipending = 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bdrate = (int) (1000000.0 / 100); // Poll frequency is 100ms
|
||||
@ -666,7 +788,7 @@ bx_serial_c::rx_timer(void)
|
||||
} else {
|
||||
// Poll at 4x baud rate to see if the next-char can
|
||||
// be read
|
||||
bdrate *= 4;
|
||||
bdrate *= 4;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1,8 +1,8 @@
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// $Id: serial.h,v 1.5 2001-10-03 13:10:38 bdenney Exp $
|
||||
// $Id: serial.h,v 1.6 2002-01-20 16:35:32 vruppert Exp $
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2001 MandrakeSoft S.A.
|
||||
// Copyright (C) 2002 MandrakeSoft S.A.
|
||||
//
|
||||
// MandrakeSoft S.A.
|
||||
// 43, rue d'Aboukir
|
||||
@ -55,8 +55,14 @@ typedef struct {
|
||||
*/
|
||||
Boolean rx_empty;
|
||||
Boolean tx_empty;
|
||||
Boolean ls_interrupt;
|
||||
Boolean ms_interrupt;
|
||||
Boolean rx_interrupt;
|
||||
Boolean tx_interrupt;
|
||||
Boolean ls_ipending;
|
||||
Boolean ms_ipending;
|
||||
Boolean rx_ipending;
|
||||
Boolean tx_ipending;
|
||||
|
||||
int baudrate;
|
||||
int tx_timer_index;
|
||||
|
Loading…
Reference in New Issue
Block a user