diff --git a/gemma/src/chip8/computer.rs b/gemma/src/chip8/computer.rs index bcdd97f..4c9a8b5 100644 --- a/gemma/src/chip8/computer.rs +++ b/gemma/src/chip8/computer.rs @@ -81,6 +81,7 @@ impl Chip8Computer { let new_location = current_index + offset; self.memory.poke(new_location, new_value); } + self.registers.set_pc(offset); } pub fn step_system(&mut self) -> &mut Chip8Computer { diff --git a/gemma/src/chip8/computer_manager.rs b/gemma/src/chip8/computer_manager.rs new file mode 100644 index 0000000..120bd47 --- /dev/null +++ b/gemma/src/chip8/computer_manager.rs @@ -0,0 +1,127 @@ +use std::sync::mpsc::{channel, Sender}; +use std::thread; +use std::thread::{sleep, JoinHandle, Thread}; +use std::time::{Duration, Instant}; +use crate::chip8::computer::Chip8Computer; +use crate::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction; + +pub enum ManagerDumpables { + Video, + Registers, + Keyboard +} + + +pub struct Chip8ComputerManager { + pub core_should_run: bool, + pub one_step: bool, + pub core_cycle_timer: bool, + pub core_last_cycle_start: Instant, + computer: Chip8Computer +} + +impl Default for Chip8ComputerManager { + fn default() -> Self { + Chip8ComputerManager { + core_should_run: false, + one_step: true, + core_cycle_timer: false, + core_last_cycle_start: Instant::now() , + computer: Chip8Computer::new() + } + } +} + +impl Chip8ComputerManager { + + pub fn reset(&mut self) { + 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)); + } + }); + + Chip8ComputerManager::default() + } + + pub fn start(managed: &mut Chip8ComputerManager) { + managed.core_should_run = true; + } + + pub fn stop(managed: &mut Chip8ComputerManager) { + managed.core_should_run = false + } + + pub fn step(managed: &mut Chip8ComputerManager) { + managed.one_step = true; + } + + pub fn state(&mut self) -> &Chip8Computer { + &self.computer + } + + pub fn tick( &mut self) { + println!("STARTING TICK"); + if self.one_step | self.core_should_run { + self.core_last_cycle_start = Instant::now(); + self.computer.step_system(); + println!("SYSTEM STEP"); + }; + if self.one_step { + println!("SYSTEM HALTED AFTER 1 STEP"); + // stop the CPU for the next cycle, we are only + // wanting one step. + self.one_step = false; + } + } + + pub fn press_key(&mut self, key_index: u8) { + self.computer.keypad.push_key(key_index); + } + pub fn release_key(&mut self, key_index: u8) { + self.computer.keypad.release_key(key_index); + } + + pub fn sound(managed: &Chip8ComputerManager) -> bool { + managed.computer.sound_timer.current() > 0 + } + + pub fn wait_for_instruction(&mut self) { + self.computer.state = WaitingForInstruction; + } + + pub fn is_key_pressed(&self, key_index: u8) -> bool { + self.computer.keypad.pressed(key_index) + } + + pub fn num_cycles(&self) -> i32 { + self.computer.num_cycles + } + + pub fn load_bytes_to_system_memory(&mut self, bytes_to_load: Vec) { + 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() + } + } + } +} \ No newline at end of file diff --git a/gemma/src/chip8/video.rs b/gemma/src/chip8/video.rs index f807878..2745938 100644 --- a/gemma/src/chip8/video.rs +++ b/gemma/src/chip8/video.rs @@ -1,4 +1,4 @@ -use log::{debug, trace}; +use log::{debug}; use crate::constants::{CHIP8_VIDEO_MEMORY, CHIP8_VIDEO_WIDTH}; #[derive(Clone, Copy)] @@ -85,12 +85,7 @@ impl Chip8Video { impl Default for Chip8Video { fn default() -> Self { - debug!("DEFAULT VIDEO PREPARED"); - - let new_struct = Chip8Video { memory: [false; CHIP8_VIDEO_MEMORY], has_frame_changed: false }; - println!("NEW DEFAULT MEMORY : {}", new_struct.format_as_string()); - - new_struct.clone() + Chip8Video { memory: [false; CHIP8_VIDEO_MEMORY], has_frame_changed: false } } } diff --git a/gemma/src/lib.rs b/gemma/src/lib.rs index 233b207..b95b90c 100644 --- a/gemma/src/lib.rs +++ b/gemma/src/lib.rs @@ -11,6 +11,8 @@ pub mod chip8 { pub mod registers; pub mod stack; + + pub mod computer_manager; } pub mod constants; \ No newline at end of file diff --git a/gemmaegui/src/bin/support/gemma_egui_state.rs b/gemmaegui/src/bin/support/gemma_egui_state.rs index a84315c..4133261 100644 --- a/gemmaegui/src/bin/support/gemma_egui_state.rs +++ b/gemmaegui/src/bin/support/gemma_egui_state.rs @@ -1,11 +1,12 @@ +use std::ops::Range; +use gemma::chip8::computer_manager::Chip8ComputerManager; pub struct GemmaEGuiState { pub display_video: bool, pub display_memory: bool, pub display_registers: bool, - pub memory_view_min: i32, - pub memory_view_max: i32, - pub is_running: bool, + pub memory_view: Range, + pub computer: Chip8ComputerManager, pub selected_rom_filename: String } @@ -15,9 +16,8 @@ impl Default for GemmaEGuiState { display_video: true, display_memory: true, display_registers: true, - memory_view_min: 0x00, - memory_view_max: 0x100, - is_running: false, + memory_view: 0x00..0x200, + computer: Chip8ComputerManager::new(), selected_rom_filename: String::new() } } diff --git a/gemmaimgui/src/bin/gemmaimgui.rs b/gemmaimgui/src/bin/gemmaimgui.rs index 3682bff..dfa4d7a 100644 --- a/gemmaimgui/src/bin/gemmaimgui.rs +++ b/gemmaimgui/src/bin/gemmaimgui.rs @@ -8,6 +8,7 @@ use gemma::{ use imgui::*; use sys::{ImColor, ImVec2, ImVector_ImU32}; use rand::random; +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}; @@ -27,7 +28,7 @@ const LIN_KEYS: [(u16, u8); 0x10] = [(537, 0x01),(538, 0x02),(539, 0x03),(540, 0 fn main() { pretty_env_logger::init(); - let mut system = Chip8Computer::default(); + let mut system = Chip8ComputerManager::default(); let mut ui_state = ImGuiUiState::default(); support::simple_init(file!(), move |_, ui| { @@ -35,61 +36,51 @@ fn main() { // Key Checks let down_keys = ui.io().keys_down; + + // START DEBUG CODE TO DISPLAY WHAT KEYS WE TRAPPED for (idx, val) in down_keys.iter().enumerate() { if *val { println!("{idx} = {val}"); } } + // END DEBUG CODE + + for (key_code, key_reg) in LIN_KEYS { if down_keys[key_code as usize] { - system.keypad.push_key(key_reg); - system.state = WaitingForInstruction; + system.press_key(key_reg); + system.press_key(key_reg); + system.wait_for_instruction(); } else { // do we need to release it? - if system.keypad.pressed(key_reg) { - system.keypad.release_key(key_reg); + + if system.is_key_pressed(key_reg) { + system.release_key(key_reg); } } } - // Next Tick Check - let time_since_last_tick = current_time.duration_since(ui_state.last_frame_instant).as_millis(); - if ui_state.is_running && time_since_last_tick > ui_state.frame_time as u128 { - match system.state { - gemma::chip8::cpu_states::Chip8CpuStates::WaitingForInstruction => { - // this is the 'regular' mode for the CPU when we get here - system.step_system(); - }, - gemma::chip8::cpu_states::Chip8CpuStates::WaitingForKey => { - // waiting for a keychange... - }, - gemma::chip8::cpu_states::Chip8CpuStates::ExecutingInstruction => { // should never see this. - }, - gemma::chip8::cpu_states::Chip8CpuStates::Error => { - panic!("System in undefined state."); - }, - } - ui_state.last_frame_instant = current_time; - } + system.tick(); + // GUI Parts if ui_state.show_video { - GemmaImguiSupport::video_display(&system, &ui_state, ui); + GemmaImguiSupport::video_display(&system.state(), &ui_state, ui); } GemmaImguiSupport::system_controls(&mut system, &mut ui_state, ui); if ui_state.show_registers { - GemmaImguiSupport::registers_view(&system, ui); + GemmaImguiSupport::registers_view(&system.state(), ui); } if ui_state.show_memory { - let active_instruction = system.registers.peek_pc(); - GemmaImguiSupport::hex_memory_display(system.memory.clone(), (0x100, 0x10), active_instruction as i16, ui); + let active_instruction = system.state().registers.peek_pc(); + GemmaImguiSupport::hex_memory_display(system.state().memory.clone(), (0x100, 0x10), active_instruction as i16, ui); } if ui_state.show_keypad { - GemmaImguiSupport::keypad_display(&system, ui); + GemmaImguiSupport::keypad_display(&system.state(), ui); } }); } diff --git a/gemmaimgui/src/bin/support/clipboard.rs b/gemmaimgui/src/bin/support/clipboard.rs deleted file mode 100644 index e69de29..0000000 diff --git a/gemmaimgui/src/bin/support/emmagui_support.rs b/gemmaimgui/src/bin/support/emmagui_support.rs index 4efd704..eaf4122 100644 --- a/gemmaimgui/src/bin/support/emmagui_support.rs +++ b/gemmaimgui/src/bin/support/emmagui_support.rs @@ -7,10 +7,13 @@ use std::time::Duration; use imgui::{Condition, ImColor32, Ui}; use log::debug; use gemma::chip8::computer::Chip8Computer; +use gemma::chip8::computer_manager::Chip8ComputerManager; +use gemma::chip8::computer_manager::ManagerDumpables::{Keyboard, Registers, Video}; +use gemma::chip8::keypad::Keypad; use gemma::chip8::system_memory::Chip8SystemMemory; use gemma::constants::{CHIP8_VIDEO_HEIGHT, CHIP8_VIDEO_WIDTH}; use crate::ImGuiUiState; - +use crate::support::gui_file_list::GuiFileList; use super::ui_state; pub struct GemmaImguiSupport {} @@ -18,35 +21,6 @@ pub struct GemmaImguiSupport {} const CELL_WIDTH: i32 = 5i32; const CELL_HEIGHT: i32 = 5i32; -struct GuiFileList {} - -impl GuiFileList { - pub fn display_path(root: PathBuf, selected_filename: &String, ui: &Ui) -> String { - let mut working_filename = selected_filename.clone(); - ui.text(format!("Displaying {}", root.to_str().unwrap_or("Unable to parse path"))); - - let mut known_files: Vec = vec![]; - - for entry in read_dir(root.as_path()).unwrap() { - known_files.push(entry.unwrap().file_name()); - } - - known_files.sort(); - - for (index, entry) in known_files.iter().enumerate() { - let mut working_select = ui.selectable_config(format!("{}", entry.clone().into_string().unwrap())); - if entry.to_str().unwrap().to_string() == selected_filename.as_str().to_string() { - working_select = working_select.selected(true); - } - if working_select.build() { - debug!("SELECTED {index} / {entry:?}"); - working_filename = entry.clone().into_string().unwrap(); - }; - } - working_filename - } -} - impl GemmaImguiSupport { pub fn keypad_display(system_to_display: &Chip8Computer, ui: &Ui) { ui.text("Keypad"); @@ -96,12 +70,13 @@ impl GemmaImguiSupport { } }); } - pub fn system_controls(system_to_control: &mut Chip8Computer, gui_state: &mut ImGuiUiState, ui: &Ui) { + pub fn system_controls(system_to_control: &mut Chip8ComputerManager, gui_state: &mut ImGuiUiState, ui: &Ui) { + // let mut state: Chip8Computer = system_to_control; ui.window("!!!! CONTROLS !!!!") .size([345.0, 200.0], Condition::FirstUseEver) .build(|| { /* System Step Counter */ - ui.text(format!("Step {:04x}", system_to_control.num_cycles).as_str()); + ui.text(format!("Step {:04x}", system_to_control.num_cycles()).as_str()); /* ROM Lister */ let new_filename = GuiFileList::display_path(PathBuf::from("resources/roms"), &gui_state.filename_to_load, ui); @@ -112,43 +87,39 @@ impl GemmaImguiSupport { } if ui.button("Load Program") { 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(&("resources/roms/".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_memory(0x200, (&buffer).into()); + system_to_control.load_bytes_to_system_memory((&*buffer).into()); } } ui.separator(); if ui.button("Step") { - system_to_control.step_system(); + system_to_control.one_step = true; }; ui.same_line(); if ui.button("Run") { - gui_state.is_running = true; - debug!("STARTING THE SYSTEM"); + system_to_control.core_should_run = true; } ui.same_line(); if ui.button("Stop") { - gui_state.is_running = false; - debug!("STOPPING THE SYSTEM"); + system_to_control.core_should_run = false; } ui.same_line(); if ui.button("Reset") { - gui_state.is_running = false; - *system_to_control = Chip8Computer::new(); + system_to_control.reset(); } if ui.button("Dump Video Memory") { - println!("{}", system_to_control.dump_video_to_string()); + println!("{}", system_to_control.dump_to_string(Video)); } ui.same_line(); if ui.button("Dump Keypad State") { - debug!("{}", system_to_control.dump_keypad_to_string()); + debug!("{}", system_to_control.dump_to_string(Keyboard)); } ui.same_line(); if ui.button("Dump Registers") { - debug!("{}", system_to_control.dump_registers_to_string()); + debug!("{}", system_to_control.dump_to_string(Registers)); } ui.separator(); diff --git a/gemmaimgui/src/bin/support/gui_file_list.rs b/gemmaimgui/src/bin/support/gui_file_list.rs new file mode 100644 index 0000000..65a6e0f --- /dev/null +++ b/gemmaimgui/src/bin/support/gui_file_list.rs @@ -0,0 +1,34 @@ +use std::ffi::OsString; +use std::fs::read_dir; +use std::path::PathBuf; +use imgui::Ui; +use log::debug; + +pub struct GuiFileList {} + +impl GuiFileList { + pub fn display_path(root: PathBuf, selected_filename: &String, ui: &Ui) -> String { + let mut working_filename = selected_filename.clone(); + ui.text(format!("Displaying {}", root.to_str().unwrap_or("Unable to parse path"))); + + let mut known_files: Vec = vec![]; + + for entry in read_dir(root.as_path()).unwrap() { + known_files.push(entry.unwrap().file_name()); + } + + known_files.sort(); + + for (index, entry) in known_files.iter().enumerate() { + let mut working_select = ui.selectable_config(format!("{}", entry.clone().into_string().unwrap())); + if entry.to_str().unwrap().to_string() == selected_filename.as_str().to_string() { + working_select = working_select.selected(true); + } + if working_select.build() { + debug!("SELECTED {index} / {entry:?}"); + working_filename = entry.clone().into_string().unwrap(); + }; + } + working_filename + } +} diff --git a/gemmaimgui/src/bin/support/mod.rs b/gemmaimgui/src/bin/support/mod.rs index fe177a4..d0ee045 100644 --- a/gemmaimgui/src/bin/support/mod.rs +++ b/gemmaimgui/src/bin/support/mod.rs @@ -2,7 +2,6 @@ use glium::glutin::surface::WindowSurface; use glium::{Display, Surface}; use imgui::{Context, FontConfig, FontGlyphRanges, FontSource, Ui}; use imgui_glium_renderer::Renderer; -use imgui_winit_support::winit::dpi::LogicalSize; use imgui_winit_support::winit::event::{Event, WindowEvent}; use imgui_winit_support::winit::event_loop::EventLoop; use imgui_winit_support::winit::window::WindowBuilder; @@ -12,6 +11,8 @@ use std::time::Instant; pub mod ui_state; pub mod emmagui_support; +mod gui_file_list; + use copypasta::{ClipboardContext, ClipboardProvider}; use imgui::ClipboardBackend;