/* idump.c - read pixels back through the Mystique's IDUMP (VRAM -> host through the 7 KB * DMAWIN) and compare with direct framebuffer reads. Claude Opus 5.5, 2026-10-14. * usage: idump small | frame W H */ #include #include #include #include "mga.h" #define OPMODE 0x1e54 #define RST 0x1e40 #define OP_IDUMP 0x0000000a #define BU32RGB 0x0e000000 #define DMAWIN 0x0000 #define DMAWIN_SZ 0x1c00 static int cmpd(const void *a, const void *b) { double x = *(const double *) a, y = *(const double *) b; return x < y ? -1 : x > y; } /* start an IDUMP of w x h pixels at (x, y) of a 16-bit surface with the given pitch */ static void idump_start(int x, int y, int w, int h, int pitch) { int start = y * pitch + x; mga_fifo(7); mga_out(DWGCTL, OP_IDUMP | SGNZERO | SHFTZERO | BOP_COPY | BU32RGB); mga_out(AR5, pitch); mga_out(AR3, start); mga_out(AR0, start + w - 1); mga_out(FXBNDRY, (w - 1) << 16); mga_out(YDSTLEN + GO, h); /* y = 0; the source is AR3 */ } /* read n dwords from the DMAWIN, addresses incrementing and wrapping */ static int use64 = 0; static void dmawin_read(unsigned int *d, int n) { if (use64) { /* 64-bit loads (lfd): n must be even */ volatile double *w = (volatile double *)(mga_base + DMAWIN); double *dd = (double *) d; int i, k = 0; for (i = 0; i < n / 2; i++) { dd[i] = w[k]; if (++k == DMAWIN_SZ / 8) k = 0; } return; } { volatile unsigned int *w = (volatile unsigned int *)(mga_base + DMAWIN); int i, k = 0; for (i = 0; i < n; i++) { d[i] = w[k]; if (++k == DMAWIN_SZ / 4) k = 0; } } } int main(int argc, char **argv) { unsigned int opm, *buf; int i; if (argc < 2) { fprintf(stderr, "usage: idump small | frame W H\n"); return 2; } use64 = getenv("USE64") != NULL; if (mga_open()) return 1; opm = mga_in(OPMODE); printf("OPMODE was 0x%08x\n", opm); mga_fifo(8); mga_out(MACCESS, PW16 | 0x40000000); mga_out(PITCH, 640); mga_out(YDSTORG, 0); mga_out(PLNWT, 0xffffffff); mga_out(CXLEFT, 0); mga_out(CXRIGHT, 639); mga_out(YTOP, 0); mga_out(YBOT, 1637 * 640); mga_out(OPMODE, (opm & ~0x30fu) | 0x100); /* dmaDataSiz = 01: 16 bpp big endian */ buf = (unsigned int *) malloc(640 * 480 * 2 + 64); /* malloc is 8-aligned */ /* known pattern in the first line: pixel x = 0x1000 * (x & 15) + x */ for (i = 0; i < 640; i++) { unsigned short v = (unsigned short)(0x1000 * (i & 15) + i); ((volatile unsigned short *) mga_fb)[i] = (unsigned short)((v >> 8) | (v << 8)); /* LE */ } if (!strcmp(argv[1], "small")) { idump_start(0, 0, 8, 1, 640); dmawin_read(buf, 4); for (i = 0; i < 4; i++) printf("dword %d: 0x%08x\n", i, buf[i]); printf("expect pixels 0x0000 0x1001 0x2002 0x3003 ...\n"); printf("STATUS 0x%08x\n", mga_in(STATUS)); } if (!strcmp(argv[1], "frame")) { int w = atoi(argv[2]), h = atoi(argv[3]), n = (w * 16 + 31) / 32 * h, k, bad = 0; double t[15], t0; for (k = 0; k < 15; k++) { t0 = mga_now(); idump_start(0, 0, w, h, 640); dmawin_read(buf, n); t[k] = mga_now() - t0; } mga_idle(); qsort(t, 15, sizeof(double), cmpd); for (i = 0; i < w / 2; i++) { unsigned int e = ((0x1000u * ((2 * i) & 15) + 2 * i) << 16) | (0x1000u * ((2 * i + 1) & 15) + 2 * i + 1); if (buf[i] != e) bad++; } printf("IDUMP %dx%d (%d dwords): min %.1f ms median %.1f ms, %.1f MB/s; first line %d/%d dwords as expected\n", w, h, n, t[0] * 1e3, t[7] * 1e3, n * 4 / t[7] / 1e6, w / 2 - bad, w / 2); } if (!strcmp(argv[1], "rows")) { /* every pixel of row y holds y; dump w x h at pitch p and see which rows come out */ int w = atoi(argv[2]), h = atoi(argv[3]), p = atoi(argv[4]), n = (w * 16 + 31) / 32 * h, y, x; volatile unsigned short *fb = (volatile unsigned short *) mga_fb; mga_fifo(1); mga_out(PITCH, p); for (y = 0; y < h + 40; y++) for (x = 0; x < p; x++) fb[y * p + x] = (unsigned short)((y >> 8) | (y << 8)); idump_start(0, 0, w, h, p); dmawin_read(buf, n); for (y = 0; y < 6; y++) printf("out row %d: first 0x%08x last 0x%08x\n", y, buf[y * w / 2], buf[y * w / 2 + w / 2 - 1]); printf("out row %d: first 0x%08x\n", h - 1, buf[(h - 1) * w / 2]); } mga_fifo(1); mga_out(OPMODE, opm); mga_close(); return 0; }