diff --git a/STATE.md b/STATE.md index 219a829..9ebc929 100644 --- a/STATE.md +++ b/STATE.md @@ -21,7 +21,9 @@ - step dispatch shell + trace in nestest format - set_test_start for the nestest harness - nestest harness working (main.rs): loads nestest.nes, runs from $C000 with CYC:7, dumps trace to ours.log -- First 5 trace lines match the nestest reference log exactly (PC, regs, P, SP, CYC), then it panics on the next unimplemented opcode (JSR 0x20), which names the next target +- Batch 1 done (subroutines + stack): JSR, RTS, PHA, PLA, PHP, PLP, RTI, BRK, plus NOP and SEC as quick wins. Verified SP:FD->FB across JSR (push high-then-low works) +- Trace now matches the nestest reference log through line 8 (C72E SEC, CYC:29). Next panic is on BCS 0xB0 at line 9 (Batch 2) +- trace bytes are length-aware via opcode_len (fixed-width byte field, columns align for awk diff) ## Next @@ -29,8 +31,8 @@ Finish the official CPU. Nestest analysis findings: the full 8991-line log tests CPU implementation batches (by family, ordered by nestest log frequency): -- Batch 1 - Subroutines + stack: JSR 0x20 (push ret-1 high then low), RTS 0x60 (pop low then high), PHA 0x48, PLA 0x68, PHP 0x08, PLP 0x28 (B-flag on push rule), RTI 0x40, BRK 0x00 -- Batch 2 - Branches (all 8): BEQ F0, BNE D0, BCC 90, BCS B0, BPL 10, BMI 30, BVC 50, BVS 70. One shared method per flag. +1 cycle if taken, +1 more if target crosses page +- Batch 1 - DONE (see Done section) +- Batch 2 - Branches (all 8): BEQ F0, BNE D0, BCC 90, BCS B0, BPL 10, BMI 30, BVC 50, BVS 70. One shared method per flag. +1 cycle if taken, +1 more if target crosses page. Next up - Batch 3 - Loads: LDA 8 modes, LDX 5 modes, LDY 5 modes (share load + set_zp_flags) - Batch 4 - Stores: STA 7 modes, STX 3, STY 3 (share store core, no flag changes) - Batch 5 - Compares: CMP 8 modes, CPX 3, CPY 3 (set C/Z/N, no store) diff --git a/ours.log b/ours.log index 1a1fbce..39ade9d 100644 --- a/ours.log +++ b/ours.log @@ -6,4 +6,6 @@ C5FB 86 11 A:00 X:00 Y:00 P:26 SP:FD CYC:18 C5FD 20 2D C7 A:00 X:00 Y:00 P:26 SP:FD CYC:21 C72D EA A:00 X:00 Y:00 P:26 SP:FB CYC:27 C72E 38 A:00 X:00 Y:00 P:26 SP:FB CYC:29 -C72F B0 A:00 X:00 Y:00 P:27 SP:FB CYC:31 +C72F B0 04 A:00 X:00 Y:00 P:27 SP:FB CYC:31 +C735 EA A:00 X:00 Y:00 P:27 SP:FB CYC:34 +C736 18 A:00 X:00 Y:00 P:27 SP:FB CYC:36 diff --git a/src/cpu.rs b/src/cpu.rs index 747e930..5f3aae8 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -109,6 +109,14 @@ impl Cpu { 0x00 => self.brk_imp(bus), 0xEA => self.nop_imp(bus), 0x38 => self.sec_imp(bus), + 0x90 => self.bcc_rel(bus), + 0xB0 => self.bcs_rel(bus), + 0xF0 => self.beq_rel(bus), + 0xD0 => self.bne_rel(bus), + 0x10 => self.bpl_rel(bus), + 0x30 => self.bmi_rel(bus), + 0x50 => self.bvc_rel(bus), + 0x70 => self.bvs_rel(bus), _ => panic!("illegal/unkown opcode {opcode:#04X} at {:#06X}", self.pc.wrapping_sub(1)), }; self.cycles += cycles as u64; @@ -191,6 +199,21 @@ impl Cpu { self.pc.wrapping_add((offset as i8) as u16) // sign-extend + add, this allows to move the PC backwards } + // Branching helper for general Branch operations + + fn branch(&mut self, bus: &mut impl Bus, condition: bool) -> u8 { + let target: u16 = self.address_rel(bus); + if !condition { + return 2; + } + let mut cycles: u8 = 3; + if (self.pc & 0xFF00) != (target & 0xFF00) { + cycles += 1; + } + self.pc = target; + cycles + } + // NES 6502 opcode -> functions fn jmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4C, 3 bytes, 3 cycles self.pc = self.fetch16(bus); @@ -274,6 +297,16 @@ impl Cpu { 2 } + // all branching alternatives here all 2 bytes + fn bcs_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_CARRY)) } // 0x90 + fn bcc_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, !self.flag(FLAG_CARRY)) } // 0xB0 + fn beq_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_ZERO)) } // 0xF0 + fn bne_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, !self.flag(FLAG_ZERO)) } // 0xD0 + fn bpl_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, !self.flag(FLAG_NEGATIVE)) } // 0x10 + fn bmi_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_NEGATIVE)) } // 0x30 + fn bvc_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, !self.flag(FLAG_OVERFLOW)) } // 0x50 + fn bvs_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_OVERFLOW)) } //0x70 + // NES 6502 opcode length table/matcher fn opcode_len(opcode: u8) -> u8 { match opcode { @@ -290,6 +323,14 @@ impl Cpu { 0x00 => 1, // BRK imp 0xEA => 1, // NOP 0x38 => 1, // SEC imp + 0x90 => 2, // BCS rel + 0xB0 => 2, // BCC rel + 0xF0 => 2, // BEQ rel + 0xD0 => 2, // BNE rel + 0x10 => 2, // BPL rel + 0x30 => 2, // BMI rel + 0x50 => 2, // BVC rel + 0x70 => 2, // BVS rel _ => 1, // fallback, would show the invalid/unknown opcode only not its parameters } }