From 47b6a1eb82e6a107f43816527e0c97e159ccedcf Mon Sep 17 00:00:00 2001 From: Emin Arslan Date: Mon, 4 May 2026 12:15:33 +0300 Subject: [PATCH] feat: add interrupt-driven PS/2 keyboard input Co-authored-by: aider (openrouter/moonshotai/kimi-k2.6) --- kernel_entry.S | 26 ++++++++++++ platform_kernel.c | 102 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 125 insertions(+), 3 deletions(-) diff --git a/kernel_entry.S b/kernel_entry.S index d4f5032..51478f8 100644 --- a/kernel_entry.S +++ b/kernel_entry.S @@ -89,6 +89,32 @@ long_mode_start: hlt jmp 1b +.code64 +.global isr_keyboard +.align 16 +isr_keyboard: + pushq %rax + pushq %rcx + pushq %rdx + pushq %rsi + pushq %rdi + pushq %r8 + pushq %r9 + pushq %r10 + pushq %r11 + cld + call keyboard_handler + popq %r11 + popq %r10 + popq %r9 + popq %r8 + popq %rdi + popq %rsi + popq %rdx + popq %rcx + popq %rax + iretq + .section .note.Xen, "a", @note .align 4 .long 4 diff --git a/platform_kernel.c b/platform_kernel.c index ad96e64..74474e5 100644 --- a/platform_kernel.c +++ b/platform_kernel.c @@ -36,6 +36,10 @@ static inline uint8_t inb(uint16_t port) { return ret; } +static inline void io_wait(void) { + __asm__ volatile ("outb %0, %1" : : "a"((uint8_t)0), "Nd"((uint16_t)0x80)); +} + static void serial_init(void) { outb(0x3F8 + 1, 0x00); outb(0x3F8 + 3, 0x80); @@ -46,6 +50,83 @@ static void serial_init(void) { outb(0x3F8 + 4, 0x0B); } +static uint64_t idt_entries[256][2]; + +static void idt_set_gate(int vector, uint64_t offset, uint16_t selector, uint8_t type_attr) { + uint64_t low = (offset & 0xFFFFULL) | + ((uint64_t)selector << 16) | + ((uint64_t)(type_attr & 0xFF) << 40) | + ((offset & 0xFFFF0000ULL) << 32); + uint64_t high = offset >> 32; + idt_entries[vector][0] = low; + idt_entries[vector][1] = high; +} + +static struct { + uint16_t limit; + uint64_t base; +} __attribute__((packed)) idt_ptr; + +static void pic_remap(void) { + uint8_t a1 = inb(0x21); + uint8_t a2 = inb(0xA1); + + outb(0x20, 0x11); + io_wait(); + outb(0xA0, 0x11); + io_wait(); + outb(0x21, 0x20); + io_wait(); + outb(0xA1, 0x28); + io_wait(); + outb(0x21, 0x04); + io_wait(); + outb(0xA1, 0x02); + io_wait(); + outb(0x21, 0x01); + io_wait(); + outb(0xA1, 0x01); + io_wait(); + + outb(0x21, a1); + outb(0xA1, a2); +} + +static void pic_unmask_keyboard(void) { + uint8_t mask = inb(0x21); + mask &= ~(1 << 1); + outb(0x21, mask); +} + +#define KEYBUF_SIZE 256 +static volatile char keybuf[KEYBUF_SIZE]; +static volatile int keybuf_head = 0; +static volatile int keybuf_tail = 0; + +static const char scancode_map[] = { + 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b', + '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', + 0, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', + 0, '\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, + '*', 0, ' ', 0 +}; + +void keyboard_handler(void) { + uint8_t scancode = inb(0x60); + if (scancode < sizeof(scancode_map)) { + char c = scancode_map[scancode]; + if (c) { + int next = (keybuf_tail + 1) % KEYBUF_SIZE; + if (next != keybuf_head) { + keybuf[keybuf_tail] = c; + keybuf_tail = next; + forth_putchar(c); + } + } + } + outb(0x20, 0x20); +} + void forth_putchar(char c) { vga_putchar(c); while ((inb(0x3F8 + 5) & 0x20) == 0) { } @@ -53,10 +134,12 @@ void forth_putchar(char c) { } int forth_getchar(void) { - if ((inb(0x3F8 + 5) & 1) == 0) { - return FORTH_EOF; + while (keybuf_head == keybuf_tail) { + __asm__ volatile ("pause"); } - return inb(0x3F8); + char c = keybuf[keybuf_head]; + keybuf_head = (keybuf_head + 1) % KEYBUF_SIZE; + return (int)c; } void forth_fflush(void) { @@ -136,7 +219,20 @@ void forth_printf(const char* fmt, ...) { } void kernel_main(void) { + extern void isr_keyboard(void); + serial_init(); + + idt_set_gate(0x21, (uint64_t)isr_keyboard, 0x08, 0x8E); + idt_ptr.limit = sizeof(idt_entries) - 1; + idt_ptr.base = (uint64_t)idt_entries; + __asm__ volatile ("lidt %0" : : "m"(idt_ptr)); + + pic_remap(); + pic_unmask_keyboard(); + + __asm__ volatile ("sti"); + forth_printf("Kernel started\n"); forth_run(); forth_panic();