Skip to content

jenska/gost

Repository files navigation

GoST - Atari ST Emulator in Go

GoST logo

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

Status

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 emulation status

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.

Features

  • 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 .adi image 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

Project Layout

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

Requirements

  • 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.txt and internal/assets/EMUTOS-README.txt

Running

Start the emulator with the bundled EmuTOS image:

make run
go run ./cmd/gost

If 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.

CLI Flags

  • --config <path>: optional JSON config file loaded before CLI overrides
  • --preset <name>: machine preset, currently default, stf, st, or mega-st
  • --model <name>: hardware model, currently st or ste
  • --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 (default 30, set 0 to disable)
  • --hd-image <path>: optional persistent ACSI hard disk image file; raw sector images and .hdi containers 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 from clock-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, currently cpu, cpu-verbose, boot, boot-verbose, shifter, or shifter-verbose
  • --trace-start <addr>: first PC included in boot and boot-verbose traces
  • --trace-end <addr>: last PC included in boot and boot-verbose traces
  • --dump-frame <path>: write the last rendered framebuffer to a PNG file

WebAssembly

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 wasm

Serve the generated files locally:

python3 -m http.server --directory docs 8000

Then 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-frame remain desktop/headless features unless a browser-side file picker is added later.
  • The generated .wasm binary must be served over HTTP; opening docs/index.html directly from disk will not work.

Development

Run tests:

make test
go test ./...

Debug-oriented emulator probes are kept behind a build tag so the default suite stays fast:

go test -tags debugtests ./internal/emulator

Build everything:

make build
go build ./...

See available targets:

make help

Concurrency Notes

At 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.

Recommended Near-Term Approach

  • 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 Machine state and accepts input/events through channels.

Shifter Guidance

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.

Current Implementation Notes

  • The CPU core is provided by m68kemu; this repo does not implement its own 68000.
  • If no ROM path is passed, cmd/gost boots 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-image to persist hard-disk contents across emulator restarts; .hdi files are stored with an Anex86-compatible header.
  • Use --rtc to attach the ICD-compatible ACSI real-time clock backed by the host system clock.

Known Gaps

  • 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

Next Steps

  • 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

About

GoST - Atari ST Emulator in Go

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors