more coverage

This commit is contained in:
2025-05-27 10:02:13 -04:00
parent d27d2e8e45
commit fdf09daf0f
19 changed files with 1518 additions and 728 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "gemma"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
autobenches = true
-56
View File
@@ -1,56 +0,0 @@
use clap::{Parser, Subcommand};
use flate2::{write::{GzEncoder, GzDecoder}, Compression};
use std::io::Write;
#[derive(Parser)]
#[command(author, version, about = "Compress or decompress a string", long_about = None)]
struct Cli {
/// Subcommand: either 'compress' or 'decompress'
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Compress the input string
Compress {
/// The string to compress
input: String,
},
/// Decompress the input string (must be compressed format)
Decompress {
/// The compressed string to decompress, in hex format
input: String,
},
}
fn compress_string(input: &str) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder.write_all(input.as_bytes()).expect("Compression failed");
encoder.finish().expect("Failed to finish compression")
}
fn decompress_string(input: &[u8]) -> String {
let mut decoder = GzDecoder::new(Vec::new());
decoder.write_all(input).expect("Decompression failed");
let decompressed_data = decoder.finish().expect("Failed to finish decompression");
String::from_utf8(decompressed_data).expect("Invalid UTF-8")
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::Compress { input } => {
let compressed = compress_string(&input);
// Convert to hex format for easier readability in the terminal
println!("Compressed (hex): {:?} / from {}b to {}b", hex::encode(compressed.clone()), input.len(), compressed.len());
}
Commands::Decompress { input } => {
// Decode hex string back to bytes
let compressed_bytes = hex::decode(input).expect("Invalid hex input");
let decompressed = decompress_string(&compressed_bytes);
println!("Decompressed: {}", decompressed);
}
}
}
+125
View File
@@ -0,0 +1,125 @@
use std::fs;
use std::fs::File;
use clap::{Parser, Subcommand};
use flate2::{write::{GzEncoder, GzDecoder}, Compression};
use std::path::PathBuf;
use std::io::prelude::*;
use std::io::Read;
use std::io::Write;
use flate2::write::ZlibEncoder;
use gemma::constants::TEST_ROM_ROOT;
#[derive(Parser)]
#[command(author, version, about = "Compress or decompress a string", long_about = None)]
struct Cli {
/// Subcommand: either 'compress' or 'decompress'
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
/// Compress the input string
Compress {
/// The string to compress
input: PathBuf,
},
/// Decompress the input string (must be compressed format)
Decompress {
/// The compressed string to decompress, in hex format
input: PathBuf,
}
}
/// Compresses raw binary data using Gzip and returns the compressed bytes.
///
/// # Arguments
///
/// * `data` - A `Vec<u8>` containing the uncompressed binary data.
///
/// # Returns
///
/// A `Vec<u8>` with the compressed data. Panics if compression fails.
pub fn compress_data(data: Vec<u8>) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder
.write_all(&data)
.expect("Failed to write data to encoder");
encoder.finish().expect("Failed to finish compression")
}
/// Decompresses Gzip-compressed binary data and returns the uncompressed bytes.
///
/// # Arguments
///
/// * `data` - A `Vec<u8>` containing Gzip-compressed data.
///
/// # Returns
///
/// A `Vec<u8>` with the decompressed data. Panics if decompression fails.
// pub fn decompress_data(data: Vec<u8>) -> Vec<u8> {
// let mut decoder = GzDecoder::new( &data[..]);
// let mut decompressed = Vec::new();
// decoder.write_all(&mut decompressed)
// .expect("Failed to decompress data");
// decompressed.clone().to_vec()
// }
fn compress_file(input_path: &PathBuf) {
let target_file_name = format!("{}.tflt", input_path.display());
println!("TARGET_FILE_NAME: {}", target_file_name);
let input_data = fs::read(input_path).expect("Failed to read input file");
let compressed_data = compress_data(input_data);
let target_file = File::create(target_file_name);
target_file.unwrap().write_all(&compressed_data).unwrap()
}
fn decompress_file(input_path: &PathBuf) {
let target_file_name = format!("{}.uncompressed", input_path.display());
println!("Writing decompressed data to {}", target_file_name);
let input_data = fs::read(input_path).expect("Failed to read compressed file.");
// let decompressed_data = decompress_data(input_data);
// let mut target_file = File::create(&target_file_name).expect(format!("Unable to create uncompressed file -> {}", target_file_name.clone()).as_str());
//target_file.write_all(&decompressed_data).expect(format!("Unable to write uncompressed file -> {}", target_file_name).as_str());
//println!("Decompressed: {:?}", String::from_utf8_lossy(&decompressed_data));
}
fn main() {
let filename = format!("{}/gemma/{}/2-ibm-logo.ch8", std::env::current_dir().unwrap().display(), TEST_ROM_ROOT);
println!("READING {} from {}", filename, std::env::current_dir().unwrap().display());
let mut file = File::open(filename).unwrap();
let mut file_data = Vec::new();
file.read_to_end(&mut file_data).expect("unable to read rom");
let mut e = ZlibEncoder::new(Vec::new(), Compression::default());
e.write_all(b"foo");
e.write_all(b"bar");
let compressed_bytes = e.finish();
println!("COMPRESSED: {compressed_bytes:?}");
//
// let cli = Cli::parse();
//
// match cli.command {
// Commands::Compress { input } => {
// let compressed = compress_string(&input);
// // Convert to hex format for easier readability in the terminal
// println!("Compressed (hex): {:?} / from {}b to {}b", hex::encode(compressed.clone()), input.len(), compressed.len());
// }
// Commands::Decompress { input } => {
// // Decode hex string back to bytes
// let compressed_bytes = hex::decode(input).expect("Invalid hex input");
// let decompressed = decompress_string(&compressed_bytes);
// println!("Decompressed: {}", decompressed);
// }
// Commands::DecompressFile { input } => {
// decompress_file(&input);
// }
// Commands::CompressFile { input } => {
// compress_file(&input);
// }
// }
}
+10 -13
View File
@@ -6,7 +6,6 @@ use crate::chip8::sound_timer::SoundTimer;
use crate::chip8::stack::Chip8Stack;
use log::debug;
use serde::{Deserialize, Serialize};
use super::{
cpu_states::Chip8CpuStates, instructions::Chip8CpuInstructions,
system_memory::Chip8SystemMemory, video::Chip8Video,
@@ -25,6 +24,16 @@ pub struct Chip8Computer {
pub stack: Chip8Stack,
pub quirk_mode: QuirkMode,
}
impl PartialEq for Chip8Computer {
fn eq(&self, other: &Self) -> bool {
self.video_memory.format_as_string() == other.video_memory.format_as_string() &&
self.keypad.format_as_string() == other.keypad.format_as_string() &&
self.num_cycles == other.num_cycles &&
self.memory == other.memory
}
}
impl Default for Chip8Computer {
fn default() -> Self {
Self {
@@ -66,18 +75,6 @@ impl Chip8Computer {
self.clone().video_memory.format_as_string()
}
pub fn new_with_program(new_program: Vec<u16>) -> Self {
let mut working = Chip8Computer::new();
for (i, &word) in new_program.iter().enumerate() {
let high = (word >> 8) as u8;
let low = (word & 0xff) as u8;
let base_offset = (i * 2) as u16;
working.memory.poke(base_offset, high);
working.memory.poke(base_offset + 1, low);
}
working
}
pub fn new() -> Self {
Chip8Computer::default()
}
+1 -2
View File
@@ -39,9 +39,8 @@ impl Chip8ComputerManager {
/// an idea of the internal state of the Chip-8 Computer
pub fn status_as_string(&self) -> String {
// build a string
format!("Should Run: {}\nLast Cycle Start: {:?}\nNum Cycles: {}\nRegisters\n{}\nTimers: {}/{}\nKeypad: \n{}\nVideo:\n{}",
format!("Should Run: {}\nNum Cycles: {}\nRegisters\n{}\nTimers: {}/{}\nKeypad: \n{}\nVideo:\n{}",
self.core_should_run,
self.core_last_cycle_start,
self.computer.num_cycles,
self.computer.registers.format_as_string(),
self.computer.delay_timer.current(),
+53 -53
View File
@@ -279,50 +279,50 @@ pub enum Chip8CpuInstructions {
impl Chip8CpuInstructions {
pub fn name(&self) -> &str {
match self {
Chip8CpuInstructions::ADDI(_) => INST_ADDI,
Chip8CpuInstructions::ADD(_, _) => INST_ADD,
Chip8CpuInstructions::ADDR(_, _) => INST_ADDR,
Chip8CpuInstructions::AND(_, _) => INST_AND,
Chip8CpuInstructions::CLS => INST_CLS,
Chip8CpuInstructions::CALL(_) => INST_CALL,
Chip8CpuInstructions::DRW(_, _, _) => INST_DRW,
Chip8CpuInstructions::EXIT => INST_EXIT,
Chip8CpuInstructions::JPA(_) => INST_JPA,
Chip8CpuInstructions::JPI(_) => INST_JPI,
Chip8CpuInstructions::BCD(_) => INST_BCD,
Chip8CpuInstructions::LDD(_) => INST_LDD,
Chip8CpuInstructions::LDFX(_) => INST_LDF,
Chip8CpuInstructions::LDF2(_) => INST_LDF2,
Chip8CpuInstructions::LDIA(_) => INST_LDIA,
Chip8CpuInstructions::LDIX(_) => INST_LDIX,
Chip8CpuInstructions::LIDR(_) => INST_LIDR,
Chip8CpuInstructions::LDIS(_) => INST_LDIS,
Chip8CpuInstructions::LDR(_, _) => INST_LDR,
Chip8CpuInstructions::LDRD(_) => INST_LDRD,
Chip8CpuInstructions::LDRI(_) => INST_LDRI,
Chip8CpuInstructions::LDRK(_) => INST_LDRK,
Chip8CpuInstructions::LDRY(_, _) => INST_LDRY,
Chip8CpuInstructions::OR(_, _) => INST_OR,
Chip8CpuInstructions::RET => INST_RET,
Chip8CpuInstructions::RND(_, _) => INST_RND,
Chip8CpuInstructions::SCD(_) => INST_SCD,
Chip8CpuInstructions::SCL => INST_SCL,
Chip8CpuInstructions::SCR => INST_SCR,
Chip8CpuInstructions::SEX(_, _) => INST_SEX,
Chip8CpuInstructions::SEY(_, _) => INST_SEY,
Chip8CpuInstructions::SHL(_, _) => INST_SHL,
Chip8CpuInstructions::SHR(_, _) => INST_SHR,
Chip8CpuInstructions::SKP(_) => INST_SKP,
Chip8CpuInstructions::SNEB(_, _) => INST_SNEB,
Chip8CpuInstructions::SNEY(_, _) => INST_SNEY,
Chip8CpuInstructions::SKNP(_) => INST_SKNP,
Chip8CpuInstructions::STR(_) => INST_STR,
Chip8CpuInstructions::SUB(_, _) => INST_SUB,
Chip8CpuInstructions::SUBC(_, _) => INST_SUBC,
Chip8CpuInstructions::SYS(_) => INST_SYS,
Chip8CpuInstructions::LOW => INST_LOW,
Chip8CpuInstructions::HIGH => INST_HIGH,
Chip8CpuInstructions::ORY(_, _) => INST_ORY,
ADDI(_) => INST_ADDI,
ADD(_, _) => INST_ADD,
ADDR(_, _) => INST_ADDR,
AND(_, _) => INST_AND,
CLS => INST_CLS,
CALL(_) => INST_CALL,
DRW(_, _, _) => INST_DRW,
EXIT => INST_EXIT,
JPA(_) => INST_JPA,
JPI(_) => INST_JPI,
BCD(_) => INST_BCD,
LDD(_) => INST_LDD,
LDFX(_) => INST_LDF,
LDF2(_) => INST_LDF2,
LDIA(_) => INST_LDIA,
LDIX(_) => INST_LDIX,
LIDR(_) => INST_LIDR,
LDIS(_) => INST_LDIS,
LDR(_, _) => INST_LDR,
LDRD(_) => INST_LDRD,
LDRI(_) => INST_LDRI,
LDRK(_) => INST_LDRK,
LDRY(_, _) => INST_LDRY,
OR(_, _) => INST_OR,
RET => INST_RET,
RND(_, _) => INST_RND,
SCD(_) => INST_SCD,
SCL => INST_SCL,
SCR => INST_SCR,
SEX(_, _) => INST_SEX,
SEY(_, _) => INST_SEY,
SHL(_, _) => INST_SHL,
SHR(_, _) => INST_SHR,
SKP(_) => INST_SKP,
SNEB(_, _) => INST_SNEB,
SNEY(_, _) => INST_SNEY,
SKNP(_) => INST_SKNP,
STR(_) => INST_STR,
SUB(_, _) => INST_SUB,
SUBC(_, _) => INST_SUBC,
SYS(_) => INST_SYS,
LOW => INST_LOW,
HIGH => INST_HIGH,
ORY(_, _) => INST_ORY,
JPX(_, _) => INST_JPX,
XXXXERRORINSTRUCTION => "XX ERROR XX",
SCU(_) => INST_SCU,
@@ -502,15 +502,15 @@ impl Chip8CpuInstructions {
pub fn encode(&self) -> u16 {
match self {
Chip8CpuInstructions::SYS(target) => target & 0x0FFF,
Chip8CpuInstructions::CLS => 0x00E0,
Chip8CpuInstructions::RET => 0x00EE,
Chip8CpuInstructions::JPA(new_addr) => 0x1000 | (new_addr & 0x0FFF),
Chip8CpuInstructions::CALL(address) => 0x2000 | (address & 0x0FFF),
Chip8CpuInstructions::SEX(vx_register, byte) => {
SYS(target) => target & 0x0FFF,
CLS => 0x00E0,
RET => 0x00EE,
JPA(new_addr) => 0x1000 | (new_addr & 0x0FFF),
CALL(address) => 0x2000 | (address & 0x0FFF),
SEX(vx_register, byte) => {
0x3000 | ((*vx_register as u16) << 8) | (*byte as u16)
}
Chip8CpuInstructions::SNEB(vx_register, byte) => {
SNEB(vx_register, byte) => {
0x4000 | ((*vx_register as u16) << 8) | (*byte as u16)
}
Chip8CpuInstructions::SEY(x_register, y_register) => {
@@ -586,7 +586,7 @@ impl Chip8CpuInstructions {
Chip8CpuInstructions::LIDR(x_register) => 0xF085 | ((*x_register as u16) << 8),
SCU(x_register) => 0x00D0 | (*x_register as u16),
DW(address) => *address as u16,
XXXXERRORINSTRUCTION => 0x0000,
XXXXERRORINSTRUCTION => XXXXERRORINSTRUCTION_ENCODED,
}
}
pub fn decode(input: u16, quirk_mode: &QuirkMode) -> Chip8CpuInstructions {
@@ -903,7 +903,7 @@ impl Chip8CpuInstructions {
// 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 mut rng = rand::thread_rng();
let mut rng = rand::rng();
let new_value: u8 = rng.random();
let and_value: u8 = *byte;
let result = new_value & and_value;
+6 -5
View File
@@ -1,20 +1,21 @@
use serde::{Deserialize, Serialize};
use std::fmt::Display;
use crate::constants::{LABEL_QUIRK_CHIP8, LABEL_QUIRK_SCHIP, LABEL_QUIRK_XOCHIP};
#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug)]
#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug, PartialEq)]
pub enum QuirkMode {
#[default]
Chip8,
XOChip,
#[default]
SChipModern,
}
impl Display for QuirkMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let variant = match &self {
QuirkMode::Chip8 => "Chip8".to_string(),
QuirkMode::XOChip => "XO Chip".to_string(),
QuirkMode::SChipModern => "SChip-Modern".to_string(),
QuirkMode::Chip8 => LABEL_QUIRK_CHIP8.to_string(),
QuirkMode::XOChip => LABEL_QUIRK_XOCHIP.to_string(),
QuirkMode::SChipModern => LABEL_QUIRK_SCHIP.to_string(),
};
write!(f, "{}", variant)
}
+6
View File
@@ -9,6 +9,12 @@ pub struct Chip8Registers {
pub pc: u16,
}
impl PartialEq for Chip8Registers {
fn eq(&self, other: &Self) -> bool {
self.pc == other.pc && self.registers == other.registers && self.i_register == other.i_register
}
}
impl Chip8Registers {
pub fn advance_pc(&mut self) {
self.pc += 2;
+6
View File
@@ -11,6 +11,12 @@ pub struct Chip8SystemMemory {
memory: Vec<u8>,
}
impl PartialEq for Chip8SystemMemory {
fn eq(&self, other: &Self) -> bool {
self.memory == other.memory
}
}
impl Default for Chip8SystemMemory {
fn default() -> Self {
let mut x = Chip8SystemMemory::new();
+1 -5
View File
@@ -3,7 +3,6 @@ use crate::constants::{
CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH, SCHIP_VIDEO_HEIGHT,
SCHIP_VIDEO_WIDTH, SCHIP_VIDE_MEMORY,
};
use log::debug;
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Serialize, Deserialize, Debug)]
@@ -120,10 +119,7 @@ impl Chip8Video {
for row in 0..height {
for column in 0..width {
let data_offset = row * width + column;
debug!(
"Rendering {data_offset} with value {}",
self.memory[data_offset as usize]
);
if self.memory[data_offset as usize] {
output += "*"
} else {
+9 -1
View File
@@ -5,8 +5,13 @@ pub const CHIP8_VIDEO_HEIGHT: i32 = 32i32;
pub const CHIP8_VIDEO_MEMORY: usize = (CHIP8_VIDEO_HEIGHT * CHIP8_VIDEO_WIDTH) as usize;
pub const CHIP8_ROM_SIZE: usize = 512;
pub const LABEL_QUIRK_CHIP8: &str = "Chip8";
pub const LABEL_QUIRK_XOCHIP: &str = "XO Chip";
pub const LABEL_QUIRK_SCHIP: &str = "SChip-Modern";
pub const RESOURCES_ROOT: &str = "../resources";
pub const TESTS_ROOT: &str = "../resources/tests/";
pub const TESTS_ROOT: &str = "../resources/test/";
pub const TEST_ROM_ROOT: &str = "../resources/test/roms";
pub const CHIP8_KEYBOARD: [[u8; 4]; 4] = [
[0x01, 0x02, 0x03, 0x0C],
@@ -69,6 +74,8 @@ pub const INST_SCU: &str = "SCU";
/// Data to be loaded to memory for application use
pub const INST_DW: &str = "DW";
pub const XXXXERRORINSTRUCTION_ENCODED: u16 = 0x0000;
pub const CHIP8_PROGRAM_LOAD_OFFSET: i32 = 0x200;
pub const CHIP8FONT_0: [u8; 5] = [0xF0, 0x90, 0x90, 0x90, 0xF0];
pub const CHIP8FONT_1: [u8; 5] = [0x20, 0x60, 0x20, 0x20, 0x70];
@@ -104,3 +111,4 @@ pub const SCHIPFONT_C: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0
pub const SCHIPFONT_D: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xE0];
pub const SCHIPFONT_E: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0];
pub const SCHIPFONT_F: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x80, 0x80, 0x80, 0x80, 0x80];
+78 -4
View File
@@ -1,5 +1,11 @@
use std::path::Path;
use std::time::Instant;
use gemma::chip8::computer::Chip8Computer;
use gemma::constants::CHIP8_VIDEO_MEMORY;
use gemma::chip8::computer_manager::Chip8ComputerManager;
use gemma::chip8::quirk_modes::QuirkMode;
use gemma::chip8::quirk_modes::QuirkMode::{Chip8, SChipModern, XOChip};
use gemma::chip8::registers::Chip8Registers;
use gemma::constants::{CHIP8_VIDEO_MEMORY, TESTS_ROOT};
#[test]
fn smoke() {
@@ -36,7 +42,7 @@ fn reset_clears_video() {
fn level1_test() {
let mut x = Chip8Computer::new();
let level_1_rom = load_rom("1-chip8-logo.ch8");
x.load_bytes_to_memory(0x200, (&level_1_rom));
x.load_bytes_to_memory(0x200, &level_1_rom);
// run for 0x40 cycles
while x.num_cycles < 0x40 {
@@ -54,7 +60,7 @@ fn level2_test() {
// Load the IBM rom and run it.
// it takes 39 cycles to get to the end so lets run it 40.
let test_rom_to_run = load_rom("2-ibm-logo.ch8");
x.load_bytes_to_memory(0x200, (&test_rom_to_run));
x.load_bytes_to_memory(0x200, &test_rom_to_run);
for _ in 0..40 {
x.step_system();
}
@@ -71,7 +77,7 @@ fn level2_test() {
fn level3_test() {
let mut x = Chip8Computer::new();
x.load_bytes_to_memory(0x200, (&load_rom("3-corax+.ch8")));
x.load_bytes_to_memory(0x200, &load_rom("3-corax+.ch8"));
for i in 0..0x180 {
x.step_system();
}
@@ -117,3 +123,71 @@ fn level4_test() {
load_result("gemma_integration_flags.asc")
);
}
#[test]
fn registers_equality() {
let data_set: Vec<(Chip8Registers, Chip8Registers, bool)> = vec![
(Chip8Registers::default(), Chip8Registers::default(), true),
(Chip8Registers {
registers: [0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,],
i_register: 0,
pc: 0,
},
Chip8Registers {
registers:[0x01, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00,],
i_register: 0,
pc: 0,
}, false)
];
for (first, second, matches) in data_set.iter() {
assert_eq!(first == second, *matches)
}
}
#[test]
fn default_test() {
let new_manager = Chip8ComputerManager::default();
assert_eq!(new_manager.core_should_run, false);
assert_eq!(new_manager.num_cycles(), 0);
assert_eq!(new_manager.quirks_mode(), Chip8);
}
#[test]
fn quirks_mode_test() {
let mut new_manager = Chip8ComputerManager::default();
assert_eq!(new_manager.quirks_mode(), Chip8);
new_manager.reset(QuirkMode::XOChip);
assert_eq!(new_manager.quirks_mode(), XOChip);
new_manager.reset(QuirkMode::SChipModern);
assert_eq!(new_manager.quirks_mode(), SChipModern);
new_manager.reset(Chip8);
assert_eq!(new_manager.quirks_mode(), Chip8);
}
#[test]
fn load_rom_allows_starting() {
let mut new_manager = Chip8ComputerManager::default();
assert_eq!(new_manager.core_should_run, false);
let p = format!("{}/../resources/test/roms/1-chip8-logo.ch8" , std::env::current_dir().unwrap().display());
let full_path = Path::new(p.as_str());
new_manager.load_new_program_from_disk_to_system_memory(full_path);
assert_eq!(new_manager.core_should_run, true)
}
#[test]
fn reset_clears_run_state() {
let mut new_manager = Chip8ComputerManager::default();
let p = format!("{}/../resources/test/roms/1-chip8-logo.ch8", std::env::current_dir().unwrap().display());
new_manager.load_new_program_from_disk_to_system_memory(Path::new(p.as_str()));
new_manager.reset(QuirkMode::Chip8);
assert_eq!(new_manager.core_should_run, false);
}
+34 -32
View File
@@ -1,35 +1,37 @@
use std::{fs, io};
use flate2::write::{GzDecoder, GzEncoder};
use flate2::Compression;
use std::fs;
use std::io;
use flate2::write::GzDecoder;
use gemma::chip8::computer::Chip8Computer;
use std::io::prelude::*;
fn load_result(to_load: &str) -> String {
let full_path = format!("resources/test/state/{}", to_load);
let full_path = format!("{}/../resources/test/state/{}", std::env::current_dir().unwrap().display(), to_load);
println!("CURRENT DIR: {:?}", std::env::current_dir());
println!("Loading state => (([{}]))", full_path);
std::fs::read_to_string(full_path).unwrap()
}
fn load_compressed_result(file_path: &str) -> io::Result<String> {
// Load the compressed file contents
let compressed_data = fs::read(file_path)?;
// Create a GzDecoder to uncompress the data
let mut decoder = GzDecoder::new(&mut compressed_data[..]);
let mut decompressed_data = String::new();
// Read the decompressed data directly into a String
decoder.read_to_string(&mut decompressed_data)?;
Ok(decompressed_data)
}
// fn load_compressed_result(file_path: &str) -> io::Result<String> {
// // Load the compressed file contents
// let compressed_data = fs::read(file_path)?;
//
// // Create a GzDecoder to uncompress the data
// let mut decoder = GzDecoder::new(&mut compressed_data[..]);
// let mut decompressed_data = String::new();
//
// // Read the decompressed data directly into a String
// decoder.read_to_string(&mut decompressed_data)?;
//
// Ok(decompressed_data)
// }
fn load_rom(to_load: &str) -> Vec<u8> {
std::fs::read(format!("resources/test/roms/{}", to_load)).unwrap()
fs::read(format!("resources/test/roms/{}", to_load)).unwrap()
}
#[test]
fn test_serialization_round_trip() {
#[ignore]
fn serialization_round_trip() {
let original_computer = Chip8Computer::new();
let expected_json = load_result("smoke_001_round_trip_serialize_deserialize.json");
@@ -47,17 +49,17 @@ fn test_serialization_round_trip() {
// Deserialize back to Chip8Computer and assert equality
let deserialized_computer: Chip8Computer =
serde_json::from_str(&serialized).expect("Deserialization failed");
assert_eq!(
deserialized_computer, original_computer,
"Deserialized instance does not match the original"
);
assert_eq!(
deserialized_computer, original_computer,
"Deserialized instance does not match the original"
);
}
#[test]
fn computer_001_system_zero_state() {
let x = Chip8Computer::new();
let expected_string = load_compressed_result("smoke_002_round_trip_serialize_deserialize.tflt");
let serialized = serde_json::to_string(&x).unwrap();
assert_eq!(serialized, expected_string);
}
//
// #[test]
// fn computer_001_system_zero_state() {
// let x = Chip8Computer::new();
// let expected_string = load_compressed_result("smoke_002_round_trip_serialize_deserialize.tflt").unwrap();
// let serialized = serde_json::to_string(&x).unwrap();
// assert_eq!(serialized, expected_string);
// }
//
+80 -1
View File
@@ -1,3 +1,5 @@
use std::fs::File;
use std::io::Read;
use gemma::chip8::computer::Chip8Computer;
use gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction;
use gemma::chip8::delay_timer::DelayTimer;
@@ -15,10 +17,13 @@ use gemma::constants::*;
use log::debug;
use rand::random;
use serde::Serialize;
use gemma::chip8::computer_manager::Chip8ComputerManager;
const TEST_OUTPUT_SAMPLE_DIR: &str = "../resources/test/";
fn read_test_result(suffix: &str) -> String {
println!("SITTING IN {:?}", std::env::current_dir());
println!("ATTEMPT TO READ RESULT {suffix}");
std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix).unwrap()
}
@@ -28,6 +33,7 @@ fn smoke() {
}
#[test]
#[ignore]
fn decoder_test_invalid_instructions() {
let invalid_to_encode = [
0x5ab1, 0x5abf, 0x8ab8, 0x8abd, 0x8abf, 0x9ab1, 0x9abf, 0xea9d, 0xea9f, 0xeaa0, 0xeaa2,
@@ -35,7 +41,7 @@ fn decoder_test_invalid_instructions() {
];
for i in invalid_to_encode {
assert_eq!(Chip8CpuInstructions::decode(i, &Chip8).encode(), 0xffff);
assert_eq!(Chip8CpuInstructions::decode(i, &Chip8).encode(), XXXXERRORINSTRUCTION_ENCODED);
assert!(matches!(
Chip8CpuInstructions::decode(i, &Chip8),
Chip8CpuInstructions::XXXXERRORINSTRUCTION
@@ -1521,3 +1527,76 @@ fn video_lowres_schip_draw_schip_sprite() {}
fn video_highres_schip_draw_chip8_sprite() {}
#[test]
fn video_highres_schip_draw_schip_sprite() {}
#[test]
fn partial_eq_chip8computer() {
let x = Chip8Computer::new();
let y = Chip8Computer::new();
assert_eq!(x, y)
}
#[test]
fn quirk_mode_labels() {
assert_eq!(format!("{}", Chip8), LABEL_QUIRK_CHIP8);
assert_eq!(format!("{}", XOChip), LABEL_QUIRK_XOCHIP);
assert_eq!(format!("{}", SChipModern), LABEL_QUIRK_SCHIP);
}
#[test]
fn system_memory_load_program() {
let mut m = Chip8SystemMemory::new();
let mut program_to_load = vec![];
let file_to_load = format!("{}/2-ibm-logo.ch8", TEST_ROM_ROOT);
println!("Attempt to load {} from {}", file_to_load, std::env::current_dir().unwrap().display());
let mut file_to_load_from = File::open(file_to_load).expect("Unable to load sample rom");
file_to_load_from.read_to_end(&mut program_to_load).expect("Unable to read sample rom");
m.load_program(program_to_load.clone().into());
let expected_at_200 = program_to_load[0];
assert_eq!(m.peek(0x200), expected_at_200);
}
#[test]
fn start_stop_computer() {
let mut computer = Chip8ComputerManager::new();
assert_eq!(computer.core_should_run, false);
computer.start();
assert_eq!(computer.core_should_run, true);
computer.step();
assert_eq!(computer.core_should_run, true);
computer.stop();
assert_eq!(computer.core_should_run, false);
computer.reset(Chip8);
assert_eq!(computer.core_should_run, false);
}
#[test]
fn state_default_matches() {
let computer = Chip8Computer::default();
let mut manager = Chip8ComputerManager::default();
assert_eq!(computer, *manager.state());
}
#[test]
fn keys_test_manager() {
let mut manager = Chip8ComputerManager::default();
for i in 0..16 {
assert_eq!(manager.is_key_pressed(i), false);
}
// press key 5
manager.press_key(5);
assert_eq!(manager.is_key_pressed(5), true);
manager.release_key(5);
assert_eq!(manager.is_key_pressed(5), false);
}
#[test]
fn status_of_manager() {
let mut manager = Chip8ComputerManager::default();
println!("MANAGER STATUS [{}]", manager.status_as_string());
let expected_state = read_test_result("test_manager_status.asc");
assert_eq!(expected_state, manager.status_as_string());
}