prep to add savestates

This commit is contained in:
Trevor Merritt 2024-11-02 08:10:20 -04:00
parent 4e52b5b05a
commit 5663123f81
5 changed files with 565 additions and 541 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,10 @@
use gemma::chip8::computer::Chip8Computer;
use gemma::chip8::computer_manager::Chip8ComputerManager;
use std::fs::File; use std::fs::File;
use std::io::Read; use std::io::Read;
use std::ops::Add; use std::ops::Add;
use std::path::Path; use std::path::Path;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use gemma::chip8::computer::Chip8Computer;
use gemma::chip8::computer_manager::Chip8ComputerManager;
const ROM_ROOT: &str = "resources/roms"; const ROM_ROOT: &str = "resources/roms";
@ -12,8 +12,11 @@ fn main() {
let mut x = Chip8ComputerManager::new(); let mut x = Chip8ComputerManager::new();
let mut buffer = Vec::new(); let mut buffer = Vec::new();
let mut input_file = File::open(Path::new(&(ROM_ROOT.to_string() + "/mandelbrot_bench.ch8"))).expect("derp"); let mut input_file =
input_file.read_to_end(&mut buffer).expect("unable to read file"); File::open(Path::new(&(ROM_ROOT.to_string() + "/mandelbrot_bench.ch8"))).expect("derp");
input_file
.read_to_end(&mut buffer)
.expect("unable to read file");
x.load_new_program_to_system_memory((&*buffer).into()); x.load_new_program_to_system_memory((&*buffer).into());
for _ in 0..10 { for _ in 0..10 {
@ -26,17 +29,17 @@ fn main() {
x.step(); x.step();
} }
let num = num_cycles.to_string() let num = num_cycles
.to_string()
.as_bytes() .as_bytes()
.rchunks(3) .rchunks(3)
.rev() .rev()
.map(std::str::from_utf8) .map(std::str::from_utf8)
.collect::<Result<Vec<&str>, _>>() .collect::<Result<Vec<&str>, _>>()
.unwrap() .unwrap()
.join(","); // separator .join(","); // separator
let num_ips = num_cycles / 1000000; let num_ips = num_cycles / 1000000;
println!("Completed at {num_ips} Mips."); println!("Completed at {num_ips} Mips. ({num})");
} }
} }

View File

@ -1,14 +1,14 @@
use std::fmt::{Debug, Display, Formatter}; use crate::chip8::computer::Chip8Computer;
use std::ops::BitAnd;
use std::time::Instant;
use log::debug;
use rand::Rng;
use crate::chip8::computer::{Chip8Computer};
use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey; use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey;
use crate::chip8::instructions::Chip8CpuInstructions::*; use crate::chip8::instructions::Chip8CpuInstructions::*;
use crate::chip8::quirk_modes::QuirkMode; use crate::chip8::quirk_modes::QuirkMode;
use crate::chip8::util::InstructionUtil; use crate::chip8::util::InstructionUtil;
use crate::constants::{*}; use crate::constants::*;
use log::debug;
use rand::Rng;
use std::fmt::{Debug, Display, Formatter};
use std::ops::BitAnd;
use std::time::Instant;
/* /*
nnn or addr - A 12-bit value, the lowest 12 bits of the instruction nnn or addr - A 12-bit value, the lowest 12 bits of the instruction
@ -69,7 +69,7 @@ pub enum Chip8CpuInstructions {
ADD(u8, u8), ADD(u8, u8),
/// 8xy0 /// 8xy0
/// Adds the value kk to the value of register Vx, then stores the result in Vx. /// Adds the value kk to the value of register Vx, then stores the result in Vx.
LDR_Y(u8, u8), LDRY(u8, u8),
/// 8xy1 /// 8xy1
/// Stores the value of register Vy in register Vx. /// Stores the value of register Vy in register Vx.
OR(u8, u8), OR(u8, u8),
@ -184,7 +184,7 @@ pub enum Chip8CpuInstructions {
/// 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.
LDD(u8), // 0xFx15 Set Delay Timer LDD(u8), // 0xFx15 Set Delay Timer
/// Fx18 /// Fx18
/// Set sound timer = Vx. /// Set sound timer = Vx.
/// ///
@ -268,7 +268,7 @@ pub enum Chip8CpuInstructions {
/// ///
/// scroll screen content down N pixel, in XO-CHIP only selected bit /// scroll screen content down N pixel, in XO-CHIP only selected bit
/// planes are scrolled (Quirks are HP48 specific) /// planes are scrolled (Quirks are HP48 specific)
SCU(u8) SCU(u8),
} }
impl Chip8CpuInstructions { impl Chip8CpuInstructions {
@ -296,7 +296,7 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::LDRD(_) => INST_LDRD, Chip8CpuInstructions::LDRD(_) => INST_LDRD,
Chip8CpuInstructions::LDRI(_) => INST_LDRI, Chip8CpuInstructions::LDRI(_) => INST_LDRI,
Chip8CpuInstructions::LDRK(_) => INST_LDRK, Chip8CpuInstructions::LDRK(_) => INST_LDRK,
Chip8CpuInstructions::LDR_Y(_, _) => INST_LDRY, Chip8CpuInstructions::LDRY(_, _) => INST_LDRY,
Chip8CpuInstructions::OR(_, _) => INST_OR, Chip8CpuInstructions::OR(_, _) => INST_OR,
Chip8CpuInstructions::RET => INST_RET, Chip8CpuInstructions::RET => INST_RET,
Chip8CpuInstructions::RND(_, _) => INST_RND, Chip8CpuInstructions::RND(_, _) => INST_RND,
@ -320,42 +320,42 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::ORY(_, _) => INST_ORY, Chip8CpuInstructions::ORY(_, _) => INST_ORY,
JPX(_, _) => INST_JPX, JPX(_, _) => INST_JPX,
XXXXERRORINSTRUCTION => "XX ERROR XX", XXXXERRORINSTRUCTION => "XX ERROR XX",
SCU(_) => INST_SCU SCU(_) => INST_SCU,
} }
} }
pub fn operands(&self) -> String { pub fn operands(&self) -> String {
match self { match self {
JPX(x, addr) => { JPX(x, addr) => {
let addr_for_display = (*x as u16 ) << 8 | *addr; let addr_for_display = (*x as u16) << 8 | *addr;
format!("0x{x:02x}, 0x{addr_for_display:04x}") format!("0x{x:02x}, 0x{addr_for_display:04x}")
} }
Chip8CpuInstructions::SYS(addr) | Chip8CpuInstructions::SYS(addr)
Chip8CpuInstructions::JPI(addr) | | Chip8CpuInstructions::JPI(addr)
Chip8CpuInstructions::JPA(addr) | | Chip8CpuInstructions::JPA(addr)
Chip8CpuInstructions::LDIA(addr) | | Chip8CpuInstructions::LDIA(addr)
Chip8CpuInstructions::CALL(addr) => { | Chip8CpuInstructions::CALL(addr) => {
format!("0x{addr:04x}") format!("0x{addr:04x}")
} }
Chip8CpuInstructions::SEX(x, byte) | Chip8CpuInstructions::SEX(x, byte)
Chip8CpuInstructions::SNEB(x, byte) | | Chip8CpuInstructions::SNEB(x, byte)
Chip8CpuInstructions::LDR(x, byte) | | Chip8CpuInstructions::LDR(x, byte)
Chip8CpuInstructions::RND(x, byte) | | Chip8CpuInstructions::RND(x, byte)
Chip8CpuInstructions::ADD(x, byte) => { | Chip8CpuInstructions::ADD(x, byte) => {
format!("0x{x:02x}, 0x{byte:02x}") format!("0x{x:02x}, 0x{byte:02x}")
} }
// Reg, Reg // Reg, Reg
SEY(x, y) | SEY(x, y)
LDR_Y(x, y) | | Chip8CpuInstructions::LDRY(x, y)
Chip8CpuInstructions::OR(x, y) | | Chip8CpuInstructions::OR(x, y)
Chip8CpuInstructions::AND(x, y) | | Chip8CpuInstructions::AND(x, y)
Chip8CpuInstructions::ORY(x, y) | | Chip8CpuInstructions::ORY(x, y)
Chip8CpuInstructions::ADDR(x, y) | | Chip8CpuInstructions::ADDR(x, y)
Chip8CpuInstructions::SUB(x, y) | | Chip8CpuInstructions::SUB(x, y)
Chip8CpuInstructions::SHR(x, y) | | Chip8CpuInstructions::SHR(x, y)
Chip8CpuInstructions::SUBC(x, y) | | Chip8CpuInstructions::SUBC(x, y)
Chip8CpuInstructions::SHL(x, y) | | Chip8CpuInstructions::SHL(x, y)
Chip8CpuInstructions::SNEY(x, y) => { | Chip8CpuInstructions::SNEY(x, y) => {
format!("0x{x:01x}, 0x{y:01x}") format!("0x{x:01x}, 0x{y:01x}")
} }
// Reg, Reg, Nibble // Reg, Reg, Nibble
@ -363,34 +363,32 @@ impl Chip8CpuInstructions {
format!("0x{x:02x}, 0x{y:02x}, 0x{nibble:02x}") format!("0x{x:02x}, 0x{y:02x}, 0x{nibble:02x}")
} }
// Registers. 0-F // Registers. 0-F
Chip8CpuInstructions::SCU(x) | Chip8CpuInstructions::SCU(x)
Chip8CpuInstructions::LDD(x) | | Chip8CpuInstructions::LDD(x)
Chip8CpuInstructions::LDIS(x) | | Chip8CpuInstructions::LDIS(x)
Chip8CpuInstructions::ADDI(x) | | Chip8CpuInstructions::ADDI(x)
Chip8CpuInstructions::LDFX(x) | | Chip8CpuInstructions::LDFX(x)
Chip8CpuInstructions::BCD(x) | | Chip8CpuInstructions::BCD(x)
Chip8CpuInstructions::LDIX(x) | | Chip8CpuInstructions::LDIX(x)
Chip8CpuInstructions::LDRD(x) | | Chip8CpuInstructions::LDRD(x)
Chip8CpuInstructions::LDRK(x) | | Chip8CpuInstructions::LDRK(x)
Chip8CpuInstructions::LDRI(x) | | Chip8CpuInstructions::LDRI(x)
Chip8CpuInstructions::LDF2(x) | | Chip8CpuInstructions::LDF2(x)
Chip8CpuInstructions::STR(x) | | Chip8CpuInstructions::STR(x)
Chip8CpuInstructions::LIDR(x) | | Chip8CpuInstructions::LIDR(x)
Chip8CpuInstructions::SCD(x) | | Chip8CpuInstructions::SCD(x)
Chip8CpuInstructions::SKNP(x) | | Chip8CpuInstructions::SKNP(x)
Chip8CpuInstructions::SKP(x) => { | Chip8CpuInstructions::SKP(x) => {
format!("0x{x:02x}") format!("0x{x:02x}")
} }
Chip8CpuInstructions::EXIT | Chip8CpuInstructions::EXIT
Chip8CpuInstructions::HIGH | | Chip8CpuInstructions::HIGH
Chip8CpuInstructions::LOW | | Chip8CpuInstructions::LOW
Chip8CpuInstructions::SCL | | Chip8CpuInstructions::SCL
Chip8CpuInstructions::XXXXERRORINSTRUCTION | | Chip8CpuInstructions::XXXXERRORINSTRUCTION
Chip8CpuInstructions::SCR | | Chip8CpuInstructions::SCR
Chip8CpuInstructions::CLS | | Chip8CpuInstructions::CLS
Chip8CpuInstructions::RET => { | Chip8CpuInstructions::RET => String::new(),
String::new()
}
} }
} }
} }
@ -406,154 +404,85 @@ impl Display for Chip8CpuInstructions {
impl Chip8CpuInstructions { impl Chip8CpuInstructions {
pub fn from_str(input: &str) -> Chip8CpuInstructions { pub fn from_str(input: &str) -> Chip8CpuInstructions {
let mut parts = input.split(" "); let mut parts = input.split(" ");
// print!("THERE ARE {} PARTS", parts.clone().count()); // print!("THERE ARE {} PARTS", parts.clone().count());
let first_part = parts.next().unwrap_or(""); let first_part = parts.next().unwrap_or("");
// take the next value... // take the next value...
// ...strip off the extra... // ...strip off the extra...
// ...convert it to an integer from base 16 // ...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 param1 = u16::from_str_radix(
let param2 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0); parts
let param3 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0); .next()
// println!("\tFirst part is {:?} / {:?} / {:?} / {:?}", first_part, param1 ,param2 ,param3); .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 { match first_part {
INST_ADDI => { INST_ADDI => ADDI(param1 as u8),
ADDI(param1 as u8) INST_ADD => ADD(param1 as u8, param2 as u8),
} INST_CLS => CLS,
INST_ADD => { INST_DRW => DRW(param1 as u8, param2 as u8, param3 as u8),
ADD(param1 as u8, param2 as u8) INST_CALL => CALL(param1),
} INST_SYS => SYS(param1),
INST_CLS => { INST_RET => RET,
CLS INST_JPA => JPA(param1),
} INST_JPI => JPI(param1),
INST_DRW => { INST_SEX => SEX(param1 as u8, param2 as u8),
DRW(param1 as u8, param2 as u8, param3 as u8) INST_SNEB => SNEB(param1 as u8, param2 as u8),
} INST_SCD => SCD(param1 as u8),
INST_CALL => { INST_STR => STR(param1 as u8),
CALL(param1) INST_SCL => SCL,
} INST_EXIT => EXIT,
INST_SYS => { INST_LOW => LOW,
SYS(param1) INST_HIGH => HIGH,
} INST_SEY => SEY(param1 as u8, param2 as u8),
INST_RET => { INST_LDRY => LDRY(param1 as u8, param2 as u8),
RET INST_LDR => LDR(param1 as u8, param2 as u8),
} INST_OR => OR(param1 as u8, param2 as u8),
INST_JPA => { INST_AND => AND(param1 as u8, param2 as u8),
JPA(param1) INST_ORY => ORY(param1 as u8, param2 as u8),
} INST_ADDR => ADDR(param1 as u8, param2 as u8),
INST_JPI => { INST_SUB => SUB(param1 as u8, param2 as u8),
JPI(param1) INST_SHR => SHR(param1 as u8, param2 as u8),
} INST_SHL => SHL(param1 as u8, param2 as u8),
INST_SEX => { INST_SUBC => SUBC(param1 as u8, param2 as u8),
SEX(param1 as u8, param2 as u8) INST_SNEY => SNEY(param1 as u8, param2 as u8),
} INST_LDIA => LDIA(param1),
INST_SNEB => { INST_RND => RND(param1 as u8, param2 as u8),
SNEB(param1 as u8, param2 as u8) INST_SKP => SKP(param1 as u8),
} INST_SKNP => SKNP(param1 as u8),
INST_SCD => { INST_LDRD => LDRD(param1 as u8),
SCD(param1 as u8) INST_LDRK => LDRK(param1 as u8),
} INST_LDRI => LDRI(param1 as u8),
INST_STR => { INST_BCD => BCD(param1 as u8),
STR(param1 as u8) INST_LDF => LDFX(param1 as u8),
} INST_LDF2 => LDF2(param1 as u8),
INST_SCL => { INST_LDIX => LDIX(param1 as u8),
SCL INST_LIDR => LIDR(param1 as u8),
} INST_LDIS => LDIS(param1 as u8),
INST_EXIT => { INST_LDD => LDD(param1 as u8),
EXIT INST_JPX => JPX(param1 as u8, param2),
} _ => XXXXERRORINSTRUCTION,
INST_LOW => {
LOW
}
INST_HIGH => {
HIGH
}
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_SKP => {
SKP(param1 as u8)
}
INST_SKNP => {
SKNP(param1 as u8)
}
INST_LDRD => {
LDRD(param1 as u8)
}
INST_LDRK => {
LDRK(param1 as u8)
}
INST_LDRI => {
LDRI(param1 as u8)
}
INST_BCD => {
BCD(param1 as u8)
}
INST_LDF => {
LDFX(param1 as u8)
}
INST_LDF2 => {
LDF2(param1 as u8)
}
INST_LDIX => {
LDIX(param1 as u8)
}
INST_LIDR => {
LIDR(param1 as u8)
}
INST_LDIS => {
LDIS(param1 as u8)
}
INST_STR => {
STR(param1 as u8)
}
INST_LDD => {
LDD(param1 as u8)
}
INST_JPX => {
JPX(param1 as u8, param2)
}
_ => {
XXXXERRORINSTRUCTION
}
} }
} }
@ -564,27 +493,62 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::RET => 0x00EE, Chip8CpuInstructions::RET => 0x00EE,
Chip8CpuInstructions::JPA(new_addr) => 0x1000 | (new_addr & 0x0FFF), Chip8CpuInstructions::JPA(new_addr) => 0x1000 | (new_addr & 0x0FFF),
Chip8CpuInstructions::CALL(address) => 0x2000 | (address & 0x0FFF), Chip8CpuInstructions::CALL(address) => 0x2000 | (address & 0x0FFF),
Chip8CpuInstructions::SEX(vx_register, byte) => 0x3000 | ((*vx_register as u16) << 8) | (*byte as u16), Chip8CpuInstructions::SEX(vx_register, byte) => {
Chip8CpuInstructions::SNEB(vx_register, byte) => 0x4000 | ((*vx_register as u16) << 8) | (*byte as u16), 0x3000 | ((*vx_register as u16) << 8) | (*byte as u16)
Chip8CpuInstructions::SEY(x_register, y_register) => 0x5000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), }
Chip8CpuInstructions::LDR(x_register, byte) => 0x6000 | ((*x_register as u16) << 8) | (*byte as u16), Chip8CpuInstructions::SNEB(vx_register, byte) => {
Chip8CpuInstructions::ADD(x_register, byte) => 0x7000 | ((*x_register as u16) << 8) | (*byte as u16), 0x4000 | ((*vx_register as u16) << 8) | (*byte as u16)
Chip8CpuInstructions::LDR_Y(x_register, y_register) => 0x8000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), }
Chip8CpuInstructions::OR(x_register, y_register) => 0x8001 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), Chip8CpuInstructions::SEY(x_register, y_register) => {
Chip8CpuInstructions::AND(x_register, y_register) => 0x8002 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), 0x5000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
Chip8CpuInstructions::ORY(x_register, y_register) => 0x8003 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), }
Chip8CpuInstructions::ADDR(x_register, y_register) => 0x8004 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), Chip8CpuInstructions::LDR(x_register, byte) => {
Chip8CpuInstructions::SUB(x_register, y_register) => 0x8005 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), 0x6000 | ((*x_register as u16) << 8) | (*byte as u16)
Chip8CpuInstructions::SHR(x_register, y_register) => 0x8006 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), }
Chip8CpuInstructions::SUBC(x_register, y_register) => 0x8007 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), Chip8CpuInstructions::ADD(x_register, byte) => {
Chip8CpuInstructions::SHL(x_register, y_register) => 0x800E | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), 0x7000 | ((*x_register as u16) << 8) | (*byte as u16)
Chip8CpuInstructions::SNEY(x_register, y_register) => 0x9000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4), }
Chip8CpuInstructions::LDRY(x_register, y_register) => {
0x8000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::OR(x_register, y_register) => {
0x8001 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::AND(x_register, y_register) => {
0x8002 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::ORY(x_register, y_register) => {
0x8003 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::ADDR(x_register, y_register) => {
0x8004 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::SUB(x_register, y_register) => {
0x8005 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::SHR(x_register, y_register) => {
0x8006 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::SUBC(x_register, y_register) => {
0x8007 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::SHL(x_register, y_register) => {
0x800E | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::SNEY(x_register, y_register) => {
0x9000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4)
}
Chip8CpuInstructions::LDIA(addr) => 0xA000 | addr, Chip8CpuInstructions::LDIA(addr) => 0xA000 | addr,
Chip8CpuInstructions::JPI(addr) => 0xB000 | addr, Chip8CpuInstructions::JPI(addr) => 0xB000 | addr,
JPX(x_register, addr)=> (0xb000 | (*x_register as u16) << 8) | *addr, JPX(x_register, addr) => (0xb000 | (*x_register as u16) << 8) | *addr,
Chip8CpuInstructions::RND(x_register, byte) => 0xC000 | ((*x_register as u16) << 8) | (*byte as u16), Chip8CpuInstructions::RND(x_register, byte) => {
0xC000 | ((*x_register as u16) << 8) | (*byte as u16)
}
Chip8CpuInstructions::DRW(x_register, y_register, height) => { Chip8CpuInstructions::DRW(x_register, y_register, height) => {
0xD000 | ((*x_register as u16) << 8) | ((*y_register as u16) << 4) | (*height as u16) 0xD000
| ((*x_register as u16) << 8)
| ((*y_register as u16) << 4)
| (*height as u16)
} }
Chip8CpuInstructions::SKP(x_register) => 0xE09E | ((*x_register as u16) << 8), Chip8CpuInstructions::SKP(x_register) => 0xE09E | ((*x_register as u16) << 8),
Chip8CpuInstructions::SKNP(x_register) => 0xE0A1 | ((*x_register as u16) << 8), Chip8CpuInstructions::SKNP(x_register) => 0xE0A1 | ((*x_register as u16) << 8),
@ -621,18 +585,10 @@ impl Chip8CpuInstructions {
let last_nibble = (input & 0xF) as u8; let last_nibble = (input & 0xF) as u8;
match input { match input {
0x00C0..=0x00CF => { 0x00C0..=0x00CF => match quirk_mode {
match quirk_mode { QuirkMode::Chip8 => XXXXERRORINSTRUCTION,
QuirkMode::Chip8 => { QuirkMode::XOChip => SCD(last_nibble),
XXXXERRORINSTRUCTION QuirkMode::SChipModern => SCD(last_nibble),
}
QuirkMode::XOChip => {
SCD(last_nibble)
}
QuirkMode::SChipModern => {
SCD(last_nibble)
}
}
}, },
0x00E0 => Chip8CpuInstructions::CLS, 0x00E0 => Chip8CpuInstructions::CLS,
0x00EE => Chip8CpuInstructions::RET, 0x00EE => Chip8CpuInstructions::RET,
@ -642,47 +598,23 @@ impl Chip8CpuInstructions {
// does not exist on Chip8 // does not exist on Chip8
XXXXERRORINSTRUCTION XXXXERRORINSTRUCTION
} }
QuirkMode::XOChip => { QuirkMode::XOChip => Chip8CpuInstructions::SCR,
Chip8CpuInstructions::SCR QuirkMode::SChipModern => Chip8CpuInstructions::SCR,
}
QuirkMode::SChipModern => {
Chip8CpuInstructions::SCR
}
} }
}, }
0x00FC => Chip8CpuInstructions::SCL, 0x00FC => Chip8CpuInstructions::SCL,
0x00FD => Chip8CpuInstructions::EXIT, 0x00FD => Chip8CpuInstructions::EXIT,
0x00FE => { 0x00FE => match quirk_mode {
match quirk_mode { QuirkMode::Chip8 => XXXXERRORINSTRUCTION,
QuirkMode::Chip8 => { QuirkMode::XOChip | QuirkMode::SChipModern => LOW,
XXXXERRORINSTRUCTION
}
QuirkMode::XOChip |
QuirkMode::SChipModern => {
LOW
}
}
},
0x00FF => {
match quirk_mode {
QuirkMode::Chip8 => {
XXXXERRORINSTRUCTION
},
QuirkMode::XOChip |
QuirkMode::SChipModern => {
HIGH
}
}
}, },
0x0000..=0x0FFF => { 0x00FF => match quirk_mode {
match quirk_mode { QuirkMode::Chip8 => XXXXERRORINSTRUCTION,
QuirkMode::Chip8 => { QuirkMode::XOChip | QuirkMode::SChipModern => HIGH,
Chip8CpuInstructions::SYS(addr_param) },
} 0x0000..=0x0FFF => match quirk_mode {
QuirkMode::XOChip | QuirkMode::SChipModern => { QuirkMode::Chip8 => Chip8CpuInstructions::SYS(addr_param),
XXXXERRORINSTRUCTION QuirkMode::XOChip | QuirkMode::SChipModern => XXXXERRORINSTRUCTION,
}
}
}, },
0x1000..=0x1FFF => JPA(addr_param), 0x1000..=0x1FFF => JPA(addr_param),
0x2000..=0x2FFF => CALL(addr_param), 0x2000..=0x2FFF => CALL(addr_param),
@ -692,7 +624,7 @@ impl Chip8CpuInstructions {
0x6000..=0x6FFF => LDR(x_param, byte_param), 0x6000..=0x6FFF => LDR(x_param, byte_param),
0x7000..=0x7FFF => ADD(x_param, byte_param), 0x7000..=0x7FFF => ADD(x_param, byte_param),
0x8000..=0x8FFE => match last_nibble { 0x8000..=0x8FFE => match last_nibble {
0x0 => Chip8CpuInstructions::LDR_Y(x_param, y_param), 0x0 => Chip8CpuInstructions::LDRY(x_param, y_param),
0x1 => Chip8CpuInstructions::OR(x_param, y_param), 0x1 => Chip8CpuInstructions::OR(x_param, y_param),
0x2 => Chip8CpuInstructions::AND(x_param, y_param), 0x2 => Chip8CpuInstructions::AND(x_param, y_param),
0x3 => Chip8CpuInstructions::ORY(x_param, y_param), 0x3 => Chip8CpuInstructions::ORY(x_param, y_param),
@ -701,8 +633,8 @@ impl Chip8CpuInstructions {
0x6 => Chip8CpuInstructions::SHR(x_param, y_param), 0x6 => Chip8CpuInstructions::SHR(x_param, y_param),
0x7 => Chip8CpuInstructions::SUBC(x_param, y_param), 0x7 => Chip8CpuInstructions::SUBC(x_param, y_param),
0xE => Chip8CpuInstructions::SHL(x_param, y_param), 0xE => Chip8CpuInstructions::SHL(x_param, y_param),
_ => XXXXERRORINSTRUCTION _ => XXXXERRORINSTRUCTION,
} },
0x9000..=0x9FF0 if input & 0x01 == 0 => Chip8CpuInstructions::SNEY(x_param, y_param), 0x9000..=0x9FF0 if input & 0x01 == 0 => Chip8CpuInstructions::SNEY(x_param, y_param),
0xA000..=0xAFFF => Chip8CpuInstructions::LDIA(addr_param), 0xA000..=0xAFFF => Chip8CpuInstructions::LDIA(addr_param),
0xB000..=0xBFFF => Chip8CpuInstructions::JPI(addr_param), 0xB000..=0xBFFF => Chip8CpuInstructions::JPI(addr_param),
@ -711,8 +643,8 @@ impl Chip8CpuInstructions {
0xE09E..=0xEFA1 => match last_byte { 0xE09E..=0xEFA1 => match last_byte {
0x9E => Chip8CpuInstructions::SKP(ubln), 0x9E => Chip8CpuInstructions::SKP(ubln),
0xA1 => Chip8CpuInstructions::SKNP(ubln), 0xA1 => Chip8CpuInstructions::SKNP(ubln),
_ => XXXXERRORINSTRUCTION _ => XXXXERRORINSTRUCTION,
} },
0xF007..=0xFF65 => match last_byte { 0xF007..=0xFF65 => match last_byte {
0x07 => Chip8CpuInstructions::LDRD(ubln), 0x07 => Chip8CpuInstructions::LDRD(ubln),
0x0A => Chip8CpuInstructions::LDRK(ubln), 0x0A => Chip8CpuInstructions::LDRK(ubln),
@ -726,9 +658,9 @@ impl Chip8CpuInstructions {
0x65 => Chip8CpuInstructions::LDRI(ubln), 0x65 => Chip8CpuInstructions::LDRI(ubln),
0x75 => Chip8CpuInstructions::STR(ubln), 0x75 => Chip8CpuInstructions::STR(ubln),
0x85 => Chip8CpuInstructions::LIDR(ubln), 0x85 => Chip8CpuInstructions::LIDR(ubln),
_ => XXXXERRORINSTRUCTION _ => XXXXERRORINSTRUCTION,
} },
_ => XXXXERRORINSTRUCTION _ => XXXXERRORINSTRUCTION,
} }
} }
pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer { pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer {
@ -788,10 +720,13 @@ impl Chip8CpuInstructions {
} }
// 0x7xkk Set Vx = Vx + kk // 0x7xkk Set Vx = Vx + kk
Chip8CpuInstructions::ADD(vx_register, byte) => { Chip8CpuInstructions::ADD(vx_register, byte) => {
input.registers.poke(*vx_register, (input.registers.peek(*vx_register) as u16 + *byte as u16) as u8); input.registers.poke(
*vx_register,
(input.registers.peek(*vx_register) as u16 + *byte as u16) as u8,
);
} }
// 0x8xy0 Set value of Vy in Vx // 0x8xy0 Set value of Vy in Vx
Chip8CpuInstructions::LDR_Y(x, y) => { Chip8CpuInstructions::LDRY(x, y) => {
input.registers.poke(*x, input.registers.peek(*y)); input.registers.poke(*x, input.registers.peek(*y));
} }
// 0x8xy1 Set Vx = Vx OR Vy // 0x8xy1 Set Vx = Vx OR Vy
@ -881,14 +816,11 @@ impl Chip8CpuInstructions {
// If Vy > Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the results stored in Vx. // If Vy > Vx, then VF is set to 1, otherwise 0. Then Vx is subtracted from Vy, and the results stored in Vx.
let y_register = input.registers.peek(*y); let y_register = input.registers.peek(*y);
let x_register = input.registers.peek(*x); let x_register = input.registers.peek(*x);
let mut value_to_poke = 0;
let new_value = if y_register < x_register { let (new_value, value_to_poke) = if y_register < x_register {
value_to_poke = (y_register as u16 + 256) - x_register as u16; (0, (y_register as u16 + 256) - x_register as u16)
0
} else { } else {
value_to_poke = (y_register - x_register) as u16; (1, (y_register - x_register) as u16)
1
}; };
input.registers.poke(*x, value_to_poke as u8); input.registers.poke(*x, value_to_poke as u8);
input.registers.poke(0xf, new_value); input.registers.poke(0xf, new_value);
@ -936,7 +868,9 @@ impl Chip8CpuInstructions {
// Jump to location nnn + V0. // Jump to location nnn + V0.
// //
// The program counter is set to nnn plus the value of V0. // The program counter is set to nnn plus the value of V0.
input.registers.poke_pc(input.registers.peek(0) as u16 + addr); input
.registers
.poke_pc(input.registers.peek(0) as u16 + addr);
} }
// 0xBxnn Jump to Xnn+Vx // 0xBxnn Jump to Xnn+Vx
JPX(vx_register, addr) => { JPX(vx_register, addr) => {
@ -978,26 +912,35 @@ impl Chip8CpuInstructions {
let y_offset = input.registers.peek(*y) as u16; let y_offset = input.registers.peek(*y) as u16;
if input.video_memory.is_highres() { if input.video_memory.is_highres() {
// if n == 0 we have a 16 row sprite (font maybe) // if n == 0 we have a 16 row sprite (font maybe)
let actual_num_loops = if *n == 0u8 { let actual_num_loops = if *n == 0u8 { 16 } else { *n };
16
} else {
*n
};
for byte_index in 0..actual_num_loops { for byte_index in 0..actual_num_loops {
let current_byte = input.memory.peek(byte_index as u16 + source_memory_offset); let current_byte =
let next_byte = input.memory.peek(byte_index as u16 + 1u16 + source_memory_offset); input.memory.peek(byte_index as u16 + source_memory_offset);
let next_byte = input
.memory
.peek(byte_index as u16 + 1u16 + source_memory_offset);
let x_offset = (x_offset + byte_index as u16) * 64; let x_offset = (x_offset + byte_index as u16) * 64;
for bit_index in 0..8 { for bit_index in 0..8 {
input.video_memory.poke(x_offset + (y_offset + bit_index as u16), (current_byte & (0x80 >> bit_index)) != 0); input.video_memory.poke(
input.video_memory.poke(x_offset + (y_offset + bit_index as u16) + 8, (current_byte & (0x80 >> bit_index)) != 0); x_offset + (y_offset + bit_index as u16),
(current_byte & (0x80 >> bit_index)) != 0,
);
input.video_memory.poke(
x_offset + (y_offset + bit_index as u16) + 8,
(next_byte & (0x80 >> bit_index)) != 0,
);
} }
} }
} else { } else {
for byte_index in 0..*n { for byte_index in 0..*n {
let current_byte = input.memory.peek(byte_index as u16 + source_memory_offset); let current_byte =
input.memory.peek(byte_index as u16 + source_memory_offset);
let x_offset: u16 = (x_offset + byte_index as u16) * 64; let x_offset: u16 = (x_offset + byte_index as u16) * 64;
for bit_index in 0..8 { for bit_index in 0..8 {
input.video_memory.poke(x_offset + (y_offset + bit_index as u16), (current_byte & (0x80 >> bit_index)) != 0); input.video_memory.poke(
x_offset + (y_offset + bit_index as u16),
(current_byte & (0x80 >> bit_index)) != 0,
);
} }
} }
} }
@ -1103,7 +1046,8 @@ impl Chip8CpuInstructions {
let units = to_convert % 10; let units = to_convert % 10;
// Convert to BCD // Convert to BCD
let result = ((hundreds as u16) << 8) | units as u16 | ((tens as u16) << 4) | units as u16; let result =
((hundreds as u16) << 8) | units as u16 | ((tens as u16) << 4) | units as u16;
// write them to the memory pointed to by I, I+1, and I+2 // write them to the memory pointed to by I, I+1, and I+2
let target_start_offset = input.registers.peek_i(); let target_start_offset = input.registers.peek_i();
input.memory.poke(target_start_offset, hundreds); input.memory.poke(target_start_offset, hundreds);
@ -1117,7 +1061,9 @@ impl Chip8CpuInstructions {
// starting at the address in I. // starting at the address in I.
let offset = input.registers.peek_i(); let offset = input.registers.peek_i();
for i in 0..=*x { for i in 0..=*x {
input.memory.poke(offset + i as u16, input.registers.peek(i)); input
.memory
.poke(offset + i as u16, input.registers.peek(i));
} }
input.registers.poke_i(offset + 1); input.registers.poke_i(offset + 1);
} }
@ -1137,78 +1083,58 @@ impl Chip8CpuInstructions {
input.registers.poke_i(offset + 1); input.registers.poke_i(offset + 1);
} }
Chip8CpuInstructions::XXXXERRORINSTRUCTION => {} Chip8CpuInstructions::XXXXERRORINSTRUCTION => {}
Chip8CpuInstructions::SCD(x) => { Chip8CpuInstructions::SCD(x) => match input.quirk_mode {
match input.quirk_mode { QuirkMode::Chip8 => {
QuirkMode::Chip8 => { debug!("Attempt to execute SCD in Chip8 Mode");
debug!("Attempt to execute SCD in Chip8 Mode");
}
QuirkMode::XOChip |
QuirkMode::SChipModern => {
input.video_memory.scroll_down(*x as i32);
}
} }
} QuirkMode::XOChip | QuirkMode::SChipModern => {
Chip8CpuInstructions::SCR => { input.video_memory.scroll_down(*x as i32);
match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("Attempt to execute SCR in Chip8 Mode");
}
QuirkMode::XOChip |
QuirkMode::SChipModern => {
input.video_memory.scroll_right();
}
} }
} },
Chip8CpuInstructions::SCL => { Chip8CpuInstructions::SCR => match input.quirk_mode {
match input.quirk_mode { QuirkMode::Chip8 => {
QuirkMode::Chip8 => { debug!("Attempt to execute SCR in Chip8 Mode");
debug!("Attempt to execute SCL in Chip8 Mode");
}
QuirkMode::XOChip |
QuirkMode::SChipModern => {
input.video_memory.scroll_left();
}
} }
} QuirkMode::XOChip | QuirkMode::SChipModern => {
Chip8CpuInstructions::LOW => { input.video_memory.scroll_right();
match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO SET LOWRES IN CHIP8MODE");
}
QuirkMode::XOChip | QuirkMode::SChipModern => {
input.video_memory.set_lowres();
}
} }
} },
Chip8CpuInstructions::HIGH => { Chip8CpuInstructions::SCL => match input.quirk_mode {
match input.quirk_mode { QuirkMode::Chip8 => {
QuirkMode::Chip8 => { debug!("Attempt to execute SCL in Chip8 Mode");
debug!("ATTEMPT TO SET HIGHRES IN CHIP8MODE");
}
QuirkMode::XOChip | QuirkMode::SChipModern => {
input.video_memory.set_highres();
}
} }
} QuirkMode::XOChip | QuirkMode::SChipModern => {
Chip8CpuInstructions::EXIT => { input.video_memory.scroll_left();
match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO EXIT FROM CHIP8 INTERPRETER");
}
QuirkMode::XOChip |
QuirkMode::SChipModern => {
println!("EXIT INTERPRETER");
}
} }
} },
Chip8CpuInstructions::LOW => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO SET LOWRES IN CHIP8MODE");
}
QuirkMode::XOChip | QuirkMode::SChipModern => {
input.video_memory.set_lowres();
}
},
Chip8CpuInstructions::HIGH => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO SET HIGHRES IN CHIP8MODE");
}
QuirkMode::XOChip | QuirkMode::SChipModern => {
input.video_memory.set_highres();
}
},
Chip8CpuInstructions::EXIT => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO EXIT FROM CHIP8 INTERPRETER");
}
QuirkMode::XOChip | QuirkMode::SChipModern => {
println!("EXIT INTERPRETER");
}
},
Chip8CpuInstructions::LDF2(x) => { Chip8CpuInstructions::LDF2(x) => {
match input.quirk_mode { match input.quirk_mode {
QuirkMode::Chip8 => { QuirkMode::Chip8 => {
debug!("ATTEMPT TO LDF2 IN CHIP8MODE"); debug!("ATTEMPT TO LDF2 IN CHIP8MODE");
} }
QuirkMode::XOChip | QuirkMode::SChipModern => { QuirkMode::XOChip | QuirkMode::SChipModern => {
println!("POINTING TO FONT AT {x:02x}"); println!("POINTING TO FONT AT {x:02x}");
@ -1217,34 +1143,26 @@ impl Chip8CpuInstructions {
} }
} }
} }
Chip8CpuInstructions::STR(x) => { Chip8CpuInstructions::STR(x) => match input.quirk_mode {
match input.quirk_mode { QuirkMode::Chip8 => {
QuirkMode::Chip8 => { debug!("ATTEMPT TO STORE RPL IN CHIP8MODE");
debug!("ATTEMPT TO STORE RPL IN CHIP8MODE");
}
QuirkMode::XOChip |
QuirkMode::SChipModern => {
println!("STORING FROM RPL FOR {x}");
}
} }
} QuirkMode::XOChip | QuirkMode::SChipModern => {
Chip8CpuInstructions::LIDR(x) => { println!("STORING FROM RPL FOR {x}");
match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO LOAD RPL IN CHIP8MODE");
}
QuirkMode::XOChip |
QuirkMode::SChipModern => {
println!("LOADING FROM RPL FOR {x}");
}
} }
} },
Chip8CpuInstructions::LIDR(x) => match input.quirk_mode {
QuirkMode::Chip8 => {
debug!("ATTEMPT TO LOAD RPL IN CHIP8MODE");
}
QuirkMode::XOChip | QuirkMode::SChipModern => {
println!("LOADING FROM RPL FOR {x}");
}
},
SCU(x) => { SCU(x) => {
println!("SCROLL SCREEN UP {x} ROWS"); println!("SCROLL SCREEN UP {x} ROWS");
match input.quirk_mode { match input.quirk_mode {
QuirkMode::Chip8 | QuirkMode::Chip8 | QuirkMode::SChipModern => {
QuirkMode::SChipModern => {
debug!("Attempt to run SCU outside XO mode"); debug!("Attempt to run SCU outside XO mode");
} }
QuirkMode::XOChip => { QuirkMode::XOChip => {

View File

@ -1,5 +1,3 @@
use log::debug;
use rand::random;
use gemma::chip8::computer::Chip8Computer; use gemma::chip8::computer::Chip8Computer;
use gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction; use gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;
use gemma::chip8::delay_timer::DelayTimer; use gemma::chip8::delay_timer::DelayTimer;
@ -14,12 +12,13 @@ use gemma::chip8::system_memory::Chip8SystemMemory;
use gemma::chip8::util::InstructionUtil; use gemma::chip8::util::InstructionUtil;
use gemma::chip8::video::{Chip8Video, Chip8VideoModes}; use gemma::chip8::video::{Chip8Video, Chip8VideoModes};
use gemma::constants::*; use gemma::constants::*;
use log::debug;
use rand::random;
const TEST_OUTPUT_SAMPLE_DIR: &str = "../resources/test/"; const TEST_OUTPUT_SAMPLE_DIR: &str = "../resources/test/";
fn read_test_result(suffix: &str) -> String { fn read_test_result(suffix: &str) -> String {
std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix) std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix).unwrap()
.unwrap()
} }
#[test] #[test]
@ -27,18 +26,19 @@ fn smoke() {
assert!(true) assert!(true)
} }
#[test] #[test]
fn decoder_test_invalid_instructions() { fn decoder_test_invalid_instructions() {
let invalid_to_encode = [ let invalid_to_encode = [
0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf, 0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf, 0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0, 0xeaa2,
0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0, 0xf006, 0xf008,
0xeaa2, 0xf006, 0xf008
]; ];
for i in invalid_to_encode { for i in invalid_to_encode {
assert_eq!(Chip8CpuInstructions::decode(i, &Chip8).encode(), 0xffff); assert_eq!(Chip8CpuInstructions::decode(i, &Chip8).encode(), 0xffff);
assert!(matches!(Chip8CpuInstructions::decode(i, &Chip8), Chip8CpuInstructions::XXXXERRORINSTRUCTION)); assert!(matches!(
Chip8CpuInstructions::decode(i, &Chip8),
Chip8CpuInstructions::XXXXERRORINSTRUCTION
));
} }
} }
@ -109,7 +109,7 @@ fn instruction_tests() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
x.registers.poke(0x01, 0x01); x.registers.poke(0x01, 0x01);
x.registers.poke(0x02, 0x02); x.registers.poke(0x02, 0x02);
Chip8CpuInstructions::LDR_Y(0x01, 0x02).execute(&mut x); Chip8CpuInstructions::LDRY(0x01, 0x02).execute(&mut x);
assert_eq!(x.registers.peek(1), 0x02); assert_eq!(x.registers.peek(1), 0x02);
// 0x8xy1 Set Vx = Vx OR Vy // 0x8xy1 Set Vx = Vx OR Vy
@ -172,7 +172,7 @@ fn instruction_tests() {
x.registers.poke(0x0f, 0x00); x.registers.poke(0x0f, 0x00);
x.registers.poke(0x01, 0b00001000); x.registers.poke(0x01, 0b00001000);
x.registers.poke(0x02, 0b00000000); x.registers.poke(0x02, 0b00000000);
Chip8CpuInstructions::SHR(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Not Set) Chip8CpuInstructions::SHR(0x1, 0x2).execute(&mut x); // 0b0000 0010 (0x02) (Not Set)
assert_eq!(x.registers.peek(1), 0b00000100); assert_eq!(x.registers.peek(1), 0b00000100);
assert_eq!(x.registers.peek(0xf), 0); assert_eq!(x.registers.peek(0xf), 0);
@ -520,8 +520,8 @@ fn draw_nibble_vx_vy_n_test_sd() {
for row_in_sprite in 0..5 { for row_in_sprite in 0..5 {
let row_data = CHIP8FONT_2[row_in_sprite]; let row_data = CHIP8FONT_2[row_in_sprite];
for bit_in_byte in 0..8 { for bit_in_byte in 0..8 {
let data_offset = (x_offset let data_offset =
as u16 + row_in_sprite as u16) * 64 + (bit_in_byte + y_offset) as u16; (x_offset as u16 + row_in_sprite as u16) * 64 + (bit_in_byte + y_offset) as u16;
let real_bit_in_byte = 7 - bit_in_byte; let real_bit_in_byte = 7 - bit_in_byte;
let shifted_one = 0x01 << real_bit_in_byte; let shifted_one = 0x01 << real_bit_in_byte;
let one_shift_set = (shifted_one & row_data) > 0; let one_shift_set = (shifted_one & row_data) > 0;
@ -561,14 +561,16 @@ fn sub_test() {
assert_eq!(x.stack.depth(), 0); assert_eq!(x.stack.depth(), 0);
} }
#[test] #[test]
fn ldvxk_test() { fn ldvxk_test() {
// SETUP // SETUP
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
x.registers.poke(0x01, 0x01); x.registers.poke(0x01, 0x01);
Chip8CpuInstructions::LDRK(0x1).execute(&mut x); Chip8CpuInstructions::LDRK(0x1).execute(&mut x);
assert!(matches!(x.state, gemma::chip8::cpu_states::Chip8CpuStates::WaitingForKey)); assert!(matches!(
x.state,
gemma::chip8::cpu_states::Chip8CpuStates::WaitingForKey
));
} }
#[test] #[test]
@ -577,7 +579,6 @@ fn series8xy4_corex_tests() {
/// Set Vx = Vx + Vy /// Set Vx = Vx + Vy
/// Set VF=1 if Carry /// Set VF=1 if Carry
/// ///
// 1 + 1 // 1 + 1
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
x.registers.poke(0x01, 0x01); x.registers.poke(0x01, 0x01);
@ -637,8 +638,14 @@ fn series8xy4_corex_tests() {
fn random_produces_different_numbers() { fn random_produces_different_numbers() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
x.registers.poke(0x01, 0x00); x.registers.poke(0x01, 0x00);
let first_number = Chip8CpuInstructions::RND(0x01, 0xff).execute(&mut x).registers.peek(0x01); let first_number = Chip8CpuInstructions::RND(0x01, 0xff)
let second_number = Chip8CpuInstructions::RND(0x01, 0xff).execute(&mut x).registers.peek(0x01); .execute(&mut x)
.registers
.peek(0x01);
let second_number = Chip8CpuInstructions::RND(0x01, 0xff)
.execute(&mut x)
.registers
.peek(0x01);
assert_ne!(first_number, second_number); assert_ne!(first_number, second_number);
} }
@ -679,13 +686,14 @@ fn keypad_keys_check() {
assert!(k.released(1)); assert!(k.released(1));
} }
#[test] #[test]
fn keypad_string_format_test() { fn keypad_string_format_test() {
let k = Keypad::new(); let k = Keypad::new();
assert_eq!(
assert_eq!(k.format_as_string(), read_test_result("gemma_keypad_string_result.asc")); k.format_as_string(),
read_test_result("gemma_keypad_string_result.asc")
);
} }
#[test] #[test]
@ -786,10 +794,10 @@ fn stack_underflow_test() {
#[test] #[test]
fn stack_lots_of_subs() { fn stack_lots_of_subs() {
let mut x = Chip8Stack::new(); let mut x = Chip8Stack::new();
let stack_contents = [0x123, 0x321, 0xabc, 0xdef, let stack_contents = [
0xbad, 0xbef, 0xfed, 0xcab, 0x123, 0x321, 0xabc, 0xdef, 0xbad, 0xbef, 0xfed, 0xcab, 0xbed, 0xcad, 0xfeb, 0xcab, 0xfff,
0xbed, 0xcad, 0xfeb, 0xcab, 0x000, 0x001,
0xfff, 0x000, 0x001]; ];
for i in stack_contents { for i in stack_contents {
x.push(&i); x.push(&i);
} }
@ -814,7 +822,6 @@ fn stack_lots_of_subs() {
} }
} }
#[test] #[test]
fn video_split_bytes() { fn video_split_bytes() {
// from 0xABCD we should have AB high, CD low // from 0xABCD we should have AB high, CD low
@ -851,17 +858,40 @@ fn instruction_ubln() {
#[test] #[test]
fn instruction_byte_to_bool_changes() { fn instruction_byte_to_bool_changes() {
assert_eq!(InstructionUtil::byte_to_bools(0b00000000), [false, false, false, false, false, false, false, false]); assert_eq!(
assert_eq!(InstructionUtil::byte_to_bools(0b11111111), [true, true, true, true, true, true, true, true]); InstructionUtil::byte_to_bools(0b00000000),
assert_eq!(InstructionUtil::byte_to_bools(0b11001100), [false, false, true, true, false, false, true, true]); [false, false, false, false, false, false, false, false]
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!(
assert_eq!(InstructionUtil::bools_to_byte([true, true, true, true, true, true, true, true]), 0b11111111); InstructionUtil::byte_to_bools(0b11111111),
assert_eq!(InstructionUtil::bools_to_byte([false, false, true, true, false, false, true, true]), 0b11001100); [true, true, true, true, true, true, true, true]
assert_eq!(InstructionUtil::bools_to_byte([false, false, false, false, true, true, true, true]), 0b11110000); );
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
);
} }
fn real_build_checkboard(in_hd: bool) -> Chip8Video { fn real_build_checkboard(in_hd: bool) -> Chip8Video {
let mut r = Chip8Video::default(); let mut r = Chip8Video::default();
let (width, height) = if in_hd { let (width, height) = if in_hd {
@ -943,10 +973,7 @@ fn video_poke_byte_test() {
#[test] #[test]
fn video_poke_2byte_test() { fn video_poke_2byte_test() {
let to_poke: [u8; 2] = [ let to_poke: [u8; 2] = [0b11001111, 0b00111100];
0b11001111,
0b00111100
];
let mut x = Chip8Video::default(); let mut x = Chip8Video::default();
x.poke_2byte(0x00, to_poke); x.poke_2byte(0x00, to_poke);
@ -1002,12 +1029,7 @@ fn video_poke_byte_test_2() {
#[test] #[test]
fn video_poke_multi_line_test() { fn video_poke_multi_line_test() {
let mut v = Chip8Video::default(); let mut v = Chip8Video::default();
let to_poke = [ let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];
0b00000000,
0b11111111,
0b10101010,
0b01010101
];
for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() { for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {
let base_offset = byte_in_set * 64; let base_offset = byte_in_set * 64;
@ -1040,17 +1062,11 @@ fn video_poke_multi_line_test() {
#[test] #[test]
fn video_moved_poke_test() { fn video_moved_poke_test() {
let mut v = Chip8Video::default(); let mut v = Chip8Video::default();
let to_poke = [ let to_poke = [0b00000000, 0b11111111, 0b10101010, 0b01010101];
0b00000000,
0b11111111,
0b10101010,
0b01010101
];
let x_offset = 20; let x_offset = 20;
let y_offset = 5; let y_offset = 5;
for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() { for (byte_in_set, byte_to_poke) in to_poke.iter().enumerate() {
let base_offset = (x_offset + byte_in_set) * 64 + y_offset; let base_offset = (x_offset + byte_in_set) * 64 + y_offset;
v.poke_byte(base_offset as u16, *byte_to_poke); v.poke_byte(base_offset as u16, *byte_to_poke);
@ -1091,7 +1107,10 @@ fn video_verify_change_registered() {
#[test] #[test]
fn video_write_checkboard() { fn video_write_checkboard() {
let v = build_checkerboard(); let v = build_checkerboard();
assert_eq!(v.clone().format_as_string(), read_test_result("test_video_write_checkerboard.asc")); assert_eq!(
v.clone().format_as_string(),
read_test_result("test_video_write_checkerboard.asc")
);
} }
#[test] #[test]
@ -1102,14 +1121,26 @@ fn video_zero_test() {
x.poke_byte(data_offset as u16, CHIP8FONT_0[byte_index]); x.poke_byte(data_offset as u16, CHIP8FONT_0[byte_index]);
} }
assert_eq!(read_test_result("test_video_zero.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_video_zero.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_multi_sprite_test() { fn video_multi_sprite_test() {
let mut x = Chip8Video::default(); let mut x = Chip8Video::default();
// draw a row of digits 01234567 // draw a row of digits 01234567
let to_draw = [CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_3, CHIP8FONT_4, CHIP8FONT_5, CHIP8FONT_6, CHIP8FONT_7]; let to_draw = [
CHIP8FONT_0,
CHIP8FONT_1,
CHIP8FONT_2,
CHIP8FONT_3,
CHIP8FONT_4,
CHIP8FONT_5,
CHIP8FONT_6,
CHIP8FONT_7,
];
for (index, sprite) in to_draw.iter().enumerate() { for (index, sprite) in to_draw.iter().enumerate() {
let data_base_offset = index * 0x8; let data_base_offset = index * 0x8;
for (index, offset) in (0..=0x100).step_by(0x40).enumerate() { for (index, offset) in (0..=0x100).step_by(0x40).enumerate() {
@ -1117,14 +1148,20 @@ fn video_multi_sprite_test() {
} }
} }
assert_eq!(read_test_result("test_multi_sprite.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_multi_sprite.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_reset_test() { fn video_reset_test() {
let mut x = build_checkerboard(); let mut x = build_checkerboard();
x.reset(); x.reset();
assert_eq!(x.format_as_string(), read_test_result("test_reset_clears_video.asc")); assert_eq!(
x.format_as_string(),
read_test_result("test_reset_clears_video.asc")
);
} }
#[test] #[test]
@ -1157,6 +1194,15 @@ fn video_collision_test2() {
assert!(x.has_frame_changed); assert!(x.has_frame_changed);
} }
#[test]
fn video_collision_test3() {
// draw a couple sprites that do not overlap.
// goal being drawing without triggering the collision
// detection.
let mut x = Chip8Video::default();
x.poke_byte(0x00, 0b11110000);
}
#[test] #[test]
fn video_peek_out_of_bounds_doesnt_panic() { fn video_peek_out_of_bounds_doesnt_panic() {
let x = Chip8Video::default(); let x = Chip8Video::default();
@ -1172,21 +1218,30 @@ fn video_peek_out_of_bounds_doesnt_panic() {
fn video_scroll_down_1_row_test() { fn video_scroll_down_1_row_test() {
let mut x = build_checkerboard(); let mut x = build_checkerboard();
x.scroll_down(1); x.scroll_down(1);
assert_eq!(read_test_result("test_video_scroll_down_1.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_video_scroll_down_1.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_scroll_down_10_row_test() { fn video_scroll_down_10_row_test() {
let mut x = build_checkerboard(); let mut x = build_checkerboard();
x.scroll_down(10); x.scroll_down(10);
assert_eq!(read_test_result("test_video_scroll_down_10.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_video_scroll_down_10.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_high_res_has_right_resolution() { fn video_high_res_has_right_resolution() {
let x = build_checkboard_hd(); let x = build_checkboard_hd();
println!("[{}]", x.format_as_string()); println!("[{}]", x.format_as_string());
assert_eq!(read_test_result("test_video_highdef.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_video_highdef.asc"),
x.format_as_string()
);
} }
#[test] #[test]
@ -1197,45 +1252,62 @@ fn video_scroll_down_1_row_test_schip() {
println!("[{}]", x.format_as_string()); println!("[{}]", x.format_as_string());
println!("[{}]", read_test_result("test_scroll_down_1_hd.asc")); println!("[{}]", read_test_result("test_scroll_down_1_hd.asc"));
assert_eq!(read_test_result("test_scroll_down_1_hd.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_scroll_down_1_hd.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_scroll_down_10_row_test_schip() { fn video_scroll_down_10_row_test_schip() {
let mut x = build_checkboard_hd(); let mut x = build_checkboard_hd();
x.scroll_down(10); x.scroll_down(10);
assert_eq!(read_test_result("test_scroll_down_10_hd.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_scroll_down_10_hd.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_scroll_left_4_row_test_std_def() { fn video_scroll_left_4_row_test_std_def() {
let mut x = build_checkerboard(); let mut x = build_checkerboard();
x.scroll_left(); x.scroll_left();
assert_eq!(read_test_result("test_scroll_left_4.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_scroll_left_4.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_scroll_left_4_row_test_high_def() { fn video_scroll_left_4_row_test_high_def() {
let mut x = build_checkboard_hd(); let mut x = build_checkboard_hd();
x.scroll_left(); x.scroll_left();
assert_eq!(read_test_result("test_scroll_left_4_hd.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_scroll_left_4_hd.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_scroll_right_4_row_test_std_def() { fn video_scroll_right_4_row_test_std_def() {
let mut x = build_checkerboard(); let mut x = build_checkerboard();
x.scroll_right(); x.scroll_right();
assert_eq!(read_test_result("test_scroll_right_4.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_scroll_right_4.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn video_scroll_right_4_row_test_high_def() { fn video_scroll_right_4_row_test_high_def() {
let mut x = build_checkboard_hd(); let mut x = build_checkboard_hd();
x.scroll_right(); x.scroll_right();
assert_eq!(read_test_result("test_scroll_right_4_hd.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_scroll_right_4_hd.asc"),
x.format_as_string()
);
} }
#[test] #[test]
fn instructions_operands_tests() { fn instructions_operands_tests() {
assert_eq!(Chip8CpuInstructions::SYS(0x000).operands(), "0x0000"); assert_eq!(Chip8CpuInstructions::SYS(0x000).operands(), "0x0000");
@ -1267,26 +1339,38 @@ fn instruction_test_scrolling_lowres() {
x.video_memory = build_checkerboard(); x.video_memory = build_checkerboard();
x.quirk_mode = quirk.clone(); x.quirk_mode = quirk.clone();
Chip8CpuInstructions::SCR.execute(&mut x); Chip8CpuInstructions::SCR.execute(&mut x);
assert_eq!(read_test_result("test_scroll_right_4.asc"), x.dump_video_to_string()); assert_eq!(
read_test_result("test_scroll_right_4.asc"),
x.dump_video_to_string()
);
x = Chip8Computer::new(); x = Chip8Computer::new();
x.video_memory = build_checkerboard(); x.video_memory = build_checkerboard();
x.quirk_mode = quirk.clone(); x.quirk_mode = quirk.clone();
Chip8CpuInstructions::SCL.execute(&mut x); Chip8CpuInstructions::SCL.execute(&mut x);
assert_eq!(read_test_result("test_scroll_left_4.asc"), x.dump_video_to_string()); assert_eq!(
read_test_result("test_scroll_left_4.asc"),
x.dump_video_to_string()
);
x = Chip8Computer::new(); x = Chip8Computer::new();
x.video_memory = build_checkerboard(); x.video_memory = build_checkerboard();
x.quirk_mode = quirk.clone(); x.quirk_mode = quirk.clone();
Chip8CpuInstructions::SCD(0x01).execute(&mut x); Chip8CpuInstructions::SCD(0x01).execute(&mut x);
assert_eq!(read_test_result("test_video_scroll_down_1.asc"), x.dump_video_to_string()); assert_eq!(
read_test_result("test_video_scroll_down_1.asc"),
x.dump_video_to_string()
);
x = Chip8Computer::new(); x = Chip8Computer::new();
x.video_memory = build_checkerboard(); x.video_memory = build_checkerboard();
x.quirk_mode = quirk.clone(); x.quirk_mode = quirk.clone();
Chip8CpuInstructions::SCD(0xA).execute(&mut x); Chip8CpuInstructions::SCD(0xA).execute(&mut x);
assert_eq!(read_test_result("test_video_scroll_down_10.asc"), x.dump_video_to_string()); assert_eq!(
read_test_result("test_video_scroll_down_10.asc"),
x.dump_video_to_string()
);
} }
} }
@ -1295,17 +1379,18 @@ fn computer_dump_keypad_to_string() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
x.keypad.push_key(0x1); x.keypad.push_key(0x1);
x.keypad.push_key(0x2); x.keypad.push_key(0x2);
assert_eq!(read_test_result("test_keypad_to_string.asc"), x.dump_keypad_to_string()); assert_eq!(
read_test_result("test_keypad_to_string.asc"),
x.dump_keypad_to_string()
);
} }
#[test] #[test]
fn computer_dump_registers_to_string() { fn computer_dump_registers_to_string() {
let mut x = Chip8Computer::new(); let mut x = Chip8Computer::new();
let values_to_set = [0x0b, 0xad, 0xbe, 0xef, let values_to_set = [
0xca, 0xb0, 0x0b, 0xad, 0xbe, 0xef, 0xca, 0xb0, 0x7a, 0xc0, 0xca, 0x70, 0xba, 0xdb, 0xed, 0x00, 0x00,
0x7a, 0xc0, 0xca, 0x70, 0x00,
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"; 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";
@ -1322,7 +1407,10 @@ fn video_scroll_up_tests_sd() {
let mut x = build_checkerboard(); let mut x = build_checkerboard();
let distance = 1u8; let distance = 1u8;
x.scroll_up(&distance); x.scroll_up(&distance);
assert_eq!(read_test_result("test_video_scroll_up_test_sd.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_video_scroll_up_test_sd.asc"),
x.format_as_string()
);
} }
#[test] #[test]
@ -1330,7 +1418,10 @@ fn video_scroll_up_tests_hd() {
let mut x = build_checkboard_hd(); let mut x = build_checkboard_hd();
let distance = 1u8; let distance = 1u8;
x.scroll_up(&distance); x.scroll_up(&distance);
assert_eq!(read_test_result("test_video_scroll_up_test_hd.asc"), x.format_as_string()); assert_eq!(
read_test_result("test_video_scroll_up_test_hd.asc"),
x.format_as_string()
);
} }
#[test] #[test]
@ -1339,7 +1430,10 @@ fn video_resolution_changing() {
x.set_highres(); x.set_highres();
assert_eq!(x.get_resolution(), (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT)); assert_eq!(x.get_resolution(), (SCHIP_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT));
assert!(matches!(x.get_screen_resolution(), Chip8VideoModes::HighRes)); assert!(matches!(
x.get_screen_resolution(),
Chip8VideoModes::HighRes
));
x.set_lowres(); x.set_lowres();
assert_eq!(x.get_resolution(), (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT)); assert_eq!(x.get_resolution(), (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT));
assert!(matches!(x.get_screen_resolution(), Chip8VideoModes::LowRes)); assert!(matches!(x.get_screen_resolution(), Chip8VideoModes::LowRes));

View File

@ -33,7 +33,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: SYS(0x123), chip8: SYS(0x123),
schip: XXXXERRORINSTRUCTION, schip: XXXXERRORINSTRUCTION,
xochip: XXXXERRORINSTRUCTION, xochip: XXXXERRORINSTRUCTION,
} },
}, },
InstructionTest { InstructionTest {
name: INST_CLS.to_string(), name: INST_CLS.to_string(),
@ -44,8 +44,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: CLS, chip8: CLS,
schip: CLS, schip: CLS,
xochip: CLS xochip: CLS,
} },
}, },
InstructionTest { InstructionTest {
name: INST_RET.to_string(), name: INST_RET.to_string(),
@ -56,8 +56,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: RET, chip8: RET,
schip: RET, schip: RET,
xochip: RET xochip: RET,
} },
}, },
InstructionTest { InstructionTest {
name: INST_JPX.to_string(), name: INST_JPX.to_string(),
@ -81,7 +81,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: CALL(0x123), chip8: CALL(0x123),
schip: CALL(0x123), schip: CALL(0x123),
xochip: CALL(0x123), xochip: CALL(0x123),
} },
}, },
InstructionTest { InstructionTest {
name: INST_DRW.to_string(), name: INST_DRW.to_string(),
@ -92,8 +92,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: DRW(0x1, 0x2, 0x3), chip8: DRW(0x1, 0x2, 0x3),
schip: DRW(0x1, 0x2, 0x3), schip: DRW(0x1, 0x2, 0x3),
xochip: DRW(0x1, 0x2, 0x3) xochip: DRW(0x1, 0x2, 0x3),
} },
}, },
InstructionTest { InstructionTest {
name: INST_JPI.to_string(), name: INST_JPI.to_string(),
@ -104,8 +104,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: JPI(0x321), chip8: JPI(0x321),
schip: XXXXERRORINSTRUCTION, schip: XXXXERRORINSTRUCTION,
xochip: JPI(0x321) xochip: JPI(0x321),
} },
}, },
InstructionTest { InstructionTest {
name: INST_SCD.to_string(), name: INST_SCD.to_string(),
@ -116,8 +116,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: SCD(0x1), schip: SCD(0x1),
xochip: SCD(0x1) xochip: SCD(0x1),
} },
}, },
InstructionTest { InstructionTest {
name: INST_SCR.to_string(), name: INST_SCR.to_string(),
@ -128,8 +128,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: SCR, schip: SCR,
xochip: SCR xochip: SCR,
} },
}, },
InstructionTest { InstructionTest {
name: INST_SCL.to_string(), name: INST_SCL.to_string(),
@ -140,8 +140,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: SCL, schip: SCL,
xochip: SCL xochip: SCL,
} },
}, },
InstructionTest { InstructionTest {
name: INST_EXIT.to_string(), name: INST_EXIT.to_string(),
@ -152,8 +152,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: EXIT, schip: EXIT,
xochip: EXIT xochip: EXIT,
} },
}, },
InstructionTest { InstructionTest {
name: INST_LOW.to_string(), name: INST_LOW.to_string(),
@ -165,7 +165,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: LOW, schip: LOW,
xochip: LOW, xochip: LOW,
} },
}, },
InstructionTest { InstructionTest {
name: INST_HIGH.to_string(), name: INST_HIGH.to_string(),
@ -176,8 +176,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: HIGH, schip: HIGH,
xochip: HIGH xochip: HIGH,
} },
}, },
InstructionTest { InstructionTest {
name: INST_SEX.to_string(), name: INST_SEX.to_string(),
@ -188,7 +188,7 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: Chip8CpuInstructions::SEX(0x1, 0xfa), chip8: Chip8CpuInstructions::SEX(0x1, 0xfa),
schip: Chip8CpuInstructions::SEX(0x1, 0xfa), schip: Chip8CpuInstructions::SEX(0x1, 0xfa),
xochip: Chip8CpuInstructions::SEX(0x1, 0xfa) xochip: Chip8CpuInstructions::SEX(0x1, 0xfa),
}, },
}, },
InstructionTest { InstructionTest {
@ -212,8 +212,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: SEY(0x1, 0x2), chip8: SEY(0x1, 0x2),
schip: SEY(0x1, 0x2), schip: SEY(0x1, 0x2),
xochip: SEY(0x1, 0x2) xochip: SEY(0x1, 0x2),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDR.to_string(), name: INST_LDR.to_string(),
@ -224,7 +224,7 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: LDR(0xa, 0xbe), chip8: LDR(0xa, 0xbe),
schip: LDR(0xa, 0xbe), schip: LDR(0xa, 0xbe),
xochip: LDR(0xa, 0xbe) xochip: LDR(0xa, 0xbe),
}, },
}, },
InstructionTest { InstructionTest {
@ -284,7 +284,7 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: XXXXERRORINSTRUCTION, schip: XXXXERRORINSTRUCTION,
xochip: SCU(0x04) xochip: SCU(0x04),
}, },
}, },
InstructionTest { InstructionTest {
@ -297,7 +297,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: ADDR(0x01, 0x02), chip8: ADDR(0x01, 0x02),
schip: ADDR(0x01, 0x02), schip: ADDR(0x01, 0x02),
xochip: ADDR(0x01, 0x02), xochip: ADDR(0x01, 0x02),
} },
}, },
InstructionTest { InstructionTest {
name: INST_AND.to_string(), name: INST_AND.to_string(),
@ -333,8 +333,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: BCD(0xcd), chip8: BCD(0xcd),
schip: BCD(0xcd), schip: BCD(0xcd),
xochip: BCD(0xcd) xochip: BCD(0xcd),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDD.to_string(), name: INST_LDD.to_string(),
@ -346,7 +346,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: LDD(0xfc), chip8: LDD(0xfc),
schip: LDD(0xfc), schip: LDD(0xfc),
xochip: LDD(0xfc), xochip: LDD(0xfc),
} },
}, },
InstructionTest { InstructionTest {
name: INST_ORY.to_string(), name: INST_ORY.to_string(),
@ -417,9 +417,10 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: SHR(0x04, 0x4), chip8: SHR(0x04, 0x4),
schip: SHR(0x04, 0x4), schip: SHR(0x04, 0x4),
xochip: SHR(0x04, 0x4) xochip: SHR(0x04, 0x4),
} },
}, InstructionTest { },
InstructionTest {
name: INST_SHL.to_string(), name: INST_SHL.to_string(),
instruction: SHL(0x04, 0x4), instruction: SHL(0x04, 0x4),
operands: "0x4, 0x4".to_string(), operands: "0x4, 0x4".to_string(),
@ -428,8 +429,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: SHL(0x04, 0x4), chip8: SHL(0x04, 0x4),
schip: SHL(0x04, 0x4), schip: SHL(0x04, 0x4),
xochip: SHL(0x04, 0x4) xochip: SHL(0x04, 0x4),
} },
}, },
InstructionTest { InstructionTest {
name: INST_RND.to_string(), name: INST_RND.to_string(),
@ -441,19 +442,19 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: RND(0x01, 0xff), chip8: RND(0x01, 0xff),
schip: RND(0x01, 0xff), schip: RND(0x01, 0xff),
xochip: RND(0x01, 0xff), xochip: RND(0x01, 0xff),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDRY.to_string(), name: INST_LDRY.to_string(),
instruction: LDR_Y(0x01, 0x02), instruction: LDRY(0x01, 0x02),
operands: "0x1, 0x2".to_string(), operands: "0x1, 0x2".to_string(),
asm: "LDRY 0x1, 0x2".to_string(), asm: "LDRY 0x1, 0x2".to_string(),
encoded: 0x8120, encoded: 0x8120,
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: LDR_Y(0x01, 0x02), chip8: LDRY(0x01, 0x02),
schip: LDR_Y(0x01, 0x02), schip: LDRY(0x01, 0x02),
xochip: LDR_Y(0x01, 0x02) xochip: LDRY(0x01, 0x02),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDIS.to_string(), name: INST_LDIS.to_string(),
@ -465,7 +466,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: LDIS(0x01), chip8: LDIS(0x01),
schip: LDIS(0x01), schip: LDIS(0x01),
xochip: LDIS(0x01), xochip: LDIS(0x01),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LIDR.to_string(), name: INST_LIDR.to_string(),
@ -477,7 +478,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: LIDR(0x01), schip: LIDR(0x01),
xochip: LIDR(0x01), xochip: LIDR(0x01),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDF2.to_string(), name: INST_LDF2.to_string(),
@ -488,8 +489,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: XXXXERRORINSTRUCTION, chip8: XXXXERRORINSTRUCTION,
schip: LDF2(0x01), schip: LDF2(0x01),
xochip: LDF2(0x01) xochip: LDF2(0x01),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDF.to_string(), name: INST_LDF.to_string(),
@ -500,8 +501,8 @@ fn instructions_encode_decode_tests_with_quirks() {
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: LDFX(0x01), chip8: LDFX(0x01),
schip: LDFX(0x01), schip: LDFX(0x01),
xochip: LDFX(0x01) xochip: LDFX(0x01),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDIA.to_string(), name: INST_LDIA.to_string(),
@ -513,7 +514,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: LDIA(0x01), chip8: LDIA(0x01),
schip: LDIA(0x01), schip: LDIA(0x01),
xochip: LDIA(0x01), xochip: LDIA(0x01),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDIX.to_string(), name: INST_LDIX.to_string(),
@ -525,7 +526,7 @@ fn instructions_encode_decode_tests_with_quirks() {
chip8: LDIX(0x01), chip8: LDIX(0x01),
schip: LDIX(0x01), schip: LDIX(0x01),
xochip: LDIX(0x01), xochip: LDIX(0x01),
} },
}, },
InstructionTest { InstructionTest {
name: INST_LDRD.to_string(), name: INST_LDRD.to_string(),
@ -558,10 +559,10 @@ fn instructions_encode_decode_tests_with_quirks() {
asm: "SKP 0x01".to_string(), asm: "SKP 0x01".to_string(),
encoded: 0xe19e, encoded: 0xe19e,
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: SKP(0x01), chip8: SKP(0x01),
schip: SKP(0x01), schip: SKP(0x01),
xochip: SKP(0x01), xochip: SKP(0x01),
} },
}, },
InstructionTest { InstructionTest {
name: INST_SNEY.to_string(), name: INST_SNEY.to_string(),
@ -570,10 +571,10 @@ fn instructions_encode_decode_tests_with_quirks() {
asm: "SNEY 0x1, 0x1".to_string(), asm: "SNEY 0x1, 0x1".to_string(),
encoded: 0x9110, encoded: 0x9110,
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: SNEY(0x01, 0x1), chip8: SNEY(0x01, 0x1),
schip: SNEY(0x01, 0x1), schip: SNEY(0x01, 0x1),
xochip: SNEY(0x01, 0x1), xochip: SNEY(0x01, 0x1),
} },
}, },
InstructionTest { InstructionTest {
name: INST_SKNP.to_string(), name: INST_SKNP.to_string(),
@ -582,10 +583,10 @@ fn instructions_encode_decode_tests_with_quirks() {
asm: "SKNP 0x01".to_string(), asm: "SKNP 0x01".to_string(),
encoded: 0xe1a1, encoded: 0xe1a1,
quirks: InstructionTestQuirks { quirks: InstructionTestQuirks {
chip8: SKNP(0x01), chip8: SKNP(0x01),
schip: SKNP(0x01), schip: SKNP(0x01),
xochip: SKNP(0x01), xochip: SKNP(0x01),
} },
}, },
]; ];
@ -601,9 +602,17 @@ fn instructions_encode_decode_tests_with_quirks() {
// ** CONVERSION ** // ** CONVERSION **
// -> Integer to Instruction // -> Integer to Instruction
assert!(matches!(Chip8CpuInstructions::decode(current.encoded, &Chip8), i)); assert!(matches!(
Chip8CpuInstructions::decode(current.encoded, &Chip8),
i
));
// -> Instruction to Integer // -> Instruction to Integer
println!("TESTING INSTRUCTION TO INTEGER FOR {:?} / {:04x} {:04x}", current.instruction, current.encoded, instruction.encode()); println!(
"TESTING INSTRUCTION TO INTEGER FOR {:?} / {:04x} {:04x}",
current.instruction,
current.encoded,
instruction.encode()
);
assert_eq!(current.encoded, instruction.encode()); assert_eq!(current.encoded, instruction.encode());
// -> Instruction to String // -> Instruction to String
assert_eq!(instruction.to_string(), current.asm); assert_eq!(instruction.to_string(), current.asm);