/* tex3d.c - see tex3d.h. Claude Opus 5.5, 2026-10-14. */ #include #include #include #include #include "mga.h" #include "tex3d.h" #define TMR0 0x2c00 #define TEXORG 0x2c24 #define TEXWIDTH 0x2c28 #define TEXHEIGHT 0x2c2c #define TEXCTL 0x2c30 #define TEXTRANS 0x2c34 #define OPMODE 0x1e54 #define RST 0x1e40 #define OP_TEXTRAP 0x00000006 #define OP_IDUMP 0x0000000a #define BU32RGB 0x0e000000 #define NODITHER 0x40000000 #define DMAWIN_SZ 0x1c00 #define TEX_BASE 0x12c000 /* textures from here to the end of the 2 MB */ static int W, H, P, texnext = TEX_BASE, texsize[16], texoff[16], ntex; static unsigned int opmode0, cmd; int t3_nodraw = 0; static int fx15(double v) { return (int) floor(v * 32768.0 + 0.5); } static int fx20(double v) { return (int) floor(v * 1048576.0 + 0.5); } static int fx16(double v) { return (int) floor(v * 65536.0 + 0.5); } static void ybot(int lines) { mga_fifo(1); mga_out(YBOT, (lines - 1) * P); } int t3_open(int w, int h) { if (w > 640 || h > 480 || (w & 1)) return -1; W = w; H = h; P = W <= 512 ? 512 : 640; /* PITCH must be one the engine can linearise: 512, 640, ... */ if (mga_open()) return -1; /* a previous owner may have died mid-IDUMP or mid-trapezoid: soft-reset the drawing * engine (flushes its FIFO, keeps memory; hold >= 10 us) and end any DMA sequence */ { double t = mga_now(); if (getenv("T3_DEBUG")) fprintf(stderr, "t3_open: before reset STATUS 0x%08x FIFOSTATUS 0x%08x OPMODE 0x%08x\n", mga_in(STATUS), mga_in(FIFOSTATUS), mga_in(OPMODE)); mga_out(RST, 1); while (mga_now() - t < 0.0002) ; mga_out(RST, 0); mga_outb(OPMODE, 0); if (getenv("T3_DEBUG")) fprintf(stderr, "t3_open: after reset STATUS 0x%08x FIFOSTATUS 0x%08x\n", mga_in(STATUS), mga_in(FIFOSTATUS)); } opmode0 = 0; mga_fifo(10); mga_out(MACCESS, PW16 | NODITHER); mga_out(PITCH, P); mga_out(YDSTORG, 0); mga_out(ZORG, P * 2 * H); mga_out(PLNWT, 0xffffffff); mga_out(CXLEFT, 0); mga_out(CXRIGHT, W - 1); mga_out(YTOP, 0); mga_out(YBOT, (H - 1) * P); mga_out(OPMODE, (opmode0 & ~0x30fu) | 0x100); /* DMAWIN: 16 bpp big endian */ cmd = OP_TEXTRAP | SHFTZERO | BOP_COPY | AT_ZI | Z_ZLT; return 0; } void t3_close(void) { mga_idle(); mga_fifo(1); mga_out(OPMODE, opmode0); mga_close(); } static void fill(int x, int y, int w, int h, unsigned int col) { mga_fifo(5); mga_out(DWGCTL, OP_TRAP | AT_RPL | SOLID | ARZERO | SGNZERO | SHFTZERO | BOP_COPY); mga_out(FCOL, col); mga_out(FXBNDRY, ((x + w) << 16) | x); mga_out(YDSTLEN + GO, (y << 16) | h); } void t3_clear(unsigned int c) { if (t3_nodraw) return; ybot(2 * H); fill(0, 0, W, H, c | c << 16); fill(0, H, W, H, 0xffffffff); ybot(H); } int t3_texture(const unsigned short *tex, int size) { int i, n = size * size; volatile unsigned short *d; if (ntex == 16 || texnext + n * 2 > VRAM_SIZE) return -1; d = (volatile unsigned short *)(mga_fb + texnext); mga_idle(); for (i = 0; i < n; i++) d[i] = (unsigned short)((tex[i] >> 8) | (tex[i] << 8)); /* LE */ texoff[ntex] = texnext; texsize[ntex] = size; texnext += (n * 2 + 31) & ~31; return ntex++; } void t3_bind(int t, int flags) { int l = 0, size = texsize[t], rf; unsigned int ctl; if (t3_nodraw) return; while ((1 << l) < size) l++; rf = (8 - l) & 63; ctl = 3 | ((l - 3) << 16) | (1 << 25); /* TW16, pitch, takey */ if (!(flags & T3_PERSP)) ctl |= 1 << 21; if (flags & T3_MODUL) ctl |= 1 << 29; if (flags & T3_CLAMP) ctl |= 3 << 27; mga_fifo(5); mga_out(TEXORG, texoff[t]); mga_out(TEXWIDTH, l | (rf << 9) | ((size - 1) << 18)); mga_out(TEXHEIGHT, l | (rf << 9) | ((size - 1) << 18)); mga_out(TEXCTL, ctl); mga_out(TEXTRANS, 0x0000ffff); } static void edge_at(int x0, int y0, int x1, int y1, int y, int *x, int *e) { int dx = x1 - x0, dy = y1 - y0, adx, k, step, sign, ex, xx; if (dx >= 0) { sign = 1; adx = dx; ex = 0; } else { sign = -1; adx = -dx; ex = 1 - dy; } step = (adx / dy) * sign; adx %= dy; xx = x0; for (k = y0; k < y; k++) { xx += step; if ((ex += adx) > 0) { xx += sign; ex -= dy; } } *x = xx; *e = ex; } #define NP 7 static void trap(int ya, int yb, int lx0, int ly0, int lx1, int ly1, int rx0, int ry0, int rx1, int ry1, const double *p0, const double *dx, const double *dy, int px, int py) { int xl, el, xr, er, dxl = lx1 - lx0, dxr = rx1 - rx0, sdxl, sdxr, ar2, ar5, i; double v[NP]; if (yb <= ya) return; edge_at(lx0, ly0, lx1, ly1, ya, &xl, &el); edge_at(rx0, ry0, rx1, ry1, ya, &xr, &er); if (t3_nodraw) return; sdxl = dxl < 0; ar2 = sdxl ? dxl : -dxl; sdxr = dxr < 0; ar5 = sdxr ? dxr : -dxr; for (i = 0; i < NP; i++) v[i] = p0[i] + (xl - px) * dx[i] + (ya - py) * dy[i]; mga_fifo(32); mga_out(DWGCTL, cmd); mga_out(DR0, fx15(v[0])); mga_out(DR2, fx15(dx[0])); mga_out(DR3, fx15(dy[0])); mga_out(DR4, fx15(v[1])); mga_out(DR6, fx15(dx[1])); mga_out(DR7, fx15(dy[1])); mga_out(DR8, fx15(v[2])); mga_out(DR10, fx15(dx[2])); mga_out(DR11, fx15(dy[2])); mga_out(DR12, fx15(v[3])); mga_out(DR14, fx15(dx[3])); mga_out(DR15, fx15(dy[3])); mga_out(TMR0, fx20(dx[4])); mga_out(TMR0 + 4, fx20(dy[4])); mga_out(TMR0 + 8, fx20(dx[5])); mga_out(TMR0 + 12, fx20(dy[5])); mga_out(TMR0 + 16, fx16(dx[6])); mga_out(TMR0 + 20, fx16(dy[6])); mga_out(TMR0 + 24, fx20(v[4])); mga_out(TMR0 + 28, fx20(v[5])); mga_out(TMR0 + 32, fx16(v[6])); mga_out(AR0, ly1 - ly0); mga_out(AR1, ar2 - el); mga_out(AR2, ar2); mga_out(AR4, ar5 - er); mga_out(AR5, ar5); mga_out(AR6, ry1 - ry0); mga_out(SGN, (sdxl << 1) | (sdxr << 5)); mga_out(FXBNDRY, ((xr & 0xffff) << 16) | (xl & 0xffff)); mga_out(YDSTLEN + GO, (ya << 16) | (yb - ya)); } void t3_tri(t3_vtx a, t3_vtx b, t3_vtx c) { t3_vtx t; double pa[NP], pb[NP], pc[NP], dx[NP], dy[NP], det, m, k; int i, ax, ay, bx, by, cx, cy; if (a.y > b.y) { t = a; a = b; b = t; } if (b.y > c.y) { t = b; b = c; c = t; } if (a.y > b.y) { t = a; a = b; b = t; } ax = (int) a.x; ay = (int) a.y; bx = (int) b.x; by = (int) b.y; cx = (int) c.x; cy = (int) c.y; if (ay == cy) return; det = (double)(bx - ax) * (cy - ay) - (double)(cx - ax) * (by - ay); if (det == 0) return; #define P(v, p) p[0] = v.z; p[1] = v.r; p[2] = v.g; p[3] = v.b; p[4] = v.s; p[5] = v.t; p[6] = v.q P(a, pa); P(b, pb); P(c, pc); m = pa[6] > pb[6] ? pa[6] : pb[6]; if (pc[6] > m) m = pc[6]; k = 1 / m; /* largest q = 1, for the 16.16 q gradients */ for (i = 4; i < 7; i++) { pa[i] *= k; pb[i] *= k; pc[i] *= k; } for (i = 0; i < NP; i++) { double e1 = pb[i] - pa[i], e2 = pc[i] - pa[i]; dx[i] = (e1 * (cy - ay) - e2 * (by - ay)) / det; dy[i] = (e2 * (bx - ax) - e1 * (cx - ax)) / det; } if (det > 0) { trap(ay, by, ax, ay, cx, cy, ax, ay, bx, by, pa, dx, dy, ax, ay); trap(by, cy, ax, ay, cx, cy, bx, by, cx, cy, pa, dx, dy, ax, ay); } else { trap(ay, by, ax, ay, bx, by, ax, ay, cx, cy, pa, dx, dy, ax, ay); trap(by, cy, bx, by, cx, cy, ax, ay, cx, cy, pa, dx, dy, ax, ay); } } int t3_finish(void) { return t3_nodraw ? 0 : mga_idle(); } void t3_read(unsigned int *dst) { volatile double *win = (volatile double *) mga_base; /* DMAWIN at offset 0 */ double *d = (double *) dst; int i, k = 0, n = W * H / 4; /* 8-byte loads */ mga_fifo(7); mga_out(DWGCTL, OP_IDUMP | SGNZERO | SHFTZERO | BOP_COPY | BU32RGB); mga_out(AR5, P); mga_out(AR3, 0); mga_out(AR0, W - 1); mga_out(FXBNDRY, (W - 1) << 16); mga_out(YDSTLEN + GO, H); for (i = 0; i < n; i++) { d[i] = win[k]; if (++k == DMAWIN_SZ / 8) k = 0; } }