Skip to content
go-doomPublic

About

Pure-Go DOOM (id Tech 1, 1993) engine. Hand-port lineage id Software 1997 -> AndreRenaud/gore -> go-doom/engine. Bare-metal TamaGo backend, CGO=0, 4-gate provable-test protocol.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

458 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

go-doom/engine

go-doom / engine

6-arch CI CGO License

Pure-Go DOOM (id Tech 1, 1993) engine for bare-metal TamaGo + UEFI under the cloud-boot loader. Sibling of the go-quake1 family (go-quake1 / go-quake2 / go-quake3 — the id Tech 1/2/3 ports).

Fork origin

This repository is a fork of AndreRenaud/gore at commit 7dc6c65493b8a29e0b93dd00dac21a2f10a0068a (2026-05-11).

gore itself is a Go transpilation of doomgeneric performed with modernc.org/ccgo/v4 and then hand-cleaned. The engine has no CGO dependency, only standard library, and exposes its host bindings through a small DoomFrontend interface -- exactly the shape we need to wire DOOM into cloud-boot's virtio device tree.

The upstream README.md is preserved as README.upstream.md.

Why this fork exists

cloud-boot is building an OS-agnostic OCI boot architecture on top of TamaGo + UEFI. To showcase Phase 3 we want to run classic DOOM as the "OS payload", with rendering, sound, and input wired straight to virtio devices instead of SDL / X11 / a kernel.

This fork therefore adds:

  • backend/tamago/ -- a new DoomFrontend implementation that drives cloud-boot's virtio-gpu, virtio-sound, and virtio-input drivers (currently stub interfaces; real wiring lands once go-virtio/sound and go-virtio/input ship in their sibling sprints).
  • internal/embedwad/ -- an io/fs.FS shim that serves a WAD blob from memory, so the engine can load the IWAD without a real filesystem.

The upstream gore engine (doom.go, ~990 KB of transpiled code, plus doom_test.go) and its existing terminal / web / Ebitengine / SDL examples are kept verbatim, so we can rebase against upstream cleanly.

License boundary

  • The repository as a whole is GPL-2.0-only (LICENSE). The DOOM engine (doom.go, doom_test.go) comes from doomgeneric by way of gore under GPL-2.0-only, and GPL-2.0 §2 puts a work based on it, as a whole, under the same terms.
  • Each file says which licence it is under, in an SPDX-License-Identifier header, and that header is authoritative (NOTICE.md):
    • GPL-2.0-only: the engine, backend/, cmd/, embedwad/, ociblob/, wadcheck/;
    • GPL-2.0-or-later: the music bridge and most of music/;
    • LGPL-2.1-or-later (LICENSE-LGPL-2.1): music/opl/, the Go port of Nuked OPL3;
    • BSD-3-Clause (LICENSE-BSD): netgame/.
  • The rest of the cloud-boot stack is BSD-3-Clause and is not affected: cloud-boot's bootloader, runtime, and host components do not import this package. The integration happens via a thin separate "livedoom" boot artifact whose only job is to call godoom.Run. That artifact will itself be GPL-2.0, isolated to a single directory in the cloud-boot/tamago-uefi tree.

Layout

.
|-- doom.go                     # upstream gore engine (transpiled DOOM)
|-- doom_test.go                # upstream gore reference-frame tests
|-- example/                    # upstream gore examples (termdoom, web, ...)
|-- backend/
|   |-- tamago/                 # NEW: DoomFrontend over virtio-gpu/sound/input
|   `-- wasmbox/                # NEW: DoomFrontend over the wasmbox desktop protocol
|-- cmd/
|   `-- doom-wasmbox/           # NEW: the wasmbox desktop client (GOOS=js GOARCH=wasm)
|-- embedwad/                   # NEW: io/fs.FS shim over an in-memory WAD
|-- ociblob/                    # NEW: pull + verify one file from an OCI /v2 mirror
|-- wadcheck/                   # NEW: bound a WAD's header + lump directory (fuzzed)
|-- state.go                    # NEW: read-only game-state snapshot (gore.Observe)
|-- PORT.md                     # Adaptation plan (read this)
`-- README.md

Status

Aspect Status
Pure Go (CGO=0) yes (engine + all new code)
Builds on Go 1.27.1 yes (lib + tamago backend + pure-Go examples)
Cross-compiles linux/amd64 yes (CGO=0)
Cross-compiles linux/arm64 yes (CGO=0)
Runs shareware DOOM1.WAD yes (engine ticks; verified via TestMenus harness)
TamaGo backend wired scaffold only; real drivers land in follow-up sprint
Music (MUS/MIDI + OPL2/OPL3 synth) yes -- pure-Go OPL path (see music/)
Multiplayer (netgame lockstep) yes -- protocol + lockstep (see netgame/)
wasmbox desktop client yes -- cmd/doom-wasmbox, Freedoom, software renderer (see below)

Doom in the wasmbox desktop

cmd/doom-wasmbox runs the engine as a window of the wasmbox web desktop, beside Quake (launch it from the root menu: Applications -> Doom). It is a Web Worker client of the wasmbox protocol (backend/wasmbox): each 320x200 frame is scaled to 640x480 (4:3, the proportions Doom was drawn for), copied into the window's SharedArrayBuffer under the protocol's seqlock and committed; the compositor scale-fits it to the window and locks the resize grip to 4:3. Keys are the classic ones (arrows, Ctrl fire, Space use, Shift run, Alt strafe, , . strafe, Escape menu, Enter, digits for weapons); the left mouse button fires and the right one uses.

Game data is Freedoom: Phase 1 0.13.0 (BSD-3-Clause), never committed anywhere: the wasmbox site workflow downloads the official release, checks it against the release's signed CHECKSUM file, and serves freedoom1.wad from a same-origin /v2/doom-assets OCI mirror. The client pulls it with ociblob and trusts nothing it receives:

  • it fetches only from its own origin (the worker publishes the site root; the client refuses any other origin);
  • the WAD's sha256 is pinned in the binary (main.WADSHA256), the manifest must name exactly that digest, and the blob's size and sha256 are recomputed (64 MiB cap);
  • wadcheck then bounds the header and every lump offset and size against the file and the engine's 4095-lump table before the engine opens it -- the transpiled loader itself has no bounds checks. Both parsers are fuzzed in CI.

Build it with GOOS=js GOARCH=wasm go build ./cmd/doom-wasmbox; the wasmbox repository's Pages and CI workflows do that and assemble the mirror. The client logs DOOM: ... lines (DOOM: state=... on every game-state change, DOOM: fps=... every five seconds) that wasmbox's browser tests assert on.

Sound effects (DMX lumps, decoded and panned as chocolate-doom does) and OPL music play in the wasmbox window through wasmbox's audio relay. A dedicated worker has no AudioContext, so the client mixes the effects (Mixer) with the OPL music at 44.1 kHz and writes interleaved float32 PCM into a lock-free SharedArrayBuffer ring (github.com/wasmdesk/wasmbox/audioring); the wasmbox page plays it from an AudioWorklet, resuming on the first click or key. The ring is 4096 frames (93 ms) and is kept about half full. The client logs audio=relay (running once the page has played frames). A worker-scope AudioContext, where one exists, remains the fallback; with neither, the client runs silent and logs audio=unavailable. wasmbox's test/probe-audio.mjs checks it in headless Chromium against the real client, including a planted negative with the writer disabled.

Music and multiplayer (pure Go, CGO=0)

The chocolate-doom-faithful music and netgame paths ship as standalone, differentially-tested packages, all CGO-free and building on the six 64-bit arches:

Package Role
music/mus DMX MUS (D_* lumps) parse + mus2mid MUS->MIDI conversion
music/midi Standard MIDI File parser (port of midifile.c)
music/genmidi GENMIDI lump (the DMX OPL instrument bank) parser
music/opl Nuked OPL3 (Yamaha YMF262) emulator -> PCM
music/oplplayer MIDI + GENMIDI -> OPL register writes -> PCM (i_oplmusic)
netgame classic doomcom/ticcmd lockstep protocol + transport seam

The engine installs the OPL synth as its music_module (see music_opl.go); the host frontend pulls synthesised stereo PCM through gore.ReadMusicPCM.

Differential oracles. music/mus conversion is asserted byte-identical to chocolate-doom's mus2mid.c (compiled as an oracle over synthetic MUS vectors). music/opl is validated bit-exact against a Nuked OPL3 C register->PCM trace. music/oplplayer is checked against a captured i_oplmusic.c register-write trace for D_INTRO. netgame runs two, three and four in-process engines over an in-memory transport and asserts they stay in lockstep with byte-identical per-tic state hashes; ticcmd serialisation is byte-faithful to the engine's own vanilla layout.

Music and multiplayer do not perturb the frame/audio determinism oracles: the oracle/ frames and audio-event log remain byte-equal after wiring.

Quick smoke test (host)

# 1. fetch the shareware IWAD (free re-release):
curl -sSL -o doom1.wad https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad

# 2. terminal renderer (pure Go):
CGO_ENABLED=0 go run ./example/termdoom -iwad doom1.wad

cloud-boot integration points

The TamaGo backend in backend/tamago/ consumes three driver interfaces declared in that package:

Frontend method Backend interface Driver repo (sibling)
DrawFrame(*image.RGBA) GPU.Flip github.com/go-virtio/gpu
CacheSound / PlaySound Sound.Cache/Play github.com/go-virtio/sound
GetEvent(*DoomEvent) Input.Poll github.com/go-virtio/input

The WAD is delivered to the engine via gore.SetVirtualFileSystem(fs.FS) using the internal/embedwad helper, whose backing bytes can come from either (a) a go:embed blob in the cloud-boot "livedoom" boot artifact, or (b) a runtime stream from a cloud-boot OCI artifact mount.

The actual wire-in (phase3_oci_doom_boot.go in cloud-boot/tamago-uefi) is a follow-up sprint and is intentionally NOT done here.

See PORT.md for the detailed adaptation plan.

How to verify the cloud-boot DOOM demo is operational (R-doom1g)

The provable test protocol lives in cloud-boot/docs/docs/architecture/doom-provable-protocol.md. Short version:

# Re-harvest the host oracle (byte-equal reproducible — proves
# engine + WAD + determinism hooks are intact):
cd go-doom/engine
GOWORK=off go run ./cmd/harvest-reference \
    -wad embedwad/doom1.wad \
    -seed 0 \
    -checkpoints 1,35,70,140,350,1050 \
    -out /tmp/fresh-oracle

# Compare against the committed oracle — every SHA must match.
diff <(cd oracle && sha256sum frame_tic*.ppm) \
     <(cd /tmp/fresh-oracle && sha256sum frame_tic*.ppm)

For the full guest-stack gate (boots the EFI probe in QEMU+EDK2 and compares virtio-gpu output against the guest oracle):

cd cloud-boot/tamago-uefi
task doomboot:efi:amd64
bash internal/livedoomboot/provable_test.sh amd64

The oracle/ directory is committed (~1.1 MiB of PPMs + manifest). The WAD itself is .gitignored (see embedwad/.gitignore). The determinism hooks live in seed.go (sibling of doom.go, never modifying the transpiled engine source).

About

Pure-Go DOOM (id Tech 1, 1993) engine. Hand-port lineage id Software 1997 -> AndreRenaud/gore -> go-doom/engine. Bare-metal TamaGo backend, CGO=0, 4-gate provable-test protocol.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages