Releases: Maartz/atomboy
Release list
v0.4.0 — The console gets a body, and a language of its own
Atomboy runs in both directions now. It still plays Game Boy and Game Boy Color
games — but the instruction table behind the emulated CPU also emits a language
that compiles Elixir into cartridges, and a complete emulator in RISC-V
assembly that runs on a chip you can hold. On the playing side, the window
grew a console around itself, learned to write time down and replay it, and
learned to fork.
Potion — the console, written from the other side
- A language in the lineage of Andy Gavin's GOOL: the surface is Elixir, the
semantics are the console's, and the output is a real 32 KB cartridge —
header, Nintendo logo, checksums — that runs here or on hardware. - Six games in
games/, each written to teach the compiler something: a walking
square, two screens with solid walls, a room bigger than the screen with a
chasing camera, Pong with a title screen and an opponent that can lose, Tower
the upward platformer, and Moananas Island. - Sound as notation rather than registers — motifs, triplets, bar lines, bends,
three voices sharing four channels — generated identically to what the BEAM
emulator produces. - The assembler is derived from the same instruction table as the emulator's
decoder, so the two cannot disagree, and the emulator is the compiler's test
harness down to pixel-exact assertions. ELIXIR_ERL_OPTIONS="-noinput" mix atomboy.live games/pong.exskeeps the
console hot: edit the game, save, and the running machine picks up the new
cartridge without losing the ball's position or the score.
The native core — the emulator, in RISC-V
- The same instruction table emits a complete SM83 emulator in RV32 assembly,
written by plain Elixir: no C, no linker, no toolchain in the loop. - Validated differentially against the Elixir emulator as oracle, with the
comparison run inside the guest — dmg-acid2 comes out pixel-identical, and
the four voices matchAtomboy.APUsample for sample. - 18.0 RV32 instructions per SM83 instruction on a register-move loop, 27.5 on a
mixed block, 19,576 bytes of code all told — 59.7% of an ESP32-C6's
instruction cache, which was the whole point. esp32/native/runs it on an ESP32-C6 with an ILI9341 panel and an I2S
amplifier. The bus turned out to be the budget and the sound turned out to be
the clock; both lessons are recorded beside the code that acts on them.
The console gets a body
- ⌘B draws a console around the screen — four bodies chosen by the panel
preset, because the screen and the plastic around it are one decision: the
DMG's gray, the Pocket's silver, the CGB's teal, and a living-room television
forcrt. - Drawn rather than photographed, from each body's real millimetres, so they
stay crisp at any size. The D-pad rocks and the buttons sink under your thumb,
from the keyboard or a gamepad, and the power LED dims to an ember on pause. - Under them, the LCD simulation: response curve, dot structure, crosstalk, a
contrast dial, and an Age slider that creeps dead columns in from the edges. - View → Scale 1×–5× (⌥⌘1…⌥⌘5) sets the window to exactly 160×144×N.
The time machine
- Record Movie ⇧⌘R writes a
.tas: a header, an anchor, and one byte per
frame holding the eight buttons. The core touches no wall clock and no
randomness, so replay is not approximate — it is the same frames. - Re-recording is what makes it a tool: load a state while recording and the
take is truncated to that frame and keeps going. Everything after it never
happened. - Frame Advance (
.) buys exactly one frame while paused and closes the
pause behind it. - Export Movie… ⌘⇧E renders a take to MP4 with sound or GIF without, in a
second engine beside the one you are playing, so the game does not stop while
the film is developed. Needs ffmpeg (brew install ffmpeg); the CLI has
--record,--replay,--exportandmix atomboy.export.
The multiverse
- ⌘D forks the running game into four live universes. They take the same
buttons, so all four D-pads rock in unison, but each one's divider counter is
nudged by a different offset and game RNG feeds on divider timing: same input,
different luck. - Universe 0 is the unperturbed original — the future that would have happened
anyway. - Click one to listen to it, double-click to commit and let it grow into the
only one left, Esc to abort onto universe 0. - While four machines run, anything that would have to answer "in which
universe?" is refused at the engine, and the menu greys itself out from the
engine's own announcement rather than from a guess.
The clock
- Settings → Clock, per game: keep the console on real time, or hand the
cartridge a date of your choosing, with a +1 day button for the games that
want to be slept on. - During a take the panel locks. The recording owns the clock, deriving its hour
from its anchor and its frame count, so the same movie tells the cartridge the
same hour on every replay.
Fixes, and the repo in English
- Turbo picks its speed — 2×, 4×, 8× or Uncapped — held on a key or a shoulder,
latched from the menu and the HUD, and refused while the cable is plugged,
because the protocol is a paced duet. - The Settings window is finally allowed to shut its own door.
- Saves live in a library keyed by the cartridge header, so renaming or moving a
ROM never orphans what it had; Saves… ⌘⇧S browses them with thumbnails,
and profiles let two players share one ROM. - The link cable publishes a Bonjour service, so joining is a list of the
consoles on your network rather than an address to type. - The codebase speaks English: identifiers, comments and UI strings across the
emulator core, the front ends, Potion and the macOS shell. - The documentation is an 87-line README and four pages behind it —
docs/features.md,docs/design.md,docs/potion.md,docs/native.md—
plus two deep dives that each follow one thing all the way down.
Assets
| File | |
|---|---|
Atomboy.app.zip |
the macOS app (Apple Silicon), signed and notarized |
atomboy_macos_arm |
standalone CLI binary, macOS (Apple Silicon) |
atomboy_linux_x64 |
standalone CLI binary, Linux x86-64 |
The app is Developer ID signed, hardened, and stapled: it double-clicks clean.
The bare CLI binaries are ad-hoc signed — on first launch, right-click → Open
(or xattr -d com.apple.quarantine).
Or via Homebrew:
brew install --cask maartz/tap/atomboy # Atomboy.app
brew install maartz/tap/atomboy-cli # the terminal binaryatomboy 0.3.0
The first public release of atomboy — a Game Boy and Game Boy Color emulator written in Elixir, where the emulated CPU is BEAM code.
Highlights
- Full GB + GBC emulation — all 501 SM83 opcodes validated against ~500,000 SingleStepTests vectors; blargg
cpu_instrs11/11; dmg-acid2 and cgb-acid2 pixel-perfect; four audio channels; MBC1/3/5 with RTC; battery saves compatible with other emulators. - A native macOS app — SwiftUI shell over the BEAM engine: full-bleed pixels under rounded corners, Liquid Glass hover HUD, AVAudioEngine sound (no ffplay needed), gamepad support, Settings (⌘,) with a sound mixer and per-game GameShark codes.
- TCP link cable — two atomboys trade real Pokémon through the Cable Club, byte-for-byte faithful to the gen-2 serial protocol (paced to hardware transfer timing).
- Play comfort — save states (9 slots), 40-second rewind, turbo, an in-game menu drawn into the frame itself, GameShark codes applied every frame.
- Three front ends — terminal (kitty graphics protocol or ANSI half-blocks), native wxWidgets window, and the macOS app.
Assets
| File | |
|---|---|
Atomboy.app.zip |
the macOS app (Apple Silicon) |
atomboy_macos_arm |
standalone CLI binary, macOS (Apple Silicon) |
atomboy_linux_x64 |
standalone CLI binary, Linux x86-64 |
The binaries are ad-hoc signed: on first launch, right-click → Open (or xattr -d com.apple.quarantine).
Or via Homebrew: brew install --cask maartz/tap/atomboy