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 { opcode: u16, i: u16, pc: u16, sp: u8, key: Keypad, v: [u8; 16], stack: [u16; 16], memory: [u8; 4096], delay: Timer, sound: Timer, pub display: Display, } impl Default for Chip8 { fn default() -> Self { let mut chip8 = Chip8 { opcode: 0, i: 0, pc: 0x200, // Progrm counter starts at 0x200 sp: 0, key: Default::default(), v: [0; 16], stack: [0; 16], memory: [0; 4096], delay: Default::default(), sound: Default::default(), display: Display::default(), }; 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) { self.opcode = self.get_opcode(); println!("{:#x}", self.opcode); self.handle_opcode(); self.delay.tick(); self.sound.tick(); } pub fn load_font_set(&mut self) { for (i, byte) in Self::FONT_SET.iter().enumerate() { self.memory[i] = *byte; } } 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)?; for (i, byte) in rom_buf.iter().enumerate() { self.memory[i + 0x200] = *byte; } 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 handle_opcode(&mut self) { // Given: 0xA2F0 let nib_1 = (self.opcode & 0xF000) >> 12; //0xA let nib_2 = (self.opcode & 0x0F00) >> 8; // 0x2 let nib_3 = (self.opcode & 0x00F0) >> 4; // 0xF let nib_4 = self.opcode & 0x000F; // 0x0 self.pc += 2; // nib_ns are u16s so we waste 4 bytes here. match (nib_1, nib_2, nib_3, nib_4) { // CLS (0x0, 0x0, 0xE, 0x0) => self.cls(), // 00EE (0x0, 0x0, 0xE, 0xE) => self.ret(), // 1NNN (0x1, _, _, _) => self.jmp_addr(nib_2, nib_3, nib_4), // 2NNN (0x2, _, _, _) => self.call_addr(nib_2, nib_3, nib_4), // 3XKK (0x3, _, _, _) => self.se_vx_byte(nib_2, nib_3, nib_4), // 4xkk (0x4, _, _, _) => self.sne_vx_byte(nib_2, nib_3, nib_4), // 5xy0 (0x5, _, _, 0x0) => self.se_vx_vy(nib_2, nib_3), // 6xkk (0x6, _, _, _) => self.ld_vx_byte(nib_2, nib_3, nib_4), // 7xkk (0x7, _, _, _) => self.add_vx_byte(nib_2, nib_3, nib_4), // 8xy0 (0x8, _, _, 0x0) => self.ld_vx_vy(nib_2, nib_3), // 8xy1 (0x8, _, _, 0x1) => self.or_vx_vy(nib_2, nib_3), // 8xy2 (0x8, _, _, 0x2) => self.and_vx_vy(nib_2, nib_3), // 8xy3 (0x8, _, _, 0x3) => self.xor_vx_vy(nib_2, nib_3), // 8xy4 (0x8, _, _, 0x4) => self.add_vx_vy(nib_2, nib_3), // 8xy5 (0x8, _, _, 0x5) => self.sub_vx_vy(nib_2, nib_3), // 8xy6 (0x8, _, _, 0x6) => self.shr_vx(nib_2), // 8xy7 (0x8, _, _, 0x7) => self.subn_vx_vy(nib_2, nib_3), // 8xyE (0x8, _, _, 0xE) => self.shl_vx(nib_2), // 9xy0 (0x9, _, _, 0x0) => self.sne_vx_vy(nib_2, nib_3), // Annn (0xA, _, _, _) => self.ld_i_addr(nib_2, nib_3, nib_4), // Bnnn (0xB, _, _, _) => self.jmp_v0_addr(nib_2, nib_3, nib_4), // Cxkk (0xC, _, _, _) => self.rnd_vx_byte(nib_2, nib_3, nib_4), //Dxyn (0xD, _, _, _) => self.drw_vx_vy_nib(nib_2, nib_3, nib_4), // Ex9E (0xE, _, 0x9, 0xE) => self.skp_vx(nib_2), // ExA1 (0xE, _, 0xA, 0x1) => self.sknp_vx(nib_2), // Fx07 (0xF, _, 0x0, 0x7) => self.ld_vx_dt(nib_2), // Fx0A (0xF, _, 0x0, 0xA) => self.ld_vx_k(nib_2), // Fx15 (0xF, _, 0x1, 0x15) => self.ld_dt_vx(nib_2), // Fx18 (0xF, _, 0x1, 0x8) => self.ld_st_vx(nib_2), // Fx1E (0xF, _, 0x1, 0xE) => self.add_i_vx(nib_2), // Fx29 (0xF, _, 0x2, 0x9) => self.ld_f_vx(nib_2), // Fx33 (0xF, _, 0x3, 0x3) => self.ld_b_vx(nib_2), // Fx55 (0xF, _, 0x5, 0x5) => self.ld_i_vx(nib_2), // Fx65 (0xF, _, 0x6, 0x5) => self.ld_vx_i(nib_2), // General Case _ => println!("UNIMPLEMENTED OPCODE: {:#x}", self.opcode), } } fn cls(&mut self) { // Clear the display self.display.clear(); } fn jmp_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) { // sets the program counter to addr (nnn) self.pc = Self::convert_to_addr(n_1, n_2, n_3); println!("{:#x}", self.pc); } fn ret(&mut self) { // sets the program counter to the addres 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 call_addr(&mut self, n_1: u16, n_2: u16, n_3: 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 = Self::convert_to_addr(n_1, n_2, n_3); } fn se_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) { // compares Vx to kk. If they are equal, pc is incremented by 2 if self.v[x as usize] == Self::convert_to_byte(k_1, k_2) { self.pc += 2; } } fn sne_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) { // compares Vx to kk. If they are **not** equal, pc is incremented by 2 if self.v[x as usize] != Self::convert_to_byte(k_1, k_2) { self.pc += 2; } } fn se_vx_vy(&mut self, x: u16, y: u16) { // 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: u16, k_1: u16, k_2: u16) { // put value kk into Vx self.v[x as usize] = Self::convert_to_byte(k_1, k_2); } fn add_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) { // calculate Vx + kk, then store it in Vx let x = x as usize; self.v[x] = self.v[x].wrapping_add(Self::convert_to_byte(k_1, k_2)); } fn ld_vx_vy(&mut self, x: u16, y: u16) { // store Vy in Vx self.v[x as usize] = self.v[y as usize]; } fn or_vx_vy(&mut self, x: u16, y: u16) { // calc bitwise OR on Vx and Vy, then store in Vx let x = x as usize; self.v[x] = self.v[x] | self.v[y as usize]; } fn and_vx_vy(&mut self, x: u16, y: u16) { // calc bitwise AND on Vx and Vy, then store in Vx let x = x as usize; self.v[x] = self.v[x] & self.v[y as usize]; } fn xor_vx_vy(&mut self, x: u16, y: u16) { // calc bitwise XOR on Vx and Vy, then store in Vx let x = x as usize; self.v[x] = self.v[x] ^ self.v[y as usize]; } fn add_vx_vy(&mut self, x: u16, y: u16) { // 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 (res, did_overflow) = self.v[x].overflowing_add(self.v[y as usize]); self.v[0xF as usize] = if did_overflow { 1 } else { 0 }; self.v[x] = res; } fn sub_vx_vy(&mut self, x: u16, y: u16) { // subtract Vx and Vy, if Vx > Vy VF is set to 1, otherwise 0 // then set Vx to Vx - Vy let vx = self.v[x as usize]; let vy = self.v[y as usize]; self.v[0xF as usize] = if vx > vy { 1 } else { 0 }; self.v[x as usize] = vx.wrapping_sub(vy); } fn shr_vx(&mut self, x: u16) { // 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 as usize] = if (self.v[x] & 1) == 1 { 1 } else { 0 }; self.v[x] = self.v[x] >> 1; } fn subn_vx_vy(&mut self, x: u16, y: u16) { // subtract Vy and Vx, if Vy > Vx VF is set to 1, otherwise 0 // then set Vx = Vy - Vx let vx = self.v[x as usize]; let vy = self.v[y as usize]; self.v[0xF as usize] = if vy > vx { 1 } else { 0 }; self.v[x as usize] = vy - vx; } fn shl_vx(&mut self, x: u16) { // 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 as usize] = if (self.v[x] & 0x80) == 1 { 1 } else { 0 }; self.v[x] = self.v[x] << 1; } fn sne_vx_vy(&mut self, x: u16, y: u16) { // if Vx != vy program counter is increased by 2 if self.v[x as usize] != self.v[y as usize] { self.pc += 2; } } fn ld_i_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) { // set i to addr self.i = Self::convert_to_addr(n_1, n_2, n_3); } fn jmp_v0_addr(&mut self, n_1: u16, n_2: u16, n_3: u16) { // set program counter to addr + V0 self.pc = Self::convert_to_addr(n_1, n_2, n_3) + self.v[0 as usize] as u16; } fn rnd_vx_byte(&mut self, x: u16, k_1: u16, k_2: u16) { // 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::() & Self::convert_to_byte(k_1, k_2); } fn drw_vx_vy_nib(&mut self, x: u16, y: u16, nib: u16) { // read n bytes from memory starting from self.i // then display them starting at (vx, vy) let i = self.i as usize; let disp_x = self.v[x as usize]; let disp_y = self.v[y as usize]; let mut display_buf = self.display.buf; let mut pixel: u8; self.v[0xF as usize] = 0; // TODO: Rewrite Your Solution to better fit // the rest of your code base. // http://www.multigesture.net/articles/how-to-write-an-emulator-chip-8-interpreter/ for i in 0..(nib as usize) { // height of sprite (y-axis) pixel = self.memory[self.i as usize + i]; for j in 0..8 { // Loop over the 8 bits (x-axis) if (pixel & (0x80 >> j)) != 0 { let index = (disp_x as usize) + j + (((disp_y as usize) + i) * 64); if display_buf[index] == 1 { self.v[0xF as usize] = 1; } display_buf[index] ^= 1; } } } } fn skp_vx(&mut self, x: u16) { // if current key is the same as the on in Vx // program counter is increased by 2 if let Some(key) = self.key.get_pressed() { if self.v[x as usize] == key { self.pc += 2; } } } fn sknp_vx(&mut self, x: u16) { // if current key is not the sameas the one in Vx // increment the program counter by 2 match self.key.get_pressed() { Some(key) => { if self.v[x as usize] != key { self.pc += 2; } } None => self.pc += 2, } } fn ld_vx_dt(&mut self, x: u16) { // set Vx to be the value of the delay timer self.v[x as usize] = self.delay.get(); } fn ld_vx_k(&mut self, x: u16) { // wait (blocking) until a key is pressed // once pressed, store in Vx match self.key.get_pressed() { Some(key) => self.v[x as usize] = key, None => self.pc -= 2, } } fn ld_dt_vx(&mut self, x: u16) { // set delay timer to be value of Vx self.delay.set(self.v[x as usize]); } fn ld_st_vx(&mut self, x: u16) { // set sound timer to be value of Vx self.delay.set(self.v[x as usize]); } fn add_i_vx(&mut self, x: u16) { // set I to be I + Vx self.i = self.i + self.v[x as usize] as u16; } fn ld_f_vx(&mut self, x: u16) { // set I to location of hex sprite related to Vx self.i = self.v[x as usize] as u16 * 5; } fn ld_b_vx(&mut self, x: u16) { // 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 mut iter = Self::digits(self.v[x as usize] as usize); let ones = iter.next().unwrap(); // Ther has to at least be a ones lol let tens = iter.next().unwrap_or(0); let hundreds = iter.next().unwrap_or(0); self.memory[i] = hundreds as u8; self.memory[i + 1] = tens as u8; self.memory[i + 2] = ones as u8; } fn ld_i_vx(&mut self, x: u16) { // 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 ld_vx_i(&mut self, x: u16) { // read what will be values of v0 -> vx from memory starting at i let x = x as usize; for n in 0..=x { self.v[n] = self.memory[self.i as usize + n]; } } fn convert_to_byte(k_1: u16, k_2: u16) -> u8 { ((k_1 as u8) << 4) | (k_2 as u8) } fn convert_to_addr(n_1: u16, n_2: u16, n_3: u16) -> u16 { (n_1 << 8) | (n_2 << 4) | n_3 } // https://stackoverflow.com/questions/41536479/how-do-i-split-an-integer-into-individual-digits fn digits(mut num: usize) -> impl Iterator { let mut divisor = 1; while num >= divisor * 10 { divisor *= 10; } std::iter::from_fn(move || { if divisor == 0 { None } else { let v = num / divisor; num %= divisor; divisor /= 10; Some(v) } }) } }