Firmware Analysis
The flash dump gave us 32,768 bytes of raw ARM7TDMI code. This section documents what we found inside.
The analysis was performed using mcghidra, an MCP server that wraps headless Ghidra for use by Claude Code, running on port 8194. The raw binary was wrapped in an ELF container using arm-none-eabi-objcopy and a minimal linker script to set the entry point at 0x00000000. Ghidra’s auto-analysis detected 120 functions. One additional function — a UART putchar routine at 0x10C4 — was discovered manually through cross-referencing peripheral register accesses. All 121 have been fully decompiled and categorized.
Headline findings
Section titled “Headline findings”The firmware revision is V1.19, hardcoded in the factory reset display string. The manual ships with V1.03, which means this unit has been updated at least once through the dealer channel — confirming that the firmware update mechanism does work, even if the tooling is not publicly available.
The most significant discovery is what the firmware does not contain. UART0 is transmit-only. There is no receive interrupt, no FIFO configuration, no command parser, no input buffer. The MCU sends a boot message at 9600 baud and never reads the serial port again. There is no serial command protocol. There is no firmware update logic in the application firmware. The dealer update tool does not talk to this code — it talks to the LPC2103F’s ROM-based ISP bootloader, which is a completely separate program baked into the chip at the factory. The application firmware’s only job is to run the amplifier.
Clock tree
Section titled “Clock tree”The LPC2103F runs from a 14.7456 MHz crystal, multiplied by the on-chip PLL to produce the core clock. The PLL configuration register is set to 0x23: MSEL=3 (multiply by 4) and PSEL=1 (CCO divider). This yields a CCLK of 58.98 MHz — not the 70 MHz maximum the LPC2103 datasheet allows, but a deliberate choice. 14.7456 MHz is a UART-friendly crystal that divides cleanly to standard baud rates, and the ×4 multiplier keeps the CPU well within spec while giving the Memory Accelerator Module room to work with 3 wait states.
The peripheral clock (PCLK) runs at the default CCLK/4 ratio: 14.7456 MHz. This drives UART0, the I2C controller, and the system tick timer. The fact that PCLK equals the crystal frequency is not a coincidence — it means the UART baud rate divisor and timer prescaler calculations work out to exact integers with zero error.
Memory layout
Section titled “Memory layout”| Region | Address | Size | Contents |
|---|---|---|---|
| Flash | 0x00000000 | 32 KB | Application firmware (31 KB used, ~1 KB free) |
| SRAM | 0x40000000 | 8 KB | .data (1196 B) + .bss (948 B) + stacks + heap |
| Boot ROM | 0x7FFFE000 | 8 KB | Factory ISP bootloader (read-only) |
| APB peripherals | 0xE0000000 | 2 MB | UART, I2C, timers, GPIO |
| AHB peripherals | 0xFFE00000 | 2 MB | VIC, system control |
The .data section occupies 1196 bytes at the base of SRAM, copied from flash offset 0x7A88 during startup. This region holds the DSP I2C command packets, the volume attenuation curve lookup table, and the VFD display strings. The .bss section follows immediately with 948 bytes of zero-initialized globals — state variables, I2C buffers, and the settings array.