diff --git a/src/serial.c b/src/serial.c
index 76192141c..6761c04d3 100644
--- a/src/serial.c
+++ b/src/serial.c
@@ -1,3 +1,23 @@
+/*
+ * 86Box A hypervisor and IBM PC system emulator that specializes in
+ * running old operating systems and software designed for IBM
+ * PC systems and compatibles from 1981 through fairly recent
+ * system designs based on the PCI bus.
+ *
+ * This file is part of the 86Box distribution.
+ *
+ * NS8250/16450/16550 UART emulation.
+ *
+ * Version: @(#)serial.h 1.0.11 2019/10/11
+ *
+ * Author: Sarah Walker,
+ * Miran Grca,
+ * Fred N. van Kempen,
+ *
+ * Copyright 2008-2019 Sarah Walker.
+ * Copyright 2016-2019 Miran Grca.
+ * Copyright 2017-2019 Fred N. van Kempen.
+ */
#include
#include
#include
@@ -53,9 +73,12 @@ serial_log(const char *fmt, ...)
void
serial_reset_port(serial_t *dev)
{
+ dev->lsr = 0x60; /* Mark that both THR/FIFO and TXSR are empty. */
dev->iir = dev->ier = dev->lcr = dev->fcr = 0;
dev->fifo_enabled = 0;
dev->xmit_fifo_pos = dev->rcvr_fifo_pos = 0;
+ dev->baud_cycles = 0;
+ dev->bytes_transmitted = 0;
memset(dev->xmit_fifo, 0, 16);
memset(dev->rcvr_fifo, 0, 14);
}
@@ -64,25 +87,12 @@ serial_reset_port(serial_t *dev)
void
serial_transmit_period(serial_t *dev)
{
- double ddlab, byte_period, bits;
+ double ddlab;
ddlab = (double) dev->dlab;
- /* Bit period based on DLAB. */
- /* correct: 833.333333... */
- byte_period = (16000000.0 * ddlab) / 1843200.0;
- /* Data bits according to LCR 1,0. */
- bits = (double) ((dev->lcr & 0x03) + 5);
- /* Stop bits. */
- if (dev->lcr & 0x04)
- bits += !(dev->lcr & 0x03) ? 1.5 : 2.0;
- else
- bits += 1.0;
- /* Parity bits. */
- if (dev->lcr & 0x08)
- bits += 1.0;
- byte_period *= bits;
- dev->transmit_period = byte_period;
+ /* Bit period based on DLAB. */
+ dev->transmit_period = (16000000.0 * ddlab) / 1843200.0;
}
@@ -157,25 +167,71 @@ serial_transmit(serial_t *dev, uint8_t val)
}
+/* Transmit_enable flags:
+ Bit 0 = Do move if set;
+ Bit 1 = Do transmit if set. */
static void
serial_transmit_timer(void *priv)
{
serial_t *dev = (serial_t *) priv;
+ int delay = 8; /* STOP to THRE delay is 8 BAUDOUT cycles. */
- if (dev->fifo_enabled) {
- serial_transmit(dev, dev->xmit_fifo[dev->xmit_fifo_pos++]);
- if (dev->xmit_fifo_pos == 16) {
+ if ((dev->transmit_enabled & 1) && (dev->transmit_enabled & 2))
+ delay = dev->data_bits; /* Delay by less if already transmitting. */
+
+ if ((dev->baud_cycles == delay) && (dev->transmit_enabled & 1)) {
+ /* We have processed (data bits) BAUDOUT cycles. */
+ if (dev->fifo_enabled) {
+ dev->txsr = dev->xmit_fifo[dev->xmit_fifo_pos];
+ dev->xmit_fifo[dev->xmit_fifo_pos++] = 0;
+ } else {
+ dev->txsr = dev->thr;
+ dev->thr = 0;
+ }
+
+ dev->bytes_transmitted++;
+
+ if (!dev->fifo_enabled || (dev->xmit_fifo_pos == 16)) {
+ /* Update interrupts to signal THRE. */
dev->xmit_fifo_pos = 0;
- /* Mark both FIFO and shift register as empty. */
- dev->lsr |= 0x40;
- dev->transmit_enabled = 0;
- } else
+ dev->lsr |= 0x20;
+ dev->int_status |= SERIAL_INT_TRANSMIT;
+ serial_update_ints(dev);
+ }
+ if (dev->transmit_enabled & 2)
+ dev->baud_cycles++;
+ else
+ dev->baud_cycles = 0; /* If not moving while transmitting, reset BAUDOUT cycle count. */
+ dev->transmit_enabled &= ~1; /* Stop moving. */
+ dev->transmit_enabled |= 2; /* Start transmitting. */
+ timer_advance_u64(&dev->transmit_timer, (uint64_t) (dev->transmit_period * (double)TIMER_USEC));
+ } else if ((dev->baud_cycles == dev->bits) && (dev->transmit_enabled & 2)) {
+ /* We have processed (total bits) BAUDOUT cycles, transmit the byte. */
+ serial_transmit(dev, dev->txsr);
+ dev->txsr = 0;
+ /* Reset BAUDOUT cycle count. */
+ dev->baud_cycles = 0;
+ /* If FIFO is enabled and there are bytes left to transmit,
+ continue with the FIFO, otherwise stop. */
+ if (dev->fifo_enabled && (dev->bytes_transmitted == 16)) {
+ dev->transmit_enabled |= 1;
timer_advance_u64(&dev->transmit_timer, (uint64_t) (dev->transmit_period * (double)TIMER_USEC));
+ } else {
+ /* Both FIFO/THR and TXSR are empty. */
+ /* If bit 5 is set, also set bit 6 to mark both THR and shift register as empty. */
+ if (dev->lsr & 0x20)
+ dev->lsr |= 0x40;
+ dev->transmit_enabled &= ~2;
+ dev->bytes_transmitted = 0;
+ }
+ dev->int_status &= ~SERIAL_INT_TRANSMIT;
+ serial_update_ints(dev);
+ } else if (dev->transmit_enabled & 3) {
+ dev->baud_cycles++;
+ timer_advance_u64(&dev->transmit_timer, (uint64_t) (dev->transmit_period * (double)TIMER_USEC));
} else {
- serial_transmit(dev, dev->thr);
- /* Mark both THR and shift register as empty. */
- dev->lsr |= 0x40;
- dev->transmit_enabled = 0;
+ dev->baud_cycles = 0;
+ dev->bytes_transmitted = 0;
}
}
@@ -183,7 +239,7 @@ serial_transmit_timer(void *priv)
static void
serial_update_speed(serial_t *dev)
{
- if (dev->transmit_enabled) {
+ if (dev->transmit_enabled & 3) {
timer_disable(&dev->transmit_timer);
timer_set_delay_u64(&dev->transmit_timer, (uint64_t) (dev->transmit_period * (double)TIMER_USEC));
}
@@ -194,7 +250,7 @@ void
serial_write(uint16_t addr, uint8_t val, void *p)
{
serial_t *dev = (serial_t *)p;
- uint8_t new_msr, old_lsr, old;
+ uint8_t new_msr, old;
serial_log("UART: Write %02X to port %02X\n", val, addr);
@@ -209,43 +265,30 @@ serial_write(uint16_t addr, uint8_t val, void *p)
return;
}
+ /* Indicate FIFO/THR is no longer empty. */
+ dev->lsr &= 0x9f;
+ dev->int_status &= ~SERIAL_INT_TRANSMIT;
+ serial_update_ints(dev);
+
if ((dev->type >= SERIAL_NS16550) && dev->fifo_enabled) {
/* FIFO mode. */
dev->xmit_fifo[dev->xmit_fifo_pos++] = val;
dev->xmit_fifo_pos &= 0x0f;
- old_lsr = dev->lsr;
- /* Indicate FIFO is no longer empty. */
- if (dev->xmit_fifo_pos) {
- /* FIFO not yet full. */
- /* Update interrupts. */
- dev->lsr &= 0x9f;
- if ((old_lsr ^ dev->lsr) & 0x20)
- serial_update_ints(dev);
- } else {
+
+ if (dev->xmit_fifo_pos == 0) {
/* FIFO full, begin transmitting. */
timer_disable(&dev->transmit_timer);
timer_set_delay_u64(&dev->transmit_timer, (uint64_t) (dev->transmit_period * (double)TIMER_USEC));
- dev->transmit_enabled = 1;
- dev->lsr &= 0xbf;
- /* Update interrupts. */
- dev->lsr |= 0x20;
- dev->int_status |= SERIAL_INT_TRANSMIT;
- serial_update_ints(dev);
+ dev->bytes_transmitted = 0;
+ dev->transmit_enabled |= 1; /* Start moving. */
}
} else {
- /* Non-FIFO mode. */
- /* Begin transmitting. */
+ /* Non-FIFO mode, begin transmitting. */
+ dev->bytes_transmitted = 0;
timer_disable(&dev->transmit_timer);
timer_set_delay_u64(&dev->transmit_timer, (uint64_t) (dev->transmit_period * (double)TIMER_USEC));
- dev->transmit_enabled = 1;
+ dev->transmit_enabled |= 1; /* Start moving. */
dev->thr = val;
- /* Clear bit 6 because shift register is full. */
- dev->lsr &= 0xbf;
- /* But set bit 5 before THR is empty. */
- dev->lsr |= 0x20;
- /* Update interrupts. */
- dev->int_status |= SERIAL_INT_TRANSMIT;
- serial_update_ints(dev);
}
break;
case 1:
@@ -260,6 +303,8 @@ serial_write(uint16_t addr, uint8_t val, void *p)
break;
case 2:
if (dev->type >= SERIAL_NS16550) {
+ if ((val ^ dev->fcr) & 0x04)
+ dev->lsr |= 0x60;
dev->fcr = val & 0xf9;
dev->fifo_enabled = val & 0x01;
if (!dev->fifo_enabled) {
@@ -297,6 +342,16 @@ serial_write(uint16_t addr, uint8_t val, void *p)
old = dev->lcr;
dev->lcr = val;
if ((old ^ val) & 0x0f) {
+ /* Data bits + start bit. */
+ dev->bits = ((dev->lcr & 0x03) + 5) + 1;
+ /* Stop bits. */
+ dev->bits++; /* First stop bit. */
+ if (dev->lcr & 0x04)
+ dev->bits++; /* Second stop bit. */
+ /* Parity bit. */
+ if (dev->lcr & 0x08)
+ dev->bits++;
+
serial_transmit_period(dev);
serial_update_speed(dev);
}
@@ -414,9 +469,6 @@ serial_read(uint16_t addr, void *p)
ret = dev->mctrl;
break;
case 5:
- if (dev->lsr & 0x20)
- dev->lsr |= 0x40;
- dev->lsr |= 0x20;
ret = dev->lsr;
if (dev->lsr & 0x1f)
dev->lsr &= ~0x1e;
diff --git a/src/serial.h b/src/serial.h
index d5e29db07..2cfca14cd 100644
--- a/src/serial.h
+++ b/src/serial.h
@@ -6,11 +6,16 @@
*
* This file is part of the 86Box distribution.
*
- * Definitions for the SERIAL card.
+ * Definitions for the NS8250/16450/16550 UART emulation.
*
- * Version: @(#)serial.h 1.0.10 2019/03/24
+ * Version: @(#)serial.h 1.0.11 2019/10/11
*
- * Author: Fred N. van Kempen,
+ * Author: Sarah Walker,
+ * Miran Grca,
+ * Fred N. van Kempen,
+ *
+ * Copyright 2008-2019 Sarah Walker.
+ * Copyright 2016-2019 Miran Grca.
* Copyright 2017-2019 Fred N. van Kempen.
*/
#ifndef EMU_SERIAL_H
@@ -38,7 +43,8 @@ typedef struct serial_s
iir, ier, lcr, msr,
dat, int_status, scratch, fcr,
irq, type, inst, transmit_enabled,
- fifo_enabled, rcvr_fifo_len, pad, pad0;
+ fifo_enabled, rcvr_fifo_len, bits, data_bits,
+ baud_cycles, bytes_transmitted, txsr, pad;
uint16_t dlab, base_address;