Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Link Protocol

gba::link_multi and gba::link_normal use the same byte-stream protocol above the GBA serial hardware. The selected codec produces a payload, stdgba places that payload in an integrity-checked frame, and the frame is packed into the native transfer words of the selected serial mode.

This protocol is internal to stdgba. It is not the raw protocol used by commercial games, and a peer must implement the same codec, framing, and transport rules to communicate with a stdgba link.

Protocol layers

Each value passes through three layers:

  1. Codec: Converts the C++ value to a sequence of payload bytes.
  2. Frame: Adds delimiters and a CRC, then escapes reserved bytes.
  3. Transport: Packs the wire bytes into 8-, 16-, or 32-bit GBA serial transfers.

The reverse process runs independently for each remote peer, so a partial or malformed frame from one peer cannot corrupt another peer’s decoder state.

Codec payload

The codec completely defines the payload format. Both ends must use compatible codecs; the link protocol does not carry a schema, type identifier, payload length, or protocol version. See Composing Codecs for the codec model and available combinators.

Fixed-width fundamental codecs emit the GBA object representation, which is little-endian. Composite codecs emit their component codecs in the order specified by the codec. Applications that expect protocol evolution should include a version or message tag in their own codec.

For user-defined types, keep the codec as a static member of the type it represents. The codec chapter shows this pattern and explains how to build streams containing several message types.

Frame format

Before escaping, a frame has this logical layout:

0xC0 | payload | CRC low | CRC high | 0xC0

0xC0 is both the opening and closing delimiter. A receiver ignores data until it observes a delimiter, allowing it to join an existing stream or recover synchronization after damaged data.

The two-byte CRC is CRC-16-CCITT with:

PropertyValue
Polynomial0x1021
Initial value0xFFFF
Input reflectionNo
Output reflectionNo
Final XOR0x0000
Trailer byte orderLittle-endian

The CRC covers the unescaped codec payload only. It does not include either delimiter or the CRC trailer itself.

An empty codec payload is valid. Its frame contains the opening delimiter, the CRC of the empty payload, and the closing delimiter.

Byte escaping

Four byte values are reserved on the wire:

Payload or CRC byteWire encodingPurpose
0x000xDB 0xDEIdle padding
0xC00xDB 0xDCFrame delimiter
0xDB0xDB 0xDDEscape prefix
0xFF0xDB 0xDFDisconnected-player sentinel

Escaping applies to both payload bytes and CRC bytes. Unescaped 0x00, 0xC0, 0xDB, and 0xFF never appear as frame contents.

0x00 is used when a hardware transfer word has no frame byte available. Receivers discard it as idle padding. Escaping zero-valued data keeps this indistinguishable padding out of the codec payload.

Multi-Player hardware reports a missing participant as 0xFFFF. Escaping every 0xFF ensures a valid 16-bit wire word can never equal that sentinel.

An unknown escape sequence, an incomplete escape at a delimiter, or a frame shorter than its two-byte CRC trailer is malformed. A CRC mismatch is reported separately as an integrity failure.

Packing into serial words

Wire bytes are packed into hardware words with the earliest byte in the least-significant lane:

bytes:  12 34 56 78
word:   0x78563412

Unused high lanes are filled with 0x00 idle bytes.

Link typeHardware wordBytes per transfer
Normal 8-bit8 bits1
Multi-Player16 bits2
Normal 32-bit32 bits4

Framing is independent of the word size. A frame may begin or end in any byte lane, and several transfers may be needed for one encoded value.

In Multi-Player mode, each round yields one word from every connected player. stdgba maintains one frame parser and codec decoder per remote player. In Normal mode there is only one peer stream.

Sending and flow control

send() encodes and frames the complete value from main code. This keeps codec and framing work out of the serial IRQ. The IRQ consumes prepared wire bytes and stages the next hardware word immediately.

Only one outgoing frame may be in flight. send() returns false when:

  • the previous frame has not been fully staged, or
  • the escaped frame, including delimiters and CRC, is larger than Capacity.

Because escaping can turn one logical byte into two wire bytes, Capacity must account for worst-case expansion. For a fixed payload of N bytes, the largest possible frame is:

2 * N + 6

This is two delimiters plus at most twice the payload and twice the two CRC bytes.

Receive processing and recovery

The serial IRQ records completed raw words in a bounded queue. poll() drains that queue, separates peer streams, removes idle bytes, parses frames, and dispatches errors. read() then feeds complete frame payloads into the selected codec.

The receiver recovers at the next 0xC0 delimiter after malformed input. Errors are exposed as:

ErrorMeaning
receive_overflowIRQ snapshots or pending peer bytes exceeded their bounded capacity
malformed_frameInvalid escaping or an incomplete frame
integrity_failureReceived CRC did not match the payload
decode_failureThe codec rejected the payload or did not finish at the frame boundary
hardware_failureThe GBA serial hardware reported an error
topology_changedA Multi-Player peer or local player ID changed

These errors are sticky until cleared. Error callbacks and decoding run from poll() or read(), never from serial interrupt context.

See also