Chip8 emulator – part 1
October 7, 2015
Let's write a simple emulator in JavaScript. Chip8 CPU
has a very minimal architecture which makes it a great emulation project for beginners.
Here are basic hardware stats:
- 4096 bytes of memory
- 35 opcodes
- 16 registers: V0, V1, ..., VF
- Address register, I is 16 bits wide
$ hexdump Maze.ch8
0000000 60 00 61 00 a2 22 c2 01 32 01 a2 1e d0 14 70 04
0000010 30 40 12 04 60 00 71 04 31 20 12 04 12 1c 80 40
0000020 20 10 20 40 80 10
0000026
A bit of formatting.
var rom = [
0x6000, 0x6100, 0xa222, 0xc201, 0x3201, 0xa21e, 0xd014, 0x7004,
0x3040, 0x1204, 0x6000, 0x7104, 0x3120, 0x1204, 0x121c, 0x8040,
0x2010, 0x2040, 0x8010, 0xa21e, 0xc201, 0x3201, 0xa21a, 0xd014,
0x7004, 0x3040, 0x1200, 0x6000, 0x7104, 0x3120, 0x1200, 0x1218,
0x8040, 0x2010, 0x2040, 0x8010
];
Looking up first opcode in the reference:
6XNN - 'Sets VX to NN' which means first opcode in the rom, 0x6000, sets register V0 to 0.
Next opcode 0x6100 does a similar thing, setting register V1 to 0.
This is already enough to setup a quick disassembler v0.01
// roms are loaded at 0x200 in the 4096 bytes of memory space
pc = 0x200;
// registers
v = new Uint8Array(16);
rom.forEach(
function (b) {
log(pc.toString(16) + ":" + b.toString(16) + " " + run(b));
// increment program-counter to point to the next opcode
pc += 0x08;
});
function run(opcode) {
// 6XNN Sets VX to NN.
if (opcode >> 12 == 0x6) {
v[(opcode & 0xf00) >> 8)] = (opcode & 0xff);
return "Sets V" + ((opcode & 0xf00) >> 8).toString(16) +
" to " + (opcode & 0xff).toString(16);
}
};
And disassemble 4 opcodes from the rom.
In the next part, we'll add the rest of opcodes needed that disassemble entire rom.