/* Keyboard Driver for CubeCactusOS
* Handles keyboard interrupts and scancode translation
*/
#include "../kernel/kernel.h"
extern void outb(uint16_t port, uint8_t value);
extern uint8_t inb(uint16_t port);
#define KEYBOARD_DATA_PORT 0x60
#define KEYBOARD_STATUS_PORT 0x64
#define KEYBOARD_BUFFER_SIZE 256
/* US QWERTY keyboard layout scancode map */
static const char scancode_to_char[128] = {
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, /* Control */
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`',
0, /* Left shift */
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/',
0, /* Right shift */
'*',
0, /* Alt */
' ', /* Space bar */
0, /* Caps lock */
0, /* F1 */
0, 0, 0, 0, 0, 0, 0, 0,
0, /* F10 */
0, /* Num lock*/
0, /* Scroll Lock */
0, /* Home key */
0, /* Up Arrow */
0, /* Page Up */
'-',
0, /* Left Arrow */
0,
0, /* Right Arrow */
'+',
0, /* End key*/
0, /* Down Arrow */
0, /* Page Down */
0, /* Insert Key */
0, /* Delete Key */
0, 0, 0,
0, /* F11 Key */
0, /* F12 Key */
0, /* All other keys are undefined */
};
/* Shifted characters */
static const char scancode_to_char_shifted[128] = {
0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
0, /* Control */
'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~',
0, /* Left shift */
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?',
0, /* Right shift */
'*',
0, /* Alt */
' ', /* Space bar */
0, /* Caps lock */
};
/* Circular keyboard input buffer */
static char keyboard_buffer[KEYBOARD_BUFFER_SIZE];
static volatile int buffer_head = 0;
static volatile int buffer_tail = 0;
static volatile int shift_pressed = 0;
/* Interrupt mode flag */
static int interrupt_mode = 0;
/* Check if buffer has data */
int keyboard_has_data() {
return buffer_head != buffer_tail;
}
/* Read character from buffer */
char keyboard_getchar() {
if (!keyboard_has_data()) {
return 0;
}
char c = keyboard_buffer[buffer_tail];
buffer_tail = (buffer_tail + 1) % KEYBOARD_BUFFER_SIZE;
return c;
}
/* Add character to buffer */
static void keyboard_buffer_add(char c) {
int next_head = (buffer_head + 1) % KEYBOARD_BUFFER_SIZE;
/* Check for buffer overflow */
if (next_head == buffer_tail) {
return; /* Buffer full, drop character */
}
keyboard_buffer[buffer_head] = c;
buffer_head = next_head;
}
/* Keyboard interrupt handler */
void keyboard_interrupt_handler() {
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
/* Handle key release (scancode with high bit set) */
if (scancode & 0x80) {
scancode &= 0x7F; /* Clear release bit */
/* Track shift key release */
if (scancode == 0x2A || scancode == 0x36) {
shift_pressed = 0;
}
return;
}
/* Track shift key press */
if (scancode == 0x2A || scancode == 0x36) {
shift_pressed = 1;
return;
}
/* Translate scancode to character */
char c;
if (shift_pressed && scancode < 128) {
c = scancode_to_char_shifted[scancode];
} else if (scancode < 128) {
c = scancode_to_char[scancode];
} else {
return; /* Unknown scancode */
}
if (c != 0) {
keyboard_buffer_add(c);
}
}
/* Initialize keyboard */
void keyboard_init() {
buffer_head = 0;
buffer_tail = 0;
shift_pressed = 0;
interrupt_mode = 0;
/* Buffer is already zero-initialized in BSS */
}
/* Enable interrupt mode after IDT is set up */
void keyboard_enable_interrupts() {
interrupt_mode = 1;
}
/* IRQ handler called from interrupt */
void keyboard_irq_handler() {
uint8_t scancode = inb(KEYBOARD_DATA_PORT);
/* Handle key release */
if (scancode & 0x80) {
scancode &= 0x7F;
if (scancode == 0x2A || scancode == 0x36) {
shift_pressed = 0;
}
return;
}
/* Track shift */
if (scancode == 0x2A || scancode == 0x36) {
shift_pressed = 1;
return;
}
/* Translate and buffer */
if (scancode < 128) {
char c = shift_pressed ? scancode_to_char_shifted[scancode] : scancode_to_char[scancode];
if (c != 0) {
keyboard_buffer_add(c);
}
}
}
/* Wait for and read a character (blocking) - SIMPLE VERSION */
char keyboard_getchar_blocking() {
static int local_shift = 0;
while (1) {
/* Simple busy wait with yield */
for (volatile int delay = 0; delay < 10000; delay++);
/* Check status carefully */
uint8_t status;
__asm__ __volatile__("inb $0x64, %0" : "=a"(status));
if (!(status & 0x01)) {
continue; /* No data available */
}
/* Read scancode carefully */
uint8_t scancode;
__asm__ __volatile__("inb $0x60, %0" : "=a"(scancode));
/* Ignore bad scancodes */
if (scancode == 0 || scancode == 0xFF) {
continue;
}
/* Key release */
if (scancode & 0x80) {
if ((scancode & 0x7F) == 0x2A || (scancode & 0x7F) == 0x36) {
local_shift = 0;
}
continue;
}
/* Shift press */
if (scancode == 0x2A || scancode == 0x36) {
local_shift = 1;
continue;
}
/* Translate to char */
if (scancode < 128) {
char c = local_shift ? scancode_to_char_shifted[scancode] : scancode_to_char[scancode];
if (c != 0) {
return c;
}
}
}
}