Nugget
Bare-metal libraries and examples for the original PlayStation
Loading...
Searching...
No Matches
transport.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#include "monitor/transport.h"
28
29#include "monitor/link.h"
30
31/* Frame checksum: Fletcher-32 over the 16-bit word stream (TYPE, LEN, payload),
32 two register accumulators with the modulo deferred to the end. A computed
33 value of exactly 0 is sent as 0xffffffff, so 0 never appears as a checksum.
34 On ATCONS a received 0 means "not computed" and is accepted without
35 verifying; on a byte link the checksum is mandatory and 0 fails. */
36#define CKSUM_NONE 0u
37static uint32_t s_txS1, s_txS2;
38static uint32_t s_rxS1, s_rxS2;
39
40/* Not inlined: the two callers would each carry the modulo sequences. */
41static __attribute__((noinline)) uint32_t fletcherFinish(uint32_t s1, uint32_t s2) {
42 uint32_t ck = ((s2 % 65535u) << 16) | (s1 % 65535u);
43 return ck == CKSUM_NONE ? 0xffffffffu : ck;
44}
45
46void transportInit(void) { linkInit(); }
47
48/* Running checksum for the streaming send API. The monitor sends exactly one
49 frame at a time, so a single static accumulator is safe. */
50
51void transportSendBegin(uint16_t type, uint16_t len) {
52 s_txS1 = s_txS2 = 0;
53#ifdef MONITOR_LINK_IS_STREAM
54 linkPutByte(0); /* leaves console text, a frame follows */
55#endif
56 linkPutWord(FRAME_SYNC); /* SYNC is outside the checksum */
59}
60
61void transportSendWord(uint16_t w) {
62 linkPutWord(w);
63 s_txS1 += w; s_txS2 += s_txS1;
64}
65
66void transportSendEnd(void) {
67 uint32_t ck = fletcherFinish(s_txS1, s_txS2);
68 linkPutWord((uint16_t)(ck & 0xffff)); /* low word first */
69 linkPutWord((uint16_t)((ck >> 16) & 0xffff)); /* high word */
70}
71
72void transportSendFrame(uint16_t type, const uint16_t *payload, uint16_t len) {
73 transportSendBegin(type, len);
74 for (uint16_t i = 0; i < len; i++) transportSendWord(payload[i]);
76}
77
78
79#ifdef MONITOR_LINK_IS_STREAM
80/* Console input the monitor has no consumer for yet (card #676) is dropped and
81 counted. */
82static uint32_t s_consoleDropped;
83
84void transportRecvBegin(uint16_t *type, uint16_t *len) {
85 uint16_t t, l;
86 linkRxOpen();
87 uint8_t b = linkGetByte();
88 for (;;) {
89 if (b != 0) {
90 s_consoleDropped++;
91 b = linkGetByte();
92 continue;
93 }
94 /* A run of 0s is one frame start. Each byte that fails to continue
95 SYNC is looked at again as the start of what follows, so a 0 there
96 begins the next frame. A LEN no frame can have is noise. */
97 while ((b = linkGetByte()) == 0) {
98 }
99 if (b != (FRAME_SYNC & 0xff)) continue;
100 b = linkGetByte();
101 if (b != (FRAME_SYNC >> 8)) continue;
102 t = linkGetWord();
103 l = linkGetWord();
104 if (l <= TRANSPORT_STREAM_MAX_LEN) break;
105 b = linkGetByte();
106 }
107 s_rxS1 = s_rxS2 = 0;
108 s_rxS1 += t; s_rxS2 += s_rxS1;
109 s_rxS1 += l; s_rxS2 += s_rxS1;
110 *type = t;
111 *len = l;
112}
113#else
114void transportRecvBegin(uint16_t *type, uint16_t *len) {
115 while (linkGetWord() != FRAME_SYNC) {
116 /* resynchronize by hunting for the framing anchor */
117 }
118 s_rxS1 = s_rxS2 = 0;
119 uint16_t t = linkGetWord();
120 s_rxS1 += t; s_rxS2 += s_rxS1;
121 uint16_t l = linkGetWord();
122 s_rxS1 += l; s_rxS2 += s_rxS1;
123 *type = t;
124 *len = l;
125}
126#endif
127
128#ifdef MONITOR_LINK_IS_STREAM
129#ifndef MONITOR_STOP_QUIET_SPINS
130#define MONITOR_STOP_QUIET_SPINS 20000 /* ~7 ms on a retail PS1, ~80 byte times at 115200 */
131#endif
132
133int transportStopPending(void) {
134 uint8_t b;
135 if (!linkTryGetByte(&b)) return 0;
136 int stop = 0;
137 uint32_t quiet = 0;
138 linkRxOpen();
139 do {
140 if (quiet == 0) {
141 if (b == 0) {
142 stop = 1;
143 } else if (!stop) {
144 s_consoleDropped++;
145 }
146 }
147 quiet = linkTryGetByte(&b) ? 0 : quiet + 1;
148 } while (quiet < MONITOR_STOP_QUIET_SPINS);
149 linkRxClose();
150#ifdef MONITOR_LINK_SIO1
151 linkClearErrors(); /* the overrun a STOP frame longer than the FIFO leaves */
152#endif
153 return stop;
154}
155#else
156int transportStopPending(void) { return 0; }
157#endif
158
159uint16_t transportRecvWord(void) {
160 uint16_t w = linkGetWord();
161 s_rxS1 += w; s_rxS2 += s_rxS1;
162 return w;
163}
164
166 uint32_t rxck = linkGetWord();
167 rxck |= ((uint32_t)linkGetWord()) << 16;
168#ifdef MONITOR_LINK_IS_STREAM
169 linkRxClose();
170 /* The checksum is mandatory on a byte link: CKSUM_NONE fails below, since
171 fletcherFinish never produces it. */
172#else
173 if (rxck == CKSUM_NONE) return TRANSPORT_OK; /* sender skipped it */
174#endif
175 return (rxck == fletcherFinish(s_rxS1, s_rxS2)) ? TRANSPORT_OK : TRANSPORT_ECKSUM;
176}
177
178#ifdef MONITOR_LINK_HAS_RATE
179#ifndef MONITOR_RATE_WINDOW_SPINS
180#define MONITOR_RATE_WINDOW_SPINS 3000000 /* ~1.08 s on a retail PS1: 2000000 measured 720 ms */
181#endif
182
183/* The frames accepted while trying a new rate: PING with no payload, then,
184 once the host has seen the PONG, PING carrying the word 1. The host
185 retries the first freely, so the second has to differ from it. */
186static const uint8_t s_pingFrame[] = {0x00, 0xaa, 0x55, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00};
187static const uint8_t s_confirmFrame[] = {0x00, 0xaa, 0x55, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03, 0x00, 0x06, 0x00};
188
189int transportHasRate(void) { return 1; }
190
191/* Wait up to one window for the exact frame `f` at the current rate. */
192static int awaitFrame(const uint8_t *f, unsigned n) {
193 unsigned matched = 0;
194 linkRxOpen();
195 for (uint32_t spins = 0; spins < MONITOR_RATE_WINDOW_SPINS; spins++) {
196 uint8_t b;
197 if (!linkTryGetByte(&b)) continue;
198 if (b == f[matched]) {
199 if (++matched == n) {
200 linkRxClose();
201 return 1;
202 }
203 } else {
204 matched = (b == f[0]) ? 1 : 0;
205 }
206 }
207 linkRxClose();
208 return 0;
209}
210
211int transportTryRate(uint16_t reload, uint16_t pongType, uint16_t pongWord) {
212 uint16_t old = linkGetRate();
213 linkSetRate(reload);
214 /* The PING proves host -> PS1 at the new rate. Only a host that got the
215 PONG sends the confirmation, which proves PS1 -> host. */
216 if (awaitFrame(s_pingFrame, sizeof(s_pingFrame))) {
217 transportSendFrame(pongType, &pongWord, 1);
218 if (awaitFrame(s_confirmFrame, sizeof(s_confirmFrame))) return 1;
219 }
220 linkSetRate(old);
221 return 0;
222}
223#else
224int transportHasRate(void) { return 0; }
225int transportTryRate(uint16_t reload, uint16_t pongType, uint16_t pongWord) { return -1; }
226#endif
227
228int transportRecvFrame(uint16_t *type, uint16_t *payload, uint16_t maxLen, uint16_t *lenOut) {
229 uint16_t t, len;
230 transportRecvBegin(&t, &len);
231
232 if (len > maxLen) {
233 for (uint16_t i = 0; i < len; i++) transportRecvWord(); /* drain, stay aligned */
235 return TRANSPORT_EBADLEN;
236 }
237
238 for (uint16_t i = 0; i < len; i++) payload[i] = transportRecvWord();
239 int rc = transportRecvEnd();
240 if (rc != TRANSPORT_OK) return rc;
241
242 *type = t;
243 *lenOut = len;
244 return TRANSPORT_OK;
245}
__attribute__((weak))
Definition clz.c:56
uint32_t t
Definition cop0.c:79
uint8_t b
Definition gte-depthcue.c:39
int i
Definition gte-regio.c:297
static const char * s2
Definition syscalls.h:92
void uint32_t(classId, spec)
#define CKSUM_NONE
Definition transport.c:36
int transportRecvFrame(uint16_t *type, uint16_t *payload, uint16_t maxLen, uint16_t *lenOut)
Definition transport.c:228
uint16_t transportRecvWord(void)
Definition transport.c:159
void transportSendWord(uint16_t w)
Definition transport.c:61
int transportRecvEnd(void)
Definition transport.c:165
void transportRecvBegin(uint16_t *type, uint16_t *len)
Definition transport.c:114
void transportSendEnd(void)
Definition transport.c:66
int transportStopPending(void)
Definition transport.c:156
int transportTryRate(uint16_t reload, uint16_t pongType, uint16_t pongWord)
Definition transport.c:225
int transportHasRate(void)
Definition transport.c:224
void transportInit(void)
Definition transport.c:46
void transportSendBegin(uint16_t type, uint16_t len)
Definition transport.c:51
void transportSendFrame(uint16_t type, const uint16_t *payload, uint16_t len)
Definition transport.c:72
#define TRANSPORT_OK
Definition transport.h:41
#define TRANSPORT_EBADLEN
Definition transport.h:42
#define FRAME_SYNC
Definition transport.h:38
#define TRANSPORT_ECKSUM
Definition transport.h:43
#define TRANSPORT_STREAM_MAX_LEN
Definition transport.h:47