diff --git a/coverage/tarpaulin-report.html b/coverage/tarpaulin-report.html deleted file mode 100644 index 83440b3..0000000 --- a/coverage/tarpaulin-report.html +++ /dev/null @@ -1,671 +0,0 @@ - - - - - - - -
- - - - - - \ No newline at end of file diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs index efbf636..c34db82 100644 --- a/gemma/src/chip8/computer.rs +++ b/gemma/src/chip8/computer.rs @@ -12,7 +12,7 @@ use super::{ system_memory::Chip8SystemMemory, video::Chip8Video, }; -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct Chip8Computer { pub num_cycles: i32, pub memory: Chip8SystemMemory, @@ -43,15 +43,15 @@ impl Default for Chip8Computer { } impl Chip8Computer { - pub fn reset(&mut self) { + pub fn reset(&mut self, quirk_mode: QuirkMode) { self.video_memory.reset(); self.num_cycles = 0; self.registers.reset(); self.delay_timer.reset(); self.sound_timer.reset(); self.stack.reset(); - self.memory.reset(); - self.quirk_mode = QuirkMode::Chip8; + self.memory.reset(quirk_mode); + self.quirk_mode = quirk_mode; } pub fn dump_keypad_to_string(&self) -> String { @@ -108,7 +108,7 @@ impl Chip8Computer { match self.state { Chip8CpuStates::WaitingForInstruction => { - println!("Ticking sound, delay, video"); + // println!("Ticking sound, delay, video"); self.sound_timer.tick(); self.delay_timer.tick(); self.video_memory.tick(); diff --git a/gemma/src/chip8/computer_manager.rs b/gemma/src/chip8/computer_manager.rs index 14fdfbf..9cea84f 100644 --- a/gemma/src/chip8/computer_manager.rs +++ b/gemma/src/chip8/computer_manager.rs @@ -1,21 +1,22 @@ -use std::thread; -use std::thread::sleep; -use std::time::{Duration, Instant}; use crate::chip8::computer::Chip8Computer; use crate::chip8::cpu_states::Chip8CpuStates; use crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction; +use crate::chip8::quirk_modes::QuirkMode; +use std::thread; +use std::thread::sleep; +use std::time::{Duration, Instant}; + pub enum ManagerDumpables { Video, Registers, - Keyboard + Keyboard, } pub struct Chip8ComputerManager { core_should_run: bool, one_step: bool, - core_cycle_timer: bool, core_last_cycle_start: Instant, - computer: Chip8Computer + computer: Chip8Computer, } impl Default for Chip8ComputerManager { @@ -23,26 +24,27 @@ impl Default for Chip8ComputerManager { Chip8ComputerManager { core_should_run: false, one_step: false, - core_cycle_timer: false, - core_last_cycle_start: Instant::now() , - computer: Chip8Computer::new() + core_last_cycle_start: Instant::now(), + computer: Chip8Computer::new(), } } } impl Chip8ComputerManager { - pub fn reset(&mut self) { + pub fn quirks_mode(&self) -> QuirkMode { + self.computer.quirk_mode.clone() + } + + pub fn reset(&mut self, mode: QuirkMode) { self.one_step = false; self.core_should_run = false; - self.computer.reset(); + self.computer.reset(mode); } pub fn new() -> Chip8ComputerManager { - let core_handle = thread::spawn(move || { - loop { - let start_time = Instant::now(); - let sleep_time = Instant::now().duration_since(start_time).as_millis(); - sleep(Duration::from_millis((16 - sleep_time) as u64)); - } + let _ = thread::spawn(move || loop { + let start_time = Instant::now(); + let sleep_time = Instant::now().duration_since(start_time).as_millis(); + sleep(Duration::from_millis((16 - sleep_time) as u64)); }); Chip8ComputerManager::default() @@ -64,15 +66,15 @@ impl Chip8ComputerManager { &self.computer } - pub fn tick( &mut self) -> bool { - // println!("STARTING TICK"); + pub fn tick(&mut self) -> bool { + // println!("STARTING TICK"); let mut did_tick: bool = false; - if self.one_step | self.core_should_run { + if self.one_step | self.core_should_run { if let WaitingForInstruction = self.computer.state { self.core_last_cycle_start = Instant::now(); self.computer.step_system(); did_tick = true - // println!("SYSTEM STEP"); + // println!("SYSTEM STEP"); } }; if self.one_step { @@ -112,21 +114,15 @@ impl Chip8ComputerManager { } pub fn load_new_program_to_system_memory(&mut self, bytes_to_load: Vec) { - self.reset(); + self.reset(self.computer.quirk_mode.clone()); self.computer.load_bytes_to_memory(0x200, &bytes_to_load); } pub fn dump_to_string(&self, dump_type: ManagerDumpables) -> String { match dump_type { - ManagerDumpables::Video => { - self.computer.video_memory.format_as_string() - } - ManagerDumpables::Registers => { - self.computer.registers.format_as_string() - } - ManagerDumpables::Keyboard => { - self.computer.keypad.format_as_string() - } + ManagerDumpables::Video => self.computer.video_memory.format_as_string(), + ManagerDumpables::Registers => self.computer.registers.format_as_string(), + ManagerDumpables::Keyboard => self.computer.keypad.format_as_string(), } } -} \ No newline at end of file +} diff --git a/gemma/src/chip8/cpu_states.rs b/gemma/src/chip8/cpu_states.rs index f080126..b0fa9d2 100644 --- a/gemma/src/chip8/cpu_states.rs +++ b/gemma/src/chip8/cpu_states.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Default, Serialize, Deserialize)] +#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)] pub enum Chip8CpuStates { #[default] WaitingForInstruction, diff --git a/gemma/src/chip8/delay_timer.rs b/gemma/src/chip8/delay_timer.rs index a36e195..cee823a 100644 --- a/gemma/src/chip8/delay_timer.rs +++ b/gemma/src/chip8/delay_timer.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct DelayTimer { counter: u8, } diff --git a/gemma/src/chip8/instructions.rs b/gemma/src/chip8/instructions.rs index ab624ea..08fd579 100644 --- a/gemma/src/chip8/instructions.rs +++ b/gemma/src/chip8/instructions.rs @@ -7,6 +7,7 @@ use crate::constants::*; use log::debug; use rand::Rng; use serde::{Deserialize, Serialize}; +use std::ascii::AsciiExt; use std::fmt::{Debug, Display, Formatter}; use std::ops::BitAnd; use std::time::Instant; @@ -270,6 +271,11 @@ pub enum Chip8CpuInstructions { /// scroll screen content down N pixel, in XO-CHIP only selected bit /// planes are scrolled (Quirks are HP48 specific) SCU(u8), + /// 0xNNNN + /// + /// data word + /// used for specifying data to be used in system memory + DW(u16), } impl Chip8CpuInstructions { @@ -322,6 +328,7 @@ impl Chip8CpuInstructions { JPX(_, _) => INST_JPX, XXXXERRORINSTRUCTION => "XX ERROR XX", SCU(_) => INST_SCU, + DW(_) => INST_DW, } } @@ -331,7 +338,8 @@ impl Chip8CpuInstructions { let addr_for_display = (*x as u16) << 8 | *addr; format!("0x{x:02x}, 0x{addr_for_display:04x}") } - Chip8CpuInstructions::SYS(addr) + Chip8CpuInstructions::DW(addr) + | Chip8CpuInstructions::SYS(addr) | Chip8CpuInstructions::JPI(addr) | Chip8CpuInstructions::JPA(addr) | Chip8CpuInstructions::LDIA(addr) @@ -406,7 +414,7 @@ impl Chip8CpuInstructions { pub fn from_str(input: &str) -> Chip8CpuInstructions { let mut parts = input.split(" "); // print!("THERE ARE {} PARTS", parts.clone().count()); - let first_part = parts.next().unwrap_or(""); + let first_part = parts.next().unwrap_or("").to_ascii_uppercase(); // take the next value... // ...strip off the extra... // ...convert it to an integer from base 16 @@ -414,6 +422,7 @@ impl Chip8CpuInstructions { parts .next() .unwrap_or("0") + .to_ascii_lowercase() .trim_start_matches("0x") .trim_end_matches(","), 16, @@ -423,6 +432,7 @@ impl Chip8CpuInstructions { parts .next() .unwrap_or("0") + .to_ascii_lowercase() .trim_start_matches("0x") .trim_end_matches(","), 16, @@ -432,13 +442,17 @@ impl Chip8CpuInstructions { parts .next() .unwrap_or("0") + .to_ascii_lowercase() .trim_start_matches("0x") .trim_end_matches(","), 16, ) .unwrap_or(0); - // println!("\tFirst part is {:?} / {:?} / {:?} / {:?}", first_part, param1 ,param2 ,param3); - match first_part { + println!( + "\tFirst part is {:?} / {:?} / {:?} / {:?}", + first_part, param1, param2, param3 + ); + match first_part.as_str() { INST_ADDI => ADDI(param1 as u8), INST_ADD => ADD(param1 as u8, param2 as u8), INST_CLS => CLS, @@ -483,6 +497,7 @@ impl Chip8CpuInstructions { INST_LDIS => LDIS(param1 as u8), INST_LDD => LDD(param1 as u8), INST_JPX => JPX(param1 as u8, param2), + INST_DW => DW(param1 as u16), _ => XXXXERRORINSTRUCTION, } } @@ -571,8 +586,9 @@ impl Chip8CpuInstructions { Chip8CpuInstructions::LDF2(x_register) => 0xF030 | ((*x_register as u16) << 8), Chip8CpuInstructions::STR(x_register) => 0xF075 | ((*x_register as u16) << 8), Chip8CpuInstructions::LIDR(x_register) => 0xF085 | ((*x_register as u16) << 8), - XXXXERRORINSTRUCTION => 0xFFFF, SCU(x_register) => 0x00D0 | (*x_register as u16), + DW(address) => *address as u16, + XXXXERRORINSTRUCTION => 0x0000, } } pub fn decode(input: u16, quirk_mode: &QuirkMode) -> Chip8CpuInstructions { @@ -661,7 +677,7 @@ impl Chip8CpuInstructions { 0x85 => Chip8CpuInstructions::LIDR(ubln), _ => XXXXERRORINSTRUCTION, }, - _ => XXXXERRORINSTRUCTION, + _ => DW(addr_param), } } pub fn execute(&self, input: &mut Chip8Computer) -> Chip8Computer { @@ -1084,6 +1100,7 @@ impl Chip8CpuInstructions { input.registers.poke_i(offset + 1); } Chip8CpuInstructions::XXXXERRORINSTRUCTION => {} + // SCHIP1.1 Chip8CpuInstructions::SCD(x) => match input.quirk_mode { QuirkMode::Chip8 => { debug!("Attempt to execute SCD in Chip8 Mode"); @@ -1092,6 +1109,7 @@ impl Chip8CpuInstructions { input.video_memory.scroll_down(*x as i32); } }, + // SCHIP 1.1 Chip8CpuInstructions::SCR => match input.quirk_mode { QuirkMode::Chip8 => { debug!("Attempt to execute SCR in Chip8 Mode"); @@ -1100,6 +1118,7 @@ impl Chip8CpuInstructions { input.video_memory.scroll_right(); } }, + // SCHIP 1.1 Chip8CpuInstructions::SCL => match input.quirk_mode { QuirkMode::Chip8 => { debug!("Attempt to execute SCL in Chip8 Mode"); @@ -1108,6 +1127,7 @@ impl Chip8CpuInstructions { input.video_memory.scroll_left(); } }, + // SCHIP 1.0 Chip8CpuInstructions::LOW => match input.quirk_mode { QuirkMode::Chip8 => { debug!("ATTEMPT TO SET LOWRES IN CHIP8MODE"); @@ -1116,6 +1136,7 @@ impl Chip8CpuInstructions { input.video_memory.set_lowres(); } }, + // SCHIP 1.0 Chip8CpuInstructions::HIGH => match input.quirk_mode { QuirkMode::Chip8 => { debug!("ATTEMPT TO SET HIGHRES IN CHIP8MODE"); @@ -1171,6 +1192,9 @@ impl Chip8CpuInstructions { } } } + DW(addr) => { + println!("DATA WORD FOUND..."); + } }; let cycle_time = Instant::now().duration_since(start_time).as_nanos(); // println!("\t\tTook {cycle_time}ms"); diff --git a/gemma/src/chip8/keypad.rs b/gemma/src/chip8/keypad.rs index 4379043..1a032a6 100644 --- a/gemma/src/chip8/keypad.rs +++ b/gemma/src/chip8/keypad.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::constants::CHIP8_KEYBOARD; -#[derive(Clone, Copy, Default, Serialize, Deserialize)] +#[derive(Clone, Copy, Default, Serialize, Deserialize, Debug)] pub struct Keypad { keys: [bool; 0x10], } diff --git a/gemma/src/chip8/quirk_modes.rs b/gemma/src/chip8/quirk_modes.rs index d00faec..26d3caa 100644 --- a/gemma/src/chip8/quirk_modes.rs +++ b/gemma/src/chip8/quirk_modes.rs @@ -1,9 +1,21 @@ use serde::{Deserialize, Serialize}; +use std::fmt::Display; -#[derive(Default, Clone, Serialize, Deserialize)] +#[derive(Default, Clone, Serialize, Deserialize, Copy, Debug)] 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(), + }; + write!(f, "{}", variant) + } +} diff --git a/gemma/src/chip8/registers.rs b/gemma/src/chip8/registers.rs index 1795728..4fbdc3e 100644 --- a/gemma/src/chip8/registers.rs +++ b/gemma/src/chip8/registers.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; /// Registers. numbered 1-16 publicly. /// Privately using zero base array so -1 to shift from pub to priv. -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct Chip8Registers { pub registers: [u8; 16], pub i_register: u16, diff --git a/gemma/src/chip8/sound_timer.rs b/gemma/src/chip8/sound_timer.rs index 2dd1fda..8cf1f31 100644 --- a/gemma/src/chip8/sound_timer.rs +++ b/gemma/src/chip8/sound_timer.rs @@ -1,7 +1,7 @@ use log::trace; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub struct SoundTimer { counter: i32, } diff --git a/gemma/src/chip8/stack.rs b/gemma/src/chip8/stack.rs index 16adcb5..eb58ab3 100644 --- a/gemma/src/chip8/stack.rs +++ b/gemma/src/chip8/stack.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Clone, Default, Serialize, Deserialize)] +#[derive(Clone, Default, Serialize, Deserialize, Debug)] pub struct Chip8Stack { items: Vec, } diff --git a/gemma/src/chip8/system_memory.rs b/gemma/src/chip8/system_memory.rs index 59a9a3e..5a68a44 100644 --- a/gemma/src/chip8/system_memory.rs +++ b/gemma/src/chip8/system_memory.rs @@ -4,7 +4,10 @@ use crate::constants::*; use serde::Deserialize; use serde::Serialize; -#[derive(Clone, Serialize, Deserialize)] +use super::quirk_modes; +use super::quirk_modes::QuirkMode; + +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct Chip8SystemMemory { memory: Vec, } @@ -18,6 +21,7 @@ impl Default for Chip8SystemMemory { x } } + impl Chip8SystemMemory { fn empty_memory() -> Vec { let mut working_memory = vec![]; @@ -28,10 +32,19 @@ impl Chip8SystemMemory { working_memory } - pub fn reset(&mut self) { + pub fn reset(&mut self, quirk_modes: QuirkMode) { self.memory = Chip8SystemMemory::empty_memory(); - self.load_fonts_to_memory(); - self.load_schip_fonts_to_memory(); + match quirk_modes { + QuirkMode::Chip8 => { + self.load_fonts_to_memory(); + } + QuirkMode::XOChip => { + println!("NO XO FONT LOADING DONE YET"); + } + QuirkMode::SChipModern => { + self.load_schip_fonts_to_memory(); + } + } } pub fn new() -> Self { @@ -114,10 +127,10 @@ impl Chip8SystemMemory { SCHIPFONT_F, ]; for (font_index, current_font) in all_font_characters.iter().enumerate() { - let base_offset = 0x100; - for font_mem_offset in 0..=4 { - let real_offset = base_offset + font_index * 0x10 + font_mem_offset; - self.poke(real_offset as u16, current_font[font_mem_offset]); + let base_offset = SCHIPFONT_OFFSET; + for font_mem_offset in 0..=9 { + let real_offset = base_offset + (font_index * 0x0a) as u32 + font_mem_offset; + self.poke(real_offset as u16, current_font[font_mem_offset as usize]); } } } diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs index 371daa9..146d735 100644 --- a/gemma/src/chip8/video.rs +++ b/gemma/src/chip8/video.rs @@ -6,13 +6,13 @@ use crate::constants::{ use log::debug; use serde::{Deserialize, Serialize}; -#[derive(Clone, Copy, Serialize, Deserialize)] +#[derive(Clone, Copy, Serialize, Deserialize, Debug)] pub enum Chip8VideoModes { LowRes, HighRes, } -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct Chip8Video { memory: Vec, pub has_frame_changed: bool, @@ -172,14 +172,16 @@ impl Chip8Video { } pub fn scroll_left(&mut self) { + println!("SCROLLLEFTPRE:::[{}]", self.format_as_string()); let (width, height) = self.get_resolution(); for current_row in 0..height { let row_offset = current_row * width; - for current_column in 0..width - 4 { - let source: usize = (row_offset + current_column) as usize; - let target: usize = source + 4; + for current_column in (0..width - 4) { + let target: usize = (row_offset + current_column) as usize; + let source: usize = target + 4; self.memory[target] = self.memory[source]; + println!("Moving from {source} to {target}"); } let clear_start: usize = (row_offset + width - 4) as usize; @@ -187,6 +189,7 @@ impl Chip8Video { self.memory[clear_start..clear_end].fill(false); } + println!("SCROLLLEFTPOST:::[{}]", self.format_as_string()); } pub fn scroll_up(&mut self, how_far: &u8) { diff --git a/gemma/src/constants.rs b/gemma/src/constants.rs index ac4e9eb..bc46ba7 100644 --- a/gemma/src/constants.rs +++ b/gemma/src/constants.rs @@ -12,7 +12,7 @@ pub const CHIP8_KEYBOARD: [[u8; 4]; 4] = [ [0x01, 0x02, 0x03, 0x0C], [0x04, 0x05, 0x06, 0x0D], [0x07, 0x08, 0x09, 0x0E], - [0x0A, 0x00, 0x0B, 0x0F] + [0x0A, 0x00, 0x0B, 0x0F], ]; pub const SCHIP_VIDEO_HEIGHT: i32 = 64; @@ -65,6 +65,10 @@ pub const INST_LOW: &str = "LOW"; pub const INST_HIGH: &str = "HIGH"; pub const INST_ORY: &str = "ORY"; pub const INST_SCU: &str = "SCU"; +/// Data Word +/// Data to be loaded to memory for application use +pub const INST_DW: &str = "DW"; + 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]; @@ -83,90 +87,20 @@ pub const CHIP8FONT_D: [u8; 5] = [0xE0, 0x90, 0x90, 0x90, 0xE0]; pub const CHIP8FONT_E: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0xF0]; pub const CHIP8FONT_F: [u8; 5] = [0xF0, 0x80, 0xF0, 0x80, 0x80]; -pub const SCHIPFONT_0: [u8; 0x20] = [ - 0x00, 0x00, - 0x01, 0x80, - 0x03, 0xc0, - 0x06, 0x60, - 0x0c, 0x30, - 0x0c, 0x30, - 0x18, 0x18, - 0x18, 0x18, - 0x18, 0x18, - 0x18, 0x18, - 0x0c, 0x30, - 0x0c, 0x30, - 0x06, 0x60, - 0x03, 0xc0, // 0b0000001111000000 - 0x01, 0x80, // 0b0000000110000000 - 0x00, 0x00 // 0b0000000000000000 -]; -pub const SCHIPFONT_1: [u8; 0x20] = [ - 0x00, 0x00, - 0x03, 0xc0, - 0x02, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x00, 0xc0, - 0x03, 0xf0, - 0x00, 0x00 -]; -pub const SCHIPFONT_2: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC7, 0xC3, 0xC0, 0xE0, - 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0xFF, 0xFF,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78]; -pub const SCHIPFONT_3: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC7, 0xC3, 0xC0, 0xE0, - 0xF0, 0x78, 0x3C, 0x1E, 0x0F, 0x07, 0xFF, 0xFF,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78]; -pub const SCHIPFONT_4: [u8; 0x20] = [0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xF7, 0xF3, 0xF1, - 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xC0, 0xC0, 0xC0,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78]; -pub const SCHIPFONT_5: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x3F, 0x7F, 0x7F, - 0x01, 0x01, 0xC1, 0xE3, 0xFF, 0xFE, 0xFC, 0x78,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78]; -pub const SCHIPFONT_6: [u8; 0x20] = [0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_7: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0xE0, 0xF0, 0x78, 0x3C, - 0x1E, 0x0F, 0x07, 0x03, 0x01, 0x01, 0x01, 0x01,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_8: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, - 0x7E, 0xFE, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE, 0x7C,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_9: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, - 0xFF, 0x7F, 0x03, 0x03, 0xC7, 0xFF, 0xFE, 0x7C,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_A: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, - 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_B: [u8; 0x20] = [0xFE, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xFE, 0xFF, - 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_C: [u8; 0x20] = [0x7C, 0xFE, 0xFF, 0xFF, 0xC3, 0xC3, 0x01, 0x01, - 0x01, 0x01, 0xC3, 0xC3, 0xFF, 0xFE, 0xFC, 0x78,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_D: [u8; 0x20] = [0xFE, 0xFF, 0xFF, 0xFF, 0xC3, 0xC3, 0xC3, 0xC3, - 0xC3, 0xC3, 0xC3, 0xC3, 0xFF, 0xFF, 0xFE, 0x7C, 0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_E: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0xFF, - 0xFF, 0xFF, 0x03, 0x03, 0xFF, 0xFF, 0xFF, 0xFF,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78 -]; -pub const SCHIPFONT_F: [u8; 0x20] = [0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x03, 0x03, 0xFF, - 0xFF, 0xFF, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,0x78, 0xFE, 0xFF, 0xFF, 0x83, 0x01, 0x01, 0xFF, - 0xFF, 0xFF, 0xC3, 0xE3, 0xFF, 0xFE, 0xFC, 0x78]; +pub const SCHIPFONT_OFFSET: u32 = 0x81; +pub const SCHIPFONT_0: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xF0]; +pub const SCHIPFONT_1: [u8; 0x0A] = [0x20, 0x60, 0xA0, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70]; +pub const SCHIPFONT_2: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x80, 0x80, 0x80, 0x80, 0xF0]; +pub const SCHIPFONT_3: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0]; +pub const SCHIPFONT_4: [u8; 0x0A] = [0x90, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x10]; +pub const SCHIPFONT_5: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0]; +pub const SCHIPFONT_6: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0]; +pub const SCHIPFONT_7: [u8; 0x0A] = [0xF0, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10]; +pub const SCHIPFONT_8: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0xF0]; +pub const SCHIPFONT_9: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x10, 0x10, 0x10, 0x10, 0xF0]; +pub const SCHIPFONT_A: [u8; 0x0A] = [0xF0, 0x90, 0x90, 0x90, 0xF0, 0x90, 0x90, 0x90, 0x90, 0x90]; +pub const SCHIPFONT_B: [u8; 0x0A] = [0xE0, 0x90, 0x90, 0x90, 0xE0, 0x90, 0x90, 0x90, 0x90, 0xE0]; +pub const SCHIPFONT_C: [u8; 0x0A] = [0xF0, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xF0]; +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]; diff --git a/gemma/tests/computer_tests.rs b/gemma/tests/computer_tests.rs index 9716434..1385de8 100644 --- a/gemma/tests/computer_tests.rs +++ b/gemma/tests/computer_tests.rs @@ -2,7 +2,9 @@ use gemma::chip8::computer::Chip8Computer; use gemma::constants::CHIP8_VIDEO_MEMORY; #[test] -fn smoke() { assert!(true) } +fn smoke() { + assert!(true) +} fn load_result(to_load: &str) -> String { std::fs::read_to_string(format!("../resources/test/{}", to_load)).unwrap() @@ -20,7 +22,7 @@ fn reset_clears_video() { x.video_memory.poke(i as u16, i % 2 == 0); } - x.reset(); + x.reset(gemma::chip8::quirk_modes::QuirkMode::Chip8); x.video_memory.reset(); assert_eq!(x.dump_video_to_string(), x.video_memory.format_as_string()); @@ -40,7 +42,10 @@ fn level1_test() { while x.num_cycles < 0x40 { x.step_system(); } - assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_level_1_test.asc")); + assert_eq!( + x.dump_video_to_string(), + load_result("gemma_integration_level_1_test.asc") + ); } #[test] @@ -56,21 +61,25 @@ fn level2_test() { // ...then verify that the current video memory of the chip-8 // simulator matches what we expect it to be - assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_ibm_rom_output.asc")); + assert_eq!( + x.dump_video_to_string(), + load_result("gemma_integration_ibm_rom_output.asc") + ); } #[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(); } - assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_corax_plus.asc")); + assert_eq!( + x.dump_video_to_string(), + load_result("gemma_integration_corax_plus.asc") + ); } #[test] @@ -80,12 +89,18 @@ fn rps_test() { for _ in 0..0xF0 { x.step_system(); } - assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_rps_stage1.asc")); + assert_eq!( + x.dump_video_to_string(), + load_result("gemma_integration_rps_stage1.asc") + ); x.keypad.push_key(0x01); for _ in 0..0x200 { x.step_system(); } - assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_rps_stage2.asc")); + assert_eq!( + x.dump_video_to_string(), + load_result("gemma_integration_rps_stage2.asc") + ); } #[test] @@ -97,5 +112,8 @@ fn level4_test() { x.step_system(); } - assert_eq!(x.dump_video_to_string(), load_result("gemma_integration_flags.asc")); + assert_eq!( + x.dump_video_to_string(), + load_result("gemma_integration_flags.asc") + ); } diff --git a/gemma/tests/state_tests.rs b/gemma/tests/state_tests.rs index 3a3e868..e0f51bf 100644 --- a/gemma/tests/state_tests.rs +++ b/gemma/tests/state_tests.rs @@ -1,34 +1,55 @@ -use flate2::write::{GzEncoder, GzDecoder}; +use flate2::write::{GzDecoder, GzEncoder}; use flate2::Compression; -use std::io::prelude::*; use gemma::chip8::computer::Chip8Computer; +use std::io::prelude::*; fn load_result(to_load: &str) -> String { - std::fs::read_to_string(format!("resources/test/state/{}", to_load)).unwrap() + let full_path = format!("resources/test/state/{}", to_load); + println!("Loading state => (([{}]))", full_path); + std::fs::read_to_string(full_path).unwrap() } -fn load_compressed_result(to_load: &str) -> String { - // load the file... - // ...then uncompress the string. - let mut decoder = GzDecoder::new(Vec::new()); - decoder.write_all(load_result(to_load).as_ref()).expect("Decompression failed"); - let decompressed_data = decoder.finish().expect("Failed to finish decompression"); - String::from_utf8(decompressed_data).expect("Invalid UTF-8") +fn load_compressed_result(file_path: &str) -> io::Result { + // Load the compressed file contents + let compressed_data = fs::read(file_path)?; + + // Create a GzDecoder to uncompress the data + let mut decoder = GzDecoder::new(&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 { - std::fs::read(format!("resources/roms/{}", to_load)).unwrap() + std::fs::read(format!("resources/test/roms/{}", to_load)).unwrap() } + #[test] -fn smoke_round_trip_serialize_deserialize() { - let x = Chip8Computer::new(); - let expected_string = load_result("smoke_001_round_trip_serialize_deserialize.json"); - let serialized = serde_json::to_string(&x).unwrap(); - let deserialized: Chip8Computer = serde_json::from_str(&expected_string).unwrap(); - println!("SERIALIZED [{}]", serialized); - // TODO: using trim here is a hack to handle editors adding a newline at the end of a file...even a 1 line file - assert_eq!(serialized.trim(), expected_string.trim()); - assert_eq!(deserialized, x); +fn test_serialization_round_trip() { + let original_computer = Chip8Computer::new(); + let expected_json = load_result("smoke_001_round_trip_serialize_deserialize.json"); + + // Serialize the Chip8Computer instance + let serialized = serde_json::to_string(&original_computer).expect("Serialization failed"); + + // Compare the serialized output to the expected JSON + println!("Serialized Output: [{}]", serialized); + assert_eq!( + serialized.trim(), + expected_json.trim(), + "Serialized output does not match expected JSON" + ); + + // 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" + ); } #[test] @@ -36,5 +57,7 @@ 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); +} } diff --git a/gemma/tests/unit_tests.rs b/gemma/tests/unit_tests.rs index 5ba7d99..971c64e 100644 --- a/gemma/tests/unit_tests.rs +++ b/gemma/tests/unit_tests.rs @@ -4,7 +4,7 @@ use gemma::chip8::delay_timer::DelayTimer; use gemma::chip8::instructions::Chip8CpuInstructions; use gemma::chip8::instructions::Chip8CpuInstructions::JPX; use gemma::chip8::keypad::Keypad; -use gemma::chip8::quirk_modes::QuirkMode::{Chip8, SChipModern, XOChip}; +use gemma::chip8::quirk_modes::QuirkMode::{self, Chip8, SChipModern, XOChip}; use gemma::chip8::registers::Chip8Registers; use gemma::chip8::sound_timer::SoundTimer; use gemma::chip8::stack::Chip8Stack; @@ -14,6 +14,7 @@ use gemma::chip8::video::{Chip8Video, Chip8VideoModes}; use gemma::constants::*; use log::debug; use rand::random; +use serde::Serialize; const TEST_OUTPUT_SAMPLE_DIR: &str = "../resources/test/"; @@ -1497,3 +1498,26 @@ fn system_memory_new() { let x = Chip8SystemMemory::new(); assert!(true); } + +#[test] +fn video_lowres_schip_draw_chip8_sprite() { + let mut x = Chip8Computer::new(); + x.quirk_mode = QuirkMode::SChipModern; + x.video_memory.set_lowres(); + // point at the 1 from chip8 + x.registers.poke_i(0x0005); + // point to 1,2 for the drawing + x.registers.poke(0x01, 0x01); + x.registers.poke(0x02, 0x02); + Chip8CpuInstructions::DRW(0x01, 0x01, 0x08).execute(&mut x); + let expected_state = read_test_result("state/video_lowres_schip_draw_chip8_sprite_result.json"); + let actual_state = serde_json::to_string(&x).unwrap(); + assert_eq!(expected_state, actual_state); +} + +#[test] +fn video_lowres_schip_draw_schip_sprite() {} +#[test] +fn video_highres_schip_draw_chip8_sprite() {} +#[test] +fn video_highres_schip_draw_schip_sprite() {} diff --git a/gemmaegui/src/bin/gemmaegui.rs b/gemmaegui/src/bin/gemmaegui.rs index 5ad6758..373cb6c 100644 --- a/gemmaegui/src/bin/gemmaegui.rs +++ b/gemmaegui/src/bin/gemmaegui.rs @@ -1,18 +1,38 @@ -use std::path::PathBuf; -use std::time::Instant; use crate::support::gemma_egui_support::{EGuiFileList, GemmaEguiSupport}; use eframe::egui; use egui::Key; use gemma::chip8::computer::Chip8Computer; use gemma::chip8::computer_manager::Chip8ComputerManager; +use std::path::PathBuf; +use std::time::Instant; mod support; const LIN_KEYS: [[(Key, u8); 4]; 4] = [ - [(Key::Num1, 0x01), (Key::Num2, 0x02), (Key::Num3, 0x03), (Key::Num4, 0x0c)], - [(Key::Q, 0x04), (Key::W, 0x05), (Key::E, 0x06), (Key::R, 0x0d)], - [(Key::A, 0x07), (Key::S, 0x08), (Key::D, 0x09), (Key::F, 0x0e)], - [(Key::Z, 0x0a), (Key::X, 0x00), (Key::C, 0x0b), (Key::V, 0x0F)] + [ + (Key::Num1, 0x01), + (Key::Num2, 0x02), + (Key::Num3, 0x03), + (Key::Num4, 0x0c), + ], + [ + (Key::Q, 0x04), + (Key::W, 0x05), + (Key::E, 0x06), + (Key::R, 0x0d), + ], + [ + (Key::A, 0x07), + (Key::S, 0x08), + (Key::D, 0x09), + (Key::F, 0x0e), + ], + [ + (Key::Z, 0x0a), + (Key::X, 0x00), + (Key::C, 0x0b), + (Key::V, 0x0F), + ], ]; #[derive(Default)] @@ -88,29 +108,40 @@ fn main() -> eframe::Result { // state.is_running = false; } if ui.button("Reset").clicked() { - computer.reset(); + computer.reset(computer.quirks_mode()); // state.is_running = false; } }); - if ui.button(format!("Load {}", state.selected_filename)).clicked() { - // println!("Should load the bin now"); - let read_bin = std::fs::read(PathBuf::from(format!("resources/roms/{}", state.selected_filename))).unwrap(); + if ui + .button(format!("Load {}", state.selected_filename)) + .clicked() + { + // println!("Should load the bin now"); + let read_bin = std::fs::read(PathBuf::from(format!( + "resources/roms/{}", + state.selected_filename + ))) + .unwrap(); computer.load_new_program_to_system_memory(read_bin); }; - EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_filename, ui); + EGuiFileList::display_path( + PathBuf::from("resources/roms"), + &mut state.selected_filename, + ui, + ); ctx.input(|input| { // loop through the keys we are checking... for row in LIN_KEYS { for (keyboard_key, keypad_key) in row { if input.key_pressed(keyboard_key) { computer.press_key(keypad_key); - // println!("KEY {keypad_key:02x} DOWN"); + // println!("KEY {keypad_key:02x} DOWN"); } else { // release it if the user just did if computer.is_key_pressed(keypad_key) { computer.release_key(keypad_key); - // println!("KEY {keypad_key:02x} up"); + // println!("KEY {keypad_key:02x} up"); } } } diff --git a/gemmaegui/src/bin/support/gemma_egui_support.rs b/gemmaegui/src/bin/support/gemma_egui_support.rs index b504d43..eccf7a2 100644 --- a/gemmaegui/src/bin/support/gemma_egui_support.rs +++ b/gemmaegui/src/bin/support/gemma_egui_support.rs @@ -1,10 +1,10 @@ +use crate::Chip8Computer; +use egui::Rect; +use egui::Ui; +use egui::Vec2; +use egui::{Align, Color32, ComboBox, Pos2}; use std::fs::read_dir; use std::path::PathBuf; -use egui::{Align, Color32, ComboBox, Pos2, TextBuffer}; -use egui::Rect; -use egui::Vec2; -use egui::Ui; -use crate::Chip8Computer; const CELL_WIDTH: f32 = 5.0; const CELL_HEIGHT: f32 = 5.0; @@ -14,21 +14,26 @@ impl EGuiFileList { pub fn display_path(root: PathBuf, selected_filename: &mut String, ui: &mut Ui) { let working_filename = selected_filename.clone(); ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { - // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path"))); + // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path"))); ComboBox::from_label("Select ROM") .selected_text(selected_filename.clone()) .show_ui(ui, |ui| { - let mut sorted_options = vec![]; for option in read_dir(root.as_path()).unwrap() { - let to_push = option.unwrap().file_name().into_string().unwrap_or( String::new()); + let to_push = option + .unwrap() + .file_name() + .into_string() + .unwrap_or(String::new()); sorted_options.push(to_push); } sorted_options.sort(); for item in sorted_options { - // Add each option to the ComboBox - if ui.selectable_label(selected_filename.eq(&item.as_str()), item.clone()).clicked() + // Add each option to the ComboBox + if ui + .selectable_label(selected_filename.eq(&item.as_str()), item.clone()) + .clicked() { *selected_filename = item; } @@ -43,36 +48,41 @@ pub struct GemmaEguiSupport {} impl GemmaEguiSupport { pub fn controls_view(system: &mut Chip8Computer, ui: &mut Ui) { - ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| { - // ui.checkbox(&mut state.display_memory, "Display Memory"); - // ui.checkbox(&mut state.display_video, "Display Video"); - // ui.checkbox(&mut state.display_registers, "Display Registers"); + // ui.checkbox(&mut state.display_memory, "Display Memory"); + // ui.checkbox(&mut state.display_video, "Display Video"); + // ui.checkbox(&mut state.display_registers, "Display Registers"); }); } pub fn registers_view(system: &Chip8Computer, ui: &mut Ui) { - ui.label(format!("V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", - system.registers.peek(0x00), - system.registers.peek(0x01), - system.registers.peek(0x02), - system.registers.peek(0x03), - system.registers.peek(0x04), - system.registers.peek(0x05), - system.registers.peek(0x06), - system.registers.peek(0x07) + ui.label(format!( + "V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", + system.registers.peek(0x00), + system.registers.peek(0x01), + system.registers.peek(0x02), + system.registers.peek(0x03), + system.registers.peek(0x04), + system.registers.peek(0x05), + system.registers.peek(0x06), + system.registers.peek(0x07) )); - ui.label(format!("V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", - system.registers.peek(0x08), - system.registers.peek(0x09), - system.registers.peek(0x0A), - system.registers.peek(0x0B), - system.registers.peek(0x0C), - system.registers.peek(0x0D), - system.registers.peek(0x0E), - system.registers.peek(0x0F) + ui.label(format!( + "V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", + system.registers.peek(0x08), + system.registers.peek(0x09), + system.registers.peek(0x0A), + system.registers.peek(0x0B), + system.registers.peek(0x0C), + system.registers.peek(0x0D), + system.registers.peek(0x0E), + system.registers.peek(0x0F) + )); + ui.label(format!( + "PC: {:04x}\tI: {:04x}", + system.registers.peek_pc(), + system.registers.peek_i() )); - ui.label(format!("PC: {:04x}\tI: {:04x}", system.registers.peek_pc(), system.registers.peek_i())); } pub fn video_view(system: &Chip8Computer, ui: &mut Ui) { @@ -95,7 +105,7 @@ impl GemmaEguiSupport { // system.video_memory.peek(data_offset)); } } - // thread::sleep(Duration::from_secs(1)); + // thread::sleep(Duration::from_secs(1)); } pub fn memory_view(system: &Chip8Computer, ui: &mut Ui) { diff --git a/gemmaimgui/src/bin/gemmaimgui.rs b/gemmaimgui/src/bin/gemmaimgui.rs index 948b83d..2440ac9 100644 --- a/gemmaimgui/src/bin/gemmaimgui.rs +++ b/gemmaimgui/src/bin/gemmaimgui.rs @@ -3,6 +3,7 @@ use gemma::chip8::computer_manager::Chip8ComputerManager; use std::path::Path; use std::time::Instant; use std::{default::Default, path::PathBuf}; +use support::ui_state::ROMPATH_DEFAULT; use support::{emmagui_support::GemmaImguiSupport, ui_state::ImGuiUiState}; mod support; @@ -45,7 +46,12 @@ fn main() { let mut system = Chip8ComputerManager::default(); println!("GOT A ROMS_DIRECTORY => [{:?}]", cli_options.roms_directory); - let mut ui_state = ImGuiUiState::new(cli_options.roms_directory); + println!("{:?}", std::env::var("PWD")); + let mut ui_state = if cli_options.roms_directory.is_none() { + ImGuiUiState::new(Some(PathBuf::from(ROMPATH_DEFAULT))) + } else { + ImGuiUiState::new(cli_options.roms_directory) + }; support::simple_init(file!(), move |_, ui| { let current_time = Instant::now(); diff --git a/gemmaimgui/src/bin/support/emmagui_support.rs b/gemmaimgui/src/bin/support/emmagui_support.rs index 0fdbb40..3dad500 100644 --- a/gemmaimgui/src/bin/support/emmagui_support.rs +++ b/gemmaimgui/src/bin/support/emmagui_support.rs @@ -1,8 +1,10 @@ use crate::support::gui_file_list::GuiFileList; use crate::ImGuiUiState; +use dimensioned::ucum::f32consts::CUP_M; use gemma::chip8::computer::Chip8Computer; use gemma::chip8::computer_manager::Chip8ComputerManager; use gemma::chip8::computer_manager::ManagerDumpables::{Keyboard, Registers, Video}; +use gemma::chip8::quirk_modes::QuirkMode::{self, *}; use gemma::chip8::system_memory::Chip8SystemMemory; use gemma::constants::CHIP8_KEYBOARD; use imgui::{CollapsingHeader, Condition, ImColor32, Ui}; @@ -116,7 +118,7 @@ impl GemmaImguiSupport { .build(|| { /* System Step Counter */ ui.text(format!("Step {:04x}", system_to_control.num_cycles()).as_str()); - + ui.text(format!("Mode {}", system_to_control.quirks_mode())); /* ROM Lister */ if CollapsingHeader::new("Roms").build(ui) { let new_filename = GuiFileList::display_path( @@ -158,7 +160,7 @@ impl GemmaImguiSupport { system_to_control.state().memory.peek(0x200), system_to_control.state().memory.peek(0x201), ]; - let mut show_buttons = bytes[0] != 0 || bytes[1] == 0xe0; + let show_buttons = bytes[0] != 0 || bytes[1] == 0xe0; if show_buttons { if ui.button("Step") { @@ -175,7 +177,7 @@ impl GemmaImguiSupport { } ui.same_line(); if ui.button("Reset") { - system_to_control.reset(); + system_to_control.reset(system_to_control.quirks_mode()); } if ui.button("Dump Video Memory") { println!("{}", system_to_control.dump_to_string(Video)); @@ -201,6 +203,27 @@ impl GemmaImguiSupport { ui.input_int("Target IPS", &mut gui_state.target_ips) .build(); }; + + let selectors = [Chip8, SChipModern, XOChip]; + for current_selector in selectors { + let mut working_selector = + ui.selectable_config(current_selector.clone().to_string()); + match system_to_control.quirks_mode() { + Chip8 => { + working_selector = working_selector.selected(true); + } + SChipModern => { + working_selector = working_selector.selected(true); + } + XOChip => { + working_selector = working_selector.selected(true); + } + } + if working_selector.build() { + system_to_control.reset(current_selector); + println!("CLICK ON {}", ¤t_selector); + } + } }); } diff --git a/gemmaimgui/src/bin/support/ui_state.rs b/gemmaimgui/src/bin/support/ui_state.rs index 89fa4c2..e36219d 100644 --- a/gemmaimgui/src/bin/support/ui_state.rs +++ b/gemmaimgui/src/bin/support/ui_state.rs @@ -16,7 +16,7 @@ pub struct ImGuiUiState { pub frame_time: u32, pub last_frame_instant: Instant, pub target_ips: i32, - pub roms_root_path: PathBuf, + pub roms_root_path: PathBuf } impl Clone for ImGuiUiState { @@ -33,8 +33,7 @@ impl Clone for ImGuiUiState { frame_time: self.frame_time, last_frame_instant: self.last_frame_instant, target_ips: self.target_ips, - - roms_root_path: self.roms_root_path.to_owned(), + roms_root_path: self.roms_root_path.to_owned() } } } @@ -53,7 +52,7 @@ impl Default for ImGuiUiState { frame_time: 16, last_frame_instant: Instant::now(), target_ips: 200000, - roms_root_path: PathBuf::from(ROMPATH_DEFAULT), + roms_root_path: PathBuf::from(ROMPATH_DEFAULT) } } } diff --git a/gemmasdl2/src/bin/gemmasdl2.rs b/gemmasdl2/src/bin/gemmasdl2.rs index 35b3613..acff6dc 100644 --- a/gemmasdl2/src/bin/gemmasdl2.rs +++ b/gemmasdl2/src/bin/gemmasdl2.rs @@ -1,13 +1,13 @@ mod support; -use std::{sync::Arc, time::Instant}; +use crate::support::gemma_egui_support::GemmaEguiSupport; use anyhow::Context; use egui::TextBuffer; use egui_glow::glow::{HasContext, COLOR_BUFFER_BIT}; use egui_sdl2_platform::sdl2; -use sdl2::event::{Event, WindowEvent}; use gemma::chip8::computer::Chip8Computer; +use sdl2::event::{Event, WindowEvent}; +use std::{sync::Arc, time::Instant}; use support::timestep::TimeStep; -use crate::support::gemma_egui_support::GemmaEguiSupport; const SCREEN_WIDTH: u32 = 800; const SCREEN_HEIGHT: u32 = 480; @@ -15,14 +15,19 @@ const SCREEN_HEIGHT: u32 = 480; async fn run() -> anyhow::Result { // Initialize SDL2 and video subsystem let sdl = sdl2::init().map_err(|e| anyhow::anyhow!("Failed to create SDL context: {}", e))?; - let mut video = sdl.video().map_err(|e| anyhow::anyhow!("Failed to initialize SDL video subsystem: {}", e))?; + let mut video = sdl + .video() + .map_err(|e| anyhow::anyhow!("Failed to initialize SDL video subsystem: {}", e))?; // Create SDL2 window and OpenGL context - let window = video.window("Window", SCREEN_WIDTH, SCREEN_HEIGHT) + let window = video + .window("Window", SCREEN_WIDTH, SCREEN_HEIGHT) .opengl() .position_centered() .build()?; - let _gl_context = window.gl_create_context().expect("Failed to create GL context"); + let _gl_context = window + .gl_create_context() + .expect("Failed to create GL context"); // Load OpenGL functions let gl = unsafe { @@ -34,7 +39,9 @@ async fn run() -> anyhow::Result { // Setup Egui and SDL2 platform let mut platform = egui_sdl2_platform::Platform::new(window.size())?; - let mut event_pump = sdl.event_pump().map_err(|e| anyhow::anyhow!("Failed to get SDL event pump: {}", e))?; + let mut event_pump = sdl + .event_pump() + .map_err(|e| anyhow::anyhow!("Failed to get SDL event pump: {}", e))?; // Initial settings let color = [0.0, 0.0, 0.0, 1.0]; // Background color @@ -75,7 +82,12 @@ async fn run() -> anyhow::Result { // Paint Egui outputs and update textures let size = window.size(); - painter.paint_and_update_textures([size.0, size.1], 1.0, paint_jobs.as_slice(), &full_output.textures_delta); + painter.paint_and_update_textures( + [size.0, size.1], + 1.0, + paint_jobs.as_slice(), + &full_output.textures_delta, + ); window.gl_swap_window(); // Run the timestep logic @@ -85,38 +97,56 @@ async fn run() -> anyhow::Result { for event in event_pump.poll_iter() { match event { Event::Quit { .. } - | Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } => break 'main, - Event::Window { window_id, win_event, .. } if window_id == window.id() => { + | Event::KeyDown { + keycode: Some(sdl2::keyboard::Keycode::Escape), + .. + } => break 'main, + Event::Window { + window_id, + win_event, + .. + } if window_id == window.id() => { if let WindowEvent::Close = win_event { break 'main; } } - Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::F3), .. } => { + Event::KeyUp { + keycode: Some(sdl2::keyboard::Keycode::F3), + .. + } => { println!("USER PRESSED F3 -> running"); is_running = true; } - Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::F4), .. } => { + Event::KeyUp { + keycode: Some(sdl2::keyboard::Keycode::F4), + .. + } => { println!("USER PRESSED F4 -> stopping"); is_running = false; } - Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F5), .. } => { + Event::KeyDown { + keycode: Some(sdl2::keyboard::Keycode::F5), + .. + } => { println!("USER PRESSED F5 -> Step"); computer.step_system(); } - Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F6), .. } => { + Event::KeyDown { + keycode: Some(sdl2::keyboard::Keycode::F6), + .. + } => { println!("USER PRESSED F6 -> RESET"); - computer.reset(); + computer.reset(computer.quirk_mode.clone()); } Event::ControllerButtonDown { which, .. } => { println!("PLAYER {which} DOWN"); } - Event::ControllerButtonDown { button, .. } => { - println!("BUTTON {:?}", button); - } - Event::JoyButtonDown {button_idx, .. } => { + Event::JoyButtonDown { button_idx, .. } => { println!("JoyButtonDown {}", button_idx); } - Event::JoyAxisMotion { which, axis_idx, .. } => { + Event::JoyAxisMotion { + which, axis_idx, .. + } => { println!("JoyAxismotion {which} {axis_idx}"); } _ => platform.handle_event(&event, &sdl, &video), diff --git a/gemmasdl2/src/bin/support/gemma_egui_support.rs b/gemmasdl2/src/bin/support/gemma_egui_support.rs index 4205938..3f4eda3 100644 --- a/gemmasdl2/src/bin/support/gemma_egui_support.rs +++ b/gemmasdl2/src/bin/support/gemma_egui_support.rs @@ -1,10 +1,10 @@ +use crate::Chip8Computer; +use egui::Rect; +use egui::Ui; +use egui::Vec2; +use egui::{Align, Color32, ComboBox, Pos2}; use std::fs::read_dir; use std::path::PathBuf; -use egui::{Align, Color32, ComboBox, Pos2, TextBuffer}; -use egui::Rect; -use egui::Vec2; -use egui::Ui; -use crate::Chip8Computer; const CELL_WIDTH: f32 = 5.0; const CELL_HEIGHT: f32 = 5.0; @@ -14,21 +14,26 @@ impl EGuiFileList { pub fn display_path(root: PathBuf, selected_filename: &mut String, ui: &mut Ui) { let working_filename = selected_filename.clone(); ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { - // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path"))); + // ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path"))); ComboBox::from_label("Select ROM") .selected_text(selected_filename.clone()) .show_ui(ui, |ui| { - let mut sorted_options = vec![]; for option in read_dir(root.as_path()).unwrap() { - let to_push = option.unwrap().file_name().into_string().unwrap_or( String::new()); + let to_push = option + .unwrap() + .file_name() + .into_string() + .unwrap_or(String::new()); sorted_options.push(to_push); } sorted_options.sort(); for item in sorted_options { - // Add each option to the ComboBox - if ui.selectable_label(selected_filename.eq(&item.as_str()), item.clone()).clicked() + // Add each option to the ComboBox + if ui + .selectable_label(selected_filename.eq(&item.as_str()), item.clone()) + .clicked() { *selected_filename = item; } @@ -43,65 +48,69 @@ pub struct GemmaEguiSupport {} impl GemmaEguiSupport { pub fn controls_view(system: &mut Chip8Computer, ui: &mut Ui) { - ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { - if ui.button("Start").clicked() { - println!("Start"); - // state.is_running = true; - } - if ui.button("Step").clicked() { - system.step_system(); - } - - if ui.button("Stop").clicked() { - println!("STOP"); - // state.is_running = false; - } - if ui.button("Reset").clicked() { - system.reset(); - // state.is_running = false; - } - }); - - // if ui.button(format!("Load {}", state.selected_rom_filename)).clicked() { - // load the bin... - // let read_bin = std::fs::read(PathBuf::from(format!("resources/roms/{}", state.selected_rom_filename))).unwrap(); - // ...then feed the system. - // system.load_bytes_to_memory(0x200, &read_bin); - // println!("Loaded {}", state.selected_rom_filename); - // } - // EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_rom_filename, ui); + ui.with_layout(egui::Layout::left_to_right(egui::Align::TOP), |ui| { + if ui.button("Start").clicked() { + println!("Start"); + // state.is_running = true; + } + if ui.button("Step").clicked() { + system.step_system(); + } + if ui.button("Stop").clicked() { + println!("STOP"); + // state.is_running = false; + } + if ui.button("Reset").clicked() { + system.reset(system.quirk_mode.clone()); + // state.is_running = false; + } + }); + // if ui.button(format!("Load {}", state.selected_rom_filename)).clicked() { + // load the bin... + // let read_bin = std::fs::read(PathBuf::from(format!("resources/roms/{}", state.selected_rom_filename))).unwrap(); + // ...then feed the system. + // system.load_bytes_to_memory(0x200, &read_bin); + // println!("Loaded {}", state.selected_rom_filename); + // } + // EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut state.selected_rom_filename, ui); ui.with_layout(egui::Layout::left_to_right(Align::TOP), |ui| { - // ui.checkbox(&mut state.display_memory, "Display Memory"); - // ui.checkbox(&mut state.display_video, "Display Video"); - // ui.checkbox(&mut state.display_registers, "Display Registers"); + // ui.checkbox(&mut state.display_memory, "Display Memory"); + // ui.checkbox(&mut state.display_video, "Display Video"); + // ui.checkbox(&mut state.display_registers, "Display Registers"); }); } pub fn registers_view(system: &Chip8Computer, ui: &mut Ui) { - ui.label(format!("V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", - system.registers.peek(0x00), - system.registers.peek(0x01), - system.registers.peek(0x02), - system.registers.peek(0x03), - system.registers.peek(0x04), - system.registers.peek(0x05), - system.registers.peek(0x06), - system.registers.peek(0x07) + ui.label(format!( + "V0-7: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", + system.registers.peek(0x00), + system.registers.peek(0x01), + system.registers.peek(0x02), + system.registers.peek(0x03), + system.registers.peek(0x04), + system.registers.peek(0x05), + system.registers.peek(0x06), + system.registers.peek(0x07) )); - ui.label(format!("V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", - system.registers.peek(0x08), - system.registers.peek(0x09), - system.registers.peek(0x0A), - system.registers.peek(0x0B), - system.registers.peek(0x0C), - system.registers.peek(0x0D), - system.registers.peek(0x0E), - system.registers.peek(0x0F) + ui.label(format!( + "V8-F: {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} ", + system.registers.peek(0x08), + system.registers.peek(0x09), + system.registers.peek(0x0A), + system.registers.peek(0x0B), + system.registers.peek(0x0C), + system.registers.peek(0x0D), + system.registers.peek(0x0E), + system.registers.peek(0x0F) + )); + ui.label(format!( + "PC: {:04x}\tI: {:04x}", + system.registers.peek_pc(), + system.registers.peek_i() )); - ui.label(format!("PC: {:04x}\tI: {:04x}", system.registers.peek_pc(), system.registers.peek_i())); } pub fn video_view(system: &Chip8Computer, ui: &mut Ui) { @@ -124,7 +133,7 @@ impl GemmaEguiSupport { // system.video_memory.peek(data_offset)); } } - // thread::sleep(Duration::from_secs(1)); + // thread::sleep(Duration::from_secs(1)); } pub fn memory_view(system: &Chip8Computer, ui: &mut Ui) { diff --git a/gemmautil/src/bin/ch8asm.rs b/gemmautil/src/bin/ch8asm.rs index 39e4d4f..914ecd4 100644 --- a/gemmautil/src/bin/ch8asm.rs +++ b/gemmautil/src/bin/ch8asm.rs @@ -1,9 +1,14 @@ -use std::fs; +use gemma::chip8::util::InstructionUtil; +use gemma::chip8::{instructions::Chip8CpuInstructions, quirk_modes::QuirkMode}; +use std::fs::{self, File}; +use std::io::Write; // Ch8Asm // Converts well formed CH8ASM. // no variables. // no labels. // nothing fun. +// VALID FORMAT +// <0x|0X>[, ][<0x|OX>[, ][<0x|OX>]][; comment] use pest::Parser; use pest_derive::Parser; @@ -14,62 +19,30 @@ pub struct Chip8AsmParser; fn main() { println!("Taxation is Theft"); - let unparsed = fs::read_to_string("resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc").expect("Unable to read input"); + let unparsed = fs::read_to_string("resources/test/gemma_disassembler_manual_document.asc") + .expect("Unable to read input"); - let file = Chip8AsmParser::parse(Rule::file, &unparsed).expect("Unable to parse. Try again.") - .next().unwrap(); + let file = Chip8AsmParser::parse(Rule::file, &unparsed) + .expect("Unable to parse. Try again.") + .next() + .unwrap(); + + println!("PREPARING TO WRITE THE TARGET FILE..."); + let mut target_file = File::create("thisismytarget.ch8").expect("Unable to create target"); for record in file.into_inner() { + // let mut working_instruction = Chip8CpuInstructions::XXXXERRORINSTRUCTION; match record.as_rule() { - Rule::instruction => { - println!("INSTRUCTION = {:?}", record.into_inner().flatten()) - } - _ => { - println!("UNHANDLED PART."); + Rule::record => { + print!("record = {:?}\t", record.as_str()); + let x = Chip8CpuInstructions::from_str(record.as_str()); + println!("DECODED TO {:?} {:04x}", x, x.encode()); + let (high, low) = InstructionUtil::split_bytes(x.encode()); + target_file + .write_all(&[high, low]) + .expect("Unable to write to the file."); } + _ => {} } } } - -#[cfg(test)] -mod test { - use pest::Parser; - use crate::{Chip8AsmParser, Rule}; - - #[test] - fn bits_all_parse() { - println!("PARSED: {:?}", - Chip8AsmParser::parse(Rule::instruction, "CLS") - ); - println!("PARSED: {:?}", - Chip8AsmParser::parse(Rule::parameter, "0x01") - ); - let parsed = Chip8AsmParser::parse(Rule::comment, "; comment").unwrap(); - for i in parsed { - println!("PARSED COMMENT -> {:?}", i); - } - - let parsed = - Chip8AsmParser::parse(Rule::record, "CLS ; comment").unwrap(); - for i in parsed { - println!("RULE PAIR THING: {:?}", i); - } - - let parsed = Chip8AsmParser::parse(Rule::record, "ADDI 0x01 ; comment"); - for i in parsed { - println!("RULE PAIR THING: {:?}", i); - } - - println!("PARSED: {:?}", - Chip8AsmParser::parse(Rule::record, "ADD ADD ADD") - ); - - println!("PARSED: {:?}", - Chip8AsmParser::parse(Rule::record, "ADD 0x01 0x02 ; Comment") - ); - - println!("PARSED: {:?}", - Chip8AsmParser::parse(Rule::record, "ADD 0x01 0x02 ; Comment") - ); - } -} \ No newline at end of file diff --git a/gemmautil/src/chip8_asm.pest b/gemmautil/src/chip8_asm.pest index 054c309..d305ba4 100644 --- a/gemmautil/src/chip8_asm.pest +++ b/gemmautil/src/chip8_asm.pest @@ -1,7 +1,7 @@ -WHITESPACE = _{ " " } -instruction = { ASCII_ALPHA+ } -parameter = { "0X" ~ ASCII_HEX_DIGIT+ } -comment = { ";" ~ WHITESPACE* ~ ASCII* } -parameters = { parameter ~ (WHITESPACE* ~ "," ~ WHITESPACE* ~ parameter)* } -record = { instruction ~ WHITESPACE* ~ parameters? ~ WHITESPACE* ~ comment? } -file = { SOI ~ (record ~ ("\r\n" | "\n"))* ~ EOI } +WHITESPACE = _{ " " | "\t" } // Allow tabs and multiple spaces +instruction = { ASCII_ALPHA+ } // Instruction, e.g., "CLS" or "LDX" +parameter = { ("0x" | "0X") ~ ASCII_HEX_DIGIT+ } // Parameter format, e.g., "0X250" +comment = { ";" ~ (!"\n" ~ ANY)* } // Capture everything after ";", up to end of line +parameters = { (parameter ~ (WHITESPACE* ~ ","* ~ WHITESPACE* ~ parameter)*)?} // Zero or more parameters +record = { instruction ~ WHITESPACE* ~ parameters ~ WHITESPACE* ~ comment? } // Record structure +file = { SOI ~ (record ~ (("\r\n" | "\n") | WHITESPACE*))* ~ EOI } // Allow for trailing newlines and empty lines diff --git a/resources/custom/001-schip-display-sprite.ch8 b/resources/custom/001-schip-display-sprite.ch8 new file mode 100644 index 0000000..c0ff896 Binary files /dev/null and b/resources/custom/001-schip-display-sprite.ch8 differ diff --git a/resources/custom/002-schip-scroll-tests.ch8 b/resources/custom/002-schip-scroll-tests.ch8 new file mode 100644 index 0000000..a9f978f Binary files /dev/null and b/resources/custom/002-schip-scroll-tests.ch8 differ diff --git a/resources/roms/000test1.ch8 b/resources/roms/000test1.ch8 new file mode 100644 index 0000000..1eff4db Binary files /dev/null and b/resources/roms/000test1.ch8 differ diff --git a/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc b/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc index bea2542..39d4fbc 100644 --- a/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc +++ b/resources/test/gemma_disassembler_1_chip_logo_ch8_asm.asc @@ -29,7 +29,7 @@ LDX 0X00, 0X18 ; 6018 LDI 0X2C8 ; A2C8 DRW 0X00, 0X01, 0X0F ; D01F LDX 0X00, 0X20 ; 6020 -LDI 2D7 ; A2D7 +LDI 0X2D7 ; A2D7 DRW 0X00, 0X01, 0X0F ; D01F LDX 0X00, 0X28 ; 6028 LDI 0X2E6 ; A2E6 diff --git a/resources/test/gemma_disassembler_manual_document.asc b/resources/test/gemma_disassembler_manual_document.asc index a2f9a6e..7cd68ee 100644 --- a/resources/test/gemma_disassembler_manual_document.asc +++ b/resources/test/gemma_disassembler_manual_document.asc @@ -23,11 +23,11 @@ JPI 0x0123 ; Bytes [0xb123] offset [0x002a] RND 0x01, 0x23 ; Bytes [0xc123] offset [0x002c] DRW 0x01, 0x02, 0x03 ; Bytes [0xd123] offset [0x002e] SKP 0x01 ; Bytes [0xe19e] offset [0x0030] -SNKP ; Bytes [0xe1a1] offset [0x0032] +SKNP 0x01 ; Bytes [0xe1a1] offset [0x0032] LDRD 0x01 ; Bytes [0xf107] offset [0x0034] LDRK 0x01 ; Bytes [0xf10a] offset [0x0036] LDD 0x01 ; Bytes [0xf115] offset [0x0038] -LIDS 0x01 ; Bytes [0xf118] offset [0x003a] +LDIS 0x01 ; Bytes [0xf118] offset [0x003a] ADDI 0x01 ; Bytes [0xf11e] offset [0x003c] LDF 0x01 ; Bytes [0xf129] offset [0x003e] BCD 0x01 ; Bytes [0xf133] offset [0x0040] diff --git a/resources/test/roms/1-chip8-logo.ch8 b/resources/test/roms/1-chip8-logo.ch8 new file mode 100644 index 0000000..19c5cf3 Binary files /dev/null and b/resources/test/roms/1-chip8-logo.ch8 differ diff --git a/resources/test/roms/2-ibm-logo.ch8 b/resources/test/roms/2-ibm-logo.ch8 new file mode 100644 index 0000000..677f227 Binary files /dev/null and b/resources/test/roms/2-ibm-logo.ch8 differ diff --git a/resources/test/roms/3-corax+.ch8 b/resources/test/roms/3-corax+.ch8 new file mode 100644 index 0000000..9fc874c Binary files /dev/null and b/resources/test/roms/3-corax+.ch8 differ diff --git a/resources/test/roms/3a-random_number_test.ch8 b/resources/test/roms/3a-random_number_test.ch8 new file mode 100644 index 0000000..a6225e5 Binary files /dev/null and b/resources/test/roms/3a-random_number_test.ch8 differ diff --git a/resources/test/roms/4-flags.ch8 b/resources/test/roms/4-flags.ch8 new file mode 100644 index 0000000..0698a10 Binary files /dev/null and b/resources/test/roms/4-flags.ch8 differ diff --git a/resources/test/roms/5-quirks.ch8 b/resources/test/roms/5-quirks.ch8 new file mode 100644 index 0000000..1c87f6c Binary files /dev/null and b/resources/test/roms/5-quirks.ch8 differ diff --git a/resources/test/roms/RPS.ch8 b/resources/test/roms/RPS.ch8 new file mode 100644 index 0000000..3ea42d4 Binary files /dev/null and b/resources/test/roms/RPS.ch8 differ diff --git a/resources/test/state/video_lowres_schip_draw_chip8_sprite_result.json b/resources/test/state/video_lowres_schip_draw_chip8_sprite_result.json new file mode 100644 index 0000000..e69de29