more fixed tests and covered text.

This commit is contained in:
Trevor Merritt 2024-09-28 08:55:35 -04:00
parent fc62512edd
commit 731f20d894
4 changed files with 178 additions and 95 deletions

View File

@ -1,3 +1,4 @@
use std::ops::{Shl, Shr};
use imgui::ColorPicker3; use imgui::ColorPicker3;
use log::debug; use log::debug;
use rand::random; use rand::random;
@ -53,6 +54,7 @@ pub enum Chip8CpuInstructions {
LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I LdVxI(u16), // 0xFx65 Load V0 to Vx in memory starting at I
XXXXERRORINSTRUCTION, XXXXERRORINSTRUCTION,
} }
impl Chip8CpuInstructions { impl Chip8CpuInstructions {
pub fn encode(&self) -> u16 { pub fn encode(&self) -> u16 {
match self { match self {
@ -172,7 +174,7 @@ impl Chip8CpuInstructions {
let addr_param = InstructionUtil::read_addr_from_instruction(input); let addr_param = InstructionUtil::read_addr_from_instruction(input);
let byte_param = InstructionUtil::read_byte_from_instruction(input); let byte_param = InstructionUtil::read_byte_from_instruction(input);
let nibble_param = InstructionUtil::read_nibble_from_instruction(input); let nibble_param = InstructionUtil::read_nibble_from_instruction(input);
let ubln = u16::rotate_right(InstructionUtil::read_upper_byte_lower_nibble(input), 8); let ubln = InstructionUtil::read_upper_byte_lower_nibble(input);
let last_byte = input & 0xFF; let last_byte = input & 0xFF;
match input { match input {
@ -303,6 +305,8 @@ impl Chip8CpuInstructions {
0xE09E..=0xEFA1 => { 0xE09E..=0xEFA1 => {
match last_byte { match last_byte {
0x9E => { 0x9E => {
println!("DECODING {:4x}", input);
println!("UBLN: {:4x}", ubln);
Chip8CpuInstructions::SkpVx(ubln) Chip8CpuInstructions::SkpVx(ubln)
} }
0xA1 => { 0xA1 => {
@ -470,7 +474,7 @@ impl Chip8CpuInstructions {
if 0xb1 & initial_value == 1 { if 0xb1 & initial_value == 1 {
input.registers.poke(0xf, 1); input.registers.poke(0xf, 1);
} }
input.registers.poke(*x as u8, initial_value.rotate_left(1)); input.registers.poke(*x as u8, initial_value.shr(1));
} }
Chip8CpuInstructions::SubnVxVy(x, y) => { Chip8CpuInstructions::SubnVxVy(x, y) => {
// 8xy7 - SUBN Vx, Vy // 8xy7 - SUBN Vx, Vy
@ -494,7 +498,7 @@ impl Chip8CpuInstructions {
if 0x80 & initial_value == 0x80 { if 0x80 & initial_value == 0x80 {
input.registers.poke(0xf, 1); input.registers.poke(0xf, 1);
} }
input.registers.poke(*x as u8, initial_value.rotate_left(1)); input.registers.poke(*x as u8, initial_value.shl(1));
} }
Chip8CpuInstructions::SneVxVy(vx_register, vy_register) => { Chip8CpuInstructions::SneVxVy(vx_register, vy_register) => {
// 9xy0 - SNE Vx, Vy // 9xy0 - SNE Vx, Vy
@ -528,8 +532,9 @@ impl Chip8CpuInstructions {
// Cxkk - RND Vx, byte // Cxkk - RND Vx, byte
// Set Vx = random byte AND kk. // Set Vx = random byte AND kk.
// //
// The interpreter generates a random number from 0 to 255, which is then ANDed with the value kk. The results are stored in Vx. See instruction 8xy2 for more information on AND. // The interpreter generates a random number from 0 to 255,
// which is then ANDed with the value kk.
// The results are stored in Vx.
let new_value: u8 = random(); let new_value: u8 = random();
input.registers.poke(*x as u8, (new_value & *byte as u8)) input.registers.poke(*x as u8, (new_value & *byte as u8))
} }
@ -572,8 +577,6 @@ impl Chip8CpuInstructions {
// //
// Checks the keyboard, and if the key corresponding to the value of Vx is currently in the down position, PC is increased by 2. // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the down position, PC is increased by 2.
let key_to_check = input.registers.peek(*x as u8); let key_to_check = input.registers.peek(*x as u8);
} }
Chip8CpuInstructions::SnkpVx(x) => { Chip8CpuInstructions::SnkpVx(x) => {
@ -581,8 +584,6 @@ impl Chip8CpuInstructions {
// Skip next instruction if key with the value of Vx is not pressed. // Skip next instruction if key with the value of Vx is not pressed.
// //
// Checks the keyboard, and if the key corresponding to the value of Vx is currently in the up position, PC is increased by 2. // Checks the keyboard, and if the key corresponding to the value of Vx is currently in the up position, PC is increased by 2.
} }
Chip8CpuInstructions::LdVxDt(x) => { Chip8CpuInstructions::LdVxDt(x) => {
// Fx07 - LD Vx, DT // Fx07 - LD Vx, DT
@ -598,15 +599,17 @@ impl Chip8CpuInstructions {
// //
// All execution stops until a key is pressed, then the value of that key is stored in Vx. // All execution stops until a key is pressed, then the value of that key is stored in Vx.
} }
Chip8CpuInstructions::LdDtVx(new_time) => { Chip8CpuInstructions::LdDtVx(source_register) => {
// Fx15 - LD DT, Vx // Fx15 - LD DT, Vx
// Set delay timer = Vx. // Set delay timer = Vx.
// //
// DT is set equal to the value of Vx. // DT is set equal to the value of Vx.
println!("SETTING DELAY TIMER TO [{}]", *new_time); let new_time = input.registers.peek(*source_register as u8);
input.delay_timer.set_timer(*new_time as i32); println!("SETTING DELAY TIMER TO [{}]", new_time);
input.delay_timer.set_timer(new_time as i32);
} }
Chip8CpuInstructions::LdStVx(new_time) => { Chip8CpuInstructions::LdStVx(new_time) => {
println!("SETTING SOUND TIMER TO [0x{:2x}]", *new_time);
input.sound_timer.set_timer(*new_time as i32); input.sound_timer.set_timer(*new_time as i32);
} }
Chip8CpuInstructions::AddIVx(x) => { Chip8CpuInstructions::AddIVx(x) => {
@ -623,14 +626,12 @@ impl Chip8CpuInstructions {
// Set I = location of sprite for digit Vx. // Set I = location of sprite for digit Vx.
// //
// The value of I is set to the location for the hexadecimal sprite corresponding to the value of Vx. See section 2.4, Display, for more information on the Chip-8 hexadecimal font. // The value of I is set to the location for the hexadecimal sprite corresponding to the value of Vx. See section 2.4, Display, for more information on the Chip-8 hexadecimal font.
} }
Chip8CpuInstructions::LdBVx(x) => { Chip8CpuInstructions::LdBVx(x) => {
// Fx33 - LD B, Vx // Fx33 - LD B, Vx
// Store BCD representation of Vx in memory locations I, I+1, and I+2. // Store BCD representation of Vx in memory locations I, I+1, and I+2.
// //
// The interpreter takes the decimal value of Vx, and places the hundreds digit in memory at location in I, the tens digit at location I+1, and the ones digit at location I+2. // The interpreter takes the decimal value of Vx, and places the hundreds digit in memory at location in I, the tens digit at location I+1, and the ones digit at location I+2.
} }
Chip8CpuInstructions::LdIVx(x) => { Chip8CpuInstructions::LdIVx(x) => {
// Store registers V0 through Vx in memory starting at location I. // Store registers V0 through Vx in memory starting at location I.
@ -741,6 +742,7 @@ mod test {
assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd))); assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::LdBVx(0xd)));
assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe))); assert!(matches!(Chip8CpuInstructions::decode(0xfe55), Chip8CpuInstructions::LdIVx(0xe)));
assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3))); assert!(matches!(Chip8CpuInstructions::decode(0xf365), Chip8CpuInstructions::LdVxI(0x3)));
} }
/// START OF THE EXECUTION TESTS /// START OF THE EXECUTION TESTS
@ -755,9 +757,13 @@ mod test {
Chip8CpuInstructions::SysAddr(0x0AF).execute(&mut x); Chip8CpuInstructions::SysAddr(0x0AF).execute(&mut x);
assert_eq!(x.registers.peek_pc(), 0x0AF); assert_eq!(x.registers.peek_pc(), 0x0AF);
} }
fn cls_test() { fn cls_test() {
// * 0x00E0 Clear Screen // * 0x00E0 Clear Screen
// todo: Need to write this // todo: Need to write this
let mut x = Chip8Computer::new();
} }
fn ret_test() { fn ret_test() {
@ -875,7 +881,6 @@ mod test {
Chip8CpuInstructions::LdVxVy(0x01, 0x02).execute(&mut x); Chip8CpuInstructions::LdVxVy(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x02); assert_eq!(x.registers.peek(1), 0x02);
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x206);
} }
#[test] #[test]
@ -966,44 +971,65 @@ mod test {
} }
*/ */
#[test]
fn ShrVxVy_test() { fn ShrVxVy_test() {
/* /*
Set Vx = Vx SHR 1. Set Vx = Vx SHR 1.
If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2. If the least-significant bit of Vx is 1, then VF is set to 1, otherwise 0. Then Vx is divided by 2.
*/ */
let x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00); Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0x08); // 0b0000 1000 (0x08) Chip8CpuInstructions::LdVxByte(0x1, 0x08).execute(&mut x); // 0b0000 1000 (0x08)
Chip8CpuInstructions::LdVxByte(0x2, 0x2); Chip8CpuInstructions::LdVxByte(0x2, 0x2).execute(&mut x);
Chip8CpuInstructions::ShrVxVy(0x1, 0x2); // 0b0000 0010 (0x02) (Not Set) Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Not Set)
assert_eq!(x.registers.peek(1), 0x02); assert_eq!(x.registers.peek(1), 0x04);
assert_eq!(x.registers.peek(0xf), 0); assert_eq!(x.registers.peek(0xf), 0);
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x208);
let x = Chip8Computer::new(); let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0xf, 0x00); Chip8CpuInstructions::LdVxByte(0xf, 0x00).execute(&mut x);
Chip8CpuInstructions::LdVxByte(0x1, 0x09); // 0b0000 1001 (0x09) Chip8CpuInstructions::LdVxByte(0x1, 0b00001001).execute(&mut x); // 0b0000 1001 (0x09)
Chip8CpuInstructions::LdVxByte(0x2, 0x2); Chip8CpuInstructions::ShrVxVy(0x1, 0x2).execute(&mut x); // 0b0000 0100 (0x02) (Set)
Chip8CpuInstructions::ShrVxVy(0x1, 0x2); // 0b0000 0010 (0x02) (Set) Chip8CpuInstructions::ShrVxVy(0x1, 0x1).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x02); assert_eq!(x.registers.peek(0x1), 0b00000010);
assert_eq!(x.registers.peek(0xf), 1); assert_eq!(x.registers.peek(0xf), 0x1);
assert_eq!(x.registers.peek_pc(), 0x206); assert_eq!(x.registers.peek_pc(), 0x208);
} }
fn SneVxVy_test() {} fn SneVxVy_test() {}
fn LdiAddr_test() {} fn LdiAddr_test() {}
fn JpV0Addr_test() {} fn JpV0Addr_test() {}
fn RndVxByte_test() {}
#[test]
fn RndVxByte_test() {
let mut x = Chip8Computer::new();
// generate random number masked by 0xF0;
let mask = 0xF0u8;
Chip8CpuInstructions::RndVxByte(0x0, mask as u16).execute(&mut x);
let register_value = x.registers.peek(0x0);
assert!(register_value < mask);
// generate random number masked by 0x0F;
let mask2 = 0x0Fu8;
Chip8CpuInstructions::RndVxByte(0x1, mask2 as u16).execute(&mut x);
let register_value = x.registers.peek(0x1);
assert!(register_value < mask);
}
fn DrawVxVyNibble_test() {} fn DrawVxVyNibble_test() {}
fn SkpVx_test() { fn SkpVx_test() {
// skip if key pressed // skip if key pressed
} }
fn SnKpVx_test() { fn SnKpVx_test() {
// skip key not pressed // skip key not pressed
} }
#[test] #[test]
fn LdVxDt_test() { fn LdVxDt_test() {
// delay timer reading // delay timer reading
@ -1028,13 +1054,34 @@ mod test {
fn LdVxK_test() { fn LdVxK_test() {
// Wait for a key press, store the value of the key in Vx. // Wait for a key press, store the value of the key in Vx.
// All execution stops until a key is pressed, then the value of that key is stored in Vx. // All execution stops until a key is pressed, then the value of that key is stored in Vx.
} }
#[test]
fn LdStVx_test() { fn LdStVx_test() {
// sound timer setting // sound timer setting
let mut x = Chip8Computer::new();
Chip8CpuInstructions::LdVxByte(0x1, 0x10).execute(&mut x);
Chip8CpuInstructions::LdStVx(0x10).execute(&mut x);
// tick from 0x8 to 0x1
for i in 0..6 { x.sound_timer.tick(); }
assert_eq!(x.sound_timer.current(), 0xA);
} }
fn LdIVx_test() {}
fn LdIVx_test() {
// Store registers V0 through Vx in memory starting at location I.
//
// The interpreter copies the values of registers V0 through Vx
// into memory, starting at the address in I.
}
fn LdVxI_test() {} fn LdVxI_test() {}
// Read registers V0 through Vx from memory starting at location I.
//
// The interpreter reads values from memory starting at location I into registers V0 through Vx.
/* /*
#[test] #[test]
fn LdDtVx_test() { fn LdDtVx_test() {

View File

@ -56,4 +56,7 @@ impl Chip8Registers {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
#[test]
fn smoke() { assert!(true) }
} }

View File

@ -19,6 +19,7 @@ impl SoundTimer {
} }
pub fn tick(&mut self) { pub fn tick(&mut self) {
println!("TICKING SOUND FROM {} to {}", self.counter, self.counter - 1);
if self.counter > 0 { if self.counter > 0 {
self.counter -= 1; self.counter -= 1;
/* /*

View File

@ -1,6 +1,25 @@
pub struct InstructionUtil {} pub struct InstructionUtil {}
impl InstructionUtil { impl InstructionUtil {
pub fn byte_to_bools(to_convert: u8) -> [bool; 8] {
let mut return_values = [false; 8];
for i in 0..8 {
let new_value = to_convert >> i & 0x1 == 1;
return_values[i as usize] = new_value;
}
return_values
}
pub fn bools_to_byte(to_convert: [bool; 8]) -> u8 {
let mut return_value = 0u8;
for i in 0..to_convert.len() {
let new_bit = 0x1 << i;
if to_convert[i] {
return_value = return_value | new_bit
}
}
return_value
}
pub fn split_bytes(to_split: u16) -> (u8, u8) { pub fn split_bytes(to_split: u16) -> (u8, u8) {
let high = to_split.rotate_left(8) as u8; let high = to_split.rotate_left(8) as u8;
@ -40,11 +59,10 @@ impl InstructionUtil {
} }
pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u16 { pub fn read_upper_byte_lower_nibble(to_read_from: u16) -> u16 {
to_read_from & 0x0f00 (to_read_from & 0x0f00) >> 8
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
@ -83,6 +101,20 @@ mod test {
#[test] #[test]
fn ubln() { fn ubln() {
// from 0xABCD we should see B // from 0xABCD we should see B
assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0xABCD), 0xB << 8); assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0xABCD), 0xB);
assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0123), 0x1);
assert_eq!(InstructionUtil::read_upper_byte_lower_nibble(0x0000), 0x0);
}
#[test]
fn byte_to_bool_changes() {
assert_eq!(InstructionUtil::byte_to_bools(0b00000000), [false, false, false, false, false, false, false, false]);
assert_eq!(InstructionUtil::byte_to_bools(0b11111111), [true, true, true, true, true, true, true, true]);
assert_eq!(InstructionUtil::byte_to_bools(0b11001100), [false, false, true, true, false, false, true, true]);
assert_eq!(InstructionUtil::byte_to_bools(0b11110000), [false, false, false, false, true, true, true, true]);
assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, false, false, false, false]), 0b00000000);
assert_eq!(InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]), 0b11111111);
assert_eq!(InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]), 0b11001100);
assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]), 0b11110000);
} }
} }