diff --git a/src/app.rs b/src/app.rs index 88d92e5..f9979b2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,5 +1,6 @@ use crate::chip_core::{ ChipCore }; use std::path::PathBuf; +use std::process::Command; use std::time::{ Duration, Instant }; use minifb::{ Key, KeyRepeat, Menu, Window, WindowOptions }; @@ -95,9 +96,20 @@ impl App { self.window.set_title(title.as_str()); } + fn clear_console() { + if cfg!(target_os = "windows") { + let _ = Command::new("cmd").args(["/C", "cls"]).status(); + } + else { + let _ = Command::new("clear").status(); + } + } + fn check_seconds_timer(&mut self) { if self.seconds_timer.elapsed() >= Duration::from_secs(1) && self.rom_loaded && !self.chip_paused { let avg_exec_time = (self.execute_times / self.execute_count as f64) * 1000.0; + + Self::clear_console(); println!("Average execute time: {:.3} ms", avg_exec_time); self.execute_times = 0.0; diff --git a/src/chip_core.rs b/src/chip_core.rs index c733066..e9d4e7e 100644 --- a/src/chip_core.rs +++ b/src/chip_core.rs @@ -4,7 +4,7 @@ use rand::Rng; use rand::rngs::ThreadRng; pub struct ChipCore { - screen_buf: [u64; ChipCore::CHIP_SCR_HEIGHT], + chip_screen_buf: [u64; ChipCore::CHIP_SCR_HEIGHT], schip_screen_buf: [u128; ChipCore::SCHIP_SCR_HEIGHT], ram: [u8; ChipCore::RAM_SIZE], regs: [u8; 16], @@ -71,7 +71,7 @@ impl ChipCore { pub fn new() -> Self { let mut chip_core = Self { - screen_buf: [0; Self::CHIP_SCR_HEIGHT], + chip_screen_buf: [0; Self::CHIP_SCR_HEIGHT], schip_screen_buf: [0; Self::SCHIP_SCR_HEIGHT], ram: [0; Self::RAM_SIZE], regs: [0; 16], @@ -113,13 +113,15 @@ impl ChipCore { } pub fn render_to_rgb_chip_buffer(&mut self, buf: &mut [u32]) { - for i in 0..Self::CHIP_FRAMEBUFFER_SIZE { - buf[i] = if ((self.screen_buf[i >> 6] >> (Self::CHIP_SCR_WIDTH - 1 - (i & 0x3F))) & 0x1) == 1 { 0xFFFFFFFF } else { 0 }; + for (i, pixel) in buf.iter_mut().enumerate() { + let bit_pos = Self::CHIP_SCR_WIDTH - 1 - (i % Self::CHIP_SCR_WIDTH); + *pixel = if (self.chip_screen_buf[i >> 6] >> bit_pos) & 0x1 == 1 { 0xFFFFFFFF } else { 0 }; } } pub fn render_to_rgb_schip_buffer(&mut self, buf: &mut[u32]) { - for i in 0..Self::SCHIP_FRAMEBUFFER_SIZE { - buf[i] = if ((self.schip_screen_buf[i >> 7] >> (Self::SCHIP_SCR_WIDTH - 1 - (i & 0x7F))) & 0x1) == 1 { 0xFFFFFFFF } else { 0 }; + for (i, pixel) in buf.iter_mut().enumerate() { + let bit_pos = Self::SCHIP_SCR_WIDTH - 1 - (i % Self::SCHIP_SCR_WIDTH); + *pixel = if (self.schip_screen_buf[i >> 7] >> bit_pos) & 0x1 == 1 { 0xFFFFFFFF } else { 0 }; } } @@ -157,272 +159,231 @@ impl ChipCore { *elem = T::default(); } } - pub fn execute(&mut self) { - let opcode= ((self.ram[(self.pc & 0xFFF) as usize] as u16) << 8) | (self.ram[((self.pc + 1) & 0xFFF) as usize] as u16); + + fn fetch(&mut self) -> u16 { + let opcode = ((self.ram[(self.pc & 0xFFF) as usize] as u16) << 8) | self.ram[((self.pc + 1) & 0xFFF) as usize] as u16; self.pc += 2; + opcode + } - let x = || -> usize { ((opcode & 0x0F00) >> 8) as usize }; - let y = || -> usize { ((opcode & 0x00F0) >> 4) as usize }; - let data = || -> u8 { (opcode & 0x00FF) as u8 }; - let addr = || -> u16 { opcode & 0x0FFF }; - - match opcode & 0xF000 { - 0x0000 => { - match opcode { - 0x00E0 => { - if self.high_res_mode { - self.schip_screen_buf.fill(0); - } - else { - self.screen_buf.fill(0); - } - } - 0x00EE => { - self.sp = self.sp.wrapping_sub(1) & 0xF; - self.pc = self.stack[self.sp as usize]; - } - 0x00FE => { - if self.high_res_mode { - self.high_res_mode = false; - self.screen_buf.fill(0); - } - } - 0x00FF => { - if !self.high_res_mode { - self.high_res_mode = true; - self.schip_screen_buf.fill(0); - } + pub fn execute(&mut self) { + let opcode = self.fetch(); + + let nibbles = ( + ((opcode & 0xF000) >> 12) as usize, + ((opcode & 0x0F00) >> 8) as usize, + ((opcode & 0x00F0) >> 4) as usize, + (opcode & 0x000F) as usize + ); + + let op_data = (opcode & 0xFF) as u8; + let op_addr = opcode & 0xFFF; + + match nibbles { + (0, 0, 0xE, 0) => { + if self.high_res_mode { + self.schip_screen_buf.fill(0); + } + else { + self.chip_screen_buf.fill(0); + } + } + (0, 0, 0xE, 0xE) => { + self.sp = self.sp.wrapping_sub(1) & 0xF; + self.pc = self.stack[self.sp as usize]; + } + (0, 0, 0xF, 0xE) => { + if self.high_res_mode { + self.high_res_mode = false; + self.chip_screen_buf.fill(0); + } + } + (0, 0, 0xF, 0xF) => { + if !self.high_res_mode { + self.high_res_mode = true; + self.schip_screen_buf.fill(0); + } + } + (0, 0, 0xF, 0xB) => { + if self.high_res_mode { + for row in self.schip_screen_buf.iter_mut() { + *row >>= 4; } - 0x00FB => { - if self.high_res_mode { - for row in self.schip_screen_buf.iter_mut() { - *row >>= 4; - } - } - else { - for row in self.screen_buf.iter_mut() { - *row >>= 4; - } - } + } + else { + for row in self.chip_screen_buf.iter_mut() { + *row >>= 4; } - 0x00FC => { - if self.high_res_mode { - for row in self.schip_screen_buf.iter_mut() { - *row <<= 4; - } - } - else { - for row in self.screen_buf.iter_mut() { - *row <<= 4; - } - } + } + } + (0, 0, 0xF, 0xC) => { + if self.high_res_mode { + for row in self.schip_screen_buf.iter_mut() { + *row <<= 4; } - _ => { - match opcode & 0xFFF0 { - 0x00C0 => { - let n = (opcode & 0x000F) as usize; - - if self.high_res_mode { - Self::shift_screenbuf_down(&mut self.schip_screen_buf, n); - } - else { - Self::shift_screenbuf_down(&mut self.screen_buf, n); - } - } - _ => { - println!("Unknown opcode {:X}", opcode); - } - } + } + else { + for row in self.chip_screen_buf.iter_mut() { + *row <<= 4; } + } + } + (0, 0, 0xC, n) => { + if self.high_res_mode { + Self::shift_screenbuf_down(&mut self.schip_screen_buf, n); + } + else { + Self::shift_screenbuf_down(&mut self.chip_screen_buf, n); } } - 0x1000 => { - self.pc = addr(); + (1, _, _, _) => { + self.pc = op_addr; } - 0x2000 => { + (2, _, _, _) => { self.stack[self.sp as usize] = self.pc; self.sp = (self.sp + 1) & 0xF; - self.pc = addr(); + self.pc = op_addr; } - 0x3000 => { - if self.regs[x()] == data() { + (3, x, _, _) => { + if self.regs[x] == op_data { self.pc += 2; } } - 0x4000 => { - if self.regs[x()] != data() { + (4, x, _, _) => { + if self.regs[x] != op_data { self.pc += 2; } } - 0x5000 => { - match opcode & 0x000F { - 0x0000 => { - if self.regs[x()] == self.regs[y()] { - self.pc += 2; - } - } - _ => { - println!("Unknown opcode {:X}", opcode); - } + (5, x, y, 0) => { + if self.regs[x] == self.regs[y] { + self.pc += 2; } } - 0x6000 => { - self.regs[x()] = data(); + (6, x, _, _) => { + self.regs[x] = op_data; } - 0x7000 => { - self.regs[x()] = self.regs[x()].wrapping_add(data()); + (7, x, _, _) => { + self.regs[x] = self.regs[x].wrapping_add(op_data); } - 0x8000 => { - match opcode & 0x000F { - 0x0000 => { - self.regs[x()] = self.regs[y()]; - } - 0x0001 => { - self.regs[x()] |= self.regs[y()]; - self.regs[0xF] = 0; // VF reset quirk. - } - 0x0002 => { - self.regs[x()] &= self.regs[y()]; - self.regs[0xF] = 0; // VF reset quirk. - } - 0x0003 => { - self.regs[x()] ^= self.regs[y()]; - self.regs[0xF] = 0; // VF reset quirk. - } - 0x0004 => { - let (res, overflow) = self.regs[x()].overflowing_add(self.regs[y()]); - self.regs[x()] = res; - self.regs[0xF] = overflow as u8; - } - 0x0005 => { - let (res, overflow) = self.regs[x()].overflowing_sub(self.regs[y()]); - self.regs[x()] = res; - self.regs[0xF] = !overflow as u8; - } - 0x0006 => { - let shifted = self.regs[x()] & 0x1; - self.regs[x()] >>= 1; - self.regs[0xF] = shifted; - } - 0x0007 => { - let (res, overflow) = self.regs[y()].overflowing_sub(self.regs[x()]); - self.regs[x()] = res; - self.regs[0xF] = !overflow as u8; - } - 0x000E => { - let shifted = (self.regs[x()] & 0x80) >> 7; - self.regs[x()] <<= 1; - self.regs[0xF] = shifted; - } - _ => { - println!("Unknown opcode {:X}", opcode); - } + (8, x, y, 0) => { + self.regs[x] = self.regs[y]; + } + (8, x, y, 1) => { + self.regs[x] |= self.regs[y]; + self.regs[0xF] = 0; // VF reset quirk. + } + (8, x, y, 2) => { + self.regs[x] &= self.regs[y]; + self.regs[0xF] = 0; // VF reset quirk. + } + (8, x, y, 3) => { + self.regs[x] ^= self.regs[y]; + self.regs[0xF] = 0; // VF reset quirk. + } + (8, x, y, 4) => { + let (res, overflow) = self.regs[x].overflowing_add(self.regs[y]); + self.regs[x] = res; + self.regs[0xF] = overflow as u8; + } + (8, x, y, 5) => { + let (res, overflow) = self.regs[x].overflowing_sub(self.regs[y]); + self.regs[x] = res; + self.regs[0xF] = !overflow as u8; + } + (8, x, _, 6) => { + let shifted = self.regs[x] & 0x1; + self.regs[x] >>= 1; + self.regs[0xF] = shifted; + } + (8, x, y, 7) => { + let (res, overflow) = self.regs[y].overflowing_sub(self.regs[x]); + self.regs[x] = res; + self.regs[0xF] = !overflow as u8; + } + (8, x, _, 0xE) => { + let shifted = (self.regs[x] & 0x80) >> 7; + self.regs[x] <<= 1; + self.regs[0xF] = shifted; + } + (9, x, y, 0) => { + if self.regs[x] != self.regs[y] { + self.pc += 2; + } + } + (0xA, _, _, _) => { + self.i_reg = op_addr; + } + (0xB, _, _, _) => { + self.pc = (self.regs[0] as u16) + op_addr; + } + (0xC, x, _, _) => { + self.regs[x] = self.rng.gen::() & op_data; + } + (0xD, x,y, n) => { + self.dxyn(self.regs[x], self.regs[y], n as u8); + } + (0xE, x, 0x9, 0xE) => { + if self.keys[(self.regs[x] & 0xF) as usize] { + self.pc += 2; } } - 0x9000 => { - match opcode & 0x000F { - 0x0000 => { - if self.regs[x()] != self.regs[y()] { - self.pc += 2; - } - } - _ => { - println!("Unknown opcode {:X}", opcode); - } + (0xE, x, 0xA, 0x1) => { + if !self.keys[(self.regs[x] & 0xF) as usize] { + self.pc += 2; } } - 0xA000 => { - self.i_reg = addr(); + (0xF, x, 0x0, 0x7) => { + self.regs[x] = self.delay_timer; } - 0xB000 => { - self.pc = (self.regs[0] as u16) + addr(); + (0xF, x, 0x0, 0xA) => { + if !self.awaiting_key_release { + self.awaiting_key_release = true; + self.released_key_reg = x as i8; + } + else if self.released_key_reg == -1 { + self.awaiting_key_release = false; + return; + } + + self.pc -= 2; } - 0xC000 => { - self.regs[x()] = self.rng.gen::() & data(); + (0xF, x, 0x1, 0x5) => { + self.delay_timer = self.regs[x]; } - 0xD000 => { - Self::dxyn(self, opcode); + (0xF, x, 0x1, 0x8) => { + self.sound_timer = self.regs[x]; } - 0xE000 => { - match opcode & 0x00FF { - 0x009E => { - if self.keys[(self.regs[x()] & 0xF) as usize] { - self.pc += 2; - } - } - 0x00A1 => { - if !self.keys[(self.regs[x()] & 0xF) as usize] { - self.pc += 2; - } - } - _ => { - println!("Unknown opcode {:X}", opcode); - } + (0xF, x, 0x1, 0xE) => { + self.i_reg = self.i_reg.wrapping_add(self.regs[x] as u16); + } + (0xF, x, 0x2, 0x9) => { + self.i_reg = ((self.regs[x] & 0xF) * 0x5) as u16; + } + (0xF, x, 0x3, 0x0) => { + self.i_reg = Self::SCHIP_FONT_OFFSET as u16 + ((self.regs[x] & 0xF) * 10) as u16; + } + (0xF, x, 0x3, 0x3) => { + self.ram[self.i_reg as usize & 0xFFF] = self.regs[x] / 100; + self.ram[(self.i_reg as usize + 1) & 0xFFF] = (self.regs[x] / 10) % 10; + self.ram[(self.i_reg as usize + 2) & 0xFFF] = self.regs[x] % 10; + } + (0xF, x, 0x5, 0x5) => { + for i in 0..=x { + self.ram[(self.i_reg as usize + i) & 0xFFF] = self.regs[i]; } } - 0xF000 => { - match opcode & 0x00FF { - 0x0007 => { - self.regs[x()] = self.delay_timer; - } - 0x000A => { - if !self.awaiting_key_release { - self.awaiting_key_release = true; - self.released_key_reg = x() as i8; - } - else if self.released_key_reg == -1 { - self.awaiting_key_release = false; - return; - } - - self.pc -= 2; - } - 0x0015 => { - self.delay_timer = self.regs[x()]; - } - 0x0018 => { - self.sound_timer = self.regs[x()]; - } - 0x001E => { - self.i_reg = self.i_reg.wrapping_add(self.regs[x()] as u16); - } - 0x0029 => { - self.i_reg = ((self.regs[x()] & 0xF) * 0x5) as u16; - } - 0x0030 => { - self.i_reg = Self::SCHIP_FONT_OFFSET as u16 + ((self.regs[x()] & 0xF) * 10) as u16; - } - 0x0033 => { - self.ram[self.i_reg as usize & 0xFFF] = self.regs[x()] / 100; - self.ram[(self.i_reg as usize + 1) & 0xFFF] = (self.regs[x()] / 10) % 10; - self.ram[(self.i_reg as usize + 2) & 0xFFF] = self.regs[x()] % 10; - } - 0x0055 => { - for i in 0..=x() { - self.ram[(self.i_reg as usize + i) & 0xFFF] = self.regs[i]; - } - } - 0x0065 => { - for i in 0..=x() { - self.regs[i] = self.ram[(self.i_reg as usize + i) & 0xFFF]; - } - } - _ => { - println!("Unknown opcode {:X}", opcode); - } + (0xF, x, 0x6, 0x5) => { + for i in 0..=x { + self.regs[i] = self.ram[(self.i_reg as usize + i) & 0xFFF]; } } _ => { - println!("Unknown opcode {:X}", opcode); + println!("Unknown opcode {opcode:X}") } } } - fn dxyn(&mut self, opcode: u16) { - let mut x_pos = self.regs[((opcode & 0x0F00) >> 8) as usize]; - let mut y_pos = self.regs[((opcode & 0x00F0) >> 4) as usize]; - let height = opcode & 0x000F; - + fn dxyn(&mut self, mut x_pos: u8, mut y_pos: u8, height: u8) { self.regs[0xF] = 0; if !self.high_res_mode { @@ -449,12 +410,12 @@ impl ChipCore { } } - fn draw_lores_sprite(&mut self, x_pos: u8, mut y_pos: u8, height: u16) { + fn draw_lores_sprite(&mut self, x_pos: u8, mut y_pos: u8, height: u8) { let step = if DOUBLE_HEIGHT { 2 } else { 1 }; let sprite_width = if DOUBLE_HEIGHT { 15 } else { 7 }; let sprite_bound = Self::CHIP_SCR_WIDTH as u8 - sprite_width - 1; - for i in (0..if DOUBLE_HEIGHT { 32 } else { height }).step_by(step) { + for i in (0..if DOUBLE_HEIGHT { 32 } else { height as u16 }).step_by(step) { if y_pos == Self::CHIP_SCR_HEIGHT as u8 { break; } @@ -471,17 +432,17 @@ impl ChipCore { let sprite_mask = if x_pos > sprite_bound { sprite_row >> (x_pos - sprite_bound) } else { sprite_row << (Self::CHIP_SCR_WIDTH as u8 - 1 - x_pos - sprite_width) }; - self.regs[0xF] |= ((self.screen_buf[y_pos as usize] & sprite_mask) != 0) as u8; - self.screen_buf[y_pos as usize] ^= sprite_mask; + self.regs[0xF] |= ((self.chip_screen_buf[y_pos as usize] & sprite_mask) != 0) as u8; + self.chip_screen_buf[y_pos as usize] ^= sprite_mask; y_pos += 1; } } - fn draw_hires_sprite(&mut self, x_pos: u8, mut y_pos: u8, height: u16) { + fn draw_hires_sprite(&mut self, x_pos: u8, mut y_pos: u8, height: u8) { let step = if DOUBLE_HEIGHT { 2 } else { 1 }; let sprite_width = if DOUBLE_HEIGHT { 15 } else { 7 }; let sprite_bound = Self::SCHIP_SCR_WIDTH as u8 - sprite_width - 1; - for i in (0..if DOUBLE_HEIGHT { 32 } else { height }).step_by(step) { + for i in (0..if DOUBLE_HEIGHT { 32 } else { height as u16 }).step_by(step) { if y_pos == Self::SCHIP_SCR_HEIGHT as u8 { break; }