Apply .gitattributes normalization to convert all CRLF line endings inherited from Windows-origin source files to Unix LF. 175 files, zero content changes.
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:
- 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.
- 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. - 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_7instead of_0_4(different bit allocation in byte 0) - Calls
FUN_CODE_1dfbfor delay (Rev.2 callsdelay_routineat 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:
-
0x9B (reserved/STALL): Simply a placeholder in v2.06/v2.13. Rev.2 didn't need it since 0x9A was the highest command.
-
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.
-
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_0800configuration 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:
-
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.
-
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.
-
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 writeFUN_CODE_1afe(3 bytes) -- register moveFUN_CODE_1b01(67 bytes) -- byte transferFUN_CODE_1b44(76 bytes) -- ACK/NAKFUN_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.