Files
rnes/src/cpu.rs
T

1339 lines
52 KiB
Rust

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
pub 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)
}
// public API for other hardware
pub fn set_nmi(&mut self) { self.nmi_pending = true; }
pub fn set_irq(&mut self) { self.irq_pending = true; }
// shared interrumpt function
fn interrupt(&mut self, bus: &mut impl Bus, vector: u16) -> u8 {
self.push(bus, (self.pc >> 8) as u8);
self.push(bus, (self.pc & 0xFF) as u8);
self.push(bus, self.p); // B clear (unlike BRK)
self.set_flag(FLAG_INTERRUPT);
self.pc = self.read16(bus, vector);
7
}
// Step shell
pub fn step(&mut self, bus: &mut impl Bus) -> u8 {
if self.nmi_pending {
self.nmi_pending = false;
let cycles = self.interrupt(bus, 0xFFFA);
self.cycles += cycles as u64;
return cycles;
}
if self.irq_pending && !self.flag(FLAG_INTERRUPT) {
self.irq_pending = false;
let cycles = self.interrupt(bus, 0xFFFE);
self.cycles += cycles as u64;
return cycles;
}
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), 0x6C => self.jmp_ind(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),
// Binary - AND
0x29 => self.and_imm(bus), 0x25 => self.and_zp(bus), 0x35 => self.and_zpx(bus),
0x2D => self.and_abs(bus), 0x3D => self.and_abx(bus), 0x39 => self.and_aby(bus),
0x21 => self.and_izx(bus), 0x31 => self.and_izy(bus),
// Binary - ORA
0x09 => self.ora_imm(bus), 0x05 => self.ora_zp(bus), 0x15 => self.ora_zpx(bus),
0x0D => self.ora_abs(bus), 0x1D => self.ora_abx(bus), 0x19 => self.ora_aby(bus),
0x01 => self.ora_izx(bus), 0x11 => self.ora_izy(bus),
// Binary - EOR
0x49 => self.eor_imm(bus), 0x45 => self.eor_zp(bus), 0x55 => self.eor_zpx(bus),
0x4D => self.eor_abs(bus), 0x5D => self.eor_abx(bus), 0x59 => self.eor_aby(bus),
0x41 => self.eor_izx(bus), 0x51 => self.eor_izy(bus),
// Increments
0xE6 => self.inc_zp(bus), 0xF6 => self.inc_zpx(bus), 0xEE => self.inc_abs(bus),
0xFE => self.inc_abx(bus), 0xE8 => self.inx_imp(bus), 0xC8 => self.iny_imp(bus),
// Decrements
0xC6 => self.dec_zp(bus), 0xD6 => self.dec_zpx(bus), 0xCE => self.dec_abs(bus),
0xDE => self.dec_abx(bus), 0xCA => self.dex_imp(bus), 0x88 => self.dey_imp(bus),
// Shifts
0x0A => self.asl_acc(bus), 0x06 => self.asl_zp(bus), 0x16 => self.asl_zpx(bus),
0x0E => self.asl_abs(bus), 0x1E => self.asl_abx(bus),
0x4A => self.lsr_acc(bus), 0x46 => self.lsr_zp(bus), 0x56 => self.lsr_zpx(bus),
0x4E => self.lsr_abs(bus), 0x5E => self.lsr_abx(bus),
0x2A => self.rol_acc(bus), 0x26 => self.rol_zp(bus), 0x36 => self.rol_zpx(bus),
0x2E => self.rol_abs(bus), 0x3E => self.rol_abx(bus),
0x6A => self.ror_acc(bus), 0x66 => self.ror_zp(bus), 0x76 => self.ror_zpx(bus),
0x6E => self.ror_abs(bus), 0x7E => self.ror_abx(bus),
// Transfers
0xAA => self.tax_imp(bus), 0xA8 => self.tay_imp(bus), 0x8A => self.txa_imp(bus),
0x98 => self.tya_imp(bus), 0xBA => self.tsx_imp(bus), 0x9A => self.txs_imp(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; note: this one could be seem as redudant but i like it so deal with it >:3, also it would be used by the GUI later
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 */
0x29 => 2, /* AND imm */ 0x25 => 2, /* AND zp */ 0x35 => 2, /* AND zpx */ 0x2D => 3, /* AND abs */
0x3D => 3, /* AND abx */ 0x39 => 3, /* AND aby */ 0x21 => 2, /* AND izx */ 0x31 => 2, /* AND izy */
0x09 => 2, /* ORA imm */ 0x05 => 2, /* ORA zp */ 0x15 => 2, /* ORA zpx */ 0x0D => 3, /* ORA abs */
0x1D => 3, /* ORA abx */ 0x19 => 3, /* ORA aby */ 0x01 => 2, /* ORA izx */ 0x11 => 2, /* ORA izy */
0x49 => 2, /* EOR imm */ 0x45 => 2, /* EOR zp */ 0x55 => 2, /* EOR zpx */ 0x4D => 3, /* EOR abs */
0x5D => 3, /* EOR abx */ 0x59 => 3, /* EOR aby */ 0x41 => 2, /* EOR izx */ 0x51 => 2, /* EOR izy */
0xE6 => 2, /* INC zp */ 0xF6 => 2, /* INC zpx */ 0xEE => 3, /* INC abs */ 0xFE => 3, /* INC abx */
0xE8 => 1, /* INX imp */ 0xC8 => 1, /* INY imp */ 0xC6 => 2, /* DEC zp */ 0xD6 => 2, /* DEC zpx */
0xCE => 3, /* DEC abs */ 0xDE => 3, /* DEC abx */ 0xCA => 1, /* DEX imp */ 0x88 => 1, /* DEY imp */
0x0A => 1, /* ASL A */ 0x06 => 2, /* ASL zp */ 0x16 => 2, /* ASL zpx */ 0x0E => 3, /* ASL abs */
0x1E => 3, /* ASL abx */ 0x4A => 1, /* LSR A */ 0x46 => 2, /* LSR zp */ 0x56 => 2, /* LSR zpx */
0x4E => 3, /* LSR abs */ 0x5E => 3, /* LSR abx */ 0x2A => 1, /* ROL A */ 0x26 => 2, /* ROL zp */
0x36 => 2, /* ROL zpx */ 0x2E => 3, /* ROL abs */ 0x3E => 3, /* ROL abx */ 0x6A => 1, /* ROR A */
0x66 => 2, /* ROR zp */ 0x76 => 2, /* ROR zpx */ 0x6E => 3, /* ROR abs */ 0x7E => 3, /* ROR abx */
0xAA => 1, /* TAX */ 0xA8 => 1, /* TAY */ 0x8A => 1, /* TXA */ 0x98 => 1, /* TYA */
0xBA => 1, /* TSX */ 0x9A => 1, /* TXS */ 0x6C => 3, /* JMP ind */
_ => 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 (indirect) 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
}
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
}
// AND core (AND binary operation)
fn and(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
let value = self.read(bus, address);
self.a &= value;
self.set_zp_flags(self.a);
base + extra
}
// ORA core (OR binary operation)
fn ora(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
let value = self.read(bus, address);
self.a |= value;
self.set_zp_flags(self.a);
base + extra
}
// EOR core (Exclusive-OR binary operation)
fn eor(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
let value = self.read(bus, address);
self.a ^= value;
self.set_zp_flags(self.a);
base + extra
}
// INC core
fn inc(&mut self, bus: &mut impl Bus, (address, _extra): (u16, u8), base: u8) -> u8 {
let value = self.read(bus, address).wrapping_add(1);
self.write(bus, address, value);
self.set_zp_flags(value);
base // NO + extra — RMW cycles are fixed
}
// DEC core
fn dec(&mut self, bus: &mut impl Bus, (address, _extra): (u16, u8), base: u8) -> u8 {
let value = self.read(bus, address).wrapping_sub(1);
self.write(bus, address, value);
self.set_zp_flags(value);
base // NO + extra — RMW cycles are fixed
}
// Shared shifting cores
// ASL: arithmetic shift left
fn shift_asl(&mut self, value: u8) -> u8 {
if value & 0x80 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = value << 1;
self.set_zp_flags(result);
result
}
// LSR: arithmetic shift right
fn shift_lsr(&mut self, value: u8) -> u8 {
if value & 0x01 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = value >> 1;
self.set_zp_flags(result);
result
}
// ROL: rotate left through carry
fn shift_rol(&mut self, value: u8) -> u8 {
let carry_in = if self.flag(FLAG_CARRY) { 1u8 } else { 0 };
if value & 0x80 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = (value << 1) | carry_in;
self.set_zp_flags(result);
result
}
// ROR: rotate right through carry
fn shift_ror(&mut self, value: u8) -> u8 {
let carry_in = if self.flag(FLAG_CARRY) { 1u8 } else { 0 };
if value & 0x01 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = (value >> 1) | (carry_in << 7);
self.set_zp_flags(result);
result
}
// 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 + 1 cycles
let (address, extra) = self.address_abx(bus);
self.sta(bus, (address, extra), 5)
}
fn sta_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x99, 3 bytes, 4 + 1 cycles
let (address, extra) = self.address_aby(bus);
self.sta(bus, (address, extra), 5)
}
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, 6 cycles
let (address, extra) = self.address_izy(bus);
self.sta(bus, (address, extra), 6)
}
// 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
}
// AND variants
fn and_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x25, 2 bytes, 3 cycles
let (address, extra) = self.address_zp(bus);
self.and(bus, (address, extra), 3)
}
fn and_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x35, 2 bytes, 4 cycles
let (address, extra) = self.address_zpx(bus);
self.and(bus, (address, extra), 4)
}
fn and_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x2D, 3 bytes, 4 cycles
let (address, extra) = self.address_abs(bus);
self.and(bus, (address, extra), 4)
}
fn and_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x3D, 3 bytes, 4 cycles
let (address, extra) = self.address_abx(bus);
self.and(bus, (address, extra), 4)
}
fn and_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x39, 3 bytes, 4 cycles
let (address, extra) = self.address_aby(bus);
self.and(bus, (address, extra), 4)
}
fn and_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0x21, 2 bytes, 6 cycles
let (address, extra) = self.address_izx(bus);
self.and(bus, (address, extra), 6)
}
fn and_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x31, 2 bytes, 5 cycles
let (address, extra) = self.address_izy(bus);
self.and(bus, (address, extra), 5)
}
// ORA variants
fn ora_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x05, 2 bytes, 3 cycles
let (address, extra) = self.address_zp(bus);
self.ora(bus, (address, extra), 3)
}
fn ora_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x15, 2 bytes, 4 cycles
let (address, extra) = self.address_zpx(bus);
self.ora(bus, (address, extra), 4)
}
fn ora_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x0D, 3 bytes, 4 cycles
let (address, extra) = self.address_abs(bus);
self.ora(bus, (address, extra), 4)
}
fn ora_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x1D, 3 bytes, 4 cycles
let (address, extra) = self.address_abx(bus);
self.ora(bus, (address, extra), 4)
}
fn ora_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x19, 3 bytes, 4 cycles
let (address, extra) = self.address_aby(bus);
self.ora(bus, (address, extra), 4)
}
fn ora_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0x01, 2 bytes, 6 cycles
let (address, extra) = self.address_izx(bus);
self.ora(bus, (address, extra), 6)
}
fn ora_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x11, 2 bytes, 5 cycles
let (address, extra) = self.address_izy(bus);
self.ora(bus, (address, extra), 5)
}
// EOR variants
fn eor_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x45, 2 bytes, 3 cycles
let (address, extra) = self.address_zp(bus);
self.eor(bus, (address, extra), 3)
}
fn eor_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x55, 2 bytes, 4 cycles
let (address, extra) = self.address_zpx(bus);
self.eor(bus, (address, extra), 4)
}
fn eor_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4D, 3 bytes, 4 cycles
let (address, extra) = self.address_abs(bus);
self.eor(bus, (address, extra), 4)
}
fn eor_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x5D, 3 bytes, 4 cycles
let (address, extra) = self.address_abx(bus);
self.eor(bus, (address, extra), 4)
}
fn eor_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x59, 3 bytes, 4 cycles
let (address, extra) = self.address_aby(bus);
self.eor(bus, (address, extra), 4)
}
fn eor_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0x41, 2 bytes, 6 cycles
let (address, extra) = self.address_izx(bus);
self.eor(bus, (address, extra), 6)
}
fn eor_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x51, 2 bytes, 5 cycles
let (address, extra) = self.address_izy(bus);
self.eor(bus, (address, extra), 5)
}
// AND immediate variant
fn and_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0x29, 2 bytes, 2 cycles
self.a &= self.fetch(bus);
self.set_zp_flags(self.a);
2
}
// ORA immediate variant
fn ora_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0x09, 2 bytes, 2 cycles
self.a |= self.fetch(bus);
self.set_zp_flags(self.a);
2
}
// EOR immediate variant
fn eor_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0x49, 2 bytes, 2 cycles
self.a ^= self.fetch(bus);
self.set_zp_flags(self.a);
2
}
// INC variants
fn inc_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xE6, 2 bytes, 5 cycles
let (address, extra) = self.address_zp(bus);
self.inc(bus, (address, extra), 5)
}
fn inc_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0xF6, 2 bytes, 6 cycles
let (address, extra) = self.address_zpx(bus);
self.inc(bus, (address, extra), 6)
}
fn inc_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xEE, 3 bytes, 6 cycles
let (address, extra) = self.address_abs(bus);
self.inc(bus, (address, extra), 6)
}
fn inc_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0xFE, 3 bytes, 7 cycles
let (address, extra) = self.address_abx(bus);
self.inc(bus, (address, extra), 7)
}
// DEC variants
fn dec_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xC6, 2 bytes, 5 cycles
let (address, extra) = self.address_zp(bus);
self.dec(bus, (address, extra), 5)
}
fn dec_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0xD6, 2 bytes, 6 cycles
let (address, extra) = self.address_zpx(bus);
self.dec(bus, (address, extra), 6)
}
fn dec_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xCE, 3 bytes, 6 cycles
let (address, extra) = self.address_abs(bus);
self.dec(bus, (address, extra), 6)
}
fn dec_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0xDE, 3 bytes, 7 cycles
let (address, extra) = self.address_abx(bus);
self.dec(bus, (address, extra), 7)
}
// INC register variants
fn inx_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xE8, 1 byte, 2 cycles
self.x = self.x.wrapping_add(1);
self.set_zp_flags(self.x);
2
}
fn iny_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xC8, 1 byte, 2 cycles
self.y = self.y.wrapping_add(1);
self.set_zp_flags(self.y);
2
}
// DEC register variants
fn dex_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xCA, 1 byte, 2 cycles
self.x = self.x.wrapping_sub(1);
self.set_zp_flags(self.x);
2
}
fn dey_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x88, 1 byte, 2 cycles
self.y = self.y.wrapping_sub(1);
self.set_zp_flags(self.y);
2
}
// ASL variants
fn asl_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x0A, 1 byte, 2 cycles
self.a = self.shift_asl(self.a);
2
}
fn asl_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x06, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
5
}
fn asl_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x16, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
6
}
fn asl_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x0E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
6
}
fn asl_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x1E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
7
}
// LSR variants
fn lsr_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x4A, 1 byte, 2 cycles
self.a = self.shift_lsr(self.a);
2
}
fn lsr_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x46, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
5
}
fn lsr_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x56, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
6
}
fn lsr_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
6
}
fn lsr_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x5E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
7
}
// ROL variants
fn rol_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x2A, 1 byte, 2 cycles
self.a = self.shift_rol(self.a);
2
}
fn rol_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x26, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
5
}
fn rol_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x36, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
6
}
fn rol_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x2E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
6
}
fn rol_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x3E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
7
}
// ROR variants
fn ror_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x6A, 1 byte, 2 cycles
self.a = self.shift_ror(self.a);
2
}
fn ror_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x66, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
5
}
fn ror_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x76, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
6
}
fn ror_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x6E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
6
}
fn ror_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x7E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
7
}
// Transfers (all 1 byte, 2 cycles) TAX/TXx family
fn tax_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xAA: A -> X
self.x = self.a;
self.set_zp_flags(self.x);
2
}
fn tay_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xA8: A -> Y
self.y = self.a;
self.set_zp_flags(self.y);
2
}
fn txa_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x8A: X -> A
self.a = self.x;
self.set_zp_flags(self.a);
2
}
fn tya_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x98: Y -> A
self.a = self.y;
self.set_zp_flags(self.a);
2
}
fn tsx_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xBA: SP -> X
self.x = self.sp;
self.set_zp_flags(self.x);
2
}
fn txs_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x9A: X -> SP, NO flags
self.sp = self.x;
2
}
// JMP indirect
fn jmp_ind(&mut self, bus: &mut impl Bus) -> u8 { // 0x6C, 3 bytes, 5 cycles
let (addr, _) = self.address_ind(bus);
self.pc = addr;
5
}
// 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
}