Pangram verdict · v3.3
We believe that this document is fully human-written
AI likelihood · overall
HumanArticle text · 1,826 words · 6 segments analyzed
May 28, 2026 Contents Hardware Identifying and soldering the connectors Testing the UART JTAG Software Configuring OpenOCD Dumping the address space Firmware disassembly and analysis Relinking the firmware Patching the firmware More firmware analysis Making my changes Buttons LEDs Building an API My code Fixing bypass gain Flashing the new firmware Firmware update format Applying the update Future directions MIDI 2 property exchange Custom DSP effects Combined “mega-firmware” Overall thoughts I’m having a lot of fun with reverse engineering lately, so when I was looking over the service manual for my guitar amp, a Yamaha THR10c, and saw references to a UART header in the schematic, I got excited. I wondered if anything cool was hiding in there. Next to it was a JTAG header. I knew next to nothing about JTAG, but had heard the name before associated with hardware hacking, so maybe that would be useful as well. Yamaha THR10c UART and JTAG headers in schematic I had a few ideas of some changes I wanted to make to the firmware, so my main goal with this project was to find a way to dump the firmware and reflash the amp with a modified firmware. One initial idea I had in mind was to add a way to toggle the guitar speaker simulation on and off so I could hook it up to real guitar speakers (either through a modification to add speaker out jacks, or hooking it up to a TPA3118 power amp module). While not officially supported, you can do this in the stock firmware by using a computer or phone to send a special MIDI SysEx command over USB. However, the speaker simulation reverts back to normal when you change the amp model, and the volume is raised considerably as a side-effect. Another thing I wanted was a mode where the internal speaker would play even when the headphone port was connected (for instance, to additional speakers or a mixer). Hardware I opened up the amp as described in the service manual and located CB3 and CB4 on the main PCB. Unpopulated UART and JTAG ports Identifying and soldering the connectors It seemed easy enough to solder on connectors to these headers, I just needed to identify which connectors to use. I measured the pin pitch of CB4 at 2mm, and CB3 at 1mm.
The UART connector was pretty easy to identify as from the JST PH series (B4B-PH-K). The JTAG connector wasn’t so obvious to me. Was it two staggered rows of 2mm pitch or one row of 1mm pitch with staggered contacts? I searched through online connector identifiers, as well as DigiKey and Mouser product indexes, but I didn’t find anything that seemed to match. I eventually noticed a similar footprint on another part of the board that used an flat flexible cable (FFC). This is why I hadn’t found it before; I didn’t think to look through FFC connectors. I saw a JST marking on that one, and I believe it is from the FMN series. Mouser didn’t have the 8-pin reverse version in stock (08FMN-BMTTR-A-TB), so I went with the compatible Molex 52808-0870. CB3 and CB4 connectors Once I received the new connectors in the mail, I removed the main PCB from the case and then cleared the CB4 through-holes with a solder pump. This was a bit tricky for the ground pin, which conducts heat to the ground plane very easily, but with some patience I eventually got it all clean. I then soldered on the new connectors. I don’t have much experience with SMD soldering, so CB3 gave me some trouble, but I’m happy with how it turned out. CB3 and CB4 connectors soldered on main PCB I fed cables for the two new connectors though slots at the back of the amp so I could close it up and still use it while working on this project. Testing the UART Now that I had connectors in place I hooked it up to my computer using a USB-UART cable and powered on the amp. Unfortunately, nothing was printed at all. I tried a bunch of different common baud rates and still nothing. I confirmed with an oscilloscope that there was no activity on TX. Well, that’s a bummer. Maybe I’ll have better luck with JTAG. JTAG After reading more about JTAG and what it’s used for, it seemed quite promising. JTAG is a serial interface designed for hardware testing, and is commonly used for debugging embedded processors.
It has four main pins, TCK, TMS, TDI, and TDO, which are used to interact with a state machine called a TAP controller. TCK, TMS, and TDI are all outputs from your JTAG adapter; TMS is used to navigate the state machine, TDI provides data input, and TCK is a clock which samples the inputs and advances to the next state. TDO is an output from the TAP controller and provides data output. Optionally, there is also a TRST pin used to reset the controller. There are lots of diagrams showing the TAP state machine available online, and I think having a basic mental model was helpful for getting started. One of the best resources I found was a post by wrongbaud, which walked through the whole process of using JTAG with a device you know nothing about. It seems that the most popular and well supported JTAG adapters are the FTDI FT2232H-based ones. One benefit of this chip is it has two multi-purpose channels, so I can use JTAG on one and UART on the other (assuming I could get it working). There are lots of options available here, but most of the links to these products from the UrJTAG and OpenOCD documentation were dead. I went with the FTDI FT2232H Mini Module. FTDI FT2232H Mini Module The JTAG header (CB3) on my amp had 8 pins, most of which were connected directly to a labeled pin on the SSP2 (the main chip of the amp) so I didn’t have to go through the process of discovering the mapping myself. There was one pin that went into an AND gate and then to a pin labeled ICN on the SSP2. The other input to the gate was the output of an R3112N291A-TR-F IC. This appears to be some sort of low voltage detector, and the datasheet mentions that it can be used for system reset, which seemed likely. On the FT2232H, AD0-AD3 are used for TCK, TDI, TDO, and TMS respectively. AD4-7 are regular GPIO pins and are configured in software.
When I first connected my amp to the mini module, I did not connect the reset pins, which turned out to be a problem: TRST is active-low and pulled to ground by R17, so to enable the TAP controller it needs to be set high. This can be done by connecting TRST to VCC, but to allow the JTAG software to reset the TAP controller, we can use the GPIO pins on the mini module. I used AD4 for TRST and AD5 for SRST. CB3 PinMini Module PinDescription 1VCC 2CN2-14 (AD4)TRST 3CN2-10 (AD1)TDI 4CN2-12 (AD3)TMS 5CN2-7 (AD0)TCK 6CN2-9 (AD2)TDO 7CN2-13 (AD5)SRST 8CN2-6GND CB3 pinout I hooked all this up to a 2x5 header, along with a jumper wire from V3V3 to VIO as described in the mini module datasheet. I also added a jumper from CN3-1 to CN3-3 to use USB bus-power for the FT2232H. FT2232H Mini Module with adapter cable Here’s the amp all closed up with JTAG and UART hooked up to the mini module. Software Now that all the hardware bits were in place, I could start using software like OpenOCD and UrJTAG to interact with my amp over JTAG. I first tried UrJTAG as suggested by wrongbaud, but ran into some trouble with the TRST pin since it was not configurable. I did get it working by tying it to VCC, but I think it would have been easier to go straight to OpenOCD. Configuring OpenOCD The default interface/ftdi/minimodule.cfg script from OpenOCD does not set up TRST either, but it can be configured with a custom script.
adapter driver ftdi ftdi device_desc "FT2232H MiniModule" ftdi vid_pid 0x0403 0x6010 ftdi layout_init 0x0008 0x000b ftdi layout_signal nTRST -data 0x0010 -oe 0x0010 ftdi layout_signal nSRST -data 0x0020 -oe 0x0020 minimodule.cfg I launched OpenOCD with openocd -f minimodule.cfg -c 'transport select jtag', and was happy to see it had discovered the TAP controller. Warn : There are no enabled taps. AUTO PROBING MIGHT NOT WORK!! Info : JTAG tap: auto0.tap tap/device found: 0x4f1f0f0f (mfg: 0x787 (<unknown>), part: 0xf1f0, ver: 0x4) I searched around for the JTAG ID 4F1F0F0F, which seemed to be commonly used by the NXP LPC 2xxx series, which are based on an ARM7TDMI-S core. It seemed like a reasonable assumption that the SSP2 also used an ARM7TDMI-S core, so I started an OpenOCD config file. transport select jtag jtag newtap ssp2 cpu -irlen 4 -expected-id 0x4F1F0F0F target create ssp2.cpu arm7tdmi -chain-position ssp2.cpu reset_config trst_and_srst Initial thr10.cfg Indeed, it seemed to work. openocd -f minimodule.cfg -f thr10.cfg started with the message saying it had detected EmbeddedICE version 7, which provides debug support for ARM7 cores. I was now able to connect with gdb using gdb -ex 'target extended-remote :3333' and read and write memory and registers, as well as step through the code the processor was executing! However, the default of little-endian was not correct for this target.
ARM7TDMI cores can run in big-endian or little-endian depending on an input pin, and it was clear that the SSP2 used big-endian by comparing memory read byte-by-byte against memory read word-by-word: (gdb) x/8xb 0 0x0: 0xe3 0xa0 0xf4 0x02 0xe5 0x9f 0xf0 0x18 (gdb) x/2xw 0 0x0: 0xe3a0f402 0xe59ff018 I went back to update the config file to correct the endianness. While I was here, I bumped the JTAG adapter speed to 2000 KHz since the THR10 uses a 12.288 MHz clock and the OpenOCD documentation suggests at most one sixth of the CPU clock. I also added -ircapture 0x1 -irmask 0xF based on the ARM7TDMI reference manual; the TAP instruction register is 4 bits wide and upon entering the CAPTURE-IR state it is loaded with 0x1. These parameters are used by OpenOCD to verify that JTAG is operating correctly. Finally, I found I also needed adapter srst delay 100 to get system reset to work. transport select jtag jtag newtap ssp2 cpu -irlen 4 -expected-id 0x4F1F0F0F -ircapture 0x1 -irmask 0xF target create ssp2.cpu arm7tdmi -chain-position ssp2.cpu -endian big adapter speed 2000 adapter srst delay 100 reset_config trst_and_srst thr10.cfg The GDB target description format doesn’t indicate the target endianness, so I had to configure gdb for big-endian as well. I did this with a file containing startup commands. I also enabled disassemble-next-instruction, which was quite helpful for following what was going on. target extended-remote :3333 set endian big set disassemble-next-instruction on thr10.gdb Now, I could launch gdb with gdb -x thr10.gdb and everything gets configured correctly.