Skip to content
RetrogamingDeep Dive Published Updated 7 min readViews unavailable

SNES Auto-Joypad Read: VBlank Sampling, Result Words, and Manual Reads

Understand SNES auto-joypad enable and busy flags, four result words, bit ordering, VBlank sampling, and when software must switch to manual polling.

SNES input can be read manually through serial I/O or collected by the CPU’s automatic joypad-read hardware. The automatic path starts around vertical blank, clocks controller data, and writes four 16-bit result words to $4218-$421F. It is a timed bus operation that runs without making software issue sixteen reads itself. The feature has a clear limit: it retrieves a fixed number of bits and cannot replace manual polling for every accessory or every extended report.

The SNES register reference maintained in the snesdev documentation describes both the I/O registers and the returned bit order. In particular, bit zero of $4200 enables automatic joypad reading, $4212 exposes a busy flag, and $4218-$421F hold the four result words. Those details define a hardware contract that should be tested as a peripheral transaction, not implemented as a call to a host gamepad API whenever the emulator enters VBlank.

Enable, schedule, and observe completion

NMITIMEN at $4200 controls NMI, IRQ sources, and the auto-joypad-read enable bit. If the auto-read bit is set, the hardware initiates a controller scan during the VBlank sequence. The controller serial ports are accessed internally, and the results become available in the four JOY words. The operation is associated with the display frame; it does not wait for the game’s NMI handler to manually pulse the controller latch.

HVBJOY at $4212 reports video timing flags and the joypad-read-in-progress state. Software can use the J flag to avoid treating an unfinished result as final. A write that enables auto-read does not mean that the latest values are immediately replaced; the next scan happens according to the video timing. Before that scheduled scan completes, the result registers may still hold the prior frame’s data. Tests should therefore record which frame produced each JOY word.

The auto-read engine serializes a fixed number of bits from each of the two controller ports’ data lines. The SNES port wiring can carry two data streams per physical port, yielding four result words. The low/high bytes of each word are accessible separately. The order in which serial bits were received and the bit positions in the word are easy to confuse: the first bit read is placed at the high end of the 16-bit result, leaving subsequent bits in lower positions.

The timing is tied to VBlank and hardware region. A PAL and NTSC game configuration do not have identical frame lengths, and overscan affects vertical timing. Avoid keying the operation to a hard-coded host timer or simply “the next frame callback.” Advance the controller-read microsequence with the emulated PPU/video state and maintain the busy interval as guest-visible state.

Result word layout

The standard SNES pad returns B, Y, Select, Start, Up, Down, Left, Right, A, X, L, R, followed by four high bits for the remaining positions in the 16-bit report. These positions are defined by the device protocol, not by a universal rule that every connected peripheral will populate all sixteen bits identically. The first eight positions are related to an NES-style controller report but have the SNES face buttons substituted and additional controls appended.

The CPU sees each 16-bit result as low and high byte addresses. If code loads the low byte first, the first serial bit may appear in a later operation on the high byte; it is not correct to interpret the low byte as the first eight physical bits. Decode a result word using an explicit bit index and endianness rule, then map those logical button bits to the game’s desired structure. Keep the raw four words in debugging output before converting them to named buttons.

The manual read path remains available through $4016 and $4017. Software drives the latch line, clocks the port by reading, and can continue beyond the sixteen automatically sampled bits. This matters for accessories such as the SNES mouse, whose report can exceed the automatic read length. A common hybrid design uses auto-read for the initial pad word and manual reads for extra peripheral data. A device that requires custom command phases may need fully manual control instead.

Do not assume an automatic read and software-driven reads can be mixed arbitrarily during the hardware busy period. The SNES technical register documentation marks some behavior during auto-read as uncertain. A compatibility test should use documented sequences and isolate any game-specific workaround; the emulator should not invent semantics that the source does not establish.

Four words are not four independent host devices

The words JOY1 through JOY4 represent the two data lines on each physical controller port. A first-port multitap or a peripheral adapter can place additional controller states on those lines, and port configuration determines which physical protocols are in use. They are hardware result lanes, not necessarily a direct one-to-one map to player-one through player-four abstractions.

Represent the controller port as a device topology: physical port, Data1 stream, Data2 stream, attached accessory, and each accessory’s serialization state. Then the auto-read engine performs a fixed read schedule against those streams and captures four words. If an accessory does not provide a standard pad response, preserve its actual protocol rather than fabricating a neutral controller for unused words.

When auto-read is disabled, the JOY results should not be refreshed by an invisible host-side input poll. When it is enabled, input changes during the scan need to be captured according to the hardware latch and shift timing, rather than being applied retroactively after completion. A deterministic emulation model should state exactly when it snapshots the pad and which reads consume that snapshot.

A word decoder for tests

This Python helper interprets a completed 16-bit standard-pad result. The mapping table follows the documented button order and reports pressed bits without assuming host keyboard names or controller layout.

SNES_BUTTON_BITS = {
    "B": 15,
    "Y": 14,
    "SELECT": 13,
    "START": 12,
    "UP": 11,
    "DOWN": 10,
    "LEFT": 9,
    "RIGHT": 8,
    "A": 7,
    "X": 6,
    "L": 5,
    "R": 4,
}


def decode_standard_pad(word):
    if not 0 <= word <= 0xFFFF:
        raise ValueError("joypad result must be a 16-bit word")
    return {name: bool(word & (1 << bit)) for name, bit in SNES_BUTTON_BITS.items()}


assert decode_standard_pad(0x8010)["B"]
assert decode_standard_pad(0x8010)["R"]
assert not decode_standard_pad(0x8010)["A"]

The helper ignores the four trailing report bits, which are not standard named pad buttons in this mapping. A hardware test should additionally assert that the auto-read operation stores each serial bit at the correct word position and that the high/low byte reads preserve those positions.

Verification matrix

With a single standard controller, test auto-read disabled and enabled across an entire frame. Observe $4200, $4212, and all JOY registers before, during, and after VBlank. Confirm the busy flag window, exact frame of refresh, and the ordering of the four result words. Change the held-button pattern each frame so stale results cannot pass unnoticed.

Test the four data lanes independently by placing a different unique button pattern on each controller stream. Then test latch timing and change an input near the scan boundary to determine whether the reported sample is from before or after the edge. Repeat under PAL, NTSC, and overscan settings. Do not rely solely on a game screenshot; save a cycle-stamped register trace and decoded result words.

For manual polling, issue the documented latch transition and read each line until the standard report is exhausted. Continue beyond sixteen bits with a mouse or another supported accessory and verify that the manual stream state is consistent with any preceding auto-read. Include accesses attempted while busy and compare only behavior established by documentation or hardware capture.

Save-state tests should restore just before scan start, while $4212 reports the operation in progress, and after the result words update. The restored machine must complete the same internal reads, raise or clear status at the same cycles, and produce identical JOY words. Host input events arriving during a paused or restored state must not alter the already latched frame’s guest-visible snapshot.

Acceptance criteria

A correct auto-joypad model is a video-timed serial scan with a documented fixed report length, four result words, a busy indicator, and a separate manual-read path. It preserves the relation between frame, sample, raw word layout, and device topology. That is enough to emulate ordinary pad polling efficiently without falsely claiming that automatic reads cover mice, multitaps, or every peripheral extension.

Related:

Sources:

Comments