skywalker-1/vendor-commands-unknown.md
Ryan Malloy 76f0439576 Add vendor command decode and tuning protocol deep analysis
Complete reverse engineering of all unknown vendor commands (0x8F,
0x91-0x98) across v2.06, v2.13, and Rev.2 firmware versions. Full
TUNE_8PSK (cmd 0x86) protocol analysis including EP0BUF format,
modulation dispatch jump table, BCM4500 I2C indirect register
sequences, and FEC lookup tables.
2026-02-11 12:22:01 -07:00

572 lines
20 KiB
Markdown

# Genpix SkyWalker-1 Vendor Commands: Full Decode of "Unknown" Commands
## Sources
- Firmware disassembly via Ghidra MCP: v2.06 (port 8193), v2.13 FW1 (port 8194), Rev.2 v2.10.4 (port 8197)
- Linux kernel `gp8psk-fe.h` (kernel 4.9.227 and 6.18.6) -- provides definitive names for 0x8F, 0x92, 0x93, 0x94, 0x95
- Linux kernel `gp8psk.c` and `gp8psk-fe.c` -- shows driver usage
- Windows driver `SkyWalker1Control.h` -- confirms 0x8F, 0x93, 0x94
## Jump Table Corrections
The original addresses for Rev.2 were shifted by one command index. The corrected jump table targets (decoded from AJMP opcodes at CODE:0x0076) are:
| Cmd | v2.06 (8193) | v2.13 (8194) | Rev.2 (8197) |
|------|-------------|-------------|-------------|
| 0x8F | 0x01FC | 0x021E | 0x0208 |
| 0x90 | 0x020B | 0x022D | 0x0217 |
| 0x91 | 0x022C | 0x024E | 0x0238 |
| 0x92 | 0x024A | 0x026C | 0x0256 |
| 0x93 | 0x026F | 0x0293 | 0x027D |
| 0x94 | 0x01B9 | 0x01DB | 0x01C5 |
| 0x95 | 0x02DF | 0x0303 | 0x02ED |
| 0x96 | 0x02B4 | 0x02D8 | 0x02C2 |
| 0x97 | 0x02C1 | 0x02E5 | 0x02CF |
| 0x98 | 0x02CB | 0x02EF | 0x02D9 |
Note: v2.06 and v2.13 have 30 jump table entries (0x80-0x9D, range check `< 0x1E`). Rev.2 has only 27 entries (0x80-0x9A, range check `< 0x1B`). Commands 0x9B-0x9D do not exist on Rev.2 hardware.
---
## Command 0x8F: SET_DN_SWITCH
**Named by**: Linux kernel `gp8psk-fe.h`, Windows driver `SkyWalker1Control.h`
| Field | Value |
|-------|-------|
| Direction | OUT (host-to-device) |
| wValue | Switch command byte (7 bits, LSB-first) |
| wIndex | 0x0000 |
| wLength | 0 (no data phase) |
| Purpose | Send legacy Dish Network switch command via GPIO bit-bang |
### Firmware Behavior
All three versions implement the same algorithm:
1. Read `wValueL` from SETUPDAT[2] (0xE6BA)
2. Store value, then call a GPIO bit-bang subroutine:
- Assert P0.4 high (start pulse)
- Delay ~32 cycles
- Deassert P0.4
- Delay ~8 cycles
- Loop 7 times, shifting out each bit LSB-first via P0.4
- Between bits: delay ~8 cycles
3. ACK with EP0BCL = 0 (no data returned)
### Subroutine Addresses
| Version | Handler | Bit-bang Routine | Delay Routine |
|---------|---------|-----------------|---------------|
| v2.06 | 0x01FC | 0x1F04 | 0x1DFB |
| v2.13 | 0x021E | 0x1ECD | 0x14B9 |
| Rev.2 | 0x0208 | 0x1CE6 | 0x1BDA |
### Linux Driver Usage
```c
// gp8psk-fe.c: gp8psk_fe_send_legacy_dish_cmd()
u8 cmd = sw_cmd & 0x7f;
st->ops->out(st->priv, SET_DN_SWITCH, cmd, 0, NULL, 0);
// Then sets LNB voltage based on bit 7 of sw_cmd
st->ops->out(st->priv, SET_LNB_VOLTAGE, !!(sw_cmd & 0x80), 0, NULL, 0);
```
This is the `dishnetwork_send_legacy_command` callback in the DVB frontend ops. The 7-bit command value is a legacy Dish Network satellite switch protocol word, bit-banged on GPIO P0.4 with specific timing. The 8th bit (0x80) of the original command selects LNB voltage (13V or 18V) and is sent separately.
### Cross-Version Differences
None. All three versions use identical logic (same GPIO pin P0.4, same timing, same 7-bit protocol). Only the subroutine addresses differ due to code relocation.
---
## Command 0x91: I2C_ADDR_ADJUST (internal debug)
**Not named in any driver header.** This command is not used by either the Linux or Windows driver.
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0 = decrement, non-zero = increment |
| wIndex | 0x0000 |
| wLength | 1 byte |
| Returns | Current value of internal counter byte |
| Purpose | Increment/decrement an internal IRAM counter; return its value |
### Firmware Behavior
```
if wValueL != 0:
IRAM[counter]++
else:
IRAM[counter]--
EP0BUF[0] = IRAM[counter]
EP0BCL = 1
```
### Counter IRAM Addresses
| Version | IRAM Address |
|---------|-------------|
| v2.06 | 0x66 |
| v2.13 | 0x18 |
| Rev.2 | 0x18 |
### Purpose
This appears to be a diagnostic/debug command for adjusting an internal pointer or counter, possibly related to I2C bus addressing or tuner register indexing. The counter is used by other internal routines but is not exposed through any standard driver interface. The name "I2C_ADDR_ADJUST" is inferred from its proximity to I2C commands and the inc/dec readback pattern.
### Cross-Version Differences
- v2.06 uses IRAM address 0x66; v2.13 and Rev.2 use 0x18
- Logic is otherwise identical
---
## Command 0x92: GET_FW_VERS
**Named by**: Linux kernel `gp8psk-fe.h` (`#define GET_FW_VERS 0x92`)
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0x0000 |
| wIndex | 0x0000 |
| wLength | 6 bytes |
| Returns | Firmware version and build date (6 bytes, fixed per firmware build) |
| Purpose | Read firmware version identifier |
### Data Format
```
Byte 0: Version minor-minor (fw_vers[0])
Byte 1: Version minor (fw_vers[1])
Byte 2: Version major (fw_vers[2])
Byte 3: Build day
Byte 4: Build month
Byte 5: Build year (offset from 2000)
```
Full version number: `fw_vers[2] << 16 | fw_vers[1] << 8 | fw_vers[0]`
Build date: `(2000 + fw_vers[5]) / fw_vers[4] / fw_vers[3]`
### Values Per Firmware
| Version | Raw Bytes | Version | Build Date | FW_VERS macro |
|---------|-----------|---------|-----------|---------------|
| v2.06 | `04 06 02 0D 07 07` | 2.06.04 | 2007-07-13 | 0x020604 (= GP8PSK_FW_REV1) |
| v2.13 | `01 0D 02 0C 03 0A` | 2.13.01 | 2010-03-12 | 0x020D01 |
| Rev.2 | `04 0A 02 0C 03 0A` | 2.10.04 | 2010-03-12 | 0x020A04 (>= GP8PSK_FW_REV2) |
### Linux Driver Usage
```c
// gp8psk.c: gp8psk_get_fw_version()
gp8psk_usb_in_op(d, GET_FW_VERS, 0, 0, fw_vers, 6);
// gp8psk.c: gp8psk_info() -- prints on boot:
// "FW Version = 2.06.4 (0x20604) Build 2007/07/13"
// gp8psk-fe.h: Used for hardware revision detection:
#define GP8PSK_FW_REV1 0x020604
#define GP8PSK_FW_REV2 0x020704
// if GP8PSK_FW_VERS(fw) >= GP8PSK_FW_REV2 -> Rev.2 hardware
```
### Firmware Implementation
The handler writes 6 hardcoded immediate values to EP0BUF[0..5] and sets EP0BCL = 6. There is no I2C or EEPROM access -- the version bytes are compiled directly into the firmware binary.
### Cross-Version Differences
Only the embedded constant values differ (they reflect each firmware's own version). The handler structure is identical across all three versions.
---
## Command 0x93: GET_SERIAL_NUMBER
**Named by**: Linux kernel `gp8psk-fe.h`, Windows driver `SkyWalker1Control.h`
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0x0000 |
| wIndex | 0x0000 |
| wLength | 4 bytes |
| Returns | 4-byte device serial number read from I2C EEPROM |
| Purpose | Read unique device serial number from onboard EEPROM |
### Firmware Behavior
1. Call EEPROM initialization routine (sets up I2C for device address 0x51)
2. Read byte at EEPROM bit-offset 0x08 -> EP0BUF[0]
3. Read byte at EEPROM bit-offset 0x10 -> EP0BUF[1]
4. Read byte at EEPROM bit-offset 0x18 -> EP0BUF[2]
5. Read byte at EEPROM bit-offset (from init) -> EP0BUF[3]
6. Set EP0BCL = 4
The I2C EEPROM at address 0x51 (7-bit) is a standard 24Cxx-family serial EEPROM. The serial number bytes are extracted at 8-bit intervals using a shift/rotate extraction routine.
### Subroutine Addresses
| Version | Handler | EEPROM Init | Byte Extract |
|---------|---------|------------|-------------|
| v2.06 | 0x026F | 0x1DA8 | 0x077C |
| v2.13 | 0x0293 | 0x1D4B | 0x078A |
| Rev.2 | 0x027D | 0x1A50 | 0x0798 |
### Cross-Version Differences
None in behavior. The EEPROM device address (0x51) and bit-offset scheme are identical. Only subroutine addresses differ.
---
## Command 0x94: USE_EXTRA_VOLT
**Named by**: Linux kernel `gp8psk-fe.h`, Windows driver `SkyWalker1Control.h`
| Field | Value |
|-------|-------|
| Direction | OUT (host-to-device) in v2.06/v2.13; OUT with 1-byte ACK in Rev.2 |
| wValue | 0 = normal voltage (13V/18V), non-zero = high voltage (14V/19V) |
| wIndex | 0x0000 |
| wLength | 0 (v2.06/v2.13); 1 byte returned in Rev.2 |
| Purpose | Enable/disable +1V LNB voltage boost for long cable runs |
### Firmware Behavior
1. Read `wValueL` from SETUPDAT[2]
2. Compute carry flag: `CY = (wValueL >= 1)` via `ADD A,#0xFF`
3. Store carry to a bit-addressable IRAM flag
4. Call LNB voltage apply routine:
- If flag set: write 0x6A to XRAM 0xE0B6 (LNB control register)
- If flag clear: write 0x62 to XRAM 0xE0B6
5. ACK (v2.06/v2.13: EP0BCL=0; Rev.2: returns 1 byte with result)
The values 0x6A and 0x62 differ in bit 3 (0x08), which controls the extra voltage boost on the LNB power regulator IC. The register at XRAM 0xE0B6 is a hardware control register in the FX2's XRAM-mapped I/O space.
### Flag Bit Addresses
| Version | Bit Address | IRAM Byte.Bit |
|---------|------------|---------------|
| v2.06 | bit 0x07 | byte 0x20, bit 7 |
| v2.13 | bit 0x00 | byte 0x20, bit 0 |
| Rev.2 | bit 0x04 | byte 0x20, bit 4 |
### Subroutine Addresses
| Version | Handler | Voltage Apply |
|---------|---------|--------------|
| v2.06 | 0x01B9 | 0x2481 |
| v2.13 | 0x01DB | 0x23BF |
| Rev.2 | 0x01C5 | 0x21F5 |
### Linux Driver Usage
```c
// gp8psk-fe.c: gp8psk_fe_enable_high_lnb_voltage()
// Mapped to .enable_high_lnb_voltage frontend ops callback
st->ops->out(st->priv, USE_EXTRA_VOLT, onoff, 0, NULL, 0);
```
### Cross-Version Differences
- v2.06/v2.13: Pure OUT command (EP0BCL=0, no data returned)
- **Rev.2**: Returns 1 byte (carry result from the voltage apply subroutine), making it partially an IN command. The Linux driver sends it as OUT and ignores any returned data, so this is backward-compatible.
- The bit-addressable flag location changes between versions but the functional behavior is identical.
---
## Command 0x95: GET_FPGA_VERS
**Named by**: Linux kernel `gp8psk-fe.h` (`#define GET_FPGA_VERS 0x95`)
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0x0000 |
| wIndex | 0x0000 |
| wLength | 1 byte (v2.13/Rev.2); 2 bytes (v2.06) |
| Returns | EEPROM-stored hardware/FPGA revision identifier |
| Purpose | Read hardware platform identifier from onboard EEPROM |
### Firmware Behavior
All versions read from the I2C EEPROM at device address 0x51, but the EEPROM offset and return size differ:
**v2.06:**
1. Call EEPROM read at offset 0x31, read 2 bytes
2. Combine into 16-bit value (R7:R6)
3. Return 2 bytes in EP0BUF[0..1]
**v2.13 and Rev.2:**
1. Call EEPROM read at offset 0x00, read 2 bytes
2. Compute a combined value
3. Return 1 byte in EP0BUF[0]
### Subroutine Addresses
| Version | Handler | EEPROM Read |
|---------|---------|------------|
| v2.06 | 0x02DF | 0x11DD |
| v2.13 | 0x0303 | 0x21AE |
| Rev.2 | 0x02ED | 0x0FDD |
### Linux Driver Usage
```c
// gp8psk.c: gp8psk_get_fpga_version()
gp8psk_usb_in_op(d, GET_FPGA_VERS, 0, 0, &fpga_vers, 1);
// gp8psk.c: gp8psk_info() -- prints on boot:
// "FPGA Version = %i"
```
Despite the name "FPGA," this reads a version/ID byte from the I2C EEPROM. On these devices there is no separate FPGA -- the name is a legacy artifact from the Genpix product line where some models had an FPGA for signal processing. Here it returns the EEPROM-stored hardware platform identifier.
### Cross-Version Differences
- **v2.06**: Reads EEPROM offset 0x31, returns 2 bytes. The Linux driver only requests 1 byte, so it gets EP0BUF[0] (low byte of the 16-bit value).
- **v2.13/Rev.2**: Reads EEPROM offset 0x00, returns 1 byte. This is a cleaner implementation that matches what the driver actually requests.
- The change from offset 0x31 to offset 0x00 suggests the EEPROM layout was reorganized between Rev.1 and Rev.2 hardware.
---
## Command 0x96: SET_LNB_GPIO_MODE (internal/debug)
**Not named in any driver header.** This command is not used by either the Linux or Windows driver.
| Field | Value |
|-------|-------|
| Direction | OUT (host-to-device) |
| wValue | 0 = default mode, non-zero = extra-volt pin mapping |
| wIndex | 0x0000 |
| wLength | 0 (no data phase) |
| Purpose | Configure LNB power supply GPIO output enable pins |
### Firmware Behavior
1. Read `wValueL`, compute carry flag (same pattern as USE_EXTRA_VOLT)
2. Store flag to bit-addressable IRAM location
3. Call GPIO configuration routine:
**If flag set (v2.06/v2.13):**
```
IOB = (IOB & 0xF7) | 0x06 ; clear IOB.3, set IOB.2 and IOB.1
OEB = 0xFE ; enable IOB[7:1] as outputs, IOB.0 as input
```
**If flag clear (v2.06/v2.13):**
```
OEB = 0xF0 ; enable IOB[7:4] as outputs, IOB[3:0] as inputs
```
### Flag Bit Addresses
| Version | Bit Address |
|---------|------------|
| v2.06 | bit 0x07 |
| v2.13 | bit 0x04 |
| Rev.2 | bit 0x06 |
### Subroutine Addresses
| Version | Handler | GPIO Config |
|---------|---------|------------|
| v2.06 | 0x02B4 | 0x2406 |
| v2.13 | 0x02D8 | 0x2344 |
| Rev.2 | 0x02C2 | 0x20F9 |
### Cross-Version Differences
- **v2.06/v2.13**: Controls Port B (IOB/OEB) pins only. Clears IOB.3, sets IOB.2 and IOB.1 for voltage mode.
- **Rev.2**: Uses different GPIO pins reflecting the Rev.2 PCB layout:
- Clears IOB.4 (instead of IOB.3)
- Sets OEB with IOB.4 enabled
- Additionally sets Port A pins: P0.6 and P0.0 (via `ORL P0, #0x41` and `ORL OEA, #0x41`)
- Default mode: `OEB = 0xE7`, `OEA = 0x9E`
This command configures the GPIO output enable registers that control the LNB voltage regulator hardware. It works in conjunction with USE_EXTRA_VOLT (0x94) and SET_LNB_VOLTAGE (0x8B) -- cmd 0x96 sets which pins are active outputs, while 0x8B and 0x94 set the pin states.
---
## Command 0x97: SET_GPIO_PINS (internal/debug)
**Not named in any driver header.** This command is not used by either the Linux or Windows driver.
| Field | Value |
|-------|-------|
| Direction | OUT (host-to-device) |
| wValue | GPIO pin state bitmap |
| wIndex | 0x0000 |
| wLength | 0 (no data phase) |
| Purpose | Direct GPIO pin write for LNB/switch hardware control |
### Firmware Behavior
**v2.06/v2.13:**
```
IOB = (IOB & 0xF1) | (wValueL & 0x0E)
```
Clears IOB bits [3:1] and sets them to the corresponding bits from `wValueL`. This gives direct control over the 3 LNB-related GPIO pins on Port B.
**Rev.2:**
Maps individual bits of `wValueL` to different GPIO pins:
```
if wValueL.bit1: P0.6 = 1 else P0.6 = 0 ; Port A pin 6
if wValueL.bit2: P0.0 = 1 else P0.0 = 0 ; Port A pin 0
if wValueL.bit3: IOB.4 = 1 else IOB.4 = 0 ; Port B pin 4
```
### Subroutine Addresses
| Version | Handler | GPIO Write |
|---------|---------|-----------|
| v2.06 | 0x02C1 | 0x24BE |
| v2.13 | 0x02E5 | 0x2429 |
| Rev.2 | 0x02CF | 0x1FCF |
### wValue Bit Mapping
**v2.06/v2.13 (Port B bulk write):**
| wValue Bit | GPIO Pin | Function |
|-----------|---------|----------|
| bit 1 | IOB.1 | LNB regulator control line 1 |
| bit 2 | IOB.2 | LNB regulator control line 2 |
| bit 3 | IOB.3 | LNB regulator control line 3 |
**Rev.2 (individual pin mapping):**
| wValue Bit | GPIO Pin | Function |
|-----------|---------|----------|
| bit 1 | P0.6 (Port A) | LNB control line |
| bit 2 | P0.0 (Port A) | LNB control line |
| bit 3 | IOB.4 (Port B) | LNB control line |
### Cross-Version Differences
Major hardware difference: v2.06/v2.13 use a simple Port B mask write, while Rev.2 maps each bit to different pins on two different ports (A and B). This reflects the Rev.2 PCB redesign that moved LNB control circuitry to different FX2 GPIO pins.
---
## Command 0x98: GET_GPIO_STATUS (internal/debug)
**Not named in any driver header.** This command is not used by either the Linux or Windows driver.
| Field | Value |
|-------|-------|
| Direction | IN (device-to-host) |
| wValue | 0x0000 |
| wIndex | 0x0000 |
| wLength | 1 byte |
| Returns | GPIO input pin state (0 or 1) |
| Purpose | Read LNB-related GPIO input pin state |
### Firmware Behavior
**v2.06/v2.13:**
```
A = IOB & 0x01 ; read Port B bit 0
EP0BUF[0] = A ; return 0 or 1
EP0BCL = 1
```
**Rev.2:**
```
A = P0 ; read Port A
if P0.5 == 1:
R7 = 1
else:
R7 = 0
EP0BUF[0] = R7
EP0BCL = 1
```
### Subroutine Addresses
| Version | Handler | GPIO Read |
|---------|---------|----------|
| v2.06 | 0x02CB | 0x24CC |
| v2.13 | 0x02EF | 0x2437 |
| Rev.2 | 0x02D9 | 0x0046 |
### GPIO Pin Read
| Version | Pin | SFR |
|---------|-----|-----|
| v2.06 | IOB.0 (Port B bit 0) | SFR 0xB0, bit 0 |
| v2.13 | IOB.0 (Port B bit 0) | SFR 0xB0, bit 0 |
| Rev.2 | P0.5 (Port A bit 5) | SFR 0x80, bit 5 |
### Purpose
This reads a single GPIO input pin, likely a feedback/status signal from the LNB power supply circuitry (overcurrent detect, power-good, or similar). The pin assignment changed with the Rev.2 PCB redesign.
### Cross-Version Differences
- v2.06/v2.13: Reads Port B pin 0 (IOB.0) via mask `& 0x01`
- Rev.2: Reads Port A pin 5 (P0.5) via bit test `JNB ACC.5`
- Different GPIO pin due to Rev.2 hardware redesign
---
## Summary Table
| Cmd | Name | Dir | wValue | wLength | Purpose |
|------|------|-----|--------|---------|---------|
| 0x8F | **SET_DN_SWITCH** | OUT | 7-bit switch cmd | 0 | Legacy Dish Network switch via GPIO bit-bang on P0.4 |
| 0x91 | **I2C_ADDR_ADJUST** | IN | 0=dec, else inc | 1 | Inc/dec internal IRAM counter (debug) |
| 0x92 | **GET_FW_VERS** | IN | 0 | 6 | Read firmware version + build date (hardcoded) |
| 0x93 | **GET_SERIAL_NUMBER** | IN | 0 | 4 | Read 4-byte serial from I2C EEPROM (0x51) |
| 0x94 | **USE_EXTRA_VOLT** | OUT | 0=off, 1=on | 0 | Enable +1V LNB boost (13->14V, 18->19V) via XRAM 0xE0B6 |
| 0x95 | **GET_FPGA_VERS** | IN | 0 | 1 | Read EEPROM-stored hardware/platform ID |
| 0x96 | **SET_LNB_GPIO_MODE** | OUT | 0=default, 1=active | 0 | Configure LNB GPIO output enables (IOB/OEB) |
| 0x97 | **SET_GPIO_PINS** | OUT | pin bitmap | 0 | Direct write to LNB GPIO pins |
| 0x98 | **GET_GPIO_STATUS** | IN | 0 | 1 | Read LNB feedback GPIO pin (0 or 1) |
### Driver Usage
| Cmd | Linux Kernel | Windows Driver | Firmware |
|-----|-------------|----------------|----------|
| 0x8F | `dishnetwork_send_legacy_command` callback | Defined in header, not called in source | All versions |
| 0x91 | Not used | Not defined | All versions |
| 0x92 | `gp8psk_get_fw_version()` on boot | Not defined | All versions |
| 0x93 | Not called directly (defined in header) | Defined in header, not called in source | All versions |
| 0x94 | `enable_high_lnb_voltage` callback | Defined in header, not called in source | All versions |
| 0x95 | `gp8psk_get_fpga_version()` on boot | Not defined | All versions |
| 0x96 | Not used | Not defined | All versions |
| 0x97 | Not used | Not defined | All versions |
| 0x98 | Not used | Not defined | All versions |
### Cross-Version Change Summary
| Cmd | Changed? | Details |
|-----|----------|---------|
| 0x8F | No | Identical bit-bang algorithm, same GPIO pin P0.4 |
| 0x91 | Minor | IRAM counter address: 0x66 (v2.06) vs 0x18 (v2.13/Rev.2) |
| 0x92 | Data only | Different hardcoded version bytes per build |
| 0x93 | No | Same EEPROM read logic, same device 0x51 |
| 0x94 | Minor | Rev.2 returns 1 byte (ack); v2.06/v2.13 return nothing |
| 0x95 | Yes | v2.06 reads EEPROM offset 0x31, returns 2 bytes; v2.13/Rev.2 read offset 0x00, return 1 byte |
| 0x96 | Yes | Rev.2 uses different GPIO pins (IOB.4 + Port A) vs v2.06/v2.13 (IOB.3/2/1) |
| 0x97 | Yes | Rev.2 maps bits to individual pins across two ports; v2.06/v2.13 bulk-writes Port B |
| 0x98 | Yes | Rev.2 reads P0.5 (Port A); v2.06/v2.13 read IOB.0 (Port B) |
---
## FX2 Register Reference
| Address | Name | Purpose |
|---------|------|---------|
| 0xE6B8 | SETUPDAT[0] | bmRequestType |
| 0xE6B9 | SETUPDAT[1] | bRequest |
| 0xE6BA | SETUPDAT[2] | wValueL |
| 0xE6BB | SETUPDAT[3] | wValueH |
| 0xE68A | EP0BCH | EP0 byte count high |
| 0xE68B | EP0BCL | EP0 byte count low (write triggers transfer) |
| 0xE740 | EP0BUF | EP0 data buffer start |
| 0xE0B6 | (custom) | LNB voltage control register |
| SFR 0x80 | P0 / IOA | Port A (Port 0) data register |
| SFR 0xB0 | P3 / IOB | Port B data register |
| SFR 0xB2 | OEA | Port A output enable |
| SFR 0xB5 | OEB | Port B output enable |