VIP inspection tooling #4
|
@ -23,7 +23,7 @@ use crate::{
|
||||||
vram::VramProcessor,
|
vram::VramProcessor,
|
||||||
window::{
|
window::{
|
||||||
AboutWindow, AppWindow, BgMapWindow, CharacterDataWindow, FrameBufferWindow, GameWindow,
|
AboutWindow, AppWindow, BgMapWindow, CharacterDataWindow, FrameBufferWindow, GameWindow,
|
||||||
GdbServerWindow, InputWindow, ObjectWindow, WorldWindow,
|
GdbServerWindow, InputWindow, ObjectWindow, RegisterWindow, WorldWindow,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -233,6 +233,10 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||||
let world = FrameBufferWindow::new(sim_id, &self.memory, &mut self.vram);
|
let world = FrameBufferWindow::new(sim_id, &self.memory, &mut self.vram);
|
||||||
self.open(event_loop, Box::new(world));
|
self.open(event_loop, Box::new(world));
|
||||||
}
|
}
|
||||||
|
UserEvent::OpenRegisters(sim_id) => {
|
||||||
|
let registers = RegisterWindow::new(sim_id, &self.memory);
|
||||||
|
self.open(event_loop, Box::new(registers));
|
||||||
|
}
|
||||||
UserEvent::OpenDebugger(sim_id) => {
|
UserEvent::OpenDebugger(sim_id) => {
|
||||||
let debugger =
|
let debugger =
|
||||||
GdbServerWindow::new(sim_id, self.client.clone(), self.proxy.clone());
|
GdbServerWindow::new(sim_id, self.client.clone(), self.proxy.clone());
|
||||||
|
@ -496,6 +500,7 @@ pub enum UserEvent {
|
||||||
OpenObjects(SimId),
|
OpenObjects(SimId),
|
||||||
OpenWorlds(SimId),
|
OpenWorlds(SimId),
|
||||||
OpenFrameBuffers(SimId),
|
OpenFrameBuffers(SimId),
|
||||||
|
OpenRegisters(SimId),
|
||||||
OpenDebugger(SimId),
|
OpenDebugger(SimId),
|
||||||
OpenInput,
|
OpenInput,
|
||||||
OpenPlayer2,
|
OpenPlayer2,
|
||||||
|
|
|
@ -3,7 +3,9 @@ use egui::{Context, ViewportBuilder, ViewportId};
|
||||||
pub use game::GameWindow;
|
pub use game::GameWindow;
|
||||||
pub use gdb::GdbServerWindow;
|
pub use gdb::GdbServerWindow;
|
||||||
pub use input::InputWindow;
|
pub use input::InputWindow;
|
||||||
pub use vram::{BgMapWindow, CharacterDataWindow, FrameBufferWindow, ObjectWindow, WorldWindow};
|
pub use vram::{
|
||||||
|
BgMapWindow, CharacterDataWindow, FrameBufferWindow, ObjectWindow, RegisterWindow, WorldWindow,
|
||||||
|
};
|
||||||
use winit::event::KeyEvent;
|
use winit::event::KeyEvent;
|
||||||
|
|
||||||
use crate::emulator::SimId;
|
use crate::emulator::SimId;
|
||||||
|
|
|
@ -162,6 +162,12 @@ impl GameWindow {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
ui.close_menu();
|
ui.close_menu();
|
||||||
}
|
}
|
||||||
|
if ui.button("Registers").clicked() {
|
||||||
|
self.proxy
|
||||||
|
.send_event(UserEvent::OpenRegisters(self.sim_id))
|
||||||
|
.unwrap();
|
||||||
|
ui.close_menu();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
ui.menu_button("Help", |ui| {
|
ui.menu_button("Help", |ui| {
|
||||||
if ui.button("About").clicked() {
|
if ui.button("About").clicked() {
|
||||||
|
|
|
@ -2,6 +2,7 @@ mod bgmap;
|
||||||
mod chardata;
|
mod chardata;
|
||||||
mod framebuffer;
|
mod framebuffer;
|
||||||
mod object;
|
mod object;
|
||||||
|
mod registers;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod world;
|
mod world;
|
||||||
|
|
||||||
|
@ -9,4 +10,5 @@ pub use bgmap::*;
|
||||||
pub use chardata::*;
|
pub use chardata::*;
|
||||||
pub use framebuffer::*;
|
pub use framebuffer::*;
|
||||||
pub use object::*;
|
pub use object::*;
|
||||||
|
pub use registers::*;
|
||||||
pub use world::*;
|
pub use world::*;
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use egui::{
|
||||||
|
Align, CentralPanel, Context, Label, Layout, ScrollArea, TextEdit, Ui, ViewportBuilder,
|
||||||
|
ViewportId,
|
||||||
|
};
|
||||||
|
use egui_extras::{Column, Size, StripBuilder, TableBuilder};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
emulator::SimId,
|
||||||
|
memory::{MemoryClient, MemoryView},
|
||||||
|
window::{utils::UiExt, AppWindow},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct RegisterWindow {
|
||||||
|
sim_id: SimId,
|
||||||
|
memory: Arc<MemoryClient>,
|
||||||
|
registers: MemoryView,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RegisterWindow {
|
||||||
|
pub fn new(sim_id: SimId, memory: &Arc<MemoryClient>) -> Self {
|
||||||
|
Self {
|
||||||
|
sim_id,
|
||||||
|
memory: memory.clone(),
|
||||||
|
registers: memory.watch(sim_id, 0x0005f800, 0x70),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show_interrupts(&mut self, ui: &mut Ui) {
|
||||||
|
let row_height = ui.spacing().interact_size.y;
|
||||||
|
let registers = self.registers.borrow();
|
||||||
|
let [mut raw_intpnd, mut raw_intenb] = registers.read::<[u16; 2]>(0);
|
||||||
|
let mut intenb = InterruptReg::parse(raw_intenb);
|
||||||
|
let mut intpnd = InterruptReg::parse(raw_intpnd);
|
||||||
|
ui.section("Interrupt", |ui| {
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
TableBuilder::new(ui)
|
||||||
|
.id_salt("raw_values")
|
||||||
|
.column(Column::auto())
|
||||||
|
.column(Column::remainder())
|
||||||
|
.cell_layout(Layout::left_to_right(Align::Max))
|
||||||
|
.body(|mut body| {
|
||||||
|
body.row(row_height, |mut row| {
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.label("INTENB");
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
let mut text = format!("{raw_intenb:04x}");
|
||||||
|
ui.add_enabled(
|
||||||
|
false,
|
||||||
|
TextEdit::singleline(&mut text).horizontal_align(Align::Max),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
body.row(row_height, |mut row| {
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.label("INTPND");
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
let mut text = format!("{raw_intpnd:04x}");
|
||||||
|
ui.add_enabled(
|
||||||
|
false,
|
||||||
|
TextEdit::singleline(&mut text).horizontal_align(Align::Max),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(8.0);
|
||||||
|
TableBuilder::new(ui)
|
||||||
|
.id_salt("flags")
|
||||||
|
.column(Column::auto())
|
||||||
|
.columns(Column::remainder(), 2)
|
||||||
|
.cell_layout(Layout::left_to_right(Align::Center).with_main_align(Align::RIGHT))
|
||||||
|
.body(|mut body| {
|
||||||
|
body.row(row_height, |mut row| {
|
||||||
|
row.col(|_ui| {});
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.add_sized([ui.available_width(), 0.0], Label::new("ENB"));
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.add_sized([ui.available_width(), 0.0], Label::new("PND"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
let mut add_row = |label: &str, enb: &mut bool, pnd: &mut bool| {
|
||||||
|
body.row(row_height, |mut row| {
|
||||||
|
row.col(|ui| {
|
||||||
|
ui.label(label);
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
let space =
|
||||||
|
(ui.available_width() - ui.spacing().icon_width) / 2.0;
|
||||||
|
ui.add_space(space);
|
||||||
|
ui.checkbox(enb, "");
|
||||||
|
});
|
||||||
|
row.col(|ui| {
|
||||||
|
let space =
|
||||||
|
(ui.available_width() - ui.spacing().icon_width) / 2.0;
|
||||||
|
ui.add_space(space);
|
||||||
|
ui.checkbox(pnd, "");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
add_row("TIMEERR", &mut intenb.timeerr, &mut intpnd.timeerr);
|
||||||
|
add_row("XPEND", &mut intenb.xpend, &mut intpnd.xpend);
|
||||||
|
add_row("SBHIT", &mut intenb.sbhit, &mut intpnd.sbhit);
|
||||||
|
add_row("FRAMESTART", &mut intenb.framestart, &mut intpnd.framestart);
|
||||||
|
add_row("GAMESTART", &mut intenb.gamestart, &mut intpnd.gamestart);
|
||||||
|
add_row("RFBEND", &mut intenb.rfbend, &mut intpnd.rfbend);
|
||||||
|
add_row("LFBEND", &mut intenb.lfbend, &mut intpnd.lfbend);
|
||||||
|
add_row("SCANERR", &mut intenb.scanerr, &mut intpnd.scanerr);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if intpnd.update(&mut raw_intpnd) {
|
||||||
|
self.memory.write(self.sim_id, 0x0005f800, &raw_intpnd);
|
||||||
|
}
|
||||||
|
if intenb.update(&mut raw_intenb) {
|
||||||
|
self.memory.write(self.sim_id, 0x0005f802, &raw_intenb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppWindow for RegisterWindow {
|
||||||
|
fn viewport_id(&self) -> ViewportId {
|
||||||
|
ViewportId::from_hash_of(format!("registers-{}", self.sim_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sim_id(&self) -> SimId {
|
||||||
|
self.sim_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initial_viewport(&self) -> ViewportBuilder {
|
||||||
|
ViewportBuilder::default()
|
||||||
|
.with_title(format!("Registers ({})", self.sim_id))
|
||||||
|
.with_inner_size((640.0, 480.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self, ctx: &Context) {
|
||||||
|
CentralPanel::default().show(ctx, |ui| {
|
||||||
|
ScrollArea::vertical().show(ui, |ui| {
|
||||||
|
ui.horizontal_top(|ui| {
|
||||||
|
StripBuilder::new(ui)
|
||||||
|
.sizes(Size::remainder(), 4)
|
||||||
|
.horizontal(|mut strip| {
|
||||||
|
strip.cell(|ui| {
|
||||||
|
self.show_interrupts(ui);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct InterruptReg {
|
||||||
|
timeerr: bool,
|
||||||
|
xpend: bool,
|
||||||
|
sbhit: bool,
|
||||||
|
framestart: bool,
|
||||||
|
gamestart: bool,
|
||||||
|
rfbend: bool,
|
||||||
|
lfbend: bool,
|
||||||
|
scanerr: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterruptReg {
|
||||||
|
fn parse(value: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
timeerr: value & 0x8000 != 0,
|
||||||
|
xpend: value & 0x4000 != 0,
|
||||||
|
sbhit: value & 0x2000 != 0,
|
||||||
|
framestart: value & 0x0010 != 0,
|
||||||
|
gamestart: value & 0x0008 != 0,
|
||||||
|
rfbend: value & 0x0004 != 0,
|
||||||
|
lfbend: value & 0x0002 != 0,
|
||||||
|
scanerr: value & 0x0001 != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&self, value: &mut u16) -> bool {
|
||||||
|
let new_value = (*value & 0x1fe0)
|
||||||
|
| if self.timeerr { 0x8000 } else { 0x0000 }
|
||||||
|
| if self.xpend { 0x4000 } else { 0x0000 }
|
||||||
|
| if self.sbhit { 0x2000 } else { 0x0000 }
|
||||||
|
| if self.framestart { 0x0010 } else { 0x0000 }
|
||||||
|
| if self.gamestart { 0x0008 } else { 0x0000 }
|
||||||
|
| if self.rfbend { 0x0004 } else { 0x0000 }
|
||||||
|
| if self.lfbend { 0x0002 } else { 0x0000 }
|
||||||
|
| if self.scanerr { 0x0001 } else { 0x0000 };
|
||||||
|
let changed = *value != new_value;
|
||||||
|
*value = new_value;
|
||||||
|
changed
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue