Alchemy
Loading...
Searching...
No Matches

serial.c

Go to the documentation of this file.
1/// @file
2/// The link cable: payload exchange, block transfers and the link test.
3/// Part of runtime.c, the runtime object, compiled only through it.
4
6
7// The link cable. Two consoles trade a 24-byte payload each VBlank in
8// multiplayer mode; the parent clocks the transfers from timer 3, a halfword
9// at a time. Bulk data rides on that exchange in 20-byte blocks (the
10// BeginTransfer functions and SerialRuntime_StepBlockTransfer).
11
12enum {
18 /// Timer 3's reload: 13981 cycles from one halfword transfer to the next.
20};
21
22/// Resets the serial port into multiplayer mode, installs the serial and
23/// timer 3 interrupt handlers, clears gSerialRuntime and points its packet
24/// buffers into its storage, clears the bulk transfer state and sets
25/// gSerialExchangeActive, which starts the per-frame exchange. Called by the
26/// link lobby, the save menu's link transfer and the link test, and through
27/// the far-call table.
29 u32 interruptEnable;
30 volatile u16 *ime;
31 s32 index;
32 struct SerialRuntime *state;
33
34 /* FAKEMATCH: IRQ_LOCK written out with the IME address set inside the save
35 * statement, so the IME load comes before sub sp, #4; as IRQ_LOCK, 2 of 123
36 * instructions differ in tbs-en. */
37 IME_SAVE((ime = &REG_IME), interruptEnable);
38 IME_DISABLE(ime);
39 // Replace both interrupt handlers before configuring multiplayer transfers.
42 REG_IME = 0;
44 if ((REG_IF & INTR_FLAG_SERIAL) != 0)
46 if ((REG_IF & INTR_FLAG_TIMER3) != 0)
54 state = &gSerialRuntime;
55 REG_IME = 1;
56 /* FAKEMATCH: Dma_Fill32's fill word is volatile, so the zero is stored
57 * through its address (mov r0, sp; str r3, [r0]); as a plain u32, 6
58 * instructions differ in tbs-en. */
59 Dma_Fill32(0, state, sizeof(*state));
60 state->sendIndex = -1;
61 state->sendBuffer[0] = (u16 *)(state->storage + 0);
62 state->sendBuffer[1] = (u16 *)(state->storage + 32);
63 // Each console has separate incoming, completed and pending packet buffers.
64 for (index = 0; index <= 1; index++) {
65 state->incomingBuffer[index] = (u16 *)(state->storage + 64 + index * 96);
66 state->readyBuffer[index] = (u16 *)(state->storage + 96 + index * 96);
67 state->pendingBuffer[index] = (u16 *)(state->storage + 128 + index * 96);
68 }
69 REG_IME = 0;
71 REG_IME = 1;
79 IRQ_UNLOCK(interruptEnable);
80}
81
82/// Lets the timer start transfers again once a link mode is set, by setting
83/// gSerialRuntime.transferEnabled. Nothing in ☀️ calls it; WaitFrames does the
84/// same inline.
86 struct SerialRuntime *state = &gSerialRuntime;
87
88 if (state->mode != 0)
89 state->transferEnabled = 1;
90}
91
92/// Disables the serial and timer 3 interrupts, stops the transfer timer and
93/// clears gSerialRuntime.transferEnabled. Nothing in ☀️ calls it.
95 struct SerialRuntime *state;
96
97 REG_IME = 0;
99 REG_IME = 1;
101 // Timer 3 stopped, with its reload set
104 state = &gSerialRuntime;
105 state->transferEnabled = 0;
106}
107
108/// Looks for the link partner and, once connected, does one exchange (as
109/// SerialRuntime_ExchangePayloads). When this console is the parent (SIOCNT
110/// reports multiplayer mode with no error) it arms timer 3 to clock the
111/// transfers. Called by the Tret tree branch scene for its own exchange.
112/// Returns the link status (see SerialRuntime_ExchangePayloads).
113s32 SerialRuntime_PollAndTransfer(void *send, void *receive) {
114 volatile u32 *siocnt;
115 u32 control;
116 s32 status;
117 s32 result;
118
119 siocnt = &REG_SIOCNT;
120 control = *siocnt;
121 switch (gSerialRuntime.phase) {
122 case 0: {
123 u8 mask30 = control & 0x30;
124 u8 mode;
125
126 if (mask30 == 0 && (mode = control & 0x88) == 8) {
127 u8 serialError = control & 4;
128
129 if (serialError == 0 && gSerialRuntime.sendIndex == -1) {
130 u32 ie;
131
132 Io_Write16(0, &REG_IME);
133 ie = REG_IE;
135 Io_Write16(1, &REG_IME);
136 // SIOCNT's high byte: clear SIO_INTR_ENABLE, as timer 3 now clocks
137 // the transfers
138 *((volatile s8 *)siocnt + 1) &= ~0x40;
142 }
143 } else if (mask30 == 0) {
144 break;
145 }
147 }
148 // Fall through.
149 case 1:
152 break;
153 }
154
157 if (gSerialRuntime.mode == 8)
158 status |= 0x80;
159 result = SerialRuntime_AddParentFlag(status);
160 if (((control << 26) >> 30) > 1)
161 result |= 0x2000;
162 return result;
163}
164
165/// As SerialRuntime_PollAndTransfer, without exchanging data: WaitFrames calls
166/// it once a frame while gSerialExchangeActive is set, and the VBlank handler
167/// does the exchange.
169 volatile u32 *siocnt;
170 u32 control;
171 s32 status;
172 s32 result;
173
174 siocnt = &REG_SIOCNT;
175 control = *siocnt;
176 if (gSerialRuntime.phase == 0) {
177 u8 mode = control & 0x88;
178
179 if (mode == 8) {
180 u8 serialError = control & 4;
181
182 if (serialError == 0 && gSerialRuntime.sendIndex == -1) {
183 u32 ie;
184
185 Io_Write16(0, &REG_IME);
186 ie = REG_IE;
188 Io_Write16(1, &REG_IME);
189 // SIOCNT's high byte: clear SIO_INTR_ENABLE, as timer 3 now clocks
190 // the transfers
191 *((volatile s8 *)siocnt + 1) &= ~0x40;
195 }
197 }
199 }
200
202 if (gSerialRuntime.mode == 8)
203 status |= 0x80;
204 result = SerialRuntime_AddParentFlag(status);
205 if (((control << 26) >> 30) > 1)
206 result |= 0x2000;
207 return result;
208}
209
210/// Once connected (phase 1), gathers the received payloads into `receive` and
211/// builds the next packet from `send`. Called by the VBlank handler while
212/// gSerialExchangeActive is set, which stores the result in gLinkStatus.
213/// Returns the link status:
214/// bits 0 to 7 : the consoles heard from this frame (bit 7 also when parent)
215/// bits 8 to 15 : the consoles heard from since the link began
216/// 0x1000 : this console is the parent
217/// 0x2000 : SIOCNT's multiplayer id is 2 or more
218s32 SerialRuntime_ExchangePayloads(void *send, void *receive) {
219 volatile u32 *siocnt;
220 u32 control;
221 s32 status;
222 s32 result;
223
224 siocnt = &REG_SIOCNT;
225 control = *siocnt;
226 if (gSerialRuntime.phase == 1) {
230 }
232 if (gSerialRuntime.mode == 8)
233 status |= 0x80;
234 result = SerialRuntime_AddParentFlag(status);
235 // SIOCNT bits 4 and 5, the multiplayer id
236 if (((control << 26) >> 30) > 1)
237 result |= 0x2000;
238 return result;
239}
240
241/// Builds the packet to send in sendBuffer[0]: [0] the sequence number, [1]
242/// the consoles not yet heard from, [2] the checksum and [4] the 24-byte
243/// payload. The checksum is the complement of the sum of the first 28 bytes
244/// as halfwords. Then restarts the send position and the timer. Called by
245/// both exchanges.
247 /* FAKEMATCH: volatile keeps the second load of sr->sendBuffer[0], as the
248 * bytes do; through a plain pointer CSE reuses the first (differ 8, 48
249 * instructions either way). */
250 volatile struct SerialRuntime *sr;
251 u32 i;
252 u32 sum;
253
254 sr = SERIAL_RUNTIME;
255 {
256 u8 *pkt;
257 u8 cur;
258 u8 recv;
259
260 pkt = (u8 *)sr->sendBuffer[0];
261 pkt[0] = sr->sequence;
262 cur = sr->currentMask;
263 recv = sr->receivedMask;
264 sum = 0;
265 pkt[1] = recv ^ cur;
266 *(u16 *)(pkt + 2) = sum;
267 Dma_Set(payload, pkt + 4, DMA_COPY_WORDS(6), REG_DMA3);
268 }
269 {
270 u16 *pkt;
271 u32 v;
272
273 pkt = sr->sendBuffer[0];
274 for (i = 0; i < SERIAL_PACKET_HALFWORDS; i++) {
275 v = *pkt;
276 pkt++;
277 sum += v;
278 }
279 }
280 {
281 u32 inv;
282 struct SerialPacket *pkt;
283
284 inv = ~sum;
285 pkt = (struct SerialPacket *)sr->sendBuffer[0];
286 pkt->checksum = inv;
287 }
288
289 // Stop timer 3, rewind the send position and start the timer again
290 if (sr->mode != 0)
291 REG_TM3CNT_H = 0;
292 sr->sendIndex = -1;
293 if (sr->mode != 0 && sr->transferEnabled != 0)
295}
296
297/// Swaps in the packets the interrupt handler finished, then copies the
298/// payload of each one whose checksum is good to `payload` (24 bytes per
299/// console), and records the consoles heard from in currentMask and
300/// receivedMask. Called by both exchanges. Returns the mask of consoles heard
301/// from this frame.
303 u8 flags[8];
304 s32 i;
305 u32 k;
306 u16 *packet;
307 u16 *swap;
308 s32 sum;
309
310 *(u32 *)&flags[4] = 0;
311 // Swap completed buffers with interrupts masked, then validate outside it.
312 Io_Write16(0, &REG_IME);
313 for (i = 0; i <= 3; i++) {
316 gSerialRuntime.readyBuffer[i] = swap;
317 }
318 *(u32 *)flags = *(u32 *)gSerialRuntime.channelFlags;
320 Io_Write16(1, &REG_IME);
322 for (i = 0; i <= 1; i++) {
323 packet = gSerialRuntime.pendingBuffer[i];
324 sum = 0;
325 for (k = 0; k < SERIAL_PACKET_HALFWORDS; k++)
326 sum += packet[k];
327 if (flags[i] == 1 && (s16)sum == -1) {
330 REG_DMA3);
331 gSerialRuntime.currentMask |= 1 << i;
332 }
333 if ((s16)sum == -1)
334 gSerialRuntime.pendingBuffer[i][1] = ~gSerialRuntime.pendingBuffer[i][1];
335 }
338}
339
340/// The serial and timer 3 interrupt, installed by SerialRuntime_Initialize:
341/// each one moves a halfword. It sends the next halfword of the outgoing
342/// packet (0xfefe between packets) and stores the halfword received from each
343/// console; a console's fourteenth halfword completes its packet.
345 // The receive channels share one backedge; a do/while for each adds 50 bytes
346 // in tbs-en.
347 struct SerialRuntime *sendState;
348 struct SerialRuntime *receiveState;
349 struct SerialRuntime *tailState;
350 u16 recv[4];
351 volatile u32 *sioControl;
352 s32 channel;
353
354 // The four consoles' halfwords, SIOMULTI0 to 3.
355 // UB: the u16 array is written through one doubleword store
356#ifdef UBFIX
357 {
358 volatile u16 *multi = (volatile u16 *)REG_SIODATA32;
359
360 recv[0] = multi[0];
361 recv[1] = multi[1];
362 recv[2] = multi[2];
363 recv[3] = multi[3];
364 }
365#else
366 *(u64 *)recv = *(volatile u64 *)REG_SIODATA32;
367#endif
368 sioControl = &REG_SIOCNT;
369 sendState = &gSerialRuntime;
370 sendState->isParent = (*sioControl << 25) >> 31;
371
372 if (sendState->sendIndex == -1) {
373 u32 idleWord = SERIAL_IDLE_HALFWORD;
374 u16 *swap;
375
376 ((volatile u16 *)sioControl)[1] = idleWord;
377 swap = sendState->sendBuffer[1];
378 sendState->sendBuffer[1] = sendState->sendBuffer[0];
379 sendState->sendBuffer[0] = swap;
380 } else if (sendState->sendIndex >= 0) {
381 ((volatile u16 *)sioControl)[1] =
382 sendState->sendBuffer[1][sendState->sendIndex];
383 }
384 receiveState = &gSerialRuntime;
385 if (receiveState->sendIndex <= SERIAL_PACKET_HALFWORDS)
386 receiveState->sendIndex++;
387
388 channel = 0;
389receive_loop:
390 // Hand complete packets to the frame code without overwriting its buffers.
391 {
392 s32 receiveIndex;
393 u16 *incoming;
394
395 if (recv[channel] == SERIAL_IDLE_HALFWORD &&
396 receiveState->receiveIndex[channel] >= SERIAL_PACKET_HALFWORDS) {
397 receiveState->receiveIndex[channel] = -1;
398 } else {
399 receiveIndex = receiveState->receiveIndex[channel];
400 incoming = receiveState->incomingBuffer[channel];
401 incoming[receiveIndex] = recv[channel];
402 if (receiveIndex == SERIAL_PACKET_HALFWORDS - 1) {
403 u16 *swap = receiveState->readyBuffer[channel];
404
405 receiveState->readyBuffer[channel] = incoming;
406 receiveState->incomingBuffer[channel] = swap;
407 receiveState->channelFlags[channel] |= 1;
408 }
409 }
410 }
411 tailState = &gSerialRuntime;
412 if (tailState->isParent != 0)
413 tailState->channelFlags[channel] |= 2;
414 if (tailState->receiveIndex[channel] <= SERIAL_PACKET_HALFWORDS)
415 tailState->receiveIndex[channel]++;
416 channel++;
417 receiveState = tailState;
418 if (channel <= 1)
419 goto receive_loop;
420
421 if (tailState->mode == 8) {
422 REG_TM3CNT_H = 0;
423 REG_SIOCNT16 |= SIO_START;
424 REG_TM3CNT_H = TIMER_ENABLE | TIMER_INTR_ENABLE;
425 }
426}
427
428/// Clears gSerialExchangeActive, which stops the per-frame exchange, and
429/// removes the link interrupt handlers. Called when the link lobby, the save
430/// menu's transfer or a link battle ends.
435}
436
437/// Waits a frame at a time until every status bit in `mask` is set in
438/// gLinkStatus, then returns this console's multiplayer id. Called by the
439/// link test and through the far-call table.
441 if ((mask & gLinkStatus) != mask) {
442 do {
443 WaitFrames(1);
444 } while ((mask & gLinkStatus) != mask);
445 }
446 return (REG_SIOCNT << 0x1A) >> 0x1E;
447}
448
449/// Starts sending `size` bytes from `source` to the partner in 20-byte blocks:
450/// sets the send source, size and block sequence that
451/// SerialRuntime_StepBlockTransfer works from. Called by the link battle,
452/// the link lobby and the save menu's transfer. Returns -1 when a send is
453/// already running, 0 otherwise.
454s32 SerialRuntime_BeginSend(void *source, s32 size) {
455 struct SerialTransferState *state = &gSerialTransfer;
456 u32 savedInterruptMaster;
457
458 if (gSerialSendSource != 0)
459 return -1;
460 IRQ_LOCK(savedInterruptMaster);
461 state->status = 0x80;
462 gSerialSendSource = (s32)source;
463 gSerialSendSize = size;
465 state->active = 1;
466 IRQ_UNLOCK(savedInterruptMaster);
467 return 0;
468}
469
470/// Starts receiving the partner's blocks into `destination`: sets the receive
471/// destination and clears the received size. Called by the same code as
472/// SerialRuntime_BeginSend. Returns -1 when a receive is already running, 0
473/// otherwise.
474s32 SerialRuntime_BeginReceive(void *destination) {
475 struct SerialTransferState *state = &gSerialTransfer;
476 u32 savedInterruptMaster;
477
478 if (gSerialReceiveDest != 0)
479 return -1;
480 IRQ_LOCK(savedInterruptMaster);
481 state->status = 0x81;
482 gSerialReceiveDest = (s32)destination;
484 state->active = 1;
486 IRQ_UNLOCK(savedInterruptMaster);
487 return 0;
488}
489
490/// Each waits a frame at a time until `pending` is 0, giving up after 600000
491/// frames: the send, then the receive.
492#define SERIAL_DEFINE_WAIT(name, pending) \
493 void name(void) { \
494 u32 count = 0; \
495 \
496 if (pending != 0) { \
497 do { \
498 WaitFrames(1); \
499 count++; \
500 } while (count < SERIAL_TRANSFER_TIMEOUT_FRAMES && pending != 0); \
501 } \
502 }
503
506
507/// Waits until both the send and the receive have finished, giving up after
508/// 600000 frames. Reached through the far-call table.
510 u32 count;
511
512 count = 0;
513 while (gSerialSendSource != 0 || gSerialReceiveDest != 0) {
514 WaitFrames(1);
515 count++;
517 return;
518 }
519}
520
521/// Returns bit 0 while a send runs and bit 1 while a receive runs. The link
522/// battle and lobby poll it between frames.
524 s32 flags;
525
526 flags = 0;
527 if (gSerialSendSource != 0) {
528 flags = 1;
529 }
530 if (gSerialReceiveDest != 0) {
531 flags |= 2;
532 }
533 return flags;
534}
535
537
538struct OneCell {
540};
541
542/// Runs each VBlank after the exchange, called by System_VBlankHandler: moves
543/// the bulk transfer on by one 20-byte block each way. The receiver
544/// acknowledges each block's sequence number; when the sender sees an
545/// acknowledgement behind its own count it rewinds and sends the missed
546/// blocks again. A finished send or receive clears gSerialSendSource or
547/// gSerialReceiveDest.
549 /* FAKEMATCH: gSerialSendSize and gSerialBlockSequence are volatile
550 * (serial_runtime.h), so every access is its own load or store; as a plain
551 * gSerialBlockSequence, 258 instructions differ in tbs-en. */
552 struct SerialTransferState *peer;
553 u32 id;
554 struct SerialTransferState *own;
555 u32 gap;
556 /* FAKEMATCH: the halfword record is a HImode 1 loaded from the pool into r8
557 * at entry; it is not protocol state. As the constant 1 at both stores, 55
558 * of 257 instructions differ in tbs-en. */
559 struct OneCell one;
560
561 id = ~((REG_SIOCNT << 26) >> 30) & 1;
562 one.v = 1;
563 own = &gSerialTransfer;
564 peer = (struct SerialTransferState *)gSerialPeerPayloads[id];
565 // Both consoles must have been heard from this frame
566 if ((gLinkStatus & 3) != 3)
567 return;
568 // Receiving advances only when the peer presents the expected sequence.
569 // peerFlags is the sender's block kind: 1 a block with more to come, 2 the
570 // last block.
571 {
572 s32 *receive = &gSerialReceiveDest;
573 s32 dest = *receive;
574 if (dest != 0) {
575 if (own->flags == 1 && (u8)(peer->peerFlags - 1) <= 1) {
576 if (peer->active == (gSerialBlockSequence & 0x7f)) {
577 own->active = 0;
578 switch (peer->peerFlags) {
579 case 1:
580 // A block with more to come: store it and move on
581 Dma_Set(peer->payload, (void *)dest, DMA_COPY_WORDS(5), REG_DMA3);
582 *receive += SERIAL_BLOCK_BYTES;
584 own->status = (own->status + 1) | 0x80;
585 break;
586 case 2:
587 // The last block: store it and report the receive finished
588 Dma_Set(peer->payload, (void *)dest, DMA_COPY_WORDS(5), REG_DMA3);
590 own->flags = 2;
591 own->status = 0;
592 own->active = one.v;
593 break;
594 }
596 } else if (gSerialBlockSequence & 0x80) {
597 // Out of step and already asked: bit 7 of active is the request to
598 // rewind to the sequence in its low bits
599 if (own->active & 0x80) {
600 own->active = one.v;
601 } else if (own->active == 1) {
602 own->active = 0;
603 gSerialBlockSequence &= 0x7f;
604 }
605 } else {
606 own->active = gSerialBlockSequence | 0x80;
607 gSerialBlockSequence |= 0x80;
608 }
609 } else {
610 own->active = 0;
611 }
612 }
613 }
614 {
615 s32 *send = &gSerialSendSource;
616 s32 src = *send;
617 // A peer behind our sequence requests a rewind before the next block.
618 if (src != 0) {
619 if (peer->flags == 1) {
620 if (peer->active & 0x80) {
621 gap = (gSerialBlockSequence - peer->active) & 0x7f;
622 *send = src - gap * SERIAL_BLOCK_BYTES;
625 gSerialBlockSequence &= 0x7f;
626 }
627 // Send the next block, marked as the last when it empties the size
628 if (gSerialSendSize != 0) {
629 Dma_Set((void *)*send, own->payload, DMA_COPY_WORDS(5), REG_DMA3);
631 if (gSerialSendSize != 0) {
632 own->peerFlags = 1;
633 own->active = gSerialBlockSequence & 0x7f;
634 *send += SERIAL_BLOCK_BYTES;
635 } else {
636 own->peerFlags = 2;
637 own->active = gSerialBlockSequence & 0x7f;
638 *send += SERIAL_BLOCK_BYTES;
639 }
641 }
642 }
643 // The partner has stored the last block: the send is finished
644 if (own->peerFlags == 2 && peer->flags == 2) {
646 own->peerFlags = 0;
647 own->active = 1;
648 }
649 }
650 }
651 // flags tells the partner this side is receiving (1) or has received the
652 // last block (2); the receive ends once the partner has seen that
653 if (own->flags == 2) {
654 if (peer->peerFlags != 2) {
655 gSerialReceiveDest = 0;
656 own->flags = 0;
657 }
658 } else {
659 own->flags = 0;
660 if (gSerialReceiveDest != 0)
661 own->flags = 1;
662 }
663}
664
665extern const u8 Rom_Start[];
666
667/// An empty routine the link test calls to waste time.
668void SerialTest_Delay(void) {}
669
670/// A link cable test left in the game: it shows a row of tiles, receives blocks
671/// into the map cell buffer and shows them as tiles, and sends a part of the
672/// ROM when A or B is held. It never returns. Nothing in ☀️ reaches it.
673void SerialTest_Run(void) {
674 // An outer receive loop with an inner completion break changes 9 bytes in
675 // tbs-en: the key-address load is hoisted and the restart retargeted.
676 u16 *tile;
677 s32 value;
678 s32 n;
679 s32 tick;
680
683 n = 19;
684 tile = (u16 *)BG_SCREEN_BLOCK(4) + 16 * 32 + 19;
685 value = (s16)0xf093;
686 do {
687 n--;
688 *tile = value;
689 tile--;
690 value--;
691 } while (n >= 0);
692 Cpu_Fill32(0, gMapCellBuffer, 0x400);
694restart:
696 for (;;) {
697 if (gKeysHeld & KEY_A)
699 if (gKeysHeld & KEY_B)
700 SerialRuntime_BeginSend((void *)(Rom_Start + 0x1000), 0x280);
701 if (gKeysHeld & KEY_START) {
702 tick = 9999;
703 do {
704 tick--;
706 } while (tick >= 0);
707 }
708 if (gSerialReceiveDest == 0) {
709 Dma_Set(gMapCellBuffer, (void *)0x06001000, DMA_COPY_WORDS(0xa0),
710 REG_DMA3);
711 goto restart;
712 }
713 WaitFrames(1);
714 }
715}
716
717#undef SERIAL_DEFINE_WAIT
#define Cart_Restart
Definition cartboot.h:9
#define Cpu_Fill32(value, destination, size)
pret's CpuFill32: repeats one word through the BIOS CpuSet service.
Definition dma.h:51
static __inline__ void Dma_Set(const volatile void *source, volatile void *destination, u32 control, volatile u32 *channel)
Starts a DMA transfer: stores the source, destination and control words into channel's registers with...
Definition dma.h:62
#define Dma_Fill32(value, destination, size)
Fills size bytes at destination with the word value by DMA3.
Definition dma.h:30
void AudioCommand_PlayFar(s32 id)
void Runtime_SetIrqHandler(u32 irq, s32 vcount, InterruptHandler handler)
Installs handler for interrupt irq and enables that interrupt, or with a null handler disables it and...
Definition frame.c:37
void WaitFrames(s32 frames)
The game's frame loop: runs frames frames and returns.
Definition frame.c:97
struct EffectStep gMapCellBuffer[]
Map cells; battle effects reuse this as a particle array.
#define REG_IF
Definition io_reg.h:306
#define REG_RCNT
Definition io_reg.h:185
#define REG_TM3CNT
Definition io_reg.h:172
#define BG_SCREEN_BLOCK(n)
Definition io_reg.h:167
#define REG_TM3CNT_H
Definition io_reg.h:173
#define RCNT_SIO_MODE
RCNT's mode: 0 leaves the port to SIOCNT's serial modes; bit 15 alone makes it general-purpose pins.
Definition io_reg.h:196
#define TIMER_INTR_ENABLE
TMxCNT_H flags, as pret names them.
Definition io_reg.h:176
#define SIO_MULTI_MODE
Definition io_reg.h:191
#define REG_IE
Definition io_reg.h:305
#define REG_SIOCNT16
Definition io_reg.h:181
@ KEY_B
Definition io_reg.h:244
@ KEY_START
Definition io_reg.h:246
@ KEY_A
Definition io_reg.h:243
#define REG_IME
Definition io_reg.h:308
#define REG_DMA3
Definition io_reg.h:153
#define SIO_115200_BPS
Definition io_reg.h:188
#define TIMER_ENABLE
Definition io_reg.h:177
#define DMA_COPY_WORDS(n)
Definition io_reg.h:226
#define SIO_INTR_ENABLE
Definition io_reg.h:192
#define RCNT_GPIO_MODE
Definition io_reg.h:197
#define SIO_32BIT_MODE
Definition io_reg.h:190
#define REG_SIOCNT
Definition io_reg.h:180
#define REG_SIODATA32
Definition io_reg.h:179
#define Io_Write16(v, reg)
Writes a value to a 16-bit register or memory location.
Definition iowrite.h:14
#define INTR_FLAG_TIMER3
Definition irq.h:31
@ IRQ_SERIAL
Definition irq.h:20
@ IRQ_TIMER3
Definition irq.h:19
#define INTR_FLAG_SERIAL
Definition irq.h:32
#define IRQ_UNLOCK(saved)
Restores the IME value saved by IRQ_LOCK.
Definition irqlock.h:50
#define IME_DISABLE(ime)
Disables interrupts.
Definition irqlock.h:23
#define IRQ_LOCK(saved)
Disables interrupts and saves IME in saved; IRQ_UNLOCK restores it.
Definition irqlock.h:38
#define IME_SAVE(ime, saved)
Saves the original IME value in saved.
Definition irqlock.h:17
volatile u32 gKeysHeld
Keys down now.
What every part of the runtime object shares: its includes and the library call two parts make.
void SerialRuntime_DisableTransfer(void)
Disables the serial and timer 3 interrupts, stops the transfer timer and clears gSerialRuntime....
Definition serial.c:94
void SerialRuntime_EnableTransferTimer(void)
Lets the timer start transfers again once a link mode is set, by setting gSerialRuntime....
Definition serial.c:85
void SerialRuntime_WaitForSend(void)
Definition serial.c:504
const u8 Rom_Start[]
s32 SerialRuntime_BeginReceive(void *destination)
Starts receiving the partner's blocks into destination: sets the receive destination and clears the r...
Definition serial.c:474
void SerialRuntime_WaitForTransfers(void)
Waits until both the send and the receive have finished, giving up after 600000 frames.
Definition serial.c:509
void SerialTest_Delay(void)
An empty routine the link test calls to waste time.
Definition serial.c:668
s32 SerialRuntime_BeginSend(void *source, s32 size)
Starts sending size bytes from source to the partner in 20-byte blocks: sets the send source,...
Definition serial.c:454
void SerialRuntime_RemoveIrqHandlers(void)
Clears gSerialExchangeActive, which stops the per-frame exchange, and removes the link interrupt hand...
Definition serial.c:431
void BattleLink_ResetTransferState(void)
Definition serial.c:536
u32 SerialRuntime_WaitForStatusMask(s32 mask)
Waits a frame at a time until every status bit in mask is set in gLinkStatus, then returns this conso...
Definition serial.c:440
s32 SerialRuntime_PollStatus(void)
As SerialRuntime_PollAndTransfer, without exchanging data: WaitFrames calls it once a frame while gSe...
Definition serial.c:168
void SerialRuntime_WaitForReceive(void)
Definition serial.c:505
@ SERIAL_BLOCK_BYTES
Definition serial.c:15
@ SERIAL_IDLE_HALFWORD
Definition serial.c:17
@ SERIAL_PACKET_HALFWORDS
Definition serial.c:13
@ SERIAL_PAYLOAD_BYTES
Definition serial.c:14
@ SERIAL_TIMER_RELOAD
Timer 3's reload: 13981 cycles from one halfword transfer to the next.
Definition serial.c:19
@ SERIAL_TRANSFER_TIMEOUT_FRAMES
Definition serial.c:16
void SerialRuntime_PrepareSendPacket(void *payload)
Builds the packet to send in sendBuffer[0]: [0] the sequence number, [1] the consoles not yet heard f...
Definition serial.c:246
s32 SerialRuntime_PollAndTransfer(void *send, void *receive)
Looks for the link partner and, once connected, does one exchange (as SerialRuntime_ExchangePayloads)...
Definition serial.c:113
void SerialTest_Run(void)
A link cable test left in the game: it shows a row of tiles, receives blocks into the map cell buffer...
Definition serial.c:673
s32 SerialRuntime_GetActiveTransfers(void)
Returns bit 0 while a send runs and bit 1 while a receive runs.
Definition serial.c:523
#define SERIAL_DEFINE_WAIT(name, pending)
Each waits a frame at a time until pending is 0, giving up after 600000 frames: the send,...
Definition serial.c:492
void SerialRuntime_Initialize(void)
Resets the serial port into multiplayer mode, installs the serial and timer 3 interrupt handlers,...
Definition serial.c:28
void SerialRuntime_StepBlockTransfer(void)
Runs each VBlank after the exchange, called by System_VBlankHandler: moves the bulk transfer on by on...
Definition serial.c:548
s32 SerialRuntime_ExchangePayloads(void *send, void *receive)
Once connected (phase 1), gathers the received payloads into receive and builds the next packet from ...
Definition serial.c:218
u8 SerialRuntime_CollectReceivedPayloads(void *payload)
Swaps in the packets the interrupt handler finished, then copies the payload of each one whose checks...
Definition serial.c:302
void SerialRuntime_HandleTransferInterrupt(void)
The serial and timer 3 interrupt, installed by SerialRuntime_Initialize: each one moves a halfword.
Definition serial.c:344
#define SERIAL_RUNTIME
s32 gSerialReceiveDest
volatile u16 gLinkStatus
Link status, updated by the VBlank exchange handler.
volatile u16 gSerialSendSize
volatile: tagged where it steers, in SerialRuntime_StepBlockTransfer (tbs runtime....
static s32 SerialRuntime_AddParentFlag(s32 status)
struct SerialRuntime gSerialRuntime
u8 gLinkExchangeState
volatile u8 gSerialBlockSequence
volatile: tagged where it steers, in SerialRuntime_StepBlockTransfer (tbs runtime....
volatile u16 gSerialReceivedSize
volatile: tagged where it steers, in BattleLink_ReceiveActions (tbs battle.c) and LinkLobby_ReceivePa...
u16 gSerialExchangeActive
static __inline__ void SerialRuntime_ResetTransferState(void)
u8 gSerialPeerPayloads[4][24]
s32 gSerialSendSource
struct SerialTransferState gSerialTransfer
#define MUS_003
Definition songs.h:13
u16 v
Definition serial.c:539
A link cable packet: its sequence number, the consoles not yet heard from, its checksum and a 24-byte...
u16 checksum
The complement of the sum of the packet's first 14 halfwords, taken with this field 0.
The link cable's runtime: its mode and phase, the consoles heard from, the send position and the pack...
u8 storage[0x100]
The buffers' memory: two 32-byte send buffers, then 96 bytes a console for its incoming,...
u8 mode
SIOCNT's bits 7 and 3 when this console armed timer 3 to clock the transfers: 8 then,...
u16 * readyBuffer[4]
Each console's last complete packet, swapped in by the interrupt.
u16 * sendBuffer[2]
The packet being built ([0]) and the packet being sent ([1]).
u8 channelFlags[4]
Set by the interrupt for each console and cleared each frame.
s32 sendIndex
The halfword of sendBuffer[1] sent next; -1 between packets, when 0xfefe is sent and the two send buf...
u8 transferEnabled
Nonzero lets a new packet restart timer 3.
u8 phase
0 looking for the partner, 1 connected
u16 * incomingBuffer[4]
Each console's packet being received; two are used.
u8 currentMask
the consoles heard from this frame
u8 sequence
The packet sequence number, counted up each frame once connected.
u16 * pendingBuffer[4]
The packets the frame code checks and copies, swapped with readyBuffer each frame.
u8 isParent
SIOCNT's bit 6, copied by each transfer interrupt; nonzero adds 0x1000 to the link status.
u8 receivedMask
the consoles heard from since the link began
A console's block transfer state, exchanged with the partner each VBlank: its block sequence,...
u8 peerFlags
The send state: 1 a block with more to follow, 2 the last block, 0 idle.
u8 flags
The receive state: 1 receiving, 2 the last block taken, 0 idle.
u8 status
0x80 sending or 0x81 receiving when started; the receiver counts blocks (status + 1,...
u8 payload[20]
one block of the data sent
u8 active
The sequence number of the block in payload; as receiver, the one it expects with bit 7 set to ask fo...
signed int s32
Definition types.h:14
unsigned int u32
Definition types.h:15
signed char s8
Definition types.h:10
signed short s16
Definition types.h:12
unsigned short u16
Definition types.h:13
unsigned long long u64
Definition types.h:17
unsigned char u8
Definition types.h:11