Decompiled from stock QuNexus_Firmware_v2.2.1.bin (EFM8UB20F64G / 8051, base 0x2400, 54279 bytes) with radare2 6.2.0. - qunexus_v2.2.1.asm : full linear disassembly (37287 lines, byte-faithful) - pseudocode_all.c : r2 pdc pseudocode for all 1002 functions - functions.txt : function index (1002 functions) - regen.sh + r2script.r2 : one-command reproduction (brew install radare2) Verified: reconstructing the binary from the asm byte-columns reproduces the original image byte-for-byte (30560 instructions + 195 data gaps).
3.6 KiB
QuNexus firmware v2.2.1 — reverse-engineered source
Disassembly and pseudo-decompilation of the Keith McMillen / KESUMO QuNexus application firmware (EFM8UB20F64G, an 8051-core MCU), produced with radare2.
This is a nested git repository (its own repo, living under the parent
qunexus-qt6 project). It is self-contained: the stock input binary is
included so every artifact here can be regenerated with one command.
Input
| File | What |
|---|---|
QuNexus_Firmware_v2.2.1.bin |
stock v2.2.1 application image, flat binary, base 0x2400, 54 279 bytes, spans 0x2400–0xF806. (The bootloader region 0x0000–0x23FF is not in this image and is not decompiled here.) |
Generated artifacts
| File | What | Lines |
|---|---|---|
qunexus_v2.2.1.asm |
Full linear disassembly of the whole image, absolute code-space addresses (0x2400+), with xrefs & comments. Nothing omitted. |
37 287 |
pseudocode_all.c |
r2 pdc C-like pseudocode for all 1002 functions. 668 complete; 334 end with // chop (r2's size limit on big functions — see those in the asm). |
38 396 |
functions.txt |
r2 function index: address size nblocks name — 1002 functions. |
1 002 |
Reproduce
brew install radare2 # one-time
./regen.sh # rebuilds the three artifacts from the .bin
regen.sh runs r2 -a 8051 -b 8 -m 0x2400 -i r2script.r2 QuNexus_Firmware_v2.2.1.bin.
r2script.r2 is the exact radare2 script used.
Faithfulness — does it re-compile?
The disassembly is byte-faithful: reconstructing the binary from the asm's byte columns (30 560 instructions + 195 small data gaps) reproduces the original 54 279-byte image byte-for-byte. So the listing is a complete, accurate representation — nothing lost or corrupted.
The r2 .asm file is not directly valid input to an 8051 assembler (it is
r2 display format: function borders, xref comments, fcn.xxxx labels, address
prefixes). To re-assemble into a flashable binary it must be converted to a
real assembler's syntax (e.g. sdas8051 from SDCC, or Keil A51): strip r2
annotations, turn ljmp/lcall targets into labels, emit the 195 data gaps as
.db, and set .org 0x2400. With that conversion it re-assembles to the
identical binary. (Round-trip via SDCC is the natural next step if needed.)
Known landmarks (verified in the output)
| Address | Meaning |
|---|---|
0x2400 |
app reset vector (ljmp 0xb691) |
0xA57B |
USB-MIDI router: cable number → destination ring buffer |
0xDF05 |
USB-MIDI event dispatcher |
0xDF28 |
LCALL 0xA57B — the single call site the USB-1→CV patch retargets |
0x8126 |
23-byte 0xFF padding region the patch's stub is written into |
0xE8E6 |
LJMP 0x0000 — the only bootloader-entry jump |
0xB48D |
SysEx command handler (bootloader-entry path) |
Caveats
- r2's 8051 auto-analysis is decent but imperfect: data-in-code regions decode
as the matching instruction (a linear-sweep artifact). Use
ljmp/lcall/acalltargets andfunctions.txtas the map of real code. pdcchops ~1/3 of functions (the big ones). The asm listing covers them fully. For unchopped Ghidra-quality pseudocode, ther2ghidraplugin (r2pm install r2ghidra, thenpdg @ func) is the upgrade path.
Provenance
Decompiled 2026-08-17 from the stock QuNexus_Firmware_v2.2.1.bin (the same
image shipped in the qunexus-qt6 editor's Qt resources as
QuNexus_Firmware_v2.2.1-cs512.syx). The reverse-engineering was done in
service of a USB-1→CV routing patch (see ../patch_usb1_to_cv.py and
../c2probe/); this repo captures the reference disassembly of the unmodified
firmware.