chip8/src/emu.rs

963 lines
25 KiB
Rust

use super::periph::{Display, Keypad};
use super::timer::{Timer, Type as TimerType};
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],
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],
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);
}
pub fn load_font_set(&mut self) {
self.memory[0x50..0xA0].copy_from_slice(&Self::FONT_SET);
}
pub fn load_rom<P: AsRef<Path>>(&mut self, path: P) -> Result<(), io::Error> {
let mut file = File::open(path.as_ref())?;
let mut rom_buf: Vec<u8> = 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::<u8>() & 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] = Timer::get(TimerType::Delay);
}
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
Timer::set(self.v[x as usize], TimerType::Delay);
}
fn set_sound_to_vx(&mut self, x: u8) {
// set sound timer to be value of Vx
Timer::set(self.v[x as usize], TimerType::Sound);
}
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;
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[0x50..0xA0];
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]
#[ignore]
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);
unimplemented!()
}
#[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]
#[ignore]
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());
unimplemented!()
}
#[test]
#[ignore]
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());
unimplemented!()
}
#[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")
}
}