Sega Saturn¶
The Saturn UserIO path supports the standard Saturn Control Pad in 1P or 2P configurations. The 3D Control Pad is NOT supported via UserIO in either switch position — it uses a different protocol (Saturn 3-wire handshake on TH/TR/TL); use the Saturn core's SNAC mode for the 3D Pad. Saturn support is key-gated: it requires a valid db9pro.key at /media/fat/db9pro.key. See ../pro-key.md.
OSD setting¶
UserIO Joystick = Saturn, UserIO Players = 1 Player or 2 Players.
Hardware¶
Saturn uses four signal lines (D0..D3) plus two select lines (S0, S1) plus, for the 2P mux adapter, a split-select line:
USER_IO line | Role | Direction |
|---|---|---|
USER_IO[1] | D0 | input |
USER_IO[0] | D1 | input |
USER_IO[5] | D2 | input |
USER_IO[3] | D3 | input |
USER_IO[4] | S0 (push-pull) | output |
USER_IO[6] | S1 (push-pull) | output |
USER_IO[2] | 2P mux split-select (push-pull) | output (Saturn 2P only) |
The FPGA scans the controller through the four phases {S0,S1} = {0,0}, {1,0}, {0,1}, {1,1} and assembles a 16-bit value per port.
For the 2P adapter, the split-select on USER_IO[2] toggles between port A (low) and port B (high) on the 74HC157D mux board.
Internal joydb_1 bit layout¶
joydb_1 is 16 bits when Saturn is selected:
| Bit | Function |
|---|---|
| 0 | Right |
| 1 | Left |
| 2 | Down |
| 3 | Up |
| 4 | A |
| 5 | B |
| 6 | C |
| 7 | X |
| 8 | Y |
| 9 | Z |
| 10 | Start |
| 11 | R trigger (occupies the Mode/Select slot) |
| 12 | L trigger |
| 13 | unused |
| 15:14 | unused |
Bits [10:0] line up with DB9MD and DB15. The L and R shoulder triggers (bits 12 and 11) are Saturn-only — R reuses the bit-11 Mode/Select slot.
Pad detection (the reason 2P adapters don't ghost-input)¶
The helper module computes a 4-bit MD_ID from the pad's response at the {S0=1, S1=1} phase and only commits button data when the ID matches a known pad family:
MD_ID | Pad type | Behaviour |
|---|---|---|
0xB | Standard 6-button digital Control Pad | full button data committed |
0x5 | 3D Control Pad in the Analog switch position | flagged "present" only (helper accidentally OR-reduces the 3-wire response to 0x5); no digital data extractable here — use the Saturn core's SNAC mode |
0xA | Saturn Stunner / Virtua Gun (HSS-0152) | not matched by the UserIO helper — the port reads as nothing connected here. Stunner detection happens only in the Saturn core's SMPC/SNAC path (PS_NOTHING_STUNNER); use the Saturn core's SNAC mode, and set Saturn SNAC Adapter = 1P if you are on the passive cable |
| anything else | nothing connected, floating mux input, or 3D Pad in Digital switch position (uses 3-wire handshake, not the 4-phase protocol) | no data committed, port reads as zero |
A 4-bit shift register debounces the detection: a pad has to be missed four scans in a row (≈572 µs) before it is treated as disconnected, but a single hit (≈143 µs) brings it back. Both ports start out marked "disconnected" — there are no ghost inputs at boot, and unconnected sides of a 2P mux adapter never leak phantom presses.
In 1 Player mode with the 2P adapter, the routing automatically uses whichever side has a valid pad (P1 preferred). So plugging a single controller into the P2 socket of a 2P adapter still works.
Saturn 3D Control Pad¶
The 3D Pad has a physical switch on top: Digital or Analog. It uses the Saturn 3-wire handshake protocol (TH/TR/TL) in both switch positions (header byte 0x02 in Digital, 0x16 in Analog), unlike the standard 6-button digital Control Pad which uses a 4-phase TH/TR scan with no TL handshake. This means:
- UserIO=Saturn does not support the 3D Pad in either switch position. The
joydb9saturnhelper only implements the 4-phase scan; the 3-wire handshake state machine is not present. The pad will appear silent on D-lines. - The 3D Pad works only via the Saturn core's SNAC mode, where the Saturn core's own SMPC implementation runs the full 3-wire handshake.
Using the 3D Pad on a Saturn 1P passive SNAC adapter¶
- Open the Saturn core's OSD.
- Set
Saturn SNAC = ON. - Set
Saturn SNAC Adapter = 1P(the default is2P— for the 74HC157D-mux modded adapter). - Plug the 3D Pad into the SNAC port. Either switch position works (Digital for Daytona-style games, Analog for NiGHTS, Panzer Dragoon, Sega Rally, etc.).
Saturn SNAC Adapter = 1P releases every SNAC pin to open-drain, including USER_IO[2], so the FPGA does not short the pad's open-collector TL line. SMPC then drives TL via PDR_O[4] through the same path. The default Saturn SNAC Adapter = 2P drives USER_IO[2] push-pull as the mux SEL (correct for 6-button pads on the 2P-modded adapter), which is incompatible with the 3D Pad on a 1P passive cable.
Saturn SNAC Adapter is a Saturn-SNAC sub-option and inherits the same key gate as the rest of Saturn — no separate feature_mask bit. SMPC handles all pad-type detection (Saturn Pad, 3D Pad, Stunner, Mouse, Wheel, Mission, Dual Mission) automatically on either adapter; you only flip the selector to match your cable.
Note: with Saturn SNAC Adapter = 1P, the SNAC Players selector is internally force-overridden to single-player (the 1P passive cable has only one physical jack). Setting it to 2 Players has no effect — and that override is what stops the 2-Players scan from injecting phantom P2 inputs from the same single port.
2P SNAC adapter + 3D Pad¶
The 2P 74HC157D mux only handles the 4 D-lines and has no spare channel for TL routing. Stock 2P adapter cannot read the 3D Pad. A future hardware mod (single jumper from P1 jack pin 4 to DB9 pin 2 → USER_IO[7], analogous to the existing Stunner 2P mod) plus a firmware option could enable P1-only 3D Pad support; not implemented yet.
Coin / OSD combos¶
| Combo | Action |
|---|---|
Start + C | Open / close OSD |
A | OSD: confirm |
B | OSD: back |
On arcade cores, Coin is wired to the joydb_1[11] slot (the bit-11 button that DB9MD exposes as Mode); on a Saturn pad that slot is the R trigger, so R inserts the coin. The shared DB9MD Start + B → Mode synthesis does not run on the Saturn path — it is gated to DB9MD 3-button pads.
Exception — PSX. Individual cores may still define their own Saturn-pad combos. The PSX core consumes the Saturn pad through a dedicated arm (PSX.sv): the Saturn L/R shoulder triggers map to PSX shoulder buttons, and Start + B is synthesized as PSX Select (with Start and B masked while the combo is held). So on PSX the bit-11 (R) slot is a real shoulder button, not Coin, and Start + B is the synthesized extra button.
Start + C opens / closes the OSD on every core.