VIP inspection tooling #4
			
				
			
		
		
		
	
							
								
								
									
										12
									
								
								src/app.rs
								
								
								
								
							
							
						
						
									
										12
									
								
								src/app.rs
								
								
								
								
							| 
						 | 
					@ -18,7 +18,7 @@ use crate::{
 | 
				
			||||||
    controller::ControllerManager,
 | 
					    controller::ControllerManager,
 | 
				
			||||||
    emulator::{EmulatorClient, EmulatorCommand, SimId},
 | 
					    emulator::{EmulatorClient, EmulatorCommand, SimId},
 | 
				
			||||||
    input::MappingProvider,
 | 
					    input::MappingProvider,
 | 
				
			||||||
    memory::MemoryMonitor,
 | 
					    memory::MemoryClient,
 | 
				
			||||||
    persistence::Persistence,
 | 
					    persistence::Persistence,
 | 
				
			||||||
    vram::VramProcessor,
 | 
					    vram::VramProcessor,
 | 
				
			||||||
    window::{
 | 
					    window::{
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,7 @@ pub struct Application {
 | 
				
			||||||
    proxy: EventLoopProxy<UserEvent>,
 | 
					    proxy: EventLoopProxy<UserEvent>,
 | 
				
			||||||
    mappings: MappingProvider,
 | 
					    mappings: MappingProvider,
 | 
				
			||||||
    controllers: ControllerManager,
 | 
					    controllers: ControllerManager,
 | 
				
			||||||
    memory: MemoryMonitor,
 | 
					    memory: Arc<MemoryClient>,
 | 
				
			||||||
    vram: VramProcessor,
 | 
					    vram: VramProcessor,
 | 
				
			||||||
    persistence: Persistence,
 | 
					    persistence: Persistence,
 | 
				
			||||||
    viewports: HashMap<ViewportId, Viewport>,
 | 
					    viewports: HashMap<ViewportId, Viewport>,
 | 
				
			||||||
| 
						 | 
					@ -64,7 +64,7 @@ impl Application {
 | 
				
			||||||
        let persistence = Persistence::new();
 | 
					        let persistence = Persistence::new();
 | 
				
			||||||
        let mappings = MappingProvider::new(persistence.clone());
 | 
					        let mappings = MappingProvider::new(persistence.clone());
 | 
				
			||||||
        let controllers = ControllerManager::new(client.clone(), &mappings);
 | 
					        let controllers = ControllerManager::new(client.clone(), &mappings);
 | 
				
			||||||
        let memory = MemoryMonitor::new(client.clone());
 | 
					        let memory = Arc::new(MemoryClient::new(client.clone()));
 | 
				
			||||||
        let vram = VramProcessor::new();
 | 
					        let vram = VramProcessor::new();
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            let mappings = mappings.clone();
 | 
					            let mappings = mappings.clone();
 | 
				
			||||||
| 
						 | 
					@ -214,15 +214,15 @@ impl ApplicationHandler<UserEvent> for Application {
 | 
				
			||||||
                self.open(event_loop, Box::new(about));
 | 
					                self.open(event_loop, Box::new(about));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            UserEvent::OpenCharacterData(sim_id) => {
 | 
					            UserEvent::OpenCharacterData(sim_id) => {
 | 
				
			||||||
                let vram = CharacterDataWindow::new(sim_id, &mut self.memory, &mut self.vram);
 | 
					                let vram = CharacterDataWindow::new(sim_id, &self.memory, &mut self.vram);
 | 
				
			||||||
                self.open(event_loop, Box::new(vram));
 | 
					                self.open(event_loop, Box::new(vram));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            UserEvent::OpenBgMap(sim_id) => {
 | 
					            UserEvent::OpenBgMap(sim_id) => {
 | 
				
			||||||
                let bgmap = BgMapWindow::new(sim_id, &mut self.memory, &mut self.vram);
 | 
					                let bgmap = BgMapWindow::new(sim_id, &self.memory, &mut self.vram);
 | 
				
			||||||
                self.open(event_loop, Box::new(bgmap));
 | 
					                self.open(event_loop, Box::new(bgmap));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            UserEvent::OpenObjects(sim_id) => {
 | 
					            UserEvent::OpenObjects(sim_id) => {
 | 
				
			||||||
                let objects = ObjectWindow::new(sim_id, &mut self.memory, &mut self.vram);
 | 
					                let objects = ObjectWindow::new(sim_id, &self.memory, &mut self.vram);
 | 
				
			||||||
                self.open(event_loop, Box::new(objects));
 | 
					                self.open(event_loop, Box::new(objects));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            UserEvent::OpenDebugger(sim_id) => {
 | 
					            UserEvent::OpenDebugger(sim_id) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    collections::HashMap,
 | 
					    collections::HashMap,
 | 
				
			||||||
    fmt::Debug,
 | 
					    fmt::Debug,
 | 
				
			||||||
    sync::{atomic::AtomicU64, Arc, RwLock, RwLockReadGuard, TryLockError, Weak},
 | 
					    sync::{atomic::AtomicU64, Arc, Mutex, RwLock, RwLockReadGuard, TryLockError, Weak},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use bytemuck::BoxBytes;
 | 
					use bytemuck::BoxBytes;
 | 
				
			||||||
| 
						 | 
					@ -10,34 +10,41 @@ use tracing::warn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
 | 
					use crate::emulator::{EmulatorClient, EmulatorCommand, SimId};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct MemoryMonitor {
 | 
					pub struct MemoryClient {
 | 
				
			||||||
    client: EmulatorClient,
 | 
					    client: EmulatorClient,
 | 
				
			||||||
    regions: HashMap<MemoryRange, Weak<MemoryRegion>>,
 | 
					    regions: Mutex<HashMap<MemoryRange, Weak<MemoryRegion>>>,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl MemoryMonitor {
 | 
					impl MemoryClient {
 | 
				
			||||||
    pub fn new(client: EmulatorClient) -> Self {
 | 
					    pub fn new(client: EmulatorClient) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            client,
 | 
					            client,
 | 
				
			||||||
            regions: HashMap::new(),
 | 
					            regions: Mutex::new(HashMap::new()),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn view(&mut self, sim: SimId, start: u32, length: usize) -> MemoryView {
 | 
					    pub fn watch(&self, sim: SimId, start: u32, length: usize) -> MemoryView {
 | 
				
			||||||
        let range = MemoryRange { sim, start, length };
 | 
					        let range = MemoryRange { sim, start, length };
 | 
				
			||||||
        let region = self
 | 
					        let mut regions = self.regions.lock().unwrap_or_else(|e| e.into_inner());
 | 
				
			||||||
            .regions
 | 
					        let region = regions
 | 
				
			||||||
            .get(&range)
 | 
					            .get(&range)
 | 
				
			||||||
            .and_then(|r| r.upgrade())
 | 
					            .and_then(|r| r.upgrade())
 | 
				
			||||||
            .unwrap_or_else(|| {
 | 
					            .unwrap_or_else(|| {
 | 
				
			||||||
                let region = Arc::new(MemoryRegion::new(start, length));
 | 
					                let region = Arc::new(MemoryRegion::new(start, length));
 | 
				
			||||||
                self.regions.insert(range, Arc::downgrade(®ion));
 | 
					                regions.insert(range, Arc::downgrade(®ion));
 | 
				
			||||||
                self.client
 | 
					                self.client
 | 
				
			||||||
                    .send_command(EmulatorCommand::WatchMemory(range, Arc::downgrade(®ion)));
 | 
					                    .send_command(EmulatorCommand::WatchMemory(range, Arc::downgrade(®ion)));
 | 
				
			||||||
                region
 | 
					                region
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        MemoryView { region }
 | 
					        MemoryView { region }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn write<T: bytemuck::NoUninit>(&self, sim: SimId, address: u32, data: &T) {
 | 
				
			||||||
 | 
					        let data = bytemuck::bytes_of(data).to_vec();
 | 
				
			||||||
 | 
					        let (tx, _) = oneshot::channel();
 | 
				
			||||||
 | 
					        self.client
 | 
				
			||||||
 | 
					            .send_command(EmulatorCommand::WriteMemory(sim, address, data, tx));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn aligned_memory(start: u32, length: usize) -> BoxBytes {
 | 
					fn aligned_memory(start: u32, length: usize) -> BoxBytes {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,14 @@
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use egui::{
 | 
					use egui::{
 | 
				
			||||||
    Align, CentralPanel, Checkbox, Color32, Context, Image, ScrollArea, Slider, TextEdit,
 | 
					    Align, CentralPanel, Checkbox, Color32, ComboBox, Context, Image, ScrollArea, Slider, TextEdit,
 | 
				
			||||||
    TextureOptions, Ui, ViewportBuilder, ViewportId,
 | 
					    TextureOptions, Ui, ViewportBuilder, ViewportId,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use egui_extras::{Column, Size, StripBuilder, TableBuilder};
 | 
					use egui_extras::{Column, Size, StripBuilder, TableBuilder};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    emulator::SimId,
 | 
					    emulator::SimId,
 | 
				
			||||||
    memory::{MemoryMonitor, MemoryView},
 | 
					    memory::{MemoryClient, MemoryView},
 | 
				
			||||||
    vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
 | 
					    vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
 | 
				
			||||||
    window::{
 | 
					    window::{
 | 
				
			||||||
        utils::{NumberEdit, UiExt},
 | 
					        utils::{NumberEdit, UiExt},
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ use super::utils::{self, CellData, CharacterGrid};
 | 
				
			||||||
pub struct BgMapWindow {
 | 
					pub struct BgMapWindow {
 | 
				
			||||||
    sim_id: SimId,
 | 
					    sim_id: SimId,
 | 
				
			||||||
    loader: Arc<VramTextureLoader>,
 | 
					    loader: Arc<VramTextureLoader>,
 | 
				
			||||||
 | 
					    memory: Arc<MemoryClient>,
 | 
				
			||||||
    bgmaps: MemoryView,
 | 
					    bgmaps: MemoryView,
 | 
				
			||||||
    cell_index: usize,
 | 
					    cell_index: usize,
 | 
				
			||||||
    generic_palette: bool,
 | 
					    generic_palette: bool,
 | 
				
			||||||
| 
						 | 
					@ -30,7 +31,7 @@ pub struct BgMapWindow {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BgMapWindow {
 | 
					impl BgMapWindow {
 | 
				
			||||||
    pub fn new(sim_id: SimId, memory: &mut MemoryMonitor, vram: &mut VramProcessor) -> Self {
 | 
					    pub fn new(sim_id: SimId, memory: &Arc<MemoryClient>, vram: &mut VramProcessor) -> Self {
 | 
				
			||||||
        let renderer = BgMapRenderer::new(sim_id, memory);
 | 
					        let renderer = BgMapRenderer::new(sim_id, memory);
 | 
				
			||||||
        let ([cell, bgmap], params) = vram.add(renderer);
 | 
					        let ([cell, bgmap], params) = vram.add(renderer);
 | 
				
			||||||
        let loader =
 | 
					        let loader =
 | 
				
			||||||
| 
						 | 
					@ -38,7 +39,8 @@ impl BgMapWindow {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            sim_id,
 | 
					            sim_id,
 | 
				
			||||||
            loader: Arc::new(loader),
 | 
					            loader: Arc::new(loader),
 | 
				
			||||||
            bgmaps: memory.view(sim_id, 0x00020000, 0x1d800),
 | 
					            memory: memory.clone(),
 | 
				
			||||||
 | 
					            bgmaps: memory.watch(sim_id, 0x00020000, 0x1d800),
 | 
				
			||||||
            cell_index: 0,
 | 
					            cell_index: 0,
 | 
				
			||||||
            generic_palette: false,
 | 
					            generic_palette: false,
 | 
				
			||||||
            params,
 | 
					            params,
 | 
				
			||||||
| 
						 | 
					@ -93,13 +95,8 @@ impl BgMapWindow {
 | 
				
			||||||
                .texture_options(TextureOptions::NEAREST);
 | 
					                .texture_options(TextureOptions::NEAREST);
 | 
				
			||||||
            ui.add(image);
 | 
					            ui.add(image);
 | 
				
			||||||
            ui.section("Cell", |ui| {
 | 
					            ui.section("Cell", |ui| {
 | 
				
			||||||
                let cell = self.bgmaps.borrow().read::<u16>(self.cell_index);
 | 
					                let mut data = self.bgmaps.borrow().read::<u16>(self.cell_index);
 | 
				
			||||||
                let CellData {
 | 
					                let mut cell = CellData::parse(data);
 | 
				
			||||||
                    char_index,
 | 
					 | 
				
			||||||
                    mut vflip,
 | 
					 | 
				
			||||||
                    mut hflip,
 | 
					 | 
				
			||||||
                    palette_index,
 | 
					 | 
				
			||||||
                } = CellData::parse(cell);
 | 
					 | 
				
			||||||
                TableBuilder::new(ui)
 | 
					                TableBuilder::new(ui)
 | 
				
			||||||
                    .column(Column::remainder())
 | 
					                    .column(Column::remainder())
 | 
				
			||||||
                    .column(Column::remainder())
 | 
					                    .column(Column::remainder())
 | 
				
			||||||
| 
						 | 
					@ -109,12 +106,7 @@ impl BgMapWindow {
 | 
				
			||||||
                                ui.label("Character");
 | 
					                                ui.label("Character");
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let mut character_str = char_index.to_string();
 | 
					                                ui.add(NumberEdit::new(&mut cell.char_index).range(0..2048));
 | 
				
			||||||
                                ui.add_enabled(
 | 
					 | 
				
			||||||
                                    false,
 | 
					 | 
				
			||||||
                                    TextEdit::singleline(&mut character_str)
 | 
					 | 
				
			||||||
                                        .horizontal_align(Align::Max),
 | 
					 | 
				
			||||||
                                );
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
| 
						 | 
					@ -122,24 +114,33 @@ impl BgMapWindow {
 | 
				
			||||||
                                ui.label("Palette");
 | 
					                                ui.label("Palette");
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let mut palette = format!("BG {}", palette_index);
 | 
					                                ComboBox::from_id_salt("palette")
 | 
				
			||||||
                                ui.add_enabled(
 | 
					                                    .selected_text(format!("BG {}", cell.palette_index))
 | 
				
			||||||
                                    false,
 | 
					                                    .width(ui.available_width())
 | 
				
			||||||
                                    TextEdit::singleline(&mut palette).horizontal_align(Align::Max),
 | 
					                                    .show_ui(ui, |ui| {
 | 
				
			||||||
 | 
					                                        for palette in 0..4 {
 | 
				
			||||||
 | 
					                                            ui.selectable_value(
 | 
				
			||||||
 | 
					                                                &mut cell.palette_index,
 | 
				
			||||||
 | 
					                                                palette,
 | 
				
			||||||
 | 
					                                                format!("BG {palette}"),
 | 
				
			||||||
                                            );
 | 
					                                            );
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let checkbox = Checkbox::new(&mut hflip, "H-flip");
 | 
					                                ui.add(Checkbox::new(&mut cell.hflip, "H-flip"));
 | 
				
			||||||
                                ui.add_enabled(false, checkbox);
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let checkbox = Checkbox::new(&mut vflip, "V-flip");
 | 
					                                ui.add(Checkbox::new(&mut cell.vflip, "V-flip"));
 | 
				
			||||||
                                ui.add_enabled(false, checkbox);
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					                if cell.update(&mut data) {
 | 
				
			||||||
 | 
					                    let address = 0x00020000 + (self.cell_index * 2);
 | 
				
			||||||
 | 
					                    self.memory.write(self.sim_id, address as u32, &data);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            ui.section("Display", |ui| {
 | 
					            ui.section("Display", |ui| {
 | 
				
			||||||
                ui.horizontal(|ui| {
 | 
					                ui.horizontal(|ui| {
 | 
				
			||||||
| 
						 | 
					@ -223,12 +224,12 @@ struct BgMapRenderer {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl BgMapRenderer {
 | 
					impl BgMapRenderer {
 | 
				
			||||||
    pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
 | 
					    pub fn new(sim_id: SimId, memory: &MemoryClient) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            chardata: memory.view(sim_id, 0x00078000, 0x8000),
 | 
					            chardata: memory.watch(sim_id, 0x00078000, 0x8000),
 | 
				
			||||||
            bgmaps: memory.view(sim_id, 0x00020000, 0x1d800),
 | 
					            bgmaps: memory.watch(sim_id, 0x00020000, 0x1d800),
 | 
				
			||||||
            brightness: memory.view(sim_id, 0x0005f824, 8),
 | 
					            brightness: memory.watch(sim_id, 0x0005f824, 8),
 | 
				
			||||||
            palettes: memory.view(sim_id, 0x0005f860, 16),
 | 
					            palettes: memory.watch(sim_id, 0x0005f860, 16),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    emulator::SimId,
 | 
					    emulator::SimId,
 | 
				
			||||||
    memory::{MemoryMonitor, MemoryView},
 | 
					    memory::{MemoryClient, MemoryView},
 | 
				
			||||||
    vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
 | 
					    vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
 | 
				
			||||||
    window::{
 | 
					    window::{
 | 
				
			||||||
        utils::{NumberEdit, UiExt as _},
 | 
					        utils::{NumberEdit, UiExt as _},
 | 
				
			||||||
| 
						 | 
					@ -92,7 +92,7 @@ pub struct CharacterDataWindow {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CharacterDataWindow {
 | 
					impl CharacterDataWindow {
 | 
				
			||||||
    pub fn new(sim_id: SimId, memory: &mut MemoryMonitor, vram: &mut VramProcessor) -> Self {
 | 
					    pub fn new(sim_id: SimId, memory: &MemoryClient, vram: &mut VramProcessor) -> Self {
 | 
				
			||||||
        let renderer = CharDataRenderer::new(sim_id, memory);
 | 
					        let renderer = CharDataRenderer::new(sim_id, memory);
 | 
				
			||||||
        let ([char, chardata], params) = vram.add(renderer);
 | 
					        let ([char, chardata], params) = vram.add(renderer);
 | 
				
			||||||
        let loader = VramTextureLoader::new([
 | 
					        let loader = VramTextureLoader::new([
 | 
				
			||||||
| 
						 | 
					@ -102,8 +102,8 @@ impl CharacterDataWindow {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            sim_id,
 | 
					            sim_id,
 | 
				
			||||||
            loader: Arc::new(loader),
 | 
					            loader: Arc::new(loader),
 | 
				
			||||||
            brightness: memory.view(sim_id, 0x0005f824, 8),
 | 
					            brightness: memory.watch(sim_id, 0x0005f824, 8),
 | 
				
			||||||
            palettes: memory.view(sim_id, 0x0005f860, 16),
 | 
					            palettes: memory.watch(sim_id, 0x0005f860, 16),
 | 
				
			||||||
            palette: params.palette,
 | 
					            palette: params.palette,
 | 
				
			||||||
            index: params.index,
 | 
					            index: params.index,
 | 
				
			||||||
            params,
 | 
					            params,
 | 
				
			||||||
| 
						 | 
					@ -306,11 +306,11 @@ impl VramRenderer<2> for CharDataRenderer {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl CharDataRenderer {
 | 
					impl CharDataRenderer {
 | 
				
			||||||
    pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
 | 
					    pub fn new(sim_id: SimId, memory: &MemoryClient) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            chardata: memory.view(sim_id, 0x00078000, 0x8000),
 | 
					            chardata: memory.watch(sim_id, 0x00078000, 0x8000),
 | 
				
			||||||
            brightness: memory.view(sim_id, 0x0005f824, 8),
 | 
					            brightness: memory.watch(sim_id, 0x0005f824, 8),
 | 
				
			||||||
            palettes: memory.view(sim_id, 0x0005f860, 16),
 | 
					            palettes: memory.watch(sim_id, 0x0005f860, 16),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,14 @@
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use egui::{
 | 
					use egui::{
 | 
				
			||||||
    Align, CentralPanel, Checkbox, Color32, Context, Image, ScrollArea, Slider, TextEdit,
 | 
					    Align, CentralPanel, Checkbox, Color32, ComboBox, Context, Image, ScrollArea, Slider, TextEdit,
 | 
				
			||||||
    TextureOptions, Ui, ViewportBuilder, ViewportId,
 | 
					    TextureOptions, Ui, ViewportBuilder, ViewportId,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use egui_extras::{Column, Size, StripBuilder, TableBuilder};
 | 
					use egui_extras::{Column, Size, StripBuilder, TableBuilder};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    emulator::SimId,
 | 
					    emulator::SimId,
 | 
				
			||||||
    memory::{MemoryMonitor, MemoryView},
 | 
					    memory::{MemoryClient, MemoryView},
 | 
				
			||||||
    vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
 | 
					    vram::{VramImage, VramParams, VramProcessor, VramRenderer, VramTextureLoader},
 | 
				
			||||||
    window::{
 | 
					    window::{
 | 
				
			||||||
        utils::{NumberEdit, UiExt as _},
 | 
					        utils::{NumberEdit, UiExt as _},
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ use super::utils::{self, Object};
 | 
				
			||||||
pub struct ObjectWindow {
 | 
					pub struct ObjectWindow {
 | 
				
			||||||
    sim_id: SimId,
 | 
					    sim_id: SimId,
 | 
				
			||||||
    loader: Arc<VramTextureLoader>,
 | 
					    loader: Arc<VramTextureLoader>,
 | 
				
			||||||
 | 
					    memory: Arc<MemoryClient>,
 | 
				
			||||||
    objects: MemoryView,
 | 
					    objects: MemoryView,
 | 
				
			||||||
    index: usize,
 | 
					    index: usize,
 | 
				
			||||||
    generic_palette: bool,
 | 
					    generic_palette: bool,
 | 
				
			||||||
| 
						 | 
					@ -29,7 +30,7 @@ pub struct ObjectWindow {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ObjectWindow {
 | 
					impl ObjectWindow {
 | 
				
			||||||
    pub fn new(sim_id: SimId, memory: &mut MemoryMonitor, vram: &mut VramProcessor) -> Self {
 | 
					    pub fn new(sim_id: SimId, memory: &Arc<MemoryClient>, vram: &mut VramProcessor) -> Self {
 | 
				
			||||||
        let renderer = ObjectRenderer::new(sim_id, memory);
 | 
					        let renderer = ObjectRenderer::new(sim_id, memory);
 | 
				
			||||||
        let ([zoom, full], params) = vram.add(renderer);
 | 
					        let ([zoom, full], params) = vram.add(renderer);
 | 
				
			||||||
        let loader =
 | 
					        let loader =
 | 
				
			||||||
| 
						 | 
					@ -37,7 +38,8 @@ impl ObjectWindow {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            sim_id,
 | 
					            sim_id,
 | 
				
			||||||
            loader: Arc::new(loader),
 | 
					            loader: Arc::new(loader),
 | 
				
			||||||
            objects: memory.view(sim_id, 0x0003e000, 0x2000),
 | 
					            memory: memory.clone(),
 | 
				
			||||||
 | 
					            objects: memory.watch(sim_id, 0x0003e000, 0x2000),
 | 
				
			||||||
            index: 0,
 | 
					            index: 0,
 | 
				
			||||||
            generic_palette: false,
 | 
					            generic_palette: false,
 | 
				
			||||||
            params,
 | 
					            params,
 | 
				
			||||||
| 
						 | 
					@ -79,7 +81,7 @@ impl ObjectWindow {
 | 
				
			||||||
                .texture_options(TextureOptions::NEAREST);
 | 
					                .texture_options(TextureOptions::NEAREST);
 | 
				
			||||||
            ui.add(image);
 | 
					            ui.add(image);
 | 
				
			||||||
            ui.section("Properties", |ui| {
 | 
					            ui.section("Properties", |ui| {
 | 
				
			||||||
                let object = self.objects.borrow().read::<[u16; 4]>(self.index);
 | 
					                let mut object = self.objects.borrow().read::<[u16; 4]>(self.index);
 | 
				
			||||||
                let mut obj = Object::parse(object);
 | 
					                let mut obj = Object::parse(object);
 | 
				
			||||||
                TableBuilder::new(ui)
 | 
					                TableBuilder::new(ui)
 | 
				
			||||||
                    .column(Column::remainder())
 | 
					                    .column(Column::remainder())
 | 
				
			||||||
| 
						 | 
					@ -90,10 +92,7 @@ impl ObjectWindow {
 | 
				
			||||||
                                ui.label("Character");
 | 
					                                ui.label("Character");
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                ui.add_enabled(
 | 
					                                ui.add(NumberEdit::new(&mut obj.data.char_index).range(0..2048));
 | 
				
			||||||
                                    false,
 | 
					 | 
				
			||||||
                                    NumberEdit::new(&mut obj.data.char_index).range(0..2048),
 | 
					 | 
				
			||||||
                                );
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
| 
						 | 
					@ -101,11 +100,18 @@ impl ObjectWindow {
 | 
				
			||||||
                                ui.label("Palette");
 | 
					                                ui.label("Palette");
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let mut palette = format!("OBJ {}", obj.data.palette_index);
 | 
					                                ComboBox::from_id_salt("palette")
 | 
				
			||||||
                                ui.add_enabled(
 | 
					                                    .selected_text(format!("OBJ {}", obj.data.palette_index))
 | 
				
			||||||
                                    false,
 | 
					                                    .width(ui.available_width())
 | 
				
			||||||
                                    TextEdit::singleline(&mut palette).horizontal_align(Align::Max),
 | 
					                                    .show_ui(ui, |ui| {
 | 
				
			||||||
 | 
					                                        for palette in 0..4 {
 | 
				
			||||||
 | 
					                                            ui.selectable_value(
 | 
				
			||||||
 | 
					                                                &mut obj.data.palette_index,
 | 
				
			||||||
 | 
					                                                palette,
 | 
				
			||||||
 | 
					                                                format!("OBJ {palette}"),
 | 
				
			||||||
                                            );
 | 
					                                            );
 | 
				
			||||||
 | 
					                                        }
 | 
				
			||||||
 | 
					                                    });
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
| 
						 | 
					@ -113,7 +119,7 @@ impl ObjectWindow {
 | 
				
			||||||
                                ui.label("X");
 | 
					                                ui.label("X");
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                ui.add_enabled(false, NumberEdit::new(&mut obj.x).range(-512..512));
 | 
					                                ui.add(NumberEdit::new(&mut obj.x).range(-512..512));
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
| 
						 | 
					@ -121,7 +127,7 @@ impl ObjectWindow {
 | 
				
			||||||
                                ui.label("Y");
 | 
					                                ui.label("Y");
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                ui.add_enabled(false, NumberEdit::new(&mut obj.y).range(-8..=224));
 | 
					                                ui.add(NumberEdit::new(&mut obj.y).range(-8..=224));
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
| 
						 | 
					@ -129,33 +135,30 @@ impl ObjectWindow {
 | 
				
			||||||
                                ui.label("Parallax");
 | 
					                                ui.label("Parallax");
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                ui.add_enabled(
 | 
					                                ui.add(NumberEdit::new(&mut obj.parallax).range(-512..512));
 | 
				
			||||||
                                    false,
 | 
					 | 
				
			||||||
                                    NumberEdit::new(&mut obj.parallax).range(-512..512),
 | 
					 | 
				
			||||||
                                );
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let checkbox = Checkbox::new(&mut obj.data.hflip, "H-flip");
 | 
					                                ui.add(Checkbox::new(&mut obj.data.hflip, "H-flip"));
 | 
				
			||||||
                                ui.add_enabled(false, checkbox);
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let checkbox = Checkbox::new(&mut obj.data.vflip, "V-flip");
 | 
					                                ui.add(Checkbox::new(&mut obj.data.vflip, "V-flip"));
 | 
				
			||||||
                                ui.add_enabled(false, checkbox);
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let checkbox = Checkbox::new(&mut obj.lon, "Left");
 | 
					                                ui.add(Checkbox::new(&mut obj.lon, "Left"));
 | 
				
			||||||
                                ui.add_enabled(false, checkbox);
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let checkbox = Checkbox::new(&mut obj.ron, "Right");
 | 
					                                ui.add(Checkbox::new(&mut obj.ron, "Right"));
 | 
				
			||||||
                                ui.add_enabled(false, checkbox);
 | 
					 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
 | 
					                if obj.update(&mut object) {
 | 
				
			||||||
 | 
					                    let address = 0x3e000 + self.index * 8;
 | 
				
			||||||
 | 
					                    self.memory.write(self.sim_id, address as u32, &object);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            ui.section("Display", |ui| {
 | 
					            ui.section("Display", |ui| {
 | 
				
			||||||
                ui.horizontal(|ui| {
 | 
					                ui.horizontal(|ui| {
 | 
				
			||||||
| 
						 | 
					@ -254,12 +257,12 @@ struct ObjectRenderer {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl ObjectRenderer {
 | 
					impl ObjectRenderer {
 | 
				
			||||||
    pub fn new(sim_id: SimId, memory: &mut MemoryMonitor) -> Self {
 | 
					    pub fn new(sim_id: SimId, memory: &MemoryClient) -> Self {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            chardata: memory.view(sim_id, 0x00078000, 0x8000),
 | 
					            chardata: memory.watch(sim_id, 0x00078000, 0x8000),
 | 
				
			||||||
            objects: memory.view(sim_id, 0x0003e000, 0x2000),
 | 
					            objects: memory.watch(sim_id, 0x0003e000, 0x2000),
 | 
				
			||||||
            brightness: memory.view(sim_id, 0x0005f824, 8),
 | 
					            brightness: memory.watch(sim_id, 0x0005f824, 8),
 | 
				
			||||||
            palettes: memory.view(sim_id, 0x0005f860, 16),
 | 
					            palettes: memory.watch(sim_id, 0x0005f860, 16),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,11 +39,11 @@ pub struct Object {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl Object {
 | 
					impl Object {
 | 
				
			||||||
    pub fn parse(object: [u16; 4]) -> Self {
 | 
					    pub fn parse(object: [u16; 4]) -> Self {
 | 
				
			||||||
        let x = ((object[0] & 0x3ff) << 6 >> 6) as i16;
 | 
					        let x = ((object[0] & 0x03ff) << 6 >> 6) as i16;
 | 
				
			||||||
        let parallax = ((object[1] & 0x3ff) << 6 >> 6) as i16;
 | 
					        let parallax = ((object[1] & 0x03ff) << 6 >> 6) as i16;
 | 
				
			||||||
        let lon = object[1] & 0x8000 != 0;
 | 
					        let lon = object[1] & 0x8000 != 0;
 | 
				
			||||||
        let ron = object[1] & 0x4000 != 0;
 | 
					        let ron = object[1] & 0x4000 != 0;
 | 
				
			||||||
        let y = (object[2] & 0x0ff) as i16;
 | 
					        let y = (object[2] & 0x00ff) as i16;
 | 
				
			||||||
        // Y is stored as the bottom 8 bits of an i16,
 | 
					        // Y is stored as the bottom 8 bits of an i16,
 | 
				
			||||||
        // so only sign extend if it's out of range.
 | 
					        // so only sign extend if it's out of range.
 | 
				
			||||||
        let y = if y > 224 { y << 8 >> 8 } else { y };
 | 
					        let y = if y > 224 { y << 8 >> 8 } else { y };
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,30 @@ impl Object {
 | 
				
			||||||
            data,
 | 
					            data,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update(&self, source: &mut [u16; 4]) -> bool {
 | 
				
			||||||
 | 
					        let mut changed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let new_x = (self.x as u16 & 0x03ff) | (source[0] & 0xfc00);
 | 
				
			||||||
 | 
					        changed |= source[0] != new_x;
 | 
				
			||||||
 | 
					        source[0] = new_x;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let new_p = if self.lon { 0x8000 } else { 0x0000 }
 | 
				
			||||||
 | 
					            | if self.ron { 0x4000 } else { 0x0000 }
 | 
				
			||||||
 | 
					            | (self.parallax as u16 & 0x3ff)
 | 
				
			||||||
 | 
					            | (source[1] & 0x3c00);
 | 
				
			||||||
 | 
					        changed |= source[1] != new_p;
 | 
				
			||||||
 | 
					        source[1] = new_p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let new_y = (self.y as u16 & 0x00ff) | (source[2] & 0xff00);
 | 
				
			||||||
 | 
					        changed |= source[2] != new_y;
 | 
				
			||||||
 | 
					        source[2] = new_y;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.data.update(&mut source[3]) {
 | 
				
			||||||
 | 
					            changed = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        changed
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct CellData {
 | 
					pub struct CellData {
 | 
				
			||||||
| 
						 | 
					@ -79,6 +103,17 @@ impl CellData {
 | 
				
			||||||
            palette_index,
 | 
					            palette_index,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn update(&self, source: &mut u16) -> bool {
 | 
				
			||||||
 | 
					        let new_value = (self.palette_index as u16) << 14
 | 
				
			||||||
 | 
					            | if self.hflip { 0x2000 } else { 0x0000 }
 | 
				
			||||||
 | 
					            | if self.vflip { 0x1000 } else { 0x0000 }
 | 
				
			||||||
 | 
					            | (self.char_index as u16 & 0x07ff)
 | 
				
			||||||
 | 
					            | (*source & 0x0800);
 | 
				
			||||||
 | 
					        let changed = *source != new_value;
 | 
				
			||||||
 | 
					        *source = new_value;
 | 
				
			||||||
 | 
					        changed
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn read_char_row(
 | 
					pub fn read_char_row(
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue