Files
nt-lite/code/hal/hal_keyboard.c
T

161 lines
5.6 KiB
C

// hal_keyboard.c - PS/2 keyboard, interrupt-driven, with live state tracking
#include <stdint.h>
#include <stdbool.h>
#include <intrin.h>
#include <hal_api.h>
// -- 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;
// -- 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','.'
};
// -- 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;
}
void (*cb)(uint8_t) = (void (*)(uint8_t))gCallback;
if (cb) cb(make);
}
}
// -- 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];
}
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->IsKeyDown = KbdIsKeyDown;
out->Isr = KbdIsr;
out->IrqNumber = 1;
}