一个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量.zip 进入同一文件夹。 重量在旁边
阴源/ 包件,不在其中:
nesformer/ <- 从这里运行命令
QQ Nesformer/ <- Python 软件包
QQ 芯片格式/ <- Quenputer' 编译器
& > roms/ < -- 测试ROMs( 也放在这里)
示例/ <-样本GIF和框架表
* 测试/
自由主义
翻译:
读取
{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}我很高兴见到你
权重/ <- 从 nesformer- weights.zip (英语).
├── config.json
QQ 生成 config.json
* 型号。
(原始内容存档于2019-10-21) (英语). Nesformer.json.
SHA256SUMS (英语).
CLI 在 Nesformer/ 包件旁边寻找重量。 把重量放在别处
通过 -- -- 权重/路径/到/权重以运行、检查或跟踪,或给以信息路径。
为了确认未正确分解的重量,从中运行shasum -a 256-c重量/SHA256SUMS
顶部文件夹。
你需要Python 3.11或更新。
python3 - m venv.venv (英语).
来源.venv/bin/激活 #Windows:.venv\脚本并激活
pip 安装 numpy scipy 麻木枕头平通 - m Nesformer 运行 roms/thwaite.nes -- frames 600 -- sshow \
-按钮"开始@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+570-573+590-593".
当模型生成时,显示您的终端中的每一帧。 当运行结束时,CLI
保存当前文件夹中以ROM(这里为thwaite.gif)命名的动画 GIF。
希望它慢一点。 第一次运行花费了大约20秒用麻木来编译引擎。 之后一帧需要一至六秒,因此600帧(NES时间为10秒)需要25分钟左右。
点运行于任意使用 NROM board (iNES mapper 0) 的.nes 文件,如 Super Mario
兄弟们驴子金刚还是跑车您提供ROM; Nesformer 只能自由运送三艘
特许测试ROM 以 roms/.
python - m Nesformer 运行 roms/ smb.nes -- frames 600 -- sshow \
- 按钮"启动@60-65,右转@200-600,a@250-265+300-315+360-375+420-435+480-495+540-555"
这个剧本开始游戏,然后走马里奥右转,每秒跳出一次左右。 CLI在启动时将ROM加载到模型的状态中,因此相同的权重在没有重建的情况下运行任意一款NROM游戏。 其他棋盘上的游戏以信息停止:参见"限制".
--按钮需要一个逗号分隔的条目列表。 每个条目名称加入一个或多个按钮
由 +,然后@,然后由 + 加入的一个或多个帧范围:
开始@ 60- 65 个按在框架60至65期间开始
右侧@ 200-600 从框架 200 到框架 600
a@250-265+300-315 新闻一两次
right+b@200-600 hold Right and B together (运行,在"超级马里奥兄弟"中).
按钮是 a, b,选择,启动,上,下,左和右。 框架计数从
电源在每秒60秒,所以600帧是10秒。 游戏忽略屏幕上的输入
你动不了的所以早点按下按钮不会有事在"超级马里奥兄弟"中,更长的时间
媒体会跳得更高
运行选项:
运行、检查和跟踪所有接收 -- -- 重量、 -- -- 引擎、 -- -- 按钮和 -- -- 不滑动。
检查 --tokens N 设置比较距离( 默认为 50,000)。 跟踪 -- tokens 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登记册上的读取-修改-写作指示不产生一/O写作,4020-7FFF读作0(没有PRG-RAM).
- PPU和APU没有在重量。 PPU一次制造一条扫描线,所以中扫描效应没有被复制。 Sprite -0击出和sprite溢出是相近的,没有声音。
管道安装 pytest py65
python - m pytest - q 测试
测试需要几分钟。 他们检查了参考CPU与巢斯的日志,回路与参考文献的对比,权重自由运行与参考文献的对比,并检查了闲闲-loop跳过。
pip 安装"torch->= 2.6" "transfers->= 5.22"
python - m Nesformer 运行 roms/thwaite.nes -- -- 框架 3 -- -- 引擎 hf
Nesformer/hfcheck.py 显示整个程序:装入检查站,解码出一个贴花符
将机器状态写入缓存的 DeltaNet 状态,然后一次输入一个符号。
- Quenputer及其芯片前导编译器:麻省理工学院
2026 Divyansh Agrawal (LICENSE.chipformer) (英语). 该许可证也涵盖经过修改的代码
来自nesformer/core.py和nesformer/重量.py的文普特人。 它的最后一段提到:
Quenputer的CHIP-8 ROM,这个寄存器不包括。
- 测试ROMs:由凯文·霍顿(Kevin Horton;由道格·弗雷克(麻省理工学院)出品的全能游戏;由达米安·耶里克出品的Thwaite(GPL-3.0,来源于pinobatch/thwaite-nes).
根据麻省理工学院许可(见LICENSE),nesformer为发布。 ROM 装入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 Gem Box. All rights belong to the original author.