Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
texture-window-8-15.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// Texture-window probes at 8-bit (CLUT8) and 15-bit (direct) depths.
28//
29// Same formula as the 4-bit path:
30// filtered_u = (u AND NOT (mask_u * 8)) OR ((offset_u * 8) AND (mask_u * 8))
31//
32// mask_u = 0x01 -> bit 3 of u forced from offset_u
33// mask_u = 0x03 -> bits 3 AND 4 of u forced from offset_u
34// mask_u = 0x07 -> bits 3, 4, 5 of u forced (24 high bits collapse window)
35//
36// The 8-bit fixture spans u=0..63 (64 texels) and 15-bit spans the
37// same range. Mask values up to 0x07 (collapse 24 high bits, leaving
38// 3-bit window) are meaningful at these widths.
39
40// clang-format off
41
43
44static void drawTexWindow8Tri(uint8_t mask_x, uint8_t off_x) {
45 rasterReset();
46 rasterClearTestRegion(0, 0, 64, 16);
47 setTexpage(TEX8_TX, TEX8_TY, 1);
48 setTextureWindow(mask_x, 0, off_x, 0);
49 /* Triangle (0,0)-(32,0)-(0,8). UV matches screen so pixel (x,y)
50 wants texel (x, y); window filtering rewrites the U bits. */
51 rasterTexTri(TEX_MOD_NEUTRAL,
52 0, 0, 0, 0,
53 32, 0, 32, 0,
54 0, 8, 0, 8,
56 rasterFlushPrimitive();
57}
58
59static void drawTexWindow15Tri(uint8_t mask_x, uint8_t off_x) {
60 rasterReset();
61 rasterClearTestRegion(0, 0, 32, 16);
62 setTexpage(TEX15_TX, TEX15_TY, 2);
63 setTextureWindow(mask_x, 0, off_x, 0);
64 rasterTexTri(TEX_MOD_NEUTRAL,
65 0, 0, 0, 0,
66 16, 0, 16, 0,
67 0, 8, 0, 8,
69 rasterFlushPrimitive();
70}
71
72// Vertical-window variant: mask_y forces high bits of v from offset_y.
73// Useful for confirming V-axis windowing isn't subtly different from U.
74static void drawTexWindow8TriV(uint8_t mask_y, uint8_t off_y) {
75 rasterReset();
76 rasterClearTestRegion(0, 0, 64, 16);
77 setTexpage(TEX8_TX, TEX8_TY, 1);
78 setTextureWindow(0, mask_y, 0, off_y);
79 rasterTexTri(TEX_MOD_NEUTRAL,
80 0, 0, 0, 0,
81 32, 0, 32, 0,
82 0, 8, 0, 8,
84 rasterFlushPrimitive();
85}
86
87) // CESTER_BODY
88
89// ==========================================================================
90// 8-bit: mask_u=0x01, offset_u=0 -> bit 3 of u cleared
91// u=0..7 -> texel 0..7
92// u=8..15 -> texel 0..7 (bit 3 cleared)
93// u=16..23 -> texel 16..23 (bit 3 of 16=0x10 unchanged; only bit 3
94// collapses 8-aligned 8-blocks)
95// ==========================================================================
96
97CESTER_TEST(texwin8_mask01_off00_u0, gpu_raster_phase9,
98 drawTexWindow8Tri(0x01, 0x00);
99 /* u=0, filtered=0, CLUT8[0]. */
100 ASSERT_PIXEL_EQ(expectedClut8Color(0), 0, 0);
101)
102CESTER_TEST(texwin8_mask01_off00_u7, gpu_raster_phase9,
103 drawTexWindow8Tri(0x01, 0x00);
104 ASSERT_PIXEL_EQ(expectedClut8Color(7), 7, 0);
105)
106CESTER_TEST(texwin8_mask01_off00_u8_wraps_to_0, gpu_raster_phase9,
107 drawTexWindow8Tri(0x01, 0x00);
108 /* u=8, bit 3 cleared -> texel 0. */
109 ASSERT_PIXEL_EQ(expectedClut8Color(0), 8, 0);
110)
111CESTER_TEST(texwin8_mask01_off00_u15_wraps_to_7, gpu_raster_phase9,
112 drawTexWindow8Tri(0x01, 0x00);
113 /* u=15=0x0F, bit 3 cleared -> texel 7. */
114 ASSERT_PIXEL_EQ(expectedClut8Color(7), 15, 0);
115)
116CESTER_TEST(texwin8_mask01_off00_u16, gpu_raster_phase9,
117 drawTexWindow8Tri(0x01, 0x00);
118 /* u=16=0x10, bit 3 already cleared -> texel 16. */
119 ASSERT_PIXEL_EQ(expectedClut8Color(16), 16, 0);
120)
121CESTER_TEST(texwin8_mask01_off00_u24_wraps_to_16, gpu_raster_phase9,
122 drawTexWindow8Tri(0x01, 0x00);
123 /* u=24=0x18, bit 3 cleared -> texel 16. */
124 ASSERT_PIXEL_EQ(expectedClut8Color(16), 24, 0);
125)
126
127// ==========================================================================
128// 8-bit: mask_u=0x01, offset_u=0x01 -> bit 3 of u SET from offset
129// u=0..7 -> texel 8..15
130// u=8..15 -> texel 8..15 (already set)
131// ==========================================================================
132
133CESTER_TEST(texwin8_mask01_off01_u0_forced_to_8, gpu_raster_phase9,
134 drawTexWindow8Tri(0x01, 0x01);
135 ASSERT_PIXEL_EQ(expectedClut8Color(8), 0, 0);
136)
137CESTER_TEST(texwin8_mask01_off01_u3_forced_to_b, gpu_raster_phase9,
138 drawTexWindow8Tri(0x01, 0x01);
139 /* u=3, bit 3 set -> texel 0xB. */
140 ASSERT_PIXEL_EQ(expectedClut8Color(0x0b), 3, 0);
141)
142
143// ==========================================================================
144// 8-bit: mask_u=0x03 (bits 3 and 4) -> collapse 16-texel window
145// offset_u=0: u with bits 3-4 cleared
146// u=0..7 -> 0..7
147// u=8..23 -> texels 0..7 then 0..7 (16-byte wrap)
148// ==========================================================================
149
150CESTER_TEST(texwin8_mask03_off00_u20_wraps_to_4, gpu_raster_phase9,
151 drawTexWindow8Tri(0x03, 0x00);
152 /* u=20=0x14, &~0x18=0x04 -> texel 4. */
153 ASSERT_PIXEL_EQ(expectedClut8Color(4), 20, 0);
154)
155CESTER_TEST(texwin8_mask03_off00_u31_wraps_to_7, gpu_raster_phase9,
156 drawTexWindow8Tri(0x03, 0x00);
157 /* u=31=0x1F, &~0x18=0x07 -> texel 7. */
158 ASSERT_PIXEL_EQ(expectedClut8Color(7), 31, 0);
159)
160
161// ==========================================================================
162// 8-bit: mask_u=0x07 -> collapse 24 high bits, 3-bit u window
163// offset_u=0x03 (set bit 4 and bit 3 from offset_u*8 = 0x18)
164// ==========================================================================
165
166CESTER_TEST(texwin8_mask07_off03_u0_forced_to_18, gpu_raster_phase9,
167 drawTexWindow8Tri(0x07, 0x03);
168 /* u=0, &~0x38 = 0, | 0x18 = 0x18 -> texel 24. */
169 ASSERT_PIXEL_EQ(expectedClut8Color(0x18), 0, 0);
170)
171CESTER_TEST(texwin8_mask07_off03_u5_forced_to_1d, gpu_raster_phase9,
172 drawTexWindow8Tri(0x07, 0x03);
173 /* u=5, &~0x38=5, | 0x18 = 0x1D -> texel 29. */
174 ASSERT_PIXEL_EQ(expectedClut8Color(0x1d), 5, 0);
175)
176
177// ==========================================================================
178// 8-bit V-axis windowing: mask_v=0x01, off_v=0 -> bit 3 of v cleared
179// At y=8, v=8 wraps to v=0. The texture's V dimension carries
180// the row, but our fixture is constant across rows (texel value
181// depends only on u). So V-window wrapping doesn't change the
182// pixel color - it should match the equivalent non-wrapped row.
183// This is a control test: confirms V-window is applied but
184// invisible for U-only fixtures.
185// ==========================================================================
186
187CESTER_TEST(texwin8_maskV01_off00_v0, gpu_raster_phase9,
188 drawTexWindow8TriV(0x01, 0x00);
189 /* y=0, v=0, no wrap. */
190 ASSERT_PIXEL_EQ(expectedClut8Color(0), 0, 0);
191)
192
193// ==========================================================================
194// 15-bit: mask_u=0x01, offset_u=0 -> bit 3 of u cleared
195// Texel value at (u, v) = vram555(u & 0x1f, v & 0x1f, (u+v) & 0x1f).
196// Window-rewritten u changes BOTH the R channel and the (u+v) blue
197// channel, so the read-back pixel is a direct function of filtered_u.
198// ==========================================================================
199
200CESTER_TEST(texwin15_mask01_off00_u0, gpu_raster_phase9,
201 drawTexWindow15Tri(0x01, 0x00);
202 /* (u=0, v=0): vram555(0, 0, 0) = 0. NOTE: this pixel might be
203 excluded by the top-left rule (apex of axis-aligned triangle).
204 If hardware draws it, value is 0x0000; if excluded, sentinel. */
206)
207CESTER_TEST(texwin15_mask01_off00_u7, gpu_raster_phase9,
208 drawTexWindow15Tri(0x01, 0x00);
209 /* (u=7, v=0): vram555(7, 0, 7) = 7 | (7<<10) = 0x1c07. */
211)
212CESTER_TEST(texwin15_mask01_off00_u8_wraps_to_0, gpu_raster_phase9,
213 drawTexWindow15Tri(0x01, 0x00);
214 /* (u=8, filtered=0, v=0): vram555(0, 0, 0) = 0x0000. */
216)
217CESTER_TEST(texwin15_mask01_off00_u15_wraps_to_7, gpu_raster_phase9,
218 drawTexWindow15Tri(0x01, 0x00);
219 /* (u=15, filtered=7, v=0): vram555(7, 0, 7) = 0x1c07. */
221)
222
223// ==========================================================================
224// 15-bit: mask_u=0x01, offset_u=0x01 -> bit 3 forced set
225// u=0..7 -> texel 8..15 -> vram555(8..15, 0, 8..15)
226// ==========================================================================
227
228CESTER_TEST(texwin15_mask01_off01_u0_forced_to_8, gpu_raster_phase9,
229 drawTexWindow15Tri(0x01, 0x01);
230 /* (filtered=8, v=0): vram555(8, 0, 8) = 8 | (8<<10) = 0x2008. */
232)
233CESTER_TEST(texwin15_mask01_off01_u3_forced_to_b, gpu_raster_phase9,
234 drawTexWindow15Tri(0x01, 0x01);
235 /* (filtered=0xB=11, v=0): vram555(11, 0, 11) = 11 | (11<<10) = 0x2c0b. */
237)
238CESTER_TEST(texwin15_mask01_off01_u8_already_set, gpu_raster_phase9,
239 drawTexWindow15Tri(0x01, 0x01);
240 /* (filtered=8, v=0). */
242)
243
244// ==========================================================================
245// 15-bit: mask_u=0x03 (bits 3-4 collapse, 16-texel window)
246// ==========================================================================
247
248CESTER_TEST(texwin15_mask03_off00_u13, gpu_raster_phase9,
249 drawTexWindow15Tri(0x03, 0x00);
250 /* u=13=0xD, &~0x18=0x05 -> texel 5 at v=0 -> vram555(5, 0, 5). */
252)
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 TW15_M01_O01_U0_Y0
Definition raster-expected-phase9.h:56
#define TW15_M01_O00_U15_Y0
Definition raster-expected-phase9.h:53
#define TW15_M01_O01_U3_Y0
Definition raster-expected-phase9.h:57
#define TW15_M01_O01_U8_Y0
Definition raster-expected-phase9.h:58
#define TW15_M01_O00_U0_Y0
Definition raster-expected-phase9.h:50
#define TW15_M03_O00_U13_Y0
Definition raster-expected-phase9.h:61
#define TW15_M01_O00_U8_Y0
Definition raster-expected-phase9.h:52
#define TW15_M01_O00_U7_Y0
Definition raster-expected-phase9.h:51
#define ASSERT_PIXEL_EQ(expected, x_, y_)
Definition raster-helpers.h:473
#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 TEX15_TPAGE
Definition texture-fixtures.h:102
#define TEX8_TPAGE
Definition texture-fixtures.h:101
#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