use log::{debug, error}; use crate::chip8::delay_timer::DelayTimer; use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION; use crate::chip8::registers::Chip8Registers; use crate::chip8::sound_timer::SoundTimer; use crate::chip8::util::InstructionUtil; use crate::constants::{CHIP8_MEMORY_SIZE, CHIP8_REGISTER_COUNT}; use super::{ cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions, system_memory::Chip8SystemMemory, video::Chip8Video, }; #[derive(Clone, Copy)] pub struct Chip8Computer { pub sp: u8, pub memory: Chip8SystemMemory, pub registers: Chip8Registers, pub sound_timer: SoundTimer, pub delay_timer: DelayTimer, pub video_memory: Chip8Video, pub state: Chip8CpuStates, } impl Default for Chip8Computer { fn default() -> Self { Self { sp: 0x00, memory: Chip8SystemMemory::default(), video_memory: Chip8Video::default(), registers: Chip8Registers::default(), sound_timer: SoundTimer::new(), delay_timer: DelayTimer::new(), state: Chip8CpuStates::WaitingForInstruction, } } } impl Chip8Computer { pub fn new_with_program(new_program: Box>) -> Self { let mut working = Chip8Computer::new(); for i in 0..new_program.len() { let high_byte = (new_program[i as usize] >> 8) as u8; let low_byte = (new_program[i] & 0xff) as u8; let base_offset = i * 2; working.memory.poke(base_offset as u16, high_byte); working.memory.poke((base_offset + 1) as u16, low_byte); } working } pub fn new() -> Self { Chip8Computer::default() } pub fn load_bytes_to_memory(&mut self, offset: u16, to_load: Box>) { let total_len = to_load.len() as u16; for current_index in 0..total_len { let new_value = to_load[current_index as usize]; let new_location = current_index + offset; self.memory.poke(new_location, new_value); } } pub fn step_system(&mut self) -> &mut Chip8Computer { // read the next instruction let mut working_instruction: u16 = 0b0000000000000000; let start_pc = self.registers.peek_pc(); let high_byte = (self.memory.clone().peek(start_pc) as u16).rotate_left(8); let low_byte = self.memory.clone().peek(start_pc + 1) as u16; working_instruction = InstructionUtil::join_bytes(high_byte as u8, low_byte as u8); let decoded_instruction = Chip8CpuInstructions::decode(working_instruction); // println!("DECODED INSTRUCTION = {:?}", decoded_instruction); // start by moving to the next instruction // todo: THIS IS BAD AND IS A SIDE EFFECT decoded_instruction.execute(self); self.sound_timer.tick(); self.delay_timer.tick(); self } } #[cfg(test)] mod test { use rand::random; use crate::constants::CHIP8_VIDEO_MEMORY; use super::*; #[test] fn smoke() { assert!(true) } #[test] fn decoder_test_invalid_instructions() { // 'bad' instructions that should be dropped... // assert!(matches!(Chip8Computer::decode_instruction(0x5ab1), Chip8CpuInstructions::XXXXERRORINSTRUCTION)); // assert!(matches!(Chip8Computer::decode_instruction(0x8ab8), Chip8CpuInstructions::XXXXERRORINSTRUCTION)); // assert!(matches!(Chip8Computer::decode_instruction(0xeaba), Chip8CpuInstructions::XXXXERRORINSTRUCTION)); } #[test] fn new_with_program() { // set a known program that sets some registers... let new_program = Box::new(vec![ Chip8CpuInstructions::LdVxI(0x10).encode(), Chip8CpuInstructions::LdVxVy(0x1, 0x1).encode(), Chip8CpuInstructions::LdStVx(0xff).encode() ]); // ...then load it... let test_computer = Chip8Computer::new_with_program(new_program); // ...and check the registers } }