A Hondani release · C64 scene group, CSDb · hondani.com
DOOM_C64U by Hondani
GitHub ↗
A Hondani production · Commodore 64 Ultimate · from-scratch 3D engine

The engine Doom
never had, on hardware
it never knew about.

A BSP renderer built from scratch for the C64 Ultimate, walking id Software's real E1M1 map at a measured, locked frame rate on stock hardware — 64 KB of RAM, a 6510 at 64 MHz, and a 16 MB REU doing all the remembering. No id Software code. No emulation shortcuts. Open source.

16.6 fps — hardware-measured, locked frame pace Open source What's shipped, what's next →
Doom C64U hero shot: corridor with colored doors, textured walls, HUD visible
16.6
fps — hardware-measured, locked frame pace
64 KB
total RAM — engine, geometry cache, framebuffer, HUD, all of it
16 MB
REU streamed as the level's real memory, DMA'd per subsector
0
lines of id Software code — new engine, real WAD geometry
Why this matters

This isn't a port. It's a hardware-aware reinterpretation.

Doom expects floating point, megabytes of fast random-access memory, and a CPU with headroom to spare. The C64 Ultimate offers none of that natively — a 6510 at up to 64 MHz, 64 KB of directly addressable RAM, and no FPU. What it does have is a 16 MB REU that can be treated as a real streaming asset store, and enough raw clock speed that a genuinely optimized 6502-style pipeline can hit a locked, playable frame rate.

Every optimization in this engine was verified pixel-for-pixel against the previous version before being kept. The frame rate you see quoted here isn't a best-case emulator number — it's read off real hardware with make u64-fps, averaged over hundreds of consecutive frames.

At a glance

  • BSP front-to-back traversal over the real E1M1 node tree (237 convex subsectors)
  • Bounding-sphere + backface culling before a single pixel is touched
  • 16.16 fixed-point projection, table-driven — no runtime divides in the hot path
  • Level geometry, textures, sprites and music all streamed from a 16 MB REU over DMA
  • Per-sector WAD light level + distance falloff drives a shading ramp, resolved through Bayer-dithered chunky-to-multicolor conversion, double-buffered
  • WASD, joystick, or mouse input, SID music replayed live as a register stream from REU
Screenshots

What's here so far

1
Environmental variety: green grass, brown rock textures, open chamber
2
Door mid-open: brick wall texture detail, animated sector in transit
3
Sprite and geometry close-up: player view, props rendered in scene
4
HUD detail: health, armor, ammo digits clearly visible and readable

Captured straight off real hardware with HDN Shell, Hondani's other C64 Ultimate project.

Get started

Download and run

Pre-built binaries are ready to run on your C64 Ultimate. Download from the latest release on GitHub, copy to your device, and follow the quick setup steps.

↓ Download from GitHub releases

Setup steps

  1. Download the release from github.com/slesinger/doom/releases/latest
  2. Copy the files to your doom folder in your C64 Ultimate
  3. Open the Ultimate menu → DISK FILE BROWSER and navigate to the doom folder
  4. Load settings: doom.cfg → RETURN
  5. Load into REU: game.reu → RETURN
  6. Run: launcher.prg → RETURN

What you'll need

  • Commodore 64 Ultimate (official hardware, 2025+)
  • Or: VICE emulator on any PC for development & testing
  • A copy of DOOM1.WAD to build from source (licensed or free shareware)

The pre-built binaries above include the assembled engine and pre-packed assets — no build tools needed to run.

Architecture

Five stages, one raster-synced budget

Every frame moves through the same fixed pipeline. Nothing runs longer than the frame budget allows — the renderer is built to fail gracefully on complexity, not to blow the deadline.

INPUT WASD / joystick / mouse move + collide BSP TRAVERSE front-to-back walk sphere + backface culling WALL + TEXTURE projection (16.16 fixed) column clip, span fill texture sample from REU largest single stage SPRITES depth-sorted billboards masked blit CONVERT + FLIP light ramp → Bayer dither chunky → multicolor double buffer, raster- synced flip one pass per frame, bounded by the raster-synced deadline — no stage is allowed to run long and hope
renderFrame(): input → BSP traverse → wall/texture → sprites → light ramp + dither convert → flip
In practice

What the five-stage pipeline looks like in real time

Five-stage pipeline in action: chamber with colored geometry, textures, lighting, and sprites rendered in real-time

Every frame you see is the result of these five stages executing within the 59.85 ms deadline. The chambers, textures, dithering, and sprite rendering happen in lockstep.

Memory

64 KB of RAM, 16 MB treated as real

The C64U's 64 KB is the whole working set: code, the chunky framebuffer, HUD, and the small resident slice of level state the current subsector needs. Everything else — the BSP tree, segs, textures, sprites, music — lives in the 16 MB REU and is DMA'd in as the player moves, subsector by subsector.

The framebuffer (MATRIX, 160×176 chunky) alone is 28 KB — 45% of usable RAM. Almost everything else in the engine exists to keep the per-pixel work over that single buffer inside the frame deadline.

Zero page / stack / I/O shadow ~2 KB Engine code — bspLoop, doWall, spanFill, texture sampler, checkMove ~fragments MATRIX — chunky framebuffer 160 × 176, the render target every stage after BSP traverse writes to 28 KB 45% of RAM BITMAP1 + SCREEN1 — second buffer 9 KB
the 64 KB budget — approximate, illustrative of proportions
REU streaming

16 MB as a DMA-fed asset store

The level's BSP tree, segs, textures and sprite images are pre-packed into a .reu image by a Python asset pipeline built for this project, then streamed into RAM subsector-by-subsector as the BSP traversal visits them — never the whole level at once. Music streams the same way but on its own clock: the score is packed as a SID register delta stream and DMA'd from REU tick-by-tick, driven by the CIA timer rather than the render loop, so it keeps playing at its own pace regardless of frame rate.

DOOM1.WAD licensed source data wad2reu.py BSP + segs + tex pack & ramp table assets.reu 16 MB, subsector-indexed On hardware, per frame → BSP traversal visits a subsector → its segs/textures DMA in from REU → resident RAM only holds what's visible 1 byte / µs flat DMA rate — priced per phase
DOOM1.WAD → wad2reu.py → assets.reu → streamed into RAM during play

Fixed-point everywhere

No FPU means no floats in the hot path. Projection, distance, and lighting terms all run in 16.16 fixed point, with reciprocal and trig terms pulled from precomputed tables rather than divided or computed per pixel.

Lit, then dithered

Each surface's WAD light level and distance falloff pick an intensity on a shading ramp; the renderer draws that ramp into a chunky intermediate buffer, then converts to the VIC-II's multicolor bitmap mode with ordered (Bayer) dithering. Dithering is what turns the ramp's coarse intensity steps into a smooth-looking gradient within the VIC's fixed 3-colour-per-cell palette — the two stages are built to work together, not independently.

Measured, not estimated

Every number here comes from real hardware

Frame times are read with make u64-fps on an actual C64 Ultimate — never an emulator's best case — and cross-checked against a raster-sync boundary that can only land on fixed multiples, so there's nowhere for an optimistic number to hide.

Milestone 1 — closed

Walkable E1M1, flat-shaded, with music. The baseline proof that the pipeline works end-to-end on hardware.

Frame rate25.05 fps
Deadline39.90 ms
Compute (measured)37.6 ms
Frames on deadline502 / 502 (100%)

Milestone 2 — closed

Textured walls, doors, moving sectors, jump, walk bob, sprites, weapon view, and a HUD land at a deliberately lower, more spacious frame rate to make room for all of it.

Frame rate (locked)16.6 fps
Deadline59.85 ms
Compute (measured)~46–48 ms
Frames on deadline100%
Frame budget

Milestone 2's 59.85 ms deadline, spent

Base render — ~28 ms
Textures — 8.7 ms
~0.3 ms
Sprites — ~10 ms
margin
Base render + BSP traverse Wall texturing (measured, landed) Doors, jump, walk bob, HUD (near-zero cost) Sprites & weapon view (shipped)

Illustrative proportions built from the measured figures in the project's implementation log, not a profiler screenshot — the underlying numbers are real, the segmentation is approximate.

Music isn't in this chart on purpose: it's driven by its own CIA timer interrupt, off the render loop's clock, so it doesn't spend any of the 59.85 ms render deadline — it plays at its own pace regardless of frame rate.

Proof shot

Real hardware verification

make u64-fps output: 16.61 fps hardware-measured, frame timing, and deadline validation

Every frame rate number on this site comes from real C64 Ultimate hardware, verified with make u64-fps and cross-checked against raster-sync boundaries.

The optimization story

Milestone 1: 56.9 ms → 39.9 ms

56.9 ms/frame — first correct render

Naive BSP traversal, no culling. Correct pixels, no budget discipline yet.

45.1 ms/frame — culling lands

World-space seg backface test plus streamed bounding-sphere rejection of whole BSP subtrees before they're touched.

39.9 ms/frame — inside the 25 fps deadline

A frame cap plus an arithmetic pass worth roughly 9.4% closes the gap. Every step verified bit-identical: 0 of 104,448 pixels different from the reference render.

Where this is going

Milestones, honestly labeled

Nothing here is marked done until it's been measured on real hardware, not just in an emulator.

Closed

Milestone 1

Walkable E1M1, flat-shaded, collision against real geometry, WASD + joystick, SID music from REU. 25.05 fps, 100% of frames on deadline.

Closed

Milestone 2

Textured walls, doors & moving sectors, jumping, walk bob, HUD, sprites, and a weapon-view overlay — all shipped, fit into a locked 59.85 ms budget.

Future

Milestone 3+

Enemies and combat, additional maps beyond E1M1, and a look at whether a fourth raster frame or resident (non-streamed) sprite assets buys back budget for the next round of features.


Milestone 2, phase by phase

PhaseStatus
Textured wallsShipped — measured 8.7 ms
Doors & moving sectorsShipped — effectively free
Jump, walk bobShipped
HUD (health / armor / ammo)Shipped — 0 ms measured, boot-only paint
Sprites & weapon viewShipped — the last phase of M2

Beyond Milestone 2

Combat

Enemies, projectiles, and the damage loop.

Community map support

Loading custom, freely-distributable maps through the same REU-streaming pipeline — no dependency on any commercial WAD.

Budget relief

Resident downsampled sprites, or a fourth raster frame, to buy room for what M3 needs.

Questions

FAQ

You're walking through id Software's real E1M1 map — same BSP geometry, same layout — but the rendering engine, collision, and audio playback are new code, built from scratch for the C64 Ultimate. It's a fan-made reinterpretation, not a port of id Software's engine or code.

Not yet. Right now it's E1M1 with textured walls, doors, sprites, a HUD, and a weapon view. Enemies, combat, and community map support are on the roadmap, tracked openly on GitHub.

To hit the measured frame rate, yes — the official Commodore C64 Ultimate (released 2025), not the older third-party "1541 Ultimate" cartridge; this project targets Commodore's hardware specifically and does not run on 1541 Ultimate devices. For development and testing without hardware, it also runs in the VICE emulator on any PC. The frame-rate numbers quoted on this site, though, are all measured on real Commodore C64 Ultimate hardware, not the emulator.

It isn't, really — the C64 Ultimate is a modern FPGA re-implementation of the C64 that can run the 6510 core at up to 64 MHz (vs. the original ~1 MHz) and pairs it with a 16 MB REU for fast external memory. This project is built specifically to exploit that: fixed-point math, REU as a real asset store, and a rendering pipeline designed around a hard frame deadline.

An earlier build reached 25 fps without textures or sprites. Adding textures, doors, and sprites at that same rate wasn't achievable without cutting one of them — so the engine deliberately targets a locked, stable 16.6 fps with more frame budget per feature, rather than a higher number that drops frames under load. Frame-time stability is treated as more important than peak FPS throughout this project.

Yes. The GitHub repo has the full implementation log, architecture notes, and open phases. Issues and discussions are the way in.

KickAssembler, Python 3, and a copy of DOOM1.WAD to generate the asset REU image — the free shareware/demo WAD is enough, or your own licensed copy of the full game. The engine doesn't ship id Software's data or code, and the repo doesn't ship the WAD either; wad2reu.py generates the REU image from a WAD file you provide locally.

Get involved

Source, discussion, and how to help

Hondani

This project is made by Hondani, a C64 scene group — Doom C64U is a Hondani production, credited to the group rather than any one contributor.

Hondani on CSDb →

Also from HondaniHDN Shell

A modern shell for the C64 Ultimate: AI chat, a text-mode web browser, an assembler/monitor, Telegram and RSS, all in an 8 KB ROM alongside BASIC. It's Hondani's other C64U project — and, fittingly, the tool this page's own screenshots were captured with.

GitHub

Full source, implementation log, and architecture docs. Issues and Discussions are open for questions, bug reports, and technical deep-dives.

Open repository →

Video walkthrough

Full real-time E1M1 walkthrough on real C64 Ultimate hardware.

▶ Watch on YouTube →

Forums & community

Technical writeups and release threads on CSDB.dk and Lemon64.com; cross-posts on r/commodore and r/retrogaming. Links go live at launch.


Music and audio

Intro and gameplay music

Intro music — "Intermission From Doom"

By Bobby Prince from Music From Doom Vol. 1. Extracted to PCM and replayed through the C64 Ultimate's audio sampler during the intro sequence.

Gameplay tune — "DooM Medley"

By Thanos B. (DJ Space), 2025 — released by Hokuto Force. Played back on real SID hardware as a register stream from REU.


Assets

Intro screenshot and visual elements

The intro screenshot shown on this project's launch screen is captured from id Software's original Doom (1993). In-game screenshots of the C64 Ultimate engine itself are captured directly from real hardware using HDN Shell.