PIC32MX220F032D: Mixed Instruction Sets In One Array

A disassembler pointed at a PIC32MX220F032D image sometimes produces beautiful nonsense: plausible instructions whose boundaries drift, control flow that falls into the middle of operands, code that decodes fine for a hundred words and then goes wrong. Before blaming the capture, check the ISA. Microchip gave the M4K core in this family a second instruction set — MIPS16e — and a compiler squeezing 32 KB of flash will use it. The result is one array holding two encodings, and reading it correctly requires knowing where the switch happens.

Two Encodings, One Program

MIPS16e compresses the most common MIPS32 operations into 16-bit encodings, cutting typical C output by up to 40 percent. The core executes both, switching through jump instructions whose extended forms cross the width boundary. In a recovered dump the two regions have different texture: MIPS32 words at 32-bit alignment with full immediate fields; MIPS16e regions denser, with half-word rhythm and extension prefixes. Linear disassembly that assumes one width produces the drift described above — each misalignment compounding until the listing is fiction.

Reconstruction therefore proceeds in passes: identify the mode boundaries, disassemble each region under its own encoding, then stitch the control flow. The linker map is gone, but the mode-switching jumps are unambiguous once found, and the boundaries fall out of them. The payoff is an accurate listing; the cost is the analysis pass that generic tools skip. On parts like the 110F016B — 16 KB where the compression incentive is strongest — the mixed-ISA pattern is the norm, not the exception.

Where Compression Buys The Most

Thirty-two kilobytes is the density where MIPS16e stops being an option and starts being a plan. Add the USB stack — device, host or OTG, with its descriptors, endpoint handlers and the class layer on top — and pure MIPS32 output does not fit with room to spare. Compilers respond by compressing everything that is not performance-critical: enumeration code, string handling, configuration parsing, the class implementations. The hot paths — interrupt service, USB transaction handling, converter loops — stay in full-width instructions where the core’s single-cycle throughput matters. The resulting image is a deliberate split, readable in the dump as alternating texture: dense 16-bit regions for the bureaucracy, wide 32-bit runs for the machinery.

The Part Under The Code

The 220F032D is the 44-pin member of the 32 KB USB family: 32 KB of program flash from 0x1D000000 to 0x1D007FFF, 8 KB of SRAM, full-speed USB 2.0 with device, host and OTG modes, two UARTs, two SPI/I2S ports, parallel master port, five timers, 10-bit converter at 1 Msps, CTMU for touch and timing. The M4K core reaches 50 MHz and 83 DMIPS in the commercial temperature band, 40 MHz across the industrial range. Boot flash is the family-standard 3 KB at 0x1FC00000 through 0x1FC00BFF, holding startup code, the DEVCFG words and frequently a bootloader in its own erase domain. The 220F032B page covers the three-region structure; the 44-pin version changes the pin count, not the architecture.

What Mixed-ISA Means For The Customer

For archiving, nothing: the HEX is the HEX, encodings and all. For everything else — patching, porting, understanding — it matters. A patch inserted without regard to mode boundaries corrupts both regions it touches. A port that recompiles in pure MIPS32 produces a larger image that may not fit. A security review that trusts a linear disassembly misreads a third of the code. The report that accompanies a recovery names the mode boundaries and their addresses so downstream work starts from the map, not from a mystery. The firmware extraction overview describes the packaging.

Reading The Split Correctly

Mode-switching jumps anchor the reconstruction. Each extended jump or jump-and-link instruction carries a bit that selects the width of its target, so following control flow from a known point labels every reachable region. Data complicates the picture — constant tables decode as plausible instructions in either width — but the linker aligned them to word boundaries and the mode transitions skip over them cleanly. The practical workflow is: find the reset vector, walk the flow, label widths, then disassemble the remainder under the two encodings and reconcile the leftovers as data. It is an extra pass over the array, and it is the difference between a listing that builds back and a listing that merely looks impressive.

Capture, As Always

Two independent captures of both partitions, compared word for word; device ID read back to confirm the die; configuration words decoded, including the code-protect bit in DEVCFG0 — bit 28, active low, armed by a programmed zero, exitable only by the chip erase that destroys the image. Erased flash reads all ones and the live-code boundary is mapped. The reading process page details the sequence, and the chip extraction analysis service adds interpretation when the deliverable is understanding rather than a file. The silicon is ordinary; the instruction mix inside it is the part that punishes generic tooling and rewards the extra pass.

Package and page size for the neighbouring parts are listed in the PIC32MX family index.