Commit Diff


commit - 5210a85ad5036c3aff9bbcc757763582d5bea59a
commit + ebf97be247eb7e27b2df1adf663ae48c9accd391
blob - 51185056fdf6742a81bcb84add7490c87fd0d846
blob + a354d0166c46432769fc74e545d7a6bde293d80b
--- src/chip8.rs
+++ src/chip8.rs
@@ -2,6 +2,8 @@ use std::path::Path;
 
 use crate::drivers::VideoDriver;
 
+const PROGRAM_START: u16 = 0x200;
+
 #[derive(Debug)]
 /// Represents a Chip8 machine
 pub struct Chip8 {
@@ -29,23 +31,13 @@ pub struct Chip8 {
     video_driver: VideoDriver,
 }
 
-#[derive(Debug, Clone)]
-/// Instructions of the Chip8.
-pub enum Instruction {
-    /// Clears the screen.
-    CLS,
-    /// Default type for empty instruction field.
-    /// This is not an actual instruction.
-    EMPTY,
-}
-
 impl Chip8 {
     pub fn new() -> Self {
         Self {
             mem: vec![0; 4096],
             regs: vec![0; 16],
             i_reg: 0,
-            pc: 0,
+            pc: PROGRAM_START,
             stack: vec![0; 16],
             stack_pointer: 0,
             delay_timer: 0,
@@ -55,56 +47,51 @@ impl Chip8 {
     }
 
     /// Execute a given instruction.
-    pub fn execute(&mut self, instruction: Instruction) -> Result<(), String> {
+    pub fn execute(&mut self, instruction: u16) -> Result<(), String> {
         match instruction {
             // CLS
-            Instruction::CLS => {
+            0x00E0 => {
                 self.video_driver.clear();
             },
             _ => {
-                return Err(String::from("unknown instruction"));
+                return Err(format!("{:#06x}: not an instruction", instruction));
             }
         };
 
         Ok(())
     }
 
+    fn execute_next_instruction(&mut self) -> Result<(), String> {
+        let hi = self.mem[self.pc as usize] as u16;
+        let lo = self.mem[self.pc as usize + 1] as u16;
+        let ins: u16 = (hi << 8) | lo;
+
+        self.execute(ins)?;
+        self.pc = self.pc + 2;
+
+        Ok(())
+    }
+
     pub fn run(&mut self, file_path: &Path) -> Result<(), String> {
+        self.init_program(file_path)?;
+
+        while self.pc < self.mem.len() as u16 {
+            self.execute_next_instruction()?;
+        }
+
+        Ok(())
+    }
+
+    fn init_program(&mut self, file_path: &Path) -> Result<(), String> {
         let bytes = std::fs::read(file_path).expect("reading file");
-        let instructions = convert_to_instructions(bytes)?;
+        if bytes.len() > self.mem.len() - PROGRAM_START as usize {
+            return Err(String::from("Program too big"));
+        }
 
-        for i in instructions {
-            println!("executing instruction {:?} at line {}", i, self.pc);
-            self.execute(i)?;
-            self.pc = self.pc + 1;
+        for i in 0..bytes.len() {
+            self.mem[PROGRAM_START as usize + i] = bytes[i];
         }
 
         Ok(())
     }
 }
-
-// this makes the code easier to read
-/// Convert a raw u16 instruction to an Instruction enum.
-pub fn raw_to_instruction(instruction: u16) -> Result<Instruction, String> {
-    match instruction {
-        // CLS
-        0x00E0 => return Ok(Instruction::CLS),
-        _ => {
-            return Err(format!("{:x}: not an instruction", instruction));
-        }
-    }
-}
-
-fn convert_to_instructions(bytes: Vec<u8>) -> Result<Vec<Instruction>, String> {
-    // since 2 bytes make up 1 instruction this vec only needs to be half as big
-    let mut instructions: Vec<Instruction> = vec![Instruction::EMPTY; bytes.len() / 2];
-
-    let iter = bytes.chunks(2);
-
-    for (i, e) in iter.enumerate() {
-        let t: u16 = (e[0] as u16) << 8 | (e[1] as u16);
-        instructions[i] = raw_to_instruction(t)?;
-    }
-
-    Ok(instructions)
-}