Game Boy Color Infrared Port: RP Register Semantics and Link Timing
Implement the Game Boy Color RP infrared register, separate transmit and receive paths, and design game-specific pulse protocols without assuming IrDA.
The Game Boy Color’s infrared port is a small hardware interface, not a standardized wireless network stack. The $FF56 RP register controls the console’s infrared emitter and receiver path. It gives software a way to drive an IR LED and sample a sensor, but it does not define a common packet format, device address, checksum, or retransmission policy. Games and accessories choose their own pulse timing and framing above that register contract.
That distinction matters both to homebrew and to emulator developers. A front end that maps an IR accessory to a generic serial stream has already invented a protocol the console never supplied. A reliable implementation keeps RP bits, physical signal state, elapsed time, and game-level decoding separate. It also represents what the receiving sensor can report, rather than converting ambient light directly into a host boolean named “connected.”
RP is a pair of paths behind one register
The emitter and receiver are distinct circuit functions. RP bit 0 controls the outgoing infrared light. The receive-enable field occupies the upper bits of the register, while the sensor state is exposed through a read bit. Reserved and unused bits have documented read behavior and should be returned consistently. The exact mask should be taken from the selected hardware reference rather than inferred from a C structure’s padding or a host API.
Reading the sensor is not simply a software loop that polls a pin at arbitrary times. The receiver has sensitivity and adaptation behavior. Pan Docs describes a signal fade effect: a sustained light level can become the new baseline, after which the receiver no longer reports a continuously asserted condition in the same way. A model that leaves “bright” true forever can therefore be less faithful than a time-dependent sensor with explicit ambient and adaptation state.
Do not confuse the device’s physical signaling with the Game Boy serial port. Serial cable transfer uses the serial data and clock registers and a bit clock. Infrared uses optical on/off transitions through RP. An accessory may implement a pulse-width or pulse-spacing protocol over IR, but that protocol lives in the game or peripheral. It must not be decoded by the register layer.
Writes and reads have different responsibilities
An RP write can turn the emitter on or off and can enable or disable receive sensing. Keep those controls separate in state. Turning off the emitter should not erase a received event that the game has not yet sampled unless the hardware contract says the receiver state is cleared. Similarly, disabling receive sensing should affect the returned sensor reading, not silently erase the last transmitted pulse from another device’s simulation.
An RP read combines the current output configuration with the sensor result and reserved-bit values. Preserve read-only status and writable control fields independently. If the emulator stores the whole byte as a simple last-written register, a later read can incorrectly echo a value for a hardware-generated input bit. Add a test that writes several combinations, changes the external signal, and confirms the next read reflects both the write-controlled and sensor-controlled fields.
The receiver’s active level should be modeled using the documented bit meaning, not a guessed convention based on generic remote controls. A console-to-console interaction also needs the two endpoints’ relative timing: the transmitting Game Boy emits a pulse, optical propagation is effectively immediate at game timescales, and the receiving device samples according to its own enable and adaptation state. Tests should preserve whether a transition was missed because receive was disabled, the sample arrived between reads, or the sensor had adapted.
There is no universal Game Boy IR packet
The RP register exposes a signal path, not a transport protocol. Two games can use different pulse widths, gaps, symbol encodings, idle periods, and application-level checks. One game might send a short pulse for a zero and a longer pulse for a one; another might use edge spacing, a preamble, or repeat timing. Do not describe either as “the Game Boy Color infrared protocol” without naming the game or accessory whose framing was measured.
This creates an important boundary for emulator architecture. The core should provide deterministic electrical-level output transitions and receiver samples. A multiplayer service or front end may connect two emulated consoles by forwarding pulse events, but it should not interpret packet meanings unless the feature explicitly emulates a named game protocol. Keeping the interface low-level also allows an external IR source to be represented without pretending it is another GBC.
The sensor’s automatic response to illumination complicates repeated experiments. Pan Docs warns that continuous infrared light can change the receiver baseline. A test that holds a source active and repeatedly reads RP may therefore see a transition from detected to ambient even though the light did not turn off. Test fixtures should include dark baseline calibration, brief pulses, longer illumination, and a recovery interval. Record the receiver enable state and signal history with each sample.
Power and timing belong in diagnostics
Infrared emission consumes power. A handheld game that leaves the LED active unnecessarily can reduce battery life, so an emulator should expose the guest-visible control and a power estimate or activity indicator separately. Do not automatically turn the emitter off because the host application loses focus; that would alter game behavior. If a host policy pauses or disconnects external devices, report that as an environmental limitation rather than silently rewriting RP state.
The game clock determines pulse duration. Use emulated cycles or a rational timebase tied to the selected hardware speed, not host wall-clock sleeps. If the receiving device samples after a guest loop, a pulse shorter than one frame may still be observable. A frame-only API that exchanges one byte per VBlank loses the edge timing that an application may be measuring.
Timestamp every transition in the same time domain used by CPU and timer events. If the implementation batches several CPU cycles, ensure the IR edge is ordered relative to an RP read in the same batch. The guest must see a consistent result when a write toggles the emitter immediately before or after a receiver sample. Save states should serialize pending transitions, receiver adaptation level, external signal state, and timing phase.
A deterministic signal fixture
The following helper is a test harness for scheduling pulses. It does not encode a Game Boy title’s protocol or model the physical sensor. Its purpose is to keep the chosen pulse boundaries explicit and reject malformed fixtures.
def validate_ir_edges(edges):
previous_cycle = -1
previous_level = None
for cycle, level in edges:
if cycle < 0 or cycle <= previous_cycle:
raise ValueError("IR edge cycles must increase")
if level not in (0, 1) or level == previous_level:
raise ValueError("each entry must change the binary signal level")
previous_cycle = cycle
previous_level = level
return tuple(edges)
assert validate_ir_edges([(12, 1), (24, 0), (41, 1)]) == (
(12, 1), (24, 0), (41, 1)
)
Extend the fixture with a receiving enable state, a documented sampling point, and a sensor adaptation model only when that model is sourced. Keep raw edges in logs so a protocol decoder can be changed without changing the emulated electrical trace.
Validation plan for real software and peripherals
Begin with RP register tests: read default state, write each control bit, toggle receive enable, and verify reserved-bit behavior. Then connect a deterministic external signal source. Use pulses shorter than a frame, around a read boundary, and separated by increasingly long idle intervals. Repeat with the emitter active and inactive to ensure transmit configuration does not leak into the receive path.
At integration level, capture one known game or accessory exchange as timestamps plus RP writes and reads. Preserve the ROM hash, region, hardware revision if known, and the exact frame/cycle of each event. Replay the same external signal into the target and a reference implementation. Compare the guest’s raw RP values before comparing decoded application messages. When the game protocol is undocumented, state that it is an observed trace rather than a universal standard.
The receiver’s light-adaptation behavior deserves a separate test. Establish a dark baseline, apply a constant source, sample at a fixed interval, then remove the source and measure recovery. The result should be tied to the quality of available hardware evidence. Avoid claiming a precise analog threshold from an emulator cross-check alone, because that can reproduce another implementation’s simplification rather than the silicon response.
Acceptance criteria
A dependable RP model exposes the $FF56 register’s transmit control, receive enable, sensor value, reserved-bit behavior, and documented adaptation limits. It schedules optical transitions in emulated time and keeps game-specific packet interpretation above the hardware interface. Logs can distinguish a guest register mistake, disabled receiver, missed short pulse, adaptation state, and host link loss.
Related:
- Game Boy Link Cable Serial Transfer: Registers, Clocking, and Timeouts
- Game Boy JOYP Input: Active-Low Matrix Scanning and Interrupt Edges
Sources: