I took as base the information provided by Micah's notes on the DualShock 2 controller. My first step before writing the PS driver in BlueRetro was to write a packet sniffer to be able to make easily very long trace compared to what you can buffer in a logic analyzer. This usually helps a lot understanding how each packet relate to each other. See multiple traces here.
Basis
An addition compared to more common SPI interface is the use of the DSR line as an ACK. Every bytes sent by the console needs to be acknowledged by the device for the transmission of the next byte to occur. This line is shared by the four devices just like the SCK, TXD & RXD as well. If a command is not supported by a device, the command byte will not be ACKed.
The two controller outputs (RXD & DSR) are open drain and require a pull-up resistor.
Each transmission is full duplex. Typically clock rate is 250 KHz for controller and memory card but 500 KHz and 1 MHz is used by the multitap. Bits are transmit LSB first. CPOL = 1, CPHA = 1.
┌Address
│ ┌Command
│ │ ┌Ctrl input (ex: set rumble)
├┐├┐ ┌─┴────────┐
TX: 014200000000000000
RX: FF735AFFFF80808080
││ └─┬────────┘
││ └Ctrl output (ex: get buttons)
│└Payload length in DWORD.
└Peripheral ID
Packets always start with a 3 bytes header. First TX bytes is the address to select either the controller (0x01) or memory card (0x81). 2nd TX byte is the command sent by the console. The 2nd RX byte upper nibble is the ID of the peripheral. The lower nibble is the output size in DWORD (uint16_t). The 3rd RX byte is always 0x5A and signals the end of the header. The header is followed by the payload which always match the size reported in the 2nd RX byte even when not all the data is used. Unused TX bytes are set to 0x00 by PSX and 0x5A by PS2.
PS2
Input devices
Input devices will only answer/ack to frame starting with 0x01.
0x42 Polling cmd
This commands is used by all devices for getting the buttons & joystick status. It is also used to set rumble for Analog & DualShock controllers.
- Digital controller (SCPH-1010)
TX: 0142000000 RX: FF415AFFFF ├┘ └┬─┘ ID └Buttons(L D R U St R3 L3 Se □ X O △ R1 L1 R2 L2)
On a true original digital controller R3 & L3 are always '1'. Buttons are active low.
- Analog Joystick (SCPH-1110) (In Analog mode) & Dual Analog (SCPH-1180) (In Green LED mode)
TX: 014200000000000000 RX: FF535AFFFF80808080 ├┘ └┬─┘└──┬───┘ ID Buttons └Axes(RX RY LX LY)
Exactly the same as Dual Analog & DualShock except the peripheral ID (0x53). They changed the ID only to signal rumble support?? Axis ideal neutral position is 0x80, 0x00 is left & up and 0xFF is right and down.
- Dual Analog & DualShock 1/2 (SCPH-1180, SCPH-1200, SCPH-10010) (In Analog mode)
┌Rumble(Could be map to any byte via CMD 0x4D, │ but typically: ┌───┴──┐ 1st byte: Right small motor (On-0xFF/Off-0x00) TX: 014200000000000000 2nd byte: Left big motor (Variable/Off-0x00)) RX: FF735AFFFF80808080 ├┘ └┬─┘└──┬───┘ ID Buttons └Axes(RX RY LX LY)
Axis ideal neutral position is 0x80, 0x00 is left & up and 0xFF is right and down.
- DualShock 2 (SCPH-10010) (In Analog mode + pressure activated by game)
TX: 014200000000000000000000000000000000000000 RX: FF795AFFFF957D7388000000000000000000000000 ├┘ └┬─┘└──┬───┘└┬─────────────────────┘ ID Buttons └Axes └Buttons Pressure(L D R U □ X O △ R1 L1 R2 L2)
Pressure is maximal at 0xFF. 0x00 unpressed.
Mouse (SCPH-1090)
TX: 01420000000000 RX: 00125AFFFC0000 ├┘ │└──┤ ID │ └Axes(LX LY) └Buttons(Left, Right, 0 ,0)
Buttons are active low. Axes are signed Two's complement 8 bits.
Lightspan Keyboard (SCPH-2000 ??? not confirm yet)
(Make scan code) TX: 014200000000000000000000000000 RX: FF965A016600000000000000000000 ├┘ ├┘└──────────────────┬─┘ ID └Scan code length └KB Scan code & mouse data??? (Break scan code) TX: 014200000000000000000000000000 RX: FF965A02F066000000000000000000 ├┘ ├┘└──────────────────┬─┘ ID └Scan code length └KB Scan code & mouse data???
Scan code are AT Keyboard Scan Codes (Set 2).
0x43 Config mode cmd
Supported by DualShock 1/2 only
Enter or exit configuration mode. This is also used to detect DualShock 1/2 vs legacy controllers (Digital, Flightstick, Dual Analog) as the latter will not ACK the command byte.
TX: 0142000000 RX: FF735AFFFF ┌Enter config mode. ├┐ TX: 014300010000000000 RX: FF735AFFFF957D7388 ├┘ └┬─────────┘ ID └Ctrl provide poll status via 0x43 if config mode not active. TX: 0145005A5A5A5A5A5A RX: FFF35A030201020100 ├┘ └ID is 0xF3 until config mode is exit. ┌Clear config mode. ├┐ TX: 014300005A5A5A5A5A RX: FFF35A000000000000 ├┘ └┬─────────┘ ID └Ctrl do not provide poll data while in config mode via 0x43. TX: 0142000000 RX: FF735AFFFF ├┘ └ID revert on config mode exit.
0x44 Enable analog cmd
Supported by DualShock 1/2 only
Require to be in config mode
This allows software to enable/disable the analog mode without requiring users to use analog button.
┌Enable analog mode. (0x00 Disable) ├┐ TX: 014400010300000000 RX: FFF35A000000000000
0x45 Identity / Status cmd
Supported by DualShock 1/2 only
Require to be in config mode
This command is used to tell between a DS1 and DS2 controller. It also shows the state of the analog mode.
(DualShock 1) TX: 014500000000000000 RX: FFF35A010200020100 ├┘ ├┘ │ └Analog mode state. └0x01: ID DualShock 1. (DualShock 2) TX: 0145005A5A5A5A5A5A RX: FFF35A030201020100 ├┘ ├┘ │ └Analog mode state. └0x03: ID DualShock 2.
0x46, 0x47, 0x4C Get constant cmds
Supported by DualShock 1/2 only
Require to be in config mode
These read some constant on the controller. Response is same for DS1 & DS2.
┌Offset 0x00 ├┐ TX: 014600005A5A5A5A5A RX: FFF35A00000102000A ┌Offset 0x01 ├┐ TX: 014600015A5A5A5A5A RX: FFF35A000001010114 ┌Offset 0x00 (No offset 0x01 for 0x47) ├┐ TX: 014700005A5A5A5A5A RX: FFF35A000002000100 ┌Offset 0x00 ├┐ TX: 014C00005A5A5A5A5A RX: FFF35A000000040000 ┌Offset 0x01 ├┐ TX: 014C00015A5A5A5A5A RX: FFF35A000000070000
0x4D Enable Rumble cmd
Supported by DualShock 1/2 only
Require to be in config mode
This configure which byte offset in the 0x42 poll cmd are used for each rumble motor.
0x00 configure the right small motor, 0x01 configure the left big motor, 0xFF disable this offset.
Typically the 1st byte is the right small motor and the 2nd byte: is the left big motor.
┌New rumble mapping ┌─────┴────┐ TX: 014D000001FFFFFFFF RX: FFF35AFFFFFFFFFFFF └─────┬────┘ └Previous rumble mapping
0x4F Polling config cmd
Supported by DualShock 2 only
Require to be in config mode
This enables digital buttons, axes and pressure buttons base on a mask.
┌Polling enable mask ┌─┴──┐ TX: 014F00FFFF03000000 RX: FFF35A00000000005A
0x40 Pressure config cmd
Supported by DualShock 2 only
Require to be in config mode
This configure in someway the pressure buttons one at a time.
┌Button ID ├┐ TX: 014000000200000000 RX: FFF35A00000200005A ┌Button ID ├┐ TX: 014000010200000000 RX: FFF35A00000200005A ┌Button ID ├┐ TX: 014000020200000000 RX: FFF35A00000200005A
0x41 Polling config status cmd
Supported by DualShock 2 only
Require to be in config mode
This return a mask base on the polling config.
TX: 0141005A5A5A5A5A5A RX: FFF35AFFFF0300005A └─┬──┘ └Polling enable mask
Multitap
PSX
The PSX multitap aggregate data from 4 input devices and make it available to the system via a single packet. It alsos allow using 4 memory card but I haven't looked at how that part work yet. In any case input devices and memory card part are handled separately. Base on my limited testing with a few games, they look to be happy to see the multitap presence for controllers while only a regular memory card is on the bus.
The multitap is enabled by setting the 3rd TX to 0x01. If this byte is 0x00 then the controller on port A is passthrough to the console as if no multitap was present enabling software without support to be used without removing the multitap. When multitap mode is enabled, a one frame delay is introduced between the console request and the controller response.
Memory Card
TBD
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.