GoST is an Atari ST emulator in Go built around github.com/jenska/m68kemu for Motorola 68000 CPU emulation.
Browser build target: GoST WebAssembly demo
Major milestone:
GoST has moved beyond early bring-up and now provides a usable Atari ST desktop baseline:
- The bundled EmuTOS image boots to the GEM desktop in both monochrome and color-monitor modes.
- The desktop frontend runs in an Ebitengine window with working keyboard, mouse, and audio paths.
- Headless execution, PNG frame dumping, CPU/boot tracing, and browser builds are available for development and debugging.
- The machine model now includes RAM, ROM, Shifter, Blitter, MFP, IKBD/ACIA, MIDI/RS232 byte I/O, floppy DMA/FDC, YM2149-backed PSG audio, and basic STE DMA sound.
This is still not a complete Atari ST emulator for broad real-software compatibility yet. The current focus is cleanup, stabilization, and expanding compatibility from the working desktop baseline.
Latest 1000-frame desktop boot:
Current focus:
- Stabilize longer interactive GEM desktop sessions.
- Improve compatibility with more real Atari ST applications and disk images.
- Continue filling hardware behavior gaps where real software exposes them.
- Motorola 68000 emulation via
github.com/jenska/m68kemu - Atari ST machine model with a 24-bit bus, ROM overlay boot, and 1 MiB RAM default profile
- GEM desktop boot with the bundled EmuTOS ROM
- Monochrome and color-monitor boot modes
- Low, medium, and high resolution Shifter framebuffer rendering
- Working desktop input path for keyboard and mouse through IKBD/ACIA
- YM2149-backed PSG sound with live audio playback in the desktop frontend
- Basic STE DMA sound playback in STE model mode
- Atari ST Blitter register model exercised by live GEM/VDI boot
- MFP timer delivery plus GLUE-backed VBL/HBL autovector timing
- Basic MIDI and RS232 byte I/O paths for ACIA/MFP register-level testing
- Optional read-only cartridge ROM mapping at
$FA0000-$FBFFFF - Floppy DMA/FDC path with
.st,.msa,.dim, and compatible headered.adiimage support - Virtual ACSI hard disk (30 MiB default) that enumerates as C: under bundled EmuTOS
- Optional ICD-compatible ACSI real-time clock
- Desktop frontend via Ebitengine
- Headless execution with PNG framebuffer dumping
- CPU, boot, and verbose tracing for bring-up and debugging
- WebAssembly build target for browser-based experiments
- Automated Go test coverage for devices, emulator behavior, and frontend integration
cmd/gost CLI entrypoint
internal/config Presets, JSON config loading, and CLI flag parsing
internal/emulator Machine orchestration and ST bus wiring
internal/devices Atari ST hardware device models
internal/platform Host frontend integrations
- Go 1.26+
The repository includes a bundled default ROM:
- EmuTOS 1.4 US 256K image
- Source: official EmuTOS 1.4 release
- Upstream license and release readme are mirrored in
internal/assets/EMUTOS-LICENSE.txtandinternal/assets/EMUTOS-README.txt
Start the emulator with the bundled EmuTOS image:
make rungo run ./cmd/gostIf downloads/atari-st/PDATS321.msa exists locally, make run and make headless automatically mount it as drive A.
The repository ignores TOS/, so personal ROM images can be kept there for local testing without adding them to Git. The Makefile also provides convenience targets such as make headless, make run-rom, make headless-rom, make run-mega-tos102, make test, make build, and make help.
CLI flags can be passed through ARGS when using Make targets, or directly after go run ./cmd/gost.
JSON config files are also supported. Config keys use the same names as CLI flags without the leading --:
{
"preset": "mega-st",
"floppy-a": "/path/to/disk-a.msa",
"floppy-b": "/path/to/disk-b.msa",
"hd-size-mb": 30,
"rtc": true,
"cpu-mhz": 8,
"color-monitor": false,
"trace-start": "0xE00000",
"trace-end": "0xE01000"
}Load order is: preset defaults, then JSON config file, then CLI flags.
--config <path>: optional JSON config file loaded before CLI overrides--preset <name>: machine preset, currentlydefault,stf,st, ormega-st--model <name>: hardware model, currentlystorste--rom <path>: path to the TOS ROM image; bundled EmuTOS is used when omitted--cartridge <path>: optional cartridge ROM image mapped read-only at$FA0000-$FBFFFF(up to 128 KiB)--floppy-a <path>: optional floppy disk image for drive A (.st,.msa,.dim, or compatible headered.adi)--floppy-b <path>: optional floppy disk image for drive B (.st,.msa,.dim, or compatible headered.adi)--hd-size-mb <n>: virtual ACSI hard disk size in MiB (default30, set0to disable)--hd-image <path>: optional persistent ACSI hard disk image file; raw sector images and.hdicontainers are supported--rtc: enable the optional ICD-compatible ACSI real-time clock--ram-size <bytes>: emulated RAM size in bytes--clock-hz <n>: base machine clock frequency in Hz--cpu-mhz <n>: CPU frequency in MHz; changes CPU speed without changing other hardware timing--frame-hz <n>: display and VBL refresh rate in Hz; frame timing is derived fromclock-hz / frame-hz--color-monitor: emulate an Atari color monitor instead of monochrome--midres-y-scale <n>: scale medium-resolution display height on host output (1= off)--scale <n>: window scale factor--fullscreen: start fullscreen--headless: run without opening a window--frames <n>: number of frames to run in headless mode--trace <mode>: enable tracing, currentlycpu,cpu-verbose,boot,boot-verbose,shifter, orshifter-verbose--trace-start <addr>: first PC included inbootandboot-verbosetraces--trace-end <addr>: last PC included inbootandboot-verbosetraces--dump-frame <path>: write the last rendered framebuffer to a PNG file
Yes: this project already compiles to GOOS=js GOARCH=wasm, and the bundled EmuTOS image makes a browser build practical without adding ROM download steps.
Build the browser demo assets into docs/:
make wasmServe the generated files locally:
python3 -m http.server --directory docs 8000Then open http://localhost:8000.
The repository also includes a GitHub Pages workflow at ./.github/workflows/pages.yml.
Current browser-build limitations:
- The browser build always boots the bundled EmuTOS image.
- CLI paths such as
--rom,--floppy-a,--floppy-b,--hd-size-mb,--hd-image, and--dump-frameremain desktop/headless features unless a browser-side file picker is added later. - The generated
.wasmbinary must be served over HTTP; openingdocs/index.htmldirectly from disk will not work.
Run tests:
make testgo test ./...Debug-oriented emulator probes are kept behind a build tag so the default suite stays fast:
go test -tags debugtests ./internal/emulatorBuild everything:
make buildgo build ./...See available targets:
make helpAt this stage, the emulator core is intentionally single-threaded. The CPU, bus, memory map, device advancement, and interrupt dispatch currently run in a deterministic lockstep loop. That makes bring-up, debugging, and test behavior much easier to reason about while the hardware models are still incomplete.
Using goroutines "wherever possible" is not recommended yet. For an emulator, broad early concurrency tends to introduce races, lock contention, and timing bugs before correctness is established. The current priority is accurate and predictable behavior rather than parallel execution.
- Keep the emulation core single-threaded.
- Prefer goroutines only at host-side boundaries such as async file loading, trace/log streaming, debugger tooling, or future audio buffering.
- If concurrency is introduced in the machine layer, prefer a single emulation goroutine that owns all
Machinestate and accepts input/events through channels.
The shifter is a possible future concurrency boundary, but not in its current form. Today it renders directly from live RAM and live register state, so moving rendering to another goroutine would require synchronization around RAM, palette registers, resolution, base address, and framebuffer ownership.
If shifter work is parallelized later, the safer design is:
- Keep emulation-side shifter state single-threaded.
- At frame boundaries, capture an immutable snapshot of the visible video state.
- Include the screen base, resolution, palette, and the RAM bytes needed for the visible frame.
- Hand that snapshot to a renderer goroutine that converts bitplanes into an RGBA back buffer.
- Present the completed back buffer on a later host frame.
This preserves deterministic emulation while creating room for asynchronous framebuffer conversion and double-buffering.
- The CPU core is provided by
m68kemu; this repo does not implement its own 68000. - If no ROM path is passed,
cmd/gostboots the bundled EmuTOS image by default. - The machine runs with an 8 MHz default clock and 50 Hz frame cadence.
- The video path renders from RAM-backed bitplanes into an RGBA framebuffer for the host frontend.
- Interrupts are routed into the CPU through the machine layer.
- The floppy controller now covers WD1772 command groups (type I/II/III/IV) over sector images, including seek/step commands, sector and track DMA reads/writes, and read-address support.
- A virtual ACSI hard disk is attached by default with 30 MiB capacity.
- Use
--hd-imageto persist hard-disk contents across emulator restarts;.hdifiles are stored with an Anex86-compatible header. - Use
--rtcto attach the ICD-compatible ACSI real-time clock backed by the host system clock.
- Real TOS boot coverage beyond the bundled EmuTOS image is not complete yet
- MMU behavior and cycle-exact GLUE/shifter timing are still incomplete
- Shifter timing and register coverage are partial
- IKBD protocol coverage is incomplete
- Host MIDI backend/timing and copy-protected disk format support are still missing
- Improve MMU and cycle-exact shifter/GLUE behavior for broader TOS compatibility
- Expand MFP coverage and timing accuracy
- Flesh out IKBD and ACIA behavior to match TOS expectations
- Improve ACSI hard-disk command coverage and real-software compatibility
- Build debugger and trace tooling around the existing machine core

