VIP inspection tooling #4
			
				
			
		
		
		
	| 
						 | 
					@ -1,9 +1,10 @@
 | 
				
			||||||
use std::{
 | 
					use std::{
 | 
				
			||||||
    fmt::Display,
 | 
					    fmt::{Display, UpperHex},
 | 
				
			||||||
    ops::{Bound, RangeBounds},
 | 
					    ops::{Bound, RangeBounds},
 | 
				
			||||||
    str::FromStr,
 | 
					    str::FromStr,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use atoi::FromRadix16;
 | 
				
			||||||
use egui::{
 | 
					use egui::{
 | 
				
			||||||
    ecolor::HexColor, Align, Color32, CursorIcon, Event, Frame, Key, Layout, Margin, Rect,
 | 
					    ecolor::HexColor, Align, Color32, CursorIcon, Event, Frame, Key, Layout, Margin, Rect,
 | 
				
			||||||
    Response, RichText, Rounding, Sense, Shape, Stroke, TextEdit, Ui, UiBuilder, Vec2, Widget,
 | 
					    Response, RichText, Rounding, Sense, Shape, Stroke, TextEdit, Ui, UiBuilder, Vec2, Widget,
 | 
				
			||||||
| 
						 | 
					@ -38,7 +39,12 @@ impl UiExt for Ui {
 | 
				
			||||||
        let mut frame = Frame::group(self.style());
 | 
					        let mut frame = Frame::group(self.style());
 | 
				
			||||||
        frame.outer_margin.top += 10.0;
 | 
					        frame.outer_margin.top += 10.0;
 | 
				
			||||||
        frame.inner_margin.top += 2.0;
 | 
					        frame.inner_margin.top += 2.0;
 | 
				
			||||||
        let res = self.push_id(&title, |ui| frame.show(ui, add_contents));
 | 
					        let res = self.push_id(&title, |ui| {
 | 
				
			||||||
 | 
					            frame.show(ui, |ui| {
 | 
				
			||||||
 | 
					                ui.set_min_width(ui.available_width());
 | 
				
			||||||
 | 
					                add_contents(ui);
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
        let text = RichText::new(title).background_color(self.style().visuals.panel_fill);
 | 
					        let text = RichText::new(title).background_color(self.style().visuals.panel_fill);
 | 
				
			||||||
        let old_rect = res.response.rect;
 | 
					        let old_rect = res.response.rect;
 | 
				
			||||||
        let mut text_rect = old_rect;
 | 
					        let mut text_rect = old_rect;
 | 
				
			||||||
| 
						 | 
					@ -98,11 +104,35 @@ enum Direction {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub trait Number:
 | 
					pub trait Number:
 | 
				
			||||||
    Copy + One + CheckedAdd + CheckedSub + Eq + Ord + Display + FromStr + Send + Sync + 'static
 | 
					    Copy
 | 
				
			||||||
 | 
					    + One
 | 
				
			||||||
 | 
					    + CheckedAdd
 | 
				
			||||||
 | 
					    + CheckedSub
 | 
				
			||||||
 | 
					    + Eq
 | 
				
			||||||
 | 
					    + Ord
 | 
				
			||||||
 | 
					    + Display
 | 
				
			||||||
 | 
					    + FromStr
 | 
				
			||||||
 | 
					    + FromRadix16
 | 
				
			||||||
 | 
					    + UpperHex
 | 
				
			||||||
 | 
					    + Send
 | 
				
			||||||
 | 
					    + Sync
 | 
				
			||||||
 | 
					    + 'static
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
impl<
 | 
					impl<
 | 
				
			||||||
        T: Copy + One + CheckedAdd + CheckedSub + Eq + Ord + Display + FromStr + Send + Sync + 'static,
 | 
					        T: Copy
 | 
				
			||||||
 | 
					            + One
 | 
				
			||||||
 | 
					            + CheckedAdd
 | 
				
			||||||
 | 
					            + CheckedSub
 | 
				
			||||||
 | 
					            + Eq
 | 
				
			||||||
 | 
					            + Ord
 | 
				
			||||||
 | 
					            + Display
 | 
				
			||||||
 | 
					            + FromStr
 | 
				
			||||||
 | 
					            + FromRadix16
 | 
				
			||||||
 | 
					            + UpperHex
 | 
				
			||||||
 | 
					            + Send
 | 
				
			||||||
 | 
					            + Sync
 | 
				
			||||||
 | 
					            + 'static,
 | 
				
			||||||
    > Number for T
 | 
					    > Number for T
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -113,6 +143,8 @@ pub struct NumberEdit<'a, T: Number> {
 | 
				
			||||||
    precision: usize,
 | 
					    precision: usize,
 | 
				
			||||||
    min: Option<T>,
 | 
					    min: Option<T>,
 | 
				
			||||||
    max: Option<T>,
 | 
					    max: Option<T>,
 | 
				
			||||||
 | 
					    arrows: bool,
 | 
				
			||||||
 | 
					    hex: bool,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<'a, T: Number> NumberEdit<'a, T> {
 | 
					impl<'a, T: Number> NumberEdit<'a, T> {
 | 
				
			||||||
| 
						 | 
					@ -123,6 +155,8 @@ impl<'a, T: Number> NumberEdit<'a, T> {
 | 
				
			||||||
            precision: 3,
 | 
					            precision: 3,
 | 
				
			||||||
            min: None,
 | 
					            min: None,
 | 
				
			||||||
            max: None,
 | 
					            max: None,
 | 
				
			||||||
 | 
					            arrows: true,
 | 
				
			||||||
 | 
					            hex: false,
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -143,12 +177,35 @@ impl<'a, T: Number> NumberEdit<'a, T> {
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        Self { min, max, ..self }
 | 
					        Self { min, max, ..self }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn arrows(self, arrows: bool) -> Self {
 | 
				
			||||||
 | 
					        Self { arrows, ..self }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pub fn hex(self, hex: bool) -> Self {
 | 
				
			||||||
 | 
					        Self { hex, ..self }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl<T: Number> Widget for NumberEdit<'_, T> {
 | 
					impl<T: Number> Widget for NumberEdit<'_, T> {
 | 
				
			||||||
    fn ui(self, ui: &mut Ui) -> Response {
 | 
					    fn ui(self, ui: &mut Ui) -> Response {
 | 
				
			||||||
        let id = ui.id();
 | 
					        let id = ui.id();
 | 
				
			||||||
        let to_string = |val: &T| format!("{val:.0$}", self.precision);
 | 
					        let to_string = |val: &T| {
 | 
				
			||||||
 | 
					            if self.hex {
 | 
				
			||||||
 | 
					                format!("{val:.0$X}", self.precision)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                format!("{val:.0$}", self.precision)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        let from_string = |val: &str| {
 | 
				
			||||||
 | 
					            if self.hex {
 | 
				
			||||||
 | 
					                let bytes = val.as_bytes();
 | 
				
			||||||
 | 
					                let (result, consumed) = T::from_radix_16(bytes);
 | 
				
			||||||
 | 
					                (consumed == bytes.len()).then_some(result)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                val.parse::<T>().ok()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let (last_value, mut str, focus) = ui.memory(|m| {
 | 
					        let (last_value, mut str, focus) = ui.memory(|m| {
 | 
				
			||||||
            let (lv, s) = m
 | 
					            let (lv, s) = m
 | 
				
			||||||
| 
						 | 
					@ -163,7 +220,7 @@ impl<T: Number> Widget for NumberEdit<'_, T> {
 | 
				
			||||||
            str = to_string(self.value);
 | 
					            str = to_string(self.value);
 | 
				
			||||||
            stale = true;
 | 
					            stale = true;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let valid = str.parse().is_ok_and(|v: T| v == *self.value);
 | 
					        let valid = from_string(&str).is_some_and(|v: T| v == *self.value);
 | 
				
			||||||
        let mut up_pressed = false;
 | 
					        let mut up_pressed = false;
 | 
				
			||||||
        let mut down_pressed = false;
 | 
					        let mut down_pressed = false;
 | 
				
			||||||
        if focus {
 | 
					        if focus {
 | 
				
			||||||
| 
						 | 
					@ -194,17 +251,25 @@ impl<T: Number> Widget for NumberEdit<'_, T> {
 | 
				
			||||||
            .id(id)
 | 
					            .id(id)
 | 
				
			||||||
            .margin(Margin {
 | 
					            .margin(Margin {
 | 
				
			||||||
                left: 4.0,
 | 
					                left: 4.0,
 | 
				
			||||||
                right: 20.0,
 | 
					                right: if self.arrows { 20.0 } else { 4.0 },
 | 
				
			||||||
                top: 2.0,
 | 
					                top: 2.0,
 | 
				
			||||||
                bottom: 2.0,
 | 
					                bottom: 2.0,
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        let res = if valid {
 | 
					        let mut res = if valid {
 | 
				
			||||||
            ui.add(text)
 | 
					            ui.add(text)
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            let message = match (self.min, self.max) {
 | 
					            let message = match (self.min, self.max) {
 | 
				
			||||||
                (Some(min), Some(max)) => format!("Please enter a number between {min} and {max}."),
 | 
					                (Some(min), Some(max)) => format!(
 | 
				
			||||||
                (Some(min), None) => format!("Please enter a number greater than {min}."),
 | 
					                    "Please enter a number between {} and {}.",
 | 
				
			||||||
                (None, Some(max)) => format!("Please enter a number less than {max}."),
 | 
					                    to_string(&min),
 | 
				
			||||||
 | 
					                    to_string(&max)
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (Some(min), None) => {
 | 
				
			||||||
 | 
					                    format!("Please enter a number greater than {}.", to_string(&min))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                (None, Some(max)) => {
 | 
				
			||||||
 | 
					                    format!("Please enter a number less than {}.", to_string(&max))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                (None, None) => "Please enter a number.".to_string(),
 | 
					                (None, None) => "Please enter a number.".to_string(),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            ui.scope(|ui| {
 | 
					            ui.scope(|ui| {
 | 
				
			||||||
| 
						 | 
					@ -218,26 +283,28 @@ impl<T: Number> Widget for NumberEdit<'_, T> {
 | 
				
			||||||
            .on_hover_text(message)
 | 
					            .on_hover_text(message)
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let arrow_left = res.rect.max.x + 4.0;
 | 
					 | 
				
			||||||
        let arrow_right = res.rect.max.x + 20.0;
 | 
					 | 
				
			||||||
        let arrow_top = res.rect.min.y - 2.0;
 | 
					 | 
				
			||||||
        let arrow_middle = (res.rect.min.y + res.rect.max.y) / 2.0;
 | 
					 | 
				
			||||||
        let arrow_bottom = res.rect.max.y + 2.0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut delta = None;
 | 
					        let mut delta = None;
 | 
				
			||||||
        let top_arrow_rect = Rect {
 | 
					        if self.arrows {
 | 
				
			||||||
            min: (arrow_left, arrow_top).into(),
 | 
					            let arrow_left = res.rect.max.x + 4.0;
 | 
				
			||||||
            max: (arrow_right, arrow_middle).into(),
 | 
					            let arrow_right = res.rect.max.x + 20.0;
 | 
				
			||||||
        };
 | 
					            let arrow_top = res.rect.min.y - 2.0;
 | 
				
			||||||
        if draw_arrow(ui, top_arrow_rect, true).clicked_or_dragged() || up_pressed {
 | 
					            let arrow_middle = (res.rect.min.y + res.rect.max.y) / 2.0;
 | 
				
			||||||
            delta = Some(Direction::Up);
 | 
					            let arrow_bottom = res.rect.max.y + 2.0;
 | 
				
			||||||
        }
 | 
					
 | 
				
			||||||
        let bottom_arrow_rect = Rect {
 | 
					            let top_arrow_rect = Rect {
 | 
				
			||||||
            min: (arrow_left, arrow_middle).into(),
 | 
					                min: (arrow_left, arrow_top).into(),
 | 
				
			||||||
            max: (arrow_right, arrow_bottom).into(),
 | 
					                max: (arrow_right, arrow_middle).into(),
 | 
				
			||||||
        };
 | 
					            };
 | 
				
			||||||
        if draw_arrow(ui, bottom_arrow_rect, false).clicked_or_dragged() || down_pressed {
 | 
					            if draw_arrow(ui, top_arrow_rect, true).clicked_or_dragged() || up_pressed {
 | 
				
			||||||
            delta = Some(Direction::Down);
 | 
					                delta = Some(Direction::Up);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let bottom_arrow_rect = Rect {
 | 
				
			||||||
 | 
					                min: (arrow_left, arrow_middle).into(),
 | 
				
			||||||
 | 
					                max: (arrow_right, arrow_bottom).into(),
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            if draw_arrow(ui, bottom_arrow_rect, false).clicked_or_dragged() || down_pressed {
 | 
				
			||||||
 | 
					                delta = Some(Direction::Down);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let in_range =
 | 
					        let in_range =
 | 
				
			||||||
| 
						 | 
					@ -248,12 +315,18 @@ impl<T: Number> Widget for NumberEdit<'_, T> {
 | 
				
			||||||
                Direction::Down => self.value.checked_sub(&self.increment),
 | 
					                Direction::Down => self.value.checked_sub(&self.increment),
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            if let Some(new_value) = value.filter(in_range) {
 | 
					            if let Some(new_value) = value.filter(in_range) {
 | 
				
			||||||
 | 
					                if *self.value != new_value {
 | 
				
			||||||
 | 
					                    res.mark_changed();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                *self.value = new_value;
 | 
					                *self.value = new_value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            str = to_string(self.value);
 | 
					            str = to_string(self.value);
 | 
				
			||||||
            stale = true;
 | 
					            stale = true;
 | 
				
			||||||
        } else if res.changed {
 | 
					        } else if res.changed {
 | 
				
			||||||
            if let Some(new_value) = str.parse().ok().filter(in_range) {
 | 
					            if let Some(new_value) = from_string(&str).filter(in_range) {
 | 
				
			||||||
 | 
					                if *self.value != new_value {
 | 
				
			||||||
 | 
					                    res.mark_changed();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                *self.value = new_value;
 | 
					                *self.value = new_value;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            stale = true;
 | 
					            stale = true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,22 @@
 | 
				
			||||||
use std::sync::Arc;
 | 
					use std::sync::Arc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use egui::{
 | 
					use egui::{
 | 
				
			||||||
    Align, Button, CentralPanel, Checkbox, Context, Label, Layout, ScrollArea, TextEdit, Ui,
 | 
					    Align, Button, CentralPanel, Checkbox, Color32, Context, Direction, Label, Layout, ScrollArea,
 | 
				
			||||||
    ViewportBuilder, ViewportId,
 | 
					    TextEdit, 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::{MemoryClient, MemoryValue, MemoryView},
 | 
					    memory::{MemoryClient, MemoryRef, MemoryValue, MemoryView},
 | 
				
			||||||
    window::{
 | 
					    window::{
 | 
				
			||||||
        utils::{NumberEdit, UiExt},
 | 
					        utils::{NumberEdit, UiExt},
 | 
				
			||||||
        AppWindow,
 | 
					        AppWindow,
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use super::utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub struct RegisterWindow {
 | 
					pub struct RegisterWindow {
 | 
				
			||||||
    sim_id: SimId,
 | 
					    sim_id: SimId,
 | 
				
			||||||
    memory: Arc<MemoryClient>,
 | 
					    memory: Arc<MemoryClient>,
 | 
				
			||||||
| 
						 | 
					@ -26,7 +28,7 @@ impl RegisterWindow {
 | 
				
			||||||
        Self {
 | 
					        Self {
 | 
				
			||||||
            sim_id,
 | 
					            sim_id,
 | 
				
			||||||
            memory: memory.clone(),
 | 
					            memory: memory.clone(),
 | 
				
			||||||
            registers: memory.watch(sim_id, 0x0005f800, 0x70),
 | 
					            registers: memory.watch(sim_id, 0x0005f800, 0x72),
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,21 +38,21 @@ impl RegisterWindow {
 | 
				
			||||||
        let mut intenb = InterruptReg::parse(raw_intenb);
 | 
					        let mut intenb = InterruptReg::parse(raw_intenb);
 | 
				
			||||||
        let mut intpnd = InterruptReg::parse(raw_intpnd);
 | 
					        let mut intpnd = InterruptReg::parse(raw_intpnd);
 | 
				
			||||||
        ui.section("Interrupt", |ui| {
 | 
					        ui.section("Interrupt", |ui| {
 | 
				
			||||||
            ui.vertical(|ui| {
 | 
					            let width = ui.available_width();
 | 
				
			||||||
 | 
					            ui.with_layout(Layout::top_down_justified(Align::Center), |ui| {
 | 
				
			||||||
                TableBuilder::new(ui)
 | 
					                TableBuilder::new(ui)
 | 
				
			||||||
                    .id_salt("raw_values")
 | 
					                    .id_salt("raw_values")
 | 
				
			||||||
                    .column(Column::auto())
 | 
					                    .columns(Column::exact(width * 0.5), 2)
 | 
				
			||||||
                    .column(Column::remainder())
 | 
					 | 
				
			||||||
                    .cell_layout(Layout::left_to_right(Align::Max))
 | 
					                    .cell_layout(Layout::left_to_right(Align::Max))
 | 
				
			||||||
                    .body(|mut body| {
 | 
					                    .body(|mut body| {
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                ui.label("INTENB");
 | 
					                                ui.add_sized(ui.available_size(), Label::new("INTENB"));
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                let mut text = format!("{raw_intenb:04x}");
 | 
					                                let mut text = format!("{raw_intenb:04x}");
 | 
				
			||||||
                                ui.add_enabled(
 | 
					                                ui.add_sized(
 | 
				
			||||||
                                    false,
 | 
					                                    ui.available_size(),
 | 
				
			||||||
                                    TextEdit::singleline(&mut text).horizontal_align(Align::Max),
 | 
					                                    TextEdit::singleline(&mut text).horizontal_align(Align::Max),
 | 
				
			||||||
                                );
 | 
					                                );
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
| 
						 | 
					@ -71,17 +73,17 @@ impl RegisterWindow {
 | 
				
			||||||
                ui.add_space(8.0);
 | 
					                ui.add_space(8.0);
 | 
				
			||||||
                TableBuilder::new(ui)
 | 
					                TableBuilder::new(ui)
 | 
				
			||||||
                    .id_salt("flags")
 | 
					                    .id_salt("flags")
 | 
				
			||||||
                    .column(Column::auto())
 | 
					                    .column(Column::exact(width * 0.5))
 | 
				
			||||||
                    .columns(Column::remainder(), 2)
 | 
					                    .columns(Column::exact(width * 0.25), 2)
 | 
				
			||||||
                    .cell_layout(Layout::left_to_right(Align::Center).with_main_align(Align::RIGHT))
 | 
					                    .cell_layout(Layout::left_to_right(Align::Center).with_main_align(Align::RIGHT))
 | 
				
			||||||
                    .body(|mut body| {
 | 
					                    .body(|mut body| {
 | 
				
			||||||
                        body.row(row_height, |mut row| {
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
                            row.col(|_ui| {});
 | 
					                            row.col(|_ui| {});
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                ui.add_sized([ui.available_width(), 0.0], Label::new("ENB"));
 | 
					                                ui.add_sized(ui.available_size(), Label::new("ENB"));
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                            row.col(|ui| {
 | 
					                            row.col(|ui| {
 | 
				
			||||||
                                ui.add_sized([ui.available_width(), 0.0], Label::new("PND"));
 | 
					                                ui.add_sized(ui.available_size(), Label::new("PND"));
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                        let mut add_row = |label: &str, enb: &mut bool, pnd: &mut bool| {
 | 
					                        let mut add_row = |label: &str, enb: &mut bool, pnd: &mut bool| {
 | 
				
			||||||
| 
						 | 
					@ -112,7 +114,7 @@ impl RegisterWindow {
 | 
				
			||||||
                        add_row("LFBEND", &mut intenb.lfbend, &mut intpnd.lfbend);
 | 
					                        add_row("LFBEND", &mut intenb.lfbend, &mut intpnd.lfbend);
 | 
				
			||||||
                        add_row("SCANERR", &mut intenb.scanerr, &mut intpnd.scanerr);
 | 
					                        add_row("SCANERR", &mut intenb.scanerr, &mut intpnd.scanerr);
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                ui.add_space(ui.available_height());
 | 
					                ui.allocate_space(ui.available_size());
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        if intpnd.update(&mut raw_intpnd) {
 | 
					        if intpnd.update(&mut raw_intpnd) {
 | 
				
			||||||
| 
						 | 
					@ -128,9 +130,10 @@ impl RegisterWindow {
 | 
				
			||||||
        let mut raw_dpstts = self.read_address(0x0005f820);
 | 
					        let mut raw_dpstts = self.read_address(0x0005f820);
 | 
				
			||||||
        let mut dpstts = DisplayReg::parse(raw_dpstts);
 | 
					        let mut dpstts = DisplayReg::parse(raw_dpstts);
 | 
				
			||||||
        ui.section("Display", |ui| {
 | 
					        ui.section("Display", |ui| {
 | 
				
			||||||
 | 
					            let width = ui.available_width();
 | 
				
			||||||
            TableBuilder::new(ui)
 | 
					            TableBuilder::new(ui)
 | 
				
			||||||
                .column(Column::auto())
 | 
					                .column(Column::exact(width * 0.5))
 | 
				
			||||||
                .column(Column::remainder())
 | 
					                .column(Column::exact(width * 0.5))
 | 
				
			||||||
                .cell_layout(Layout::left_to_right(Align::Max))
 | 
					                .cell_layout(Layout::left_to_right(Align::Max))
 | 
				
			||||||
                .body(|mut body| {
 | 
					                .body(|mut body| {
 | 
				
			||||||
                    body.row(row_height, |mut row| {
 | 
					                    body.row(row_height, |mut row| {
 | 
				
			||||||
| 
						 | 
					@ -197,7 +200,7 @@ impl RegisterWindow {
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            ui.vertical(|ui| ui.add_space((ui.available_height() - row_height).max(0.0)));
 | 
					            ui.allocate_space(ui.available_size());
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        if dpstts.update(&mut raw_dpstts) {
 | 
					        if dpstts.update(&mut raw_dpstts) {
 | 
				
			||||||
            self.memory.write(self.sim_id, 0x0005f822, &raw_dpstts);
 | 
					            self.memory.write(self.sim_id, 0x0005f822, &raw_dpstts);
 | 
				
			||||||
| 
						 | 
					@ -209,9 +212,10 @@ impl RegisterWindow {
 | 
				
			||||||
        let [mut raw_xpstts, raw_xpctrl] = self.read_address(0x0005f840);
 | 
					        let [mut raw_xpstts, raw_xpctrl] = self.read_address(0x0005f840);
 | 
				
			||||||
        let mut xpstts = DrawingReg::parse(raw_xpstts);
 | 
					        let mut xpstts = DrawingReg::parse(raw_xpstts);
 | 
				
			||||||
        ui.section("Drawing", |ui| {
 | 
					        ui.section("Drawing", |ui| {
 | 
				
			||||||
 | 
					            let width = ui.available_width();
 | 
				
			||||||
            TableBuilder::new(ui)
 | 
					            TableBuilder::new(ui)
 | 
				
			||||||
                .column(Column::auto())
 | 
					                .column(Column::exact(width * 0.5))
 | 
				
			||||||
                .column(Column::remainder())
 | 
					                .column(Column::exact(width * 0.5))
 | 
				
			||||||
                .cell_layout(Layout::left_to_right(Align::Max))
 | 
					                .cell_layout(Layout::left_to_right(Align::Max))
 | 
				
			||||||
                .body(|mut body| {
 | 
					                .body(|mut body| {
 | 
				
			||||||
                    body.row(row_height, |mut row| {
 | 
					                    body.row(row_height, |mut row| {
 | 
				
			||||||
| 
						 | 
					@ -257,7 +261,9 @@ impl RegisterWindow {
 | 
				
			||||||
                        row.col(|ui| {
 | 
					                        row.col(|ui| {
 | 
				
			||||||
                            ui.add_enabled(
 | 
					                            ui.add_enabled(
 | 
				
			||||||
                                false,
 | 
					                                false,
 | 
				
			||||||
                                NumberEdit::new(&mut xpstts.sbcount).range(0..32),
 | 
					                                NumberEdit::new(&mut xpstts.sbcount)
 | 
				
			||||||
 | 
					                                    .arrows(false)
 | 
				
			||||||
 | 
					                                    .range(0..32),
 | 
				
			||||||
                            );
 | 
					                            );
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
| 
						 | 
					@ -291,7 +297,7 @@ impl RegisterWindow {
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            ui.vertical(|ui| ui.add_space(ui.available_height()));
 | 
					            ui.allocate_space(ui.available_size());
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        if xpstts.update(&mut raw_xpstts) {
 | 
					        if xpstts.update(&mut raw_xpstts) {
 | 
				
			||||||
            xpstts.update(&mut raw_xpstts);
 | 
					            xpstts.update(&mut raw_xpstts);
 | 
				
			||||||
| 
						 | 
					@ -299,10 +305,185 @@ impl RegisterWindow {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn read_address<T: MemoryValue>(&self, address: usize) -> T {
 | 
					    fn show_colors(&mut self, ui: &mut Ui) {
 | 
				
			||||||
        let index = (address - 0x0005f800) / size_of::<T>();
 | 
					        let row_height = ui.spacing().interact_size.y;
 | 
				
			||||||
        self.registers.borrow().read(index)
 | 
					        let registers = self.registers.borrow();
 | 
				
			||||||
 | 
					        ui.section("Colors", |ui| {
 | 
				
			||||||
 | 
					            TableBuilder::new(ui)
 | 
				
			||||||
 | 
					                .column(Column::auto())
 | 
				
			||||||
 | 
					                .columns(Column::remainder(), 4)
 | 
				
			||||||
 | 
					                .cell_layout(Layout::left_to_right(Align::Max))
 | 
				
			||||||
 | 
					                .body(|mut body| {
 | 
				
			||||||
 | 
					                    body.row(row_height, |mut row| {
 | 
				
			||||||
 | 
					                        row.col(|_ui| {});
 | 
				
			||||||
 | 
					                        row.col(|_ui| {});
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            ui.with_layout(
 | 
				
			||||||
 | 
					                                Layout::centered_and_justified(Direction::LeftToRight),
 | 
				
			||||||
 | 
					                                |ui| ui.label("1"),
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            ui.with_layout(
 | 
				
			||||||
 | 
					                                Layout::centered_and_justified(Direction::LeftToRight),
 | 
				
			||||||
 | 
					                                |ui| ui.label("2"),
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            ui.with_layout(
 | 
				
			||||||
 | 
					                                Layout::centered_and_justified(Direction::LeftToRight),
 | 
				
			||||||
 | 
					                                |ui| ui.label("3"),
 | 
				
			||||||
 | 
					                            );
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    let mut brts: [u16; 3] = [
 | 
				
			||||||
 | 
					                        read_address(®isters, 0x0005f824),
 | 
				
			||||||
 | 
					                        read_address(®isters, 0x0005f826),
 | 
				
			||||||
 | 
					                        read_address(®isters, 0x0005f828),
 | 
				
			||||||
 | 
					                    ];
 | 
				
			||||||
 | 
					                    body.row(row_height, |mut row| {
 | 
				
			||||||
 | 
					                        let mut stale = false;
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            ui.label("BRT");
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        row.col(|_ui| {});
 | 
				
			||||||
 | 
					                        for brt in brts.iter_mut() {
 | 
				
			||||||
 | 
					                            row.col(|ui| {
 | 
				
			||||||
 | 
					                                if ui
 | 
				
			||||||
 | 
					                                    .add(NumberEdit::new(brt).range(0..256).arrows(false).hex(true))
 | 
				
			||||||
 | 
					                                    .changed()
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    stale = true;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        if stale {
 | 
				
			||||||
 | 
					                            self.memory.write(self.sim_id, 0x0005f824, &brts);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    body.row(row_height, |mut row| {
 | 
				
			||||||
 | 
					                        row.col(|_ui| {});
 | 
				
			||||||
 | 
					                        for shade in utils::parse_brts(&brts, Color32::RED) {
 | 
				
			||||||
 | 
					                            row.col(|ui| {
 | 
				
			||||||
 | 
					                                ui.painter().rect_filled(
 | 
				
			||||||
 | 
					                                    ui.available_rect_before_wrap(),
 | 
				
			||||||
 | 
					                                    0.0,
 | 
				
			||||||
 | 
					                                    shade,
 | 
				
			||||||
 | 
					                                );
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    let mut palettes = read_address::<[u16; 8]>(®isters, 0x0005f860);
 | 
				
			||||||
 | 
					                    let mut add_row = |name: &str, address: u32, value: &mut u16| {
 | 
				
			||||||
 | 
					                        let mut c1 = (*value >> 2) & 0x03;
 | 
				
			||||||
 | 
					                        let mut c2 = (*value >> 4) & 0x03;
 | 
				
			||||||
 | 
					                        let mut c3 = (*value >> 6) & 0x03;
 | 
				
			||||||
 | 
					                        let mut stale = false;
 | 
				
			||||||
 | 
					                        body.row(row_height, |mut row| {
 | 
				
			||||||
 | 
					                            row.col(|ui| {
 | 
				
			||||||
 | 
					                                ui.label(name);
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            row.col(|ui| {
 | 
				
			||||||
 | 
					                                if ui
 | 
				
			||||||
 | 
					                                    .add(
 | 
				
			||||||
 | 
					                                        NumberEdit::new(value)
 | 
				
			||||||
 | 
					                                            .range(0..256)
 | 
				
			||||||
 | 
					                                            .arrows(false)
 | 
				
			||||||
 | 
					                                            .hex(true),
 | 
				
			||||||
 | 
					                                    )
 | 
				
			||||||
 | 
					                                    .changed()
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    stale = true;
 | 
				
			||||||
 | 
					                                };
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            row.col(|ui| {
 | 
				
			||||||
 | 
					                                if ui
 | 
				
			||||||
 | 
					                                    .add(NumberEdit::new(&mut c1).range(0..4).arrows(false))
 | 
				
			||||||
 | 
					                                    .changed()
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    *value = (*value & 0xfff3) | (c1 << 2);
 | 
				
			||||||
 | 
					                                    stale = true;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            row.col(|ui| {
 | 
				
			||||||
 | 
					                                if ui
 | 
				
			||||||
 | 
					                                    .add(NumberEdit::new(&mut c2).range(0..4).arrows(false))
 | 
				
			||||||
 | 
					                                    .changed()
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    *value = (*value & 0xffcf) | (c2 << 4);
 | 
				
			||||||
 | 
					                                    stale = true;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                            row.col(|ui| {
 | 
				
			||||||
 | 
					                                if ui
 | 
				
			||||||
 | 
					                                    .add(NumberEdit::new(&mut c3).range(0..4).arrows(false))
 | 
				
			||||||
 | 
					                                    .changed()
 | 
				
			||||||
 | 
					                                {
 | 
				
			||||||
 | 
					                                    *value = (*value & 0xff3f) | (c3 << 6);
 | 
				
			||||||
 | 
					                                    stale = true;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        if stale {
 | 
				
			||||||
 | 
					                            self.memory.write(self.sim_id, address, value);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    add_row("GPLT0", 0x0005f860, &mut palettes[0]);
 | 
				
			||||||
 | 
					                    add_row("GPLT1", 0x0005f862, &mut palettes[1]);
 | 
				
			||||||
 | 
					                    add_row("GPLT2", 0x0005f864, &mut palettes[2]);
 | 
				
			||||||
 | 
					                    add_row("GPLT3", 0x0005f866, &mut palettes[3]);
 | 
				
			||||||
 | 
					                    add_row("JPLT0", 0x0005f868, &mut palettes[4]);
 | 
				
			||||||
 | 
					                    add_row("JPLT1", 0x0005f86a, &mut palettes[5]);
 | 
				
			||||||
 | 
					                    add_row("JPLT2", 0x0005f86c, &mut palettes[6]);
 | 
				
			||||||
 | 
					                    add_row("JPLT3", 0x0005f86e, &mut palettes[7]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    body.row(row_height, |mut row| {
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            ui.label("BKCOL");
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            let mut bkcol: u16 = read_address(®isters, 0x0005f870);
 | 
				
			||||||
 | 
					                            if ui
 | 
				
			||||||
 | 
					                                .add(NumberEdit::new(&mut bkcol).range(0..4).arrows(false))
 | 
				
			||||||
 | 
					                                .changed()
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                self.memory.write(self.sim_id, 0x0005f870, &bkcol);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        row.col(|_ui| {});
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            ui.label("REST");
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                        row.col(|ui| {
 | 
				
			||||||
 | 
					                            let mut rest: u16 = read_address(®isters, 0x0005f82a);
 | 
				
			||||||
 | 
					                            if ui
 | 
				
			||||||
 | 
					                                .add(
 | 
				
			||||||
 | 
					                                    NumberEdit::new(&mut rest)
 | 
				
			||||||
 | 
					                                        .range(0..256)
 | 
				
			||||||
 | 
					                                        .arrows(false)
 | 
				
			||||||
 | 
					                                        .hex(true),
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                                .changed()
 | 
				
			||||||
 | 
					                            {
 | 
				
			||||||
 | 
					                                self.memory.write(self.sim_id, 0x0005f82a, &rest);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        });
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ui.allocate_space(ui.available_size_before_wrap());
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn read_address<T: MemoryValue>(&self, address: usize) -> T {
 | 
				
			||||||
 | 
					        read_address(&self.registers.borrow(), address)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn read_address<T: MemoryValue>(registers: &MemoryRef, address: usize) -> T {
 | 
				
			||||||
 | 
					    let index = (address - 0x0005f800) / size_of::<T>();
 | 
				
			||||||
 | 
					    registers.read(index)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl AppWindow for RegisterWindow {
 | 
					impl AppWindow for RegisterWindow {
 | 
				
			||||||
| 
						 | 
					@ -317,15 +498,19 @@ impl AppWindow for RegisterWindow {
 | 
				
			||||||
    fn initial_viewport(&self) -> ViewportBuilder {
 | 
					    fn initial_viewport(&self) -> ViewportBuilder {
 | 
				
			||||||
        ViewportBuilder::default()
 | 
					        ViewportBuilder::default()
 | 
				
			||||||
            .with_title(format!("Registers ({})", self.sim_id))
 | 
					            .with_title(format!("Registers ({})", self.sim_id))
 | 
				
			||||||
            .with_inner_size((640.0, 480.0))
 | 
					            .with_inner_size((800.0, 480.0))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn show(&mut self, ctx: &Context) {
 | 
					    fn show(&mut self, ctx: &Context) {
 | 
				
			||||||
        CentralPanel::default().show(ctx, |ui| {
 | 
					        CentralPanel::default().show(ctx, |ui| {
 | 
				
			||||||
            ScrollArea::vertical().show(ui, |ui| {
 | 
					            ScrollArea::vertical().show(ui, |ui| {
 | 
				
			||||||
                ui.horizontal_top(|ui| {
 | 
					                ui.horizontal_top(|ui| {
 | 
				
			||||||
 | 
					                    let width = ui.available_width() - (ui.spacing().item_spacing.x * 6.0);
 | 
				
			||||||
                    StripBuilder::new(ui)
 | 
					                    StripBuilder::new(ui)
 | 
				
			||||||
                        .sizes(Size::remainder(), 4)
 | 
					                        .size(Size::exact(width * 0.2))
 | 
				
			||||||
 | 
					                        .size(Size::exact(width * 0.2))
 | 
				
			||||||
 | 
					                        .size(Size::exact(width * 0.3))
 | 
				
			||||||
 | 
					                        .size(Size::exact(width * 0.2))
 | 
				
			||||||
                        .horizontal(|mut strip| {
 | 
					                        .horizontal(|mut strip| {
 | 
				
			||||||
                            strip.cell(|ui| {
 | 
					                            strip.cell(|ui| {
 | 
				
			||||||
                                self.show_interrupts(ui);
 | 
					                                self.show_interrupts(ui);
 | 
				
			||||||
| 
						 | 
					@ -340,6 +525,9 @@ impl AppWindow for RegisterWindow {
 | 
				
			||||||
                                    });
 | 
					                                    });
 | 
				
			||||||
                                });
 | 
					                                });
 | 
				
			||||||
                            });
 | 
					                            });
 | 
				
			||||||
 | 
					                            strip.cell(|ui| {
 | 
				
			||||||
 | 
					                                self.show_colors(ui);
 | 
				
			||||||
 | 
					                            });
 | 
				
			||||||
                        });
 | 
					                        });
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,15 @@ pub const fn parse_shades(brts: &[u8; 8]) -> [u8; 4] {
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pub fn parse_brts(brts: &[u16; 3], color: Color32) -> [Color32; 4] {
 | 
				
			||||||
 | 
					    [
 | 
				
			||||||
 | 
					        Color32::BLACK,
 | 
				
			||||||
 | 
					        shade(brts[0] as u8, color),
 | 
				
			||||||
 | 
					        shade(brts[1] as u8, color),
 | 
				
			||||||
 | 
					        shade((brts[0] + brts[1] + brts[2]) as u8, color),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub fn palette_colors(palette: u8, brts: &[u8; 8], color: Color32) -> [Color32; 4] {
 | 
					pub fn palette_colors(palette: u8, brts: &[u8; 8], color: Color32) -> [Color32; 4] {
 | 
				
			||||||
    let colors = parse_shades(brts).map(|s| shade(s, color));
 | 
					    let colors = parse_shades(brts).map(|s| shade(s, color));
 | 
				
			||||||
    parse_palette(palette).map(|p| colors[p as usize])
 | 
					    parse_palette(palette).map(|p| colors[p as usize])
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue