skywalker-1/rev2-deep-analysis.md
Ryan Malloy bbdcb243dc Normalize line endings to LF across entire repository
Apply .gitattributes normalization to convert all CRLF line
endings inherited from Windows-origin source files to Unix LF.
175 files, zero content changes.
2026-02-20 10:55:50 -07:00

38 KiB

Genpix SkyWalker-1 Rev.2 v2.10.4 Deep Firmware Analysis

Executive Summary

Rev.2 v2.10.4 has 107 functions -- the most of any firmware version (vs 61 for v2.06, 88 for v2.13 FW1). The higher function count is driven by three factors:

  1. Granular function decomposition: Rev.2 breaks large operations into many small helper functions (10-30 bytes each), where v2.06 inlines them and v2.13 recombines them differently.
  2. A massive 874-byte configuration dispatcher (FUN_CODE_0800) that contains an embedded copy of the main loop, making Ghidra count additional entry points as separate functions.
  3. Extra I2C/demodulator helper chains, GPIO control primitives, and hardware-polling wait loops that exist as individual callable units.

Rev.2 sits architecturally between v2.06 and v2.13 -- it has v2.06's INT0 USB re-enumeration behavior but already uses the same descriptor base offset (0x0E00) and stack pointer (SP=0x4F) pattern that v2.13 would adopt. It supports 27 vendor commands (0x80-0x9A) vs 30 in v2.06/v2.13.


1. Complete Function Inventory (107 Functions)

1.1 Vector Table and ISR Region (0x0000-0x0055)

Address Name Size Role
0x0000 RESET_vector 3 Jump to main (0x155F)
0x0003 INT0_ISR 12 INT0 handler -- USB re-enumeration (bit check + branch)
0x000F INT0_ISR_bit_clear 36 INT0 continuation -- CPUCS pulse, IRQ clear, delay
0x0033 INT2_USB_GPIF_vector 3 INT2 vector -- clears CCON.4 (PCA timer)
0x0036 i2c_exchange_byte 5 I2C byte exchange primitive
0x003B I2C_ISR 8 I2C interrupt-style handler, calls FUN_CODE_19f4
0x0043 INT4_FX2_vector 8 INT4 vector -- sets _0_1, clears EXIF.4
0x004B INT5_FX2_vector 3 INT5 vector -- empty (RETI)
0x004E FUN_CODE_004e 3 Unused vector slot -- empty (RET)
0x0051 FUN_CODE_0051 2 Unused vector slot -- empty (RET)
0x0053 INT6_FX2_vector 3 INT6 vector -- sets _0_1, clears EXIF.4

1.2 Vendor Command Dispatch (0x0056-0x0318)

Address Name Size Role
0x0056 vendor_cmd_dispatch 342 Vendor request dispatcher (0x80-0x9A range check, jump table at 0x0076)
0x01AC FUN_CODE_01ac 361 Vendor cmd 0x80: GET_8PSK_CONFIG -- reads config, calls FUN_CODE_1f5c
0x0315 vendor_cmd_stall 2 Stall handler (empty RET)
0x0317 FUN_CODE_0317 2 EP0 completion (empty RET)
0x0319 FUN_CODE_0319 869 Standard USB request handler (massive switch for bRequest 0x00-0x0B)

1.3 Math and Memory Operations (0x067E-0x07FE)

Address Name Size Role
0x067E FUN_CODE_067e 38 Multi-mode data access dispatcher (computed jump based on address mode)
0x06A4 FUN_CODE_06a4 45 Memory read with address mode selection (IRAM/XRAM/CODE)
0x06D1 FUN_CODE_06d1 18 Memory write with address mode selection (IRAM/XRAM/paged)
0x06E3 FUN_CODE_06e3 85 16-bit division routine (param_1!=0: 16/8 long div, else 8/8 fast div)
0x0738 FUN_CODE_0738 79 32-bit multiplication (4-byte x 4-byte partial product accumulation)
0x0787 FUN_CODE_0787 36 32-bit subtraction with borrow chain
0x07AB FUN_CODE_07ab 38 Table-driven function dispatcher (triplet lookup: key, addr_hi, addr_lo)
0x07D1 FUN_CODE_07d1 45 DiSEqC byte transmit -- 8 data bits + odd parity via P0.4
0x07FE FUN_CODE_07fe 2 Empty stub (RET)

1.4 Configuration Dispatcher (0x0800-0x09A8)

Address Name Size Role
0x0800 FUN_CODE_0800 425 Massive config/tuning dispatcher -- 128-entry switch on demod type, embedded main loop copy
0x09A9 FUN_CODE_09a9 699 Main init + main loop -- initializes hardware, enters infinite poll loop

1.5 Demodulator/BCM4500 Management (0x0C64-0x0F00)

Address Name Size Role
0x0C64 FUN_CODE_0c64 280 BCM4500 firmware loader -- I2C block transfer with address tracking
0x0D7C FUN_CODE_0d7c 128 GPIF/slave FIFO configuration -- enables/disables streaming mode
0x0DFC FUN_CODE_0dfc 4 Set BANK3_R1 (simple register store)
0x0F00 FUN_CODE_0f00 256 I2C multi-byte read with parameter setup

1.6 USB Endpoint and Descriptor Setup (0x1000-0x1420)

Address Name Size Role
0x1000 FUN_CODE_1000 217 USB endpoint configuration (FIFO sizes, etc.)
0x10D9 FUN_CODE_10d9 201 USB/peripheral descriptor setup -- IFCONFIG, GPIF, Timer2, GPIO init
0x11A2 FUN_CODE_11a2 94 Serial number / EEPROM reader
0x1200 INT4_INT6_handler 184 INT4/INT6 unified handler -- sets _0_1 flag, clears EXIF.4
0x12B8 FUN_CODE_12b8 180 Configuration structure init (I2C device setup)
0x136C FUN_CODE_136c 180 I2C multi-byte write with address/register params
0x1420 FUN_CODE_1420 319 Configuration update function

1.7 Main Entry and Init (0x155F-0x1670)

Address Name Size Role
0x155F main 71 RESET entry -- clears IRAM 0x00-0x7F, sets SP=0x4F, jumps to main_init
0x15A6 main_init 69 Init data table processor (table at CODE:0B48), jumps to FUN_CODE_09a9
0x15EB FUN_CODE_15eb 133 Descriptor table walker
0x1670 FUN_CODE_1670 241 I2C demod write with verify -- write, wait, read-back check

1.8 I2C Transfer and Demod Functions (0x1761-0x1B90)

Address Name Size Role
0x1761 FUN_CODE_1761 112 I2C multi-byte read with address auto-increment
0x17D1 FUN_CODE_17d1 44 I2C bus wait -- polls FUN_CODE_224f until ready, with timeout
0x17FD FUN_CODE_17fd 3 Empty stub (RET)
0x1800 FUN_CODE_1800 106 GPIF/FIFO management (identical logic to v2.06/v2.13)
0x186A FUN_CODE_186a 105 Tuning/acquisition sequence
0x18D3 FUN_CODE_18d3 101 Extended tuning with polling
0x1938 FUN_CODE_1938 94 Configuration parameter write (USB setup data -> I2C)
0x1996 FUN_CODE_1996 94 Configuration parameter read (I2C -> USB EP0BUF)
0x19F4 FUN_CODE_19f4 92 I2C bus controller -- manages SDA/SCL via XRAM 0xE678
0x1A50 FUN_CODE_1a50 83 I2C address select + start condition
0x1AA3 FUN_CODE_1aa3 82 I2C stop condition + cleanup
0x1AF5 FUN_CODE_1af5 9 I2C address write helper
0x1AFE FUN_CODE_1afe 3 Tiny helper (register move)
0x1B01 FUN_CODE_1b01 67 I2C byte-level transfer
0x1B44 FUN_CODE_1b44 76 I2C ACK/NAK handling
0x1B90 FUN_CODE_1b90 74 I2C bus reset / error recovery

1.9 Delay and DiSEqC Support (0x1BDA-0x1C65)

Address Name Size Role
0x1BDA delay_routine 70 Clock-compensated delay -- adjusts for 12/24/48 MHz CPU clock
0x1C20 FUN_CODE_1c20 69 Configuration update (I2C register write chain)
0x1C65 FUN_CODE_1c65 249 Configuration update (extended I2C register block)

1.10 DiSEqC GPIO Bit-Bang (0x1D5E-0x1E3D)

Address Name Size Role
0x1D5E FUN_CODE_1d5e 59 DiSEqC message sender -- iterates bytes, calls FUN_CODE_1e3d per byte
0x1D99 FUN_CODE_1d99 55 I2C device probe with retry -- 20 attempts, calls FUN_CODE_21e4 + FUN_CODE_2224
0x1DD0 FUN_CODE_1dd0 109 Demod scan -- tries 3 I2C addresses via FUN_CODE_1670
0x1E3D FUN_CODE_1e3d 54 DiSEqC byte transmit wrapper -- sets P0.2, calls FUN_CODE_136c, adds delay

1.11 I2C Bus Wait / Polling Functions (0x1E73-0x1F5C)

Address Name Size Role
0x1E73 FUN_CODE_1e73 54 I2C bus wait (3-check) -- polls FUN_CODE_222d, FUN_CODE_2215, FUN_CODE_224f
0x1EA9 FUN_CODE_1ea9 49 I2C bus wait (2-check) -- polls FUN_CODE_2215, FUN_CODE_222d
0x1EDA FUN_CODE_1eda 44 USB endpoint reset pulse -- CPUCS bit 0 toggle with delay
0x1F06 FUN_CODE_1f06 43 I2C completion wait -- polls XRAM 0xE678 bit 0 with 16-bit timeout
0x1F31 FUN_CODE_1f31 43 Descriptor version checker -- walks descriptor chain checking byte+1==0x03
0x1F5C FUN_CODE_1f5c 41 LNB voltage I2C select -- probes I2C device 0x60 or custom addr from 0xE0B6

1.12 GPIO Control Primitives (0x1F85-0x2038)

Address Name Size Role
0x1F85 FUN_CODE_1f85 37 I2C completion wait (2-flag) -- polls 0xE678 bits 0 and 2
0x1FBE FUN_CODE_1fbe 17 Microsecond delay loop -- DPL=0, counts 0xFDA5 iterations (~600 cycles)
0x1FCF FUN_CODE_1fcf 46 GPIO pin controller -- sets P0.6, P0.0, P3.4 based on param bits 1-3
0x1FFD FUN_CODE_1ffd 3 Empty stub (RET)
0x2000 FUN_CODE_2000 30 I2C busy wait -- polls XRAM 0xE678 bit 6 with 16-bit timeout
0x201E FUN_CODE_201e 26 Main loop poll -- calls FUN_CODE_1938 + FUN_CODE_1996
0x2038 FUN_CODE_2038 51 GPIO clock strobe -- calls FUN_CODE_1fcf three times (setup, clock, cleanup)

1.13 I2C Register Helpers (0x206B-0x20F9)

Address Name Size Role
0x206B FUN_CODE_206b 25 I2C single-byte read -- reads from register via FUN_CODE_0f00
0x2084 GPIF_INT4_INT6_handler 48 Duplicate INT4/INT6 handler (sets _0_1, clears EXIF.4)
0x20B4 FUN_CODE_20b4 23 I2C register write -- writes to addr 0xE114, register 0xA0
0x20CB FUN_CODE_20cb 23 I2C register read -- reads from addr 0xE114 via FUN_CODE_0f00
0x20E2 FUN_CODE_20e2 23 22kHz tone burst -- P0.3 ON, 25 ticks via FUN_CODE_225f, P0.3 OFF
0x20F9 FUN_CODE_20f9 67 GPIO mode switch -- configures P0, P3, IPL1 based on _0_6 flag

1.14 USB Descriptor and EP Management (0x213C-0x219F)

Address Name Size Role
0x213C FUN_CODE_213c 22 DiSEqC bit symbol -- carrier on/off via P0.3, data via _0_4 (P0.4)
0x2152 FUN_CODE_2152 21 EP0BUF write -- stores BANK3 register to EP0BUF (0xE740)
0x2167 FUN_CODE_2167 19 EP0 flush -- waits for EP0CS busy bit clear
0x217A FUN_CODE_217a 19 I2C start + address -- calls FUN_CODE_2000, then FUN_CODE_2206
0x218D FUN_CODE_218d 18 EP0BUF write variant -- stores BANK3_R1 to EP0BUF
0x219F FUN_CODE_219f 18 GPIFADR set -- computes (param_1 << 4

1.15 LNB / Demod Control (0x21B1-0x2206)

Address Name Size Role
0x21B1 FUN_CODE_21b1 17 LNB voltage select (13/18V) -- sets/clears P0.4, updates DAT_INTMEM_4e bit 5
0x21C2 FUN_CODE_21c2 17 22kHz tone enable -- sets/clears P0.3, updates DAT_INTMEM_4e bit 4
0x21D3 FUN_CODE_21d3 17 DiSEqC port direction -- sets/clears P3.6, updates DAT_INTMEM_4e bit 3
0x21E4 FUN_CODE_21e4 34 I2C write + start -- calls FUN_CODE_2000 then FUN_CODE_2206(addr*2)
0x2206 FUN_CODE_2206 15 I2C start condition -- sets 0xE678=0x80, 0xE679=param, calls FUN_CODE_1f06

1.16 I2C Register Read Wrappers (0x2215-0x2257)

Address Name Size Role
0x2215 FUN_CODE_2215 15 Read I2C reg 0xA8 -- calls FUN_CODE_20cb(0xA8), checks bit 0
0x2224 FUN_CODE_2224 9 I2C write (data only) -- sets 0xE679=param, calls FUN_CODE_1f06
0x222D FUN_CODE_222d 10 Read I2C reg 0xA2 -- calls FUN_CODE_20cb(0xA2)
0x223F USB_GPIF_handler 8 INT2 handler duplicate -- clears CCON.4
0x2247 FUN_CODE_2247 8 Read I2C reg 0xA2 (shifted) -- reads 0xA2, returns shifted result
0x224F FUN_CODE_224f 8 Read I2C reg 0xA4 -- reads 0xA4, returns shifted result
0x2257 FUN_CODE_2257 8 DiSEqC status read -- calls FUN_CODE_206b(0x7F, 0xF9)

1.17 Timer and Tail Functions (0x225F-0x2269)

Address Name Size Role
0x225F FUN_CODE_225f 6 Timer2 tick wait -- polls TF2, clears on overflow (500us tick)
0x2265 FUN_CODE_2265 2 Empty stub (RET)
0x2267 FUN_CODE_2267 2 Empty stub (RET)
0x2269 FUN_CODE_2269 2 Empty stub (RET)

2. Cross-Version Function Comparison

2.1 v2.06 Functions (61 total, port 8193)

Address Name
0x0003 INT0_vec
0x0036 FUN_CODE_0036
0x0056 FUN_CODE_0056
0x032A FUN_CODE_032a
0x06C7 FUN_CODE_06c7
0x071C FUN_CODE_071c
0x076B FUN_CODE_076b
0x078F FUN_CODE_078f
0x07FE FUN_CODE_07fe
0x09A7 FUN_CODE_09a7
0x0DDD FUN_CODE_0ddd
0x10F2 FUN_CODE_10f2
0x12EA FUN_CODE_12ea
0x13C3 FUN_CODE_13c3
0x148E FUN_CODE_148e
0x1556 FUN_CODE_1556
0x15E9 FUN_CODE_15e9
0x16B8 FUN_CODE_16b8
0x17FE FUN_CODE_17fe
0x188D main
0x1919 FUN_CODE_1919
0x1A0E FUN_CODE_1a0e
0x1A81 FUN_CODE_1a81
0x1AF2 FUN_CODE_1af2
0x1BCE FUN_CODE_1bce
0x1C37 FUN_CODE_1c37
0x1C95 FUN_CODE_1c95
0x1CF3 FUN_CODE_1cf3
0x1D4F FUN_CODE_1d4f
0x1DA8 FUN_CODE_1da8
0x1DFB FUN_CODE_1dfb
0x2000 FUN_CODE_2000
0x20C5 FUN_CODE_20c5
0x211D FUN_CODE_211d
0x2149 FUN_CODE_2149
0x2174 FUN_CODE_2174
0x219F FUN_CODE_219f
0x21C8 FUN_CODE_21c8
0x21ED FUN_CODE_21ed
0x2201 FUN_CODE_2201
0x2212 FUN_CODE_2212
0x2258 FUN_CODE_2258
0x2279 FUN_CODE_2279
0x2297 FUN_CODE_2297
0x2314 FUN_CODE_2314
0x2344 FUN_CODE_2344
0x235B FUN_CODE_235b
0x23CB FUN_CODE_23cb
0x23F3 FUN_CODE_23f3
0x2419 FUN_CODE_2419
0x242B FUN_CODE_242b
0x243D FUN_CODE_243d
0x244E FUN_CODE_244e
0x2470 FUN_CODE_2470
0x2492 FUN_CODE_2492
0x24AD FUN_CODE_24ad
0x24D2 FUN_CODE_24d2
0x24D6 FUN_CODE_24d6
0x24D8 FUN_CODE_24d8
0x24DA FUN_CODE_24da
0x24DC FUN_CODE_24dc

2.2 v2.13 Functions (88 total, port 8194)

Address Name
0x0000 RESET_vector
0x0003 INT0_vector
0x0033 INT2_USB_GPIF_vector
0x0036 FUN_CODE_0036
0x0043 INT4_FX2_vector
0x004B INT5_FX2_vector
0x0050 FUN_CODE_0050
0x0053 INT6_FX2_vector
0x0056 FUN_CODE_0056
0x0211 FUN_CODE_0211
0x034E FUN_CODE_034e
0x06D9 FUN_CODE_06d9
0x0718 FUN_CODE_0718
0x072A FUN_CODE_072a
0x0779 FUN_CODE_0779
0x079D FUN_CODE_079d
0x0800 FUN_CODE_0800
0x0CA4 FUN_CODE_0ca4
0x0DBC FUN_CODE_0dbc
0x0EEA FUN_CODE_0eea
0x0FC7 FUN_CODE_0fc7
0x0FFE FUN_CODE_0ffe
0x1000 FUN_CODE_1000
0x10D9 FUN_CODE_10d9
0x11AB FUN_CODE_11ab
0x1405 FUN_CODE_1405
0x14B9 FUN_CODE_14b9
0x1500 thunk_FUN_CODE_2252
0x15B8 FUN_CODE_15b8
0x170D main_entry
0x1799 FUN_CODE_1799
0x1800 FUN_CODE_1800
0x19ED FUN_CODE_19ed
0x1A5D FUN_CODE_1a5d
0x1AC6 FUN_CODE_1ac6
0x1B2A FUN_CODE_1b2a
0x1B88 FUN_CODE_1b88
0x1BE6 FUN_CODE_1be6
0x1C44 FUN_CODE_1c44
0x1CA0 FUN_CODE_1ca0
0x1CF6 FUN_CODE_1cf6
0x1D4B FUN_CODE_1d4b
0x1DED FUN_CODE_1ded
0x1E3C FUN_CODE_1e3c
0x1E88 FUN_CODE_1e88
0x1F76 FUN_CODE_1f76
0x1FE2 FUN_CODE_1fe2
0x2031 FUN_CODE_2031
0x2060 FUN_CODE_2060
0x208D FUN_CODE_208d
0x20B9 FUN_CODE_20b9
0x20E5 FUN_CODE_20e5
0x2110 FUN_CODE_2110
0x213B FUN_CODE_213b
0x2164 FUN_CODE_2164
0x2189 FUN_CODE_2189
0x219D FUN_CODE_219d
0x21D1 FUN_CODE_21d1
0x21EC FUN_CODE_21ec
0x2206 FUN_CODE_2206
0x2239 FUN_CODE_2239
0x2252 FUN_CODE_2252
0x2282 FUN_CODE_2282
0x2299 FUN_CODE_2299
0x22B0 FUN_CODE_22b0
0x22F3 FUN_CODE_22f3
0x2309 FUN_CODE_2309
0x2331 FUN_CODE_2331
0x2344 FUN_CODE_2344
0x2357 FUN_CODE_2357
0x2369 FUN_CODE_2369
0x237B FUN_CODE_237b
0x238C FUN_CODE_238c
0x23AE FUN_CODE_23ae
0x23D0 FUN_CODE_23d0
0x23EE FUN_CODE_23ee
0x23F7 FUN_CODE_23f7
0x2409 FUN_CODE_2409
0x2411 FUN_CODE_2411
0x2419 FUN_CODE_2419
0x2421 FUN_CODE_2421
0x2429 FUN_CODE_2429
0x243D FUN_CODE_243d
0x2441 FUN_CODE_2441
0x2443 FUN_CODE_2443
0x2445 FUN_CODE_2445
0x2447 FUN_CODE_2447
0x2449 FUN_CODE_2449

3. Functions Unique to Rev.2

By analyzing functional equivalence (matching by decompiled behavior, not address), the following ~40 functions exist in Rev.2 but have no direct counterpart in v2.13:

3.1 INT0 ISR Split (2 functions)

Rev.2 splits its INT0 into two named functions that Ghidra recognizes separately:

Rev.2 Purpose v2.13 Equivalent
INT0_ISR (0x0003) Bit check + branch to 0x000F or 0x0016 v2.13 has INT0_vector -- completely different (demod polling)
INT0_ISR_bit_clear (0x000F) The CPUCS-only path of INT0 No equivalent -- v2.13 moved this to FUN_CODE_2031

3.2 Vector Stubs and Duplicate Handlers (5 functions)

Rev.2 Purpose Why Unique
FUN_CODE_004e (0x004E) Empty RET at unused vector v2.06 doesn't have this vector defined
FUN_CODE_0051 (0x0051) Empty RET at unused vector Same as above
GPIF_INT4_INT6_handler (0x2084) Duplicate INT4/INT6 handler v2.13 doesn't duplicate this handler
USB_GPIF_handler (0x223F) Duplicate INT2 handler v2.13 doesn't duplicate
FUN_CODE_1ffd (0x1FFD) Empty stub Placeholder not present in other versions

3.3 I2C Granular Primitives (12 functions)

Rev.2 decomposes I2C operations into many small functions that v2.06 inlines and v2.13 reorganizes:

Rev.2 Size Purpose
i2c_exchange_byte (0x0036) 5 Single byte I2C exchange
I2C_ISR (0x003B) 8 I2C interrupt handler wrapper
FUN_CODE_1af5 (0x1AF5) 9 I2C address write helper
FUN_CODE_1afe (0x1AFE) 3 Register move micro-helper
FUN_CODE_1b01 (0x1B01) 67 I2C byte-level transfer
FUN_CODE_1b44 (0x1B44) 76 I2C ACK/NAK handling
FUN_CODE_1b90 (0x1B90) 74 I2C bus reset / error recovery
FUN_CODE_2206 (0x2206) 15 I2C start condition (0xE678=0x80)
FUN_CODE_2224 (0x2224) 9 I2C write (data only, no start)
FUN_CODE_217a (0x217A) 19 I2C start + address combined
FUN_CODE_21e4 (0x21E4) 34 I2C write with start condition
FUN_CODE_206b (0x206B) 25 I2C single-byte read wrapper

3.4 I2C Bus Wait/Polling Variants (4 functions)

Rev.2 has multiple wait functions that poll different combinations of I2C status flags:

Rev.2 Polls Purpose
FUN_CODE_1e73 (0x1E73) regs 0xA2, 0xA8, 0xA4 3-register bus wait with 0x0A00 timeout
FUN_CODE_1ea9 (0x1EA9) regs 0xA8, 0xA2 2-register bus wait variant
FUN_CODE_1f06 (0x1F06) 0xE678 bit 0,2,1 I2C completion with 3-flag check
FUN_CODE_1f85 (0x1F85) 0xE678 bit 0,2 I2C completion with 2-flag check

In v2.06 and v2.13, these are either inlined or consolidated into fewer functions.

3.5 GPIO Control and DiSEqC Helpers (6 functions)

Rev.2 Purpose
FUN_CODE_1fcf (0x1FCF) GPIO pin controller -- P0.6, P0.0, P3.4 from param bits
FUN_CODE_2038 (0x2038) GPIO clock strobe (setup/clock/cleanup)
FUN_CODE_21b1 (0x21B1) LNB voltage select (P0.4 toggle)
FUN_CODE_21c2 (0x21C2) 22kHz tone enable (P0.3 toggle)
FUN_CODE_21d3 (0x21D3) DiSEqC port direction (P3.6 toggle)
FUN_CODE_20f9 (0x20F9) GPIO mode switch (configures P0, P3, IPL1)

3.6 Demod Scan and Version Check (3 functions)

Rev.2 Purpose
FUN_CODE_1dd0 (0x1DD0) Demod scan -- tries 3 I2C addresses
FUN_CODE_1f31 (0x1F31) Descriptor version checker (walks chain for byte==0x03)
FUN_CODE_1d5e (0x1D5E) DiSEqC message sender with retry

3.7 Math Library Functions (3 functions)

Rev.2 Purpose
FUN_CODE_06e3 (0x06E3) 16-bit division with remainder
FUN_CODE_0738 (0x0738) 32-bit multiplication
FUN_CODE_0787 (0x0787) 32-bit subtraction with borrow

3.8 Miscellaneous Unique Functions (5 functions)

Rev.2 Purpose
FUN_CODE_1fbe (0x1FBE) Microsecond-level busy-wait loop (~600 CPU cycles)
FUN_CODE_2257 (0x2257) DiSEqC status read helper (I2C read 0x7F, 0xF9)
FUN_CODE_2265 (0x2265) Empty stub
FUN_CODE_2267 (0x2267) Empty stub
FUN_CODE_2269 (0x2269) Empty stub

4. INT0 ISR Deep Analysis

4.1 Rev.2 INT0 (0x0003-0x0031): USB Re-enumeration

Rev.2's INT0 is a 47-byte inline ISR that spans from address 0x0003 to 0x0031, consuming the INT0 vector slot plus the Timer0, INT1, Timer1, and UART0 vector slots (0x000B, 0x0013, 0x001B, 0x0023).

Disassembly:

CODE:0003  JNB  0x04, 0x000F      ; If bit 0x04 (byte0.4) == 0, skip
CODE:0006  MOV  DPTR, #0xE680     ; CPUCS register
CODE:0009  MOVX A, @DPTR
CODE:000A  ORL  A, #0x0A          ; Set bits 3+1 (re-enumerate + 48MHz)
CODE:000C  MOVX @DPTR, A
CODE:000D  SJMP 0x0016            ; Skip the alternate path
CODE:000F  MOV  DPTR, #0xE680     ; CPUCS register (alternate path)
CODE:0012  MOVX A, @DPTR
CODE:0013  ORL  A, #0x08          ; Set bit 3 only (re-enumerate, keep clock)
CODE:0015  MOVX @DPTR, A
CODE:0016  MOV  R7, #0xDC         ; Delay parameter low = 220
CODE:0018  MOV  R6, #0x05         ; Delay parameter high = 5
CODE:001A  LCALL 0x1BDA           ; delay_routine(5, 0xDC)
CODE:001D  MOV  DPTR, #0xE65D     ; EPIRQ register
CODE:0020  MOV  A, #0xFF
CODE:0022  MOVX @DPTR, A          ; Clear all endpoint IRQs
CODE:0023  MOV  DPTR, #0xE65F     ; USBIRQ register
CODE:0026  MOVX @DPTR, A          ; Clear all USB IRQs
CODE:0027  ANL  0x91, #0xEF       ; Clear EXIF.4 (USB interrupt pending)
CODE:002A  MOV  DPTR, #0xE680     ; CPUCS register
CODE:002D  MOVX A, @DPTR
CODE:002E  ANL  A, #0xF7          ; Clear bit 3 (end re-enumeration)
CODE:0030  MOVX @DPTR, A
CODE:0031  RET

Decompiled C equivalent:

void INT0_ISR(void) {
    if (_0_4 == 0) {
        CPUCS |= 0x08;           // Re-enumerate only
    } else {
        CPUCS |= 0x0A;           // Re-enumerate + set CPUCS.1
    }
    delay_routine(5, 0xDC);      // ~1500 Timer2 ticks
    EPIRQ  = 0xFF;               // Clear all endpoint interrupts
    USBIRQ = 0xFF;               // Clear all USB interrupts
    EXIF  &= 0xEF;               // Clear external interrupt flag
    CPUCS &= 0xF7;               // Clear re-enumerate bit
}

4.2 Comparison: v2.06 INT0 (0x0003)

v2.06's INT0 is functionally identical to Rev.2's. The only differences:

  • Checks bit _0_7 instead of _0_4 (different bit allocation in byte 0)
  • Calls FUN_CODE_1dfb for delay (Rev.2 calls delay_routine at 0x1BDA)
  • Same 47-byte inline ISR spanning the same vector slots

Disassembly comparison:

v2.06:  JNB  0x07, 0x000F    ; bit 7 of byte 0
Rev.2:  JNB  0x04, 0x000F    ; bit 4 of byte 0

All other instructions are byte-identical except for the delay function address (v2.06: 0x1DFB, Rev.2: 0x1BDA).

4.3 Comparison: v2.13 INT0 (0x0003)

v2.13 completely replaces INT0's purpose. Instead of USB re-enumeration, it performs demodulator availability polling:

void INT0_vector(void) {
    for (DAT_INTMEM_37 = 0x28; DAT_INTMEM_37 != 0; DAT_INTMEM_37--) {
        result = FUN_CODE_2239(0x7F);      // I2C read from demod at 0x7F
        if (result != 0x01) {
            result = FUN_CODE_2239(0x3F);  // Try alternate address 0x3F
            if (result != 0x01) break;
        }
    }
    _1_4 = (DAT_INTMEM_37 == 0);  // Flag if no demod found
}

v2.13 moved the USB re-enumeration logic to FUN_CODE_2031, called as a normal function before the main loop. This freed INT0 for periodic demodulator health checks.

4.4 Key Insight

Rev.2 represents the transitional state: it still uses INT0 for USB re-enumeration (like v2.06) but has already restructured the rest of the codebase in ways that v2.13 would inherit. The INT0 repurposing was the last major architectural change between Rev.2 and v2.13.


5. Vendor Command Coverage

5.1 Rev.2 Jump Table (27 commands: 0x80-0x9A)

The vendor command dispatcher at 0x0056 performs: ADD A, #0x80 followed by CJNE A, #0x1B. This means the valid range is 0x80-0x9A (27 commands, range check < 0x1B).

Jump table at CODE:0076 (54 bytes = 27 entries x 2 bytes):

bRequest Jump Target Function Purpose
0x80 0x01AC FUN_CODE_01ac GET_8PSK_CONFIG
0x81 0x0315 vendor_cmd_stall STALL (not implemented)
0x82 0x0315 vendor_cmd_stall STALL (not implemented)
0x83 0x01DB FUN_CODE_01db* I2C_WRITE
0x84 0x01EC FUN_CODE_01ec* I2C_READ
0x85 0x01FA FUN_CODE_01fa* ARM_TRANSFER
0x86 0x2118 FUN_CODE_2118* TUNE_8PSK
0x87 0x214C FUN_CODE_214c* GET_SIGNAL_STRENGTH
0x88 0x0315 vendor_cmd_stall STALL (BCM4500 load via other mechanism)
0x89 0x01BE FUN_CODE_01be* BOOT_8PSK
0x8A 0x21A8 FUN_CODE_21a8* START_INTERSIL
0x8B 0x21D7 FUN_CODE_21d7* SET_LNB_VOLTAGE
0x8C 0x21E9 FUN_CODE_21e9* SET_22KHZ_TONE
0x8D 0x21FB FUN_CODE_21fb* SEND_DISEQC_COMMAND
0x8E 0x0315 vendor_cmd_stall STALL (SET_DVB_MODE)
0x8F 0x0108 FUN_CODE_0108* Unknown read-back
0x90 0x0117 FUN_CODE_0117* GET_SIGNAL_LOCK
0x91 0x0138 FUN_CODE_0138* I2C read-back
0x92 0x0156 FUN_CODE_0156* I2C read-back
0x93 0x017D FUN_CODE_017d* GET_SERIAL_NUMBER
0x94 0x21C5 FUN_CODE_21c5* LNB-related
0x95 0x21ED FUN_CODE_21ed* Read-back function
0x96 0x21C2 FUN_CODE_21c2 22kHz tone control
0x97 0x21CF FUN_CODE_21cf* Similar handler
0x98 0x21D9 FUN_CODE_21d9* Similar handler
0x99 0x0101 FUN_CODE_0101* Partial implementation
0x9A 0x212A FUN_CODE_212a* Partial implementation

Note: Addresses marked with * are computed from the jump table bytes and represent targets within or called by vendor_cmd_dispatch. Some may be inline code within the FUN_CODE_0319 mega-function.

5.2 Missing Commands vs v2.06/v2.13

v2.06 and v2.13 both support 30 commands (0x80-0x9D, range check < 0x1E). Rev.2 supports only 27 (0x80-0x9A, range check < 0x1B).

The 3 missing commands are:

bRequest v2.06 v2.13 Rev.2
0x9B STALL STALL Missing (out of range)
0x9C STALL DELAY_COMMAND (new) Missing
0x9D SET_MODE_FLAG SET_MODE_FLAG (enhanced) Missing

Why they're missing:

  1. 0x9B (reserved/STALL): Simply a placeholder in v2.06/v2.13. Rev.2 didn't need it since 0x9A was the highest command.

  2. 0x9C (DELAY_COMMAND): This was added in v2.13 to allow host-controlled tuning acquisition delays. Rev.2's tuning logic handles delays internally without host control, so this command wasn't needed yet.

  3. 0x9D (SET_MODE_FLAG): This is the most significant absence. In v2.06, it handles hardware revision-aware mode switching. In v2.13, it triggers conditional demodulator resets. Rev.2 handles these functions through different code paths -- the FUN_CODE_0800 configuration dispatcher has embedded logic for mode switching that later versions extracted into a separate vendor command.

5.3 Commands Present but Differently Implemented

0x99 and 0x9A exist in Rev.2 but appear to point to different targets than v2.13:

  • Rev.2 0x99 -> 0x0101 (within the vendor dispatch region, likely a minimal read-back)
  • Rev.2 0x9A -> 0x212A (in the utility function region)
  • v2.13 0x99 -> 0x0317 (GET_DEMOD_STATUS, reads I2C reg 0xF9)
  • v2.13 0x9A -> 0x0140 (INIT_DEMOD, 3-attempt init sequence)

Rev.2's implementations of 0x99 and 0x9A are proto-versions of what v2.13 later fleshed out. They exist but with different (likely simpler) behavior.


6. Key Function Comparisons

6.1 Main Entry

Aspect Rev.2 (0x155F) v2.06 (0x188D) v2.13 (0x170D)
SP value 0x4F 0x72 0x50
IRAM clear 0x00-0x7F 0x00-0x7F 0x00-0x7F
Init table addr CODE:0B48 CODE:0B46 CODE:0B88
Architecture 2-stage (main -> main_init -> FUN_CODE_09a9) 1-stage (main processes init table inline, jumps to FUN_CODE_09a7) 1-stage (main_entry processes init table inline, jumps to FUN_CODE_0800)

Key difference: Rev.2 separates the RESET entry (main) from the init table processor (main_init), making them two distinct functions. v2.06 and v2.13 do both inline in a single function. This is one source of Rev.2's higher function count.

The init table processor is identical in algorithm across all three: it reads a compressed data stream from CODE space and writes initialization values to IRAM, XRAM, and SFR spaces. The table format uses a packed header byte where:

  • Bits 7:6 select the target address space
  • Bits 5:0 encode the data length
  • Multi-page transfers use an additional page count byte

6.2 Main Loop

All three versions have structurally identical main loops:

// Common main loop structure (all versions)
while (true) {
    // Inner poll: check USB endpoints
    do {
        poll_usb();                    // Rev.2: FUN_CODE_201e
        if (vendor_request_pending) {
            handle_vendor_request();   // Rev.2: FUN_CODE_0319
            clear_flag();
        }
    } while (!data_ready);

    // Process data
    clear_data_flag();
    while (check_ep2_status()) {
        if (no_more_data) break;
    }
    reset_endpoints();
    sync_gpif();
}

Differences in main loop functions called:

Role Rev.2 v2.06 v2.13
USB poll FUN_CODE_201e FUN_CODE_2297 FUN_CODE_21ec
Vendor handler FUN_CODE_0319 FUN_CODE_032a FUN_CODE_034e
Data ready flag FUN_CODE_2265 FUN_CODE_24da FUN_CODE_2445
EP2 check FUN_CODE_1faa FUN_CODE_21ed FUN_CODE_2189
EP reset FUN_CODE_1eda FUN_CODE_211d FUN_CODE_20b9
GPIF sync FUN_CODE_2267 FUN_CODE_24dc FUN_CODE_2447

6.3 USB Descriptor Setup (FUN_CODE_10d9)

Rev.2's descriptor setup matches the common pattern:

void usb_descriptor_setup(void) {
    USBCS &= 0xFD;          // Clear disconnect
    _0_0 = 0; _0_2 = 1;     // Init flags
    IFCONFIG = 0x10;          // Internal clock, 48MHz
    REVCTL = 0xCA;
    GPIFIDLECTL = 0xFF;
    PORTACFG = 0x07;

    // I2C init
    FUN_CODE_19f4(0);         // I2C controller init

    // GPIO configuration
    P0 = 0x84;               // P0.7=1, P0.2=1
    IPL1 = 0x9E;
    P3 = 0xE1;

    // Timer2 for DiSEqC timing
    T2CON = 0x04;             // Auto-reload, running
    RCAP2H = 0xF8;
    RCAP2L = 0x2F;            // 500us tick period

    // LNB and demod init
    FUN_CODE_1f5c();          // LNB voltage I2C select
    FUN_CODE_12b8();          // Configuration structure init
    FUN_CODE_0d7c();          // GPIF/slave FIFO config
    FUN_CODE_21b1();          // LNB voltage select (P0.4)
    FUN_CODE_21c2();          // 22kHz tone enable (P0.3)
}

Compared to v2.06: Nearly identical, but Rev.2 uses P0=0x84 where v2.06 uses different pin assignments (reflecting different PCB layout).

Compared to v2.13: v2.13 adds the INT0_vector() demod probe call during setup. Rev.2 does not probe the demodulator during init -- it assumes the hardware is present (like v2.06).


7. Function Address Shift Patterns

7.1 Code Organization Comparison

The firmware binary is organized into logical regions. Comparing equivalent functions across versions reveals systematic address shifts:

Region Rev.2 Range v2.06 Range v2.13 Range Shift Pattern
Vectors + dispatch 0x0000-0x0318 0x0003-0x032A 0x0000-0x034E Rev.2 slightly shorter
Math library 0x067E-0x07FE 0x06C7-0x07FE 0x06D9-0x079D Rev.2 earliest, all similar size
Config dispatcher 0x0800-0x09A8 0x09A7 (single func) 0x0800-0x0CA4 v2.06 has no 0x0800 dispatcher
BCM4500/demod 0x0C64-0x0F00 0x0DDD 0x0CA4-0x0FFE Rev.2 earlier due to shorter dispatch
USB endpoints 0x1000-0x1420 0x10F2-0x1556 0x1000-0x15B8 Consistent ~0x100 shift
Main entry 0x155F-0x15EB 0x188D-0x1919 0x170D-0x1800 Rev.2 earliest (smaller code)
I2C + DiSEqC 0x1670-0x1E3D 0x16B8-0x1DFB 0x1800-0x1E88 Rev.2 similar to v2.06
Delay + helpers 0x1BDA-0x2038 0x1DFB-0x2000 0x14B9-0x2060 v2.13 moved delay earlier
Tail functions 0x2038-0x2269 0x2000-0x24DC 0x2031-0x2449 v2.06/v2.13 extend further

7.2 Why Rev.2 Has Code at Lower Addresses

Rev.2's code starts earlier in several regions because:

  1. No init table processor in main(): Rev.2 splits main() into a 6-instruction stub (10 bytes) + separate main_init(). v2.06/v2.13 inline the init table processor (92 instructions, ~130 bytes) into main(), pushing subsequent code later.

  2. Shorter vendor dispatch: Rev.2 handles 27 commands (54-byte jump table) vs 30 commands (60-byte table), saving 6 bytes plus the handling code for 3 missing commands.

  3. More compact I2C layer: Rev.2's granular I2C functions are individually small but collectively pack tighter in the binary than v2.06's larger monolithic I2C functions.

7.3 Why Rev.2 Ends at 0x2269 (Smaller Total)

Despite having 107 functions, Rev.2's code ends at 0x226B (total ~8.7 KB) while:

  • v2.06 ends at 0x24DD (total ~9.4 KB)
  • v2.13 ends at 0x244B (total ~9.3 KB)

Rev.2 is the smallest binary despite having the most functions. This is because:

  • Many functions are tiny (2-8 bytes) -- empty stubs, simple register moves
  • The granular decomposition creates more function entry points but reuses code through calls rather than duplication
  • Missing vendor commands 0x9B-0x9D save ~100-200 bytes of handler code

8. Why Rev.2 Has 107 Functions

The 107 function count is driven by several factors:

8.1 Granular Function Decomposition (+25-30 vs v2.06)

Rev.2 breaks operations into many small callable units. Where v2.06 has a single I2C transfer function with inlined bus control, Rev.2 has:

  • i2c_exchange_byte (5 bytes)
  • I2C_ISR (8 bytes)
  • FUN_CODE_1af5 (9 bytes) -- address write
  • FUN_CODE_1afe (3 bytes) -- register move
  • FUN_CODE_1b01 (67 bytes) -- byte transfer
  • FUN_CODE_1b44 (76 bytes) -- ACK/NAK
  • FUN_CODE_1b90 (74 bytes) -- bus reset

That's 7 functions for what v2.06 handles in 2-3 functions.

8.2 Duplicate Interrupt Handlers (+3)

Rev.2 has INT4_FX2_vector, INT6_FX2_vector, GPIF_INT4_INT6_handler, AND INT4_INT6_handler -- four functions for the same interrupt behavior. v2.06 and v2.13 consolidate these.

8.3 Empty Stubs and Placeholders (+5-6)

Rev.2 defines explicit empty functions at:

  • FUN_CODE_004e, FUN_CODE_0051 (vector slots)
  • FUN_CODE_07fe, FUN_CODE_17fd, FUN_CODE_1ffd (call targets)
  • FUN_CODE_2265, FUN_CODE_2267, FUN_CODE_2269 (tail stubs)

These are RET-only functions that serve as placeholders for features not yet implemented or intentionally disabled. v2.06 doesn't define explicit functions at these addresses.

8.4 INT0 Split (+1)

Ghidra recognizes two functions (INT0_ISR and INT0_ISR_bit_clear) for what is logically one handler, because the branch at 0x0003 creates two entry points.

8.5 The FUN_CODE_0800 Monster (+10-15 internal entry points)

The 874-byte FUN_CODE_0800 configuration dispatcher is the most complex function in any firmware version. It contains a 128-entry switch statement that handles different demodulator types and signal modulations. Ghidra's analysis identifies multiple internal entry points within this function as separate functions, inflating the count. This function also contains an embedded copy of the main loop (the while(true) loop at the bottom), which Ghidra may count additional entry points for.

8.6 Summary Count

Factor Additional Functions
Granular I2C primitives +12
I2C wait/polling variants +4
GPIO control helpers +6
Duplicate interrupt handlers +3
Empty stubs/placeholders +8
INT0 split +1
Demod scan/version check +3
Math library (separate) +3
Config dispatcher internal entries +5
Total unique additions ~45

Starting from v2.06's 61 functions, adding ~45 unique functions brings us close to 107. The remaining difference comes from Rev.2 having explicit function definitions where v2.06 has inline code or code that Ghidra doesn't recognize as separate functions.


9. Rev.2's Position in the Firmware Timeline

Rev.2 v2.10.4 is an intermediate development version that bridges v2.06 and v2.13:

Feature v2.06 Rev.2 v2.10.4 v2.13 FW1
INT0 purpose USB re-enum USB re-enum Demod polling
SP value 0x72 0x4F 0x50
Init table 0x0B46 0x0B48 0x0B88
Descriptor base 0x1200 0x0E00 0x0E00
Vendor commands 30 (0x80-0x9D) 27 (0x80-0x9A) 30 (0x80-0x9D)
DiSEqC data pin P0.7 P0.4 P0.0
Demod probe at init No No Yes
Retry loops No No Yes (20-attempt)
HW revision check No Yes (FUN_CODE_1f31) Yes (_1_3 flag)
Function count 61 107 88
Binary size ~9.4 KB ~8.7 KB ~9.3 KB
Config dispatcher No 0x0800 874-byte FUN_CODE_0800 Yes (different)

Rev.2 adopted the lower stack pointer (0x4F vs v2.06's 0x72), the descriptor base at 0x0E00, and the DiSEqC data pin at P0.4 -- changes that stuck through v2.13 (with minor adjustments). However, it retained v2.06's INT0 USB re-enumeration behavior and lacked v2.13's demodulator polling, retry loops, and 3 additional vendor commands.

The high function count reflects an engineering style favoring small, reusable functions over inline code -- a style that was partially consolidated back in v2.13 when the firmware was cleaned up for release.