#![allow(clippy::upper_case_acronyms)] use core::panic; use std::process::exit; use termion::cursor::Goto; use termion::{clear, color}; use crate::cpu::{Cpu, StatusFlag}; use crate::memory::{MemoryReader, MemoryWriter}; type InstrFn = fn(&mut Cpu, Option); type AddressFn = fn(&mut Cpu) -> u16; #[derive(Clone, Copy, Debug)] pub struct Instruction<'a> { pub instr_fn: Option, pub address_fn: Option, pub cycles: u8, pub name: &'a str, pub addr_mode: &'a str, } impl Instruction<'_> { pub fn call(&self, cpu: &mut Cpu) { // TODO: add flag to print these // print!("{}{}a: {a:#04x}, x: {x:#04x}, y: {y:#04x}, pc: {pc:#06x}, sp: {s:#04x}, sr: {p:#010b}, irq: {irq:?}, nmi: {nmi:?}", Goto(0, 31),clear::UntilNewline, a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); // print!( // "{}instruction: {:?}, pc: {:#04x}", // Goto(0, 31), // self.name, // cpu.pc // ); // print!( // "{}{}instruction: {:?}, mode: {:?}", // Goto(0, 32), // clear::UntilNewline, // self.name, // self.addr_mode // ); // print!( // "{}{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {}{}{:02x}{}{} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", // Goto(0, 33), // cpu.read(cpu.pc.wrapping_sub(8)), // cpu.read(cpu.pc.wrapping_sub(7)), // cpu.read(cpu.pc.wrapping_sub(6)), // cpu.read(cpu.pc.wrapping_sub(5)), // cpu.read(cpu.pc.wrapping_sub(4)), // cpu.read(cpu.pc.wrapping_sub(3)), // cpu.read(cpu.pc.wrapping_sub(2)), // cpu.read(cpu.pc.wrapping_sub(1)), // color::Bg(color::Rgb(0xFF, 0xCC, 0x00)), // color::Fg(color::Rgb(0x11, 0x05, 0x00)), // cpu.read(cpu.pc), // color::Fg(color::Rgb(0xFF, 0xCC, 0x00)), // color::Bg(color::Rgb(0x11, 0x05, 0x00)), // cpu.read(cpu.pc.wrapping_add(1)), // cpu.read(cpu.pc.wrapping_add(2)), // cpu.read(cpu.pc.wrapping_add(3)), // cpu.read(cpu.pc.wrapping_add(4)), // cpu.read(cpu.pc.wrapping_add(5)), // cpu.read(cpu.pc.wrapping_add(6)), // cpu.read(cpu.pc.wrapping_add(7)), // cpu.read(cpu.pc.wrapping_add(8)), // ); cpu.pc = cpu.pc.wrapping_add(1); // read instruction byte match self.instr_fn { // existence of instr_fn means this is a valid instruction Some(instr_fn) => { cpu.pending_cycles += self.cycles as usize; match self.address_fn { // if we have address_fn, that means // addressing mode isn't implied/stack, // so we need to get an address Some(address_fn) => { let address = address_fn(cpu); instr_fn(cpu, Some(address)); } None => { instr_fn(cpu, None); } // None for address_fn implies it's implied (lol)/stack } } None => { panic!( "An invalid instruction was called at {:04x}, with opcode {:02x}", cpu.pc, cpu.read(cpu.pc) ) } // idk if we should panic here, if we don't it's undefined behavior, which // might actually be what we want in the cases where it's an undocumented // 6502 instruction, but otherwise we should panic prolly, for now i'm just // gonna panic } } } fn accumulator(cpu: &mut Cpu) -> u16 { let byte = cpu.a; byte as u16 } fn immediate(cpu: &mut Cpu) -> u16 { // # let address: u16 = cpu.pc; cpu.pc = cpu.pc.wrapping_add(1); address } fn absolute_a(cpu: &mut Cpu) -> u16 { // a let address = cpu.read_word(cpu.pc); cpu.pc = cpu.pc.wrapping_add(2); address } fn zero_page(cpu: &mut Cpu) -> u16 { // zp let address = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); address as u16 } fn absolute_indexed_with_x( // a, y cpu: &mut Cpu, ) -> u16 { let word = cpu.read_word(cpu.pc); cpu.pc = cpu.pc.wrapping_add(2); let address: u16 = word + cpu.x as u16; address } fn absolute_indexed_with_y( // a, y cpu: &mut Cpu, ) -> u16 { let word = cpu.read_word(cpu.pc); cpu.pc = cpu.pc.wrapping_add(2); let address: u16 = word + cpu.y as u16; address } fn zero_page_indexed_with_x( // zp, x cpu: &mut Cpu, ) -> u16 { let byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); let address: u16 = (byte.wrapping_add(cpu.x)) as u16; address } fn zero_page_indexed_with_y( // zp, y cpu: &mut Cpu, ) -> u16 { let byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); let address: u16 = (byte + cpu.y) as u16; address } fn absolute_indirect( // (a) cpu: &mut Cpu, ) -> u16 { let word = cpu.read_word(cpu.pc); let address = cpu.read_word(word); cpu.pc = cpu.pc.wrapping_add(2); address } fn absolute_indexed_indirect( // (a, x), only used with the JMP instruction cpu: &mut Cpu, ) -> u16 { let word = cpu.read_word(cpu.pc); let address = cpu.read_word(word + cpu.x as u16); cpu.pc = cpu.pc.wrapping_add(2); address } fn zero_page_indirect( // (zp) cpu: &mut Cpu, ) -> u16 { let byte: u8 = cpu.read(cpu.pc); let address: u16 = cpu.read_word(byte as u16); cpu.pc = cpu.pc.wrapping_add(1); address } fn zero_page_indexed_indirect( // (zp, x) cpu: &mut Cpu, ) -> u16 { let byte = cpu.read(cpu.pc); let address = cpu.read_word((byte.wrapping_add(cpu.x)) as u16); // Anytime you see something like `byte as u16`, it's using the byte as a zero-page address cpu.pc = cpu.pc.wrapping_add(1); address } fn zero_page_indirect_indexed_with_y( // (zp), y cpu: &mut Cpu, ) -> u16 { let zp: u8 = cpu.read(cpu.pc); let address = cpu.read_word(zp as u16); let address: u16 = address.wrapping_add(cpu.y as u16); cpu.pc = cpu.pc.wrapping_add(1); println!("{}indirect addr: {:#04x}", Goto(1, 39), address); address } fn signed_byte_to_word(value: u8) -> u16 { let mut value = u16::from(value); if value & 0x80 > 0 { value |= 0xff00; } value } fn relative( // return the address to jump to if branch condition met, in instr_fn // we can set the program counter based on the condition // r cpu: &mut Cpu, ) -> u16 { let byte = cpu.read(cpu.pc); signed_byte_to_word(byte).wrapping_add(cpu.pc) } fn relative_test( // used for bbs/bbr instructions, where the dest // address is read one byte after the value to test // r cpu: &mut Cpu, ) -> u16 { let byte = cpu.read(cpu.pc + 1); signed_byte_to_word(byte).wrapping_add(cpu.pc) } fn branch(cpu: &mut Cpu, condition: bool, address: u16) { if condition { // coundition met? if address & 0xff00 != cpu.pc & 0xff00 { // page boundary? cpu.pending_cycles += 2; } else { cpu.pending_cycles += 1; } cpu.pc = address; } cpu.pc = cpu.pc.wrapping_add(1); } fn brkpt(cpu: &mut Cpu, address: Option) { cpu.breakpoint() } fn adc(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as u16 + byte as u16 + carry as u16; cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); cpu.set_flag(StatusFlag::Zero, result as u8 == 0); cpu.set_flag( StatusFlag::Overflow, (cpu.a ^ byte) & (cpu.a ^ result as u8) & 0b1000_0000 > 0, // Truly have no idea what's happening here, // check out https://docs.rs/emulator_6502/latest/src/emulator_6502/opcodes/mod.rs.html ); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as u8)); cpu.a = result as u8; } fn and(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.a &= cpu.read(address); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); } fn as_left(cpu: &mut Cpu, value: u8) -> u8 { cpu.set_flag(StatusFlag::Carry, value & 0b1000_0000 == 0b1000_0000); let shifted_value = value << 1; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); cpu.set_flag(StatusFlag::Zero, shifted_value == 0); shifted_value } fn asl(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let value = cpu.read(address); let result = as_left(cpu, value); cpu.write(address, result); } fn asl_a(cpu: &mut Cpu, _address: Option) { cpu.a = as_left(cpu, cpu.a); } fn bbr0(cpu: &mut Cpu, address: Option) { // address is address to branch to if condition met // program counter is on value to test let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_0001 != 0b0000_0001, address); } fn bbr1(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_0010 != 0b0000_0010, address); } fn bbr2(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_0100 != 0b0000_0100, address); } fn bbr3(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_1000 != 0b0000_1000, address); } fn bbr4(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0001_0000 != 0b0001_0000, address); } fn bbr5(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0010_0000 != 0b0010_0000, address); } fn bbr6(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0100_0000 != 0b0100_0000, address); } fn bbr7(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b1000_0000 != 0b1000_0000, address); } fn bbs0(cpu: &mut Cpu, address: Option) { // address is address to branch to if condition met // program counter is on value to test let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_0001 == 0b0000_0001, address); } fn bbs1(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_0010 == 0b0000_0010, address); } fn bbs2(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_0100 == 0b0000_0100, address); } fn bbs3(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0000_1000 == 0b0000_1000, address); } fn bbs4(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0001_0000 == 0b0001_0000, address); } fn bbs5(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0010_0000 == 0b0010_0000, address); } fn bbs6(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b0100_0000 == 0b0100_0000, address); } fn bbs7(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let test_byte = cpu.read(cpu.pc); cpu.pc = cpu.pc.wrapping_add(1); branch(cpu, test_byte & 0b1000_0000 == 0b1000_0000, address); } fn bcc(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, !cpu.get_flag(StatusFlag::Carry), address); } fn bcs(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, cpu.get_flag(StatusFlag::Carry), address); } fn beq(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, cpu.get_flag(StatusFlag::Zero), address); } fn bit(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let result = cpu.a & cpu.read(address); cpu.set_flag(StatusFlag::Zero, result == 0); cpu.set_flag( StatusFlag::Overflow, result & StatusFlag::Overflow as u8 == StatusFlag::Overflow as u8, ); cpu.set_flag( StatusFlag::Negative, result & StatusFlag::Negative as u8 == StatusFlag::Negative as u8, ); } fn bmi(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, cpu.get_flag(StatusFlag::Negative), address); } fn bne(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, !cpu.get_flag(StatusFlag::Zero), address); } fn bpl(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, !cpu.get_flag(StatusFlag::Negative), address); } fn bra(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, true, address); } fn brk(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::Brk, true); } fn bvc(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, !cpu.get_flag(StatusFlag::Overflow), address); } fn bvs(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); branch(cpu, cpu.get_flag(StatusFlag::Overflow), address); } fn clc(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::Carry, false); } fn cld(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::Decimal, false); } fn cli(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::IrqDisable, false); } fn clv(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::Overflow, false); } fn cmp(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); cpu.set_flag(StatusFlag::Carry, cpu.a >= byte); cpu.set_flag(StatusFlag::Zero, cpu.a == byte); cpu.set_flag(StatusFlag::Negative, cpu.a <= byte); } fn cpx(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); cpu.set_flag(StatusFlag::Carry, cpu.x >= byte); cpu.set_flag(StatusFlag::Zero, cpu.x == byte); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x - byte)); } fn cpy(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); cpu.set_flag(StatusFlag::Carry, cpu.y >= byte); cpu.set_flag(StatusFlag::Zero, cpu.y == byte); cpu.set_flag(StatusFlag::Negative, cpu.y <= byte); } fn dec(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let dec_byte = byte.wrapping_sub(1); cpu.write(address, dec_byte); cpu.set_flag(StatusFlag::Zero, dec_byte == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(dec_byte)); } fn dec_a(cpu: &mut Cpu, _address: Option) { cpu.a = cpu.a.wrapping_sub(1); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); } fn dex(cpu: &mut Cpu, _address: Option) { cpu.x = cpu.x.wrapping_sub(1); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); } fn dey(cpu: &mut Cpu, _address: Option) { cpu.y = cpu.y.wrapping_sub(1); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); } fn eor(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.a ^= cpu.read(address); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); } fn inc(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let inc_byte = byte.wrapping_add(1); cpu.set_flag(StatusFlag::Zero, inc_byte == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(inc_byte)); cpu.write(address, inc_byte); } fn inc_a(cpu: &mut Cpu, _address: Option) { cpu.a = cpu.a.wrapping_add(1); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); } fn inx(cpu: &mut Cpu, _address: Option) { cpu.x = cpu.x.wrapping_add(1); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); } fn iny(cpu: &mut Cpu, _address: Option) { cpu.y = cpu.y.wrapping_add(1); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); } fn jmp(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.pc = address; } fn jsr(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let return_address = cpu.pc - 1; cpu.push_stack_word(return_address); cpu.pc = address; } fn lda(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); cpu.a = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); } fn ldx(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); cpu.x = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); } fn ldy(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); cpu.y = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); } fn ls_right(cpu: &mut Cpu, value: u8) -> u8 { cpu.set_flag(StatusFlag::Carry, value & 1 == 1); let shifted_value = value >> 1; cpu.set_flag(StatusFlag::Zero, shifted_value == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(value)); shifted_value } fn lsr(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let value = cpu.read(address); let result = ls_right(cpu, value); cpu.write(address, result); } fn lsr_a(cpu: &mut Cpu, _address: Option) { cpu.a = ls_right(cpu, cpu.a); } fn nop(_cpu: &mut Cpu, _address: Option) { // ʕ·ᴥ·ʔ- ♥ george loves u ♥ } fn ora(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.a |= cpu.read(address); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); } fn pha(cpu: &mut Cpu, _address: Option) { cpu.push_stack(cpu.a); } fn php(cpu: &mut Cpu, _address: Option) { cpu.push_stack(cpu.p); } fn phx(cpu: &mut Cpu, _address: Option) { cpu.push_stack(cpu.x); } fn phy(cpu: &mut Cpu, _address: Option) { cpu.push_stack(cpu.y); } fn pla(cpu: &mut Cpu, _address: Option) { cpu.a = cpu.pop_stack(); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)) } fn plp(cpu: &mut Cpu, _address: Option) { cpu.p = cpu.pop_stack(); } fn plx(cpu: &mut Cpu, _address: Option) { cpu.x = cpu.pop_stack(); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)) } fn ply(cpu: &mut Cpu, _address: Option) { cpu.y = cpu.pop_stack(); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)) } fn rmb0(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b1111_1110; cpu.write(address, reset_byte); } fn rmb1(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b1111_1101; cpu.write(address, reset_byte); } fn rmb2(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b1111_1011; cpu.write(address, reset_byte); } fn rmb3(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b1111_0111; cpu.write(address, reset_byte); } fn rmb4(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b1110_1111; cpu.write(address, reset_byte); } fn rmb5(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b1101_1111; cpu.write(address, reset_byte); } fn rmb6(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b1011_1111; cpu.write(address, reset_byte); } fn rmb7(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b0111_1111; cpu.write(address, reset_byte); } fn rot_left(cpu: &mut Cpu, value: u8) -> u8 { let carry = cpu.get_flag(StatusFlag::Carry) as u8; cpu.set_flag(StatusFlag::Carry, value >> 7 == 1); let shifted_value = (value << 1) + carry; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); cpu.set_flag(StatusFlag::Zero, shifted_value == 0); shifted_value } fn rol(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let value = cpu.read(address); let result = rot_left(cpu, value); cpu.write(address, result); } fn rol_a(cpu: &mut Cpu, _address: Option) { cpu.a = rot_left(cpu, cpu.a); } fn rot_right(cpu: &mut Cpu, value: u8) -> u8 { let carry = cpu.get_flag(StatusFlag::Carry) as u8; cpu.set_flag(StatusFlag::Carry, value & 1 == 1); let shifted_value = (value >> 1) + (carry << 7); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(shifted_value)); cpu.set_flag(StatusFlag::Zero, shifted_value == 0); shifted_value } fn ror(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let value = cpu.read(address); let result = rot_right(cpu, value); cpu.write(address, result); } fn ror_a(cpu: &mut Cpu, _address: Option) { cpu.a = rot_right(cpu, cpu.a); } fn rti(cpu: &mut Cpu, _address: Option) { cpu.p = cpu.pop_stack(); cpu.pc = cpu.pop_stack_word(); } fn rts(cpu: &mut Cpu, _address: Option) { let return_address = cpu.pop_stack_word(); // cpu.pc = return_address + 3; // Go back to where we jsr'ed, skipping the operand cpu.pc = return_address.wrapping_add(1); // we love an off by 2 error! // TODO: figure out why // this needs to be like this } fn sbc(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as u16 + byte as u16 + !carry as u16; cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); cpu.set_flag(StatusFlag::Zero, result as u8 == 0); cpu.set_flag( StatusFlag::Overflow, (cpu.a ^ byte) & (cpu.a ^ result as u8) & 0b1000_0000 > 0, // Truly have no idea what's happening here, // check out https://docs.rs/emulator_6502/latest/src/emulator_6502/opcodes/mod.rs.html ); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as u8)); } fn sec(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::Carry, true); } fn sed(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::Decimal, true); } fn sei(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::IrqDisable, true); } fn smb0(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte | 0b0000_0001; cpu.write(address, reset_byte); } fn smb1(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte | 0b0000_0010; cpu.write(address, reset_byte); } fn smb2(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte | 0b0000_0100; cpu.write(address, reset_byte); } fn smb3(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte | 0b0000_1000; cpu.write(address, reset_byte); } fn smb4(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte | 0b0001_0000; cpu.write(address, reset_byte); } fn smb5(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte | 0b0010_0000; cpu.write(address, reset_byte); } fn smb6(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte & 0b0100_0000; cpu.write(address, reset_byte); } fn smb7(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); let reset_byte = byte | 0b1000_0000; cpu.write(address, reset_byte); } fn sta(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.write(address, cpu.a); } fn stp(cpu: &mut Cpu, _address: Option) { cpu.stop(); } fn stx(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.write(address, cpu.x); } fn sty(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.write(address, cpu.y); } fn stz(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); cpu.write(address, 0); } fn tax(cpu: &mut Cpu, _address: Option) { cpu.x = cpu.a; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); } fn tay(cpu: &mut Cpu, _address: Option) { cpu.y = cpu.a; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); } fn trb(cpu: &mut Cpu, address: Option) { // Still not really sure when you would // use TRB or TSB, but this was helpful // (Garth rocks) http://forum.6502.org/viewtopic.php?t=48 let address = address.unwrap(); let byte = cpu.read(address); cpu.write(address, !cpu.a & byte); cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); } fn tsb(cpu: &mut Cpu, address: Option) { let address = address.unwrap(); let byte = cpu.read(address); cpu.write(address, cpu.a | byte); cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); } fn tsx(cpu: &mut Cpu, _address: Option) { cpu.x = cpu.s; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); } fn txa(cpu: &mut Cpu, _address: Option) { cpu.a = cpu.x; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); } fn txs(cpu: &mut Cpu, _address: Option) { cpu.s = cpu.x; } fn tya(cpu: &mut Cpu, _address: Option) { cpu.a = cpu.y; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); } fn wai(cpu: &mut Cpu, _address: Option) { cpu.set_flag(StatusFlag::Brk, true); cpu.wait_for_interrupt(); } // if you want to optimize this later https://llx.com/Neil/a2/opcodes.html pub fn get_instruction(opcode: u8) -> Instruction<'static> { OPCODES[opcode as usize] } pub const OPCODES: [Instruction; 256] = [ Instruction { instr_fn: Some(brk), address_fn: None, cycles: 7, name: "brk", addr_mode: "none", }, Instruction { instr_fn: Some(ora), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "ora", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: Some(brkpt), address_fn: None, cycles: 0, name: "brkpt", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(tsb), address_fn: Some(zero_page), cycles: 5, name: "tsb", addr_mode: "zero_page", }, Instruction { instr_fn: Some(ora), address_fn: Some(zero_page), cycles: 3, name: "ora", addr_mode: "zero_page", }, Instruction { instr_fn: Some(asl), address_fn: Some(zero_page), cycles: 5, name: "asl", addr_mode: "zero_page", }, Instruction { instr_fn: Some(rmb0), address_fn: Some(zero_page), cycles: 5, name: "rmb0", addr_mode: "zero_page", }, Instruction { instr_fn: Some(php), address_fn: None, cycles: 3, name: "php", addr_mode: "none", }, Instruction { instr_fn: Some(ora), address_fn: Some(immediate), cycles: 2, name: "ora", addr_mode: "immediate", }, Instruction { instr_fn: Some(asl_a), address_fn: Some(accumulator), cycles: 2, name: "asl_a", addr_mode: "accumulator", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(tsb), address_fn: Some(absolute_a), cycles: 6, name: "tsb", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(ora), address_fn: Some(absolute_a), cycles: 4, name: "ora", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(asl), address_fn: Some(absolute_a), cycles: 6, name: "asl", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbr0), address_fn: Some(relative_test), cycles: 4, name: "bbr0", addr_mode: "relative_test", }, Instruction { instr_fn: Some(bpl), address_fn: Some(relative), cycles: 2, name: "bpl", addr_mode: "relative", }, Instruction { instr_fn: Some(ora), address_fn: Some(absolute_indexed_with_y), cycles: 5, name: "ora", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(ora), address_fn: Some(zero_page_indirect), cycles: 5, name: "ora", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(trb), address_fn: Some(zero_page), cycles: 5, name: "trb", addr_mode: "zero_page", }, Instruction { instr_fn: Some(ora), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "ora", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(asl), address_fn: Some(zero_page_indexed_with_x), cycles: 6, name: "asl", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(rmb1), address_fn: Some(zero_page), cycles: 5, name: "rmb1", addr_mode: "zero_page", }, Instruction { instr_fn: Some(clc), address_fn: None, cycles: 2, name: "clc", addr_mode: "none", }, Instruction { instr_fn: Some(ora), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "ora", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(inc_a), address_fn: Some(accumulator), cycles: 2, name: "inc_a", addr_mode: "accumulator", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(trb), address_fn: Some(absolute_a), cycles: 6, name: "trb", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(ora), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "ora", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(asl), address_fn: Some(absolute_indexed_with_x), cycles: 7, name: "asl", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(bbr1), address_fn: Some(relative_test), cycles: 4, name: "bbr1", addr_mode: "relative_test", }, Instruction { instr_fn: Some(jsr), address_fn: Some(absolute_a), cycles: 6, name: "jsr", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(and), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "and", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(bit), address_fn: Some(zero_page), cycles: 3, name: "bit", addr_mode: "zero_page", }, Instruction { instr_fn: Some(and), address_fn: Some(zero_page), cycles: 3, name: "and", addr_mode: "zero_page", }, Instruction { instr_fn: Some(rol), address_fn: Some(zero_page), cycles: 5, name: "rol", addr_mode: "zero_page", }, Instruction { instr_fn: Some(rmb2), address_fn: Some(zero_page), cycles: 5, name: "rmb2", addr_mode: "zero_page", }, Instruction { instr_fn: Some(plp), address_fn: None, cycles: 4, name: "plp", addr_mode: "none", }, Instruction { instr_fn: Some(and), address_fn: Some(immediate), cycles: 2, name: "and", addr_mode: "immediate", }, Instruction { instr_fn: Some(rol_a), address_fn: Some(accumulator), cycles: 2, name: "rol_a", addr_mode: "accumulator", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(bit), address_fn: Some(absolute_a), cycles: 4, name: "bit", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(and), address_fn: Some(absolute_a), cycles: 4, name: "and", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(rol), address_fn: Some(absolute_a), cycles: 6, name: "rol", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbr2), address_fn: Some(relative_test), cycles: 4, name: "bbr2", addr_mode: "relative_test", }, Instruction { instr_fn: Some(bmi), address_fn: Some(relative), cycles: 2, name: "bmi", addr_mode: "relative", }, Instruction { instr_fn: Some(and), address_fn: Some(zero_page_indirect_indexed_with_y), cycles: 5, name: "and", addr_mode: "zero_page_indirect_indexed_with_y", }, Instruction { instr_fn: Some(and), address_fn: Some(zero_page_indirect), cycles: 5, name: "and", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(bit), address_fn: Some(zero_page_indexed_with_x), cycles: 3, name: "bit", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(and), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "and", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(rol), address_fn: Some(zero_page_indexed_with_x), cycles: 6, name: "rol", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(rmb3), address_fn: Some(zero_page), cycles: 5, name: "rmb3", addr_mode: "zero_page", }, Instruction { instr_fn: Some(sec), address_fn: None, cycles: 2, name: "sec", addr_mode: "none", }, Instruction { instr_fn: Some(and), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "and", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(dec_a), address_fn: Some(accumulator), cycles: 2, name: "dec_a", addr_mode: "accumulator", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(bit), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "bit", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(and), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "and", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(rol), address_fn: Some(absolute_indexed_with_x), cycles: 7, name: "rol", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(bbr3), address_fn: Some(relative_test), cycles: 4, name: "bbr3", addr_mode: "relative_test", }, Instruction { instr_fn: Some(rti), address_fn: None, cycles: 6, name: "rti", addr_mode: "none", }, Instruction { instr_fn: Some(eor), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "eor", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(eor), address_fn: Some(zero_page), cycles: 3, name: "eor", addr_mode: "zero_page", }, Instruction { instr_fn: Some(lsr), address_fn: Some(zero_page), cycles: 5, name: "lsr", addr_mode: "zero_page", }, Instruction { instr_fn: Some(rmb4), address_fn: Some(zero_page), cycles: 5, name: "rmb4", addr_mode: "zero_page", }, Instruction { instr_fn: Some(pha), address_fn: None, cycles: 3, name: "pha", addr_mode: "none", }, Instruction { instr_fn: Some(eor), address_fn: Some(immediate), cycles: 2, name: "eor", addr_mode: "immediate", }, Instruction { instr_fn: Some(lsr_a), address_fn: Some(accumulator), cycles: 2, name: "lsr_a", addr_mode: "accumulator", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(jmp), address_fn: Some(absolute_a), cycles: 3, name: "jmp", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(eor), address_fn: Some(absolute_a), cycles: 4, name: "eor", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(lsr), address_fn: Some(absolute_a), cycles: 6, name: "lsr", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbr4), address_fn: Some(relative_test), cycles: 4, name: "bbr4", addr_mode: "relative_test", }, Instruction { instr_fn: Some(bvc), address_fn: Some(relative), cycles: 2, name: "bvc", addr_mode: "relative", }, Instruction { instr_fn: Some(eor), address_fn: Some(zero_page_indirect_indexed_with_y), cycles: 5, name: "eor", addr_mode: "zero_page_indirect_indexed_with_y", }, Instruction { instr_fn: Some(eor), address_fn: Some(zero_page_indirect), cycles: 5, name: "eor", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(eor), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "eor", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(lsr), address_fn: Some(zero_page_indexed_with_x), cycles: 6, name: "lsr", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(rmb5), address_fn: Some(zero_page), cycles: 5, name: "rmb5", addr_mode: "zero_page", }, Instruction { instr_fn: Some(cli), address_fn: None, cycles: 2, name: "cli", addr_mode: "none", }, Instruction { instr_fn: Some(eor), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "eor", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(phy), address_fn: None, cycles: 3, name: "phy", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(eor), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "eor", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(lsr), address_fn: Some(absolute_indexed_with_x), cycles: 7, name: "lsr", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(bbr5), address_fn: Some(relative_test), cycles: 4, name: "bbr5", addr_mode: "relative_test", }, Instruction { instr_fn: Some(rts), address_fn: None, cycles: 6, name: "rts", addr_mode: "none", }, Instruction { instr_fn: Some(adc), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "adc", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(stz), address_fn: Some(zero_page), cycles: 3, name: "stz", addr_mode: "zero_page", }, Instruction { instr_fn: Some(adc), address_fn: Some(zero_page), cycles: 3, name: "adc", addr_mode: "zero_page", }, Instruction { instr_fn: Some(ror), address_fn: Some(zero_page), cycles: 5, name: "ror", addr_mode: "zero_page", }, Instruction { instr_fn: Some(rmb6), address_fn: Some(zero_page), cycles: 5, name: "rmb6", addr_mode: "zero_page", }, Instruction { instr_fn: Some(pla), address_fn: None, cycles: 4, name: "pla", addr_mode: "none", }, Instruction { instr_fn: Some(adc), address_fn: Some(immediate), cycles: 2, name: "adc", addr_mode: "immediate", }, Instruction { instr_fn: Some(ror_a), address_fn: Some(accumulator), cycles: 2, name: "ror_a", addr_mode: "accumulator", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(jmp), address_fn: Some(absolute_indirect), cycles: 5, name: "jmp", addr_mode: "absolute_indirect", }, Instruction { instr_fn: Some(adc), address_fn: Some(absolute_a), cycles: 4, name: "adc", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(ror), address_fn: Some(absolute_a), cycles: 6, name: "ror", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbr6), address_fn: Some(relative), cycles: 4, name: "bbr6", addr_mode: "relative", }, Instruction { instr_fn: Some(bvs), address_fn: Some(relative), cycles: 2, name: "bvs", addr_mode: "relative", }, Instruction { instr_fn: Some(adc), address_fn: Some(zero_page_indirect_indexed_with_y), cycles: 5, name: "adc", addr_mode: "zero_page_indirect_indexed_with_y", }, Instruction { instr_fn: Some(adc), address_fn: Some(zero_page_indirect), cycles: 5, name: "adc", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(stz), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "stz", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(adc), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "adc", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(ror), address_fn: Some(zero_page_indexed_with_x), cycles: 6, name: "ror", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(rmb7), address_fn: Some(zero_page), cycles: 5, name: "rmb7", addr_mode: "zero_page", }, Instruction { instr_fn: Some(sei), address_fn: None, cycles: 2, name: "sei", addr_mode: "none", }, Instruction { instr_fn: Some(adc), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "adc", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(ply), address_fn: None, cycles: 4, name: "ply", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(jmp), address_fn: Some(absolute_indexed_indirect), cycles: 6, name: "jmp", addr_mode: "absolute_indexed_indirect", }, Instruction { instr_fn: Some(adc), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "adc", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(ror), address_fn: Some(absolute_indexed_with_x), cycles: 7, name: "ror", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(bbr7), address_fn: Some(relative_test), cycles: 4, name: "bbr7", addr_mode: "relative_test", }, Instruction { instr_fn: Some(bra), address_fn: Some(relative), cycles: 3, name: "bra", addr_mode: "relative", }, Instruction { instr_fn: Some(sta), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "sta", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(sty), address_fn: Some(zero_page), cycles: 3, name: "sty", addr_mode: "zero_page", }, Instruction { instr_fn: Some(sta), address_fn: Some(zero_page), cycles: 3, name: "sta", addr_mode: "zero_page", }, Instruction { instr_fn: Some(stx), address_fn: Some(zero_page), cycles: 3, name: "stx", addr_mode: "zero_page", }, Instruction { instr_fn: Some(smb0), address_fn: Some(zero_page), cycles: 5, name: "smb0", addr_mode: "zero_page", }, Instruction { instr_fn: Some(dey), address_fn: None, cycles: 2, name: "dey", addr_mode: "none", }, Instruction { instr_fn: Some(bit), address_fn: Some(immediate), cycles: 3, name: "bit", addr_mode: "immediate", }, Instruction { instr_fn: Some(txa), address_fn: None, cycles: 2, name: "txa", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(sty), address_fn: Some(absolute_a), cycles: 4, name: "sty", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(sta), address_fn: Some(absolute_a), cycles: 4, name: "sta", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(stx), address_fn: Some(absolute_a), cycles: 4, name: "stx", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbs0), address_fn: Some(relative_test), cycles: 4, name: "bbs0", addr_mode: "relative_test", }, Instruction { instr_fn: Some(bcc), address_fn: Some(relative), cycles: 2, name: "bcc", addr_mode: "relative", }, Instruction { instr_fn: Some(sta), address_fn: Some(zero_page_indirect_indexed_with_y), cycles: 6, name: "sta", addr_mode: "zero_page_indirect_indexed_with_y", }, Instruction { instr_fn: Some(sta), address_fn: Some(zero_page_indirect), cycles: 5, name: "sta", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(sty), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "sty", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(sta), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "sta", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(stx), address_fn: Some(zero_page_indexed_with_y), cycles: 4, name: "stx", addr_mode: "zero_page_indexed_with_y", }, Instruction { instr_fn: Some(smb1), address_fn: Some(zero_page), cycles: 5, name: "smb1", addr_mode: "zero_page", }, Instruction { instr_fn: Some(tya), address_fn: None, cycles: 2, name: "tya", addr_mode: "none", }, Instruction { instr_fn: Some(sta), address_fn: Some(absolute_indexed_with_y), cycles: 5, name: "sta", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(txs), address_fn: None, cycles: 2, name: "txs", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(stz), address_fn: Some(absolute_a), cycles: 4, name: "stz", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(sta), address_fn: Some(absolute_indexed_with_x), cycles: 5, name: "sta", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(stz), address_fn: Some(absolute_indexed_with_x), cycles: 5, name: "stz", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(bbs1), address_fn: Some(relative_test), cycles: 4, name: "bbs1", addr_mode: "relative_test", }, Instruction { instr_fn: Some(ldy), address_fn: Some(immediate), cycles: 2, name: "ldy", addr_mode: "immediate", }, Instruction { instr_fn: Some(lda), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "lda", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: Some(ldx), address_fn: Some(immediate), cycles: 2, name: "ldx", addr_mode: "immediate", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(ldy), address_fn: Some(zero_page), cycles: 3, name: "ldy", addr_mode: "zero_page", }, Instruction { instr_fn: Some(lda), address_fn: Some(zero_page), cycles: 3, name: "lda", addr_mode: "zero_page", }, Instruction { instr_fn: Some(ldx), address_fn: Some(zero_page), cycles: 3, name: "ldx", addr_mode: "zero_page", }, Instruction { instr_fn: Some(smb2), address_fn: Some(zero_page), cycles: 5, name: "smb2", addr_mode: "zero_page", }, Instruction { instr_fn: Some(tay), address_fn: None, cycles: 2, name: "tay", addr_mode: "none", }, Instruction { instr_fn: Some(lda), address_fn: Some(immediate), cycles: 2, name: "lda", addr_mode: "immediate", }, Instruction { instr_fn: Some(tax), address_fn: None, cycles: 2, name: "tax", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(ldy), address_fn: Some(absolute_a), cycles: 4, name: "ldy", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(lda), address_fn: Some(absolute_a), cycles: 4, name: "lda", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(ldx), address_fn: Some(absolute_a), cycles: 4, name: "ldx", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbs2), address_fn: Some(relative_test), cycles: 4, name: "bbs2", addr_mode: "relative_test", }, Instruction { instr_fn: Some(bcs), address_fn: Some(relative), cycles: 2, name: "bcs", addr_mode: "relative", }, Instruction { instr_fn: Some(lda), address_fn: Some(zero_page_indirect_indexed_with_y), cycles: 5, name: "lda", addr_mode: "zero_page_indirect_indexed_with_y", }, Instruction { instr_fn: Some(lda), address_fn: Some(zero_page_indirect), cycles: 5, // Unsure, see https://cx16.dk/65c02/reference.html#LDA name: "lda", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(ldy), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "ldy", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(lda), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "lda", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(ldx), address_fn: Some(zero_page_indexed_with_y), cycles: 4, name: "ldx", addr_mode: "zero_page_indexed_with_y", }, Instruction { instr_fn: Some(smb3), address_fn: Some(zero_page), cycles: 5, name: "smb3", addr_mode: "zero_page", }, Instruction { instr_fn: Some(clv), address_fn: None, cycles: 2, name: "clv", addr_mode: "none", }, Instruction { instr_fn: Some(lda), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "lda", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(tsx), address_fn: None, cycles: 2, name: "tsx", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(ldy), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "ldy", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(lda), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "lda", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(ldx), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "ldx", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(bbs3), address_fn: Some(relative_test), cycles: 4, name: "bbs3", addr_mode: "relative_test", }, Instruction { instr_fn: Some(cpy), address_fn: Some(immediate), cycles: 2, name: "cpy", addr_mode: "immediate", }, Instruction { instr_fn: Some(cmp), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "cmp", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(cpy), address_fn: Some(zero_page), cycles: 3, name: "cpy", addr_mode: "zero_page", }, Instruction { instr_fn: Some(cmp), address_fn: Some(zero_page), cycles: 3, name: "cmp", addr_mode: "zero_page", }, Instruction { instr_fn: Some(dec), address_fn: Some(zero_page), cycles: 5, name: "dec", addr_mode: "zero_page", }, Instruction { instr_fn: Some(smb4), address_fn: Some(zero_page), cycles: 5, name: "smb4", addr_mode: "zero_page", }, Instruction { instr_fn: Some(iny), address_fn: None, cycles: 2, name: "iny", addr_mode: "none", }, Instruction { instr_fn: Some(cmp), address_fn: Some(immediate), cycles: 2, name: "cmp", addr_mode: "immediate", }, Instruction { instr_fn: Some(dex), address_fn: None, cycles: 2, name: "dex", addr_mode: "none", }, Instruction { instr_fn: Some(wai), address_fn: None, cycles: 3, name: "wai", addr_mode: "none", }, Instruction { instr_fn: Some(cpy), address_fn: Some(absolute_a), cycles: 4, name: "cpy", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(cmp), address_fn: Some(absolute_a), cycles: 4, name: "cmp", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(dec), address_fn: Some(absolute_a), cycles: 6, name: "dec", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbs4), address_fn: Some(relative_test), cycles: 4, name: "bbs4", addr_mode: "relative_test", }, Instruction { instr_fn: Some(bne), address_fn: Some(relative), cycles: 2, name: "bne", addr_mode: "relative", }, Instruction { instr_fn: Some(cmp), address_fn: Some(zero_page_indirect_indexed_with_y), cycles: 5, name: "cmp", addr_mode: "zero_page_indirect_indexed_with_y", }, Instruction { instr_fn: Some(cmp), address_fn: Some(zero_page_indirect), cycles: 5, // Unsure, look here: https://cx16.dk/65c02/reference.html#CMP name: "cmp", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(cmp), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "cmp", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(dec), address_fn: Some(zero_page_indexed_with_x), cycles: 6, name: "dec", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(smb5), address_fn: Some(zero_page), cycles: 5, name: "smb5", addr_mode: "zero_page", }, Instruction { instr_fn: Some(cld), address_fn: None, cycles: 2, name: "cld", addr_mode: "none", }, Instruction { instr_fn: Some(cmp), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "cmp", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(phx), address_fn: None, cycles: 3, name: "phx", addr_mode: "none", }, Instruction { instr_fn: Some(stp), address_fn: None, cycles: 3, name: "stp", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(cmp), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "cmp", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(dec), address_fn: Some(absolute_indexed_with_x), cycles: 7, name: "dec", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(bbs5), address_fn: Some(relative_test), cycles: 4, name: "bbs5", addr_mode: "relative_test", }, Instruction { instr_fn: Some(cpx), address_fn: Some(immediate), cycles: 2, name: "cpx", addr_mode: "immediate", }, Instruction { instr_fn: Some(sbc), address_fn: Some(zero_page_indexed_indirect), cycles: 6, name: "sbc", addr_mode: "zero_page_indexed_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(cpx), address_fn: Some(zero_page), cycles: 3, name: "cpx", addr_mode: "zero_page", }, Instruction { instr_fn: Some(sbc), address_fn: Some(zero_page), cycles: 3, name: "sbc", addr_mode: "zero_page", }, Instruction { instr_fn: Some(inc), address_fn: Some(zero_page), cycles: 5, name: "inc", addr_mode: "zero_page", }, Instruction { instr_fn: Some(smb6), address_fn: Some(zero_page), cycles: 5, name: "smb6", addr_mode: "zero_page", }, Instruction { instr_fn: Some(inx), address_fn: None, cycles: 2, name: "inx", addr_mode: "none", }, Instruction { instr_fn: Some(sbc), address_fn: Some(immediate), cycles: 2, name: "sbc", addr_mode: "immediate", }, Instruction { instr_fn: Some(nop), address_fn: None, cycles: 2, name: "nop", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(cpx), address_fn: Some(absolute_a), cycles: 4, name: "cpx", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(sbc), address_fn: Some(absolute_a), cycles: 4, name: "sbc", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(inc), address_fn: Some(absolute_a), cycles: 6, name: "inc", addr_mode: "absolute_a", }, Instruction { instr_fn: Some(bbs6), address_fn: Some(relative_test), cycles: 4, name: "bbs6", addr_mode: "relative_test", }, Instruction { instr_fn: Some(beq), address_fn: Some(relative), cycles: 2, name: "beq", addr_mode: "relative", }, Instruction { instr_fn: Some(sbc), address_fn: Some(zero_page_indirect_indexed_with_y), cycles: 5, name: "sbc", addr_mode: "zero_page_indirect_indexed_with_y", }, Instruction { instr_fn: Some(sbc), address_fn: Some(zero_page_indirect), cycles: 5, name: "sbc", addr_mode: "zero_page_indirect", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(sbc), address_fn: Some(zero_page_indexed_with_x), cycles: 4, name: "sbc", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(inc), address_fn: Some(zero_page_indexed_with_x), cycles: 6, name: "inc", addr_mode: "zero_page_indexed_with_x", }, Instruction { instr_fn: Some(smb7), address_fn: Some(zero_page), cycles: 5, name: "smb7", addr_mode: "zero_page", }, Instruction { instr_fn: Some(sed), address_fn: None, cycles: 2, name: "sed", addr_mode: "none", }, Instruction { instr_fn: Some(sbc), address_fn: Some(absolute_indexed_with_y), cycles: 4, name: "sbc", addr_mode: "absolute_indexed_with_y", }, Instruction { instr_fn: Some(plx), address_fn: None, cycles: 4, name: "plx", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: None, address_fn: None, cycles: 0, name: "none", addr_mode: "none", }, Instruction { instr_fn: Some(sbc), address_fn: Some(absolute_indexed_with_x), cycles: 4, name: "sbc", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(inc), address_fn: Some(absolute_indexed_with_x), cycles: 7, name: "inc", addr_mode: "absolute_indexed_with_x", }, Instruction { instr_fn: Some(bbs7), address_fn: Some(relative_test), cycles: 4, name: "bbs7", addr_mode: "relative_test", }, ];