The Art of CHIP-8
The Art of CHIP-8 / CHIP-8 的艺术
CHIP-8 is a programming language originally developed for the 1977 COSMAC VIP kit computer. CHIP-8 programs are composed of a series of two-byte instructions resembling machine-code for a simple virtual instruction-set architecture, so CHIP-8 interpreters are often also referred to as “emulators”. CHIP-8 是一种最初为 1977 年 COSMAC VIP 套件计算机开发的编程语言。CHIP-8 程序由一系列双字节指令组成,类似于简单虚拟指令集架构的机器码,因此 CHIP-8 解释器通常也被称为“模拟器”。
Indeed, writing a CHIP-8 interpreter is an excellent way to learn the principles underlying emulators for antique computers and game consoles, and as a result there are a dizzying array of thousands of CHIP-8 runtimes available for almost every conceivable platform. For historical platforms to live, rather than simply be preserved, we must write new software for them. 事实上,编写一个 CHIP-8 解释器是学习古董计算机和游戏机模拟器底层原理的绝佳途径。正因如此,几乎在所有可想到的平台上,都有成千上万种令人眼花缭乱的 CHIP-8 运行时可供使用。为了让历史平台能够“活着”而不仅仅是被“保存”,我们必须为它们编写新的软件。
The profusion of CHIP-8 implementations comes in turn with a great deal of confusion, as a half-century-long game of telephone has produced a wide variety of diverging behaviors in interpreters. Over the course of developing Octo, my high-level CHIP-8 assembler, I helped popularize and standardize a variety of “quirks flags” which capture common divergences between extant CHIP-8 flavors, and investigated many dark, unspecified corners of influential interpreters. CHIP-8 实现的泛滥也带来了极大的困惑,因为长达半个世纪的“传话游戏”导致解释器在行为上产生了各种各样的分歧。在开发我的高级 CHIP-8 汇编器 Octo 的过程中,我帮助推广并标准化了各种“特性标志(quirks flags)”,这些标志捕捉了现存 CHIP-8 变体之间的常见差异,并深入研究了许多有影响力的解释器中那些晦涩且未明确定义的角落。
There are now mature test suites available for CHIP-8 interpreters and their variants, so there’s no excuse for modern interpreters to get the details wrong. Still, the reality of CHIP-8 in the wild is fragmented: many interpreters for obscure platforms are written by beginners unaware of any broader hobbyist community and abandoned as soon as they (appear to) correctly run PONG.CH8. 目前已经有针对 CHIP-8 解释器及其变体的成熟测试套件,因此现代解释器没有理由在细节上出错。尽管如此,CHIP-8 在现实中的应用依然是碎片化的:许多针对冷门平台的解释器是由不了解更广泛爱好者社区的初学者编写的,一旦它们(看起来)能正确运行 PONG.CH8,这些项目往往就会被弃置。
In this article I will examine CHIP-8 as an instruction set and its practical implications for writing new programs, distilling a number of scattered tutorials, examples, and FAQs I’ve written in the past. I will specifically point out approaches which are portable across all but the buggiest and least complete existing CHIP-8 interpreters. 在本文中,我将把 CHIP-8 作为一种指令集进行剖析,探讨其在编写新程序时的实际意义,并提炼我过去编写的许多零散教程、示例和常见问题解答。我将特别指出那些在除最糟糕、最不完整的现有 CHIP-8 解释器之外的所有平台上都具有可移植性的方法。
Example code will use Octo’s notation; this document is not intended as a complete reference manual for Octo assembly language, but I will endeavor to explain new ideas as we encounter them. 示例代码将使用 Octo 的符号;本文档并非 Octo 汇编语言的完整参考手册,但我会尽力在遇到新概念时对其进行解释。
System Overview / 系统概述
CHIP-8 operates in a 12-bit address space. The original CHIP-8 interpreter resided in the first 512 bytes of this space, with programs starting at address 0x200. It also reserved some of the upper region of the address space for a stack, a framebuffer, and several scratchpads. As a result, we are left with a maximum of 3232 bytes for our code and data. CHIP-8 在 12 位地址空间内运行。最初的 CHIP-8 解释器驻留在该空间的前 512 字节中,程序从地址 0x200 开始。它还保留了地址空间的上部区域用于堆栈、帧缓冲区和几个暂存区。因此,我们最多剩下 3232 字节用于存放代码和数据。
Modern CHIP-8 interpreters are often more generous, leaving up to 3584 bytes for user programs, and they will often use the low 512 bytes of memory to store their hex font(s) or nothing at all, leaving it available for programs to manipulate. Your code and data should fit within 3232 bytes for maximum portability. 现代 CHIP-8 解释器通常更为慷慨,为用户程序留出多达 3584 字节的空间,并且它们通常使用低 512 字节的内存来存储十六进制字体,或者干脆留空,供程序自由操作。为了获得最大的可移植性,你的代码和数据应控制在 3232 字节以内。
We have a file of 16 general-purpose 8-bit registers named v0-vf, giving the platform a pleasantly RISC-ey feel. The 12-bit “index register” i is used for all operations which reference or manipulate memory. There is an internal stack for threading subroutine return addresses, but it is opaque: the instruction set does not allow programs to freely push or pop temporary values or inspect the contents of the stack. 我们拥有 16 个名为 v0-vf 的通用 8 位寄存器,这赋予了该平台一种令人愉悦的 RISC 风格。12 位的“索引寄存器” i 用于所有引用或操作内存的指令。内部有一个用于处理子程序返回地址的堆栈,但它是透明的(不可直接访问):指令集不允许程序自由地压入或弹出临时值,也不允许检查堆栈的内容。
Programs take input from a hexadecimal keypad. For output, we have a 64x32 pixel 1-bit bitmapped display and a simple piezo buzzer for making noise. We are also afforded a non-interrupting delay timer and a random number generator. 程序通过十六进制键盘获取输入。在输出方面,我们拥有一个 64x32 像素的 1 位位图显示器和一个用于发声的简单压电蜂鸣器。我们还配备了一个非中断式的延迟计时器和一个随机数生成器。
Arithmetic Instructions / 算术指令
Two arithmetic instructions take immediate arguments: 两条算术指令接受立即数参数:
| Machine Code | Octo Syntax |
|---|---|
| 6XNN | vx := NN |
| 7XNN | vx += NN |
The remainder manipulate two registers, with vx storing the result. 其余指令操作两个寄存器,并将结果存储在 vx 中。
| Machine Code | Octo Syntax |
|---|---|
| 8XY0 | vx := vy |
| 8XY1 | vx |= vy |
| 8XY2 | vx &= vy |
| 8XY3 | vx ^= vy |
| 8XY4 | vx += vy |
| 8XY5 | vx -= vy |
| 8XY6 | vx >>= vy |
| 8XY7 | vx =- vy |
| 8XYE | vx <<= vy |
Octo uses syntax similar to C-family languages for these operations, with a Pascal-style := assignment operator for symmetry. The ^=, =- , <<= and >>= instructions were present in the original CHIP-8 interpreter, but not documented; they arose as a natural consequence of the RCA-1802 instruction set. Octo 在这些操作中使用了类似于 C 语言家族的语法,并为了对称性采用了 Pascal 风格的 := 赋值运算符。^=、=-、<<= 和 >>= 指令在最初的 CHIP-8 解释器中就已存在,但未被记录;它们是 RCA-1802 指令集的自然产物。