Make properties editable
This commit is contained in:
parent
7356287030
commit
b888d1140a
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