// hal_screen.c - GOP framebuffer screen driver with embedded 8x16 font // // 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 static uint32_t* gFb = (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 // --------------------------------------------------------------------------- // 8x16 VGA font - printable ASCII only (glyph 0 = space = 0x20) // --------------------------------------------------------------------------- #include "font8x16.h" // see font8x16.h below // --------------------------------------------------------------------------- // Internals // --------------------------------------------------------------------------- static inline void PutPixelRaw(int x, int y, uint32_t color) { if ((unsigned)x < (unsigned)gWidth && (unsigned)y < (unsigned)gHeight) gFb[y * gStride + x] = color; } // --------------------------------------------------------------------------- // HAL_SCREEN implementations // --------------------------------------------------------------------------- static void ScreenClear(uint32_t color) { for (int y = 0; y < gHeight; y++) for (int x = 0; x < gWidth; x++) gFb[y * gStride + x] = color; gCursorX = 0; gCursorY = 0; } static void ScreenPutPixel(int x, int y, uint32_t color) { PutPixelRaw(x, y, color); } 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); // If the width we are drawing exactly matches the physical screen stride, // AND it matches the original source image width, memory is perfectly contiguous. if (drawW == gStride && drawW == w) { uint32_t* dst = gFb + (startY * gStride) + startX; const uint32_t* src = pixels + (srcOffsetY * w) + srcOffsetX; memcpy(dst, src, bytesPerRow * drawH); return; } // Copy row-by-row for (int row = 0; row < drawH; row++) { // Destination in the actual framebuffer (accounting for stride) uint32_t* dst = gFb + ((startY + row) * gStride) + startX; // Source from the provided pixel buffer const uint32_t* src = pixels + ((srcOffsetY + row) * w) + srcOffsetX; memcpy(dst, src, bytesPerRow); } } static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; } static void ScreenScroll(int lines) { if (lines <= 0 || lines >= gHeight) { ScreenClear(0); return; } int rowBytes = gStride * sizeof(uint32_t); memmove(gFb, gFb + lines * gStride, (size_t)(gHeight - lines) * rowBytes); // Clear the revealed rows at the bottom. memset(gFb + (gHeight - lines) * gStride, 0, (size_t)lines * rowBytes); } static void ScreenPutChar(char c, int x, int y, uint32_t fg, uint32_t bg) { if ((unsigned char)c < 0x20 || (unsigned char)c > 0x7E) c = '?'; const uint8_t* glyph = Font8x16 + ((unsigned char)c - 0x20) * 16; for (int row = 0; row < 16; row++) { uint8_t bits = glyph[row]; for (int col = 0; col < 8; col++) { uint32_t color = (bits & (1 << col)) ? fg : bg; // i don't know why, i don't want to know why // i shouldn't have to wonder why but the font is backwards unless we do this terriblness PutPixelRaw(x + col, y + row, color); } } } static void ScreenPrintChar(char c, uint32_t fg, uint32_t bg) { int cols = gWidth / FONT_W; int rows = gHeight / FONT_H; // Mirror to serial while ((__inbyte(0x3FD) & 0x20) == 0); if (c == '\n') __outbyte(0x3F8, '\r'); __outbyte(0x3F8, c); 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 = 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]; } } } // 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 = &gFb[fy * gStride + fx]; *dst = 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; } // --------------------------------------------------------------------------- // Populate the HAL_SCREEN vtable // --------------------------------------------------------------------------- void HalScreenInitialize(uintptr_t fb, int w, int h, int stride, HAL_SCREEN* out) { gFb = (uint32_t *)fb; gWidth = w; gHeight = h; gStride = stride; out->Clear = ScreenClear; out->PutPixel = ScreenPutPixel; out->Blit = ScreenBlit; out->GetSize = ScreenGetSize; out->Scroll = ScreenScroll; out->PutChar = ScreenPutChar; out->PrintChar = ScreenPrintChar; out->DrawCursor = HalScreenDrawCursor; out->EraseCursor = HalScreenEraseCursor; ScreenClear(0x000000); }