commit ebf97be247eb7e27b2df1adf663ae48c9accd391 from: Witcher01 date: Sun Aug 1 16:58:52 2021 UTC removed enum, save program in memory removed the previously added enum to instead store the program in memory. this is to avoid complications with addressing memory later in, which could be tedious when representing instructions as enums. no readability should have been lost if all the instructions are documented when decoded. 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 { - match instruction { - // CLS - 0x00E0 => return Ok(Instruction::CLS), - _ => { - return Err(format!("{:x}: not an instruction", instruction)); - } - } -} - -fn convert_to_instructions(bytes: Vec) -> Result, String> { - // since 2 bytes make up 1 instruction this vec only needs to be half as big - let mut instructions: Vec = 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) -}