Master the fundamental concepts of emulation through this focused micro-challenge.
Three hints are available for this task, revealed one at a time inside the code workspace so you can struggle productively before seeing them.
Every task includes starter code, theory, and hidden tests so you can implement and verify locally in the browser.
How it worksCHIP-8 is a virtual machine from the 1970s COSMAC VIP, now the standard first emulator project. 35 opcodes, 4 KiB RAM, and a 64x32 display teach fetch-decode-execute without console hardware quirks.
Core state:
Opcodes are 16-bit big-endian words. \`DXYN\` draws an \`N\`-byte sprite at (\`VX\`, \`VY\`).
For example, opcode \`0xA000\` sets index register \`I\` to \`0x000\`; \`0x600A\` loads 10 into \`V0\`.
\`\`\`c uint16_t opcode = (mem[pc] << 8) | mem[pc + 1]; pc += 2; \`\`\`
Timers must tick at roughly 60 Hz regardless of how fast you emulate CPU cycles. Games read delay timer for input debouncing; sound timer drives a buzzer flag many front ends beep on.
This exercise asks you to implement the CHIP-8 CPU core with fetch-decode-execute. You will handle memory, registers, stack calls, timers, and XOR drawing for a subset of opcodes.
You will use the same mental model here when reading production interpreter source later in the track. Sketch one concrete input on paper, predict the outcome, then confirm with code. That discipline catches logic errors early and makes debugging far faster when you extend the implementation in follow-on tasks.
Implement a CHIP-8 interpreter: 4 KiB of memory, 16 registers V0-VF, I, a 16-level stack, delay and sound timers, and a 64×32 monochrome display. Load the program at 0x200 and the built-in font at 0x050. Then run the fetch-decode-execute loop: fetch a big-endian 16-bit opcode, advance PC by 2, and execute it. Print a trace and the final machine state.
Whitespace-separated tokens. # comments out the rest of a line.
6A05) is the next program word.trace off disables the trace.cycles N sets the instruction limit (1..100000, default 1000).seed N seeds RND.keys K... marks hex keys as held for the whole run (to the end of the line).run runs the program loaded so far, then starts over with a fresh machine and default settings. The end of input runs any pending program.The Cowgod mnemonics are CLS RET JP CALL SE SNE LD ADD OR AND XOR SUB SHR SUBN SHL RND DRW SKP SKNP. They cover:
00E0, 00EE, 1nnn, 2nnn, 3xkk, 4xkk, 5xy0, 6xkk, 7xkk;8xy0-8xy7 and 8xyE;9xy0, Annn, Bnnn, Cxkk, Dxyn, Ex9E, ExA1;Fx07, Fx0A, Fx15, Fx18, Fx1E, Fx29, Fx33, Fx55, Fx65.Use these modern semantics:
7xkk does not touch VF.8xy4 sets VF to the carry, 8xy5 sets VF = (Vx ≥ Vy), and 8xy7 sets VF = (Vy ≥ Vx). VF is written after the result.8xy6/8xyE shift Vx in place (Vy is ignored), and VF gets the bit shifted out.Bnnn jumps to nnn + V0. Fx55/Fx65 leave I unchanged. Fx29 points I at the 5-byte glyph at 0x50 + 5·digit.Dxyn XORs an n-row sprite from I at (Vx mod 64, Vy mod 32). Pixels past the right or bottom edge are clipped, and VF = 1 if any lit pixel was erased.Cxkk: state = (state × 1103515245 + 12345) mod 2^31 (seed 1 by default). The random byte is (state >> 16) & 0xFF, then ANDed with kk.Ex9E/ExA1 test the key numbered by Vx. Fx0A stores the lowest held key.The font is the standard 0-F set (F0 90 90 90 F0 for 0, 20 60 20 20 70 for 1, … F0 80 F0 80 80 for F).
The run stops with a reason:
jump to self at 0xPPP (a 1nnn to its own address, the usual CHIP-8 "halt");cycle limit N reached;stack overflow at 0xPPP (a 17th CALL);stack underflow at 0xPPP;unknown opcode XXXX at 0xPPP;waiting for a key at 0xPPP (Fx0A with no key held);PC out of memory at 0xPPP.The halting instruction is not counted.
cLoading…
PPP OPCD MNEMONIC for each fetched instruction, including the one that halts.
0x%03X and bytes as 0x%02X.LD V0, 0x05, ADD V2, V3, SHR V4, JP V0, 0x21E, DRW V0, V1, 5, LD F, V2, LD B, V1, LD [I], V2, LD V2, [I], LD V0, K, LD DT, VE and LD VB, DT.???.#/. rows, or display: blank.bad program word: X (the word is skipped), no program (a run with nothing loaded, or empty input), and cycles: 1..100000 (the limit stays 1000).Input:
cLoading…
Output:
cLoading…
Hidden tests cover a draw-collision flag with timers counting down, carry/borrow/shift flags with RND and Bnnn, clipping and coordinate wrap in DRW, stack overflow and underflow, unknown opcodes, key input, the cycle limit, and input errors.