delete old frontend stuff

This commit is contained in:
2023-12-10 16:03:18 -05:00
parent 98c2c90c08
commit 582e291329
6 changed files with 1 additions and 1106 deletions
-11
View File
@@ -7,14 +7,3 @@ edition = "2021"
[dependencies] [dependencies]
snes-core = { path = "../snes-core" } 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
-15
View File
@@ -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<String>,
}
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)
}
}
-3
View File
@@ -1,3 +0,0 @@
pub mod ppu;
pub mod cpu;
pub mod state;
+1 -522
View File
@@ -1,526 +1,5 @@
extern crate imgui_winit_support; use snes_core::emulator::Emulator;
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
fn main() { fn main() {
// Windowing state
let mut state = State::new();
let mut emulator = Emulator::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);
});
} }
-318
View File
@@ -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::<Vec<u16>>();
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);
}
});
}
-237
View File
@@ -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<imgui::TextureId>,
pub char_texture_id: Option<imgui::TextureId>,
pub framebuffer: Vec<u8>,
pub char_framebuffer: Vec<u8>,
}
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(),
}
}
}