161 lines
5.6 KiB
C
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;
|
|
}
|