// hal_keyboard.c - PS/2 keyboard, interrupt-driven, with live state tracking #include #include #include #include // -- Port addresses ------------------------------------------------------------ #define PS2_DATA 0x60u #define PS2_STAT 0x64u #define PS2_CMD 0x64u // -- Ring buffer (for ASCII/typing) ------------------------------------------- #define KBD_BUF_SIZE 256u #define KBD_BUF_MASK (KBD_BUF_SIZE - 1u) static uint8_t gBuf[KBD_BUF_SIZE]; static volatile int gHead = 0; static volatile int gTail = 0; // -- Deferred callback queue --------------------------------------------------- // The hard IRQ path updates the key state and stores completed make codes. // Optional client callbacks are delivered later from the kernel-queued DPC, // never directly from DIRQL/ISR context. #define KBD_DPC_BUF_SIZE 128u #define KBD_DPC_BUF_MASK (KBD_DPC_BUF_SIZE - 1u) static uint8_t gDpcBuf[KBD_DPC_BUF_SIZE]; static volatile int gDpcHead = 0; static volatile int gDpcTail = 0; // -- Live Key State (for GetAsyncKeyState) ------------------------------------ // These arrays track whether a key is currently held down. static volatile uint8_t gKeyState[128]; // Standard scan codes static volatile uint8_t gKeyStateExt[128]; // Extended (E0) scan codes // -- Scan-code set 1 constants ------------------------------------------------- #define SC1_BREAK_BIT 0x80u #define SC1_MAKE_MASK 0x7Fu #define SC1_EXT_PREFIX 0xE0u // -- Modifier flags ------------------------------------------------------------ #define MOD_LSHIFT (1u << 0) #define MOD_RSHIFT (1u << 1) #define MOD_LCTRL (1u << 2) #define MOD_RCTRL (1u << 3) #define MOD_LALT (1u << 4) #define MOD_RALT (1u << 5) #define MOD_CAPS (1u << 6) #define MOD_NUMLOCK (1u << 7) #define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT) #define MOD_CTRL (MOD_LCTRL | MOD_RCTRL) #define MOD_ALT (MOD_LALT | MOD_RALT) static volatile uint32_t gModifiers = 0; static volatile bool gNextExtended = false; static void (* volatile gCallback)(uint8_t) = NULL; // -- ASCII Translation Tables -------------------------------------------------- static const char gUnshifted[128] = { 0, 0, '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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.' }; static const char gShifted[128] = { 0, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.' }; // -- Deferred callback delivery ------------------------------------------------ static void KbdDpc(void) { for (;;) { if (gDpcHead == gDpcTail) break; uint8_t make = gDpcBuf[gDpcHead]; gDpcHead = (gDpcHead + 1) & (int)KBD_DPC_BUF_MASK; void (*cb)(uint8_t) = (void (*)(uint8_t))gCallback; if (cb) cb(make); } } // -- ISR: The Engine ----------------------------------------------------------- void KbdIsr(void) { uint8_t sc = __inbyte(PS2_DATA); if (sc == SC1_EXT_PREFIX) { gNextExtended = true; return; } bool extended = gNextExtended; bool is_break = (sc & SC1_BREAK_BIT) != 0; uint8_t make = sc & SC1_MAKE_MASK; gNextExtended = false; // Update the live state arrays if (extended) { gKeyStateExt[make] = is_break ? 0 : 1; } else { gKeyState[make] = is_break ? 0 : 1; } // Update modifiers if (!extended) { switch (make) { case 0x2A: gModifiers = is_break ? (gModifiers & ~MOD_LSHIFT) : (gModifiers | MOD_LSHIFT); break; case 0x36: gModifiers = is_break ? (gModifiers & ~MOD_RSHIFT) : (gModifiers | MOD_RSHIFT); break; case 0x1D: gModifiers = is_break ? (gModifiers & ~MOD_LCTRL) : (gModifiers | MOD_LCTRL); break; case 0x38: gModifiers = is_break ? (gModifiers & ~MOD_LALT) : (gModifiers | MOD_LALT); break; case 0x3A: if (!is_break) gModifiers ^= MOD_CAPS; break; case 0x45: if (!is_break) gModifiers ^= MOD_NUMLOCK; break; } } else { switch (make) { case 0x1D: gModifiers = is_break ? (gModifiers & ~MOD_RCTRL) : (gModifiers | MOD_RCTRL); break; case 0x38: gModifiers = is_break ? (gModifiers & ~MOD_RALT) : (gModifiers | MOD_RALT); break; } } // Buffer management (only for 'make' codes) if (!is_break) { int next = (gTail + 1) & (int)KBD_BUF_MASK; if (next != gHead) { gBuf[gTail] = make; gTail = next; } // Queue optional callback delivery to the DPC path. Keep a separate // callback queue so the public ReadScancode ring remains independent. if (gCallback) { int dpcNext = (gDpcTail + 1) & (int)KBD_DPC_BUF_MASK; if (dpcNext != gDpcHead) { gDpcBuf[gDpcTail] = make; gDpcTail = dpcNext; } } } } // -- Public API ---------------------------------------------------------------- // Use this inside your GetAsyncKeyState implementation! bool KbdIsKeyDown(uint8_t scancode, bool extended) { if (extended) return gKeyStateExt[scancode & 0x7F] != 0; return gKeyState[scancode & 0x7F] != 0; } static uint8_t KbdPoll(void) { return (gHead == gTail) ? 0 : gBuf[gHead]; } static uint8_t KbdReadScancode(void) { while (gHead == gTail) { _mm_pause(); } uint8_t sc = gBuf[gHead]; gHead = (gHead + 1) & (int)KBD_BUF_MASK; return sc; } static char KbdReadChar(void) { uint8_t make = KbdReadScancode(); if (make >= 128) return 0; uint32_t mods = gModifiers; bool shifted = (mods & MOD_SHIFT) != 0; if (mods & MOD_CAPS) { char base = gUnshifted[make]; if (base >= 'a' && base <= 'z') shifted = !shifted; } return shifted ? gShifted[make] : gUnshifted[make]; } static uint32_t KbdGetModifiers(void) { return gModifiers; } static void KbdSetCallback(void (*cb)(uint8_t scancode)) { gCallback = cb; } void HalKeyboardInitialize(HAL_KEYBOARD *out) { while (__inbyte(PS2_STAT) & 2); __outbyte(PS2_CMD, 0xA8); while (__inbyte(PS2_STAT) & 1) { __inbyte(PS2_DATA); } out->Poll = KbdPoll; out->ReadScancode = KbdReadScancode; out->ReadChar = KbdReadChar; out->GetModifiers = KbdGetModifiers; out->SetCallback = KbdSetCallback; out->IsKeyDown = KbdIsKeyDown; out->Isr = KbdIsr; out->Dpc = KbdDpc; out->IrqNumber = 1; }