Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
uv-boundary.c
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// U/V boundary probes. Each test renders a 1-pixel-wide textured
28// rect at the chosen UV and reads back the texel. UVs that exceed
29// the depth-dependent page width are the interesting cases.
30
31// clang-format off
32
34
35/* The off-page probes read VRAM outside the fixtures. Write a known
36 value there first, so the readback says which address was sampled
37 instead of whatever the console happened to leave in VRAM. Then
38 flush the texture cache so the draw sees it. */
39static void writeVramPixel(uint16_t x, uint16_t y, uint16_t value) {
40 waitGPU();
41 GPU_DATA = 0xa0000000u;
42 GPU_DATA = ((uint32_t)y << 16) | x;
43 GPU_DATA = (1u << 16) | 1u;
45 waitGPU();
46 GPU_DATA = 0x01000000u;
47}
48
49static void drawTex8At(uint8_t u, uint8_t v) {
50 rasterReset();
51 rasterClearTestRegion(0, 0, 32, 8);
52 setTexpage(TEX8_TX, TEX8_TY, 1);
53 setTextureWindow(0, 0, 0, 0);
54 rasterTexRect(TEX_MOD_NEUTRAL, 0, 0, u, v, 1, 1, CLUT8_FIELD);
55 rasterFlushPrimitive();
56}
57
58static void drawTex15At(uint8_t u, uint8_t v) {
59 rasterReset();
60 rasterClearTestRegion(0, 0, 32, 8);
61 setTexpage(TEX15_TX, TEX15_TY, 2);
62 setTextureWindow(0, 0, 0, 0);
63 rasterTexRect(TEX_MOD_NEUTRAL, 0, 0, u, v, 1, 1, CLUT15_FIELD);
64 rasterFlushPrimitive();
65}
66
67static void drawTex4At(uint8_t u, uint8_t v) {
68 rasterReset();
69 rasterClearTestRegion(0, 0, 32, 8);
70 setTexpage(TEX4_TX, TEX4_TY, 0);
71 setTextureWindow(0, 0, 0, 0);
72 rasterTexRect(TEX_MOD_NEUTRAL, 0, 0, u, v, 1, 1, CLUT4_FIELD);
73 rasterFlushPrimitive();
74}
75
76) // CESTER_BODY
77
78// ============================================================================
79// 8-bit page (128 wide × 256 tall in texel coords). U > 127 is the
80// boundary. Captured value lets us infer wrap-mod / clamp / extend.
81// ============================================================================
82
83CESTER_TEST(uv8_u0_v0, gpu_raster_phase16,
84 drawTex8At(0, 0);
85 /* Baseline: u=0 -> CLUT8[0]. */
86 ASSERT_PIXEL_EQ(expectedClut8Color(0), 0, 0);
87)
88
89CESTER_TEST(uv8_u127_v0, gpu_raster_phase16,
90 drawTex8At(127, 0);
91 /* Last in-page texel for 8-bit (page width = 128). */
92 ASSERT_PIXEL_EQ(expectedClut8Color(127), 0, 0);
93)
94
95CESTER_TEST(uv8_u128_v0, gpu_raster_phase16,
96 drawTex8At(128, 0);
97 /* First off-page texel. If hardware wraps mod 128 -> CLUT8[0].
98 If hardware extends into the next VRAM region -> something
99 else. */
101)
102
103CESTER_TEST(uv8_u200_v0, gpu_raster_phase16,
104 drawTex8At(200, 0);
105 /* Deep off-page. */
107)
108
109CESTER_TEST(uv8_u255_v0, gpu_raster_phase16,
110 drawTex8At(255, 0);
111 /* Last representable U. */
113)
114
115// ============================================================================
116// 15-bit page (64 wide × 256 tall). U > 63 is the boundary.
117// ============================================================================
118
119CESTER_TEST(uv15_u0_v0, gpu_raster_phase16,
120 drawTex15At(0, 0);
121 /* Baseline: texel(0, 0) = vram555(0, 0, 0) = 0x0000 - transparent
122 per the phase-9 finding. Sentinel preserved. */
124)
125
126CESTER_TEST(uv15_u1_v0, gpu_raster_phase16,
127 drawTex15At(1, 0);
128 /* texel(1, 0) = vram555(1, 0, 1) = 1 | (1<<10) = 0x0401 */
129 ASSERT_PIXEL_EQ(expectedTex15Color(1, 0), 0, 0);
130)
131
132CESTER_TEST(uv15_u63_v0, gpu_raster_phase16,
133 drawTex15At(63, 0);
134 /* Last in-page texel for 15-bit. */
135 ASSERT_PIXEL_EQ(expectedTex15Color(63 & 0x1f, 0), 0, 0);
136)
137
138CESTER_TEST(uv15_u64_v0, gpu_raster_phase16,
139 writeVramPixel(704, 0, UV15_PROBE_TEXEL);
140 drawTex15At(64, 0);
141 /* First off-page. Wrap mod 64 -> texel(0, 0) = transparent? Or
142 extend into next texpage? */
144)
145
146CESTER_TEST(uv15_u128_v0, gpu_raster_phase16,
147 writeVramPixel(768, 0, UV15_PROBE_TEXEL);
148 drawTex15At(128, 0);
149 /* Two pages over. */
151)
152
153CESTER_TEST(uv15_u255_v0, gpu_raster_phase16,
154 writeVramPixel(895, 0, UV15_PROBE_TEXEL);
155 drawTex15At(255, 0);
157)
158
159// ============================================================================
160// V wrap. V=255 vs V=256 (8-bit wraps mod 256 to V=0).
161// Note: V is uint8_t in the GP0 command word, so V=256 isn't even
162// representable - it's truncated to 0. But the rasterizer's
163// per-pixel V interpolation could exceed 255 if the triangle is
164// large; that's a separate concern (rect path doesn't interpolate
165// the same way).
166//
167// For rects, V starts at the command UV and increments per row. So
168// V=255 with a 2-row rect would touch V=255 (drawn) and V=256
169// (which wraps via uint8 truncation back to V=0).
170// ============================================================================
171
172/* Reads row 255 of the TEX8 page, outside the fixture upload. */
173CESTER_TEST(uv8_u0_v255, gpu_raster_phase16,
174 writeVramPixel(TEX8_VRAM_BASE_X, 255, UV8_V255_PROBE_INDEX);
175 drawTex8At(0, 255);
177)
178
179CESTER_TEST(uv4_u0_v0, gpu_raster_phase16,
180 drawTex4At(0, 0);
181 /* 4-bit baseline. Texel u=0 -> CLUT4[0] = vram555(0, 31, 0). */
182 ASSERT_PIXEL_EQ(expectedClut4Color(0), 0, 0);
183)
184
185CESTER_TEST(uv4_u15_v0, gpu_raster_phase16,
186 drawTex4At(15, 0);
187 /* In-pattern: CLUT4[15] = vram555(15, 16, 0). */
188 ASSERT_PIXEL_EQ(expectedClut4Color(15), 0, 0);
189)
190
191/* Reads u=16 of the TEX4 page, just past the fixture pattern
192 (which only writes u=0..15): VRAM pixel 516, low nibble. */
193CESTER_TEST(uv4_u16_v0, gpu_raster_phase16,
194 writeVramPixel(TEX4_VRAM_BASE_X + 4, 0, UV4_U16_PROBE_INDEX);
195 drawTex4At(16, 0);
197)
198
199CESTER_TEST(uv4_u255_v0, gpu_raster_phase16,
200 writeVramPixel(TEX4_VRAM_BASE_X + 63, 0, UV4_U255_PROBE_INDEX << 12);
201 drawTex4At(255, 0);
202 /* Last representable U at 4-bit. Still within the 4-bit page
203 (256 wide). Outside fixture data. */
205)
CESTER_TEST(cddaLevelHalf, test_instances, static const uint8_t atv[4]={0x40, 0x00, 0x40, 0x00};unsigned changes=0;int peak=cddaPeak(atv, 1, &changes);ramsyscall_printf("CD-DA level at half ATV: peak %i, %u sign changes\n", peak, changes);cester_assert_int_ge(peak, 11300);cester_assert_int_le(peak, 11800);cester_assert_uint_ge(changes, 4);) CESTER_TEST(cddaLevelClipped
CESTER_BODY(static uint16_t s_cddaCapture;static int cddaSpuDma(uint32_t spuAddr, void *buf, uint32_t bytes, int isRead) { const uint16_t tsa=spuAddr > > 3;const uint16_t mode=isRead ? 0x30 :0x20;SPU_RAM_DTA=tsa;for(int i=0;i< 0xf01 &&SPU_RAM_DTA !=tsa;i++);SPU_CTRL=(SPU_CTRL &~0x30)|mode;for(volatile int i=0;i< 60;i++);for(int i=0;i< 0xf01 &&(SPU_STATUS &0x30) !=mode;i++);CDDA_SPU_DELAY=(CDDA_SPU_DELAY &0xf0ffffff)|(isRead ? 0x22000000 :0x20000000);DMA_CTRL[DMA_SPU].MADR=(uint32_t) buf &0x1fffffff;DMA_CTRL[DMA_SPU].BCR=((bytes > > 6)<< 16)|0x10;DMA_CTRL[DMA_SPU].CHCR=isRead ? 0x01000200 :0x01000201;int ok=0;for(unsigned t=0;t< 4000000u;t++) { if((DMA_CTRL[DMA_SPU].CHCR &0x01000000)==0) { ok=1;break;} } SPU_CTRL=SPU_CTRL &~0x30;for(volatile int i=0;i< 60;i++);return ok;} static int cddaSimpleCmd(uint8_t cmd) { uint8_t response[16];CDROM_REG0=0;CDROM_REG1=cmd;uint32_t timeout=2000000;int ok=waitCDRomIRQWithTimeout(&timeout);ackCDRomCause();readResponse(response);return ok;} static void cddaWaitPauseComplete() { initializeTime();uint32_t elapsed=0;while(elapsed< 5000000u) { uint32_t budget=200000u;int ok=waitCDRomIRQWithTimeout(&budget);elapsed=budget;if(!ok) continue;uint8_t cause=ackCDRomCause();uint8_t response[16];readResponse(response);if(cause==2) return;} } static int cddaAudioSetup(const uint8_t atv[4], int demute) { if(!resetCDRom()) return 0;DPCR|=0x000b0000;SPU_CTRL=0;for(volatile int i=0;i< 20000;i++);SPU_VOL_MAIN_LEFT=0;SPU_VOL_MAIN_RIGHT=0;SPU_KEY_OFF_LOW=0xffff;SPU_KEY_OFF_HIGH=0xffff;SPU_RAM_DTC=4;SPU_CTRL=0x8001;for(volatile int i=0;i< 20000;i++);for(int i=0;i< 0x400;i++) s_cddaCapture[i]=CDDA_MARKER;if(!cddaSpuDma(0x000, s_cddaCapture, 0x800, 0)) return 0;if(!cddaSimpleCmd(demute ? CDL_DEMUTE :CDL_MUTE)) return 0;CDROM_REG0=2;CDROM_REG2=atv[0];CDROM_REG3=atv[1];CDROM_REG0=3;CDROM_REG1=atv[2];CDROM_REG2=atv[3];CDROM_REG0=3;CDROM_REG3=0x20;CDROM_REG0=0;SPU_VOL_CD_LEFT=0x7fff;SPU_VOL_CD_RIGHT=0x7fff;SPU_CTRL=SPU_CTRL|0x8001;return 1;} static int cddaBufferPeak(unsigned *signChanges) { int peak=0;unsigned changes=0;int prev=0;for(int i=0;i< 0x200;i++) { int v=(int16_t) s_cddaCapture[i];int a=v< 0 ? -v :v;if(a > peak) peak=a;int sign=v >=0 ? 1 :-1;if(i &&sign !=prev) changes++;prev=sign;} *signChanges=changes;return peak;} static int cddaCapturePeak(unsigned *signChanges) { int peak=0;for(unsigned attempt=0;attempt< 8;attempt++) { if(attempt) { initializeTime();while(updateTime()< 250000u);} if(!cddaSpuDma(0x000, s_cddaCapture, 0x800, 1)) return -1;peak=cddaBufferPeak(signChanges);if(peak) { if(attempt) ramsyscall_printf("capture was empty %u time(s) before audio\n", attempt);break;} } return peak;} static int cddaPeak(const uint8_t atv[4], int demute, unsigned *signChanges) { if(!cddaAudioSetup(atv, demute)) return -1;if(!setMode(0)) return -1;CDROM_REG0=0;CDROM_REG2=0x02;if(!cddaSimpleCmd(CDL_PLAY)) return -1;initializeTime();while(updateTime()< 1500000u);int peak=cddaCapturePeak(signChanges);CDROM_REG0=0;CDROM_REG1=CDL_PAUSE;cddaWaitPauseComplete();CDROM_REG0=0;CDROM_REG2=0x00;CDROM_REG2=0x02;CDROM_REG2=0x16;if(cddaSimpleCmd(CDL_SETLOC) &&cddaSimpleCmd(CDL_SEEKL)) cddaWaitPauseComplete();return peak;}[0x400])
Definition cdda.c:37
#define GPU_DATA
Definition hwregs.h:52
#define UV15_PROBE_TEXEL
Definition raster-expected-phase16.h:64
#define UV8_U0_V255
Definition raster-expected-phase16.h:59
#define UV4_U16_V0
Definition raster-expected-phase16.h:74
#define UV4_U16_PROBE_INDEX
Definition raster-expected-phase16.h:72
#define UV15_U128_V0
Definition raster-expected-phase16.h:66
#define UV8_U128_V0
Definition raster-expected-phase16.h:54
#define UV8_U200_V0
Definition raster-expected-phase16.h:55
#define UV15_U64_V0
Definition raster-expected-phase16.h:65
#define UV8_U255_V0
Definition raster-expected-phase16.h:56
#define UV4_U255_PROBE_INDEX
Definition raster-expected-phase16.h:73
#define UV15_U255_V0
Definition raster-expected-phase16.h:67
#define UV8_V255_PROBE_INDEX
Definition raster-expected-phase16.h:58
#define UV4_U255_V0
Definition raster-expected-phase16.h:75
#define ASSERT_PIXEL_UNTOUCHED(x_, y_)
Definition raster-helpers.h:486
#define ASSERT_PIXEL_EQ(expected, x_, y_)
Definition raster-helpers.h:473
static int value
Definition syscalls.h:553
#define CLUT8_FIELD
Definition texture-fixtures.h:84
#define TEX15_TY
Definition texture-fixtures.h:66
#define TEX8_TY
Definition texture-fixtures.h:61
#define CLUT4_FIELD
Definition texture-fixtures.h:83
#define TEX4_VRAM_BASE_X
Definition texture-fixtures.h:57
#define TEX8_VRAM_BASE_X
Definition texture-fixtures.h:62
#define CLUT15_FIELD
Definition texture-fixtures.h:85
#define TEX15_TX
Definition texture-fixtures.h:65
#define TEX8_TX
Definition texture-fixtures.h:60
#define TEX_MOD_NEUTRAL
Definition texture-fixtures.h:308
#define TEX4_TY
Definition texture-fixtures.h:56
#define TEX4_TX
Definition texture-fixtures.h:55