Infra 6 min read

A Qwen3.5 model nobody trained runs a working NES console in its weights.

Nobody trained these compiled weights. A NES CPU runs inside Qwen3.5, I checked 200,000 tokens on a CPU-only box, and here is what a startup can steal.

Abstract illustration of seven thin teal slabs stacked like model layers, with a grid of amber and coral pixel squares between them on a navy background.

Compiled weights are model weights that nobody trained: a compiler computes every number so the network behaves like a program. This week Drew Breunig published nesformer, an MIT-licensed repo that compiles the CPU of the Nintendo NES, its 2 KB of RAM and a game cartridge into a stock Qwen3.5 checkpoint. It builds on Divyansh Agrawal’s Qwenputer, which did the same trick for a RISC-V CPU a week earlier.

In this post I go over what is really inside those weights, what I ran on my own CPU-only machine, and where the idea breaks. My verdict: it is a toy as a product, but the way the authors proved it works is something every small AI team should copy on Monday.

I cloned the repo and ran it on a 4-core box with no GPU. The weights file is 933,307,736 bytes, it unzips from a 3.3 MB download, and its checksum matched the repo’s SHA256SUMS. That size gap is the first surprise. The model has 466,647,616 parameters, and I counted 9,340,331 of them as non-zero, which is 2.00%. Almost all of it is zeros.

Abstract illustration of seven thin teal slabs stacked like model layers, with a grid of amber and coral pixel squares between them on a navy background.
Seven layers, mostly zeros: the whole 6502 circuit fits in a handful of them.

What are compiled weights, and what is inside this one?

The model uses the Qwen3.5 architecture, with a hidden size of 768 and seven Gated DeltaNet layers that hold the circuit, plus one all-zero attention layer that the Hugging Face cache needs. The README says it loads with a normal Qwen3_5ForCausalLM.from_pretrained and needs no custom code.

What the model does is write down what a 6502 processor would do. Every instruction becomes a few tokens, and the last token of the record commits it. Here is the start of the trace from the README, with the assembly on the left:

SEI            P(000100 px0 L1)
LDX #$40       X($40 c0 v0 px0 L2)
STX $4017      IOW($4017=$40 L3)
TXS            S($FF L1)

Not everything lives in the weights. The README is clear about the split: the CPU, the RAM and the cartridge ROM are inside the model, but the picture chip (PPU), the controllers and the timing of the screen interrupt live in a normal Python host program. So the weights do not draw Mario. They predict the CPU log, and a small program around them draws the screen.

What did I run on a CPU-only box?

Three things, all from the repo’s own commands. First python -m nesformer check roms/nestest.nes --tokens 20000: it runs the weights and a plain Python reference CPU side by side and stops at the first difference. It printed that the weights “generated 20,000 tokens identical to the reference CPU’s trace”, at 1,188 tokens a second.

Then the same check on a homebrew game, Full Game by Doug Fraker, for 200,000 tokens. Identical again, 194 frames, 1,215 tokens a second, 2 minutes 52 seconds on my machine.

Third, I let it play: python -m nesformer run for 120 frames. Thwaite took 91 seconds for 110,770 tokens, and Full Game took 116 seconds for 137,087 tokens. I did not look at the GIFs, so I will not describe the screen. I also did not run Super Mario Bros. (I have no ROM), the Qwenputer model itself (an 18.8 GB checkpoint that wants a 24 GB GPU), or the stock transformers path, because installing torch failed in my sandbox. The authors say they checked 2,651 positions there with no mismatch. I just read that line.

Why does the RAM only work with two settings pinned?

Here is the trick, in the words of the Qwenputer write-up: set the gate to 1, set the write strength (beta) to 1, and use one-hot keys. Then each write overwrites exactly one row of the DeltaNet state, and each read gets the row back exact. A state matrix becomes memory cells, and unlike an attention cache it stays the same size however long the run is.

I did not trust that on faith, and I could not run Qwenputer, so I tested the idea alone in numpy. I used a 128-wide state, wrote 64 cells, overwrote 16 of them, read all 64 back and repeated it 200 times. With one-hot keys, gate 1 and beta 1, the biggest read error was 4.4e-16, which is float rounding.

Then I changed one knob at a time. Gate 0.98 gave an average worst error of 2.77. Beta 0.5 gave 2.07. Random unit keys instead of one-hot gave 2.77 again. My values are standard normal numbers, so an error near 2 or 3 means the memory is garbage. That is my simulation of the rule, not the real model. It shows why the authors compiled the weights and did not train them: ordinary training would never land on exactly 1.0 and 1.0.

How slow is a CPU inside a model?

Slow, and the authors say so first. The README says a real NES would need about 700,000 tokens per second, and their engine does about 1,650 on one core. Overall they report nesformer running 150 to 290 times slower than the real console.

My own numbers are for the first 120 frames only, which are mostly boot and title screen. Those 120 frames are 3,601,063 CPU cycles, or 2.0 seconds of NES time. Taking 91 and 116 seconds means roughly 45 and 58 times slower than real time. Do not mix that with the authors’ whole-game figure, because the two are measured on different stretches of the game.

Whatever the exact multiple, the shape is clear. A model that executes a processor one token at a time needs roughly 700 tokens for every millisecond of console time, if you take the README’s 700,000 a second. Anyone who tells you the model will just run your whole app inside its weights has not done this arithmetic.

What can a startup steal from how this was tested?

The tests. Bon, this is the only part I’d copy tomorrow. Look at how the README says it was checked. The reference CPU matches the community’s nestest golden log for 3,348 instructions, and the py65 emulator on 5.7 million instructions of game code. Then the weights are run free and their output must match that reference token for token. No vibes. A trace, and a diff that either passes or stops at the first wrong token.

That works for any job with rules that can be written as code. I wrote about a language whose compiler was mostly written by an AI in my Goose post, and the same lesson came back: the agent is only as good as the check it can loop against.

At Trellis the candidate would be something like a fee calculator for Amazon sellers (I am making up the function here, it is not a claim about our internals). Record a few hundred real inputs and outputs from the deterministic code, hand them to an agent as a golden trace, and make it pass before you read a line of what it wrote. Me, I’d rather have a failing diff at 9 a.m. than a clean-looking pull request I have to trust.

Should you build on compiled weights?

No, not for a product. The repo is only days old, it only runs NROM cartridges, an unofficial opcode just halts, and a jump into RAM halts too. There is also a licence detail: GitHub shows the repo as “Other” because a second file, LICENSE.chipformer, covers code adapted from Qwenputer, so read the MIT licence file and that second one before you reuse anything. Thwaite, one of the bundled ROMs, is GPL-3.0.

For arithmetic and rules, a plain tool call is faster and cheaper. Qwenputer’s author reports 97.5% on a DeepMind big-division set against 95% for base Qwen with a Python tool, and 100% on long-integer arithmetic, the same as the tool. Those are his numbers, not mine, and a tie with a Python call is not a reason to ship 18.8 GB.

Where compiled weights could matter is when you cannot call out: on a device, in an air-gapped box, or a monitor the model cannot switch off, which the Qwenputer author wrote a follow-up about. I don’t know yet if that holds up. What I will do this week is run the same check idea against one of our own functions and see how many tokens it takes before the first diff shows up.

Dominic Plouffe

CTO of Trellis, with exits at FuseTalk, 360pi and DeadHeat. I write about making agents and big data work together safely, and I'm building yaaf, an open-source platform for running teams of AI agents that actually finish the job: yaaf.dplouffe.ca