diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 7580cc3..15ec57c 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -9,10 +9,8 @@ - - - + - + + + - - @@ -259,6 +263,7 @@ + @@ -268,17 +273,6 @@ - - - - - file://$PROJECT_DIR$/emma/src/chip8/system_memory.rs - 46 - - - - diff --git a/Cargo.lock b/Cargo.lock index 2a6a087..bb4c37b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -743,6 +743,15 @@ dependencies = [ "wayland-client 0.31.6", ] +[[package]] +name = "catppuccin-egui" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2413b3bbc4e1b627b453d9147c156076b74f78e102e90797463bab0486eee6" +dependencies = [ + "egui 0.29.1", +] + [[package]] name = "cc" version = "1.1.30" @@ -1785,6 +1794,7 @@ dependencies = [ name = "gemmaegui" version = "0.1.0" dependencies = [ + "catppuccin-egui", "eframe", "egui 0.29.1", "gemma", @@ -1795,6 +1805,7 @@ name = "gemmaimgui" version = "0.1.0" dependencies = [ "chrono", + "clap", "copypasta", "dimensioned", "gemma", diff --git a/coverage/tarpaulin-report.html b/coverage/tarpaulin-report.html new file mode 100644 index 0000000..29b0636 --- /dev/null +++ b/coverage/tarpaulin-report.html @@ -0,0 +1,671 @@ + + + + + + + +
+ + + + + + \ No newline at end of file diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs index 3ee4de5..509dc7a 100644 --- a/gemma/src/chip8/computer.rs +++ b/gemma/src/chip8/computer.rs @@ -1,6 +1,7 @@ use log::{debug}; use crate::chip8::delay_timer::DelayTimer; use crate::chip8::keypad::Keypad; +use crate::chip8::quirk_modes::QuirkMode; use crate::chip8::registers::Chip8Registers; use crate::chip8::sound_timer::SoundTimer; use crate::chip8::stack::Chip8Stack; @@ -19,7 +20,8 @@ pub struct Chip8Computer { pub video_memory: Chip8Video, pub state: Chip8CpuStates, pub keypad: Keypad, - pub stack: Chip8Stack + pub stack: Chip8Stack, + pub quirk_mode: QuirkMode } impl Default for Chip8Computer { fn default() -> Self { @@ -32,7 +34,8 @@ impl Default for Chip8Computer { delay_timer: DelayTimer::new(), state: Chip8CpuStates::default(), keypad: Keypad::default(), - stack: Chip8Stack::default() + stack: Chip8Stack::default(), + quirk_mode: QuirkMode::default() } } } @@ -45,6 +48,8 @@ impl Chip8Computer { self.delay_timer.reset(); self.sound_timer.reset(); self.stack.reset(); + self.memory.reset(); + self.quirk_mode = QuirkMode::Chip8; } pub fn dump_keypad_to_string(&self) -> String { diff --git a/gemma/src/chip8/computer_manager.rs b/gemma/src/chip8/computer_manager.rs index 9cba622..fb8d558 100644 --- a/gemma/src/chip8/computer_manager.rs +++ b/gemma/src/chip8/computer_manager.rs @@ -23,7 +23,7 @@ impl Default for Chip8ComputerManager { fn default() -> Self { Chip8ComputerManager { core_should_run: false, - one_step: true, + one_step: false, core_cycle_timer: false, core_last_cycle_start: Instant::now() , computer: Chip8Computer::new() @@ -32,18 +32,16 @@ impl Default for Chip8ComputerManager { } impl Chip8ComputerManager { - pub fn reset(&mut self) { + self.one_step = false; + self.core_should_run = false; self.computer.reset(); } pub fn new() -> Chip8ComputerManager { let core_handle = thread::spawn(move || { loop { let start_time = Instant::now(); - // println!("Core Thread starting at {start_time:?}"); - let sleep_time = Instant::now().duration_since(start_time).as_millis(); - // println!("Core Thread sleeping for {sleep_time}ms"); sleep(Duration::from_millis((16 - sleep_time) as u64)); } }); @@ -67,13 +65,15 @@ impl Chip8ComputerManager { &self.computer } - pub fn tick( &mut self) { + pub fn tick( &mut self) -> bool { // println!("STARTING TICK"); + let mut did_tick: bool = false; if self.one_step | self.core_should_run { match self.computer.state { WaitingForInstruction => { self.core_last_cycle_start = Instant::now(); self.computer.step_system(); + did_tick = true // println!("SYSTEM STEP"); } _ => {} @@ -84,7 +84,9 @@ impl Chip8ComputerManager { // stop the CPU for the next cycle, we are only // wanting one step. self.one_step = false; + did_tick = true; } + did_tick } pub fn press_key(&mut self, key_index: u8) { diff --git a/gemma/src/chip8/delay_timer.rs b/gemma/src/chip8/delay_timer.rs index f289675..4ce2b44 100644 --- a/gemma/src/chip8/delay_timer.rs +++ b/gemma/src/chip8/delay_timer.rs @@ -1,10 +1,10 @@ #[derive(Clone, Copy)] pub struct DelayTimer { - counter: i32 + counter: u8 } impl DelayTimer { - pub fn current(&self) -> i32 { + pub fn current(&self) -> u8 { self.counter } @@ -18,8 +18,8 @@ impl DelayTimer { self.counter = 0xff; } - pub fn set_timer(&mut self, new_value: i32) { - self.counter = new_value + pub fn set_timer(&mut self, new_value: u8) { + self.counter = new_value as u8 } pub fn tick(&mut self) { diff --git a/gemma/src/chip8/instructions.rs b/gemma/src/chip8/instructions.rs index 171b657..1bf7297 100644 --- a/gemma/src/chip8/instructions.rs +++ b/gemma/src/chip8/instructions.rs @@ -2,11 +2,12 @@ use std::arch::x86_64::_mm_xor_pd; use std::fmt::{Debug, Display, Formatter}; use std::ops::{BitAnd, Deref, Shr}; use std::time::Instant; +use chrono::ParseMonthError; use log::debug; use rand::{random, Rng}; use crate::chip8::computer::{Chip8Computer}; use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey; -use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION; +use crate::chip8::instructions::Chip8CpuInstructions::*; use crate::chip8::util::InstructionUtil; use crate::constants::{*}; @@ -188,7 +189,7 @@ pub enum Chip8CpuInstructions { /// Set sound timer = Vx. /// /// ST is set equal to the value of Vx. - LDI_S(u8), + LDIS(u8), /// Fx1E - ADD I, Vx /// Set I = I + Vx. /// @@ -280,7 +281,7 @@ impl Chip8CpuInstructions { Chip8CpuInstructions::LDIA(_) => INST_LDIA, Chip8CpuInstructions::LDIX(_) => INST_LDIX, Chip8CpuInstructions::LIDR(_) => INST_LIDR, - Chip8CpuInstructions::LDI_S(_) => INST_LIDS, + Chip8CpuInstructions::LDIS(_) => INST_LDIS, Chip8CpuInstructions::LDR(_, _) => INST_LDR, Chip8CpuInstructions::LDRD(_) => INST_LDRD, Chip8CpuInstructions::LDRI(_) => INST_LDRI, @@ -327,6 +328,7 @@ impl Chip8CpuInstructions { Chip8CpuInstructions::ADD(x, byte) => { format!("0x{x:02x}, 0x{byte:02x}") } + // Reg, Reg Chip8CpuInstructions::SEY(x, y) | Chip8CpuInstructions::LDR_Y(x, y) | Chip8CpuInstructions::OR(x, y) | @@ -338,50 +340,162 @@ impl Chip8CpuInstructions { Chip8CpuInstructions::SUBC(x, y) | Chip8CpuInstructions::SHL(x, y) | Chip8CpuInstructions::SNEY(x, y) => { - format!("0x{x:02x}, 0x{y:02x}") + format!("0x{x:01x}, 0x{y:01x}") } + // Reg, Reg, Nibble Chip8CpuInstructions::DRW(x, y, nibble) => { format!("0x{x:02x}, 0x{y:02x}, 0x{nibble:02x}") } + // Registers. 0-F Chip8CpuInstructions::LDD(x) | - Chip8CpuInstructions::LDI_S(x) | + Chip8CpuInstructions::LDIS(x) | Chip8CpuInstructions::ADDI(x) | Chip8CpuInstructions::LDFX(x) | Chip8CpuInstructions::BCD(x) | Chip8CpuInstructions::LDIX(x) | - Chip8CpuInstructions::SKP(x) | Chip8CpuInstructions::LDRD(x) | Chip8CpuInstructions::LDRK(x) | Chip8CpuInstructions::LDRI(x) | Chip8CpuInstructions::LDF2(x) | Chip8CpuInstructions::STR(x) | Chip8CpuInstructions::LIDR(x) | + Chip8CpuInstructions::SDN(x) | + Chip8CpuInstructions::SKNP(x) | Chip8CpuInstructions::SKP(x) => { format!("0x{x:02x}") } - _ => { String::new() } + Chip8CpuInstructions::EXIT | + Chip8CpuInstructions::ENA | + Chip8CpuInstructions::DIS | + Chip8CpuInstructions::SLF | + Chip8CpuInstructions::XXXXERRORINSTRUCTION | + Chip8CpuInstructions::SRT | + Chip8CpuInstructions::CLS | + Chip8CpuInstructions::RET => { + String::new() + } } } } impl Display for Chip8CpuInstructions { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{} {}", self.name(), self.operands()) + let ops = self.operands(); + let space = if ops.is_empty() { "" } else { " " }; + write!(f, "{}{}{}", self.name(), space, ops) } } impl Chip8CpuInstructions { pub fn from_str(input: &str) -> Chip8CpuInstructions { - let parts = input.split(" "); - println!("THERE ARE {} PARTS", parts.count()); - XXXXERRORINSTRUCTION - // - // match input.to_uppercase().as_str() { - // INST_ADDI => Chip8CpuInstructions::ADDI(parts.nth(1)), - // INST_ADD => Chip8CpuInstructions::ADD(parts[1], parts[2]), - // INST_ADDR => Chip8CpuInstructions::ADDR(parts[1], parts[2]), - // _ => XXXXERRORINSTRUCTION - // } + let mut parts = input.split(" "); + // print!("THERE ARE {} PARTS", parts.clone().count()); + let first_part = parts.next().unwrap_or(""); + // take the next value... + // ...strip off the extra... + // ...convert it to an integer from base 16 + let param1 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0); + let param2 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0); + let param3 = u16::from_str_radix(parts.next().unwrap_or("0").trim_start_matches("0x").trim_end_matches(","), 16).unwrap_or(0); + // println!("\tFirst part is {:?} / {:?} / {:?} / {:?}", first_part, param1 ,param2 ,param3); + match first_part { + INST_CLS => { + CLS + } + INST_DRW => { + DRW(param1 as u8, param2 as u8, param3 as u8) + } + INST_ADD => { + ADD(param1 as u8, param2 as u8) + } + INST_CALL => { + CALL(param1) + } + INST_SYS => { + SYS(param1) + } + INST_RET => { + RET + } + INST_JPA => { + JPA(param1) + } + INST_JPI => { + JPI(param1) + } + INST_SEX => { + SEX(param1 as u8, param2 as u8) + } + INST_SNEB => { + SNEB(param1 as u8, param2 as u8) + } + INST_SDN => { + SDN(param1 as u8) + } + INST_SRT => { + SRT + } + INST_SLF => { + SLF + } + INST_EXIT => { + EXIT + } + INST_DIS => { + DIS + } + INST_ENA => { + ENA + } + INST_SEY => { + SEY(param1 as u8, param2 as u8) + } + INST_LDRY => { + LDR_Y(param1 as u8, param2 as u8) + } + INST_LDR => { + LDR(param1 as u8, param2 as u8) + } + INST_OR => { + OR(param1 as u8, param2 as u8) + } + INST_AND => { + AND(param1 as u8, param2 as u8) + } + INST_ORY => { + ORY(param1 as u8, param2 as u8) + } + INST_ADDR => { + ADDR(param1 as u8, param2 as u8) + } + INST_SUB => { + SUB(param1 as u8, param2 as u8) + } + INST_SHR => { + SHR(param1 as u8, param2 as u8) + } + INST_SHL => { + SHL(param1 as u8, param2 as u8) + } + INST_SUBC => { + SUBC(param1 as u8, param2 as u8) + } + INST_SNEY => { + SNEY(param1 as u8, param2 as u8) + } + INST_LDIA => { + LDIA(param1) + } + INST_RND => { + RND(param1 as u8, param2 as u8) + } + INST_DRW => { + DRW(param1 as u8, param2 as u8, param3 as u8) + } + _ => { + XXXXERRORINSTRUCTION + } + } } pub fn encode(&self) -> u16 { @@ -417,7 +531,7 @@ impl Chip8CpuInstructions { Chip8CpuInstructions::LDRD(x_register) => 0xF007 | ((*x_register as u16) << 8), Chip8CpuInstructions::LDRK(x_register) => 0xF00A | ((*x_register as u16) << 8), Chip8CpuInstructions::LDD(x_register) => 0xF015 | ((*x_register as u16) << 8), - Chip8CpuInstructions::LDI_S(x_register) => 0xF018 | ((*x_register as u16) << 8), + Chip8CpuInstructions::LDIS(x_register) => 0xF018 | ((*x_register as u16) << 8), Chip8CpuInstructions::ADDI(x_register) => 0xF01E | ((*x_register as u16) << 8), Chip8CpuInstructions::LDFX(x_register) => 0xF029 | ((*x_register as u16) << 8), Chip8CpuInstructions::BCD(x_register) => 0xF033 | ((*x_register as u16) << 8), @@ -488,7 +602,7 @@ impl Chip8CpuInstructions { 0x07 => Chip8CpuInstructions::LDRD(ubln), 0x0A => Chip8CpuInstructions::LDRK(ubln), 0x15 => Chip8CpuInstructions::LDD(ubln), - 0x18 => Chip8CpuInstructions::LDI_S(ubln), + 0x18 => Chip8CpuInstructions::LDIS(ubln), 0x1E => Chip8CpuInstructions::ADDI(ubln), 0x29 => Chip8CpuInstructions::LDFX(ubln), 0x30 => Chip8CpuInstructions::LDF2(ubln), @@ -816,9 +930,9 @@ impl Chip8CpuInstructions { // // DT is set equal to the value of Vx. let new_time = input.registers.peek(*source_register as u8); - input.delay_timer.set_timer(new_time as i32); + input.delay_timer.set_timer(new_time); } - Chip8CpuInstructions::LDI_S(new_time) => { + Chip8CpuInstructions::LDIS(new_time) => { let new_value = input.registers.peek(*new_time as u8); input.sound_timer.set_timer(new_value as i32); } diff --git a/gemma/src/chip8/keypad.rs b/gemma/src/chip8/keypad.rs index 039b1f7..05bacf5 100644 --- a/gemma/src/chip8/keypad.rs +++ b/gemma/src/chip8/keypad.rs @@ -11,15 +11,17 @@ impl Keypad { // draw a 4x4 grid showing the keys with * filling the cells that are depressed for row in CHIP8_KEYBOARD.iter() { for (index, key) in row.iter().enumerate() { - let is_lit = if self.keys[*key as usize] { "*".to_string() } else { char::from_digit(*key as u32, 16).unwrap_or(' ').to_string() }; - match index { - 3 => { - // last in col - return_value += format!("|{}|\n", is_lit).as_str(); - } - _=> { - return_value += format!("|{}", is_lit).as_str(); - } + let is_lit = if self.keys[*key as usize] { + "*".to_string() + } else { + char::from_digit(*key as u32, 16).unwrap_or(' ').to_string() + }; + + if index == 3 { + return_value += format!("|{}|\n", is_lit).as_str(); + + } else { + return_value += format!("|{}", is_lit).as_str(); } } } diff --git a/gemma/src/chip8/quirk_modes.rs b/gemma/src/chip8/quirk_modes.rs new file mode 100644 index 0000000..17c443d --- /dev/null +++ b/gemma/src/chip8/quirk_modes.rs @@ -0,0 +1,8 @@ +#[derive(Default, Clone)] +pub enum QuirkMode { + #[default] + Chip8, + SChipLegacy, + XOChip, + SChipModern +} diff --git a/gemma/src/chip8/system_memory.rs b/gemma/src/chip8/system_memory.rs index cbed98d..d009a6a 100644 --- a/gemma/src/chip8/system_memory.rs +++ b/gemma/src/chip8/system_memory.rs @@ -20,6 +20,12 @@ impl Default for Chip8SystemMemory { } } impl Chip8SystemMemory { + + pub fn reset(&mut self){ + self.memory = [0x00; CHIP8_MEMORY_SIZE as usize]; + self.load_fonts_to_memory(); + self.load_schip_fonts_to_memory(); + } pub fn new() -> Self { Chip8SystemMemory { diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs index 57e35c0..a8941d0 100644 --- a/gemma/src/chip8/video.rs +++ b/gemma/src/chip8/video.rs @@ -136,10 +136,9 @@ impl Chip8Video { (CHIP8_VIDEO_WIDTH, CHIP8_VIDEO_HEIGHT) } } - fn get_memory_size(&self) -> i32 { - let w = self.get_resolution(); - w.1 * w.0 + let (width, height) = self.get_resolution(); + width * height } pub fn tick(&mut self) { diff --git a/gemma/src/constants.rs b/gemma/src/constants.rs index dbeb3f3..21452b5 100644 --- a/gemma/src/constants.rs +++ b/gemma/src/constants.rs @@ -36,7 +36,7 @@ pub const INST_LDF2: &str = "LDF2"; pub const INST_LDIA: &str = "LDIA"; pub const INST_LDIX: &str = "LDIX"; pub const INST_LIDR: &str = "LIDR"; -pub const INST_LIDS: &str = "LIDS"; +pub const INST_LDIS: &str = "LIDS"; pub const INST_LDR: &str = "LDR"; pub const INST_LDRD: &str = "LDRD"; pub const INST_LDRI: &str = "LDRI"; diff --git a/gemma/src/lib.rs b/gemma/src/lib.rs index b95b90c..1f26cfb 100644 --- a/gemma/src/lib.rs +++ b/gemma/src/lib.rs @@ -11,8 +11,8 @@ pub mod chip8 { pub mod registers; pub mod stack; - pub mod computer_manager; + pub mod quirk_modes; } pub mod constants; \ No newline at end of file diff --git a/gemma/tests/unit_tests.rs b/gemma/tests/unit_tests.rs index 402c34d..eae71dc 100644 --- a/gemma/tests/unit_tests.rs +++ b/gemma/tests/unit_tests.rs @@ -1,8 +1,10 @@ +use std::collections::{BTreeMap, BTreeSet}; use log::debug; use rand::random; use gemma::chip8::computer::Chip8Computer; use gemma::chip8::delay_timer::DelayTimer; use gemma::chip8::instructions::Chip8CpuInstructions; +use gemma::chip8::instructions::Chip8CpuInstructions::JPA; use gemma::chip8::keypad::Keypad; use gemma::chip8::registers::Chip8Registers; use gemma::chip8::sound_timer::SoundTimer; @@ -11,7 +13,7 @@ use gemma::chip8::util::InstructionUtil; use gemma::chip8::video::Chip8Video; use gemma::constants::*; -const TEST_OUTPUT_SAMPLE_DIR: &str = "/home/tmerritt/Projects/trevors_chip8_toy/resources/test/"; +const TEST_OUTPUT_SAMPLE_DIR: &str = "/home/tmerritt/Projects/chip8_toy/resources/test/"; fn read_test_result(suffix: &str) -> String { std::fs::read_to_string(TEST_OUTPUT_SAMPLE_DIR.to_owned() + suffix) @@ -54,7 +56,7 @@ fn encode_decode_test() { assert_eq!(Chip8CpuInstructions::LDRD(0x1).encode(), 0xf107); assert_eq!(Chip8CpuInstructions::LDRK(0x4).encode(), 0xf40a); assert_eq!(Chip8CpuInstructions::LDD(0x6).encode(), 0xf615); - assert_eq!(Chip8CpuInstructions::LDI_S(0xb).encode(), 0xfb18); + assert_eq!(Chip8CpuInstructions::LDIS(0xb).encode(), 0xfb18); assert_eq!(Chip8CpuInstructions::ADDI(0xd).encode(), 0xfd1e); assert_eq!(Chip8CpuInstructions::LDFX(0xc).encode(), 0xfc29); assert_eq!(Chip8CpuInstructions::BCD(0xd).encode(), 0xfd33); @@ -110,7 +112,7 @@ fn encode_decode_test() { assert!(matches!(Chip8CpuInstructions::decode(0xf107), Chip8CpuInstructions::LDRD(0x1))); assert!(matches!(Chip8CpuInstructions::decode(0xf40a), Chip8CpuInstructions::LDRK(0x4))); assert!(matches!(Chip8CpuInstructions::decode(0xf615), Chip8CpuInstructions::LDD(0x6))); - assert!(matches!(Chip8CpuInstructions::decode(0xfb18), Chip8CpuInstructions::LDI_S(0xb))); + assert!(matches!(Chip8CpuInstructions::decode(0xfb18), Chip8CpuInstructions::LDIS(0xb))); assert!(matches!(Chip8CpuInstructions::decode(0xfd1e), Chip8CpuInstructions::ADDI(0xd))); assert!(matches!(Chip8CpuInstructions::decode(0xfc29), Chip8CpuInstructions::LDFX(0xc))); assert!(matches!(Chip8CpuInstructions::decode(0xfd33), Chip8CpuInstructions::BCD(0xd))); @@ -334,7 +336,7 @@ fn addivx_test() { fn ldstvt_test() { let mut x = Chip8Computer::new(); x.registers.poke(0x1, 0xf0); - Chip8CpuInstructions::LDI_S(0x01).execute(&mut x); + Chip8CpuInstructions::LDIS(0x01).execute(&mut x); assert_eq!(x.sound_timer.current(), 0xf0); x.sound_timer.tick(); x.sound_timer.tick(); @@ -565,7 +567,6 @@ fn subn_vx_vy_test() { assert_eq!(x.registers.peek_pc(), 0x206); } -#[test] fn draw_nibble_vx_vy_n_test_hd() { let mut x = Chip8Computer::new(); @@ -770,6 +771,15 @@ fn keypad_keys_check() { assert!(k.released(1)); } + +#[test] +fn keypad_string_format_test() { + let mut k = Keypad::new(); + + + assert_eq!(k.format_as_string(), read_test_result("gemma_keypad_string_result.asc")); +} + #[test] fn register_rw_test() { let mut x = Chip8Registers::default(); @@ -853,7 +863,7 @@ fn stack_push_pop_test() { #[should_panic] fn stack_overflow_test() { let mut x = Chip8Stack::new(); - for i in 0..17 { + for i in 0..17 { x.push(&i); } } @@ -894,7 +904,6 @@ fn stack_lots_of_subs() { } assert_eq!(x.depth(), 15); } - } @@ -945,7 +954,6 @@ fn instruction_byte_to_bool_changes() { } - fn real_build_checkboard(in_hd: bool) -> Chip8Video { let mut r = Chip8Video::default(); let (width, height) = if in_hd { @@ -1318,3 +1326,320 @@ fn video_scroll_right_4_row_test_high_def() { x.scroll_right(); assert_eq!(read_test_result("test_scroll_right_4_hd.asc"), x.format_as_string()); } + +struct InstructionTest { + name: String, + instruction: Chip8CpuInstructions, + asm: String, + encoded: u16, +} + +#[test] +fn instructions_name_tests() { + assert_eq!(Chip8CpuInstructions::SYS(0x0000).name(), INST_SYS); + assert_eq!(Chip8CpuInstructions::ADD(0x0, 0x1).name(), INST_ADD); + assert_eq!(Chip8CpuInstructions::ADDI(0x0).name(), INST_ADDI); + assert_eq!(Chip8CpuInstructions::ADDR(0x01, 0x02).name(), INST_ADDR); + assert_eq!(Chip8CpuInstructions::AND(0x01, 0x02).name(), INST_AND); + + let it = vec![ + InstructionTest { + name: INST_SYS.to_string(), + instruction: Chip8CpuInstructions::SYS(0x123), + asm: "SYS 0x0123".to_string(), + encoded: 0x0123 + }, + InstructionTest { + name: INST_CLS.to_string(), + instruction: Chip8CpuInstructions::CLS, + asm: "CLS".to_string(), + encoded: 0x00E0 + }, + InstructionTest { + name: INST_RET.to_string(), + instruction: Chip8CpuInstructions::RET, + asm: "RET".to_string(), + encoded: 0x00ee + }, + InstructionTest { + name: INST_JPA.to_string(), + instruction: JPA(0x234), + asm: "JPA 0x0234".to_string(), + encoded: 0xb234 + }, + InstructionTest { + name: INST_CALL.to_string(), + instruction: Chip8CpuInstructions::CALL(0x123), + asm: "CALL 0x0123".to_string(), + encoded: 0x2123, + }, + InstructionTest { + name: INST_DRW.to_string(), + instruction: Chip8CpuInstructions::DRW(0x01, 0x02, 0x03), + asm: "DRW 0x01, 0x02, 0x03".to_string(), + encoded: 0xd123 + }, + InstructionTest { + name: INST_JPI.to_string(), + instruction: Chip8CpuInstructions::JPI(0x321), + asm: "JPI 0x0321".to_string(), + encoded: 0xb321 + }, + InstructionTest { + name: INST_SDN.to_string(), + instruction: Chip8CpuInstructions::SDN(0x01), + asm: "SDN 0x01".to_string(), + encoded: 0x00c1 + }, + InstructionTest { + name: INST_SRT.to_string(), + instruction: Chip8CpuInstructions::SRT, + asm: "SRT".to_string(), + encoded: 0x00FB + }, + InstructionTest { + name: INST_SLF.to_string(), + instruction: Chip8CpuInstructions::SLF, + asm: "SLF".to_string(), + encoded: 0x00FC, + }, + InstructionTest { + name: INST_EXIT.to_string(), + instruction: Chip8CpuInstructions::EXIT, + asm: "EXIT".to_string(), + encoded: 0x00FD, + }, + InstructionTest { + name: INST_DIS.to_string(), + instruction: Chip8CpuInstructions::DIS, + asm: "DIS".to_string(), + encoded: 0x00FE, + }, + InstructionTest { + name: INST_ENA.to_string(), + instruction: Chip8CpuInstructions::ENA, + asm: "ENA".to_string(), + encoded: 0x00FF, + }, + InstructionTest { + name: INST_SEX.to_string(), + instruction: Chip8CpuInstructions::SEX(0x01, 0xfa), + asm: "SEX 0x01, 0xfa".to_string(), + encoded: 0x32fa, + }, + InstructionTest { + name: INST_SNEB.to_string(), + instruction: Chip8CpuInstructions::SNEB(0x01, 0xab), + asm: "SNEB 0x01, 0xab".to_string(), + encoded: 0x41ab, + }, + InstructionTest { + name: INST_SEY.to_string(), + instruction: Chip8CpuInstructions::SEY(0x1, 0x2), + asm: "SEY 0x1, 0x2".to_string(), + encoded: 0x5120 + }, + InstructionTest { + name: INST_LDR.to_string(), + instruction: Chip8CpuInstructions::LDR(0xa, 0xbe), + asm: "LDR 0x0a, 0xbe".to_string(), + encoded: 0x6abe, + }, + InstructionTest { + name: INST_ADD.to_string(), + instruction: Chip8CpuInstructions::ADD(0x01, 0xab), + asm: "ADD 0x01, 0xab".to_string(), + encoded: 0x71ab + }, + InstructionTest { + name: INST_LDRY.to_string(), + instruction: Chip8CpuInstructions::LDR_Y(0x1, 0x2), + asm: "LDRY 0x1, 0x2".to_string(), + encoded: 0x8120, + }, + InstructionTest { + name: INST_OR.to_string(), + instruction: Chip8CpuInstructions::OR(0x1, 0x2), + asm: "OR 0x1, 0x2".to_string(), + encoded: 0x8121 + }, + InstructionTest { + name: INST_AND.to_string(), + instruction: Chip8CpuInstructions::AND(0xb, 0xc), + asm: "AND 0xb, 0xc".to_string(), + encoded: 0x8bc2, + }, + InstructionTest { + name: INST_ORY.to_string(), + instruction: Chip8CpuInstructions::ORY(0xa, 0x3), + asm: "ORY 0xa, 0x3".to_string(), + encoded: 0x8a33 + }, + InstructionTest { + name: INST_ADDR.to_string(), + instruction: Chip8CpuInstructions::ADDR(0x1, 0x2), + asm: "ADDR 0x1, 0x2".to_string(), + encoded: 0x8124 + }, + InstructionTest { + name: INST_SUB.to_string(), + instruction: Chip8CpuInstructions::SUB(0x4, 0x5), + asm: "SUB 0x4, 0x5".to_string(), + encoded: 0x8455 + }, + InstructionTest { + name: INST_SHR.to_string(), + instruction: Chip8CpuInstructions::SHR(0x01, 0x1), + asm: "SHR 0x1, 0x1".to_string(), + encoded: 0x8116, + }, + InstructionTest { + name: INST_SUBC.to_string(), + instruction: Chip8CpuInstructions::SUBC(0xf, 0xa), + asm: "SUBC 0xf, 0xa".to_string(), + encoded: 0x8fa7, + }, + InstructionTest { + name: INST_SHL.to_string(), + instruction: Chip8CpuInstructions::SHL(0x1, 0x4), + asm: "SHL 0x1, 0x4".to_string(), + encoded: 0x814e, + }, + InstructionTest { + name: INST_SNEY.to_string(), + instruction: Chip8CpuInstructions::SNEY(0x4, 0x5), + asm: "SNEY 0x4, 0x5".to_string(), + encoded: 0x9450, + }, + InstructionTest { + name: INST_LDIA.to_string(), + instruction: Chip8CpuInstructions::LDIA(0xbee), + asm: "LDIA 0x0bee".to_string(), + encoded: 0x9bee + }, + InstructionTest { + name: INST_JPI.to_string(), + instruction: Chip8CpuInstructions::JPI(0xfee), + asm: "JPI 0x0fee".to_string(), + encoded: 0xbfee + }, + InstructionTest { + name: INST_RND.to_string(), + instruction: Chip8CpuInstructions::RND(0x1, 0xae), + asm: "RND 0x01, 0xae".to_string(), + encoded: 0xc1ae, + }, + InstructionTest { + name: INST_DRW.to_string(), + instruction: Chip8CpuInstructions::DRW(0x1, 0x2, 0xf), + asm: "DRW 0x01, 0x02, 0x0f".to_string(), + encoded: 0xd12f + } + /* + 0xE09E..=0xEFA1 => match last_byte { + 0x9E => Chip8CpuInstructions::SKP(ubln), + 0xA1 => Chip8CpuInstructions::SKNP(ubln), + 0xF007..=0xFF65 => match last_byte { + 0x07 => Chip8CpuInstructions::LDRD(ubln), + 0x0A => Chip8CpuInstructions::LDRK(ubln), + 0x15 => Chip8CpuInstructions::LDD(ubln), + 0x18 => Chip8CpuInstructions::LDIS(ubln), + 0x1E => Chip8CpuInstructions::ADDI(ubln), + 0x29 => Chip8CpuInstructions::LDFX(ubln), + 0x30 => Chip8CpuInstructions::LDF2(ubln), + 0x33 => Chip8CpuInstructions::BCD(ubln), + 0x55 => Chip8CpuInstructions::LDIX(ubln), + 0x65 => Chip8CpuInstructions::LDRI(ubln), + 0x75 => Chip8CpuInstructions::STR(ubln), + 0x85 => Chip8CpuInstructions::LIDR(ubln), + + */ + ]; + + for current in it { + assert_eq!(current.instruction.name(), current.name); + let i = current.instruction; + assert!(matches!(Chip8CpuInstructions::decode(current.encoded), i)); + assert_eq!(i.to_string(), current.asm); + let asm = Chip8CpuInstructions::from_str(¤t.asm); + assert_eq!(i.to_string(), asm.to_string()); + } +} + +#[test] +fn instructions_operands_tests() { + assert_eq!(Chip8CpuInstructions::SYS(0x000).operands(), "0x0000"); + assert_eq!(Chip8CpuInstructions::JPI(0x123).operands(), "0x0123"); + assert_eq!(Chip8CpuInstructions::JPA(0x234).operands(), "0x0234"); + assert_eq!(Chip8CpuInstructions::LDIA(0x345).operands(), "0x0345"); + assert_eq!(Chip8CpuInstructions::CALL(0x456).operands(), "0x0456"); +} + +#[test] +fn instruction_ena_dis_tests() { + let mut x = Chip8Computer::new(); + assert!(!x.video_memory.is_highres()); + Chip8CpuInstructions::ENA.execute(&mut x); + assert!(x.video_memory.is_highres()); + Chip8CpuInstructions::ENA.execute(&mut x); + assert!(x.video_memory.is_highres()); + Chip8CpuInstructions::DIS.execute(&mut x); + assert!(!x.video_memory.is_highres()); +} + +#[test] +fn instruction_test_scrolling_lowres() { + let mut x = Chip8Computer::new(); + x.video_memory = build_checkerboard(); + Chip8CpuInstructions::SRT.execute(&mut x); + + assert_eq!(read_test_result("test_scroll_right_4.asc"), x.dump_video_to_string()); + + x = Chip8Computer::new(); + x.video_memory = build_checkerboard(); + Chip8CpuInstructions::SLF.execute(&mut x); + + assert_eq!(read_test_result("test_scroll_left_4.asc"), x.dump_video_to_string()); + + x = Chip8Computer::new(); + x.video_memory = build_checkerboard(); + Chip8CpuInstructions::SDN(0x01).execute(&mut x); + assert_eq!(read_test_result("test_video_scroll_down_1.asc"), x.dump_video_to_string()); + + x = Chip8Computer::new(); + x.video_memory = build_checkerboard(); + Chip8CpuInstructions::SDN(0xA).execute(&mut x); + assert_eq!(read_test_result("test_video_scroll_down_10.asc"), x.dump_video_to_string()); +} + +#[test] +fn computer_dump_keypad_to_string() { + +} + +#[test] +fn computer_dump_registers_to_string() { + let mut x = Chip8Computer::new(); + let values_to_set = [0x0b, 0xad, 0xbe, 0xef, + 0xca, 0xb0, + 0x7a, 0xc0, 0xca, 0x70, + 0xba, 0xdb, 0xed, 0x00, + 0x00, 0x00 + ]; + let expected_value = "Vx: 0x0b 0xad 0xbe 0xef 0xca 0xb0 0x7a 0xc0\n 0xca 0x70 0xba 0xdb 0xed 0x00 0x00 0x00\nI: 0x0000\tPC: 0x0200"; + + for i in 0..16 { + x.registers.poke(i, values_to_set[i as usize]); + } + + // now verify. + assert_eq!(expected_value, x.dump_registers_to_string()); +} + +//#[test] +fn quirks_chip8_vf_reset_tests() { + // vF reset - The AND, OR and XOR opcodes (8xy1, 8xy2 and 8xy3) reset the flags + // register to zero. Test will show ERR1 if the AND and OR tests don't behave + // the same and ERR2 if the AND and XOR tests don't behave the same. + let mut x = Chip8Computer::new(); +} \ No newline at end of file diff --git a/gemmaegui/Cargo.toml b/gemmaegui/Cargo.toml index 57c7aba..f0c8646 100644 --- a/gemmaegui/Cargo.toml +++ b/gemmaegui/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" gemma = { path = "../gemma" } egui.workspace = true eframe.workspace = true +catppuccin-egui = { version = "5.3.0", default-features = false, features = ["egui29"] } diff --git a/gemmaegui/src/bin/gemmaegui.rs b/gemmaegui/src/bin/gemmaegui.rs index 3241260..f5bc9f2 100644 --- a/gemmaegui/src/bin/gemmaegui.rs +++ b/gemmaegui/src/bin/gemmaegui.rs @@ -58,8 +58,10 @@ fn main() -> eframe::Result { }; eframe::run_simple_native("EGUI Gemma", options, move |ctx, _frame| { + catppuccin_egui::set_theme(&ctx, catppuccin_egui::MOCHA); egui::CentralPanel::default().show(ctx, |ui| { - computer.tick(); + let frame_start_time = Instant::now(); + let local_computer = computer.state(); //if state.display_video { GemmaEguiSupport::video_view(local_computer, ui); @@ -116,6 +118,13 @@ fn main() -> eframe::Result { } }); + // run our target number of ticks in the amount of available time. + let time_consumed = Instant::now().duration_since(frame_start_time).as_millis(); + let mut num_ticks = 0; + while num_ticks < 1000 { + computer.tick(); + num_ticks += 1; + } ctx.request_repaint(); }); }) diff --git a/gemmaegui/src/bin/gemmaegui_viewer.rs b/gemmaegui/src/bin/gemmaegui_viewer.rs deleted file mode 100644 index 587cf17..0000000 --- a/gemmaegui/src/bin/gemmaegui_viewer.rs +++ /dev/null @@ -1,61 +0,0 @@ -use egui::Ui; -use gemma::chip8::instructions::Chip8CpuInstructions; - -/// GemmaEGui - Viewer -/// -/// -/// This program lets a user open a CH8 file and it will be decoded -/// as Chip-8 Instructions. -/// Colour variants for instructions show colours for -/// -> Jumps -/// -> Load -/// -> Store -/// -> Timers -/// -> Math (Add, Sub) -/// -> Logic -/// -> Video - -fn display_instruction(offset: i32, to_display: &Chip8CpuInstructions, ui: &mut Ui) { - ui.label(format!("{offset:04x} {}", to_display.name())); - ui.label(format!("{}", to_display.operands())); - ui.end_row(); -} - -fn display_instructions(base_offset: i32, instructions: Vec, ui: &mut Ui) { - egui::Grid::new("my_grid") - .min_col_width(200.0) - .show(ui, |ui| { - for (index, instruction) in instructions.iter().enumerate() { - display_instruction(base_offset + index as i32, instruction, ui); - } - }); -} - -fn main() -> eframe::Result { - let mut edit_space: String = String::new(); - - let options = eframe::NativeOptions { - viewport: egui::ViewportBuilder::default().with_inner_size([640.0, 480.0]), - ..Default::default() - }; - - eframe::run_simple_native("Gemma - Chip8 Machine Code Viewer", options, move |ctx, frame| { - egui::CentralPanel::default().show(ctx, |ui| { - ui.label("Taxation is Theft"); - // file picker - ui.vertical_centered(|ui| { - if ui.button("Load File").clicked() { - println!("Clicked load file"); - } - }); - ui.label("File Picker Herre"); - ui.label("display of file here"); - - ui.vertical(|ui| { - display_instruction(0x010, &Chip8CpuInstructions::ADDI(0x01), ui); - display_instruction(0x012, &Chip8CpuInstructions::RND(0x01, 0x02), ui); - }); - ui.text_edit_multiline(&mut edit_space); - }); - }) -} \ No newline at end of file diff --git a/gemmaimgui/Cargo.toml b/gemmaimgui/Cargo.toml index 719d87c..f6dd850 100644 --- a/gemmaimgui/Cargo.toml +++ b/gemmaimgui/Cargo.toml @@ -17,3 +17,4 @@ image = { version = "0.23" } imgui = { version = "0.12.0" } winit = { version = "0.27", features = ["x11", "mint"]} copypasta = { version = "0.8" } +clap = { version = "4.5.20", features = ["derive"] } \ No newline at end of file diff --git a/gemmaimgui/src/bin/gemmaimgui.rs b/gemmaimgui/src/bin/gemmaimgui.rs index 5ead06c..1d3f7cf 100644 --- a/gemmaimgui/src/bin/gemmaimgui.rs +++ b/gemmaimgui/src/bin/gemmaimgui.rs @@ -12,6 +12,10 @@ use gemma::chip8::computer_manager::Chip8ComputerManager; use gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction; use gemma::chip8::system_memory::Chip8SystemMemory; use support::{emmagui_support::GemmaImguiSupport, ui_state::ImGuiUiState}; +use clap::{Parser, Subcommand}; +use gemma::chip8::quirk_modes::QuirkMode; +use gemma::chip8::quirk_modes::QuirkMode::Chip8; + mod support; @@ -28,8 +32,10 @@ const LIN_KEYS: [(u16, u8); 0x10] = [(537, 0x01),(538, 0x02),(539, 0x03),(540, 0 fn main() { pretty_env_logger::init(); + let mut system = Chip8ComputerManager::default(); let mut ui_state = ImGuiUiState::default(); + let target_ips = ui_state.target_ips; support::simple_init(file!(), move |_, ui| { let current_time = Instant::now(); @@ -59,9 +65,16 @@ fn main() { } } - while Instant::now().duration_since(current_time).as_millis() < 16 && num_cycles < 1000 { - system.tick(); - num_cycles += 1; + let target_ms = ui_state.frame_time; + let loop_start_time = Instant::now(); + while Instant::now().duration_since(current_time).as_millis() < target_ms as u128 && num_cycles < target_ips { + if system.tick() { + num_cycles += 1; + } + } + let cycles_time = Instant::now().duration_since(loop_start_time); + if num_cycles > 0 { + println!("Ran for {}ms and executed {}/{} cycles.", cycles_time.as_millis(), num_cycles, target_ips); } // GUI Parts if ui_state.show_video { diff --git a/gemmaimgui/src/bin/support/emmagui_support.rs b/gemmaimgui/src/bin/support/emmagui_support.rs index 5c342bd..1a03a04 100644 --- a/gemmaimgui/src/bin/support/emmagui_support.rs +++ b/gemmaimgui/src/bin/support/emmagui_support.rs @@ -16,7 +16,7 @@ use crate::ImGuiUiState; use crate::support::gui_file_list::GuiFileList; use super::ui_state; -const ROM_ROOT: &str = "/home/tmerritt/Projects/trevors_chip8_toy/resources/roms"; +const ROM_ROOT: &str = "/home/tmerritt/Projects/chip8_toy/resources/roms"; pub struct GemmaImguiSupport {} @@ -25,37 +25,39 @@ const CELL_HEIGHT: i32 = 5i32; impl GemmaImguiSupport { pub fn keypad_display(system_to_display: &Chip8Computer, ui: &Ui) { - ui.text("Keypad"); - - for row in CHIP8_KEYBOARD { - for key in row { - let label = if system_to_display.keypad.pressed(key) { - format!("*{:1x}*", key) - } else { - format!("{:1x}", key) - }; - ui.text(format!("{}", label)); - ui.same_line(); - } - ui.text(""); - } + ui.window(format!("Keypad")) + .size([100.0, 100.0], Condition::FirstUseEver) + .build(|| { + for row in CHIP8_KEYBOARD { + for key in row { + let label = if system_to_display.keypad.pressed(key) { + format!("*{:1x}*", key) + } else { + format!("{:1x}", key) + }; + ui.text(format!("{}", label)); + ui.same_line(); + } + ui.text(""); + } + }); } pub fn video_display(system_to_control: &Chip8Computer, gui_state: &ImGuiUiState, ui: &Ui) { // draw area size let (width, height) = system_to_control.video_memory.get_resolution(); let draw_area_size = ui.io().display_size; - // println!("DRAW_AREA_SIZE = {}x{}", draw_area_size[0], draw_area_size[1]); + // println!("DRAW_AREA_SIZE = {}x{}", draw_area_size[0], draw_area_size[1]); let cell_width = ((draw_area_size[0] as i32 / width) * 6) / 10; let cell_height = ((draw_area_size[1] as i32 / height) * 6) / 10; ui.window(format!("Display {cell_width}x{cell_height}")) .size([300.0, 300.0], Condition::Once) .build(|| { - let origin = ui.cursor_screen_pos(); + let origin = ui.cursor_pos(); let fg = ui.get_window_draw_list(); - if (system_to_control.video_memory.is_highres()) { - ui.text("High Def Video here"); + if system_to_control.video_memory.is_highres() { + // ui.text("High Def Video here"); for current_row in 0..=height { let y_offset = origin[1] as i32 + (current_row * cell_height); for current_column in 0..=width { @@ -73,10 +75,9 @@ impl GemmaImguiSupport { } } } else { - ui.text("StdDef video here."); - for current_row in 0..=height { + for current_row in 0..height { let y_offset = origin[1] as i32 + (current_row * cell_height); - for current_column in 0..=width { + for current_column in 0..width { let x_offset = origin[0] as i32 + (current_column * cell_width); let current_origin = [x_offset as f32, y_offset as f32]; let current_limit = [(x_offset + cell_width) as f32, (y_offset + cell_height) as f32]; @@ -94,7 +95,7 @@ impl GemmaImguiSupport { }); } pub fn system_controls(system_to_control: &mut Chip8ComputerManager, gui_state: &mut ImGuiUiState, ui: &Ui) { - // let mut state: Chip8Computer = system_to_control; + // let mut state: Chip8Computer = system_to_control; ui.window("!!!! CONTROLS !!!!") .size([345.0, 200.0], Condition::FirstUseEver) .build(|| { @@ -102,7 +103,7 @@ impl GemmaImguiSupport { ui.text(format!("Step {:04x}", system_to_control.num_cycles()).as_str()); /* ROM Lister */ - let new_filename = GuiFileList::display_path(PathBuf::from("/home/tmerritt/Projects/trevors_chip8_toy/resources/roms/"), &gui_state.filename_to_load, ui); + let new_filename = GuiFileList::display_path(PathBuf::from(ROM_ROOT), &gui_state.filename_to_load, ui); if !new_filename.is_empty() { if new_filename != gui_state.filename_to_load { debug!("NEW FILENAME SELECTED -> {new_filename}"); @@ -112,19 +113,21 @@ impl GemmaImguiSupport { let mut buffer = Vec::new(); debug!("PREPARING TO LOAD {}", gui_state.filename_to_load); // let mut input_file = File::open(Path::new("./1-chip8-logo.ch8")).expect("put 1-chip8-logo.ch8 in this directory"); - let mut input_file = File::open(Path::new(&("/home/tmerritt/Projects/trevors_chip8_toy/resources/roms/".to_string() + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory"); + let mut input_file = File::open(Path::new(&(ROM_ROOT.to_string() + "/" + &gui_state.filename_to_load))).expect("put 1-chip8-logo.ch8 in this directory"); input_file.read_to_end(&mut buffer).expect("unable to read file"); system_to_control.load_bytes_to_system_memory((&*buffer).into()); } } ui.separator(); - if ui.button("Step") { - system_to_control.step(); - - }; - ui.same_line(); - if ui.button("Run") { - system_to_control.start(); + // if the system has no program loaded hide the buttons. + if system_to_control.state().memory.peek(0x200) != 0x00 { + if ui.button("Step") { + system_to_control.step(); + }; + ui.same_line(); + if ui.button("Run") { + system_to_control.start(); + } } ui.same_line(); if ui.button("Stop") { @@ -183,55 +186,53 @@ impl GemmaImguiSupport { ui.window("System Memory") .size([400.0, 300.0], Condition::FirstUseEver) .build(|| { - let mut current_x_hover: i32 = 0; - let mut current_y_hover: i32 = 0; - // display a block of data - for current_row in 0..rows { - ui.text(format!("{:02x}", current_row * cols)); - ui.same_line(); - for current_column in 0..cols { - let data_offset = current_row * cols + current_column; - let formatted_text = format!("{:02x}", bytes.peek(data_offset as u16)); - let text_location = ui.cursor_screen_pos(); - let text_size = ui.calc_text_size(formatted_text.clone()); - let bounding_box = imgui::sys::ImVec2 { - x: text_location[0] + text_size[0], - y: text_location[1] + text_size[1], - }; + let mut current_x_hover: i32 = 0; + let mut current_y_hover: i32 = 0; + // display a block of data + for current_row in 0..rows { + ui.text(format!("{:02x}", current_row * cols)); + ui.same_line(); + for current_column in 0..cols { + let data_offset = current_row * cols + current_column; + let formatted_text = format!("{:02x}", bytes.peek(data_offset as u16)); + let text_location = ui.cursor_screen_pos(); + let text_size = ui.calc_text_size(formatted_text.clone()); + let bounding_box = imgui::sys::ImVec2 { + x: text_location[0] + text_size[0], + y: text_location[1] + text_size[1], + }; - let hovering = ui.is_mouse_hovering_rect(text_location, [bounding_box.x, bounding_box.y]); - let is_active = data_offset == active as i32; + let hovering = ui.is_mouse_hovering_rect(text_location, [bounding_box.x, bounding_box.y]); + let is_active = data_offset == active as i32; - ui.text_colored(if hovering { - [0., 1., 1., 1.] - } else if is_active { - [1., 0., 1., 1.] - } else { - [1., 1., 0., 1.] - }, formatted_text.clone()); + ui.text_colored(if hovering { + [0., 1., 1., 1.] + } else if is_active { + [1., 0., 1., 1.] + } else { + [1., 1., 0., 1.] + }, formatted_text.clone()); + // if we are hovering show that at the bottom... + if hovering { + // Optionally change the text color to indicate it's interactable + current_x_hover = current_column; + current_y_hover = current_row; - // if we are hovering show that at the bottom... - if hovering { - // Optionally change the text color to indicate it's interactable - current_x_hover = current_column; - current_y_hover = current_row; + // Check if the left mouse button is clicked while hovering over the text + if ui.is_mouse_clicked(imgui::MouseButton::Left) { + debug!("Offset: [{}] [0x{:02x}] Value: [{}]", data_offset, data_offset, formatted_text.clone()); + // Perform any action here, e.g., call a function, trigger an event, etc. + } + } - // Check if the left mouse button is clicked while hovering over the text - if ui.is_mouse_clicked(imgui::MouseButton::Left) { - debug!("Offset: [{}] [0x{:02x}] Value: [{}]", data_offset, data_offset, formatted_text.clone()); - // Perform any action here, e.g., call a function, trigger an event, etc. + // are we on the same line? + if current_column != (cols - 1) { + ui.same_line(); } } - - // are we on the same line? - if current_column != (cols - 1) { - ui.same_line(); - } } - } - ui.text(format!("Offset 0x{:03x}", current_x_hover * cols + current_y_hover)); + ui.text(format!("Offset 0x{:03x}", current_x_hover * cols + current_y_hover)); }); } - } \ No newline at end of file diff --git a/gemmaimgui/src/bin/support/ui_state.rs b/gemmaimgui/src/bin/support/ui_state.rs index f0e317a..8ef7af3 100644 --- a/gemmaimgui/src/bin/support/ui_state.rs +++ b/gemmaimgui/src/bin/support/ui_state.rs @@ -10,8 +10,9 @@ pub struct ImGuiUiState { pub on_colour: ImColor32, pub off_colour: ImColor32, pub is_running: bool, - pub frame_time: f32, - pub last_frame_instant: Instant + pub frame_time: u32, + pub last_frame_instant: Instant, + pub target_ips: i32 } impl Clone for ImGuiUiState { @@ -26,7 +27,8 @@ impl Clone for ImGuiUiState { off_colour: self.off_colour, is_running: self.is_running, frame_time: self.frame_time, - last_frame_instant: self.last_frame_instant + last_frame_instant: self.last_frame_instant, + target_ips: self.target_ips } } } @@ -42,8 +44,9 @@ impl Default for ImGuiUiState { on_colour: ImColor32::from_rgb(0xff, 0xff, 0x00), off_colour: ImColor32::from_rgb(0x00, 0xff, 0xff), is_running: false, - frame_time: 1.0, - last_frame_instant: Instant::now() + frame_time: 16, + last_frame_instant: Instant::now(), + target_ips: 20 } } } diff --git a/resources/octoroms/caveexplorer.ch8 b/resources/octoroms/caveexplorer.ch8 new file mode 100644 index 0000000..c70de42 Binary files /dev/null and b/resources/octoroms/caveexplorer.ch8 differ diff --git a/resources/roms/chipwar.ch8 b/resources/roms/chipwar.ch8 new file mode 100644 index 0000000..e043ab1 Binary files /dev/null and b/resources/roms/chipwar.ch8 differ diff --git a/resources/roms/deep8.ch8 b/resources/roms/deep8.ch8 new file mode 100644 index 0000000..14eea8b Binary files /dev/null and b/resources/roms/deep8.ch8 differ diff --git a/resources/test/gemma_keypad_string_result.asc b/resources/test/gemma_keypad_string_result.asc new file mode 100644 index 0000000..643a5a4 --- /dev/null +++ b/resources/test/gemma_keypad_string_result.asc @@ -0,0 +1,4 @@ +|1|2|3|c| +|4|5|6|d| +|7|8|9|e| +|a|0|b|f|