use std::ops::SubAssign; use std::sync::atomic::{AtomicU16, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use std::thread; use std::thread::{JoinHandle, sleep}; use std::time::Duration; use crate::device::instruction::Instruction; use crate::device::timer::Timer; pub struct Device { pub registers: RegisterFile, pub memory: Box<[u8; Self::DEVICE_MEMORY_SIZE]>, pub timer: Timer, pub stack: Vec, pub frame_buffer: Arc>> } impl Device { pub const DEVICE_MEMORY_SIZE: usize = 1 << 12; pub const FRAME_BUFFER_WIDTH: usize = 64; pub const FRAME_BUFFER_HEIGHT: usize = 32; pub const FRAME_BUFFER_SIZE: usize = Self::FRAME_BUFFER_WIDTH*Self::FRAME_BUFFER_HEIGHT; pub fn new(timer: Timer, fb: Arc>>) -> Device { let memory = vec![0u8; Self::DEVICE_MEMORY_SIZE].into_boxed_slice().try_into().unwrap(); log::trace!("Successfully initiated device memory"); Device { registers: RegisterFile::new(), memory, frame_buffer: fb, stack: Vec::with_capacity(16), timer, } } } impl Device { const DEFAULT_FONT: [u8; 5 * 16] = [ 0xF0, 0x90, 0x90, 0x90, 0xF0, // 0 0x20, 0x60, 0x20, 0x20, 0x70, // 1 0xF0, 0x10, 0xF0, 0x80, 0xF0, // 2 0xF0, 0x10, 0xF0, 0x10, 0xF0, // 3 0x90, 0x90, 0xF0, 0x10, 0x10, // 4 0xF0, 0x80, 0xF0, 0x10, 0xF0, // 5 0xF0, 0x80, 0xF0, 0x90, 0xF0, // 6 0xF0, 0x10, 0x20, 0x40, 0x40, // 7 0xF0, 0x90, 0xF0, 0x90, 0xF0, // 8 0xF0, 0x90, 0xF0, 0x10, 0xF0, // 9 0xF0, 0x90, 0xF0, 0x90, 0x90, // A 0xE0, 0x90, 0xE0, 0x90, 0xE0, // B 0xF0, 0x80, 0x80, 0x80, 0xF0, // C 0xE0, 0x90, 0x90, 0x90, 0xE0, // D 0xF0, 0x80, 0xF0, 0x80, 0xF0, // E 0xF0, 0x80, 0xF0, 0x80, 0x80 // F ]; const FONT_DEFAULT_MEM_LOCATION_START: usize = 0x50; const FONT_DEFAULT_MEM_LOCATION_END: usize = 0x9F; const ROM_START: usize = 0x200; pub fn cycle(&mut self) { let pc = self.registers.pc as usize; let instr_slice = self.memory.get(pc..pc + 2).expect("Failed to get memory"); self.registers.pc += 2; let instruction = Instruction::decode_instruction(instr_slice); self.execute_instruction(instruction); } /// convert the 2 indices into one fn get_framebuffer_index(x:usize,y:usize)->usize{ y*Self::FRAME_BUFFER_WIDTH + x } pub fn execute_instruction(&mut self, instruction: Instruction) { // thread::sleep(Duration::from_millis(250)); log::trace!("Executing {:?}, {:?}",&instruction,&self.registers); match instruction{ Instruction::PassThrough => { log::info!("Executing passthrough"); } Instruction::ClearScreen => { let mut frame_buffer = self.frame_buffer.lock().expect("Failed to grab framebuffer for drawing"); for pixel in frame_buffer.iter_mut(){ *pixel = false; } log::trace!("ClearScreen") } Instruction::JumpTo(new_pc) => { // hint that we're jumping back to self self.registers.pc = new_pc; } Instruction::SetRegister(reg_location, value) => { self.registers.v[reg_location] = value; } Instruction::AddValueToRegister(reg_location, value) => { self.registers.v[reg_location] += value; } Instruction::SetIndex(value) => { self.registers.i = value; } Instruction::Draw(regx,regy, n) => { let x = self.registers.v[regx] as usize; let y = self.registers.v[regy] as usize; let toggle_state = self.draw_sprite_at_location(x, y, n); self.set_flag_register(toggle_state); }, Instruction::JumpAndLink(jump_location) => { self.stack.push(self.registers.pc); self.registers.pc = jump_location; } Instruction::ReturnFromProcedure =>{ let old_pc = self.stack.pop().expect("Expected value on stack pop"); self.registers.pc = old_pc; } Instruction::ConditionalEqSkipNext(_, _) => {} Instruction::ConditionalInEqSkipNext(_, _) => {} Instruction::ConditionalEqRegisterSkipNext(_, _) => {} Instruction::ConditionalInEqRegisterSkipNext(_, _) => {} Instruction::JumpWithOffset(_, _) => {} Instruction::RandomOr(_, _) => {} Instruction::SkipIfKeyPressed(_) => {} Instruction::SkipIfKeyNotPressed(_) => {} Instruction::Set(x, y) => { self.registers.v[x] = self.registers.v[y]; } Instruction::Or(x, y) => { self.registers.v[x] |= self.registers.v[y]; } Instruction::And(x,y) => { self.registers.v[x] &= self.registers.v[y]; } Instruction::Xor(x,y) => { self.registers.v[x] ^= self.registers.v[y]; } Instruction::Add(x,y) => { let left = self.registers.v[x]; let (wrapped_addition_result, is_overflow) = left.overflowing_add(self.registers.v[y]); self.registers.v[x] = wrapped_addition_result; self.set_flag_register(is_overflow); } Instruction::Sub(x,y) => { let left = self.registers.v[x]; let (wrapped_subtraction_result, is_overflow) = left.overflowing_sub(self.registers.v[y]); self.registers.v[x] = wrapped_subtraction_result; self.set_flag_register(is_overflow); } Instruction::RShift(_, _) => {} Instruction::RSub(x, y) => { let left = self.registers.v[y]; let (wrapped_subtraction_result, is_overflow) = left.overflowing_sub(self.registers.v[x]); self.registers.v[x] = wrapped_subtraction_result; self.set_flag_register(is_overflow); } Instruction::LShift(_, _) => {} }; } /// /// Draw a sprite at location at (x,y) for n pixels long and 8 pixels wide. /// Returns whether any pixel was toggled fn draw_sprite_at_location(&mut self, x: usize, y: usize, n: u8)-> bool { let mut frame_buffer = self.frame_buffer.lock().expect("Failed to grab framebuffer for drawing"); let mut is_pixel_toggled_off = false; for i in 0..n as usize { let index = Self::get_framebuffer_index(x, y + i); let slice_from_memory = self.memory[self.registers.i as usize + i]; for bit_index in (0..8).rev() { // if i'm going to the next line, stop if Self::get_framebuffer_index(0, y + 1) == index { break; } let bit_is_true = (slice_from_memory & (1 << bit_index)) == (1< RegisterFile { RegisterFile { v: [0; 0x10], pc: Self::DEFAULT_PC_VALUE, i: 0, } } }