323 lines
9.9 KiB
C
323 lines
9.9 KiB
C
// ============================================================================
|
|
// demo.c - Demonstration of using Windows 3.x display drivers from DOS
|
|
//
|
|
// This program loads a Windows 3.x accelerated display driver (.DRV file)
|
|
// and uses its hardware-accelerated functions to draw on screen.
|
|
//
|
|
// Usage: demo [-d] <driver.drv>
|
|
// -d Enable debug output
|
|
//
|
|
// Example:
|
|
// demo vga.drv
|
|
// demo -d s3trio.drv
|
|
// ============================================================================
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <inttypes.h>
|
|
#include <pc.h>
|
|
#include <dpmi.h>
|
|
|
|
#include "win31drv/windrv.h"
|
|
#include "win31drv/wintypes.h"
|
|
#include "win31drv/log.h"
|
|
|
|
__attribute__((noinline))
|
|
static void demoDrawing(WdrvHandleT drv);
|
|
static void printDriverInfo(WdrvHandleT drv);
|
|
static void printUsage(const char *progName);
|
|
static void setupPalette(WdrvHandleT drv);
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
const char *driverPath = NULL;
|
|
bool debug = false;
|
|
|
|
// Parse arguments
|
|
for (int i = 1; i < argc; i++) {
|
|
if (strcmp(argv[i], "-d") == 0) {
|
|
debug = true;
|
|
} else if (argv[i][0] != '-') {
|
|
driverPath = argv[i];
|
|
} else {
|
|
printUsage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!driverPath) {
|
|
printUsage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
logInit("OUTPUT.LOG");
|
|
|
|
// Initialize the library
|
|
logMsg("Initializing windrv library...\n");
|
|
int32_t err = wdrvInit();
|
|
if (err != WDRV_OK) {
|
|
logErr("Failed to initialize: %s\n", wdrvGetLastErrorString());
|
|
return 1;
|
|
}
|
|
|
|
if (debug) {
|
|
wdrvSetDebug(true);
|
|
}
|
|
|
|
// Load the driver
|
|
logMsg("Loading driver: %s\n", driverPath);
|
|
WdrvHandleT drv = wdrvLoadDriver(driverPath);
|
|
if (!drv) {
|
|
logErr("Failed to load driver: %s\n", wdrvGetLastErrorString());
|
|
wdrvShutdown();
|
|
return 1;
|
|
}
|
|
|
|
// Print driver info
|
|
printDriverInfo(drv);
|
|
|
|
// Redirect stdout to the log file before entering SVGA mode.
|
|
// In graphics mode, console output through INT 21h can crash
|
|
// because the console handler tries to access VGA text memory
|
|
// that's now part of the SVGA framebuffer.
|
|
freopen("OUTPUT.LOG", "a", stdout);
|
|
|
|
// Enable the driver (set video mode)
|
|
logMsg("Enabling driver...\n");
|
|
err = wdrvEnable(drv, 0, 0, 0); // Use driver defaults
|
|
if (err != WDRV_OK) {
|
|
logErr("Failed to enable driver: %s\n", wdrvGetLastErrorString());
|
|
wdrvUnloadDriver(drv);
|
|
wdrvShutdown();
|
|
return 1;
|
|
}
|
|
|
|
// Set up a 256-color palette via the driver's SetPalette DDI.
|
|
// This updates both the driver's internal color table (used by
|
|
// RealizeObject for color matching) and the VGA DAC hardware.
|
|
setupPalette(drv);
|
|
|
|
// Run drawing demos
|
|
demoDrawing(drv);
|
|
|
|
logMsg("Drawing complete. Press any key...\n");
|
|
|
|
// Wait for a keypress so we can see the output
|
|
while (!kbhit()) {
|
|
// Busy-wait; kbhit() does keyboard I/O which keeps DOSBox-X responsive
|
|
}
|
|
(void)getkey(); // consume the key
|
|
|
|
// Disable the driver (restore text mode)
|
|
logMsg("Calling wdrvDisable...\n");
|
|
err = wdrvDisable(drv);
|
|
if (err != WDRV_OK) {
|
|
logMsg("wdrvDisable failed (err=%d), forcing text mode via INT 10h\n", (int)err);
|
|
__dpmi_regs dr;
|
|
memset(&dr, 0, sizeof(dr));
|
|
dr.x.ax = 0x0003; // INT 10h AH=00 AL=03: set 80x25 text mode
|
|
__dpmi_int(0x10, &dr);
|
|
}
|
|
|
|
// Clean up
|
|
logMsg("Unloading driver...\n");
|
|
wdrvUnloadDriver(drv);
|
|
wdrvShutdown();
|
|
|
|
logMsg("Done.\n");
|
|
logShutdown();
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void printUsage(const char *progName)
|
|
{
|
|
logErr("Usage: %s [-d] <driver.drv>\n", progName);
|
|
logErr(" -d Enable debug output\n");
|
|
logErr("\nLoads a Windows 3.x display driver and demonstrates\n");
|
|
logErr("its accelerated drawing functions from DOS.\n");
|
|
}
|
|
|
|
|
|
static void printDriverInfo(WdrvHandleT drv)
|
|
{
|
|
WdrvInfoT info;
|
|
wdrvGetInfo(drv, &info);
|
|
|
|
logMsg("\n=== Driver Information ===\n");
|
|
logMsg(" Name: %s\n", info.driverName);
|
|
logMsg(" Version: %u.%u\n", info.driverVersion >> 8,
|
|
info.driverVersion & 0xFF);
|
|
logMsg(" Resolution: %" PRId32 "x%" PRId32 "\n", info.maxWidth, info.maxHeight);
|
|
logMsg(" Color depth: %" PRId32 " bpp\n", info.maxBpp);
|
|
logMsg(" Colors: %" PRId32 "\n", info.numColors);
|
|
logMsg(" Raster caps: 0x%04" PRIX32 "\n", info.rasterCaps);
|
|
logMsg(" Capabilities:\n");
|
|
logMsg(" BitBlt: %s\n", info.hasBitBlt ? "yes" : "no");
|
|
logMsg(" Output: %s\n", info.hasOutput ? "yes" : "no");
|
|
logMsg(" Pixel: %s\n", info.hasPixel ? "yes" : "no");
|
|
logMsg(" StretchBlt: %s\n", info.hasStretchBlt ? "yes" : "no");
|
|
logMsg(" ExtTextOut: %s\n", info.hasExtTextOut ? "yes" : "no");
|
|
logMsg(" SetPalette: %s\n", info.hasSetPalette ? "yes" : "no");
|
|
logMsg(" SetCursor: %s\n", info.hasSetCursor ? "yes" : "no");
|
|
}
|
|
|
|
|
|
// noinline: when inlined into main with -O2, the optimizer mishandles
|
|
// callee-saved registers across the thunk calls in the Demo 2 → Demo 3
|
|
// transition, causing the handle pointer to be corrupted.
|
|
__attribute__((noinline))
|
|
static void demoDrawing(WdrvHandleT drv)
|
|
{
|
|
WdrvInfoT info;
|
|
wdrvGetInfo(drv, &info);
|
|
|
|
int16_t screenW = (int16_t)info.maxWidth;
|
|
int16_t screenH = (int16_t)info.maxHeight;
|
|
|
|
if (screenW == 0) {
|
|
screenW = 640;
|
|
}
|
|
if (screenH == 0) {
|
|
screenH = 480;
|
|
}
|
|
|
|
logMsg("demoDrawing: screenW=%d screenH=%d hasBitBlt=%d hasOutput=%d hasPixel=%d\n",
|
|
screenW, screenH, info.hasBitBlt, info.hasOutput, info.hasPixel);
|
|
|
|
// Demo 1: Fill rectangles
|
|
if (info.hasBitBlt) {
|
|
logMsg("Demo 1: Fill rectangles\n");
|
|
|
|
// Clear screen to white
|
|
wdrvFillRect(drv, 0, 0, screenW, screenH, MAKE_RGB(255, 255, 255));
|
|
|
|
static const uint32_t vgaColors[] = {
|
|
MAKE_RGB( 0, 0, 0), // black
|
|
MAKE_RGB( 0, 0, 255), // blue
|
|
MAKE_RGB( 0, 255, 0), // green
|
|
MAKE_RGB( 0, 255, 255), // cyan
|
|
MAKE_RGB(255, 0, 0), // red
|
|
MAKE_RGB(255, 0, 255), // magenta
|
|
MAKE_RGB(255, 255, 0), // yellow
|
|
MAKE_RGB(170, 170, 170), // light gray
|
|
MAKE_RGB( 85, 85, 85), // dark gray
|
|
MAKE_RGB( 85, 85, 255), // light blue
|
|
MAKE_RGB( 85, 255, 85), // light green
|
|
MAKE_RGB( 85, 255, 255), // light cyan
|
|
MAKE_RGB(255, 85, 85), // light red
|
|
MAKE_RGB(255, 85, 255), // light magenta
|
|
MAKE_RGB(255, 255, 85), // yellow-ish
|
|
MAKE_RGB(128, 128, 128), // mid gray
|
|
};
|
|
|
|
int16_t boxW = screenW / 4;
|
|
int16_t boxH = screenH / 4;
|
|
|
|
for (int16_t row = 0; row < 4; row++) {
|
|
for (int16_t col = 0; col < 4; col++) {
|
|
int idx = row * 4 + col;
|
|
wdrvFillRect(drv, col * boxW + 4, row * boxH + 4,
|
|
boxW - 8, boxH - 8, vgaColors[idx]);
|
|
}
|
|
}
|
|
|
|
logMsg(" Drew %d colored rectangles\n", 16);
|
|
}
|
|
|
|
// Demo 2: Draw pixel patterns
|
|
if (info.hasPixel) {
|
|
logMsg("Demo 2: Pixel patterns (8x8)\n");
|
|
int pixCount = 0;
|
|
for (int16_t y = 0; y < 8 && y < screenH; y++) {
|
|
for (int16_t x = 0; x < 8 && x < screenW; x++) {
|
|
uint8_t r = (uint8_t)(x * 32);
|
|
uint8_t g = (uint8_t)(y * 32);
|
|
uint8_t b = (uint8_t)((x + y) * 16);
|
|
wdrvSetPixel(drv, x + screenW - 18, y + 10, MAKE_RGB(r, g, b));
|
|
pixCount++;
|
|
}
|
|
}
|
|
logMsg(" Drew %d pixels\n", pixCount);
|
|
}
|
|
|
|
// Demo 3: Draw lines using Output (polyline with realized pen)
|
|
if (info.hasOutput) {
|
|
logMsg("Demo 3: Lines (starburst)\n");
|
|
|
|
int16_t cx = screenW / 2;
|
|
int16_t cy = screenH / 2;
|
|
int16_t radius = (screenH < screenW ? screenH : screenW) / 3;
|
|
int lineCount = 0;
|
|
|
|
for (int angle = 0; angle < 360; angle += 15) {
|
|
int32_t dx = 0;
|
|
int32_t dy = 0;
|
|
|
|
int a = angle % 360;
|
|
int qa = a % 90;
|
|
int32_t s = (int32_t)qa * radius / 90;
|
|
int32_t c = (int32_t)(90 - qa) * radius / 90;
|
|
|
|
if (a < 90) {
|
|
dx = s;
|
|
dy = -c;
|
|
} else if (a < 180) {
|
|
dx = c;
|
|
dy = s;
|
|
} else if (a < 270) {
|
|
dx = -s;
|
|
dy = c;
|
|
} else {
|
|
dx = -c;
|
|
dy = -s;
|
|
}
|
|
|
|
Point16T pts[2];
|
|
pts[0].x = cx;
|
|
pts[0].y = cy;
|
|
pts[1].x = (int16_t)(cx + dx);
|
|
pts[1].y = (int16_t)(cy + dy);
|
|
|
|
uint32_t lineColor = MAKE_RGB(
|
|
(uint8_t)(angle * 255 / 360),
|
|
(uint8_t)(255 - angle * 255 / 360),
|
|
128);
|
|
|
|
wdrvPolyline(drv, pts, 2, lineColor);
|
|
lineCount++;
|
|
}
|
|
logMsg(" Drew %d lines\n", lineCount);
|
|
}
|
|
|
|
// Demo 4: Screen-to-screen blit test
|
|
if (info.hasBitBlt) {
|
|
logMsg("Demo 4: Screen-to-screen blit\n");
|
|
|
|
WdrvBitBltParamsT bp;
|
|
memset(&bp, 0, sizeof(bp));
|
|
bp.srcX = 0;
|
|
bp.srcY = 0;
|
|
bp.dstX = screenW / 2;
|
|
bp.dstY = screenH / 2;
|
|
bp.width = screenW / 4;
|
|
bp.height = screenH / 4;
|
|
bp.rop3 = SRCCOPY;
|
|
wdrvBitBlt(drv, &bp);
|
|
logMsg(" Screen blit done\n");
|
|
}
|
|
}
|
|
|
|
|
|
static void setupPalette(WdrvHandleT drv)
|
|
{
|
|
// The driver sets up its own palette during Enable (via VBE 4F09)
|
|
// and stores an internal color table that RealizeObject uses for
|
|
// color matching. We leave the palette as-is so the DAC and the
|
|
// internal table stay in sync. RealizeObject will find the best
|
|
// matching index, and the DAC will display the correct color.
|
|
(void)drv;
|
|
}
|
|
|