chip8c
chip8c
chip8c is a dependency-free CommonJS compiler for a small CHIP-8 language.
It turns .ch8s source files into ROMs that can be loaded by a CHIP-8 emulator.
This first release is published as a beta. The compiler pipeline and encoded output are tested, but the language and backend are intentionally small.
Install
This release is a prerelease, so install the beta tag explicitly:
npm install --global chip8c@beta
Check the installation:
chip8c --version
chip8c --help
Compile a program
Compile a source file by providing an output path:
chip8c program.ch8s --output program.ch8
-o is the short form of --output:
chip8c program.ch8s -o program.ch8
The output is a raw CHIP-8 ROM. Load it at address 0x200 in your emulator.
Try the included example from a source checkout:
chip8c test.ch8s -o test.ch8
Draw the included sprite example:
chip8c examples/draw.ch8s -o draw.ch8
Load draw.ch8 at 0x200 in a CHIP-8 emulator. The language provides the
clear() and draw(x, y, "hex bytes") builtins. Sprite strings contain one
to fifteen two-digit hexadecimal bytes, stored in ROM data and loaded into I
before the generated DXYN instruction runs.
Language guide
Declarations and values
Use let for mutable variables and const for values that cannot be assigned
again. Values are unsigned 8-bit integers or booleans.
let counter = 3;
const limit = 10;
let enabled = true;
counter = counter + 1;
enabled = false;
Numeric literals may be decimal, hexadecimal, or binary:
let decimal = 42;
let hexadecimal = 0x2a;
let binary = 0b101010;
Arithmetic and comparisons
The operators +, -, *, /, and % are supported. Comparisons produce
0 or 1 and support ==, !=, <, <=, >, and >=.
let sum = 4 + 5;
let remainder = 10 % 3;
if (sum >= 9) {
V0 = 1;
} else {
V0 = 0;
}
Unary - and !, plus logical && and ||, are also supported.
Control flow
Use if/else and while statements. Braces are recommended for clarity.
let value = 0;
while (value < 10) {
value += 1;
}
Functions
Functions can accept up to three parameters and return a value with return.
Arguments are passed using the compiler's CHIP-8 calling convention.
function addOne(value) {
return value + 1;
}
V0 = addOne(7);
V0 is the return register. Function arguments use V1, V2, and V3.
CHIP-8 registers and memory
Registers V0 through VF can be read and assigned. Global and local source
variables are allocated in CHIP-8 memory automatically.
V0 = 10;
V1 = V0 + 2;
JavaScript API
The package also exposes the compiler from its CommonJS main entrypoint:
const { compileSource, compileFile } = require("chip8c");
const result = compileSource("V0 = 42;", "example.ch8s");
console.log(result.bytes);
compileFile("example.ch8s", "example.ch8");
The compiler pipeline is:
.ch8s -> lexer -> parser/AST -> semantic analysis -> IR -> CodeGenerator -> CHIP-8 encoder -> ROM
Limitations
- CHIP-8 has no floating-point type. Numeric constants must be integer bytes
from
0through255. - Arithmetic is 8-bit and wraps modulo 256 where applicable.
- Functions support at most three parameters. Arguments use
V1throughV3and return values useV0. - Values outside one byte and addresses outside
0x000through0xFFFare compile errors. - Arrays, member expressions, timers, keyboard input, and display instructions
other than
clear()anddraw()are not yet lowered by the backend. - Division and modulo by zero produce zero rather than trapping.
Development
Run the test suite with:
npm test
The tests cover exact encoded instruction bytes, comparison edge cases, and source-to-ROM compilation.
License
MIT. See LICENSE.