869 lines
27 KiB
C
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;
|
|
}
|
|
}
|
|
}
|
|
}
|