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