use super::periph::{Display, Keypad, Timer}; use std::fs::File; use std::io::{self, Read}; use std::path::Path; #[derive(Copy, Clone)] pub struct Chip8 { i: u16, pc: u16, sp: u8, v: [u8; 16], stack: [u16; 16], memory: [u8; 4096], delay: Timer, sound: Timer, pub key: Keypad, pub display: Display, pub request_redraw: bool, } impl Default for Chip8 { fn default() -> Self { let mut chip8 = Chip8 { i: 0, pc: 0x200, // Progrm counter starts at 0x200 sp: 0, v: [0; 16], stack: [0; 16], memory: [0; 4096], delay: Default::default(), sound: Default::default(), key: Default::default(), display: Default::default(), request_redraw: false, }; chip8.load_font_set(); chip8 } } impl Chip8 { const FONT_SET: [u8; 80] = [ 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 ]; pub fn execute_cycle(&mut self) { // Reset Request Redraw self.request_redraw = false; let opcode = self.get_opcode(); self.pc += 2; // Immediately increment the Program Counter self.execute_opcode(opcode); self.delay.tick(); self.sound.tick(); } pub fn load_font_set(&mut self) { self.memory[0x50..0xA0].copy_from_slice(&Self::FONT_SET); } pub fn load_rom>(&mut self, path: P) -> Result<(), io::Error> { let mut file = File::open(path.as_ref())?; let mut rom_buf: Vec = vec![]; file.read_to_end(&mut rom_buf)?; self.memory[0x200..(0x200 + rom_buf.len())].copy_from_slice(&rom_buf); Ok(()) } fn get_opcode(&self) -> u16 { let pc = self.pc as usize; ((self.memory[pc] as u16) << 8) | self.memory[pc + 1] as u16 } fn get_nibs(opcode: u16) -> (u8, u8, u8, u8) { // Given 0xA2F0 ( ((opcode & 0xF000) >> 12) as u8, // 0xA ((opcode & 0x0F00) >> 8) as u8, // 0x2 ((opcode & 0x00F0) >> 4) as u8, // 0xF (opcode & 0x000F) as u8, // 0x0 ) } fn execute_opcode(&mut self, opcode: u16) { let (nib_1, nib_2, nib_3, nib_4) = Self::get_nibs(opcode); let nnn: u16 = opcode & 0x0FFF; let kk: u8 = (opcode & 0x00FF) as u8; let x = nib_2; let y = nib_3; let n = nib_4; match (nib_1, nib_2, nib_3, nib_4) { // CLS (0x0, 0x0, 0xE, 0x0) => self.cls(), // 00EE (0x0, 0x0, 0xE, 0xE) => self.ret(), // 0NNN (0x0, _, _, _) => { /* Nothing to see here */ } // 1NNN (0x1, _, _, _) => self.jmp_addr(nnn), // 2NNN (0x2, _, _, _) => self.call_addr(nnn), // 3XKK (0x3, _, _, _) => self.se_vx_byte(x, kk), // 4xkk (0x4, _, _, _) => self.sne_vx_byte(x, kk), // 5xy0 (0x5, _, _, 0x0) => self.se_vx_vy(x, y), // 6xkk (0x6, _, _, _) => self.ld_vx_byte(x, kk), // 7xkk (0x7, _, _, _) => self.add_vx_byte(x, kk), // 8xy0 (0x8, _, _, 0x0) => self.ld_vx_vy(x, y), // 8xy1 (0x8, _, _, 0x1) => self.or(x, y), // 8xy2 (0x8, _, _, 0x2) => self.and_vx_vy(x, y), // 8xy3 (0x8, _, _, 0x3) => self.xor(x, y), // 8xy4 (0x8, _, _, 0x4) => self.add_vx_vy(x, y), // 8xy5 (0x8, _, _, 0x5) => self.sub_vx_vy(x, y), // 8xy6 (0x8, _, _, 0x6) => self.shr(x), // 8xy7 (0x8, _, _, 0x7) => self.subn_vx_vy(x, y), // 8xyE (0x8, _, _, 0xE) => self.shl(x), // 9xy0 (0x9, _, _, 0x0) => self.sne_vx_vy(x, y), // Annn (0xA, _, _, _) => self.load_addr_to_i(nnn), // Bnnn (0xB, _, _, _) => self.jmp_addr_with_offset(nnn), // Cxkk (0xC, _, _, _) => self.rand(x, kk), //Dxyn (0xD, _, _, _) => self.draw(x, y, n), // Ex9E (0xE, _, 0x9, 0xE) => self.skip_on_press(x), // ExA1 (0xE, _, 0xA, 0x1) => self.skip_not_pressed(x), // Fx07 (0xF, _, 0x0, 0x7) => self.copy_delay_timer_val(x), // Fx0A (0xF, _, 0x0, 0xA) => self.loop_until_key_vx(x), // Fx15 (0xF, _, 0x1, 0x5) => self.set_delay_to_vx(x), // Fx18 (0xF, _, 0x1, 0x8) => self.set_sound_to_vx(x), // Fx1E (0xF, _, 0x1, 0xE) => self.add_vx_to_i(x), // Fx29 (0xF, _, 0x2, 0x9) => self.set_i_to_hex_sprite_loc(x), // Fx33 (0xF, _, 0x3, 0x3) => self.copy_digits_to_i(x), // Fx55 (0xF, _, 0x5, 0x5) => self.copy_from_vx_to_memory(x), // Fx65 (0xF, _, 0x6, 0x5) => self.load_into_vx_from_i(x), // Otherwise... _ => eprintln!("UNIMPLEMENTED OPCODE: {:#x}", opcode), } } fn cls(&mut self) { // Clear the display self.display.clear(); self.request_redraw = true; } fn ret(&mut self) { // sets the program counter to the address at the top of the stack, // then subtracts one (1) from the stack pointer self.pc = self.stack[self.sp as usize]; self.sp -= 1; } fn jmp_addr(&mut self, nnn: u16) { // sets the program counter to addr (nnn) self.pc = nnn; // We want to execute nnn so decrement pc by 2; } fn call_addr(&mut self, nnn: u16) { // increments the stack pointer, then puts current pc on top of stack // pc is then set to addr self.sp += 1; self.stack[self.sp as usize] = self.pc; self.pc = nnn; // see Chip8#jmp_addr() } fn se_vx_byte(&mut self, x: u8, kk: u8) { // compares Vx to kk. If they are equal, pc is incremented by 2 if self.v[x as usize] == kk { self.pc += 2; } } fn sne_vx_byte(&mut self, x: u8, kk: u8) { // compares Vx to kk. If they are **not** equal, pc is incremented by 2 if self.v[x as usize] != kk { self.pc += 2; } } fn se_vx_vy(&mut self, x: u8, y: u8) { // compares Vx to Vy. If they are equal, pc is incremented by 2 if self.v[x as usize] == self.v[y as usize] { self.pc += 2; } } fn ld_vx_byte(&mut self, x: u8, kk: u8) { // put value kk into Vx self.v[x as usize] = kk; } fn add_vx_byte(&mut self, x: u8, kk: u8) { // calculate Vx + kk, then store it in Vx // This does indeed overflow without setting VF self.v[x as usize] = self.v[x as usize].wrapping_add(kk); } fn ld_vx_vy(&mut self, x: u8, y: u8) { // store Vy in Vx self.v[x as usize] = self.v[y as usize]; } fn or(&mut self, x: u8, y: u8) { // calc bitwise OR on Vx and Vy, then store in Vx self.v[x as usize] |= self.v[y as usize]; } fn and_vx_vy(&mut self, x: u8, y: u8) { // calc bitwise AND on Vx and Vy, then store in Vx self.v[x as usize] &= self.v[y as usize]; } fn xor(&mut self, x: u8, y: u8) { // calc bitwise XOR on Vx and Vy, then store in Vx self.v[x as usize] ^= self.v[y as usize]; } fn add_vx_vy(&mut self, x: u8, y: u8) { // add Vx and Vy, if result is greater than 8 bits // set VF to 1, otherwise 0 // only the lowest 8 bits of result are stored in Vx let x = x as usize; let (sum, did_overflow) = self.v[x].overflowing_add(self.v[y as usize]); self.v[x] = sum; self.v[0xF] = if did_overflow { 1 } else { 0 }; } fn sub_vx_vy(&mut self, x: u8, y: u8) { // subtract Vx and Vy, if Vx > Vy VF is set to 1, otherwise 0 // then set Vx to Vx - Vy let x = x as usize; let y = y as usize; self.v[0xF] = if self.v[x] > self.v[y] { 1 } else { 0 }; self.v[x] = self.v[x].wrapping_sub(self.v[y]); } fn shr(&mut self, x: u8) { // if LSB is set to 1, set VF to 1, otherwise set VF to 0 // then shift Vx one to the right let x = x as usize; self.v[0xF] = if (self.v[x] & 0x01) == 0x01 { 1 } else { 0 }; self.v[x] >>= 1; } fn subn_vx_vy(&mut self, x: u8, y: u8) { // subtract Vy and Vx, if Vy > Vx VF is set to 1, otherwise 0 // then set Vx = Vy - Vx let x = x as usize; let y = y as usize; self.v[0xF] = if self.v[y] > self.v[x] { 1 } else { 0 }; self.v[x as usize] = self.v[y].wrapping_sub(self.v[x]); } fn shl(&mut self, x: u8) { // if MSB is set to 1, set VF to 1, otherwise set VF to 0 // then shift Vx one to the left; let x = x as usize; self.v[0xF] = if (self.v[x] >> 7) == 1 { 1 } else { 0 }; self.v[x] <<= 1; } fn sne_vx_vy(&mut self, x: u8, y: u8) { // if Vx != vy program counter is increased by 2 if self.v[x as usize] != self.v[y as usize] { self.pc += 2; } } fn load_addr_to_i(&mut self, nnn: u16) { // set i to addr self.i = nnn; } fn jmp_addr_with_offset(&mut self, nnn: u16) { // set program counter to addr + V0 self.pc = nnn + self.v[0] as u16; // see Chip8#jmp_addr } fn rand(&mut self, x: u8, kk: u8) { // generate a random number from 0 to 255 // AND with the value of kk, then store in Vx self.v[x as usize] = rand::random::() & kk; } fn draw(&mut self, x: u8, y: u8, n: u8) { let i = self.i as usize; let draw_pos = (self.v[x as usize], self.v[y as usize]); let sprite_data = &self.memory[i..(i + n as usize)]; let collision = self.display.draw_sprite(draw_pos, sprite_data); self.v[0xF] = if collision { 1 } else { 0 }; self.request_redraw = true; } fn skip_on_press(&mut self, x: u8) { // if current key is the same as the on in Vx // program counter is increased by 2 if self.key.is_pressed(self.v[x as usize]) { self.pc += 2; } } fn skip_not_pressed(&mut self, x: u8) { // if current key is not the sameas the one in Vx // increment the program counter by 2 if !self.key.is_pressed(self.v[x as usize]) { self.pc += 2; } } fn copy_delay_timer_val(&mut self, x: u8) { // set Vx to be the value of the delay timer self.v[x as usize] = self.delay.get(); } fn loop_until_key_vx(&mut self, x: u8) { // wait (blocking) until a key is pressed // once pressed, store in Vx match self.key.get_any_pressed() { Some(key) => self.v[x as usize] = key, None => self.pc -= 2, } } fn set_delay_to_vx(&mut self, x: u8) { // set delay timer to be value of Vx self.delay.set(self.v[x as usize]); } fn set_sound_to_vx(&mut self, x: u8) { // set sound timer to be value of Vx self.sound.set(self.v[x as usize]); } fn add_vx_to_i(&mut self, x: u8) { // set I to be I + Vx // Althought not standard, we check for overflow as well here // Memory is 4KB (12-bits) so anything mor than that "overflows" let sum = self.i + self.v[x as usize] as u16; self.v[0xF] = if sum > 0x0FFF { 1 } else { 0 }; self.i = sum & 0x0FFF; } fn set_i_to_hex_sprite_loc(&mut self, x: u8) { // set I to location of hex sprite related to Vx self.i = 0x50 + (5 * self.v[x as usize] as u16); } fn copy_digits_to_i(&mut self, x: u8) { // take hundreds digit and place it at I // take tens digit and place it at I + 1 // take ones digit and place it at I + 2 let i = self.i as usize; let digits: [u8; 3] = Self::to_digits(self.v[x as usize]); self.memory[i] = digits[2]; self.memory[i + 1] = digits[1]; self.memory[i + 2] = digits[0]; } fn copy_from_vx_to_memory(&mut self, x: u8) { // copy values v0 -> Vx to memory starting at i for n in 0..=(x as usize) { self.memory[self.i as usize + n] = self.v[n]; } } fn load_into_vx_from_i(&mut self, x: u8) { // read what will be values of v0 -> vx from memory starting at i for n in 0..=(x as usize) { self.v[n] = self.memory[self.i as usize + n]; } } fn to_digits(num: u8) -> [u8; 3] { let mut cpy = num.clone(); let mut digits = [0, 0, 0]; for digit in digits.iter_mut() { *digit = cpy % 10; cpy /= 10; } digits } } #[cfg(test)] mod test { use super::Chip8; #[test] fn chip8_initializes_properly() { let chip8: Chip8 = Default::default(); assert_eq!(chip8.pc, 0x200); let slice: &[u8] = &chip8.memory[0..0x50]; let font_set: &[u8] = &Chip8::FONT_SET; for (i, byte) in slice.iter().enumerate() { assert_eq!(*byte, font_set[i]); } } #[test] fn get_opcode_works() { let mut chip8: Chip8 = Default::default(); let expected = 0x00E0; // Clear Screen chip8.memory[0x202] = 0x00; chip8.memory[0x203] = 0xE0; chip8.pc = 0x202; assert_eq!(chip8.get_opcode(), expected); } #[test] fn get_nibs_works() { let opcode = 0xA2F0; let (n1, n2, n3, n4) = Chip8::get_nibs(opcode); assert_eq!(n1, 0xA); assert_eq!(n2, 0x2); assert_eq!(n3, 0xF); assert_eq!(n4, 0x0); } #[test] fn opcode_cls_works() { let mut chip8: Chip8 = Default::default(); // Display is now noise for byte in chip8.display.buf.iter_mut() { *byte = rand::random(); } chip8.cls(); // now check that everything in the display buffer is zeroed out for byte in chip8.display.buf.iter() { assert_eq!(*byte, 0x0); } } #[test] fn opcode_ret_works() { let mut chip8: Chip8 = Default::default(); chip8.pc = 0x202; chip8.stack[0] = 0xABF0; chip8.stack[1] = 0x50D5; // 80 chip8.sp = 1; chip8.ret(); // Ret should set the programcouner to the address at the top of the stack // and then decrement the stack pointer by 1 assert_eq!(chip8.sp, 0); assert_eq!(chip8.pc, 0x50D5); } #[test] fn opcode_jmp_addr_works() { let mut chip8: Chip8 = Default::default(); let opcode = 0x1ABC; let nnn = opcode & 0x0FFF; chip8.jmp_addr(nnn); assert_eq!(chip8.pc, nnn); } #[test] fn opcode_call_addr_works() { let mut chip8: Chip8 = Default::default(); let nnn = 0x0ABC; chip8.call_addr(nnn); assert_eq!(chip8.sp, 1); assert_eq!(chip8.stack[chip8.sp as usize], 0x200); assert_eq!(chip8.pc, nnn); } #[test] fn opcode_se_vx_byte_works() { let mut chip8: Chip8 = Default::default(); // Opcode: 0x32D5 // Test Vx == kk chip8.pc = 0x200; chip8.v[2] = 0xD5; chip8.se_vx_byte(0x2, 0xD5); assert_eq!(chip8.pc, 0x202); // Test Vx != kk chip8.pc = 0x200; chip8.v[2] = 0xD6; chip8.se_vx_byte(0x2, 0xD5); assert_ne!(chip8.pc, 0x202); } #[test] fn opcode_sne_vx_byte_works() { let mut chip8: Chip8 = Default::default(); // Opcode: 0x32D6 // Test Vx != kk chip8.pc = 0x200; chip8.v[2] = 0xD5; chip8.sne_vx_byte(0x2, 0xD6); assert_eq!(chip8.pc, 0x202); // Test Vx == kk chip8.pc = 0x200; chip8.v[2] = 0xD6; chip8.sne_vx_byte(0x2, 0xD6); assert_ne!(chip8.pc, 0x202); } #[test] fn opcode_se_vx_vy_works() { let mut chip8: Chip8 = Default::default(); // When Equal chip8.pc = 0x200; chip8.v[4] = 0xAB; chip8.v[7] = 0xAB; chip8.se_vx_vy(0x4, 0x7); assert_eq!(chip8.pc, 0x202); // When not Equal chip8.pc = 0x200; chip8.v[4] = 0xAC; chip8.v[7] = 0xAB; chip8.se_vx_vy(0x4, 0x7); assert_eq!(chip8.pc, 0x200); } #[test] fn opcode_ld_vx_byte_works() { let mut chip8: Chip8 = Default::default(); assert_ne!(chip8.v[5], 0xAB); chip8.ld_vx_byte(0x5, 0xAB); assert_eq!(chip8.v[5], 0xAB); } #[test] fn opcode_add_vx_byte_works() { let mut chip8: Chip8 = Default::default(); chip8.v[3] = 0x0A; chip8.add_vx_byte(0x3, 0x36); assert_eq!(chip8.v[3], 0x40); } #[test] fn opcode_ld_vx_vy_works() { let mut chip8: Chip8 = Default::default(); chip8.v[1] = 0x64; chip8.v[4] = 0x96; chip8.ld_vx_vy(0x1, 0x4); assert_eq!(chip8.v[1], 0x96); } #[test] fn opcode_or_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0xA] = 0xAB; chip8.v[0x9] = 0xC6; chip8.or(0xA, 0x9); assert_eq!(chip8.v[0xA], 0xAB | 0xC6); } #[test] fn opcode_and_vx_vy_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0xC] = 0x58; chip8.v[0x2] = 0x9a; chip8.and_vx_vy(0xC, 0x2); assert_eq!(chip8.v[0xC], 0x58 & 0x9a); } #[test] fn opcode_xor_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0x5] = 0x38; chip8.v[0xB] = 0x4C; chip8.xor(0x5, 0xB); assert_eq!(chip8.v[0x5], 0x38 ^ 0x4C); } #[test] fn opcode_add_vx_vy_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0xA] = 0x01; chip8.v[0x8] = 0xC8; chip8.add_vx_vy(0xA, 0x8); assert_eq!(chip8.v[0xA], 0x01 + 0xC8); assert_eq!(chip8.v[0xF], 0); } #[test] fn opcode_add_vx_vy_works_vf_set() { let mut chip8: Chip8 = Default::default(); chip8.v[0xE] = 0xFF; chip8.v[0x7] = 0x01; chip8.add_vx_vy(0xE, 0x7); assert_eq!(chip8.v[0xA], (0xFF as u8).wrapping_add(0x01)); assert_eq!(chip8.v[0xF], 1); } #[test] fn opcode_sub_vx_vy_works_vf_set() { let mut chip8: Chip8 = Default::default(); chip8.v[0x3] = 0xD5; chip8.v[0xD] = 0x71; chip8.sub_vx_vy(0x3, 0xD); assert_eq!(chip8.v[0xF], 1); assert_eq!(chip8.v[0x3], 0xD5 - 0x71); } #[test] fn opcode_sub_vx_vy_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0xB] = 0x23; chip8.v[0x5] = 0xCA; chip8.sub_vx_vy(0xB, 0x5); assert_eq!(chip8.v[0xF], 0); assert_eq!(chip8.v[0xB], (0x23 as u8).wrapping_sub(0xCA)) } #[test] fn opcode_shr_works() { let mut chip8: Chip8 = Default::default(); // LSB == 1 chip8.v[0x6] = 0xC8; chip8.shr(0x6); assert_eq!(chip8.v[0xF], 0); assert_eq!(chip8.v[0x6], 0xC8 >> 1); } #[test] fn opcode_shr_works_vf_set() { let mut chip8: Chip8 = Default::default(); chip8.v[0xD] = 0xB5; chip8.shr(0xD); assert_eq!(chip8.v[0xF], 1); assert_eq!(chip8.v[0xD], 0xB5 >> 1); } #[test] fn opcode_subn_vx_vy_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0xC] = 0x87; chip8.v[0x9] = 0x04; chip8.subn_vx_vy(0xC, 0x9); assert_eq!(chip8.v[0xC], (0x04 as u8).wrapping_sub(0x87)); assert_eq!(chip8.v[0xF], 0); } #[test] fn opcode_subn_vx_vy_works_vf_set() { let mut chip8: Chip8 = Default::default(); chip8.v[0x6] = 0x4C; chip8.v[0x4] = 0xE8; chip8.subn_vx_vy(0x6, 0x4); assert_eq!(chip8.v[0xF], 1); assert_eq!(chip8.v[0x6], 0xE8 - 0x4C); } #[test] fn opcode_shl_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0xC] = 0x47; chip8.shl(0xC); assert_eq!(chip8.v[0xC], 0x47 << 1); assert_eq!(chip8.v[0xF], 0); } #[test] fn opcode_shl_works_vf_set() { let mut chip8: Chip8 = Default::default(); chip8.v[0x7] = 0xC7; chip8.shl(0x7); assert_eq!(chip8.v[0x7], 0xC7 << 1); assert_eq!(chip8.v[0xF], 1); } #[test] fn opcode_sne_vx_vy_works() { let mut chip8: Chip8 = Default::default(); // Vx != Vy chip8.pc = 0x200; chip8.v[0x4] = 0x5F; chip8.v[0x3] = 0xF8; chip8.sne_vx_vy(0x4, 0x3); assert_eq!(chip8.pc, 0x202); // Vx == Vy chip8.pc = 0x200; chip8.v[0x4] = 0x5F; chip8.v[0x3] = 0x5F; chip8.sne_vx_vy(0x4, 0x3); assert_ne!(chip8.pc, 0x202); } #[test] fn opcode_load_addr_to_i_works() { let mut chip8: Chip8 = Default::default(); chip8.i = 0; chip8.load_addr_to_i(0x1ABC & 0x0FFF); assert_eq!(chip8.i, 0x0ABC); } #[test] fn opcode_jmp_addr_with_offset_works() { let mut chip8: Chip8 = Default::default(); chip8.pc = 0x200; chip8.v[0x0] = 0xA2; chip8.jmp_addr_with_offset(0x05D2); assert_eq!(chip8.pc, 0x05D2 + 0xA2); } #[test] fn opcode_rand_works() { // Random Value so there's no real way to test it // I don't think we can seed the RNG when we use rand::random() // However, sine we bitwise AND the random byte with another byte, // we can test that random_byte & 0x00 == 0x00 let mut chip8: Chip8 = Default::default(); chip8.v[0xE] = 0x77; // Make sure that VE is not 0x00 chip8.rand(0xE, 0x00); assert_eq!(chip8.v[0xE], 0x00); } #[test] #[ignore] fn opcode_draw_works() { // Will do this once I've rewritten the opcode for 0xDxyn todo!("TODO: Write this Opcode Test") } #[test] fn opcode_skip_on_press_works() { let mut chip8: Chip8 = Default::default(); // Key and Vx are equal chip8.pc = 0x200; chip8.v[0xD] = 0xA; chip8.key.set_key(0xA); chip8.skip_on_press(0xD); assert_eq!(chip8.pc, 0x202); // Key and Vx are **not** equal chip8.key.reset(); chip8.pc = 0x200; chip8.v[0xD] = 0xA; chip8.key.set_key(0x3); chip8.skip_on_press(0xD); assert_ne!(chip8.pc, 0x202); } #[test] fn opcode_skip_not_pressed_works() { let mut chip8: Chip8 = Default::default(); // Key and Vx are equal chip8.pc = 0x200; chip8.v[0xD] = 0xA; chip8.key.set_key(0xA); chip8.skip_not_pressed(0xD); assert_ne!(chip8.pc, 0x202); // Key and Vx are **not** equal chip8.key.reset(); chip8.pc = 0x200; chip8.v[0xD] = 0xA; chip8.key.set_key(0x3); chip8.skip_not_pressed(0xD); assert_eq!(chip8.pc, 0x202); } #[test] fn opcode_copy_delay_timer_works() { let mut chip8: Chip8 = Default::default(); chip8.delay.set(0x0F); chip8.v[0x2] = 0x00; chip8.copy_delay_timer_val(0x2); assert_eq!(chip8.v[0x2], 0x0F); } #[test] fn opcode_loop_until_key_vx_works() { let mut chip8: Chip8 = Default::default(); // Nothing Pressed chip8.pc = 0x200; chip8.v[0xC] = 0xFF; // Not a valid Key chip8.loop_until_key_vx(0xC); assert_eq!(chip8.v[0xC], 0xFF); assert_eq!(chip8.pc, 0x1FE); // Something Pressed chip8.key.set_key(0x3); chip8.pc = 0x200; chip8.v[0xC] = 0xFF; // Not a valid Key chip8.loop_until_key_vx(0xC); assert_eq!(chip8.v[0xC], 0x3); assert_eq!(chip8.pc, 0x200); } #[test] fn opcode_set_delay_to_vx_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0x1] = 0x0F; chip8.set_delay_to_vx(0x01); assert_eq!(chip8.v[0x1], chip8.delay.get()); } #[test] fn opcode_set_sound_to_vx_works() { let mut chip8: Chip8 = Default::default(); chip8.v[0x2] = 0x0F; chip8.set_sound_to_vx(0x2); assert_eq!(chip8.v[0x2], chip8.sound.get()); } #[test] fn opcode_add_vx_to_i_works() { let mut chip8: Chip8 = Default::default(); chip8.i = 0x22; chip8.v[0x6] = 0xC9; chip8.add_vx_to_i(0x6); assert_eq!(chip8.i, 0xEB); } #[test] #[ignore] fn opcode_set_i_to_hex_sprite_loc_works() { todo!("Figure out how this instruction works") } }