Skip to content

Commit a8edd28

Browse files
Remove implementation details and code from protocol docs
Strip all C code, pseudocode, PIO programs, GPIO pin assignments, and project-specific implementation details from protocol documentation. Replace with prose descriptions, tables, and ASCII diagrams. Protocol docs now serve as general protocol references independent of any specific implementation.
1 parent 76a9cba commit a8edd28

File tree

4 files changed

+448
-1793
lines changed

4 files changed

+448
-1793
lines changed

docs/protocols/3DO_PBUS.md

Lines changed: 27 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**Serial shift register daisy-chain system supporting hot-swappable multi-device input**
44

5-
Implemented by Robert Dale Smith (2025)
5+
Documented by Robert Dale Smith (2025)
66
Based on 3DO Opera hardware documentation and PBUS protocol specification
77

88
---
@@ -21,8 +21,7 @@ Based on 3DO Opera hardware documentation and PBUS protocol specification
2121
- [Daisy Chain Mechanism](#daisy-chain-mechanism)
2222
- [Device Identification](#device-identification)
2323
- [Initialization & Hot-Swapping](#initialization--hot-swapping)
24-
- [PIO Implementation](#pio-implementation)
25-
- [Implementation Notes](#implementation-notes)
24+
- [Common Pitfalls](#common-pitfalls)
2625

2726
---
2827

@@ -51,17 +50,6 @@ The PBUS protocol is described in patent [WO1994010636A1](https://patents.google
5150

5251
The 3DO uses a 9-pin (DE-9) controller connector. See the [FZ-1 Technical Guide](https://3dodev.com) for model-specific pinout details.
5352

54-
### RP2040 GPIO Mapping
55-
56-
For the Waveshare RP2040 Zero adaptation (matching [USBTo3DO](https://github.com/FCare/USBTo3DO)):
57-
58-
| GPIO | Signal | Direction | Description |
59-
|------|--------|-----------|-------------|
60-
| 2 | CLK | Input | Clock from 3DO console |
61-
| 3 | DATA_OUT | Output | Serial data to 3DO console |
62-
| 4 | DATA_IN | Input | Serial data from daisy chain |
63-
| 5 | CS_CTRL | Control | Chip select / control signal |
64-
6553
### Electrical Characteristics
6654

6755
- **Logic levels**: TTL compatible (0V / 5V)
@@ -197,16 +185,9 @@ Byte 3: DX_lower (8 bits)
197185
- Negative values use two's complement
198186
- Deltas are relative motion since last frame
199187

200-
**Conversion** (from `3do_device.c`):
201-
```c
202-
// Delta Y (10-bit signed)
203-
int16_t dy = ((byte1 >> 4) & 0x0F) << 6 | (byte2 & 0x3F);
204-
if (dy & 0x200) dy |= 0xFC00;
205-
206-
// Delta X (10-bit signed)
207-
int16_t dx = ((byte2 >> 6) & 0x03) << 8 | byte3;
208-
if (dx & 0x200) dx |= 0xFC00;
209-
```
188+
**Conversion**:
189+
- **Delta Y**: Assembled as a 10-bit value by taking the lower 4 bits of byte 1 as the upper nibble (bits 9-6) and the lower 6 bits of byte 2 as the lower portion (bits 5-0). If bit 9 is set, the value is sign-extended to 16 bits by filling the upper 6 bits with ones (two's complement).
190+
- **Delta X**: Assembled as a 10-bit value by taking the upper 2 bits of byte 2 as the upper portion (bits 9-8) and all 8 bits of byte 3 as the lower portion (bits 7-0). Sign extension follows the same rule: if bit 9 is set, fill the upper 6 bits with ones.
210191

211192
### Lightgun
212193

@@ -227,11 +208,7 @@ Byte 3: [Counter[3:0]][Line[4:0]][Buttons]
227208
- Number of scanlines where beam was detected
228209
- NTSC default: YSCANTIME=12707
229210

230-
**Position Calculation**:
231-
```c
232-
x_position = (counter * XSCANTIME) + TIMEOFFSET;
233-
y_position = line_count * YSCANTIME;
234-
```
211+
**Position Calculation**: The X position is derived by multiplying the 20-bit timing counter by the XSCANTIME constant and adding the TIMEOFFSET correction. The Y position is derived by multiplying the 5-bit line pulse count by the YSCANTIME constant. For NTSC systems, the defaults are XSCANTIME=1030, TIMEOFFSET=-12835, and YSCANTIME=12707.
235212

236213
### Arcade Controls (Silly Control Pad)
237214

@@ -262,8 +239,8 @@ Used for Orbatak arcade cabinet integration.
262239

263240
```
264241
┌─────────┐ ┌────────┐ ┌────────┐ ┌────────┐
265-
│ Console │────────▶│ USB │────────▶│ 3DO │────────▶│ 3DO │
266-
│ │◀────────│ Adapter│◀────────│ Pad #1 │◀────────│ Pad #2 │
242+
│ Console │────────▶│ Device │────────▶│ 3DO │────────▶│ 3DO │
243+
│ │◀────────│ #0 │◀────────│ Pad #1 │◀────────│ Pad #2 │
267244
└─────────┘ └────────┘ └────────┘ └────────┘
268245
│ │ │ │
269246
CLK ────────────────────────────────────────────────────────▶
@@ -274,14 +251,14 @@ Used for Orbatak arcade cabinet integration.
274251
### Data Flow
275252

276253
**Console Perspective**:
277-
1. Console shifts out controller data on DATA_OUT (USB adapter data)
254+
1. Console shifts out controller data on DATA_OUT (first device's data)
278255
2. Simultaneously samples DATA_IN for extension device data
279256
3. Each clock cycle: send 1 bit, receive 1 bit
280257
4. After 8-448 bits, complete device data captured
281258

282259
**Passthrough Buffering**:
283-
- USB adapter must buffer extension data from one field
284-
- Next field: send USB data + buffered extension data
260+
- First device in chain must buffer extension data from one field
261+
- Next field: send own data + buffered extension data
285262
- This creates 1-frame latency for extension controllers
286263

287264
---
@@ -304,31 +281,17 @@ Used for Orbatak arcade cabinet integration.
304281

305282
### ID Detection Algorithm
306283

307-
From `parse_extension_controllers()` in `3do_device.c`:
308-
309-
```c
310-
uint8_t byte0 = buffer[offset];
311-
uint8_t id_nibble = (byte0 >> 4) & 0x0F;
312-
313-
if ((id_nibble & 0b1100) != 0) {
314-
// Joypad: upper 2 bits of nibble are non-zero
315-
device_type = DEVICE_JOYPAD;
316-
bytes_to_read = 2;
317-
} else if (byte0 == 0x01 &&
318-
buffer[offset+1] == 0x7B && buffer[offset+2] == 0x08) {
319-
device_type = DEVICE_FLIGHTSTICK;
320-
bytes_to_read = 9;
321-
} else if (byte0 == 0x49) {
322-
device_type = DEVICE_MOUSE;
323-
bytes_to_read = 4;
324-
} else if (byte0 == 0x4D) {
325-
device_type = DEVICE_LIGHTGUN;
326-
bytes_to_read = 4;
327-
} else if (byte0 == 0xC0) {
328-
device_type = DEVICE_ARCADE;
329-
bytes_to_read = 2;
330-
}
331-
```
284+
To parse device types from a PBUS data stream, read the first byte at the current offset and apply the following rules in order:
285+
286+
| Priority | Condition | Device Type | Packet Size |
287+
|----------|-----------|-------------|-------------|
288+
| 1 | Upper 2 bits of byte 0's high nibble are non-zero (i.e., bits 7-6 of byte 0 are not both 0) | Joypad | 2 bytes |
289+
| 2 | Byte 0 is 0x01, followed by 0x7B and 0x08 (3-byte signature) | Flightstick | 9 bytes |
290+
| 3 | Byte 0 is 0x49 | Mouse | 4 bytes |
291+
| 4 | Byte 0 is 0x4D | Lightgun | 4 bytes |
292+
| 5 | Byte 0 is 0xC0 | Arcade | 2 bytes |
293+
294+
Check for joypads first since they are the most common device. The joypad check exploits the physical impossibility of pressing up+down simultaneously on a d-pad: any byte where the upper two bits of the high nibble are non-zero must be a joypad. If the byte does not match a joypad, check for the flightstick's 3-byte signature before falling through to the single-byte ID matches.
332295

333296
### End-of-Chain Detection
334297

@@ -361,50 +324,7 @@ Portfolio OS loads drivers by device ID:
361324

362325
---
363326

364-
## PIO Implementation
365-
366-
### RP2040 PIO State Machine
367-
368-
From `output.pio`:
369-
```asm
370-
.program output
371-
372-
.define CLK_PIN 2
373-
374-
public entry_point:
375-
wait 0 gpio CLK_PIN ; Wait for clock low
376-
.wrap_target
377-
start:
378-
out pins, 1 ; Shift out 1 bit to DATA_OUT
379-
wait 1 gpio CLK_PIN ; Wait for clock high
380-
wait 0 gpio CLK_PIN ; Wait for clock low
381-
in pins, 1 ; Shift in 1 bit from DATA_IN
382-
.wrap
383-
```
384-
385-
### DMA Architecture
386-
387-
Dual-channel DMA for simultaneous TX/RX:
388-
389-
```c
390-
// Channel 0: OUTPUT - Send data to console
391-
channel_config_set_transfer_data_size(&cfg_out, DMA_SIZE_8);
392-
channel_config_set_read_increment(&cfg_out, true);
393-
channel_config_set_dreq(&cfg_out, pio_get_dreq(pio, sm, true));
394-
395-
// Channel 1: INPUT - Receive extension data
396-
channel_config_set_transfer_data_size(&cfg_in, DMA_SIZE_8);
397-
channel_config_set_write_increment(&cfg_in, true);
398-
channel_config_set_dreq(&cfg_in, pio_get_dreq(pio, sm, false));
399-
```
400-
401-
**Buffer**: `controller_buffer[201]` — padded beyond the 56-byte max (448 bits) for DMA alignment.
402-
403-
---
404-
405-
## Implementation Notes
406-
407-
### Common Pitfalls
327+
## Common Pitfalls
408328

409329
1. **Active-HIGH encoding**: PBUS uses 1=pressed (opposite of PCEngine). Invert when porting from other protocols.
410330

@@ -416,24 +336,13 @@ channel_config_set_dreq(&cfg_in, pio_get_dreq(pio, sm, false));
416336

417337
5. **Extension latency**: Extension controllers have 1-frame delay due to passthrough buffering.
418338

419-
6. **Buffer size**: Allocate full 201-byte buffer even if unused. Prevents overflow with many devices connected.
339+
6. **Buffer sizing**: The maximum PBUS field is 448 bits (56 bytes). Allocate buffers with padding beyond this maximum to prevent overflow when many devices are connected.
420340

421341
---
422342

423-
## Acknowledgments
424-
425-
- **[3dodev.com](https://3dodev.com)**: PBUS protocol specification and Opera hardware documentation
426-
- **[FCare's USBTo3DO](https://github.com/FCare/USBTo3DO)**: Original USB-to-3DO adapter that pioneered USB controller conversion for the 3DO
427-
- **[SNES23DO](https://github.com/RobertDaleSmith/snes23do)**: SNES-to-3DO adapter with extension controller parsing
428-
429343
## References
430344

431345
- **3DO PBUS Specification**: [3dodev.com/documentation/hardware/opera/pbus](https://3dodev.com/documentation/hardware/opera/pbus)
432-
- **Patent WO1994010636A1**: Player Bus Apparatus and Method (1994)
433-
- **USBTo3DO**: [github.com/FCare/USBTo3DO](https://github.com/FCare/USBTo3DO)
434-
435-
### Implementation
436-
437-
- **Joypad 3DO Module**: `src/native/device/3do/` — Full PBUS implementation with extension detection
438-
- **PIO State Machines**: `src/native/device/3do/output.pio` — Clock-synchronized bidirectional I/O
439-
- **Extension Parsing**: `src/native/device/3do/3do_device.c` — `parse_extension_controllers()`
346+
- **Patent WO1994010636A1**: [Player Bus Apparatus and Method (1994)](https://patents.google.com/patent/WO1994010636A1)
347+
- **FCare's USBTo3DO**: [github.com/FCare/USBTo3DO](https://github.com/FCare/USBTo3DO) — Open-source USB-to-3DO adapter with PBUS implementation
348+
- **3dodev.com**: [3dodev.com](https://3dodev.com) — Opera hardware documentation and protocol specifications

0 commit comments

Comments
 (0)