Files
snes/snes-frontend/src/emu_ui/debug/ppu.rs

266 lines
12 KiB
Rust

use eframe::egui::{self, Color32, Rect, Ui, Vec2};
use snes_core::ppu::registers::PPURegisters;
use snes_core::utils::color::rgb555_to_rgb888;
use crate::emu_state::debug_options::PPUDebugControlOptions;
use crate::emu_ui::debug::common::sanitize_input;
pub fn build_ppu_debug_controls(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugControlOptions, ppu_registers: &PPURegisters) {
if !ppu_debug_options.is_enabled {
return
}
egui::Window::new("PPU Debug Controls")
.auto_sized()
.max_width(125.0)
.open(&mut ppu_debug_options.is_enabled)
.show(ctx, |ui| {
ui.monospace("Controls:");
ui.horizontal(|ui| {
if ui.selectable_label(
ppu_debug_options.show_registers,
"Show PPU Registers"
).clicked() {
ppu_debug_options.show_registers = !ppu_debug_options.show_registers;
}
if ui.selectable_label(
ppu_debug_options.show_vram,
"Show VRAM"
).clicked() {
ppu_debug_options.show_vram = !ppu_debug_options.show_vram;
}
});
ui.monospace("Backgrounds:");
ui.horizontal(|ui| {
for bgdebug in ppu_debug_options.backgrounds.iter_mut() {
if ui.selectable_label(
bgdebug.is_enabled,
format!("Show {:?}", bgdebug.background),
).clicked() {
bgdebug.is_enabled = !bgdebug.is_enabled;
}
}
});
});
build_ppu_registers_window(ctx, ppu_debug_options, ppu_registers);
build_vram_window(ctx, ppu_debug_options, ppu_registers);
build_cgram_window(ctx, ppu_debug_options, ppu_registers);
}
fn build_ppu_registers_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugControlOptions, ppu_registers: &PPURegisters) {
if !ppu_debug_options.show_registers {
return;
}
egui::Window::new("PPU Registers")
.max_width(180.0)
.open(&mut ppu_debug_options.show_registers)
.show(ctx, |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
ui.monospace(format!(
"H count: {} V count: {}",
ppu_registers.h_count,
ppu_registers.v_count,
));
ui.monospace(format!("VBlanking: {}", ppu_registers.is_vblanking()));
ui.monospace(format!("NMI Flag: {}", ppu_registers.vblank_nmi));
ui.separator();
ui.monospace("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.monospace(format!("{:04X} {}: {:02X} {:08b}", register, register_name, register_value, register_value));
}
});
});
}
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 ",
}
}
fn build_vram_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugControlOptions, ppu_registers: &PPURegisters) {
if !ppu_debug_options.show_vram {
return;
}
egui::Window::new("VRAM Viewer")
.open(&mut ppu_debug_options.show_vram)
.default_width(610.0)
.show(ctx, |ui| {
ui.horizontal(|ui| {
ui.label("Address Start: ");
ui.text_edit_singleline(&mut ppu_debug_options.vram_inputs.address_start);
});
ui.horizontal(|ui| {
ui.label("Address End: ");
ui.text_edit_singleline(&mut ppu_debug_options.vram_inputs.address_end);
});
if ui.button("Search").clicked() {
sanitize_input(&mut ppu_debug_options.vram_inputs.address_start, false);
sanitize_input(&mut ppu_debug_options.vram_inputs.address_end, false);
ppu_debug_options.vram_inputs_result.address_start = ppu_debug_options.vram_inputs.address_start.to_string();
ppu_debug_options.vram_inputs_result.address_end = ppu_debug_options.vram_inputs.address_end.to_string();
}
ui.separator();
egui::ScrollArea::both().show(ui, |ui| {
let address_start = u16::from_str_radix(&ppu_debug_options.vram_inputs_result.address_start, 16).unwrap();
let address_end = u16::from_str_radix(&ppu_debug_options.vram_inputs_result.address_end, 16).unwrap();
let mut header = String::from(" | ");
for page in 0x00..=0x0F {
header = format!("{} {:02X} ", header, page);
}
ui.monospace(header);
let mut divider = String::from("-----|-");
for _ in 0x00..=0x0F {
divider = format!("{}-----", divider);
}
ui.monospace(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 {
let effective_vram_address = ((*address) & 0x7FFF) as usize;
address_row = format!("{}{:04X} ", address_row, ppu_registers.vram()[effective_vram_address]);
}
ui.monospace(address_row);
}
});
});
}
fn build_cgram_window(ctx: &egui::Context, ppu_debug_options: &mut PPUDebugControlOptions, ppu_registers: &PPURegisters) {
if !ppu_debug_options.show_cgram {
return;
}
egui::Window::new("CGRAM Viewer")
.open(&mut ppu_debug_options.show_cgram)
.default_width(610.0)
.max_width(610.0)
.show(ctx, |ui| {
egui::ScrollArea::both().show(ui, |ui| {
let address_start = 0x00;
let address_end = 0xFF;
let mut header = String::from(" | ");
for page in 0x00..=0x0F {
header = format!("{} {:02X} ", header, page);
}
ui.monospace(header);
let mut divider = String::from("-----|-");
for _ in 0x00..=0x0F {
divider = format!("{}-----", divider);
}
ui.monospace(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.cgram()[((*address) & 0x7FFF) as usize]);
}
ui.monospace(address_row);
}
});
ui.separator();
let mut address: u8 = 0x00;
for row in 0x00..=0x0F {
ui.horizontal(|ui| {
ui.label(format!("0x{:02X}: ", row));
for _ in 0x00..=0x0F {
paint_cgram_color_address(ui, address, ppu_registers);
address = address.wrapping_add(1);
}
});
}
});
}
fn paint_cgram_color_address(ui: &mut Ui, address: u8, ppu_registers: &PPURegisters) {
let rgb555_value = ppu_registers.read_cgram(address);
let rgb888_value = rgb555_to_rgb888((
(rgb555_value & 0b11111) as u8,
((rgb555_value >> 5) & 0b11111) as u8,
((rgb555_value >> 10) & 0b11111) as u8,
));
paint_square_color(ui, rgb888_value);
}
fn paint_square_color(ui: &mut Ui, color: (u8, u8, u8)) {
let (_, painter) = ui.allocate_painter(Vec2::splat(15.0), egui::Sense::hover());
let square_color = Color32::from_rgb(color.0, color.1, color.2);
let rect = Rect::EVERYTHING;
painter.rect_filled(rect, 0.0, square_color);
}