• Recovering a bricked Commodore 64 Ultimate over JTAG with a Raspberry Pi

    Recovering a bricked Commodore 64 Ultimate over JTAG with a Raspberry Pi

    Bricked Commodore 64 Ultimate on a desk with a Raspberry Pi 4 and JTAG jumper wires connected to its P5 header.
    The bricked Commodore 64 Ultimate next to the Raspberry Pi that brought it back.

    So this happened: I bricked my Commodore 64 Ultimate flashing a custom firmware build I’d put together off Gideon’s master. Not a hard brick — networking stayed up and the management menu was reachable — but BASIC handoff was permanently broken, and the in-menu update flow that would normally let me flash a known-good build back quietly failed every time. Through-the-front-door recovery wasn’t available.

    The good news: Gideon Zweijtzer (the C64U’s designer) ships an official JTAG recovery procedure for exactly this situation. The not-so-good news: it requires an FT232H USB-MPSSE adapter, and I didn’t have one on the desk. Mouser delivery would be 2–5 days, and Friday was Vappu (Finnish May Day) so even slower.

    I had a different option, though. I already had a Raspberry Pi wired up as a Xilinx JTAG programmer for some unrelated FPGA work. JTAG is JTAG — at the protocol level, the Pi’s GPIO can do exactly what an FT232H can do, just slower. So I ported Gideon’s recovery script to the Pi.

    The “unrelated FPGA work” is a small pile of earlier Amiga-era projects on the same rig: programming the Cyclone V SoC for the Z3660 accelerator (upstream repo); bringing up the Firebird PCI daughterboard during my Amiga 4000D restoration; and assembling a MultifixAGA board. Same Pi, same xc3sprog/openFPGALoader toolchain, different bitstreams. So this time, when the C64U was in trouble, the rig was already on the desk.

    This is the story of that port, the bug I hit along the way, and the open-source repo it turned into.

    What broke

    I had been running the Commodore-line c64u_v1.1.0.ue2 firmware happily for a while, and I’d been chasing upstream. Gideon’s master branch has accumulated nice things since 1.1.0 — mouse-pointer support among them — so I’d taken a few stabs at building a C64U-compatible artifact straight off master myself. The C64U target on master needs more toolchain love than my off-the-shelf setup was willing to give it; some attempts fell over at link/pack, and the ones that did produce a flashable .ue2 all had something subtly wrong with the C64-mode handoff.

    I flashed one anyway. The result was an interesting half-broken state. The management plane stayed alive — networking was reachable throughout, and once I’d re-poked the HDMI settings through the telnet menu the on-screen menu UI itself looked normal. As far as the Ultimate’s own face was concerned, the device was up.

    The problems were below that:

    • BASIC handoff hung every time. The C64 side of the device — the whole reason the thing exists — wouldn’t come up. Drop into the menu, try to launch C64 mode, and the device would freeze. No build attempt I made off master ever fixed this; BASIC handoff would always still fail.
    • The in-menu .ue2 LOAD path quietly failed. That’s the normal recovery route: load a known-good firmware from a USB stick through the menu. It returned no error and didn’t actually flash anything. The obvious “just flash 1.1.0 back through the front door” path was closed.

    This is the canonical “bricked by incompatible firmware” state. The C64U Mark 2 / Elite II hardware family has had a few cases of this in the wild — see GideonZ/1541ultimate#537 and the related PR #636 for the upstream context.

    The official recovery path

    Gideon’s recovery is a two-stage JTAG procedure that runs entirely in volatile memory — it never touches flash, and a power-cycle reverts everything. The stages are:

    1. Load a recovery FPGA bitstream (u64_mk2_artix.bit) into the on-board Artix-7 fabric. This is just a normal Xilinx 7-series JTAG configuration — xc3sprog and openFPGALoader both speak it natively. Once configured, the FPGA exposes a custom JTAG-AXI bridge via the standard Xilinx USER4 BSCANE2 primitive.

    2. Talk to that bridge to (a) assert the on-FPGA RISC-V soft-core’s reset, (b) stream a recovery RISC-V firmware image (ultimate.bin) into DRAM in 16 KB chunks, (c) write a boot-magic word at a specific DRAM address, and (d) release the CPU from reset so it boots from DRAM.

    After Stage 2, the device comes up with a working recovery menu on HDMI. From there, you run a normal update.ue2 from a USB stick to permanently restore flash. Until you do that last step, the recovery is volatile — power-cycle and you’re back to the bricked state.

    Gideon ships a Python script (recover.py) that does Stage 2 over an FT232H USB-MPSSE bridge via pyftdi. Stage 1 you can do with xc3sprog or openFPGALoader against the same FT232H.

    The Raspberry Pi alternative

    The Pi JTAG rig on my desk was already wired up for Xilinx programming via the MATRIX Labs xc3sprog fork, using the well-known LinuxJedi 2025 Raspberry Pi JTAG layout. The pinout there matches xc3sprog‘s matrix_creator / gpiod_creator cable definition:

    Pi BCM (board pin) Signal C64U P5 pin
    GPIO 17 (pin 11) TCK 1
    GPIO 4 (pin 7) TMS 5
    GPIO 22 (pin 15) TDI 9
    GPIO 27 (pin 13) TDO 3
    GND (pin 6/9/14/…) GND 2 or 10

    Don’t connect 3.3V — the C64U powers its JTAG side from its own PSU. Connecting Pi 3.3V to the C64U’s 3.3V rail back-feeds the C64U from the Pi.

    Wiring diagram showing Raspberry Pi 40-pin GPIO header connected to the Commodore 64 Ultimate's P5 JTAG header. Five signals — TCK, TMS, TDI, TDO, GND — each on a single coloured jumper wire. A red warning bar at the bottom warns NOT to connect 3.3V.
    Pi GPIO ↔ C64U P5 JTAG header. One jumper wire per signal — no level shifter, no special hardware. Pin assignments match the standard xc3sprog matrix_creator / LinuxJedi 2025 Pi-JTAG layout.

    The chain test (Stage 0) was reassuring:

    $ xc3sprog -c matrix_creator -j
    JTAG loc.: 0  IDCODE: 0x0362c093  Desc:  XC7A50T Rev: A  IR length: 6

    That’s the C64U’s Artix-7 IDCODE coming back exactly as expected. Wiring confirmed. Now I just needed Stage 2 — Gideon’s recover.py, ported away from pyftdi to plain Pi GPIO bit-banging.

    The port

    Worth saying up front: this is where Claude did most of the heavy lifting. Reverse-engineering Gideon’s recover.py and the BSCANE2 user-side protocol cold, working out what the bridge actually expected, mapping the FT232H reference onto a bit-banged GPIO transport — that was Claude leading the analysis and me driving the engineering: integrating, debugging on hardware, deciding what to ship. Where I say “I” in the next two sections, it should mostly read “we”.

    Gideon’s recover.py is about 170 lines. The vast majority of it is bridge-protocol code: the BSCANE2 IR opcodes, the DR formats, the address-command structure, the boot-magic semantics. None of that needed to change between FT232H and Pi GPIO — it’s the same JTAG protocol either way.

    What did need to change was the transport layer: the JTAG TAP state machine, IR/DR shifts, TDO sampling. That’s what pyftdi was abstracting; on the Pi I had to implement it from scratch on top of RPi.GPIO‘s raw pin operations.

    A JtagBitbang class that maintains TAP state and exposes irscan() / drscan_int() / drscan_bytes() was about 120 lines of straightforward TAP-state-machine bookkeeping. Then I dropped Gideon’s protocol layer on top of it almost verbatim.

    I deployed it to the Pi, ran the smoke test… and got the right IDCODE. So far so good.

    The bug

    I ran the full recovery. It completed without errors. ~960 KB streamed to DRAM in about 70 seconds. CPU released from reset. And then…

    …nothing. HDMI stayed dark. The C64U was unchanged.

    Worse: when I read the bridge’s user-side IDCODE after boot — which Gideon designs to return a deliberate magic value (0xdead1541) once the recovery firmware is running — I got back garbage:

    INFO Releasing CPU reset (output 0x00) — RISC-V should now boot from DRAM…
    INFO User-side IDCODE: 0xbd5a2a83

    0xbd5a2a83 is “the bridge protocol got desynced” garbage. The recovery firmware almost certainly hadn’t booted at all.

    The bug took a few hours to find. It comes down to a subtlety in how set_user_ir is supposed to drive the JTAG TAP state machine.

    The bridge expects a sequence like this: USER4 IRSCAN → a 5-bit DR write that selects the inner bridge IR → USER4 IRSCAN → enter Shift-DR, shift a single mode-bit “0” to mark “what follows is data”, then continue shifting the payload in the same Shift-DR run, finally exiting with one Update-DR at the end. The bridge sees the mode bit and the payload as one continuous DR transaction committed by a single Update-DR.

    My initial port was doing it the obvious-looking way: shift the mode bit, exit Shift-DR (Update-DR + go back to RTI), then start a fresh Shift-DR for the payload. That produces two Update-DR events instead of one, and the bridge interprets the second drscan’s payload as if it were bridge-IR, not data. Everything downstream was operating on the wrong register.

    The fix is one parameter change. set_user_ir now leaves the TAP in Shift-DR (no exit), and the next drscan continues from there:

    def set_user_ir(self, ir):
        self.j.irscan(XILINX_USER4)
        self.j.drscan_int((ir << 1) | 1, 5, exit_to_rti=True)
        self.j.irscan(XILINX_USER4)
        # Enter Shift-DR, shift the '0' mode bit, STAY in Shift-DR.
        self.j.drscan_int(0, 1, exit_to_rti=False)
    
    def read_user_dr(self, n_bits):
        # Continues from Shift-DR. One final Update-DR commits the full
        # mode-bit + n_bits to the bridge.
        return self.j.drscan_int(0, n_bits, exit_to_rti=True)

    pyftdi handles this implicitly via its higher-level shift_register() API; it never exposes the intermediate Update-DR boundary, so Gideon’s original script doesn’t need to think about it. On a hand-rolled bit-banger that can pulse TMS=1 too eagerly on the last bit, it bites you. I wrote up the full TAP-state-machine analysis with a Mermaid diagram in docs/PROTOCOL.md for anyone porting BSCANE2-based recovery scripts to other JTAG transports — this is exactly the kind of detail that’s invisible in the upstream code but critical when you change layers.

    The recovery itself

    Vertical recovery flow chart from Stage 0 (chain test) through Stage 3 (permanent flash). Stages 0-2 are marked VOLATILE on the right side; Stage 3 is marked PERSISTENT in red.
    Recovery flow. Stages 0–2 only touch FPGA fabric and DRAM, so they’re volatile — every power-cycle reverts. Stage 3 (the permanent fix) runs from inside the recovered HDMI menu and writes flash.

    With the bug fixed, the recovery ran clean:

    INFO IDCODE: 0x0362c093
    INFO User-side IDCODE: 0x0362c093
    INFO Asserting CPU reset (output 0x80)…
    INFO Uploading ./ultimate.bin to DRAM at 0x00030000…
    INFO Uploaded chunk -> next addr 0x00040000, total 16384 bytes
    …
    INFO Upload complete: 984040 bytes
    INFO Writing boot magic at 0x0000fff8: addr=0x00030000 sig=0x1571babe
    INFO Releasing CPU reset (output 0x00) — RISC-V should now boot from DRAM…
    INFO User-side IDCODE: 0xdead1541
    INFO Stage 2 done. Watch the C64U: HDMI/menu should come up shortly.

    0xdead1541 is Gideon’s deliberate “recovery firmware booted” signature. As soon as that came back, HDMI lit up:

    Photo of an HDMI screen showing the Commodore 64 Ultimate Updater menu with 'Detected FPGA type', 'Board Revision: C64U V2.5 (Mass Prod)' and a 'Reformat Flash Disk?' yes/no prompt. Below the screen, the open Commodore 64 Ultimate is visible on a desk with a JTAG ribbon cable connected to its P5 header.
    Recovery menu booted from DRAM, detecting the board as ‘C64U V2.5 (Mass Prod)’ and prompting to reformat flash. JTAG ribbon visibly running into P5 on the open carrier board below.

    The recovery menu detected the board correctly — C64U V2.5 (Mass Prod) — and offered to reformat the flash disk. End-to-end Stage 2 runtime was about 70 seconds; total time including Stage 0 chain test and Stage 1 FPGA load was under 2 minutes.

    But the recovery so far was only in DRAM. The flash was still bricked. Power-cycling at this point would lose everything.

    The flash restoration

    To make the recovery permanent, I navigated to a known-good c64u_v1.1.0.ue2 on a USB stick from inside the recovered HDMI menu and ran it. That’s the official update flow, just running on top of a temporarily-recovered device instead of a working one.

    Photo of an HDMI screen showing the Commodore 64 Ultimate Updater writing files: 'Creating /flash/roms/: OK!', 'Writing 1571.rom to /flash: OK!', and 'Flashing Runtime FPGA.. Programming 5514'.
    Stage 3 in progress: the recovered Updater is writing the runtime FPGA bitstream and the kernal/BASIC/1541/1571/1581 ROMs back to flash from a known-good update.ue2.

    The Updater wrote everything to flash: the runtime FPGA bitstream, the kernal/BASIC/1541/1571/1581/SD ROMs, the ESP32 firmware, the Ultimate application, the management web HTML. About 3–5 minutes, then auto-reboot.

    After reboot, the device came up on its own permanently-flashed firmware. JTAG cable came off. Power-cycled it a few times to be sure. All good.

    Takeaways

    A few things worth pulling out of this experience:

    1. Bricks aren’t bricks. As long as the JTAG header on the board is functional and the FPGA itself isn’t physically dead, the device is recoverable. Flash being garbage just means flash needs to be bypassed via volatile JTAG-loaded code, then rewritten from inside that recovered environment.

    2. FPGA bitstream versions matter — but you can JTAG-load any compatible recovery bitstream regardless of what’s currently in flash. The recovery FPGA + RISC-V image pair from upstream’s recovery/u64ii/ is what makes this work on the C64 Ultimate hardware family.

    3. Any FT232H-based JTAG recovery script can probably be ported to a Pi, if the protocol underneath is just standard IRSCAN/DRSCAN sequences. The non-obvious part is the TAP state machine — specifically how aggressively your transport pulses TMS to exit Shift-DR. Anything where the upstream protocol assumes “stay in Shift-DR across calls” needs a transport that allows it.

    4. Diagnose by checking the bridge IDCODE. Gideon’s 0xdead1541 signature is brilliant — it gives you a one-bit answer to “did the recovery firmware actually boot?” If you don’t get that back, your protocol or wiring is wrong, regardless of what the upload step claimed.

    A small Bookworm gotcha (GPIO library)

    One thing tripped me up early. On Raspberry Pi OS Bookworm (Debian 12), my first instinct was to use libgpiod’s Python bindings — they’re the modern, Pi-5-friendly choice. But:

    • apt install python-gpiod doesn’t exist. The package is named python3-libgpiod.
    • The PyPI gpiod package’s API changed significantly between v1 and v2, and the v1 examples I had on hand didn’t drop in cleanly to the v2 binding shipped on Bookworm.

    I fell back to the older RPi.GPIO library via apt install python3-rpi.gpio, which the Raspberry Pi Foundation has specifically patched for Bookworm. That worked first try. Do not pip install RPi.GPIO on Bookworm — the upstream PyPI version is older and broken under the newer kernel; only the apt build is current.

    For a future Pi-5 backend, lgpio or python3-libgpiod v2 would be the right path; PRs welcome.

    What’s next

    Post-recovery, the device is back on 1.1.0 stable — but the original goal hasn’t moved: I want a build off Gideon’s master running on the C64U, with the mouse-pointer additions and everything else that’s landed upstream since 1.1.0. The next round is figuring out the proper build recipe for the C64U target on current master — what the toolchain actually expects, which configuration knobs need to be flipped, and where the C64-mode handoff is going wrong on my own builds. Ideally I’d have it nailed down before the next official Commodore firmware drop makes the question moot.

    Code & credits

    The full recovery tool is open source: github.com/jusii/ultimate64-jtag-recovery-pi (GPLv3, matching upstream). Tagged release: v1.0.0.

    The repo includes:

    • recover.py — Stage 2 (DRAM upload + boot)
    • soft_reset.py — bonus utility, soft-resets the recovered RISC-V via JTAG without redoing the full sequence
    • shell wrappers for Stage 0 (chain test) and Stage 1 (FPGA load)
    • Verbatim copies of upstream’s u64_mk2_artix.bit and ultimate.bin recovery artifacts (with refresh instructions in the README)
    • The wiring diagram and recovery-flow diagram embedded above
    • docs/PROTOCOL.md with the BSCANE2 user-side protocol breakdown and the TAP-state-machine analysis

    This work would not have been possible without:

    • Gideon Zweijtzer (@GideonZ) — designer of the entire 1541 Ultimate firmware family, author of the original recover.py, designer of the BSCANE2 user-side recovery bridge. The protocol layer in my repo is byte-faithful to his FT232H reference implementation; only the JTAG transport was reimplemented.
    • LinuxJedi — whose Raspberry Pi JTAG Programming 2025 Edition guide documents the Pi-as-Xilinx-programmer setup and the GPIO pin layout I ended up using as default.
    • MATRIX Labs xc3sprog forksysfscreator cable definition, source of the default pinout.
    • Gee-64, robotfreak, and the contributors to upstream issue #537 and PR #636.
    • Claude  — did most of the reverse-engineering of Gideon’s recover.py and the BSCANE2 user-side protocol, including the TAP-state-machine analysis that uncovered the Update-DR bug.

    If you have a similar hardware mishap, the procedure is documented step-by-step in the repo README. This Pi port is unofficial and isn’t endorsed by upstream. If you want to share that it worked, compare hardware revisions, or report a problem, drop a comment here on the blog or open an issue/discussion on the recovery repo.

  • Finalizing Amiga 4000D (…for now)

    Finalizing Amiga 4000D (…for now)

    Finally all 3D printed parts are ready, time for assembling everything together.

    Gotek, 3.5″ Floppy and DVD-ROM mounted to my new 3D printed drive cage. When putting cage in, you can move it few millimeters to right so SD extender cable has plenty of room to slide inside.

    Moment of truth, putting front bezel back. And it fits perfect!

    First Firebird daughter card which provides 4 PCI slots, 4 Zorro slots and 1 Video slot. In video slot I have the Scandoubler daughterboard from Cybervision 64/3D, it just has superior image quality compared to native output or any VGA-adapter connected to it, even OSSC. I modeled bracket for it, as the original stays with the Cybervision card. You can find that also at printables.com.
    Above that sits S3 ViRGE display card, but funny enough, its only purpose in this setup is to provide DMA memory for other PCI cards.
    And on top there’s USB host card with NEC chip from which the USB extender cable goes to front face of Gotek.
    On backplane you can see the HDMI and Ethernet bracket with extender cables. They’re routed back to Z3660.

    And believe it or not, case closed! Eagleplayer playing Das Boot, Ami-Back taking backup to NAS over SMB2, which btw works just great! 100MB+ backup in five minutes or so.
    And the black box you see on top of Amiga, is a passive audio mixer I quickly built. As I now have AHI audio besides native Amiga audio, I wanted to have them both to play through same AMP input I have, so no switching inputs for sound.

    Schematics for the mixer are pretty simple, all inputs connected to output through 1k resistor. You could add volume level controls with potentiometers, but as I tested this, both audio outputs levels were pretty much the same.

  • New Gotek bracket with SD card reader

    New Gotek bracket with SD card reader

    As Amiga 4000D doesn’t have too much spare space in front for additional gadgets and connectors, I decided to model completely new 3.5″ Gotek mounting bracket with 0,91″ OLED display, rotary encoder, additional USB port and SD card reader slot. You can find it at printables.com. My first model ever with Fusion 360, so the model file itself is kinda mess, sorry.

    I had to remove Gotek LEDs and buttons, which are redundant because of the rotary encoder, to fit the USB extender there. It’s just fixed to the PCB, possibly you could add normal PCB USB connector there, but didn’t have one available. This seems to work just fine. Other end of the USB cable goes to USB PCI card with NEC chipset which has 4 external and 1 internal USB port.

  • Fitting Gotek with SD card reader to front

    Fitting Gotek with SD card reader to front

    Like I said, things has to be done properly. As Z3660 limits the length of sd card extender (150-200mm), it can’t be routed to the back. I already has designed Gotek carriage in place of second 3.5″ floppy drive with additional normalk USB-connector. I decided to modify that a little and add sd card holder below the OLED screen. Here’s first prototyping.

    And as you can see from the picture, I had to make and redesign the whole 5.25″ / 3.5″ drive bay, as there’s no way to route sd card extenders cable through the original carriage. But that’s how these projects usually go. Above model is by LinuxJedi and you can find it at Thingiverse.

    A4000D Front drive bay is ready, final version currently printing. You can find it at printables.com. Had to make quite a few revisions, I first thought that Gotek would go on lower bay but then I found out that that won’t work with my only floppy cable and its orientation, the DF0 device has to go to bottom slot. So I made new revision which has all screw holes for 3.5 drives and passthrough for SD card extender on both bays.

  • Project Z3660 accelerator

    Project Z3660 accelerator

    Next soldering project, Z3660. PCB’s arrived from JLCPB partly populated as you can see from the picture. Bottom side and all the connectors was my job.

    And fully populated board, including 68LC060 CPU.

    And as I like everything to be done properly, I got really nice bracket for HDMI and Ethernet connectors.

    And cables routed behing daughtercard to the Z3660 HDMI and Ethernet connectors.

  • Amiga 4000D restoration and tweaks

    Amiga 4000D restoration and tweaks

    Quick recap what I’ve done to this day with Amiga 4000D. Found this, mostly by accident, from local ‘ebay’ for 200€. Just by pure luck, it was located next to my home town only 30 minutes drive. As I could promise, I’ll be picking it up in less than an hour, it was mine. Poor guys inbox must have been filled in just minutes.

    It was in running state, but by no means it was fully working unit. And for extra 20€ I got (faulty) 68040 CPU card and for 150€ fully working Cybervision 64/3D. Inside there was 68EC030 CPU card.

    And as expected, it had the most common faults. Leaking caps, varta damage around RTC and broken SIMM clips. Couple traces around audio circuit was also busted with opamp. But nothing special.

    And here it’s running fully equipped for the first time. 3D printed sd card carrier for easy swap.

    And as you can see, already new addon waiting to be assembled. It’s PCI daughtercard designed by Hese, so instead of only Zorro or ISA cards, I can add PCI cards to add networking, USB, Graphics card etc.

    Daughtercard assembled and programming firmware with Raspberry Pi as JTAG. Did this before soldering rest of the connectors so it would be easier to debug, if the card wasn’t working.

    And then some real life testing, CV64/3D is found and so are NEC USB card and RTL8xxx network adapter.

    But then I learned after this point, that to get those PCI cards to actually work, I’d need some PCI graphics card which would then provide DMA memory for most of the other cards to work. Oh well. I did find some 20€ S3ViRGE PCI card afterwards, but before that I already learned about opensource Z3660 project which is basically CPU accelerator, RTG (graphics card), Soundcard, ‘virtual’ SCSI emulator etc. And I was sold, but more about that in next post.

  • Caffeine OS for z3660 accelerator

    How to modify Caffeine OS to run on z3660

    First extract caffeine partition from the image CaffeineOS_Storm_9311.img (with Linux below. In Windows I guess you can use Win32DiskImager, install WSL and use dd as above or download Windows version of dd command)

    Use kpartx to make disk and partition loop-back devices for easy partition extraction:

    jusii@lucifer:~/Downloads$ sudo kpartx -av CaffeineOS_Storm_9311.img 
    add map loop12p1 (252:1): 0 2299904 linear 7:12 2048
    add map loop12p2 (252:2): 0 118235136 linear 7:12 2301952  # Caffeine partition
    

    Copy partition 2 which contains Caffeine OS to separate image file:

    dd if=/dev/mapper/loop12p2 of=CaffeineOS_z3660_9311.hdf bs=4M status=progress conv=fdatasync

    Run FS-UAE / WinUAE with the extracted image as file hard drive. After running Caffeine initial setup go to DEVS:Monitors and delete file emu68-VideoCore without this I just got some garbled and empty WB screen

    Copy image to z3660 sdcard partition 2 under hdf and edit z3660cfg.txt
    Copy kickstart 3.1 to kicks directory and edit z3660cfg.txt

    Insert into zTurn and boot

    MakeLink in s:startup-sequence gave some error code, ignored it for now and just edited it out.

    ed s:startup-sequence and reboot

    Quick test and ran tiny bobble with whdload.

    Next ‘butchered’ version, ie 3.9 -> 3.2.2.1 with the provided script.

    This might be easier with WinUAE as Caffeine OS fat partition contains ready config file to boot Caffeine OS with Windows and WinUAE. First grab latest TheButcher.lha from Pedro. I extracted this to directory and added directory as Directory hard drive in WinUAE for easy access. You can also copy it to WB, extract there etc.

    When you have WinUAE configured with Caffeine OS (sdcard or file hard drive) + TheButcher, then just start WinUAE, do initial setup and run the butcher. And the rest you can follow from above, extracting partition 2 etc. And make sure you have correct kickstart, 3.2.2.1 (47.111) in place.

    Then with Linux and FS_UAE. Pretty straightforward too. Just go with the same instructions as with non-butchered versio. Only 2 differences comes when you first run Caffeine OS from extracted image.
    In here too, put extracted TheButcher directory as Directory hard drive. You also need to provide Caffeine OS FAT32 partition as USERFAT volume, or TheButcher won’t run! (you can Alternatively edit TheButcher script and delete all USERFAT related stuff from the start).
    But might be easier to just make another Directory hard drive containing FAT32 partition files. Like this:

    (see your 1st partition loop device from above):

    sudo mount /dev/mapper/loop12p1 /mnt
    mkdir ~/FAT32
    cp -R /mnt/* ~/FAT32

    In your FS-UAE configuration file you have to name FAT32 volume:

    hard_drive_0 = CaffeineOS_z3660_9311_work.hdf # My extracted Caffeine OS partition image
    hard_drive_1 = <path to your fat32 directory>/FAT32
    hard_drive_1_label = USERFAT

    Now Caffeine OS and TheButcher will be happy. I think even Caffeine Update should work.
    And then same as with non-butchered version. Copy Caffeine partition image to sdcard and edit z3660cfg.txt accordingly.
    Rest of the z3660 setup should go as normal, haven’t tried installing ztop, rtg etc yet.