use crate::bus::Bus; // Flag bit constants const FLAG_CARRY: u8 = 0b0000_0001; const FLAG_ZERO: u8 = 0b0000_0010; const FLAG_INTERRUPT: u8 = 0b0000_0100; const FLAG_DECIMAL: u8 = 0b0000_1000; const FLAG_BREAK: u8 = 0b0001_0000; const FLAG_UNUSED: u8 = 0b0010_0000; const FLAG_OVERFLOW: u8 = 0b0100_0000; const FLAG_NEGATIVE: u8 = 0b1000_0000; pub struct Cpu { a: u8, // Accumulator Register x: u8, // X Register y: u8, // Y Register sp: u8, // Stack Pointer pc: u16, // Program Counter p: u8, // Status Register cycles: u64, irq_pending: bool, nmi_pending: bool, } impl Cpu { // Testing related pub fn set_test_start(&mut self, pc: u16, cycles: u64) { self.pc = pc; self.cycles = cycles; } // Helpers for changing flags on the CPU fn set_flag(&mut self, bit: u8) { self.p |= bit; } fn clear_flag(&mut self, bit: u8) { self.p &= !bit; } fn flag(&self, bit: u8) -> bool { self.p & bit != 0 } fn set_zp_flags(&mut self, value: u8) { // Clear before setting self.clear_flag(FLAG_ZERO); self.clear_flag(FLAG_NEGATIVE); if value == 0 { self.set_flag(FLAG_ZERO); } // Set Zero if Zero if value & 0x80 != 0 { self.set_flag(FLAG_NEGATIVE) } // Set if Negative } // New and reset methods pub fn new() -> Self { Self { a: 0, x: 0, y: 0, sp: 0xFD, pc: 0, p: FLAG_UNUSED | FLAG_INTERRUPT, cycles: 0, irq_pending: false, nmi_pending: false } // kick stars a NES CPU } pub fn reset(&mut self, bus: &mut impl Bus) { self.sp = 0xFD; self.p = FLAG_UNUSED | FLAG_INTERRUPT; self.pc = self.read16(bus, 0xFFFC); } // Memory helpers fn fetch(&mut self, bus: &mut impl Bus) -> u8 { let value: u8 = bus.read(self.pc); self.pc = self.pc.wrapping_add(1); value } fn fetch16(&mut self, bus: &mut impl Bus) -> u16 { let low = self.fetch(bus) as u16; let high = self.fetch(bus) as u16; high << 8 | low } fn read(&self, bus: &mut impl Bus, address: u16) -> u8 { bus.read(address) } fn write(&mut self, bus: &mut impl Bus, address: u16, value: u8) { bus.write(address, value); } fn read16(&self, bus: &mut impl Bus, address: u16) -> u16 { let low: u16 = bus.read(address) as u16; let high: u16 = bus.read(address.wrapping_add(1)) as u16; high << 8 | low // returt in little-endian format } // Stack related Memory helpers fn push(&mut self, bus: &mut impl Bus, value: u8) { self.write(bus, 0x0100 + self.sp as u16, value); self.sp = self.sp.wrapping_sub(1); } fn pop(&mut self, bus: &mut impl Bus) -> u8 { self.sp = self.sp.wrapping_add(1); self.read(bus, 0x0100 + self.sp as u16) } // Step shell pub fn step(&mut self, bus: &mut impl Bus) -> u8 { let opcode: u8 = self.fetch(bus); let cycles = match opcode { // 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), // 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), // 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); let len: u8 = Self::opcode_len(opcode); let mut bytes: String = String::new(); for i in 0..len { bytes.push_str(&format!("{:02X} ", bus.read(self.pc.wrapping_add(i as u16)))); } format!( "{:04X} {:<8} A:{:02X} X:{:02X} Y:{:02X} P:{:02X} SP:{:02X} CYC:{}", self.pc, bytes, self.a, self.x, self.y, self.p, self.sp, self.cycles ) } // Address mode helpers fn address_zp(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Zero Page (self.fetch(bus) as u16, 0) } fn address_zpx(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Zero Page, X mode (self.fetch(bus).wrapping_add(self.x) as u16, 0) } fn address_zpy(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Zero Page, Y mode (self.fetch(bus).wrapping_add(self.y) as u16, 0) } fn address_abs(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Absolute address (self.fetch16(bus), 0) } fn address_abx(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Absolute, X address let base: u16 = self.fetch16(bus); let address: u16 = base.wrapping_add(self.x as u16); let extra: u8 = if (base & 0xFF00) != (address & 0xFF00) { 1 } else { 0 }; (address, extra) } fn address_aby(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Absolute, Y address let base: u16 = self.fetch16(bus); let address: u16 = base.wrapping_add(self.y as u16); let extra: u8 = if (base & 0xFF00) != (address & 0xFF00) { 1 } else { 0 }; (address, extra) } fn address_izx(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Index-Indirect mode + X let zp: u8 = self.fetch(bus); // Operand: Address on Zero-Page let ptr_address: u8 = zp.wrapping_add(self.x); // We add x to it wrapped let low: u16 = self.read(bus, ptr_address as u16) as u16; let high: u16 = self.read(bus, ptr_address.wrapping_add(1) as u16) as u16; (high << 8 | low, 0) // Return in little endian, 16-bit pointer } fn address_izy(&mut self, bus: &mut impl Bus) -> (u16, u8) { // Index-Indirect mode + Y let zp: u8 = self.fetch(bus); // Operand: Address on Zero-Page let low: u16 = self.read(bus, zp as u16) as u16; let high: u16 = self.read(bus, zp.wrapping_add(1) as u16) as u16; let base: u16 = high << 8 | low; // 16-bit pointer to somewhere on WRAM, little-endian let address: u16 = base.wrapping_add(self.y as u16); // Add Y to the 16-bit pointer let extra: u8 = if (base & 0xFF00) != (address & 0xFF00) { 1 } else { 0 }; (address, extra) } fn address_ind(&mut self, bus: &mut impl Bus) -> (u16, u8) { // used by JMP instruction + hardware bug where the page wraps around let ptr: u16 = self.fetch16(bus); // pointer itself let low: u16 = self.read(bus, ptr) as u16; let high_ptr: u16 = (ptr & 0xFF00) | (ptr.wrapping_add(1) & 0x00FF); // Hardware bug wrap here let high = self.read(bus, high_ptr) as u16; (high << 8 | low, 0) // little-endian Return } fn address_rel(&mut self, bus: &mut impl Bus) -> u16 { // Branch relative let offset: u8 = self.fetch(bus); // signed 8-bit offset from argument 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 } // 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 } fn ldx(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { self.x = self.read(bus, address); self.set_zp_flags(self.x); base + extra } fn ldy(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { self.y = self.read(bus, address); self.set_zp_flags(self.y); base + extra } // Shared cores for STx instructions fn sta(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { self.write(bus, address, self.a); base + extra } fn stx(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { self.write(bus, address, self.x); base + extra } fn sty(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { self.write(bus, address, self.y); base + extra } // shared cores for CMP and CPx instructions fn cmp(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { let value = self.read(bus, address); let result = self.a.wrapping_sub(value); if self.a >= value { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); } self.set_zp_flags(result); base + extra } fn cpx(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { let value = self.read(bus, address); let result = self.x.wrapping_sub(value); if self.x >= value { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); } self.set_zp_flags(result); base + extra } fn cpy(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { let value = self.read(bus, address); let result = self.y.wrapping_sub(value); if self.y >= value { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); } self.set_zp_flags(result); base + extra } // shared add/sbc cores (based on adding the complement) fn add_with_carry(&mut self, value: u8) { let carry = if self.flag(FLAG_CARRY) { 1u16 } else { 0 }; let sum = self.a as u16 + value as u16 + carry; let result = sum as u8; if sum > 0xFF { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); } if (self.a ^ result) & (value ^ result) & 0x80 != 0 { self.set_flag(FLAG_OVERFLOW); } else { self.clear_flag(FLAG_OVERFLOW); } self.set_zp_flags(result); self.a = result; } fn adc(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { let value = self.read(bus, address); self.add_with_carry(value); // add as-is base + extra } fn sbc(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { let value = self.read(bus, address); self.add_with_carry(!value); // add the complement => subtract base + extra } // BIT core fn bit(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 { let value = self.read(bus, address); self.clear_flag(FLAG_ZERO); if self.a & value == 0 { self.set_flag(FLAG_ZERO); } self.clear_flag(FLAG_NEGATIVE); if value & 0x80 != 0 { self.set_flag(FLAG_NEGATIVE); } self.clear_flag(FLAG_OVERFLOW); if value & 0x40 != 0 { self.set_flag(FLAG_OVERFLOW); } base + extra } // NES 6502 opcode -> functions fn jmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4C, 3 bytes, 3 cycles self.pc = self.fetch16(bus); 3 } fn jsr_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x20, 3 bytes, 6 cycles let target = self.fetch16(bus); let ret = self.pc.wrapping_sub(1); self.push(bus, (ret >> 8) as u8); self.push(bus, (ret &0xFF) as u8); self.pc = target; 6 } fn rts_imp(&mut self, bus: &mut impl Bus) -> u8 { // 0x60, 1 byte, 6 cycles let low = self.pop(bus) as u16; let high = self.pop(bus) as u16; self.pc = (high << 8 | low).wrapping_add(1); 6 } fn pha_imp(&mut self, bus: &mut impl Bus) -> u8 { // 0x48, 1 byte, 3 cycles self.push(bus, self.a); 3 } fn pla_imp(&mut self, bus: &mut impl Bus) -> u8 { // 0x68, 1 byte, 4 cycles self.a = self.pop(bus); self.set_zp_flags(self.a); 4 } fn php_imp(&mut self, bus: &mut impl Bus) -> u8 { // 0x08, 1 byte, 3 cycles; B-flag model: B exists only in the pushed copy self.push(bus, self.p | FLAG_BREAK); 3 } fn plp_imp(&mut self, bus: &mut impl Bus) -> u8 { // 0x28, 1 byte, 4 cycles self.p = (self.pop(bus) & !FLAG_BREAK) | FLAG_UNUSED; 4 } fn rti_imp(&mut self, bus: &mut impl Bus) -> u8 { // 0x40, 1 byte, 6 cycles self.p = (self.pop(bus) & !FLAG_BREAK) | FLAG_UNUSED; let low: u16 = self.pop(bus) as u16; let high: u16 = self.pop(bus) as u16; self.pc = high << 8 | low; 6 } fn brk_imp(&mut self, bus: &mut impl Bus) -> u8 { // 0x00, 1 byte, 7 cycles self.pc = self.pc.wrapping_add(1); // Skip padding byte let ret = self.pc; self.push(bus, (ret >> 8) as u8); self.push(bus, (ret & 0xFF) as u8); self.push(bus, self.p | FLAG_BREAK); self.set_flag(FLAG_INTERRUPT); self.pc = self.read16(bus, 0xFFFE); // IRQ vector 7 } fn nop_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xEA, 1 byte, 2 cycles 2 } fn sec_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x38, 1 byte, 2 cycles self.set_flag(FLAG_CARRY); 2 } fn clc_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x18, 1 byte, 2 cycles self.clear_flag(FLAG_CARRY); 2 } fn cli_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x58, 1 byte, 2 cycles self.clear_flag(FLAG_INTERRUPT); 2 } fn clv_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xB8, 1 byte, 2 cycles self.clear_flag(FLAG_OVERFLOW); 2 } fn cld_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xD8, 1 byte, 2 cycles self.clear_flag(FLAG_DECIMAL); 2 } fn sei_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x78, 1 byte, 2 cycles self.set_flag(FLAG_INTERRUPT); 2 } fn sed_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xF8, 1 byte, 2 cycles self.set_flag(FLAG_DECIMAL); 2 } // LDA alternatives 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) } // LDX alternatives fn ldx_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xA6, 2 bytes let (address, extra) = self.address_zp(bus); self.ldx(bus, (address, extra), 3) } fn ldx_zpy(&mut self, bus: &mut impl Bus) -> u8 { // 0xB6, 2 bytes let (address, extra) = self.address_zpy(bus); self.ldx(bus, (address, extra), 4) } fn ldx_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xAE, 3 bytes let (address, extra) = self.address_abs(bus); self.ldx(bus, (address, extra), 4) } fn ldx_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0xBE, 3 bytes let (address, extra) = self.address_aby(bus); self.ldx(bus, (address, extra), 4) } // LDY alternatives fn ldy_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xA4, 2 bytes let (address, extra) = self.address_zp(bus); self.ldy(bus, (address, extra), 3) } fn ldy_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0xB4, 2 bytes let (address, extra) = self.address_zpx(bus); self.ldy(bus, (address, extra), 4) } fn ldy_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xAC, 3 bytes let (address, extra) = self.address_abs(bus); self.ldy(bus, (address, extra), 4) } fn ldy_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0xBC, 3 bytes let (address, extra) = self.address_abx(bus); self.ldy(bus, (address, extra), 4) } // imm variants for LDx fn lda_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xA9, 2 bytes, 2 cycles self.a = self.fetch(bus); self.set_zp_flags(self.a); 2 } fn ldx_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xA2, 2 bytes, 2 cycles self.x = self.fetch(bus); self.set_zp_flags(self.x); 2 } fn ldy_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xA0, 2 bytes, 2 cycles self.y = self.fetch(bus); self.set_zp_flags(self.y); 2 } // STA variants fn sta_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x85, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.sta(bus, (address, extra), 3) } fn sta_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x95, 2 bytes, 4 cycles let (address, extra) = self.address_zpx(bus); self.sta(bus, (address, extra), 4) } fn sta_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x8D, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.sta(bus, (address, extra), 4) } fn sta_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x9D, 3 bytes, 4 cycles let (address, extra) = self.address_abx(bus); self.sta(bus, (address, extra), 4) } fn sta_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x99, 3 bytes, 4 cycles let (address, extra) = self.address_aby(bus); self.sta(bus, (address, extra), 4) } fn sta_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0x81, 2 bytes, 6 cycles let (address, extra) = self.address_izx(bus); self.sta(bus, (address, extra), 6) } fn sta_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x91, 2 bytes, 5 cycles let (address, extra) = self.address_izy(bus); self.sta(bus, (address, extra), 5) } // STX variants fn stx_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x86, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.stx(bus, (address, extra), 3) } fn stx_zpy(&mut self, bus: &mut impl Bus) -> u8 { // 0x96, 2 bytes, 4 cycles let (address, extra) = self.address_zpy(bus); self.stx(bus, (address, extra), 4) } fn stx_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x8E, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.stx(bus, (address, extra), 4) } // STY variants fn sty_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x84, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.sty(bus, (address, extra), 3) } fn sty_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x94, 2 bytes, 4 cycles let (address, extra) = self.address_zpx(bus); self.sty(bus, (address, extra), 4) } fn sty_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x8C, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.sty(bus, (address, extra), 4) } // CMP variants fn cmp_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xC5, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.cmp(bus, (address, extra), 3) } fn cmp_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0xD5, 2 bytes, 4 cycles let (address, extra) = self.address_zpx(bus); self.cmp(bus, (address, extra), 4) } fn cmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xCD, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.cmp(bus, (address, extra), 4) } fn cmp_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0xDD, 3 bytes, 4 cycles let (address, extra) = self.address_abx(bus); self.cmp(bus, (address, extra), 4) } fn cmp_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0xD9, 3 bytes, 4 cycles let (address, extra) = self.address_aby(bus); self.cmp(bus, (address, extra), 4) } fn cmp_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0xC1, 2 bytes, 6 cycles let (address, extra) = self.address_izx(bus); self.cmp(bus, (address, extra), 6) } fn cmp_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0xD1, 2 bytes, 5 cycles let (address, extra) = self.address_izy(bus); self.cmp(bus, (address, extra), 5) } // CPX variants fn cpx_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xE4, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.cpx(bus, (address, extra), 3) } fn cpx_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xEC, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.cpx(bus, (address, extra), 4) } // CPY variants fn cpy_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xC4, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.cpy(bus, (address, extra), 3) } fn cpy_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xCC, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.cpy(bus, (address, extra), 4) } // CMP / CPx immediate mode variants fn cmp_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xC9, 2 bytes, 2 cycles let value = self.fetch(bus); let result = self.a.wrapping_sub(value); if self.a >= value { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); } self.set_zp_flags(result); 2 } fn cpx_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xE0, 2 bytes, 2 cycles let value = self.fetch(bus); let result = self.x.wrapping_sub(value); if self.x >= value { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); } self.set_zp_flags(result); 2 } fn cpy_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xC0, 2 bytes, 2 cycles let value = self.fetch(bus); let result = self.y.wrapping_sub(value); if self.y >= value { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); } self.set_zp_flags(result); 2 } // ADC variants fn adc_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x65, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.adc(bus, (address, extra), 3) } fn adc_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x75, 2 bytes, 4 cycles let (address, extra) = self.address_zpx(bus); self.adc(bus, (address, extra), 4) } fn adc_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x6D, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.adc(bus, (address, extra), 4) } fn adc_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x7D, 3 bytes, 4 cycles let (address, extra) = self.address_abx(bus); self.adc(bus, (address, extra), 4) } fn adc_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x79, 3 bytes, 4 cycles let (address, extra) = self.address_aby(bus); self.adc(bus, (address, extra), 4) } fn adc_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0x61, 2 bytes, 6 cycles let (address, extra) = self.address_izx(bus); self.adc(bus, (address, extra), 6) } fn adc_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x71, 2 bytes, 5 cycles let (address, extra) = self.address_izy(bus); self.adc(bus, (address, extra), 5) } // SBC variants fn sbc_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xE5, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.sbc(bus, (address, extra), 3) } fn sbc_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0xF5, 2 bytes, 4 cycles let (address, extra) = self.address_zpx(bus); self.sbc(bus, (address, extra), 4) } fn sbc_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xED, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.sbc(bus, (address, extra), 4) } fn sbc_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0xFD, 3 bytes, 4 cycles let (address, extra) = self.address_abx(bus); self.sbc(bus, (address, extra), 4) } fn sbc_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0xF9, 3 bytes, 4 cycles let (address, extra) = self.address_aby(bus); self.sbc(bus, (address, extra), 4) } fn sbc_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0xE1, 2 bytes, 6 cycles let (address, extra) = self.address_izx(bus); self.sbc(bus, (address, extra), 6) } fn sbc_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0xF1, 2 bytes, 5 cycles let (address, extra) = self.address_izy(bus); self.sbc(bus, (address, extra), 5) } // BIT variants fn bit_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x24, 2 bytes, 3 cycles let (address, extra) = self.address_zp(bus); self.bit(bus, (address, extra), 3) } fn bit_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x2C, 3 bytes, 4 cycles let (address, extra) = self.address_abs(bus); self.bit(bus, (address, extra), 4) } // ADC/SBC immediate variants fn adc_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0x69, 2 bytes, 2 cycles let value = self.fetch(bus); self.add_with_carry(value); 2 } fn sbc_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xE9, 2 bytes, 2 cycles let value = self.fetch(bus); self.add_with_carry(!value); 2 } // 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 bcc_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, !self.flag(FLAG_CARRY)) } // 0x90 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 }