diff --git a/OVMF/OVMF_VARS_4M_d.fd b/OVMF/OVMF_VARS_4M_d.fd index a873cd4..2b8c7a4 100644 Binary files a/OVMF/OVMF_VARS_4M_d.fd and b/OVMF/OVMF_VARS_4M_d.fd differ diff --git a/code/hal/font8x16.h b/code/hal/font8x16.h index ddd46a5..ccc0a0b 100644 --- a/code/hal/font8x16.h +++ b/code/hal/font8x16.h @@ -1,5 +1,5 @@ #pragma once -// font8x16.h - embedded 8x16 VGA bitmap font, printable ASCII (0x20–0x7E) +// font8x16.h - embedded 8x16 VGA bitmap font, printable ASCII (0x20-0x7E) // 95 glyphs × 16 bytes = 1520 bytes total. // Each byte is one scanline; bit 7 = leftmost pixel. #include @@ -135,7 +135,7 @@ static const uint8_t Font8x16[95 * 16] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x60 '`' 0x0C,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - // 0x61–0x7A lowercase - condensed to save space, using same shapes as upper offset by 32 + // 0x61-0x7A lowercase - condensed to save space, using same shapes as upper offset by 32 // 0x61 'a' 0x00,0x00,0x00,0x1C,0x30,0x3C,0x33,0x33,0x6E,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 0x62 'b' diff --git a/code/hal/hal_api.h b/code/hal/hal_api.h index 46ca93d..254af46 100644 --- a/code/hal/hal_api.h +++ b/code/hal/hal_api.h @@ -15,6 +15,9 @@ typedef struct HAL_SCREEN { void (*Scroll)(int lines); void (*PutChar)(char c, int x, int y, uint32_t fg, uint32_t bg); void (*PrintChar)(char c, uint32_t fg, uint32_t bg); + // Hardware mouse cursor: save/restore background and blit sprite + void (*DrawCursor)(int x, int y); + void (*EraseCursor)(void); } HAL_SCREEN; typedef struct HAL_KEYBOARD { @@ -27,6 +30,27 @@ typedef struct HAL_KEYBOARD { uint8_t IrqNumber; } HAL_KEYBOARD; +// HAL_MOUSE – PS/2 auxiliary mouse on IRQ 12. +// Packet format: standard 3-byte PS/2 (buttons, deltaX, deltaY). +// Y is already screen-oriented (positive = down) after ISR inversion. +typedef struct HAL_MOUSE { + // Non-blocking: returns true if a packet is waiting in the ring buffer. + bool (*Poll)(void); + // Non-blocking dequeue. Returns false when buffer is empty. + // buttons: bit0=left, bit1=right, bit2=middle + bool (*Read)(int* dx, int* dy, uint8_t* buttons); + // Callback invoked from ISR context after every completed packet. + void (*SetCallback)(void (*cb)(int dx, int dy, uint8_t buttons)); + // Absolute cursor position, clamped to screen bounds. + void (*GetPosition)(int* x, int* y); + void (*SetPosition)(int x, int y); + // Update the clamping rectangle (call after mode change / resize). + void (*SetBounds)(int w, int h); + // Raw ISR – called by KeIrqDispatch for IRQ 12. + void (*Isr)(void); + uint8_t IrqNumber; // always 12 +} HAL_MOUSE; + typedef struct HAL_DISK { KSTATUS (*ReadSectors)(uint8_t drive, uint64_t lba, uint32_t count, void* buf); @@ -75,6 +99,7 @@ typedef struct HAL_API { void *(*PhysToVirt)(uint64_t physAddr); HAL_SCREEN Screen; HAL_KEYBOARD Keyboard; + HAL_MOUSE Mouse; HAL_DISK Disk; HAL_TIMER Timer; HAL_PCI Pci; diff --git a/code/hal/hal_keyboard.c b/code/hal/hal_keyboard.c index a5f49a7..73156a6 100644 --- a/code/hal/hal_keyboard.c +++ b/code/hal/hal_keyboard.c @@ -1,4 +1,4 @@ -// hal_keyboard.c – PS/2 keyboard, interrupt-driven, 256-entry ring buffer +// hal_keyboard.c - PS/2 keyboard, interrupt-driven, 256-entry ring buffer // // Scan-code set 1 (XT-compatible), as output by the 8042's built-in // translation layer. Supports make/break for all standard keys, Shift / @@ -17,7 +17,7 @@ // ----------------------- // 1. Register KbdIsr with your interrupt dispatcher for IRQ 1. // 2. Send the APIC/PIC EOI *after* KbdIsr returns, in the surrounding -// wrapper – not inside KbdIsr itself. +// wrapper - not inside KbdIsr itself. // 3. Unmask IRQ 1 in the APIC or 8259 before expecting any interrupts. // #include @@ -68,7 +68,7 @@ static volatile bool gNextExtended = false; // previous byte was 0xE0 // rather than caching the value seen at startup. static void (* volatile gCallback)(uint8_t) = NULL; -// -- ASCII translation tables – scan-code set 1, non-extended keys only -------- +// -- ASCII translation tables - scan-code set 1, non-extended keys only -------- // Index = 7-bit make code (SC1_BREAK_BIT cleared). 0 = no printable glyph. // Numpad digits yield their digit characters; a NumLock-aware wrapper can // substitute cursor-movement codes (e.g. HAL_KEY_HOME) when NumLock is clear. @@ -78,58 +78,58 @@ static void (* volatile gCallback)(uint8_t) = NULL; static const char gUnshifted[128] = { /*00*/ 0, /*01*/ 0, // ESC -/*02*/ '1','2','3','4','5','6','7','8','9','0','-','=', // 02–0D +/*02*/ '1','2','3','4','5','6','7','8','9','0','-','=', // 02-0D /*0E*/ '\b', /*0F*/ '\t', -/*10*/ 'q','w','e','r','t','y','u','i','o','p','[',']', // 10–1B +/*10*/ 'q','w','e','r','t','y','u','i','o','p','[',']', // 10-1B /*1C*/ '\n', /*1D*/ 0, // Left Ctrl -/*1E*/ 'a','s','d','f','g','h','j','k','l',';','\'','`', // 1E–29 +/*1E*/ 'a','s','d','f','g','h','j','k','l',';','\'','`', // 1E-29 /*2A*/ 0, // Left Shift /*2B*/ '\\', -/*2C*/ 'z','x','c','v','b','n','m',',','.','/', // 2C–35 +/*2C*/ 'z','x','c','v','b','n','m',',','.','/', // 2C-35 /*36*/ 0, // Right Shift /*37*/ '*', // Numpad * (also PrtSc on some keyboards) /*38*/ 0, // Left Alt /*39*/ ' ', /*3A*/ 0, // Caps Lock -/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1–F10 (3B–44) +/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1-F10 (3B-44) /*45*/ 0, // Num Lock /*46*/ 0, // Scroll Lock -/*47*/ '7','8','9', // Numpad 7–9 -/*4A*/ '-', // Numpad – -/*4B*/ '4','5','6', // Numpad 4–6 +/*47*/ '7','8','9', // Numpad 7-9 +/*4A*/ '-', // Numpad - +/*4B*/ '4','5','6', // Numpad 4-6 /*4E*/ '+', // Numpad + -/*4F*/ '1','2','3', // Numpad 1–3 +/*4F*/ '1','2','3', // Numpad 1-3 /*52*/ '0', // Numpad 0 /*53*/ '.', // Numpad . /*54*/ 0,0,0, /*57*/ 0, // F11 /*58*/ 0, // F12 -/*59*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 59–68 - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 69–78 - 0,0,0,0,0,0,0, // 79–7F +/*59*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 59-68 + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 69-78 + 0,0,0,0,0,0,0, // 79-7F }; static const char gShifted[128] = { /*00*/ 0, /*01*/ 0, // ESC -/*02*/ '!','@','#','$','%','^','&','*','(',')','_','+', // 02–0D +/*02*/ '!','@','#','$','%','^','&','*','(',')','_','+', // 02-0D /*0E*/ '\b', /*0F*/ '\t', -/*10*/ 'Q','W','E','R','T','Y','U','I','O','P','{','}', // 10–1B +/*10*/ 'Q','W','E','R','T','Y','U','I','O','P','{','}', // 10-1B /*1C*/ '\n', /*1D*/ 0, // Left Ctrl -/*1E*/ 'A','S','D','F','G','H','J','K','L',':','"','~', // 1E–29 +/*1E*/ 'A','S','D','F','G','H','J','K','L',':','"','~', // 1E-29 /*2A*/ 0, // Left Shift /*2B*/ '|', -/*2C*/ 'Z','X','C','V','B','N','M','<','>','?', // 2C–35 +/*2C*/ 'Z','X','C','V','B','N','M','<','>','?', // 2C-35 /*36*/ 0, // Right Shift /*37*/ '*', /*38*/ 0, // Left Alt /*39*/ ' ', /*3A*/ 0, // Caps Lock -/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1–F10 +/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1-F10 /*45*/ 0, /*46*/ 0, /*47*/ '7','8','9', @@ -184,10 +184,10 @@ static void KbdIsr(void) { gModifiers = is_break ? (gModifiers & ~MOD_LALT) : (gModifiers | MOD_LALT); return; - case 0x3A: // Caps Lock – toggle on make only + case 0x3A: // Caps Lock - toggle on make only if (!is_break) gModifiers ^= MOD_CAPS; return; - case 0x45: // Num Lock – toggle on make only + case 0x45: // Num Lock - toggle on make only if (!is_break) gModifiers ^= MOD_NUMLOCK; return; default: @@ -250,7 +250,7 @@ static uint8_t KbdReadScancode(void) { // Shift and CapsLock state. Returns 0 for keys with no ASCII mapping // (function keys, arrows, unmodified Ctrl/Alt keys, etc.). // -// CapsLock inverts shift only for alphabetic keys (a–z); digit and symbol +// CapsLock inverts shift only for alphabetic keys (a-z); digit and symbol // keys are always governed solely by the physical Shift state. static char KbdReadChar(void) { uint8_t make = KbdReadScancode(); @@ -282,6 +282,11 @@ static uint32_t KbdGetModifiers(void) { // -- Initializer --------------------------------------------------------------- void HalKeyboardInitialize(HAL_KEYBOARD *out) { + // Enable PS/2 auxiliary port so the mouse driver can claim IRQ 12. + // The 8042 must have an empty input buffer before we can issue commands. + while (__inbyte(PS2_STAT) & 2) { /* wait for input buffer empty */ } + __outbyte(PS2_CMD, 0xA8); // enable aux port + // Drain any stale bytes sitting in the PS/2 output buffer before // enabling interrupts, so the first ISR call sees a clean state. while (__inbyte(PS2_STAT) & 1) { diff --git a/code/hal/hal_main.c b/code/hal/hal_main.c index 6c5b9c4..989c8d9 100644 --- a/code/hal/hal_main.c +++ b/code/hal/hal_main.c @@ -9,6 +9,7 @@ // Declared in other hal_*.c files: void HalScreenInitialize(uintptr_t fb, int w, int h, int stride, HAL_SCREEN* out); void HalKeyboardInitialize(HAL_KEYBOARD* out); +void HalMouseInitialize(HAL_MOUSE* out, int screenW, int screenH); KSTATUS HalTimerInitialize(uintptr_t lapicPhys, uintptr_t hpetPhys, HAL_TIMER *out); void HalPciInitialize(HAL_PCI* out); void HalDiskInitialize(HAL_DISK* out); @@ -339,6 +340,9 @@ __declspec(dllexport) KSTATUS HalInitialize(const HAL_INIT_PARAMS* initParams) { HalApicInitialize(gLapicPhys, gIoapicPhys); HalKeyboardInitialize(&gApi.Keyboard); + HalMouseInitialize(&gApi.Mouse, + (int)initParams->FramebufferWidth, + (int)initParams->FramebufferHeight); KSTATUS ts = HalTimerInitialize(gLapicPhys, gHpetPhys, &gApi.Timer); if (!KSUCCESS(ts)) HalDbgPrint("HAL: HPET unavailable, using fallback\n"); diff --git a/code/hal/hal_mouse.c b/code/hal/hal_mouse.c new file mode 100644 index 0000000..9b947fe --- /dev/null +++ b/code/hal/hal_mouse.c @@ -0,0 +1,245 @@ +// hal_mouse.c - PS/2 auxiliary mouse driver +// +// Sits on IRQ 12, sharing the 8042 controller with the keyboard (IRQ 1). +// hal_keyboard.c has already issued command 0xA8 (enable aux port) by the +// time HalMouseInitialize is called, so we only need to: +// 1. Enable aux interrupts in the 8042 command byte (bit 1, clears bit 5). +// 2. Send the standard mouse init sequence (reset → defaults → enable). +// 3. Wire up the ISR and expose the HAL_MOUSE vtable. +// +// Packet format - standard 3-byte PS/2 mouse (no scroll wheel): +// Byte 0: [YOV][XOV][YS][XS][1][M][R][L] +// Byte 1: delta-X unsigned magnitude (sign in byte-0 bit 4) +// Byte 2: delta-Y unsigned magnitude (sign in byte-0 bit 5); PS/2 Y+ = up +// +// The ISR accumulates the 3-byte packet, converts it to a screen-space delta +// (Y-axis flipped), clamps the absolute position to [0, bounds), enqueues a +// MOUSE_EVENT, and then calls EraseOldCursor / DrawNewCursor via the two +// screen helpers that hal_screen.c exports. +// +// Threading model +// --------------- +// Single-producer (ISR on IRQ 12) / single-consumer ring buffer. +// All ISR-written fields are volatile; on a uniprocessor this is sufficient. +// On SMP, add acquire/release barriers around gHead / gTail. + +#include +#include +#include +#include + +// -- PS/2 port addresses ------------------------------------------------------- +#define PS2_DATA 0x60u +#define PS2_STAT 0x64u +#define PS2_CMD 0x64u + +// -- Ring buffer --------------------------------------------------------------- +#define MOUSE_BUF_SIZE 64u +#define MOUSE_BUF_MASK (MOUSE_BUF_SIZE - 1u) + +typedef struct { + int dx, dy; + uint8_t buttons; // bit0=left, bit1=right, bit2=middle +} MOUSE_EVENT; + +static MOUSE_EVENT gBuf[MOUSE_BUF_SIZE]; +static volatile int gHead = 0; // consumer read pointer +static volatile int gTail = 0; // producer write pointer (updated by ISR) + +// -- Absolute cursor position & screen bounds ---------------------------------- +static volatile int gMouseX = 0; +static volatile int gMouseY = 0; +static volatile int gBoundsW = 1920; +static volatile int gBoundsH = 1080; + +// -- 3-byte packet assembly ---------------------------------------------------- +static volatile uint8_t gPacket[3]; +static volatile int gPacketIdx = 0; // 0, 1, or 2 + +// -- Optional callback (called from ISR) --------------------------------------- +static void (* volatile gCallback)(int dx, int dy, uint8_t buttons) = NULL; + +// -- Cursor helpers provided by hal_screen.c ----------------------------------- +// Declared here; defined in hal_screen.c. +extern void HalScreenDrawCursor(int x, int y); +extern void HalScreenEraseCursor(void); + +// -- 8042 low-level helpers ---------------------------------------------------- +// Wait until the 8042's input buffer is empty (bit 1 of status = 0) so we +// can safely write to PS2_DATA or PS2_CMD. +static void Ps2WaitInput(void) { + int timeout = 100000; + while ((__inbyte(PS2_STAT) & 0x02u) && --timeout) { /* spin */ } +} + +// Wait until the 8042's output buffer has data (bit 0 of status = 1). +static void Ps2WaitOutput(void) { + int timeout = 100000; + while (!(__inbyte(PS2_STAT) & 0x01u) && --timeout) { /* spin */ } +} + +// Route the next write to the auxiliary (mouse) device rather than the keyboard. +static void MouseSendByte(uint8_t byte) { + Ps2WaitInput(); + __outbyte(PS2_CMD, 0xD4u); // "write to mouse" prefix + Ps2WaitInput(); + __outbyte(PS2_DATA, byte); +} + +// Read one byte from the PS/2 output buffer (shared by keyboard and mouse). +static uint8_t MouseRecvByte(void) { + Ps2WaitOutput(); + return __inbyte(PS2_DATA); +} + +// -- Interrupt service routine -------------------------------------------------- +// Called with interrupts disabled by KeIrqDispatch (IRQ 12). +// The dispatcher sends the APIC EOI before this function is invoked. +static void MouseIsr(void) { + uint8_t data = __inbyte(PS2_DATA); + + // Synchronisation guard: the first byte of every packet has bit 3 set. + // If we lose sync, wait for the next valid first byte before assembling. + if (gPacketIdx == 0) { + if (!(data & 0x08u)) return; // not a valid first byte - discard + } + + gPacket[gPacketIdx++] = data; + if (gPacketIdx < 3) return; // packet not yet complete + gPacketIdx = 0; + + // -- Decode flags ---------------------------------------------------------- + uint8_t flags = gPacket[0]; + + // Overflow bits: if either is set the movement data is meaningless. + if (flags & 0xC0u) return; + + uint8_t buttons = flags & 0x07u; + + // Convert unsigned magnitudes + sign bits to signed 9-bit values. + int dx = (int)gPacket[1] - ((flags & 0x10u) ? 256 : 0); + int dy = (int)gPacket[2] - ((flags & 0x20u) ? 256 : 0); + dy = -dy; // PS/2 Y+ = up; screen Y+ = down, so invert. + + // -- Update absolute position (clamped to screen bounds) ------------------ + HalScreenEraseCursor(); + + int nx = gMouseX + dx; + int ny = gMouseY + dy; + if (nx < 0) nx = 0; + if (nx >= gBoundsW) nx = gBoundsW - 1; + if (ny < 0) ny = 0; + if (ny >= gBoundsH) ny = gBoundsH - 1; + gMouseX = nx; + gMouseY = ny; + + HalScreenDrawCursor(nx, ny); + + // -- Enqueue event; silently drop if buffer is full ----------------------- + int next = (gTail + 1) & (int)MOUSE_BUF_MASK; + if (next != gHead) { + gBuf[gTail].dx = dx; + gBuf[gTail].dy = dy; + gBuf[gTail].buttons = buttons; + gTail = next; + } + + // -- Optional callback (raw ISR context) ----------------------------------- + void (*cb)(int, int, uint8_t) = (void (*)(int, int, uint8_t))gCallback; + if (cb) cb(dx, dy, buttons); +} + +// -- Consumer API -------------------------------------------------------------- +static bool MousePoll(void) { + return gHead != gTail; +} + +static bool MouseRead(int* dx, int* dy, uint8_t* buttons) { + if (gHead == gTail) return false; + *dx = gBuf[gHead].dx; + *dy = gBuf[gHead].dy; + *buttons = gBuf[gHead].buttons; + gHead = (gHead + 1) & (int)MOUSE_BUF_MASK; + return true; +} + +static void MouseGetPosition(int* x, int* y) { + *x = gMouseX; + *y = gMouseY; +} + +static void MouseSetPosition(int x, int y) { + gMouseX = x; + gMouseY = y; +} + +static void MouseSetBounds(int w, int h) { + gBoundsW = w; + gBoundsH = h; +} + +static void MouseSetCallback(void (*cb)(int dx, int dy, uint8_t buttons)) { + gCallback = cb; +} + +// -- Initializer --------------------------------------------------------------- +// Called from HalInitialize in hal_main.c after the screen is ready. +// hal_keyboard.c has already sent 0xA8 (enable aux port) to port 0x64. +void HalMouseInitialize(HAL_MOUSE* out, int screenW, int screenH) { + gBoundsW = screenW; + gBoundsH = screenH; + gMouseX = screenW / 2; + gMouseY = screenH / 2; + + // ----------------------------------------------------------------- + // Step 1: Enable aux interrupts in the 8042 command byte. + // - Bit 1 (0x02): enable auxiliary interrupt (drives IRQ 12). + // - Bit 5 (0x20): disables the aux clock when set; clear it. + // ----------------------------------------------------------------- + Ps2WaitInput(); + __outbyte(PS2_CMD, 0x20u); // read current command byte + Ps2WaitOutput(); + uint8_t cmd = __inbyte(PS2_DATA); + cmd |= 0x02u; // enable aux interrupt + cmd &= ~0x20u; // clear "disable aux clock" + Ps2WaitInput(); + __outbyte(PS2_CMD, 0x60u); // write command byte + Ps2WaitInput(); + __outbyte(PS2_DATA, cmd); + + // ----------------------------------------------------------------- + // Step 2: Reset mouse (0xFF). + // Mouse replies: ACK (0xFA), self-test pass (0xAA), ID (0x00). + // ----------------------------------------------------------------- + MouseSendByte(0xFFu); // reset + MouseRecvByte(); // 0xFA - ACK + MouseRecvByte(); // 0xAA - BAT passed + MouseRecvByte(); // 0x00 - device ID + + // ----------------------------------------------------------------- + // Step 3: Set default parameters (sampling rate 100, 4 counts/mm). + // Mouse replies: ACK. + // ----------------------------------------------------------------- + MouseSendByte(0xF6u); // set defaults + MouseRecvByte(); // ACK + + // ----------------------------------------------------------------- + // Step 4: Enable data reporting. + // Mouse will now send 3-byte packets on every movement/button event. + // ----------------------------------------------------------------- + MouseSendByte(0xF4u); // enable data reporting + MouseRecvByte(); // ACK + + // Draw the initial cursor at the centre of the screen. + HalScreenDrawCursor(gMouseX, gMouseY); + + // Populate vtable. + out->Poll = MousePoll; + out->Read = MouseRead; + out->SetCallback = MouseSetCallback; + out->GetPosition = MouseGetPosition; + out->SetPosition = MouseSetPosition; + out->SetBounds = MouseSetBounds; + out->Isr = MouseIsr; + out->IrqNumber = 12; +} diff --git a/code/hal/hal_screen.c b/code/hal/hal_screen.c index 0af77e6..9dcd609 100644 --- a/code/hal/hal_screen.c +++ b/code/hal/hal_screen.c @@ -144,6 +144,95 @@ static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) { } } +// --------------------------------------------------------------------------- +// Mouse cursor – 12×19 monochrome arrow sprite +// 0 = transparent 1 = black outline 2 = white fill +// The hotspot is at (0,0) (top-left pixel = tip of the arrow). +// --------------------------------------------------------------------------- +#define CURSOR_W 12 +#define CURSOR_H 19 + +static const uint8_t gCursorBitmap[CURSOR_H][CURSOR_W] = { + {1,0,0,0,0,0,0,0,0,0,0,0}, + {1,1,0,0,0,0,0,0,0,0,0,0}, + {1,2,1,0,0,0,0,0,0,0,0,0}, + {1,2,2,1,0,0,0,0,0,0,0,0}, + {1,2,2,2,1,0,0,0,0,0,0,0}, + {1,2,2,2,2,1,0,0,0,0,0,0}, + {1,2,2,2,2,2,1,0,0,0,0,0}, + {1,2,2,2,2,2,2,1,0,0,0,0}, + {1,2,2,2,2,2,2,2,1,0,0,0}, + {1,2,2,2,2,2,2,2,2,1,0,0}, + {1,2,2,2,2,2,2,2,2,2,1,0}, + {1,2,2,2,2,2,1,1,1,1,1,1}, + {1,2,2,2,1,2,2,1,0,0,0,0}, + {1,2,2,1,0,1,2,2,1,0,0,0}, + {1,2,1,0,0,0,1,2,2,1,0,0}, + {1,1,0,0,0,0,0,1,2,2,1,0}, + {1,0,0,0,0,0,0,0,1,2,1,0}, + {0,0,0,0,0,0,0,0,0,1,1,0}, + {0,0,0,0,0,0,0,0,0,0,0,0}, +}; + +// Saved framebuffer pixels beneath the current cursor position. +static uint32_t gCursorSavedBg[CURSOR_W * CURSOR_H]; +static int gCursorDrawX = -1; // -1 = cursor not yet drawn +static int gCursorDrawY = -1; + +// Save the background rectangle [x,y,CURSOR_W,CURSOR_H] from the framebuffer. +static void CursorSaveBg(int x, int y) { + for (int row = 0; row < CURSOR_H; row++) { + int fy = y + row; + for (int col = 0; col < CURSOR_W; col++) { + int fx = x + col; + uint32_t px = 0; + if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) + px = gFb[fy * gStride + fx]; + gCursorSavedBg[row * CURSOR_W + col] = px; + } + } +} + +// Restore the background previously saved by CursorSaveBg. +static void CursorRestoreBg(int x, int y) { + for (int row = 0; row < CURSOR_H; row++) { + int fy = y + row; + for (int col = 0; col < CURSOR_W; col++) { + int fx = x + col; + if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) + gFb[fy * gStride + fx] = gCursorSavedBg[row * CURSOR_W + col]; + } + } +} + +// Draw the arrow sprite at (x,y), saving the background first. +// Called from hal_mouse.c via extern declaration. +void HalScreenDrawCursor(int x, int y) { + CursorSaveBg(x, y); + gCursorDrawX = x; + gCursorDrawY = y; + for (int row = 0; row < CURSOR_H; row++) { + int fy = y + row; + for (int col = 0; col < CURSOR_W; col++) { + uint8_t p = gCursorBitmap[row][col]; + if (!p) continue; + int fx = x + col; + if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) { + uint32_t color = (p == 1) ? 0x000000u : 0xFFFFFFu; + gFb[fy * gStride + fx] = color; + } + } + } +} + +// Erase the cursor by restoring the saved background. +void HalScreenEraseCursor(void) { + if (gCursorDrawX < 0) return; // never drawn + CursorRestoreBg(gCursorDrawX, gCursorDrawY); + gCursorDrawX = -1; + gCursorDrawY = -1; +} + // --------------------------------------------------------------------------- // Populate the HAL_SCREEN vtable // --------------------------------------------------------------------------- @@ -160,5 +249,7 @@ void HalScreenInitialize(uintptr_t fb, int w, int h, int stride, out->Scroll = ScreenScroll; out->PutChar = ScreenPutChar; out->PrintChar = ScreenPrintChar; + out->DrawCursor = HalScreenDrawCursor; + out->EraseCursor = HalScreenEraseCursor; ScreenClear(0x000000); } diff --git a/code/kernel/kmain.c b/code/kernel/kmain.c index 9bb2793..d8ad71a 100644 --- a/code/kernel/kmain.c +++ b/code/kernel/kmain.c @@ -382,6 +382,7 @@ extern void KernelMain(BOOT_INFO* bootInfo) { KeIrqRegister(hal->Timer.IrqNumber, hal->Timer.Isr); KeIrqRegister(hal->Keyboard.IrqNumber, hal->Keyboard.Isr); + KeIrqRegister(hal->Mouse.IrqNumber, hal->Mouse.Isr); HalRegisterScheduler(KeSchedulerTick); diff --git a/code/win32/kernel32.c b/code/win32/kernel32.c index d39c580..2d4c619 100644 --- a/code/win32/kernel32.c +++ b/code/win32/kernel32.c @@ -289,7 +289,7 @@ __declspec(dllexport) BOOL WriteFile(HANDLE hFile, const void* buf, DWORD toWrit } // =========================================================================== -// Extended shims – funcs.txt additions +// Extended shims - funcs.txt additions // All functions here return plausible values; no real implementation exists // unless a matching syscall stub is available. // =========================================================================== @@ -341,7 +341,7 @@ __declspec(dllexport) HANDLE GetCurrentThread(void) { __declspec(dllexport) DWORD GetCurrentThreadId(void) { return (DWORD)SysGetCurrentTid(); } __declspec(dllexport) DWORD GetCurrentProcessId(void) { return (DWORD)SysGetCurrentPid(); } -// GetModuleHandleA/W – no PE map; return NULL so callers that check can fail +// GetModuleHandleA/W - no PE map; return NULL so callers that check can fail // gracefully. GetModuleHandleA(NULL) on Windows returns the exe base; we // return a recognisable non-NULL sentinel so code that just passes it to // GetProcAddress doesn't immediately crash. @@ -353,14 +353,14 @@ __declspec(dllexport) HANDLE GetModuleHandleW(const unsigned short* lpModuleName return lpModuleName ? NULL : (HANDLE)(uintptr_t)0x00400000UL; } -// GetProcAddress – no dynamic linker; always fail. +// GetProcAddress - no dynamic linker; always fail. __declspec(dllexport) void* GetProcAddress(HANDLE hModule, const char* lpProcName) { (void)hModule; (void)lpProcName; gLastError = 127; // ERROR_PROC_NOT_FOUND return NULL; } -// LoadLibraryA – no dynamic loader; always fail. +// LoadLibraryA - no dynamic loader; always fail. __declspec(dllexport) HANDLE LoadLibraryA(const char* lpLibFileName) { (void)lpLibFileName; gLastError = 2; // ERROR_FILE_NOT_FOUND @@ -385,7 +385,7 @@ __declspec(dllexport) BOOL TerminateProcess(HANDLE hProcess, unsigned int uExitC while (true) ; } -// STARTUPINFOA – only cb matters for our stub. +// STARTUPINFOA - only cb matters for our stub. typedef struct { DWORD cb; char* lpReserved; @@ -437,7 +437,7 @@ __declspec(dllexport) HANDLE CreateThread(void* lpThreadAttributes, return KH_TO_HANDLE(tid); } -// WaitForSingleObject – blocks on process handles via SysWaitForProcess; +// WaitForSingleObject - blocks on process handles via SysWaitForProcess; // thread and other handle types are not yet waitable so return immediately. __declspec(dllexport) DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds) { (void)dwMilliseconds; @@ -614,7 +614,7 @@ __declspec(dllexport) BOOL ReleaseSemaphore(HANDLE hSemaphore, long lReleaseCoun // Single-threaded: all locks are no-ops. // --------------------------------------------------------------------------- -// Minimal opaque types; callers allocate these from their Windows headers – +// Minimal opaque types; callers allocate these from their Windows headers - // we just need the pointer to be accepted by our stubs. typedef struct { uintptr_t _opaque[5]; } CRITICAL_SECTION; typedef struct { void* _ptr; } CONDITION_VARIABLE; @@ -862,7 +862,7 @@ __declspec(dllexport) BOOL IsDebuggerPresent(void) { } // --------------------------------------------------------------------------- -// RTL Exception / Unwind (SEH infrastructure – all stubs) +// RTL Exception / Unwind (SEH infrastructure - all stubs) // --------------------------------------------------------------------------- typedef struct _RUNTIME_FUNCTION { diff --git a/readme.md b/readme.md index 158622c..c60f2f0 100644 --- a/readme.md +++ b/readme.md @@ -14,7 +14,7 @@ It boots into a shell. - UEFI bootloader - physical memory manager - virtual memory manager with kernel pool -- HAL (screen, keyboard, timer, disk, PCI, APIC) +- HAL (screen, keyboard, mouse, timer, disk, PCI, APIC) - preemptive scheduler with priority levels - processes and threads - VFS with IRPs @@ -85,7 +85,7 @@ currently shipping: AHCI (interrupts), IDE (PIO/DMA), FAT32 (read-only). "2:\data\config.ini" -> drive 2 ``` -drives 0–7. a drive only works if a filesystem registered on it. +drives 0-7. a drive only works if a filesystem registered on it. ## screenshots