Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
readnosetloc.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// clang-format off
28
29// A read command sent with no new Setloc since the previous read: once while
30// the previous read is still delivering sectors, and once after a Pause. Logs
31// every interrupt, with the sector header for each data-ready one, and checks
32// where the second read starts.
33
35 typedef struct {
36 uint32_t time;
37 uint8_t cause;
38 uint8_t status;
39 uint8_t header[4];
40 } NoSetLocEvent;
41
42 // Collects interrupts until `wantData` data-ready ones were seen, the log
43 // is full, or 3s pass without an interrupt. Returns the new event count.
44 static unsigned noSetLocCollect(NoSetLocEvent* events, unsigned count, unsigned max, unsigned wantData) {
45 unsigned dataReady = 0;
46 while ((count < max) && (dataReady < wantData)) {
47 uint32_t timeout = 3000000;
48 if (!waitCDRomIRQWithTimeout(&timeout)) break;
49 NoSetLocEvent* e = &events[count++];
50 e->time = timeout;
51 e->cause = ackCDRomCause();
52 uint8_t response[16];
54 e->status = response[0];
55 e->header[0] = e->header[1] = e->header[2] = e->header[3] = 0;
56 if (e->cause == 1) {
57 CDROM_REG0 = 0;
58 CDROM_REG3 = 0x80;
59 while ((CDROM_REG0 & 0x40) == 0);
60 for (unsigned i = 0; i < 4; i++) e->header[i] = CDROM_REG2;
61 CDROM_REG3 = 0;
62 dataReady++;
63 }
64 }
65 return count;
66 }
67
68 // Pauses and consumes every interrupt up to the Pause completion, so that
69 // nothing is left pending for the next test. Returns 1 once INT2 is seen.
70 static int noSetLocPauseAndDrain() {
71 CDROM_REG0 = 0;
73 while (1) {
74 uint32_t timeout = 3000000;
75 if (!waitCDRomIRQWithTimeout(&timeout)) return 0;
76 uint8_t cause = ackCDRomCause();
77 discardResponse();
78 if (cause == 2) return 1;
79 }
80 }
81
82 static void printNoSetLocEvents(const char* name, const NoSetLocEvent* events, unsigned count, unsigned split) {
83 ramsyscall_printf("%s, %u events:", name, count);
84 for (unsigned i = 0; i < count; i++) {
85 const NoSetLocEvent* e = &events[i];
86 if (i == split) ramsyscall_printf(" |");
87 ramsyscall_printf(" [%ius int%u stat %02x", e->time, e->cause, e->status);
88 if (e->cause == 1) {
89 ramsyscall_printf(" hdr %02x:%02x:%02x m%u", e->header[0], e->header[1], e->header[2], e->header[3]);
90 }
92 }
94 }
95
96 static uint32_t noSetLocHeaderLBA(const NoSetLocEvent* e) {
97 return MSF2LBA(btoi(e->header[0]), btoi(e->header[1]), btoi(e->header[2]));
98 }
99
100 // LBA of the last data-ready header in [from, to), or 0 if there is none.
101 static uint32_t noSetLocLastLBA(const NoSetLocEvent* events, unsigned from, unsigned to) {
102 uint32_t lba = 0;
103 for (unsigned i = from; i < to; i++) {
104 if (events[i].cause == 1) lba = noSetLocHeaderLBA(&events[i]);
105 }
106 return lba;
107 }
108
109 // LBA of the first data-ready header in [from, to), or 0 if there is none.
110 static uint32_t noSetLocFirstLBA(const NoSetLocEvent* events, unsigned from, unsigned to) {
111 for (unsigned i = from; i < to; i++) {
112 if (events[i].cause == 1) return noSetLocHeaderLBA(&events[i]);
113 }
114 return 0;
115 }
116)
117
118CESTER_TEST(readSResentMidStreamNoSetLoc, test_instances,
119 NoSetLocEvent events[24];
120 int ok = resetCDRom() && setMode(0x20) && setLoc(0, 0x02, 0x16);
121 if (!ok) {
123 return;
124 }
125
129 unsigned count = noSetLocCollect(events, 0, 24, 6);
130 unsigned split = count;
131
132 CDROM_REG0 = 0;
134 count = noSetLocCollect(events, count, 24, 4);
135 int drained = noSetLocPauseAndDrain();
136
137 printNoSetLocEvents("ReadS at 00:02:16, six sectors, ReadS again with no Setloc", events, count, split);
138 uint32_t before = noSetLocLastLBA(events, 0, split);
139 uint32_t after = noSetLocFirstLBA(events, split, count);
140 ramsyscall_printf("last LBA before %u, first LBA after %u, Setloc LBA %u\n", before, after, MSF2LBA(0, 2, 16));
144 // No rewind to the Setloc target: the second read carries on with the
145 // next sector.
147)
148
149CESTER_TEST(readSAfterPauseNoSetLoc, test_instances,
150 NoSetLocEvent events[24];
151 int ok = resetCDRom() && setMode(0x20) && setLoc(0, 0x02, 0x16);
152 if (!ok) {
154 return;
155 }
156
158 CDROM_REG0 = 0;
160 unsigned count = noSetLocCollect(events, 0, 24, 6);
161
162 CDROM_REG0 = 0;
164 // Pause ack, then its completion; a sector or two can still land between.
165 unsigned paused = 0;
166 while ((count < 24) && !paused) {
167 unsigned prev = count;
168 count = noSetLocCollect(events, count, prev + 1, 1);
169 if (count == prev) break;
170 if (events[count - 1].cause == 2) paused = 1;
171 }
172 unsigned split = count;
173
175 while (updateTime() < 100000);
176
178 CDROM_REG0 = 0;
180 count = noSetLocCollect(events, count, 24, 4);
181 int drained = noSetLocPauseAndDrain();
182
183 printNoSetLocEvents("ReadS at 00:02:16, six sectors, Pause, ReadS again with no Setloc", events, count, split);
184 uint32_t before = noSetLocLastLBA(events, 0, split);
185 uint32_t after = noSetLocFirstLBA(events, split, count);
186 ramsyscall_printf("last LBA before %u, first LBA after %u, Setloc LBA %u\n", before, after, MSF2LBA(0, 2, 16));
187 cester_assert_true(paused);
191 // Resumes one sector before the last one delivered, not at the Setloc
192 // target (00:02:20 after 00:02:21 on a SCPH-9002, three runs out of three).
194)
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
test_instances
Definition cdda.c:214
uint8_t response[16]
Definition cdlgetlocl.c:84
uint32_t timeout
Definition cdlid.c:94
readResponse(response)
ackCDRomCause()
int ok
Definition cdlstop.c:75
uint8_t cause
Definition cdltest.c:207
ramsyscall_printf("T21 phase=%s t=%u v=%02x cause=%u size=%u stat=%02x\n", phase, t/1000, response[0], cause, size,(nopCause==3 &&nopSize >=1) ? stat[0] :0xff)
uint32_t time
Definition cdltest.c:208
@ CDL_READS
Definition cdrom.h:69
@ CDL_PAUSE
Definition cdrom.h:51
#define CDROM_REG3
Definition cdrom.h:34
#define CDROM_REG2
Definition cdrom.h:33
struct EventInfo * events
Definition globals.h:49
int i
Definition gte-regio.c:299
constexpr uint8_t btoi(uint8_t b)
Definition msf.hh:35
CDROM_REG0
Definition readnosetloc.c:127
printNoSetLocEvents("ReadS at 00:02:16, six sectors, ReadS again with no Setloc", events, count, split)
CESTER_BODY(typedef struct { uint32_t time;uint8_t cause;uint8_t status;uint8_t header[4];} NoSetLocEvent;static unsigned noSetLocCollect(NoSetLocEvent *events, unsigned count, unsigned max, unsigned wantData) { unsigned dataReady=0;while((count< max) &&(dataReady< wantData)) { uint32_t timeout=3000000;if(!waitCDRomIRQWithTimeout(&timeout)) break;NoSetLocEvent *e=&events[count++];e->time=timeout;e->cause=ackCDRomCause();uint8_t response[16];readResponse(response);e->status=response[0];e->header[0]=e->header[1]=e->header[2]=e->header[3]=0;if(e->cause==1) { CDROM_REG0=0;CDROM_REG3=0x80;while((CDROM_REG0 &0x40)==0);for(unsigned i=0;i< 4;i++) e->header[i]=CDROM_REG2;CDROM_REG3=0;dataReady++;} } return count;} static int noSetLocPauseAndDrain() { CDROM_REG0=0;CDROM_REG1=CDL_PAUSE;while(1) { uint32_t timeout=3000000;if(!waitCDRomIRQWithTimeout(&timeout)) return 0;uint8_t cause=ackCDRomCause();discardResponse();if(cause==2) return 1;} } static void printNoSetLocEvents(const char *name, const NoSetLocEvent *events, unsigned count, unsigned split) { ramsyscall_printf("%s, %u events:", name, count);for(unsigned i=0;i< count;i++) { const NoSetLocEvent *e=&events[i];if(i==split) ramsyscall_printf(" |");ramsyscall_printf(" [%ius int%u stat %02x", e->time, e->cause, e->status);if(e->cause==1) { ramsyscall_printf(" hdr %02x:%02x:%02x m%u", e->header[0], e->header[1], e->header[2], e->header[3]);} ramsyscall_printf("]");} ramsyscall_printf("\n");} static uint32_t noSetLocHeaderLBA(const NoSetLocEvent *e) { return MSF2LBA(btoi(e->header[0]), btoi(e->header[1]), btoi(e->header[2]));} static uint32_t noSetLocLastLBA(const NoSetLocEvent *events, unsigned from, unsigned to) { uint32_t lba=0;for(unsigned i=from;i< to;i++) { if(events[i].cause==1) lba=noSetLocHeaderLBA(&events[i]);} return lba;} static uint32_t noSetLocFirstLBA(const NoSetLocEvent *events, unsigned from, unsigned to) { for(unsigned i=from;i< to;i++) { if(events[i].cause==1) return noSetLocHeaderLBA(&events[i]);} return 0;})
Definition readnosetloc.c:34
cester_assert_uint_eq(before+1, after)
cester_assert_uint_ne(0, before)
uint32_t before
Definition readnosetloc.c:138
cester_assert_true(drained)
initializeTime()
CDROM_REG1
Definition readnosetloc.c:128
int drained
Definition readnosetloc.c:135
unsigned count
Definition readnosetloc.c:129
unsigned split
Definition readnosetloc.c:130
uint32_t after
Definition readnosetloc.c:139
char e[1]
Definition string.c:119