// HAL screen support. This file owns GOP framebuffer drawing before a runtime display provider takes over. // The 8x16 bitmap font is a standard VGA/PC font. We embed just the // printable ASCII range (0x20–0x7E). Each glyph is 16 bytes; bit 7 of // each byte is the leftmost pixel. #include #include #include #include #include static uint32_t* gFb = (uint32_t *)0; static uint32_t* gBackbuffer = (uint32_t *)0; static int gWidth = 0; static int gHeight = 0; static int gStride = 0; // pixels per row static int gCursorX = 0, gCursorY = 0; #define FONT_W 8 #define FONT_H 16 #define SCREEN_DEFAULT_FG 0xCCCCCCu #define SCREEN_DEFAULT_BG 0x012456u // 8x16 VGA font - printable ASCII only (glyph 0 = space = 0x20). #include // see font8x16.h // Internals. static inline uint32_t* ScreenBackbufferPixel(int x, int y) { return gBackbuffer ? (gBackbuffer + y * gStride + x) : (gFb + y * gStride + x); } // Updates pixel Raw. static inline void PutPixelRaw(int x, int y, uint32_t color) { if ((unsigned)x >= (unsigned)gWidth || (unsigned)y >= (unsigned)gHeight) return; ScreenBackbufferPixel(x, y)[0] = color; gFb[y * gStride + x] = color; } // Implements the screen Flush Rows operation. static void ScreenFlushRows(int startY, int rows) { if (!gBackbuffer || rows <= 0) return; if (startY < 0) { rows += startY; startY = 0; } if (startY >= gHeight || rows <= 0) return; if (startY + rows > gHeight) rows = gHeight - startY; size_t rowBytes = (size_t)gStride * sizeof(uint32_t); memcpy(gFb + startY * gStride, gBackbuffer + startY * gStride, (size_t)rows * rowBytes); } // HAL_SCREEN implementations. static void ScreenClear(uint32_t color) { for (int y = 0; y < gHeight; y++) for (int x = 0; x < gWidth; x++) ScreenBackbufferPixel(x, y)[0] = color; ScreenFlushRows(0, gHeight); if (!gBackbuffer) { // ScreenFlushRows is a no-op without a backbuffer. for (int y = 0; y < gHeight; y++) for (int x = 0; x < gWidth; x++) gFb[y * gStride + x] = color; } gCursorX = 0; gCursorY = 0; } // Implements the screen Put Pixel operation. static void ScreenPutPixel(int x, int y, uint32_t color) { PutPixelRaw(x, y, color); } // Implements the screen Blit operation. static void ScreenBlit(int x, int y, int w, int h, const uint32_t* pixels) { // Calculate clipping bounds. int startX = (x < 0) ? 0 : x; int startY = (y < 0) ? 0 : y; int endX = (x + w > gWidth) ? gWidth : (x + w); int endY = (y + h > gHeight) ? gHeight : (y + h); int drawW = endX - startX; int drawH = endY - startY; // If the image is entirely off-screen, do nothing. if (drawW <= 0 || drawH <= 0) { return; } // Adjust the source pointer if the image was clipped on the top or left. int srcOffsetX = startX - x; int srcOffsetY = startY - y; // Calculate bytes to copy per row. size_t bytesPerRow = (size_t)drawW * sizeof(uint32_t); for (int row = 0; row < drawH; row++) { uint32_t* shadow = ScreenBackbufferPixel(startX, startY + row); uint32_t* fb = gFb + ((startY + row) * gStride) + startX; const uint32_t* src = pixels + ((srcOffsetY + row) * w) + srcOffsetX; memcpy(shadow, src, bytesPerRow); memcpy(fb, src, bytesPerRow); } } // Implements the screen Get Size operation. static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; } // Implements the screen Get Framebuffer operation. static KSTATUS ScreenGetFramebuffer(HAL_FRAMEBUFFER_INFO* outInfo) { if (!outInfo || !gFb || gWidth <= 0 || gHeight <= 0 || gStride <= 0) return STATUS_INVALID_PARAMETER; outInfo->Base = (uintptr_t)gFb; outInfo->Width = (uint32_t)gWidth; outInfo->Height = (uint32_t)gHeight; outInfo->Stride = (uint32_t)gStride; outInfo->Format = 1; // X8R8G8B8 return STATUS_SUCCESS; } // Implements the screen Scroll operation. static void ScreenScroll(int lines) { if (lines <= 0) return; if (lines >= gHeight) { ScreenClear(SCREEN_DEFAULT_BG); return; } size_t rowBytes = (size_t)gStride * sizeof(uint32_t); uint32_t* shadow = gBackbuffer ? gBackbuffer : gFb; memmove(shadow, shadow + lines * gStride, (size_t)(gHeight - lines) * rowBytes); // Fill the revealed rows with the console background. Do not memset here: // the background color is not zero. uint32_t* fill = shadow + (gHeight - lines) * gStride; for (int y = 0; y < lines; y++) { for (int x = 0; x < gWidth; x++) fill[y * gStride + x] = SCREEN_DEFAULT_BG; } ScreenFlushRows(0, gHeight); } // Implements the screen Put Char operation. static void ScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) { const uint8_t* glyph = Font8x16 + ((uint8_t)c * 16); for (int row = 0; row < 16; row++) { uint8_t bits = glyph[row]; for (int col = 0; col < 8; col++) { uint32_t color = (bits & (0x80u >> col)) ? fg : bg; PutPixelRaw(x + col, y + row, color); } } } // Implements the screen Print Char operation. static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) { int cols = gWidth / FONT_W; int rows = gHeight / FONT_H; if (c == '\n') { gCursorX = 0; gCursorY++; } else if (c == '\r') { gCursorX = 0; } else if (c == '\b') { if (gCursorX > 0) { gCursorX--; } else if (gCursorY > 0) { gCursorY--; gCursorX = cols - 1; } } else { ScreenPutChar(c, gCursorX * FONT_W, gCursorY * FONT_H, fg, bg); if (++gCursorX >= cols) { gCursorX = 0; gCursorY++; } } if (gCursorY >= rows) { ScreenScroll(FONT_H); gCursorY = rows - 1; } } // 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 16 #define CURSOR_H 25 // Each entry is 0xAARRGGBB stored as a uint32_t. // Pixels with brightness < 24 are fully transparent. static const unsigned int gCursorPixels[CURSOR_H][CURSOR_W] = { { 0x7F575757, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFAFAFA, 0x855D5D5D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFEFEFE, 0xFFFFFFFF, 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFFFFFF, 0x8A626262, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8D656565, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x8E666666, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x91696969, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x946C6C6C, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x956D6D6D, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9B737373, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0x9D757575, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFFFFFFF, 0xA0787878, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFDFDFD, 0xFFFDFDFD, 0xFFFCFCFC, 0xFFF9F9F9, 0xFFFFFFFF, 0xA1797979, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xAE868686, 0x00000000 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFFFFFF, 0xFDD5D5D5, 0xF4CCCCCC, 0xF4CCCCCC, 0xF4CCCCCC, 0xF3CBCBCB, 0xF1C9C9C9, 0xFAD2D2D2, 0x7E565656 }, { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFEFEFE, 0xFFFCFCFC, 0xFFEFEFEF, 0xFFFCFCFC, 0xFFFFFFFF, 0x9A727272, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x7F575757, 0x8F676767, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFE0E0E0, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFEFEFE, 0xFFFDFDFD, 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0xFFEBEBEB, 0xFFFFFFFF, 0xFFFFFFFF, 0x81595959, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFDFDFD, 0xFFFFFFFF, 0x8C646464, 0x00000000, 0x00000000, 0x00000000, 0xB78F8F8F, 0xFFFFFFFF, 0xFFFEFEFE, 0xECC4C4C4, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0xFFFFFFFF, 0x90686868, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x50282828, 0xFFF9F9F9, 0xFFFFFFFF, 0xFFFFFFFF, 0x623A3A3A, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x865E5E5E, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xD8B0B0B0, 0xFFFFFFFF, 0xFFFFFFFF, 0xCDA5A5A5, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x6C444444, 0xFFFFFFFF, 0xFFFCFCFC, 0xFFF4F4F4, 0x4D252525, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0xF2CACACA, 0xFFFFFFFF, 0xFFFFFFFF, 0x99717171, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, { 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x8E666666, 0xEFC7C7C7, 0x7D555555, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000 }, }; // 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 = ScreenBackbufferPixel(fx, fy)[0]; 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) { uint32_t px = gBackbuffer ? ScreenBackbufferPixel(fx, fy)[0] : gCursorSavedBg[row * CURSOR_W + col]; gFb[fy * gStride + fx] = px; } } } } // Alpha-blend a cursor pixel over a framebuffer pixel. // Framebuffer format: 0x00RRGGBB (high byte ignored / always 0). // Cursor pixel format: 0xAARRGGBB. static inline uint32_t BlendPixel(uint32_t dst, uint32_t src) { uint8_t a = (uint8_t)(src >> 24); if (a == 0) return dst; if (a == 255) return src & 0x00FFFFFFu; uint8_t sr = (uint8_t)(src >> 16); uint8_t sg = (uint8_t)(src >> 8); uint8_t sb = (uint8_t)(src ); uint8_t dr = (uint8_t)(dst >> 16); uint8_t dg = (uint8_t)(dst >> 8); uint8_t db = (uint8_t)(dst ); uint8_t na = 255 - a; uint8_t or_ = (uint8_t)((a * sr + na * dr) >> 8); uint8_t og = (uint8_t)((a * sg + na * dg) >> 8); uint8_t ob = (uint8_t)((a * sb + na * db) >> 8); return ((uint32_t)or_ << 16) | ((uint32_t)og << 8) | ob; } // 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++) { uint32_t src = gCursorPixels[row][col]; if ((src >> 24) == 0) continue; // fully transparent, skip int fx = x + col; if ((unsigned)fx < (unsigned)gWidth && (unsigned)fy < (unsigned)gHeight) { uint32_t dst = ScreenBackbufferPixel(fx, fy)[0]; gFb[fy * gStride + fx] = BlendPixel(dst, src); } } } } // Erase the cursor by restoring the saved background. void HalScreenEraseCursor(void) { if (gCursorDrawX < 0) return; // never drawn CursorRestoreBg(gCursorDrawX, gCursorDrawY); gCursorDrawX = -1; gCursorDrawY = -1; } // Writes one HAL output record through the boot framebuffer text console. static void HalScreenWriteOutputRecord(void* context, const HAL_OUTPUT_RECORD* record) { (void)context; if (!record || !record->Buffer) return; for (size_t i = 0; i < record->Length; i++) ScreenPrintChar(record->Buffer[i], record->Foreground, record->Background); } // Registers the boot framebuffer text endpoint. void HalScreenRegisterOutputEndpoint(HAL_OUTPUT_ROUTER* output) { if (!output || !output->RegisterEndpoint || !gFb || gWidth <= 0 || gHeight <= 0) return; HAL_OUTPUT_ENDPOINT endpoint; endpoint.Name = "boot-framebuffer"; endpoint.StreamMask = HAL_OUTPUT_STREAM_ALL; endpoint.Capabilities = HAL_OUTPUT_ENDPOINT_PANIC_SAFE; endpoint.Write = HalScreenWriteOutputRecord; endpoint.Context = 0; uint32_t endpointId = HAL_OUTPUT_ENDPOINT_INVALID; if (KSUCCESS(output->RegisterEndpoint(&endpoint, &endpointId))) output->BootFramebufferEndpointId = endpointId; } // Populate the HAL_SCREEN vtable. void HalScreenInitialize(void* (*AllocatePool)(size_t size), uintptr_t fb, int w, int h, int stride, HAL_SCREEN* out) { gFb = (uint32_t *)fb; gWidth = w; gHeight = h; gStride = stride; size_t backbufferPixels = (size_t)gStride * (size_t)gHeight; size_t backbufferBytes = backbufferPixels * sizeof(uint32_t); if (gStride > 0 && gHeight > 0 && backbufferPixels / (size_t)gStride == (size_t)gHeight && backbufferBytes / sizeof(uint32_t) == backbufferPixels) { gBackbuffer = (uint32_t*)AllocatePool(backbufferBytes); } out->Clear = ScreenClear; out->PutPixel = ScreenPutPixel; out->Blit = ScreenBlit; out->GetSize = ScreenGetSize; out->GetFramebuffer = ScreenGetFramebuffer; out->Scroll = ScreenScroll; out->PutChar = ScreenPutChar; out->PrintChar = ScreenPrintChar; out->DrawCursor = HalScreenDrawCursor; out->EraseCursor = HalScreenEraseCursor; ScreenClear(SCREEN_DEFAULT_BG); }