skywalker-1/firmware-dump/STARTUP_DISASSEMBLY.md
Ryan Malloy 97c1000d8b Add stock firmware dump, 8051 disassembler, and analysis notes
- stock_firmware.bin: 15KB dump from working device (v2.13)
- disasm8051.py / v2: Custom 8051 disassemblers for FX2LP firmware
  analysis, used to trace init block loading and I2C sequences
- STARTUP_DISASSEMBLY.md: Annotated startup sequence disassembly
- TODO: Notes on stock vs custom firmware BCM4500 init differences
2026-02-20 10:56:59 -07:00

23 KiB

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)