Files
nt-lite/code/kernel/hal/hal_screen.c
T

350 lines
16 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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 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;
}
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->GetFramebuffer = ScreenGetFramebuffer;
out->Scroll = ScreenScroll;
out->PutChar = ScreenPutChar;
out->PrintChar = ScreenPrintChar;
out->DrawCursor = HalScreenDrawCursor;
out->EraseCursor = HalScreenEraseCursor;
ScreenClear(SCREEN_DEFAULT_BG);
}