// 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; } } // --------------------------------------------------------------------------- // 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; ScreenClear(0x000000); }