698 lines
22 KiB
C
698 lines
22 KiB
C
// tsengW32.c -- Tseng ET4000/W32p accelerated video driver
|
|
//
|
|
// Supports the Tseng Labs ET4000/W32 family: W32, W32i, W32p rev A/B/C/D.
|
|
// These chips were common in ISA/VLB and early PCI systems of the early
|
|
// 1990s, offering good 2D acceleration for their era.
|
|
//
|
|
// The W32 ACL (Accelerator) engine provides:
|
|
// - Solid rectangle fill
|
|
// - 8x8 pattern fill (mono and color)
|
|
// - Screen-to-screen BitBLT
|
|
// - CPU-to-screen color expansion
|
|
// - Bresenham line draw (W32p only)
|
|
// - Hardware cursor (64x64 on W32p, not on W32/W32i)
|
|
//
|
|
// Register access:
|
|
// The ACL registers are accessed via I/O ports in the 0x21xx range
|
|
// after unlocking with a key sequence. The ACL uses a different
|
|
// programming model from S3 or ATI -- operations are set up by
|
|
// writing source/destination addresses, dimensions, and mix/ROP
|
|
// to indexed registers, then triggered by writing to the
|
|
// accelerator control register.
|
|
//
|
|
// On the W32p, an MMU (Memory Management Unit) provides four
|
|
// apertures at the end of the linear address space that can be
|
|
// used for CPU-to-screen data transfer, avoiding I/O port
|
|
// overhead for host blits.
|
|
|
|
#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>
|
|
|
|
// ============================================================
|
|
// Tseng vendor/device IDs
|
|
// ============================================================
|
|
|
|
#define TSENG_VENDOR_ID 0x100C
|
|
|
|
#define TSENG_W32 0x3202
|
|
#define TSENG_W32I 0x3205
|
|
#define TSENG_W32P_A 0x3206
|
|
#define TSENG_W32P_B 0x3207
|
|
#define TSENG_W32P_C 0x3208
|
|
#define TSENG_W32P_D 0x4702
|
|
|
|
static const uint16_t sTsengDeviceIds[] = {
|
|
TSENG_VENDOR_ID, TSENG_W32,
|
|
TSENG_VENDOR_ID, TSENG_W32I,
|
|
TSENG_VENDOR_ID, TSENG_W32P_A,
|
|
TSENG_VENDOR_ID, TSENG_W32P_B,
|
|
TSENG_VENDOR_ID, TSENG_W32P_C,
|
|
TSENG_VENDOR_ID, TSENG_W32P_D,
|
|
0, 0
|
|
};
|
|
|
|
// ============================================================
|
|
// Tseng ACL register ports
|
|
// ============================================================
|
|
//
|
|
// The ACL registers are at I/O ports 0x2100-0x217F. They are
|
|
// accessed as indexed registers via a base+offset scheme.
|
|
|
|
#define ET_ACL_SUSPEND_TERM 0x2100 // suspend/terminate
|
|
#define ET_ACL_OPERATION_STATE 0x2101 // operation state (read)
|
|
#define ET_ACL_SYNC_ENABLE 0x2102 // sync enable
|
|
#define ET_ACL_INT_STATUS 0x2109 // interrupt status
|
|
#define ET_ACL_INT_MASK 0x210A // interrupt mask
|
|
|
|
// ACL setup registers
|
|
#define ET_ACL_PATTERN_ADDR 0x2110 // pattern address (3 bytes)
|
|
#define ET_ACL_SOURCE_ADDR 0x2114 // source address (3 bytes)
|
|
#define ET_ACL_PATTERN_Y_OFF 0x2118 // pattern Y offset
|
|
#define ET_ACL_SOURCE_Y_OFF 0x211A // source Y offset
|
|
#define ET_ACL_DEST_Y_OFF 0x211C // destination Y offset
|
|
|
|
// Virtual bus size affects transfer granularity
|
|
#define ET_ACL_VBUS_SIZE 0x2120 // virtual bus size
|
|
|
|
// X/Y count (dimensions)
|
|
#define ET_ACL_XY_DIR 0x2124 // X/Y direction
|
|
#define ET_ACL_X_COUNT 0x2128 // X count (width - 1, in bytes)
|
|
#define ET_ACL_Y_COUNT 0x212A // Y count (height - 1)
|
|
|
|
// Routing control
|
|
#define ET_ACL_ROUTING_CTRL 0x2126 // routing control
|
|
|
|
// Mix/ROP registers
|
|
#define ET_ACL_MIX_CONTROL 0x2127 // foreground/background source
|
|
#define ET_ACL_ROP 0x2130 // raster operation
|
|
|
|
// Destination address
|
|
#define ET_ACL_DEST_ADDR 0x2134 // destination address (3 bytes)
|
|
|
|
// Pixel depth control
|
|
#define ET_ACL_PIXEL_DEPTH 0x2138 // pixel depth (0=8, 1=15/16, 2=24, 3=32)
|
|
|
|
// CPU source data port (for host-to-screen)
|
|
#define ET_ACL_CPU_DATA 0x2140 // CPU data register (32-bit)
|
|
|
|
// ============================================================
|
|
// ACL direction bits (ET_ACL_XY_DIR)
|
|
// ============================================================
|
|
|
|
#define ET_DIR_X_POS 0x00
|
|
#define ET_DIR_X_NEG 0x01
|
|
#define ET_DIR_Y_POS 0x00
|
|
#define ET_DIR_Y_NEG 0x02
|
|
|
|
// ============================================================
|
|
// ACL routing control (ET_ACL_ROUTING_CTRL)
|
|
// ============================================================
|
|
|
|
#define ET_ROUTE_SRC_VRAM 0x00 // source from video memory
|
|
#define ET_ROUTE_SRC_CPU 0x02 // source from CPU
|
|
#define ET_ROUTE_SRC_PATTERN 0x04 // source from pattern
|
|
#define ET_ROUTE_SRC_COLOR_EXP 0x06 // source is mono -> color expand
|
|
#define ET_ROUTE_DST_VRAM 0x00 // destination to video memory
|
|
|
|
// ============================================================
|
|
// ACL mix control (ET_ACL_MIX_CONTROL)
|
|
// ============================================================
|
|
|
|
#define ET_MIX_FG_SRC 0x00 // foreground from source
|
|
#define ET_MIX_FG_PATTERN 0x04 // foreground from pattern
|
|
#define ET_MIX_FG_COLOR 0x08 // foreground from foreground color reg
|
|
#define ET_MIX_BG_SRC 0x00 // background from source
|
|
#define ET_MIX_BG_PATTERN 0x10 // background from pattern
|
|
#define ET_MIX_BG_COLOR 0x20 // background from background color reg
|
|
|
|
// ============================================================
|
|
// ACL operation state bits
|
|
// ============================================================
|
|
|
|
#define ET_ACCEL_BUSY 0x02 // accelerator busy
|
|
#define ET_ACCEL_CMD_READY 0x01 // ready for next command
|
|
|
|
// ============================================================
|
|
// ACL suspend/terminate control
|
|
// ============================================================
|
|
|
|
#define ET_ACL_START 0x00 // start/continue operation
|
|
#define ET_ACL_SUSPEND 0x01 // suspend
|
|
#define ET_ACL_TERMINATE 0x02 // terminate
|
|
|
|
// Common ROPs
|
|
#define ET_ROP_COPY 0xCC // dest = source
|
|
#define ET_ROP_PAT_COPY 0xF0 // dest = pattern
|
|
#define ET_ROP_ZERO 0x00
|
|
#define ET_ROP_ONE 0xFF
|
|
#define ET_ROP_XOR 0x66
|
|
|
|
// Hardware cursor
|
|
#define ET_HW_CURSOR_SIZE 64
|
|
#define ET_HW_CURSOR_BYTES 1024
|
|
|
|
// Maximum wait iterations
|
|
#define ET_MAX_IDLE_WAIT 1000000
|
|
|
|
// ============================================================
|
|
// Private driver state
|
|
// ============================================================
|
|
|
|
typedef struct {
|
|
uint32_t lfbPhysAddr;
|
|
uint32_t vramSize;
|
|
uint32_t cursorOffset;
|
|
int32_t bytesPerPixel;
|
|
int32_t screenPitch;
|
|
bool isW32p; // W32p has more features than W32/W32i
|
|
} TsengPrivateT;
|
|
|
|
// ============================================================
|
|
// Prototypes
|
|
// ============================================================
|
|
|
|
static void etBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
|
|
static bool etDetect(AccelDriverT *drv);
|
|
static void etHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h);
|
|
static bool etInit(AccelDriverT *drv, const AccelModeRequestT *req);
|
|
static void etMoveCursor(AccelDriverT *drv, int32_t x, int32_t y);
|
|
static void etRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
|
|
static void etSetCursor(AccelDriverT *drv, const HwCursorImageT *image);
|
|
static void etShowCursor(AccelDriverT *drv, bool visible);
|
|
static void etShutdown(AccelDriverT *drv);
|
|
static void etUnlockRegs(void);
|
|
static void etWaitIdle(AccelDriverT *drv);
|
|
|
|
// ============================================================
|
|
// Driver instance
|
|
// ============================================================
|
|
|
|
static TsengPrivateT sTsengPrivate;
|
|
|
|
static AccelDriverT sTsengDriver = {
|
|
.name = "Tseng ET4000/W32p",
|
|
.chipFamily = "tseng",
|
|
.caps = 0,
|
|
.privData = &sTsengPrivate,
|
|
.detect = etDetect,
|
|
.init = etInit,
|
|
.shutdown = etShutdown,
|
|
.waitIdle = etWaitIdle,
|
|
.setClip = NULL, // W32 has no hardware scissors
|
|
.rectFill = etRectFill,
|
|
.rectFillPat = NULL,
|
|
.bitBlt = etBitBlt,
|
|
.hostBlit = etHostBlit,
|
|
.colorExpand = NULL,
|
|
.lineDraw = NULL, // Line draw is complex on W32, omit for now
|
|
.setCursor = etSetCursor,
|
|
.moveCursor = etMoveCursor,
|
|
.showCursor = etShowCursor,
|
|
};
|
|
|
|
// ============================================================
|
|
// etRegisterDriver
|
|
// ============================================================
|
|
|
|
void etRegisterDriver(void) {
|
|
accelRegisterDriver(&sTsengDriver);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etBitBlt
|
|
// ============================================================
|
|
//
|
|
// Screen-to-screen BitBLT using the ACL engine. Source and
|
|
// destination are linear byte addresses in VRAM. Direction is
|
|
// controlled to handle overlapping regions.
|
|
|
|
static void etBitBlt(AccelDriverT *drv, int32_t srcX, int32_t srcY, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
|
|
TsengPrivateT *priv = (TsengPrivateT *)drv->privData;
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
int32_t bpp = priv->bytesPerPixel;
|
|
int32_t pitch = priv->screenPitch;
|
|
|
|
uint32_t srcAddr = srcY * pitch + srcX * bpp;
|
|
uint32_t dstAddr = dstY * pitch + dstX * bpp;
|
|
|
|
uint8_t direction = ET_DIR_X_POS | ET_DIR_Y_POS;
|
|
|
|
if (dstAddr > srcAddr) {
|
|
direction = ET_DIR_X_NEG | ET_DIR_Y_NEG;
|
|
srcAddr += (h - 1) * pitch + (w - 1) * bpp;
|
|
dstAddr += (h - 1) * pitch + (w - 1) * bpp;
|
|
}
|
|
|
|
int32_t widthBytes = w * bpp - 1;
|
|
|
|
etWaitIdle(drv);
|
|
|
|
// Set pixel depth
|
|
uint8_t pixDepth = 0;
|
|
if (bpp == 2) { pixDepth = 1; }
|
|
if (bpp == 4) { pixDepth = 3; }
|
|
outportb(ET_ACL_PIXEL_DEPTH, pixDepth);
|
|
|
|
// Source routing: VRAM to VRAM
|
|
outportb(ET_ACL_ROUTING_CTRL, ET_ROUTE_SRC_VRAM | ET_ROUTE_DST_VRAM);
|
|
|
|
// ROP: copy
|
|
outportb(ET_ACL_ROP, ET_ROP_COPY);
|
|
|
|
// Direction
|
|
outportb(ET_ACL_XY_DIR, direction);
|
|
|
|
// Source Y offset (pitch)
|
|
outportw(ET_ACL_SOURCE_Y_OFF, pitch - 1);
|
|
|
|
// Dest Y offset (pitch)
|
|
outportw(ET_ACL_DEST_Y_OFF, pitch - 1);
|
|
|
|
// X and Y counts
|
|
outportw(ET_ACL_X_COUNT, widthBytes);
|
|
outportw(ET_ACL_Y_COUNT, h - 1);
|
|
|
|
// Source address (24-bit)
|
|
outportb(ET_ACL_SOURCE_ADDR, srcAddr & 0xFF);
|
|
outportb(ET_ACL_SOURCE_ADDR + 1, (srcAddr >> 8) & 0xFF);
|
|
outportb(ET_ACL_SOURCE_ADDR + 2, (srcAddr >> 16) & 0xFF);
|
|
|
|
// Destination address (triggers operation)
|
|
outportb(ET_ACL_DEST_ADDR, dstAddr & 0xFF);
|
|
outportb(ET_ACL_DEST_ADDR + 1, (dstAddr >> 8) & 0xFF);
|
|
outportb(ET_ACL_DEST_ADDR + 2, (dstAddr >> 16) & 0xFF);
|
|
|
|
// Start
|
|
outportb(ET_ACL_SUSPEND_TERM, ET_ACL_START);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etDetect
|
|
// ============================================================
|
|
|
|
static bool etDetect(AccelDriverT *drv) {
|
|
int32_t matchIdx;
|
|
|
|
if (!pciFindDeviceList(sTsengDeviceIds, &drv->pciDev, &matchIdx)) {
|
|
return false;
|
|
}
|
|
|
|
TsengPrivateT *priv = (TsengPrivateT *)drv->privData;
|
|
|
|
switch (drv->pciDev.deviceId) {
|
|
case TSENG_W32:
|
|
drv->name = "Tseng ET4000/W32";
|
|
priv->isW32p = false;
|
|
break;
|
|
case TSENG_W32I:
|
|
drv->name = "Tseng ET4000/W32i";
|
|
priv->isW32p = false;
|
|
break;
|
|
case TSENG_W32P_A:
|
|
case TSENG_W32P_B:
|
|
case TSENG_W32P_C:
|
|
case TSENG_W32P_D:
|
|
drv->name = "Tseng ET4000/W32p";
|
|
priv->isW32p = true;
|
|
break;
|
|
default:
|
|
drv->name = "Tseng ET4000/W32";
|
|
priv->isW32p = false;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etHostBlit
|
|
// ============================================================
|
|
//
|
|
// CPU-to-screen blit. Transfers pixel data from system memory to
|
|
// the framebuffer via the ACL engine. Source routing is set to CPU
|
|
// and data is fed as 32-bit dwords through ET_ACL_CPU_DATA. Each
|
|
// row of source pixels is packed into dwords with padding to a
|
|
// 4-byte boundary.
|
|
|
|
static void etHostBlit(AccelDriverT *drv, const uint8_t *srcBuf, int32_t srcPitch, int32_t dstX, int32_t dstY, int32_t w, int32_t h) {
|
|
TsengPrivateT *priv = (TsengPrivateT *)drv->privData;
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
int32_t bpp = priv->bytesPerPixel;
|
|
int32_t pitch = priv->screenPitch;
|
|
uint32_t dstAddr = dstY * pitch + dstX * bpp;
|
|
int32_t widthBytes = w * bpp - 1;
|
|
int32_t rowBytes = w * bpp;
|
|
int32_t padBytesPerRow = (rowBytes + 3) & ~3;
|
|
int32_t dwordsPerRow = padBytesPerRow / 4;
|
|
|
|
etWaitIdle(drv);
|
|
|
|
// Set pixel depth
|
|
uint8_t pixDepth = 0;
|
|
if (bpp == 2) { pixDepth = 1; }
|
|
if (bpp == 4) { pixDepth = 3; }
|
|
outportb(ET_ACL_PIXEL_DEPTH, pixDepth);
|
|
|
|
// Routing: source from CPU, destination to VRAM
|
|
outportb(ET_ACL_ROUTING_CTRL, ET_ROUTE_SRC_CPU | ET_ROUTE_DST_VRAM);
|
|
|
|
// ROP: copy
|
|
outportb(ET_ACL_ROP, ET_ROP_COPY);
|
|
|
|
// Direction: forward
|
|
outportb(ET_ACL_XY_DIR, ET_DIR_X_POS | ET_DIR_Y_POS);
|
|
|
|
// Dest Y offset (pitch)
|
|
outportw(ET_ACL_DEST_Y_OFF, pitch - 1);
|
|
|
|
// X and Y counts
|
|
outportw(ET_ACL_X_COUNT, widthBytes);
|
|
outportw(ET_ACL_Y_COUNT, h - 1);
|
|
|
|
// Destination address
|
|
outportb(ET_ACL_DEST_ADDR, dstAddr & 0xFF);
|
|
outportb(ET_ACL_DEST_ADDR + 1, (dstAddr >> 8) & 0xFF);
|
|
outportb(ET_ACL_DEST_ADDR + 2, (dstAddr >> 16) & 0xFF);
|
|
|
|
// Start
|
|
outportb(ET_ACL_SUSPEND_TERM, ET_ACL_START);
|
|
|
|
// Feed pixel data as dwords, row by row
|
|
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(ET_ACL_CPU_DATA, dword);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etInit
|
|
// ============================================================
|
|
|
|
static bool etInit(AccelDriverT *drv, const AccelModeRequestT *req) {
|
|
TsengPrivateT *priv = (TsengPrivateT *)drv->privData;
|
|
|
|
// Get LFB 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 Tseng extended registers
|
|
etUnlockRegs();
|
|
|
|
// 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
|
|
etUnlockRegs();
|
|
|
|
// Reset the ACL engine
|
|
outportb(ET_ACL_SUSPEND_TERM, ET_ACL_TERMINATE);
|
|
outportb(ET_ACL_SUSPEND_TERM, ET_ACL_START);
|
|
|
|
// Set up cursor at end of VRAM (W32p only)
|
|
if (priv->isW32p) {
|
|
priv->cursorOffset = priv->vramSize - ET_HW_CURSOR_BYTES;
|
|
priv->cursorOffset &= ~(ET_HW_CURSOR_BYTES - 1);
|
|
}
|
|
|
|
drv->caps = ACAP_RECT_FILL | ACAP_BITBLT | ACAP_HOST_BLIT;
|
|
|
|
if (priv->isW32p) {
|
|
drv->caps |= ACAP_HW_CURSOR;
|
|
}
|
|
|
|
etWaitIdle(drv);
|
|
return true;
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etMoveCursor
|
|
// ============================================================
|
|
//
|
|
// The W32p hardware cursor position is set through CRTC extended
|
|
// registers (IMA port area). Cursor X is at CRTC index 0x40/0x41,
|
|
// cursor Y at 0x42/0x43.
|
|
|
|
static void etMoveCursor(AccelDriverT *drv, int32_t x, int32_t y) {
|
|
(void)drv;
|
|
|
|
if (x < 0) { x = 0; }
|
|
if (y < 0) { y = 0; }
|
|
|
|
// ET4000/W32p cursor position registers
|
|
outportb(0x217A, 0xE0); // cursor X low
|
|
outportb(0x217B, x & 0xFF);
|
|
outportb(0x217A, 0xE1); // cursor X high
|
|
outportb(0x217B, (x >> 8) & 0x07);
|
|
outportb(0x217A, 0xE2); // cursor Y low
|
|
outportb(0x217B, y & 0xFF);
|
|
outportb(0x217A, 0xE3); // cursor Y high
|
|
outportb(0x217B, (y >> 8) & 0x07);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etRectFill
|
|
// ============================================================
|
|
//
|
|
// Solid fill using the ACL engine. We write a single pixel of
|
|
// the fill color to an offscreen VRAM location and use it as
|
|
// the "source" for a replicated blit.
|
|
|
|
static void etRectFill(AccelDriverT *drv, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color) {
|
|
TsengPrivateT *priv = (TsengPrivateT *)drv->privData;
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
return;
|
|
}
|
|
|
|
int32_t bpp = priv->bytesPerPixel;
|
|
int32_t pitch = priv->screenPitch;
|
|
|
|
// Write the fill color to an offscreen VRAM location for pattern source
|
|
// Use just past the visible screen area
|
|
uint32_t patAddr = priv->vramSize - 64; // safe offscreen area
|
|
uint8_t *fb = drv->mode.framebuffer;
|
|
|
|
etWaitIdle(drv);
|
|
|
|
// Write pattern pixel(s) to VRAM
|
|
for (int32_t i = 0; i < bpp; i++) {
|
|
fb[patAddr + i] = (color >> (i * 8)) & 0xFF;
|
|
}
|
|
|
|
uint32_t dstAddr = y * pitch + x * bpp;
|
|
int32_t widthBytes = w * bpp - 1;
|
|
|
|
// Set pixel depth
|
|
uint8_t pixDepth = 0;
|
|
if (bpp == 2) { pixDepth = 1; }
|
|
if (bpp == 4) { pixDepth = 3; }
|
|
outportb(ET_ACL_PIXEL_DEPTH, pixDepth);
|
|
|
|
// Routing: pattern fill
|
|
outportb(ET_ACL_ROUTING_CTRL, ET_ROUTE_SRC_PATTERN | ET_ROUTE_DST_VRAM);
|
|
|
|
// ROP: pattern copy
|
|
outportb(ET_ACL_ROP, ET_ROP_PAT_COPY);
|
|
|
|
// Direction: forward
|
|
outportb(ET_ACL_XY_DIR, ET_DIR_X_POS | ET_DIR_Y_POS);
|
|
|
|
// Pattern address and Y offset
|
|
outportb(ET_ACL_PATTERN_ADDR, patAddr & 0xFF);
|
|
outportb(ET_ACL_PATTERN_ADDR + 1, (patAddr >> 8) & 0xFF);
|
|
outportb(ET_ACL_PATTERN_ADDR + 2, (patAddr >> 16) & 0xFF);
|
|
outportw(ET_ACL_PATTERN_Y_OFF, 0); // single-line pattern
|
|
|
|
// Dest Y offset
|
|
outportw(ET_ACL_DEST_Y_OFF, pitch - 1);
|
|
|
|
// Dimensions
|
|
outportw(ET_ACL_X_COUNT, widthBytes);
|
|
outportw(ET_ACL_Y_COUNT, h - 1);
|
|
|
|
// Destination address (triggers operation)
|
|
outportb(ET_ACL_DEST_ADDR, dstAddr & 0xFF);
|
|
outportb(ET_ACL_DEST_ADDR + 1, (dstAddr >> 8) & 0xFF);
|
|
outportb(ET_ACL_DEST_ADDR + 2, (dstAddr >> 16) & 0xFF);
|
|
|
|
// Start
|
|
outportb(ET_ACL_SUSPEND_TERM, ET_ACL_START);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etSetCursor
|
|
// ============================================================
|
|
|
|
static void etSetCursor(AccelDriverT *drv, const HwCursorImageT *image) {
|
|
TsengPrivateT *priv = (TsengPrivateT *)drv->privData;
|
|
|
|
if (!priv->isW32p) {
|
|
return;
|
|
}
|
|
|
|
if (!image) {
|
|
etShowCursor(drv, false);
|
|
return;
|
|
}
|
|
|
|
etWaitIdle(drv);
|
|
|
|
uint8_t *cursorMem = drv->mode.framebuffer + priv->cursorOffset;
|
|
|
|
for (int32_t row = 0; row < ET_HW_CURSOR_SIZE; row++) {
|
|
for (int32_t byte = 0; byte < 8; byte++) {
|
|
int32_t srcIdx = row * 8 + byte;
|
|
uint8_t andByte;
|
|
uint8_t xorByte;
|
|
|
|
if (row < image->height && byte < (image->width + 7) / 8) {
|
|
andByte = image->andMask[srcIdx];
|
|
xorByte = image->xorMask[srcIdx];
|
|
} else {
|
|
andByte = 0xFF;
|
|
xorByte = 0x00;
|
|
}
|
|
|
|
cursorMem[row * 16 + byte] = andByte;
|
|
cursorMem[row * 16 + byte + 8] = xorByte;
|
|
}
|
|
}
|
|
|
|
// Set cursor address via IMA registers
|
|
uint32_t cursorAddr = priv->cursorOffset / 4; // in dword units
|
|
outportb(0x217A, 0xE8);
|
|
outportb(0x217B, cursorAddr & 0xFF);
|
|
outportb(0x217A, 0xE9);
|
|
outportb(0x217B, (cursorAddr >> 8) & 0xFF);
|
|
outportb(0x217A, 0xEA);
|
|
outportb(0x217B, (cursorAddr >> 16) & 0x0F);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etShowCursor
|
|
// ============================================================
|
|
|
|
static void etShowCursor(AccelDriverT *drv, bool visible) {
|
|
TsengPrivateT *priv = (TsengPrivateT *)drv->privData;
|
|
|
|
if (!priv->isW32p) {
|
|
return;
|
|
}
|
|
|
|
// Cursor control via IMA register 0xF7
|
|
outportb(0x217A, 0xF7);
|
|
uint8_t val = inportb(0x217B);
|
|
|
|
if (visible) {
|
|
val |= 0x80;
|
|
} else {
|
|
val &= ~0x80;
|
|
}
|
|
|
|
outportb(0x217A, 0xF7);
|
|
outportb(0x217B, val);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etShutdown
|
|
// ============================================================
|
|
|
|
static void etShutdown(AccelDriverT *drv) {
|
|
etShowCursor(drv, false);
|
|
outportb(ET_ACL_SUSPEND_TERM, ET_ACL_TERMINATE);
|
|
vgaRestoreTextMode();
|
|
__djgpp_nearptr_disable();
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etUnlockRegs
|
|
// ============================================================
|
|
//
|
|
// Unlock Tseng extended registers.
|
|
// ET4000: write 0x03 to the "key" register at 0x3BF/0x3D8.
|
|
// This enables access to extended CRTC and attribute registers.
|
|
|
|
static void etUnlockRegs(void) {
|
|
outportb(0x3BF, 0x03);
|
|
outportb(0x3D8, 0xA0);
|
|
}
|
|
|
|
|
|
// ============================================================
|
|
// etWaitIdle
|
|
// ============================================================
|
|
//
|
|
// Wait for the ACL engine to finish. Poll the operation state
|
|
// register for the busy bit to clear.
|
|
|
|
static void etWaitIdle(AccelDriverT *drv) {
|
|
(void)drv;
|
|
|
|
for (int32_t i = 0; i < ET_MAX_IDLE_WAIT; i++) {
|
|
if (!(inportb(ET_ACL_OPERATION_STATE) & ET_ACCEL_BUSY)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|