richer test suite
Updates imgui to have target ips and limits correctly.
Initial IPS = 1000, plan to expose to user
More tests for encode/decode of instructions
Adding text to instruction parsing
This commit is contained in:
2024-10-28 14:42:14 -04:00
parent 1694157e27
commit b492eb5f49
25 changed files with 1347 additions and 239 deletions
+7 -2
View File
@@ -1,6 +1,7 @@
use log::{debug};
use crate::chip8::delay_timer::DelayTimer;
use crate::chip8::keypad::Keypad;
use crate::chip8::quirk_modes::QuirkMode;
use crate::chip8::registers::Chip8Registers;
use crate::chip8::sound_timer::SoundTimer;
use crate::chip8::stack::Chip8Stack;
@@ -19,7 +20,8 @@ pub struct Chip8Computer {
pub video_memory: Chip8Video,
pub state: Chip8CpuStates,
pub keypad: Keypad,
pub stack: Chip8Stack
pub stack: Chip8Stack,
pub quirk_mode: QuirkMode
}
impl Default for Chip8Computer {
fn default() -> Self {
@@ -32,7 +34,8 @@ impl Default for Chip8Computer {
delay_timer: DelayTimer::new(),
state: Chip8CpuStates::default(),
keypad: Keypad::default(),
stack: Chip8Stack::default()
stack: Chip8Stack::default(),
quirk_mode: QuirkMode::default()
}
}
}
@@ -45,6 +48,8 @@ impl Chip8Computer {
self.delay_timer.reset();
self.sound_timer.reset();
self.stack.reset();
self.memory.reset();
self.quirk_mode = QuirkMode::Chip8;
}
pub fn dump_keypad_to_string(&self) -> String {
+8 -6
View File
@@ -23,7 +23,7 @@ impl Default for Chip8ComputerManager {
fn default() -> Self {
Chip8ComputerManager {
core_should_run: false,
one_step: true,
one_step: false,
core_cycle_timer: false,
core_last_cycle_start: Instant::now() ,
computer: Chip8Computer::new()
@@ -32,18 +32,16 @@ impl Default for Chip8ComputerManager {
}
impl Chip8ComputerManager {
pub fn reset(&mut self) {
self.one_step = false;
self.core_should_run = false;
self.computer.reset();
}
pub fn new() -> Chip8ComputerManager {
let core_handle = thread::spawn(move || {
loop {
let start_time = Instant::now();
// println!("Core Thread starting at {start_time:?}");
let sleep_time = Instant::now().duration_since(start_time).as_millis();
// println!("Core Thread sleeping for {sleep_time}ms");
sleep(Duration::from_millis((16 - sleep_time) as u64));
}
});
@@ -67,13 +65,15 @@ impl Chip8ComputerManager {
&self.computer
}
pub fn tick( &mut self) {
pub fn tick( &mut self) -> bool {
// println!("STARTING TICK");
let mut did_tick: bool = false;
if self.one_step | self.core_should_run {
match self.computer.state {
WaitingForInstruction => {
self.core_last_cycle_start = Instant::now();
self.computer.step_system();
did_tick = true
// println!("SYSTEM STEP");
}
_ => {}
@@ -84,7 +84,9 @@ impl Chip8ComputerManager {
// stop the CPU for the next cycle, we are only
// wanting one step.
self.one_step = false;
did_tick = true;
}
did_tick
}
pub fn press_key(&mut self, key_index: u8) {
+4 -4
View File
@@ -1,10 +1,10 @@
#[derive(Clone, Copy)]
pub struct DelayTimer {
counter: i32
counter: u8
}
impl DelayTimer {
pub fn current(&self) -> i32 {
pub fn current(&self) -> u8 {
self.counter
}
@@ -18,8 +18,8 @@ impl DelayTimer {
self.counter = 0xff;
}
pub fn set_timer(&mut self, new_value: i32) {
self.counter = new_value
pub fn set_timer(&mut self, new_value: u8) {
self.counter = new_value as u8
}
pub fn tick(&mut self) {
+136 -22
View File
@@ -2,11 +2,12 @@ use std::arch::x86_64::_mm_xor_pd;
use std::fmt::{Debug, Display, Formatter};
use std::ops::{BitAnd, Deref, Shr};
use std::time::Instant;
use chrono::ParseMonthError;
use log::debug;
use rand::{random, Rng};
use crate::chip8::computer::{Chip8Computer};
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;
use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION;
use crate::chip8::instructions::Chip8CpuInstructions::*;
use crate::chip8::util::InstructionUtil;
use crate::constants::{*};
@@ -188,7 +189,7 @@ pub enum Chip8CpuInstructions {
/// Set sound timer = Vx.
///
/// ST is set equal to the value of Vx.
LDI_S(u8),
LDIS(u8),
/// Fx1E - ADD I, Vx
/// Set I = I + Vx.
///
@@ -280,7 +281,7 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::LDIA(_) => INST_LDIA,
Chip8CpuInstructions::LDIX(_) => INST_LDIX,
Chip8CpuInstructions::LIDR(_) => INST_LIDR,
Chip8CpuInstructions::LDI_S(_) => INST_LIDS,
Chip8CpuInstructions::LDIS(_) => INST_LDIS,
Chip8CpuInstructions::LDR(_, _) => INST_LDR,
Chip8CpuInstructions::LDRD(_) => INST_LDRD,
Chip8CpuInstructions::LDRI(_) => INST_LDRI,
@@ -327,6 +328,7 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::ADD(x, byte) => {
format!("0x{x:02x}, 0x{byte:02x}")
}
// Reg, Reg
Chip8CpuInstructions::SEY(x, y) |
Chip8CpuInstructions::LDR_Y(x, y) |
Chip8CpuInstructions::OR(x, y) |
@@ -338,50 +340,162 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::SUBC(x, y) |
Chip8CpuInstructions::SHL(x, y) |
Chip8CpuInstructions::SNEY(x, y) => {
format!("0x{x:02x}, 0x{y:02x}")
format!("0x{x:01x}, 0x{y:01x}")
}
// Reg, Reg, Nibble
Chip8CpuInstructions::DRW(x, y, nibble) => {
format!("0x{x:02x}, 0x{y:02x}, 0x{nibble:02x}")
}
// Registers. 0-F
Chip8CpuInstructions::LDD(x) |
Chip8CpuInstructions::LDI_S(x) |
Chip8CpuInstructions::LDIS(x) |
Chip8CpuInstructions::ADDI(x) |
Chip8CpuInstructions::LDFX(x) |
Chip8CpuInstructions::BCD(x) |
Chip8CpuInstructions::LDIX(x) |
Chip8CpuInstructions::SKP(x) |
Chip8CpuInstructions::LDRD(x) |
Chip8CpuInstructions::LDRK(x) |
Chip8CpuInstructions::LDRI(x) |
Chip8CpuInstructions::LDF2(x) |
Chip8CpuInstructions::STR(x) |
Chip8CpuInstructions::LIDR(x) |
Chip8CpuInstructions::SDN(x) |
Chip8CpuInstructions::SKNP(x) |
Chip8CpuInstructions::SKP(x) => {
format!("0x{x:02x}")
}
_ => { String::new() }
Chip8CpuInstructions::EXIT |
Chip8CpuInstructions::ENA |
Chip8CpuInstructions::DIS |
Chip8CpuInstructions::SLF |
Chip8CpuInstructions::XXXXERRORINSTRUCTION |
Chip8CpuInstructions::SRT |
Chip8CpuInstructions::CLS |
Chip8CpuInstructions::RET => {
String::new()
}
}
}
}
impl Display for Chip8CpuInstructions {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} {}", self.name(), self.operands())
let ops = self.operands();
let space = if ops.is_empty() { "" } else { " " };
write!(f, "{}{}{}", self.name(), space, ops)
}
}
impl Chip8CpuInstructions {
pub fn from_str(input: &str) -> Chip8CpuInstructions {
let parts = input.split(" ");
println!("THERE ARE {} PARTS", parts.count());
XXXXERRORINSTRUCTION
//
// match input.to_uppercase().as_str() {
// INST_ADDI => Chip8CpuInstructions::ADDI(parts.nth(1)),
// INST_ADD => Chip8CpuInstructions::ADD(parts[1], parts[2]),
// INST_ADDR => Chip8CpuInstructions::ADDR(parts[1], parts[2]),
// _ => XXXXERRORINSTRUCTION
// }
let mut parts = input.split(" ");
// print!("THERE ARE {} PARTS", parts.clone().count());
let first_part = parts.next().unwrap_or("");
// take the next value...
// ...strip off the extra...
// ...convert it to an integer from base 16
let param1 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0);
let param2 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0);
let param3 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0);
// println!("\tFirst part is {:?} / {:?} / {:?} / {:?}", first_part, param1 ,param2 ,param3);
match first_part {
INST_CLS => {
CLS
}
INST_DRW => {
DRW(param1 as u8, param2 as u8, param3 as u8)
}
INST_ADD => {
ADD(param1 as u8, param2 as u8)
}
INST_CALL => {
CALL(param1)
}
INST_SYS => {
SYS(param1)
}
INST_RET => {
RET
}
INST_JPA => {
JPA(param1)
}
INST_JPI => {
JPI(param1)
}
INST_SEX => {
SEX(param1 as u8, param2 as u8)
}
INST_SNEB => {
SNEB(param1 as u8, param2 as u8)
}
INST_SDN => {
SDN(param1 as u8)
}
INST_SRT => {
SRT
}
INST_SLF => {
SLF
}
INST_EXIT => {
EXIT
}
INST_DIS => {
DIS
}
INST_ENA => {
ENA
}
INST_SEY => {
SEY(param1 as u8, param2 as u8)
}
INST_LDRY => {
LDR_Y(param1 as u8, param2 as u8)
}
INST_LDR => {
LDR(param1 as u8, param2 as u8)
}
INST_OR => {
OR(param1 as u8, param2 as u8)
}
INST_AND => {
AND(param1 as u8, param2 as u8)
}
INST_ORY => {
ORY(param1 as u8, param2 as u8)
}
INST_ADDR => {
ADDR(param1 as u8, param2 as u8)
}
INST_SUB => {
SUB(param1 as u8, param2 as u8)
}
INST_SHR => {
SHR(param1 as u8, param2 as u8)
}
INST_SHL => {
SHL(param1 as u8, param2 as u8)
}
INST_SUBC => {
SUBC(param1 as u8, param2 as u8)
}
INST_SNEY => {
SNEY(param1 as u8, param2 as u8)
}
INST_LDIA => {
LDIA(param1)
}
INST_RND => {
RND(param1 as u8, param2 as u8)
}
INST_DRW => {
DRW(param1 as u8, param2 as u8, param3 as u8)
}
_ => {
XXXXERRORINSTRUCTION
}
}
}
pub fn encode(&self) -> u16 {
@@ -417,7 +531,7 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::LDRD(x_register) => 0xF007 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LDRK(x_register) => 0xF00A | ((*x_register as u16) << 8),
Chip8CpuInstructions::LDD(x_register) => 0xF015 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LDI_S(x_register) => 0xF018 | ((*x_register as u16) << 8),
Chip8CpuInstructions::LDIS(x_register) => 0xF018 | ((*x_register as u16) << 8),
Chip8CpuInstructions::ADDI(x_register) => 0xF01E | ((*x_register as u16) << 8),
Chip8CpuInstructions::LDFX(x_register) => 0xF029 | ((*x_register as u16) << 8),
Chip8CpuInstructions::BCD(x_register) => 0xF033 | ((*x_register as u16) << 8),
@@ -488,7 +602,7 @@ impl Chip8CpuInstructions {
0x07 => Chip8CpuInstructions::LDRD(ubln),
0x0A => Chip8CpuInstructions::LDRK(ubln),
0x15 => Chip8CpuInstructions::LDD(ubln),
0x18 => Chip8CpuInstructions::LDI_S(ubln),
0x18 => Chip8CpuInstructions::LDIS(ubln),
0x1E => Chip8CpuInstructions::ADDI(ubln),
0x29 => Chip8CpuInstructions::LDFX(ubln),
0x30 => Chip8CpuInstructions::LDF2(ubln),
@@ -816,9 +930,9 @@ impl Chip8CpuInstructions {
//
// DT is set equal to the value of Vx.
let new_time = input.registers.peek(*source_register as u8);
input.delay_timer.set_timer(new_time as i32);
input.delay_timer.set_timer(new_time);
}
Chip8CpuInstructions::LDI_S(new_time) => {
Chip8CpuInstructions::LDIS(new_time) => {
let new_value = input.registers.peek(*new_time as u8);
input.sound_timer.set_timer(new_value as i32);
}
+11 -9
View File
@@ -11,15 +11,17 @@ impl Keypad {
// draw a 4x4 grid showing the keys with * filling the cells that are depressed
for row in CHIP8_KEYBOARD.iter() {
for (index, key) in row.iter().enumerate() {
let is_lit = if self.keys[*key as usize] { "*".to_string() } else { char::from_digit(*key as u32, 16).unwrap_or(' ').to_string() };
match index {
3 => {
// last in col
return_value += format!("|{}|\n", is_lit).as_str();
}
_=> {
return_value += format!("|{}", is_lit).as_str();
}
let is_lit = if self.keys[*key as usize] {
"*".to_string()
} else {
char::from_digit(*key as u32, 16).unwrap_or(' ').to_string()
};
if index == 3 {
return_value += format!("|{}|\n", is_lit).as_str();
} else {
return_value += format!("|{}", is_lit).as_str();
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#[derive(Default, Clone)]
pub enum QuirkMode {
#[default]
Chip8,
SChipLegacy,
XOChip,
SChipModern
}
+6
View File
@@ -20,6 +20,12 @@ impl Default for Chip8SystemMemory {
}
}
impl Chip8SystemMemory {
pub fn reset(&mut self){
self.memory = [0x00; CHIP8_MEMORY_SIZE as usize];
self.load_fonts_to_memory();
self.load_schip_fonts_to_memory();
}
pub fn new() -> Self {
Chip8SystemMemory {
+2 -3
View File
@@ -136,10 +136,9 @@ impl Chip8Video {
(CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)
}
}
fn get_memory_size(&self) -> i32 {
let w = self.get_resolution();
w.1 * w.0
let (width, height) = self.get_resolution();
width * height
}
pub fn tick(&mut self) {
+1 -1
View File
@@ -36,7 +36,7 @@ pub const INST_LDF2: &str = "LDF2";
pub const INST_LDIA: &str = "LDIA";
pub const INST_LDIX: &str = "LDIX";
pub const INST_LIDR: &str = "LIDR";
pub const INST_LIDS: &str = "LIDS";
pub const INST_LDIS: &str = "LIDS";
pub const INST_LDR: &str = "LDR";
pub const INST_LDRD: &str = "LDRD";
pub const INST_LDRI: &str = "LDRI";
+1 -1
View File
@@ -11,8 +11,8 @@ pub mod chip8 {
pub mod registers;
pub mod stack;
pub mod computer_manager;
pub mod quirk_modes;
}
pub mod constants;
+333 -8
View File
@@ -1,8 +1,10 @@
use std::collections::{BTreeMap, BTreeSet};
use log::debug;
use rand::random;
use gemma::chip8::computer::Chip8Computer;
use gemma::chip8::delay_timer::DelayTimer;
use gemma::chip8::instructions::Chip8CpuInstructions;
use gemma::chip8::instructions::Chip8CpuInstructions::JPA;
use gemma::chip8::keypad::Keypad;
use gemma::chip8::registers::Chip8Registers;
use gemma::chip8::sound_timer::SoundTimer;
@@ -11,7 +13,7 @@ use gemma::chip8::util::InstructionUtil;
use gemma::chip8::video::Chip8Video;
use gemma::constants::*;
const TEST_OUTPUT_SAMPLE_DIR: &str = "/home/tmerritt/Projects/trevors_chip8_toy/resources/test/";
const TEST_OUTPUT_SAMPLE_DIR: &str = "/home/tmerritt/Projects/chip8_toy/resources/test/";
fn read_test_result(suffix: &str) -> String {
std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix)
@@ -54,7 +56,7 @@ fn encode_decode_test() {
assert_eq!(Chip8CpuInstructions::LDRD(0x1).encode(), 0xf107);
assert_eq!(Chip8CpuInstructions::LDRK(0x4).encode(), 0xf40a);
assert_eq!(Chip8CpuInstructions::LDD(0x6).encode(), 0xf615);
assert_eq!(Chip8CpuInstructions::LDI_S(0xb).encode(), 0xfb18);
assert_eq!(Chip8CpuInstructions::LDIS(0xb).encode(), 0xfb18);
assert_eq!(Chip8CpuInstructions::ADDI(0xd).encode(), 0xfd1e);
assert_eq!(Chip8CpuInstructions::LDFX(0xc).encode(), 0xfc29);
assert_eq!(Chip8CpuInstructions::BCD(0xd).encode(), 0xfd33);
@@ -110,7 +112,7 @@ fn encode_decode_test() {
assert!(matches!(Chip8CpuInstructions::decode(0xf107), Chip8CpuInstructions::LDRD(0x1)));
assert!(matches!(Chip8CpuInstructions::decode(0xf40a), Chip8CpuInstructions::LDRK(0x4)));
assert!(matches!(Chip8CpuInstructions::decode(0xf615), Chip8CpuInstructions::LDD(0x6)));
assert!(matches!(Chip8CpuInstructions::decode(0xfb18), Chip8CpuInstructions::LDI_S(0xb)));
assert!(matches!(Chip8CpuInstructions::decode(0xfb18), Chip8CpuInstructions::LDIS(0xb)));
assert!(matches!(Chip8CpuInstructions::decode(0xfd1e), Chip8CpuInstructions::ADDI(0xd)));
assert!(matches!(Chip8CpuInstructions::decode(0xfc29), Chip8CpuInstructions::LDFX(0xc)));
assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::BCD(0xd)));
@@ -334,7 +336,7 @@ fn addivx_test() {
fn ldstvt_test() {
let mut x = Chip8Computer::new();
x.registers.poke(0x1, 0xf0);
Chip8CpuInstructions::LDI_S(0x01).execute(&mut x);
Chip8CpuInstructions::LDIS(0x01).execute(&mut x);
assert_eq!(x.sound_timer.current(), 0xf0);
x.sound_timer.tick();
x.sound_timer.tick();
@@ -565,7 +567,6 @@ fn subn_vx_vy_test() {
assert_eq!(x.registers.peek_pc(), 0x206);
}
#[test]
fn draw_nibble_vx_vy_n_test_hd() {
let mut x = Chip8Computer::new();
@@ -770,6 +771,15 @@ fn keypad_keys_check() {
assert!(k.released(1));
}
#[test]
fn keypad_string_format_test() {
let mut k = Keypad::new();
assert_eq!(k.format_as_string(), read_test_result("gemma_keypad_string_result.asc"));
}
#[test]
fn register_rw_test() {
let mut x = Chip8Registers::default();
@@ -853,7 +863,7 @@ fn stack_push_pop_test() {
#[should_panic]
fn stack_overflow_test() {
let mut x = Chip8Stack::new();
for i in 0..17 {
for i in 0..17 {
x.push(&i);
}
}
@@ -894,7 +904,6 @@ fn stack_lots_of_subs() {
}
assert_eq!(x.depth(), 15);
}
}
@@ -945,7 +954,6 @@ fn instruction_byte_to_bool_changes() {
}
fn real_build_checkboard(in_hd: bool) -> Chip8Video {
let mut r = Chip8Video::default();
let (width, height) = if in_hd {
@@ -1318,3 +1326,320 @@ fn video_scroll_right_4_row_test_high_def() {
x.scroll_right();
assert_eq!(read_test_result("test_scroll_right_4_hd.asc"), x.format_as_string());
}
struct InstructionTest {
name: String,
instruction: Chip8CpuInstructions,
asm: String,
encoded: u16,
}
#[test]
fn instructions_name_tests() {
assert_eq!(Chip8CpuInstructions::SYS(0x0000).name(), INST_SYS);
assert_eq!(Chip8CpuInstructions::ADD(0x0, 0x1).name(), INST_ADD);
assert_eq!(Chip8CpuInstructions::ADDI(0x0).name(), INST_ADDI);
assert_eq!(Chip8CpuInstructions::ADDR(0x01, 0x02).name(), INST_ADDR);
assert_eq!(Chip8CpuInstructions::AND(0x01, 0x02).name(), INST_AND);
let it = vec![
InstructionTest {
name: INST_SYS.to_string(),
instruction: Chip8CpuInstructions::SYS(0x123),
asm: "SYS 0x0123".to_string(),
encoded: 0x0123
},
InstructionTest {
name: INST_CLS.to_string(),
instruction: Chip8CpuInstructions::CLS,
asm: "CLS".to_string(),
encoded: 0x00E0
},
InstructionTest {
name: INST_RET.to_string(),
instruction: Chip8CpuInstructions::RET,
asm: "RET".to_string(),
encoded: 0x00ee
},
InstructionTest {
name: INST_JPA.to_string(),
instruction: JPA(0x234),
asm: "JPA 0x0234".to_string(),
encoded: 0xb234
},
InstructionTest {
name: INST_CALL.to_string(),
instruction: Chip8CpuInstructions::CALL(0x123),
asm: "CALL 0x0123".to_string(),
encoded: 0x2123,
},
InstructionTest {
name: INST_DRW.to_string(),
instruction: Chip8CpuInstructions::DRW(0x01, 0x02, 0x03),
asm: "DRW 0x01, 0x02, 0x03".to_string(),
encoded: 0xd123
},
InstructionTest {
name: INST_JPI.to_string(),
instruction: Chip8CpuInstructions::JPI(0x321),
asm: "JPI 0x0321".to_string(),
encoded: 0xb321
},
InstructionTest {
name: INST_SDN.to_string(),
instruction: Chip8CpuInstructions::SDN(0x01),
asm: "SDN 0x01".to_string(),
encoded: 0x00c1
},
InstructionTest {
name: INST_SRT.to_string(),
instruction: Chip8CpuInstructions::SRT,
asm: "SRT".to_string(),
encoded: 0x00FB
},
InstructionTest {
name: INST_SLF.to_string(),
instruction: Chip8CpuInstructions::SLF,
asm: "SLF".to_string(),
encoded: 0x00FC,
},
InstructionTest {
name: INST_EXIT.to_string(),
instruction: Chip8CpuInstructions::EXIT,
asm: "EXIT".to_string(),
encoded: 0x00FD,
},
InstructionTest {
name: INST_DIS.to_string(),
instruction: Chip8CpuInstructions::DIS,
asm: "DIS".to_string(),
encoded: 0x00FE,
},
InstructionTest {
name: INST_ENA.to_string(),
instruction: Chip8CpuInstructions::ENA,
asm: "ENA".to_string(),
encoded: 0x00FF,
},
InstructionTest {
name: INST_SEX.to_string(),
instruction: Chip8CpuInstructions::SEX(0x01, 0xfa),
asm: "SEX 0x01, 0xfa".to_string(),
encoded: 0x32fa,
},
InstructionTest {
name: INST_SNEB.to_string(),
instruction: Chip8CpuInstructions::SNEB(0x01, 0xab),
asm: "SNEB 0x01, 0xab".to_string(),
encoded: 0x41ab,
},
InstructionTest {
name: INST_SEY.to_string(),
instruction: Chip8CpuInstructions::SEY(0x1, 0x2),
asm: "SEY 0x1, 0x2".to_string(),
encoded: 0x5120
},
InstructionTest {
name: INST_LDR.to_string(),
instruction: Chip8CpuInstructions::LDR(0xa, 0xbe),
asm: "LDR 0x0a, 0xbe".to_string(),
encoded: 0x6abe,
},
InstructionTest {
name: INST_ADD.to_string(),
instruction: Chip8CpuInstructions::ADD(0x01, 0xab),
asm: "ADD 0x01, 0xab".to_string(),
encoded: 0x71ab
},
InstructionTest {
name: INST_LDRY.to_string(),
instruction: Chip8CpuInstructions::LDR_Y(0x1, 0x2),
asm: "LDRY 0x1, 0x2".to_string(),
encoded: 0x8120,
},
InstructionTest {
name: INST_OR.to_string(),
instruction: Chip8CpuInstructions::OR(0x1, 0x2),
asm: "OR 0x1, 0x2".to_string(),
encoded: 0x8121
},
InstructionTest {
name: INST_AND.to_string(),
instruction: Chip8CpuInstructions::AND(0xb, 0xc),
asm: "AND 0xb, 0xc".to_string(),
encoded: 0x8bc2,
},
InstructionTest {
name: INST_ORY.to_string(),
instruction: Chip8CpuInstructions::ORY(0xa, 0x3),
asm: "ORY 0xa, 0x3".to_string(),
encoded: 0x8a33
},
InstructionTest {
name: INST_ADDR.to_string(),
instruction: Chip8CpuInstructions::ADDR(0x1, 0x2),
asm: "ADDR 0x1, 0x2".to_string(),
encoded: 0x8124
},
InstructionTest {
name: INST_SUB.to_string(),
instruction: Chip8CpuInstructions::SUB(0x4, 0x5),
asm: "SUB 0x4, 0x5".to_string(),
encoded: 0x8455
},
InstructionTest {
name: INST_SHR.to_string(),
instruction: Chip8CpuInstructions::SHR(0x01, 0x1),
asm: "SHR 0x1, 0x1".to_string(),
encoded: 0x8116,
},
InstructionTest {
name: INST_SUBC.to_string(),
instruction: Chip8CpuInstructions::SUBC(0xf, 0xa),
asm: "SUBC 0xf, 0xa".to_string(),
encoded: 0x8fa7,
},
InstructionTest {
name: INST_SHL.to_string(),
instruction: Chip8CpuInstructions::SHL(0x1, 0x4),
asm: "SHL 0x1, 0x4".to_string(),
encoded: 0x814e,
},
InstructionTest {
name: INST_SNEY.to_string(),
instruction: Chip8CpuInstructions::SNEY(0x4, 0x5),
asm: "SNEY 0x4, 0x5".to_string(),
encoded: 0x9450,
},
InstructionTest {
name: INST_LDIA.to_string(),
instruction: Chip8CpuInstructions::LDIA(0xbee),
asm: "LDIA 0x0bee".to_string(),
encoded: 0x9bee
},
InstructionTest {
name: INST_JPI.to_string(),
instruction: Chip8CpuInstructions::JPI(0xfee),
asm: "JPI 0x0fee".to_string(),
encoded: 0xbfee
},
InstructionTest {
name: INST_RND.to_string(),
instruction: Chip8CpuInstructions::RND(0x1, 0xae),
asm: "RND 0x01, 0xae".to_string(),
encoded: 0xc1ae,
},
InstructionTest {
name: INST_DRW.to_string(),
instruction: Chip8CpuInstructions::DRW(0x1, 0x2, 0xf),
asm: "DRW 0x01, 0x02, 0x0f".to_string(),
encoded: 0xd12f
}
/*
0xE09E..=0xEFA1 => match last_byte {
0x9E => Chip8CpuInstructions::SKP(ubln),
0xA1 => Chip8CpuInstructions::SKNP(ubln),
0xF007..=0xFF65 => match last_byte {
0x07 => Chip8CpuInstructions::LDRD(ubln),
0x0A => Chip8CpuInstructions::LDRK(ubln),
0x15 => Chip8CpuInstructions::LDD(ubln),
0x18 => Chip8CpuInstructions::LDIS(ubln),
0x1E => Chip8CpuInstructions::ADDI(ubln),
0x29 => Chip8CpuInstructions::LDFX(ubln),
0x30 => Chip8CpuInstructions::LDF2(ubln),
0x33 => Chip8CpuInstructions::BCD(ubln),
0x55 => Chip8CpuInstructions::LDIX(ubln),
0x65 => Chip8CpuInstructions::LDRI(ubln),
0x75 => Chip8CpuInstructions::STR(ubln),
0x85 => Chip8CpuInstructions::LIDR(ubln),
*/
];
for current in it {
assert_eq!(current.instruction.name(), current.name);
let i = current.instruction;
assert!(matches!(Chip8CpuInstructions::decode(current.encoded), i));
assert_eq!(i.to_string(), current.asm);
let asm = Chip8CpuInstructions::from_str(&current.asm);
assert_eq!(i.to_string(), asm.to_string());
}
}
#[test]
fn instructions_operands_tests() {
assert_eq!(Chip8CpuInstructions::SYS(0x000).operands(), "0x0000");
assert_eq!(Chip8CpuInstructions::JPI(0x123).operands(), "0x0123");
assert_eq!(Chip8CpuInstructions::JPA(0x234).operands(), "0x0234");
assert_eq!(Chip8CpuInstructions::LDIA(0x345).operands(), "0x0345");
assert_eq!(Chip8CpuInstructions::CALL(0x456).operands(), "0x0456");
}
#[test]
fn instruction_ena_dis_tests() {
let mut x = Chip8Computer::new();
assert!(!x.video_memory.is_highres());
Chip8CpuInstructions::ENA.execute(&mut x);
assert!(x.video_memory.is_highres());
Chip8CpuInstructions::ENA.execute(&mut x);
assert!(x.video_memory.is_highres());
Chip8CpuInstructions::DIS.execute(&mut x);
assert!(!x.video_memory.is_highres());
}
#[test]
fn instruction_test_scrolling_lowres() {
let mut x = Chip8Computer::new();
x.video_memory = build_checkerboard();
Chip8CpuInstructions::SRT.execute(&mut x);
assert_eq!(read_test_result("test_scroll_right_4.asc"), x.dump_video_to_string());
x = Chip8Computer::new();
x.video_memory = build_checkerboard();
Chip8CpuInstructions::SLF.execute(&mut x);
assert_eq!(read_test_result("test_scroll_left_4.asc"), x.dump_video_to_string());
x = Chip8Computer::new();
x.video_memory = build_checkerboard();
Chip8CpuInstructions::SDN(0x01).execute(&mut x);
assert_eq!(read_test_result("test_video_scroll_down_1.asc"), x.dump_video_to_string());
x = Chip8Computer::new();
x.video_memory = build_checkerboard();
Chip8CpuInstructions::SDN(0xA).execute(&mut x);
assert_eq!(read_test_result("test_video_scroll_down_10.asc"), x.dump_video_to_string());
}
#[test]
fn computer_dump_keypad_to_string() {
}
#[test]
fn computer_dump_registers_to_string() {
let mut x = Chip8Computer::new();
let values_to_set = [0x0b, 0xad, 0xbe, 0xef,
0xca, 0xb0,
0x7a, 0xc0, 0xca, 0x70,
0xba, 0xdb, 0xed, 0x00,
0x00, 0x00
];
let expected_value = "Vx: 0x0b 0xad 0xbe 0xef 0xca 0xb0 0x7a 0xc0\n 0xca 0x70 0xba 0xdb 0xed 0x00 0x00 0x00\nI: 0x0000\tPC: 0x0200";
for i in 0..16 {
x.registers.poke(i, values_to_set[i as usize]);
}
// now verify.
assert_eq!(expected_value, x.dump_registers_to_string());
}
//#[test]
fn quirks_chip8_vf_reset_tests() {
// vF reset - The AND, OR and XOR opcodes (8xy1, 8xy2 and 8xy3) reset the flags
// register to zero. Test will show ERR1 if the AND and OR tests don't behave
// the same and ERR2 if the AND and XOR tests don't behave the same.
let mut x = Chip8Computer::new();
}