aboutsummaryrefslogtreecommitdiff
path: root/mubios
diff options
context:
space:
mode:
Diffstat (limited to 'mubios')
-rw-r--r--mubios/core/bda.c3
-rw-r--r--mubios/drivers/ata.c3
-rw-r--r--mubios/drivers/ioapic.c2
-rw-r--r--mubios/drivers/kbd.c12
-rw-r--r--mubios/drivers/serial.c113
-rw-r--r--mubios/drivers/vga.c27
-rw-r--r--mubios/include/bios.h6
-rw-r--r--mubios/services/int13.c15
-rw-r--r--mubios/services/int14.c75
-rw-r--r--mubios/services/int15.c6
-rw-r--r--mubios/services/int16.c473
11 files changed, 586 insertions, 149 deletions
diff --git a/mubios/core/bda.c b/mubios/core/bda.c
index 5aad85c..ddcedb7 100644
--- a/mubios/core/bda.c
+++ b/mubios/core/bda.c
@@ -24,6 +24,8 @@
extern u8 __ebda_start[];
#define BDA_COM1_OFFSET 0x0000u
+#define BDA_COM_TIMEOUT_OFFSET 0x007Cu
+#define BIOS_COM_TIMEOUT 0x0Au
#define BDA_LPT1_OFFSET 0x0008u
static void
@@ -47,6 +49,7 @@ bda_init (void)
store_u16 (bda + BDA_EQUIPMENT_OFFSET, 0x4227u);
store_u16 (bda + BDA_COM1_OFFSET, 0x03F8u);
+ bda[BDA_COM_TIMEOUT_OFFSET] = BIOS_COM_TIMEOUT;
store_u16 (bda + BDA_LPT1_OFFSET, 0x0378u);
store_u16 (bda + BDA_CONVENTIONAL_MEMORY_OFFSET, (u16)(ebda_base >> 10));
diff --git a/mubios/drivers/ata.c b/mubios/drivers/ata.c
index 86bab21..7d23a9c 100644
--- a/mubios/drivers/ata.c
+++ b/mubios/drivers/ata.c
@@ -96,8 +96,7 @@ ata_read_transfer28 (u32 lba, u16 *buffer, u16 count)
for (sector = 0; sector < count; ++sector)
{
status = ata_wait_not_busy ();
- if ((status & ATA_STATUS_ERR) != 0
- || (status & ATA_STATUS_DRQ) == 0)
+ if ((status & ATA_STATUS_ERR) != 0 || (status & ATA_STATUS_DRQ) == 0)
return 1;
for (index = 0; index < 256; ++index)
diff --git a/mubios/drivers/ioapic.c b/mubios/drivers/ioapic.c
index 7a0b6cb..552f497 100644
--- a/mubios/drivers/ioapic.c
+++ b/mubios/drivers/ioapic.c
@@ -84,7 +84,7 @@ ioapic_mask_all (void)
for (pin = 0; pin <= maximum_pin; ++pin)
{
ioapic_write ((u8)(IOAPIC_REDIRECTION_BASE + 2u * pin),
- IOAPIC_REDIRECTION_MASK);
+ IOAPIC_REDIRECTION_MASK);
ioapic_write ((u8)(IOAPIC_REDIRECTION_BASE + 2u * pin + 1u), 0);
}
}
diff --git a/mubios/drivers/kbd.c b/mubios/drivers/kbd.c
index 4de0a96..eb99e70 100644
--- a/mubios/drivers/kbd.c
+++ b/mubios/drivers/kbd.c
@@ -33,9 +33,9 @@
/*
* 8042 command byte bits.
- * Bit 0 enables the keyboard IRQ, bit 1 the auxiliary port IRQ, bit 4 and bit 5
- * hold the keyboard and auxiliary clock, and bit 6 selects set 1 scan codes on
- * the keyboard interface.
+ * Bit 0 enables the keyboard IRQ, bit 1 the auxiliary port IRQ, bit 4 and bit
+ * 5 hold the keyboard and auxiliary clock, and bit 6 selects set 1 scan codes
+ * on the keyboard interface.
*/
#define KBD_CONFIG_IRQ1 0x01u
#define KBD_CONFIG_IRQ12 0x02u
@@ -103,7 +103,8 @@ kbd_init (void)
/* Disable both ports before the controller is reconfigured. */
kbd_write_command (KBD_CMD_DISABLE_KBD);
- /* Drain the self-test and any pending scan code left in the output buffer. */
+ /* Drain the self-test and any pending scan code left in the output buffer.
+ */
while ((io_in8 (KBD_STATUS_PORT) & KBD_STATUS_OBF) != 0u)
(void)io_in8 (KBD_DATA_PORT);
@@ -115,7 +116,8 @@ kbd_init (void)
config = io_in8 (KBD_DATA_PORT);
- config &= (u8)~(KBD_CONFIG_DISABLE_KBD_CLOCK | KBD_CONFIG_DISABLE_AUX_CLOCK);
+ config
+ &= (u8) ~(KBD_CONFIG_DISABLE_KBD_CLOCK | KBD_CONFIG_DISABLE_AUX_CLOCK);
config |= KBD_CONFIG_IRQ1 | KBD_CONFIG_IRQ12 | KBD_CONFIG_TRANSLATE;
if (kbd_write_command (KBD_CMD_WRITE_CONFIG) != 0)
diff --git a/mubios/drivers/serial.c b/mubios/drivers/serial.c
index 3da2093..c8b9adf 100644
--- a/mubios/drivers/serial.c
+++ b/mubios/drivers/serial.c
@@ -30,6 +30,7 @@
#define COM_LCR_OFFSET 0x03u
#define COM_MCR_OFFSET 0x04u
#define COM_LSR_OFFSET 0x05u
+#define COM_MSR_OFFSET 0x06u
#define COM_LCR_DLAB 0x80u
#define COM_LCR_8N1 0x03u
@@ -44,6 +45,12 @@
#define COM_MCR_OUT2 0x08u
#define COM_LSR_THRE 0x20u
+#define COM_LSR_IDLE 0x40u
+#define COM_LSR_DATA_READY 0x01u
+#define BIOS_SERIAL_TIMEOUT 0x80u
+
+#define PIT_COUNTER0 0x0040u
+#define PIT_COMMAND 0x0043u
static u16
com1_port (u16 offset)
@@ -51,6 +58,112 @@ com1_port (u16 offset)
return (u16)(COM1_BASE + offset);
}
+/* Latching channel 0 works while the protected-mode service has IRQs off. */
+u16
+serial_timer_counter (void)
+{
+ u16 counter;
+
+ io_out8 (PIT_COMMAND, 0x00u);
+ counter = io_in8 (PIT_COUNTER0);
+ counter |= (u16)((u16)io_in8 (PIT_COUNTER0) << 8);
+ return counter;
+}
+
+static int
+serial_wait (u16 base, u8 mask, u8 timeout, u8 *status)
+{
+ u16 previous;
+ u16 current;
+ u32 elapsed;
+ u32 duration;
+
+ previous = serial_timer_counter ();
+ elapsed = 0;
+ /* One BDA timeout unit is one 65536-clock BIOS timer tick. */
+ duration = (u32)timeout << 16;
+
+ for (;;)
+ {
+ *status = io_in8 ((u16)(base + COM_LSR_OFFSET));
+ if ((*status & mask) == mask)
+ return 1;
+
+ if (elapsed >= duration)
+ {
+ *status |= BIOS_SERIAL_TIMEOUT;
+ return 0;
+ }
+
+ current = serial_timer_counter ();
+ elapsed += (u16)(previous - current);
+ previous = current;
+ }
+}
+
+u16
+serial_status (u16 base)
+{
+ u8 line;
+ u8 modem;
+
+ line = io_in8 ((u16)(base + COM_LSR_OFFSET));
+ modem = io_in8 ((u16)(base + COM_MSR_OFFSET));
+ return (u16)(((u16)line << 8) | modem);
+}
+
+u16
+serial_configure (u16 base, u8 parameters)
+{
+ static const u16 divisors[]
+ = { 1047u, 768u, 384u, 192u, 96u, 48u, 24u, 12u };
+ u16 divisor;
+
+ /* INT 14h AL: bits 7:5 select baud; bits 4:0 are the UART format. */
+ divisor = divisors[parameters >> 5];
+ io_out8 ((u16)(base + COM_LCR_OFFSET), COM_LCR_DLAB);
+ io_out8 ((u16)(base + COM_DATA_OFFSET), (u8)divisor);
+ io_out8 ((u16)(base + COM_IER_OFFSET), (u8)(divisor >> 8));
+ io_out8 ((u16)(base + COM_LCR_OFFSET), parameters & 0x1Fu);
+ /* Do not reset the FIFO here: pending terminal input must survive. */
+ return serial_status (base);
+}
+
+u8
+serial_transmit (u16 base, u8 value, u8 timeout)
+{
+ u8 status;
+
+ if (serial_wait (base, COM_LSR_THRE | COM_LSR_IDLE, timeout, &status))
+ io_out8 ((u16)(base + COM_DATA_OFFSET), value);
+
+ return status;
+}
+
+u8
+serial_receive (u16 base, u8 *value, u8 timeout)
+{
+ u8 status;
+
+ if (serial_wait (base, COM_LSR_DATA_READY, timeout, &status))
+ *value = io_in8 ((u16)(base + COM_DATA_OFFSET));
+
+ return status;
+}
+
+int
+serial_try_receive (u16 base, u8 *value)
+{
+ u8 status;
+
+ status = io_in8 ((u16)(base + COM_LSR_OFFSET));
+ if ((status & COM_LSR_DATA_READY) == 0u)
+ return 0;
+
+ *value = io_in8 ((u16)(base + COM_DATA_OFFSET));
+ return 1;
+}
+
void
serial_init (void)
{
diff --git a/mubios/drivers/vga.c b/mubios/drivers/vga.c
index 57e1f8f..16f88e5 100644
--- a/mubios/drivers/vga.c
+++ b/mubios/drivers/vga.c
@@ -35,8 +35,7 @@
#define VGA_ATTR_INDEX_DATA 0x03C0u
static void
-vga_write_registers (u16 index_port, u16 data_port, const u8 *values,
- u8 count)
+vga_write_registers (u16 index_port, u16 data_port, const u8 *values, u8 count)
{
u8 index;
@@ -50,19 +49,17 @@ vga_write_registers (u16 index_port, u16 data_port, const u8 *values,
void
vga_text_mode_init (void)
{
- static const u8 sequencer[] = {0x00u, 0x00u, 0x03u, 0x00u, 0x02u};
- static const u8 crtc[] = {0x5Fu, 0x4Fu, 0x50u, 0x82u, 0x55u, 0x81u,
- 0xBFu, 0x1Fu, 0x00u, 0x4Fu, 0x0Du, 0x0Eu,
- 0x00u, 0x00u, 0x00u, 0x00u, 0x9Cu, 0x8Eu,
- 0x8Fu, 0x28u, 0x1Fu, 0x96u, 0xB9u, 0xA3u,
- 0xFFu};
- static const u8 graphics[] = {0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
- 0x10u, 0x0Fu, 0x00u, 0xFFu};
- static const u8 attributes[] = {0x00u, 0x01u, 0x02u, 0x03u, 0x04u,
- 0x05u, 0x06u, 0x07u, 0x08u, 0x09u,
- 0x0Au, 0x0Bu, 0x0Cu, 0x0Du, 0x0Eu,
- 0x0Fu, 0x0Cu, 0x00u, 0x0Fu, 0x08u,
- 0x00u};
+ static const u8 sequencer[] = { 0x00u, 0x00u, 0x03u, 0x00u, 0x02u };
+ static const u8 crtc[]
+ = { 0x5Fu, 0x4Fu, 0x50u, 0x82u, 0x55u, 0x81u, 0xBFu, 0x1Fu, 0x00u,
+ 0x4Fu, 0x0Du, 0x0Eu, 0x00u, 0x00u, 0x00u, 0x00u, 0x9Cu, 0x8Eu,
+ 0x8Fu, 0x28u, 0x1Fu, 0x96u, 0xB9u, 0xA3u, 0xFFu };
+ static const u8 graphics[]
+ = { 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x10u, 0x0Fu, 0x00u, 0xFFu };
+ static const u8 attributes[]
+ = { 0x00u, 0x01u, 0x02u, 0x03u, 0x04u, 0x05u, 0x06u,
+ 0x07u, 0x08u, 0x09u, 0x0Au, 0x0Bu, 0x0Cu, 0x0Du,
+ 0x0Eu, 0x0Fu, 0x0Cu, 0x00u, 0x0Fu, 0x08u, 0x00u };
volatile u16 *video;
volatile u8 *bda;
u16 cell;
diff --git a/mubios/include/bios.h b/mubios/include/bios.h
index 3c8b0dc..979e445 100644
--- a/mubios/include/bios.h
+++ b/mubios/include/bios.h
@@ -43,6 +43,12 @@ void bda_init (void);
void serial_init (void);
void serial_putc (u8 value);
void serial_write (const char *text);
+u16 serial_timer_counter (void);
+u16 serial_status (u16 base);
+u16 serial_configure (u16 base, u8 parameters);
+u8 serial_transmit (u16 base, u8 value, u8 timeout);
+u8 serial_receive (u16 base, u8 *value, u8 timeout);
+int serial_try_receive (u16 base, u8 *value);
u8 cmos_read8 (u8 index);
diff --git a/mubios/services/int13.c b/mubios/services/int13.c
index 033a045..7186377 100644
--- a/mubios/services/int13.c
+++ b/mubios/services/int13.c
@@ -137,9 +137,9 @@ int13_service (struct bios_regs *regs)
head = (u8)(regs->edx >> 8);
if ((regs->edx & 0x00FFu) != BIOS_HARD_DISK || count == 0u
- || get_ata_geometry (&geometry) != 0
- || sector == 0u || sector > geometry.sectors_per_track
- || head >= geometry.heads || cylinder >= geometry.cylinders)
+ || get_ata_geometry (&geometry) != 0 || sector == 0u
+ || sector > geometry.sectors_per_track || head >= geometry.heads
+ || cylinder >= geometry.cylinders)
{
int13_fail (regs);
return;
@@ -152,9 +152,9 @@ int13_service (struct bios_regs *regs)
}
lba = (((u32)cylinder * geometry.heads) + head)
- * geometry.sectors_per_track + (u32)sector - 1u;
- buffer_address
- = ((u32)regs->es << 4) + (u16)regs->ebx;
+ * geometry.sectors_per_track
+ + (u32)sector - 1u;
+ buffer_address = ((u32)regs->es << 4) + (u16)regs->ebx;
if (ata_read_sectors28 (lba, (u16 *)buffer_address, count) != 0)
{
@@ -188,8 +188,7 @@ int13_service (struct bios_regs *regs)
max_head = (u8)(geometry.heads - 1u);
sectors_per_track = (u8)geometry.sectors_per_track;
regs->eax &= 0xFFFF0000u;
- regs->ecx = (regs->ecx & 0xFFFF0000u)
- | (u32)(sectors_per_track & 0x3Fu)
+ regs->ecx = (regs->ecx & 0xFFFF0000u) | (u32)(sectors_per_track & 0x3Fu)
| ((u32)(max_cylinder & 0x00FFu) << 8)
| ((u32)(max_cylinder >> 8) << 14);
regs->edx = (regs->edx & 0xFFFF0000u) | ((u32)max_head << 8) | 1u;
diff --git a/mubios/services/int14.c b/mubios/services/int14.c
index 86019b7..0cf25fb 100644
--- a/mubios/services/int14.c
+++ b/mubios/services/int14.c
@@ -7,55 +7,68 @@
*/
#include "bios.h"
-#include "io.h"
#include "regs.h"
-#define COM1_BASE 0x03F8u
-#define COM_DATA 0x00u
-#define COM_LSR 0x05u
-#define COM_LSR_THRE 0x20u
-#define COM_LSR_IDLE 0x40u
-
-static void
-int14_putc (u8 value)
-{
- while ((io_in8 ((u16)(COM1_BASE + COM_LSR)) & COM_LSR_THRE) == 0u)
- {
- }
-
- io_out8 ((u16)(COM1_BASE + COM_DATA), value);
-}
+#define BIOS_CF 0x0001u
+#define BIOS_INVALID_FUNCTION 0x86u
+#define BDA_COM_PORTS 0x0400u
+#define BDA_COM_TIMEOUTS 0x047Cu
+#define BIOS_COM_PORT_COUNT 4u
void
int14_service (struct bios_regs *regs)
{
u8 function;
u8 status;
+ u8 value;
+ u8 timeout;
+ u16 port;
+ u16 base;
+ u16 result;
+ volatile u16 *ports;
+ volatile u8 *timeouts;
function = (u8)(regs->eax >> 8);
+ port = (u16)regs->edx;
+ ports = (volatile u16 *)BDA_COM_PORTS;
+ timeouts = (volatile u8 *)BDA_COM_TIMEOUTS;
- if (function == 0x00u)
+ /* BIOS numbers COM ports through DX and advertises their bases in BDA. */
+ if (function > 0x03u || port >= BIOS_COM_PORT_COUNT || ports[port] == 0u)
{
- serial_init ();
- regs->eax &= 0xFFFF0000u;
+ regs->eax
+ = (regs->eax & 0xFFFF00FFu) | ((u32)BIOS_INVALID_FUNCTION << 8);
+ regs->flags |= BIOS_CF;
return;
}
- if (function == 0x01u)
- {
- int14_putc ((u8)regs->eax);
- status = (u8)(io_in8 ((u16)(COM1_BASE + COM_LSR))
- | COM_LSR_THRE | COM_LSR_IDLE);
- regs->eax = (regs->eax & 0xFFFF00FFu) | ((u32)status << 8);
- return;
- }
+ base = ports[port];
+ timeout = timeouts[port];
- if (function == 0x03u)
+ switch (function)
{
- status = io_in8 ((u16)(COM1_BASE + COM_LSR));
+ case 0x00u:
+ result = serial_configure (base, (u8)regs->eax);
+ regs->eax = (regs->eax & 0xFFFF0000u) | result;
+ break;
+
+ case 0x01u:
+ status = serial_transmit (base, (u8)regs->eax, timeout);
regs->eax = (regs->eax & 0xFFFF00FFu) | ((u32)status << 8);
- return;
+ break;
+
+ case 0x02u:
+ value = (u8)regs->eax;
+ status = serial_receive (base, &value, timeout);
+ regs->eax = (regs->eax & 0xFFFF0000u) | ((u32)status << 8) | value;
+ break;
+
+ case 0x03u:
+ result = serial_status (base);
+ regs->eax = (regs->eax & 0xFFFF0000u) | result;
+ break;
}
- regs->eax = 0x0100u;
+ /* As in SeaBIOS, timeouts use AH bit 7; CF denotes an invalid call. */
+ regs->flags &= (u16)~BIOS_CF;
}
diff --git a/mubios/services/int15.c b/mubios/services/int15.c
index b9edb76..4100935 100644
--- a/mubios/services/int15.c
+++ b/mubios/services/int15.c
@@ -62,9 +62,9 @@ get_memory_end (void)
}
else
{
- extended_kib = (u16)cmos_read8 (CMOS_EXTENDED_MEMORY_LOW_16M)
- | (u16)((u16)cmos_read8 (CMOS_EXTENDED_MEMORY_HIGH_16M)
- << 8);
+ extended_kib
+ = (u16)cmos_read8 (CMOS_EXTENDED_MEMORY_LOW_16M)
+ | (u16)((u16)cmos_read8 (CMOS_EXTENDED_MEMORY_HIGH_16M) << 8);
if (extended_kib != 0u)
memory_end = E820_MEMORY_BASE + ((u32)extended_kib << 10);
else
diff --git a/mubios/services/int16.c b/mubios/services/int16.c
index cadc1ff..7e480a1 100644
--- a/mubios/services/int16.c
+++ b/mubios/services/int16.c
@@ -23,74 +23,332 @@
#define BIOS_ZF 0x0040u
#define BDA_KBD_HEAD 0x041Au
#define BDA_KBD_TAIL 0x041Cu
-#define BDA_KBD_BUFFER 0x041Eu
#define BDA_KBD_BUFFER_START 0x001Eu
#define BDA_KBD_BUFFER_END 0x003Eu
+#define BDA_KBD_FLAGS 0x0417u
+#define BDA_KBD_EXTENDED_FLAGS 0x0496u
+#define BDA_COM1 0x0400u
+#define KBD_STATUS_PORT 0x0064u
+#define KBD_DATA_PORT 0x0060u
+#define KBD_STATUS_OBF 0x01u
+#define KBD_STATUS_AUX 0x20u
+#define KBD_RELEASE 0x80u
+#define KBD_IGNORED 0xFFFFu
+#define TERMINAL_ESCAPE 0x1Bu
+#define TERMINAL_ESCAPE_CLOCKS 119319u /* 100 ms at the PIT's 1193182 Hz. */
-static u8
-scancode_to_ascii (u8 scancode)
+struct ascii_key
{
- switch (scancode)
+ u8 normal;
+ u8 shifted;
+};
+
+/* Set 1 positions, also used to give terminal ASCII its BIOS scan code. */
+static const struct ascii_key ascii_keys[] = {
+ [0x01] = { 0x1B, 0x1B }, [0x02] = { '1', '!' }, [0x03] = { '2', '@' },
+ [0x04] = { '3', '#' }, [0x05] = { '4', '$' }, [0x06] = { '5', '%' },
+ [0x07] = { '6', '^' }, [0x08] = { '7', '&' }, [0x09] = { '8', '*' },
+ [0x0A] = { '9', '(' }, [0x0B] = { '0', ')' }, [0x0C] = { '-', '_' },
+ [0x0D] = { '=', '+' }, [0x0E] = { '\b', '\b' }, [0x0F] = { '\t', '\t' },
+ [0x10] = { 'q', 'Q' }, [0x11] = { 'w', 'W' }, [0x12] = { 'e', 'E' },
+ [0x13] = { 'r', 'R' }, [0x14] = { 't', 'T' }, [0x15] = { 'y', 'Y' },
+ [0x16] = { 'u', 'U' }, [0x17] = { 'i', 'I' }, [0x18] = { 'o', 'O' },
+ [0x19] = { 'p', 'P' }, [0x1A] = { '[', '{' }, [0x1B] = { ']', '}' },
+ [0x1C] = { '\r', '\r' }, [0x1E] = { 'a', 'A' }, [0x1F] = { 's', 'S' },
+ [0x20] = { 'd', 'D' }, [0x21] = { 'f', 'F' }, [0x22] = { 'g', 'G' },
+ [0x23] = { 'h', 'H' }, [0x24] = { 'j', 'J' }, [0x25] = { 'k', 'K' },
+ [0x26] = { 'l', 'L' }, [0x27] = { ';', ':' }, [0x28] = { '\'', '"' },
+ [0x29] = { '`', '~' }, [0x2B] = { '\\', '|' }, [0x2C] = { 'z', 'Z' },
+ [0x2D] = { 'x', 'X' }, [0x2E] = { 'c', 'C' }, [0x2F] = { 'v', 'V' },
+ [0x30] = { 'b', 'B' }, [0x31] = { 'n', 'N' }, [0x32] = { 'm', 'M' },
+ [0x33] = { ',', '<' }, [0x34] = { '.', '>' }, [0x35] = { '/', '?' },
+ [0x37] = { '*', '*' }, [0x39] = { ' ', ' ' }, [0x4A] = { '-', '-' },
+ [0x4E] = { '+', '+' }, [0x56] = { '\\', '|' }
+};
+
+struct terminal_sequence
+{
+ char text[5];
+ u16 key;
+};
+
+/* CSI and SS3 sequences used by terminals in normal/application mode. */
+static const struct terminal_sequence terminal_sequences[]
+ = { { "[A", 0x48E0u }, { "[B", 0x50E0u }, { "[C", 0x4DE0u },
+ { "[D", 0x4BE0u }, { "OA", 0x48E0u }, { "OB", 0x50E0u },
+ { "OC", 0x4DE0u }, { "OD", 0x4BE0u }, { "[H", 0x47E0u },
+ { "[F", 0x4FE0u }, { "OH", 0x47E0u }, { "OF", 0x4FE0u },
+ { "[1~", 0x47E0u }, { "[4~", 0x4FE0u }, { "[7~", 0x47E0u },
+ { "[8~", 0x4FE0u }, { "[2~", 0x52E0u }, { "[3~", 0x53E0u },
+ { "[5~", 0x49E0u }, { "[6~", 0x51E0u }, { "[Z", 0x0F00u },
+ { "OP", 0x3B00u }, { "OQ", 0x3C00u }, { "OR", 0x3D00u },
+ { "OS", 0x3E00u }, { "[11~", 0x3B00u }, { "[12~", 0x3C00u },
+ { "[13~", 0x3D00u }, { "[14~", 0x3E00u }, { "[15~", 0x3F00u },
+ { "[17~", 0x4000u }, { "[18~", 0x4100u }, { "[19~", 0x4200u },
+ { "[20~", 0x4300u }, { "[21~", 0x4400u }, { "[23~", 0x8500u },
+ { "[24~", 0x8600u } };
+
+static u8 terminal_buffer[16];
+static u8 terminal_count;
+static u8 terminal_escape_pending;
+static u16 terminal_counter;
+static u32 terminal_elapsed;
+static u16 decoded_tail;
+
+static u16
+next_position (u16 position)
+{
+ position += 2u;
+ if (position >= BDA_KBD_BUFFER_END)
+ position = BDA_KBD_BUFFER_START;
+ return position;
+}
+
+static int
+keyboard_full (void)
+{
+ volatile u16 *head = (volatile u16 *)BDA_KBD_HEAD;
+ volatile u16 *tail = (volatile u16 *)BDA_KBD_TAIL;
+
+ return next_position (*tail) == *head;
+}
+
+static void
+enqueue_key (u16 key)
+{
+ volatile u16 *tail = (volatile u16 *)BDA_KBD_TAIL;
+ volatile u16 *slot = (volatile u16 *)(MUBIOS_BDA_BASE + *tail);
+
+ *slot = key;
+ *tail = next_position (*tail);
+ /* This producer stores a complete BIOS key, not an IRQ1 raw scan code. */
+ decoded_tail = *tail;
+}
+
+static u16
+scancode_to_key (u8 scancode)
+{
+ if (scancode < sizeof (ascii_keys) / sizeof (ascii_keys[0])
+ && ascii_keys[scancode].normal != 0u)
+ return (u16)(((u16)scancode << 8) | ascii_keys[scancode].normal);
+
+ if (scancode >= 0x3Bu && scancode <= 0x44u)
+ return (u16)((u16)scancode << 8);
+ if (scancode >= 0x47u && scancode <= 0x53u)
+ return (u16)(((u16)scancode << 8) | 0xE0u);
+ if (scancode == 0x57u || scancode == 0x58u)
+ return (u16)((u16)(scancode + 0x2Eu) << 8);
+
+ /* Modifier make codes and controller responses are not character keys. */
+ return KBD_IGNORED;
+}
+
+static void
+decode_irq_keys (void)
+{
+ volatile u16 *tail = (volatile u16 *)BDA_KBD_TAIL;
+ volatile u16 *slot;
+ u16 read_position;
+ u16 write_position;
+ u16 end;
+ u16 key;
+
+ if (decoded_tail == 0u)
+ decoded_tail = BDA_KBD_BUFFER_START;
+
+ /* IRQ1 currently queues raw set 1 codes; decode only its new entries. */
+ read_position = decoded_tail;
+ write_position = decoded_tail;
+ end = *tail;
+ while (read_position != end)
+ {
+ slot = (volatile u16 *)(MUBIOS_BDA_BASE + read_position);
+ key = scancode_to_key ((u8)(*slot >> 8));
+ if (key != KBD_IGNORED)
+ {
+ slot = (volatile u16 *)(MUBIOS_BDA_BASE + write_position);
+ *slot = key;
+ write_position = next_position (write_position);
+ }
+ read_position = next_position (read_position);
+ }
+ /* Compact away modifier codes before publishing the decoded tail. */
+ *tail = write_position;
+ decoded_tail = write_position;
+}
+
+static void
+poll_keyboard_controller (void)
+{
+ u8 status;
+ u8 scancode;
+ u16 key;
+
+ /* INT 16h C services run with IRQs off, so a blocking read must poll. */
+ while (!keyboard_full ())
{
+ status = io_in8 (KBD_STATUS_PORT);
+ if ((status & KBD_STATUS_OBF) == 0u)
+ return;
+
+ /* The auxiliary device shares the data port, not the keyboard IRQ. */
+ if ((status & KBD_STATUS_AUX) != 0u)
+ return;
+
+ scancode = io_in8 (KBD_DATA_PORT);
+ if ((scancode & KBD_RELEASE) != 0u)
+ continue;
+
+ key = scancode_to_key (scancode);
+ if (key != KBD_IGNORED)
+ enqueue_key (key);
+ }
+}
+
+static u16
+ascii_to_key (u8 character)
+{
+ u16 scan;
+
+ /* Terminals commonly send DEL for the Backspace key. */
+ if (character == 0x7Fu)
+ return 0x0E08u;
+ if (character == '\n')
+ return 0x1C0Au;
+ if (character == 0u)
+ return 0x0300u;
+
+ for (scan = 1; scan < sizeof (ascii_keys) / sizeof (ascii_keys[0]); ++scan)
+ {
+ if (ascii_keys[scan].normal == character
+ || ascii_keys[scan].shifted == character)
+ return (u16)((scan << 8) | character);
+ }
+
+ /* A terminal encodes Ctrl+A through Ctrl+Z as ASCII 1 through 26. */
+ if (character >= 1u && character <= 26u)
+ {
+ for (scan = 1; scan < sizeof (ascii_keys) / sizeof (ascii_keys[0]);
+ ++scan)
+ {
+ if (ascii_keys[scan].normal == (u8)('a' + character - 1u))
+ return (u16)((scan << 8) | character);
+ }
+ }
+
+ switch (character)
+ {
+ case 0x1Cu:
+ return 0x2B1Cu;
+ case 0x1Du:
+ return 0x1B1Du;
case 0x1Eu:
- return 'a';
- case 0x30u:
- return 'b';
- case 0x2Eu:
- return 'c';
- case 0x20u:
- return 'd';
- case 0x12u:
- return 'e';
- case 0x21u:
- return 'f';
- case 0x22u:
- return 'g';
- case 0x23u:
- return 'h';
- case 0x17u:
- return 'i';
- case 0x24u:
- return 'j';
- case 0x25u:
- return 'k';
- case 0x26u:
- return 'l';
- case 0x32u:
- return 'm';
- case 0x31u:
- return 'n';
- case 0x18u:
- return 'o';
- case 0x19u:
- return 'p';
- case 0x10u:
- return 'q';
- case 0x13u:
- return 'r';
+ return 0x071Eu;
case 0x1Fu:
- return 's';
- case 0x14u:
- return 't';
- case 0x16u:
- return 'u';
- case 0x2Fu:
- return 'v';
- case 0x11u:
- return 'w';
- case 0x2Du:
- return 'x';
- case 0x15u:
- return 'y';
- case 0x2Cu:
- return 'z';
- case 0x39u:
- return ' ';
- case 0x1Cu:
- return '\r';
+ return 0x0C1Fu;
default:
+ /* Byte-oriented BIOS input has no scan code for other byte values. */
+ return character;
+ }
+}
+
+static void
+consume_terminal (u8 length)
+{
+ u8 index;
+
+ terminal_count -= length;
+ for (index = 0; index < terminal_count; ++index)
+ terminal_buffer[index] = terminal_buffer[index + length];
+ terminal_escape_pending = 0;
+}
+
+static int
+escape_expired (void)
+{
+ u16 current;
+
+ current = serial_timer_counter ();
+ if (!terminal_escape_pending)
+ {
+ terminal_counter = current;
+ terminal_elapsed = 0;
+ terminal_escape_pending = 1;
return 0;
}
+
+ terminal_elapsed += (u16)(terminal_counter - current);
+ terminal_counter = current;
+ return terminal_elapsed >= TERMINAL_ESCAPE_CLOCKS;
+}
+
+static void
+poll_terminal (void)
+{
+ volatile u16 *com1 = (volatile u16 *)BDA_COM1;
+ u16 base;
+ u16 key;
+ u16 sequence;
+ u8 index;
+ u8 length;
+ u8 prefix;
+ u8 matched;
+
+ base = *com1;
+ /* No console UART is advertised when the BDA COM1 entry is zero. */
+ if (base == 0u)
+ return;
+
+ while (terminal_count < sizeof (terminal_buffer)
+ && serial_try_receive (base, &terminal_buffer[terminal_count]))
+ ++terminal_count;
+
+ while (terminal_count != 0u && !keyboard_full ())
+ {
+ matched = 0;
+ prefix = 0;
+ if (terminal_buffer[0] == TERMINAL_ESCAPE)
+ {
+ for (sequence = 0; sequence < sizeof (terminal_sequences)
+ / sizeof (terminal_sequences[0]);
+ ++sequence)
+ {
+ length = 0;
+ while (terminal_sequences[sequence].text[length] != '\0')
+ ++length;
+
+ for (index = 0; index < length && index + 1u < terminal_count;
+ ++index)
+ {
+ if (terminal_buffer[index + 1u]
+ != (u8)terminal_sequences[sequence].text[index])
+ break;
+ }
+
+ if (index == length)
+ {
+ key = terminal_sequences[sequence].key;
+ consume_terminal ((u8)(length + 1u));
+ matched = 1;
+ break;
+ }
+ if (index + 1u == terminal_count)
+ prefix = 1;
+ }
+
+ /* Keep fragmented sequences; a lone Escape must eventually return.
+ */
+ if (!matched && prefix && !escape_expired ())
+ return;
+ }
+
+ if (!matched)
+ {
+ key = ascii_to_key (terminal_buffer[0]);
+ consume_terminal (1);
+ }
+ enqueue_key (key);
+ }
}
void
@@ -98,48 +356,95 @@ int16_service (struct bios_regs *regs)
{
volatile u16 *head;
volatile u16 *tail;
- volatile u16 *buffer;
+ volatile u16 *slot;
+ volatile u8 *flags;
u8 function;
u16 position;
u16 value;
- u8 scancode;
- u8 ascii;
+ u8 consume;
+ u8 extended;
function = (u8)(regs->eax >> 8);
- if (function != 0x00u && function != 0x01u)
- {
- regs->eax = 0;
- regs->flags |= BIOS_ZF;
- return;
- }
-
head = (volatile u16 *)BDA_KBD_HEAD;
tail = (volatile u16 *)BDA_KBD_TAIL;
- buffer = (volatile u16 *)BDA_KBD_BUFFER;
+ flags = (volatile u8 *)BDA_KBD_FLAGS;
+ decode_irq_keys ();
- if (*head == *tail)
+ switch (function)
{
- regs->eax = 0;
- regs->flags |= BIOS_ZF;
+ case 0x02u:
+ regs->eax = (regs->eax & 0xFFFFFF00u) | flags[0];
+ return;
+ case 0x05u:
+ /* AH=05h returns AL=1 when the BDA ring cannot accept another key. */
+ value = (u16)keyboard_full ();
+ if (value == 0u)
+ enqueue_key ((u16)regs->ecx);
+ regs->eax = (regs->eax & 0xFFFFFF00u) | value;
+ return;
+ case 0x09u:
+ /* Advertise enhanced reads/status, not unimplemented keyboard-ID calls.
+ */
+ regs->eax = (regs->eax & 0xFFFFFF00u) | 0x20u;
+ return;
+ case 0x12u:
+ regs->eax = (regs->eax & 0xFFFF0000u)
+ | ((u32)((flags[1] & 0xF3u)
+ | (*(volatile u8 *)BDA_KBD_EXTENDED_FLAGS & 0x0Cu))
+ << 8)
+ | flags[0];
+ return;
+ case 0x00u:
+ case 0x01u:
+ case 0x10u:
+ case 0x11u:
+ break;
+ default:
+ /* SeaBIOS leaves unsupported INT 16h function registers unchanged. */
return;
}
- position = *head;
- value = buffer[(position - BDA_KBD_BUFFER_START) >> 1];
- scancode = (u8)(value >> 8);
- ascii = scancode_to_ascii (scancode);
+ consume = (u8)((function & 1u) == 0u);
+ extended = function >= 0x10u;
- regs->eax = ((u32)scancode << 8) | ascii;
- regs->flags &= (u16)~BIOS_ZF;
-
- if (function == 0x00u)
+ for (;;)
{
- position += 2;
+ poll_terminal ();
+ poll_keyboard_controller ();
+
+ if (*head != *tail)
+ {
+ position = *head;
+ slot = (volatile u16 *)(MUBIOS_BDA_BASE + position);
+ value = *slot;
+ if (!extended)
+ {
+ /* Legacy reads expose navigation keys with AL=0, not E0h. */
+ if ((u8)value == 0xE0u && (value & 0xFF00u) != 0u)
+ value &= 0xFF00u;
+ else if (value == 0xE00Du || value == 0xE00Au)
+ value = (u16)(0x1C00u | (u8)value);
+ else if (value == 0xE02Fu)
+ value = 0x352Fu;
+ }
+ if ((u8)value == 0xF0u && (value & 0xFF00u) != 0u)
+ value &= 0xFF00u;
- if (position >= BDA_KBD_BUFFER_END)
- position = BDA_KBD_BUFFER_START;
+ regs->eax = (regs->eax & 0xFFFF0000u) | value;
+ if (consume)
+ *head = next_position (position);
+ else
+ regs->flags &= (u16)~BIOS_ZF;
+ return;
+ }
- *head = position;
+ if (!consume)
+ {
+ regs->flags |= BIOS_ZF;
+ return;
+ }
+ /* IRQs stay disabled here; both input sources are polled on every pass.
+ */
}
}