N64 Input Interface¶
Reads native N64 controllers via the joybus-pio library. The single-wire joybus protocol is implemented entirely in PIO for precise timing. Supports the analog stick, all buttons, and rumble pak.
Protocol¶
- Bus: Joybus single-wire bidirectional (open-drain with pull-up)
- Method: PIO state machine via
joybus-piolibrary (src/lib/joybus-pio) - Polling: 60Hz (N64 native rate, configurable via
N64_POLLING_RATE) - Location:
src/native/host/n64/
The N64 joybus protocol uses a single data line for bidirectional communication: 1. Host sends a poll command (0x01) via PIO 2. Controller responds with 32 bits of button/stick data 3. Each bit is encoded as a timed pulse (1us low + 3us high = 0, 3us low + 1us high = 1)
Supported Controllers¶
| Device | Type | Notes |
|---|---|---|
| Standard N64 controller | 0x0000 | Stick + 14 buttons |
| Controller with rumble pak | 0x0002 | Auto-detected, rumble initialized after 10 polls |
| Controller with controller pak | 0x0001 | Detected but pak not read |
Device type is determined from the N64 status response byte.
Button Mapping¶
| N64 Button | JP_BUTTON_* | Notes |
|---|---|---|
| A | B1 | Primary face button |
| C-Down | B2 | Also maps to ANALOG_RY=255 |
| B | B3 | Secondary face button |
| C-Left | B4 | Also maps to ANALOG_RX=0 |
| Z | R1 | Shoulder/bumper position |
| L | L2 | Left trigger (digital) |
| R | R2 | Right trigger (digital) |
| C-Up | L3 | Also maps to ANALOG_RY=0 |
| C-Right | R3 | Also maps to ANALOG_RX=255 |
| Start | S2 | |
| D-pad Up | DU | |
| D-pad Down | DD | |
| D-pad Left | DL | |
| D-pad Right | DR |
C-buttons are mapped both as digital buttons (B2, B4, L3, R3) and as right analog stick values for dual-stick mode.
Analog Axes¶
Stick Scaling¶
The N64 analog stick typically reaches only +/-80 (not the full +/-128 range). The driver scales the raw value to use the full 0-255 range:
This maps the N64's effective range to the full 0-255 unsigned range with 128 as center.
Y-Axis Inversion¶
The N64 uses inverted Y convention (positive = up). The driver inverts Y during conversion: stick_y = convert_stick_axis(-report.stick_y).
C-Button Right Stick¶
C-buttons produce digital right stick values via map_c_buttons_to_analog():
| C-Button | ANALOG_RX | ANALOG_RY |
|---|---|---|
| C-Left | 0 | 128 |
| C-Right | 255 | 128 |
| C-Up | 128 | 0 |
| C-Down | 128 | 255 |
N64 L and R triggers are digital only -- ANALOG_L2 and ANALOG_R2 remain at 0.
Connection Detection¶
- Connect:
N64Controller_IsInitialized()returns true after successful status command - Disconnect debounce: 30 consecutive failed polls (~500ms at 60Hz) before reporting disconnect
- On disconnect, cleared input is submitted to prevent stuck buttons
- Brief disconnects during pak commands are ignored by the debounce window
Feedback¶
- Rumble pak: Auto-detected on connect. Initialized after 10 stable polls (~170ms). Binary on/off control via
N64Controller_SetRumble(). - Rumble rate limiting: Minimum 50ms between rumble commands to prevent blocking the main loop
- Deferred rumble:
n64_host_set_rumble()marks rumble as pending; actual joybus write happens inn64_host_flush_rumble()after time-critical tasks
Configuration¶
| Setting | Default | Override |
|---|---|---|
| N64_PIN_DATA | GPIO 4 | #define N64_PIN_DATA <pin> |
| N64_POLLING_RATE | 60 Hz | #define N64_POLLING_RATE <hz> |
| N64_MAX_PORTS | 1 | (future multitap) |
PIO assignment:
- Default: PIO0, auto-assigned SM and offset
- Dreamcast builds (CONFIG_DC): PIO1 SM3 at offset 10 (leaves room for maple_rx at 0-9)
- Device address range: 0xE0+ (port 0 = 0xE0)
- Transport type:
INPUT_TRANSPORT_NATIVE - Input source:
INPUT_SOURCE_NATIVE_N64