From 67ca71ccb7550a5f02cc1efa3284e670a052ea88 Mon Sep 17 00:00:00 2001 From: Trevor Merritt Date: Tue, 5 Nov 2024 10:02:19 -0500 Subject: [PATCH] my first schip rom works in my schip emulator. BUGFIX: Corrects runaway after drawing in my first schip rom scroll down, left, right all test with test rom assembler now assembles to the expected output it seems fixes incorrect loading of schip font to memory replaces schip font from new chatgpt feedback --- coverage/tarpaulin-report.html | 671 ------------------ gemma/src/chip8/computer.rs | 10 +- gemma/src/chip8/computer_manager.rs | 60 +- gemma/src/chip8/cpu_states.rs | 2 +- gemma/src/chip8/delay_timer.rs | 2 +- gemma/src/chip8/instructions.rs | 36 +- gemma/src/chip8/keypad.rs | 2 +- gemma/src/chip8/quirk_modes.rs | 16 +- gemma/src/chip8/registers.rs | 2 +- gemma/src/chip8/sound_timer.rs | 2 +- gemma/src/chip8/stack.rs | 2 +- gemma/src/chip8/system_memory.rs | 29 +- gemma/src/chip8/video.rs | 13 +- gemma/src/constants.rs | 110 +-- gemma/tests/computer_tests.rs | 40 +- gemma/tests/state_tests.rs | 63 +- gemma/tests/unit_tests.rs | 26 +- gemmaegui/src/bin/gemmaegui.rs | 57 +- .../src/bin/support/gemma_egui_support.rs | 78 +- gemmaimgui/src/bin/gemmaimgui.rs | 8 +- gemmaimgui/src/bin/support/emmagui_support.rs | 29 +- gemmaimgui/src/bin/support/ui_state.rs | 7 +- gemmasdl2/src/bin/gemmasdl2.rs | 70 +- .../src/bin/support/gemma_egui_support.rs | 129 ++-- gemmautil/src/bin/ch8asm.rs | 77 +- gemmautil/src/chip8_asm.pest | 14 +- resources/custom/001-schip-display-sprite.ch8 | Bin 0 -> 22 bytes resources/custom/002-schip-scroll-tests.ch8 | Bin 0 -> 26 bytes resources/roms/000test1.ch8 | Bin 0 -> 12 bytes ...gemma_disassembler_1_chip_logo_ch8_asm.asc | 2 +- .../gemma_disassembler_manual_document.asc | 4 +- resources/test/roms/1-chip8-logo.ch8 | Bin 0 -> 260 bytes resources/test/roms/2-ibm-logo.ch8 | Bin 0 -> 132 bytes resources/test/roms/3-corax+.ch8 | Bin 0 -> 761 bytes resources/test/roms/3a-random_number_test.ch8 | Bin 0 -> 34 bytes resources/test/roms/4-flags.ch8 | Bin 0 -> 1041 bytes resources/test/roms/5-quirks.ch8 | Bin 0 -> 3232 bytes resources/test/roms/RPS.ch8 | Bin 0 -> 2017 bytes ...lowres_schip_draw_chip8_sprite_result.json | 0 39 files changed, 509 insertions(+), 1052 deletions(-) delete mode 100644 coverage/tarpaulin-report.html create mode 100644 resources/custom/001-schip-display-sprite.ch8 create mode 100644 resources/custom/002-schip-scroll-tests.ch8 create mode 100644 resources/roms/000test1.ch8 create mode 100644 resources/test/roms/1-chip8-logo.ch8 create mode 100644 resources/test/roms/2-ibm-logo.ch8 create mode 100644 resources/test/roms/3-corax+.ch8 create mode 100644 resources/test/roms/3a-random_number_test.ch8 create mode 100644 resources/test/roms/4-flags.ch8 create mode 100644 resources/test/roms/5-quirks.ch8 create mode 100644 resources/test/roms/RPS.ch8 create mode 100644 resources/test/state/video_lowres_schip_draw_chip8_sprite_result.json 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 0000000000000000000000000000000000000000..c0ff8966b16375a4dfb80cadccba7d93b265b1d6 GIT binary patch literal 22 dcmZR0u%MCQUm{Zy(?zXBgCv6m-50f(!~kT{2qORh literal 0 HcmV?d00001 diff --git a/resources/custom/002-schip-scroll-tests.ch8 b/resources/custom/002-schip-scroll-tests.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..a9f978fd17f55ffb62bb29a64de0e1ddc1547dfd GIT binary patch literal 26 icmZR0u%MCQUm{Zy(?zXBgCv6m-50eOjxhXY_yYipr3%IX literal 0 HcmV?d00001 diff --git a/resources/roms/000test1.ch8 b/resources/roms/000test1.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..1eff4db64fa5fdb411b26bc62cb46ee552945a7a GIT binary patch literal 12 TcmZR0u%MCQUm{Zy(?tycArAz= literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..19c5cf30f469f86fce62351b46c5a68454d93429 GIT binary patch literal 260 zcmZR0kjR+8u_)kze1gEDcpxdUC=W;~EUE>P8jE^>q`{)u7vvKKfT~sjc>;@e0!fKQ zCxE2FqU%6XW6?7pX|U+)1$iMqekKrLV34;jeDL(aiv_$|8X6i53=%tbSXkKaXOLXF zci~(H1|C^iMqb&ujK?)KHPsmye*8Ii3u8sE(r%6T-=WO zB430%IN=}%-f$3y8ywuvo1TM%pmG)L;!wZuHIdwsy!^iJeeZi8Y1Hfz`(>9K@0jT6 z!|O0RNqYC-4?fHN>YzR>IW4yh2PX;q{E|Ryv>`f8hE+JsBzo9_7^MzJxpZwQSKv68 z@uDNMo>ky;Zm(q)%3daM;}zBJHTqWvH-;tHU%Jg!%MhE4Vwk;y`jt8{%MfWy+9$Lf zw8&|BEPQ0;XOZubyLGP>Zdh&V3u}rh4~ z-Xwf_Zr4%fo1tU1T4$p+IGsJU*@iaj!t~5}$>7ZyWoww7PmS{EW>a@-e&@G?lPfj#wF^yMUuo*7 z_cLXvRn&@9rJxwm71ZSfsot_%Wqwo6^gD|DL)^u`O5WyZi^svVqJqLt2cwSO9=Abry-()FK(1o4%cE0z0Z{ENrghk}*gw}|d zItdcm&sEBn2no1O*)pO**$u=TWkp1jvYUtxD7%GdQFa@#LD>o-r|b@5kFvXng0g#v z?GKN>*ptr3PS6PsvxB{66uuw7^1V_kGI-zAS`!iwS<7!^c1{lV zYS(s74fbsEoOolZ*UoK_O5Bc6{w(e!+XoJLns?81hV|>T=gK;PHCI1Rd(Egl7izvJ zO#(D?Ga8NdTG1M@=R?(3r8*0$!i*}TCTWyA$!_xHPh$7HzRgb z=~JLjI`Q^sHRi;&Mq5MGm%0*h=Aak#vL1moSC6V*;Oa!jpnI|Vy^Z=s_abPsN;}Pl z4pI9!Xky)Tc%rd#7%PW(CGV#H=K<$C4c+oK(m%yR=rvu!!?)_4?tyiix=I^Wg zRj1#^{C$;wL3K?tAU82jn7`b`{AH)w9)Z5>roiRLj0Vdlb2=ILylI?cSU zGOxqbF-%=|)Bl-C9FVU0GH~J)74yA cSXgC4m%};bL<#rQLB?;cTQ#+lSQq6@DJ}fl4H`u zk&DxLqlH`D*xpi76EbFkTNJ~Bu7MJ024S%6KwGmfCKlU%3MF&xuBW8PQK2YBM1U{b zDp=2)h7Ebs(+yHz{<}pf?cJ2q!pzb1?uQ>qC;LGzQ;wPb^b$oQ;YD#ClXbM!XOej}zyj;&+H|N5w&6Gb+Mb z{&lY$dAGK4>2imV!y{zhGA8dP} z3|9N!HN36C1;_6&`ZcP@H36I@1YV~I%^hgF&=x5QY=LF`g12gW zia{RK#G-V`=8>W_YdiZdX?3{IG*?lYvo&u~n#X-Up&i8C4y~~${mACsIB#kpnt6va zLqBjK#ko6`#h=dIhh5y0@3T`>w<^f5QT7-$roDtZJ=Lhm?Wpg3Wz+n(=98s*Xfaa9iJfgQD3M(3dlp$FX|g* zPJKErObEv7k);e=@ay6U$OvO(2`6AoI71dY0cQlNF+Z#b@|AuepOUZkLq2u($Q$B= zPYv8LE~mJ0E0r~FpMP;ez_&@5a2q_b#05-A7>f|RF>8U0Jy@wit-kyeFeLfZRY68afVBE|74aSv( zVBEr(ZYO4gaVD~qPp&(e273~isUiknPB3Iw@RfuMnFU{^HEQ@Mcf5Z&;Wn-&T*glm zb;eRcG;SoAaT7Hg9L<0+Nt%x;nmkmqfiJ>LpPx z={9a9J;v>1z43F@4mjGf@dN7nSp69o4_}&?o2_*@_~;P!7ju6~eO6o{{CZICAusJ? z?m?&TfPK$8^~;XlbH)tzd~sswSJlocLMZ0k6hnQEqAN7$FbkckKEdZ5CCnJJ!f%D` zjfhRY_CFZrY!_p7%WV%#O{D>6we=LlByNw{K>1%kwgX-h#Et=}WKe zIDeOi|Lc5y`}|E<Cw3wuJGk`Rp*w zoT=y3oR!O2rm9+2*t9GjwsKZDY?-*wE!;yJchIotq)E1}TV@VTQ%Bp>xt`ka2x?w6jJfBEZgehZ-Vz0iDmoq0Up3!R<>=(v*4-@HGE0p_+!0DQsLv-puOgYPSV z!D*}@U;{RMD+u#HsCX+gE3m>!*Gkvw#_GljTVaKb!p63r0kh+F+*jCvxG%o#M?)zf zRuIIdV)|G8t2MtL{7^vDh6!fPbeNS{*^h{`I4c373qfom;-xqd@9uOj>f ze*tW;*+vC`xl*mPq8ezFO@v*x?GhsP3~;ZK1m2@WYd{@pE2hTErE-btuZ1Qim71oL zE9F}S)j{>pRnp$hxhk-;7Vqb^CA10pV9M;7vcp46xjKE=@`9ZR3}N0P0x(> zYlGUV;P4?Bg7T~Ri0Q4Q$Gu`!5nGAv^y5F+d57qk(ECH_{(pQ;2Qm;kkP$)${tcdX B8T|kN literal 0 HcmV?d00001 diff --git a/resources/test/roms/RPS.ch8 b/resources/test/roms/RPS.ch8 new file mode 100644 index 0000000000000000000000000000000000000000..3ea42d4eeb79ee26cfe187f4faebe0c7088aaa63 GIT binary patch literal 2017 zcmbu9Z)_8F7{{OAUAuO-nHG?=r(4?q$yz00h?B{RZHAI@BdiPuF;OTcvX+@;FiBxk zdox2yaer7wMvQ?9)Gi|ie4)M=3r%=2-hkC;%#4mEnlK}|4OmziZO`X-cVmr&SALWB z^LzgOp69NqrN>?S<%v@#cDg~);jPFov)shQ!*0V=7vq@ywV>cO) z`D|t;t@=Hz!iU*;)mFaQnsJXDz8VG~HUe$AOav}x0Mhr{*N1Kc{4gy^H~YZQXEK$Q z+w-|RfOuHM%H;2VW@!UE`^X^MO$0y|!`bRgCXL5yKv))l>AQ*5pMlqIFE1_b}nROw69x^xon z0Rl8JpZ#!t&l*dKp25MMs*J7(1z>kjf#{8;X3|N#dO-#~3XL??f>UjmWoT{D%{%2v zvE2ls8o=k2&t`D}TC2~oYH|B3V@kc~@d^UNbTC;cCRB^Vske*D{0nzcURC4?hr?d) zZm$RXQ#Bk@Dd*(_hYncezf6>{XeJ>drl(`c*f$pe zq~SZgpN}F(r05dl2emJ^Jy!z|YT9{d`OnDL*M&~KgRHF$(OnHX)SskbDuT6mv{gq~b88ay=#ph@;;_o0$Z7jtdO~%W<%l;(V#$uzMMSHPQIms!r zIc1&!xrF0Nj3$tM{rx^_bH&9RwNw3C+*qu{wR#yQ-%g#OMyF8U0WbyA#;Iw5F*vg2 zNFBf&OxD$12e<;AwL&Mr5d75GI0;~bdZA;b)`Y_8n8K_JSK{$8tm^)2)v7tP9qv`` zVSMf1of;Z!2H4#=xpb)wpkqa0snP|ryk79aF@Q?}*BbZ&XE=FsBMbt}4$cigBS6ZR zvcU=fmSLHKo3&%dK`+jaumAXQiE_;0E~gwC7`R7yW?(?sbFO<3Cwr`DbY$eW#}-7c z51)=Kn!V9zROzO=q{tm-f%%V6FVrt4i+H3TO@4oWe_eO~4OxJSivp>*Autuw0_$8S z=$yL*y>p4B2c&bWof{u;tSOxZ)wQK4u+S@MAyH`+WmB$FwpE z&8@6LTX1!tbRwd}$F(>mjZ@l!rE0u}SaWV-z3bZHW~_6r7u~Ei=knAr{4zJg8_>)0 z&!Cs^%h1d6F0{+hOZb)OWq6m%15!c=k^q@Fns_T%O!*%EfD)@UUybG?=nH7Rkmh4R zssg?<@oDhY!1EKIBqBkUvWoD1=u^(