Skip to content
RetrogamingDeep Dive Published Updated 9 min readViews unavailable

Mega Drive Six-Button Pad: TH Handshake, Active-Low Phases, and Detection

Model the Mega Drive six-button pad through TH edges, active-low samples, extended-button signatures, phase recovery, and reproducible controller tests.

A Mega Drive controller is not a packet device with a clock and a byte buffer. The console exposes a parallel I/O port, and software changes the direction or level of the TH pin before sampling several active-low data lines. A three-button pad reuses those lines in two ordinary phases. A six-button pad recognizes a sequence of TH transitions and temporarily multiplexes X, Y, Z, and Mode onto the same wires.

That design makes the six-button pad a small stateful peripheral. A game can detect it by asking for a response that a three-button pad cannot produce, but a single read is not enough. An emulator that returns all six extra buttons at once, or that treats a data-port write as an instantaneous phase change, can break games that poll at raster-sensitive moments or expect the controller to recover after a partial handshake.

The port is a set of sampled wires

The console-side port has data bits for Up, Down, Left, Right, B, and C, plus the TH signal exposed through the I/O control circuitry. The external controller pulls data lines low for pressed buttons, so software typically inverts or masks the result to produce a logical “pressed” state. The exact register address and direction bits depend on which I/O port is being used, but the peripheral contract is the same: software drives TH, waits as required by the platform and software routine, and reads the pins.

The first six-button phase is deliberately backward compatible. With TH high, the ordinary pad reports C and B on the upper button lines and Right, Left, Down, and Up on the lower four. With TH low, the two upper button lines carry Start and A; the lower two continue to expose Down and Up, while Left and Right are forced inactive. Old three-button games can use those two familiar views without knowing that a six-button controller is attached.

This is not a purely combinational mux. The pad observes edges on TH. In the extended protocol, it must see the expected transitions in sequence before it exposes the extra controls. The line level says which view should be sampled, but the transition history says whether the device is still in ordinary mode or has entered the extended window. Keep the external line level and the controller’s internal phase as separate state in an emulator.

The extended handshake is a sequence, not a magic read

After two ordinary high/low pairs, the protocol presents one more ordinary high sample. The next low phase is the identification signature: the lower four inputs are forced low. A following high phase exposes Mode, X, Y, and Z, and a final low phase forces those lower four lines high again. That forced-low sample is a useful signature: a standard pad continues to return ordinary directional values and has no reason to produce the six-button identification pattern.

The sequence should be modeled from TH transitions rather than from the number of reads. A game may write TH several times without reading after each write, or sample a phase more than once. A read should return the current pin levels for the current phase; it should not itself advance the pad. Conversely, a direction-register write that leaves TH at the same electrical level is not necessarily a new edge. This distinction prevents read frequency from changing the controller protocol.

The Sega technical reference describes the extended mode as an edge-driven handshake. Established emulator implementations preserve a transition counter and a phase-specific output rather than deriving six-button data from the most recent data-port byte. Implementations also account for delayed internal switching and recovery behavior. Those details explain why edge timestamps and reset conditions belong in the controller state even when a simple game appears to work with a stateless lookup table.

The extended phase is not an unlimited extra bank. The controller protocol cycles back to the ordinary views after its extended response window. Do not keep returning X/Y/Z/Mode after every later TH toggle. Treat the signature and extended sample as bounded states; subsequent transitions return to the standard pattern until the next valid handshake. This is particularly important for games that poll continuously rather than executing one tightly controlled read routine.

Electrical levels, direction registers, and timing

On the Mega Drive, software configures I/O lines as inputs or outputs through control registers and drives TH through the data register. Reading the data register yields both output-state and externally resolved input lines. A test harness must therefore model direction as well as value: a bit configured as an input is not equivalent to a CPU output of one, and a peripheral cannot force a line that the console is actively driving to the opposite level.

Many controller routines toggle TH and read immediately. Real hardware has propagation and internal switching time; some software inserts a delay, while compatibility implementations may represent a short transition latency. Do not invent a universal nanosecond delay when the software and chip revisions do not support one. Instead, document the timing model your emulator follows, tie it to the source implementation or hardware test that establishes it, and make the delay visible in cycle-based tests.

A robust design records the current TH output, whether the console drives it, the last stable edge, the extended-protocol phase, and any pending transition latency. The pad’s button snapshot should be sampled at a defined point. If host input changes halfway through a multi-phase read, the emulator must choose and document whether the physical model exposes that change immediately or latches it for a poll. Mixing an edge-driven phase machine with arbitrary per-bit host reads produces impossible combinations such as X coming from one host sample while Y comes from another.

Do not confuse TH with the serial-data line used by later Sega devices. This is a parallel protocol whose “serialization” is the timed presentation of different button groups across repeated reads. It has no packet length, checksum, or byte-order field. The Mega Drive 2-button/3-button path remains meaningful, and adapters or multitaps may add their own state machines above or around the pad. Keep each topology layer distinct.

A testable phase model

The following reference function represents the canonical six-button sample tables after the console has correctly counted TH transitions. Inputs are logical button booleans; outputs are the lower six port bits, with zero meaning electrically asserted. It is deliberately not a complete register model: a production emulator still needs port direction, edge timing, adapter routing, and hardware-specific transition latency.

BUTTONS = ("up", "down", "left", "right", "b", "c",
           "a", "start", "z", "y", "x", "mode")


def sample_six_button_pad(phase, pressed):
    """Return active-low D5..D0 for one already-selected TH phase."""
    unknown = set(pressed) - set(BUTTONS)
    if unknown:
        raise ValueError(f"unknown buttons: {sorted(unknown)}")
    levels = 0x3F  # unpressed inputs are high

    def assert_bit(bit, button):
        nonlocal levels
        if button in pressed:
            levels &= ~(1 << bit)

    if phase in {0, 2, 4}:  # ordinary TH high: ?1 C B R L D U
        for bit, name in enumerate(("up", "down", "left", "right", "b", "c")):
            assert_bit(bit, name)
    elif phase in {1, 3}:  # ordinary TH low: ?0 S A 0 0 D U
        assert_bit(0, "up")
        assert_bit(1, "down")
        assert_bit(4, "a")
        assert_bit(5, "start")
    elif phase == 5:  # third low: identification signature, D3..D0 forced low
        assert_bit(4, "a")
        assert_bit(5, "start")
        levels &= ~0x0F
    elif phase == 6:  # fourth high: ?1 C B M X Y Z
        for bit, name in enumerate(("z", "y", "x", "mode", "b", "c")):
            assert_bit(bit, name)
    elif phase == 7:  # fourth low: D3..D0 forced high
        assert_bit(4, "a")
        assert_bit(5, "start")
    else:
        raise ValueError("phase must be 0..7 in the documented poll sequence")
    return levels


assert sample_six_button_pad(5, set()) & 0x0F == 0
assert sample_six_button_pad(6, {"x"}) & (1 << 2) == 0

The model uses an abstract eight-sample index for clarity. Do not copy that index directly into a hardware core without mapping the reference transitions and reset path. A correct transition machine should handle a repeated level, the first edge after reset, the return to ordinary mode, and a poll that begins halfway through the sequence. Invalid or partial sequences should fail safely into a documented baseline state rather than keeping stale extended-button values forever.

Detection without false positives

Controller detection should compare the whole expected response, not merely one bit that could also result from ordinary button presses. A useful fixture starts with no buttons pressed, executes the exact TH sequence, and confirms both the ordinary phases and the extended signature. Then repeat with each button individually asserted. The test should prove that X/Y/Z/Mode do not leak into the normal view and that a three-button device never claims the extended response.

Test the edge cases that arise from real software: write TH high when it is already high, read multiple times without toggling, change the direction register from input to output, interrupt the sequence with a reset or peripheral reconfiguration, and stop polling in the extended phase. Also test delayed reads around a TH edge. If the implementation has a cycle-level latency, place reads just before and after the transition becomes visible and check both the returned pins and phase counter.

At the system level, run at least one title that uses the extra buttons and one that detects pads during startup. Preserve the distinction between a physical six-button controller and a host mapping: a modern gamepad may provide more inputs, but the emulated console still needs to expose them through the old TH protocol. If the frontend changes controller type dynamically, reset the pad state at an explicit boundary so a half-finished handshake cannot survive a topology change.

Acceptance criteria and diagnostic trace

A reliable implementation can explain each sample using the configured port, TH level, edge count, transition timestamp, host button snapshot, and returned active-low bits. It can restart after incomplete polling, keeps ordinary and extended button mappings disjoint, and reproduces the signature phase. Keep a compact trace in debug builds; it turns “Street Fighter does not see X” into a finite sequence of register writes and sampled values.

Related:

Sources:

Comments