Finally find (& fix) bug in BIT instructions
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 26s
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
This commit is contained in:
2235
Cargo.lock
generated
2235
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,10 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitfield = "0.19.3"
|
bitfield = "0.19.3"
|
||||||
iced = { version = "0.13.1", features = ["debug", "canvas", "tokio", "lazy"] }
|
# iced = { version = "0.14.0", features = ["debug", "canvas", "tokio", "lazy", "image", "advanced"] }
|
||||||
iced_graphics = { version = "0.13.0", features = ["geometry", "image"] }
|
iced = { path = "../iced", features = ["debug", "canvas", "tokio", "lazy", "image", "advanced"] }
|
||||||
iced_widget = { version = "0.13.4", features = ["canvas", "image"] }
|
# iced_graphics = { version = "0.14.0", features = ["geometry", "image"] }
|
||||||
|
# iced_widget = { version = "0.13.4", features = ["canvas", "image"] }
|
||||||
thiserror = "2.0.17"
|
thiserror = "2.0.17"
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = { version = "0.3.20", features = ["ansi", "chrono", "env-filter", "json", "serde"] }
|
tracing-subscriber = { version = "0.3.20", features = ["ansi", "chrono", "env-filter", "json", "serde"] }
|
||||||
|
|||||||
BIN
images/ic_fluent_pause_24_filled.png
Normal file
BIN
images/ic_fluent_pause_24_filled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 211 B |
4
images/ic_fluent_pause_24_filled.svg
Normal file
4
images/ic_fluent_pause_24_filled.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="#000000" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5.74609 3C4.7796 3 3.99609 3.7835 3.99609 4.75V19.25C3.99609 20.2165 4.7796 21 5.74609 21H9.24609C10.2126 21 10.9961 20.2165 10.9961 19.25V4.75C10.9961 3.7835 10.2126 3 9.24609 3H5.74609ZM14.7461 3C13.7796 3 12.9961 3.7835 12.9961 4.75V19.25C12.9961 20.2165 13.7796 21 14.7461 21H18.2461C19.2126 21 19.9961 20.2165 19.9961 19.25V4.75C19.9961 3.7835 19.2126 3 18.2461 3H14.7461Z" fill="#000000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 550 B |
BIN
images/ic_fluent_play_24_filled.png
Normal file
BIN
images/ic_fluent_play_24_filled.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 321 B |
4
images/ic_fluent_play_24_filled.svg
Normal file
4
images/ic_fluent_play_24_filled.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="#000000" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5 5.27368C5 3.56682 6.82609 2.48151 8.32538 3.2973L20.687 10.0235C22.2531 10.8756 22.2531 13.124 20.687 13.9762L8.32538 20.7024C6.82609 21.5181 5 20.4328 5 18.726V5.27368Z" fill="#000000"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 344 B |
@@ -105,9 +105,15 @@ impl APU {
|
|||||||
pub fn run_one_clock_cycle(&mut self) -> bool {
|
pub fn run_one_clock_cycle(&mut self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
pub fn peek_nmi(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
pub fn nmi_waiting(&mut self) -> bool {
|
pub fn nmi_waiting(&mut self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
pub fn peek_irq(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
pub fn irq_waiting(&mut self) -> bool {
|
pub fn irq_waiting(&mut self) -> bool {
|
||||||
// TODO: implement logic
|
// TODO: implement logic
|
||||||
false
|
false
|
||||||
|
|||||||
44
src/debug.rs
44
src/debug.rs
@@ -18,31 +18,49 @@ impl DebugLog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotate(&mut self) {
|
pub fn rotate(&mut self) {
|
||||||
let rot = std::mem::take(&mut self.current);
|
// if self.current.len() > 500 {
|
||||||
if let Some(max) = self.max_history {
|
let mut rot = std::mem::take(&mut self.current);
|
||||||
if self.history.len() < max.into() {
|
self.current = rot.split_off(rot.rfind('\n').unwrap_or(rot.len()));
|
||||||
self.history.push(rot);
|
// if let Some(max) = self.max_history {
|
||||||
} else {
|
// if self.history.len() < max.into() {
|
||||||
self.history[self.pos] = rot;
|
// self.history.extend(rot.lines().map(|s| s.to_owned()));
|
||||||
self.pos = (self.pos + 1) % max.get();
|
// } else {
|
||||||
}
|
// self.history[self.pos] = rot;
|
||||||
} else {
|
// self.pos = (self.pos + 1) % max.get();
|
||||||
self.history.push(rot);
|
// }
|
||||||
|
// } else {
|
||||||
|
// self.history.push(rot);
|
||||||
|
self.history.extend(rot.lines().map(|s| s.to_owned()));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub fn current(&self) -> &str {
|
||||||
|
// &self.current
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn history(&self) -> &[String] {
|
||||||
|
&self.history[self.history.len().saturating_sub(100)..]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Write for DebugLog {
|
impl std::fmt::Write for DebugLog {
|
||||||
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
fn write_str(&mut self, s: &str) -> std::fmt::Result {
|
||||||
self.current.write_str(s)
|
let tmp = self.current.write_str(s);
|
||||||
|
self.rotate();
|
||||||
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_char(&mut self, c: char) -> std::fmt::Result {
|
fn write_char(&mut self, c: char) -> std::fmt::Result {
|
||||||
self.current.write_char(c)
|
let tmp = self.current.write_char(c);
|
||||||
|
self.rotate();
|
||||||
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::fmt::Result {
|
fn write_fmt(&mut self, args: std::fmt::Arguments<'_>) -> std::fmt::Result {
|
||||||
self.current.write_fmt(args)
|
let tmp = self.current.write_fmt(args);
|
||||||
|
self.rotate();
|
||||||
|
tmp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
232
src/debugger.rs
Normal file
232
src/debugger.rs
Normal file
@@ -0,0 +1,232 @@
|
|||||||
|
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()
|
||||||
|
}
|
||||||
@@ -1,163 +1,36 @@
|
|||||||
//! Pick lists display a dropdown list of selectable options.
|
use iced::advanced::graphics::core::{keyboard, touch, window};
|
||||||
//!
|
use iced::advanced::text::paragraph;
|
||||||
//! # Example
|
use iced::advanced::widget::{Tree, tree};
|
||||||
//! ```no_run
|
use iced::advanced::{
|
||||||
//! # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
|
Clipboard, Layout, Shell, Text, Widget, layout, mouse, overlay, renderer, text,
|
||||||
//! # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
|
};
|
||||||
//! #
|
use iced::overlay::menu::Menu;
|
||||||
//! use iced::widget::pick_list;
|
use iced::{
|
||||||
//!
|
Background, Border, Color, Element, Event, Point, Rectangle, Size, Theme, Vector, alignment,
|
||||||
//! struct State {
|
};
|
||||||
//! favorite: Option<Fruit>,
|
use iced::{
|
||||||
//! }
|
Length, Padding, Pixels,
|
||||||
//!
|
widget::{button, overlay::menu},
|
||||||
//! #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
//! enum Fruit {
|
|
||||||
//! Apple,
|
|
||||||
//! Orange,
|
|
||||||
//! Strawberry,
|
|
||||||
//! Tomato,
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! #[derive(Debug, Clone)]
|
|
||||||
//! enum Message {
|
|
||||||
//! FruitSelected(Fruit),
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn view(state: &State) -> Element<'_, Message> {
|
|
||||||
//! let fruits = [
|
|
||||||
//! Fruit::Apple,
|
|
||||||
//! Fruit::Orange,
|
|
||||||
//! Fruit::Strawberry,
|
|
||||||
//! Fruit::Tomato,
|
|
||||||
//! ];
|
|
||||||
//!
|
|
||||||
//! pick_list(
|
|
||||||
//! fruits,
|
|
||||||
//! state.favorite,
|
|
||||||
//! Message::FruitSelected,
|
|
||||||
//! )
|
|
||||||
//! .placeholder("Select your favorite fruit...")
|
|
||||||
//! .into()
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! fn update(state: &mut State, message: Message) {
|
|
||||||
//! match message {
|
|
||||||
//! Message::FruitSelected(fruit) => {
|
|
||||||
//! state.favorite = Some(fruit);
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//!
|
|
||||||
//! impl std::fmt::Display for Fruit {
|
|
||||||
//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
//! f.write_str(match self {
|
|
||||||
//! Self::Apple => "Apple",
|
|
||||||
//! Self::Orange => "Orange",
|
|
||||||
//! Self::Strawberry => "Strawberry",
|
|
||||||
//! Self::Tomato => "Tomato",
|
|
||||||
//! })
|
|
||||||
//! }
|
|
||||||
//! }
|
|
||||||
//! ```
|
|
||||||
use iced::alignment;
|
|
||||||
use iced::event::{self, Event};
|
|
||||||
use iced::keyboard;
|
|
||||||
// use iced::layout;
|
|
||||||
use iced::mouse;
|
|
||||||
use iced::overlay;
|
|
||||||
// use iced::renderer;
|
|
||||||
// use iced::text::paragraph;
|
|
||||||
// use iced::text::{self, Text};
|
|
||||||
use iced::touch;
|
|
||||||
// use iced::widget::tree::{self, Tree};
|
|
||||||
use iced::overlay::menu::{self, Menu};
|
|
||||||
use iced::{
|
|
||||||
Background, Border, Color, Element, Length, Padding, Pixels, Point, Rectangle, Size, Theme,
|
|
||||||
Vector,
|
|
||||||
};
|
};
|
||||||
use iced_graphics::core::text::paragraph;
|
|
||||||
use iced_graphics::core::widget::{Tree, tree};
|
|
||||||
use iced_graphics::core::{Clipboard, Layout, Shell, Text, Widget, layout, renderer, text};
|
|
||||||
|
|
||||||
use std::borrow::Borrow;
|
use std::borrow::Borrow;
|
||||||
use std::f32;
|
use std::f32;
|
||||||
|
|
||||||
/// A widget for selecting a single value from a list of options.
|
pub fn header_menu<'a, T, L, Message: Clone>(
|
||||||
///
|
title: impl ToString,
|
||||||
/// # Example
|
options: L,
|
||||||
/// ```no_run
|
f: impl Fn(T) -> Message + 'a,
|
||||||
/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
|
) -> HeaderMenu<'a, T, L, Message>
|
||||||
/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
|
where
|
||||||
/// #
|
T: ToString + Clone,
|
||||||
/// use iced::widget::pick_list;
|
L: Borrow<[T]> + 'a,
|
||||||
///
|
{
|
||||||
/// struct State {
|
HeaderMenu::new(title, options, f)
|
||||||
/// favorite: Option<Fruit>,
|
}
|
||||||
/// }
|
|
||||||
///
|
pub struct HeaderMenu<'a, T, L, Message, Theme = iced::Theme, Renderer = iced::Renderer>
|
||||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
where
|
||||||
/// enum Fruit {
|
T: ToString + Clone,
|
||||||
/// Apple,
|
|
||||||
/// Orange,
|
|
||||||
/// Strawberry,
|
|
||||||
/// Tomato,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Clone)]
|
|
||||||
/// enum Message {
|
|
||||||
/// FruitSelected(Fruit),
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn view(state: &State) -> Element<'_, Message> {
|
|
||||||
/// let fruits = [
|
|
||||||
/// Fruit::Apple,
|
|
||||||
/// Fruit::Orange,
|
|
||||||
/// Fruit::Strawberry,
|
|
||||||
/// Fruit::Tomato,
|
|
||||||
/// ];
|
|
||||||
///
|
|
||||||
/// pick_list(
|
|
||||||
/// fruits,
|
|
||||||
/// state.favorite,
|
|
||||||
/// Message::FruitSelected,
|
|
||||||
/// )
|
|
||||||
/// .placeholder("Select your favorite fruit...")
|
|
||||||
/// .into()
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn update(state: &mut State, message: Message) {
|
|
||||||
/// match message {
|
|
||||||
/// Message::FruitSelected(fruit) => {
|
|
||||||
/// state.favorite = Some(fruit);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl std::fmt::Display for Fruit {
|
|
||||||
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
/// f.write_str(match self {
|
|
||||||
/// Self::Apple => "Apple",
|
|
||||||
/// Self::Orange => "Orange",
|
|
||||||
/// Self::Strawberry => "Strawberry",
|
|
||||||
/// Self::Tomato => "Tomato",
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
#[allow(missing_debug_implementations)]
|
|
||||||
pub struct HeaderMenu<
|
|
||||||
'a,
|
|
||||||
T,
|
|
||||||
L,
|
|
||||||
// V,
|
|
||||||
Message,
|
|
||||||
Theme = iced::Theme,
|
|
||||||
Renderer = iced::Renderer,
|
|
||||||
> where
|
|
||||||
T: ToString + PartialEq + Clone,
|
|
||||||
L: Borrow<[T]> + 'a,
|
L: Borrow<[T]> + 'a,
|
||||||
// V: Borrow<T> + 'a,
|
|
||||||
Theme: Catalog,
|
Theme: Catalog,
|
||||||
Renderer: text::Renderer,
|
Renderer: text::Renderer,
|
||||||
{
|
{
|
||||||
@@ -176,35 +49,30 @@ pub struct HeaderMenu<
|
|||||||
handle: Handle<Renderer::Font>,
|
handle: Handle<Renderer::Font>,
|
||||||
class: <Theme as Catalog>::Class<'a>,
|
class: <Theme as Catalog>::Class<'a>,
|
||||||
menu_class: <Theme as menu::Catalog>::Class<'a>,
|
menu_class: <Theme as menu::Catalog>::Class<'a>,
|
||||||
|
last_status: Option<Status>,
|
||||||
|
menu_height: Length,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, L, Message, Theme, Renderer> HeaderMenu<'a, T, L, Message, Theme, Renderer>
|
impl<'a, T, L, Message, Theme, Renderer> HeaderMenu<'a, T, L, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
T: ToString + PartialEq + Clone,
|
T: ToString + Clone,
|
||||||
L: Borrow<[T]> + 'a,
|
L: Borrow<[T]> + 'a,
|
||||||
// V: Borrow<T> + 'a,
|
|
||||||
Message: Clone,
|
Message: Clone,
|
||||||
Theme: Catalog,
|
Theme: Catalog,
|
||||||
Renderer: text::Renderer,
|
Renderer: text::Renderer,
|
||||||
{
|
{
|
||||||
/// Creates a new [`PickList`] with the given list of options, the current
|
/// Creates a new [`PickList`] with the given list of options, the current
|
||||||
/// selected value, and the message to produce when an option is selected.
|
/// selected value, and the message to produce when an option is selected.
|
||||||
pub fn new(
|
pub fn new(title: impl ToString, options: L, on_select: impl Fn(T) -> Message + 'a) -> Self {
|
||||||
title: impl Into<String>,
|
|
||||||
options: L,
|
|
||||||
// selected: Option<V>,
|
|
||||||
on_click: impl Fn(T) -> Message + 'a,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
title: title.into(),
|
title: title.to_string(),
|
||||||
on_select: Box::new(on_click),
|
on_select: Box::new(on_select),
|
||||||
on_open: None,
|
on_open: None,
|
||||||
on_close: None,
|
on_close: None,
|
||||||
options,
|
options,
|
||||||
placeholder: None,
|
placeholder: None,
|
||||||
// selected,
|
|
||||||
width: Length::Shrink,
|
width: Length::Shrink,
|
||||||
padding: Padding::new(0.1),
|
padding: button::DEFAULT_PADDING,
|
||||||
text_size: None,
|
text_size: None,
|
||||||
text_line_height: text::LineHeight::default(),
|
text_line_height: text::LineHeight::default(),
|
||||||
text_shaping: text::Shaping::default(),
|
text_shaping: text::Shaping::default(),
|
||||||
@@ -212,6 +80,8 @@ where
|
|||||||
handle: Handle::default(),
|
handle: Handle::default(),
|
||||||
class: <Theme as Catalog>::default(),
|
class: <Theme as Catalog>::default(),
|
||||||
menu_class: <Theme as Catalog>::default_menu(),
|
menu_class: <Theme as Catalog>::default_menu(),
|
||||||
|
last_status: None,
|
||||||
|
menu_height: Length::Shrink,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,6 +97,12 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the height of the [`Menu`].
|
||||||
|
pub fn menu_height(mut self, menu_height: impl Into<Length>) -> Self {
|
||||||
|
self.menu_height = menu_height.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the [`Padding`] of the [`PickList`].
|
/// Sets the [`Padding`] of the [`PickList`].
|
||||||
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
pub fn padding<P: Into<Padding>>(mut self, padding: P) -> Self {
|
||||||
self.padding = padding.into();
|
self.padding = padding.into();
|
||||||
@@ -294,14 +170,27 @@ where
|
|||||||
self.menu_class = (Box::new(style) as menu::StyleFn<'a, Theme>).into();
|
self.menu_class = (Box::new(style) as menu::StyleFn<'a, Theme>).into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the style class of the [`PickList`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn class(mut self, class: impl Into<<Theme as Catalog>::Class<'a>>) -> Self {
|
||||||
|
self.class = class.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the style class of the [`Menu`].
|
||||||
|
#[must_use]
|
||||||
|
pub fn menu_class(mut self, class: impl Into<<Theme as menu::Catalog>::Class<'a>>) -> Self {
|
||||||
|
self.menu_class = class.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T, L, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
impl<'a, T, L, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||||
for HeaderMenu<'a, T, L, Message, Theme, Renderer>
|
for HeaderMenu<'a, T, L, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
T: Clone + ToString + PartialEq + 'a,
|
T: Clone + ToString + 'a,
|
||||||
L: Borrow<[T]>,
|
L: Borrow<[T]>,
|
||||||
// V: Borrow<T>,
|
|
||||||
Message: Clone + 'a,
|
Message: Clone + 'a,
|
||||||
Theme: Catalog + 'a,
|
Theme: Catalog + 'a,
|
||||||
Renderer: text::Renderer + 'a,
|
Renderer: text::Renderer + 'a,
|
||||||
@@ -322,7 +211,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
@@ -344,8 +233,8 @@ where
|
|||||||
size: text_size,
|
size: text_size,
|
||||||
line_height: self.text_line_height,
|
line_height: self.text_line_height,
|
||||||
font,
|
font,
|
||||||
horizontal_alignment: alignment::Horizontal::Left,
|
align_x: text::Alignment::Default,
|
||||||
vertical_alignment: alignment::Vertical::Center,
|
align_y: alignment::Vertical::Center,
|
||||||
shaping: self.text_shaping,
|
shaping: self.text_shaping,
|
||||||
wrapping: text::Wrapping::default(),
|
wrapping: text::Wrapping::default(),
|
||||||
};
|
};
|
||||||
@@ -353,14 +242,14 @@ where
|
|||||||
for (option, paragraph) in options.iter().zip(state.options.iter_mut()) {
|
for (option, paragraph) in options.iter().zip(state.options.iter_mut()) {
|
||||||
let label = option.to_string();
|
let label = option.to_string();
|
||||||
|
|
||||||
paragraph.update(Text {
|
let _ = paragraph.update(Text {
|
||||||
content: &label,
|
content: &label,
|
||||||
..option_text
|
..option_text
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(placeholder) = &self.placeholder {
|
if let Some(placeholder) = &self.placeholder {
|
||||||
state.placeholder.update(Text {
|
let _ = state.placeholder.update(Text {
|
||||||
content: placeholder,
|
content: placeholder,
|
||||||
..option_text
|
..option_text
|
||||||
});
|
});
|
||||||
@@ -398,22 +287,22 @@ where
|
|||||||
layout::Node::new(size)
|
layout::Node::new(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_event(
|
fn update(
|
||||||
&mut self,
|
&mut self,
|
||||||
tree: &mut Tree,
|
tree: &mut Tree,
|
||||||
event: Event,
|
event: &Event,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
cursor: mouse::Cursor,
|
||||||
_renderer: &Renderer,
|
_renderer: &Renderer,
|
||||||
_clipboard: &mut dyn Clipboard,
|
_clipboard: &mut dyn Clipboard,
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
_viewport: &Rectangle,
|
_viewport: &Rectangle,
|
||||||
) -> event::Status {
|
) {
|
||||||
|
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
|
||||||
|
|
||||||
if state.is_open {
|
if state.is_open {
|
||||||
// Event wasn't processed by overlay, so cursor was clicked either outside its
|
// Event wasn't processed by overlay, so cursor was clicked either outside its
|
||||||
// bounds or on the drop-down, either way we close the overlay.
|
// bounds or on the drop-down, either way we close the overlay.
|
||||||
@@ -423,58 +312,30 @@ where
|
|||||||
shell.publish(on_close.clone());
|
shell.publish(on_close.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
event::Status::Captured
|
shell.capture_event();
|
||||||
} else if cursor.is_over(layout.bounds()) {
|
} else if cursor.is_over(layout.bounds()) {
|
||||||
// let selected = self.selected.as_ref().map(Borrow::borrow);
|
|
||||||
|
|
||||||
state.is_open = true;
|
state.is_open = true;
|
||||||
// state.hovered_option = self
|
|
||||||
// .options
|
|
||||||
// .borrow()
|
|
||||||
// .iter()
|
|
||||||
// .position(|option| Some(option) == selected);
|
|
||||||
|
|
||||||
if let Some(on_open) = &self.on_open {
|
if let Some(on_open) = &self.on_open {
|
||||||
shell.publish(on_open.clone());
|
shell.publish(on_open.clone());
|
||||||
}
|
}
|
||||||
event::Status::Captured
|
|
||||||
} else {
|
shell.capture_event();
|
||||||
event::Status::Ignored
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Mouse(mouse::Event::WheelScrolled {
|
Event::Mouse(mouse::Event::WheelScrolled {
|
||||||
delta: mouse::ScrollDelta::Lines { y, .. },
|
delta: mouse::ScrollDelta::Lines { y, .. },
|
||||||
}) => {
|
}) => {
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
|
||||||
|
|
||||||
if state.keyboard_modifiers.command()
|
if state.keyboard_modifiers.command()
|
||||||
&& cursor.is_over(layout.bounds())
|
&& cursor.is_over(layout.bounds())
|
||||||
&& !state.is_open
|
&& !state.is_open
|
||||||
{
|
{
|
||||||
fn find_next<'a, T: PartialEq>(
|
|
||||||
selected: &'a T,
|
|
||||||
mut options: impl Iterator<Item = &'a T>,
|
|
||||||
) -> Option<&'a T> {
|
|
||||||
let _ = options.find(|&option| option == selected);
|
|
||||||
|
|
||||||
options.next()
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = self.options.borrow();
|
let options = self.options.borrow();
|
||||||
let selected = None; // self.selected.as_ref().map(Borrow::borrow);
|
|
||||||
|
|
||||||
let next_option = if y < 0.0 {
|
let next_option = if *y < 0.0 {
|
||||||
if let Some(selected) = selected {
|
|
||||||
find_next(selected, options.iter())
|
|
||||||
} else {
|
|
||||||
options.first()
|
options.first()
|
||||||
}
|
} else if *y > 0.0 {
|
||||||
} else if y > 0.0 {
|
|
||||||
if let Some(selected) = selected {
|
|
||||||
find_next(selected, options.iter().rev())
|
|
||||||
} else {
|
|
||||||
options.last()
|
options.last()
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -483,19 +344,34 @@ where
|
|||||||
shell.publish((self.on_select)(next_option.clone()));
|
shell.publish((self.on_select)(next_option.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
event::Status::Captured
|
shell.capture_event();
|
||||||
} else {
|
|
||||||
event::Status::Ignored
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
|
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
state.keyboard_modifiers = *modifiers;
|
||||||
|
|
||||||
state.keyboard_modifiers = modifiers;
|
|
||||||
|
|
||||||
event::Status::Ignored
|
|
||||||
}
|
}
|
||||||
_ => event::Status::Ignored,
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = {
|
||||||
|
let is_hovered = cursor.is_over(layout.bounds());
|
||||||
|
|
||||||
|
if state.is_open {
|
||||||
|
Status::Opened { is_hovered }
|
||||||
|
} else if is_hovered {
|
||||||
|
Status::Hovered
|
||||||
|
} else {
|
||||||
|
Status::Active
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Event::Window(window::Event::RedrawRequested(_now)) = event {
|
||||||
|
self.last_status = Some(status);
|
||||||
|
} else if self
|
||||||
|
.last_status
|
||||||
|
.is_some_and(|last_status| last_status != status)
|
||||||
|
{
|
||||||
|
shell.request_redraw();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,26 +400,19 @@ where
|
|||||||
theme: &Theme,
|
theme: &Theme,
|
||||||
_style: &renderer::Style,
|
_style: &renderer::Style,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
cursor: mouse::Cursor,
|
_cursor: mouse::Cursor,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
let font = self.font.unwrap_or_else(|| renderer.default_font());
|
let font = self.font.unwrap_or_else(|| renderer.default_font());
|
||||||
// let selected = None; // self.selected.as_ref().map(Borrow::borrow);
|
|
||||||
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
let state = tree.state.downcast_ref::<State<Renderer::Paragraph>>();
|
||||||
|
|
||||||
let bounds = layout.bounds();
|
let bounds = layout.bounds();
|
||||||
let is_mouse_over = cursor.is_over(bounds);
|
|
||||||
// let is_selected = selected.is_some();
|
|
||||||
|
|
||||||
let status = if state.is_open {
|
let style = Catalog::style(
|
||||||
Status::Opened
|
theme,
|
||||||
} else if is_mouse_over {
|
&self.class,
|
||||||
Status::Hovered
|
self.last_status.unwrap_or(Status::Active),
|
||||||
} else {
|
);
|
||||||
Status::Active
|
|
||||||
};
|
|
||||||
|
|
||||||
let style = Catalog::style(theme, &self.class, status);
|
|
||||||
|
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
@@ -601,8 +470,8 @@ where
|
|||||||
line_height,
|
line_height,
|
||||||
font,
|
font,
|
||||||
bounds: Size::new(bounds.width, f32::from(line_height.to_absolute(size))),
|
bounds: Size::new(bounds.width, f32::from(line_height.to_absolute(size))),
|
||||||
horizontal_alignment: alignment::Horizontal::Right,
|
align_x: text::Alignment::Right,
|
||||||
vertical_alignment: alignment::Vertical::Center,
|
align_y: alignment::Vertical::Center,
|
||||||
shaping,
|
shaping,
|
||||||
wrapping: text::Wrapping::default(),
|
wrapping: text::Wrapping::default(),
|
||||||
},
|
},
|
||||||
@@ -610,13 +479,11 @@ where
|
|||||||
bounds.x + bounds.width - self.padding.right,
|
bounds.x + bounds.width - self.padding.right,
|
||||||
bounds.center_y(),
|
bounds.center_y(),
|
||||||
),
|
),
|
||||||
style.text_color,
|
style.handle_color,
|
||||||
*viewport,
|
*viewport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// let label = None; // selected.map(ToString::to_string);
|
|
||||||
|
|
||||||
let text_size = self.text_size.unwrap_or_else(|| renderer.default_size());
|
let text_size = self.text_size.unwrap_or_else(|| renderer.default_size());
|
||||||
|
|
||||||
renderer.fill_text(
|
renderer.fill_text(
|
||||||
@@ -626,20 +493,16 @@ where
|
|||||||
line_height: self.text_line_height,
|
line_height: self.text_line_height,
|
||||||
font,
|
font,
|
||||||
bounds: Size::new(
|
bounds: Size::new(
|
||||||
bounds.width - self.padding.horizontal(),
|
bounds.width - self.padding.x(),
|
||||||
f32::from(self.text_line_height.to_absolute(text_size)),
|
f32::from(self.text_line_height.to_absolute(text_size)),
|
||||||
),
|
),
|
||||||
horizontal_alignment: alignment::Horizontal::Left,
|
align_x: text::Alignment::Default,
|
||||||
vertical_alignment: alignment::Vertical::Center,
|
align_y: alignment::Vertical::Center,
|
||||||
shaping: self.text_shaping,
|
shaping: self.text_shaping,
|
||||||
wrapping: text::Wrapping::default(),
|
wrapping: text::Wrapping::default(),
|
||||||
},
|
},
|
||||||
Point::new(bounds.x + self.padding.left, bounds.center_y()),
|
Point::new(bounds.x + self.padding.left, bounds.center_y()),
|
||||||
// if is_selected {
|
style.text_color,
|
||||||
// style.text_color
|
|
||||||
// } else {
|
|
||||||
style.placeholder_color,
|
|
||||||
// },
|
|
||||||
*viewport,
|
*viewport,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -649,6 +512,7 @@ where
|
|||||||
tree: &'b mut Tree,
|
tree: &'b mut Tree,
|
||||||
layout: Layout<'_>,
|
layout: Layout<'_>,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
|
viewport: &Rectangle,
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
let state = tree.state.downcast_mut::<State<Renderer::Paragraph>>();
|
||||||
@@ -680,7 +544,12 @@ where
|
|||||||
menu = menu.text_size(text_size);
|
menu = menu.text_size(text_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(menu.overlay(layout.position() + translation, bounds.height))
|
Some(menu.overlay(
|
||||||
|
layout.position() + translation,
|
||||||
|
*viewport,
|
||||||
|
bounds.height,
|
||||||
|
self.menu_height,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -690,9 +559,8 @@ where
|
|||||||
impl<'a, T, L, Message, Theme, Renderer> From<HeaderMenu<'a, T, L, Message, Theme, Renderer>>
|
impl<'a, T, L, Message, Theme, Renderer> From<HeaderMenu<'a, T, L, Message, Theme, Renderer>>
|
||||||
for Element<'a, Message, Theme, Renderer>
|
for Element<'a, Message, Theme, Renderer>
|
||||||
where
|
where
|
||||||
T: Clone + ToString + PartialEq + 'a,
|
T: Clone + ToString + 'a,
|
||||||
L: Borrow<[T]> + 'a,
|
L: Borrow<[T]> + 'a,
|
||||||
// V: Borrow<T> + 'a,
|
|
||||||
Message: Clone + 'a,
|
Message: Clone + 'a,
|
||||||
Theme: Catalog + 'a,
|
Theme: Catalog + 'a,
|
||||||
Renderer: text::Renderer + 'a,
|
Renderer: text::Renderer + 'a,
|
||||||
@@ -784,11 +652,14 @@ pub enum Status {
|
|||||||
/// The [`PickList`] is being hovered.
|
/// The [`PickList`] is being hovered.
|
||||||
Hovered,
|
Hovered,
|
||||||
/// The [`PickList`] is open.
|
/// The [`PickList`] is open.
|
||||||
Opened,
|
Opened {
|
||||||
|
/// Whether the [`PickList`] is hovered, while open.
|
||||||
|
is_hovered: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The appearance of a pick list.
|
/// The appearance of a pick list.
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct Style {
|
pub struct Style {
|
||||||
/// The text [`Color`] of the pick list.
|
/// The text [`Color`] of the pick list.
|
||||||
pub text_color: Color,
|
pub text_color: Color,
|
||||||
@@ -843,7 +714,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
|
|||||||
let active = Style {
|
let active = Style {
|
||||||
text_color: palette.background.weak.text,
|
text_color: palette.background.weak.text,
|
||||||
background: palette.background.weak.color.into(),
|
background: palette.background.weak.color.into(),
|
||||||
placeholder_color: palette.background.strong.color,
|
placeholder_color: palette.secondary.base.color,
|
||||||
handle_color: palette.background.weak.text,
|
handle_color: palette.background.weak.text,
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: 2.0.into(),
|
radius: 2.0.into(),
|
||||||
@@ -854,7 +725,7 @@ pub fn default(theme: &Theme, status: Status) -> Style {
|
|||||||
|
|
||||||
match status {
|
match status {
|
||||||
Status::Active => active,
|
Status::Active => active,
|
||||||
Status::Hovered | Status::Opened => Style {
|
Status::Hovered | Status::Opened { .. } => Style {
|
||||||
border: Border {
|
border: Border {
|
||||||
color: palette.primary.strong.color,
|
color: palette.primary.strong.color,
|
||||||
..active.border
|
..active.border
|
||||||
@@ -863,82 +734,3 @@ pub fn default(theme: &Theme, status: Status) -> Style {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`PickList`].
|
|
||||||
///
|
|
||||||
/// Pick lists display a dropdown list of selectable options.
|
|
||||||
///
|
|
||||||
/// # Example
|
|
||||||
/// ```no_run
|
|
||||||
/// # mod iced { pub mod widget { pub use iced_widget::*; } pub use iced_widget::Renderer; pub use iced_widget::core::*; }
|
|
||||||
/// # pub type Element<'a, Message> = iced_widget::core::Element<'a, Message, iced_widget::Theme, iced_widget::Renderer>;
|
|
||||||
/// #
|
|
||||||
/// use iced::widget::pick_list;
|
|
||||||
///
|
|
||||||
/// struct State {
|
|
||||||
/// favorite: Option<Fruit>,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
||||||
/// enum Fruit {
|
|
||||||
/// Apple,
|
|
||||||
/// Orange,
|
|
||||||
/// Strawberry,
|
|
||||||
/// Tomato,
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[derive(Debug, Clone)]
|
|
||||||
/// enum Message {
|
|
||||||
/// FruitSelected(Fruit),
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn view(state: &State) -> Element<'_, Message> {
|
|
||||||
/// let fruits = [
|
|
||||||
/// Fruit::Apple,
|
|
||||||
/// Fruit::Orange,
|
|
||||||
/// Fruit::Strawberry,
|
|
||||||
/// Fruit::Tomato,
|
|
||||||
/// ];
|
|
||||||
///
|
|
||||||
/// pick_list(
|
|
||||||
/// fruits,
|
|
||||||
/// state.favorite,
|
|
||||||
/// Message::FruitSelected,
|
|
||||||
/// )
|
|
||||||
/// .placeholder("Select your favorite fruit...")
|
|
||||||
/// .into()
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// fn update(state: &mut State, message: Message) {
|
|
||||||
/// match message {
|
|
||||||
/// Message::FruitSelected(fruit) => {
|
|
||||||
/// state.favorite = Some(fruit);
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// impl std::fmt::Display for Fruit {
|
|
||||||
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
/// f.write_str(match self {
|
|
||||||
/// Self::Apple => "Apple",
|
|
||||||
/// Self::Orange => "Orange",
|
|
||||||
/// Self::Strawberry => "Strawberry",
|
|
||||||
/// Self::Tomato => "Tomato",
|
|
||||||
/// })
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn header_menu<'a, T, L, Message, Theme, Renderer>(
|
|
||||||
title: impl Into<String>,
|
|
||||||
options: L,
|
|
||||||
on_selected: impl Fn(T) -> Message + 'a,
|
|
||||||
) -> HeaderMenu<'a, T, L, Message, Theme, Renderer>
|
|
||||||
where
|
|
||||||
T: ToString + PartialEq + Clone + 'a,
|
|
||||||
L: Borrow<[T]> + 'a,
|
|
||||||
Message: Clone,
|
|
||||||
Theme: Catalog + overlay::menu::Catalog,
|
|
||||||
Renderer: text::Renderer,
|
|
||||||
{
|
|
||||||
HeaderMenu::new(title, options, on_selected)
|
|
||||||
}
|
|
||||||
|
|||||||
524
src/lib.rs
524
src/lib.rs
@@ -1,17 +1,17 @@
|
|||||||
mod apu;
|
mod apu;
|
||||||
mod controllers;
|
mod controllers;
|
||||||
pub mod debug;
|
pub mod debug;
|
||||||
|
pub mod debugger;
|
||||||
pub mod header_menu;
|
pub mod header_menu;
|
||||||
pub mod hex_view;
|
pub mod hex_view;
|
||||||
mod mem;
|
mod mem;
|
||||||
mod ppu;
|
mod ppu;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_roms;
|
mod test_roms;
|
||||||
|
|
||||||
pub use ppu::{Color, PPU, RenderBuffer};
|
pub use ppu::{Color, PPU, RenderBuffer};
|
||||||
|
|
||||||
use std::{fmt::{Arguments, Write as _}, fs::File, io::Read, path::Path};
|
use std::{fmt::Write as _, fs::File, io::Read, path::Path};
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, info};
|
use tracing::{debug, info};
|
||||||
@@ -115,14 +115,14 @@ impl std::fmt::Debug for NES {
|
|||||||
|
|
||||||
bitfield::bitfield! {
|
bitfield::bitfield! {
|
||||||
pub struct CpuStatus(u8);
|
pub struct CpuStatus(u8);
|
||||||
carry, set_carry: 0;
|
pub carry, set_carry: 0;
|
||||||
zero, set_zero: 1;
|
pub zero, set_zero: 1;
|
||||||
interrupt_disable, set_interrupt_disable: 2;
|
pub interrupt_disable, set_interrupt_disable: 2;
|
||||||
decimal, set_decimal: 3;
|
pub decimal, set_decimal: 3;
|
||||||
brk, set_brk: 4;
|
pub brk, set_brk: 4;
|
||||||
php, set_php: 5;
|
pub php, set_php: 5;
|
||||||
overflow, set_overflow: 6;
|
pub overflow, set_overflow: 6;
|
||||||
negative, set_negative: 7;
|
pub negative, set_negative: 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Debug for CpuStatus {
|
impl std::fmt::Debug for CpuStatus {
|
||||||
@@ -151,16 +151,16 @@ impl std::fmt::Debug for CpuStatus {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cpu {
|
pub struct Cpu {
|
||||||
a: u8,
|
pub a: u8,
|
||||||
x: u8,
|
pub x: u8,
|
||||||
y: u8,
|
pub y: u8,
|
||||||
pc: u16,
|
pub pc: u16,
|
||||||
sp: u8,
|
pub sp: u8,
|
||||||
status: CpuStatus,
|
pub status: CpuStatus,
|
||||||
clock_state: ClockState,
|
pub clock_state: ClockState,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ClockState {
|
pub enum ClockState {
|
||||||
ReadInstruction,
|
ReadInstruction,
|
||||||
ReadOperands {
|
ReadOperands {
|
||||||
instruction: u8,
|
instruction: u8,
|
||||||
@@ -499,22 +499,38 @@ impl NES {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
0x48 => inst!("PHA", 2, || {
|
0x48 => inst!("PHA", 2, || {
|
||||||
log!("{addr:04X}: PHA | {:02X} -> 01{:02X}", self.cpu.a, self.cpu.sp);
|
log!(
|
||||||
|
"{addr:04X}: PHA | {:02X} -> 01{:02X}",
|
||||||
|
self.cpu.a,
|
||||||
|
self.cpu.sp
|
||||||
|
);
|
||||||
self.push(self.cpu.a);
|
self.push(self.cpu.a);
|
||||||
}),
|
}),
|
||||||
0x08 => inst!("PHP", 2, || {
|
0x08 => inst!("PHP", 2, || {
|
||||||
log!("{addr:04X}: PHP | {:02X} -> 01{:02X}", self.cpu.status.0, self.cpu.sp);
|
log!(
|
||||||
|
"{addr:04X}: PHP | {:02X} -> 01{:02X}",
|
||||||
|
self.cpu.status.0,
|
||||||
|
self.cpu.sp
|
||||||
|
);
|
||||||
self.push(self.cpu.status.0 | 0b0011_0000);
|
self.push(self.cpu.status.0 | 0b0011_0000);
|
||||||
}),
|
}),
|
||||||
0x68 => inst!("PLA", 3, || {
|
0x68 => inst!("PLA", 3, || {
|
||||||
self.cpu.a = self.pop();
|
self.cpu.a = self.pop();
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: PLA | {:02X} <- 01{:02X}", self.cpu.a, self.cpu.sp);
|
log!(
|
||||||
|
"{addr:04X}: PLA | {:02X} <- 01{:02X}",
|
||||||
|
self.cpu.a,
|
||||||
|
self.cpu.sp
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x28 => inst!("PLP", 3, || {
|
0x28 => inst!("PLP", 3, || {
|
||||||
self.cpu.status.0 = self.pop() & 0b1100_1111;
|
self.cpu.status.0 = self.pop() & 0b1100_1111;
|
||||||
log!("{addr:04X}: PLP | {:02X} <- 01{:02X}", self.cpu.status.0, self.cpu.sp);
|
log!(
|
||||||
|
"{addr:04X}: PLP | {:02X} <- 01{:02X}",
|
||||||
|
self.cpu.status.0,
|
||||||
|
self.cpu.sp
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Loads
|
// Loads
|
||||||
@@ -540,19 +556,34 @@ impl NES {
|
|||||||
self.cpu.a = self.read_abs(low, high);
|
self.cpu.a = self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: LDA ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: LDA ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xBD => inst!("LDA abs,x", 1, |low, high| {
|
0xBD => inst!("LDA abs,x", 1, |low, high| {
|
||||||
self.cpu.a = self.read_abs_x(low, high);
|
self.cpu.a = self.read_abs_x(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: LDA ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: LDA ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xB9 => inst!("LDA abs,y", 1, |low, high| {
|
0xB9 => inst!("LDA abs,y", 1, |low, high| {
|
||||||
self.cpu.a = self.read_abs_y(low, high);
|
self.cpu.a = self.read_abs_y(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: LDA ${:02X}{:02X},y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: LDA ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xA1 => inst!("LDA (ind,x)", 4, |off| {
|
0xA1 => inst!("LDA (ind,x)", 4, |off| {
|
||||||
let low = self.read_zp_x(off);
|
let low = self.read_zp_x(off);
|
||||||
@@ -592,13 +623,23 @@ impl NES {
|
|||||||
self.cpu.x = self.read_abs(low, high);
|
self.cpu.x = self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.x == 0);
|
self.cpu.status.set_zero(self.cpu.x == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: LDX ${:02X}{:02X} | {:02X}", high, low, self.cpu.x);
|
log!(
|
||||||
|
"{addr:04X}: LDX ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.x
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xBE => inst!("LDX abs,y", 1, |low, high| {
|
0xBE => inst!("LDX abs,y", 1, |low, high| {
|
||||||
self.cpu.x = self.read_abs_y(low, high);
|
self.cpu.x = self.read_abs_y(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.x == 0);
|
self.cpu.status.set_zero(self.cpu.x == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: LDX ${:02X}{:02X},y | {:02X}", high, low, self.cpu.x);
|
log!(
|
||||||
|
"{addr:04X}: LDX ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.x
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xA0 => inst!("LDY imm", 0, |y| {
|
0xA0 => inst!("LDY imm", 0, |y| {
|
||||||
self.cpu.y = y;
|
self.cpu.y = y;
|
||||||
@@ -622,13 +663,23 @@ impl NES {
|
|||||||
self.cpu.y = self.read_abs(low, high);
|
self.cpu.y = self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.y == 0);
|
self.cpu.status.set_zero(self.cpu.y == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: LDY ${:02X}{:02X} | {:02X}", high, low, self.cpu.y);
|
log!(
|
||||||
|
"{addr:04X}: LDY ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.y
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xBC => inst!("LDX abs,x", 1, |low, high| {
|
0xBC => inst!("LDX abs,x", 1, |low, high| {
|
||||||
self.cpu.y = self.read_abs_x(low, high);
|
self.cpu.y = self.read_abs_x(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.y == 0);
|
self.cpu.status.set_zero(self.cpu.y == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: LDY ${:02X}{:02X},x | {:02X}", high, low, self.cpu.y);
|
log!(
|
||||||
|
"{addr:04X}: LDY ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.y
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Stores
|
// Stores
|
||||||
@@ -642,15 +693,30 @@ impl NES {
|
|||||||
}),
|
}),
|
||||||
0x8D => inst!("STA abs", 1, |low, high| {
|
0x8D => inst!("STA abs", 1, |low, high| {
|
||||||
self.write_abs(low, high, self.cpu.a);
|
self.write_abs(low, high, self.cpu.a);
|
||||||
log!("{addr:04X}: STA ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: STA ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x9D => inst!("STA abs,x", 1, |low, high| {
|
0x9D => inst!("STA abs,x", 1, |low, high| {
|
||||||
self.write_abs_x(low, high, self.cpu.a);
|
self.write_abs_x(low, high, self.cpu.a);
|
||||||
log!("{addr:04X}: STA ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: STA ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x99 => inst!("STA abs,y", 1, |low, high| {
|
0x99 => inst!("STA abs,y", 1, |low, high| {
|
||||||
self.write_abs_y(low, high, self.cpu.a);
|
self.write_abs_y(low, high, self.cpu.a);
|
||||||
log!("{addr:04X}: STA ${:02X}{:02X},y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: STA ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x81 => inst!("STA (ind,x)", 4, |off| {
|
0x81 => inst!("STA (ind,x)", 4, |off| {
|
||||||
let low = self.read_zp_x(off);
|
let low = self.read_zp_x(off);
|
||||||
@@ -677,7 +743,12 @@ impl NES {
|
|||||||
}),
|
}),
|
||||||
0x8E => inst!("STX abs", 1, |low, high| {
|
0x8E => inst!("STX abs", 1, |low, high| {
|
||||||
self.write_abs(low, high, self.cpu.x);
|
self.write_abs(low, high, self.cpu.x);
|
||||||
log!("{addr:04X}: STX ${:02X}{:02X} | {:02X}", high, low, self.cpu.x);
|
log!(
|
||||||
|
"{addr:04X}: STX ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.x
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x84 => inst!("STY zp", 1, |off| {
|
0x84 => inst!("STY zp", 1, |off| {
|
||||||
self.write(off as u16, self.cpu.y);
|
self.write(off as u16, self.cpu.y);
|
||||||
@@ -689,7 +760,12 @@ impl NES {
|
|||||||
}),
|
}),
|
||||||
0x8C => inst!("STY abs", 1, |low, high| {
|
0x8C => inst!("STY abs", 1, |low, high| {
|
||||||
self.write_abs(low, high, self.cpu.y);
|
self.write_abs(low, high, self.cpu.y);
|
||||||
log!("{addr:04X}: STY ${:02X}{:02X} | {:02X}", high, low, self.cpu.y);
|
log!(
|
||||||
|
"{addr:04X}: STY ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.y
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Transfers
|
// Transfers
|
||||||
@@ -804,7 +880,13 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.a >= val);
|
self.cpu.status.set_carry(self.cpu.a >= val);
|
||||||
log!("{addr:04X}: CMP #${:02X} | {:02X} - {:02X} -> {:?}", val, self.cpu.a, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CMP #${:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
val,
|
||||||
|
self.cpu.a,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xC5 => inst!("CMP zp", 1, |off| {
|
0xC5 => inst!("CMP zp", 1, |off| {
|
||||||
let val = self.read_abs(off, 0);
|
let val = self.read_abs(off, 0);
|
||||||
@@ -812,7 +894,13 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.a >= val);
|
self.cpu.status.set_carry(self.cpu.a >= val);
|
||||||
log!("{addr:04X}: CMP ${:02X} | {:02X} - {:02X} -> {:?}", off, self.cpu.a, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CMP ${:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
off,
|
||||||
|
self.cpu.a,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xD5 => inst!("CMP zp,x", 2, |off| {
|
0xD5 => inst!("CMP zp,x", 2, |off| {
|
||||||
let val = self.read_zp_x(off);
|
let val = self.read_zp_x(off);
|
||||||
@@ -820,7 +908,13 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.a >= val);
|
self.cpu.status.set_carry(self.cpu.a >= val);
|
||||||
log!("{addr:04X}: CMP ${:02X},x | {:02X} - {:02X} -> {:?}", off, self.cpu.a, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CMP ${:02X},x | {:02X} - {:02X} -> {:?}",
|
||||||
|
off,
|
||||||
|
self.cpu.a,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xCD => inst!("CMP abs", 1, |low, high| {
|
0xCD => inst!("CMP abs", 1, |low, high| {
|
||||||
let val = self.read_abs(low, high);
|
let val = self.read_abs(low, high);
|
||||||
@@ -828,7 +922,14 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.a >= val);
|
self.cpu.status.set_carry(self.cpu.a >= val);
|
||||||
log!("{addr:04X}: CMP ${:02X}{:02X} | {:02X} - {:02X} -> {:?}", high, low, self.cpu.a, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CMP ${:02X}{:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xDD => inst!("CMP abs,x", 1, |low, high| {
|
0xDD => inst!("CMP abs,x", 1, |low, high| {
|
||||||
let val = self.read_abs_x(low, high);
|
let val = self.read_abs_x(low, high);
|
||||||
@@ -836,7 +937,14 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.a >= val);
|
self.cpu.status.set_carry(self.cpu.a >= val);
|
||||||
log!("{addr:04X}: CMP ${:02X}{:02X},x | {:02X} - {:02X} -> {:?}", high, low, self.cpu.a, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CMP ${:02X}{:02X},x | {:02X} - {:02X} -> {:?}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xD9 => inst!("CMP abs,y", 1, |low, high| {
|
0xD9 => inst!("CMP abs,y", 1, |low, high| {
|
||||||
let val = self.read_abs_y(low, high);
|
let val = self.read_abs_y(low, high);
|
||||||
@@ -844,14 +952,27 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.a >= val);
|
self.cpu.status.set_carry(self.cpu.a >= val);
|
||||||
log!("{addr:04X}: CMP ${:02X}{:02X},y | {:02X} - {:02X} -> {:?}", high, low, self.cpu.a, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CMP ${:02X}{:02X},y | {:02X} - {:02X} -> {:?}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xE0 => inst!("CPX imm", 0, |val| {
|
0xE0 => inst!("CPX imm", 0, |val| {
|
||||||
let v = self.cpu.x.wrapping_sub(val);
|
let v = self.cpu.x.wrapping_sub(val);
|
||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.x >= val);
|
self.cpu.status.set_carry(self.cpu.x >= val);
|
||||||
log!("{addr:04X}: CPX #${:02X} | {:02X} - {:02X} -> {:?}", val, self.cpu.x, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CPX #${:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
val,
|
||||||
|
self.cpu.x,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xE4 => inst!("CPX zp", 1, |off| {
|
0xE4 => inst!("CPX zp", 1, |off| {
|
||||||
let val = self.read_abs(off, 0);
|
let val = self.read_abs(off, 0);
|
||||||
@@ -859,7 +980,13 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.x >= val);
|
self.cpu.status.set_carry(self.cpu.x >= val);
|
||||||
log!("{addr:04X}: CPX ${:02X} | {:02X} - {:02X} -> {:?}", off, self.cpu.x, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CPX ${:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
off,
|
||||||
|
self.cpu.x,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xEC => inst!("CPX abs", 1, |low, high| {
|
0xEC => inst!("CPX abs", 1, |low, high| {
|
||||||
let val = self.read_abs(low, high);
|
let val = self.read_abs(low, high);
|
||||||
@@ -867,14 +994,27 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.x >= val);
|
self.cpu.status.set_carry(self.cpu.x >= val);
|
||||||
log!("{addr:04X}: CPX ${:02X}{:02X} | {:02X} - {:02X} -> {:?}", high, low, self.cpu.x, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CPX ${:02X}{:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.x,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xC0 => inst!("CPY imm", 0, |val| {
|
0xC0 => inst!("CPY imm", 0, |val| {
|
||||||
let v = self.cpu.y.wrapping_sub(val);
|
let v = self.cpu.y.wrapping_sub(val);
|
||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.y >= val);
|
self.cpu.status.set_carry(self.cpu.y >= val);
|
||||||
log!("{addr:04X}: CPY #${:02X} | {:02X} - {:02X} -> {:?}", val, self.cpu.y, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CPY #${:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
val,
|
||||||
|
self.cpu.y,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xC4 => inst!("CPY zp", 1, |off| {
|
0xC4 => inst!("CPY zp", 1, |off| {
|
||||||
let val = self.read_abs(off, 0);
|
let val = self.read_abs(off, 0);
|
||||||
@@ -882,7 +1022,13 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.y >= val);
|
self.cpu.status.set_carry(self.cpu.y >= val);
|
||||||
log!("{addr:04X}: CPY ${:02X} | {:02X} - {:02X} -> {:?}", off, self.cpu.y, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CPY ${:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
off,
|
||||||
|
self.cpu.y,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xCC => inst!("CPY zp", 1, |low, high| {
|
0xCC => inst!("CPY zp", 1, |low, high| {
|
||||||
let val = self.read_abs(low, high);
|
let val = self.read_abs(low, high);
|
||||||
@@ -890,7 +1036,14 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(v == 0);
|
self.cpu.status.set_zero(v == 0);
|
||||||
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
self.cpu.status.set_negative(v & 0x80 == 0x80);
|
||||||
self.cpu.status.set_carry(self.cpu.y >= val);
|
self.cpu.status.set_carry(self.cpu.y >= val);
|
||||||
log!("{addr:04X}: CPY ${:02X}{:02X} | {:02X} - {:02X} -> {:?}", high, low, self.cpu.y, val, self.cpu.status);
|
log!(
|
||||||
|
"{addr:04X}: CPY ${:02X}{:02X} | {:02X} - {:02X} -> {:?}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.y,
|
||||||
|
val,
|
||||||
|
self.cpu.status
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Arithmetic
|
// Arithmetic
|
||||||
@@ -943,7 +1096,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ADC ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ADC ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x7D => inst!("ADC abs,x", 0, |low, high| {
|
0x7D => inst!("ADC abs,x", 0, |low, high| {
|
||||||
let val = self.read_abs_x(low, high);
|
let val = self.read_abs_x(low, high);
|
||||||
@@ -956,7 +1114,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ADC ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ADC ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x79 => inst!("ADC abs,y", 0, |low, high| {
|
0x79 => inst!("ADC abs,y", 0, |low, high| {
|
||||||
let val = self.read_abs_y(low, high);
|
let val = self.read_abs_y(low, high);
|
||||||
@@ -969,7 +1132,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ADC ${:02X}{:02X},y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ADC ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x61 => inst!("ADC (ind,x)", 0, |off| {
|
0x61 => inst!("ADC (ind,x)", 0, |off| {
|
||||||
let low = self.read_abs(off, 0);
|
let low = self.read_abs(off, 0);
|
||||||
@@ -984,7 +1152,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ADC (${:02X}{:02X},x) | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ADC (${:02X}{:02X},x) | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x71 => inst!("ADC (ind),y", 0, |off| {
|
0x71 => inst!("ADC (ind),y", 0, |off| {
|
||||||
let low = self.read_abs(off, 0);
|
let low = self.read_abs(off, 0);
|
||||||
@@ -999,7 +1172,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ADC (${:02X}{:02X}),y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ADC (${:02X}{:02X}),y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xE9 => inst!("SBC imm", 0, |val| {
|
0xE9 => inst!("SBC imm", 0, |val| {
|
||||||
let (a, carry_1) = self.cpu.a.overflowing_add(!val);
|
let (a, carry_1) = self.cpu.a.overflowing_add(!val);
|
||||||
@@ -1050,7 +1228,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: SBC ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: SBC ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xFD => inst!("SBC abs,x", 0, |low, high| {
|
0xFD => inst!("SBC abs,x", 0, |low, high| {
|
||||||
let val = self.read_abs_x(low, high);
|
let val = self.read_abs_x(low, high);
|
||||||
@@ -1063,7 +1246,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: SBC ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: SBC ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xF9 => inst!("SBC abs,y", 0, |low, high| {
|
0xF9 => inst!("SBC abs,y", 0, |low, high| {
|
||||||
let val = self.read_abs_y(low, high);
|
let val = self.read_abs_y(low, high);
|
||||||
@@ -1076,7 +1264,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: SBC ${:02X}{:02X},y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: SBC ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xE1 => inst!("SBC (ind,x)", 0, |off| {
|
0xE1 => inst!("SBC (ind,x)", 0, |off| {
|
||||||
let low = self.read_abs(off, 0);
|
let low = self.read_abs(off, 0);
|
||||||
@@ -1091,7 +1284,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: SBC (${:02X}{:02X},x) | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: SBC (${:02X}{:02X},x) | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xF1 => inst!("SBC (ind),y", 0, |off| {
|
0xF1 => inst!("SBC (ind),y", 0, |off| {
|
||||||
let low = self.read_abs(off, 0);
|
let low = self.read_abs(off, 0);
|
||||||
@@ -1106,7 +1304,12 @@ impl NES {
|
|||||||
self.cpu.status.set_carry(carry_1 | carry_2);
|
self.cpu.status.set_carry(carry_1 | carry_2);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: SBC (${:02X}{:02X}),y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: SBC (${:02X}{:02X}),y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xC6 => inst!("DEC zp", 3, |off| {
|
0xC6 => inst!("DEC zp", 3, |off| {
|
||||||
let val = self.read_abs(off, 0).wrapping_sub(1);
|
let val = self.read_abs(off, 0).wrapping_sub(1);
|
||||||
@@ -1127,14 +1330,24 @@ impl NES {
|
|||||||
self.write_abs(low, high, val);
|
self.write_abs(low, high, val);
|
||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: DEC ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: DEC ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xDE => inst!("DEC abs,x", 3, |low, high| {
|
0xDE => inst!("DEC abs,x", 3, |low, high| {
|
||||||
let val = self.read_abs_x(low, high).wrapping_sub(1);
|
let val = self.read_abs_x(low, high).wrapping_sub(1);
|
||||||
self.write_abs_x(low, high, val);
|
self.write_abs_x(low, high, val);
|
||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: DEC ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: DEC ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xCA => inst!("DEX", 1, || {
|
0xCA => inst!("DEX", 1, || {
|
||||||
self.cpu.x = self.cpu.x.wrapping_sub(1);
|
self.cpu.x = self.cpu.x.wrapping_sub(1);
|
||||||
@@ -1167,14 +1380,24 @@ impl NES {
|
|||||||
self.write_abs(low, high, val);
|
self.write_abs(low, high, val);
|
||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: INC ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: INC ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xFE => inst!("INC abs,x", 3, |low, high| {
|
0xFE => inst!("INC abs,x", 3, |low, high| {
|
||||||
let val = self.read_abs_x(low, high).wrapping_add(1);
|
let val = self.read_abs_x(low, high).wrapping_add(1);
|
||||||
self.write_abs_x(low, high, val);
|
self.write_abs_x(low, high, val);
|
||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: INC ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: INC ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0xE8 => inst!("INX", 1, || {
|
0xE8 => inst!("INX", 1, || {
|
||||||
self.cpu.x = self.cpu.x.wrapping_add(1);
|
self.cpu.x = self.cpu.x.wrapping_add(1);
|
||||||
@@ -1212,19 +1435,34 @@ impl NES {
|
|||||||
self.cpu.a |= self.read_abs(low, high);
|
self.cpu.a |= self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ORA ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ORA ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x1D => inst!("ORA abs,x", 2, |low, high| {
|
0x1D => inst!("ORA abs,x", 2, |low, high| {
|
||||||
self.cpu.a |= self.read_abs_x(low, high);
|
self.cpu.a |= self.read_abs_x(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ORA ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ORA ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x19 => inst!("ORA abs,y", 1, |low, high| {
|
0x19 => inst!("ORA abs,y", 1, |low, high| {
|
||||||
self.cpu.a |= self.read_abs_y(low, high);
|
self.cpu.a |= self.read_abs_y(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ORA ${:02X}{:02X},y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ORA ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x01 => inst!("ORA (ind,x)", 4, |off| {
|
0x01 => inst!("ORA (ind,x)", 4, |off| {
|
||||||
let low = self.read_zp_x(off);
|
let low = self.read_zp_x(off);
|
||||||
@@ -1232,7 +1470,12 @@ impl NES {
|
|||||||
self.cpu.a |= self.read_abs(low, high);
|
self.cpu.a |= self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ORA (${:02X}{:02X},x) | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ORA (${:02X}{:02X},x) | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x11 => inst!("ORA (ind),y", 4, |off| {
|
0x11 => inst!("ORA (ind),y", 4, |off| {
|
||||||
let low = self.read_abs(off, 0);
|
let low = self.read_abs(off, 0);
|
||||||
@@ -1240,7 +1483,12 @@ impl NES {
|
|||||||
self.cpu.a |= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
self.cpu.a |= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: ORA (${:02X}{:02X}),y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ORA (${:02X}{:02X}),y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x29 => inst!("AND imm", 0, |val| {
|
0x29 => inst!("AND imm", 0, |val| {
|
||||||
self.cpu.a &= val;
|
self.cpu.a &= val;
|
||||||
@@ -1264,19 +1512,34 @@ impl NES {
|
|||||||
self.cpu.a &= self.read_abs(low, high);
|
self.cpu.a &= self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: AND ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: AND ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x3D => inst!("AND abs,x", 2, |low, high| {
|
0x3D => inst!("AND abs,x", 2, |low, high| {
|
||||||
self.cpu.a &= self.read_abs_x(low, high);
|
self.cpu.a &= self.read_abs_x(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: AND ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: AND ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x39 => inst!("AND abs,y", 1, |low, high| {
|
0x39 => inst!("AND abs,y", 1, |low, high| {
|
||||||
self.cpu.a &= self.read_abs_y(low, high);
|
self.cpu.a &= self.read_abs_y(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: AND ${:02X}{:02X},y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: AND ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x21 => inst!("AND (ind,x)", 4, |off| {
|
0x21 => inst!("AND (ind,x)", 4, |off| {
|
||||||
let low = self.read_zp_x(off);
|
let low = self.read_zp_x(off);
|
||||||
@@ -1284,7 +1547,12 @@ impl NES {
|
|||||||
self.cpu.a &= self.read_abs(low, high);
|
self.cpu.a &= self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: AND (${:02X}{:02X},x) | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: AND (${:02X}{:02X},x) | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x31 => inst!("AND (ind),y", 4, |off| {
|
0x31 => inst!("AND (ind),y", 4, |off| {
|
||||||
let low = self.read_abs(off, 0);
|
let low = self.read_abs(off, 0);
|
||||||
@@ -1292,7 +1560,12 @@ impl NES {
|
|||||||
self.cpu.a &= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
self.cpu.a &= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: AND (${:02X}{:02X}),y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: AND (${:02X}{:02X}),y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x49 => inst!("EOR imm", 0, |val| {
|
0x49 => inst!("EOR imm", 0, |val| {
|
||||||
self.cpu.a ^= val;
|
self.cpu.a ^= val;
|
||||||
@@ -1316,19 +1589,34 @@ impl NES {
|
|||||||
self.cpu.a ^= self.read_abs(low, high);
|
self.cpu.a ^= self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: EOR ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: EOR ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x5D => inst!("EOR abs,x", 1, |low, high| {
|
0x5D => inst!("EOR abs,x", 1, |low, high| {
|
||||||
self.cpu.a ^= self.read_abs_x(low, high);
|
self.cpu.a ^= self.read_abs_x(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: EOR ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: EOR ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x59 => inst!("EOR abs,y", 1, |low, high| {
|
0x59 => inst!("EOR abs,y", 1, |low, high| {
|
||||||
self.cpu.a ^= self.read_abs_y(low, high);
|
self.cpu.a ^= self.read_abs_y(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: EOR ${:02X}{:02X},y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: EOR ${:02X}{:02X},y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x41 => inst!("EOR (ind,x)", 4, |off| {
|
0x41 => inst!("EOR (ind,x)", 4, |off| {
|
||||||
let low = self.read_zp_x(off);
|
let low = self.read_zp_x(off);
|
||||||
@@ -1336,7 +1624,12 @@ impl NES {
|
|||||||
self.cpu.a ^= self.read_abs(low, high);
|
self.cpu.a ^= self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: EOR (${:02X}{:02X},x) | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: EOR (${:02X}{:02X},x) | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x51 => inst!("EOR (ind),y", 3, |off| {
|
0x51 => inst!("EOR (ind),y", 3, |off| {
|
||||||
let low = self.read_abs(off, 0);
|
let low = self.read_abs(off, 0);
|
||||||
@@ -1344,21 +1637,31 @@ impl NES {
|
|||||||
self.cpu.a ^= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
self.cpu.a ^= self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
||||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: EOR (${:02X}{:02X}),y | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: EOR (${:02X}{:02X}),y | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x24 => inst!("BIT zp", 1, |off| {
|
0x24 => inst!("BIT zp", 1, |off| {
|
||||||
let val = self.cpu.a & self.read_abs(off, 0);
|
let val = self.read_abs(off, 0);
|
||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_overflow(val & 0x40 == 0x40);
|
self.cpu.status.set_overflow(val & 0x40 == 0x40);
|
||||||
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: BIT ${:02X} | {:02X}", off, self.cpu.a);
|
log!("{addr:04X}: BIT ${:02X} | {:02X}", off, val);
|
||||||
}),
|
}),
|
||||||
0x2C => inst!("BIT abs", 1, |low, high| {
|
0x2C => inst!("BIT abs", 1, |low, high| {
|
||||||
let val = self.cpu.a & self.read_abs(low, high);
|
let val = self.read_abs(low, high);
|
||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_overflow(val & 0x40 == 0x40);
|
self.cpu.status.set_overflow(val & 0x40 == 0x40);
|
||||||
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
self.cpu.status.set_negative(val & 0x80 == 0x80);
|
||||||
log!("{addr:04X}: BIT ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: BIT ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
val
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
// Shifts
|
// Shifts
|
||||||
@@ -1394,7 +1697,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs(low, high, val);
|
self.write_abs(low, high, val);
|
||||||
log!("{addr:04X}: LSR ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: LSR ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x5E => inst!("LSR abs,x", 4, |low, high| {
|
0x5E => inst!("LSR abs,x", 4, |low, high| {
|
||||||
let val = self.read_abs_x(low, high);
|
let val = self.read_abs_x(low, high);
|
||||||
@@ -1403,7 +1711,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs_x(low, high, val);
|
self.write_abs_x(low, high, val);
|
||||||
log!("{addr:04X}: LSR ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: LSR ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x0A => inst!("ASL A", 1, || {
|
0x0A => inst!("ASL A", 1, || {
|
||||||
self.cpu.status.set_carry(self.cpu.a & 0x80 == 0x80);
|
self.cpu.status.set_carry(self.cpu.a & 0x80 == 0x80);
|
||||||
@@ -1437,7 +1750,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs(low, high, val);
|
self.write_abs(low, high, val);
|
||||||
log!("{addr:04X}: ASL ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ASL ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x1E => inst!("ASL abs,x", 4, |low, high| {
|
0x1E => inst!("ASL abs,x", 4, |low, high| {
|
||||||
let val = self.read_abs_x(low, high);
|
let val = self.read_abs_x(low, high);
|
||||||
@@ -1446,7 +1764,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs_x(low, high, val);
|
self.write_abs_x(low, high, val);
|
||||||
log!("{addr:04X}: ASL ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ASL ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x6A => inst!("ROR A", 1, || {
|
0x6A => inst!("ROR A", 1, || {
|
||||||
let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 };
|
let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 };
|
||||||
@@ -1484,7 +1807,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs(low, high, val);
|
self.write_abs(low, high, val);
|
||||||
log!("{addr:04X}: ROR ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ROR ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x7E => inst!("ROR abs,x", 4, |low, high| {
|
0x7E => inst!("ROR abs,x", 4, |low, high| {
|
||||||
let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 };
|
let old_carry = if self.cpu.status.carry() { 0x80 } else { 0x00 };
|
||||||
@@ -1494,7 +1822,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs_x(low, high, val);
|
self.write_abs_x(low, high, val);
|
||||||
log!("{addr:04X}: ROR ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ROR ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x2A => inst!("ROL A", 1, || {
|
0x2A => inst!("ROL A", 1, || {
|
||||||
let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 };
|
let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 };
|
||||||
@@ -1532,7 +1865,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs(low, high, val);
|
self.write_abs(low, high, val);
|
||||||
log!("{addr:04X}: ROL ${:02X}{:02X} | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ROL ${:02X}{:02X} | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
0x3E => inst!("ROL abs,x", 4, |low, high| {
|
0x3E => inst!("ROL abs,x", 4, |low, high| {
|
||||||
let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 };
|
let old_carry = if self.cpu.status.carry() { 0x01 } else { 0x00 };
|
||||||
@@ -1542,7 +1880,12 @@ impl NES {
|
|||||||
self.cpu.status.set_zero(val == 0);
|
self.cpu.status.set_zero(val == 0);
|
||||||
self.cpu.status.set_negative(false);
|
self.cpu.status.set_negative(false);
|
||||||
self.write_abs_x(low, high, val);
|
self.write_abs_x(low, high, val);
|
||||||
log!("{addr:04X}: ROL ${:02X}{:02X},x | {:02X}", high, low, self.cpu.a);
|
log!(
|
||||||
|
"{addr:04X}: ROL ${:02X}{:02X},x | {:02X}",
|
||||||
|
high,
|
||||||
|
low,
|
||||||
|
self.cpu.a
|
||||||
|
);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
0xEA => inst!("NOP", 1, || {
|
0xEA => inst!("NOP", 1, || {
|
||||||
@@ -1556,6 +1899,13 @@ impl NES {
|
|||||||
self.peek(self.cpu.pc)
|
self.peek(self.cpu.pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn peek_nmi(&self) -> bool {
|
||||||
|
self.ppu.peek_nmi() || self.apu.peek_nmi()
|
||||||
|
}
|
||||||
|
pub fn peek_irq(&self) -> bool {
|
||||||
|
self.ppu.peek_irq() || self.apu.peek_irq()
|
||||||
|
}
|
||||||
|
|
||||||
fn clock_cpu(&mut self) {
|
fn clock_cpu(&mut self) {
|
||||||
self.cpu.clock_state = match self.cpu.clock_state {
|
self.cpu.clock_state = match self.cpu.clock_state {
|
||||||
ClockState::HoldNmi { cycles } => {
|
ClockState::HoldNmi { cycles } => {
|
||||||
|
|||||||
66
src/main.rs
66
src/main.rs
@@ -1,15 +1,12 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
fmt,
|
fmt,
|
||||||
io::{Read, stdin},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use iced::{
|
use iced::{
|
||||||
Color, Element, Font,
|
Color, Element, Font,
|
||||||
Length::Fill,
|
Length::Fill,
|
||||||
Point, Renderer, Size, Subscription, Task, Theme, Vector,
|
Point, Renderer, Size, Subscription, Task, Theme, mouse,
|
||||||
futures::io::Window,
|
|
||||||
mouse,
|
|
||||||
widget::{
|
widget::{
|
||||||
Canvas, button,
|
Canvas, button,
|
||||||
canvas::{Frame, Program},
|
canvas::{Frame, Program},
|
||||||
@@ -18,13 +15,13 @@ use iced::{
|
|||||||
window::{self, Id, Settings},
|
window::{self, Id, Settings},
|
||||||
};
|
};
|
||||||
use nes_emu::{
|
use nes_emu::{
|
||||||
NES, PPU, RenderBuffer,
|
debugger::{DebuggerMessage, DebuggerState}, header_menu::header_menu, hex_view::{HexEvent, HexView}, NES, PPU
|
||||||
header_menu::header_menu,
|
|
||||||
hex_view::{self, HexEvent, HexView},
|
|
||||||
};
|
};
|
||||||
use tracing::{debug, info};
|
|
||||||
use tracing_subscriber::EnvFilter;
|
use tracing_subscriber::EnvFilter;
|
||||||
|
|
||||||
|
const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "basic_init_1.nes");
|
||||||
|
// const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
||||||
|
|
||||||
extern crate nes_emu;
|
extern crate nes_emu;
|
||||||
|
|
||||||
fn main() -> Result<(), iced::Error> {
|
fn main() -> Result<(), iced::Error> {
|
||||||
@@ -32,16 +29,11 @@ fn main() -> Result<(), iced::Error> {
|
|||||||
.with_env_filter(EnvFilter::from_default_env())
|
.with_env_filter(EnvFilter::from_default_env())
|
||||||
.init();
|
.init();
|
||||||
|
|
||||||
iced::daemon(Emulator::title, Emulator::update, Emulator::view)
|
iced::daemon(Emulator::new, Emulator::update, Emulator::view)
|
||||||
.subscription(Emulator::subscriptions)
|
.subscription(Emulator::subscriptions)
|
||||||
.theme(|_, _| Theme::Dark)
|
.theme(Theme::Dark)
|
||||||
.run_with(Emulator::new)
|
.title(Emulator::title)
|
||||||
|
.run()
|
||||||
// iced::application(title, Emulator::update, Emulator::view)
|
|
||||||
// .subscription(Emulator::subscriptions)
|
|
||||||
// .theme(|_| Theme::Dark)
|
|
||||||
// .centered()
|
|
||||||
// .run()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MemoryTy {
|
enum MemoryTy {
|
||||||
@@ -53,6 +45,7 @@ enum WindowType {
|
|||||||
Memory(MemoryTy, HexView),
|
Memory(MemoryTy, HexView),
|
||||||
TileMap,
|
TileMap,
|
||||||
TileViewer,
|
TileViewer,
|
||||||
|
Debugger,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
@@ -60,6 +53,7 @@ enum HeaderButton {
|
|||||||
OpenMemory,
|
OpenMemory,
|
||||||
OpenTileMap,
|
OpenTileMap,
|
||||||
OpenTileViewer,
|
OpenTileViewer,
|
||||||
|
OpenDebugger,
|
||||||
Reset,
|
Reset,
|
||||||
PowerCycle,
|
PowerCycle,
|
||||||
}
|
}
|
||||||
@@ -70,6 +64,7 @@ impl fmt::Display for HeaderButton {
|
|||||||
Self::OpenMemory => write!(f, "Open Memory Viewer"),
|
Self::OpenMemory => write!(f, "Open Memory Viewer"),
|
||||||
Self::OpenTileMap => write!(f, "Open TileMap Viewer"),
|
Self::OpenTileMap => write!(f, "Open TileMap Viewer"),
|
||||||
Self::OpenTileViewer => write!(f, "Open Tile Viewer"),
|
Self::OpenTileViewer => write!(f, "Open Tile Viewer"),
|
||||||
|
Self::OpenDebugger => write!(f, "Open Debugger"),
|
||||||
Self::Reset => write!(f, "Reset"),
|
Self::Reset => write!(f, "Reset"),
|
||||||
Self::PowerCycle => write!(f, "Power Cycle"),
|
Self::PowerCycle => write!(f, "Power Cycle"),
|
||||||
}
|
}
|
||||||
@@ -79,6 +74,7 @@ impl fmt::Display for HeaderButton {
|
|||||||
struct Emulator {
|
struct Emulator {
|
||||||
nes: NES,
|
nes: NES,
|
||||||
windows: HashMap<Id, WindowType>,
|
windows: HashMap<Id, WindowType>,
|
||||||
|
debugger: DebuggerState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -92,13 +88,12 @@ enum Message {
|
|||||||
WindowClosed(Id),
|
WindowClosed(Id),
|
||||||
Header(HeaderButton),
|
Header(HeaderButton),
|
||||||
Hex(Id, HexEvent),
|
Hex(Id, HexEvent),
|
||||||
|
Debugger(DebuggerMessage),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Emulator {
|
impl Emulator {
|
||||||
fn new() -> (Self, Task<Message>) {
|
fn new() -> (Self, Task<Message>) {
|
||||||
let rom_file = concat!(env!("ROM_DIR"), "/", "basic_init_1.nes");
|
let mut nes = nes_emu::NES::load_nes_file(ROM_FILE)
|
||||||
// let rom_file = "./Super Mario Bros. (World).nes";
|
|
||||||
let mut nes = nes_emu::NES::load_nes_file(rom_file)
|
|
||||||
.expect("Failed to load nes file");
|
.expect("Failed to load nes file");
|
||||||
nes.reset();
|
nes.reset();
|
||||||
let (win, task) = iced::window::open(Settings::default());
|
let (win, task) = iced::window::open(Settings::default());
|
||||||
@@ -106,6 +101,7 @@ impl Emulator {
|
|||||||
Self {
|
Self {
|
||||||
nes,
|
nes,
|
||||||
windows: HashMap::from_iter([(win, WindowType::Main)]),
|
windows: HashMap::from_iter([(win, WindowType::Main)]),
|
||||||
|
debugger: DebuggerState::new(),
|
||||||
},
|
},
|
||||||
task.map(Message::WindowOpened),
|
task.map(Message::WindowOpened),
|
||||||
)
|
)
|
||||||
@@ -116,6 +112,7 @@ impl Emulator {
|
|||||||
Some(WindowType::Memory(_, _)) => "NES MemoryView".into(),
|
Some(WindowType::Memory(_, _)) => "NES MemoryView".into(),
|
||||||
Some(WindowType::TileMap) => "NES TileMap".into(),
|
Some(WindowType::TileMap) => "NES TileMap".into(),
|
||||||
Some(WindowType::TileViewer) => "NES Tile Viewer".into(),
|
Some(WindowType::TileViewer) => "NES Tile Viewer".into(),
|
||||||
|
Some(WindowType::Debugger) => "NES Debugger".into(),
|
||||||
None => todo!(),
|
None => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +134,7 @@ impl Emulator {
|
|||||||
Message::DMA => while !self.nes.run_one_clock_cycle().dma {},
|
Message::DMA => while !self.nes.run_one_clock_cycle().dma {},
|
||||||
Message::CPU => while !self.nes.run_one_clock_cycle().cpu_exec {},
|
Message::CPU => while !self.nes.run_one_clock_cycle().cpu_exec {},
|
||||||
Message::DebugInt => while !self.nes.run_one_clock_cycle().dbg_int {},
|
Message::DebugInt => while !self.nes.run_one_clock_cycle().dbg_int {},
|
||||||
Message::WindowOpened(id) => {
|
Message::WindowOpened(_id) => {
|
||||||
// Window
|
// Window
|
||||||
}
|
}
|
||||||
Message::WindowClosed(id) => {
|
Message::WindowClosed(id) => {
|
||||||
@@ -154,6 +151,9 @@ impl Emulator {
|
|||||||
Message::Header(HeaderButton::OpenTileViewer) => {
|
Message::Header(HeaderButton::OpenTileViewer) => {
|
||||||
return self.open(WindowType::TileViewer);
|
return self.open(WindowType::TileViewer);
|
||||||
}
|
}
|
||||||
|
Message::Header(HeaderButton::OpenDebugger) => {
|
||||||
|
return self.open(WindowType::Debugger);
|
||||||
|
}
|
||||||
Message::Hex(id, val) => {
|
Message::Hex(id, val) => {
|
||||||
if let Some(WindowType::Memory(_, view)) = self.windows.get_mut(&id) {
|
if let Some(WindowType::Memory(_, view)) = self.windows.get_mut(&id) {
|
||||||
return view.update(val).map(move |e| Message::Hex(id, e));
|
return view.update(val).map(move |e| Message::Hex(id, e));
|
||||||
@@ -161,6 +161,7 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
Message::Header(HeaderButton::Reset) => { self.nes.reset(); }
|
Message::Header(HeaderButton::Reset) => { self.nes.reset(); }
|
||||||
Message::Header(HeaderButton::PowerCycle) => { self.nes.power_cycle(); }
|
Message::Header(HeaderButton::PowerCycle) => { self.nes.power_cycle(); }
|
||||||
|
Message::Debugger(debugger_message) => self.debugger.update(debugger_message, &mut self.nes),
|
||||||
}
|
}
|
||||||
// self.image.0.clone_from(self.nes.image());
|
// self.image.0.clone_from(self.nes.image());
|
||||||
Task::none()
|
Task::none()
|
||||||
@@ -197,7 +198,11 @@ impl Emulator {
|
|||||||
Some(WindowType::TileViewer) => {
|
Some(WindowType::TileViewer) => {
|
||||||
container(Canvas::new(DbgImage::NameTable(self.nes.ppu()))).width(Fill).height(Fill).into()
|
container(Canvas::new(DbgImage::NameTable(self.nes.ppu()))).width(Fill).height(Fill).into()
|
||||||
}
|
}
|
||||||
_ => todo!(),
|
Some(WindowType::Debugger) => {
|
||||||
|
container(self.debugger.view(&self.nes).map(Message::Debugger)).width(Fill).height(Fill).into()
|
||||||
|
}
|
||||||
|
None => panic!("Window not found"),
|
||||||
|
// _ => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,6 +240,7 @@ impl Emulator {
|
|||||||
header_menu(
|
header_menu(
|
||||||
"Debugging",
|
"Debugging",
|
||||||
[
|
[
|
||||||
|
HeaderButton::OpenDebugger,
|
||||||
HeaderButton::OpenMemory,
|
HeaderButton::OpenMemory,
|
||||||
HeaderButton::OpenTileMap,
|
HeaderButton::OpenTileMap,
|
||||||
HeaderButton::OpenTileViewer,
|
HeaderButton::OpenTileViewer,
|
||||||
@@ -252,13 +258,13 @@ impl Program<Message> for Emulator {
|
|||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
_state: &Self::State,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: iced::Rectangle,
|
_bounds: iced::Rectangle,
|
||||||
_cursor: mouse::Cursor,
|
_cursor: mouse::Cursor,
|
||||||
) -> Vec<iced::widget::canvas::Geometry<Renderer>> {
|
) -> Vec<iced::widget::canvas::Geometry<Renderer>> {
|
||||||
const SIZE: f32 = 2.;
|
// const SIZE: f32 = 2.;
|
||||||
let mut frame = Frame::new(
|
let mut frame = Frame::new(
|
||||||
renderer,
|
renderer,
|
||||||
iced::Size {
|
iced::Size {
|
||||||
@@ -291,13 +297,13 @@ impl Program<Message> for DbgImage<'_> {
|
|||||||
|
|
||||||
fn draw(
|
fn draw(
|
||||||
&self,
|
&self,
|
||||||
state: &Self::State,
|
_state: &Self::State,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
theme: &Theme,
|
_theme: &Theme,
|
||||||
bounds: iced::Rectangle,
|
_bounds: iced::Rectangle,
|
||||||
_cursor: mouse::Cursor,
|
_cursor: mouse::Cursor,
|
||||||
) -> Vec<iced::widget::canvas::Geometry<Renderer>> {
|
) -> Vec<iced::widget::canvas::Geometry<Renderer>> {
|
||||||
const SIZE: f32 = 2.;
|
// const SIZE: f32 = 2.;
|
||||||
let mut name_table_frame =
|
let mut name_table_frame =
|
||||||
Frame::new(renderer, Size::new(256. * 4. + 260. * 2., 256. * 4.));
|
Frame::new(renderer, Size::new(256. * 4. + 260. * 2., 256. * 4.));
|
||||||
name_table_frame.scale(2.);
|
name_table_frame.scale(2.);
|
||||||
|
|||||||
22
src/ppu.rs
22
src/ppu.rs
@@ -1,5 +1,4 @@
|
|||||||
use iced::{Point, Size, widget::canvas::Frame};
|
use iced::{advanced::graphics::geometry::Renderer, widget::canvas::{Fill, Frame}, Point, Size};
|
||||||
use iced_graphics::geometry::{Fill, Renderer};
|
|
||||||
|
|
||||||
use crate::{hex_view::Memory, mem::{MemoryMap, Segment}};
|
use crate::{hex_view::Memory, mem::{MemoryMap, Segment}};
|
||||||
|
|
||||||
@@ -81,7 +80,7 @@ pub struct Background {
|
|||||||
cur_shift_low: u8,
|
cur_shift_low: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Mask {
|
pub struct Mask {
|
||||||
grayscale: bool,
|
grayscale: bool,
|
||||||
background_on_left_edge: bool,
|
background_on_left_edge: bool,
|
||||||
sprites_on_left_edge: bool,
|
sprites_on_left_edge: bool,
|
||||||
@@ -97,12 +96,12 @@ pub struct PPU {
|
|||||||
frame_count: usize,
|
frame_count: usize,
|
||||||
nmi_enabled: bool,
|
nmi_enabled: bool,
|
||||||
nmi_waiting: bool,
|
nmi_waiting: bool,
|
||||||
even: bool,
|
pub even: bool,
|
||||||
scanline: usize,
|
pub scanline: usize,
|
||||||
pixel: usize,
|
pub pixel: usize,
|
||||||
|
|
||||||
mask: Mask,
|
pub mask: Mask,
|
||||||
vblank: bool,
|
pub vblank: bool,
|
||||||
|
|
||||||
pub(crate) memory: MemoryMap<PPUMMRegisters>,
|
pub(crate) memory: MemoryMap<PPUMMRegisters>,
|
||||||
background: Background,
|
background: Background,
|
||||||
@@ -191,6 +190,7 @@ impl PPU {
|
|||||||
let tmp = if self.vblank { 0b1000_0000 } else { 0 };
|
let tmp = if self.vblank { 0b1000_0000 } else { 0 };
|
||||||
self.vblank = false;
|
self.vblank = false;
|
||||||
self.background.w = false;
|
self.background.w = false;
|
||||||
|
println!("Reading status: {:02X}", tmp);
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
3 => panic!("oamaddr is write-only"),
|
3 => panic!("oamaddr is write-only"),
|
||||||
@@ -357,6 +357,9 @@ impl PPU {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn peek_nmi(&self) -> bool {
|
||||||
|
self.nmi_waiting
|
||||||
|
}
|
||||||
pub fn nmi_waiting(&mut self) -> bool {
|
pub fn nmi_waiting(&mut self) -> bool {
|
||||||
if self.nmi_waiting {
|
if self.nmi_waiting {
|
||||||
self.nmi_waiting = false;
|
self.nmi_waiting = false;
|
||||||
@@ -365,6 +368,9 @@ impl PPU {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn peek_irq(&self) -> bool {
|
||||||
|
self.nmi_waiting
|
||||||
|
}
|
||||||
pub fn irq_waiting(&mut self) -> bool {
|
pub fn irq_waiting(&mut self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,9 +54,10 @@ rom_test!(basic_init_0, "basic_init_0.nes", |nes| {
|
|||||||
});
|
});
|
||||||
|
|
||||||
rom_test!(basic_init_1, "basic_init_1.nes", |nes| {
|
rom_test!(basic_init_1, "basic_init_1.nes", |nes| {
|
||||||
assert_eq!(nes.last_instruction, "0x8017 HLT :2 []");
|
assert_eq!(nes.last_instruction, "0x801C HLT :2 []");
|
||||||
assert_eq!(nes.cycle, 41);
|
assert_eq!(nes.cycle, 27403);
|
||||||
assert_eq!(nes.cpu.pc, 0x8018);
|
assert_eq!(nes.ppu.pixel, 30);
|
||||||
|
assert_eq!(nes.cpu.pc, 0x801D);
|
||||||
assert_eq!(nes.cpu.sp, 0xFF);
|
assert_eq!(nes.cpu.sp, 0xFF);
|
||||||
assert_eq!(nes.cpu.a, 0x00);
|
assert_eq!(nes.cpu.a, 0x00);
|
||||||
assert_eq!(nes.cpu.x, 0x00);
|
assert_eq!(nes.cpu.x, 0x00);
|
||||||
|
|||||||
Reference in New Issue
Block a user