DOS_Video/demo.c
2026-04-13 19:40:45 -05:00

869 lines
27 KiB
C

// demo.c -- Test/demo application for accelerated video drivers
//
// Detects the video card, sets a graphics mode, exercises the
// hardware acceleration (fill rects, blit, draw lines, color
// expand), and provides a simple interactive benchmark comparing
// hardware vs software rendering speed.
//
// Usage: demo [width height bpp]
// Defaults to 640x480x16 if no arguments given.
//
// Press ESC to exit, 'b' to run benchmark, space to cycle tests.
#include "accelVid.h"
#include "pci.h"
#include <dpmi.h>
#include <go32.h>
#include <pc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/nearptr.h>
#include <time.h>
// Scancode for ESC key
#define KEY_ESC 0x01
// Default video mode
#define DEFAULT_WIDTH 640
#define DEFAULT_HEIGHT 480
#define DEFAULT_BPP 16
// Benchmark iteration counts
#define BENCH_FILL_COUNT 1000
#define BENCH_BLIT_COUNT 1000
#define BENCH_LINE_COUNT 5000
#define BENCH_EXPAND_COUNT 500
#define BENCH_HBLIT_COUNT 1000
#define BENCH_PATFILL_COUNT 1000
// Host blit test pattern dimensions
#define HBLIT_PAT_W 100
#define HBLIT_PAT_H 100
// ============================================================
// External driver registration functions
// ============================================================
extern void atiRegisterDriver(void);
extern void bansheeRegisterDriver(void);
extern void clRegisterDriver(void);
extern void etRegisterDriver(void);
extern void lagunaRegisterDriver(void);
extern void mgaRegisterDriver(void);
extern void nvRegisterDriver(void);
extern void s3RegisterDriver(void);
extern void sisRegisterDriver(void);
extern void tridentRegisterDriver(void);
// ============================================================
// Prototypes
// ============================================================
static void demoBenchmark(AccelDriverT *drv);
static void demoBitBlt(AccelDriverT *drv);
static void demoColorExpand(AccelDriverT *drv);
static void demoFillRects(AccelDriverT *drv);
static void demoHostBlit(AccelDriverT *drv);
static void demoLines(AccelDriverT *drv);
static void demoPatternFill(AccelDriverT *drv);
static bool isKeyPressed(void);
static uint32_t packColor16(uint8_t r, uint8_t g, uint8_t b);
static uint8_t readKey(void);
static void softFillRect(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
// ============================================================
// demoBenchmark
// ============================================================
//
// Runs timed comparisons of hardware vs software rendering for
// rectangle fills and blits. Prints results to stdout after
// restoring text mode.
static void demoBenchmark(AccelDriverT *drv) {
int32_t screenW = drv->mode.width;
int32_t screenH = drv->mode.height;
// Benchmark hardware rect fill
clock_t hwFillStart = clock();
for (int32_t i = 0; i < BENCH_FILL_COUNT; i++) {
int32_t x = (i * 37) % (screenW - 100);
int32_t y = (i * 53) % (screenH - 100);
drv->rectFill(drv, x, y, 100, 100, packColor16(i & 0xFF, (i >> 3) & 0xFF, (i >> 6) & 0xFF));
}
drv->waitIdle(drv);
clock_t hwFillEnd = clock();
// Benchmark software rect fill
clock_t swFillStart = clock();
for (int32_t i = 0; i < BENCH_FILL_COUNT; i++) {
int32_t x = (i * 37) % (screenW - 100);
int32_t y = (i * 53) % (screenH - 100);
softFillRect(drv, x, y, 100, 100, packColor16(i & 0xFF, (i >> 3) & 0xFF, (i >> 6) & 0xFF));
}
clock_t swFillEnd = clock();
// Benchmark hardware bitblt
clock_t hwBltStart = clock();
for (int32_t i = 0; i < BENCH_BLIT_COUNT; i++) {
int32_t sx = (i * 31) % (screenW - 100);
int32_t sy = (i * 47) % (screenH - 100);
int32_t dx = (i * 43) % (screenW - 100);
int32_t dy = (i * 59) % (screenH - 100);
drv->bitBlt(drv, sx, sy, dx, dy, 100, 100);
}
drv->waitIdle(drv);
clock_t hwBltEnd = clock();
// Benchmark hardware line draw
clock_t hwLineStart = clock();
for (int32_t i = 0; i < BENCH_LINE_COUNT; i++) {
int32_t x1 = (i * 37) % screenW;
int32_t y1 = (i * 53) % screenH;
int32_t x2 = (i * 71) % screenW;
int32_t y2 = (i * 89) % screenH;
drv->lineDraw(drv, x1, y1, x2, y2, packColor16(255, 255, 255));
}
drv->waitIdle(drv);
clock_t hwLineEnd = clock();
// Benchmark host blit (CPU-to-screen)
int32_t bytesPerPix = (drv->mode.bpp + 7) / 8;
int32_t hblitPitch = HBLIT_PAT_W * bytesPerPix;
uint8_t *hblitBuf = (uint8_t *)malloc(hblitPitch * HBLIT_PAT_H);
clock_t hwHblitEnd = 0;
clock_t hwHblitStart = 0;
bool hblitValid = false;
if (hblitBuf) {
// Fill buffer with a checkerboard pattern
for (int32_t row = 0; row < HBLIT_PAT_H; row++) {
for (int32_t col = 0; col < HBLIT_PAT_W; col++) {
uint32_t color;
if ((row / 8 + col / 8) & 1) {
color = packColor16(255, 255, 0);
} else {
color = packColor16(0, 0, 128);
}
if (bytesPerPix == 2) {
((uint16_t *)(hblitBuf + row * hblitPitch))[col] = (uint16_t)color;
} else if (bytesPerPix == 4) {
((uint32_t *)(hblitBuf + row * hblitPitch))[col] = color;
} else {
hblitBuf[row * hblitPitch + col] = (uint8_t)color;
}
}
}
hwHblitStart = clock();
for (int32_t i = 0; i < BENCH_HBLIT_COUNT; i++) {
int32_t dx = (i * 37) % (screenW - HBLIT_PAT_W);
int32_t dy = (i * 53) % (screenH - HBLIT_PAT_H);
drv->hostBlit(drv, hblitBuf, hblitPitch, dx, dy, HBLIT_PAT_W, HBLIT_PAT_H);
}
drv->waitIdle(drv);
hwHblitEnd = clock();
hblitValid = true;
free(hblitBuf);
}
// Benchmark pattern fill
static const uint8_t benchPattern[8] = {
0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55
};
clock_t hwPatStart = clock();
for (int32_t i = 0; i < BENCH_PATFILL_COUNT; i++) {
int32_t px = (i * 37) % (screenW - 100);
int32_t py = (i * 53) % (screenH - 100);
drv->rectFillPat(drv, px, py, 100, 100, benchPattern, packColor16(255, 255, 255), packColor16(0, 0, 0));
}
drv->waitIdle(drv);
clock_t hwPatEnd = clock();
// Calculate times in milliseconds
double hwFillMs = (double)(hwFillEnd - hwFillStart) * 1000.0 / CLOCKS_PER_SEC;
double swFillMs = (double)(swFillEnd - swFillStart) * 1000.0 / CLOCKS_PER_SEC;
double hwBltMs = (double)(hwBltEnd - hwBltStart) * 1000.0 / CLOCKS_PER_SEC;
double hwLineMs = (double)(hwLineEnd - hwLineStart) * 1000.0 / CLOCKS_PER_SEC;
double hwHblitMs = (double)(hwHblitEnd - hwHblitStart) * 1000.0 / CLOCKS_PER_SEC;
double hwPatMs = (double)(hwPatEnd - hwPatStart) * 1000.0 / CLOCKS_PER_SEC;
// Store results, then restore text mode to print
accelShutdown(drv);
printf("\n=== Benchmark Results ===\n\n");
printf("Rectangle Fill (%d x 100x100):\n", BENCH_FILL_COUNT);
printf(" Hardware: %.1f ms (%.0f rects/sec)\n",
hwFillMs, BENCH_FILL_COUNT * 1000.0 / hwFillMs);
printf(" Software: %.1f ms (%.0f rects/sec)\n",
swFillMs, BENCH_FILL_COUNT * 1000.0 / swFillMs);
if (swFillMs > 0) {
printf(" Speedup: %.1fx\n", swFillMs / hwFillMs);
}
printf("\nBitBlt (%d x 100x100 screen-to-screen):\n", BENCH_BLIT_COUNT);
printf(" Hardware: %.1f ms (%.0f blits/sec)\n",
hwBltMs, BENCH_BLIT_COUNT * 1000.0 / hwBltMs);
printf("\nLine Draw (%d lines):\n", BENCH_LINE_COUNT);
printf(" Hardware: %.1f ms (%.0f lines/sec)\n",
hwLineMs, BENCH_LINE_COUNT * 1000.0 / hwLineMs);
if (hblitValid) {
printf("\nHost Blit (%d x %dx%d CPU-to-screen):\n",
BENCH_HBLIT_COUNT, HBLIT_PAT_W, HBLIT_PAT_H);
printf(" Hardware: %.1f ms (%.0f blits/sec)\n",
hwHblitMs, BENCH_HBLIT_COUNT * 1000.0 / hwHblitMs);
}
printf("\nPattern Fill (%d x 100x100):\n", BENCH_PATFILL_COUNT);
printf(" Hardware: %.1f ms (%.0f fills/sec)\n",
hwPatMs, BENCH_PATFILL_COUNT * 1000.0 / hwPatMs);
printf("\nPress any key to exit...\n");
readKey();
}
// ============================================================
// demoBitBlt
// ============================================================
//
// Demonstrates screen-to-screen BitBLT by filling colored
// rectangles and then copying them around the screen.
static void demoBitBlt(AccelDriverT *drv) {
int32_t screenW = drv->mode.width;
int32_t screenH = drv->mode.height;
// Clear screen
drv->rectFill(drv, 0, 0, screenW, screenH, 0);
// Draw some source rectangles
drv->rectFill(drv, 10, 10, 100, 100, packColor16(255, 0, 0));
drv->rectFill(drv, 120, 10, 100, 100, packColor16(0, 255, 0));
drv->rectFill(drv, 230, 10, 100, 100, packColor16(0, 0, 255));
drv->rectFill(drv, 340, 10, 100, 100, packColor16(255, 255, 0));
drv->waitIdle(drv);
// Copy them diagonally across the screen
for (int32_t i = 0; i < 5; i++) {
int32_t offsetY = 120 + i * 60;
if (offsetY + 100 > screenH) {
break;
}
drv->bitBlt(drv, 10, 10, 10 + i * 30, offsetY, 430, 100);
}
drv->waitIdle(drv);
}
// ============================================================
// demoColorExpand
// ============================================================
//
// Demonstrates monochrome color expansion by rendering text-like
// patterns. Creates a simple 8x16 glyph and renders it repeatedly.
static void demoColorExpand(AccelDriverT *drv) {
int32_t screenW = drv->mode.width;
int32_t screenH = drv->mode.height;
// Clear screen
drv->rectFill(drv, 0, 0, screenW, screenH, packColor16(0, 0, 128));
drv->waitIdle(drv);
// 8x16 glyph bitmaps for several characters
static const uint8_t glyphA[16] = {
0x00, 0x18, 0x3C, 0x66, 0x66, 0xC3, 0xC3, 0xFF,
0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0x00, 0x00
};
static const uint8_t glyphB[16] = {
0x00, 0xFC, 0xC6, 0xC6, 0xC6, 0xFC, 0xC6, 0xC3,
0xC3, 0xC3, 0xC6, 0xFC, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t glyphC[16] = {
0x00, 0x3E, 0x63, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0,
0xC0, 0xC0, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t glyphD[16] = {
0x00, 0xFC, 0xC6, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
0xC3, 0xC3, 0xC6, 0xFC, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t glyphE[16] = {
0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xFF, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t glyphF[16] = {
0x00, 0xFF, 0xC0, 0xC0, 0xC0, 0xFE, 0xC0, 0xC0,
0xC0, 0xC0, 0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t *glyphs[6] = {
glyphA, glyphB, glyphC, glyphD, glyphE, glyphF
};
#define NUM_GLYPHS 6
// Color pairs for different rows (foreground/background)
static const uint8_t colorPairs[][6] = {
// R G B R G B (fg, then bg)
{255, 255, 255, 0, 0, 128}, // white on dark blue
{255, 255, 0, 0, 0, 0}, // yellow on black
{ 0, 255, 0, 0, 64, 0}, // green on dark green
{255, 128, 0, 64, 0, 0}, // orange on dark red
{ 0, 255, 255, 0, 0, 64}, // cyan on navy
{255, 0, 255, 32, 0, 32}, // magenta on dark purple
};
#define NUM_COLOR_PAIRS 6
int32_t cols = screenW / 8;
int32_t rows = screenH / 16;
for (int32_t row = 0; row < rows; row++) {
int32_t pairIdx = row % NUM_COLOR_PAIRS;
const uint8_t *pair = colorPairs[pairIdx];
uint32_t fg = packColor16(pair[0], pair[1], pair[2]);
uint32_t bg = packColor16(pair[3], pair[4], pair[5]);
for (int32_t col = 0; col < cols; col++) {
const uint8_t *glyph = glyphs[(row + col) % NUM_GLYPHS];
drv->colorExpand(drv, glyph, 1,
col * 8, row * 16, 8, 16, fg, bg);
}
}
drv->waitIdle(drv);
#undef NUM_GLYPHS
#undef NUM_COLOR_PAIRS
}
// ============================================================
// demoFillRects
// ============================================================
//
// Demonstrates hardware rectangle fill with various colors
// and sizes. Draws a pattern of overlapping rectangles.
static void demoFillRects(AccelDriverT *drv) {
int32_t screenW = drv->mode.width;
int32_t screenH = drv->mode.height;
// Clear screen to dark blue
drv->rectFill(drv, 0, 0, screenW, screenH, packColor16(0, 0, 64));
drv->waitIdle(drv);
// Draw concentric rectangles
int32_t colors[][3] = {
{255, 0, 0},
{0, 255, 0},
{0, 0, 255},
{255, 255, 0},
{255, 0, 255},
{0, 255, 255},
{255, 128, 0},
{128, 0, 255}
};
int32_t numColors = 8;
int32_t cx = screenW / 2;
int32_t cy = screenH / 2;
for (int32_t i = 0; i < numColors; i++) {
int32_t size = 200 - i * 20;
if (size < 10) {
break;
}
uint32_t color = packColor16(colors[i][0], colors[i][1], colors[i][2]);
drv->rectFill(drv, cx - size / 2, cy - size / 2, size, size, color);
}
// Draw a grid of small rectangles
for (int32_t y = 10; y < screenH - 30; y += 25) {
for (int32_t x = 10; x < 150; x += 25) {
uint32_t color = packColor16((x * 7) & 0xFF, (y * 3) & 0xFF, ((x + y) * 5) & 0xFF);
drv->rectFill(drv, x, y, 20, 20, color);
}
}
// Draw grid on right side too
for (int32_t y = 10; y < screenH - 30; y += 25) {
for (int32_t x = screenW - 160; x < screenW - 10; x += 25) {
uint32_t color = packColor16((x * 3) & 0xFF, (y * 7) & 0xFF, ((x + y) * 2) & 0xFF);
drv->rectFill(drv, x, y, 20, 20, color);
}
}
drv->waitIdle(drv);
}
// ============================================================
// demoHostBlit
// ============================================================
//
// Demonstrates CPU-to-screen blit by creating a colorful gradient
// pattern in system RAM, then tiling copies across the screen.
static void demoHostBlit(AccelDriverT *drv) {
int32_t screenW = drv->mode.width;
int32_t screenH = drv->mode.height;
// Clear screen
drv->rectFill(drv, 0, 0, screenW, screenH, 0);
drv->waitIdle(drv);
// Create a gradient tile in system RAM
int32_t tileW = 64;
int32_t tileH = 64;
int32_t bytesPerPix = (drv->mode.bpp + 7) / 8;
int32_t tilePitch = tileW * bytesPerPix;
uint8_t *tileBuf = (uint8_t *)malloc(tilePitch * tileH);
if (!tileBuf) {
return;
}
// Fill tile with a radial gradient pattern
int32_t cx = tileW / 2;
int32_t cy = tileH / 2;
for (int32_t row = 0; row < tileH; row++) {
for (int32_t col = 0; col < tileW; col++) {
int32_t dx = col - cx;
int32_t dy = row - cy;
int32_t dist = dx * dx + dy * dy;
// Map distance to color -- creates concentric rings
uint8_t r = (dist * 7) & 0xFF;
uint8_t g = (dist * 3 + col * 4) & 0xFF;
uint8_t b = (row * 8 + col * 2) & 0xFF;
uint32_t color = packColor16(r, g, b);
if (bytesPerPix == 2) {
((uint16_t *)(tileBuf + row * tilePitch))[col] = (uint16_t)color;
} else if (bytesPerPix == 4) {
((uint32_t *)(tileBuf + row * tilePitch))[col] = color;
} else {
tileBuf[row * tilePitch + col] = (uint8_t)color;
}
}
}
// Tile the pattern across the screen
for (int32_t y = 0; y + tileH <= screenH; y += tileH) {
for (int32_t x = 0; x + tileW <= screenW; x += tileW) {
drv->hostBlit(drv, tileBuf, tilePitch, x, y, tileW, tileH);
}
}
drv->waitIdle(drv);
// Draw a border around each tile using rect fills for contrast
uint32_t borderColor = packColor16(255, 255, 255);
for (int32_t y = 0; y + tileH <= screenH; y += tileH) {
drv->rectFill(drv, 0, y, screenW, 1, borderColor);
}
for (int32_t x = 0; x + tileW <= screenW; x += tileW) {
drv->rectFill(drv, x, 0, 1, screenH, borderColor);
}
drv->waitIdle(drv);
free(tileBuf);
}
// ============================================================
// demoLines
// ============================================================
//
// Demonstrates hardware line drawing with a starburst pattern.
static void demoLines(AccelDriverT *drv) {
int32_t screenW = drv->mode.width;
int32_t screenH = drv->mode.height;
// Clear screen
drv->rectFill(drv, 0, 0, screenW, screenH, 0);
drv->waitIdle(drv);
int32_t cx = screenW / 2;
int32_t cy = screenH / 2;
// Draw starburst from center
for (int32_t i = 0; i < 360; i += 3) {
// Simple integer approximation of sin/cos using a lookup
// approach. For a demo, we just use the endpoint calculation.
int32_t dx = 0;
int32_t dy = 0;
// Approximate angle -> direction
int32_t radius = (screenH / 2) - 10;
int32_t angle = i;
// Crude trig via quadrant decomposition
int32_t quadrant = (angle / 90) % 4;
int32_t subAngle = angle % 90;
// Linear interpolation within each quadrant (good enough for demo)
int32_t frac = subAngle * radius / 90;
int32_t comp = radius - frac;
switch (quadrant) {
case 0: dx = frac; dy = -comp; break;
case 1: dx = comp; dy = frac; break;
case 2: dx = -frac; dy = comp; break;
case 3: dx = -comp; dy = -frac; break;
}
uint32_t color = packColor16(
(i * 3) & 0xFF,
(i * 5 + 100) & 0xFF,
(i * 7 + 50) & 0xFF
);
drv->lineDraw(drv, cx, cy, cx + dx, cy + dy, color);
}
// Draw border rectangle with lines
uint32_t white = packColor16(255, 255, 255);
drv->lineDraw(drv, 0, 0, screenW - 1, 0, white);
drv->lineDraw(drv, screenW - 1, 0, screenW - 1, screenH - 1, white);
drv->lineDraw(drv, screenW - 1, screenH - 1, 0, screenH - 1, white);
drv->lineDraw(drv, 0, screenH - 1, 0, 0, white);
drv->waitIdle(drv);
}
// ============================================================
// demoPatternFill
// ============================================================
//
// Demonstrates 8x8 pattern fills with several distinct patterns
// drawn side by side in colored rectangles.
static void demoPatternFill(AccelDriverT *drv) {
int32_t screenW = drv->mode.width;
int32_t screenH = drv->mode.height;
// Clear screen to dark gray
drv->rectFill(drv, 0, 0, screenW, screenH, packColor16(32, 32, 32));
drv->waitIdle(drv);
// Define several 8x8 patterns
static const uint8_t patCheckerboard[8] = {
0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55
};
static const uint8_t patCrosshatch[8] = {
0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80
};
static const uint8_t patDiagStripes[8] = {
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
static const uint8_t patDots[8] = {
0x00, 0x22, 0x00, 0x88, 0x00, 0x22, 0x00, 0x88
};
static const uint8_t patHorzStripes[8] = {
0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0x00
};
static const uint8_t patVertStripes[8] = {
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA
};
struct {
const uint8_t *pattern;
uint32_t fg;
uint32_t bg;
} patterns[] = {
{patCheckerboard, packColor16(255, 255, 255), packColor16(0, 0, 0)},
{patCrosshatch, packColor16(255, 255, 0), packColor16(0, 0, 128)},
{patDiagStripes, packColor16(0, 255, 0), packColor16(0, 64, 0)},
{patDots, packColor16(255, 0, 0), packColor16(64, 0, 0)},
{patHorzStripes, packColor16(0, 255, 255), packColor16(0, 0, 64)},
{patVertStripes, packColor16(255, 0, 255), packColor16(64, 0, 64)},
};
int32_t numPatterns = 6;
// Arrange patterns in a 3x2 grid
int32_t margin = 20;
int32_t spacing = 10;
int32_t cellW = (screenW - 2 * margin - (3 - 1) * spacing) / 3;
int32_t cellH = (screenH - 2 * margin - (2 - 1) * spacing) / 2;
for (int32_t i = 0; i < numPatterns; i++) {
int32_t gridCol = i % 3;
int32_t gridRow = i / 3;
int32_t x = margin + gridCol * (cellW + spacing);
int32_t y = margin + gridRow * (cellH + spacing);
drv->rectFillPat(drv, x, y, cellW, cellH,
patterns[i].pattern,
patterns[i].fg, patterns[i].bg);
}
drv->waitIdle(drv);
}
// ============================================================
// isKeyPressed
// ============================================================
//
// Non-blocking check for a keypress using BIOS INT 16h.
static bool isKeyPressed(void) {
__dpmi_regs r;
memset(&r, 0, sizeof(r));
r.h.ah = 0x11; // check for extended keystroke
__dpmi_int(0x16, &r);
return !(r.x.flags & 0x40); // ZF clear = key available
}
// ============================================================
// main
// ============================================================
int main(int argc, char *argv[]) {
int32_t reqW = DEFAULT_WIDTH;
int32_t reqH = DEFAULT_HEIGHT;
int32_t reqBpp = DEFAULT_BPP;
if (argc >= 4) {
reqW = atoi(argv[1]);
reqH = atoi(argv[2]);
reqBpp = atoi(argv[3]);
}
printf("DOS Accelerated Video Driver Demo\n");
printf("Requested mode: %ldx%ldx%ld\n\n", (long)reqW, (long)reqH, (long)reqBpp);
// Register all available drivers
atiRegisterDriver();
bansheeRegisterDriver();
clRegisterDriver();
etRegisterDriver();
lagunaRegisterDriver();
mgaRegisterDriver();
nvRegisterDriver();
s3RegisterDriver();
sisRegisterDriver();
tridentRegisterDriver();
// Detect hardware
printf("Scanning PCI bus for supported video hardware...\n");
AccelDriverT *drv = accelDetect();
if (!drv) {
printf("No supported video hardware found.\n");
printf("\nPCI video devices present:\n");
// Enumerate and display all VGA-class PCI devices for diagnostics
for (int32_t bus = 0; bus < 256; bus++) {
for (int32_t dev = 0; dev < 32; dev++) {
uint16_t vid = pciRead16(bus, dev, 0, PCI_VENDOR_ID);
if (vid == 0xFFFF) {
continue;
}
uint8_t baseClass = pciRead8(bus, dev, 0, PCI_BASE_CLASS);
if (baseClass == PCI_CLASS_DISPLAY) {
uint16_t did = pciRead16(bus, dev, 0, PCI_DEVICE_ID);
printf(" %02lX:%02lX.0 vendor=%04X device=%04X\n",
(long)bus, (long)dev, vid, did);
}
}
}
return 1;
}
// Initialize with requested mode
AccelModeRequestT modeReq;
modeReq.width = reqW;
modeReq.height = reqH;
modeReq.bpp = reqBpp;
if (!accelInit(drv, &modeReq)) {
printf("Failed to initialize video driver.\n");
return 1;
}
printf("\nDriver: %s\n", accelGetName(drv));
printf("Mode: %ldx%ldx%ld (pitch=%ld)\n",
(long)drv->mode.width, (long)drv->mode.height,
(long)drv->mode.bpp, (long)drv->mode.pitch);
printf("VRAM: %lu KB\n", (unsigned long)(drv->mode.vramSize / 1024));
printf("\nPress any key to start demos...\n");
printf(" SPACE = next demo\n");
printf(" B = benchmark\n");
printf(" ESC = exit\n");
readKey();
// Run demos in a loop
int32_t currentDemo = 0;
int32_t numDemos = 6;
bool running = true;
while (running) {
switch (currentDemo) {
case 0:
demoFillRects(drv);
break;
case 1:
demoBitBlt(drv);
break;
case 2:
demoLines(drv);
break;
case 3:
demoColorExpand(drv);
break;
case 4:
demoHostBlit(drv);
break;
case 5:
demoPatternFill(drv);
break;
}
// Wait for keypress
while (!isKeyPressed()) {
// spin
}
uint8_t key = readKey();
switch (key) {
case 0x01: // ESC
running = false;
break;
case 0x30: // 'b'
demoBenchmark(drv);
return 0; // benchmark already shut down the driver
case 0x39: // space
currentDemo = (currentDemo + 1) % numDemos;
break;
default:
currentDemo = (currentDemo + 1) % numDemos;
break;
}
}
accelShutdown(drv);
printf("Demo complete.\n");
return 0;
}
// ============================================================
// packColor16
// ============================================================
//
// Packs an RGB triplet into 16-bit 565 format.
// This is a simplification -- a real integration would use the
// display's actual pixel format. For the demo, 565 is fine since
// that's what most 16-bit VESA modes use.
static uint32_t packColor16(uint8_t r, uint8_t g, uint8_t b) {
return ((uint32_t)(r >> 3) << 11)
| ((uint32_t)(g >> 2) << 5)
| ((uint32_t)(b >> 3));
}
// ============================================================
// readKey
// ============================================================
//
// Blocking read of one keypress via BIOS INT 16h.
// Returns the scan code.
static uint8_t readKey(void) {
__dpmi_regs r;
memset(&r, 0, sizeof(r));
r.h.ah = 0x10; // read extended keystroke
__dpmi_int(0x16, &r);
return r.h.ah; // scan code
}
// ============================================================
// softFillRect
// ============================================================
//
// Software rectangle fill for benchmark comparison. Writes
// directly to the LFB (intentionally slow due to PCI bus writes).
static void softFillRect(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) {
uint8_t *fb = drv->mode.framebuffer;
int32_t pitch = drv->mode.pitch;
int32_t bpp = (drv->mode.bpp + 7) / 8;
for (int32_t row = 0; row < h; row++) {
uint8_t *dst = fb + (y + row) * pitch + x * bpp;
if (bpp == 2) {
uint16_t *dst16 = (uint16_t *)dst;
for (int32_t col = 0; col < w; col++) {
dst16[col] = (uint16_t)color;
}
} else if (bpp == 4) {
uint32_t *dst32 = (uint32_t *)dst;
for (int32_t col = 0; col < w; col++) {
dst32[col] = color;
}
} else {
for (int32_t col = 0; col < w; col++) {
dst[col] = (uint8_t)color;
}
}
}
}