M-VAVE FM1 reverse-engineering research, SysEx protocol tools, and firmware update scripts
Python
57
12 commits
updated Oct 7, 2026
This document contains technical research, hardware details, and reverse-engineering results for the M-VAVE FM1 synthesizer (powered by the JieLi AC791N processor) and the M-UPGRADE-FM1.app (macOS) utility.
Community-developed custom firmwares, mods, and experimental firmware projects for the M-VAVE FM-1:
Felucca by hugelton (Web Installer | Web Editor):
baud girl Custom Firmware Mod:
SLOOP by isod89 (Web Installer | Web Editor):
FoMni-1 by charlesvestal (Web Installer):
ChoralRoot FM-1 by Quixotic7 (Web Installer | Web Emulator):
Melodee by keremimo (Web Installer | Web Editor):
.syx import/export, 38 dedicated editing pages, full backup/restore before flashing, and WebMIDI management.FM1 Quest by hericdk (Web Installer):
[!TIP] Community Flashing Experience & Safe Upgrade Practice:
While not an absolute hard rule, there is a prominent community recommendation to avoid flashing one custom firmware directly over another. To minimize bricking risks, some users suggest rolling back to the official stock firmware (v15) via M-VAVE's official updater first, and only then flashing the next custom firmware build.
Target Device: M-VAVE FM1 (FM Synthesizer / MIDI Controller).
Processor SoC: JieLi AC791N (WL82 series, 32-bit RISC core).
Official Development Board: JL_AC79_DevKit V1.0 (AC791N evaluation kit).

Synthesizer Product Page: Cuvave / M-VAVE FM-1 Product Page
Latest Available Synthesizer Firmware: FM-1.fwsc (v15 2026-07-30)
Firmware v15 Entropy Analysis: (Source Issue #1)

M-VAVE Ecosystem: Many other M-VAVE audio products (MIDI foot controllers, audio interfaces, and digital pedals) use the same JieLi AC791N / AC69xx processor platform and share similar OTA firmware architectures.
UBOOT1.00) through the external USB-C port using a dedicated RP2040-based hardware dongle (or vendor JieLi USB Updater). This dongle bit-bangs the hardware boot key (0x16EF at ~50 kHz) over USB D+/D- lines at power-up, allowing SPI flash recovery and unbricking without opening the enclosure (see ip2k/mvave-fm1-open-firmware).To open the physical enclosure of the M-VAVE FM1:
Yes, the firmware update occurs entirely over the MIDI SysEx protocol.
The application does not use dedicated USB DFU/CDC emulation or direct low-level raw flash access during standard operation. The entire process (from handshake to streaming every byte of firmware and preset data) is executed via USB MIDI SysEx messages using the RtMidi library on top of macOS CoreMIDI.
The firmware binary is embedded directly inside the native executable file.
There are no external .bin or .hex files in the resources directory. Inside the executable file M-UPGRADE-FM1, the firmware image is stored as an embedded Qt resource package:
0x47EB4 (0x000ABC14 = 704,052 bytes).0x47EB8 – 0xF3CEC (704,052 bytes). Contains stock V14 (FM-1_014).@JMUA (offset 0xF3BB8), JLUFW (offset 0xF3CDC), and target CPU tag AC791N at +0x424.Asymmetric cryptographic signature verification (RSA/ECDSA/AES) is NOT implemented.
Security checks include:
@JMUA / JLUFW signatures, target device type, and version strings.Embedded firmware too small: %1 bytes (min %2).MainView, SelectFile, LoadingWidget).RtMidiIn, RtMidiOut, MidiInCore, MidiOutCore).OtaUpgradeWorker: Background worker managing the two-step firmware flashing process.PresetUpdateWorker: Background worker handling preset bank uploads.OtaFormat: Utility class responsible for packing (pack), parsing (parse, tryParse), and building metadata frames (buildMetaSequence).F0 00 32 45 00 00 00 40 7F F7F0 00 32 45 58 01 00 00 23 4D 5A 44 XX XX XX ... 40 [chk] F7
00 59 11 to a 34-byte ID block containing model identity (FM-1_015 or FM-1_014).0x40 (64). Queries sent to all other Parameter IDs produce no response.Analysis of the hardware verifier (AL-255/FM-1-RE, docs/io/11-ota-protocol.md, Issue #2) confirms that the firmware update is a device-pull architecture rather than host-push:
Step 1: Verification Mode
F0 22 24 35 7F F7.UPDATA_PARM boot record and soft-resets into the OTA loader.4D4A:4155 (ota-FM-1).Step 2: OTA Flash Upgrade Mode
F0 22 24 35 7F F7 again to the OTA loader.0xF0000000 (length 8); host replies with "success\0" on channel 00 32 41 01. The device then reboots into the new firmware.All payload data between the host and device is framed as:
F0 00 32 41 41 [f1:4][addr:4][len:4] [pack7(data)...] F7
00 32 41 41 (7-bit wire packing of internal channel 00 59 30).f1, addr, and len are 32-bit little-endian integers encoded as 4×7-bit groups (b0 | b1<<7 | b2<<14 | b3<<21).
len encodes (length << 4) | flashtype.f1 = 0.f1 = length >> 4.length data bytes plus 1 checksum byte:
checksum = ~(flashtype + sum(data) + sum(addr_LE4) + sum(len_LE3)) & 0xFF
In JieLi microcontrollers (AC791N / WL82 series):
0x00000000 (size 16 KB – 64 KB).app_dir_head / isd_config.ini): Partition table and pin definitions (Power Pin, UART, SD).app.bin): User application code starts at Flash offset 0x4000 (16 KB) or 0x10000 (64 KB) as specified by isd_config.ini.Flashing custom firmware via this protocol is fully supported, BUT requires strict adherence to bootloader re-entry logic:
[!CAUTION] CRITICAL BOOTLOADER RE-ENTRY REQUIREMENT:
Because the M-VAVE FM1 PCB lacks physical recovery buttons or debug pads, any custom firmware MUST retain or implement a mechanism to enter the bootloader / OTA mode (e.g. by listening for the SysEx verification/upgrade commands0x01/0x02or handling a button combination during startup).
If custom firmware is flashed without a bootloader entry handler, the device will be permanently soft-locked against future MIDI updates or rollback to stock firmware.
Requirements for compiling custom firmware:
origin Flash: 0x4000 / 0x10000).isd_tools): Package output binaries using isd_download / fw_pack into a UFW container with @JMUA / JLUFW headers.M-UPGRADE-FM1 or the custom Python script (fm1_flasher.py).Binary offsets extracted from analysis:
Native Application Binary:
0xEEDDC: Resource path string usb_hid_ota.bin.0xF14AD: Bootloader configuration block isd_config.ini, app_dir_head, uboot, POWER_PIN.0xF3BB8: Container signature @JMUA.0xF3CDC: JieLi firmware container magic JLUFW.0x14C010: SysEx header data symbol _s_arrSysexHead.Supported Hardware & UBOOT Documentation:
Firmware size is constrained by three factors:
AC791N chips feature 4 MB (32 Mbit) or 8 MB (64 Mbit) internal/external SPI Flash.
16 KB – 64 KB (0x00000000 – 0x00010000).16 KB – 64 KB of Flash.Embedded firmware too small: %1 bytes (min %2) (requires valid @JMUA header and >64 KB size).Code runs via XIP (Execute-In-Place) directly from SPI Flash via the MCU hardware cache. SRAM size limits dynamically allocated memory (.bss / .data), but does not restrict binary code size in Flash.
fm1_flasher.py)A standalone CLI tool fm1_flasher.py provides firmware extraction and experimental protocol tooling. Project environment is managed via uv (configured in pyproject.toml).
[!WARNING] FLASHING STATUS & PROTOCOL DISCLAIMER (Issue #2):
Direct firmware flashing implemented in fm1_flasher.py is experimental and non-functional on physical hardware because it assumes a host-push packet structure, whereas the actual synthesizer hardware runs a device-pull protocol with 8→7 LSB-first bitstream encoding (documented in Section 5).
For verified, byte-exact firmware flashing on physical hardware, usetools/fm1_ota.pyfrom AL-255/FM-1-RE.
--extract): Fully functional. Carves the exact 704,052-byte .fwsc package (0x47EB8–0xF3CEC, stock V14) from the macOS M-UPGRADE-FM1 binary by resolving the big-endian Qt resource length prefix and verifying the AC791N marker.--list): Scans and enumerates available MIDI inputs and outputs.--file): Research skeleton for protocol analysis.# List available MIDI ports
uv run fm1-flasher --list
# Extract embedded .fwsc firmware from macOS updater app
uv run fm1-flasher --extract FM-1_014.fwsc --app M-UPGRADE-FM1.app/Contents/MacOS/M-UPGRADE-FM1
# Flashing on physical hardware (use AL-255's verified tool):
python3 tools/fm1_ota.py flash FM-1.fwsc
Reverse-engineering JieLi microcontrollers (AC791N / WL82 / AC69x series) requires specialized tools due to JieLi's proprietary 32-bit RISC processor architectures (Pi32, Pi32v2, q32s).
Ghidra Processor Module (ghidra-jieli): An open-source Ghidra processor module targeting JieLi CPU architectures:
pi32: Functional disassembly support for older JieLi chips.pi32v2: Processor definition for AC791N (WL82 series). Enables Ghidra to parse function boundaries, construct control flow graphs (CFG), and trace MMIO register addresses (JL_PORTA, JL_PORTB, JL_PORTC).q32s: Processor definition for BD19/BD29 series.JieLi Official Toolchain (pi32v2-elf-objdump):
Provides 100% accurate instruction decoding from the vendor SDK, used in combination with Ghidra for verifying complex instruction groups.
Useful open-source tools, documentation, and SDK repositories:
USB_KEY hardware recovery dongle (dongle/ directory) that forces the JieLi AC791N SoC into mask-ROM USB download mode (UBOOT1.00) via the external USB-C port, enabling low-level flash backup and unbricking.pi32, pi32v2, and q32s CPU binaries..fwsc firmware files for the M-VAVE SMK-37 PRO (DX7 FM MIDI keyboard) and identifying the underlying JieLi AC791N platform structure using jl-misctools..syx files.58 followers · starred Sep 2026
M-VAVE FM1 reverse-engineering research, SysEx protocol tools, and firmware update scripts
Python
57
12 commits
updated Oct 7, 2026
This document contains technical research, hardware details, and reverse-engineering results for the M-VAVE FM1 synthesizer (powered by the JieLi AC791N processor) and the M-UPGRADE-FM1.app (macOS) utility.
Community-developed custom firmwares, mods, and experimental firmware projects for the M-VAVE FM-1:
Felucca by hugelton (Web Installer | Web Editor):
baud girl Custom Firmware Mod:
SLOOP by isod89 (Web Installer | Web Editor):
FoMni-1 by charlesvestal (Web Installer):
ChoralRoot FM-1 by Quixotic7 (Web Installer | Web Emulator):
Melodee by keremimo (Web Installer | Web Editor):
.syx import/export, 38 dedicated editing pages, full backup/restore before flashing, and WebMIDI management.FM1 Quest by hericdk (Web Installer):
[!TIP] Community Flashing Experience & Safe Upgrade Practice:
While not an absolute hard rule, there is a prominent community recommendation to avoid flashing one custom firmware directly over another. To minimize bricking risks, some users suggest rolling back to the official stock firmware (v15) via M-VAVE's official updater first, and only then flashing the next custom firmware build.
Target Device: M-VAVE FM1 (FM Synthesizer / MIDI Controller).
Processor SoC: JieLi AC791N (WL82 series, 32-bit RISC core).
Official Development Board: JL_AC79_DevKit V1.0 (AC791N evaluation kit).

Synthesizer Product Page: Cuvave / M-VAVE FM-1 Product Page
Latest Available Synthesizer Firmware: FM-1.fwsc (v15 2026-07-30)
Firmware v15 Entropy Analysis: (Source Issue #1)

M-VAVE Ecosystem: Many other M-VAVE audio products (MIDI foot controllers, audio interfaces, and digital pedals) use the same JieLi AC791N / AC69xx processor platform and share similar OTA firmware architectures.
UBOOT1.00) through the external USB-C port using a dedicated RP2040-based hardware dongle (or vendor JieLi USB Updater). This dongle bit-bangs the hardware boot key (0x16EF at ~50 kHz) over USB D+/D- lines at power-up, allowing SPI flash recovery and unbricking without opening the enclosure (see ip2k/mvave-fm1-open-firmware).To open the physical enclosure of the M-VAVE FM1:
Yes, the firmware update occurs entirely over the MIDI SysEx protocol.
The application does not use dedicated USB DFU/CDC emulation or direct low-level raw flash access during standard operation. The entire process (from handshake to streaming every byte of firmware and preset data) is executed via USB MIDI SysEx messages using the RtMidi library on top of macOS CoreMIDI.
The firmware binary is embedded directly inside the native executable file.
There are no external .bin or .hex files in the resources directory. Inside the executable file M-UPGRADE-FM1, the firmware image is stored as an embedded Qt resource package:
0x47EB4 (0x000ABC14 = 704,052 bytes).0x47EB8 – 0xF3CEC (704,052 bytes). Contains stock V14 (FM-1_014).@JMUA (offset 0xF3BB8), JLUFW (offset 0xF3CDC), and target CPU tag AC791N at +0x424.Asymmetric cryptographic signature verification (RSA/ECDSA/AES) is NOT implemented.
Security checks include:
@JMUA / JLUFW signatures, target device type, and version strings.Embedded firmware too small: %1 bytes (min %2).MainView, SelectFile, LoadingWidget).RtMidiIn, RtMidiOut, MidiInCore, MidiOutCore).OtaUpgradeWorker: Background worker managing the two-step firmware flashing process.PresetUpdateWorker: Background worker handling preset bank uploads.OtaFormat: Utility class responsible for packing (pack), parsing (parse, tryParse), and building metadata frames (buildMetaSequence).F0 00 32 45 00 00 00 40 7F F7F0 00 32 45 58 01 00 00 23 4D 5A 44 XX XX XX ... 40 [chk] F7
00 59 11 to a 34-byte ID block containing model identity (FM-1_015 or FM-1_014).0x40 (64). Queries sent to all other Parameter IDs produce no response.Analysis of the hardware verifier (AL-255/FM-1-RE, docs/io/11-ota-protocol.md, Issue #2) confirms that the firmware update is a device-pull architecture rather than host-push:
Step 1: Verification Mode
F0 22 24 35 7F F7.UPDATA_PARM boot record and soft-resets into the OTA loader.4D4A:4155 (ota-FM-1).Step 2: OTA Flash Upgrade Mode
F0 22 24 35 7F F7 again to the OTA loader.0xF0000000 (length 8); host replies with "success\0" on channel 00 32 41 01. The device then reboots into the new firmware.All payload data between the host and device is framed as:
F0 00 32 41 41 [f1:4][addr:4][len:4] [pack7(data)...] F7
00 32 41 41 (7-bit wire packing of internal channel 00 59 30).f1, addr, and len are 32-bit little-endian integers encoded as 4×7-bit groups (b0 | b1<<7 | b2<<14 | b3<<21).
len encodes (length << 4) | flashtype.f1 = 0.f1 = length >> 4.length data bytes plus 1 checksum byte:
checksum = ~(flashtype + sum(data) + sum(addr_LE4) + sum(len_LE3)) & 0xFF
In JieLi microcontrollers (AC791N / WL82 series):
0x00000000 (size 16 KB – 64 KB).app_dir_head / isd_config.ini): Partition table and pin definitions (Power Pin, UART, SD).app.bin): User application code starts at Flash offset 0x4000 (16 KB) or 0x10000 (64 KB) as specified by isd_config.ini.Flashing custom firmware via this protocol is fully supported, BUT requires strict adherence to bootloader re-entry logic:
[!CAUTION] CRITICAL BOOTLOADER RE-ENTRY REQUIREMENT:
Because the M-VAVE FM1 PCB lacks physical recovery buttons or debug pads, any custom firmware MUST retain or implement a mechanism to enter the bootloader / OTA mode (e.g. by listening for the SysEx verification/upgrade commands0x01/0x02or handling a button combination during startup).
If custom firmware is flashed without a bootloader entry handler, the device will be permanently soft-locked against future MIDI updates or rollback to stock firmware.
Requirements for compiling custom firmware:
origin Flash: 0x4000 / 0x10000).isd_tools): Package output binaries using isd_download / fw_pack into a UFW container with @JMUA / JLUFW headers.M-UPGRADE-FM1 or the custom Python script (fm1_flasher.py).Binary offsets extracted from analysis:
Native Application Binary:
0xEEDDC: Resource path string usb_hid_ota.bin.0xF14AD: Bootloader configuration block isd_config.ini, app_dir_head, uboot, POWER_PIN.0xF3BB8: Container signature @JMUA.0xF3CDC: JieLi firmware container magic JLUFW.0x14C010: SysEx header data symbol _s_arrSysexHead.Supported Hardware & UBOOT Documentation:
Firmware size is constrained by three factors:
AC791N chips feature 4 MB (32 Mbit) or 8 MB (64 Mbit) internal/external SPI Flash.
16 KB – 64 KB (0x00000000 – 0x00010000).16 KB – 64 KB of Flash.Embedded firmware too small: %1 bytes (min %2) (requires valid @JMUA header and >64 KB size).Code runs via XIP (Execute-In-Place) directly from SPI Flash via the MCU hardware cache. SRAM size limits dynamically allocated memory (.bss / .data), but does not restrict binary code size in Flash.
fm1_flasher.py)A standalone CLI tool fm1_flasher.py provides firmware extraction and experimental protocol tooling. Project environment is managed via uv (configured in pyproject.toml).
[!WARNING] FLASHING STATUS & PROTOCOL DISCLAIMER (Issue #2):
Direct firmware flashing implemented in fm1_flasher.py is experimental and non-functional on physical hardware because it assumes a host-push packet structure, whereas the actual synthesizer hardware runs a device-pull protocol with 8→7 LSB-first bitstream encoding (documented in Section 5).
For verified, byte-exact firmware flashing on physical hardware, usetools/fm1_ota.pyfrom AL-255/FM-1-RE.
--extract): Fully functional. Carves the exact 704,052-byte .fwsc package (0x47EB8–0xF3CEC, stock V14) from the macOS M-UPGRADE-FM1 binary by resolving the big-endian Qt resource length prefix and verifying the AC791N marker.--list): Scans and enumerates available MIDI inputs and outputs.--file): Research skeleton for protocol analysis.# List available MIDI ports
uv run fm1-flasher --list
# Extract embedded .fwsc firmware from macOS updater app
uv run fm1-flasher --extract FM-1_014.fwsc --app M-UPGRADE-FM1.app/Contents/MacOS/M-UPGRADE-FM1
# Flashing on physical hardware (use AL-255's verified tool):
python3 tools/fm1_ota.py flash FM-1.fwsc
Reverse-engineering JieLi microcontrollers (AC791N / WL82 / AC69x series) requires specialized tools due to JieLi's proprietary 32-bit RISC processor architectures (Pi32, Pi32v2, q32s).
Ghidra Processor Module (ghidra-jieli): An open-source Ghidra processor module targeting JieLi CPU architectures:
pi32: Functional disassembly support for older JieLi chips.pi32v2: Processor definition for AC791N (WL82 series). Enables Ghidra to parse function boundaries, construct control flow graphs (CFG), and trace MMIO register addresses (JL_PORTA, JL_PORTB, JL_PORTC).q32s: Processor definition for BD19/BD29 series.JieLi Official Toolchain (pi32v2-elf-objdump):
Provides 100% accurate instruction decoding from the vendor SDK, used in combination with Ghidra for verifying complex instruction groups.
Useful open-source tools, documentation, and SDK repositories:
USB_KEY hardware recovery dongle (dongle/ directory) that forces the JieLi AC791N SoC into mask-ROM USB download mode (UBOOT1.00) via the external USB-C port, enabling low-level flash backup and unbricking.pi32, pi32v2, and q32s CPU binaries..fwsc firmware files for the M-VAVE SMK-37 PRO (DX7 FM MIDI keyboard) and identifying the underlying JieLi AC791N platform structure using jl-misctools..syx files.58 followers · starred Sep 2026