Skip to content

Commit 064dfe4

Browse files
tannevaledclaude
andcommitted
Fork AndreRenaud/gore as cloud-boot/godoom; scaffold TamaGo backend
Forks AndreRenaud/gore@7dc6c65 (pure-Go transpilation of doomgeneric, GPL-2.0) as the engine basis for cloud-boot's Phase 3 OS-agnostic OCI bare-metal DOOM demo. The engine is left verbatim so we can rebase against upstream cleanly; cloud-boot-specific additions live in new subdirectories. Adds: - backend/tamago: DoomFrontend implementation routing draw/sound/input through small driver interfaces (GPU, Sound, Input) that the upcoming go-virtio/gpu, go-virtio/sound, go-virtio/input drivers will satisfy. Nil devices are no-ops so the package can be linked against today, before the real drivers land in their parallel sibling sprints. - internal/embedwad: io/fs.FS shim that serves a WAD blob from an in-memory byte slice, so the engine's SetVirtualFileSystem hook can work with a go:embed'd shareware IWAD or a streamed OCI artifact on a bare-metal target with no filesystem. - PORT.md: adaptation contract between godoom and the sibling sprints producing the virtio drivers + the cloud-boot livedoom boot artifact (rendering, audio, input, WAD ingestion, TamaGo runtime constraints, sprint plan, open questions). - README.md: fork origin, license boundary (engine GPL-2.0, rest of cloud-boot stays BSD-3 via the isolated livedoom artifact), status table, smoke-test recipe. Mechanics: - go.mod module path -> github.com/cloud-boot/godoom; engine package name kept as `gore` (avoids touching 990 KB of transpiled code). Backend package imports the engine under an alias. - All five upstream examples rewritten to the new import path. - Upstream README preserved as README.upstream.md. Smoke test (Go 1.26.4, CGO=0, darwin/arm64 + linux/amd64 + linux/arm64): library, tamago backend, embedwad, and the pure-Go termdoom/webserver examples all build clean; the engine loads shareware doom1.wad and ticks through the menu via the gore reference test harness. License boundary: this repository inherits GPL-2.0 from the engine; the rest of cloud-boot remains BSD-3-Clause, isolated via the future single-purpose livedoom artifact (not in this repo). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 7dc6c65 commit 064dfe4

14 files changed

Lines changed: 888 additions & 92 deletions

File tree

PORT.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
# PORT.md -- godoom adaptation plan for cloud-boot TamaGo + UEFI
2+
3+
This document is the engineering contract between `cloud-boot/godoom` and the
4+
sibling sprints producing `go-virtio/gpu`, `go-virtio/sound`,
5+
`go-virtio/input`, and the `cloud-boot/tamago-uefi` "livedoom" boot artifact.
6+
It is the source of truth for *what* godoom needs to do and *what shape* its
7+
dependencies must take.
8+
9+
## 0. Premises (verified at sprint start)
10+
11+
- Engine: a fork of `AndreRenaud/gore`, which is a pure-Go transpilation of
12+
`doomgeneric`. ~990 KB of generated Go, single package `gore`.
13+
- CGO: **never**. Confirmed builds with `CGO_ENABLED=0` for
14+
`darwin/arm64`, `linux/amd64`, `linux/arm64` (i.e. the live TamaGo targets).
15+
- Frontend contract: the engine consumes one interface,
16+
`gore.DoomFrontend`:
17+
18+
```go
19+
type DoomFrontend interface {
20+
DrawFrame(img *image.RGBA)
21+
SetTitle(title string)
22+
GetEvent(event *DoomEvent) bool
23+
CacheSound(name string, data []byte)
24+
PlaySound(name string, channel, vol, sep int)
25+
}
26+
```
27+
28+
This is the entire surface we need to drive virtio devices.
29+
30+
- WAD ingestion: the engine reads its IWAD via `fs.FS`, settable through
31+
`gore.SetVirtualFileSystem`. We do not need a real filesystem on the
32+
bare-metal target.
33+
34+
- Memory budget: TamaGo amd64 ships with a 256 MiB heap. DOOM's working
35+
set is ~32 MiB plus a 4 MiB shareware WAD (or 12 MiB DOOM2). Comfortable
36+
with a 4x margin.
37+
38+
## 1. Rendering: DOOM -> virtio-gpu framebuffer
39+
40+
### Engine contract
41+
- DrawFrame is called once per game tick with an `*image.RGBA` of exactly
42+
`SCREENWIDTH x SCREENHEIGHT` (320x200) RGBA8888.
43+
44+
### Adaptation
45+
1. `backend/tamago` exposes a `GPU` interface with `Flip(*image.RGBA) error`.
46+
2. The real `go-virtio/gpu` driver will:
47+
- Allocate a host-visible 2D resource of size 320x200 (or a larger
48+
scanout with integer scale, decided by the driver).
49+
- Map the engine's RGBA backing slice as the transfer source.
50+
- Submit `RESOURCE_FLUSH` + `SET_SCANOUT` on each Flip.
51+
3. We deliberately do not impose a vsync model in the interface; the
52+
driver may block or be wait-free. The engine has its own 35 Hz tick.
53+
4. Letter-boxing and aspect correction (4:3 vs 320x200) is the driver's
54+
problem, not godoom's.
55+
56+
### Open questions for go-virtio/gpu
57+
- Does Flip take ownership of the pixel buffer (no), or is it a borrow
58+
(yes, current assumption)? -- documented in `frontend.go` GoDoc.
59+
- Is there a cursor / overlay plane needed for the menu? -- no, DOOM
60+
draws its own cursor.
61+
62+
## 2. Audio: DOOM SFX -> virtio-sound
63+
64+
### Engine contract
65+
- CacheSound is called once per `dmx` lump with the full lump bytes
66+
(8-byte header + 8-bit unsigned PCM @ 11025 Hz mono).
67+
- PlaySound is called per game event with (name, channel, vol[0..127],
68+
sep[0..255]). The engine assumes mixing happens host-side.
69+
70+
### Adaptation
71+
1. `backend/tamago` exposes a `Sound` interface
72+
`{ Cache(name, data) error; Play(name, ch, vol, sep) error }`.
73+
2. The real `go-virtio/sound` driver will:
74+
- Open one PCM output stream (CONFIG_PCM_OUTPUT) at 11025 Hz, mono,
75+
u8. If the host advertises only s16le, we resample/convert in the
76+
driver, not in godoom.
77+
- Maintain an in-driver SFX dictionary keyed by name; Cache copies the
78+
PCM payload (skipping the 8-byte dmx header) into a pinned buffer.
79+
- Play schedules mixing of the named buffer into the stream at the
80+
given volume / panning. We expect the driver to support at least
81+
8 voices to match DOOM's channel count.
82+
83+
### Out of scope for sprint 1
84+
- MUS / MIDI music. The engine emits MUS events to a `sound_module`
85+
interface we currently leave nil. Sprint 2 will add a tiny soundfont
86+
synth (likely a port of `go-soundfont` used by
87+
`danielgatis/go-doom`) routed through the same virtio-sound stream.
88+
89+
## 3. Input: keyboard -> virtio-input
90+
91+
### Engine contract
92+
- GetEvent fills a `gore.DoomEvent {Type, Key, Mouse}`. Returns true to
93+
consume an event, false to stop draining for this tick.
94+
- Mouse is optional; sprint 1 keyboard-only.
95+
96+
### Adaptation
97+
1. `backend/tamago` exposes an `Input` interface returning raw HID
98+
keyboard events `{HIDUsage uint16, Down bool}`.
99+
2. Translation table from HID usage IDs to DOOM scancodes lives in
100+
`frontend.go::hidUsageToDoomKey`. Initial coverage: arrows, enter,
101+
escape, space (use), tab (automap), Ctrl (fire).
102+
3. Auto-repeat: handled by the input device (it stops generating events
103+
after the HID auto-key-repeat period). godoom does NOT need to
104+
synthesize a release-after-N-ticks the way `termdoom` does, because
105+
we have real key-up events from HID.
106+
107+
### Open questions for go-virtio/input
108+
- Does the driver coalesce events or hand us one per Poll? Either works
109+
but the GoDoc on `Input.Poll` must specify.
110+
111+
## 4. WAD ingestion: embed vs OCI stream
112+
113+
### Phase 1 -- embed (sprint 1, planned)
114+
The cloud-boot "livedoom" artifact does:
115+
116+
```go
117+
//go:embed doom1.wad
118+
var doom1 []byte
119+
120+
func main() {
121+
gore.SetVirtualFileSystem(embedwad.New("doom1.wad", doom1))
122+
fe := tamago.New(gpu, snd, in)
123+
gore.Run(fe, []string{"-iwad", "doom1.wad"})
124+
}
125+
```
126+
127+
This works on day one with zero IO machinery.
128+
129+
### Phase 2 -- OCI artifact stream (sprint follow-up)
130+
- The bootloader pulls the WAD as an OCI artifact layer and exposes the
131+
bytes to the kernel image at a fixed memory address.
132+
- "livedoom" reads from that address into a `[]byte` slice, then wraps it
133+
in `embedwad.New(...)`. Same fs.FS shim, different backing.
134+
- No change required in godoom itself.
135+
136+
## 5. TamaGo runtime constraints
137+
138+
| Constraint | Effect on godoom |
139+
|-------------------------------------|-----------------------------------------------|
140+
| No signal handling | The engine never installs handlers; ok. |
141+
| No `syscall` package | doom.go uses only `os.DirFS` + std lib; `SetVirtualFileSystem` lets us replace it before any IO. |
142+
| No real preemptive scheduler | The engine runs in a single goroutine via `Run`; no fan-out. ok. |
143+
| `time.Sleep` uses runtime timers | TamaGo provides these. We do NOT use busyWait. |
144+
| 256 MiB heap on amd64 | DOOM 32 MiB working set + 4 MiB WAD = ~36 MiB. 4x margin. |
145+
| No filesystem | All IO goes through the `fs.FS` shim. |
146+
| No CGO | gore is pure Go; SDL/Ebitengine examples are excluded from livedoom builds via `go build .` of `livedoom/` only. |
147+
148+
## 6. Migration plan (sprints)
149+
150+
| Sprint | Work item | Status |
151+
|-------:|-----------------------------------------------------------------|----------|
152+
| 1A | Survey + fork + scaffold + smoke test | DONE (this sprint) |
153+
| 1B | Land `go-virtio/sound` driver matching `Sound` interface | parallel sprint |
154+
| 1C | Land `go-virtio/input` driver matching `Input` interface | parallel sprint |
155+
| 2A | Wire `livedoom/` boot artifact in cloud-boot/tamago-uefi | next |
156+
| 2B | Live demo run on bare-metal UEFI VM, capture video | next |
157+
| 2C | MUS -> MIDI -> soundfont music path | optional |
158+
159+
## 7. Non-goals (this repository, ever)
160+
161+
- Network multiplayer.
162+
- Mod / PWAD support beyond what the engine already gives us
163+
(the engine reads multiple WADs via `-file`; no extra work needed but
164+
also not a sprint focus).
165+
- Saving / loading game state across reboots (no persistent storage in
166+
the demo target).
167+
- Rewriting `doom.go`. The transpiled blob stays as-is. Improvements
168+
upstream in `gore` will be rebased in periodically.
169+
170+
## 8. Open questions tracked upstream
171+
172+
- gore #N (TBD): `unsafe` usage in scanline blitter -- safe under TamaGo?
173+
- gore #N (TBD): exported `KEY_*` constants are upper-case ALL CAPS
174+
because of the transpiler; this is harmless for our use.
175+
- gore #N (TBD): "Run called twice, ignoring second call" warns at
176+
WARN level; we should make this an error since TamaGo will only call
177+
Run once per boot.

README.md

Lines changed: 89 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,112 @@
1-
# 🔥 GORE 🔥
2-
## A Minimal Go Port of doomgeneric
1+
# cloud-boot/godoom
32

4-
```
5-
██████╗ ██████╗ ██████╗ ███╗ ███╗
6-
██╔══██╗██╔═══██╗██╔═══██╗████╗ ████║
7-
██║ ██║██║ ██║██║ ██║██╔████╔██║
8-
██║ ██║██║ ██║██║ ██║██║╚██╔╝██║
9-
██████╔╝╚██████╔╝╚██████╔╝██║ ╚═╝ ██║
10-
╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝
11-
.GO
12-
```
3+
A pure-Go DOOM engine adapted for cloud-boot's bare-metal TamaGo + UEFI demo
4+
target.
135

14-
## TLDR
15-
Tired of reading already?
16-
```bash
17-
wget https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad
18-
go run github.com/AndreRenaud/gore/example/termdoom@latest
19-
```
6+
## Fork origin
207

21-
## 💀 WHAT FRESH HELL IS THIS?
8+
This repository is a fork of [AndreRenaud/gore](https://github.com/AndreRenaud/gore)
9+
at commit `7dc6c65493b8a29e0b93dd00dac21a2f10a0068a` (2026-05-11).
2210

23-
This is a **minimal, platform-agnostic Go port** of the legendary DOOM engine, transpiled from the `doomgeneric` codebase. No CGo. No platform dependencies. Just pure, unadulterated demon-slaying action powered by the glory of Go's cross-compilation.
11+
`gore` itself is a Go transpilation of [doomgeneric](https://github.com/ozkl/doomgeneric)
12+
performed with [`modernc.org/ccgo/v4`](https://gitlab.com/cznic/doomgeneric.git)
13+
and then hand-cleaned. The engine has no CGO dependency, only standard
14+
library, and exposes its host bindings through a small `DoomFrontend`
15+
interface -- exactly the shape we need to wire DOOM into cloud-boot's
16+
virtio device tree.
2417

25-
The original C code was converted to Go using (modernc.org/ccgo/v4), by cznic (https://gitlab.com/cznic/doomgeneric.git). This was then manually cleaned up to remove a lot of manual pointer manipulation, and make things more Go-ish, whilst still maintaining compatibility with the original Doom, and its overall structure.
18+
The upstream `README.md` is preserved as `README.upstream.md`.
2619

27-
## 🔫 FEATURES
20+
## Why this fork exists
2821

29-
-**Platform Agnostic**: Runs anywhere Go runs
30-
-**Minimal Dependencies**: Only requires Go standard library
31-
-**Multiple DOOM Versions**: Supports DOOM, DOOM II, Ultimate DOOM, Final DOOM
32-
-**WAD File Support**: Bring your own demons via WAD files
33-
-**Memory Safe**: Go's GC protects you from buffer overflows (but not from Cacodemons) (WIP - 95% complete)
34-
-**Cross Compilation**: Build for any target from any platform
22+
cloud-boot is building an OS-agnostic OCI boot architecture on top of
23+
TamaGo + UEFI. To showcase Phase 3 we want to run classic DOOM as the
24+
"OS payload", with rendering, sound, and input wired straight to virtio
25+
devices instead of SDL / X11 / a kernel.
3526

36-
### Missing Features
37-
- One instance per process: Still has a lot of the original global variables, which prevent multiple instances from running
38-
- Random exported consts: The original C code used the standard convention of all upper case for const/enum values. This results in the Go code assuming these are exported values, when really they're internal state info
39-
- Nice external API for state inspection: It would be good to be able to change the running state externally, without exposing everything in such a raw way
40-
- `unsafe`: There are still some instances of `unsafe` in the code. It would be good to get rid of these to have better bounds access guarantees
27+
This fork therefore adds:
4128

42-
## 🚀 INSTALLATION
29+
- `backend/tamago/` -- a new `DoomFrontend` implementation that drives
30+
cloud-boot's virtio-gpu, virtio-sound, and virtio-input drivers
31+
(currently stub interfaces; real wiring lands once
32+
[`go-virtio/sound`](https://github.com/go-virtio/sound) and
33+
[`go-virtio/input`](https://github.com/go-virtio/input) ship in their
34+
sibling sprints).
35+
- `internal/embedwad/` -- an `io/fs.FS` shim that serves a WAD blob from
36+
memory, so the engine can load the IWAD without a real filesystem.
4337

44-
### Prerequisites
45-
- Go 1.24+
46-
- A WAD file
38+
The upstream `gore` engine (`doom.go`, ~990 KB of transpiled code, plus
39+
`doom_test.go`) and its existing terminal / web / Ebitengine / SDL
40+
examples are kept verbatim, so we can rebase against upstream cleanly.
4741

48-
### Running the examples
49-
These examples are both very minimal, and whilst technically run the game, they are not really fully complete games in their own right (ie: Missing key bindings etc...). They all assume that a Doom wad is available in the current directory. The shareware Doom wad is available at https://www.doomworld.com/classicdoom/info/shareware.php, or bring your own from a commercial copy.
42+
## License boundary
5043

51-
```bash
52-
git clone https://github.com/AndreRenaud/gore
53-
cd gore
54-
```
44+
- The DOOM engine (`doom.go`, `doom_test.go`, `LICENSE`) inherits
45+
**GPL-2.0-only** from `doomgeneric` / `gore`. All cloud-boot additions
46+
in this repository (`backend/tamago`, `internal/embedwad`) are released
47+
under the same license, by necessity (linking + derived work).
48+
- The rest of the cloud-boot stack is **BSD-3-Clause** and is *not*
49+
affected: cloud-boot's bootloader, runtime, and host components do not
50+
import this package. The integration happens via a thin separate
51+
"livedoom" boot artifact whose only job is to call `godoom.Run`.
52+
That artifact will itself be GPL-2.0, isolated to a single directory in
53+
the cloud-boot/tamago-uefi tree.
54+
55+
## Layout
5556

56-
#### Terminal based
57-
This example renders the Doom output using ANSI color codes suitable for a 256-bit color capable terminal. It has very limited input support, as terminals typically do not support key-up events, or control-key support. So `fire` has been remapped to `,`, and it is necessary to repeatedly tap keys to get them to continue, as opposed to press & hold.
58-
```bash
59-
go run ./example/termdoom -iwad doom1.wad
57+
```
58+
.
59+
|-- doom.go # upstream gore engine (transpiled DOOM)
60+
|-- doom_test.go # upstream gore reference-frame tests
61+
|-- example/ # upstream gore examples (termdoom, web, ...)
62+
|-- backend/
63+
| `-- tamago/ # NEW: DoomFrontend over virtio-gpu/sound/input
64+
|-- internal/
65+
| `-- embedwad/ # NEW: io/fs.FS shim over an in-memory WAD
66+
|-- PORT.md # Adaptation plan (read this)
67+
`-- README.md
6068
```
6169

62-
<video width="640" src="https://github.com/user-attachments/assets/c461e38f-5948-4485-bf84-7b6982580a4e"></video>
70+
## Status
6371

64-
#### Web based
65-
```bash
66-
go run ./example/webserver
67-
```
68-
Now browse to http://localhost:8080 to play
72+
| Aspect | Status |
73+
|-----------------------------------|-------------------------------------------|
74+
| Pure Go (CGO=0) | yes (engine + all new code) |
75+
| Builds on Go 1.26.4 | yes (lib + tamago backend + pure-Go examples) |
76+
| Cross-compiles linux/amd64 | yes (CGO=0) |
77+
| Cross-compiles linux/arm64 | yes (CGO=0) |
78+
| Runs shareware DOOM1.WAD | yes (engine ticks; verified via TestMenus harness) |
79+
| TamaGo backend wired | scaffold only; real drivers land in follow-up sprint |
80+
| Music (MUS -> MIDI) | out of scope for sprint 1 |
81+
| Multiplayer | out of scope |
82+
83+
## Quick smoke test (host)
6984

70-
#### Ebitengine
7185
```bash
72-
go run ./example/ebitengine
73-
```
74-
The window should pop up to run Doom
75-
76-
### Getting WAD Files
77-
You need the game data files (WAD) to run DOOM:
78-
- **Shareware**: Download `doom1.wad` (free)
79-
- **Retail**: Use your legally owned copy of DOOM.WAD or doom2.wad
80-
- **Ultimate DOOM**: doom.wad from Ultimate DOOM
81-
- **Final DOOM**: tnt.wad or plutonia.wad
82-
83-
## 🔧 PLATFORM IMPLEMENTATION
84-
85-
Similar to `doomgeneric`, the actual input/output is provided externally. The following interface is required:
86-
```go
87-
type DoomFrontend interface {
88-
DrawFrame(img *image.RGBA)
89-
SetTitle(title string)
90-
GetEvent(event *DoomEvent) bool
91-
CacheSound(name string, data []byte)
92-
PlaySound(name string, channel, vol, sep int)
93-
}
86+
# 1. fetch the shareware IWAD (free re-release):
87+
curl -sSL -o doom1.wad https://distro.ibiblio.org/slitaz/sources/packages/d/doom1.wad
88+
89+
# 2. terminal renderer (pure Go):
90+
CGO_ENABLED=0 go run ./example/termdoom -iwad doom1.wad
9491
```
9592

96-
| Function | Purpose |
97-
|----------|---------|
98-
| `DrawFrame()` | Render the frame to your display |
99-
| `SetTitle()` | Set the window title as appropriate to the given WAD |
100-
| `GetEvent()` | Report key presses/mouse movements |
101-
| `CacheSound()` | This will supply sound effect 8-bit 11025Hz mono audio samples |
102-
| `PlaySound()` | Play a given sound effect |
93+
## cloud-boot integration points
94+
95+
The TamaGo backend in `backend/tamago/` consumes three driver
96+
interfaces declared in that package:
97+
98+
| Frontend method | Backend interface | Driver repo (sibling) |
99+
|--------------------------|-------------------|-----------------------|
100+
| `DrawFrame(*image.RGBA)` | `GPU.Flip` | `github.com/go-virtio/gpu` |
101+
| `CacheSound / PlaySound` | `Sound.Cache/Play`| `github.com/go-virtio/sound` |
102+
| `GetEvent(*DoomEvent)` | `Input.Poll` | `github.com/go-virtio/input` |
103103

104-
Only `DrawFrame` and `GetEvent` are vital to implement to get a functioning game. The others can be left blank, and things will still basically function fine.
104+
The WAD is delivered to the engine via `gore.SetVirtualFileSystem(fs.FS)`
105+
using the `internal/embedwad` helper, whose backing bytes can come from
106+
either (a) a `go:embed` blob in the cloud-boot "livedoom" boot artifact,
107+
or (b) a runtime stream from a cloud-boot OCI artifact mount.
105108

106-
## 📜 LICENSE
109+
The actual wire-in (`phase3_oci_doom_boot.go` in cloud-boot/tamago-uefi)
110+
is a follow-up sprint and is intentionally NOT done here.
107111

108-
DOOM source code is released under the GNU General Public License.
109-
This Go port maintains the same licensing terms.
112+
See `PORT.md` for the detailed adaptation plan.

0 commit comments

Comments
 (0)