一个NES CPU编译成股票的权重Qwen3.5型号。
NES的6502 CPU(其2A03芯片的核心),工作RAM的2 KB,以及一个弹匣的程序
ROM运行在标准Qwen3 5ForCausalLM检查站内。 没有人训练这些重量;a
编译器计算每个。 当玩游戏时,模型预测CPU的执行日志
一次一个标志。 一个小主机程序在它周围播放休息台:它绘制
图片,读取控制器,并发射vblank中断。
Nesformer以Divyansh Agrawal的Qunputer为基础,将一个CHIP-8,然后一个RISC-V CPU编成Qwen3.5-4B. 它重新使用Quenputer的编译器及其中心戏法:模型的Gated DeltaNet状态是机器的内存。
这些运行中的每个CPU符号都来自模型。 对于捆绑的测试游戏,每个帧也完全匹配常规的6502仿真器输出(参见如何检查).
Unzip nesformer.zip 和 nesformer-qu量s.zip 进入同一文件夹。 体重在旁边
阴源/ 包件,不在其中:
Nesformer/ <- 从这里运行命令
QQ Nesformer/ <- Python 软件包
QQ 芯片格式/ <- Quenputer 的编译器
├── roms/ <- test ROMs( 也放在这里)
示例/ <-样本GIFs和框架工作表
测试/测试
自由主义
翻译:
* * 读本。
{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}我很高兴见到你
加权数/ <- from nesformer- weights.zip (英语).
(原始内容存档于2018-09-29). QQ config.json.
QQ 生成 config.json
QQ 型号安全器
(原始内容存档于2017-09-29). Nisformer.json.
SHA256SUMS (英语).
CLI 寻找重/ 在 Nesformer/ 包旁。 把重量放在别的地方
通过 -- 权重 / 路径/ 到/ 权重运行、 检查或跟踪,或给信息路径。
为了确认未正确解析的重量,从中运行shasum -a 256 -c重量/SHA256SUMS
顶文件夹。
你需要Python 3.11或更新。
python3 - m venv.venv (英语).
源.venv/bin/激活 #Windows:.venv\脚本\激活
pip 安装 numpy scipy poilpithon - m Nesformer 运行 roms/thwaite.nes - 帧 600 - 显示 \
-按钮"启动@90-95+200-205,a@230-233+250-253+270-273+290-293+310-313+350-353+370-373+390-393+410-413+430-433+450-453+470-473+490-493+510-513+530-533+570-573+590-593".
当模型生成时,显示您终端中的每个框架。 当运行结束时,CLI
保存当前文件夹中以ROM(这里为thwaite.gif)命名的动画 GIF。
希望它慢一点。 第一次运行花费了大约20秒的时间用麻木来编译引擎。 之后,一个帧需要1-6秒,因此600帧(NES时间为10秒)需要25分钟左右。
点运行于任意使用 NROM 板( iNES mapper 0) 的.nes 文件,如 Super Mario
兄弟们驴子金刚还是电动车您提供ROM; Nesformer 只能自由运送三艘
特许测试ROM 以 roms/.
python - m Nesformer 运行 roms/smb.nes -- frames 600 -- 显示 \
- 按钮“启动@60-65,右转@200-600,a@250-265+300-315+360-375+420-435+480-495+540-555”
剧本开始游戏,然后走马里奥右转,每秒跳一次左右。 CLI在启动时将ROM加载到模型状态,因此相同的权重在没有重建的情况下运行任意一款NROM游戏。 其他棋盘上的游戏以信息停止:参见限制。
--按钮需要一个逗号分隔的条目列表。 每个条目名称加入一个或多个按钮
由 +,然后 @,然后由 + 加入的一个或多个帧范围:
开始@ 60-65 press 60至65帧期间启动
右侧@ 200- 600 从框架 200 到框架 600
a@250-265+300-315 新闻两次
right+b@200-600 hold Right and B (运行,在Super Mario Bros.).
按钮为a,b,选择,启动,上,下,左右。 框架计数来源
电源在每秒60秒,600帧是10秒游戏忽略屏幕上的输入
你动不了所以早点按下按钮不会有什么伤害在超级马里奥兄弟,一个更长的时间
媒体会跳得更高
运行的选项:
运行、检查和跟踪所有接受者 -- -- 重量、 -- -- 引擎、 -- -- 按钮和 -- -- 不滑动。
请检查date=中的日期值 (帮助) --tokens N设置了比较的距离(默认为50,000)。 跟踪( T) N 设置数量
打印符号( 默认 40 )。 建 -- 出 DIR 写检查点以外的地方
权重/.
这种分裂遵循了Quenputer的CHIP-8构建,其主机注入计时器滴答,并从微量中抽取显示。 主机除了通过微量外,从未看到CPU的登记器或RAM.
记忆技巧是这样的每个DeltaNet头都保留着一个128×128的状态矩阵,三角洲规则在每个符号上更新。 闸门被打开和一热键,更新完全覆盖了该矩阵的一行,一个一热查询读回来。 所以,每行都是一个内存单元格,而墨盒,RAM和登记器都是排成行的。
权重/ 是普通的Hugging Face文件夹。 装满了
Qwen3 5ForCausalLM. from pretrained 不需要自定义代码。
Nesformer.json记录了机器每个部分的层层和头部。 运行写着
墨盒登记和记录进入这些 DeltaNet 状态,然后贪婪地解码。
每条指示都成为记录,记录的最后一种表示承诺。 和Quenputer的RISC-V核心一样,PC是隐含的而非书面的:每个最终的符号带有其指令的长度,三个DeltaNet计数自上次跳跃以来每个长度的指令,PC是跳跃目标加上抵消。 Full Game 摘录:
SEI P( 000100 px0 L1) (中文(简体) ).
LDX # 40 X (40美元 c0 v0 px0 L2) (韩语).
STX 4017 IOW(4017美元=40 L3美元)
TXS S( FF L1美元)
BIT 2002 IOR(2002年美元) IN(00美元) P(000110 px0 L3) < - 主机注入IN
BPL 8017 J(8017美元)
JSR 8279美元 PSH($FE) PSH($FD) J($8279) < 返回地址被推入电路
RTS PUL($FC) LO($7B) PUL($FD) J($827C)
STA(72美元),Y LO(08美元) HI(03美元) ST(00美元 a7=0 L2美元) < 指针字节已固定,然后是商店
STA 4014 IOW(4014美元=02 L0) DM(0:$FF). DM(255:$00) NT(L3) < OAM DMA
大多数指令都采用单一的令牌;真实的游戏代码平均为每指令1.1–1.3令牌。
Nesformer/spec.py 记录每个象征性的家庭。
- 参考CPU(nesformer/cpu.py)与巢鸟的黄金日志相匹配,用于3,348个指令
和570万个游戏代码指令上的py65模拟器。
- 准确的电路,在Quenputer的评审员中运行,并用其bf16精确度检查,预测每个参考符号:所有巢加283,610个位置的两款游戏的靴子序列。
- 库存变压器中的检查站(bf16,CPU)预测2,651个最窝点和Full Game的位置没有错配。
- 重量,自由运行,产生与参考CPU相同的痕迹:
- 闲置- loop 跳过不改变任何可见之处:帧和 I/O 写入,包含周期时间戳,匹配一个没有它的运行。
默认引擎计算出Qwen3.5从型号的前进传。 安全器,用麻木编译
并跳过所有零块。 它运行在一台CPU核心上的大约1,650个令牌/秒
这是建立在。 平型数字发动机约210台,股票变压器约10台。 一个真正的
NES每秒需要约70万令牌。
游戏将每个帧的大部分花在一个等待vblank的循环中。 当主机看到一个循环机体完全重复,没有RAM或I/O写入时,它将时钟推进到紧接下一个PPU事件之前,剩下的迭代仍然运行。 那个削减的代号是2.6x到4.2x 总体而言,Nesformer运行的150–290×比真正的控制台慢。
- 只有NROM弹匣(iNES映射器0). CPU一次只能处理32个程序ROM的KB. Bigger游戏搭载一个银行切换芯片(一个映射器,如MMC1或MMC3),用于交换CPU看到的一部分ROM. Nesformer的ROM读数发生在模型内部,从一个固定的32 KB表,所以它还不能跟随银行开关。 支持一个映射器意味着将整个ROM存储在州内,将银行登记册也保存在那里,并增加一个将每个CPU地址变为ROM地址的阶段。
- 只有官方代码一个非官方的操作码发出HALT。
- 代码必须从弹匣ROM运行。 一跃而入的RAM发布HALT。
- 没有IRQs(APU框架IRQ,DMC). BRK和NMI的工作。
- 在I/O登记册上读取-修改-写作指示不产生I/O书写,4020 - 7FFF读作0(没有PRG-RAM).
- PPU和APU不在重量级 PPU一次做一个扫描线,所以中扫描效应没有复制。 Sprite-0命中和Sprite溢出是大致的,没有声音。
pip 安装 pytest py65
python - m pytest - q 测试
测试需要几分钟。 他们将参考CPU对照巢湖的日志,电路对照参考文献,重量自由运行对照参考文献,闲闲跳跳。
pip 安装"torch>=2.6" "transfers>=5.22"
python - m Nesformer 运行 roms/thwaite.nes - 帧 3 - 引擎 hf
Nesformer/hfcheck.py 显示整个程序:加载检查站,解码一个贴纸符
将机器状态写入缓存的 DeltaNet 状态,然后一次输入一个符号。
- Quenputer及其芯片前导编译器:麻省理工学院
2026 迪维扬什·阿格拉瓦尔(LICENSE.chipformer). 该许可证还涵盖经修改的代码
以nesformer/core.py和nesformer/重量计算。 它的最后一段提到:
Quenputer的CHIP-8 ROM,这个寄存器不包括。
- 测试ROMs:由凯文·霍顿(Kevin Horton);Doug Fraker(麻省理工学院);Thwaite由Damian Yerrick(GPL-3.0,来源于pinobatch/thwaite-nes)制作的全游戏。
Nesformer是麻省理工学院许可下的发布(见LICENSE). ROMs的ROMs/ 保留它们自己的
许可证,列于ROS/Sources.md。
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。
An NES CPU compiled into the weights of a stock Qwen3.5 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. 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.
nesformer builds on Qwenputer by Divyansh Agrawal, which compiled a CHIP-8 and then a RISC-V CPU into Qwen3.5-4B. It reuses Qwenputer's compiler and its central trick: the model's Gated DeltaNet state is the machine's memory.
Every CPU token in these runs came from the model. For the bundled test games, every frame also matches a conventional 6502 emulator's output exactly (see How it was checked).
Unzip nesformer.zip and nesformer-weights.zip into the same folder. The weights go next to
the nesformer/ package, not inside it:
nesformer/ <- run commands from here
├── nesformer/ <- the Python package
├── chipformer/ <- Qwenputer's compiler
├── roms/ <- test ROMs (put your own here too)
├── examples/ <- sample GIFs and frame sheets
├── tests/
├── LICENSE
├── LICENSE.chipformer
├── README.md
├── pyproject.toml
└── weights/ <- from nesformer-weights.zip
├── config.json
├── generation_config.json
├── model.safetensors
├── nesformer.json
└── SHA256SUMS
The CLI looks for weights/ beside the nesformer/ package. To keep the weights elsewhere,
pass --weights /path/to/weights to run, check or trace, or give the path to info.
To confirm the weights unzipped correctly, run shasum -a 256 -c weights/SHA256SUMS from the
top folder.
You need Python 3.11 or newer.
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install numpy scipy numba pillowpython -m nesformer run roms/thwaite.nes --frames 600 --show \
--buttons "start@90-95+200-205,a@230-233+250-253+270-273+290-293+310-313+330-333+350-353+370-373+390-393+410-413+430-433+450-453+470-473+490-493+510-513+530-533+550-553+570-573+590-593"
--show draws each frame in your terminal as the model generates it. When the run ends, the CLI
saves an animated GIF named after the ROM (here thwaite.gif) in the current folder.
Expect it to be slow. The first run spends about 20 seconds compiling the engine with numba. After that, a frame takes 1–6 seconds, so 600 frames (10 seconds of NES time) take around 25 minutes.
Point run at any .nes file that uses the NROM board (iNES mapper 0), such as Super Mario
Bros., Donkey Kong or Excitebike. You supply the ROM; nesformer only ships the three freely
licensed test ROMs in roms/.
python -m nesformer run roms/smb.nes --frames 600 --show \
--buttons "start@60-65,right@200-600,a@250-265+300-315+360-375+420-435+480-495+540-555"
That script starts the game, then walks Mario right and jumps about once a second. The CLI loads the ROM into the model's state at startup, so the same weights run any NROM game without a rebuild. Games on other boards stop with a message: see Limitations.
--buttons takes a comma-separated list of entries. Each entry names one or more buttons joined
by +, then @, then one or more frame ranges joined by +:
start@60-65 press Start during frames 60 to 65
right@200-600 hold Right from frame 200 to frame 600
a@250-265+300-315 press A twice
right+b@200-600 hold Right and B together (run, in Super Mario Bros.)
The buttons are a, b, select, start, up, down, left and right. Frames count from
power-on at 60 per second, so frame 600 is 10 seconds in. Games ignore input on screens where
you can't move, so holding a button a little early does no harm. In Super Mario Bros., a longer
A press makes a higher jump.
Options for run:
run, check and trace all accept --weights, --engine, --buttons and --no-skip.
check --tokens N sets how far to compare (default 50,000). trace --tokens N sets how many
tokens to print (default 40). build --out DIR writes the checkpoint somewhere other than
weights/.
This split follows Qwenputer's CHIP-8 build, whose host injects timer ticks and draws the display from the trace. The host never sees the CPU's registers or RAM except through the trace.
The memory trick works like this. 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.
weights/ is an ordinary Hugging Face folder. It loads with
Qwen3_5ForCausalLM.from_pretrained and needs no custom code.
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. As in Qwenputer's RISC-V core, the PC 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. Excerpts from Full Game:
SEI P(000100 px0 L1)
LDX #$40 X($40 c0 v0 px0 L2)
STX $4017 IOW($4017=$40 L3)
TXS S($FF L1)
BIT $2002 IOR($2002) IN($00) P(000110 px0 L3) <- the host injects IN
BPL $8017 J($8017)
JSR $8279 PSH($FE) PSH($FD) J($8279) <- the return address is pushed in-circuit
RTS PUL($FC) LO($7B) PUL($FD) J($827C)
STA ($72),Y LO($08) HI($03) ST($00 a7=0 L2) <- pointer bytes latched, then the store
STA $4014 IOW($4014=$02 L0) DM(0:$FF) … DM(255:$00) NT(L3) <- OAM DMA
Most instructions take a single token; real game code averages 1.1–1.3 tokens per instruction.
nesformer/spec.py documents every token family.
- The reference CPU (nesformer/cpu.py) 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: 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.
- The weights, free-running, generate traces identical to the reference CPU's:
- The idle-loop skip changes nothing visible: frames and I/O writes, cycle timestamps included, match a run without it.
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/s 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.
Games spend most of each frame in a loop that waits for vblank. When the host sees a loop body repeat exactly, with no RAM or I/O writes, it advances the clock to just before the next PPU event, and the remaining iterations still run. That cuts the token count by 2.6× to 4.2×. Overall, nesformer runs 150–290× slower than the real console.
- NROM cartridges only (iNES mapper 0). The CPU can address only 32 KB of program ROM at a time. Bigger games carry a bank-switching chip (a mapper, such as MMC1 or MMC3) that swaps which part of the ROM the CPU sees. nesformer's ROM reads happen inside the model, from a fixed 32 KB table, so it can't follow a bank switch yet. Supporting a mapper means storing the whole ROM in the state, keeping the bank registers there too, and adding one stage that turns each CPU address into a ROM address.
- Official opcodes only. An unofficial opcode emits HALT.
- Code must run from the cartridge ROM. A jump into RAM emits HALT.
- No IRQs (APU frame IRQ, DMC). BRK and NMI work.
- Read-modify-write instructions on I/O registers don't produce an I/O write, and $4020–$7FFF reads as 0 (no PRG-RAM).
- The PPU and APU aren't in the weights. The PPU renders one scanline at a time, so mid-scanline effects aren't reproduced. Sprite-0 hit and sprite overflow are approximate, and there's no sound.
pip install pytest py65
python -m pytest -q tests
The tests take a few minutes. They check the reference CPU against nestest's log, the circuit against the reference, the weights free-running against the reference, and the idle-loop skip.
pip install "torch>=2.6" "transformers>=5.22"
python -m nesformer run roms/thwaite.nes --frames 3 --engine hf
nesformer/hfcheck.py shows the whole procedure: load the checkpoint, decode one padding token,
write the machine's state into the cache's DeltaNet states, then feed tokens one at a time.
- Qwenputer and its chipformer compiler: MIT,
© 2026 Divyansh Agrawal (LICENSE.chipformer). That license also covers the code adapted
from Qwenputer in nesformer/core.py and nesformer/weights.py. Its last paragraph refers to
Qwenputer's CHIP-8 ROMs, which this repository does not include.
- Test ROMs: nestest by Kevin Horton; Full Game by Doug Fraker (MIT); Thwaite by Damian Yerrick (GPL-3.0, source at pinobatch/thwaite-nes).
nesformer is released under the MIT License (see LICENSE). The ROMs in roms/ keep their own
licenses, listed in roms/SOURCES.md.
Auto-fetched and translated by AI Atlas. All rights belong to the original author.