630 lines
19 KiB
C
630 lines
19 KiB
C
// trident.c -- Trident TGUI9440/9660/9680 accelerated video driver
|
|
//
|
|
// Supports the Trident TGUI family: TGUI9440, TGUI9660, TGUI9680,
|
|
// ProVidia 9685, Blade3D, and CyberBlade. These were common PCI
|
|
// chips in low-cost 1990s desktop and laptop systems.
|
|
//
|
|
// The TGUI 2D engine provides:
|
|
// - Solid rectangle fill (pattern source)
|
|
// - Screen-to-screen BitBLT
|
|
// - CPU-to-screen blit (host data transfer)
|
|
// - Hardware cursor (64x64)
|
|
//
|
|
// Register access:
|
|
// The GER (Graphics Engine Register) set uses I/O ports in the
|
|
// 0x2120-0x214F range. Operations are programmed by writing
|
|
// coordinates, dimensions, ROP, and command byte, then the engine
|
|
// executes asynchronously. Status is polled at 0x2120.
|
|
|
|
#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>
|
|
|
|
// ============================================================
|
|
// Trident vendor/device IDs
|
|
// ============================================================
|
|
|
|
#define TRIDENT_VENDOR_ID 0x1023
|
|
|
|
#define TRIDENT_TGUI9440 0x9440
|
|
#define TRIDENT_TGUI9660 0x9660
|
|
#define TRIDENT_TGUI9680 0x9680
|
|
#define TRIDENT_PROVIDIA 0x9685
|
|
#define TRIDENT_BLADE3D 0x9880
|
|
#define TRIDENT_CYBERBLADE 0x9910
|
|
|
|
static const uint16_t sTridentDeviceIds[] = {
|
|
TRIDENT_VENDOR_ID, TRIDENT_TGUI9440,
|
|
TRIDENT_VENDOR_ID, TRIDENT_TGUI9660,
|
|
TRIDENT_VENDOR_ID, TRIDENT_TGUI9680,
|
|
TRIDENT_VENDOR_ID, TRIDENT_PROVIDIA,
|
|
TRIDENT_VENDOR_ID, TRIDENT_BLADE3D,
|
|
TRIDENT_VENDOR_ID, TRIDENT_CYBERBLADE,
|
|
0, 0
|
|
};
|
|
|
|
// ============================================================
|
|
// GER (Graphics Engine Register) ports
|
|
// ============================================================
|
|
|
|
#define GER_STATUS 0x2120 // word: bit 0 = engine busy
|
|
#define GER_OPERMODE 0x2122 // word: bits 2:0 = bpp encoding
|
|
#define GER_COMMAND 0x2124 // byte: command register
|
|
#define GER_ROP 0x2125 // byte: raster operation
|
|
#define GER_FG_COLOR 0x2128 // dword: foreground color
|
|
#define GER_BG_COLOR 0x212C // dword: background color
|
|
#define GER_PAT_ADDR 0x2130 // dword: pattern address
|
|
#define GER_SRC_X 0x2138 // word: source X
|
|
#define GER_SRC_Y 0x213A // word: source Y
|
|
#define GER_DST_X 0x213C // word: destination X
|
|
#define GER_DST_Y 0x213E // word: destination Y
|
|
#define GER_DIM_X 0x2140 // word: width - 1
|
|
#define GER_DIM_Y 0x2142 // word: height - 1
|
|
#define GER_STYLE 0x2144 // dword: line style/pattern
|
|
#define GER_CKEY 0x2148 // dword: color key
|
|
|
|
// ============================================================
|
|
// GER status bits
|
|
// ============================================================
|
|
|
|
#define GER_STATUS_BUSY 0x0001
|
|
|
|
// ============================================================
|
|
// GER command byte encoding
|
|
// ============================================================
|
|
//
|
|
// Bit 0: X direction (0=left, 1=right)
|
|
// Bit 1: Y direction (0=up, 1=down)
|
|
// Bits 3:2: source select (00=video, 01=system, 10=pattern)
|
|
// Bit 4: draw enable (must be set)
|
|
// Bit 5: mono source
|
|
// Bits 7:6: command type (00=bitblt)
|
|
|
|
#define GER_CMD_X_RIGHT 0x01
|
|
#define GER_CMD_X_LEFT 0x00
|
|
#define GER_CMD_Y_DOWN 0x02
|
|
#define GER_CMD_Y_UP 0x00
|
|
#define GER_CMD_SRC_VIDEO 0x00
|
|
#define GER_CMD_SRC_SYSTEM 0x04
|
|
#define GER_CMD_SRC_PATTERN 0x08
|
|
#define GER_CMD_DRAW 0x10
|
|
#define GER_CMD_MONO 0x20
|
|
#define GER_CMD_BITBLT 0x00
|
|
|
|
// Composite commands
|
|
#define GER_CMD_SOLID_FILL (GER_CMD_BITBLT | GER_CMD_SRC_PATTERN | GER_CMD_DRAW | GER_CMD_X_RIGHT | GER_CMD_Y_DOWN)
|
|
#define GER_CMD_SCRBLT_FWD (GER_CMD_BITBLT | GER_CMD_SRC_VIDEO | GER_CMD_DRAW | GER_CMD_X_RIGHT | GER_CMD_Y_DOWN)
|
|
#define GER_CMD_HOSTBLT (GER_CMD_BITBLT | GER_CMD_SRC_SYSTEM | GER_CMD_DRAW | GER_CMD_X_RIGHT | GER_CMD_Y_DOWN)
|
|
|
|
// ============================================================
|
|
// GER opermode bpp encoding (bits 2:0)
|
|
// ============================================================
|
|
|
|
#define GER_BPP_8 0x00
|
|
#define GER_BPP_16 0x01
|
|
#define GER_BPP_32 0x02
|
|
|
|
// ============================================================
|
|
// ROPs for GER engine
|
|
// ============================================================
|
|
|
|
#define TGUI_ROP_COPY 0xCC
|
|
#define TGUI_ROP_PAT_COPY 0xF0
|
|
|
|
// ============================================================
|
|
// Hardware cursor
|
|
// ============================================================
|
|
//
|
|
// 64x64 cursor stored at end of VRAM. Each row is 16 bytes:
|
|
// 8 bytes AND mask followed by 8 bytes XOR mask.
|
|
// Enable via CRTC extended register 0x50 bit 7.
|
|
// Position via CRTC registers 0x40-0x43.
|
|
|
|
#define TGUI_CURSOR_SIZE 64
|
|
#define TGUI_CURSOR_BYTES (TGUI_CURSOR_SIZE * 16) // 1024 bytes
|
|
|
|
// ============================================================
|
|
// CRTC extended registers for cursor
|
|
// ============================================================
|
|
|
|
#define TGUI_CRTC_CURSOR_X_LO 0x40
|
|
#define TGUI_CRTC_CURSOR_X_HI 0x41
|
|
#define TGUI_CRTC_CURSOR_Y_LO 0x42
|
|
#define TGUI_CRTC_CURSOR_Y_HI 0x43
|
|
#define TGUI_CRTC_CURSOR_CTRL 0x50
|
|
|
|
// ============================================================
|
|
// Miscellaneous
|
|
// ============================================================
|
|
|
|
#define TGUI_MAX_IDLE_WAIT 1000000
|
|
|
|
// ============================================================
|
|
// Private driver state
|
|
// ============================================================
|
|
|
|
typedef struct {
|
|
uint32_t lfbPhysAddr;
|
|
uint32_t vramSize;
|
|
uint32_t cursorOffset;
|
|
int32_t bytesPerPixel;
|
|
int32_t screenPitch;
|
|
uint16_t chipId;
|
|
} TridentPrivateT;
|
|
|
|
// ============================================================
|
|
// Prototypes
|
|
// ============================================================
|
|
|
|
static void tgBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
|
|
static bool tgDetect(AccelDriverT *drv);
|
|
static uint8_t tgGetBppMode(int32_t bytesPerPixel);
|
|
static void tgHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
|
|
static bool tgInit(AccelDriverT *drv, const AccelModeRequestT *req);
|
|
static void tgMoveCursor(AccelDriverT *drv, int32_t x, int32_t y);
|
|
static void tgRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
|
|
static void tgSetCursor(AccelDriverT *drv, const HwCursorImageT *image);
|
|
static void tgShowCursor(AccelDriverT *drv, bool visible);
|
|
static void tgShutdown(AccelDriverT *drv);
|
|
static void tgUnlockRegs(void);
|
|
static void tgWaitIdle(AccelDriverT *drv);
|
|
|
|
// ============================================================
|
|
// Driver instance
|
|
// ============================================================
|
|
|
|
static TridentPrivateT sTridentPrivate;
|
|
|
|
static AccelDriverT sTridentDriver = {
|
|
.name = "Trident TGUI",
|
|
.chipFamily = "trident",
|
|
.caps = 0,
|
|
.privData = &sTridentPrivate,
|
|
.detect = tgDetect,
|
|
.init = tgInit,
|
|
.shutdown = tgShutdown,
|
|
.waitIdle = tgWaitIdle,
|
|
.setClip = NULL,
|
|
.rectFill = tgRectFill,
|
|
.rectFillPat = NULL,
|
|
.bitBlt = tgBitBlt,
|
|
.hostBlit = tgHostBlit,
|
|
.colorExpand = NULL,
|
|
.lineDraw = NULL,
|
|
.setCursor = tgSetCursor,
|
|
.moveCursor = tgMoveCursor,
|
|
.showCursor = tgShowCursor,
|
|
};
|
|
|
|
// ============================================================
|
|
// tridentRegisterDriver
|
|
// ============================================================
|
|
|
|
void tridentRegisterDriver(void) {
|
|
accelRegisterDriver(&sTridentDriver);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgBitBlt
|
|
// ============================================================
|
|
//
|
|
// Screen-to-screen BitBLT. Direction bits are set to handle
|
|
// overlapping source/destination regions correctly.
|
|
|
|
static void tgBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
tgWaitIdle(drv);
|
|
|
|
TridentPrivateT *priv = (TridentPrivateT *)drv->privData;
|
|
|
|
// Determine copy direction for overlap handling
|
|
uint8_t cmd = GER_CMD_BITBLT | GER_CMD_SRC_VIDEO | GER_CMD_DRAW;
|
|
|
|
int32_t sx = srcX;
|
|
int32_t sy = srcY;
|
|
int32_t dx = dstX;
|
|
int32_t dy = dstY;
|
|
|
|
if (dstY > srcY || (dstY == srcY && dstX > srcX)) {
|
|
// Copy bottom-to-top, right-to-left
|
|
sx += w - 1;
|
|
sy += h - 1;
|
|
dx += w - 1;
|
|
dy += h - 1;
|
|
cmd |= GER_CMD_X_LEFT | GER_CMD_Y_UP;
|
|
} else {
|
|
// Copy top-to-bottom, left-to-right
|
|
cmd |= GER_CMD_X_RIGHT | GER_CMD_Y_DOWN;
|
|
}
|
|
|
|
// Set operation mode (bpp)
|
|
outportw(GER_OPERMODE, tgGetBppMode(priv->bytesPerPixel));
|
|
|
|
// ROP: copy
|
|
outportb(GER_ROP, TGUI_ROP_COPY);
|
|
|
|
// Source coordinates
|
|
outportw(GER_SRC_X, sx);
|
|
outportw(GER_SRC_Y, sy);
|
|
|
|
// Destination coordinates
|
|
outportw(GER_DST_X, dx);
|
|
outportw(GER_DST_Y, dy);
|
|
|
|
// Dimensions (width - 1, height - 1)
|
|
outportw(GER_DIM_X, w - 1);
|
|
outportw(GER_DIM_Y, h - 1);
|
|
|
|
// Fire command
|
|
outportb(GER_COMMAND, cmd);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgDetect
|
|
// ============================================================
|
|
|
|
static bool tgDetect(AccelDriverT *drv) {
|
|
int32_t matchIdx;
|
|
|
|
if (!pciFindDeviceList(sTridentDeviceIds, &drv->pciDev, &matchIdx)) {
|
|
return false;
|
|
}
|
|
|
|
TridentPrivateT *priv = (TridentPrivateT *)drv->privData;
|
|
priv->chipId = drv->pciDev.deviceId;
|
|
|
|
switch (drv->pciDev.deviceId) {
|
|
case TRIDENT_TGUI9440:
|
|
drv->name = "Trident TGUI9440";
|
|
break;
|
|
case TRIDENT_TGUI9660:
|
|
drv->name = "Trident TGUI9660";
|
|
break;
|
|
case TRIDENT_TGUI9680:
|
|
drv->name = "Trident TGUI9680";
|
|
break;
|
|
case TRIDENT_PROVIDIA:
|
|
drv->name = "Trident ProVidia 9685";
|
|
break;
|
|
case TRIDENT_BLADE3D:
|
|
drv->name = "Trident Blade3D";
|
|
break;
|
|
case TRIDENT_CYBERBLADE:
|
|
drv->name = "Trident CyberBlade";
|
|
break;
|
|
default:
|
|
drv->name = "Trident TGUI";
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgGetBppMode
|
|
// ============================================================
|
|
//
|
|
// Return the GER_OPERMODE bpp encoding for the given bytes per pixel.
|
|
|
|
static uint8_t tgGetBppMode(int32_t bytesPerPixel) {
|
|
switch (bytesPerPixel) {
|
|
case 2:
|
|
return GER_BPP_16;
|
|
case 4:
|
|
return GER_BPP_32;
|
|
default:
|
|
return GER_BPP_8;
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgHostBlit
|
|
// ============================================================
|
|
//
|
|
// CPU-to-screen blit. Sets source select to system/CPU and feeds
|
|
// pixel data through the GER data port. Each scanline of source
|
|
// data is written as a series of 32-bit dwords.
|
|
|
|
static void tgHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
TridentPrivateT *priv = (TridentPrivateT *)drv->privData;
|
|
|
|
int32_t rowBytes = w * priv->bytesPerPixel;
|
|
int32_t padBytes = (rowBytes + 3) & ~3;
|
|
int32_t dwordsPerRow = padBytes / 4;
|
|
|
|
tgWaitIdle(drv);
|
|
|
|
// Set operation mode (bpp)
|
|
outportw(GER_OPERMODE, tgGetBppMode(priv->bytesPerPixel));
|
|
|
|
// ROP: copy
|
|
outportb(GER_ROP, TGUI_ROP_COPY);
|
|
|
|
// Source coordinates (not meaningful for host data, set to 0)
|
|
outportw(GER_SRC_X, 0);
|
|
outportw(GER_SRC_Y, 0);
|
|
|
|
// Destination coordinates
|
|
outportw(GER_DST_X, dstX);
|
|
outportw(GER_DST_Y, dstY);
|
|
|
|
// Dimensions
|
|
outportw(GER_DIM_X, w - 1);
|
|
outportw(GER_DIM_Y, h - 1);
|
|
|
|
// Fire host blit command
|
|
outportb(GER_COMMAND, GER_CMD_HOSTBLT);
|
|
|
|
// Feed pixel data row by row as dwords
|
|
for (int32_t row = 0; row < h; row++) {
|
|
const uint8_t *rowData = srcBuf + row * srcPitch;
|
|
|
|
for (int32_t d = 0; d < dwordsPerRow; d++) {
|
|
int32_t base = d * 4;
|
|
uint32_t dword = 0;
|
|
|
|
for (int32_t b = 0; b < 4; b++) {
|
|
int32_t idx = base + b;
|
|
uint8_t byte = (idx < rowBytes) ? rowData[idx] : 0;
|
|
dword |= (uint32_t)byte << (b * 8);
|
|
}
|
|
|
|
outportl(GER_SRC_X, dword);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgInit
|
|
// ============================================================
|
|
|
|
static bool tgInit(AccelDriverT *drv, const AccelModeRequestT *req) {
|
|
TridentPrivateT *priv = (TridentPrivateT *)drv->privData;
|
|
|
|
// Get LFB physical address from PCI BAR0
|
|
uint32_t bar0 = pciRead32(drv->pciDev.bus, drv->pciDev.dev,
|
|
drv->pciDev.func, PCI_BAR0);
|
|
priv->lfbPhysAddr = bar0 & 0xFFFFFFF0;
|
|
priv->vramSize = pciSizeBar(drv->pciDev.bus, drv->pciDev.dev,
|
|
drv->pciDev.func, PCI_BAR0);
|
|
|
|
// Unlock Trident extended registers
|
|
tgUnlockRegs();
|
|
|
|
// Find and set VESA mode
|
|
VesaModeResultT vesa;
|
|
if (!vesaFindAndSetMode(req->width, req->height, req->bpp, &vesa)) {
|
|
return false;
|
|
}
|
|
|
|
// Map LFB via DPMI
|
|
DpmiMappingT lfbMap;
|
|
if (!dpmiMapFramebuffer(priv->lfbPhysAddr, priv->vramSize, &lfbMap)) {
|
|
vgaRestoreTextMode();
|
|
return false;
|
|
}
|
|
|
|
// Fill in driver mode info
|
|
priv->bytesPerPixel = (vesa.bpp + 7) / 8;
|
|
priv->screenPitch = vesa.pitch;
|
|
|
|
drv->mode.width = vesa.width;
|
|
drv->mode.height = vesa.height;
|
|
drv->mode.bpp = vesa.bpp;
|
|
drv->mode.pitch = vesa.pitch;
|
|
drv->mode.framebuffer = lfbMap.ptr;
|
|
drv->mode.vramSize = priv->vramSize;
|
|
drv->mode.offscreenBase = vesa.pitch * vesa.height;
|
|
|
|
// Re-unlock after mode set (VESA BIOS may re-lock)
|
|
tgUnlockRegs();
|
|
|
|
// Set GER operation mode for current bpp
|
|
outportw(GER_OPERMODE, tgGetBppMode(priv->bytesPerPixel));
|
|
|
|
// Set up hardware cursor at end of VRAM
|
|
priv->cursorOffset = priv->vramSize - TGUI_CURSOR_BYTES;
|
|
priv->cursorOffset &= ~(uint32_t)(TGUI_CURSOR_BYTES - 1);
|
|
|
|
// Set cursor start address via CRTC extended registers
|
|
// The cursor address is stored as a byte offset divided by 1024
|
|
uint32_t cursorAddrReg = priv->cursorOffset / 1024;
|
|
vgaCrtcWrite(0x44, cursorAddrReg & 0xFF);
|
|
vgaCrtcWrite(0x45, (cursorAddrReg >> 8) & 0xFF);
|
|
|
|
drv->caps = ACAP_RECT_FILL | ACAP_BITBLT | ACAP_HOST_BLIT | ACAP_HW_CURSOR;
|
|
|
|
tgWaitIdle(drv);
|
|
return true;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgMoveCursor
|
|
// ============================================================
|
|
//
|
|
// Set the hardware cursor position via CRTC extended registers
|
|
// 0x40-0x43. X is at 0x40/0x41, Y is at 0x42/0x43.
|
|
|
|
static void tgMoveCursor(AccelDriverT *drv, int32_t x, int32_t y) {
|
|
(void)drv;
|
|
|
|
if (x < 0) { x = 0; }
|
|
if (y < 0) { y = 0; }
|
|
|
|
vgaCrtcWrite(TGUI_CRTC_CURSOR_X_LO, x & 0xFF);
|
|
vgaCrtcWrite(TGUI_CRTC_CURSOR_X_HI, (x >> 8) & 0x07);
|
|
vgaCrtcWrite(TGUI_CRTC_CURSOR_Y_LO, y & 0xFF);
|
|
vgaCrtcWrite(TGUI_CRTC_CURSOR_Y_HI, (y >> 8) & 0x07);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgRectFill
|
|
// ============================================================
|
|
//
|
|
// Solid rectangle fill using the GER engine in pattern source mode.
|
|
// The foreground color register provides the fill color, and the
|
|
// ROP is set to pattern copy (0xF0).
|
|
|
|
static void tgRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) {
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
TridentPrivateT *priv = (TridentPrivateT *)drv->privData;
|
|
|
|
tgWaitIdle(drv);
|
|
|
|
// Set operation mode (bpp)
|
|
outportw(GER_OPERMODE, tgGetBppMode(priv->bytesPerPixel));
|
|
|
|
// Foreground color for the fill
|
|
outportl(GER_FG_COLOR, color);
|
|
|
|
// ROP: pattern copy (solid fill uses fg color as pattern)
|
|
outportb(GER_ROP, TGUI_ROP_PAT_COPY);
|
|
|
|
// Destination coordinates
|
|
outportw(GER_DST_X, x);
|
|
outportw(GER_DST_Y, y);
|
|
|
|
// Dimensions (width - 1, height - 1)
|
|
outportw(GER_DIM_X, w - 1);
|
|
outportw(GER_DIM_Y, h - 1);
|
|
|
|
// Fire solid fill command
|
|
outportb(GER_COMMAND, GER_CMD_SOLID_FILL);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgSetCursor
|
|
// ============================================================
|
|
//
|
|
// Upload a cursor image to VRAM at the cursor offset. The TGUI
|
|
// cursor format is 64x64 with 16 bytes per row: 8 bytes AND mask
|
|
// followed by 8 bytes XOR mask.
|
|
|
|
static void tgSetCursor(AccelDriverT *drv, const HwCursorImageT *image) {
|
|
TridentPrivateT *priv = (TridentPrivateT *)drv->privData;
|
|
|
|
if (!image) {
|
|
tgShowCursor(drv, false);
|
|
return;
|
|
}
|
|
|
|
tgWaitIdle(drv);
|
|
|
|
uint8_t *cursorMem = drv->mode.framebuffer + priv->cursorOffset;
|
|
|
|
for (int32_t row = 0; row < TGUI_CURSOR_SIZE; row++) {
|
|
for (int32_t col = 0; col < 8; col++) {
|
|
int32_t srcIdx = row * 8 + col;
|
|
uint8_t andByte;
|
|
uint8_t xorByte;
|
|
|
|
if (row < image->height && col < (image->width + 7) / 8) {
|
|
andByte = image->andMask[srcIdx];
|
|
xorByte = image->xorMask[srcIdx];
|
|
} else {
|
|
// Transparent: AND=0xFF, XOR=0x00
|
|
andByte = 0xFF;
|
|
xorByte = 0x00;
|
|
}
|
|
|
|
cursorMem[row * 16 + col] = andByte;
|
|
cursorMem[row * 16 + col + 8] = xorByte;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgShowCursor
|
|
// ============================================================
|
|
//
|
|
// Enable or disable the hardware cursor via CRTC extended
|
|
// register 0x50, bit 7.
|
|
|
|
static void tgShowCursor(AccelDriverT *drv, bool visible) {
|
|
(void)drv;
|
|
|
|
uint8_t val = vgaCrtcRead(TGUI_CRTC_CURSOR_CTRL);
|
|
|
|
if (visible) {
|
|
val |= 0x80;
|
|
} else {
|
|
val &= ~0x80;
|
|
}
|
|
|
|
vgaCrtcWrite(TGUI_CRTC_CURSOR_CTRL, val);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgShutdown
|
|
// ============================================================
|
|
|
|
static void tgShutdown(AccelDriverT *drv) {
|
|
tgShowCursor(drv, false);
|
|
tgWaitIdle(drv);
|
|
vgaRestoreTextMode();
|
|
__djgpp_nearptr_disable();
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgUnlockRegs
|
|
// ============================================================
|
|
//
|
|
// Unlock Trident extended registers. Reading SR0B returns the
|
|
// chip version/ID and simultaneously unlocks the extended
|
|
// sequencer and CRTC registers. Then writing 0x01 to SR0E
|
|
// enables new-mode registers on TGUI chips.
|
|
|
|
static void tgUnlockRegs(void) {
|
|
// Read SR0B to unlock extensions (returns chip ID)
|
|
outportb(VGA_SEQ_INDEX, 0x0B);
|
|
(void)inportb(VGA_SEQ_DATA);
|
|
|
|
// Enable new-mode TGUI registers
|
|
outportb(VGA_SEQ_INDEX, 0x0E);
|
|
outportb(VGA_SEQ_DATA, 0x01);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// tgWaitIdle
|
|
// ============================================================
|
|
//
|
|
// Wait for the GER engine to finish. Polls the status register
|
|
// at 0x2120 until bit 0 (busy) clears.
|
|
|
|
static void tgWaitIdle(AccelDriverT *drv) {
|
|
(void)drv;
|
|
|
|
for (int32_t i = 0; i < TGUI_MAX_IDLE_WAIT; i++) {
|
|
if (!(inportw(GER_STATUS) & GER_STATUS_BUSY)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|