Compare commits

...

4 Commits

Author SHA1 Message Date
b5e1d1a4c3 Fix 'oops cycle' timing, to pass timing test rom
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 25s
2026-01-20 00:14:17 -06:00
2e5e2ed1e7 Fix most basic timing issues 2026-01-19 17:30:34 -06:00
42c3af28b4 Add run to address to debugger 2026-01-19 17:28:52 -06:00
ac745f60e9 Implement more features for PPU and APU 2026-01-19 17:28:12 -06:00
5 changed files with 376 additions and 102 deletions

View File

@@ -4,7 +4,7 @@ bitfield::bitfield! {
pub struct DutyVol(u8); pub struct DutyVol(u8);
impl Debug; impl Debug;
duty, set_duty: 7, 6; duty, set_duty: 7, 6;
r#loop, set_loop: 5; length_counter_halt, set_length_counter_halt: 5;
const_vol, set_const_vol: 4; const_vol, set_const_vol: 4;
volume, set_volume: 3, 0; volume, set_volume: 3, 0;
} }
@@ -27,6 +27,62 @@ bitfield::bitfield! {
struct PulseChannel { struct PulseChannel {
enabled: bool, enabled: bool,
duty_vol: DutyVol,
sweep: Sweep,
timer_low: u8,
length_timer_high: LengthTimerHigh,
cur_time: u16,
cur_length: u8,
}
impl PulseChannel {
pub fn new() -> Self {
Self {
enabled: false,
duty_vol: DutyVol(0),
sweep: Sweep(0),
timer_low: 0,
length_timer_high: LengthTimerHigh(0),
cur_time: 0,
cur_length: 0,
}
}
fn use_envelope(&self) -> bool {
!self.duty_vol.const_vol()
}
fn volume(&self) -> u8 {
self.duty_vol.volume()
}
fn timer(&self) -> u16 {
self.timer_low as u16 | ((self.length_timer_high.timer_high() as u16) << 8)
}
fn length(&self) -> u8 {
self.length_timer_high.length()
}
pub fn reset(&mut self) {
// TODO
self.cur_time = self.timer();
}
pub fn clock(&mut self) {
if self.cur_time == 0 {
self.cur_time = self.timer();
} else {
self.cur_time -= 1;
}
// TODO
}
pub fn q_frame_clock(&mut self) {
if !self.duty_vol.length_counter_halt() && self.cur_length > 0 {
self.cur_length -= 1;
}
}
pub fn h_frame_clock(&mut self) {
if !self.duty_vol.length_counter_halt() && self.cur_length > 0 {
self.cur_length -= 1;
}
}
} }
bitfield::bitfield! { bitfield::bitfield! {
@@ -46,6 +102,12 @@ struct DeltaChannel {
enabled: bool, enabled: bool,
} }
impl DeltaChannel {
pub fn int(&self) -> bool {
false
}
}
struct FrameCounter { struct FrameCounter {
count: usize, count: usize,
mode_5_step: bool, mode_5_step: bool,
@@ -76,8 +138,8 @@ impl std::fmt::Debug for APU {
impl APU { impl APU {
pub fn init() -> Self { pub fn init() -> Self {
Self { Self {
pulse_1: PulseChannel { enabled: false }, pulse_1: PulseChannel::new(),
pulse_2: PulseChannel { enabled: false }, pulse_2: PulseChannel::new(),
triangle: TriangleChannel { enabled: false }, triangle: TriangleChannel { enabled: false },
noise: NoiseChannel { enabled: false }, noise: NoiseChannel { enabled: false },
dmc: DeltaChannel { enabled: false }, dmc: DeltaChannel { enabled: false },
@@ -90,18 +152,38 @@ impl APU {
} }
} }
pub fn read_reg(&mut self, offset: u16) -> u8 { pub fn read_reg(&mut self, offset: u16) -> u8 {
// println!("APU read: {offset:02X}");
match offset { match offset {
0x15 => {
let val = (u8::from(self.dmc.int()) << 7)
| (u8::from(self.frame_counter.irq) << 6)
| (u8::from(self.dmc.enabled) << 4)
| (u8::from(self.noise.enabled) << 3)
| (u8::from(self.triangle.enabled) << 2)
| (u8::from(self.pulse_2.enabled) << 1)
| (u8::from(self.pulse_1.enabled) << 0);
self.frame_counter.irq = false;
val
}
_ => panic!("No register at {:X}", offset), _ => panic!("No register at {:X}", offset),
} }
} }
pub fn write_reg(&mut self, offset: u16, val: u8) { pub fn write_reg(&mut self, offset: u16, val: u8) {
// println!("APU write: {offset:02X} <= {val:02X}");
match offset { match offset {
0x15 => { 0x00 => self.pulse_1.duty_vol.0 = val,
self.dmc.enabled = val & 0b0001_0000 != 0; 0x01 => self.pulse_1.sweep.0 = val,
self.noise.enabled = val & 0b0000_1000 != 0; 0x02 => self.pulse_1.timer_low = val,
self.triangle.enabled = val & 0b0000_0100 != 0; 0x03 => {
self.pulse_2.enabled = val & 0b0000_0010 != 0; self.pulse_1.length_timer_high.0 = val;
self.pulse_1.enabled = val & 0b0000_0001 != 0; self.pulse_1.reset();
}
0x04 => self.pulse_2.duty_vol.0 = val,
0x05 => self.pulse_2.sweep.0 = val,
0x06 => self.pulse_2.timer_low = val,
0x07 => {
self.pulse_2.length_timer_high.0 = val;
self.pulse_2.reset();
} }
0x10 => { 0x10 => {
assert_eq!(val, 0x00); assert_eq!(val, 0x00);
@@ -110,27 +192,60 @@ impl APU {
0x11 => { 0x11 => {
// TODO: load dmc counter with (val & 7F) // TODO: load dmc counter with (val & 7F)
} }
_ => (), 0x15 => {
// _ => panic!("No register at {:X}", offset), self.dmc.enabled = val & 0b0001_0000 != 0;
self.noise.enabled = val & 0b0000_1000 != 0;
self.triangle.enabled = val & 0b0000_0100 != 0;
self.pulse_2.enabled = val & 0b0000_0010 != 0;
self.pulse_1.enabled = val & 0b0000_0001 != 0;
}
0x17 => {
self.frame_counter.mode_5_step = val & 0b1000_0000 == 0;
self.frame_counter.interrupt_enabled = val & 0b0100_0000 == 0;
if !self.frame_counter.interrupt_enabled {
self.frame_counter.irq = false;
}
}
// _ => (),
_ => panic!("No register at {:X}", offset),
} }
} }
fn q_frame_clock(&mut self) {
self.pulse_1.q_frame_clock();
self.pulse_2.q_frame_clock(); // TODO: clock all
}
fn h_frame_clock(&mut self) {
self.pulse_1.q_frame_clock();
self.pulse_1.h_frame_clock();
self.pulse_2.q_frame_clock();
self.pulse_2.h_frame_clock();
}
pub fn run_one_clock_cycle(&mut self, ppu_cycle: usize) -> bool { pub fn run_one_clock_cycle(&mut self, ppu_cycle: usize) -> bool {
if ppu_cycle % 6 == 1 { if ppu_cycle % 6 == 1 {
// APU Frame Counter clock cycle // APU Frame Counter clock cycle
if !self.frame_counter.mode_5_step if self.frame_counter.mode_5_step {
&& self.frame_counter.interrupt_enabled todo!()
&& self.frame_counter.count == 14914 } else {
{ if self.frame_counter.count == 3728 {
self.frame_counter.irq = true; self.q_frame_clock();
} else if !self.frame_counter.mode_5_step } else if self.frame_counter.count == 7456 {
&& self.frame_counter.interrupt_enabled self.h_frame_clock();
&& self.frame_counter.count == 14915 } else if self.frame_counter.count == 11185 {
{ self.q_frame_clock();
self.frame_counter.irq = true; } else if self.frame_counter.count == 14914 {
self.frame_counter.count = 0; self.frame_counter.irq = self.frame_counter.interrupt_enabled;
self.h_frame_clock();
} else if self.frame_counter.count == 14915 {
self.frame_counter.count = 0;
self.frame_counter.irq = self.frame_counter.interrupt_enabled;
}
} }
self.frame_counter.count += 1; self.frame_counter.count += 1;
self.pulse_1.clock();
self.pulse_2.clock();
} }
false false
} }
@@ -144,8 +259,6 @@ impl APU {
self.frame_counter.irq self.frame_counter.irq
} }
pub fn irq_waiting(&mut self) -> bool { pub fn irq_waiting(&mut self) -> bool {
let res = self.frame_counter.irq; self.frame_counter.irq
self.frame_counter.irq = false;
res
} }
} }

View File

@@ -1,13 +1,28 @@
use std::rc::Rc; use std::rc::Rc;
use iced::{ use iced::{
Element,
Length::{self, Fill},
Point, Renderer, Size, Theme,
advanced::{ advanced::{
layout::Node, widget::{ Widget,
tree::{State, Tag}, Tree layout::Node,
}, Widget widget::{
}, mouse, widget::{ Tree,
self, button, canvas::{Frame, Program}, checkbox, column, container::bordered_box, image, number_input, row, rule::horizontal, scrollable, text, Canvas, Text tree::{State, Tag},
}, window::Event, Element, Length::{self, Fill}, Point, Renderer, Size, Theme },
},
mouse,
widget::{
self, Canvas, Text, button,
canvas::{Frame, Program},
checkbox, column,
container::bordered_box,
image, number_input, hex_input, row,
rule::horizontal,
scrollable, text,
},
window::Event,
}; };
use crate::{CycleResult, NES, PPU}; use crate::{CycleResult, NES, PPU};
@@ -20,6 +35,7 @@ pub struct DebuggerState {
scan_lines: usize, scan_lines: usize,
to_scan_line: usize, to_scan_line: usize,
frames: usize, frames: usize,
breakpoint: usize,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@@ -38,6 +54,8 @@ pub enum DebuggerMessage {
RunToScanLine, RunToScanLine,
SetFrames(usize), SetFrames(usize),
RunFrames, RunFrames,
SetBreakpoint(usize),
RunBreakpoint,
} }
pub fn hex16<'a, Theme, Renderer>(val: u16) -> Text<'a, Theme, Renderer> pub fn hex16<'a, Theme, Renderer>(val: u16) -> Text<'a, Theme, Renderer>
@@ -78,6 +96,7 @@ impl DebuggerState {
scan_lines: 1, scan_lines: 1,
to_scan_line: 1, to_scan_line: 1,
frames: 1, frames: 1,
breakpoint: 0xEA5A,
// cpu_cycles: 1, // cpu_cycles: 1,
} }
} }
@@ -126,6 +145,7 @@ impl DebuggerState {
labelled("Cycle", text(nes.ppu().pixel)), labelled("Cycle", text(nes.ppu().pixel)),
labelled("Scanline", text(nes.ppu().scanline)), labelled("Scanline", text(nes.ppu().scanline)),
labelled("PPU Cycle", text(nes.ppu().cycle)), labelled("PPU Cycle", text(nes.ppu().cycle)),
labelled("Frame", text(nes.ppu().frame_count)),
labelled("V:", hex16(nes.ppu().background.v)), labelled("V:", hex16(nes.ppu().background.v)),
labelled("T:", hex16(nes.ppu().background.t)), labelled("T:", hex16(nes.ppu().background.t)),
labelled("X:", hex8(nes.ppu().background.x)), labelled("X:", hex8(nes.ppu().background.x)),
@@ -205,6 +225,12 @@ impl DebuggerState {
DebuggerMessage::SetFrames, DebuggerMessage::SetFrames,
DebuggerMessage::RunFrames DebuggerMessage::RunFrames
), ),
run_type_hex(
"To Address:",
self.breakpoint,
DebuggerMessage::SetBreakpoint,
DebuggerMessage::RunBreakpoint
),
], ],
] ]
.spacing(5.), .spacing(5.),
@@ -257,6 +283,7 @@ impl DebuggerState {
DebuggerMessage::SetScanLines(n) => self.scan_lines = n, DebuggerMessage::SetScanLines(n) => self.scan_lines = n,
DebuggerMessage::SetToScanLine(n) => self.to_scan_line = n, DebuggerMessage::SetToScanLine(n) => self.to_scan_line = n,
DebuggerMessage::SetFrames(n) => self.frames = n, DebuggerMessage::SetFrames(n) => self.frames = n,
DebuggerMessage::SetBreakpoint(n) => self.breakpoint = n,
DebuggerMessage::RunPPUCycles => Self::run_n_clock_cycles(nes, self.ppu_cycles), DebuggerMessage::RunPPUCycles => Self::run_n_clock_cycles(nes, self.ppu_cycles),
DebuggerMessage::RunCPUCycles => Self::run_n_clock_cycles(nes, self.cpu_cycles * 3), DebuggerMessage::RunCPUCycles => Self::run_n_clock_cycles(nes, self.cpu_cycles * 3),
DebuggerMessage::RunInstructions => { DebuggerMessage::RunInstructions => {
@@ -267,6 +294,7 @@ impl DebuggerState {
Self::run_until(nes, |_, n| n.ppu.scanline == self.to_scan_line, 1) Self::run_until(nes, |_, n| n.ppu.scanline == self.to_scan_line, 1)
} }
DebuggerMessage::RunFrames => Self::run_n_clock_cycles(nes, self.frames * 341 * 262), DebuggerMessage::RunFrames => Self::run_n_clock_cycles(nes, self.frames * 341 * 262),
DebuggerMessage::RunBreakpoint => Self::run_until(nes, |_, nes| nes.cpu.pc as usize == self.breakpoint, 1),
DebuggerMessage::Run => Self::run_until(nes, |_, nes| nes.halted, 1), DebuggerMessage::Run => Self::run_until(nes, |_, nes| nes.halted, 1),
DebuggerMessage::Pause => todo!(), DebuggerMessage::Pause => todo!(),
} }
@@ -288,6 +316,21 @@ fn run_type<'a, Message: Clone + 'a>(
.spacing(1.) .spacing(1.)
.into() .into()
} }
fn run_type_hex<'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(hex_input(val).on_input(update).on_submit(run.clone())).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>( pub fn labelled<'a, Message: 'a>(
label: &'a str, label: &'a str,

View File

@@ -182,6 +182,12 @@ pub enum ClockState {
count: u8, count: u8,
addr: u16, addr: u16,
}, },
Oops {
instruction: u8,
ops: [u8; 5],
count: u8,
addr: u16,
},
HoldNmi { HoldNmi {
cycles: u8, cycles: u8,
}, },
@@ -224,6 +230,17 @@ impl std::fmt::Debug for ClockState {
ClockState::HoldIrq { cycles } => { ClockState::HoldIrq { cycles } => {
f.debug_struct("HoldIrq").field("cycles", cycles).finish() f.debug_struct("HoldIrq").field("cycles", cycles).finish()
} }
ClockState::Oops {
instruction,
ops,
count,
addr,
} => f
.debug_struct("Oops")
.field("instruction", instruction)
.field("addr", addr)
.field("ops", &&ops[..*count as usize])
.finish(),
} }
} }
} }
@@ -232,6 +249,7 @@ enum ExecState {
Done, Done,
MoreParams, MoreParams,
Hold(u8), Hold(u8),
Oops,
} }
pub enum CPUMMRegisters { pub enum CPUMMRegisters {
@@ -313,8 +331,15 @@ impl NES {
Segment::mirror("Mirror of APU & IO", 0x4018, 0x0008, 2), Segment::mirror("Mirror of APU & IO", 0x4018, 0x0008, 2),
]; ];
// let mut cur = 0x4020; // let mut cur = 0x4020;
assert!(prg_rom.len() <= 0x8000, "Mappers for larger sizes not supported"); assert!(
segments.push(Segment::rom("PROG ROM", 0x8000 + (0x8000 - prg_rom.len() as u16), prg_rom)); prg_rom.len() <= 0x8000,
"Mappers for larger sizes not supported"
);
segments.push(Segment::rom(
"PROG ROM",
0x8000 + (0x8000 - prg_rom.len() as u16),
prg_rom,
));
Self { Self {
cycle: 7, cycle: 7,
dbg_int: false, dbg_int: false,
@@ -360,7 +385,6 @@ impl NES {
} }
} }
0x16 => self.controller.write_joy_strobe(val), 0x16 => self.controller.write_joy_strobe(val),
0x17 => (), // TODO: frame counter control
_ => self.apu.write_reg(offset, val), _ => self.apu.write_reg(offset, val),
}, },
}); });
@@ -430,7 +454,14 @@ impl NES {
} }
/// Returns true if more bytes are needed /// Returns true if more bytes are needed
fn exec_instruction(&mut self, ins: u8, mut params: &[u8], held: bool, addr: u16) -> ExecState { fn exec_instruction(
&mut self,
ins: u8,
mut params: &[u8],
held: bool,
oops: bool,
addr: u16,
) -> ExecState {
macro_rules! inst { macro_rules! inst {
($val:expr, $hold:expr, |$($name:pat_param),*| $eval:expr) => {{ ($val:expr, $hold:expr, |$($name:pat_param),*| $eval:expr) => {{
let hold_time: u8 = ($hold).into(); let hold_time: u8 = ($hold).into();
@@ -582,6 +613,7 @@ impl NES {
); );
}), }),
0xBD => inst!("LDA abs,x", 1, |low, high| { 0xBD => inst!("LDA abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
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);
@@ -593,6 +625,7 @@ impl NES {
); );
}), }),
0xB9 => inst!("LDA abs,y", 1, |low, high| { 0xB9 => inst!("LDA abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -614,6 +647,7 @@ impl NES {
0xB1 => inst!("LDA (ind),y", 3, |off| { 0xB1 => inst!("LDA (ind),y", 3, |off| {
let low = self.read_abs(off, 0); let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -631,7 +665,7 @@ impl NES {
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}", off, self.cpu.x); log!("{addr:04X}: LDX ${:02X} | {:02X}", off, self.cpu.x);
}), }),
0xB6 => inst!("LDX zp,y", 1, |off| { 0xB6 => inst!("LDX zp,y", 2, |off| {
self.cpu.x = self.read_zp_y(off); self.cpu.x = self.read_zp_y(off);
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);
@@ -649,6 +683,7 @@ impl NES {
); );
}), }),
0xBE => inst!("LDX abs,y", 1, |low, high| { 0xBE => inst!("LDX abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -671,7 +706,7 @@ impl NES {
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}", off, self.cpu.y); log!("{addr:04X}: LDY ${:02X} | {:02X}", off, self.cpu.y);
}), }),
0xB4 => inst!("LDX zp,x", 1, |off| { 0xB4 => inst!("LDX zp,x", 2, |off| {
self.cpu.y = self.read_zp_x(off); self.cpu.y = self.read_zp_x(off);
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);
@@ -689,6 +724,7 @@ impl NES {
); );
}), }),
0xBC => inst!("LDX abs,x", 1, |low, high| { 0xBC => inst!("LDX abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
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);
@@ -718,7 +754,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x9D => inst!("STA abs,x", 1, |low, high| { 0x9D => inst!("STA abs,x", 2, |low, high| {
self.write_abs_x(low, high, self.cpu.a); self.write_abs_x(low, high, self.cpu.a);
log!( log!(
"{addr:04X}: STA ${:02X}{:02X},x | {:02X}", "{addr:04X}: STA ${:02X}{:02X},x | {:02X}",
@@ -727,7 +763,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x99 => inst!("STA abs,y", 1, |low, high| { 0x99 => inst!("STA abs,y", 2, |low, high| {
self.write_abs_y(low, high, self.cpu.a); self.write_abs_y(low, high, self.cpu.a);
log!( log!(
"{addr:04X}: STA ${:02X}{:02X},y | {:02X}", "{addr:04X}: STA ${:02X}{:02X},y | {:02X}",
@@ -865,11 +901,11 @@ impl NES {
log!(" : - JMP ${:04X}", self.cpu.pc); log!(" : - JMP ${:04X}", self.cpu.pc);
} }
}), }),
0x4C => inst!("JMP abs", 3, |low, high| { 0x4C => inst!("JMP abs", 0, |low, high| {
self.cpu.pc = u16::from_le_bytes([low, high]); self.cpu.pc = u16::from_le_bytes([low, high]);
log!("{addr:04X}: JMP ${:04X}", self.cpu.pc); log!("{addr:04X}: JMP ${:04X}", self.cpu.pc);
}), }),
0x6C => inst!("JMP abs", 3, |low, high| { 0x6C => inst!("JMP abs", 2, |low, high| {
self.cpu.pc = u16::from_le_bytes([ self.cpu.pc = u16::from_le_bytes([
self.read_abs(low, high), self.read_abs(low, high),
self.read_abs(low.wrapping_add(1), high), // Known CPU bug self.read_abs(low.wrapping_add(1), high), // Known CPU bug
@@ -950,6 +986,7 @@ impl NES {
); );
}), }),
0xDD => inst!("CMP abs,x", 1, |low, high| { 0xDD => inst!("CMP abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
let val = self.read_abs_x(low, high); let val = self.read_abs_x(low, high);
let v = self.cpu.a.wrapping_sub(val); let v = self.cpu.a.wrapping_sub(val);
self.cpu.status.set_zero(v == 0); self.cpu.status.set_zero(v == 0);
@@ -965,6 +1002,42 @@ impl NES {
); );
}), }),
0xD9 => inst!("CMP abs,y", 1, |low, high| { 0xD9 => inst!("CMP abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
let val = self.read_abs_y(low, high);
let v = self.cpu.a.wrapping_sub(val);
self.cpu.status.set_zero(v == 0);
self.cpu.status.set_negative(v & 0x80 == 0x80);
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
);
}),
0xC1 => inst!("CMP (ind,x)", 4, |off| {
let low = self.read_zp_x(off);
let high = self.read_zp_x(off.wrapping_add(1));
let val = self.read_abs(low, high);
let v = self.cpu.a.wrapping_sub(val);
self.cpu.status.set_zero(v == 0);
self.cpu.status.set_negative(v & 0x80 == 0x80);
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
);
}),
0xD1 => inst!("CMP (ind),y", 3, |off| {
let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0);
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
let val = self.read_abs_y(low, high); let val = self.read_abs_y(low, high);
let v = self.cpu.a.wrapping_sub(val); let v = self.cpu.a.wrapping_sub(val);
self.cpu.status.set_zero(v == 0); self.cpu.status.set_zero(v == 0);
@@ -1077,7 +1150,7 @@ impl NES {
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}", val, self.cpu.a); log!("{addr:04X}: ADC #${:02X} | {:02X}", val, self.cpu.a);
}), }),
0x65 => inst!("ADC zp", 0, |off| { 0x65 => inst!("ADC zp", 1, |off| {
let val = self.read_abs(off, 0); let val = self.read_abs(off, 0);
let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_1) = self.cpu.a.overflowing_add(val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1090,7 +1163,7 @@ impl NES {
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}", off, self.cpu.a); log!("{addr:04X}: ADC ${:02X} | {:02X}", off, self.cpu.a);
}), }),
0x75 => inst!("ADC zp,x", 0, |off| { 0x75 => inst!("ADC zp,x", 2, |off| {
let val = self.read_zp_x(off); let val = self.read_zp_x(off);
let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_1) = self.cpu.a.overflowing_add(val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1103,7 +1176,7 @@ impl NES {
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},x | {:02X}", off, self.cpu.a); log!("{addr:04X}: ADC ${:02X},x | {:02X}", off, self.cpu.a);
}), }),
0x6D => inst!("ADC abs", 0, |low, high| { 0x6D => inst!("ADC abs", 1, |low, high| {
let val = self.read_abs(low, high); let val = self.read_abs(low, high);
let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_1) = self.cpu.a.overflowing_add(val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1121,7 +1194,8 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x7D => inst!("ADC abs,x", 0, |low, high| { 0x7D => inst!("ADC abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
let val = self.read_abs_x(low, high); let val = self.read_abs_x(low, high);
let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_1) = self.cpu.a.overflowing_add(val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1139,7 +1213,8 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x79 => inst!("ADC abs,y", 0, |low, high| { 0x79 => inst!("ADC abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
let val = self.read_abs_y(low, high); let val = self.read_abs_y(low, high);
let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_1) = self.cpu.a.overflowing_add(val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1157,7 +1232,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x61 => inst!("ADC (ind,x)", 0, |off| { 0x61 => inst!("ADC (ind,x)", 4, |off| {
let low = self.read_abs(off, 0); let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
@@ -1177,9 +1252,10 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x71 => inst!("ADC (ind),y", 0, |off| { 0x71 => inst!("ADC (ind),y", 3, |off| {
let low = self.read_abs(off, 0); let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
let (a, carry_1) = self.cpu.a.overflowing_add(val); let (a, carry_1) = self.cpu.a.overflowing_add(val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1210,7 +1286,7 @@ impl NES {
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}", val, self.cpu.a); log!("{addr:04X}: SBC #${:02X} | {:02X}", val, self.cpu.a);
}), }),
0xE5 => inst!("SBC zp", 0, |off| { 0xE5 => inst!("SBC zp", 1, |off| {
let val = self.read_abs(off, 0); let val = self.read_abs(off, 0);
let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_1) = self.cpu.a.overflowing_add(!val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1222,7 +1298,7 @@ impl NES {
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);
}), }),
0xF5 => inst!("SBC zp,x", 0, |off| { 0xF5 => inst!("SBC zp,x", 2, |off| {
let val = self.read_zp_x(off); let val = self.read_zp_x(off);
let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_1) = self.cpu.a.overflowing_add(!val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1235,7 +1311,7 @@ impl NES {
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},x | {:02X}", off, self.cpu.a); log!("{addr:04X}: SBC ${:02X},x | {:02X}", off, self.cpu.a);
}), }),
0xED => inst!("SBC abs", 0, |low, high| { 0xED => inst!("SBC abs", 1, |low, high| {
let val = self.read_abs(low, high); let val = self.read_abs(low, high);
let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_1) = self.cpu.a.overflowing_add(!val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1253,7 +1329,8 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xFD => inst!("SBC abs,x", 0, |low, high| { 0xFD => inst!("SBC abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
let val = self.read_abs_x(low, high); let val = self.read_abs_x(low, high);
let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_1) = self.cpu.a.overflowing_add(!val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1271,7 +1348,8 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xF9 => inst!("SBC abs,y", 0, |low, high| { 0xF9 => inst!("SBC abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
let val = self.read_abs_y(low, high); let val = self.read_abs_y(low, high);
let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_1) = self.cpu.a.overflowing_add(!val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1289,7 +1367,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xE1 => inst!("SBC (ind,x)", 0, |off| { 0xE1 => inst!("SBC (ind,x)", 4, |off| {
let low = self.read_abs(off, 0); let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
@@ -1309,9 +1387,10 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xF1 => inst!("SBC (ind),y", 0, |off| { 0xF1 => inst!("SBC (ind),y", 3, |off| {
let low = self.read_abs(off, 0); let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16); let val = self.read(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
let (a, carry_1) = self.cpu.a.overflowing_add(!val); let (a, carry_1) = self.cpu.a.overflowing_add(!val);
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into()); let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
@@ -1355,7 +1434,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xDE => inst!("DEC abs,x", 3, |low, high| { 0xDE => inst!("DEC abs,x", 4, |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);
@@ -1405,7 +1484,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xFE => inst!("INC abs,x", 3, |low, high| { 0xFE => inst!("INC abs,x", 4, |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);
@@ -1460,7 +1539,8 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x1D => inst!("ORA abs,x", 1, |low, high| { // TODO: page crossing 0x1D => inst!("ORA abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
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);
@@ -1472,6 +1552,7 @@ impl NES {
); );
}), }),
0x19 => inst!("ORA abs,y", 1, |low, high| { 0x19 => inst!("ORA abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -1498,6 +1579,10 @@ impl NES {
0x11 => inst!("ORA (ind),y", 3, |off| { 0x11 => inst!("ORA (ind),y", 3, |off| {
let low = self.read_abs(off, 0); let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
if !oops && low.checked_add(self.cpu.y).is_none() {
return ExecState::Oops;
}
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);
@@ -1526,7 +1611,7 @@ impl NES {
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},x | {:02X}", off, self.cpu.a); log!("{addr:04X}: AND ${:02X},x | {:02X}", off, self.cpu.a);
}), }),
0x2D => inst!("AND abs", 2, |low, high| { 0x2D => inst!("AND abs", 1, |low, high| {
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);
@@ -1537,7 +1622,8 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x3D => inst!("AND abs,x", 2, |low, high| { 0x3D => inst!("AND abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
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);
@@ -1549,6 +1635,7 @@ impl NES {
); );
}), }),
0x39 => inst!("AND abs,y", 1, |low, high| { 0x39 => inst!("AND abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -1572,9 +1659,10 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x31 => inst!("AND (ind),y", 4, |off| { 0x31 => inst!("AND (ind),y", 3, |off| {
let low = self.read_abs(off, 0); let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -1615,6 +1703,7 @@ impl NES {
); );
}), }),
0x5D => inst!("EOR abs,x", 1, |low, high| { 0x5D => inst!("EOR abs,x", 1, |low, high| {
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
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);
@@ -1626,6 +1715,7 @@ impl NES {
); );
}), }),
0x59 => inst!("EOR abs,y", 1, |low, high| { 0x59 => inst!("EOR abs,y", 1, |low, high| {
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -1652,6 +1742,7 @@ impl NES {
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);
let high = self.read_abs(off.wrapping_add(1), 0); let high = self.read_abs(off.wrapping_add(1), 0);
if !oops && low.checked_add(self.cpu.y).is_none() { return ExecState::Oops; }
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);
@@ -1931,39 +2022,32 @@ impl NES {
} }
} }
ClockState::ReadInstruction => { ClockState::ReadInstruction => {
// if self.cpu.nmi_pending { let addr = self.cpu.pc;
// self.cpu.nmi_pending = false; let instruction = self.read(self.cpu.pc);
// writeln!(self.debug_log, "NMI detected").unwrap(); if instruction != 0x02 {
// ClockState::HoldNmi { cycles: 5 } self.cpu.pc = self.cpu.pc.wrapping_add(1);
// } else if !self.cpu.status.interrupt_disable() }
// && (self.ppu.irq_waiting() || self.apu.irq_waiting()) match self.exec_instruction(instruction, &[], false, false, addr) {
// { ExecState::Done => ClockState::ReadInstruction,
// // TODO: handle proper irq detection ExecState::MoreParams => ClockState::ReadOperands {
// writeln!(self.debug_log, "IRQ detected").unwrap(); instruction,
// ClockState::HoldIrq { cycles: 6 } ops: [0u8; 5],
// } else count: 0,
{ addr,
let addr = self.cpu.pc; },
let instruction = self.read(self.cpu.pc); ExecState::Hold(cycles) => ClockState::Hold {
if instruction != 0x02 { cycles,
self.cpu.pc = self.cpu.pc.wrapping_add(1); instruction,
} ops: [0u8; 5],
match self.exec_instruction(instruction, &[], false, addr) { count: 0,
ExecState::Done => ClockState::ReadInstruction, addr,
ExecState::MoreParams => ClockState::ReadOperands { },
instruction, ExecState::Oops => ClockState::Oops {
ops: [0u8; 5], instruction,
count: 0, ops: [0u8; 5],
addr, count: 0,
}, addr,
ExecState::Hold(cycles) => ClockState::Hold { },
cycles,
instruction,
ops: [0u8; 5],
count: 0,
addr,
},
}
} }
} }
ClockState::ReadOperands { ClockState::ReadOperands {
@@ -1977,7 +2061,13 @@ impl NES {
} }
ops[count as usize] = self.read(self.cpu.pc); ops[count as usize] = self.read(self.cpu.pc);
self.cpu.pc = self.cpu.pc.wrapping_add(1); self.cpu.pc = self.cpu.pc.wrapping_add(1);
match self.exec_instruction(instruction, &ops[..count as usize + 1], false, addr) { match self.exec_instruction(
instruction,
&ops[..count as usize + 1],
false,
false,
addr,
) {
ExecState::Done => ClockState::ReadInstruction, ExecState::Done => ClockState::ReadInstruction,
ExecState::MoreParams => ClockState::ReadOperands { ExecState::MoreParams => ClockState::ReadOperands {
instruction, instruction,
@@ -1992,6 +2082,12 @@ impl NES {
count: count + 1, count: count + 1,
addr, addr,
}, },
ExecState::Oops => ClockState::Oops {
instruction,
ops,
count: count + 1,
addr,
},
} }
} }
ClockState::Hold { ClockState::Hold {
@@ -2002,15 +2098,24 @@ impl NES {
addr, addr,
} => { } => {
if cycles == 0 { if cycles == 0 {
match self.exec_instruction(instruction, &ops[..count as usize], true, addr) { match self.exec_instruction(
instruction,
&ops[..count as usize],
true,
false,
addr,
) {
ExecState::Done => ClockState::ReadInstruction, ExecState::Done => ClockState::ReadInstruction,
ExecState::MoreParams => ClockState::ReadOperands { ExecState::MoreParams => {
panic!("Should never return MoreParams after holding")
}
ExecState::Hold(_) => panic!("Should never return Hold after holding"),
ExecState::Oops => ClockState::Oops {
instruction, instruction,
ops, ops,
count: count + 1, count,
addr, addr,
}, },
ExecState::Hold(_) => panic!("Should never return Hold after holding"),
} }
} else { } else {
ClockState::Hold { ClockState::Hold {
@@ -2022,12 +2127,25 @@ impl NES {
} }
} }
} }
ClockState::Oops {
instruction,
ops,
count,
addr,
} => {
match self.exec_instruction(instruction, &ops[..count as usize], true, true, addr) {
ExecState::Done => ClockState::ReadInstruction,
_ => panic!("Must execute after oops"),
}
}
}; };
if self.cpu.clock_state == ClockState::ReadInstruction { if self.cpu.clock_state == ClockState::ReadInstruction {
if self.cpu.nmi_pending { if self.cpu.nmi_pending {
self.cpu.nmi_pending = false; self.cpu.nmi_pending = false;
self.cpu.clock_state = ClockState::HoldNmi { cycles: 6 }; self.cpu.clock_state = ClockState::HoldNmi { cycles: 6 };
writeln!(self.debug_log, "NMI detected").unwrap();
} else if self.cpu.irq_pending && !self.cpu.status.interrupt_disable() { } else if self.cpu.irq_pending && !self.cpu.status.interrupt_disable() {
writeln!(self.debug_log, "IRQ detected").unwrap();
self.cpu.clock_state = ClockState::HoldIrq { cycles: 6 }; self.cpu.clock_state = ClockState::HoldIrq { cycles: 6 };
} }
} }

View File

@@ -26,8 +26,8 @@ use tracing_subscriber::EnvFilter;
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "ppu_fill_red.nes"); // const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "ppu_fill_red.nes");
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "ppu_fill_name_table.nes"); // const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "ppu_fill_name_table.nes");
// const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "int_nmi_exit_timing.nes"); // const ROM_FILE: &str = concat!(env!("ROM_DIR"), "/", "int_nmi_exit_timing.nes");
const ROM_FILE: &str = "./Super Mario Bros. (World).nes"; // const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
// const ROM_FILE: &str = "./cpu_timing_test.nes"; const ROM_FILE: &str = "./cpu_timing_test.nes";
extern crate nes_emu; extern crate nes_emu;

View File

@@ -467,7 +467,7 @@ impl Palette {
pub struct PPU { pub struct PPU {
// registers: PPURegisters, // registers: PPURegisters,
frame_count: usize, pub frame_count: usize,
nmi_enabled: bool, nmi_enabled: bool,
// nmi_waiting: bool, // nmi_waiting: bool,
pub even: bool, pub even: bool,