Fix most basic timing issues

This commit is contained in:
2026-01-19 17:30:34 -06:00
parent 42c3af28b4
commit 2e5e2ed1e7

View File

@@ -313,8 +313,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 +367,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),
}, },
}); });
@@ -631,7 +637,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);
@@ -671,7 +677,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);
@@ -718,7 +724,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 +733,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 +871,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
@@ -979,6 +985,41 @@ impl NES {
self.cpu.status 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| {
// TODO: iymode
let low = self.read_abs(off, 0);
let high = self.read_abs(off.wrapping_add(1), 0);
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
);
}),
0xE0 => inst!("CPX imm", 0, |val| { 0xE0 => inst!("CPX imm", 0, |val| {
let v = self.cpu.x.wrapping_sub(val); let v = self.cpu.x.wrapping_sub(val);
self.cpu.status.set_zero(v == 0); self.cpu.status.set_zero(v == 0);
@@ -1077,7 +1118,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 +1131,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 +1144,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 +1162,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x7D => inst!("ADC abs,x", 0, |low, high| { 0x7D => inst!("ADC abs,x", 1, |low, high| {
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 +1180,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x79 => inst!("ADC abs,y", 0, |low, high| { 0x79 => inst!("ADC abs,y", 1, |low, high| {
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 +1198,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,7 +1218,7 @@ 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);
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);
@@ -1210,7 +1251,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 +1263,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 +1276,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 +1294,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xFD => inst!("SBC abs,x", 0, |low, high| { 0xFD => inst!("SBC abs,x", 1, |low, high| {
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 +1312,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0xF9 => inst!("SBC abs,y", 0, |low, high| { 0xF9 => inst!("SBC abs,y", 1, |low, high| {
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 +1330,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,7 +1350,7 @@ 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);
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);
@@ -1355,7 +1396,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 +1446,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 +1501,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| {
// TODO: page crossing
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);
@@ -1526,7 +1568,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 +1579,7 @@ impl NES {
self.cpu.a self.cpu.a
); );
}), }),
0x3D => inst!("AND abs,x", 2, |low, high| { 0x3D => inst!("AND abs,x", 1, |low, high| {
self.cpu.a &= self.read_abs_x(low, high); self.cpu.a &= self.read_abs_x(low, high);
self.cpu.status.set_zero(self.cpu.a == 0); self.cpu.status.set_zero(self.cpu.a == 0);
self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80); self.cpu.status.set_negative(self.cpu.a & 0x80 == 0x80);
@@ -1572,7 +1614,7 @@ 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);
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);
@@ -2027,7 +2069,9 @@ impl NES {
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 };
} }
} }