Numerous serial port bug fixes, now it passes all AMIDIAG tests.

This commit is contained in:
OBattler
2019-10-31 05:20:40 +01:00
parent 84adef4c25
commit 1b67c97481
2 changed files with 141 additions and 60 deletions

View File

@@ -8,7 +8,9 @@
* *
* NS8250/16450/16550 UART emulation. * NS8250/16450/16550 UART emulation.
* *
* Version: @(#)serial.h 1.0.12 2019/10/29 * Now passes all the AMIDIAG tests.
*
* Version: @(#)serial.h 1.0.13 2019/10/31
* *
* Author: Sarah Walker, <http://pcem-emulator.co.uk/> * Author: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com> * Miran Grca, <mgrca8@gmail.com>
@@ -42,7 +44,8 @@ enum
SERIAL_INT_LSR = 1, SERIAL_INT_LSR = 1,
SERIAL_INT_RECEIVE = 2, SERIAL_INT_RECEIVE = 2,
SERIAL_INT_TRANSMIT = 4, SERIAL_INT_TRANSMIT = 4,
SERIAL_INT_MSR = 8 SERIAL_INT_MSR = 8,
SERIAL_INT_TIMEOUT = 16
}; };
@@ -77,8 +80,8 @@ serial_reset_port(serial_t *dev)
dev->iir = dev->ier = dev->lcr = dev->fcr = 0; dev->iir = dev->ier = dev->lcr = dev->fcr = 0;
dev->fifo_enabled = 0; dev->fifo_enabled = 0;
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0; dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
dev->rcvr_fifo_full = 0;
dev->baud_cycles = 0; dev->baud_cycles = 0;
dev->bytes_transmitted = 0;
memset(dev->xmit_fifo, 0, 16); memset(dev->xmit_fifo, 0, 16);
memset(dev->rcvr_fifo, 0, 14); memset(dev->rcvr_fifo, 0, 14);
} }
@@ -107,6 +110,10 @@ serial_update_ints(serial_t *dev)
/* Line status interrupt */ /* Line status interrupt */
stat = 1; stat = 1;
dev->iir = 6; dev->iir = 6;
} else if ((dev->ier & 1) && (dev->int_status & SERIAL_INT_TIMEOUT)) {
/* Received data available */
stat = 1;
dev->iir = 0x0c;
} else if ((dev->ier & 1) && (dev->int_status & SERIAL_INT_RECEIVE)) { } else if ((dev->ier & 1) && (dev->int_status & SERIAL_INT_RECEIVE)) {
/* Received data available */ /* Received data available */
stat = 1; stat = 1;
@@ -131,37 +138,69 @@ serial_update_ints(serial_t *dev)
} }
void static void
serial_write_fifo(serial_t *dev, uint8_t dat) serial_clear_timeout(serial_t *dev)
{ {
serial_log("serial_write_fifo(%08X, %02X, %i)\n", dev, dat, (dev->type >= SERIAL_NS16550) && dev->fifo_enabled); /* Disable timeout timer and clear timeout condition. */
timer_disable(&dev->timeout_timer);
dev->int_status &= ~SERIAL_INT_TIMEOUT;
serial_update_ints(dev);
}
static void
write_fifo(serial_t *dev, uint8_t dat)
{
serial_log("write_fifo(%08X, %02X, %i, %i)\n", dev, dat, (dev->type >= SERIAL_NS16550) && dev->fifo_enabled, dev->rcvr_fifo_pos & 0x0f);
if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) { if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) {
/* FIFO mode. */ /* FIFO mode. */
dev->rcvr_fifo[dev->rcvr_fifo_pos++] = dat; timer_disable(&dev->timeout_timer);
dev->rcvr_fifo_pos %= dev->rcvr_fifo_len; /* Indicate overrun. */
if (dev->rcvr_fifo_full)
dev->lsr |= 0x02;
else
dev->rcvr_fifo[dev->rcvr_fifo_pos] = dat;
dev->lsr &= 0xfe; dev->lsr &= 0xfe;
dev->lsr |= (!dev->rcvr_fifo_pos);
dev->int_status &= ~SERIAL_INT_RECEIVE; dev->int_status &= ~SERIAL_INT_RECEIVE;
if (!dev->rcvr_fifo_pos) { if (dev->rcvr_fifo_pos == (dev->rcvr_fifo_len - 1)) {
dev->lsr |= 0x01;
dev->int_status |= SERIAL_INT_RECEIVE; dev->int_status |= SERIAL_INT_RECEIVE;
serial_update_ints(dev);
} }
if (dev->rcvr_fifo_pos < 15)
dev->rcvr_fifo_pos++;
else
dev->rcvr_fifo_full = 1;
serial_update_ints(dev);
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
} else { } else {
/* Non-FIFO mode. */ /* Non-FIFO mode. */
/* Indicate overrun. */
if (dev->lsr & 0x01)
dev->lsr |= 0x02;
dev->dat = dat; dev->dat = dat;
dev->lsr |= 1; dev->lsr |= 0x01;
dev->int_status |= SERIAL_INT_RECEIVE; dev->int_status |= SERIAL_INT_RECEIVE;
serial_update_ints(dev); serial_update_ints(dev);
} }
} }
void
serial_write_fifo(serial_t *dev, uint8_t dat)
{
serial_log("serial_write_fifo(%08X, %02X, %i, %i)\n", dev, dat, (dev->type >= SERIAL_NS16550) && dev->fifo_enabled, dev->rcvr_fifo_pos & 0x0f);
if (!(dev->mctrl & 0x10))
write_fifo(dev, dat);
}
void void
serial_transmit(serial_t *dev, uint8_t val) serial_transmit(serial_t *dev, uint8_t val)
{ {
if (dev->mctrl & 0x10) if (dev->mctrl & 0x10)
serial_write_fifo(dev, val); write_fifo(dev, val);
else if (dev->sd->dev_write) else if (dev->sd->dev_write)
dev->sd->dev_write(dev, dev->sd->priv, val); dev->sd->dev_write(dev, dev->sd->priv, val);
} }
@@ -170,19 +209,27 @@ serial_transmit(serial_t *dev, uint8_t val)
static void static void
serial_move_to_txsr(serial_t *dev) serial_move_to_txsr(serial_t *dev)
{ {
int i = 0;
if (dev->fifo_enabled) { if (dev->fifo_enabled) {
dev->txsr = dev->xmit_fifo[dev->xmit_fifo_pos]; dev->txsr = dev->xmit_fifo[0];
dev->xmit_fifo[dev->xmit_fifo_pos++] = 0; if (dev->xmit_fifo_pos > 0) {
/* Move the entire fifo forward by one byte. */
for (i = 1; i < 16; i++)
dev->xmit_fifo[i - 1] = dev->xmit_fifo[i];
/* Decrease FIFO position. */
dev->xmit_fifo_pos--;
}
} else { } else {
dev->txsr = dev->thr; dev->txsr = dev->thr;
dev->thr = 0; dev->thr = 0;
} }
dev->bytes_transmitted++; dev->lsr &= ~0x40;
serial_log("serial_move_to_txsr(): FIFO %sabled, FIFO pos = %i\n", dev->fifo_enabled ? "en" : "dis", dev->xmit_fifo_pos & 0x0f);
if (!dev->fifo_enabled || (dev->xmit_fifo_pos == 16)) { if (!dev->fifo_enabled || (dev->xmit_fifo_pos == 0x0)) {
/* Update interrupts to signal THRE. */ /* Update interrupts to signal THRE and that TXSR is no longer empty. */
dev->xmit_fifo_pos = 0;
dev->lsr |= 0x20; dev->lsr |= 0x20;
dev->int_status |= SERIAL_INT_TRANSMIT; dev->int_status |= SERIAL_INT_TRANSMIT;
serial_update_ints(dev); serial_update_ints(dev);
@@ -191,7 +238,8 @@ serial_move_to_txsr(serial_t *dev)
dev->baud_cycles++; dev->baud_cycles++;
else else
dev->baud_cycles = 0; /* If not moving while transmitting, reset BAUDOUT cycle count. */ dev->baud_cycles = 0; /* If not moving while transmitting, reset BAUDOUT cycle count. */
dev->transmit_enabled &= ~1; /* Stop moving. */ if (!dev->fifo_enabled || (dev->xmit_fifo_pos == 0x0))
dev->transmit_enabled &= ~1; /* Stop moving. */
dev->transmit_enabled |= 2; /* Start transmitting. */ dev->transmit_enabled |= 2; /* Start transmitting. */
} }
@@ -199,13 +247,14 @@ serial_move_to_txsr(serial_t *dev)
static void static void
serial_process_txsr(serial_t *dev) serial_process_txsr(serial_t *dev)
{ {
serial_log("serial_process_txsr(): FIFO %sabled\n", dev->fifo_enabled ? "en" : "dis");
serial_transmit(dev, dev->txsr); serial_transmit(dev, dev->txsr);
dev->txsr = 0; dev->txsr = 0;
/* Reset BAUDOUT cycle count. */ /* Reset BAUDOUT cycle count. */
dev->baud_cycles = 0; dev->baud_cycles = 0;
/* If FIFO is enabled and there are bytes left to transmit, /* If FIFO is enabled and there are bytes left to transmit,
continue with the FIFO, otherwise stop. */ continue with the FIFO, otherwise stop. */
if (dev->fifo_enabled && (dev->bytes_transmitted < 16)) if (dev->fifo_enabled && (dev->xmit_fifo_pos != 0x0))
dev->transmit_enabled |= 1; dev->transmit_enabled |= 1;
else { else {
/* Both FIFO/THR and TXSR are empty. */ /* Both FIFO/THR and TXSR are empty. */
@@ -213,7 +262,6 @@ serial_process_txsr(serial_t *dev)
if (dev->lsr & 0x20) if (dev->lsr & 0x20)
dev->lsr |= 0x40; dev->lsr |= 0x40;
dev->transmit_enabled &= ~2; dev->transmit_enabled &= ~2;
dev->bytes_transmitted = 0;
} }
dev->int_status &= ~SERIAL_INT_TRANSMIT; dev->int_status &= ~SERIAL_INT_TRANSMIT;
serial_update_ints(dev); serial_update_ints(dev);
@@ -247,18 +295,45 @@ serial_transmit_timer(void *priv)
timer_on_auto(&dev->transmit_timer, dev->transmit_period); timer_on_auto(&dev->transmit_timer, dev->transmit_period);
} else { } else {
dev->baud_cycles = 0; dev->baud_cycles = 0;
dev->bytes_transmitted = 0;
return; return;
} }
} }
static void
serial_timeout_timer(void *priv)
{
serial_t *dev = (serial_t *) priv;
#ifdef ENABLE_SERIAL_LOG
serial_log("serial_timeout_timer()\n");
#endif
dev->lsr |= 0x01;
dev->int_status |= SERIAL_INT_TIMEOUT;
serial_update_ints(dev);
}
static void static void
serial_update_speed(serial_t *dev) serial_update_speed(serial_t *dev)
{ {
if (dev->transmit_enabled & 3) { if (dev->transmit_enabled & 3)
timer_on_auto(&dev->transmit_timer, dev->transmit_period); timer_on_auto(&dev->transmit_timer, dev->transmit_period);
}
if (timer_is_enabled(&dev->timeout_timer))
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
}
static void
serial_reset_fifo(serial_t *dev)
{
dev->lsr = (dev->lsr & 0xfe) | 0x60;
dev->int_status = (dev->int_status & ~SERIAL_INT_RECEIVE) | SERIAL_INT_TRANSMIT;
serial_update_ints(dev);
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
dev->rcvr_fifo_full = 0;
} }
@@ -286,20 +361,13 @@ serial_write(uint16_t addr, uint8_t val, void *p)
dev->int_status &= ~SERIAL_INT_TRANSMIT; dev->int_status &= ~SERIAL_INT_TRANSMIT;
serial_update_ints(dev); serial_update_ints(dev);
if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) { if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled && (dev->xmit_fifo_pos < 16)) {
/* FIFO mode. */ /* FIFO mode, begin transmitting. */
timer_on_auto(&dev->transmit_timer, dev->transmit_period);
dev->transmit_enabled |= 1; /* Start moving. */
dev->xmit_fifo[dev->xmit_fifo_pos++] = val; dev->xmit_fifo[dev->xmit_fifo_pos++] = val;
dev->xmit_fifo_pos &= 0x0f;
if (dev->xmit_fifo_pos == 0) {
/* FIFO full, begin transmitting. */
timer_on_auto(&dev->transmit_timer, dev->transmit_period);
dev->bytes_transmitted = 0;
dev->transmit_enabled |= 1; /* Start moving. */
}
} else { } else {
/* Non-FIFO mode, begin transmitting. */ /* Non-FIFO mode, begin transmitting. */
dev->bytes_transmitted = 0;
timer_on_auto(&dev->transmit_timer, dev->transmit_period); timer_on_auto(&dev->transmit_timer, dev->transmit_period);
dev->transmit_enabled |= 1; /* Start moving. */ dev->transmit_enabled |= 1; /* Start moving. */
dev->thr = val; dev->thr = val;
@@ -312,25 +380,29 @@ serial_write(uint16_t addr, uint8_t val, void *p)
serial_update_speed(dev); serial_update_speed(dev);
return; return;
} }
if ((val & 2) && (dev->lsr & 0x20))
dev->int_status |= SERIAL_INT_TRANSMIT;
dev->ier = val & 0xf; dev->ier = val & 0xf;
serial_update_ints(dev); serial_update_ints(dev);
break; break;
case 2: case 2:
if (dev->type >= SERIAL_NS16550) { if (dev->type >= SERIAL_NS16550) {
if ((val ^ dev->fcr) & 0x04) if ((val ^ dev->fcr) & 0x01)
dev->lsr |= 0x60; serial_reset_fifo(dev);
dev->fcr = val & 0xf9; dev->fcr = val & 0xf9;
dev->fifo_enabled = val & 0x01; dev->fifo_enabled = val & 0x01;
if (!dev->fifo_enabled) { if (!dev->fifo_enabled) {
memset(dev->rcvr_fifo, 0, 14); memset(dev->rcvr_fifo, 0, 14);
memset(dev->xmit_fifo, 0, 16); memset(dev->xmit_fifo, 0, 16);
dev->rcvr_fifo_pos = dev->xmit_fifo_pos = 0; dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
dev->rcvr_fifo_full = 0;
dev->rcvr_fifo_len = 1; dev->rcvr_fifo_len = 1;
break; break;
} }
if (val & 0x02) { if (val & 0x02) {
memset(dev->rcvr_fifo, 0, 14); memset(dev->rcvr_fifo, 0, 14);
dev->rcvr_fifo_pos = 0; dev->rcvr_fifo_pos = 0;
dev->rcvr_fifo_full = 0;
} }
if (val & 0x04) { if (val & 0x04) {
memset(dev->xmit_fifo, 0, 16); memset(dev->xmit_fifo, 0, 16);
@@ -350,6 +422,7 @@ serial_write(uint16_t addr, uint8_t val, void *p)
dev->rcvr_fifo_len = 14; dev->rcvr_fifo_len = 14;
break; break;
} }
serial_log("FIFO now %sabled, receive FIFO length = %i\n", dev->fifo_enabled ? "en" : "dis", dev->rcvr_fifo_len);
} }
break; break;
case 3: case 3:
@@ -375,6 +448,10 @@ serial_write(uint16_t addr, uint8_t val, void *p)
if (dev->sd->rcr_callback) if (dev->sd->rcr_callback)
dev->sd->rcr_callback(dev, dev->sd->priv); dev->sd->rcr_callback(dev, dev->sd->priv);
} }
if (!(val & 8) && (dev->mctrl & 8))
picintc(1 << dev->irq);
if ((val ^ dev->mctrl) & 0x10)
serial_reset_fifo(dev);
dev->mctrl = val; dev->mctrl = val;
if (val & 0x10) { if (val & 0x10) {
new_msr = (val & 0x0c) << 4; new_msr = (val & 0x0c) << 4;
@@ -393,6 +470,7 @@ serial_write(uint16_t addr, uint8_t val, void *p)
dev->msr = new_msr; dev->msr = new_msr;
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0; dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
dev->rcvr_fifo_full = 0;
} }
break; break;
case 5: case 5:
@@ -423,7 +501,7 @@ uint8_t
serial_read(uint16_t addr, void *p) serial_read(uint16_t addr, void *p)
{ {
serial_t *dev = (serial_t *)p; serial_t *dev = (serial_t *)p;
uint8_t ret = 0; uint8_t i, ret = 0;
sub_cycles(ISA_CYCLES(8)); sub_cycles(ISA_CYCLES(8));
@@ -436,22 +514,23 @@ serial_read(uint16_t addr, void *p)
if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) { if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) {
/* FIFO mode. */ /* FIFO mode. */
if (dev->mctrl & 0x10) {
ret = dev->xmit_fifo[dev->xmit_fifo_pos++]; serial_clear_timeout(dev);
dev->xmit_fifo_pos %= 16;
if (!dev->xmit_fifo_pos) { ret = dev->rcvr_fifo[0];
dev->lsr &= 0xfe; dev->rcvr_fifo_full = 0;
dev->int_status &= ~SERIAL_INT_RECEIVE; if (dev->rcvr_fifo_pos > 0) {
serial_update_ints(dev); for (i = 1; i < 16; i++)
} dev->rcvr_fifo[i - 1] = dev->rcvr_fifo[i];
serial_log("FIFO position %i: read %02X, next %02X\n", dev->rcvr_fifo_pos, ret, dev->rcvr_fifo[0]);
dev->rcvr_fifo_pos--;
/* At least one byte remains to be read, start the timeout
timer so that a timeout is indicated in case of no read. */
timer_on_auto(&dev->timeout_timer, 4.0 * dev->bits * dev->transmit_period);
} else { } else {
ret = dev->rcvr_fifo[dev->rcvr_fifo_pos++]; dev->lsr &= 0xfe;
dev->rcvr_fifo_pos %= dev->rcvr_fifo_len; dev->int_status &= ~SERIAL_INT_RECEIVE;
if (!dev->rcvr_fifo_pos) { serial_update_ints(dev);
dev->lsr &= 0xfe;
dev->int_status &= ~SERIAL_INT_RECEIVE;
serial_update_ints(dev);
}
} }
} else { } else {
ret = dev->dat; ret = dev->dat;
@@ -599,6 +678,7 @@ serial_init(const device_t *info)
dev->fcr = 0x06; dev->fcr = 0x06;
serial_transmit_period(dev); serial_transmit_period(dev);
timer_add(&dev->transmit_timer, serial_transmit_timer, dev, 0); timer_add(&dev->transmit_timer, serial_transmit_timer, dev, 0);
timer_add(&dev->timeout_timer, serial_timeout_timer, dev, 0);
} }
next_inst++; next_inst++;

View File

@@ -8,7 +8,7 @@
* *
* Definitions for the NS8250/16450/16550 UART emulation. * Definitions for the NS8250/16450/16550 UART emulation.
* *
* Version: @(#)serial.h 1.0.11 2019/10/11 * Version: @(#)serial.h 1.0.12 2019/10/31
* *
* Author: Sarah Walker, <http://pcem-emulator.co.uk/> * Author: Sarah Walker, <http://pcem-emulator.co.uk/>
* Miran Grca, <mgrca8@gmail.com> * Miran Grca, <mgrca8@gmail.com>
@@ -44,14 +44,15 @@ typedef struct serial_s
dat, int_status, scratch, fcr, dat, int_status, scratch, fcr,
irq, type, inst, transmit_enabled, irq, type, inst, transmit_enabled,
fifo_enabled, rcvr_fifo_len, bits, data_bits, fifo_enabled, rcvr_fifo_len, bits, data_bits,
baud_cycles, bytes_transmitted, txsr, pad; baud_cycles, rcvr_fifo_full, txsr, pad;
uint16_t dlab, base_address; uint16_t dlab, base_address;
uint8_t rcvr_fifo_pos, rcvr_fifo[14]; uint8_t rcvr_fifo_pos, xmit_fifo_pos,
uint8_t xmit_fifo_pos, xmit_fifo[16]; pad0, pad1,
rcvr_fifo[16], xmit_fifo[16];
pc_timer_t transmit_timer; pc_timer_t transmit_timer, timeout_timer;
double transmit_period; double transmit_period;
struct serial_device_s *sd; struct serial_device_s *sd;