Fix 'oops cycle' timing, to pass timing test rom
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 25s
Some checks failed
Cargo Build & Test / Rust project - latest (stable) (push) Failing after 25s
This commit is contained in:
156
src/lib.rs
156
src/lib.rs
@@ -182,6 +182,12 @@ pub enum ClockState {
|
||||
count: u8,
|
||||
addr: u16,
|
||||
},
|
||||
Oops {
|
||||
instruction: u8,
|
||||
ops: [u8; 5],
|
||||
count: u8,
|
||||
addr: u16,
|
||||
},
|
||||
HoldNmi {
|
||||
cycles: u8,
|
||||
},
|
||||
@@ -224,6 +230,17 @@ impl std::fmt::Debug for ClockState {
|
||||
ClockState::HoldIrq { cycles } => {
|
||||
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,
|
||||
MoreParams,
|
||||
Hold(u8),
|
||||
Oops,
|
||||
}
|
||||
|
||||
pub enum CPUMMRegisters {
|
||||
@@ -436,7 +454,14 @@ impl NES {
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
($val:expr, $hold:expr, |$($name:pat_param),*| $eval:expr) => {{
|
||||
let hold_time: u8 = ($hold).into();
|
||||
@@ -588,6 +613,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -599,6 +625,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -620,6 +647,7 @@ impl NES {
|
||||
0xB1 => inst!("LDA (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; }
|
||||
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_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -655,6 +683,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.x == 0);
|
||||
self.cpu.status.set_negative(self.cpu.x & 0x80 == 0x80);
|
||||
@@ -695,6 +724,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.y == 0);
|
||||
self.cpu.status.set_negative(self.cpu.y & 0x80 == 0x80);
|
||||
@@ -956,6 +986,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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 v = self.cpu.a.wrapping_sub(val);
|
||||
self.cpu.status.set_zero(v == 0);
|
||||
@@ -971,6 +1002,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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);
|
||||
@@ -1003,9 +1035,9 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
0xD1 => inst!("CMP (ind),y", 3, |off| {
|
||||
// TODO: iymode
|
||||
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 v = self.cpu.a.wrapping_sub(val);
|
||||
self.cpu.status.set_zero(v == 0);
|
||||
@@ -1163,6 +1195,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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 (a, carry_1) = self.cpu.a.overflowing_add(val);
|
||||
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
|
||||
@@ -1181,6 +1214,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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 (a, carry_1) = self.cpu.a.overflowing_add(val);
|
||||
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
|
||||
@@ -1221,6 +1255,7 @@ impl NES {
|
||||
0x71 => inst!("ADC (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(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
||||
let (a, carry_1) = self.cpu.a.overflowing_add(val);
|
||||
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
|
||||
@@ -1295,6 +1330,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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 (a, carry_1) = self.cpu.a.overflowing_add(!val);
|
||||
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
|
||||
@@ -1313,6 +1349,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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 (a, carry_1) = self.cpu.a.overflowing_add(!val);
|
||||
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
|
||||
@@ -1353,6 +1390,7 @@ impl NES {
|
||||
0xF1 => inst!("SBC (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(u16::from_le_bytes([low, high]) + self.cpu.y as u16);
|
||||
let (a, carry_1) = self.cpu.a.overflowing_add(!val);
|
||||
let (a, carry_2) = a.overflowing_add(self.cpu.status.carry().into());
|
||||
@@ -1502,7 +1540,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
0x1D => inst!("ORA abs,x", 1, |low, high| {
|
||||
// TODO: page crossing
|
||||
if !oops && low.checked_add(self.cpu.x).is_none() { return ExecState::Oops; }
|
||||
self.cpu.a |= self.read_abs_x(low, high);
|
||||
self.cpu.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1514,6 +1552,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1540,6 +1579,10 @@ impl NES {
|
||||
0x11 => inst!("ORA (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; }
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1580,6 +1623,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1591,6 +1635,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1617,6 +1662,7 @@ impl NES {
|
||||
0x31 => inst!("AND (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; }
|
||||
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_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1657,6 +1703,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1668,6 +1715,7 @@ impl NES {
|
||||
);
|
||||
}),
|
||||
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.status.set_zero(self.cpu.a == 0);
|
||||
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1694,6 +1742,7 @@ impl NES {
|
||||
0x51 => inst!("EOR (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; }
|
||||
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_negative(self.cpu.a & 0x80 == 0x80);
|
||||
@@ -1973,39 +2022,32 @@ impl NES {
|
||||
}
|
||||
}
|
||||
ClockState::ReadInstruction => {
|
||||
// if self.cpu.nmi_pending {
|
||||
// self.cpu.nmi_pending = false;
|
||||
// writeln!(self.debug_log, "NMI detected").unwrap();
|
||||
// ClockState::HoldNmi { cycles: 5 }
|
||||
// } else if !self.cpu.status.interrupt_disable()
|
||||
// && (self.ppu.irq_waiting() || self.apu.irq_waiting())
|
||||
// {
|
||||
// // TODO: handle proper irq detection
|
||||
// writeln!(self.debug_log, "IRQ detected").unwrap();
|
||||
// ClockState::HoldIrq { cycles: 6 }
|
||||
// } else
|
||||
{
|
||||
let addr = self.cpu.pc;
|
||||
let instruction = self.read(self.cpu.pc);
|
||||
if instruction != 0x02 {
|
||||
self.cpu.pc = self.cpu.pc.wrapping_add(1);
|
||||
}
|
||||
match self.exec_instruction(instruction, &[], false, addr) {
|
||||
ExecState::Done => ClockState::ReadInstruction,
|
||||
ExecState::MoreParams => ClockState::ReadOperands {
|
||||
instruction,
|
||||
ops: [0u8; 5],
|
||||
count: 0,
|
||||
addr,
|
||||
},
|
||||
ExecState::Hold(cycles) => ClockState::Hold {
|
||||
cycles,
|
||||
instruction,
|
||||
ops: [0u8; 5],
|
||||
count: 0,
|
||||
addr,
|
||||
},
|
||||
}
|
||||
let addr = self.cpu.pc;
|
||||
let instruction = self.read(self.cpu.pc);
|
||||
if instruction != 0x02 {
|
||||
self.cpu.pc = self.cpu.pc.wrapping_add(1);
|
||||
}
|
||||
match self.exec_instruction(instruction, &[], false, false, addr) {
|
||||
ExecState::Done => ClockState::ReadInstruction,
|
||||
ExecState::MoreParams => ClockState::ReadOperands {
|
||||
instruction,
|
||||
ops: [0u8; 5],
|
||||
count: 0,
|
||||
addr,
|
||||
},
|
||||
ExecState::Hold(cycles) => ClockState::Hold {
|
||||
cycles,
|
||||
instruction,
|
||||
ops: [0u8; 5],
|
||||
count: 0,
|
||||
addr,
|
||||
},
|
||||
ExecState::Oops => ClockState::Oops {
|
||||
instruction,
|
||||
ops: [0u8; 5],
|
||||
count: 0,
|
||||
addr,
|
||||
},
|
||||
}
|
||||
}
|
||||
ClockState::ReadOperands {
|
||||
@@ -2019,7 +2061,13 @@ impl NES {
|
||||
}
|
||||
ops[count as usize] = self.read(self.cpu.pc);
|
||||
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::MoreParams => ClockState::ReadOperands {
|
||||
instruction,
|
||||
@@ -2034,6 +2082,12 @@ impl NES {
|
||||
count: count + 1,
|
||||
addr,
|
||||
},
|
||||
ExecState::Oops => ClockState::Oops {
|
||||
instruction,
|
||||
ops,
|
||||
count: count + 1,
|
||||
addr,
|
||||
},
|
||||
}
|
||||
}
|
||||
ClockState::Hold {
|
||||
@@ -2044,15 +2098,24 @@ impl NES {
|
||||
addr,
|
||||
} => {
|
||||
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::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,
|
||||
ops,
|
||||
count: count + 1,
|
||||
count,
|
||||
addr,
|
||||
},
|
||||
ExecState::Hold(_) => panic!("Should never return Hold after holding"),
|
||||
}
|
||||
} else {
|
||||
ClockState::Hold {
|
||||
@@ -2064,6 +2127,17 @@ 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.nmi_pending {
|
||||
|
||||
@@ -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_name_table.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 = "./cpu_timing_test.nes";
|
||||
// const ROM_FILE: &str = "./Super Mario Bros. (World).nes";
|
||||
const ROM_FILE: &str = "./cpu_timing_test.nes";
|
||||
|
||||
extern crate nes_emu;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user