Major work
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 10s
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 10s
This commit is contained in:
378
src/debugger.rs
378
src/debugger.rs
@@ -1,13 +1,16 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use iced::{
|
||||
Element,
|
||||
Length::Fill,
|
||||
widget::{
|
||||
self, button, checkbox, column, container::bordered_box, image, number_input, row,
|
||||
scrollable, text,
|
||||
},
|
||||
advanced::{
|
||||
layout::Node, widget::{
|
||||
tree::{State, Tag}, Tree
|
||||
}, Widget
|
||||
}, mouse, widget::{
|
||||
self, button, canvas::{Frame, Program}, checkbox, column, container::bordered_box, image, number_input, row, rule::horizontal, scrollable, text, Canvas, Text
|
||||
}, window::Event, Element, Length::{self, Fill}, Point, Renderer, Size, Theme
|
||||
};
|
||||
|
||||
use crate::{CycleResult, NES};
|
||||
use crate::{CycleResult, NES, PPU};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DebuggerState {
|
||||
@@ -37,6 +40,35 @@ pub enum DebuggerMessage {
|
||||
RunFrames,
|
||||
}
|
||||
|
||||
pub fn hex16<'a, Theme, Renderer>(val: u16) -> Text<'a, Theme, Renderer>
|
||||
where
|
||||
Theme: text::Catalog + 'a,
|
||||
Renderer: iced::advanced::text::Renderer,
|
||||
{
|
||||
text(format!("{val:04X}"))
|
||||
}
|
||||
pub fn hex8<'a, Theme, Renderer>(val: u8) -> Text<'a, Theme, Renderer>
|
||||
where
|
||||
Theme: text::Catalog + 'a,
|
||||
Renderer: iced::advanced::text::Renderer,
|
||||
{
|
||||
text(format!("{val:02X}"))
|
||||
}
|
||||
pub fn bin8<'a, Theme, Renderer>(val: u8) -> Text<'a, Theme, Renderer>
|
||||
where
|
||||
Theme: text::Catalog + 'a,
|
||||
Renderer: iced::advanced::text::Renderer,
|
||||
{
|
||||
text(format!("{val:08b}"))
|
||||
}
|
||||
pub fn bin32<'a, Theme, Renderer>(val: u32) -> Text<'a, Theme, Renderer>
|
||||
where
|
||||
Theme: text::Catalog + 'a,
|
||||
Renderer: iced::advanced::text::Renderer,
|
||||
{
|
||||
text(format!("{val:032b}"))
|
||||
}
|
||||
|
||||
impl DebuggerState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
@@ -85,38 +117,56 @@ impl DebuggerState {
|
||||
text("IRQs:"),
|
||||
labelled_box("NMI", nes.peek_nmi()),
|
||||
labelled_box("Cart", false),
|
||||
labelled_box("Frame Counter", false),
|
||||
labelled_box("Frame Counter", nes.apu().peek_irq()),
|
||||
labelled_box("DMC", false),
|
||||
]
|
||||
.spacing(5.),
|
||||
row![
|
||||
column![
|
||||
labelled("Cycle", text(nes.ppu.pixel)),
|
||||
labelled("Scanline", text(nes.ppu.scanline)),
|
||||
labelled("PPU Cycle", text(nes.ppu.cycle)),
|
||||
labelled("Cycle", text(nes.ppu().pixel)),
|
||||
labelled("Scanline", text(nes.ppu().scanline)),
|
||||
labelled("PPU Cycle", text(nes.ppu().cycle)),
|
||||
labelled("V:", hex16(nes.ppu().background.v)),
|
||||
labelled("T:", hex16(nes.ppu().background.t)),
|
||||
labelled("X:", hex8(nes.ppu().background.x)),
|
||||
text(""),
|
||||
labelled("NT:", hex8(nes.ppu().background.cur_nametable)),
|
||||
labelled2(
|
||||
"AT:",
|
||||
hex8(nes.ppu().background.next_attr),
|
||||
hex16(
|
||||
0x23C0
|
||||
| (nes.ppu().background.v & 0x0C00)
|
||||
| ((nes.ppu().background.v >> 4) & 0x38)
|
||||
| ((nes.ppu().background.v >> 2) & 0x07)
|
||||
)
|
||||
),
|
||||
labelled("AT:", hex8(nes.ppu().background.cur_attr)),
|
||||
labelled("HI:", bin32(nes.ppu().background.cur_shift_high)),
|
||||
labelled("LO:", bin32(nes.ppu().background.cur_shift_low)),
|
||||
],
|
||||
column![
|
||||
labelled_box("Sprite 0 Hit", false),
|
||||
labelled_box("Sprite 0 Overflow", false),
|
||||
labelled_box("Vertical Blank", nes.ppu.vblank),
|
||||
labelled_box("Write Toggle", false),
|
||||
labelled_box("", false),
|
||||
labelled_box("Vertical Blank", nes.ppu().vblank),
|
||||
labelled_box("Write Toggle", nes.ppu().background.w),
|
||||
text(""),
|
||||
labelled_box("Large Sprites", false),
|
||||
labelled_box("Vertical Write", false),
|
||||
labelled_box("NMI on VBlank", false),
|
||||
labelled_box("BG at $1000", false),
|
||||
labelled_box("Vertical Write", nes.ppu().background.vram_column),
|
||||
labelled_box("NMI on VBlank", nes.ppu().nmi_on_vblank()),
|
||||
labelled_box("BG at $1000", nes.ppu().background.second_pattern),
|
||||
labelled_box("Sprites at $1000", false),
|
||||
],
|
||||
column![
|
||||
labelled_box("Even frame", nes.ppu.even),
|
||||
labelled_box("BG Enabled", false),
|
||||
labelled_box("Sprites Enabled", false),
|
||||
labelled_box("BG Mask", false),
|
||||
labelled_box("Sprites Mask", false),
|
||||
labelled_box("Grayscale", false),
|
||||
labelled_box("Intensify Red", false),
|
||||
labelled_box("Intensify Green", false),
|
||||
labelled_box("Intensify Blue", false),
|
||||
labelled_box("Even frame", nes.ppu().even),
|
||||
labelled_box("BG Enabled", nes.ppu().mask.enable_background),
|
||||
labelled_box("Sprites Enabled", nes.ppu().mask.enable_sprites),
|
||||
labelled_box("BG Mask", nes.ppu().mask.background_on_left_edge),
|
||||
labelled_box("Sprites Mask", nes.ppu().mask.sprites_on_left_edge),
|
||||
labelled_box("Grayscale", nes.ppu().mask.grayscale),
|
||||
labelled_box("Intensify Red", nes.ppu().mask.em_red),
|
||||
labelled_box("Intensify Green", nes.ppu().mask.em_green),
|
||||
labelled_box("Intensify Blue", nes.ppu().mask.em_blue),
|
||||
],
|
||||
column![
|
||||
run_type(
|
||||
@@ -158,6 +208,7 @@ impl DebuggerState {
|
||||
],
|
||||
]
|
||||
.spacing(5.),
|
||||
horizontal(2),
|
||||
scrollable(
|
||||
column(
|
||||
nes.debug_log()
|
||||
@@ -178,15 +229,15 @@ impl DebuggerState {
|
||||
|
||||
fn run_n_clock_cycles(nes: &mut NES, n: usize) {
|
||||
for _ in 0..n {
|
||||
nes.run_one_clock_cycle();
|
||||
if nes.halted {
|
||||
if nes.run_one_clock_cycle().dbg_int || nes.halted {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn run_until(nes: &mut NES, mut f: impl FnMut(CycleResult, &NES) -> bool, mut count: usize) {
|
||||
loop {
|
||||
if f(nes.run_one_clock_cycle(), nes) {
|
||||
let res = nes.run_one_clock_cycle();
|
||||
if res.dbg_int || f(res, nes) {
|
||||
count -= 1;
|
||||
if count <= 0 {
|
||||
break;
|
||||
@@ -250,8 +301,277 @@ pub fn labelled<'a, Message: 'a>(
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn labelled2<'a, Message: 'a>(
|
||||
label: &'a str,
|
||||
content: impl Into<Element<'a, Message>>,
|
||||
content2: impl Into<Element<'a, Message>>,
|
||||
) -> Element<'a, Message> {
|
||||
row![
|
||||
widget::container(text(label)).padding(2.),
|
||||
widget::container(content).style(bordered_box).padding(2.),
|
||||
widget::container(content2).style(bordered_box).padding(2.),
|
||||
]
|
||||
.spacing(1.)
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn labelled_box<'a, Message: 'a>(label: &'a str, value: bool) -> Element<'a, Message> {
|
||||
row![checkbox(value), widget::container(text(label)),]
|
||||
.spacing(1.)
|
||||
.into()
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DbgImage<'a> {
|
||||
NameTable(&'a PPU),
|
||||
PatternTable(&'a PPU),
|
||||
Palette(&'a PPU),
|
||||
}
|
||||
|
||||
impl<Message, T> Program<Message, T> for DbgImage<'_> {
|
||||
type State = ();
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &Self::State,
|
||||
renderer: &Renderer,
|
||||
_theme: &T,
|
||||
_bounds: iced::Rectangle,
|
||||
_cursor: mouse::Cursor,
|
||||
) -> Vec<iced::widget::canvas::Geometry<Renderer>> {
|
||||
// const SIZE: f32 = 2.;
|
||||
let mut name_table_frame =
|
||||
Frame::new(renderer, Size::new(256. * 4. + 260. * 2., 256. * 4.));
|
||||
name_table_frame.scale(2.);
|
||||
// println!("Position: {:?}", cursor.position());
|
||||
match self {
|
||||
DbgImage::NameTable(ppu) => ppu.render_name_table(&mut name_table_frame),
|
||||
DbgImage::PatternTable(ppu) => ppu.render_pattern_tables(&mut name_table_frame),
|
||||
DbgImage::Palette(ppu) => ppu.render_palette(&mut name_table_frame),
|
||||
};
|
||||
vec![name_table_frame.into_geometry()]
|
||||
}
|
||||
}
|
||||
|
||||
impl DbgImage<'_> {
|
||||
fn width(&self) -> Length {
|
||||
match self {
|
||||
DbgImage::NameTable(_) => Length::Fixed(512. * 2.),
|
||||
DbgImage::PatternTable(_) => Length::Fixed(16. * 8. * 2.),
|
||||
DbgImage::Palette(_) => Length::Fixed(40. * 2.),
|
||||
}
|
||||
}
|
||||
fn height(&self) -> Length {
|
||||
match self {
|
||||
DbgImage::NameTable(_) => Length::Fixed(512. * 2.),
|
||||
DbgImage::PatternTable(_) => Length::Fixed(16. * 8. * 2. * 2.),
|
||||
DbgImage::Palette(_) => Length::Fixed(80. * 2.),
|
||||
}
|
||||
}
|
||||
fn help(&self, cursor: Point) -> Option<String> {
|
||||
match self {
|
||||
DbgImage::NameTable(ppu) => ppu.name_cursor_info(cursor),
|
||||
DbgImage::PatternTable(ppu) => ppu.pattern_cursor_info(cursor),
|
||||
DbgImage::Palette(ppu) => ppu.palette_cursor_info(cursor),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DbgImageSetup<'a, M, T: text::Catalog> {
|
||||
dbg: DbgImage<'a>,
|
||||
image: Canvas<DbgImage<'a>, M, T>,
|
||||
text: Text<'a, T>,
|
||||
padding: f32,
|
||||
drawn: bool,
|
||||
// image: DbgImage<'a>,
|
||||
// text: te,
|
||||
}
|
||||
|
||||
// pub trait Container<Message, Theme, Renderer> {
|
||||
// // Not ideal
|
||||
// fn children(&self) -> &[&dyn Widget<Message, Theme, Renderer>];
|
||||
// }
|
||||
|
||||
impl<'s, Message, Theme> Widget<Message, Theme, Renderer> for DbgImageSetup<'s, Message, Theme>
|
||||
where
|
||||
Theme: text::Catalog + 's,
|
||||
{
|
||||
fn size(&self) -> Size<iced::Length> {
|
||||
// self.
|
||||
Size::new(Fill, Fill)
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
renderer: &Renderer,
|
||||
limits: &iced::advanced::layout::Limits,
|
||||
) -> Node {
|
||||
let img_node = self.image.layout(&mut tree.children[0], renderer, limits);
|
||||
let txt_node = Widget::<Message, Theme, _>::layout(
|
||||
&mut self.text,
|
||||
&mut tree.children[1],
|
||||
renderer,
|
||||
&limits.shrink(Size::new(
|
||||
img_node.size().width + self.padding * 2.,
|
||||
self.padding * 2.,
|
||||
)),
|
||||
)
|
||||
.move_to(Point::new(
|
||||
img_node.size().width + self.padding,
|
||||
self.padding,
|
||||
));
|
||||
Node::with_children(limits.max(), vec![img_node, txt_node])
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Theme,
|
||||
style: &iced::advanced::renderer::Style,
|
||||
layout: iced::advanced::Layout<'_>,
|
||||
cursor: iced::advanced::mouse::Cursor,
|
||||
viewport: &iced::Rectangle,
|
||||
) {
|
||||
self.image.draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout.child(0),
|
||||
cursor,
|
||||
viewport,
|
||||
);
|
||||
Widget::<Message, Theme, _>::draw(
|
||||
&self.text,
|
||||
&tree.children[1],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout.child(1),
|
||||
cursor,
|
||||
viewport,
|
||||
)
|
||||
}
|
||||
|
||||
fn tag(&self) -> Tag {
|
||||
Tag::of::<Rc<String>>()
|
||||
}
|
||||
|
||||
fn state(&self) -> State {
|
||||
State::new(Rc::new(String::new()))
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
vec![
|
||||
Tree::new(&self.image as &dyn Widget<Message, Theme, Renderer>),
|
||||
Tree::new(&self.text as &dyn Widget<Message, Theme, Renderer>),
|
||||
]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children(&[
|
||||
&self.image as &dyn Widget<Message, Theme, Renderer>,
|
||||
&self.text as &dyn Widget<Message, Theme, Renderer>,
|
||||
]);
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
layout: iced::advanced::Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
operation: &mut dyn iced::advanced::widget::Operation,
|
||||
) {
|
||||
operation.container(None, layout.bounds());
|
||||
operation.traverse(&mut |op| {
|
||||
self.image
|
||||
.operate(&mut tree.children[0], layout.child(0), renderer, op);
|
||||
Widget::<Message, Theme, _>::operate(
|
||||
&mut self.text,
|
||||
&mut tree.children[1],
|
||||
layout.child(1),
|
||||
renderer,
|
||||
op,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: &iced::Event,
|
||||
layout: iced::advanced::Layout<'_>,
|
||||
cursor: iced::advanced::mouse::Cursor,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn iced::advanced::Clipboard,
|
||||
shell: &mut iced::advanced::Shell<'_, Message>,
|
||||
viewport: &iced::Rectangle,
|
||||
) {
|
||||
self.image.update(
|
||||
&mut tree.children[0],
|
||||
event,
|
||||
layout.child(0),
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
viewport,
|
||||
);
|
||||
if matches!(event, iced::Event::Mouse(mouse::Event::CursorMoved { .. })) || !self.drawn {
|
||||
if let Some(help) = cursor
|
||||
.position_in(layout.child(0).bounds())
|
||||
.and_then(|pos| self.dbg.help(Point::new(pos.x / 2., pos.y / 2.)))
|
||||
{
|
||||
self.text = text(help);
|
||||
shell.invalidate_layout();
|
||||
shell.request_redraw();
|
||||
}
|
||||
self.drawn = true;
|
||||
}
|
||||
Widget::<Message, Theme, _>::update(
|
||||
&mut self.text,
|
||||
&mut tree.children[1],
|
||||
event,
|
||||
layout.child(1),
|
||||
cursor,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
viewport,
|
||||
);
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
_tree: &iced::advanced::widget::Tree,
|
||||
_layout: iced::advanced::Layout<'_>,
|
||||
_cursor: iced::advanced::mouse::Cursor,
|
||||
_viewport: &iced::Rectangle,
|
||||
_renderer: &Renderer,
|
||||
) -> iced::advanced::mouse::Interaction {
|
||||
iced::advanced::mouse::Interaction::default()
|
||||
}
|
||||
|
||||
fn overlay<'a>(
|
||||
&'a mut self,
|
||||
_tree: &'a mut iced::advanced::widget::Tree,
|
||||
_layout: iced::advanced::Layout<'a>,
|
||||
_renderer: &Renderer,
|
||||
_viewport: &iced::Rectangle,
|
||||
_translation: iced::Vector,
|
||||
) -> Option<iced::advanced::overlay::Element<'a, Message, Theme, Renderer>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dbg_image<'a, Message: 'a>(img: DbgImage<'a>) -> Element<'a, Message> {
|
||||
Element::new(DbgImageSetup {
|
||||
dbg: img,
|
||||
image: Canvas::new(img).width(img.width()).height(img.height()),
|
||||
text: text(""),
|
||||
padding: 10.,
|
||||
drawn: false,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user