Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 26s
- BIT not longer ANDs the A register - I now a pretty good debug view for debugging the CPU - I wrote a number_input element for iced - I upgraded to iced 0.14 - I added images for play and pause - The debug log now displays in the debug view
233 lines
8.3 KiB
Rust
233 lines
8.3 KiB
Rust
use iced::{
|
|
Element,
|
|
Length::Fill,
|
|
widget::{
|
|
self, button, checkbox, column, container::bordered_box, image, number_input, row,
|
|
scrollable, text,
|
|
},
|
|
};
|
|
|
|
use crate::{CycleResult, NES};
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct DebuggerState {
|
|
ppu_cycles: usize,
|
|
cpu_cycles: usize,
|
|
instructions: usize,
|
|
scan_lines: usize,
|
|
to_scan_line: usize,
|
|
frames: usize,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum DebuggerMessage {
|
|
Run,
|
|
Pause,
|
|
SetPPUCycles(usize),
|
|
RunPPUCycles,
|
|
SetCPUCycles(usize),
|
|
RunCPUCycles,
|
|
SetInstructions(usize),
|
|
RunInstructions,
|
|
SetScanLines(usize),
|
|
RunScanLines,
|
|
SetToScanLine(usize),
|
|
RunToScanLine,
|
|
SetFrames(usize),
|
|
RunFrames,
|
|
}
|
|
|
|
impl DebuggerState {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
ppu_cycles: 1,
|
|
cpu_cycles: 1,
|
|
instructions: 1,
|
|
scan_lines: 1,
|
|
to_scan_line: 1,
|
|
frames: 1,
|
|
// cpu_cycles: 1,
|
|
}
|
|
}
|
|
|
|
pub fn view<'s>(&'s self, nes: &'s NES) -> Element<'s, DebuggerMessage> {
|
|
column![
|
|
row![
|
|
button(image("./images/ic_fluent_play_24_filled.png"))
|
|
.on_press(DebuggerMessage::Run),
|
|
button(image("./images/ic_fluent_pause_24_filled.png"))
|
|
.on_press(DebuggerMessage::Pause),
|
|
],
|
|
iced::widget::rule::horizontal(2.),
|
|
row![column![
|
|
text("Status"),
|
|
row![
|
|
labelled("A:", text(format!("{:02X}", nes.cpu.a))),
|
|
labelled("X:", text(format!("{:02X}", nes.cpu.x))),
|
|
labelled("Y:", text(format!("{:02X}", nes.cpu.y))),
|
|
labelled("PC:", text(format!("{:04X}", nes.cpu.pc))),
|
|
labelled("Cycle:", text(format!("{}", nes.cycle))),
|
|
labelled("SP:", text(format!("{:02X}", nes.cpu.sp))),
|
|
]
|
|
.spacing(5.),
|
|
row![
|
|
labelled("P:", text(format!("{:02X}", nes.cpu.status.0))),
|
|
labelled_box("Carry", nes.cpu.status.carry()),
|
|
labelled_box("Zero", nes.cpu.status.zero()),
|
|
labelled_box("Interrupt", nes.cpu.status.interrupt_disable()),
|
|
labelled_box("--", false),
|
|
labelled_box("--", false),
|
|
labelled_box("Overflow", nes.cpu.status.overflow()),
|
|
labelled_box("Negative", nes.cpu.status.negative()),
|
|
]
|
|
.spacing(5.),
|
|
row![
|
|
text("IRQs:"),
|
|
labelled_box("NMI", nes.peek_nmi()),
|
|
labelled_box("Cart", false),
|
|
labelled_box("Frame Counter", false),
|
|
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)),
|
|
],
|
|
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("Large Sprites", false),
|
|
labelled_box("Vertical Write", false),
|
|
labelled_box("NMI on VBlank", false),
|
|
labelled_box("BG at $1000", false),
|
|
labelled_box("Sprites at $1000", false),
|
|
],
|
|
column![
|
|
run_type(
|
|
"PPU Cycles:",
|
|
self.ppu_cycles,
|
|
DebuggerMessage::SetPPUCycles,
|
|
DebuggerMessage::RunPPUCycles
|
|
),
|
|
run_type(
|
|
"CPU Cycles:",
|
|
self.cpu_cycles,
|
|
DebuggerMessage::SetCPUCycles,
|
|
DebuggerMessage::RunCPUCycles
|
|
),
|
|
run_type(
|
|
"Instructions:",
|
|
self.instructions,
|
|
DebuggerMessage::SetInstructions,
|
|
DebuggerMessage::RunInstructions
|
|
),
|
|
run_type(
|
|
"Scanlines:",
|
|
self.scan_lines,
|
|
DebuggerMessage::SetScanLines,
|
|
DebuggerMessage::RunScanLines
|
|
),
|
|
run_type(
|
|
"To Scanline:",
|
|
self.to_scan_line,
|
|
DebuggerMessage::SetToScanLine,
|
|
DebuggerMessage::RunToScanLine
|
|
),
|
|
run_type(
|
|
"Frames:",
|
|
self.frames,
|
|
DebuggerMessage::SetFrames,
|
|
DebuggerMessage::RunFrames
|
|
),
|
|
],
|
|
]
|
|
.spacing(5.),
|
|
scrollable(column(
|
|
nes.debug_log()
|
|
.history()
|
|
.into_iter()
|
|
.rev()
|
|
.map(|s| text(s).line_height(0.9).into())
|
|
).spacing(0))
|
|
.width(Fill),
|
|
],],
|
|
]
|
|
.width(Fill)
|
|
.height(Fill)
|
|
.into()
|
|
}
|
|
|
|
fn run_n_clock_cycles(nes: &mut NES, n: usize) {
|
|
for _ in 0..n {
|
|
nes.run_one_clock_cycle();
|
|
}
|
|
}
|
|
fn run_until(nes: &mut NES, mut f: impl FnMut(CycleResult, &NES) -> bool) {
|
|
loop {
|
|
if f(nes.run_one_clock_cycle(), nes) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn update(&mut self, message: DebuggerMessage, nes: &mut NES) {
|
|
match message {
|
|
DebuggerMessage::SetPPUCycles(n) => self.ppu_cycles = n,
|
|
DebuggerMessage::SetCPUCycles(n) => self.cpu_cycles = n,
|
|
DebuggerMessage::SetInstructions(n) => self.instructions = n,
|
|
DebuggerMessage::SetScanLines(n) => self.scan_lines = n,
|
|
DebuggerMessage::SetToScanLine(n) => self.to_scan_line = n,
|
|
DebuggerMessage::SetFrames(n) => self.frames = n,
|
|
DebuggerMessage::RunPPUCycles => Self::run_n_clock_cycles(nes, self.ppu_cycles),
|
|
DebuggerMessage::RunCPUCycles => Self::run_n_clock_cycles(nes, self.cpu_cycles * 3),
|
|
DebuggerMessage::RunInstructions => Self::run_until(nes, |c, _| c.cpu_exec),
|
|
DebuggerMessage::RunScanLines => Self::run_n_clock_cycles(nes, self.scan_lines * 341),
|
|
DebuggerMessage::RunToScanLine => {
|
|
Self::run_until(nes, |_, n| n.ppu.scanline == self.to_scan_line)
|
|
}
|
|
DebuggerMessage::RunFrames => Self::run_n_clock_cycles(nes, self.frames * 341 * 261),
|
|
DebuggerMessage::Run => todo!(),
|
|
DebuggerMessage::Pause => todo!(),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn run_type<'a, Message: Clone + 'a>(
|
|
label: &'a str,
|
|
val: usize,
|
|
update: impl Fn(usize) -> Message + 'a,
|
|
run: Message,
|
|
) -> Element<'a, Message> {
|
|
row![
|
|
widget::container(text(label)).padding(2.),
|
|
widget::container(number_input(val).on_input(update)).padding(2.),
|
|
widget::container(button(image("./images/ic_fluent_play_24_filled.png")).on_press(run))
|
|
.padding(2.),
|
|
]
|
|
.spacing(1.)
|
|
.into()
|
|
}
|
|
|
|
pub fn labelled<'a, Message: 'a>(
|
|
label: &'a str,
|
|
content: impl Into<Element<'a, Message>>,
|
|
) -> Element<'a, Message> {
|
|
row![
|
|
widget::container(text(label)).padding(2.),
|
|
widget::container(content).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()
|
|
}
|