Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
line-endpoints.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// Line endpoints suite. GP0(0x40) flat untextured line - two vertex
28// words after the command. Documents:
29// - Whether start AND end pixels are both drawn (inclusive on both ends).
30// - Bresenham octant symmetry: same shape across +X+Y, -X+Y, etc.
31// - Zero-length lines (start == end): does anything draw?
32// - Shallow vs steep lines (major axis selection).
33// - Diagonal at exactly 45 deg vs slope-0 / slope-infinity edge cases.
34
35// clang-format off
36
38
39// Horizontal 1px line at y=10 from x=5 to x=10.
40static void rasterDrawLineH(void) {
41 rasterReset();
42 rasterClearTestRegion(0, 0, 16, 16);
43 rasterFlatLine(RASTER_CMD_GREEN, 5, 10, 10, 10);
44 rasterFlushPrimitive();
45}
46
47// Vertical 1px line at x=10 from y=5 to y=10.
48static void rasterDrawLineV(void) {
49 rasterReset();
50 rasterClearTestRegion(0, 0, 16, 16);
51 rasterFlatLine(RASTER_CMD_RED, 10, 5, 10, 10);
52 rasterFlushPrimitive();
53}
54
55// Diagonal +45 deg from (5,5) to (10,10).
56static void rasterDrawLineD45(void) {
57 rasterReset();
58 rasterClearTestRegion(0, 0, 16, 16);
59 rasterFlatLine(RASTER_CMD_BLUE, 5, 5, 10, 10);
60 rasterFlushPrimitive();
61}
62
63// Diagonal -45 deg from (5,10) to (10,5).
64static void rasterDrawLineDN45(void) {
65 rasterReset();
66 rasterClearTestRegion(0, 0, 16, 16);
67 rasterFlatLine(RASTER_CMD_WHITE, 5, 10, 10, 5);
68 rasterFlushPrimitive();
69}
70
71// Zero-length line: same start and end point.
72static void rasterDrawLineZero(void) {
73 rasterReset();
74 rasterClearTestRegion(16, 16, 16, 8);
75 rasterFlatLine(RASTER_CMD_RED, 20, 20, 20, 20);
76 rasterFlushPrimitive();
77}
78
79// Shallow slope line (more horizontal than vertical): (0,0) to (10,3).
80// Major axis is X. Tests Bresenham major-axis selection.
81static void rasterDrawLineShallow(void) {
82 rasterReset();
83 rasterClearTestRegion(0, 0, 16, 8);
84 rasterFlatLine(RASTER_CMD_GREEN, 0, 0, 10, 3);
85 rasterFlushPrimitive();
86}
87
88) // CESTER_BODY
89
90// --------------------------------------------------------------------------
91// Horizontal line (5,10) - (10,10)
92// --------------------------------------------------------------------------
93
94CESTER_TEST(lineH_before_start_pixel_4_10, gpu_raster_phase2,
95 rasterDrawLineH();
97)
98
99CESTER_TEST(lineH_start_pixel_5_10, gpu_raster_phase2,
100 rasterDrawLineH();
102)
103
104CESTER_TEST(lineH_interior_pixel_7_10, gpu_raster_phase2,
105 rasterDrawLineH();
107)
108
109CESTER_TEST(lineH_end_pixel_10_10_inclusive, gpu_raster_phase2,
110 rasterDrawLineH();
112)
113
114CESTER_TEST(lineH_past_end_pixel_11_10, gpu_raster_phase2,
115 rasterDrawLineH();
117)
118
119CESTER_TEST(lineH_below_line_pixel_5_11, gpu_raster_phase2,
120 rasterDrawLineH();
122)
123
124// --------------------------------------------------------------------------
125// Vertical line (10,5) - (10,10)
126// --------------------------------------------------------------------------
127
128CESTER_TEST(lineV_before_start_pixel_10_4, gpu_raster_phase2,
129 rasterDrawLineV();
131)
132
133CESTER_TEST(lineV_start_pixel_10_5, gpu_raster_phase2,
134 rasterDrawLineV();
136)
137
138CESTER_TEST(lineV_interior_pixel_10_7, gpu_raster_phase2,
139 rasterDrawLineV();
141)
142
143CESTER_TEST(lineV_end_pixel_10_10_inclusive, gpu_raster_phase2,
144 rasterDrawLineV();
146)
147
148CESTER_TEST(lineV_past_end_pixel_10_11, gpu_raster_phase2,
149 rasterDrawLineV();
151)
152
153// --------------------------------------------------------------------------
154// Diagonal +45 deg (5,5) - (10,10)
155// --------------------------------------------------------------------------
156
157CESTER_TEST(lineD45_start_pixel_5_5, gpu_raster_phase2,
158 rasterDrawLineD45();
160)
161
162CESTER_TEST(lineD45_interior_pixel_7_7, gpu_raster_phase2,
163 rasterDrawLineD45();
165)
166
167CESTER_TEST(lineD45_end_pixel_10_10, gpu_raster_phase2,
168 rasterDrawLineD45();
170)
171
172CESTER_TEST(lineD45_off_diag_pixel_5_6, gpu_raster_phase2,
173 rasterDrawLineD45();
175)
176
177CESTER_TEST(lineD45_off_diag_pixel_6_5, gpu_raster_phase2,
178 rasterDrawLineD45();
180)
181
182// --------------------------------------------------------------------------
183// Diagonal -45 deg (5,10) - (10,5)
184// --------------------------------------------------------------------------
185
186CESTER_TEST(lineDN45_start_pixel_5_10, gpu_raster_phase2,
187 rasterDrawLineDN45();
189)
190
191CESTER_TEST(lineDN45_interior_pixel_7_8, gpu_raster_phase2,
192 rasterDrawLineDN45();
194)
195
196CESTER_TEST(lineDN45_end_pixel_10_5, gpu_raster_phase2,
197 rasterDrawLineDN45();
199)
200
201// --------------------------------------------------------------------------
202// Zero-length line (20,20) - (20,20)
203// --------------------------------------------------------------------------
204
205CESTER_TEST(lineZero_start_pixel_20_20, gpu_raster_phase2,
206 rasterDrawLineZero();
208)
209
210CESTER_TEST(lineZero_neighbor_pixel_21_20, gpu_raster_phase2,
211 rasterDrawLineZero();
213)
214
215// --------------------------------------------------------------------------
216// Shallow line (0,0) - (10,3): major axis is X
217// --------------------------------------------------------------------------
218
219CESTER_TEST(lineShallow_start_pixel_0_0, gpu_raster_phase2,
220 rasterDrawLineShallow();
222)
223
224CESTER_TEST(lineShallow_midpoint_pixel_5_2, gpu_raster_phase2,
225 rasterDrawLineShallow();
227)
228
229CESTER_TEST(lineShallow_end_pixel_10_3, gpu_raster_phase2,
230 rasterDrawLineShallow();
232)
233
234CESTER_TEST(lineShallow_x_2_y_0_bresenham_choice, gpu_raster_phase2,
235 rasterDrawLineShallow();
236 // At x=2, the Bresenham accumulator should have stepped y to 1.
237 // Pixel (2, 0) on the original major-axis row should be empty if
238 // the line steps y up before x=2. Verifies major-axis stepping.
240)
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 EXPECT_LINE_ZERO_PIXEL_20_20
Definition raster-expected-phase2.h:77
#define EXPECT_LINE_H_PIXEL_5_11
Definition raster-expected-phase2.h:53
#define EXPECT_LINE_D45_PIXEL_7_7
Definition raster-expected-phase2.h:65
#define EXPECT_LINE_H_PIXEL_4_10
Definition raster-expected-phase2.h:48
#define EXPECT_LINE_ZERO_PIXEL_21_20
Definition raster-expected-phase2.h:78
#define EXPECT_LINE_V_PIXEL_10_5
Definition raster-expected-phase2.h:57
#define EXPECT_LINE_H_PIXEL_11_10
Definition raster-expected-phase2.h:52
#define EXPECT_LINE_SHALLOW_PIXEL_2_0
Definition raster-expected-phase2.h:88
#define EXPECT_LINE_V_PIXEL_10_4
Definition raster-expected-phase2.h:56
#define EXPECT_LINE_SHALLOW_PIXEL_5_2
Definition raster-expected-phase2.h:86
#define EXPECT_LINE_V_PIXEL_10_11
Definition raster-expected-phase2.h:60
#define EXPECT_LINE_V_PIXEL_10_10
Definition raster-expected-phase2.h:59
#define EXPECT_LINE_DN45_PIXEL_10_5
Definition raster-expected-phase2.h:73
#define EXPECT_LINE_H_PIXEL_7_10
Definition raster-expected-phase2.h:50
#define EXPECT_LINE_D45_PIXEL_6_5
Definition raster-expected-phase2.h:68
#define EXPECT_LINE_H_PIXEL_10_10
Definition raster-expected-phase2.h:51
#define EXPECT_LINE_SHALLOW_PIXEL_0_0
Definition raster-expected-phase2.h:85
#define EXPECT_LINE_D45_PIXEL_5_5
Definition raster-expected-phase2.h:64
#define EXPECT_LINE_H_PIXEL_5_10
Definition raster-expected-phase2.h:49
#define EXPECT_LINE_D45_PIXEL_5_6
Definition raster-expected-phase2.h:67
#define EXPECT_LINE_DN45_PIXEL_5_10
Definition raster-expected-phase2.h:71
#define EXPECT_LINE_SHALLOW_PIXEL_10_3
Definition raster-expected-phase2.h:87
#define EXPECT_LINE_V_PIXEL_10_7
Definition raster-expected-phase2.h:58
#define EXPECT_LINE_DN45_PIXEL_7_8
Definition raster-expected-phase2.h:72
#define EXPECT_LINE_D45_PIXEL_10_10
Definition raster-expected-phase2.h:66
#define RASTER_CMD_BLUE
Definition raster-helpers.h:128
#define RASTER_CMD_RED
Definition raster-helpers.h:124
#define RASTER_CMD_WHITE
Definition raster-helpers.h:130
#define RASTER_CMD_GREEN
Definition raster-helpers.h:126
#define ASSERT_PIXEL_EQ(expected, x_, y_)
Definition raster-helpers.h:473