# Genpix SkyWalker-1 Firmware Startup Disassembly **Binary**: `skywalker1_eeprom_full64k.bin` (65536 bytes, RAM image after EEPROM boot) **CPU**: FX2LP (CY7C68013A) — 8051 core @ 48MHz **Firmware region**: 0x0000-0x24FF (9472 bytes) **Compiler**: Keil C51 **USB**: VID=0x09C0 (Cypress), PID=0x0203 --- ## Phase 1: Reset Vector (0x0000) ``` 0000: 02 18 8D LJMP 0x188D ; -> Keil C51 startup ``` ## Phase 2: Keil C51 Startup (0x188D) ### IDATA Clear ``` 188D: 78 7F MOV R0,#7Fh ; Start at IDATA address 0x7F 188F: E4 CLR A ; A = 0 1890: F6 MOV @R0,A ; Clear IDATA[R0] 1891: D8 FD DJNZ R0,1890h ; Decrement R0, loop until 0 ; Clears IDATA 0x7F..0x01 (127 bytes) ``` ### Stack Pointer ``` 1893: 75 81 72 MOV SP,#72h ; Stack pointer = 0x72 ; Stack grows up: 0x73..0x7F = 13 bytes ``` ### XDATA Initialization ``` 1896: 02 18 D4 LJMP 18D4h ; Jump to init table interpreter ; Init table interpreter at 0x18D4 (Keil standard STARTUP.A51): ; Reads structured init records from CODE space ; Each record has: ; - Length byte (bits 5:0 = count, bits 7:6 = mode) ; - Mode 00: IDATA/XDATA fill (address + data pairs) ; - Mode 01: Bit initialization ; - Mode 10: XDATA block copy (source addr + destination + data) ; - Zero byte terminates the table ; ; When table exhausted: 1899: 02 09 A7 LJMP 09A7h ; -> main() ``` ## Phase 3: main() (0x09A7) ### Clear Global Variables ``` 09A7: E4 CLR A ; A = 0 09A8: F5 2D MOV 2Dh,A ; Clear counter/state variables 09AA: F5 2C MOV 2Ch,A ; 4-byte counter at 0x2A-0x2D 09AC: F5 2B MOV 2Bh,A 09AE: F5 2A MOV 2Ah,A 09B0: F5 35 MOV 35h,A ; 4-byte counter at 0x32-0x35 09B2: F5 34 MOV 34h,A 09B4: F5 33 MOV 33h,A 09B6: F5 32 MOV 32h,A 09B8: C2 03 CLR bit_03h ; Clear SUSPEND-pending flag 09BA: C2 01 CLR bit_01h ; Clear SETUP-pending flag ``` ### Call hw_init() ``` 09BC: 12 13 C3 LCALL 13C3h ; -> hw_init() ``` ## Phase 4: hw_init() (0x13C3) ### CPU Configuration ``` 13C3: MOV DPTR,#E605h ; REVCTL (FX2LP revision control) 13C6: MOVX A,@DPTR ; Read current value 13C7: ANL A,#FDh ; Clear bit 1 (NOAUTOARM) 13C9: MOVX @DPTR,A ; REVCTL &= ~0x02 13CE: MOV DPTR,#E600h ; CPUCS 13D1: MOVX A,@DPTR 13D2: ANL A,#E5h ; Clear bits 4,3,1 (clock bits) 13D4: ORL A,#10h ; Set bit 4 13D6: MOVX @DPTR,A ; CPUCS = 48MHz clock mode ; NOP x3 (sync delay) ``` ### Interface Configuration ``` 13DA: MOV DPTR,#E601h ; IFCONFIG 13DD: MOV A,#CAh ; 0xCA = 1100_1010: ; IFCLKSRC=1 (internal) ; 3048MHZ=1 (48MHz) ; IFCLKOE=0 (don't output IFCLK) ; IFCLKPOL=0 ; ASYNC=1 (async GPIF) ; GSTATE=0 ; IFCFG=10 (GPIF mode) 13DF: MOVX @DPTR,A ``` ### GPIF Waveform Init ``` 13E3: MOV DPTR,#E6F5h ; Undocumented register 13E6: MOV A,#FFh 13E8: MOVX @DPTR,A 13E9: MOV 0xAF,#07h ; Unknown SFR (FX2-specific) 13F1: LCALL 12EAh ; GPIF waveform configuration: ; Reads waveform data from XDATA 0xE000-0xE08E ; Copies 128 bytes to GPIF waveform RAM via autopointers ; Configures: GPIFCTLCFG, GPIFIDLECS, GPIFIDLECTL, ; GPIFWFSELECT, GPIFADR, CTL states ; PORTCCFG=0xFF (all alt function for GPIF) ; PORTECFG.7=1 (GPIF ready signal) ``` ### State Initialization ``` 13F4: CLR A 13F5: MOV 6Dh,A ; Status flags byte = 0 ; bit 0: init in progress ; bit 1: 8PSK firmware loaded ; bit 3: signature check mode ; bit 6: GPIF configured ; bit 7: streaming active 13F7: CLR bit_05h 13F9: CLR bit_04h 13FB: MOV 68h,A ; Clear more state 13FD: MOV 69h,A 13FF: MOV 66h,A 1401: CLR bit_06h ; GPIF-configured flag = 0 ``` ### GPIO Configuration ``` 1403: MOV DPTR,#E670h ; PORTACFG 1406: MOVX @DPTR,A ; = 0x00 (all GPIO, no alt functions) 1407: MOV IOA,#A4h ; Port A initial: 1010_0100 ; PA7=1 (GPIF/transport select?) ; PA5=1 (8PSK_RESET deasserted) ; PA2=1 (LNB voltage select?) ; PA1=0, PA0=input 140A: MOV OEA,#FEh ; Port A direction: 1111_1110 ; PA7-PA1 = output ; PA0 = input (lock detect?) 140D: MOV IOD,#F0h ; Port D initial: 1111_0000 ; PD7=1 (8PSK bus D7?) ; PD6=1 (8PSK bus D6?) ; PD5=1 (8PSK bus D5?) ; PD4=1 (8PSK bus D4?) 1410: MOV OED,#F0h ; Port D direction: upper 4 output ``` ### FIFO Reset Sequence ``` 141C: MOV DPTR,#E604h ; FIFORESET 141F: MOV A,#80h ; NAK-ALL = 1 (hold off USB during reset) 1421: MOVX @DPTR,A 1425: MOV A,#02h ; Reset EP2 1427: MOVX @DPTR,A 142B: MOV A,#04h ; Reset EP4 142D: MOVX @DPTR,A 1431: MOV A,#06h ; Reset EP6 1433: MOVX @DPTR,A 1437: MOV A,#08h ; Reset EP8 1439: MOVX @DPTR,A 143D: CLR A ; NAK-ALL = 0 (release) 143E: MOVX @DPTR,A ``` ### GPIF Timing ``` 1442: MOV DPTR,#E618h ; GPIFHOLDAMOUNT 1445: MOV A,#0Ch ; 12 IFCLK cycles hold 1447: MOVX @DPTR,A ; E619-E61B (FLOWSTATE regs) = 0x00 ``` ### I2C Configuration ``` 1461: MOV DPTR,#E67Ah ; I2CTL 1464: MOVX A,@DPTR 1465: ORL A,#01h ; Set bit 0: 400kHz I2C clock 1467: MOVX @DPTR,A ``` ### Timer 2 Setup ``` 1468: ANL CKCON,#DFh ; Clear Timer2 clock source bit 146B: MOV T2CON,#04h ; Timer 2: auto-reload mode 146E: MOV RCAP2H,#F8h ; Reload value = 0xF8xx (fast tick) ``` ## Phase 5: Descriptor Setup (back in main, 0x09BF) ``` 09BF: MOV 0Ch,#12h ; Descriptor table high byte 09C2: MOV 0Dh,#00h ; Descriptor table low byte = 0x1200 ; -> USB Device Descriptor ; Multiple descriptor pointer pairs stored in direct RAM: ; [0C:0D] = 0x1200 Device Descriptor ; [14:15] = 0x1212 Device Qualifier ; [0A:0B] = 0x121C Configuration Descriptor ; [12:13] = 0x1254 Other Speed Config ; [16:17] = 0x128C String Descriptors ; [08:09] = 0x12E8 (high-speed descriptor variant?) ; 09E3-0AE0: Copy all descriptors from CODE to XDATA ; Calculates actual XDATA base address ; Adjusts all pointers for runtime location ; Prepares EP configuration from config descriptor ``` ## Phase 6: USB Enable (0x0AE1) ``` 0AE1: MOV R7,09h / R6,08h ; Pass descriptor base pointer 0AE5: LCALL 1A0Eh ; Configure USB descriptors in FX2 ; Reads XDATA desc, calculates checksums ; Sets up descriptor table for USB core ; Checks descriptor type -> sets bit_06h ; Enable USB interrupts 0AE8: SETB EIE.0 ; Enable INT2 (USB interrupt) 0AEA: ORL EICON,#20h ; INT2 edge trigger 0AED: MOV DPTR,#E668h ; INTSETUP 0AF0: MOVX A,@DPTR 0AF1: ORL A,#09h ; Enable autovectoring for INT2 and INT4 0AF3: MOVX @DPTR,A 0AF4: MOV DPTR,#E65Ch ; USBIE (USB Interrupt Enable) 0AF7: MOVX A,@DPTR 0AF8: ORL A,#3Dh ; Enable: SUDAV|SOF|SUTOK|SUSPEND|USBRESET ; 0x3D = 0011_1101 0AFA: MOVX @DPTR,A 0AFB: SETB EA ; Global interrupt enable (IE.7) ``` ### USB Re-enumeration ``` 0AFD: SETB bit_07h ; Mark "disconnected" state 0AFF: LCALL 0003h ; Call re-enumeration handler: ; Sets USBCS.DISCON=1, USBCS.RENUM=1 ; Delay ~375,000 cycles (~7.8ms @ 48MHz) ; Clears USBIRQ (write 0xFF) ; Clears EPIRQ (write 0xFF) ; Clears EXIF.4 ; Clears USBCS.DISCON (reconnect) ; After re-enum completes: 0B02: MOV DPTR,#E680h ; USBCS 0B05: MOVX A,@DPTR 0B06: ANL A,#F7h ; Clear DISCON bit (bit 3) 0B08: MOVX @DPTR,A ; *** USB device now visible to host *** 0B09: ANL CKCON,#F8h ; Final timer prescaler setup ``` ## Phase 7: Main Loop (0x0B0C) ``` ; ---- MAIN POLLING LOOP ---- 0B0C: LCALL 2297h ; ep1_poll(): ; Check EP1IN ready, arm if needed ; Check FLOW state registers ; Return C=1 if EP1 armed 0B0F: JNB bit_01h,0B17h ; SETUP pending (from SUDAV ISR)? 0B12: LCALL 032Ah ; Yes -> handle_setup() 0B15: CLR bit_01h ; Clear pending flag 0B17: JNB bit_03h,0B0Ch ; SUSPEND pending (from SUSPEND ISR)? 0B1A: LCALL 24DAh ; Check suspend condition 0B1D: JNC 0B0Ch ; False alarm -> continue loop 0B1F: CLR bit_03h 0B21: LCALL 21EDh ; Enter suspend (WAKEUPCS handling) ; ... wakeup recovery code ... 0B3D: LCALL 211Dh ; I2C device polling (demod status?) ; ... loop back to 0B0C ... ``` ## Phase 8: SETUP Packet Handler (0x032A) ### Standard Request Dispatch ``` 032A: MOV DPTR,#E6B9h ; SETUPDAT[1] = bRequest (alt mapping) 032D: MOVX A,@DPTR ; Read bRequest 032E: CJNE A,#0Ch,0331h ; Compare with 12 0331: JC 0335h ; If bRequest < 12 -> standard USB request 0333: AJMP 05ABh ; Else -> vendor request handler ; Standard USB request jump table (bRequest 0-11): 0335: MOV DPTR,#033Bh 0338: ADD A,ACC ; index * 2 (each AJMP is 2 bytes) 033A: JMP @A+DPTR ; Computed jump ; Table at 033B (12 entries): ; [0] GET_STATUS -> 0403h ; [1] CLEAR_FEATURE -> 04A6h ; [2] (reserved) -> 05ABh (stall) ; [3] SET_FEATURE -> 0536h ; [4] (reserved) -> 05ABh ; [5] SET_ADDRESS -> 05ABh (handled by hardware) ; [6] GET_DESCRIPTOR -> 0353h ; [7] (reserved) -> 05ABh ; [8] GET_CONFIGURATION -> 03FEh ; [9] SET_CONFIGURATION -> 03F4h ; [10] GET_INTERFACE -> 03DBh ; [11] SET_INTERFACE -> 03E5h ``` ### Vendor Request Dispatch (0x05AB -> 0x0056) ``` 05AB: LCALL 0056h ; Call vendor request dispatcher 05AE: JC 05B7h ; If handled (C=1) -> skip stall 05B0: MOV DPTR,#E6A0h ; EP0CS 05B3: MOVX A,@DPTR 05B4: ORL A,#01h ; Set STALL bit (unhandled request) 05B6: MOVX @DPTR,A 05B7: MOV DPTR,#E6A0h ; EP0CS 05BA: MOVX A,@DPTR 05BB: ORL A,#80h ; Set HSNAK bit (handshake) 05BD: MOVX @DPTR,A 05BE: RET ``` ### Vendor Dispatch at 0x0056 ``` ; NOTE: E6B8-E6BF maps to SETUPDAT[0]-SETUPDAT[7] (alternate XDATA addresses) ; This is an undocumented but valid FX2LP mapping. 0056: MOV DPTR,#E6B8h ; SETUPDAT[0] = bmRequestType 0059: MOVX A,@DPTR 005A: JB ACC.6,005Fh ; Test bit 6: vendor request flag 005D: CLR C ; Not vendor -> return unhandled 005E: RET 005F: MOV DPTR,#E6B9h ; SETUPDAT[1] = bRequest 0062: MOVX A,@DPTR 0063: ADD A,#80h ; Remap: bRequest 0x80 -> index 0x00 ; bRequest 0x89 -> index 0x09 ; bRequest 0x9D -> index 0x1D 0065: CJNE A,#1Eh,0068h ; Check if index < 30 0068: JC 006Ch ; In range -> use jump table 006A: AJMP 0326h ; Out of range -> return unhandled (C=0) 006C: MOV DPTR,#0076h ; Jump table base 006F: ADD A,ACC ; index * 2 (AJMP = 2 bytes each) 0071: JNC 0075h ; Handle page crossing 0073: INC DPH 0075: JMP @A+DPTR ; Computed jump into vendor table ``` ### Vendor Command Jump Table (0x0076) | bRequest | Index | Target | Function | |----------|-------|--------|----------| | 0x80 | 0 | 00B2h | GET_STATUS — returns status byte `6Dh` via EP0 | | 0x81 | 1 | 0326h | (unhandled) | | 0x82 | 2 | 0326h | (unhandled) | | 0x83 | 3 | 00F1h | EP0_SETUP_READ — sets up descriptor read via SUDPTR | | 0x84 | 4 | 0102h | EP0_DATA_WRITE — host writes data to EP0 | | 0x85 | 5 | 0110h | EP0_DATA_READ — host reads data from EP0 | | 0x86 | 6 | 012Eh | I2C_WRITE — write I2C data block | | 0x87 | 7 | 0140h | EP0_MULTI_READ — multi-byte EP0 read | | 0x88 | 8 | 0326h | (unhandled) | | **0x89** | **9** | **00C4h** | **BOOT_8PSK** — boot the 8PSK demodulator | | 0x8A | 10 | 019Ch | I2C_READ (with status check) | | 0x8B | 11 | 01CBh | I2C_WRITE (variant) | | 0x8C | 12 | 01DDh | I2C_READ (variant) | | 0x8D | 13 | 01EFh | DiSEqC / LNB control? | | 0x8E | 14 | 0326h | (unhandled) | | 0x8F | 15 | 01FCh | GPIF flow control / read register | | 0x90 | 16 | 020Bh | TUNE — set frequency / symbol rate | | 0x91 | 17 | 022Ch | TUNE_STATUS — check lock | | 0x92 | 18 | 024Ah | SET_TRANSPORT — configure TS stream | | 0x93 | 19 | 026Fh | GET_SIGNAL_QUALITY — read SNR/BER | | 0x94 | 20 | 01B9h | I2C bus control | | 0x95 | 21 | 02DFh | GET_DEMOD_STATUS | | 0x96 | 22 | 02B4h | I2C_ADDR_SELECT | | 0x97 | 23 | 02C1h | RAW_REGISTER_READ | | 0x98 | 24 | 02CBh | GET_EP0_BUFFER | | 0x99-9C | 25-28 | 0326h | (unhandled) | | 0x9D | 29 | 02FAh | FIRMWARE_VERSION? | ## BOOT_8PSK (0x89) Handler: Complete Trace ### Entry Point (0x00C4) ``` 00C4: JNB bit_06h,00DAh ; Is GPIF configured? ; [If GPIF configured]: 00C7: MOV DPTR,#E6BAh ; SETUPDAT[2] = wValueL 00CA: MOVX A,@DPTR 00CB: ADD A,#FFh ; Test if wValue == 0 (ADD #FF sets C if A>0) 00CD: MOV bit_07h,C ; bit_07h = (wValue != 0) = "load-from-host" flag 00CF: LCALL 1D4Fh ; -> boot_8psk_dispatch() ; [If NOT configured]: 00DA: CLR bit_07h ; Force "no load" mode 00DC: LCALL 1D4Fh ; -> boot_8psk_dispatch() ; [Common exit]: 00E5: CLR A 00E6: MOV DPTR,#E68Ah ; EP0BCH = 0 00E9: MOVX @DPTR,A 00EA: MOV DPTR,#E68Bh ; EP0BCL = 1 00ED: INC A 00EE: MOVX @DPTR,A ; Send 1-byte response (success/fail in EP0BUF[0]) 00EF: AJMP 0328h ; SETB C; RET (command handled) ``` ### boot_8psk_dispatch (0x1D4F) ``` 1D4F: MOV A,6Dh ; Read status flags 1D51: RRC A ; bit 0 (init flag) -> C -> bit_08h 1D52: MOV bit_08h,C ; bit_08h = "currently booted" state 1D54: ORL IOD,#E0h ; PD7:PD5 = 1 (8PSK bus enable) 1D57: JNB bit_07h,1D93h ; If NOT "load from host" -> error path 1D5A: JB bit_08h,1DA5h ; If already booted -> return success ; --- Signature Check --- 1D5D: LCALL 16B8h ; Check 8PSK demod chip signature: ; Read descriptor type byte from XDATA ; Switch IFCONFIG to port mode (0xC0) ; Set OEB=0 (Port B = input) ; Set PA7=1, PA6=1 (select chip) ; Read Port B: ; Type 3: expect IOB=0xA5 ; Type 4: expect IOB=0x5A ; Type 5: expect IOB=0x5B ; Type 6: expect IOB=0x5C ; Restore IFCONFIG ; Return C=1 if signature match 1D60: JNC 1DA5h ; Signature fail -> return with current state ; --- Begin 8PSK Boot --- 1D62: ANL IOA,#DFh ; PA5=0 (assert 8PSK reset) 1D65: ANL 6Dh,#BFh ; Clear "streaming" flag in 6Dh 1D68: CLR bit_09h ; Clear streaming-active flag 1D6A: LCALL 1919h ; GPIF abort + FIFO cleanup: ; EP2FIFOPFH, GPIFABORT, GPIFCTRL ; Wait for GPIF idle ; Reset EP2 FIFO ; --- Configure for programming --- 1D6D: MOV A,IOA 1D6F: ANL A,#FBh ; PA2=0 1D71: ORL A,#02h ; PA1=1 (select programming mode) 1D73: MOV IOA,A 1D75: MOV R7,#1Eh / R6,#00h 1D79: LCALL 1DFBh ; Delay ~30 * (CPUCS-based divisor) cycles ; --- Deassert reset --- 1D7C: ORL IOA,#20h ; PA5=1 (deassert 8PSK reset) 1D7F: ORL 6Dh,#01h ; Set "init in progress" flag ; --- Load 8PSK firmware --- 1D82: LCALL 10F2h ; load_8psk_firmware(): ; I2C read from demod (addr 0x51) to get segment table ; Loop: read segment header (addr, length) ; I2C write firmware data blocks to demod ; Retry up to 5x on write failures ; Return C=0 on success, C=1 on failure 1D85: JC 1D93h ; If load failed -> error ; --- Boot init blocks --- 1D87: LCALL 0DDDh ; boot_init_blocks(): ; 3 iterations (init blocks 0,1,2) ; Each: wait_for_ready() then I2C write ; Sends register configuration to demod 1D8A: JC 1D93h ; If init failed -> error 1D8C: SETB bit_08h ; Mark "8PSK booted" 1D8E: ORL IOD,#E0h ; PD7:PD5 = 1 (8PSK bus active) 1D91: SJMP 1DA5h ; -> success exit ; --- Error path --- 1D93: ANL 6Dh,#BCh ; Clear bits 0,1,6 in status 1D96: CLR bit_09h ; Clear streaming flag 1D98: LCALL 1919h ; GPIF abort + cleanup 1D9B: MOV A,IOA 1D9D: ANL A,#FDh ; PA1=0 1D9F: ORL A,#04h ; PA2=1 (restore normal GPIO) 1DA1: MOV IOA,A 1DA3: CLR bit_08h ; Mark "not booted" ; --- Return --- 1DA5: MOV C,bit_08h ; Return C = boot success 1DA7: RET ``` ## USB Interrupt Vector Table (0x1600) The FX2LP USB autovector table at 0x1600. Each entry is a 3-byte LJMP + 1 NOP (4 bytes per slot). The hardware indexes into this table via INT2IVEC. | Offset | IRQ Source | Target | Status | |--------|-----------------|--------|--------| | +0x00 | SUDAV | 22E4h | **Active** — sets bit_01h, clears USBIRQ | | +0x04 | SOF | 239Fh | **Active** | | +0x08 | SUTOK | 2389h | **Active** | | +0x0C | SUSPEND | 22FCh | **Active** — sets bit_03h | | +0x10 | USBRESET | 1FB6h | **Active** | | +0x14 | HISPEED | 1EC7h | **Active** | | +0x18 | EP0ACK | 0032h | Stub (RETI) | | +0x24 | EP0OUT | 0FFFh | **Active** | | +0x28+ | EP1-8, IBN, etc | 24E0h+ | All stubs | ## USB Descriptors (0x1200) ``` Device Descriptor (18 bytes): bLength: 18 bDescriptorType: 1 (DEVICE) bcdUSB: 02.00 bDeviceClass: 0xFF (vendor-specific) bDeviceSubClass: 0xFF bDeviceProtocol: 0x00 bMaxPacketSize0: 64 idVendor: 0x09C0 (Cypress Semiconductor) idProduct: 0x0203 bcdDevice: 0x0001 iManufacturer: 1 iProduct: 2 iSerialNumber: 3 bNumConfigurations: 1 ``` ## Bit-Addressable Flag Map | Bit | Name/Purpose | |-------|-------------| | bit_00h | Wakeup/resume active | | bit_01h | SETUP packet pending (from SUDAV ISR) | | bit_02h | System initialized | | bit_03h | SUSPEND pending (from SUSPEND ISR) | | bit_04h | (unused/reserved) | | bit_05h | (unused/reserved) | | bit_06h | GPIF descriptor present | | bit_07h | USB disconnect / "load-from-host" flag (dual use) | | bit_08h | 8PSK demod booted successfully | | bit_09h | Streaming active (GPIF/TS transfer in progress) | | bit_0Ah | Autopointer direction flag | ## Direct RAM Variable Map | Address | Name/Purpose | |---------|-------------| | 08h:09h | Descriptor pointer 7 (high-speed variant?) | | 0Ah:0Bh | Configuration descriptor pointer | | 0Ch:0Dh | Device descriptor pointer (0x1200) | | 0Eh:0Fh | (adjusted descriptor offset) | | 10h:11h | (adjusted descriptor offset) | | 12h:13h | Other-speed config descriptor pointer | | 14h:15h | Device qualifier descriptor pointer | | 16h:17h | String descriptor pointer | | 22h-25h | 32-bit counter/offset (XDATA copy) | | 26h-29h | 32-bit counter (descriptor calculation) | | 2Ah-2Dh | 32-bit counter (main state) | | 32h-35h | 32-bit counter (main state) | | 36h:37h | USB descriptor base (passed to 1A0E) | | 38h | Descriptor iteration counter | | 3Bh | Boot init block index / temp | | 3Ch | I2C temp / ready flag | | 3Dh:3Eh | wait_for_ready countdown | | 3Fh:40h | Delay counter / retry counter | | 45h:46h | I2C buffer pointer high:low | | 49h-4Bh | Autopointer save area | | 4Ch:4Dh | Saved IRQ state | | 4Eh-50h | EP buffer config | | 66h | (state variable) | | 68h:69h | (state variables) | | 6Dh | Master status flags byte | | 6Fh:70h | Secondary wait counter | ## Key Observations for RAM-Load Boot Failure 1. **IDATA cleared on reset**: The startup clears IDATA 0x01-0x7F. All bit-addressable flags and direct RAM variables start at 0. This is critical — if you're loading into RAM without resetting the CPU, these won't be cleared. 2. **XDATA init from CODE space**: The Keil startup copies initialization data from a table in CODE space to XDATA. If your RAM load doesn't include this init data at the correct CODE-relative offset, XDATA won't be initialized properly. 3. **CPUCS clock switch**: The firmware switches to 48MHz immediately in hw_init(). If CPUCS is already set (from a previous boot), the second write may cause timing issues. 4. **USB re-enumeration**: The firmware explicitly disconnects (USBCS.DISCON=1), waits ~8ms, then reconnects. If you're loading via USB, this disconnect cycle will kill your USB connection. 5. **E6B8-E6BF SETUPDAT mapping**: The firmware uses an alternate XDATA mapping for SETUPDAT at 0xE6B8 instead of the standard 0xE6A5. Both mappings are valid, but your host-side code must account for this if doing SETUP data inspection. 6. **Interrupt vector overwrite risk**: The reset vector at 0x0000 and all interrupt vectors (0x0003-0x006B) contain actual code, not just jump stubs. The INT0 vector at 0x0003 contains the USB re-enumeration handler. Loading code that overwrites these vectors will break USB. 7. **GPIF waveform data**: The GPIF config at 0x12EA reads waveform data from XDATA 0xE000-0xE08E. This data must be present in XDATA before hw_init() runs. If it's missing (because the EEPROM-to-XDATA copy didn't happen), GPIF will be misconfigured. --- *Generated from firmware analysis of skywalker1_eeprom_full64k.bin* *Disassembly tool: disasm8051.py (custom 8051 disassembler)*