337 lines
15 KiB
C
337 lines
15 KiB
C
// 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 <stdint.h>
|
||
#include <string.h>
|
||
#include <intrin.h>
|
||
#include <ntlite/ddi/hal_api.h>
|
||
#include <kernel/mm_virt.h>
|
||
|
||
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 <kernel/hal/font8x16.h> // see font8x16.h
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Internals
|
||
// ---------------------------------------------------------------------------
|
||
static inline uint32_t* ScreenBackbufferPixel(int x, int y) {
|
||
return gBackbuffer ? (gBackbuffer + y * gStride + x) : (gFb + y * gStride + x);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
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);
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
static void ScreenGetSize(int* w, int* h) { *w = gWidth; *h = gHeight; }
|
||
|
||
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);
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
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 = 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;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// 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->Scroll = ScreenScroll;
|
||
out->PutChar = ScreenPutChar;
|
||
out->PrintChar = ScreenPrintChar;
|
||
out->DrawCursor = HalScreenDrawCursor;
|
||
out->EraseCursor = HalScreenEraseCursor;
|
||
ScreenClear(SCREEN_DEFAULT_BG);
|
||
}
|