CHIP-8's instruction set is pretty straightforward. I'm going to categorize them by their first nibble, since that seems to be as good a way as any. If you look at the Wikipedia reference, you'll see 3 placeholder letters being used.
Register placeholders X and Y are used in place of register numbers - when you see X or Y in number, for instance 8XY0, X and Y are replaced with a one of 16 register numbers 0-F.
Immediate Values N is used wherever the number is interpreted as immediate, that is used as is. 2 Ns (NN) is for a byte of data, and 3 Ns (NNN) signifies an address.
There are 3 ops starting with zero. One clears the screen, and one is return from subroutine. The 3rd will execute a subroutine in an RCA 1802 - since we aren't emulating the 1802 processor we won't do that instruction. I didn't see a CHIP-8 programs use that.
Here's the Assembly syntax I'll use:
Op Nemonic 00E0 CLS 00EE RTS
The 1xxx opcodes are absolute jumps. The opcode 12A8 equates to JUMP $2A8
2NNN will jump to a subroutine by:
pushing the address if the next instruction on a stack
setting the PC to the address contained in the instruction
22A8 would yeild CALL $2A8
Each subroutine has to end with a 00EE
instruction (RTS). CHIP-8 assumes that the programs can call up to 16 nested subroutines - it is not defined what happens if you call more than 16.
The BNNN opcode performs an indexed jump. B290 would jump to the absolute address $290 plus the value contined in V0. So if V0 contained #$08, the program would jump to $298. B290 will show in my disassembly as JUMP $290(V0)
.
These instructions will compare 2 values and skip the next instruction if the test passes. I'm going to assign assembly nemonic "SKIP" to the instruction, so exmples are:
OP NEMONIC 3A00 SKIP.EQ VA, #$00 4800 SKIP.NE V8, #$00 5A70 SKIP.EQ VA, V7 93B0 SKIP.NE V3, VB
These let programs do conditional execution. A sequence like this would clear the screen unless V5 contained zero:
PC INSTRUCTION ---- ----------- 021A SKIP.NE V5, #$00 021C JUMP $220 021E CLS 0220 MOV V5, #$00
There are 9 instructions in the 8xxx group that use 2 registers. VX will be modified by VY. You can see in the Wikipedia table that some of the operations will modify VF based on the result of the math. This is CHIP-8's version of condition codes.
For the assembly syntax, I'm going to use a dot '.' after the instruction to indicate that the instruction will modify VF. Here are the sample assembly:
OPCODE NEMONIC 83A0 MOV V3, VA 83A1 OR V3, VA 83A2 AND V3, VA 83A3 XOR V3, VA 83B4 ADD. V3, VB 83B5 SUB. V3, VB 83x6 SHR. V3 83B7 SUBB. V3, VB 83xE SHL. V3
The SUBB
instruction is "backward subtract", where VX=VY-VX.
The rest of the instructions are hard to categorize so we'll cover them during the emulator implementation. They mostly have to do with drawing sprites, timers, and keyboard interaction. I'll just present my assembler syntax for them:
Op Syntax DXYN SPRITE VX, VY, #$N EX9E SKIP.KEY VX EXA1 SKIP.NOKEY VX FX07 MOV VX, DELAY FX0A WAITKEY VX FX15 MOV DELAY, VX FX18 MOV SOUND, VX FX1E ADD I, VX FX29 SPRITECHAR VX FX33 MOVBCD VX FX55 MOVM (I), V0-VX FX65 MOVM V0-VX, (I)
This should be plenty of information to finish the disassembler and get us started on the emulator.
View my CHIP-8 Disassembler source
← Prev: chip-8-disassembler Next: chip-8-emulator →Post questions or comments on Twitter @realemulator101, or if you find issues in the code, file them on the github repository.