Mandelbrot Upic: Live Fractal Generation on the Commodore 64 Ultimate
Mandelbrot Upic v1.0.0 -- live fractal generation and interactive zoom for the Commodore 64 Ultimate

Mandelbrot Upic: Live Fractal Generation on the Commodore 64 Ultimate

September 11, 2026
C646502demoretroUltimate II+Oscar64

Mandelbrot Upic v1.0.0 is out: a Mandelbrot fractal generator for the Commodore 64 Ultimate. The fractal is computed on-device at 64 MHz turbo and displayed live as it builds, using Upic, a border-color raster picture technique giving a full 384x256 picture with independent per-pixel color -- no character-cell color clash. Confirmed working on real Ultimate 64 Elite 2 hardware.

A Picture Made of Border Flicker

Upic, designed by Aleksi Eeben, does not use any of the VIC-II's bitmap modes at all. With the display enabled bit held low, the whole visible area becomes "border", and the border color register can be changed by the CPU faster than the chip settles into a stable color -- one change per horizontal pixel pair, timed against the raster beam with no slack. render_line_pixels(), the routine doing this, is a single, fully unrolled sequence with no loop at all, since even a loop's own branch overhead would be enough to drift the timing off the beam.

Fixed-Point Fractal Math, No 32KB Table

The escape-time iteration runs entirely in Q5.11 fixed point -- a 16-bit signed value with 11 fractional bits, following the range and precision choice documented by the mandelbr8 project. Their own biggest speed-up is a 16384-entry squaring table, but that alone is 32KB, and this project's picture buffer already takes up most of the machine's RAM. Instead, squaring runs through a 1KB quarter-square multiply table: the identity ab = floor((a+b)^2/4) - floor((a-b)^2/4)* turns a multiply into two table lookups and a subtract, at a fraction of the table-size cost of the direct approach. A closed-form cardioid and period-2-bulb check skips the iteration loop entirely for any point that provably never escapes, which covers a large share of the visible image.

The default view, showing the black main cardioid and period-2 bulb the closed-form check skips entirely

Picking a Region to Zoom Into

Once generation completes, the current view can be panned, zoomed out gradually toward the original overview, or zoomed into any chosen rectangle: a key opens a movable, resizable selection box with a corner marker at each of its four corners.

Box mode: four corner markers outlining a zoom target

Those markers are drawn directly into the packed picture buffer, not VIC-II hardware sprites. Two isolated standalone tests on real hardware confirmed why: a sprite shows up fine on an ordinary text screen, but the exact same sprite setup, with every register reading back correct, is simply invisible once the border-racing technique is holding the display disabled across the whole frame. That is a different situation from the well-documented "sprites over an open border" trick, which only toggles one register briefly per line rather than holding the display off for an entire multi-frame session.

The result of confirming a zoom into a selected region

A Genuine Interrupt Collision

The interactive controls surfaced a real, reproducible crash: pressing any key while browsing or zooming would occasionally drop the display straight to a JiffyDOS text screen. It reproduced even in a build with none of the zoom feature's own code, which ruled out the new controls themselves as the cause. The actual mechanism sits lower down: this project banks ROM out permanently to get enough RAM for the picture buffer, and a trampoline routine keeps that safe by re-banking ROM in whenever a real hardware interrupt fires and handing off to the genuine KERNAL interrupt handler -- otherwise a stray interrupt while ROM is out would jump into whatever happens to sit at the KERNAL's usual addresses instead of real code. That KERNAL handler runs its own keyboard-matrix scan on every timer tick, and this project also scans the same CIA registers directly every frame -- two independent scans of the same hardware, timed independently of each other. The fix was to mask interrupts globally, once, for the program's entire lifetime: nothing in this project actually needs a real interrupt for anything, since even the display's own cycle-exact timing is plain busy-polled rather than interrupt-driven.

Watch It In Action

A recording of it running on real Ultimate 64 Elite 2 hardware, generation through to interactive zoom:

Ultimate 64: Mandelbrot Upic - Live Fractal Generation & Interactive Zoom

v1.0.0 Is Available Now

v1.0.0 is the first release: on-device generation, live build-up, the four-corner zoom box, gradual zoom-out, and four selectable color gradients. It requires firmware 3.15 or newer, which in practice means an Ultimate 64 Elite 2 for now, since the corresponding firmware for the original Ultimate 64 board has not been released yet. The release zip bundles a configuration file that switches on the Command Interface and U64 turbo registers automatically, alongside the program itself.

Credits

  • Code: Xander Mol
  • Upic border-color raster technique: Aleksi Eeben
  • Fixed-point algorithm design: inspired by 0x444454's mandelbr8 project (documented approach only; no code from that project is used)
  • Compiler: Oscar64 by drmortalwombat
  • Ultimate 64 hardware: designed by Gideon Zweijtzer / Gideon's Logic

Licensed under the GNU General Public License v3.0.

Sources and More Information

mandelbrot-upic repository -- source code, full documentation, and the release: github.com/xahmol/mandelbrot-upic

mandelbr8 -- the project whose documented algorithm design this generator's fixed-point approach is based on: github.com/0x444454/mandelbr8

Oscar64 compiler -- C99/C++ cross-compiler for 6502, by drmortalwombat: github.com/drmortalwombat/oscar64

Ultimate 64 hardware -- product page for the FPGA-based C64 mainboard this project targets: ultimate64.com

Share:
Facebook X Bluesky WhatsApp