diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs index 9e3bb64..2d930c7 100644 --- a/gemma/src/chip8/computer.rs +++ b/gemma/src/chip8/computer.rs @@ -11,6 +11,7 @@ use super::{ #[derive(Clone)] pub struct Chip8Computer { + pub num_cycles: i32, pub memory: Chip8SystemMemory, pub registers: Chip8Registers, pub sound_timer: SoundTimer, @@ -23,6 +24,7 @@ pub struct Chip8Computer { impl Default for Chip8Computer { fn default() -> Self { Self { + num_cycles: 0, memory: Chip8SystemMemory::default(), video_memory: Chip8Video::default(), registers: Chip8Registers::default(), @@ -36,10 +38,12 @@ impl Default for Chip8Computer { } impl Chip8Computer { - pub fn reset(&mut self) -> Self{ - let mut working = Chip8Computer::new(); - working.video_memory.reset(); - working + pub fn reset(&mut self) { + self.video_memory.reset(); + self.num_cycles = 0; + self.registers.reset(); + self.delay_timer.reset(); + self.sound_timer.reset(); } pub fn dump_keypad_to_string(&self) -> String { diff --git a/gemma/src/chip8/delay_timer.rs b/gemma/src/chip8/delay_timer.rs index cdc1b98..eb5a7f6 100644 --- a/gemma/src/chip8/delay_timer.rs +++ b/gemma/src/chip8/delay_timer.rs @@ -13,6 +13,11 @@ impl DelayTimer { counter: 0xff } } + + pub fn reset(&mut self) { + self.counter = 0xff; + } + pub fn set_timer(&mut self, new_value: i32) { self.counter = new_value } diff --git a/gemma/src/chip8/instructions.rs b/gemma/src/chip8/instructions.rs index 8fbffe4..04fbb41 100644 --- a/gemma/src/chip8/instructions.rs +++ b/gemma/src/chip8/instructions.rs @@ -1,6 +1,6 @@ use std::ops::{BitAnd, Shr}; use log::debug; -use rand::random; +use rand::{random, Rng}; use crate::chip8::computer::{Chip8Computer}; use crate::chip8::cpu_states::Chip8CpuStates::WaitingForKey; use crate::chip8::instructions::Chip8CpuInstructions::XXXXERRORINSTRUCTION; @@ -240,7 +240,7 @@ impl Chip8CpuInstructions { match input { 0x00E0 => { - // 00E0 - CLS + // 00E0 - f // Clear the display. Chip8CpuInstructions::CLS } @@ -428,9 +428,7 @@ impl Chip8CpuInstructions { } // * 0x00E0 Clear Screen Chip8CpuInstructions::CLS => { - for i in 0..(64 * 32) { - input.video_memory.poke(i, false); - } + input.video_memory.cls(); } // 0x00EE Return from Subroutine Chip8CpuInstructions::RET => { @@ -653,7 +651,8 @@ impl Chip8CpuInstructions { // The interpreter generates a random number from 0 to 255, // which is then ANDed with the value kk. // The results are stored in Vx. - let new_value: u8 = random(); + let mut rng = rand::thread_rng(); + let new_value: u8 = rng.random(); let and_value: u8 = *byte; let result = new_value & and_value; debug!("RANDOM: [{new_value:02x}] AND: [{and_value:02x} Result: [{result:02x}]"); @@ -664,7 +663,6 @@ impl Chip8CpuInstructions { // The interpreter reads n bytes from memory, starting at the address stored in I. - // These bytes are then displayed as sprites on screen at coordinates (Vx, Vy). // Sprites are XORed onto the existing screen. // If this causes any pixels to be erased, VF is set to 1, @@ -676,7 +674,6 @@ impl Chip8CpuInstructions { let x_offset = input.registers.peek(*x as u8); let y_offset = input.registers.peek(*y as u8); - // let target_memory_offset = x_offset as u16 * 64 + y_offset as u16; let num_bytes_to_read = *n; @@ -837,6 +834,7 @@ impl Chip8CpuInstructions { #[cfg(test)] mod test { use crate::chip8::system_memory::{CHIP8FONT_0, CHIP8FONT_1, CHIP8FONT_2, CHIP8FONT_9}; + use crate::constants::CHIP8_VIDEO_MEMORY; use super::*; #[test] @@ -1129,6 +1127,19 @@ mod test { let mut x = Chip8Computer::new(); Chip8CpuInstructions::CLS.execute(&mut x); assert_eq!(x.registers.peek_pc(), 0x202); + for i in 0..CHIP8_VIDEO_MEMORY { + assert_eq!(x.video_memory.peek(i as u16), false); + } + // draw some thing to the video memory + x.video_memory.poke(0x01, true); + x.video_memory.poke(0x03, true); + x.video_memory.poke(0x05, true); + + Chip8CpuInstructions::CLS.execute(&mut x); + + for i in 0..CHIP8_VIDEO_MEMORY { + assert_eq!(x.video_memory.peek(i as u16), false); + } } #[test] diff --git a/gemma/src/chip8/registers.rs b/gemma/src/chip8/registers.rs index d05f4da..a5de509 100644 --- a/gemma/src/chip8/registers.rs +++ b/gemma/src/chip8/registers.rs @@ -24,6 +24,12 @@ impl Default for Chip8Registers { } impl Chip8Registers { + pub fn reset(&mut self) { + self.registers = [0x00; 16]; + self.i_register = 0x00; + self.pc = 0x200; + } + pub fn poke_pc(&mut self, new_pc: u16) { self.pc = new_pc } @@ -79,6 +85,7 @@ impl Chip8Registers { #[cfg(test)] mod test { + use rand::random; use crate::chip8::registers::Chip8Registers; #[test] @@ -118,4 +125,17 @@ mod test { let result_string = x.format_as_string(); assert_eq!(result_string, String::from("Vx: 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07\n 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f\nI: 0x0cab\tPC: 0x0abc")); } + + #[test] + fn reset_clears_registers() { + let mut x = Chip8Registers::default(); + + for register in 0..0x10 { + x.registers[register] = random::(); + } + x.reset(); + for register in 0..0x10 { + assert_eq!(x.registers[register], 0x00); + } + } } \ No newline at end of file diff --git a/gemma/src/chip8/sound_timer.rs b/gemma/src/chip8/sound_timer.rs index 257eb1e..711c70e 100644 --- a/gemma/src/chip8/sound_timer.rs +++ b/gemma/src/chip8/sound_timer.rs @@ -32,6 +32,10 @@ impl SoundTimer { */ } } + + pub fn reset(&mut self) { + self.counter = 0x00; + } } #[cfg(test)] diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs index f50a6b9..9e610af 100644 --- a/gemma/src/chip8/video.rs +++ b/gemma/src/chip8/video.rs @@ -31,11 +31,7 @@ impl Chip8Video { } pub fn peek(self, address: u16) -> bool { - let effective_address = if address >= 2048 { - address - 2048 - } else { - address - }; + let effective_address = address % 2048; self.memory[effective_address as usize] } @@ -43,24 +39,50 @@ impl Chip8Video { // println!("OFFSET: {address} - POKING {new_value}"); // Loop the address - let effective_address = if address >= 2048 { - address - 2048 - } else { - address - }; + let effective_address = address % 2048; let old_value = self.memory[effective_address as usize]; let xored_value = new_value ^ old_value; // XOR of the video let value_changed = old_value != xored_value; // From True to False is a change. - self.has_frame_changed = if xored_value && value_changed { false } else { true }; if value_changed { - self.has_frame_changed = true; - } + self.has_frame_changed = true + }; self.memory[effective_address as usize] = xored_value; } + /* + CHATGPT + pub fn poke_byte(&mut self, first_address: u16, to_write: u8) { + let effective_address = first_address as usize % CHIP8_VIDEO_MEMORY; + + // Loop through each bit of the byte + for i in 0..8 { + let is_set = (to_write & (0x80 >> i)) != 0; + let address = effective_address + i; + + if address < CHIP8_VIDEO_MEMORY { + self.poke(address as u16, is_set); + } + } +} + */ + + + pub fn poke_byte(&mut self, first_address: u16, to_write: u8) { + for i in (0..8).rev() { + let shifted = ((1 << i) & to_write) >> i; + // + let target_address = first_address + (7 - i); + let is_set = (to_write & (1 << i)) != 0; + self.poke(target_address, is_set); + } + } + + + /* + MINE pub fn poke_byte(&mut self, first_address: u16, to_write: u8) { for i in (0..8).rev() { let shifted = ((1 << i) & to_write) >> i; @@ -71,13 +93,16 @@ impl Chip8Video { } } + */ + + pub fn poke_sprite(&mut self, first_address: u16, to_write: Vec) -> Self { let sprite_length = to_write.len(); for (index, byte) in to_write.iter().enumerate() { let real_address = index * 64; self.poke_byte(real_address as u16, *byte); - } + }; self.to_owned() } @@ -111,7 +136,7 @@ impl Default for Chip8Video { let new_struct = Chip8Video { memory: [false; CHIP8_VIDEO_MEMORY], has_frame_changed: false }; println!("NEW DEFAULT MEMORY : {}", new_struct.format_as_string()); - new_struct + new_struct.clone() } } @@ -146,10 +171,10 @@ mod test { let mut x = Chip8Video::default(); for i in 0..CHIP8_VIDEO_MEMORY { - assert!(!x.peek(i as u16)); + assert!(!x.clone().peek(i as u16)); // then flip the value and test again. &x.poke(i as u16, true); - assert!(x.peek(i as u16)); + assert!(x.clone().peek(i as u16)); } } @@ -271,24 +296,24 @@ mod test { // row 2 column 1 { - assert!(v.peek(0x40)); - assert!(v.peek(0x41)); - assert!(v.peek(0x42)); - assert!(v.peek(0x43)); - assert!(v.peek(0x44)); - assert!(v.peek(0x45)); - assert!(v.peek(0x46)); - assert!(v.peek(0x47)); + assert!(v.clone().peek(0x40)); + assert!(v.clone().peek(0x41)); + assert!(v.clone().peek(0x42)); + assert!(v.clone().peek(0x43)); + assert!(v.clone().peek(0x44)); + assert!(v.clone().peek(0x45)); + assert!(v.clone().peek(0x46)); + assert!(v.clone().peek(0x47)); // row 3 column 1 assert!(!v.peek(0xC0)); - assert!(v.peek(0xC1)); - assert!(!v.peek(0xC2)); - assert!(v.peek(0xC3)); - assert!(!v.peek(0xC4)); - assert!(v.peek(0xC5)); - assert!(!v.peek(0xC6)); - assert!(v.peek(0xC7)); + assert!(v.clone().peek(0xC1)); + assert!(!v.clone().peek(0xC2)); + assert!(v.clone().peek(0xC3)); + assert!(!v.clone().peek(0xC4)); + assert!(v.clone().peek(0xC5)); + assert!(!v.clone().peek(0xC6)); + assert!(v.clone().peek(0xC7)); } } @@ -313,23 +338,23 @@ mod test { let test_offset = (x_offset * 64 + y_offset) as u16; assert!(!v.peek(test_offset)); - assert!(!v.peek(test_offset + 1)); - assert!(!v.peek(test_offset + 2)); - assert!(!v.peek(test_offset + 3)); - assert!(!v.peek(test_offset + 4)); - assert!(!v.peek(test_offset + 5)); - assert!(!v.peek(test_offset + 6)); - assert!(!v.peek(test_offset + 7)); + assert!(!v.clone().peek(test_offset + 1)); + assert!(!v.clone().peek(test_offset + 2)); + assert!(!v.clone().peek(test_offset + 3)); + assert!(!v.clone().peek(test_offset + 4)); + assert!(!v.clone().peek(test_offset + 5)); + assert!(!v.clone().peek(test_offset + 6)); + assert!(!v.clone().peek(test_offset + 7)); let test_offset = test_offset + 0x40; - assert!(v.peek(test_offset)); - assert!(v.peek(test_offset + 1)); - assert!(v.peek(test_offset + 2)); - assert!(v.peek(test_offset + 3)); - assert!(v.peek(test_offset + 4)); - assert!(v.peek(test_offset + 5)); - assert!(v.peek(test_offset + 6)); - assert!(v.peek(test_offset + 7)); + assert!(v.clone().peek(test_offset)); + assert!(v.clone().peek(test_offset + 1)); + assert!(v.clone().peek(test_offset + 2)); + assert!(v.clone().peek(test_offset + 3)); + assert!(v.clone().peek(test_offset + 4)); + assert!(v.clone().peek(test_offset + 5)); + assert!(v.clone().peek(test_offset + 6)); + assert!(v.clone().peek(test_offset + 7)); } #[test] @@ -458,8 +483,8 @@ mod test { fn peek_out_of_bounds_doesnt_panic() { let x = Chip8Video::default(); - let y = x.peek(2049); - let y = x.peek(0); + let y = x.clone().peek(2049); + let y = x.clone().peek(0); // if we got here we didn't panic assert!(true); diff --git a/gemmaegui/src/bin/gemmaegui.rs b/gemmaegui/src/bin/gemmaegui.rs index 56c4559..cae3817 100644 --- a/gemmaegui/src/bin/gemmaegui.rs +++ b/gemmaegui/src/bin/gemmaegui.rs @@ -34,6 +34,9 @@ fn main() -> eframe::Result { if state.display_registers { GemmaEguiSupport::registers_view(&computer, ui); } + if state.is_running { + computer.step_system(); + } }); }) } diff --git a/gemmaegui/src/bin/support/gemma_egui_state.rs b/gemmaegui/src/bin/support/gemma_egui_state.rs index 5503281..ff25060 100644 --- a/gemmaegui/src/bin/support/gemma_egui_state.rs +++ b/gemmaegui/src/bin/support/gemma_egui_state.rs @@ -4,7 +4,8 @@ pub struct GemmaEGuiState { pub display_memory: bool, pub display_registers: bool, pub memory_view_min: i32, - pub memory_view_max: i32 + pub memory_view_max: i32, + pub is_running: bool } impl Default for GemmaEGuiState { @@ -14,7 +15,8 @@ impl Default for GemmaEGuiState { display_memory: true, display_registers: true, memory_view_min: 0x00, - memory_view_max: 0x100 + memory_view_max: 0x100, + is_running: false } } } diff --git a/gemmaegui/src/bin/support/gemma_egui_support.rs b/gemmaegui/src/bin/support/gemma_egui_support.rs index 5a6f936..e75e43f 100644 --- a/gemmaegui/src/bin/support/gemma_egui_support.rs +++ b/gemmaegui/src/bin/support/gemma_egui_support.rs @@ -19,16 +19,18 @@ impl EGuiFileList { let mut working_filename = selected_filename.clone(); ui.label(format!("Displaying {}", root.to_str().unwrap_or("Unable to Load Path"))); - egui::ComboBox::from_label(format!( - "Currently selected string: {}", - selected_filename - )) + ComboBox::from_label("Choose an option") .selected_text(selected_filename.clone()) .show_ui(ui, |ui| { for option in read_dir(root.as_path()).unwrap() { - ui.label(format!("{:?}", option.unwrap().file_name())); + // Add each option to the ComboBox + let mut label = option.unwrap().file_name(); + ui.selectable_value(selected_filename, selected_filename.clone(), label.into_string().unwrap()); } }); + + // Display the selected option + ui.label(format!("Selected value: {}", selected_filename)); } } @@ -39,6 +41,7 @@ impl GemmaEguiSupport { 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() { @@ -47,6 +50,7 @@ impl GemmaEguiSupport { if ui.button("Stop").clicked() { println!("STOP"); + state.is_running = false; } if ui.button("Reset").clicked() { system.reset(); @@ -60,6 +64,8 @@ impl GemmaEguiSupport { // ...then feed the system. system.load_bytes_to_memory(0x200, &read_bin); } + let mut target = String::new(); + EGuiFileList::display_path(PathBuf::from("resources/roms"), &mut target, ui); });