diff --git a/snes-frontend/Cargo.toml b/snes-frontend/Cargo.toml index d2a0fda..5b56b10 100644 --- a/snes-frontend/Cargo.toml +++ b/snes-frontend/Cargo.toml @@ -12,3 +12,4 @@ snes-core = { path = "../snes-core" } eframe = "0.24.1" env_logger = "0.10.1" rfd = "0.12.1" +regex = "1.10.2" diff --git a/snes-frontend/src/emu_state/debug_options.rs b/snes-frontend/src/emu_state/debug_options.rs index c11c36f..9c492f7 100644 --- a/snes-frontend/src/emu_state/debug_options.rs +++ b/snes-frontend/src/emu_state/debug_options.rs @@ -1,6 +1,26 @@ +pub struct MemoryMapInputs { + pub page_start: String, + pub page_end: String, + pub address_start: String, + pub address_end: String, +} + +impl MemoryMapInputs { + pub fn new() -> Self { + Self { + page_start: String::from("00"), + page_end: String::from("0F"), + address_start: String::from("0000"), + address_end: String::from("01FF"), + } + } +} + pub struct DebugOptions { pub enable_debugging: bool, pub show_debug_options_window: bool, + pub show_memory_map: bool, + pub memory_map_inputs: MemoryMapInputs, } impl DebugOptions { @@ -8,6 +28,8 @@ impl DebugOptions { Self { enable_debugging: true, show_debug_options_window: true, + show_memory_map: true, + memory_map_inputs: MemoryMapInputs::new(), } } -} \ No newline at end of file +} diff --git a/snes-frontend/src/emu_ui/debug/debug.rs b/snes-frontend/src/emu_ui/debug/debug.rs index d71708a..518ba3c 100644 --- a/snes-frontend/src/emu_ui/debug/debug.rs +++ b/snes-frontend/src/emu_ui/debug/debug.rs @@ -1,9 +1,12 @@ use eframe::egui; +use snes_core::emulator::Emulator; use crate::emu_state::AppState; +use super::memory_map::build_memory_map_window; -pub fn build_debug_window(ctx: &egui::Context, app_state: &mut AppState) { + +fn build_debug_options_window(ctx: &egui::Context, app_state: &mut AppState) { egui::Window::new("Debug") .open(&mut app_state.debug_options.show_debug_options_window) .show(ctx, |ui| { @@ -11,5 +14,22 @@ pub fn build_debug_window(ctx: &egui::Context, app_state: &mut AppState) { &mut app_state.debug_options.enable_debugging, "Enable debugging", ); + ui.separator(); + if ui.selectable_label( + app_state.debug_options.show_memory_map, + "Show memory map" + ).clicked() { + app_state.debug_options.show_memory_map = !app_state.debug_options.show_memory_map; + } }); -} \ No newline at end of file +} + +pub fn build_all_debug_options(ctx: &egui::Context, app_state: &mut AppState, emulator: &Emulator) { + build_debug_options_window(ctx, app_state); + + if !app_state.debug_options.enable_debugging { + return; + } + + build_memory_map_window(ctx, app_state, emulator); +} diff --git a/snes-frontend/src/emu_ui/debug/memory_map.rs b/snes-frontend/src/emu_ui/debug/memory_map.rs new file mode 100644 index 0000000..ed1381a --- /dev/null +++ b/snes-frontend/src/emu_ui/debug/memory_map.rs @@ -0,0 +1,91 @@ +use eframe::egui; +use regex::Regex; +use snes_core::emulator::Emulator; + +use crate::emu_state::{AppState, debug_options::MemoryMapInputs}; + + +pub fn build_memory_map_window(ctx: &egui::Context, app_state: &mut AppState, emulator: &Emulator) { + if !app_state.debug_options.show_memory_map { + return + } + + egui::Window::new("Memory Map") + .min_size([400.0, 400.0]) + .open(&mut app_state.debug_options.show_memory_map) + .show(ctx, |ui| { + build_inputs(ui, &mut app_state.debug_options.memory_map_inputs); + ui.separator(); + egui::ScrollArea::both().show(ui, |ui| { + build_memory_map_text( + ui, + &app_state.debug_options.memory_map_inputs, + emulator, + ); + }); + }); +} + +fn build_inputs(ui: &mut egui::Ui, input_values: &mut MemoryMapInputs) { + ui.horizontal(|ui| { + ui.label("Page Start: "); + ui.text_edit_singleline(&mut input_values.page_start); + }); + ui.horizontal(|ui| { + ui.label("Page End: "); + ui.text_edit_singleline(&mut input_values.page_end); + }); + ui.horizontal(|ui| { + ui.label("Address Start: "); + ui.text_edit_singleline(&mut input_values.address_start); + }); + ui.horizontal(|ui| { + ui.label("Address End: "); + ui.text_edit_singleline(&mut input_values.address_end); + }); + if ui.button("Search").clicked() { + sanitize_input(&mut input_values.page_start, true); + sanitize_input(&mut input_values.page_end, true); + sanitize_input(&mut input_values.address_start, false); + sanitize_input(&mut input_values.address_end, false); + } +} + +fn sanitize_input(input: &mut String, is_page: bool) { + let re = Regex::new(r"^(0x)?[0-9a-fA-F]{2,4}$").unwrap(); + if !re.is_match(input) { + *input = String::from("0x00"); + } + let trimmed_input = input.trim_start_matches("0x"); + *input = match is_page { + true => format!("{:02X}", u8::from_str_radix(&trimmed_input, 16).unwrap()), + false => format!("{:04X}", u16::from_str_radix(&trimmed_input, 16).unwrap()), + } +} + +fn build_memory_map_text(ui: &mut egui::Ui, input_values: &MemoryMapInputs, emulator: &Emulator) { + let page_start = u8::from_str_radix(&input_values.page_start, 16).unwrap(); + let page_end = u8::from_str_radix(&input_values.page_end, 16).unwrap(); + let address_start = u16::from_str_radix(&input_values.address_start, 16).unwrap(); + let address_end = u16::from_str_radix(&input_values.address_end, 16).unwrap(); + + let mut header = String::from(" | "); + for page in (page_start..=page_end).rev() { + header = format!("{}{:02X} ", header, page); + } + ui.monospace(header); + let mut divider = String::from("-----|-"); + for _page in (page_start..=page_end).rev() { + divider = format!("{}---", divider); + } + ui.monospace(divider); + for address in (address_start..=address_end).rev() { + let mut address_row = format!("{:04X} | ", address); + for page in (page_start..=page_end).rev() { + let bus_address = ((page as u32) << 16) | (address as u32); + address_row = format!("{}{:02X} ", address_row, emulator.bus.read_external(bus_address)); + } + ui.monospace(address_row); + } +} + diff --git a/snes-frontend/src/emu_ui/debug/mod.rs b/snes-frontend/src/emu_ui/debug/mod.rs index 67f1c5c..c4597e9 100644 --- a/snes-frontend/src/emu_ui/debug/mod.rs +++ b/snes-frontend/src/emu_ui/debug/mod.rs @@ -1,3 +1,4 @@ pub mod debug; +pub mod memory_map; -pub use debug::build_debug_window; \ No newline at end of file +pub use debug::build_all_debug_options; \ No newline at end of file diff --git a/snes-frontend/src/main.rs b/snes-frontend/src/main.rs index 591efcf..6ad12eb 100644 --- a/snes-frontend/src/main.rs +++ b/snes-frontend/src/main.rs @@ -24,7 +24,7 @@ impl eframe::App for SnesEmulatorApp { ui.separator(); // ui::game::build_game_window(ctx); }); - emu_ui::debug::build_debug_window(ctx, &mut self.state); + emu_ui::debug::build_all_debug_options(ctx, &mut self.state, &self.emulator); } }