From 91f8fd0f4e77dc923fce16923f1eab80ee0eab1c Mon Sep 17 00:00:00 2001 From: JakeBreath Date: Sat, 8 Aug 2026 23:26:31 -0500 Subject: [PATCH] changed format of match tables to use 4 matches per line to have a more compact layout --- STATE.md | 7 +- src/cpu.rs | 248 +++++++++++++++-------------------------------------- 2 files changed, 71 insertions(+), 184 deletions(-) diff --git a/STATE.md b/STATE.md index 327d86d..59afe9c 100644 --- a/STATE.md +++ b/STATE.md @@ -24,10 +24,11 @@ - 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 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 38 (C782 BIT, CYC:98), field-for-field (PC/A/X/Y/P/SP/CYC verified by label-comparison script). Next panic is on 0x24 (BIT zp) at line 38, top of Batch 6 +- Trace now matches the nestest reference log through line 73 (C7E9 AND, CYC:192), field-for-field (PC/A/X/Y/P/SP/CYC verified by label-comparison script). Next panic is on 0x29 (AND imm) at line 73, top of Batch 7 - Batch 3 done (loads): LDA (8 modes), LDX (5 modes), LDY (5 modes) via three shared cores (lda/ldx/ldy) + thin wrappers. Caught two real bugs in review: all 4 LDY wrappers called self.ldx (would write to X instead of Y), and 6 of 8 LDX/LDY wrappers had wrong base cycles (3 instead of 4). Both fixed - Batch 4 done (stores): STA (7 modes), STX (3 modes), STY (3 modes) via three shared cores (sta/stx/sty) + thin wrappers. No flag changes. Refactored stx_zp to use the shared stx core for consistency. Fixed a comment typo (sta_abx was labeled 0x95 instead of 0x9D) - Batch 5 done (compares): CMP (8 modes), CPX (3 modes), CPY (3 modes) via three shared cores (cmp/cpx/cpy) + wrappers + immediate variants. Sets C (reg >= mem), Z, N; no store. Uses wrapping_sub for the borrow math. 70 of 151 official opcodes done. No diff progress expected until the log reaches these opcodes +- Batch 6 done (arithmetic): ADC (8 modes), SBC (8 modes), BIT (2 modes) via shared add_with_carry core + adc/sbc/bit cores + wrappers. SBC adds the one's complement (A + ~mem + C), which makes the V-flag math correct. Decimal flag inert (binary only). 88 of 151 official opcodes done. Big diff progress: BIT/PHP/PLA/PHA carried the log from 38 to 73 lines - trace bytes are length-aware via opcode_len (fixed-width byte field, columns align for awk diff) ## Next @@ -41,8 +42,8 @@ CPU implementation batches (by family, ordered by nestest log frequency): - Batch 3 - DONE (loads, see Done section) - Batch 4 - DONE (stores, see Done section) - Batch 5 - DONE (compares, see Done section) -- Batch 6 - Arithmetic: ADC 8 modes, SBC 8 modes (N/V/C/Z, decimal flag inert), BIT 2 modes. Next up -- Batch 7 - Logic: AND, ORA, EOR (8 modes each) +- Batch 6 - DONE (arithmetic, see Done section) +- Batch 7 - Logic: AND, ORA, EOR (8 modes each). Next up - 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 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 diff --git a/src/cpu.rs b/src/cpu.rs index fed74ae..1cd8113 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -96,100 +96,81 @@ impl Cpu { pub fn step(&mut self, bus: &mut impl Bus) -> u8 { let opcode: u8 = self.fetch(bus); let cycles = match opcode { - 0x4C => self.jmp_abs(bus), - 0xA2 => self.ldx_imm(bus), - 0x86 => self.stx_zp(bus), - 0x20 => self.jsr_abs(bus), - 0x60 => self.rts_imp(bus), - 0x48 => self.pha_imp(bus), - 0x68 => self.pla_imp(bus), - 0x08 => self.php_imp(bus), + // Jumps & subroutines + 0x4C => self.jmp_abs(bus), 0x20 => self.jsr_abs(bus), 0x60 => self.rts_imp(bus), + 0x40 => self.rti_imp(bus), 0x00 => self.brk_imp(bus), + // Stack + 0x48 => self.pha_imp(bus), 0x68 => self.pla_imp(bus), 0x08 => self.php_imp(bus), 0x28 => self.plp_imp(bus), - 0x40 => self.rti_imp(bus), - 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), - 0x18 => self.clc_imp(bus), - 0x58 => self.cli_imp(bus), - 0xB8 => self.clv_imp(bus), - 0xD8 => self.cld_imp(bus), - 0x78 => self.sei_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), - 0xA6 => self.ldx_zp(bus), - 0xB6 => self.ldx_zpy(bus), - 0xAE => self.ldx_abs(bus), - 0xBE => self.ldx_aby(bus), - 0xA0 => self.ldy_imm(bus), - 0xA4 => self.ldy_zp(bus), - 0xB4 => self.ldy_zpx(bus), - 0xAC => self.ldy_abs(bus), - 0xBC => self.ldy_abx(bus), - 0x85 => self.sta_zp(bus), - 0x95 => self.sta_zpx(bus), - 0x8D => self.sta_abs(bus), - 0x9D => self.sta_abx(bus), - 0x99 => self.sta_aby(bus), - 0x81 => self.sta_izx(bus), - 0x91 => self.sta_izy(bus), - 0x96 => self.stx_zpy(bus), - 0x8E => self.stx_abs(bus), - 0x84 => self.sty_zp(bus), - 0x94 => self.sty_zpx(bus), + // Flags & misc + 0x38 => self.sec_imp(bus), 0x18 => self.clc_imp(bus), 0x58 => self.cli_imp(bus), + 0x78 => self.sei_imp(bus), 0xD8 => self.cld_imp(bus), 0xF8 => self.sed_imp(bus), + 0xB8 => self.clv_imp(bus), 0xEA => self.nop_imp(bus), + // Branches + 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), + // Loads + 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), 0xA2 => self.ldx_imm(bus), + 0xA6 => self.ldx_zp(bus), 0xB6 => self.ldx_zpy(bus), 0xAE => self.ldx_abs(bus), + 0xBE => self.ldx_aby(bus), 0xA0 => self.ldy_imm(bus), 0xA4 => self.ldy_zp(bus), + 0xB4 => self.ldy_zpx(bus), 0xAC => self.ldy_abs(bus), 0xBC => self.ldy_abx(bus), + // Stores + 0x85 => self.sta_zp(bus), 0x95 => self.sta_zpx(bus), 0x8D => self.sta_abs(bus), + 0x9D => self.sta_abx(bus), 0x99 => self.sta_aby(bus), 0x81 => self.sta_izx(bus), + 0x91 => self.sta_izy(bus), 0x86 => self.stx_zp(bus), 0x96 => self.stx_zpy(bus), + 0x8E => self.stx_abs(bus), 0x84 => self.sty_zp(bus), 0x94 => self.sty_zpx(bus), 0x8C => self.sty_abs(bus), - 0xC9 => self.cmp_imm(bus), - 0xC5 => self.cmp_zp(bus), - 0xD5 => self.cmp_zpx(bus), - 0xCD => self.cmp_abs(bus), - 0xDD => self.cmp_abx(bus), - 0xD9 => self.cmp_aby(bus), - 0xC1 => self.cmp_izx(bus), - 0xD1 => self.cmp_izy(bus), - 0xE0 => self.cpx_imm(bus), - 0xE4 => self.cpx_zp(bus), - 0xEC => self.cpx_abs(bus), - 0xC0 => self.cpy_imm(bus), - 0xC4 => self.cpy_zp(bus), - 0xCC => self.cpy_abs(bus), - 0x69 => self.adc_imm(bus), - 0x65 => self.adc_zp(bus), - 0x75 => self.adc_zpx(bus), - 0x6D => self.adc_abs(bus), - 0x7D => self.adc_abx(bus), - 0x79 => self.adc_aby(bus), - 0x61 => self.adc_izx(bus), - 0x71 => self.adc_izy(bus), - 0xE9 => self.sbc_imm(bus), - 0xE5 => self.sbc_zp(bus), - 0xF5 => self.sbc_zpx(bus), - 0xED => self.sbc_abs(bus), - 0xFD => self.sbc_abx(bus), - 0xF9 => self.sbc_aby(bus), - 0xE1 => self.sbc_izx(bus), - 0xF1 => self.sbc_izy(bus), - 0x24 => self.bit_zp(bus), - 0x2C => self.bit_abs(bus), + // Compares + 0xC9 => self.cmp_imm(bus), 0xC5 => self.cmp_zp(bus), 0xD5 => self.cmp_zpx(bus), + 0xCD => self.cmp_abs(bus), 0xDD => self.cmp_abx(bus), 0xD9 => self.cmp_aby(bus), + 0xC1 => self.cmp_izx(bus), 0xD1 => self.cmp_izy(bus), 0xE0 => self.cpx_imm(bus), + 0xE4 => self.cpx_zp(bus), 0xEC => self.cpx_abs(bus), 0xC0 => self.cpy_imm(bus), + 0xC4 => self.cpy_zp(bus), 0xCC => self.cpy_abs(bus), + // Arithmetic + 0x69 => self.adc_imm(bus), 0x65 => self.adc_zp(bus), 0x75 => self.adc_zpx(bus), + 0x6D => self.adc_abs(bus), 0x7D => self.adc_abx(bus), 0x79 => self.adc_aby(bus), + 0x61 => self.adc_izx(bus), 0x71 => self.adc_izy(bus), 0xE9 => self.sbc_imm(bus), + 0xE5 => self.sbc_zp(bus), 0xF5 => self.sbc_zpx(bus), 0xED => self.sbc_abs(bus), + 0xFD => self.sbc_abx(bus), 0xF9 => self.sbc_aby(bus), 0xE1 => self.sbc_izx(bus), + 0xF1 => self.sbc_izy(bus), 0x24 => self.bit_zp(bus), 0x2C => self.bit_abs(bus), _ => panic!("illegal/unkown opcode {opcode:#04X} at {:#06X}", self.pc.wrapping_sub(1)), }; self.cycles += cycles as u64; cycles } + // NES 6502 opcode length table/matcher + pub fn opcode_len(opcode: u8) -> u8 { + match opcode { + 0x4C => 3, /* JMP abs */ 0xA2 => 2, /* LDX imm */ 0x86 => 2, /* STX zp */ 0x20 => 3, /* JSR abs */ + 0x60 => 1, /* RTS imp */ 0x48 => 1, /* PHA imp */ 0x68 => 1, /* PLA imp */ 0x08 => 1, /* PHP imp */ + 0x28 => 1, /* PLP imp */ 0x40 => 1, /* RTI imp */ 0x00 => 1, /* BRK imp */ 0xEA => 1, /* NOP */ + 0x38 => 1, /* SEC imp */ 0x18 => 1, /* CLC imp */ 0x58 => 1, /* CLI imp */ 0x78 => 1, /* SEI imp */ + 0xD8 => 1, /* CLD imp */ 0xF8 => 1, /* SED imp */ 0xB8 => 1, /* CLV imp */ 0x90 => 2, /* BCC rel */ + 0xB0 => 2, /* BCS 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 */ 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 */ 0xA6 => 2, /* LDX zp */ + 0xB6 => 2, /* LDX zpy */ 0xAE => 3, /* LDX abs */ 0xBE => 3, /* LDX aby */ 0xA0 => 2, /* LDY imm */ + 0xA4 => 2, /* LDY zp */ 0xB4 => 2, /* LDY zpx */ 0xAC => 3, /* LDY abs */ 0xBC => 3, /* LDY abx */ + 0x85 => 2, /* STA zp */ 0x95 => 2, /* STA zpx */ 0x8D => 3, /* STA abs */ 0x9D => 3, /* STA abx */ + 0x99 => 3, /* STA aby */ 0x81 => 2, /* STA izx */ 0x91 => 2, /* STA izy */ 0x96 => 2, /* STX zpy */ + 0x8E => 3, /* STX abs */ 0x84 => 2, /* STY zp */ 0x94 => 2, /* STY zpx */ 0x8C => 3, /* STY abs */ + 0xC9 => 2, /* CMP imm */ 0xC5 => 2, /* CMP zp */ 0xD5 => 2, /* CMP zpx */ 0xCD => 3, /* CMP abs */ + 0xDD => 3, /* CMP abx */ 0xD9 => 3, /* CMP aby */ 0xC1 => 2, /* CMP izx */ 0xD1 => 2, /* CMP izy */ + 0xE0 => 2, /* CPX imm */ 0xE4 => 2, /* CPX zp */ 0xEC => 3, /* CPX abs */ 0xC0 => 2, /* CPY imm */ + 0xC4 => 2, /* CPY zp */ 0xCC => 3, /* CPY abs */ 0x69 => 2, /* ADC imm */ 0x65 => 2, /* ADC zp */ + 0x75 => 2, /* ADC zpx */ 0x6D => 3, /* ADC abs */ 0x7D => 3, /* ADC abx */ 0x79 => 3, /* ADC aby */ + 0x61 => 2, /* ADC izx */ 0x71 => 2, /* ADC izy */ 0xE9 => 2, /* SBC imm */ 0xE5 => 2, /* SBC zp */ + 0xF5 => 2, /* SBC zpx */ 0xED => 3, /* SBC abs */ 0xFD => 3, /* SBC abx */ 0xF9 => 3, /* SBC aby */ + 0xE1 => 2, /* SBC izx */ 0xF1 => 2, /* SBC izy */ 0x24 => 2, /* BIT zp */ 0x2C => 3, /* BIT abs */ + _ => 1, // fallback, would show the invalid/unknown opcode only not its parameters + } + } + // Trace for testing the cpu pub fn trace(&self, bus: &mut impl Bus) -> String { let opcode: u8 = bus.read(self.pc); @@ -827,100 +808,5 @@ impl Cpu { 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 - pub fn opcode_len(opcode: u8) -> u8 { - match opcode { - 0x4C => 3, // JMP abs - 0xA2 => 2, // LDX imm - 0x86 => 2, // STX zp - 0x20 => 3, // JSR abs - 0x60 => 1, // RTS imp - 0x48 => 1, // PHA imp - 0x68 => 1, // PLA imp - 0x08 => 1, // PHP imp - 0x28 => 1, // PLP imp - 0x40 => 1, // RTI imp - 0x00 => 1, // BRK imp - 0xEA => 1, // NOP - 0x38 => 1, // SEC imp - 0x90 => 2, // BCC rel - 0xB0 => 2, // BCS 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 - 0x18 => 1, // CLC imp - 0x58 => 1, // CLI imp - 0xB8 => 1, // CLV imp - 0xD8 => 1, // CLD imp - 0x78 => 1, // SEI 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 - 0xA6 => 2, // LDX zp - 0xB6 => 2, // LDX zpy - 0xAE => 3, // LDX abs - 0xBE => 3, // LDX aby - 0xA0 => 2, // LDY imm - 0xA4 => 2, // LDY zp - 0xB4 => 2, // LDY zpx - 0xAC => 3, // LDY abs - 0xBC => 3, // LDY abx - 0x85 => 2, // STA zp - 0x95 => 2, // STA zpx - 0x8D => 3, // STA abs - 0x9D => 3, // STA abx - 0x99 => 3, // STA aby - 0x81 => 2, // STA izx - 0x91 => 2, // STA izy - 0x96 => 2, // STX zpy - 0x8E => 3, // STX abs - 0x84 => 2, // STY zp - 0x94 => 2, // STY zpx - 0x8C => 3, // STY abs - 0xC9 => 2, // CMP imm - 0xC5 => 2, // CMP zp - 0xD5 => 2, // CMP zpx - 0xCD => 3, // CMP abs - 0xDD => 3, // CMP abx - 0xD9 => 3, // CMP aby - 0xC1 => 2, // CMP izx - 0xD1 => 2, // CMP izy - 0xE0 => 2, // CPX imm - 0xE4 => 2, // CPX zp - 0xEC => 3, // CPX abs - 0xC0 => 2, // CPY imm - 0xC4 => 2, // CPY zp - 0xCC => 3, // CPY abs - 0x69 => 2, // ADC imm - 0x65 => 2, // ADC zp - 0x75 => 2, // ADC zpx - 0x6D => 3, // ADC abs - 0x7D => 3, // ADC abx - 0x79 => 3, // ADC aby - 0x61 => 2, // ADC izx - 0x71 => 2, // ADC izy - 0xE9 => 2, // SBC imm - 0xE5 => 2, // SBC zp - 0xF5 => 2, // SBC zpx - 0xED => 3, // SBC abs - 0xFD => 3, // SBC abx - 0xF9 => 3, // SBC aby - 0xE1 => 2, // SBC izx - 0xF1 => 2, // SBC izy - 0x24 => 2, // BIT zp - 0x2C => 3, // BIT abs - _ => 1, // fallback, would show the invalid/unknown opcode only not its parameters - } - } - }