Compare commits
4 Commits
13e4158b7b
...
b5e1d1a4c3
| Author | SHA1 | Date | |
|---|---|---|---|
|
b5e1d1a4c3
|
|||
|
2e5e2ed1e7
|
|||
|
42c3af28b4
|
|||
|
ac745f60e9
|
163
src/apu.rs
163
src/apu.rs
@@ -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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
254
src/lib.rs
254
src/lib.rs
@@ -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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user