# ROM Analysis Walkthrough ## 1. Identify Your Files A firmware dump from an ADSP-2191 typically comes as separate files for each memory space: - **PM files** (Program Memory): 24-bit words — code and PM data - **DM files** (Data Memory): 16-bit words — variables, buffers Only PM files contain executable code. DM files are pure data and cannot be disassembled as instructions. If you have multiple sets (e.g. three directories), these may be: - Different firmware versions - Different memory banks (Block 0, 1, 2) - Boot loader vs application code ## 2. Determine the PM Format ADSP-2191 instructions are 24 bits (3 bytes). A raw dump can be packed (3 bytes/word) or padded (4 bytes/word, 32-bit aligned). ### Check with od Dump the first 24 bytes and look at the pattern: od -A x -t x1 -N 24 firmware_pm.bin **Packed (3 bytes/word)** — most common: 000000 1c 00 30 00 00 00 50 10 00 41 23 40 ... Every group of 3 bytes is one instruction. The first byte of real code is typically 0x1C (JUMP), 0x00 (NOP), or 0x50/0x30 (register load). **Padded (4 bytes/word)** — some EPROM/JTAG tools: 000000 00 1c 00 30 00 00 00 00 00 50 10 00 ... There is a leading 0x00 before each 3-byte instruction. To strip padding: `dd if=input.bin of=output.bin bs=3 count=N` after removing every 4th byte. **Boot stream** — ADSP boot loader format: 000000 xx xx xx xx yy yy ... Has block headers (target address, byte count, flags) before the actual code data. Look for a repeating structure of header + data blocks. The data payload inside is packed 24-bit. ### Check file size ls -la firmware_pm.bin - Packed: file size is divisible by 3 - Padded: file size is divisible by 4 - Boot stream: neither cleanly divisible python3 -c "import os; s=os.path.getsize('firmware_pm.bin'); print(f'Size: {s} bytes, /3={s/3:.1f}, /4={s/4:.1f}')" ### Verify with a test disassembly r2 -a adsp219x -b 24 -e asm.parser=null -q -c "pd 20" firmware_pm.bin If you see coherent instructions (register loads, JUMPs, NOPs), the format is correct. If the output is mostly `unk 0x...` or nonsensical, try the other format or adjust the start offset. ## 3. Load in radare2 ### Packed 3-byte format (direct) r2 -a adsp219x -b 24 -e asm.parser=null firmware_pm.bin ### Padded 4-byte format Strip padding first, then load: python3 -c " d = open('firmware_pm.bin','rb').read() o = b''.join(d[i+1:i+4] for i in range(0, len(d), 4)) open('firmware_pm_packed.bin','wb').write(o) " r2 -a adsp219x -b 24 -e asm.parser=null firmware_pm_packed.bin ### Suppress the parser warning Add to ~/.radare2rc (one-time): echo "e asm.parser=null" >> ~/.radare2rc ## 4. Initial Analysis [0x00000000]> aaa # Full auto-analysis [0x00000000]> afl # List detected functions [0x00000000]> afb @ main # Show basic blocks [0x00000000]> VV # Visual control flow graph ## 5. Find the Entry Point The reset vector is at PM address 0x0000. Typical patterns: 0x0000: JUMP main (Type 10a, opcode byte 0x1C) 0x0000: NOP (entry at next instruction) The interrupt vector table occupies the first ~128 PM words (0x000-0x17F), with 4-word spacing per vector. Most vectors contain RTI or JUMP to a handler. ## 6. Identify Code vs Data Regions **Code regions** produce coherent disassembly: register loads, compute instructions, jumps, and loops in logical sequence. **Data regions** (coefficient tables, lookup tables) produce nonsensical output: random-looking mnemonics, jumps to invalid addresses, many `unk` opcodes. Mark as data in r2: Cd 300 @ 0x1000 # 300 bytes as data at offset 0x1000 **Null regions** (0x000000 repeated) are uninitialized memory: # Find next non-null byte /x 01 # Skip to it s hit0_0 ## 7. Recognize DSP Patterns ### FIR Filter CNTR = N; DO loop_end UNTIL CE; MR = MR + MX0*MY0 (SS), MX0 = DM(I0,M0), MY0 = PM(I4,M4); loop_end: ... Look for: DO UNTIL CE + multifunction MAC instructions. ### IIR Filter (Biquad) Nested loops: outer over samples, inner over biquad sections. Contains ASHIFT for inter-stage scaling. ### Initialization Sequence Sequences of Type 6/7 loads (I/M/L register setup). Circular buffer initialization before entering a processing loop. ### I/O Configuration IO(addr) = Dreg / Dreg = IO(addr) instructions configure peripherals: Serial Ports, Timers, DMA, etc. ## 8. DM File Analysis DM files contain 16-bit data words. These are not code. You can inspect them for patterns: od -A x -t x2 -N 200 firmware_dm.bin # 16-bit hex words od -A x -t d2 -N 200 firmware_dm.bin # Signed 16-bit decimal Common contents: - Filter coefficients (Q15 fixed-point: values near 0x0000-0x7FFF) - Lookup tables (sine, cosine, window functions) - Configuration data (peripheral registers) ## 9. Useful r2 Commands Reference pd 200 # Disassemble 200 instructions pD 600 # Disassemble 600 bytes (= 200 words) /x 1c # Find unconditional JUMPs /x 16 # Find DO UNTIL loops /x 0a # Find RTS/RTI instructions /x 0b # Find indirect JUMP/CALL axt @ addr # Who references this address? axf @ addr # What does this address reference? VV # Visual graph mode V # Visual hex/disasm mode pdf # Print current function disassembly agf # ASCII control flow graph