Nugget
Loading...
Searching...
No Matches
raster-helpers.h
Go to the documentation of this file.
1/*
2
3MIT License
4
5Copyright (c) 2026 PCSX-Redux authors
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to deal
9in the Software without restriction, including without limitation the rights
10to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in all
15copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23SOFTWARE.
24
25*/
26
27#pragma once
28
29// Shared bare-metal helpers for the GPU rasterizer edge-behavior test suite.
30//
31// VRAM I/O patterns lifted from src/mips/arcade-tests/probe-common.h, adapted
32// for cester. The arcade-tests suite was characterizing 573 dual-bank VRAM
33// quirks via a custom PROBE_PASS / PROBE_FAIL log format; here we want
34// per-pixel cester assertions so failures surface as `expected X, received Y
35// at file:line` and feed a soft-renderer punch-list.
36//
37// IMPORTANT (per ../../../../.. learnings, src/gpu/gpu.md):
38// - GP1(0x00) full reset between tests, not GP1(0x01) FIFO-only.
39// - One waitGPU() before a multi-word GP0 command. Do NOT poll bit 26
40// between sub-words; it goes LOW for the duration of the multi-word
41// transfer.
42// - GP0(0xA0) upload H is silently capped to 511 per the formula
43// ((H-1) & 0x1FF) + 1; for >511-row fills, do multiple passes.
44// - GP0(0x02) fast-fill is DIFFERENT - H & 0x1FF, with H==512 producing
45// no fill. We deliberately do not use fast-fill in our test setup
46// because its own quirks are part of what other tests characterize.
47// - The 24-bit color field in GP0 primitive commands is 8:8:8. The GPU
48// >>3s each channel before composing the VRAM 5:5:5 pixel. To draw VRAM
49// value 0x001F (5:5:5 red) the command color must be 0x0000F8.
50
51#include <stdint.h>
52
53#include "common/hardware/dma.h"
54#include "common/hardware/gpu.h"
56#include "common/hardware/irq.h"
58
59// --------------------------------------------------------------------------
60// VRAM region constants
61// --------------------------------------------------------------------------
62
63// Tests draw into the lower half of VRAM at known coordinates. The draw
64// area covers a full 1024x512 region so primitives can land at extreme
65// corners without bumping into clipping unless explicitly testing it.
66#define RASTER_DRAW_AREA_X1 0
67#define RASTER_DRAW_AREA_Y1 0
68#define RASTER_DRAW_AREA_X2 1024
69#define RASTER_DRAW_AREA_Y2 512
70
71// Sentinel value VRAM is filled with before each test. Reading back a
72// sentinel pixel after drawing means the rasterizer chose NOT to write
73// that pixel; reading back any other value means it did.
74//
75// COLLISION CAVEAT (acknowledged 2026-05-15): 0xDEAD is a VALID VRAM 5:5:5
76// pixel value (mask=1, B=27, G=26, R=13). If a test primitive's actual
77// rendered color happens to equal 0xDEAD, an absence-test for that
78// coordinate produces a false positive (the rasterizer DID write but we
79// cannot tell it apart from the untouched sentinel). To avoid this:
80//
81// 1. The named primaries below (RASTER_VRAM_*) deliberately never
82// encode to 0xDEAD - all-red, all-green, all-blue, all-white are
83// max-channel-or-zero combinations that produce 0x001F, 0x03E0,
84// 0x7C00, 0x7FFF respectively. None equal 0xDEAD.
85// 2. When writing a test that uses a CUSTOM color (e.g., mask-bit
86// tests that intentionally render a mask-set value), check the
87// VRAM-side value at design time. If it collides with the sentinel,
88// switch THAT suite to a non-colliding sentinel via a local
89// override - do not change RASTER_SENTINEL globally because most
90// tests work fine with 0xDEAD and the diagnostic value is high.
91// 3. The OBS log lines emitted by ASSERT_PIXEL_EQ always show actual
92// values, so even a collision is recoverable by reading the log -
93// it only fools the cester pass/fail count.
94#define RASTER_SENTINEL 0xDEADu
95
96// --------------------------------------------------------------------------
97// Color encoding (5:5:5 VRAM <-> 8:8:8 GP0 command field)
98// --------------------------------------------------------------------------
99
100// Command-side color is 8:8:8. The GPU drops the low 3 bits of each channel
101// when composing the VRAM 5:5:5 pixel. To draw a specific VRAM color you
102// must left-shift each 5-bit channel by 3 before packing the command word.
103//
104// rasterCmdColor(r5, g5, b5) produces a 24-bit command color field that
105// will render as the requested 5:5:5 VRAM pixel.
106static inline uint32_t rasterCmdColor(uint8_t r5, uint8_t g5, uint8_t b5) {
107 uint32_t r8 = (uint32_t)(r5 & 0x1f) << 3;
108 uint32_t g8 = (uint32_t)(g5 & 0x1f) << 3;
109 uint32_t b8 = (uint32_t)(b5 & 0x1f) << 3;
110 return r8 | (g8 << 8) | (b8 << 16);
111}
112
113// rasterVram555(r5, g5, b5) builds the VRAM pixel value the above command
114// color will produce.
115static inline uint16_t rasterVram555(uint8_t r5, uint8_t g5, uint8_t b5) {
116 return (uint16_t)((r5 & 0x1f) | ((g5 & 0x1f) << 5) | ((b5 & 0x1f) << 10));
117}
118
119// Named primaries the tests will reach for. Each pair is (command, VRAM)
120// chosen so that the VRAM value is recognizable and asymmetric (i.e. not
121// confused with the sentinel or with another primary if a single channel
122// gets dropped or scrambled).
123#define RASTER_CMD_RED rasterCmdColor(0x1f, 0x00, 0x00) // command 0x0000F8
124#define RASTER_VRAM_RED rasterVram555(0x1f, 0x00, 0x00) // VRAM 0x001F
125#define RASTER_CMD_GREEN rasterCmdColor(0x00, 0x1f, 0x00) // command 0x00F800
126#define RASTER_VRAM_GREEN rasterVram555(0x00, 0x1f, 0x00) // VRAM 0x03E0
127#define RASTER_CMD_BLUE rasterCmdColor(0x00, 0x00, 0x1f) // command 0xF80000
128#define RASTER_VRAM_BLUE rasterVram555(0x00, 0x00, 0x1f) // VRAM 0x7C00
129#define RASTER_CMD_WHITE rasterCmdColor(0x1f, 0x1f, 0x1f) // command 0xF8F8F8
130#define RASTER_VRAM_WHITE rasterVram555(0x1f, 0x1f, 0x1f) // VRAM 0x7FFF
131
132// --------------------------------------------------------------------------
133// GPU reset / setup
134// --------------------------------------------------------------------------
135
136// Full-fat reset, modeled on src/mips/arcade-tests/probe-common.h.
137// Bring the GPU into a known polled-FIFO state so subsequent VRAM transfers
138// do not hang on DMA-readiness bits.
139static inline void rasterFullReset(void) {
140 IMASK = 0;
141 IREG = 0;
142 for (unsigned i = 0; i < 7; i++) {
143 DMA_CTRL[i].CHCR = 0;
144 DMA_CTRL[i].BCR = 0;
145 DMA_CTRL[i].MADR = 0;
146 }
147 DPCR = 0x800;
149 DICR = dicr;
150 DICR = 0;
151
152 // GP1(0x00) full reset (clears all internal latched state).
153 GPU_STATUS = 0x00000000;
154
155 // Restore a sane display mode. Not load-bearing for the tests; we just
156 // want the GPU out of any weird state Unirom may have left it in.
157 struct DisplayModeConfig config = {
159 .vResolution = VR_240,
160 .videoMode = VM_NTSC,
161 .colorDepth = CD_15BITS,
162 .videoInterlace = VI_OFF,
163 .hResolutionExtended = HRE_NORMAL,
164 };
165 setDisplayMode(&config);
166 setHorizontalRange(0, 0xa00);
167 setVerticalRange(16, 255);
168 setDisplayArea(0, 0);
169
170 // GP1(0x04, 1): DMA direction = CPU-to-FIFO polling. Unblocks the
171 // status bits that gate VRAM transfers when we write GPU_DATA from
172 // the CPU. Without this, GP0(0xA0)/GP0(0xC0) handshake hangs.
173 sendGPUStatus(0x04000001u);
174
175 // Drawing area + offset to a clean test default. Individual tests may
176 // override these.
179 setDrawingOffset(0, 0);
180
181 // Texture window = full 256x256 page (offset 0, mask 0). E2 command.
182 sendGPUData(0xe2000000u);
183 // Mask setting (E6) cleared - no set-mask, no check-mask.
184 sendGPUData(0xe6000000u);
185 // Dither off, draw to display area enabled, default texpage state.
186 // E1 command: texpage 0, semi mode 0, texdepth 0, dither 0, draw 1.
187 sendGPUData(0xe1000400u);
188}
189
190// Faster inter-test reset that preserves the display mode set in
191// rasterFullReset(). Returns the GPU to a clean E1/E2/E3/E5/E6 state and
192// re-enables FIFO polling mode. Use in CESTER_BEFORE_EACH; the heavyweight
193// rasterFullReset() runs once at BEFORE_ALL.
194static inline void rasterReset(void) {
195 sendGPUStatus(0x00000000u); // GP1(0x00) full reset
196 sendGPUStatus(0x04000001u); // GP1(0x04, 1) FIFO mode
199 setDrawingOffset(0, 0);
200 sendGPUData(0xe2000000u);
201 sendGPUData(0xe6000000u);
202 sendGPUData(0xe1000400u);
203}
204
205// --------------------------------------------------------------------------
206// VRAM I/O via GP0(0xA0) upload and GP0(0xC0) readback
207// --------------------------------------------------------------------------
208
209// Pace large streamed payloads via status bit 25 ("FIFO has room").
210// Without pacing, transfers of more than a few hundred words drop or
211// deadlock under FIFO mode. See gpu.md "GP0 Streaming Pace Bit".
212static inline void rasterStreamPace(int idx) {
213 if ((idx & 7) == 0) {
214 while ((GPU_STATUS & 0x02000000u) == 0) {
215 }
216 }
217}
218
219// Fill a rectangular VRAM region with a single 16-bit value via GP0(0xA0).
220// Width must be even (the GPU consumes data in 32-bit words = 2 pixels).
221// Height up to 511 - for larger fills make multiple calls; H==512 is
222// clean per the ((H-1)&0x1FF)+1 formula but writing it that way keeps the
223// data-phase word count exact.
224static inline void rasterFillRect(int16_t x, int16_t y, int16_t w, int16_t h,
225 uint16_t value) {
226 waitGPU();
227 GPU_DATA = 0xa0000000u;
228 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
229 GPU_DATA = ((uint32_t)(uint16_t)h << 16) | (uint32_t)(uint16_t)w;
230 uint32_t doubled = (uint32_t)value | ((uint32_t)value << 16);
231 int words = ((int)w * (int)h) >> 1;
232 for (int i = 0; i < words; i++) {
233 rasterStreamPace(i);
234 GPU_DATA = doubled;
235 }
236}
237
238// Read a single 16-bit pixel at (x, y) via GP0(0xC0).
239// The readback uses W=2 H=1 (the smallest natural transfer; one word holds
240// two packed 5:5:5 pixels) and returns the low half-word which is the
241// pixel at column x. Bit 27 ("VRAM-to-CPU ready") gates the read; under
242// FIFO mode 0x04000001 it advances correctly.
243static inline uint16_t rasterReadPixel(int16_t x, int16_t y) {
244 waitGPU();
245 GPU_DATA = 0xc0000000u;
246 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
247 GPU_DATA = ((uint32_t)(uint16_t)1 << 16) | (uint32_t)(uint16_t)2;
248 while ((GPU_STATUS & 0x08000000u) == 0) {
249 }
250 uint32_t word = GPU_DATA;
251 return (uint16_t)(word & 0xffff);
252}
253
254// Read a horizontal strip of |w| pixels starting at (x, y). |w| must be
255// even (round caller-side if odd). Output is written to |dst| as raw
256// 16-bit values in left-to-right order.
257static inline void rasterReadStrip(int16_t x, int16_t y, int16_t w,
258 uint16_t* dst) {
259 waitGPU();
260 GPU_DATA = 0xc0000000u;
261 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
262 GPU_DATA = ((uint32_t)(uint16_t)1 << 16) | (uint32_t)(uint16_t)w;
263 int words = (w + 1) >> 1;
264 for (int i = 0; i < words; i++) {
265 while ((GPU_STATUS & 0x08000000u) == 0) {
266 }
267 uint32_t word = GPU_DATA;
268 dst[i * 2] = (uint16_t)(word & 0xffff);
269 if (i * 2 + 1 < w) dst[i * 2 + 1] = (uint16_t)(word >> 16);
270 }
271}
272
273// Fill the working test rectangle with the sentinel before each draw. This
274// is just rasterFillRect with the named sentinel; kept as a separate name
275// so tests document intent.
276static inline void rasterClearTestRegion(int16_t x, int16_t y, int16_t w,
277 int16_t h) {
278 rasterFillRect(x, y, w, h, RASTER_SENTINEL);
279}
280
281// --------------------------------------------------------------------------
282// Primitive senders (flat shading, untextured first - simplest oracle)
283// --------------------------------------------------------------------------
284
285// GP0(0x20) flat untextured triangle. Verts are sign-extended 11-bit on the
286// silicon; we pass them as int16_t and let the GPU mask.
287static inline void rasterFlatTri(uint32_t cmdColor, int16_t x0, int16_t y0,
288 int16_t x1, int16_t y1, int16_t x2,
289 int16_t y2) {
290 waitGPU();
291 GPU_DATA = 0x20000000u | (cmdColor & 0x00ffffffu);
292 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
293 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
294 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
295}
296
297// GP0(0x22) semi-trans flat untextured triangle. Same layout as 0x20.
298// Blend mode comes from the current E1 ABR field (bits 5-6).
299static inline void rasterFlatTriSemi(uint32_t cmdColor, int16_t x0, int16_t y0,
300 int16_t x1, int16_t y1, int16_t x2,
301 int16_t y2) {
302 waitGPU();
303 GPU_DATA = 0x22000000u | (cmdColor & 0x00ffffffu);
304 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
305 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
306 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
307}
308
309// GP0(0x2A) semi-trans flat untextured quad.
310static inline void rasterFlatQuadSemi(uint32_t cmdColor, int16_t x0, int16_t y0,
311 int16_t x1, int16_t y1, int16_t x2,
312 int16_t y2, int16_t x3, int16_t y3) {
313 waitGPU();
314 GPU_DATA = 0x2a000000u | (cmdColor & 0x00ffffffu);
315 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
316 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
317 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
318 GPU_DATA = ((uint32_t)(uint16_t)y3 << 16) | (uint32_t)(uint16_t)x3;
319}
320
321// GP0(0x62) semi-trans variable-size rect.
322static inline void rasterFlatRectSemi(uint32_t cmdColor, int16_t x, int16_t y,
323 int16_t w, int16_t h) {
324 waitGPU();
325 GPU_DATA = 0x62000000u | (cmdColor & 0x00ffffffu);
326 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
327 GPU_DATA = ((uint32_t)(uint16_t)h << 16) | (uint32_t)(uint16_t)w;
328}
329
330// Set the current ABR (semi-trans blend mode) via E1. ABR is bits 5-6;
331// other E1 fields preserved at the test-default state.
332// 0 = B/2 + F/2 (average)
333// 1 = B + F (additive)
334// 2 = B - F (subtractive)
335// 3 = B + F/4 (add quarter)
336static inline void rasterSetAbr(uint8_t abr) {
337 uint32_t e1 = 0xe1000400u | ((uint32_t)(abr & 3) << 5);
338 sendGPUData(e1);
339}
340
341// Set the E6 mask control bits.
342// set_mask = 1 -> bit 15 of every drawn pixel is forced to 1
343// check_mask = 1 -> pixels with bit 15 already set in VRAM are not
344// overwritten (semi-trans bypassed for those pixels)
345static inline void rasterSetMaskCtrl(int set_mask, int check_mask) {
346 uint32_t e6 = 0xe6000000u |
347 ((uint32_t)(set_mask & 1)) |
348 ((uint32_t)(check_mask & 1) << 1);
349 sendGPUData(e6);
350}
351
352// GP0(0x28) flat untextured quad. Vertex order matters - the GPU
353// decomposes 0,1,2 + 1,2,3 internally (or so the soft renderer believes;
354// hardware truth is among the things this suite characterizes).
355static inline void rasterFlatQuad(uint32_t cmdColor, int16_t x0, int16_t y0,
356 int16_t x1, int16_t y1, int16_t x2,
357 int16_t y2, int16_t x3, int16_t y3) {
358 waitGPU();
359 GPU_DATA = 0x28000000u | (cmdColor & 0x00ffffffu);
360 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
361 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
362 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
363 GPU_DATA = ((uint32_t)(uint16_t)y3 << 16) | (uint32_t)(uint16_t)x3;
364}
365
366// GP0(0x40) flat untextured line.
367static inline void rasterFlatLine(uint32_t cmdColor, int16_t x0, int16_t y0,
368 int16_t x1, int16_t y1) {
369 waitGPU();
370 GPU_DATA = 0x40000000u | (cmdColor & 0x00ffffffu);
371 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
372 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
373}
374
375// GP0(0x42) semi-trans flat line.
376static inline void rasterFlatLineSemi(uint32_t cmdColor,
377 int16_t x0, int16_t y0,
378 int16_t x1, int16_t y1) {
379 waitGPU();
380 GPU_DATA = 0x42000000u | (cmdColor & 0x00ffffffu);
381 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
382 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
383}
384
385// GP0(0x50) gouraud line (per-vertex color).
386static inline void rasterGouraudLine(uint32_t c0, int16_t x0, int16_t y0,
387 uint32_t c1, int16_t x1, int16_t y1) {
388 waitGPU();
389 GPU_DATA = 0x50000000u | (c0 & 0x00ffffffu);
390 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
391 GPU_DATA = (c1 & 0x00ffffffu);
392 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
393}
394
395// GP0(0x48) flat polyline. 3-vertex variant (single 0x55555555
396// terminator after the third vertex). Higher-vertex polylines follow
397// the same pattern; we expose a 3-vertex form for the tests.
398static inline void rasterFlatPolyline3(uint32_t cmdColor,
399 int16_t x0, int16_t y0,
400 int16_t x1, int16_t y1,
401 int16_t x2, int16_t y2) {
402 waitGPU();
403 GPU_DATA = 0x48000000u | (cmdColor & 0x00ffffffu);
404 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
405 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
406 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
407 GPU_DATA = 0x55555555u; /* polyline terminator */
408}
409
410// GP0(0x60) flat variable-size rectangle.
411static inline void rasterFlatRect(uint32_t cmdColor, int16_t x, int16_t y,
412 int16_t w, int16_t h) {
413 waitGPU();
414 GPU_DATA = 0x60000000u | (cmdColor & 0x00ffffffu);
415 GPU_DATA = ((uint32_t)(uint16_t)y << 16) | (uint32_t)(uint16_t)x;
416 GPU_DATA = ((uint32_t)(uint16_t)h << 16) | (uint32_t)(uint16_t)w;
417}
418
419// GP0(0x30) gouraud (per-vertex shaded) untextured triangle. The
420// command-color of vertex 0 is packed into the leading word; vertices 1
421// and 2 each have their own 24-bit color words preceding their position
422// words. Each cN argument is a 24-bit 8:8:8 command-color (use
423// rasterCmdColor() to build one with VRAM-friendly channel values).
424//
425// Word layout (six 32-bit words total):
426// 0: 0x30 << 24 | c0_24bit
427// 1: y0 << 16 | x0
428// 2: 0x00 << 24 | c1_24bit
429// 3: y1 << 16 | x1
430// 4: 0x00 << 24 | c2_24bit
431// 5: y2 << 16 | x2
432static inline void rasterGouraudTri(uint32_t c0, int16_t x0, int16_t y0,
433 uint32_t c1, int16_t x1, int16_t y1,
434 uint32_t c2, int16_t x2, int16_t y2) {
435 waitGPU();
436 GPU_DATA = 0x30000000u | (c0 & 0x00ffffffu);
437 GPU_DATA = ((uint32_t)(uint16_t)y0 << 16) | (uint32_t)(uint16_t)x0;
438 GPU_DATA = (c1 & 0x00ffffffu);
439 GPU_DATA = ((uint32_t)(uint16_t)y1 << 16) | (uint32_t)(uint16_t)x1;
440 GPU_DATA = (c2 & 0x00ffffffu);
441 GPU_DATA = ((uint32_t)(uint16_t)y2 << 16) | (uint32_t)(uint16_t)x2;
442}
443
444// Toggle dither bit (E1, bit 9). Other E1 fields preserved at the test-
445// default state set by rasterReset/rasterFullReset. Test-cycle pattern:
446// rasterReset();
447// rasterSetDither(true);
448// ... draw ...
449// rasterSetDither(false); // restore for next test
450static inline void rasterSetDither(int on) {
451 // E1 base: texpage 0, semi 0, depth 0, draw-to-display on (bit 10).
452 uint32_t e1 = 0xe1000400u;
453 if (on) e1 |= 0x00000200u;
454 sendGPUData(e1);
455}
456
457// Wait for the GPU to drain after a primitive. Used before reading back
458// VRAM so we know the rasterizer has finished writing.
459static inline void rasterFlushPrimitive(void) { waitGPU(); }
460
461// --------------------------------------------------------------------------
462// Observation logging (ground-truth capture)
463// --------------------------------------------------------------------------
464
465// Every test that asserts a pixel value also logs the observed value via
466// ramsyscall_printf. Cester reports failures as "expected X, received Y at
467// file:line", which is the per-pixel diff signal; OBS lines provide
468// ground-truth capture regardless of pass/fail so a hardware run produces
469// a complete dump that can be greppped to patch raster-expected.h.
470//
471// Usage: ASSERT_PIXEL_EQ(EXPECT_FOO_x_y, x, y);
472#define ASSERT_PIXEL_EQ(expected, x_, y_) \
473 do { \
474 int16_t _ax = (int16_t)(x_); \
475 int16_t _ay = (int16_t)(y_); \
476 uint16_t _aval = rasterReadPixel(_ax, _ay); \
477 ramsyscall_printf("OBS x=%d y=%d val=0x%04x expect=0x%04x\n", \
478 (int)_ax, (int)_ay, (unsigned)_aval, \
479 (unsigned)(expected)); \
480 cester_assert_uint_eq((unsigned)(expected), (unsigned)_aval); \
481 } while (0)
482
483// When the expected value is a sentinel (the test is asserting NOTHING
484// drew at that pixel), use this for clarity at the call site.
485#define ASSERT_PIXEL_UNTOUCHED(x_, y_) \
486 ASSERT_PIXEL_EQ(RASTER_SENTINEL, (x_), (y_))
uint32_t dicr
Definition cester-cop0.c:98
@ VM_NTSC
Definition gpu.h:47
@ HRE_NORMAL
Definition gpu.h:62
@ VR_240
Definition gpu.h:42
@ HR_320
Definition gpu.h:36
@ VI_OFF
Definition gpu.h:57
@ CD_15BITS
Definition gpu.h:52
sendGPUStatus(0)
sendGPUData(0xe1000000)
#define DMA_CTRL
Definition dma.h:36
int i
Definition gte-regio.c:287
#define DPCR
Definition hwregs.h:49
#define GPU_DATA
Definition hwregs.h:52
#define IMASK
Definition hwregs.h:47
#define GPU_STATUS
Definition hwregs.h:53
#define DICR
Definition hwregs.h:50
#define IREG
Definition hwregs.h:46
#define RASTER_DRAW_AREA_Y2
Definition raster-helpers.h:69
#define RASTER_DRAW_AREA_X1
Definition raster-helpers.h:66
#define RASTER_DRAW_AREA_Y1
Definition raster-helpers.h:67
#define RASTER_DRAW_AREA_X2
Definition raster-helpers.h:68
#define RASTER_SENTINEL
Definition raster-helpers.h:94
Definition gpu.h:66
enum HResolution hResolution
Definition gpu.h:67
static int value
Definition syscalls.h:534
void int(code1, code2)
void uint32_t(classId, spec)