registers: [u8; 16], // 4 bits can address a 16 positions register. The last position (0xF) is used to indicate an overflow.
position_in_memory: usize, // Programm counter.
memory: [u8; 0x1000], // 4 KB of memory.
+
+ stack: [u16; 16],
+ stack_pointer: usize,
}
impl CPU {
registers: [0; 16],
position_in_memory: 0,
memory: [0; 0x1000],
+ stack: [0; 16],
+ stack_pointer: 0,
}
}
let x = ((opcode & 0x0F00) >> 8) as u8;
let y = ((opcode & 0x00F0) >> 4) as u8;
let d = ((opcode & 0x000F) >> 0) as u8;
+ let nnn = opcode & 0x0FFF;
match (c, x, y, d) {
(0, 0, 0, 0) => { return; }
+ (0, 0, 0xE, 0xE) => self.ret(),
+ (0x2, _, _, _) => self.call(nnn),
(0x8, _, _, 0x4) => self.add_xy(x, y),
_ => todo!("opcode: {:04x}", opcode),
}
}
}
+ fn call(&mut self, addr: u16) {
+ let sp = self.stack_pointer;
+ let stack = &mut self.stack;
+
+ if sp > stack.len() {
+ panic!("Stack overflow!");
+ }
+
+ stack[sp] = self.position_in_memory as u16;
+ self.stack_pointer += 1;
+ self.position_in_memory = addr as usize;
+ }
+
+ fn ret(&mut self) {
+ if self.stack_pointer == 0 {
+ panic!("Stack underflow!");
+ }
+
+ self.stack_pointer -= 1;
+ let addr = self.stack[self.stack_pointer];
+ self.position_in_memory = addr as usize;
+
+ }
+
fn add_xy(&mut self, x: u8, y: u8) {
let arg1 = self.registers[x as usize];
let arg2 = self.registers[y as usize];
assert_eq!(cpu.registers[0], 35);
}
+
+ // 5 + (10 * 2) + (10 * 2) = 45. (two calls).
+ #[test]
+ fn call_and_ret() {
+ let mut cpu = CPU::new();
+
+ cpu.registers[0] = 5;
+ cpu.registers[1] = 10;
+
+ let code: [u8; 12] = [
+ 0x21, 0x00, // Call function at 0x100.
+ 0x21, 0x00, // Call function at 0x100.
+ 0x00, 0x00, // Halt, end of program.
+
+ 0x80, 0x14, // Add register 1 to register 0.
+ 0x80, 0x14, // Add register 1 to register 0.
+ 0x00, 0xEE, // Return from the function.
+ ];
+
+ cpu.memory[0x000..0x006].copy_from_slice(&code[0..6]);
+ cpu.memory[0x100..0x106].copy_from_slice(&code[6..12]);
+
+ cpu.run();
+
+ assert_eq!(cpu.registers[0], 45);
+ }
+
}
\ No newline at end of file