LDA instructions implemented
This commit is contained in:
@@ -22,7 +22,9 @@
|
|||||||
- set_test_start for the nestest harness
|
- 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
|
- nestest harness working (main.rs): loads nestest.nes, runs from $C000 with CYC:7, dumps trace to ours.log
|
||||||
- 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)
|
- 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)
|
- Batch 2 done (branches): all 8 branches (BEQ/BNE/BCC/BCS/BPL/BMI/BVC/BVS) via one shared branch() routine with condition param. Taken = +3 cycles, page-cross = +1 more. Verified both taken and not-taken paths in the log
|
||||||
|
- Flag set/clear ops done (part of Batch 10): CLC, CLI, CLV, CLD, SEI, SED (SEC done in Batch 1). SEI/SED set, the rest clear. Fixed a real bug where SEI/SED were clearing instead of setting
|
||||||
|
- Trace now matches the nestest reference log through line 22 (C754 A9, CYC:61), field-for-field (PC/A/X/Y/P/SP/CYC verified by label-comparison script). Next panic is on A9 (LDA immediate) at line 22, top of Batch 3
|
||||||
- trace bytes are length-aware via opcode_len (fixed-width byte field, columns align for awk diff)
|
- trace bytes are length-aware via opcode_len (fixed-width byte field, columns align for awk diff)
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
@@ -32,15 +34,15 @@ Finish the official CPU. Nestest analysis findings: the full 8991-line log tests
|
|||||||
CPU implementation batches (by family, ordered by nestest log frequency):
|
CPU implementation batches (by family, ordered by nestest log frequency):
|
||||||
|
|
||||||
- Batch 1 - DONE (see Done section)
|
- 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 2 - DONE (branches, see Done section)
|
||||||
- Batch 3 - Loads: LDA 8 modes, LDX 5 modes, LDY 5 modes (share load + set_zp_flags)
|
- Batch 3 - Loads: LDA 8 modes, LDX 5 modes, LDY 5 modes (share load + set_zp_flags). Next up
|
||||||
- Batch 4 - Stores: STA 7 modes, STX 3, STY 3 (share store core, no flag changes)
|
- 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)
|
- Batch 5 - Compares: CMP 8 modes, CPX 3, CPY 3 (set C/Z/N, no store)
|
||||||
- Batch 6 - Arithmetic: ADC 8 modes, SBC 8 modes (N/V/C/Z, decimal flag inert), BIT 2 modes
|
- Batch 6 - Arithmetic: ADC 8 modes, SBC 8 modes (N/V/C/Z, decimal flag inert), BIT 2 modes
|
||||||
- Batch 7 - Logic: AND, ORA, EOR (8 modes each)
|
- Batch 7 - Logic: AND, ORA, EOR (8 modes each)
|
||||||
- Batch 8 - Inc/Dec: INC/DEC 4 modes each (RMW), INX, INY, DEX, DEY
|
- Batch 8 - Inc/Dec: INC/DEC 4 modes each (RMW), INX, INY, DEX, DEY
|
||||||
- Batch 9 - Shifts: ASL, LSR, ROL, ROR (accumulator + 4 memory modes each, RMW)
|
- Batch 9 - Shifts: ASL, LSR, ROL, ROR (accumulator + 4 memory modes each, RMW)
|
||||||
- Batch 10 - Flags + misc: CLC 18, SEC 38, CLI 58, SEI 78, CLD D8, SED F8, CLV B8, NOP EA, JMP abs 4C, JMP ind 6C, transfers TAX/TAY/TXA/TYA/TSX/TXS
|
- Batch 10 - PARTIAL: flag ops done (CLC/CLI/CLV/CLD/SEI/SED, plus SEC/NOP). Remaining: JMP ind 6C, transfers TAX/TAY/TXA/TYA/TSX/TXS
|
||||||
|
|
||||||
Rules for each batch:
|
Rules for each batch:
|
||||||
- Add the opcode's length to opcode_len in the same edit (never let them drift)
|
- Add the opcode's length to opcode_len in the same edit (never let them drift)
|
||||||
|
|||||||
@@ -19,4 +19,19 @@ C74B EA A:00 X:00 Y:00 P:27 SP:FB CYC:52
|
|||||||
C74C 18 A:00 X:00 Y:00 P:27 SP:FB CYC:54
|
C74C 18 A:00 X:00 Y:00 P:27 SP:FB CYC:54
|
||||||
C74D 90 04 A:00 X:00 Y:00 P:26 SP:FB CYC:56
|
C74D 90 04 A:00 X:00 Y:00 P:26 SP:FB CYC:56
|
||||||
C753 EA A:00 X:00 Y:00 P:26 SP:FB CYC:59
|
C753 EA A:00 X:00 Y:00 P:26 SP:FB CYC:59
|
||||||
C754 A9 A:00 X:00 Y:00 P:26 SP:FB CYC:61
|
C754 A9 00 A:00 X:00 Y:00 P:26 SP:FB CYC:61
|
||||||
|
C756 F0 04 A:00 X:00 Y:00 P:26 SP:FB CYC:63
|
||||||
|
C75C EA A:00 X:00 Y:00 P:26 SP:FB CYC:66
|
||||||
|
C75D A9 40 A:00 X:00 Y:00 P:26 SP:FB CYC:68
|
||||||
|
C75F F0 03 A:40 X:00 Y:00 P:24 SP:FB CYC:70
|
||||||
|
C761 4C 68 C7 A:40 X:00 Y:00 P:24 SP:FB CYC:72
|
||||||
|
C768 EA A:40 X:00 Y:00 P:24 SP:FB CYC:75
|
||||||
|
C769 A9 40 A:40 X:00 Y:00 P:24 SP:FB CYC:77
|
||||||
|
C76B D0 04 A:40 X:00 Y:00 P:24 SP:FB CYC:79
|
||||||
|
C771 EA A:40 X:00 Y:00 P:24 SP:FB CYC:82
|
||||||
|
C772 A9 00 A:40 X:00 Y:00 P:24 SP:FB CYC:84
|
||||||
|
C774 D0 03 A:00 X:00 Y:00 P:26 SP:FB CYC:86
|
||||||
|
C776 4C 7D C7 A:00 X:00 Y:00 P:26 SP:FB CYC:88
|
||||||
|
C77D EA A:00 X:00 Y:00 P:26 SP:FB CYC:91
|
||||||
|
C77E A9 FF A:00 X:00 Y:00 P:26 SP:FB CYC:93
|
||||||
|
C780 85 A:FF X:00 Y:00 P:A4 SP:FB CYC:95
|
||||||
|
|||||||
+60
@@ -123,6 +123,14 @@ impl Cpu {
|
|||||||
0xD8 => self.cld_imp(bus),
|
0xD8 => self.cld_imp(bus),
|
||||||
0x78 => self.sei_imp(bus),
|
0x78 => self.sei_imp(bus),
|
||||||
0xF8 => self.sed_imp(bus),
|
0xF8 => self.sed_imp(bus),
|
||||||
|
0xA9 => self.lda_imm(bus),
|
||||||
|
0xA5 => self.lda_zp(bus),
|
||||||
|
0xB5 => self.lda_zpx(bus),
|
||||||
|
0xAD => self.lda_abs(bus),
|
||||||
|
0xBD => self.lda_abx(bus),
|
||||||
|
0xB9 => self.lda_aby(bus),
|
||||||
|
0xA1 => self.lda_izx(bus),
|
||||||
|
0xB1 => self.lda_izy(bus),
|
||||||
_ => panic!("illegal/unkown opcode {opcode:#04X} at {:#06X}", self.pc.wrapping_sub(1)),
|
_ => panic!("illegal/unkown opcode {opcode:#04X} at {:#06X}", self.pc.wrapping_sub(1)),
|
||||||
};
|
};
|
||||||
self.cycles += cycles as u64;
|
self.cycles += cycles as u64;
|
||||||
@@ -220,6 +228,20 @@ impl Cpu {
|
|||||||
cycles
|
cycles
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shared cores for LDx instructions
|
||||||
|
fn lda(&mut self,bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
|
||||||
|
self.a = self.read(bus, address);
|
||||||
|
self.set_zp_flags(self.a);
|
||||||
|
base + extra
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0xA9, LDA immediate instructions: operand is inline, fetch consumes it, no address helper
|
||||||
|
fn lda_imm(&mut self, bus: &mut impl Bus) -> u8 {
|
||||||
|
self.a = self.fetch(bus);
|
||||||
|
self.set_zp_flags(self.a);
|
||||||
|
2
|
||||||
|
}
|
||||||
|
|
||||||
// NES 6502 opcode -> functions
|
// NES 6502 opcode -> functions
|
||||||
fn jmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4C, 3 bytes, 3 cycles
|
fn jmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4C, 3 bytes, 3 cycles
|
||||||
self.pc = self.fetch16(bus);
|
self.pc = self.fetch16(bus);
|
||||||
@@ -333,6 +355,36 @@ impl Cpu {
|
|||||||
2
|
2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LDA alternatives here
|
||||||
|
fn lda_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xA5, 2 bytes
|
||||||
|
let (address, extra) = self.address_zp(bus);
|
||||||
|
self.lda(bus, (address, extra), 3)
|
||||||
|
}
|
||||||
|
fn lda_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0xB5, 2 bytes
|
||||||
|
let (address, extra) = self.address_zpx(bus);
|
||||||
|
self.lda(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
fn lda_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xAD, 3 bytes
|
||||||
|
let (address, extra) = self.address_abs(bus);
|
||||||
|
self.lda(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
fn lda_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0xBD, 3 bytes
|
||||||
|
let (address, extra) = self.address_abx(bus);
|
||||||
|
self.lda(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
fn lda_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0xB9, 3 bytes
|
||||||
|
let (address, extra) = self.address_aby(bus);
|
||||||
|
self.lda(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
fn lda_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0xA1, 2 bytes
|
||||||
|
let (address, extra) = self.address_izx(bus);
|
||||||
|
self.lda(bus, (address, extra), 6)
|
||||||
|
}
|
||||||
|
fn lda_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0xB1, 2 bytes
|
||||||
|
let (address, extra) = self.address_izy(bus);
|
||||||
|
self.lda(bus, (address, extra), 5)
|
||||||
|
}
|
||||||
|
|
||||||
// all branching alternatives here all 1 bytes
|
// all branching alternatives here all 1 bytes
|
||||||
fn bcs_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_CARRY)) } // 0xB0
|
fn bcs_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_CARRY)) } // 0xB0
|
||||||
fn bcc_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)) } // 0x90
|
||||||
@@ -373,6 +425,14 @@ impl Cpu {
|
|||||||
0xD8 => 1, // CLD imp
|
0xD8 => 1, // CLD imp
|
||||||
0x78 => 1, // SEI imp
|
0x78 => 1, // SEI imp
|
||||||
0xF8 => 1, // SED imp
|
0xF8 => 1, // SED imp
|
||||||
|
0xA9 => 2, // LDA imm
|
||||||
|
0xA5 => 2, // LDA zp
|
||||||
|
0xB5 => 2, // LDA zpx
|
||||||
|
0xAD => 3, // LDA abs
|
||||||
|
0xBD => 3, // LDA abx
|
||||||
|
0xB9 => 3, // LDA aby
|
||||||
|
0xA1 => 2, // LDA izx
|
||||||
|
0xB1 => 2, // LDA izy
|
||||||
_ => 1, // fallback, would show the invalid/unknown opcode only not its parameters
|
_ => 1, // fallback, would show the invalid/unknown opcode only not its parameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user