VIP inspection tooling #4
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -23,7 +23,7 @@ use crate::{
 | 
			
		|||
    vram::VramProcessor,
 | 
			
		||||
    window::{
 | 
			
		||||
        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);
 | 
			
		||||
                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) => {
 | 
			
		||||
                let debugger =
 | 
			
		||||
                    GdbServerWindow::new(sim_id, self.client.clone(), self.proxy.clone());
 | 
			
		||||
| 
						 | 
				
			
			@ -496,6 +500,7 @@ pub enum UserEvent {
 | 
			
		|||
    OpenObjects(SimId),
 | 
			
		||||
    OpenWorlds(SimId),
 | 
			
		||||
    OpenFrameBuffers(SimId),
 | 
			
		||||
    OpenRegisters(SimId),
 | 
			
		||||
    OpenDebugger(SimId),
 | 
			
		||||
    OpenInput,
 | 
			
		||||
    OpenPlayer2,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,9 @@ use egui::{Context, ViewportBuilder, ViewportId};
 | 
			
		|||
pub use game::GameWindow;
 | 
			
		||||
pub use gdb::GdbServerWindow;
 | 
			
		||||
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 crate::emulator::SimId;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -162,6 +162,12 @@ impl GameWindow {
 | 
			
		|||
                    .unwrap();
 | 
			
		||||
                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| {
 | 
			
		||||
            if ui.button("About").clicked() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ mod bgmap;
 | 
			
		|||
mod chardata;
 | 
			
		||||
mod framebuffer;
 | 
			
		||||
mod object;
 | 
			
		||||
mod registers;
 | 
			
		||||
mod utils;
 | 
			
		||||
mod world;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -9,4 +10,5 @@ pub use bgmap::*;
 | 
			
		|||
pub use chardata::*;
 | 
			
		||||
pub use framebuffer::*;
 | 
			
		||||
pub use object::*;
 | 
			
		||||
pub use registers::*;
 | 
			
		||||
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