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