CHIP-8 模拟器(Emulator)

Rust 编写的 CHIP-8 解释器编译成 WebAssembly,逐条执行 1977 年的虚拟机指令

    汇编源码(可以修改后运行)

    电脑键盘 1234 QWER ASDF ZXCV 对应 CHIP-8 的 16 个按键(Pong 用 1 上、Q 下)  |  手机上按右侧的键盘

    📖 原理说明

    CHIP-8 是 1977 年为 COSMAC VIP 等 8 位电脑设计的一种虚拟机:4 KB 内存、16 个 8 位寄存器、64×32 的黑白屏幕、16 键键盘,只有 35 条指令。当年的游戏就是这种虚拟机的字节码。这个页面用 Rust 实现了完整的解释器,每秒按设定的速度取出一条条两字节的指令来执行。页面还带着一个汇编器:内置的三个程序都以汇编源码形式保存,可以直接修改、重新汇编并运行。

    🧮算法原理

    取指 → 译码 → 执行每条指令 2 字节,按最高 4 位分成 16 类,再由其余位取出寄存器号、立即数或地址
    像素 ^= 精灵位,VF = 是否擦掉了像素绘图全靠异或:同一位置画两次就擦掉,同时得到碰撞检测
    指令循环
    程序计数器 PC 指向下一条指令,读出两个字节后 PC 加 2,再根据操作码执行:加载、算术、跳转、调用子程序(用 16 层的栈保存返回地址)、按键判断、画精灵等。模拟器并不「理解」游戏,只是忠实地逐条执行。
    60Hz 定时器
    延时定时器 DT 和声音定时器 ST 每秒递减 60 次,与指令速度无关。游戏通过读 DT 控制节奏;ST 大于 0 时发出蜂鸣声(页面用 Web Audio 的方波振荡器)。浏览器每一帧可能不是 1/60 秒(比如 120Hz 屏幕),页面按真实时间累积,每满 1/60 秒才执行一帧的指令并推进定时器。
    精灵与字体
    精灵是一串字节,每字节是一行 8 个像素。内存里内置了 0~F 十六个数字的 4×5 字体,LD F, Vx 指向对应字形,LD B, Vx 把数值拆成百位、十位、个位,Pong 的比分就是这样画出来的。
    兼容性差异(quirks)
    CHIP-8 从来没有正式规范,不同年代的解释器对几条指令的理解不同:移位用 Vx 还是 Vy、批量存取寄存器后 I 是否前进、逻辑运算是否清 VF、精灵到边缘是裁剪还是绕回、每帧是否只允许画一次。页面提供原版 COSMAC VIP 和现代(CHIP-48 风格)两组设置;不支持 SUPER-CHIP 的扩展指令(高分辨率、滚屏)。
    汇编与反汇编
    内置程序用常见的 Cowgod 助记符书写(LD V0, 0x05、DRW V1, V2, 6),汇编器扫描两遍:第一遍记下每个标签的地址,第二遍把每行翻译成操作码。调试面板里的反汇编是反方向的翻译;二进制 ROM 里代码和数据混在一起,所以数据区也会被当成指令显示。

    🔄Rust 与 JavaScript 的分工

    1. JS选择程序时,JS 调用 chip8_assemble(源码) 得到 ROM 字节,再用 chip8_load 装入内存;本地 ROM 文件则直接装入。
    2. Rust每个 60Hz 帧 JS 调用一次 chip8_frame(每帧指令数):Rust 连续执行这么多条指令(遇到等待按键、「每帧只画一次」或出错会提前停下),然后推进两个定时器。
    3. JSJS 读取 chip8_display() 的 2048 个像素画到 64×32 的 Canvas 上(CSS 放大并保持像素锐利,可选余辉效果减轻闪烁),按 chip8_sound() 开关蜂鸣,并用 chip8_regs() 和 chip8_disasm() 刷新调试面板。按键通过 chip8_key 传入。

    ⚡性能要点

    • 原版 COSMAC VIP 每秒大约只能执行几百条 CHIP-8 指令,页面默认每帧 12 条(每秒 720 条),大多数游戏按这个速度设计。实测这个 WASM 解释器全速运行时每秒约 3 亿条指令,模拟本身几乎不占时间,开销主要在每帧画屏和刷新调试面板。
    • 闪烁是 CHIP-8 的特点:程序先用异或擦掉精灵、移动后再画上去,屏幕刷新恰好落在两者之间时就会看到半透明的闪烁。「余辉」选项模拟老式显示器的荧光余晖来减轻它。
    • 汇编整个 Pong 程序约 0.05ms。验证:解释器通过了 Timendus 的 CHIP-8 测试套件中的指令测试(corax+)、标志位测试(flags)和 COSMAC VIP 兼容性测试(quirks)。

    源码crates/games/chip8/src/vm.rscrates/games/chip8/src/asm.rscrates/games/chip8/src/programs/pong.asmwww/chip8/index.js