aboutsummaryrefslogtreecommitdiff
path: root/serial.h
diff options
context:
space:
mode:
authorJavier <dev.git@javispedro.com>2026-08-30 02:21:09 +0200
committerJavier <dev.git@javispedro.com>2026-08-30 02:21:09 +0200
commit1b0344660de2832b472b0a413c50ff7f111a9293 (patch)
tree5ae7303643d6c9187da6366cf30a4f6a4c73d54b /serial.h
parent003a26a76982f512e2f3a1108e46b88aa29ab306 (diff)
downloadvbados-1b0344660de2832b472b0a413c50ff7f111a9293.tar.gz
vbados-1b0344660de2832b472b0a413c50ff7f111a9293.zip
first steps towards (optional) serial mouse support
Diffstat (limited to 'serial.h')
-rw-r--r--serial.h117
1 files changed, 99 insertions, 18 deletions
diff --git a/serial.h b/serial.h
index beeaf50..1ddc317 100644
--- a/serial.h
+++ b/serial.h
@@ -2,6 +2,7 @@
#define SERIAL_H
#include <stdint.h>
+#include <conio.h>
#include "bda.h"
@@ -12,6 +13,7 @@ enum SERIAL_UART_REGS {
SERIAL_DIVISOR_LO = 0,
/** (only with DLAB enabled) MSB of divisor/baud rate */
SERIAL_DIVISOR_HI = 1,
+ SERIAL_DIVISOR = SERIAL_DIVISOR_LO,
/** Interrupt Enable Register(read/write) */
SERIAL_IER = 1,
/** Fifo Control Register (write-only) */
@@ -45,6 +47,19 @@ enum SERIAL_DIVISOR {
SERIAL_DIVISOR_1200 = 96
};
+enum SERIAL_IER {
+ /** No interrupts */
+ SERIAL_IER_NONE = 0,
+ /** Enable Receiver Buffer Full Interrupt */
+ SERIAL_IER_ERBFI = 1 << 0,
+ /** Enable Transmitter Buffer Empty Interrupt */
+ SERIAL_IER_ETBEI = 1 << 1,
+ /** Enable Line Status Interrupt */
+ SERIAL_IER_ELSI = 1 << 2,
+ /** Enable Delta Status Signals Interrupt */
+ SERIAL_IER_EDSSI = 1 << 3,
+};
+
enum SERIAL_FCR {
SERIAL_FCR_RX_TRIGER_1 = 0 << 6,
SERIAL_FCR_RX_TRIGER_4 = 1 << 6,
@@ -54,6 +69,31 @@ enum SERIAL_FCR {
SERIAL_FCR_TX_RESET = 1 << 2,
SERIAL_FCR_RX_RESET = 1 << 1,
SERIAL_FCR_FIFO_ENABLE = 1 << 0,
+ SERIAL_FCR_FIFO_RESET = SERIAL_FCR_TX_RESET | SERIAL_FCR_RX_RESET,
+ SERIAL_FCR_FIFO_DISABLE = 0
+};
+
+enum SERIAL_IIR {
+ /** Mask for retrieving the interrupt that occured. See enum SERIAL_IID. */
+ SERIAL_IIR_ID = 0xF,
+
+ SERIAL_IIR_FIFO1 = 1 << 6,
+ SERIAL_IIR_FIFO2 = 1 << 7,
+};
+
+enum SERIAL_IID {
+ /** No interrupt. */
+ SERIAL_IID_NONE = 1,
+ /** Highest priority: line error. OE, PE, etc. Serviced by reading LSR. */
+ SERIAL_IID_ERROR = 6,
+ /** Second highest priority: data received or trigger reached. Serviced by reading data. */
+ SERIAL_IID_DATA = 4,
+ /** Second highest priority: stale data in FIFO. Serviced by reading data. */
+ SERIAL_IID_STALE = 0xC,
+ /** Third priority: transmitter buffer has room. Serviced by reading this reg/writing. */
+ SERIAL_IID_THRE = 2,
+ /** Fourth priority: MSR change. Serviced by reading MSR. */
+ SERIAL_IID_MODEM = 0,
};
enum SERIAL_LCR {
@@ -115,14 +155,14 @@ enum SERIAL_MSR {
struct serial_config
{
- uint8_t divisor_lo, divisor_hi;
+ uint16_t divisor;
uint8_t ier, lcr, mcr;
};
static unsigned serial_num_ports()
{
- uint8_t equipment = bda_get_byte(0x11);
- uint8_t numports = (equipment & 0xE) >> 1;
+ uint16_t equipment = bda_get_equipment();
+ uint8_t numports = (equipment & 0xE00) >> 9;
return numports;
}
@@ -131,11 +171,31 @@ static uint16_t serial_get_iobase(unsigned port)
uint16_t bdaport = bda_get_word((port - 1) * 2);
if (bdaport > 10) {
return bdaport;
- } else if (port == 1) {
- // Even if BIOS says no, hardcode the basic COM1 port at least.
+ }
+
+ // Even if BIOS says no, hardcode basic COM1/COM2 at least.
+ switch (port) {
+ case 1:
return 0x3F8;
+ case 2:
+ return 0x2F8;
+ default:
+ return 0;
+ }
+}
+
+static uint8_t serial_get_irq(unsigned port)
+{
+ switch (port) {
+ case 1:
+ case 3:
+ return 4;
+ case 2:
+ case 4:
+ return 3;
+ default:
+ return 0;
}
- return 0;
}
static void serial_save_config(unsigned iobase, struct serial_config *config)
@@ -145,8 +205,7 @@ static void serial_save_config(unsigned iobase, struct serial_config *config)
config->mcr = inp(iobase + SERIAL_MCR);
// Set DLAB so that we can read divisor
outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB);
- config->divisor_lo = inp(iobase + SERIAL_DIVISOR_LO);
- config->divisor_hi = inp(iobase + SERIAL_DIVISOR_HI);
+ config->divisor = inpw(iobase + SERIAL_DIVISOR);
// Restore DLAB to whatever it was
outp(iobase + SERIAL_LCR, config->lcr);
}
@@ -154,22 +213,34 @@ static void serial_save_config(unsigned iobase, struct serial_config *config)
static void serial_restore_config(unsigned iobase, const struct serial_config *config)
{
outp(iobase + SERIAL_LCR, config->lcr | SERIAL_LCR_DLAB);
- outp(iobase + SERIAL_DIVISOR_LO, config->divisor_lo);
- outp(iobase + SERIAL_DIVISOR_HI, config->divisor_hi);
+ outpw(iobase + SERIAL_DIVISOR, config->divisor);
outp(iobase + SERIAL_LCR, config->lcr);
outp(iobase + SERIAL_MCR, config->mcr);
outp(iobase + SERIAL_IER, config->ier);
}
-static void serial_configure(unsigned iobase, uint16_t divisor, uint8_t lcr)
+/// Configure line characteristics (speed, databytes, etc.)
+/// Disables interrupts
+static void serial_configure_line(unsigned iobase, uint16_t divisor, uint8_t lcr)
{
- outp(iobase + SERIAL_IER, 0); // Disable all interrupts
outp(iobase + SERIAL_LCR, SERIAL_LCR_DLAB); // Enable DLAB and clear all other flags
- outp(iobase + SERIAL_DIVISOR_LO, divisor);
- outp(iobase + SERIAL_DIVISOR_HI, divisor >> 8);
+ outpw(iobase + SERIAL_DIVISOR, divisor);
outp(iobase + SERIAL_LCR, lcr); // This also disables DLAB
- // Enable and reset both FIFOs
- outp(iobase + SERIAL_FCR, SERIAL_FCR_RX_TRIGER_14|SERIAL_FCR_FIFO_ENABLE|SERIAL_FCR_RX_RESET|SERIAL_FCR_TX_RESET);
+}
+
+/// Configures when exceptions are going to be raised
+/// it also clears all pending interrupts
+/// and enables/disables the OUT2 gator here, which I'm not sure if is conceptually clean
+static void serial_configure_interrupts(unsigned iobase, uint8_t ier)
+{
+ uint8_t mcr = inp(iobase + SERIAL_MCR);
+ outp(iobase + SERIAL_IER, ier);
+ if (ier) {
+ mcr |= SERIAL_MCR_OUT2; // Actually enables IRQ line for this UART
+ } else {
+ mcr &= ~SERIAL_MCR_OUT2;
+ }
+ outp(iobase + SERIAL_MCR, mcr);
}
static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr)
@@ -177,9 +248,14 @@ static inline void serial_set_modem_control(unsigned iobase, uint8_t mcr)
outp(iobase + SERIAL_MCR, mcr);
}
-static inline void serial_set_modem_status(unsigned iobase, uint8_t msr)
+static inline void serial_set_fifo_control(unsigned iobase, uint8_t fcr)
{
- outp(iobase + SERIAL_MSR, msr);
+ outp(iobase + SERIAL_FCR, fcr);
+}
+
+static inline uint8_t serial_read_pending_interrupt(unsigned iobase)
+{
+ return inp(iobase + SERIAL_IIR) & SERIAL_IIR_ID;
}
static inline uint8_t serial_get_line_status(unsigned iobase)
@@ -187,6 +263,11 @@ static inline uint8_t serial_get_line_status(unsigned iobase)
return inp(iobase + SERIAL_LSR);
}
+static inline uint8_t serial_get_modem_status(unsigned iobase)
+{
+ return inp(iobase + SERIAL_MSR);
+}
+
static inline bool serial_data_ready(unsigned iobase)
{
return serial_get_line_status(iobase) & SERIAL_LSR_DR;