Add call/ret operations
authorGreg Burri <greg.burri@gmail.com>
Sun, 11 Sep 2022 19:09:08 +0000 (21:09 +0200)
committerGreg Burri <greg.burri@gmail.com>
Sun, 11 Sep 2022 19:09:08 +0000 (21:09 +0200)
ch5-data-in-depth/src/chip8.rs

index 77c7363..b5a1844 100644 (file)
@@ -9,6 +9,9 @@ struct CPU {
     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 {
@@ -17,6 +20,8 @@ impl CPU {
             registers: [0; 16],
             position_in_memory: 0,
             memory: [0; 0x1000],
+            stack: [0; 16],
+            stack_pointer: 0,
         }
     }
 
@@ -36,15 +41,42 @@ impl CPU {
             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];
@@ -86,4 +118,31 @@ mod tests {
 
         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