# DOS Accelerated Video Driver Framework Hardware-accelerated 2D video drivers for DOS/DJGPP. Programs the acceleration engines on PCI video cards directly -- no VESA, no BIOS calls for rendering. A common API lets applications use acceleration without knowing which chip is present. ## Supported Video Cards ### S3 (s3Trio.c) | Chip | Device ID | Notes | |------|-----------|-------| | Trio32 | 0x8810 | | | Trio64 | 0x8811 | MMIO at LFB+16MB | | Trio64V+ | 0x8814 | MMIO at LFB+16MB | | ViRGE | 0x5631 | MMIO, 3D engine ignored | | ViRGE/VX | 0x883D | | | ViRGE/DX/GX | 0x8A01 | | | ViRGE/GX2 | 0x8A10 | | | ViRGE/MX | 0x8C01, 0x8C03 | | | Savage3D | 0x8A20, 0x8A21 | | | Savage4 | 0x8A22 | | | Savage/MX | 0x8C10, 0x8C11 | | | Savage/IX | 0x8C12, 0x8C13 | | | Savage 2000 | 0x9102 | | | Vision864 | 0x88C0, 0x88C1 | I/O only (no MMIO) | | Vision868 | 0x8880 | I/O only | | Vision964 | 0x88D0 | I/O only | | Vision968 | 0x88F0, 0x88F1 | I/O only | Hardware ops: RectFill, PatFill, BitBlt, HostBlit, ColorExpand, LineDraw, HwCursor, Clip ### ATI Mach64 / Rage (atiMach64.c) | Chip | Device ID | Notes | |------|-----------|-------| | Mach64 GX | 0x4758 | I/O only | | Mach64 CX | 0x4358 | I/O only | | Mach64 CT | 0x4354 | MMIO at end of aperture | | Mach64 ET | 0x4554 | | | Mach64 VT | 0x5654, 0x5655 | | | 3D Rage II | 0x4754, 0x4755 | | | Rage Pro | 0x4750, 0x4752 | | | Rage 128 | 0x5245, 0x5246, 0x524B, 0x524C | | | Rage 128 Pro | 0x5046, 0x5052 | | Hardware ops: RectFill, PatFill, BitBlt, HostBlit, ColorExpand, LineDraw, HwCursor, Clip ### Matrox MGA (matroxMga.c) | Chip | Device ID | Notes | |------|-----------|-------| | Millennium (MGA2064W) | 0x0519 | Separate MMIO BAR | | Mystique (MGA1064SG) | 0x051A | | | G100 | 0x1000, 0x1001 | | | G200 | 0x0520, 0x0521 | | | G400 | 0x0525 | | | G450 | 0x2527 | | Hardware ops: RectFill, PatFill, BitBlt, HostBlit, ColorExpand, LineDraw, HwCursor, Clip ### 3dfx (banshee.c) | Chip | Device ID | Notes | |------|-----------|-------| | Banshee | 0x0003 | MMIO + launch area for data | | Voodoo3 | 0x0005 | | Hardware ops: RectFill, PatFill, BitBlt, HostBlit, ColorExpand, LineDraw, HwCursor, Clip ### Cirrus Logic GD54xx (cirrusGd54.c) | Chip | Device ID | Notes | |------|-----------|-------| | GD5434 | 0x00A0, 0x00A8 | BLT via GR registers | | GD5436 | 0x00AC | | | GD5446 | 0x00B8 | | | GD5480 | 0x00BC | | Hardware ops: RectFill, BitBlt, HostBlit, ColorExpand, HwCursor ### Cirrus Logic Laguna (cirrusLaguna.c) | Chip | Device ID | Notes | |------|-----------|-------| | GD5462 | 0x00D0 | MMIO, different engine from GD54xx | | GD5464 | 0x00D4 | | | GD5465 | 0x00D6 | | Hardware ops: RectFill, BitBlt, HostBlit, ColorExpand, HwCursor, Clip ### Nvidia RIVA / TNT (nvidia.c) | Chip | Device ID | Notes | |------|-----------|-------| | RIVA 128 | 0x0018 | PGRAPH subchannel interface | | RIVA 128 ZX | 0x0019 | | | TNT | 0x0020 | | | TNT2 | 0x0028 | | | TNT2 Ultra | 0x0029 | | | TNT2 M64 | 0x002D | | | Vanta | 0x002C | | Hardware ops: RectFill, BitBlt, HostBlit, HwCursor, Clip ### Tseng ET4000/W32 (tsengW32.c) | Chip | Device ID | Notes | |------|-----------|-------| | W32 | 0x3202 | ACL engine via I/O ports | | W32i | 0x3205 | | | W32p rev A | 0x3206 | HwCursor on W32p only | | W32p rev B | 0x3207 | | | W32p rev C | 0x3208 | | | W32p rev D | 0x4702 | | Hardware ops: RectFill, BitBlt, HostBlit, HwCursor (W32p only) ### Trident TGUI (trident.c) | Chip | Device ID | Notes | |------|-----------|-------| | TGUI9440 | 0x9440 | GER engine via I/O ports | | TGUI9660 | 0x9660 | | | TGUI9680 | 0x9680 | | | ProVidia 9685 | 0x9685 | | | Blade3D | 0x9880 | | | CyberBlade | 0x9910 | | Hardware ops: RectFill, BitBlt, HostBlit, HwCursor ### SiS (sis.c) | Chip | Device ID | Notes | |------|-----------|-------| | 6326 | 0x6326 | MMIO queue-based engine | | 300 | 0x0300 | | | 305 | 0x0305 | | | 315 | 0x0315 | | | 330 | 0x0330 | | Hardware ops: RectFill, BitBlt, HostBlit, HwCursor, Clip ## Capability Matrix Operations not implemented in hardware get automatic software fallbacks. Every function pointer is always callable -- callers never need to check for NULL. | Operation | S3 | ATI | Matrox | 3dfx | CL 54xx | CL Laguna | Nvidia | Tseng | Trident | SiS | |-----------|:--:|:---:|:------:|:----:|:-------:|:---------:|:------:|:-----:|:-------:|:---:| | RectFill | HW | HW | HW | HW | HW | HW | HW | HW | HW | HW | | PatFill | HW | HW | HW | HW | sw | sw | sw | sw | sw | sw | | BitBlt | HW | HW | HW | HW | HW | HW | HW | HW | HW | HW | | HostBlit | HW | HW | HW | HW | HW | HW | HW | HW | HW | HW | | ColorExpand | HW | HW | HW | HW | HW | HW | sw | sw | sw | sw | | LineDraw | HW | HW | HW | HW | sw | sw | sw | sw | sw | sw | | HwCursor | HW | HW | HW | HW | HW | HW | HW | W32p | HW | HW | | Clip | HW | HW | HW | HW | sw | HW | HW | sw | sw | HW | HW = hardware accelerated, sw = software fallback ## API Usage ### Basic Lifecycle ```c #include "accelVid.h" // Declare registration functions for the drivers you want extern void s3RegisterDriver(void); extern void atiRegisterDriver(void); // ... etc int main(void) { // 1. Register drivers (order = detection priority) s3RegisterDriver(); atiRegisterDriver(); // 2. Detect hardware AccelDriverT *drv = accelDetect(); if (!drv) { printf("No supported video card found\n"); return 1; } // 3. Initialize with a video mode AccelModeRequestT req; req.width = 640; req.height = 480; req.bpp = 16; if (!accelInit(drv, &req)) { printf("Failed to set video mode\n"); return 1; } // Mode info is now available printf("Mode: %dx%dx%d pitch=%d\n", drv->mode.width, drv->mode.height, drv->mode.bpp, drv->mode.pitch); // 4. Draw drv->rectFill(drv, 0, 0, 640, 480, 0x001F); // blue drv->waitIdle(drv); // 5. Shut down accelShutdown(drv); return 0; } ``` ### Drawing Operations All drawing functions take the driver pointer as the first argument. Colors are packed in the display's native pixel format. ```c // Solid rectangle fill drv->rectFill(drv, x, y, w, h, color); // 8x8 mono pattern fill (1=fg, 0=bg, MSB first, 8 bytes) uint8_t checkerboard[8] = { 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55 }; drv->rectFillPat(drv, x, y, w, h, checkerboard, fgColor, bgColor); // Screen-to-screen blit (handles overlapping regions) drv->bitBlt(drv, srcX, srcY, dstX, dstY, w, h); // CPU-to-screen blit (transfer RAM buffer to VRAM) // srcBuf = packed pixels in display format, srcPitch = byte stride drv->hostBlit(drv, buffer, pitch, dstX, dstY, w, h); // Monochrome color expansion (1bpp -> full color) // Each 1-bit becomes fg, each 0-bit becomes bg // srcBuf = packed MSB-first mono bitmap, srcPitch = byte stride drv->colorExpand(drv, glyphData, 1, dstX, dstY, 8, 16, fg, bg); // Bresenham line draw (inclusive endpoints) drv->lineDraw(drv, x1, y1, x2, y2, color); // Hardware clip rectangle drv->setClip(drv, clipX, clipY, clipW, clipH); ``` ### Hardware Cursor ```c // Define a cursor image (64x64 max, AND/XOR masks) HwCursorImageT cursor; cursor.width = 16; cursor.height = 16; cursor.hotX = 0; cursor.hotY = 0; memset(cursor.andMask, 0xFF, sizeof(cursor.andMask)); // transparent memset(cursor.xorMask, 0x00, sizeof(cursor.xorMask)); // ... fill in actual cursor shape ... // Upload and enable drv->setCursor(drv, &cursor); drv->showCursor(drv, true); // Move (call on every mouse poll) drv->moveCursor(drv, mouseX, mouseY); // Hide drv->showCursor(drv, false); ``` ### Checking Capabilities The `caps` field indicates which operations are hardware-accelerated. Software fallbacks are always installed, so you can call any operation regardless of caps. Use caps to make optimization decisions: ```c if (drv->caps & ACAP_COLOR_EXPAND) { // Use color expansion for text -- 16x less bus traffic drv->colorExpand(drv, glyph, 1, x, y, 8, 16, fg, bg); } else { // Software fallback is installed but may be slow -- // consider pre-rendering text to a RAM buffer instead drv->colorExpand(drv, glyph, 1, x, y, 8, 16, fg, bg); } if (drv->caps & ACAP_HW_CURSOR) { // Hardware cursor eliminates cursor dirty rectangles drv->setCursor(drv, &cursorImage); drv->showCursor(drv, true); } ``` ### Synchronization The acceleration engine runs asynchronously. Drawing functions return immediately after queuing the command. Use `waitIdle` before reading from VRAM or when you need all pending operations to complete: ```c drv->rectFill(drv, 0, 0, 100, 100, color1); drv->rectFill(drv, 50, 50, 100, 100, color2); drv->bitBlt(drv, 0, 0, 200, 0, 150, 150); // Wait for everything to finish before reading VRAM drv->waitIdle(drv); uint16_t pixel = *(uint16_t *)(drv->mode.framebuffer + offset); ``` ### Mode Information After `accelInit` succeeds, `drv->mode` contains: | Field | Description | |-------|-------------| | `width` | Horizontal resolution in pixels | | `height` | Vertical resolution in pixels | | `bpp` | Bits per pixel (8, 15, 16, or 32) | | `pitch` | Bytes per scanline (may exceed width * bpp/8) | | `framebuffer` | Direct pointer to the linear framebuffer | | `vramSize` | Total video RAM in bytes | | `offscreenBase` | Byte offset where offscreen VRAM begins | The framebuffer pointer can be used for direct pixel access when the acceleration engine doesn't offer a suitable operation. ## Adding a New Driver 1. Create a new source file (e.g., `newchip.c`) 2. Include `accelVid.h`, `vgaCommon.h`, and `pci.h` 3. Define a static `AccelDriverT` with your function pointers 4. Use shared helpers for boilerplate: - `vesaFindAndSetMode()` for VESA mode enumeration and setting - `dpmiMapFramebuffer()` for DPMI physical address mapping - `pciSizeBar()` for PCI BAR size detection 5. Leave unsupported operations as NULL -- the driver manager installs software fallbacks automatically 6. Add a registration function: `void newchipRegisterDriver(void)` 7. Add the source file to the Makefile and call the registration function from `main()` See `trident.c` (simplest driver) or `matroxMga.c` (most complete) as reference implementations. ## Building Requires a DJGPP cross-compiler targeting i586-pc-msdosdjgpp. ``` make # build bin/demo.exe make clean # remove build artifacts ``` The Makefile expects the DJGPP toolchain at `$HOME/djgpp/djgpp`. Override with `make DJGPP_PREFIX=/path/to/djgpp`. Compiler flags: `-O2 -Wall -Wextra -Werror -march=i486 -mtune=i586` ## Testing The `test/` directory contains an 86Box configuration for testing with an emulated S3 Trio64. See `test/README.txt` for setup instructions. ``` demo.exe [width height bpp] ``` Default mode: 640x480x16. Controls: SPACE cycles demos, B runs benchmarks, ESC exits. ## Project Structure ``` accelVid.h Driver abstraction and manager API accelVid.c Driver manager, software fallbacks pci.h / pci.c PCI configuration space access vgaCommon.h / .c Shared VGA registers, VESA, DPMI helpers s3Trio.c S3 Trio/ViRGE/Savage/Vision driver atiMach64.c ATI Mach64 / Rage driver matroxMga.c Matrox Millennium / Mystique / G-series driver banshee.c 3dfx Banshee / Voodoo3 driver cirrusGd54.c Cirrus Logic GD5434/36/46/80 driver cirrusLaguna.c Cirrus Logic Laguna GD5462/64/65 driver nvidia.c Nvidia RIVA 128 / TNT family driver tsengW32.c Tseng ET4000/W32 family driver trident.c Trident TGUI / Blade / CyberBlade driver sis.c SiS 6326/300/315 driver demo.c Test/demo application Makefile DJGPP cross-compilation build PLAN.md Architecture plan and chipset reference test/ 86Box test configuration and setup guide ```