Skip to content
RetrogamingDeep Dive Published Updated 9 min readViews unavailable

Super Game Boy Command Protocol: Packets, Timing, and Multiplayer Input

Implement Super Game Boy commands with correct JOYP pulse encoding, packet framing, transfer delays, multiplayer requests, and emulator validation.

The Super Game Boy (SGB) is not simply a Game Boy picture enlarged by a Super Nintendo. The adapter contains Game Boy circuitry and an intercommunication device; its system software transfers the Game Boy image into SNES memory, mixes the handheld’s audio through the SNES audio path, and can process commands for features such as color palettes, borders, sound effects, and multiple controllers. A compatible Game Boy program requests those features over the same two joypad-select lines used by ordinary button scanning. That shared interface is why SGB support belongs in the emulator’s input and timing model, not just in a post-processing filter.

The most useful engineering boundary is to treat an SGB command as a serial protocol with explicit framing and pacing. The Game Boy sends command packets through JOYP’s P14/P15 lines, least-significant bit first, and leaves a required interval between packets. A command may be one or several 16-byte packets. Software that sends only the payload bytes, reverses bit order, omits the packet-ending bit, or streams packets back-to-back can appear to work in permissive emulators while failing on hardware.

The adapter and the two data paths

The SGB receives the Game Boy’s four-level image signal and converts it into SNES character data. Its system program copies that data through SNES work RAM and video RAM for display; the SNES surrounds the handheld image with a frame and can apply SGB palettes to regions of the 160-by-144-pixel Game Boy window. Commands and small control records use the JOYP register file. Larger graphics or sound data can use a different path in which the Game Boy deliberately renders data through its video output for the adapter to capture.

Do not conflate these paths. A palette or multiplayer request is a JOYP command packet. Character, border, or attribute data may require a documented transfer command followed by image-signal transfer and masking. An implementation that models only the JOYP command stream may support palette changes and controller selection while still lacking the larger data-transfer features. Report that capability boundary accurately.

JOYP signals encode the serial bits

JOYP is at FF00. Bits 4 and 5, often named P14 and P15, select the two button groups during normal input polling. For SGB command transfer, the program uses low-going pulses on those same lines: pulling P14 low represents a zero bit; pulling P15 low represents a one bit. The receiving circuit is first reset and armed by driving both lines low. Bits are transmitted least-significant-bit first.

Each packet contains 128 data bits (16 bytes), followed by a 129th zero bit that terminates that packet. The next packet in a multi-packet command is a new transfer with its own start sequence and terminating bit. The command header occupies byte zero: the upper five bits contain the command number and the low three bits specify the total packet count, including the first packet. Thus the header value is (command << 3) | packet_count; packet counts range from one through seven. The remaining fifteen bytes in packet zero, and all sixteen bytes in each later packet, carry command data.

The following is a framing sketch rather than a ready-to-flash routine. send_pulse must drive the selected JOYP line low for a valid pulse and return both selection lines high for the inter-pulse space. The bit loop order is important.

void send_packet(const uint8_t packet[16]) {
    joyp_write(0x00);           // Start/reset: P14 and P15 both low
    pulse_space();

    for (unsigned byte = 0; byte < 16; ++byte) {
        for (unsigned bit = 0; bit < 8; ++bit) {
            if ((packet[byte] >> bit) & 1)
                send_pulse(P15_LOW); // one
            else
                send_pulse(P14_LOW); // zero
        }
    }

    send_pulse(P14_LOW);        // Packet 129 terminator: zero
    joyp_write(0x30);           // Release both select lines
}

This sketch intentionally hides hardware-specific register writes and delays. It also assumes that send_pulse restores the inactive-high state and that the caller enforces the inter-packet delay. A production routine should preserve the previous interrupt state, avoid racing the game’s joypad interrupt handler, and restore the ordinary JOYP selection state after command transmission. Do not copy the hexadecimal JOYP values without checking the register’s active-low semantics and the exact write behavior of the target Game Boy revision.

The Nintendo Game Boy Programming Manual specifies a minimum pulse width of 5 microseconds and a minimum space of 15 microseconds in its transmission diagram. Pan Docs also records the common SGB routine’s timing in machine cycles. Use a routine validated against the target hardware and emulator compatibility tests; do not infer that faster pulses are universally safe just because one device accepts them. Between completed packets, wait approximately 60 milliseconds (four frames). The SGB system software may be occupied with other work, so omitting the gap is not a harmless throughput optimization.

Constructing commands and keeping payloads bounded

Build packets from a fixed 16-byte buffer initialized to zero. This avoids leaking uninitialized RAM into reserved fields, which can make behavior dependent on power-on state. Set byte zero from the command number and total packet count, then populate only fields defined for that command. Multi-packet commands use the rest of packet zero and subsequent full packets as one command’s payload; they are not a sequence of unrelated commands.

For example, the MLT_REQ command requests one-, two-, or four-controller mode. Its command number is $11, so a one-packet header is $89 ($11 * 8 + 1). The request value is in byte one: zero requests single-player mode, one requests two-player mode, and three requests four-player mode. Four-player play needs a compatible external adapter because a standard SNES controller interface has only two ports. A game must not assume that a four-player request creates four physical inputs on its own.

Keep command constants separate from packet framing and command-specific payload construction. That makes it easier to test the invariant that every packet is 16 bytes, has a valid header count, and carries a zero terminator. It also makes unsupported command handling explicit. The SGB command table includes commands that transfer palettes, set area attributes, transfer character data, mask the handheld window, and request multiplayer mode. Each has its own payload layout and, in some cases, a second image-signal transfer phase. Do not reuse one command’s payload interpretation for another merely because both fit in the same packet size.

Multiplayer input is a JOYP mode change

After requesting multiple controllers, the Game Boy program reads player information through JOYP. With both button groups deselected, the lower nibble identifies which player’s button state follows: $F for player one, $E for player two, $D for player three, and $C for player four. The game then scans the ordinary button groups for that selected player. This protocol is distinct from a Game Link Cable transfer: there is no peer Game Boy clocking eight serial bits through SB/SC.

Treat the MLT_REQ transition as stateful. Changing from four-player to two-player mode while a later controller is selected changes the active index according to the SGB’s mode behavior, and command processing itself can advance the controller selection counter. A robust game performs its mode setup during initialization, reads the ID pattern as documented, and handles absent or unsupported SGB hardware by continuing with one player. Do not infer four-player hardware solely because the software sent a request.

SGB and SGB2 are not interchangeable in every physical detail. For example, Nintendo’s programming manual describes a communication connector on SGB2 that the original model lacks. Keep feature detection and model assumptions separate from the common command packet format. If a game needs a model-specific facility, test it explicitly and preserve a fallback path.

Emulation and regression tests

An SGB-capable emulator needs to distinguish ordinary button-group reads from command transmission. During command framing, it should observe the reset, classify each low pulse as zero or one, assemble bits least-significant-first, consume exactly 128 data bits plus the terminating zero, and apply the command only after a valid packet sequence. For a multi-packet command, verify the declared count and required delay instead of applying each continuation packet independently. Invalid framing should not mutate palettes or controller state.

Build deterministic tests around protocol boundaries:

  • Send $11 with packet count one and a four-player request; verify the header, payload byte, and returned player ID sequence.
  • Reverse the bit order and confirm that the command is rejected rather than interpreted as another command.
  • Omit the 129th bit, use a nonzero terminator, or declare two packets but send one; verify incomplete commands do not take effect.
  • Send two valid packets with and without the documented inter-packet wait, and confirm the implementation models receiver availability rather than accepting an impossible instantaneous stream.
  • Run the same game with SGB support disabled and confirm ordinary active-low button polling remains correct.
  • Verify screen masking and large data-transfer commands separately from register-file commands; they use different transfer mechanisms.

Hardware validation should include at least an original SGB and SGB2 when available, because emulator acceptance alone does not establish pulse timing on both revisions. Log the JOYP writes and emulated time for every pulse during debugging, but avoid turning timing logs into permanent per-frame output. If a game freezes only on real hardware, first compare line release, bit order, packet terminator, interrupt interference, and the four-frame packet gap before changing command payloads.

A precise compatibility contract

SGB support is a small protocol surface with surprisingly broad consequences: the JOYP register serves both controls and commands, packets are bit-timed, multi-packet operations are paced, and some operations transition into a separate video-data path. Keep these layers independently testable. A correct packet decoder does not imply correct SGB border rendering, and working palette commands do not imply that multiplayer input or SGB2-specific behavior is implemented.

Document which commands are supported, which SGB revisions were tested, and whether timings were validated on hardware or only against reference tests. That turns “SGB compatible” from a vague checkbox into a useful engineering claim, while giving game code and emulator implementations a clear fallback for the ordinary Game Boy path.

Related:

Sources:

Comments