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

715 lines
26 KiB
C

// banshee.c -- 3dfx Banshee/Voodoo3 accelerated video driver
//
// Supports the 3dfx Banshee and Voodoo3 2D/3D accelerators.
// The Banshee was 3dfx's first 2D/3D combo chip, and the Voodoo3
// improved on it with higher clock speeds. Both share the same
// 2D register interface:
// - Hardware rectangle fill
// - Screen-to-screen BitBLT
// - CPU-to-screen blit (host blit via launch area)
// - Monochrome color expansion (host blit with mono source)
// - Bresenham line draw
// - Hardware clip rectangle
// - 64x64 hardware cursor
//
// Register access:
// BAR0 maps the 32KB MMIO register block. The 2D engine
// registers live at offsets 0x200-0x270 within this block.
// The status register at 0x100 provides engine busy state.
//
// For host-to-screen operations, pixel data is fed through the
// "launch area" -- a write-combining window at MMIO physical
// address + 0x80000. Data is written as 32-bit dwords.
//
// BAR1 maps the linear framebuffer.
#include "accelVid.h"
#include "vgaCommon.h"
#include "pci.h"
#include <pc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/nearptr.h>
// ============================================================
// 3dfx vendor/device IDs
// ============================================================
#define TDFX_VENDOR_ID 0x121A
#define TDFX_BANSHEE 0x0003
#define TDFX_VOODOO3 0x0005
static const uint16_t sBansheeDeviceIds[] = {
TDFX_VENDOR_ID, TDFX_BANSHEE,
TDFX_VENDOR_ID, TDFX_VOODOO3,
0, 0
};
// ============================================================
// 2D engine register offsets (from MMIO base)
// ============================================================
#define BAN_STATUS 0x100 // bits 0-10: busy when any set
#define BAN_INTRCTRL 0x108 // interrupt control
#define BAN_CLIP0MIN 0x200 // clip rect 0 min (X | Y<<16)
#define BAN_CLIP0MAX 0x204 // clip rect 0 max (X | Y<<16)
#define BAN_DSTBASEADDR 0x208 // destination base address
#define BAN_DSTFORMAT 0x20C // pitch<<16 | bpp encoding
#define BAN_SRCCKMIN 0x210 // source color key min
#define BAN_SRCCKMAX 0x214 // source color key max
#define BAN_DSTCKMIN 0x218 // dest color key min
#define BAN_DSTCKMAX 0x21C // dest color key max
#define BAN_BRESERROR0 0x220 // Bresenham error 0
#define BAN_BRESERROR1 0x224 // Bresenham error 1
#define BAN_ROP 0x230 // raster operation (bits 7:0)
#define BAN_SRCBASEADDR 0x234 // source base address
#define BAN_COMMANDEXTRA 0x238 // command extra
#define BAN_LINESTIPPLE 0x23C // line stipple
#define BAN_LINESTYLE 0x240 // line style
#define BAN_PATTERN0 0x244 // pattern alias 0
#define BAN_PATTERN1 0x248 // pattern alias 1
#define BAN_CLIP1MIN 0x24C // clip rect 1 min
#define BAN_CLIP1MAX 0x250 // clip rect 1 max
#define BAN_SRCFORMAT 0x254 // pitch<<16 | bpp encoding
#define BAN_SRCSIZE 0x258 // width | height<<16
#define BAN_SRCXY 0x25C // X | Y<<16
#define BAN_COLORBACK 0x260 // background color
#define BAN_COLORFORE 0x264 // foreground color
#define BAN_DSTSIZE 0x268 // width | height<<16
#define BAN_DSTXY 0x26C // X | Y<<16
#define BAN_COMMAND 0x270 // command (triggers operation)
// ============================================================
// Command register encoding
// ============================================================
// Command types (bits 3:0)
#define BAN_CMD_NOP 0x00
#define BAN_CMD_S2S_BLIT 0x01 // screen-to-screen blit
#define BAN_CMD_S2S_STRETCH 0x02 // screen-to-screen stretch blit
#define BAN_CMD_H2S_BLIT 0x03 // host-to-screen blit
#define BAN_CMD_RECTFILL 0x05 // rectangle fill
#define BAN_CMD_LINEDRAW 0x06 // line draw
#define BAN_CMD_POLYLINE 0x07 // polyline
// Command flags
#define BAN_CMD_INITIATE (1 << 4) // must be set to start operation
#define BAN_CMD_STIPPLE (1 << 8) // stipple line
#define BAN_CMD_CLIPSEL1 (1 << 9) // use clip1 instead of clip0
#define BAN_CMD_SRCCKENA (1 << 12) // source color key enable
#define BAN_CMD_DSTCKENA (1 << 13) // dest color key enable
#define BAN_CMD_MONOPAT (1 << 14) // mono pattern
#define BAN_CMD_SRCMONO (1 << 15) // source is monochrome
// ============================================================
// BPP format encodings (for srcFormat/dstFormat low bits)
// ============================================================
#define BAN_FMT_8BPP 1
#define BAN_FMT_16BPP 3
#define BAN_FMT_32BPP 5
// ============================================================
// Status register
// ============================================================
#define BAN_STATUS_BUSY_MASK 0x7FF // bits 0-10: engine busy
// ============================================================
// Hardware cursor registers
// ============================================================
#define BAN_VIDPROCCFG 0x5C // bit 27 = cursor enable
#define BAN_CURSORLOC 0x60 // X | Y<<16
#define BAN_CURSOR_ENABLE (1 << 27)
// ============================================================
// Launch area
// ============================================================
#define BAN_LAUNCH_OFFSET 0x80000 // offset from MMIO phys base
#define BAN_LAUNCH_MAP_SIZE 4096 // map 4KB of launch area
// ============================================================
// Misc constants
// ============================================================
#define BAN_MMIO_SIZE 32768 // BAR0: 32KB MMIO
#define BAN_MAX_IDLE_WAIT 1000000
#define BAN_ROP_COPY 0xCC
#define BAN_HW_CURSOR_SIZE 64
// ============================================================
// Private driver state
// ============================================================
typedef struct {
uint32_t lfbPhysAddr;
uint32_t mmioPhysAddr;
uint32_t vramSize;
int32_t bytesPerPixel;
int32_t screenPitch;
uint32_t bppFormat;
volatile uint32_t *mmio;
volatile uint32_t *launch;
DpmiMappingT mmioMap;
DpmiMappingT lfbMap;
DpmiMappingT launchMap;
} BansheePrivateT;
// ============================================================
// Prototypes
// ============================================================
static void bansheeBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
static void bansheeColorExpand(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h, uint32_t fg, uint32_t bg);
static bool bansheeDetect(AccelDriverT *drv);
static void bansheeHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
static bool bansheeInit(AccelDriverT *drv, const AccelModeRequestT *req);
static void bansheeLineDraw(AccelDriverT *drv, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color);
static void bansheeMoveCursor(AccelDriverT *drv, int32_t x, int32_t y);
static void bansheeRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
static void bansheeRectFillPat(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *pattern, uint32_t fg, uint32_t bg);
static void bansheeSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h);
static void bansheeSetCursor(AccelDriverT *drv, const HwCursorImageT *image);
static void bansheeShowCursor(AccelDriverT *drv, bool visible);
static void bansheeShutdown(AccelDriverT *drv);
static void bansheeWaitIdle(AccelDriverT *drv);
static uint32_t bppToFormat(int32_t bpp);
static inline void bansheeWrite(BansheePrivateT *priv, uint32_t reg, uint32_t val) {
priv->mmio[reg / 4] = val;
}
static inline uint32_t bansheeRead(BansheePrivateT *priv, uint32_t reg) {
return priv->mmio[reg / 4];
}
// ============================================================
// Driver instance
// ============================================================
static BansheePrivateT sBansheePrivate;
static AccelDriverT sBansheeDriver = {
.name = "3dfx Banshee",
.chipFamily = "3dfx",
.caps = 0,
.privData = &sBansheePrivate,
.detect = bansheeDetect,
.init = bansheeInit,
.shutdown = bansheeShutdown,
.waitIdle = bansheeWaitIdle,
.setClip = bansheeSetClip,
.rectFill = bansheeRectFill,
.rectFillPat = bansheeRectFillPat,
.bitBlt = bansheeBitBlt,
.hostBlit = bansheeHostBlit,
.colorExpand = bansheeColorExpand,
.lineDraw = bansheeLineDraw,
.setCursor = bansheeSetCursor,
.moveCursor = bansheeMoveCursor,
.showCursor = bansheeShowCursor,
};
// ============================================================
// bansheeRegisterDriver
// ============================================================
void bansheeRegisterDriver(void) {
accelRegisterDriver(&sBansheeDriver);
}
// ============================================================
// bansheeBitBlt
// ============================================================
//
// Screen-to-screen BitBLT. The Banshee engine handles overlapping
// regions automatically when srcXY and dstXY are set correctly --
// the hardware determines the blit direction internally.
static void bansheeBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
if (w <= 0 || h <= 0) {
return;
}
bansheeWaitIdle(drv);
bansheeWrite(priv, BAN_SRCBASEADDR, 0);
bansheeWrite(priv, BAN_DSTBASEADDR, 0);
bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY);
bansheeWrite(priv, BAN_SRCSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_SRCXY, (uint32_t)srcX | ((uint32_t)srcY << 16));
bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16));
bansheeWrite(priv, BAN_COMMAND, BAN_CMD_S2S_BLIT | BAN_CMD_INITIATE);
}
// ============================================================
// bansheeColorExpand
// ============================================================
//
// Monochrome-to-color expansion using host-to-screen blit with
// the SRCMONO flag. Mono bitmap bits are expanded to fg/bg colors
// by the hardware. Data is fed as dwords through the launch area.
static void bansheeColorExpand(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h, uint32_t fg, uint32_t bg) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
if (w <= 0 || h <= 0) {
return;
}
int32_t bytesPerRow = (w + 7) / 8;
int32_t dwordsPerRow = (bytesPerRow + 3) / 4;
bansheeWaitIdle(drv);
bansheeWrite(priv, BAN_DSTBASEADDR, 0);
bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)bytesPerRow << 16) | BAN_FMT_8BPP);
bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY);
bansheeWrite(priv, BAN_COLORFORE, fg);
bansheeWrite(priv, BAN_COLORBACK, bg);
bansheeWrite(priv, BAN_SRCSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16));
bansheeWrite(priv, BAN_COMMAND, BAN_CMD_H2S_BLIT | BAN_CMD_INITIATE | BAN_CMD_SRCMONO);
// Feed mono data row by row through the launch area
for (int32_t row = 0; row < h; row++) {
const uint8_t *rowPtr = srcBuf + row * srcPitch;
for (int32_t dw = 0; dw < dwordsPerRow; dw++) {
uint32_t val = 0;
int32_t offset = dw * 4;
for (int32_t b = 0; b < 4; b++) {
if (offset + b < bytesPerRow) {
val |= (uint32_t)rowPtr[offset + b] << (b * 8);
}
}
priv->launch[0] = val;
}
}
}
// ============================================================
// bansheeDetect
// ============================================================
static bool bansheeDetect(AccelDriverT *drv) {
int32_t matchIdx;
if (!pciFindDeviceList(sBansheeDeviceIds, &drv->pciDev, &matchIdx)) {
return false;
}
switch (drv->pciDev.deviceId) {
case TDFX_BANSHEE:
drv->name = "3dfx Banshee";
break;
case TDFX_VOODOO3:
drv->name = "3dfx Voodoo3";
break;
default:
drv->name = "3dfx Banshee/Voodoo3";
break;
}
return true;
}
// ============================================================
// bansheeHostBlit
// ============================================================
//
// CPU-to-screen blit using host-to-screen command. Pixel data is
// fed as dwords through the launch area write-combining window.
static void bansheeHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
if (w <= 0 || h <= 0) {
return;
}
int32_t bytesPerRow = w * priv->bytesPerPixel;
int32_t dwordsPerRow = (bytesPerRow + 3) / 4;
bansheeWaitIdle(drv);
bansheeWrite(priv, BAN_DSTBASEADDR, 0);
bansheeWrite(priv, BAN_SRCBASEADDR, 0);
bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)(w * priv->bytesPerPixel) << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY);
bansheeWrite(priv, BAN_SRCSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_DSTXY, (uint32_t)dstX | ((uint32_t)dstY << 16));
bansheeWrite(priv, BAN_COMMAND, BAN_CMD_H2S_BLIT | BAN_CMD_INITIATE);
// Feed pixel data row by row through the launch area
for (int32_t row = 0; row < h; row++) {
const uint8_t *rowPtr = srcBuf + row * srcPitch;
for (int32_t dw = 0; dw < dwordsPerRow; dw++) {
uint32_t val = 0;
int32_t offset = dw * 4;
for (int32_t b = 0; b < 4; b++) {
if (offset + b < bytesPerRow) {
val |= (uint32_t)rowPtr[offset + b] << (b * 8);
}
}
priv->launch[0] = val;
}
}
}
// ============================================================
// bansheeInit
// ============================================================
static bool bansheeInit(AccelDriverT *drv, const AccelModeRequestT *req) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
// Read BARs
uint32_t bar0 = pciRead32(drv->pciDev.bus, drv->pciDev.dev,
drv->pciDev.func, PCI_BAR0);
uint32_t bar1 = pciRead32(drv->pciDev.bus, drv->pciDev.dev,
drv->pciDev.func, PCI_BAR1);
priv->mmioPhysAddr = bar0 & 0xFFFFFFF0;
priv->lfbPhysAddr = bar1 & 0xFFFFFFF0;
// Size the framebuffer BAR
priv->vramSize = pciSizeBar(drv->pciDev.bus, drv->pciDev.dev,
drv->pciDev.func, PCI_BAR1);
// Map MMIO control registers (32KB)
if (!dpmiMapFramebuffer(priv->mmioPhysAddr, BAN_MMIO_SIZE, &priv->mmioMap)) {
return false;
}
priv->mmio = (volatile uint32_t *)priv->mmioMap.ptr;
// Map launch area (4KB at MMIO phys + 0x80000)
if (!dpmiMapFramebuffer(priv->mmioPhysAddr + BAN_LAUNCH_OFFSET, BAN_LAUNCH_MAP_SIZE, &priv->launchMap)) {
dpmiUnmapFramebuffer(&priv->mmioMap);
return false;
}
priv->launch = (volatile uint32_t *)priv->launchMap.ptr;
// Find and set VESA mode
VesaModeResultT vesa;
if (!vesaFindAndSetMode(req->width, req->height, req->bpp, &vesa)) {
dpmiUnmapFramebuffer(&priv->launchMap);
dpmiUnmapFramebuffer(&priv->mmioMap);
return false;
}
// Map framebuffer
if (!dpmiMapFramebuffer(priv->lfbPhysAddr, priv->vramSize, &priv->lfbMap)) {
vgaRestoreTextMode();
dpmiUnmapFramebuffer(&priv->launchMap);
dpmiUnmapFramebuffer(&priv->mmioMap);
return false;
}
priv->bytesPerPixel = (vesa.bpp + 7) / 8;
priv->screenPitch = vesa.pitch;
priv->bppFormat = bppToFormat(vesa.bpp);
drv->mode.width = vesa.width;
drv->mode.height = vesa.height;
drv->mode.bpp = vesa.bpp;
drv->mode.pitch = vesa.pitch;
drv->mode.framebuffer = priv->lfbMap.ptr;
drv->mode.vramSize = priv->vramSize;
drv->mode.offscreenBase = vesa.pitch * vesa.height;
// Wait for engine idle before configuring
bansheeWaitIdle(drv);
// Set default engine state
bansheeWrite(priv, BAN_SRCBASEADDR, 0);
bansheeWrite(priv, BAN_DSTBASEADDR, 0);
bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_SRCFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY);
bansheeWrite(priv, BAN_COMMANDEXTRA, 0);
drv->caps = ACAP_RECT_FILL
| ACAP_RECT_FILL_PAT
| ACAP_BITBLT
| ACAP_HOST_BLIT
| ACAP_COLOR_EXPAND
| ACAP_LINE_DRAW
| ACAP_HW_CURSOR
| ACAP_CLIP;
// Full screen clip
bansheeSetClip(drv, 0, 0, vesa.width, vesa.height);
return true;
}
// ============================================================
// bansheeLineDraw
// ============================================================
//
// Bresenham line draw with inclusive endpoints. The Banshee engine
// takes start/end XY coordinates directly via srcXY/dstXY registers.
static void bansheeLineDraw(AccelDriverT *drv, int32_t x1, int32_t y1, int32_t x2, int32_t y2, uint32_t color) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
bansheeWaitIdle(drv);
bansheeWrite(priv, BAN_DSTBASEADDR, 0);
bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY);
bansheeWrite(priv, BAN_COLORFORE, color);
bansheeWrite(priv, BAN_SRCXY, (uint32_t)x1 | ((uint32_t)y1 << 16));
bansheeWrite(priv, BAN_DSTXY, (uint32_t)x2 | ((uint32_t)y2 << 16));
bansheeWrite(priv, BAN_COMMAND, BAN_CMD_LINEDRAW | BAN_CMD_INITIATE);
}
// ============================================================
// bansheeMoveCursor
// ============================================================
static void bansheeMoveCursor(AccelDriverT *drv, int32_t x, int32_t y) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
bansheeWrite(priv, BAN_CURSORLOC, (uint32_t)x | ((uint32_t)y << 16));
}
// ============================================================
// bansheeRectFill
// ============================================================
//
// Solid rectangle fill using the Banshee RECTFILL command. The
// foreground color is set, coordinates and dimensions are loaded,
// and the command register triggers the fill.
static void bansheeRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
if (w <= 0 || h <= 0) {
return;
}
bansheeWaitIdle(drv);
bansheeWrite(priv, BAN_DSTBASEADDR, 0);
bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY);
bansheeWrite(priv, BAN_COLORFORE, color);
bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_DSTXY, (uint32_t)x | ((uint32_t)y << 16));
bansheeWrite(priv, BAN_COMMAND, BAN_CMD_RECTFILL | BAN_CMD_INITIATE);
}
// ============================================================
// bansheeRectFillPat
// ============================================================
//
// 8x8 mono pattern fill using the Banshee RECTFILL command with
// BAN_CMD_MONOPAT. The pattern is 8 bytes (one per row, MSB-first),
// written to pattern0Alias and pattern1Alias as two 32-bit values.
// 1-bits use the foreground color, 0-bits use the background.
static void bansheeRectFillPat(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, const uint8_t *pattern, uint32_t fg, uint32_t bg) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
if (w <= 0 || h <= 0) {
return;
}
// Pack pattern rows 0-3 into PATTERN0 and rows 4-7 into PATTERN1
uint32_t pat0 = (uint32_t)pattern[0]
| ((uint32_t)pattern[1] << 8)
| ((uint32_t)pattern[2] << 16)
| ((uint32_t)pattern[3] << 24);
uint32_t pat1 = (uint32_t)pattern[4]
| ((uint32_t)pattern[5] << 8)
| ((uint32_t)pattern[6] << 16)
| ((uint32_t)pattern[7] << 24);
bansheeWaitIdle(drv);
bansheeWrite(priv, BAN_DSTBASEADDR, 0);
bansheeWrite(priv, BAN_DSTFORMAT, ((uint32_t)priv->screenPitch << 16) | priv->bppFormat);
bansheeWrite(priv, BAN_ROP, BAN_ROP_COPY);
bansheeWrite(priv, BAN_COLORFORE, fg);
bansheeWrite(priv, BAN_COLORBACK, bg);
bansheeWrite(priv, BAN_PATTERN0, pat0);
bansheeWrite(priv, BAN_PATTERN1, pat1);
bansheeWrite(priv, BAN_DSTSIZE, (uint32_t)w | ((uint32_t)h << 16));
bansheeWrite(priv, BAN_DSTXY, (uint32_t)x | ((uint32_t)y << 16));
bansheeWrite(priv, BAN_COMMAND, BAN_CMD_RECTFILL | BAN_CMD_INITIATE | BAN_CMD_MONOPAT);
}
// ============================================================
// bansheeSetClip
// ============================================================
static void bansheeSetClip(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
bansheeWrite(priv, BAN_CLIP0MIN, (uint32_t)x | ((uint32_t)y << 16));
bansheeWrite(priv, BAN_CLIP0MAX, (uint32_t)(x + w) | ((uint32_t)(y + h) << 16));
}
// ============================================================
// bansheeSetCursor
// ============================================================
//
// The Banshee hardware cursor is a 64x64 two-color cursor stored
// in VRAM. The format is 2 bits per pixel: AND plane followed by
// XOR plane, packed as 64x64 = 1024 bytes per plane.
static void bansheeSetCursor(AccelDriverT *drv, const HwCursorImageT *image) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
if (!image) {
bansheeShowCursor(drv, false);
return;
}
bansheeWaitIdle(drv);
// Store cursor image at end of VRAM (1KB AND + 1KB XOR = 2KB)
uint32_t cursorOffset = priv->vramSize - 2048;
cursorOffset &= ~0x7FF; // align to 2KB
uint8_t *cursorMem = drv->mode.framebuffer + cursorOffset;
// Write AND mask then XOR mask, each 64x64 / 8 = 512 bytes
for (int32_t row = 0; row < BAN_HW_CURSOR_SIZE; row++) {
for (int32_t byteIdx = 0; byteIdx < 8; byteIdx++) {
int32_t srcIdx = row * 8 + byteIdx;
uint8_t andByte;
uint8_t xorByte;
if (row < image->height && byteIdx < (image->width + 7) / 8) {
andByte = image->andMask[srcIdx];
xorByte = image->xorMask[srcIdx];
} else {
andByte = 0xFF; // transparent
xorByte = 0x00;
}
cursorMem[row * 16 + byteIdx] = andByte;
cursorMem[row * 16 + byteIdx + 8] = xorByte;
}
}
}
// ============================================================
// bansheeShowCursor
// ============================================================
static void bansheeShowCursor(AccelDriverT *drv, bool visible) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
uint32_t vidProcCfg = bansheeRead(priv, BAN_VIDPROCCFG);
if (visible) {
vidProcCfg |= BAN_CURSOR_ENABLE;
} else {
vidProcCfg &= ~BAN_CURSOR_ENABLE;
}
bansheeWrite(priv, BAN_VIDPROCCFG, vidProcCfg);
}
// ============================================================
// bansheeShutdown
// ============================================================
static void bansheeShutdown(AccelDriverT *drv) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
bansheeShowCursor(drv, false);
vgaRestoreTextMode();
dpmiUnmapFramebuffer(&priv->launchMap);
dpmiUnmapFramebuffer(&priv->lfbMap);
dpmiUnmapFramebuffer(&priv->mmioMap);
priv->mmio = NULL;
priv->launch = NULL;
}
// ============================================================
// bansheeWaitIdle
// ============================================================
//
// Wait until the 2D engine is completely idle. Bits 0-10 of the
// status register must all be zero.
static void bansheeWaitIdle(AccelDriverT *drv) {
BansheePrivateT *priv = (BansheePrivateT *)drv->privData;
for (int32_t i = 0; i < BAN_MAX_IDLE_WAIT; i++) {
uint32_t stat = bansheeRead(priv, BAN_STATUS);
if (!(stat & BAN_STATUS_BUSY_MASK)) {
return;
}
}
}
// ============================================================
// bppToFormat
// ============================================================
//
// Convert bits-per-pixel to the Banshee srcFormat/dstFormat
// encoding for the low bits of those registers.
static uint32_t bppToFormat(int32_t bpp) {
switch (bpp) {
case 8:
return BAN_FMT_8BPP;
case 15:
case 16:
return BAN_FMT_16BPP;
case 32:
return BAN_FMT_32BPP;
default:
return BAN_FMT_16BPP;
}
}