Doom for C64 Ultimateby Hondani
GitHub ↗

Doom's E1M1, rebuilt
from scratch for the
C64 Ultimate.

A BSP renderer 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. Open source.

16.6 fps, hardware-measured 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

Not a port — a rebuild for hardware Doom was never designed for

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. See the Performance tab for how that's measured.

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

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.

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.

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

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.

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

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.

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%

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.

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.

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.

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.

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 →

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.


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.


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.