Atari 2600
StableTested and in active use. Breaking changes will be called out. CPU only — the Metal compiler cannot handle 6502 opcode dispatch.
A full Atari 2600 emulator written in Mojo: the 6502 CPU, the TIA video and audio chip, and the RIOT RAM/IO/timer chip. It runs original ROMs, and its cycle-accurate TIA is the sole renderer.
This is not a shortcut to more environments. It exists to answer a specific question about the native arcade engines: are they faithful? Validating a reimplementation against the actual hardware behaviour is a stronger check than comparing training curves.
What it emulates
Section titled “What it emulates”| Component | Scope |
|---|---|
| 6502 CPU | full instruction set, cycle counting |
| TIA | cycle-accurate video, playfield, sprites, collisions |
| RIOT | 128 bytes RAM, timers, controller I/O |
| Cartridge | standard bank-switching schemes |
Around 30,000 CPU instructions execute per rendered frame.
The game registry
Section titled “The game registry”One compiled binary plays every game. AtariGame is a value-enum id, and
everything game-specific dispatches at runtime:
game.rom_file() # "roms/<name>.bin", ale-py naminggame.num_actions() # minimal action set sizegame.action(idx) # the idx-th ALE actiongame_signals(game, ram) # score, reward, lives, terminal — read from RAMRuntime dispatch is deliberate. The lookup costs a handful of RAM reads per step, against ~30k emulated instructions per frame — invisible — and it avoids compiling one binary per game.
Score, reward and termination are extracted by reading the known RAM
addresses for each title, which is how ALE does it too. The registry carries
103 games (NUM_GAMES in noeira/envs/atari/games/registry.mojo),
verified frame-exact against reference behaviour.
Observation modes
Section titled “Observation modes”The emulator supports several preprocessing pipelines, matching the conventions different papers assume:
| Mode | Output | Used by |
|---|---|---|
0 | RAM (128 bytes) | RAM-based agents |
1 | 4×84×84 grayscale stack | standard DQN / Rainbow |
2 | RGB-96 | EfficientZero V2 |
3 | grayscale-96, single frame | DreamerV3 |
4 | grayscale-96, 4-frame stack | DreamerV3 variants |
5 | grayscale-64, single frame | DreamerV3 reference resolution |
Matching a paper’s preprocessing exactly matters more than it sounds — resolution and frame-stacking choices materially change what a world model can learn.
ROMs are not distributed with the project. Fetch the canonical Farama bundle:
pixi run setup-romsThen:
pixi run mojo run -I . examples/arcade_games/play_atari_pong.mojopixi run mojo run -I . examples/arcade_games/atari_smoke_all.mojoTraining on it
Section titled “Training on it”It works, and DreamerV3 on emulated Pong is the project’s most substantial result — a full reproduction reaching a perfect +21 game by 90k steps and averaging +14–16 at 100k, using the grayscale-96 single-frame mode.
pixi run -e nvidia mojo run -I . examples/atari/dreamerv3_atari_pong_training.mojopixi run -e nvidia mojo run -I . examples/arcade_games/rainbow_atari_pong_pixel_training_gpu.mojo- Arcade games — the fast native reimplementations
- Environments overview — the full catalogue