diff --git a/snes-frontend/Cargo.toml b/snes-frontend/Cargo.toml index 2cc42f5..653f1ed 100644 --- a/snes-frontend/Cargo.toml +++ b/snes-frontend/Cargo.toml @@ -7,14 +7,3 @@ edition = "2021" [dependencies] snes-core = { path = "../snes-core" } -regex = "1.8.1" -winit = "0.26.1" -imgui = "0.8.2" -wgpu = "0.12.0" -imgui-wgpu = "0.19.0" -pollster = "0.2.5" -rfd = "0.10.0" -[dependencies.imgui-winit-support] -version = "0.8.2" -features = ["winit-26"] -default-features = false diff --git a/snes-frontend/src/cpu.rs b/snes-frontend/src/cpu.rs deleted file mode 100644 index f19950f..0000000 --- a/snes-frontend/src/cpu.rs +++ /dev/null @@ -1,15 +0,0 @@ -extern crate snes_core; -use snes_core::{emulator::Emulator, cpu::instructions::mapper::map_opcode_to_instruction}; - -pub struct CPUDisassembler { - _history_limit: usize, - _instruction_history: Vec, -} - -impl CPUDisassembler { - pub fn get_next_instruction(emulator: &Emulator) -> String { - let opcode = emulator.bus.read_external(emulator.cpu.registers.get_pc_address()); - let instruction = map_opcode_to_instruction(opcode); - instruction.mnemonic(&emulator.cpu.registers, &emulator.bus, opcode) - } -} diff --git a/snes-frontend/src/lib.rs b/snes-frontend/src/lib.rs deleted file mode 100644 index 67a8a1b..0000000 --- a/snes-frontend/src/lib.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod ppu; -pub mod cpu; -pub mod state; \ No newline at end of file diff --git a/snes-frontend/src/main.rs b/snes-frontend/src/main.rs index c97799e..ce2bf90 100644 --- a/snes-frontend/src/main.rs +++ b/snes-frontend/src/main.rs @@ -1,526 +1,5 @@ -extern crate imgui_winit_support; - -use rfd::FileDialog; -use imgui::*; -use imgui_wgpu::{Renderer, RendererConfig}; -use pollster::block_on; - -use std::time::Instant; -use winit::{ - dpi::LogicalSize, - event::{ElementState, Event, KeyboardInput, VirtualKeyCode, WindowEvent}, - event_loop::{ControlFlow, EventLoop}, - window::Window, -}; - -use snes_frontend::state::State; -extern crate snes_core; -use snes_core::{emulator::Emulator, ppu::registers::{MAX_BG_WIDTH, MAX_BG_HEIGHT}}; -use snes_frontend::ppu as ppu_render; -use snes_frontend::cpu as cpu_debug; - - -// TODO: refactor this please +use snes_core::emulator::Emulator; fn main() { - // Windowing state - let mut state = State::new(); let mut emulator = Emulator::new(); - - - // Set up window and GPU - let event_loop = EventLoop::new(); - - let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY); - - let (window, size, surface) = { - let version = env!("CARGO_PKG_VERSION"); - - let window = Window::new(&event_loop).unwrap(); - window.set_inner_size(LogicalSize { - width: 1280.0, - height: 720.0, - }); - window.set_title(&format!("SNES {}", version)); - let size = window.inner_size(); - - let surface = unsafe { instance.create_surface(&window) }; - - (window, size, surface) - }; - - let hidpi_factor = window.scale_factor(); - - let adapter = block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - force_fallback_adapter: false, - })) - .unwrap(); - - let (device, queue) = - block_on(adapter.request_device(&wgpu::DeviceDescriptor::default(), None)).unwrap(); - - // Set up swap chain - let surface_desc = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - width: size.width as u32, - height: size.height as u32, - present_mode: wgpu::PresentMode::Mailbox, - }; - - surface.configure(&device, &surface_desc); - - // Set up dear imgui - let mut imgui = imgui::Context::create(); - let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui); - platform.attach_window( - imgui.io_mut(), - &window, - imgui_winit_support::HiDpiMode::Default, - ); - imgui.set_ini_filename(None); - - let font_size = (14.0 * hidpi_factor) as f32; - imgui.io_mut().font_global_scale = (1.0 / hidpi_factor) as f32; - - imgui.fonts().add_font(&[FontSource::DefaultFontData { - config: Some(imgui::FontConfig { - oversample_h: 1, - pixel_snap_h: true, - size_pixels: font_size, - ..Default::default() - }), - }]); - - // - // Set up dear imgui wgpu renderer - // - let clear_color = wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }; - - let renderer_config = RendererConfig { - texture_format: surface_desc.format, - ..Default::default() - }; - - let mut renderer = Renderer::new(&mut imgui, &device, &queue, renderer_config); - - let mut last_frame = Instant::now(); - - let mut last_cursor = None; - - // Generate dummy texture - const WIDTH: usize = 400; - const HEIGHT: usize = 400; - let mut data = Vec::with_capacity(WIDTH * HEIGHT); - for i in 0..WIDTH { - for j in 0..HEIGHT { - // Insert RGB values - data.push(i as u8); - data.push(j as u8); - data.push((i + j) as u8); - } - } - - let texture = imgui_wgpu::Texture::new( - &device, - &renderer, - imgui_wgpu::TextureConfig { - label: Some("framebuffer texture"), - size: wgpu::Extent3d { - width: WIDTH as u32, - height: HEIGHT as u32, - depth_or_array_layers: 1, - }, - format: Some(wgpu::TextureFormat::Rgba8Unorm), - ..imgui_wgpu::TextureConfig::default() - }, - ); - let texture_id = renderer.textures.insert(texture); - - for bgdebug in state.ppudebug.backgrounds.iter_mut() { - bgdebug.texture_id = Some( - ppu_render::background_texture(&device, &mut renderer, bgdebug.background, MAX_BG_WIDTH as u32, MAX_BG_HEIGHT as u32, "Background") - ); - bgdebug.char_texture_id = Some( - ppu_render::background_texture(&device, &mut renderer, bgdebug.background, 16 * 8, 8 * 8, "Charset") - ); - } - - - // Event loop - event_loop.run(move |event, _, control_flow| { - *control_flow = if cfg!(feature = "metal-auto-capture") { - ControlFlow::Exit - } else { - ControlFlow::Poll - }; - match event { - Event::WindowEvent { - event: WindowEvent::Resized(_), - .. - } => { - let size = window.inner_size(); - - let surface_desc = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: wgpu::TextureFormat::Bgra8UnormSrgb, - width: size.width as u32, - height: size.height as u32, - present_mode: wgpu::PresentMode::Mailbox, - }; - - surface.configure(&device, &surface_desc); - } - Event::WindowEvent { - event: - WindowEvent::KeyboardInput { - input: - KeyboardInput { - virtual_keycode: Some(VirtualKeyCode::Escape), - state: ElementState::Pressed, - .. - }, - .. - }, - .. - } - | Event::WindowEvent { - event: WindowEvent::CloseRequested, - .. - } => { - *control_flow = ControlFlow::Exit; - } - Event::MainEventsCleared => window.request_redraw(), - Event::RedrawEventsCleared => { - let now = Instant::now(); - imgui.io_mut().update_delta_time(now - last_frame); - last_frame = now; - - let frame = match surface.get_current_texture() { - Ok(frame) => frame, - Err(e) => { - eprintln!("dropped frame: {:?}", e); - return; - } - }; - platform - .prepare_frame(imgui.io_mut(), &window) - .expect("Failed to prepare frame"); - let ui = imgui.frame(); - - { - ui.main_menu_bar(|| { - ui.menu("Emulator", || { - if imgui::MenuItem::new("Load ROM") - .build(&ui) - { - if let Some(path) = FileDialog::new() - .pick_file() - { - match emulator.bus.rom.load(&String::from(path.to_str().unwrap())) { - Ok(_) => { - // state.emulation.is_paused = false; - }, - Err(err) => { - state.error_message.show = true; - state.error_message.message = format!( - "Could not load rom: {}", err, - ); - } - } - } - } - ui.separator(); - }); - ui.menu("Debug", || { - if imgui::MenuItem::new("Show Debug menu") - .build(&ui) - { - state.debug_options.show_debug_window = true; - } - }); - }); - - if state.debug_options.show_debug_window { - let window = imgui::Window::new("Debug window"); - window - .position([15.0, 885.0], imgui::Condition::FirstUseEver) - .size([300.0, 300.0], Condition::FirstUseEver) - .opened(&mut state.debug_options.show_debug_window) - .build(&ui, || { - ui.text("Controls:"); - let pause_text = if state.emulation.is_paused {"Resume"} else {"Pause"}; - if ui.button(pause_text) { - state.emulation.is_paused = !state.emulation.is_paused; - } - ui.disabled(!state.emulation.is_paused, || { - if ui.button("Tick") { - emulator.tick(); - } - }); - if ui.button("Reset Vector") { - emulator.reset(); - } - ui.separator(); - - ui.checkbox( - "Enable debugging", - &mut state.debug_options.is_enabled, - ); - ui.separator(); - ui.checkbox( - "Show Memory Map", - &mut state.debug_options.memory_map.is_enabled , - ); - ui.checkbox( - "Show PPU debugging options", - &mut state.ppudebug.is_enabled, - ); - ui.checkbox( - "Show CPU registers", - &mut state.debug_options.show_cpu_registers, - ); - ui.checkbox( - "Show CPU disassembler", - &mut state.debug_options.show_cpu_disassembler, - ); - }); - } - - // Render all debugging stuff - if state.debug_options.is_enabled { - if state.ppudebug.is_enabled { - let window = imgui::Window::new("PPU Debugging options"); - window - .position([330.0, 885.0], imgui::Condition::FirstUseEver) - .size([180.0, 280.0], Condition::FirstUseEver) - .build(&ui, || { - ui.text("Backgrounds:"); - for bgdebug in state.ppudebug.backgrounds.iter_mut() { - ui.checkbox( - format!("Show {:?}", bgdebug.background), - &mut bgdebug.is_enabled, - ); - } - - ui.separator(); - - ui.text("Registers:"); - ui.checkbox( - "Show PPU Registers", - &mut state.ppudebug.show_registers, - ); - - ui.separator(); - - ui.text("VRAM:"); - ui.checkbox( - "Show PPU VRAM", - &mut state.ppudebug.show_vram, - ); - }); - for bgdebug in state.ppudebug.backgrounds.iter_mut() { - ppu_render::background_window( - bgdebug, - &emulator.bus.ppu.registers, - &ui, - &mut renderer, - &queue, - ) - } - - if state.ppudebug.show_registers { - ppu_render::registers_window( - &emulator.bus.ppu.registers, - &mut state.ppudebug.show_registers, - &ui, - ) - } - - if state.ppudebug.show_vram { - ppu_render::vram_window( - &emulator.bus.ppu.registers, - &mut state.ppudebug.vram_map, - &mut state.ppudebug.show_vram, - &ui, - ) - } - } - - if state.debug_options.show_cpu_registers { - let window = imgui::Window::new("CPU Registers"); - window - .position([15.0, 480.0], Condition::FirstUseEver) - .size([220.0, 310.0], Condition::FirstUseEver) - .build(&ui, || { - ui.text(format!("SP: | {:#06X}", emulator.cpu.registers.sp)); - ui.text(format!("X: | {:#06X}", emulator.cpu.registers.x)); - ui.text(format!("Y: | {:#06X}", emulator.cpu.registers.y)); - ui.text(format!("A: | {:#06X}", emulator.cpu.registers.a)); - ui.text(format!("P: | {:#04X}", emulator.cpu.registers.p)); - ui.text(format!("D: | {:#06X}", emulator.cpu.registers.d)); - ui.text(format!("PBR: | {:#04X}", emulator.cpu.registers.pbr)); - ui.text(format!("DBR: | {:#04X}", emulator.cpu.registers.dbr)); - ui.text(format!("PC: | {:#06X}", emulator.cpu.registers.pc)); - ui.text(format!("EMU MODE: | {}", emulator.cpu.registers.emulation_mode)); - ui.separator(); - ui.text("Status flags:"); - ui.text("NVMXDIZC"); - ui.text(format!("{:08b}", emulator.cpu.registers.p)); - }); - } - - if state.debug_options.show_cpu_disassembler { - let window = imgui::Window::new("CPU Disassembler"); - window - .position([15.0, 800.0], imgui::Condition::FirstUseEver) - .size([300.0, 70.0], Condition::FirstUseEver) - .build(&ui, || { - ui.text("Upcoming instruction:"); - ui.text(cpu_debug::CPUDisassembler::get_next_instruction(&mut emulator)); - }); - } - - if state.debug_options.memory_map.is_enabled { - let window = imgui::Window::new("Memory Map"); - window - .position([500.0, 30.0], Condition::FirstUseEver) - .size([450.0, 480.0], Condition::FirstUseEver) - .build(&ui, || { - let page_start_input = ui.input_text( - "Page start", - &mut state.debug_options.memory_map.page_start_input - ); - page_start_input.build(); - - let page_end_input = ui.input_text( - "Page end", - &mut state.debug_options.memory_map.page_end_input - ); - page_end_input.build(); - - let address_start_input = ui.input_text( - "Address start", - &mut state.debug_options.memory_map.address_start_input - ); - address_start_input.build(); - - let address_end_input = ui.input_text( - "Address end", - &mut state.debug_options.memory_map.address_end_input, - ); - address_end_input.build(); - - if ui.button("Apply") { - state.debug_options.memory_map.set_values_from_inputs(); - } - - ui.separator(); - let page_start = state.debug_options.memory_map.page_start; - let page_end = state.debug_options.memory_map.page_end; - let address_start = state.debug_options.memory_map.address_start; - let address_end = state.debug_options.memory_map.address_end; - let mut header = String::from(" | "); - for page in (page_start..=page_end).rev() { - header = format!("{}{:02X} ", header, page); - } - ui.text(header); - let mut divider = String::from("-----|-"); - for _page in (page_start..=page_end).rev() { - divider = format!("{}---", divider); - } - ui.text(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.text(address_row); - } - }); - } - } - - // Actually run the emulation - if !state.emulation.is_paused { - emulator.loop_frame(); - } - - // Render emulator framebuffer - { - let tex = renderer.textures.get_mut(texture_id).unwrap(); - tex.write(&queue, &vec![0xAA; 400 * 400 * 4], 400, 400); - let game_window = imgui::Window::new("Game"); - game_window - .position([15.0, 30.0], Condition::FirstUseEver) - .size([425.0, 440.0], Condition::FirstUseEver) - .collapsible(false) - .build(&ui, || { - let game_image = imgui::Image::new(texture_id, [400.0, 400.0]); - game_image.build(&ui); - }); - } - - if state.error_message.show { - let window = imgui::Window::new("Error"); - window - .size([300.0, 100.0], Condition::Always) - .collapsible(false) - .build(&ui, || { - ui.text(&state.error_message.message); - if ui.button("Ok") { - state.error_message.show = false - } - }); - } - - } - - let mut encoder: wgpu::CommandEncoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - - if last_cursor != Some(ui.mouse_cursor()) { - last_cursor = Some(ui.mouse_cursor()); - platform.prepare_render(&ui, &window); - } - - let view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(clear_color), - store: true, - }, - }], - depth_stencil_attachment: None, - }); - - renderer - .render(ui.render(), &queue, &device, &mut rpass) - .expect("Rendering failed"); - - drop(rpass); - - queue.submit(Some(encoder.finish())); - - frame.present(); - } - _ => (), - } - - platform.handle_event(imgui.io_mut(), &window, &event); - }); } diff --git a/snes-frontend/src/ppu.rs b/snes-frontend/src/ppu.rs deleted file mode 100644 index a2f2743..0000000 --- a/snes-frontend/src/ppu.rs +++ /dev/null @@ -1,318 +0,0 @@ -extern crate snes_core; -use snes_core::ppu::registers::{ - MAX_BG_WIDTH, - MAX_BG_HEIGHT, - PPURegisters, - Background as PPUBg, -}; -use imgui_wgpu::Renderer; -use imgui::TextureId; -use wgpu::{Device, Queue}; - -use super::state::{BgDebug, VRAMMap}; - - -pub fn background_texture( - device: &Device, - renderer: &mut Renderer, - background: PPUBg, - width: u32, - height: u32, - name: &str, -) -> TextureId { - let texture = imgui_wgpu::Texture::new( - device, - renderer, - imgui_wgpu::TextureConfig { - label: Some(&format!("{} {:?} texture", name, background)), - size: wgpu::Extent3d { - width: width, - height: height, - depth_or_array_layers: 1, - }, - format: Some(wgpu::TextureFormat::Rgba8Unorm), - ..imgui_wgpu::TextureConfig::default() - }, - ); - renderer.textures.insert(texture) -} - -fn render_background(bgdebug: &mut BgDebug, registers: &PPURegisters) { - let (tile_width, tile_height) = registers.get_bg_tile_size(bgdebug.background).to_usize(); - let (bg_width, bg_height) = registers.get_bg_size(bgdebug.background).to_usize(); - let width = tile_width * bg_width; - let height = tile_height * bg_height; - for x in 0..width { - for y in 0..height { - let index = ((y * MAX_BG_WIDTH) + x) * 4; - let r = index; - let g = index + 1; - let b = index + 2; - let a = index + 3; - - bgdebug.framebuffer[r] = 0xFF; - bgdebug.framebuffer[g] = 0xFF; - bgdebug.framebuffer[b] = 0xFF; - bgdebug.framebuffer[a] = 0xFF; - } - } -} - -fn render_background_char_2bpp(bgdebug: &mut BgDebug, registers: &PPURegisters) { - let vram_base_address = registers.get_bg_char_base_address(bgdebug.background) as usize; - let vram = registers.vram(); - let width: usize = 8 * 16; - let height: usize = 8 * 8; - for y in 0..height { - for x in 0..width { - let current_char = (x / 8) + ((y / 8) * 16); - let current_char_column = x.rem_euclid(8); - let current_char_row = y.rem_euclid(8); - // 8x8 pixels, 2 bitplanes, each word (16bit) holds 8 pixels - // so 1 char is 8 bytes x 2 - let char_base_vram_address = current_char * 8; - let effective_vram_address = vram_base_address + ( - char_base_vram_address + (current_char_row) - ); - let current_pixel = x + (y * width); - - let vram_word = vram[effective_vram_address]; - let lsb_bitplane= vram_word as u8; - let msb_bitplane= (vram_word >> 8) as u8; - let pixels = [ - ( - (lsb_bitplane >> 7) | - ((msb_bitplane >> 7) << 1) - ), - ( - ((lsb_bitplane >> 6) & 1) | - (((msb_bitplane >> 6) & 1) << 1) - ), - ( - ((lsb_bitplane >> 5) & 1) | - (((msb_bitplane >> 5) & 1) << 1) - ), - ( - ((lsb_bitplane >> 4) & 1) | - (((msb_bitplane >> 4) & 1) << 1) - ), - ( - ((lsb_bitplane >> 3) & 1) | - (((msb_bitplane >> 3) & 1) << 1) - ), - ( - ((lsb_bitplane >> 2) & 1) | - (((msb_bitplane >> 2) & 1) << 1) - ), - ( - ((lsb_bitplane >> 1) & 1) | - (((msb_bitplane >> 1) & 1) << 1) - ), - ( - (lsb_bitplane & 1) | - ((msb_bitplane & 1) << 1) - ), - ]; - let effective_pixel = pixels[current_char_column]; - - let fb_index = current_pixel * 4; - let r = fb_index; - let g = fb_index + 1; - let b = fb_index + 2; - let a = fb_index + 3; - - bgdebug.char_framebuffer[a] = 0xFF; - match effective_pixel { - 0b00 => { - bgdebug.char_framebuffer[r] = 0xFF; - bgdebug.char_framebuffer[g] = 0xFF; - bgdebug.char_framebuffer[b] = 0xFF; - }, - 0b01 => { - bgdebug.char_framebuffer[r] = 0xAC; - bgdebug.char_framebuffer[g] = 0xAC; - bgdebug.char_framebuffer[b] = 0xAC; - }, - 0b10 => { - bgdebug.char_framebuffer[r] = 0x56; - bgdebug.char_framebuffer[g] = 0x56; - bgdebug.char_framebuffer[b] = 0x56; - }, - 0b11 => { - bgdebug.char_framebuffer[r] = 0x00; - bgdebug.char_framebuffer[g] = 0x00; - bgdebug.char_framebuffer[b] = 0x00; - }, - _ => unreachable!(), - }; - } - } -} - -pub fn background_window(bgdebug: &mut BgDebug, registers: &PPURegisters, ui: &imgui::Ui, renderer: &mut Renderer, queue: &Queue) { - if !bgdebug.is_enabled { - return; - } - let texture_id = bgdebug.texture_id.unwrap(); - let char_texture_id = bgdebug.char_texture_id.unwrap(); - render_background(bgdebug, registers); - render_background_char_2bpp(bgdebug, registers); - - let tex = renderer.textures.get_mut(texture_id).unwrap(); - tex.write(queue, &bgdebug.framebuffer, MAX_BG_WIDTH as u32, MAX_BG_HEIGHT as u32); - let char_tex = renderer.textures.get_mut(char_texture_id).unwrap(); - char_tex.write(queue, &bgdebug.char_framebuffer, 16 * 8, 8 * 8); - let bg_window = imgui::Window::new(format!("BG: {:?}", bgdebug.background)); - bg_window - .size([MAX_BG_WIDTH as f32, MAX_BG_HEIGHT as f32], imgui::Condition::FirstUseEver) - .opened(&mut bgdebug.is_enabled) - .build(ui, || { - ui.text("Charset:"); - let charset_image = imgui::Image::new(char_texture_id, [16.0 * 8.0 * 2.0, 8.0 * 8.0 * 2.0]); - charset_image.build(&ui); - ui.separator(); - ui.text("Background:"); - let bg_image = imgui::Image::new(texture_id, [MAX_BG_WIDTH as f32, MAX_BG_HEIGHT as f32]); - bg_image.build(&ui); - }); -} - -fn get_register_name(register: u16) -> &'static str { - match register { - snes_core::ppu::registers::INIDISP => "INIDISP", - snes_core::ppu::registers::OBSEL => "OBSEL ", - snes_core::ppu::registers::OAMADDL => "OAMADDL", - snes_core::ppu::registers::OAMADDH => "OAMADDH", - snes_core::ppu::registers::OAMDATA => "OAMDATA", - snes_core::ppu::registers::BGMODE => "BGMODE ", - snes_core::ppu::registers::MOSAIC => "MOSAIC ", - snes_core::ppu::registers::BG1SC => "BG1SC ", - snes_core::ppu::registers::BG2SC => "BG2SC ", - snes_core::ppu::registers::BG3SC => "BG3SC ", - snes_core::ppu::registers::BG4SC => "BG4SC ", - snes_core::ppu::registers::BG12NBA => "BG12NBA", - snes_core::ppu::registers::BG34NBA => "BG34NBA", - snes_core::ppu::registers::BG1HOFS => "BG1HOFS", - snes_core::ppu::registers::BG1VOFS => "BG1VOFS", - snes_core::ppu::registers::BG2HOFS => "BG2HOFS", - snes_core::ppu::registers::BG2VOFS => "BG2VOFS", - snes_core::ppu::registers::BG3HOFS => "BG3HOFS", - snes_core::ppu::registers::BG3VOFS => "BG3VOFS", - snes_core::ppu::registers::BG4HOFS => "BG4HOFS", - snes_core::ppu::registers::BG4VOFS => "BG4VOFS", - snes_core::ppu::registers::VMAIN => "VMAIN ", - snes_core::ppu::registers::VMADDL => "VMADDL ", - snes_core::ppu::registers::VMADDH => "VMADDH ", - snes_core::ppu::registers::VMDATAL => "VMDATAL", - snes_core::ppu::registers::VMDATAH => "VMDATAH", - snes_core::ppu::registers::M7SEL => "M7SEL ", - snes_core::ppu::registers::M7A => "M7A ", - snes_core::ppu::registers::M7B => "M7B ", - snes_core::ppu::registers::M7C => "M7C ", - snes_core::ppu::registers::M7D => "M7D ", - snes_core::ppu::registers::M7X => "M7X ", - snes_core::ppu::registers::M7Y => "M7Y ", - snes_core::ppu::registers::CGADD => "CGADD ", - snes_core::ppu::registers::CGDATA => "CGDATA ", - snes_core::ppu::registers::W12SEL => "W12SEL ", - snes_core::ppu::registers::W34SEL => "W34SEL ", - snes_core::ppu::registers::WOBJSEL => "WOBJSEL", - snes_core::ppu::registers::WH0 => "WH0 ", - snes_core::ppu::registers::WH1 => "WH1 ", - snes_core::ppu::registers::WH2 => "WH2 ", - snes_core::ppu::registers::WH3 => "WH3 ", - snes_core::ppu::registers::WBGLOG => "WBGLOG ", - snes_core::ppu::registers::WOBJLOG => "WOBJLOG", - snes_core::ppu::registers::TM => "TM ", - snes_core::ppu::registers::TS => "TS ", - snes_core::ppu::registers::TMW => "TMW ", - snes_core::ppu::registers::TSW => "TSW ", - snes_core::ppu::registers::CGWSEL => "CGWSEL ", - snes_core::ppu::registers::CGADSUB => "CGADSUB", - snes_core::ppu::registers::COLDATA => "COLDATA", - snes_core::ppu::registers::SETINI => "SETINI ", - snes_core::ppu::registers::MPYL => "MPYL ", - snes_core::ppu::registers::MPYM => "MPYM ", - snes_core::ppu::registers::MPYH => "MPYH ", - snes_core::ppu::registers::SLHV => "SLHV ", - snes_core::ppu::registers::RDOAM => "RDOAM ", - snes_core::ppu::registers::RDVRAML => "RDVRAML", - snes_core::ppu::registers::RDVRAMH => "RDVRAMH", - snes_core::ppu::registers::RDCGRAM => "RDCGRAM", - snes_core::ppu::registers::OPHCT => "OPHCT ", - snes_core::ppu::registers::OPVCT => "OPVCT ", - snes_core::ppu::registers::STAT77 => "STAT77 ", - snes_core::ppu::registers::STAT78 => "STAT78 ", - _ => "N/A ", - } -} - -pub fn registers_window(ppu_registers: &PPURegisters, show_registers: &mut bool, ui: &imgui::Ui) { - let window = imgui::Window::new("PPU Registers"); - window - .position([250.0, 480.0], imgui::Condition::FirstUseEver) - .size([230.0, 310.0], imgui::Condition::FirstUseEver) - .opened(show_registers) - .build(ui, || { - ui.text("H/V Counters:"); - ui.text(format!("V: {}", ppu_registers.v_count)); - ui.text(format!("H: {}", ppu_registers.h_count)); - ui.text(format!("VBlanking: {}", ppu_registers.is_vblanking())); - ui.text(format!("NMI Flag: {}", ppu_registers.vblank_nmi)); - ui.separator(); - ui.text("Registers:"); - for (index, register_value) in ppu_registers.registers().iter().enumerate() { - let register = (index as u16) + 0x2100; - let register_name = get_register_name(register); - ui.text(format!("{:04X} {}: {:02X} {:08b}", register, register_name, register_value, register_value)); - } - }); -} - -pub fn vram_window(ppu_registers: &PPURegisters, vram_debug: &mut VRAMMap, show_vram: &mut bool, ui: &imgui::Ui) { - let window = imgui::Window::new("VRAM"); - window - .size([450.0, 435.0], imgui::Condition::FirstUseEver) - .position([500.0, 530.0], imgui::Condition::FirstUseEver) - .opened(show_vram) - .build(ui, || { - let address_start_input = ui.input_text( - "Address start", - &mut vram_debug.address_start_input - ); - address_start_input.build(); - - let address_end_input = ui.input_text( - "Address end", - &mut vram_debug.address_end_input, - ); - address_end_input.build(); - - if ui.button("Apply") { - vram_debug.set_values_from_inputs(); - } - - ui.separator(); - let address_start = vram_debug.address_start; - let address_end = vram_debug.address_end; - let mut header = String::from(" | "); - for page in 0x00..=0x0F { - header = format!("{} {:02X} ", header, page); - } - ui.text(header); - let mut divider = String::from("-----|-"); - for _ in 0x00..=0x0F { - divider = format!("{}-----", divider); - } - ui.text(divider); - let vector = (address_start..=address_end).collect::>(); - let chunks = vector.chunks(0x10); - for row in chunks { - let mut address_row = format!("{:04X} | ", row[0]); - for address in row { - address_row = format!("{}{:04X} ", address_row, ppu_registers.vram()[((*address) & 0x7FFF) as usize]); - } - ui.text(address_row); - } - }); -} diff --git a/snes-frontend/src/state.rs b/snes-frontend/src/state.rs deleted file mode 100644 index 16cef61..0000000 --- a/snes-frontend/src/state.rs +++ /dev/null @@ -1,237 +0,0 @@ -extern crate snes_core; -use snes_core::ppu::registers::{ - Background as PPUBg, - MAX_BG_WIDTH, - MAX_BG_HEIGHT, -}; -use regex::Regex; - - -pub struct MemoryMap { - pub is_enabled: bool, - pub page_start: u8, - pub page_end: u8, - pub address_start: u16, - pub address_end: u16, - pub page_start_input: String, - pub page_end_input: String, - pub address_start_input: String, - pub address_end_input: String, -} - -impl MemoryMap { - pub fn new() -> Self { - Self { - is_enabled: true, - page_start: 0xF0, - page_end: 0xFF, - address_start: 0xFFF0, - address_end: 0xFFFF, - page_start_input: String::from("0xF0"), - page_end_input: String::from("0xFF"), - address_start_input: String::from("0xFFF0"), - address_end_input: String::from("0xFFFF"), - } - } - - fn validate_values(&mut self) { - if self.page_start > self.page_end { - self.page_start = self.page_end; - } - if self.page_end < self.page_start { - self.page_end = self.page_start; - } - if self.address_start > self.address_end { - self.address_start = self.address_end; - } - if self.address_end < self.address_start { - self.address_end = self.address_start; - } - } - - pub fn set_values_from_inputs(&mut self) { - let page_regex = Regex::new(r"^(0x)?[0-9a-fA-F]{2,2}$").unwrap(); - let address_regex = Regex::new(r"^(0x)?[0-9a-fA-F]{4,4}$").unwrap(); - - if !page_regex.is_match(&self.page_start_input) { - println!("Page start didn't match"); - self.page_start_input = String::from("0x00"); - } - if !page_regex.is_match(&self.page_end_input) { - println!("Page end didn't match"); - self.page_end_input = String::from("0x00"); - } - if !address_regex.is_match(&self.address_start_input) { - println!("Addr start didn't match"); - self.address_start_input = String::from("0x0000"); - } - if !address_regex.is_match(&self.address_end_input) { - println!("Addr end didn't match"); - self.address_end_input = String::from("0x0000"); - } - - self.page_start = u8::from_str_radix(&self.page_start_input.trim_start_matches("0x"), 16).unwrap(); - self.page_end = u8::from_str_radix(&self.page_end_input.trim_start_matches("0x"), 16).unwrap(); - self.address_start = u16::from_str_radix(&self.address_start_input.trim_start_matches("0x"), 16).unwrap(); - self.address_end = u16::from_str_radix(&self.address_end_input.trim_start_matches("0x"), 16).unwrap(); - self.validate_values() - } -} - -pub struct VRAMMap { - pub address_start: u16, - pub address_end: u16, - pub address_start_input: String, - pub address_end_input: String, -} - -impl VRAMMap { - pub fn new() -> Self { - Self { - address_start: 0x0000, - address_end: 0x00FF, - address_start_input: String::from("0000"), - address_end_input: String::from("0100"), - } - } - - fn validate_values(&mut self) { - if self.address_start > self.address_end { - self.address_start = self.address_end; - } - if self.address_end < self.address_start { - self.address_end = self.address_start; - } - } - - pub fn set_values_from_inputs(&mut self) { - let address_regex = Regex::new(r"^(0x)?[0-9a-fA-F]{4,4}$").unwrap(); - - if !address_regex.is_match(&self.address_start_input) { - println!("Addr start didn't match"); - self.address_start_input = String::from("0x0000"); - } - if !address_regex.is_match(&self.address_end_input) { - println!("Addr end didn't match"); - self.address_end_input = String::from("0x0000"); - } - - self.address_start = u16::from_str_radix(&self.address_start_input.trim_start_matches("0x"), 16).unwrap(); - self.address_end = u16::from_str_radix(&self.address_end_input.trim_start_matches("0x"), 16).unwrap(); - self.validate_values() - } -} - -pub struct DebugOptions { - pub is_enabled: bool, - pub show_debug_window: bool, - pub show_cpu_registers: bool, - pub show_cpu_disassembler: bool, - pub show_spc700_registers: bool, - pub memory_map: MemoryMap, -} - -impl DebugOptions { - pub fn new() -> Self { - Self { - is_enabled: true, - show_debug_window: true, - show_cpu_registers: true, - show_cpu_disassembler: true, - show_spc700_registers: true, - memory_map: MemoryMap::new(), - } - } -} - -pub struct ErrorMessage { - pub show: bool, - pub message: String, -} - -impl ErrorMessage { - pub fn new() -> Self { - Self { - show: false, - message: String::from(""), - } - } -} - -pub struct BgDebug { - pub background: PPUBg, - pub is_enabled: bool, - pub texture_id: Option, - pub char_texture_id: Option, - pub framebuffer: Vec, - pub char_framebuffer: Vec, -} - -impl BgDebug { - pub fn new(background: PPUBg) -> Self { - Self { - background: background, - is_enabled: false, - texture_id: None, - char_texture_id: None, - framebuffer: vec![0x00; MAX_BG_WIDTH * MAX_BG_HEIGHT * 4], - // 8x8 pixels, 16x8 characters - char_framebuffer: vec![0x00; 8 * 8 * 16 * 8 * 4], - } - } -} - -pub struct PPUDebug { - pub is_enabled: bool, - pub show_registers: bool, - pub show_vram: bool, - pub backgrounds: [BgDebug; 4], - pub vram_map: VRAMMap, -} - -impl PPUDebug { - pub fn new() -> Self { - Self { - is_enabled: true, - show_registers: true, - show_vram: true, - backgrounds: [ - BgDebug::new(PPUBg::Bg1), - BgDebug::new(PPUBg::Bg2), - BgDebug::new(PPUBg::Bg3), - BgDebug::new(PPUBg::Bg4), - ], - vram_map: VRAMMap::new(), - } - } -} - -pub struct Emulation { - pub is_paused: bool, -} - -impl Emulation { - pub fn new() -> Self { - Self { - is_paused: true, - } - } -} - -pub struct State { - pub debug_options: DebugOptions, - pub error_message: ErrorMessage, - pub ppudebug: PPUDebug, - pub emulation: Emulation, -} - -impl State { - pub fn new() -> Self { - Self { - debug_options: DebugOptions::new(), - error_message: ErrorMessage::new(), - ppudebug: PPUDebug::new(), - emulation: Emulation::new(), - } - } -}