- 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:
Volker Ruppert 2002-01-20 16:35:32 +00:00
parent a9c1b785f0
commit d22d280571
2 changed files with 184 additions and 56 deletions

View File

@ -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

View File

@ -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;