Tag: rusty-nail
42 posts tagged with "rusty-nail".
Reading a Real Cartridge Through a Fake CH340
The console's USB host port met a GBFlash cartridge reader that pretends to be a CH340 serial chip. Three wrong theories, one packet-size discovery, and by the end of the day the firmware read a physical Game Boy cartridge's header and validated it.
Running Ahead of the Sound Chip
The SNES core was losing four million cycles a frame waiting for a sound chip on another board to answer a one-byte question. Teaching the H7 to predict the answer and execute speculatively lifted dense Super Mario World from 35 to 49-52 fps. Then the fastest exact build ever measured painted garbage on the top of the screen while every checksum said it was perfect.
A flight recorder for firmware
Timing-sensitive failures on this console keep beating my telemetry: the screen goes wrong while every counter reads clean. This is the build log of a recorder that captures a working run and a failing run of the same workload, so a tool can find the earliest difference that matters.
The Receiver Was Innocent
I spent days optimising the HDMI coprocessor because every symptom said it was holding the SNES back. The optimisations worked, the frame rate climbed, and then the measurements turned around and convicted a different part entirely: the wire. This is the story of proving a bottleneck before building its replacement.
When 97% Is the Most Valuable Number
I soldered two bodge wires, taught the HDMI coprocessor to talk back, and made it run the SNES sound chip with real Super Mario World music. It reached 97% of realtime. That number is worth more than a pass.
Every Millisecond Now Has a Name
The from-scratch SNES core matched the reference emulator to the cycle across a 750-frame run of Super Mario World. Then the hardware priced five optimisations: two worked, two did nothing, and a profiler said the real problem is memory, not arithmetic.
Press Left Mouse Button
The emulated Amiga gets a mouse, opens a window, and then boots Turrican II - whose custom trackloader refused to work until the floppy drive learned to actually spin.
363960 Free Memory
One evening after the 68000 passed its cycle budget, the emulated Amiga boots a real Workbench 1.3 floppy to the desktop. The chipset, the floppy, and the three bugs that stood between a spinning motor and the blue screen.
One ROM, Three Chips, and the Missing Cable
I built a desktop flasher that turns one ROM into the matching H7 runtime, RP2040 receiver and RNCT v3 cartridge. It also survived a cancelled write, exposed a hidden allocator, and stopped pretending a missing USB cable was progress.
Kickstart 1.3 Meets the Cycle Counter
First hardware numbers for the Amiga 500 programme: a real Kickstart 1.3 ROM executing deterministically on the STM32H753, with a p99 of 2.51M cycles per PAL frame against a 3.35M gate.
The Red Square Before Sonic
I put a Mega Drive emulator on a 480MHz Cortex-M7, carried its video over an 8MHz jumper-wire link, and got perfect music beside a screen that tore in half. The fix was to make a slow frame arrive atomically.
Same bytes, different song
Celeste's soundtrack sounded wrong on the console, so I diffed the cart against the BBS: byte-identical. Two synth bugs later - a music clock that cut bars to a quarter, and a noise fit that matched the wrong metric - the envelope correlation went from 0.49 to 0.94.
The drive is not a USB stick
Planning USB floppy support for the console: four transport dialects from 1998, a FAT12 layer nobody ships, and why the first firmware milestone is nothing but reading descriptors.
The shell, the stack and the secret palette
Two days building the console's v2 shell: a SPLORE-souled library, a crash hunt that ended eight kilobytes from a stack floor, and PICO-8's hidden palette arriving over an 8-bit wire.
Nine FPS under the clouds
Cattle Crisis opened its first level at 9 FPS on the H753. One day of hardware A/Bs later it holds 58, Dank Tomb stopped dying of OOM, and the biggest single win was a compiler flag I should have set weeks ago.
The wire learns a second shape: Game Boy first light
Two days from a feasibility study to DMG code rendering pixel-perfect on the TV: picking an emulator core, passing Blargg and dmg-acid2 on the host, and teaching the console's SPI video wire to carry two frame geometries at once.
The Console That Files Its Own Bug Reports
Giving a bare-metal fantasy console a crash reporter: capture the fault, survive the reset, journal it to flash, upload it, and email me. Plus three ways an STM32H7 loses your data across a warm reset that I had to learn the hard way.
Seventeen demos in forty-nine pixels
The launcher header's blinking sparkle became a 9x9 demoscene square: a breakout ball with real physics, copper bars, fire, the Boing ball, and thirteen friends, dealt from a shuffled deck.
The cart is the artwork
The cartridge shell got redesigned around the PICO-8 label: a flush sticker pocket, the proper chamfered corner, hidden pins - and the whole thing test-fitted virtually, sticker against pocket, before printing a single gram or sheet.
What all the wires are for
A guided tour of Rusty Nail as it stands: the little OS on screen, the four breakout boards doing the real work, and the Game Boy cartridge shell hiding a 16 MiB SPI flash chip.
Three by three, and the heap that lied
The HDMI picture was pixel-perfect and still looked soft. Fixing that took one good idea (a 3x full-resolution palette scanout) and one long afternoon of being framed by a heap that only sometimes existed.
The Cartridge That Killed Its Own Soundtrack
The morning after deleting the old Lua core: 32,000 lines gone, everything played - and then the speakers started clicking. Two wrong fixes, one command trace, and the crossfade every PICO-8 reimplementation eventually learns it needs.
The Default Flips
Stage 9: the fixed-point core becomes THE runtime. Getting there took an executor split, a clock experiment, two optimisations executed for measuring zero, a one-byte regression that cost 19 carts, and a PLL trick that ended the night at 59fps on jumper wires.
The Ghost, the Ladder, and the Cartridge
One evening, three fronts: a haunted iterator that cloned the player, a link clock ladder re-measured rung by rung, and a physical cartridge that finally keeps its saves.
The Swap, Measured
z8lua lands behind a flag: a bug class from a C port, an allocator caught red-handed on the pad, a licensed PICO-8 pressed into service as an oracle, and a compatibility matrix instead of a feeling.
Measure First: the Runtime Baseline
Before any z8lua code lands, Stage 1 of the runtime migration: a permanent benchmark instrument, input tapes recorded from real play, and the numbers the new VM has to beat.
Why I Didn't Replace the PICO-8 Runtime
I sat down to decide whether the homegrown PICO-8 runtime should be replaced with an existing emulator. Twenty-one test cartridges, four codebase teardowns and two cross-compiles later, the answer was: keep everything, swap one organ.
Threading the HDMI funnel by hand
The eight little wires into the HDMI connector were the one part of the board I'd decided to hand off. A script almost did it, then made it worse. The right tool did.
Four wires, five villains
Getting real console frames from the H7 to the Feather RP2040 DVI took two days and five separate root causes: a CPU-starving encoder, a FIFO race, a missing ground return, a clock divider that lied to me, and a GPIO port that mutes my DAC.
Sixteen bars and a bouncing box
The Feather RP2040 DVI arrived this morning and had the console's palette on a monitor within 45 minutes of hitting the bench - after a wrong-variant pinout diagram, a factory firmware with no drive, and a 3D printer slicer typing G-code into my REPL.
It was never the password
Rusty Nail can now download PICO-8 carts over WiFi and run them from a pop-up launcher. Getting there meant a reason-15 rabbit hole that turned out to be my own off-by-one, a GPIO pin that genuinely died on the bench, and a coexistence crash I caused myself.
Reading the framebuffer out of RAM
I wanted to redesign Rusty Nail's boot splash, but the standalone build sends nothing back to a host - the panel is the only display. So before I could change the splash, I had to work out how to screenshot a screen my Mac can't see.
16 megabytes for a 32-kilobyte game
I want to dock a physical cartridge into the fantasy console, so I picked the flash chip, mapped the firmware, and found most of the work was already done. None of it is built yet - this is the plan.
The HDMI board I bought before reading the datasheet
I bought a 50-pin RGB888-to-HDMI board for the fantasy console, then counted the pins. Two of them are Ethernet, three are the audio bus, and the Nucleo never had 24 to give. The fix might be a second microcontroller whose whole job is HDMI.
A divide per pixel
Once the SPI panel was off the critical path, the frame became the cart's own Lua and the slow part moved inside it. A missing string builtin, then the real prize: a stretched-sprite blit doing an integer divide per pixel on a chip with no fast divide. The profiler found it; the obvious guess was wrong. About 41fps to about 58fps.
Getting the panel off the critical path
The SPI blit was a blocking, row-by-row write that stalled the whole console for about 13ms every frame. DMA fixed that, the data cache nearly broke it, and a frame I was packing for nobody was the last easy win. About 14fps to about 41fps.
The controller layer was the chip
I wanted a Bluetooth gamepad to drive the fantasy console. I picked the wrong ESP32, learned that BLE-only is silicon and not a setting, and ended up swapping the chip instead of the code.
I keep choosing hard mode (the SPI panel and me)
I had a working HDMI path at 12:48 in the morning. Then at 10am, well rested and full of hope, I picked up the tiny SPI screen instead and spent nine hours reinventing flicker. A note on patience, and on why hardware scanout is the thing I should have just waited for.
The SPI panel saga: from a blank screen to 25 MHz
Getting the desktop off the USB stream and onto a real LCD. A dead-end controller that latches on the wrong clock edge, a switch to the ST7735, and climbing from 1 MHz to 25 MHz without losing the audio.
Getting sound out of it
An I2S DAC, SAI plus DMA, and three gotchas that each ate a session: a mute pin nobody pulls high, a data cache fighting the DMA, and an async executor that starved the audio to silence.
Running real PICO-8 Lua on a Cortex-M7
Vendoring Lua 5.4 into a bare-metal firmware, the 64-bit integer bug that printed 42 minus two-to-the-32, and why a hardware watchdog beat an instruction-count hook for stopping runaway carts.
A fantasy console that boots on a dev board
Rusty Nail is a windowed pixel-desktop OS running bare-metal on a Nucleo-H753ZI. How it is put together, and why the display layer is an abstraction from day one.