UltimateDemo2026 v1.1.1 is a small release: a rewritten timing loop and three bugs, no new scenes. What makes it worth writing up is where the bugs came from. All three were invisible in the emulator this project leans on for most of its day-to-day testing, and only ever showed up against a genuine Ultimate 64.
This is a follow-up to the palette control post and the original UltimateDemo2026 post. This one is shorter and more technical.
New this cycle: talking to the real device programmatically
Most of this project's testing happens against an emulator, for good reason -- fast, repeatable, no physical hardware needed. But an emulator can only be as accurate as its model of the real board, and some bugs only exist in the gap between the two.
This cycle added a bridge tool that talks to a real, physically connected Ultimate 64 over its own network interface -- loading and running programs, reading the screen and memory back, and driving the keyboard, all without touching the machine by hand. That closed the gap enough to catch all three of the following.
First, a rewritten timing loop
Turbo detection works by timing a burn-cycles loop against the one clock on this hardware that keeps real time regardless of CPU speed, and checking whether it finished suspiciously fast. The loop doing the burning used to be a deliberately unoptimised C loop -- correct, but with real run-to-run timing jitter, since an optimising compiler's idea of "deliberately slow" isn't a fixed, predictable cost.
It's now a short, hand-written processor-level loop instead: a handful of instructions with a fixed, precisely computable cost per iteration. Far less jitter, which in turn means far less safety margin is needed around the pass/fail threshold -- a tighter margin that plausibly made the first bug below easier to actually trigger, though that's not something that got proven either way.
Bug one: turbo detection failing, intermittently, for no visible reason
The demo's hardware detection screen checks that turbo mode is genuinely running fast, not just that the speed register accepted a write. Most of the time this passed cleanly. Every so often, on an otherwise identical build, it would report turbo as not detected -- while the demo was visibly running at full speed moments later.
Intermittent failures that vanish the moment you look closely are the worst kind. The fix here was to stop guessing and get real numbers: a couple of temporary lines were added to stash the detection routine's internal values at a fixed memory address, and the real hardware bridge read that address back after a live run. That turned up the actual figures behind a pass and a fail side by side, from the exact same binary.
The real cause: this project ships a small settings file alongside the program that the Ultimate firmware auto-applies at launch, including its own turbo speed setting. By the time the detection routine ran, the speed register was already sitting at whatever that settings file had put it at -- not at the naive one-megahertz starting point the routine assumed. Jumping straight from that pre-existing, already-elevated state to maximum speed turned out to be an unreliable transition for the hardware's clock generator to settle from, roughly half the time.
The fix: force a genuine return to one megahertz first, with its own brief settle pause, before ever asking for maximum speed. A real disable-then-enable-at-max transition, rather than a same-state nudge. Confirmed reliable across repeated real-hardware runs afterward, where it had failed intermittently before.
Bug two: a hardware-family mixup hiding behind an ambiguous name
While re-checking the code around that first fix, a second, unrelated bug turned up in how the demo classifies the top speed a given Ultimate 64 can reach. There are effectively two hardware families here: an older 48 MHz-class line, and a newer 64 MHz-class line that includes the Commodore-branded Commodore 64 Ultimate. The demo asks the firmware for a hardware identity string and matches against it to pick a label.
The problem: one specific identity string is genuinely ambiguous. Both the original 48 MHz-class Ultimate 64 and the newer, unrelated Commodore 64 Ultimate report the exact same bare string for that field -- nothing in it distinguishes them. The old matching logic treated that ambiguous string as evidence of the older, slower hardware, which meant a genuine 64 MHz Commodore 64 Ultimate could get mislabeled 48 MHz on the detection screen.
The fix flips the assumption: only a different, genuinely distinct string variant -- used exclusively by the older hardware's higher-end model -- is now trusted as evidence of 48 MHz. Everything else, including the ambiguous bare string, defaults to 64 MHz. The only machine that can still be mislabeled under this rule is the original, plainest, and by now rarest member of the older family -- an accepted tradeoff, since it affects a label on a detection screen, not anything the demo actually does differently.
Bug three: a one-pixel scar the scene never noticed it was leaving

The last one is smaller and purely visual: a single stray column of stale pixels at the screen's left edge, visible throughout the closing scroller scene's horizontal fine-scrolling.
The cause: the scroller's smooth horizontal motion naturally produces a small edge artifact in its leftmost column every frame -- an inherent side effect of how fine-scrolling works, not a mistake. Narrowing the display to thirty-eight columns instead of the usual forty tucks that column behind the border where it can't be seen, which is how the scene was always meant to hide it. The library call the scene uses to switch the display into text mode in the first place, though, unconditionally resets column width to the standard forty as part of doing that -- and the scene's own follow-up adjustment only ever touched its horizontal scroll offset, never column width, so that default was quietly left standing every single time the scene ran. The fix makes the scene narrow the display itself, explicitly, right after that mode switch, and put the width back afterward for the following end screen.
A dead end worth mentioning
One more thing came out of this cycle, in the spirit of this blog's habit of writing up what didn't work, not just what did: an attempt to measure the two hardware families' actual clock speeds directly, by timing a long loop against the same real-time clock reference used for turbo detection, rather than relying on the identity-string approach above.
It didn't hold up. A single short timing pass can't tell the two speed tiers apart at all -- they're both fast enough to finish within the clock's measurement resolution. Stretching the loop out to get a bigger, more separable number made things less trustworthy, not more: timings for what should have been identical settings varied run to run, and both tiers measured well below their nominal speed for any multi-second continuous pass. Most likely holding the processor's interrupt-disable state for that long is itself interacting with something on real hardware, though that was never chased down further. Either way, direct timing is now off the table for this permanently -- the identity-string approach above is staying as the only method.
Try it
Full changelog and download: github.com/xahmol/UltimateDemo2026, release v1.1.1.
Credits
Code: Xander Mol
Music: 4ev.mod ("Forever Young") -- artist unknown
UCI/DOS library: Scott Hutter and Francesco Sblendorio -- github.com/xlar54/ultimateii-dos-lib
PETSCII font: Small Round PETSCII Font by Cupid
Compiler: Oscar64 by drmortalwombat -- github.com/drmortalwombat/oscar64
Ultimate 64 hardware and firmware: designed by Gideon Zweijtzer / Gideon's Logic -- ultimate64.com
Real-hardware bridge tooling: c64bridge by Chris Gleissner -- github.com/chrisgleissner/c64bridge
Licensed under the GNU General Public License v3.0.
Sources and More Information
UltimateDemo2026 v1.1.1 release -- release notes and download: github.com/xahmol/UltimateDemo2026/releases/tag/v1.1.1
UltimateDemo2026 repository -- source code, libraries, and all documentation manuals: github.com/xahmol/UltimateDemo2026
Previous post: palette control -- the v1.1.0 firmware palette work this release follows up on: idreamtin8bits.com/blogs/ultimatedemo2026-palette-control
Original UltimateDemo2026 post -- the project's origin story, toolchain, and the first eight scenes: idreamtin8bits.com/blogs/ultimatedemo2026
c64bridge -- the real-hardware bridge tool used to catch these bugs, by Chris Gleissner: github.com/chrisgleissner/c64bridge
Oscar64 compiler -- C99/C++ cross-compiler for 6502, by drmortalwombat: github.com/drmortalwombat/oscar64
Ultimate 64 firmware documentation -- firmware releases and update instructions: ultimate64.com/Firmware