#!/usr/bin/env python3 """8051 disassembler for Genpix SkyWalker-1 firmware analysis. Produces annotated disassembly of the FX2LP (8051-based) firmware, focusing on the Keil C51 startup sequence and USB initialization. """ import sys import re OPCODES = {} def op(code, mnem, length, desc=""): OPCODES[code] = (mnem, length, desc) # NOP op(0x00, "NOP", 1) # AJMP/ACALL (page 0-7) for page in range(8): op(0x01 + page*0x20, f"AJMP {{a11:{page}}}", 2) op(0x11 + page*0x20, f"ACALL {{a11:{page}}}", 2) op(0x02, "LJMP {a16}", 3) op(0x12, "LCALL {a16}", 3) op(0x03, "RR A", 1) op(0x13, "RRC A", 1) op(0x23, "RL A", 1) op(0x33, "RLC A", 1) # INC op(0x04, "INC A", 1) op(0x05, "INC {d}", 2) op(0x06, "INC @R0", 1) op(0x07, "INC @R1", 1) for i in range(8): op(0x08+i, f"INC R{i}", 1) # JBC, JB, JNB, JC, JNC, JZ, JNZ op(0x10, "JBC {bit},{r8}", 3) op(0x20, "JB {bit},{r8}", 3) op(0x30, "JNB {bit},{r8}", 3) op(0x40, "JC {r8}", 2) op(0x50, "JNC {r8}", 2) op(0x60, "JZ {r8}", 2) op(0x70, "JNZ {r8}", 2) # DEC op(0x14, "DEC A", 1) op(0x15, "DEC {d}", 2) op(0x16, "DEC @R0", 1) op(0x17, "DEC @R1", 1) for i in range(8): op(0x18+i, f"DEC R{i}", 1) # ADD op(0x24, "ADD A,#{imm}", 2) op(0x25, "ADD A,{d}", 2) op(0x26, "ADD A,@R0", 1) op(0x27, "ADD A,@R1", 1) for i in range(8): op(0x28+i, f"ADD A,R{i}", 1) # ADDC op(0x34, "ADDC A,#{imm}", 2) op(0x35, "ADDC A,{d}", 2) op(0x36, "ADDC A,@R0", 1) op(0x37, "ADDC A,@R1", 1) for i in range(8): op(0x38+i, f"ADDC A,R{i}", 1) # ORL op(0x42, "ORL {d},A", 2) op(0x43, "ORL {d},#{imm}", 3) op(0x44, "ORL A,#{imm}", 2) op(0x45, "ORL A,{d}", 2) op(0x46, "ORL A,@R0", 1) op(0x47, "ORL A,@R1", 1) for i in range(8): op(0x48+i, f"ORL A,R{i}", 1) # ANL op(0x52, "ANL {d},A", 2) op(0x53, "ANL {d},#{imm}", 3) op(0x54, "ANL A,#{imm}", 2) op(0x55, "ANL A,{d}", 2) op(0x56, "ANL A,@R0", 1) op(0x57, "ANL A,@R1", 1) for i in range(8): op(0x58+i, f"ANL A,R{i}", 1) # XRL op(0x62, "XRL {d},A", 2) op(0x63, "XRL {d},#{imm}", 3) op(0x64, "XRL A,#{imm}", 2) op(0x65, "XRL A,{d}", 2) op(0x66, "XRL A,@R0", 1) op(0x67, "XRL A,@R1", 1) for i in range(8): op(0x68+i, f"XRL A,R{i}", 1) # ORL C, ANL C op(0x72, "ORL C,{bit}", 2) op(0xA0, "ORL C,/{bit}", 2) op(0x82, "ANL C,{bit}", 2) op(0xB0, "ANL C,/{bit}", 2) # MOV bit op(0x92, "MOV {bit},C", 2) op(0xA2, "MOV C,{bit}", 2) # MOV direct op(0x75, "MOV {d},#{imm}", 3) op(0x85, "MOV {d2},{d1}", 3) op(0xE5, "MOV A,{d}", 2) op(0xF5, "MOV {d},A", 2) op(0xA5, "DB A5h", 1) # MOV A,#imm op(0x74, "MOV A,#{imm}", 2) # MOV A,@Ri / MOV @Ri,A op(0xE6, "MOV A,@R0", 1) op(0xE7, "MOV A,@R1", 1) op(0xF6, "MOV @R0,A", 1) op(0xF7, "MOV @R1,A", 1) # MOV A,Rn / MOV Rn,A for i in range(8): op(0xE8+i, f"MOV A,R{i}", 1) op(0xF8+i, f"MOV R{i},A", 1) # MOV Rn,#imm for i in range(8): op(0x78+i, f"MOV R{i},#{{imm}}", 2) # MOV Rn,direct for i in range(8): op(0xA8+i, f"MOV R{i},{{d}}", 2) # MOV direct,Rn for i in range(8): op(0x88+i, f"MOV {{d}},R{i}", 2) op(0x86, "MOV {d},@R0", 2) op(0x87, "MOV {d},@R1", 2) op(0xA6, "MOV @R0,{d}", 2) op(0xA7, "MOV @R1,{d}", 2) op(0x76, "MOV @R0,#{imm}", 2) op(0x77, "MOV @R1,#{imm}", 2) # MOV DPTR,#imm16 op(0x90, "MOV DPTR,#{imm16}", 3) # MOVC op(0x83, "MOVC A,@A+PC", 1) op(0x93, "MOVC A,@A+DPTR", 1) # MOVX op(0xE0, "MOVX A,@DPTR", 1) op(0xE2, "MOVX A,@R0", 1) op(0xE3, "MOVX A,@R1", 1) op(0xF0, "MOVX @DPTR,A", 1) op(0xF2, "MOVX @R0,A", 1) op(0xF3, "MOVX @R1,A", 1) # PUSH/POP op(0xC0, "PUSH {d}", 2) op(0xD0, "POP {d}", 2) # XCH op(0xC5, "XCH A,{d}", 2) op(0xC6, "XCH A,@R0", 1) op(0xC7, "XCH A,@R1", 1) for i in range(8): op(0xC8+i, f"XCH A,R{i}", 1) # XCHD op(0xD6, "XCHD A,@R0", 1) op(0xD7, "XCHD A,@R1", 1) # DJNZ op(0xD5, "DJNZ {d},{r8}", 3) for i in range(8): op(0xD8+i, f"DJNZ R{i},{{r8}}", 2) # CJNE op(0xB4, "CJNE A,#{imm},{r8}", 3) op(0xB5, "CJNE A,{d},{r8}", 3) op(0xB6, "CJNE @R0,#{imm},{r8}", 3) op(0xB7, "CJNE @R1,#{imm},{r8}", 3) for i in range(8): op(0xB8+i, f"CJNE R{i},#{{imm}},{{r8}}", 3) # SJMP op(0x80, "SJMP {r8}", 2) # JMP @A+DPTR op(0x73, "JMP @A+DPTR", 1) # RET, RETI op(0x22, "RET", 1) op(0x32, "RETI", 1) # CLR, SETB, CPL op(0xC2, "CLR {bit}", 2) op(0xC3, "CLR C", 1) op(0xD2, "SETB {bit}", 2) op(0xD3, "SETB C", 1) op(0xB2, "CPL {bit}", 2) op(0xB3, "CPL C", 1) op(0xE4, "CLR A", 1) op(0xF4, "CPL A", 1) # MUL, DIV, DA, SWAP op(0xA4, "MUL AB", 1) op(0x84, "DIV AB", 1) op(0xD4, "DA A", 1) op(0xC4, "SWAP A", 1) # SUBB op(0x94, "SUBB A,#{imm}", 2) op(0x95, "SUBB A,{d}", 2) op(0x96, "SUBB A,@R0", 1) op(0x97, "SUBB A,@R1", 1) for i in range(8): op(0x98+i, f"SUBB A,R{i}", 1) # INC DPTR op(0xA3, "INC DPTR", 1) # ============ SFR / XDATA / BIT names ============ SFR_NAMES = { 0x80: "IOA", 0x81: "SP", 0x82: "DPL", 0x83: "DPH", 0x84: "DPL1", 0x85: "DPH1", 0x86: "DPS", 0x87: "PCON", 0x88: "TCON", 0x89: "TMOD", 0x8A: "TL0", 0x8B: "TL1", 0x8C: "TH0", 0x8D: "TH1", 0x8E: "CKCON", 0x90: "IOB", 0x91: "EXIF", 0x98: "SCON0", 0x99: "SBUF0", 0x9A: "AUTOPTRH1", 0x9B: "AUTOPTRL1", 0x9C: "AUTOPTRH2", 0x9D: "AUTOPTRL2", 0x9E: "AUTOPTRSETUP", 0xA0: "IOC", 0xA8: "IE", 0xAA: "EP2468STAT", 0xAB: "EP24FIFOFLGS", 0xAC: "EP68FIFOFLGS", 0xB0: "IOD", 0xB1: "IOE", 0xB2: "OEA", 0xB3: "OEB", 0xB4: "OEC", 0xB5: "OED", 0xB6: "OEE", 0xC0: "SCON1", 0xC1: "SBUF1", 0xC8: "T2CON", 0xCA: "RCAP2L", 0xCB: "RCAP2H", 0xCC: "TL2", 0xCD: "TH2", 0xD0: "PSW", 0xD8: "EICON", 0xE0: "ACC", 0xE8: "EIE", 0xF0: "B", 0xF8: "EIP", } XREG_NAMES = { 0xE600: "CPUCS", 0xE601: "IFCONFIG", 0xE602: "PINFLAGSAB", 0xE603: "PINFLAGSCD", 0xE604: "FIFORESET", 0xE609: "REVCTL", 0xE60A: "GPIFTRIG", 0xE60B: "GPIFSGLDATH", 0xE60C: "GPIFSGLDATLX", 0xE610: "FLOWSTATE", 0xE611: "FLOWLOGIC", 0xE618: "GPIFHOLDAMOUNT", 0xE620: "EP2CFG", 0xE621: "EP4CFG", 0xE622: "EP6CFG", 0xE623: "EP8CFG", 0xE624: "EP2FIFOCFG", 0xE625: "EP4FIFOCFG", 0xE626: "EP6FIFOCFG", 0xE627: "EP8FIFOCFG", 0xE628: "EP2AUTOINLENH", 0xE629: "EP2AUTOINLENL", 0xE630: "EP2FIFOPFH", 0xE631: "EP2FIFOPFL", 0xE640: "EP2ISOINPKTS", 0xE648: "INPKTEND", 0xE649: "OUTPKTEND", 0xE650: "EP2FIFOIE", 0xE651: "EP2FIFOIRQ", 0xE65C: "USBIE", 0xE65D: "USBIRQ", 0xE65E: "EPIE", 0xE65F: "EPIRQ", 0xE660: "GPIFIE", 0xE661: "GPIFIRQ", 0xE662: "USBERRIE", 0xE663: "USBERRIRQ", 0xE666: "INT2IVEC", 0xE667: "INT4IVEC", 0xE668: "INTSETUP", 0xE670: "PORTACFG", 0xE671: "PORTCCFG", 0xE672: "PORTECFG", 0xE678: "I2CS", 0xE679: "I2DAT", 0xE67A: "I2CTL", 0xE67B: "XAUTODAT1", 0xE67C: "XAUTODAT2", 0xE680: "USBCS", 0xE681: "SUSPEND", 0xE682: "WAKEUPCS", 0xE683: "TOGCTL", 0xE684: "USBFRAMEH", 0xE685: "USBFRAMEL", 0xE686: "MICROFRAME", 0xE687: "FNADDR", 0xE68A: "EP0BCH", 0xE68B: "EP0BCL", 0xE68C: "EP1OUTBC", 0xE68D: "EP1INBC", 0xE68F: "EP1INCS", 0xE690: "EP2CS", 0xE691: "EP4CS", 0xE692: "EP6CS", 0xE693: "EP8CS", 0xE694: "EP2FIFOFLGS", 0xE695: "EP4FIFOFLGS", 0xE696: "EP6FIFOFLGS", 0xE697: "EP8FIFOFLGS", 0xE698: "EP2BCH", 0xE699: "EP2BCL", 0xE6A0: "EP0CS", 0xE6A1: "EP0STAT", 0xE6A2: "EP0STALLBITS", 0xE6A3: "CLRTOGS", 0xE6A5: "SETUPDAT[0]", 0xE6A6: "SETUPDAT[1]", 0xE6A7: "SETUPDAT[2]", 0xE6A8: "SETUPDAT[3]", 0xE6A9: "SETUPDAT[4]", 0xE6AA: "SETUPDAT[5]", 0xE6AB: "SETUPDAT[6]", 0xE6AC: "SETUPDAT[7]", 0xE6C0: "GPIFWFSELECT", 0xE6C1: "GPIFIDLECS", 0xE6C2: "GPIFIDLECTL", 0xE6C3: "GPIFCTLCFG", 0xE6C4: "GPIFADRH", 0xE6C5: "GPIFADRL", 0xE6CE: "GPIFREADYCFG", 0xE6CF: "GPIFREADYSTAT", 0xE6D0: "GPIFABORT", 0xE6F8: "SUDPTRH", 0xE6F9: "SUDPTRL", 0xE6FA: "SUDPTRCTL", 0xE740: "EP0BUF[0]", 0xE780: "EP1OUTBUF[0]", 0xE7C0: "EP1INBUF[0]", } BIT_NAMES = { 0xD0: "PSW.P", 0xD1: "PSW.1", 0xD2: "PSW.OV", 0xD3: "PSW.RS0", 0xD4: "PSW.RS1", 0xD5: "PSW.F0", 0xD6: "PSW.AC", 0xD7: "PSW.CY", 0xA8: "EX0", 0xA9: "ET0", 0xAA: "EX1", 0xAB: "ET1", 0xAC: "ES0", 0xAD: "ET2", 0xAE: "ES1", 0xAF: "EA", 0x80: "IOA.0", 0x81: "IOA.1", 0x82: "IOA.2", 0x83: "IOA.3", 0x84: "IOA.4", 0x85: "IOA.5", 0x86: "IOA.6", 0x87: "IOA.7", 0x90: "IOB.0", 0x91: "IOB.1", 0x92: "IOB.2", 0x93: "IOB.3", 0x94: "IOB.4", 0x95: "IOB.5", 0x96: "IOB.6", 0x97: "IOB.7", 0x88: "IT0", 0x89: "IE0", 0x8A: "IT1", 0x8B: "IE1", 0x8C: "TR0", 0x8D: "TF0", 0x8E: "TR1", 0x8F: "TF1", 0x98: "RI_0", 0x99: "TI_0", 0xD8: "EICON.0", 0xDB: "INT6", 0xDC: "RESI", 0xDD: "ERESI", } KNOWN_LABELS = { 0x0000: "reset_vector", 0x0003: "int0_vector", 0x000B: "timer0_vector", 0x0013: "int1_vector", 0x001B: "timer1_vector", 0x0023: "serial0_vector", 0x002B: "timer2_vector", 0x0033: "resume_vector", 0x003B: "serial1_vector", 0x0043: "usb_int2_vector", 0x004B: "i2c_int3_vector", 0x0053: "gpif_int4_vector", 0x005B: "int5_vector", 0x0063: "int6_vector", 0x099A: "tune_function", 0x0DDD: "boot_init_blocks", 0x0EE9: "tune_init_blocks", 0x1200: "usb_device_desc", 0x1556: "i2c_combined_read", 0x188D: "keil_startup", 0x1A81: "i2c_write", 0x1D87: "boot_8psk", 0x2000: "wait_for_ready", } def sfr_name(addr): if addr in SFR_NAMES: return SFR_NAMES[addr] if addr >= 0x80: return f"SFR_{addr:02X}h" return f"{addr:02X}h" def bit_name(addr): if addr in BIT_NAMES: return BIT_NAMES[addr] if addr >= 0x80: base = addr & 0xF8 bit = addr & 0x07 if base in SFR_NAMES: return f"{SFR_NAMES[base]}.{bit}" return f"bit_{addr:02X}h" def xreg_name(addr): if addr in XREG_NAMES: return XREG_NAMES[addr] if 0xE740 <= addr <= 0xE77F: return f"EP0BUF[{addr-0xE740}]" if 0xE780 <= addr <= 0xE7BF: return f"EP1OUTBUF[{addr-0xE780}]" if 0xE7C0 <= addr <= 0xE7FF: return f"EP1INBUF[{addr-0xE7C0}]" if 0xE600 <= addr <= 0xE6FF: return f"XSFR_{addr:04X}h" return None def disasm_one(data, pc): """Returns (mnem, hex_str, length, comment, branch_target).""" if pc >= len(data): return ("???", "", 1, "", None) opc = data[pc] entry = OPCODES.get(opc) if entry is None: return (f"DB {opc:02X}h", f"{opc:02X}", 1, "; UNKNOWN", None) mnem_fmt, length, desc = entry if pc + length > len(data): return (f"DB {opc:02X}h", f"{opc:02X}", 1, "; TRUNCATED", None) raw_bytes = data[pc:pc+length] hex_str = " ".join(f"{b:02X}" for b in raw_bytes) comment = "" branch_target = None mnem = mnem_fmt # Resolve address fields if "{a16}" in mnem: addr16 = (data[pc+1] << 8) | data[pc+2] label = KNOWN_LABELS.get(addr16) mnem = mnem.replace("{a16}", label if label else f"{addr16:04X}h") branch_target = addr16 m = re.search(r'\{a11:(\d+)\}', mnem) if m: page = int(m.group(1)) addr11 = (page << 8) | data[pc+1] | ((pc + 2) & 0xF800) label = KNOWN_LABELS.get(addr11) mnem = re.sub(r'\{a11:\d+\}', label if label else f"{addr11:04X}h", mnem) branch_target = addr11 if "{r8}" in mnem: rel_byte = data[pc + length - 1] rel = rel_byte if rel_byte < 0x80 else rel_byte - 256 target = (pc + length + rel) & 0xFFFF label = KNOWN_LABELS.get(target) mnem = mnem.replace("{r8}", label if label else f"{target:04X}h") branch_target = target # Direct addressing - handle the special MOV d2,d1 case if "{d2}" in mnem and "{d1}" in mnem: src = data[pc+1] dst = data[pc+2] mnem = mnem.replace("{d1}", sfr_name(src) if src >= 0x20 else f"{src:02X}h") mnem = mnem.replace("{d2}", sfr_name(dst) if dst >= 0x20 else f"{dst:02X}h") elif "{d}" in mnem: d = data[pc+1] mnem = mnem.replace("{d}", sfr_name(d) if d >= 0x20 else f"{d:02X}h") if "{bit}" in mnem: b = data[pc+1] mnem = mnem.replace("{bit}", bit_name(b)) if "{imm16}" in mnem: imm16 = (data[pc+1] << 8) | data[pc+2] xname = xreg_name(imm16) if xname: mnem = mnem.replace("{imm16}", f"{imm16:04X}h") comment = f"; = {xname}" elif imm16 in KNOWN_LABELS: mnem = mnem.replace("{imm16}", f"{imm16:04X}h") comment = f"; -> {KNOWN_LABELS[imm16]}" else: mnem = mnem.replace("{imm16}", f"{imm16:04X}h") if "{imm}" in mnem: # Determine which byte is the immediate if length == 3 and opc in (0x43, 0x53, 0x63): imm = data[pc+2] # ORL/ANL/XRL d,#imm elif length == 3 and opc == 0x75: imm = data[pc+2] # MOV d,#imm elif length == 3 and opc == 0xB4: imm = data[pc+1] # CJNE A,#imm,r8 elif length == 3 and 0xB6 <= opc <= 0xBF: imm = data[pc+1] # CJNE @Ri/#imm or CJNE Rn,#imm elif length == 3 and opc == 0xD5: # DJNZ d,r8 - no imm, but {d} already handled imm = 0 # shouldn't happen else: imm = data[pc + length - 1] mnem = mnem.replace("{imm}", f"{imm:02X}h") return (mnem, hex_str, length, comment, branch_target) def disasm_range(data, start, end, title=""): lines = [] if title: lines.append(f"\n{'='*80}") lines.append(f" {title}") lines.append(f"{'='*80}") pc = start while pc < end and pc < len(data): if pc in KNOWN_LABELS: lines.append(f"\n {KNOWN_LABELS[pc]}:") mnem, hex_str, length, comment, target = disasm_one(data, pc) addr_str = f"{pc:04X}" hex_padded = f"{hex_str:<12s}" line = f" {addr_str}: {hex_padded} {mnem}" if comment: line = f"{line:<58s} {comment}" lines.append(line) pc += length return "\n".join(lines) def track_dptr_accesses(data, start, end): """Track all MOVX accesses with DPTR context.""" results = [] pc = start dptr = None while pc < end and pc < len(data): opc = data[pc] entry = OPCODES.get(opc) if entry is None: pc += 1 continue _, length, _ = entry if pc + length > len(data): break if opc == 0x90: # MOV DPTR,#imm16 dptr = (data[pc+1] << 8) | data[pc+2] elif opc in (0xE0, 0xF0) and dptr is not None: xname = xreg_name(dptr) direction = "READ" if opc == 0xE0 else "WRITE" results.append((pc, dptr, xname, direction)) elif opc == 0xA3: # INC DPTR if dptr is not None: dptr += 1 pc += length return results def main(): fw_path = "/home/rpm/claude/ham/satellite/genpix/skywalker-1/firmware-dump/skywalker1_eeprom_full64k.bin" with open(fw_path, "rb") as f: data = f.read() out = [] out.append("=" * 80) out.append(" GENPIX SKYWALKER-1 FIRMWARE DISASSEMBLY") out.append(" FX2LP (CY7C68013A) 8051 Core - Keil C51 Compiled") out.append(" Firmware size: 9472 bytes (0x0000-0x24FF)") out.append(" Binary: 65536 bytes total (RAM image)") out.append("=" * 80) # ==== SECTION 1: Reset + Interrupt Vectors ==== out.append(disasm_range(data, 0x0000, 0x006B, "SECTION 1: Reset Vector & Interrupt Vector Table (0x0000-0x006A)")) # ==== SECTION 2: Keil C51 Startup ==== out.append(disasm_range(data, 0x188D, 0x1950, "SECTION 2: Keil C51 Startup (STARTUP.A51) @ 0x188D")) # Find main() by tracing startup pc = 0x188D main_addr = None for _ in range(500): if pc >= len(data) - 2: break opc = data[pc] entry = OPCODES.get(opc) if entry is None: break _, length, _ = entry if opc == 0x02: # LJMP addr = (data[pc+1] << 8) | data[pc+2] # The startup has two LJMPs: one internal loop, one to main # main() is the last LJMP before we hit data/different code if addr < 0x188D: # Jump backward = likely to main() main_addr = addr pc += length if main_addr: KNOWN_LABELS[main_addr] = "main" out.append(f"\n >>> Keil startup jumps to main() at {main_addr:04X}h") # ==== SECTION 3: main() ==== if main_addr: # Disassemble main() - generous range to cover init out.append(disasm_range(data, main_addr, min(main_addr + 400, 0x2500), f"SECTION 3: main() @ {main_addr:04X}h (first ~400 bytes)")) # ==== SECTION 4: Vendor command dispatch ==== out.append("\n" + "=" * 80) out.append(" SECTION 4: USB Vendor Command Dispatch") out.append("=" * 80) # Find all reads of SETUPDAT[1] (bRequest) for pc in range(0x2500): if pc + 2 < len(data) and data[pc] == 0x90 and data[pc+1] == 0xE6 and data[pc+2] == 0xA6: out.append(f"\n SETUPDAT[1] read at {pc:04X}h:") ctx_start = max(0, pc - 16) ctx_end = min(pc + 120, 0x2500) out.append(disasm_range(data, ctx_start, ctx_end, f" Dispatch context @ {pc:04X}h")) # ==== SECTION 5: Find all CJNE A,#xx comparisons (command dispatch) ==== out.append("\n" + "=" * 80) out.append(" SECTION 5: All CJNE A,#xx Instructions (Command Dispatch Table)") out.append("=" * 80) for pc in range(0x2500): if pc + 2 < len(data) and data[pc] == 0xB4: imm = data[pc+1] rel = data[pc+2] if rel >= 0x80: rel -= 256 target = (pc + 3 + rel) & 0xFFFF out.append(f" {pc:04X}: CJNE A,#{imm:02X}h,{target:04X}h ; bRequest?=0x{imm:02X} skip->{target:04X}h") # ==== SECTION 6: boot_8psk ==== out.append(disasm_range(data, 0x1D87, min(0x1D87 + 250, 0x2500), "SECTION 6: boot_8psk() @ 0x1D87")) # ==== SECTION 7: SFR access map ==== out.append("\n" + "=" * 80) out.append(" SECTION 7: All XDATA SFR Accesses (DPTR-tracked)") out.append("=" * 80) accesses = track_dptr_accesses(data, 0x0000, 0x2500) for pc, addr, name, direction in accesses: name_str = name if name else f"XDATA_{addr:04X}h" out.append(f" {pc:04X}: {direction:5s} [{addr:04X}h] {name_str}") # ==== SECTION 8: Key functions ==== out.append(disasm_range(data, 0x0DDD, 0x0DDD + 120, "SECTION 8: boot_init_blocks() @ 0x0DDD")) out.append(disasm_range(data, 0x1A81, 0x1A81 + 150, "SECTION 9: i2c_write() @ 0x1A81")) out.append(disasm_range(data, 0x1556, 0x1556 + 150, "SECTION 10: i2c_combined_read() @ 0x1556")) out.append(disasm_range(data, 0x2000, 0x2000 + 120, "SECTION 11: wait_for_ready() @ 0x2000")) out.append(disasm_range(data, 0x099A, 0x099A + 200, "SECTION 12: tune_function() @ 0x099A")) # ==== SECTION 13: USB Descriptors ==== out.append("\n" + "=" * 80) out.append(" SECTION 13: USB Descriptors @ 0x1200") out.append("=" * 80) desc = data[0x1200:0x1212] out.append(f" Device Descriptor (18 bytes):") out.append(f" bLength: {desc[0]} (0x{desc[0]:02X})") out.append(f" bDescriptorType: {desc[1]} (DEVICE)") out.append(f" bcdUSB: {desc[3]:02X}.{desc[2]:02X}") out.append(f" bDeviceClass: 0x{desc[4]:02X} (vendor-specific)") out.append(f" bDeviceSubClass: 0x{desc[5]:02X}") out.append(f" bDeviceProtocol: 0x{desc[6]:02X}") out.append(f" bMaxPacketSize0: {desc[7]} bytes") vid = desc[8] | (desc[9] << 8) pid = desc[10] | (desc[11] << 8) out.append(f" idVendor: 0x{vid:04X} (Cypress)") out.append(f" idProduct: 0x{pid:04X}") bcd = desc[12] | (desc[13] << 8) out.append(f" bcdDevice: {bcd:04X}") out.append(f" iManufacturer: {desc[14]}") out.append(f" iProduct: {desc[15]}") out.append(f" iSerialNumber: {desc[16]}") out.append(f" bNumConfigurations: {desc[17]}") # Raw descriptor dump out.append(f"\n Raw descriptor area 0x1200-0x1300:") for off in range(0, 0x100, 16): addr = 0x1200 + off chunk = data[addr:addr+16] hexl = " ".join(f"{b:02X}" for b in chunk) asciil = "".join(chr(b) if 32 <= b < 127 else "." for b in chunk) out.append(f" {addr:04X}: {hexl} {asciil}") # ==== SECTION 14: Hex dump of interesting code regions ==== out.append("\n" + "=" * 80) out.append(" SECTION 14: Raw Hex - Code Space Near USB ISR Targets") out.append("=" * 80) # Show hex around the USB interrupt vector dispatch # The USB ISR at 0x0043 likely jumps somewhere for start_addr in [0x0043, 0x004B]: out.append(f"\n Hex dump at {start_addr:04X}h:") for off in range(0, 48, 16): addr = start_addr + off chunk = data[addr:addr+16] hexl = " ".join(f"{b:02X}" for b in chunk) out.append(f" {addr:04X}: {hexl}") print("\n".join(out)) if __name__ == "__main__": main()