An opcode is a number that tells a CPU emulator which operation to perform. In CHIP-8, an opcode can also identify the registers or other values that the operation uses. The same bytes in memory become instructions when the emulator reads and executes them. section 5.7.1 section 5.8
Decode one addition instruction
Split 0x8014 into four hexadecimal digits: 8 selects a two-register operation, 0 selects register 0, 1 selects register 1, and 4 selects addition. With 5 in register 0 and 10 in register 1, executing it adds 10 to 5. Masks and shifts extract the digits; a match selects the addition method. section 5.7.1
Listing 5.20 Reading the opcode
impl CPU {
fn read_opcode(&self) -> u16 { #1
self.current_operation #1
} #1
fn run(&mut self) {
// loop { #2
let opcode = self.read_opcode();
let c = ((opcode & 0xF000) >> 12) as u8; #3
let x = ((opcode & 0x0F00) >> 8) as u8; #3
let y = ((opcode & 0x00F0) >> 4) as u8; #3
let d = ((opcode & 0x000F) >> 0) as u8; #3
match (c, x, y, d) {
(0x8, _, _, 0x4) => self.add_xy(x, y), #4
_ => todo!("opcode {:04x}", opcode), #5
}
// } #6
}
fn add_xy(&mut self, x: u8, y: u8) {
self.registers[x as usize] += self.registers[y as usize];
}
}
Follow the masks, shifts, and match arm that turn a 16-bit opcode into a register operation. section 5.7.1
To run more than one instruction, store opcodes in RAM. The emulator reads two bytes at the current memory position, combines them into a 16-bit opcode, advances the position by two bytes, and executes it. An opcode of 0x0000 stops this loop. section 5.7.3
Listing 5.24 Reading an opcode from memory
8 fn read_opcode(&self) -> u16 {
9 let p = self.position_in_memory;
10 let op_byte1 = self.memory[p] as u16;
11 let op_byte2 = self.memory[p + 1] as u16;
12
13 op_byte1 << 8 | op_byte2 #1
14 }
See how two successive memory bytes become one opcode. section 5.7.3
Opcodes can change where execution goes next. CALL (0x2nnn) transfers execution to address nnn while saving the return position on a stack; RETURN (0x00EE) restores that position. A function containing two addition opcodes followed by RETURN can be written as three 16-bit values or six bytes loaded into RAM. section 5.7.4
Comparisons and changes to the current memory position provide control flow without a CPU-level while or for instruction. Opcodes are not limited to CPUs: characters from the Base 16 alphabet also serve as opcodes in a LOGO-like language for drawing avatars. section 5.7.5 section 10.4
