# Genpix SkyWalker-1 Firmware v2.13.x Sub-Variant Comparison Report ## Executive Summary Three firmware sub-variants were extracted from `SW1_update_2_13_x.exe` and analyzed as 8051 (Cypress FX2) binaries via Ghidra. The analysis reveals that **FW1 (v2.13.1) targets fundamentally different hardware** than FW2/FW3, while **FW2 (v2.13.2) and FW3 (v2.13.3) target different revisions of a newer hardware platform** with an external demodulator connected via a parallel data bus. --- ## 1. Function List Comparison ### Function Counts | Variant | Port | Functions | Extra Function | |---------|------|-----------|----------------| | FW1 (v2.13.1) | 8194 | 82 | FUN_CODE_0fc7, FUN_CODE_1405, FUN_CODE_14b9 (unique) | | FW2 (v2.13.2) | 8195 | 83 | FUN_CODE_1288 (unique), FUN_CODE_0ffc | | FW3 (v2.13.3) | 8196 | 83 | FUN_CODE_1288, FUN_CODE_0706 (unique) | ### Shared Core Functions (Same Address Across All Three) The following functions exist at identical addresses in all variants: ``` CODE:0000 RESET_vector CODE:0003 INT0_vector CODE:0033 INT2_USB_GPIF_vector CODE:0036 FUN_CODE_0036 CODE:0043 INT4_FX2_vector CODE:004b INT5_FX2_vector CODE:0050 FUN_CODE_0050 CODE:0053 INT6_FX2_vector CODE:0056 FUN_CODE_0056 CODE:034e FUN_CODE_034e (vendor cmd handler) CODE:06d9 FUN_CODE_06d9 CODE:0718 FUN_CODE_0718 CODE:072a FUN_CODE_072a CODE:0779 FUN_CODE_0779 CODE:079d FUN_CODE_079d CODE:0800 FUN_CODE_0800 (main loop) CODE:0ca4 FUN_CODE_0ca4 CODE:0eea FUN_CODE_0eea CODE:1000 FUN_CODE_1000 CODE:1500 thunk (target differs!) CODE:15b8 FUN_CODE_15b8 CODE:170d main_entry CODE:1799 FUN_CODE_1799 CODE:1800 FUN_CODE_1800 CODE:19ed FUN_CODE_19ed CODE:1a5d FUN_CODE_1a5d CODE:1ac6 FUN_CODE_1ac6 CODE:1b2a FUN_CODE_1b2a ``` ### Functions That Shift Addresses (Same Logic, Different Location) Many functions exist in all three variants but at shifted addresses: | Purpose | FW1 Address | FW2 Address | FW3 Address | |---------|-------------|-------------|-------------| | Hardware init | 0x11ab | 0x1288 | 0x1288 | | Demod setup | 0x10d9 | 0x10dd | 0x10dd | | I2C/parallel data transfer | 0x0eea (I2C) | 0x0eea (parallel) | 0x0eea (parallel) | | Tuner detect | 0x1405 | 0x0eea | 0x0eea | | Delay loop | 0x14b9 | 0x1e92 (FW2) | 0x1e88 (FW3) | | EEPROM checksum | 0x1ca0 | 0x1cff | 0x1ca1 | | USB descriptor setup | 0x2031 | 0x206c | 0x206c | | Thunk target | 0x2252 | 0x228d | 0x228d | | 2nd init call | 0x1be6 | 0x1c45 | 0x1cf7 | ### Functions Unique to Each Variant **FW1 only:** - `FUN_CODE_0fc7` -- An I2C write-with-retry function (tries 0x14 = 20 times via I2C bus using FUN_CODE_23ae/23ee) - `FUN_CODE_1405` -- Tuner/demodulator identification via **I2C bus + P1 port reads** with signature matching - `FUN_CODE_14b9` -- Calibrated delay function using CPUCS clock divider awareness **FW2 only:** - `FUN_CODE_0ffc` -- Stores a parameter into BANK3_R1 (register save) - `FUN_CODE_1288` -- New hardware initialization (loads from external memory at e080-e08e) **FW3 only:** - `FUN_CODE_0706` -- Memory write dispatcher that handles 3 addressing modes (XDATA, IDATA, direct) - `FUN_CODE_1288` -- Same as FW2 - Uses `FUN_CODE_1ffc` (at a different address from FW2's 0x1ffd) --- ## 2. Main Entry Comparison (CODE:170D) ### Identical Across All Three: - IRAM clear loop (0x7F down to 0x00) - Init table parsing from CODE:0B88 - Bit config table reference at CODE:1740 - Final call to FUN_CODE_0800 ### One Key Difference -- Stack Pointer: | Variant | SP Value | |---------|----------| | FW1 | SP = 0x50 | | FW2 | SP = 0x50 | | FW3 | SP = **0x52** | FW3 sets `SP = 0x52`, requiring 2 more bytes of IRAM for stack usage. This indicates FW3 uses additional internal RAM locations (0x51-0x52) for state variables that FW1/FW2 don't need, pushing the stack higher. **Confirmed**: FW3 uses `DAT_INTMEM_51` as a hardware status register throughout its code, while FW1/FW2 use `DAT_INTMEM_4f` for the same purpose. The 2-byte difference in SP exactly accounts for this. ### Memory at CODE:170D: ``` FW1: 787f e4f6 d8fd 7581 50 02 1754 020800 e4 FW2: 787f e4f6 d8fd 7581 50 02 1754 020800 e4 (identical to FW1) FW3: 787f e4f6 d8fd 7581 52 02 1754 020800 e4 (byte at 0x1714 = 0x52 vs 0x50) ``` --- ## 3. Key Function Decompilation Comparison ### 3.1 FUN_CODE_0800 (Main Loop) -- All at Same Address **Structure identical across all three:** 1. Clear INTMEM locations 0x22-0x2D, 0x32-0x35 2. Clear bit flags _1_0 and _0_6 3. Call hardware init (address differs) 4. Set up BANK register pairs (XDATA pointers): all use the same values (0x0E00, 0x0E12, 0x0E1C, 0x0E54, 0x0E8C, 0x0EE8) 5. Call FUN_CODE_072a with init params 6. Memory copy loop (0x80 bytes at offset 0x0E00) 7. Retry loop with FUN_CODE_1799 (20 attempts) 8. Retry loop with EEPROM checksum function (20 attempts) 9. Check demod type byte at offset +10 from BANK1_R4/R5 (== 0x03 -> set flag) 10. Enable interrupts, enter main event loop **Differences in called function addresses (relocated, not functionally different):** | Call Purpose | FW1 | FW2 | FW3 | |-------------|------|------|------| | Hardware init | FUN_CODE_11ab | FUN_CODE_1288 | FUN_CODE_1288 | | EEPROM checksum | FUN_CODE_1ca0 | FUN_CODE_1cff | FUN_CODE_1ca1 | | USB setup | FUN_CODE_2031 | FUN_CODE_206c | FUN_CODE_206c | | Main loop poll | FUN_CODE_21ec | FUN_CODE_2227 | FUN_CODE_2227 | | Interrupt check | FUN_CODE_2445 | FUN_CODE_247c | FUN_CODE_2473 | | Status check | FUN_CODE_2189 | FUN_CODE_21c4 | FUN_CODE_21c4 | | Buffer flush | FUN_CODE_20b9 | FUN_CODE_20f4 | FUN_CODE_20f4 | | EP complete | FUN_CODE_2447 | FUN_CODE_247e | FUN_CODE_2475 | ### 3.2 Hardware Init Function (FW1: 0x11ab, FW2/FW3: 0x1288) **Functionally identical** across all three except: | Parameter | FW1 | FW2 | FW3 | |-----------|------|------|------| | P0 init value | **0xa4** | **0xa4** | **0xa0** | | Status register | DAT_INTMEM_4f | DAT_INTMEM_4f | **DAT_INTMEM_51** | | Sub-init call 1 | FUN_CODE_1c44 | FUN_CODE_1ca3 | FUN_CODE_1c45 | | Sub-init call 2 | FUN_CODE_1000 | FUN_CODE_10dd | FUN_CODE_10dd | | I2C/bus init | FUN_CODE_213b | FUN_CODE_2176 | FUN_CODE_2176 | | Tuner init | FUN_CODE_1be6 | FUN_CODE_1c45 | FUN_CODE_1cf7 | **P0 = 0xa4 vs 0xa0**: P0 on the FX2 controls GPIO port 0. The difference is bit 2: - FW1/FW2: P0 bit 2 = 1 (0xa4 = 1010 0100) - FW3: P0 bit 2 = 0 (0xa0 = 1010 0000) This suggests different default GPIO state for a control signal, likely related to the demodulator interface mode or reset polarity. ### 3.3 FUN_CODE_0eea -- The Most Revealing Difference **FW1**: This is a standard **I2C master transfer** function: - Uses `FUN_CODE_23ae` (I2C START), `FUN_CODE_23ee` (I2C byte write), `FUN_CODE_23d0` (I2C address) - Reads back via `FUN_CODE_2164` - Standard I2C retry with NACK detection **FW2**: This is a **parallel bus read with demodulator handshake**: - Reads demod type from address table (BANK1_R4/R5 + offset) - Uses `FUN_CODE_11b6` for demod selection - Toggles P0 bits 6/7 for bus control (P0.6 = chip select, P0.7 = read strobe) - Reads data from P1 port (parallel data bus) - Checks P1 ^ 0x1D (signature) then reads P1 for device ID - Matches device IDs: 0xC5/0xD5 (for type 3), 0x5A (type 4), 0x5B (type 5), 0x5C (type 6) - Controls P3 bits for demod power/reset **FW3**: Similar parallel bus read as FW2 but with **different timing and bus protocol**: - Sets P0 | 0x80 once at start (not per-iteration like FW2) - Uses `DAT_INTMEM_3f` and `DAT_INTMEM_40` as OR-accumulators for P1 reads - Two separate P1 reads per cycle: one with P0.6 high, one with P0.6 low - Calls `FUN_CODE_1b2a` with 3 parameters (accumulated OR values) vs FW2's 2 parameters - Uses `P0 | 0x44` and `P0 & 0xBF` toggle pattern (vs FW2's different bit dance) ### 3.4 Vendor Command Handler (FUN_CODE_034e) **Structurally identical** across all three -- same switch/case table structure at CODE:035e. Key differences: | Feature | FW1 | FW2 | FW3 | |---------|------|------|------| | Case 0x35f/0x427 call | FUN_CODE_0ffe (nop) | FUN_CODE_1ffd | FUN_CODE_0ffe (nop) | | Case 0x361 call | FUN_CODE_2441 | FUN_CODE_2478 | FUN_CODE_246f | | Case 0x365 call | FUN_CODE_2443 | FUN_CODE_247a | FUN_CODE_2471 | | Case 0x36f call | FUN_CODE_2357 | FUN_CODE_2392 | FUN_CODE_2392 | | Case 0x371 call | FUN_CODE_243d | FUN_CODE_0ffc | FUN_CODE_1ffc | | Case 0x373/0x3ff call | FUN_CODE_2309 | FUN_CODE_2344 | FUN_CODE_2344 | | Case 0xf0 indirect call | func_0x231e | func_0x2359 | func_0x2359 | | Case 0x39d return | func_0x06e4 | DAT=0x0 | DAT=0x0 | | Case 0x3d1 call | FUN_CODE_2110 | FUN_CODE_214b | FUN_CODE_214b | | Case 0x3d3 behavior | TR2 timer check | OR operation | OR operation | | Case 0x405 behavior | Goto LAB_05db | Conditional branch | Conditional branch | | Case 0x421 behavior | Simple check | Extra P2_1, RL A logic | Extra P2_1, RL A logic | **FW1's unique case 0x3d3**: Checks Timer 2 Run flag (TR2) -- this is used for I2C bus timeout recovery, consistent with FW1 being I2C-based. **FW2/FW3's unique case 0x421-0x423**: Includes a rotate-left and P2.1 write -- this is a parallel bus data direction control, consistent with the external demodulator interface. --- ## 4. Memory Comparison at Key Offsets ### CODE:0000-0x000F (Reset Vector) ``` FW1: 02170d 753728 e53760 1b7ffc 7e7f12 22 FW2: 02170d 753728 e53760 1b7ffc 7e7f12 22 FW3: 02170d 753728 e53760 1b7ffc 7e7f12 22 ALL IDENTICAL -- LJMP 0x170D, then INT0 vector handler ``` ### CODE:0B88-0x0B9F (Init Table Start) ``` FW1: 41e0b6 626033 e0c609 070939 4f0000 000000 000000 000000 FW2: 41e0b6 626033 e0c609 070939 4f0000 000000 000000 000000 FW3: 41e0b6 626033 e0c609 070939 4f0000 000000 000000 000000 ALL IDENTICAL -- Same register/SFR initialization table ``` ### CODE:1500 (Thunk/INT Vector Target) ``` FW1: 02 2252 00 02 22dd 00 02 22c7 00 02 226a 00 FW2: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00 FW3: 02 228d 00 02 2318 00 02 2302 00 02 22a5 00 ``` FW1 jumps to different addresses than FW2/FW3. **FW2 and FW3 are IDENTICAL here** -- their interrupt handlers are at the same addresses. ### CODE:1740-0x174F (Bit Config Table) ``` FW1: 4004 f456 8001 46f6 dfe4 800b 0102 0408 FW2: 4004 f456 8001 46f6 dfe4 800b 0102 0408 FW3: 4004 f456 8001 46f6 dfe4 800b 0102 0408 ALL IDENTICAL -- Same bit manipulation lookup table ``` ### CODE:0800 (Main Loop Start) ``` FW1: e4f52d...c208c206 12 11ab 750c0e... FW2: e4f52d...c208c206 12 1288 750c0e... FW3: e4f52d...c208c206 12 1288 750c0e... ``` Only difference: the LCALL target (FW1: 0x11ab, FW2/FW3: 0x1288). ### CODE:06D9 (Utility Functions) ``` FW1: bb010c e58229 f582e5 833af5 83e022 5006e9 2582f8 e622 FW2: bb010c e58229 f582e5 833af5 83e022 5006e9 2582f8 e622 FW3: bb010c e58229 f582e5 833af5 83e022 5006e9 2582f8 e622 ALL IDENTICAL -- Generic memory access utilities shared by all ``` ### CODE:0EEA (Critical Divergence Point) ``` FW1: 8f44 8c45 8d46 8b47 754a14 e544 b451... (I2C transfer params in registers) FW2: 753e14 e50d 240a f582 e435 0cf5 83e0... (reads from DPTR+offset table) FW3: 753e14 e4f5 3ff5 40 e50d 240a f582... (similar to FW2 + accumulator init) ``` This confirms: FW1's FUN_CODE_0eea is a completely different function (I2C master) than FW2/FW3's (parallel bus demod interface). --- ## 5. FW2 vs FW3 Specific Differences FW2 and FW3 are the most similar pair (1,525 bytes different). Key differences: | Feature | FW2 | FW3 | |---------|------|------| | Stack Pointer | SP = 0x50 | SP = 0x52 | | Status register | DAT_INTMEM_4f | DAT_INTMEM_51 | | P0 init | 0xa4 | 0xa0 (bit 2 different) | | FUN_CODE_0eea bus protocol | Single-phase P1 read | Dual-phase P1 read with OR accumulation | | I2C buffer addresses | DAT_INTMEM_48/49 | DAT_INTMEM_4a/4b | | Unique function | FUN_CODE_0ffc (register store) | FUN_CODE_0706 (multi-mode write) | | P0 bus timing | P0 &= ~0x40; P0 |= 0x80 per iteration | P0 |= 0x80 once; P0 |= 0x44 / P0 &= ~0x40 per phase | | Delay function address | FUN_CODE_1e92 | FUN_CODE_1e88 | ### The P1 Read Difference is Critical **FW2** reads P1 once per bus cycle: ```c uVar1 = P1; // Read with one bus state P0 |= 0x40; // Then change control line uVar2 = P1; // Read again FUN_CODE_1b2a(uVar2, uVar1); // Process both samples ``` **FW3** reads P1 in two phases and OR-accumulates: ```c DAT_INTMEM_3f = 0; DAT_INTMEM_40 = 0; // Clear accumulators // Phase 1: P0.6 high P0 |= 0x44; bVar2 = P1; DAT_INTMEM_3f |= bVar2; // OR-accumulate // Phase 2: P0.6 low P0 &= ~0x40; bVar2 = P1; DAT_INTMEM_40 |= bVar2; // OR-accumulate FUN_CODE_1b2a(0, DAT_INTMEM_3f, DAT_INTMEM_40); // Process accumulated ``` This OR-accumulation pattern in FW3 suggests dealing with a bus that may have metastable signals or requires multiple samples, characteristic of **a different demodulator chip with different bus timing**. --- ## 6. Hypothesis: What Distinguishes Each Variant ### FW1 (v2.13.1) -- Original I2C-Connected Demodulator Hardware **Target**: First-generation SkyWalker-1 PCB with an **I2C-connected demodulator** (likely a Conexant/Zarlink integrated tuner+demod). Evidence: - Uses standard I2C protocol functions (START, STOP, ACK/NACK, byte read/write) - FUN_CODE_0fc7: I2C write retry loop - FUN_CODE_1405: Reads demodulator identification via I2C + P1 GPIO, checks device signatures: - Type 3: P1 == 0xA5 or 0xB5 - Type 4: P1 == 0x5A - Type 5: P1 == 0x5B - Type 6: P1 == 0x5C - Has timer-based I2C timeout (TR2 check in vendor handler) - SP=0x50, fewer IRAM state variables needed - `func_0x06e4` called for unknown vendor commands (older error path) **Likely demodulator**: An I2C-bus demodulator supporting DVB-S/DCII/DSS, with the FX2 as USB bridge. The type codes 3-6 likely correspond to different supported modulation modes or demod silicon revisions. ### FW2 (v2.13.2) -- Second-Generation Parallel-Bus Demodulator **Target**: Revised SkyWalker-1 PCB with a **parallel-bus connected demodulator** (likely a different demod chip or a custom FPGA/ASIC). Evidence: - FUN_CODE_0eea: Parallel bus read using P0 GPIO for control (CS, RD strobe) and P1 for data - FUN_CODE_10dd: Copies configuration from external memory (e080-e08e) into demod registers (e6c0-e6cd) -- reads 15 configuration bytes from what appears to be EEPROM/flash config area - Reads same device signatures but via parallel bus (P1 ^ 0x1D check, then P1 reads for 0xC5/0xD5/0x5A/0x5B/0x5C) - P0 = 0xa4 (bit 2 set = specific bus mode select) - SP = 0x50 - Extra vendor command paths for parallel data direction (P2.1 control in case 0x421/0x423) - Uses FUN_CODE_14e2: Busy-wait on e678 bit 6 (demod ready flag) with 0xFFFF timeout counter **Likely demodulator**: A parallel-bus demodulator with 8-bit data port on P1, active-low chip select and read strobe on P0. The external config block (e080-e08e) stores per-unit calibration/tuning data. ### FW3 (v2.13.3) -- Third-Generation with Enhanced Bus Protocol **Target**: Further revised PCB with the **same parallel-bus demodulator as FW2** but with a **different bus interface revision** or a variant chip that requires modified timing. Evidence: - Same demod configuration loading as FW2 (FUN_CODE_10dd identical) - Same parallel bus architecture but with dual-phase reading and OR-accumulation - P0 = 0xa0 (bit 2 clear = different bus mode or reset polarity) - SP = 0x52 (2 more IRAM bytes: status register moved from 0x4F to 0x51) - FUN_CODE_0706 (unique): Multi-mode memory write supporting XDATA, IDATA, and direct addressing -- suggests the demod communicates through multiple address spaces - The OR-accumulation of P1 reads suggests either: - A demodulator with open-drain outputs requiring multiple read cycles - Bus settling time issues on the newer PCB layout - A chip variant that serializes data across multiple bus phases --- ## 7. Summary Table | Aspect | FW1 (v2.13.1) | FW2 (v2.13.2) | FW3 (v2.13.3) | |--------|---------------|---------------|---------------| | **Demod interface** | I2C bus | Parallel bus (P0/P1) | Parallel bus (enhanced) | | **Bus protocol** | I2C START/STOP/ACK | Single-phase P1 read | Dual-phase P1 read + OR accumulate | | **Stack pointer** | 0x50 | 0x50 | 0x52 | | **P0 init** | 0xa4 | 0xa4 | 0xa0 | | **Status register** | INTMEM 0x4F | INTMEM 0x4F | INTMEM 0x51 | | **Config source** | Hardcoded | External (e080-e08e) | External (e080-e08e) | | **Demod types supported** | 3-6 (via I2C) | 3-6 (via parallel) | 3-6 (via parallel) | | **Binary distance from FW1** | -- | 3,993 bytes | 3,789 bytes | | **Binary distance from FW2** | 3,993 bytes | -- | 1,525 bytes | ## 8. Conclusion The three v2.13 firmware sub-variants represent an evolutionary progression of the SkyWalker-1 hardware: 1. **v2.13.1 (FW1)**: Original design with I2C-connected demodulator. The FX2 communicates with the demod entirely through I2C, using standard master-mode transactions. This is the simplest interface but limited in bandwidth. 2. **v2.13.2 (FW2)**: Redesigned with a parallel-bus demodulator. The demod data port is connected directly to FX2's P1, with P0 bits used for bus control signals (chip select, read/write strobes). Configuration data is loaded from an external EEPROM area. This provides higher throughput for TS data transfer. 3. **v2.13.3 (FW3)**: Refinement of the FW2 design, likely for a newer demod silicon revision or PCB layout. Uses dual-phase bus reads with signal accumulation, different GPIO defaults, and additional IRAM for state tracking. The OR-accumulation pattern suggests dealing with bus signal integrity improvements. The updater program's format string `"FW 2.13.%i"` and its selection logic presumably check the hardware revision (likely via a GPIO strap or I2C ID read) to determine which of the three firmware images to flash. All three variants support the same modulation types (DVB-S/QPSK, Turbo QPSK/8PSK, DCII, DSS) -- the demod type codes 3-6 appear in all variants. The differences are purely about the hardware interface, not the feature set.