nesformer compiles an NES CPU into stock Qwen3.5 weights

nesformer compiles an NES CPU into stock Qwen3.5 weights

nesformer is a GitHub repository that puts an NES CPU inside a language model. The NES's 6502 CPU (the core of its 2A03 chip), its 2 KB of work RAM and a cartridge's program ROM run inside a standard Qwen3_5ForCausalLM checkpoint. According to the README, nobody trained these weights; a compiler computed every one. When you play a game, the model predicts the CPU's execution log one token at a time. A small host program plays the rest of the console around it: it draws the picture, reads the controller and fires the vblank interrupt.

The project builds on Qwenputer by Divyansh Agrawal, which compiled a CHIP-8 and then a RISC-V CPU into Qwen3.5-4B. nesformer reuses Qwenputer's compiler and its central trick: the model's Gated DeltaNet state is the machine's memory. Each DeltaNet head keeps a 128 × 128 state matrix that the delta rule updates at every token. With the gate held open and a one-hot key, an update overwrites exactly one row of that matrix, and a one-hot query reads it back. So every row is a memory cell, and the cartridge, RAM and registers all live in rows.

The checkpoint is an ordinary Hugging Face folder that loads with Qwen3_5ForCausalLM.from_pretrained and needs no custom code. A file called nesformer.json records which layer and head holds each part of the machine. A run writes the cartridge, registers and RAM into those DeltaNet states, then decodes greedily. Each instruction becomes a record, and the record's last token commits it. The program counter is implied rather than written: each final token carries its instruction's length, three DeltaNet counters tally the instructions of each length since the last jump, and the PC is the jump target plus that offset. Most instructions take a single token; real game code averages 1.1 to 1.3 tokens per instruction. The README shows excerpts of the trace from the Full Game test ROM, such as a JSR pushing its return address in-circuit and the host injecting a value for a read of the PPU status register.

The README says every CPU token in the runs came from the model, and that for the bundled test games every frame also matches a conventional 6502 emulator's output exactly. Its checks, in order: the reference CPU matches nestest's golden log for 3,348 instructions and the py65 emulator on 5.7 million instructions of game code; the exact circuit, run in Qwenputer's evaluator with its bf16 exactness checks, predicts every reference token for all of nestest plus 283,610 positions of two games' boot sequences; the checkpoint in stock transformers (bf16, CPU) predicts 2,651 positions of nestest and Full Game with no mismatches; and the free-running weights generate traces identical to the reference CPU's. An idle-loop skip changes nothing visible: frames and I/O writes, cycle timestamps included, match a run without it.

It is slow. The default engine computes Qwen3.5's forward pass from model.safetensors, compiled with numba and skipping the all-zero blocks. It ran at about 1,650 tokens per second on one CPU core of the machine it was built on. The plain numpy engine does about 210, and stock transformers about 10. A real NES would need about 700,000 tokens per second. To close some of the gap, the host watches for the loop where games wait for vblank; when a loop body repeats exactly with no RAM or I/O writes, it advances the clock to just before the next PPU event, while the remaining iterations still run. That cuts the token count by 2.6× to 4.2×. Overall, nesformer runs 150 to 290 times slower than the real console. The first run spends about 20 seconds compiling the engine with numba; after that a frame takes 1 to 6 seconds, so 600 frames (10 seconds of NES time) take around 25 minutes.

To use it, you unzip nesformer.zip and nesformer-weights.zip into one folder, need Python 3.11 or newer, install numpy, scipy, numba and pillow, and run a command such as python -m nesformer run roms/thwaite.nes --frames 600 --show with a --buttons script that describes controller presses by frame range. The CLI draws each frame in the terminal and saves an animated GIF when the run ends. You supply your own ROM; the repo ships only three freely licensed test ROMs: nestest, Full Game and Thwaite. The README names Super Mario Bros., Donkey Kong and Excitebike as examples of NROM games you can point it at.

The limits are explicit. Only NROM cartridges (iNES mapper 0) work, because the CPU can address only 32 KB of program ROM at a time and ROM reads happen inside the model from a fixed 32 KB table. Bank-switched games, with mappers such as MMC1 or MMC3, cannot be followed yet. Only official opcodes are supported, and an unofficial opcode emits HALT; so does a jump into RAM. There are no IRQs (APU frame IRQ, DMC), though BRK and NMI work. Read-modify-write instructions on I/O registers do not produce an I/O write, and $4020 to $7FFF reads as 0, so there is no PRG-RAM. The PPU and APU are not in the weights: the PPU renders one scanline at a time, so mid-scanline effects are not reproduced, sprite-0 hit and sprite overflow are approximate, and there is no sound. nesformer is MIT licensed; the test ROMs keep their own licenses.

Key facts

  • nesformer compiles an NES 6502 CPU, its 2 KB of work RAM and a cartridge's program ROM into the weights of a stock Qwen3_5ForCausalLM checkpoint; per the README, nobody trained the weights and a compiler computed every one.
  • The trick, reused from Divyansh Agrawal's Qwenputer, is to use the Gated DeltaNet state as memory: each row of a head's 128 × 128 state matrix is one memory cell holding cartridge, RAM and registers.
  • For the bundled test games, the README says every frame matches a conventional 6502 emulator's output exactly; the reference CPU matches nestest's golden log over 3,348 instructions.
  • It is slow: the default numba engine does about 1,650 tokens per second on one CPU core against the 700,000 a real NES would need, and the whole system runs 150 to 290 times slower than the console.
  • Only NROM (mapper 0) cartridges and official opcodes are supported, with no sound and no IRQs; the code is MIT licensed and you supply your own ROMs.

Why it matters

This is a proof of concept that a real 8-bit console CPU can run inside an unmodified transformer architecture, with no custom model code. The unusual part is how: the weights are computed by a compiler, and the Gated DeltaNet state of Qwen3.5 serves as the machine's memory. nesformer extends Qwenputer, which did the same for a CHIP-8 and a RISC-V CPU, to a CPU with real game software. It is a demonstration of what the architecture can express, not a faster or better way to run games.

Who it affects

Mainly people who like to poke at how transformers work, and retro-computing hobbyists. Anyone with a legally obtained NROM-board ROM and a Python environment can try it. Players hoping for a usable emulator will not get one: it is far slower than a real console and lacks sound.

How to use it

Unzip nesformer.zip and nesformer-weights.zip into the same folder, with weights/ beside the nesformer/ package, and check them with shasum -a 256 -c weights/SHA256SUMS. You need Python 3.11 or newer and the packages numpy, scipy, numba and pillow. Run python -m nesformer run roms/thwaite.nes --frames 600 --show with a --buttons argument such as start@90-95, where each entry names buttons, then @, then frame ranges. Frames count from power-on at 60 per second. The commands run, check and trace accept --weights, --engine, --buttons and --no-skip. Expect about 20 seconds of one-time compilation, then 1 to 6 seconds per frame. Running the tests needs pytest and py65 and takes a few minutes; the stock transformers engine needs torch 2.6 or newer and transformers 5.22 or newer. The code is MIT licensed, and the ROMs in roms/ keep their own licenses (Thwaite is GPL-3.0).

How solid is it

The checks come from the project's own README, with tests bundled in the repo. They range from the reference CPU against nestest and the py65 emulator, to the compiled circuit against the reference, to the checkpoint in stock transformers on 2,651 positions and the free-running weights against the reference CPU's traces. The frame-exact match to a conventional emulator is claimed only for the bundled test games. No other game is reported as verified.

Risks and caveats

Only NROM cartridges (mapper 0) work, so bank-switched games cannot be followed yet. Unofficial opcodes and jumps into RAM emit HALT, IRQs are not supported, and there is no PRG-RAM. The PPU and APU are not in the weights, so there is no sound, mid-scanline effects are not reproduced, and sprite-0 hit and sprite overflow are approximate. Speed is the main practical limit: 600 frames take around 25 minutes. The weights are compiled, not trained, so the model does not play or learn games in any sense. You must supply your own ROM.

“Nobody trained these weights; a compiler computed every one.”

— nesformer README