-
Notifications
You must be signed in to change notification settings - Fork 0
Emulator atari800
The Atari 800XL bundle is the only one in GenX-DOS where we ended up compiling the emulator from source, because nothing else fit.
The shortlist was three projects. All three fell out, in order:
-
jsA8E (
AnimaInCorpore/A8E) — looked promising; a real WASM build of A8E, working in a browser. NoLICENSEfile in the repo, though. Under default copyright we can't legally redistribute, so it was out. -
EmulatorJS — covers a lot of ground but the upstream supported-systems list does not include Atari 800; the closest core is
a5200. No good. -
libretro-atari800 in the official RetroArch emscripten nightly — plausible because the same nightly ships ~90 other cores. We downloaded the full 760 MB
RetroArch.7zfrombuildbot.libretro.com/nightly/emscripten/RetroArch.7zto verify. atari800 is not one of the cores in the web build. (Useful side-effect: that audit confirmed VICE, gearcoleco, and o2em are in there, which seeded the C64 / Coleco / Odyssey² bundles we did later.)
So we built atari800 from source.
emscripten via emsdk, fetched into /tmp because the build needs to be reproducible and that's the path the recipe encodes:
cd /tmp && git clone --depth 1 https://github.com/emscripten-core/emsdk.git
cd /tmp/emsdk && ./emsdk install latest && ./emsdk activate latest
source /tmp/emsdk/emsdk_env.shemsdk lives at ~/emsdk/ on this machine for persistence between sessions.
atari800's autoconf uses AM_PATH_SDL2, which shells out to sdl2-config. emscripten ships SDL2 as a port (it's pulled in automatically by -sUSE_SDL=2) but doesn't ship the sdl2-config binary. The build dies on detection before it even gets to the C files.
The fix was a six-line shim that pretends to be sdl2-config and tells autoconf whatever it wants to hear:
cat > /tmp/fake-sdl2-config <<'EOF'
#!/bin/sh
case "$1" in
--cflags|--cflags-only-I|--libs|--libs-only-l|--static-libs|--prefix) echo "" ;;
--version) echo "2.30.0" ;;
esac
EOF
chmod +x /tmp/fake-sdl2-configThen SDL2_CONFIG=/tmp/fake-sdl2-config emconfigure ./configure … runs cleanly.
SDL2_CONFIG=/tmp/fake-sdl2-config emconfigure ./configure \
--target=default \
--with-video=sdl2 \
--with-sound=sdl2 \
--without-opengl \
--disable-netsio \
--disable-riodevice --disable-rserial --disable-rnetwork \
--disable-audiorecording --disable-videorecording \
--enable-altirra_bios \
CFLAGS="-O2 -sUSE_SDL=2" LIBS="-sUSE_SDL=2"Why each flag is there:
-
--without-opengl— atari800's SDL+GL path callseglCreateContext, which fails in emscripten and leaves the canvas black. The software SDL renderer works fine. -
--disable-netsio/--disable-riodevice/--disable-rserial/--disable-rnetwork— these all needclock_gettimeor a real host TTY/serial port, both of which trip emcc's strict implicit-declaration check. Nothing about them works in a browser anyway. -
--disable-audiorecording/--disable-videorecording— strips RLE/ZMBV/WAV/ADPCM/μ-law codecs we don't need. -
--enable-altirra_bios— this is the big one. It bakes Avery Lee's AltirraOS-XL 3.41 directly into the WASM. No copyrighted Atari OS ROM needs to ship alongside the bundle; the emulator falls back to AltirraOS automatically when no real ROM is found. This is also whysystems/atari800/has noroms/directory full of.binfiles — the OS lives insideatari800.wasm. -
-sUSE_SDL=2— pulls emscripten's SDL2 port for canvas, keyboard, and WebAudio.
The plain emmake make builds a Node-style src/atari800 script, not a browser-loadable bundle. To get the browser artefacts:
cd src
OBJS=$(ls atari800-*.o sdl/atari800-*.o roms/atari800-*.o codecs/atari800-*.o atari_ntsc/atari800-*.o 2>/dev/null | grep -v video_gl)
emcc -O2 -sUSE_SDL=2 -sASYNCIFY -sALLOW_MEMORY_GROWTH=1 -sINITIAL_MEMORY=64MB \
-sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS -sEXIT_RUNTIME=0 \
-o atari800.html $OBJS -lm-sASYNCIFY is non-optional. atari800's for(;;) main loop in sdl/main.c would hang the browser without it. ASYNCIFY suspends and resumes at SDL_Delay so the page can paint between frames.
Output: atari800.js (186 KB) + atari800.wasm (1.7 MB). The default emcc shell HTML gets thrown away — play.html replaces it.
play.html sets up window.Module before injecting atari800.js:
-
canvas— the<canvas>element -
arguments—['/game.atr']for disks,['-cart', '/game.rom']for cartridges, optionally['-cart-type', N, '-cart', '/game.rom']for headerless cart dumps (Star Raiders needscartType:1= Standard 8KB) -
preRun— callsModule.FS.createPreloadedFile('/', vfsName, romUrl, true, false)to fetch the ROM into the WASM virtual FS beforemain()runs
The Star Raiders .atr shipped as a8b_Star_Raiders_1979_Atari_US_k_file is a cart-binary-in-disk-wrapper — it boots straight into a 6502 CIM at $A184. The right source is the 8 KB .rom from a8b_cart_Star_Raiders_1979_Atari_US, launched with -cart-type 1.
Headless chromium screenshots of SDL2/WASM canvases look black even when frames are being drawn — SwiftShader/compositor timing makes them unreliable. Verify in a real browser, or read pixels via getImageData from a probe page.
systems/atari800/
├── play.html
├── atari800.js
├── atari800.wasm ← AltirraOS baked in here
├── games.json
├── controls.html
├── COPYING
└── roms/ ← cart / disk / cassette images
The reproducible build script lives in the Retro-Jack/atari800 fork — upstream source untouched, just build-wasm.sh + BUILDING-WASM.md on top. (It is not copied into the bundle.)
The Atari 400/800 page wraps the canvas in the 80s.png wood-cabinet TV bezel (a floating set on the 70s wallpaper, screen keyed to a transparent hole). The interesting part was making the picture fill the screen cutout corner-to-corner.
The obvious approach — scale the canvas past 100% with CSS and clip the overscan — fought atari800's video pipeline. atari800 sizes its framebuffer from the canvas's pixel dimensions and renders with near-integer scaling, so the visible-picture size jumps in coarse, viewport-dependent steps as you nudge the CSS percentage. A value tuned to fill one window doesn't hold on another.
The fix is to let the emulator do the filling. atari800's SDL build exposes its VIDEOMODE system on the command line, passed straight through Module.arguments:
-stretch full # stretch the visible area to the whole canvas (no integer-scale letterbox)
-fit-screen both # fit both axes
-image-aspect none # don't preserve 4:3 — allow a non-uniform fill
-horiz-area tv # trim the overscan border to the TV-safe area
-vert-area tv
With those, the canvas sits at a plain width/height: 100% of the cutout and the picture meets all four corners on every game, at any window size — deterministic, no calibration. (Aside: the argument builder had to be reworked to append cart/disk args rather than reassigning the array, or the video args got clobbered.)
- Emulators — index of all engines
- Emulator-XRoar-CoCo — the other from-source emscripten build
- File-Structure — where bundles sit in the repo