#![allow(clippy::upper_case_acronyms)] use crate::cpu::{Cpu, StatusFlag}; use crate::error::{AddressingModeError, ExecutionError, GeorgeErrorKind}; use crate::types::{Byte, Word}; use std::collections::HashMap; #[derive(Clone, Debug)] pub enum Instruction { Valid(ValidInstruction), Invalid(InvalidInstruction), } #[derive(Clone, Debug)] pub struct ValidInstruction { pub opcode: Opcode, pub cycles: Byte, } #[derive(Clone, Debug)] pub struct InvalidInstruction { pub opcode: Byte, } #[derive(Clone, Debug)] pub enum AddressingMode { AbsoluteA, // a AbsoluteIndexedWithX, // a, x AbsoluteIndexedWithY, // a, y AbsoluteIndirect, // (a) AbsoluteIndexedIndirect, // (a,x) (4), only used with the JMP instruction Accumulator, // A Immediate, // # Implied, // i ProgramCounterRelative, // r ProgramCounterRelativeTest, // r, but for instructions like BBR0-7, where the first operand // is a zero-page address, and the second is the relative branch // address Stack, // s ZeroPage, // zp ZeroPageIndexedWithX, // zp, x ZeroPageIndexedWithY, // zp, y ZeroPageIndirect, // (zp) (4) ZeroPageIndexedIndirect, // (zp, x) ZeroPageIndirectIndexedWithY, // (zp), y } // if you want to optimize this later https://llx.com/Neil/a2/opcodes.html pub fn get_instruction(opcode: u8) -> Instruction { let opcodes: HashMap = HashMap::from([ ( 0x00, Instruction::Valid(ValidInstruction { opcode: Opcode::BRK(AddressingMode::Implied), cycles: 7, }), ), ( 0x01, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0x02, Instruction::Invalid(InvalidInstruction { opcode: 0x02 }), ), ( 0x03, Instruction::Invalid(InvalidInstruction { opcode: 0x03 }), ), ( 0x04, Instruction::Valid(ValidInstruction { opcode: Opcode::TSB(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x05, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x06, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x07, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB0(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x08, Instruction::Valid(ValidInstruction { opcode: Opcode::PHP(AddressingMode::Stack), cycles: 3, }), ), ( 0x09, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::Immediate), cycles: 2, }), ), ( 0x0a, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::Accumulator), cycles: 2, }), ), ( 0x0b, Instruction::Invalid(InvalidInstruction { opcode: 0x0b }), ), ( 0x0c, Instruction::Valid(ValidInstruction { opcode: Opcode::TSB(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0x0d, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x0e, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0x0f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR0(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x10, Instruction::Valid(ValidInstruction { opcode: Opcode::BPL(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0x11, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithY), cycles: 5, }), ), ( 0x12, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPageIndirect), cycles: 5, }), ), ( 0x13, Instruction::Invalid(InvalidInstruction { opcode: 0x13 }), ), ( 0x14, Instruction::Valid(ValidInstruction { opcode: Opcode::TRB(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x15, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0x16, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::ZeroPageIndexedWithX), cycles: 6, }), ), ( 0x17, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB1(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x18, Instruction::Valid(ValidInstruction { opcode: Opcode::CLC(AddressingMode::Implied), cycles: 2, }), ), ( 0x19, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0x1a, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::Accumulator), cycles: 2, }), ), ( 0x1b, Instruction::Invalid(InvalidInstruction { opcode: 0x1b }), ), ( 0x1c, Instruction::Valid(ValidInstruction { opcode: Opcode::TRB(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0x1d, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0x1e, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::AbsoluteIndexedWithX), cycles: 7, }), ), ( 0x1f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR1(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x20, Instruction::Valid(ValidInstruction { opcode: Opcode::JSR(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0x21, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0x22, Instruction::Invalid(InvalidInstruction { opcode: 0x22 }), ), ( 0x23, Instruction::Invalid(InvalidInstruction { opcode: 0x23 }), ), ( 0x24, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x25, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x26, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x27, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB2(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x28, Instruction::Valid(ValidInstruction { opcode: Opcode::PLP(AddressingMode::Stack), cycles: 4, }), ), ( 0x29, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::Immediate), cycles: 2, }), ), ( 0x2a, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::Accumulator), cycles: 2, }), ), ( 0x2b, Instruction::Invalid(InvalidInstruction { opcode: 0x2b }), ), ( 0x2c, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x2d, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x2e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0x2f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR2(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x30, Instruction::Valid(ValidInstruction { opcode: Opcode::BMI(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0x31, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndirectIndexedWithY), cycles: 5, }), ), ( 0x32, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndirect), cycles: 5, }), ), ( 0x33, Instruction::Invalid(InvalidInstruction { opcode: 0x33 }), ), ( 0x34, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::ZeroPageIndexedWithX), cycles: 3, }), ), ( 0x35, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0x36, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::ZeroPageIndexedWithX), cycles: 6, }), ), ( 0x37, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB3(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x38, Instruction::Valid(ValidInstruction { opcode: Opcode::SEC(AddressingMode::Implied), cycles: 2, }), ), ( 0x39, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0x3a, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::Accumulator), cycles: 2, }), ), ( 0x3b, Instruction::Invalid(InvalidInstruction { opcode: 0x3b }), ), ( 0x3c, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0x3d, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0x3e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::AbsoluteIndexedWithX), cycles: 7, }), ), ( 0x3f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR3(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x40, Instruction::Valid(ValidInstruction { opcode: Opcode::RTI(AddressingMode::Stack), cycles: 6, }), ), ( 0x41, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0x42, Instruction::Invalid(InvalidInstruction { opcode: 0x42 }), ), ( 0x43, Instruction::Invalid(InvalidInstruction { opcode: 0x43 }), ), ( 0x44, Instruction::Invalid(InvalidInstruction { opcode: 0x44 }), ), ( 0x45, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x46, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x47, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB4(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x48, Instruction::Valid(ValidInstruction { opcode: Opcode::PHA(AddressingMode::Stack), cycles: 3, }), ), ( 0x49, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::Immediate), cycles: 2, }), ), ( 0x4a, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::Accumulator), cycles: 2, }), ), ( 0x4b, Instruction::Invalid(InvalidInstruction { opcode: 0x4b }), ), ( 0x4c, Instruction::Valid(ValidInstruction { opcode: Opcode::JMP(AddressingMode::AbsoluteA), cycles: 3, }), ), ( 0x4d, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x4e, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0x4f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR4(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x50, Instruction::Valid(ValidInstruction { opcode: Opcode::BVC(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0x51, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndirectIndexedWithY), cycles: 5, }), ), ( 0x52, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndirect), cycles: 5, }), ), ( 0x53, Instruction::Invalid(InvalidInstruction { opcode: 0x53 }), ), ( 0x54, Instruction::Invalid(InvalidInstruction { opcode: 0x54 }), ), ( 0x55, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0x56, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::ZeroPageIndexedWithX), cycles: 6, }), ), ( 0x57, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB5(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x58, Instruction::Valid(ValidInstruction { opcode: Opcode::CLI(AddressingMode::Implied), cycles: 2, }), ), ( 0x59, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0x5a, Instruction::Valid(ValidInstruction { opcode: Opcode::PHY(AddressingMode::Stack), cycles: 3, }), ), ( 0x5b, Instruction::Invalid(InvalidInstruction { opcode: 0x5b }), ), ( 0x5c, Instruction::Invalid(InvalidInstruction { opcode: 0x5c }), ), ( 0x5d, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0x5e, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::AbsoluteIndexedWithX), cycles: 7, }), ), ( 0x5f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR5(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x60, Instruction::Valid(ValidInstruction { opcode: Opcode::RTS(AddressingMode::Stack), cycles: 6, }), ), ( 0x61, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0x62, Instruction::Invalid(InvalidInstruction { opcode: 0x62 }), ), ( 0x63, Instruction::Invalid(InvalidInstruction { opcode: 0x63 }), ), ( 0x64, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x65, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x66, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x67, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB6(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x68, Instruction::Valid(ValidInstruction { opcode: Opcode::PLA(AddressingMode::Stack), cycles: 4, }), ), ( 0x69, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::Immediate), cycles: 2, }), ), ( 0x6a, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::Accumulator), cycles: 2, }), ), ( 0x6b, Instruction::Invalid(InvalidInstruction { opcode: 0x6b }), ), ( 0x6c, Instruction::Valid(ValidInstruction { opcode: Opcode::JMP(AddressingMode::AbsoluteIndirect), cycles: 5, }), ), ( 0x6d, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x6e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0x6f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR6(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x70, Instruction::Valid(ValidInstruction { opcode: Opcode::BVS(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0x71, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndirectIndexedWithY), cycles: 5, }), ), ( 0x72, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndirect), cycles: 5, }), ), ( 0x73, Instruction::Invalid(InvalidInstruction { opcode: 0x73 }), ), ( 0x74, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0x75, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0x76, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::ZeroPageIndexedWithX), cycles: 6, }), ), ( 0x77, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB7(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x78, Instruction::Valid(ValidInstruction { opcode: Opcode::SEI(AddressingMode::Implied), cycles: 2, }), ), ( 0x79, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0x7a, Instruction::Valid(ValidInstruction { opcode: Opcode::PLY(AddressingMode::Stack), cycles: 4, }), ), ( 0x7b, Instruction::Invalid(InvalidInstruction { opcode: 0x7b }), ), ( 0x7c, Instruction::Valid(ValidInstruction { opcode: Opcode::JMP(AddressingMode::AbsoluteIndexedIndirect), cycles: 6, }), ), ( 0x7d, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0x7e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::AbsoluteIndexedWithX), cycles: 7, }), ), ( 0x7f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR7(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x80, Instruction::Valid(ValidInstruction { opcode: Opcode::BRA(AddressingMode::ProgramCounterRelative), cycles: 3, }), ), ( 0x81, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0x82, Instruction::Invalid(InvalidInstruction { opcode: 0x82 }), ), ( 0x83, Instruction::Invalid(InvalidInstruction { opcode: 0x83 }), ), ( 0x84, Instruction::Valid(ValidInstruction { opcode: Opcode::STY(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x85, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x86, Instruction::Valid(ValidInstruction { opcode: Opcode::STX(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0x87, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB0(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x88, Instruction::Valid(ValidInstruction { opcode: Opcode::DEY(AddressingMode::Implied), cycles: 2, }), ), ( 0x89, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::Immediate), cycles: 3, }), ), ( 0x8a, Instruction::Valid(ValidInstruction { opcode: Opcode::TXA(AddressingMode::Implied), cycles: 2, }), ), ( 0x8b, Instruction::Invalid(InvalidInstruction { opcode: 0x8b }), ), ( 0x8c, Instruction::Valid(ValidInstruction { opcode: Opcode::STY(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x8d, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x8e, Instruction::Valid(ValidInstruction { opcode: Opcode::STX(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x8f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS0(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0x90, Instruction::Valid(ValidInstruction { opcode: Opcode::BCC(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0x91, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndirectIndexedWithY), cycles: 6, }), ), ( 0x92, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndirect), cycles: 5, }), ), ( 0x93, Instruction::Invalid(InvalidInstruction { opcode: 0x93 }), ), ( 0x94, Instruction::Valid(ValidInstruction { opcode: Opcode::STY(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0x95, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0x96, Instruction::Valid(ValidInstruction { opcode: Opcode::STX(AddressingMode::ZeroPageIndexedWithY), cycles: 4, }), ), ( 0x97, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB1(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0x98, Instruction::Valid(ValidInstruction { opcode: Opcode::TYA(AddressingMode::Implied), cycles: 2, }), ), ( 0x99, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::AbsoluteIndexedWithY), cycles: 5, }), ), ( 0x9a, Instruction::Valid(ValidInstruction { opcode: Opcode::TXS(AddressingMode::Implied), cycles: 2, }), ), ( 0x9b, Instruction::Invalid(InvalidInstruction { opcode: 0x9b }), ), ( 0x9c, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0x9d, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::AbsoluteIndexedWithX), cycles: 5, }), ), ( 0x9e, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::AbsoluteIndexedWithX), cycles: 5, }), ), ( 0x9f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS1(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0xa0, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::Immediate), cycles: 2, }), ), ( 0xa1, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0xa2, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::Immediate), cycles: 2, }), ), ( 0xa3, Instruction::Invalid(InvalidInstruction { opcode: 0xa3 }), ), ( 0xa4, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0xa5, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0xa6, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0xa7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB2(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xa8, Instruction::Valid(ValidInstruction { opcode: Opcode::TAY(AddressingMode::Implied), cycles: 2, }), ), ( 0xa9, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::Immediate), cycles: 2, }), ), ( 0xaa, Instruction::Valid(ValidInstruction { opcode: Opcode::TAX(AddressingMode::Implied), cycles: 2, }), ), ( 0xab, Instruction::Invalid(InvalidInstruction { opcode: 0xab }), ), ( 0xac, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0xad, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0xae, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0xaf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS2(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0xb0, Instruction::Valid(ValidInstruction { opcode: Opcode::BCS(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0xb1, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndirectIndexedWithY), cycles: 5, }), ), ( 0xb2, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndirect), cycles: 5, // Unsure, see https://cx16.dk/65c02/reference.html#LDA }), ), ( 0xb3, Instruction::Invalid(InvalidInstruction { opcode: 0xb3 }), ), ( 0xb4, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0xb5, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0xb6, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::ZeroPageIndexedWithY), cycles: 4, }), ), ( 0xb7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB3(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xb8, Instruction::Valid(ValidInstruction { opcode: Opcode::CLV(AddressingMode::Implied), cycles: 2, }), ), ( 0xb9, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0xba, Instruction::Valid(ValidInstruction { opcode: Opcode::TSX(AddressingMode::Implied), cycles: 2, }), ), ( 0xbb, Instruction::Invalid(InvalidInstruction { opcode: 0xbb }), ), ( 0xbc, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0xbd, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0xbe, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0xbf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS3(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0xc0, Instruction::Valid(ValidInstruction { opcode: Opcode::CPY(AddressingMode::Immediate), cycles: 2, }), ), ( 0xc1, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0xc2, Instruction::Invalid(InvalidInstruction { opcode: 0xc2 }), ), ( 0xc3, Instruction::Invalid(InvalidInstruction { opcode: 0xc3 }), ), ( 0xc4, Instruction::Valid(ValidInstruction { opcode: Opcode::CPY(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0xc5, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0xc6, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xc7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB4(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xc8, Instruction::Valid(ValidInstruction { opcode: Opcode::INY(AddressingMode::Implied), cycles: 2, }), ), ( 0xc9, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::Immediate), cycles: 2, }), ), ( 0xca, Instruction::Valid(ValidInstruction { opcode: Opcode::DEX(AddressingMode::Implied), cycles: 2, }), ), ( 0xcb, Instruction::Valid(ValidInstruction { opcode: Opcode::WAI(AddressingMode::Implied), cycles: 3, }), ), ( 0xcc, Instruction::Valid(ValidInstruction { opcode: Opcode::CPY(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0xcd, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0xce, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0xcf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS4(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0xd0, Instruction::Valid(ValidInstruction { opcode: Opcode::BNE(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0xd1, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndirectIndexedWithY), cycles: 5, }), ), ( 0xd2, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndirect), cycles: 5, // Unsure, look here: https://cx16.dk/65c02/reference.html#CMP }), ), ( 0xd3, Instruction::Invalid(InvalidInstruction { opcode: 0xd3 }), ), ( 0xd4, Instruction::Invalid(InvalidInstruction { opcode: 0xd4 }), ), ( 0xd5, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0xd6, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::ZeroPageIndexedWithX), cycles: 6, }), ), ( 0xd7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB5(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xd8, Instruction::Valid(ValidInstruction { opcode: Opcode::CLD(AddressingMode::Implied), cycles: 2, }), ), ( 0xd9, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0xda, Instruction::Valid(ValidInstruction { opcode: Opcode::PHX(AddressingMode::Stack), cycles: 3, }), ), ( 0xdb, Instruction::Valid(ValidInstruction { opcode: Opcode::STP(AddressingMode::Implied), cycles: 3, }), ), ( 0xdc, Instruction::Invalid(InvalidInstruction { opcode: 0xdc }), ), ( 0xdd, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0xde, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::AbsoluteIndexedWithX), cycles: 7, }), ), ( 0xdf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS5(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0xe0, Instruction::Valid(ValidInstruction { opcode: Opcode::CPX(AddressingMode::Immediate), cycles: 2, }), ), ( 0xe1, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndexedIndirect), cycles: 6, }), ), ( 0xe2, Instruction::Invalid(InvalidInstruction { opcode: 0xe2 }), ), ( 0xe3, Instruction::Invalid(InvalidInstruction { opcode: 0xe3 }), ), ( 0xe4, Instruction::Valid(ValidInstruction { opcode: Opcode::CPX(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0xe5, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPage), cycles: 3, }), ), ( 0xe6, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xe7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB6(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xe8, Instruction::Valid(ValidInstruction { opcode: Opcode::INX(AddressingMode::Implied), cycles: 2, }), ), ( 0xe9, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::Immediate), cycles: 2, }), ), ( 0xea, Instruction::Valid(ValidInstruction { opcode: Opcode::NOP(AddressingMode::Implied), cycles: 2, }), ), ( 0xeb, Instruction::Invalid(InvalidInstruction { opcode: 0xeb }), ), ( 0xec, Instruction::Valid(ValidInstruction { opcode: Opcode::CPX(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0xed, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::AbsoluteA), cycles: 4, }), ), ( 0xee, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::AbsoluteA), cycles: 6, }), ), ( 0xef, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS6(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ( 0xf0, Instruction::Valid(ValidInstruction { opcode: Opcode::BEQ(AddressingMode::ProgramCounterRelative), cycles: 2, }), ), ( 0xf1, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndirectIndexedWithY), cycles: 5, }), ), ( 0xf2, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndirect), cycles: 5, }), ), ( 0xf3, Instruction::Invalid(InvalidInstruction { opcode: 0xf3 }), ), ( 0xf4, Instruction::Invalid(InvalidInstruction { opcode: 0xf4 }), ), ( 0xf5, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndexedWithX), cycles: 4, }), ), ( 0xf6, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::ZeroPageIndexedWithX), cycles: 6, }), ), ( 0xf7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB7(AddressingMode::ZeroPage), cycles: 5, }), ), ( 0xf8, Instruction::Valid(ValidInstruction { opcode: Opcode::SED(AddressingMode::Implied), cycles: 2, }), ), ( 0xf9, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::AbsoluteIndexedWithY), cycles: 4, }), ), ( 0xfa, Instruction::Valid(ValidInstruction { opcode: Opcode::PLX(AddressingMode::Stack), cycles: 4, }), ), ( 0xfb, Instruction::Invalid(InvalidInstruction { opcode: 0xfb }), ), ( 0xfc, Instruction::Invalid(InvalidInstruction { opcode: 0xfc }), ), ( 0xfd, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::AbsoluteIndexedWithX), cycles: 4, }), ), ( 0xfe, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::AbsoluteIndexedWithX), cycles: 7, }), ), ( 0xff, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS7(AddressingMode::ProgramCounterRelativeTest), cycles: 4, }), ), ]); opcodes.get(&opcode).unwrap().clone() } #[derive(Clone, Debug)] pub enum Opcode { ADC(AddressingMode), AND(AddressingMode), ASL(AddressingMode), BBR0(AddressingMode), BBR1(AddressingMode), BBR2(AddressingMode), BBR3(AddressingMode), BBR4(AddressingMode), BBR5(AddressingMode), BBR6(AddressingMode), BBR7(AddressingMode), BBS0(AddressingMode), BBS1(AddressingMode), BBS2(AddressingMode), BBS3(AddressingMode), BBS4(AddressingMode), BBS5(AddressingMode), BBS6(AddressingMode), BBS7(AddressingMode), BCC(AddressingMode), BCS(AddressingMode), BEQ(AddressingMode), BIT(AddressingMode), BMI(AddressingMode), BNE(AddressingMode), BPL(AddressingMode), BRA(AddressingMode), BRK(AddressingMode), BVC(AddressingMode), BVS(AddressingMode), CLC(AddressingMode), CLD(AddressingMode), CLI(AddressingMode), CLV(AddressingMode), CMP(AddressingMode), CPX(AddressingMode), CPY(AddressingMode), DEC(AddressingMode), DEX(AddressingMode), DEY(AddressingMode), EOR(AddressingMode), INC(AddressingMode), INX(AddressingMode), INY(AddressingMode), JMP(AddressingMode), JSR(AddressingMode), LDA(AddressingMode), LDX(AddressingMode), LDY(AddressingMode), LSR(AddressingMode), NOP(AddressingMode), ORA(AddressingMode), PHA(AddressingMode), PHP(AddressingMode), PHX(AddressingMode), PHY(AddressingMode), PLA(AddressingMode), PLP(AddressingMode), PLX(AddressingMode), PLY(AddressingMode), RMB0(AddressingMode), RMB1(AddressingMode), RMB2(AddressingMode), RMB3(AddressingMode), RMB4(AddressingMode), RMB5(AddressingMode), RMB6(AddressingMode), RMB7(AddressingMode), ROL(AddressingMode), ROR(AddressingMode), RTI(AddressingMode), RTS(AddressingMode), SBC(AddressingMode), SEC(AddressingMode), SED(AddressingMode), SEI(AddressingMode), SMB0(AddressingMode), SMB1(AddressingMode), SMB2(AddressingMode), SMB3(AddressingMode), SMB4(AddressingMode), SMB5(AddressingMode), SMB6(AddressingMode), SMB7(AddressingMode), STA(AddressingMode), STP(AddressingMode), STX(AddressingMode), STY(AddressingMode), STZ(AddressingMode), TAX(AddressingMode), TAY(AddressingMode), TRB(AddressingMode), TSB(AddressingMode), TSX(AddressingMode), TXA(AddressingMode), TXS(AddressingMode), TYA(AddressingMode), WAI(AddressingMode), } #[derive(Clone, Copy)] enum AddressingModeValue { Implied, // Used when an instruction doesn't need an operation, e.g. RTS Relative(Byte), // Used by branch instructions to offset the program counter RelativeTest(Byte, Byte), // Used by branch instructions to offset the program counter Absolute(Word), // Just a plain ol' address Accumulator(Byte), // This isn't technically accurate, since the datasheet says this would be // Implied, but we do use the value of the accumulator as the operand so this // is how i'm gonna do it } impl TryFrom for Word { type Error = AddressingModeError; fn try_from(value: AddressingModeValue) -> Result { match value { AddressingModeValue::Implied => Err(AddressingModeError::IncompatibleAddrMode), AddressingModeValue::RelativeTest(_zero_page, offset) => Ok(offset as Word), AddressingModeValue::Relative(inner_value) => Ok(inner_value as Word), AddressingModeValue::Absolute(inner_value) => Ok(inner_value), AddressingModeValue::Accumulator(inner_value) => Ok(inner_value as Word), } } } fn get_address( mode: &AddressingMode, cpu: &mut Cpu, ) -> Result { fn accumulator(cpu: &mut Cpu) -> AddressingModeValue { let byte = cpu.a; AddressingModeValue::Accumulator(byte) } fn implied(_cpu: &mut Cpu) -> AddressingModeValue { // i AddressingModeValue::Implied } fn immediate(cpu: &mut Cpu) -> AddressingModeValue { // # let address: Word = cpu.pc; cpu.pc += 1; AddressingModeValue::Absolute(address) } fn absolute_a(cpu: &mut Cpu) -> Result { // a let address = cpu.read_word(cpu.pc)?; cpu.pc += 2; Ok(AddressingModeValue::Absolute(address)) } fn zero_page(cpu: &mut Cpu) -> Result { // zp let address = cpu.read(cpu.pc)?; cpu.pc += 1; Ok(AddressingModeValue::Absolute(address as Word)) } fn absolute_indexed_with_x( // a, y cpu: &mut Cpu, ) -> Result { let word = cpu.read_word(cpu.pc)?; cpu.pc += 2; let address: Word = word + cpu.x as Word; Ok(AddressingModeValue::Absolute(address)) } fn absolute_indexed_with_y( // a, y cpu: &mut Cpu, ) -> Result { let word = cpu.read_word(cpu.pc)?; cpu.pc += 2; let address: Word = word + cpu.y as Word; Ok(AddressingModeValue::Absolute(address)) } fn zero_page_indexed_with_x( // zp, x cpu: &mut Cpu, ) -> Result { let byte = cpu.read(cpu.pc)?; cpu.pc += 1; let address: Word = (byte.wrapping_add(cpu.x)) as Word; Ok(AddressingModeValue::Absolute(address)) } fn zero_page_indexed_with_y( // zp, y cpu: &mut Cpu, ) -> Result { let byte = cpu.read(cpu.pc)?; cpu.pc += 1; let address: Word = (byte + cpu.y) as Word; Ok(AddressingModeValue::Absolute(address)) } fn absolute_indirect( // (a) cpu: &mut Cpu, ) -> Result { let word = cpu.read_word(cpu.pc)?; let address = cpu.read_word(word)?; cpu.pc += 2; Ok(AddressingModeValue::Absolute(address)) } fn absolute_indexed_indirect( // (a, x), only used with the JMP instruction cpu: &mut Cpu, ) -> Result { let word = cpu.read_word(cpu.pc)?; let address = cpu.read_word(word + cpu.x as Word)?; cpu.pc += 2; Ok(AddressingModeValue::Absolute(address)) } fn zero_page_indirect( // (zp) cpu: &mut Cpu, ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; let address: Word = cpu.read_word(byte as Word)?; cpu.pc += 1; Ok(AddressingModeValue::Absolute(address as Word)) } fn zero_page_indexed_indirect( // (zp, x) cpu: &mut Cpu, ) -> Result { let byte = cpu.read(cpu.pc)?; let address = cpu.read_word((byte + cpu.x) as Word)?; // Anytime you see something like `byte as Word`, it's using the byte as a zero-page address cpu.pc += 1; Ok(AddressingModeValue::Absolute(address)) } fn zero_page_indirect_indexed_with_y( // (zp), y cpu: &mut Cpu, ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; let address: Word = cpu.read_word(byte as Word)?; cpu.pc += 1; Ok(AddressingModeValue::Absolute(address + cpu.y as Word)) } fn relative( // r cpu: &mut Cpu, ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; cpu.pc += 1; Ok(AddressingModeValue::Relative(byte)) } fn relative_test( // r cpu: &mut Cpu, ) -> Result { let byte: Byte = cpu.read(cpu.pc)?; cpu.pc += 1; let address = cpu.read(cpu.pc)?; cpu.pc += 1; Ok(AddressingModeValue::RelativeTest(byte, address)) } match mode { AddressingMode::AbsoluteA => absolute_a(cpu), AddressingMode::AbsoluteIndexedWithX => absolute_indexed_with_x(cpu), AddressingMode::AbsoluteIndexedWithY => absolute_indexed_with_y(cpu), AddressingMode::AbsoluteIndirect => absolute_indirect(cpu), AddressingMode::AbsoluteIndexedIndirect => absolute_indexed_indirect(cpu), AddressingMode::Immediate => Ok(immediate(cpu)), AddressingMode::ProgramCounterRelative => relative(cpu), AddressingMode::ZeroPage => zero_page(cpu), AddressingMode::ZeroPageIndexedWithX => zero_page_indexed_with_x(cpu), AddressingMode::ZeroPageIndexedWithY => zero_page_indexed_with_y(cpu), AddressingMode::ZeroPageIndirect => zero_page_indirect(cpu), AddressingMode::ZeroPageIndexedIndirect => zero_page_indexed_indirect(cpu), AddressingMode::ZeroPageIndirectIndexedWithY => zero_page_indirect_indexed_with_y(cpu), AddressingMode::Accumulator => Ok(accumulator(cpu)), AddressingMode::ProgramCounterRelativeTest => relative_test(cpu), _ => Ok(implied(cpu)), } } fn signed_byte_to_word(value: Byte) -> Word { let mut value = Word::from(value); if value & 0x80 > 0 { value |= 0xff00; } value } fn branch( cpu: &mut Cpu, condition: bool, value: AddressingModeValue, ) -> Result<(), AddressingModeError> { match value { AddressingModeValue::Relative(address) => { let address = signed_byte_to_word(address).wrapping_add(cpu.pc); if condition { if address & 0xff00 != cpu.pc & 0xff00 { cpu.pending_cycles += 2; } else { cpu.pending_cycles += 1; } cpu.pc = address; } Ok(()) } AddressingModeValue::RelativeTest(_zero_page, address) => { let address = signed_byte_to_word(address).wrapping_add(cpu.pc); if condition { if address & 0xff00 != cpu.pc & 0xff00 { cpu.pending_cycles += 2; } else { cpu.pending_cycles += 1; } cpu.pc = address; } Ok(()) } _ => Err(AddressingModeError::IncompatibleAddrMode), } } impl Opcode { pub fn call(&self, cpu: &mut Cpu) -> Result<(), GeorgeErrorKind> { match self { Opcode::ADC(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as Word + byte as Word + carry as Word; println!("{result:#04x}"); cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value())); cpu.set_flag(StatusFlag::Zero, result as Byte == 0); cpu.set_flag( StatusFlag::Overflow, (cpu.a ^ byte) & (cpu.a ^ result as Byte) & 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 Byte)); cpu.a = result as Byte; println!("{:#04x}", cpu.a); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::AND(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; cpu.a &= cpu.read(address.try_into()?)?; cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::ASL(mode) => { fn asl(cpu: &mut Cpu, value: Byte) -> Byte { 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 } match mode { AddressingMode::Accumulator => { cpu.a = asl(cpu, cpu.a); Ok(()) } AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; let value = cpu.read(address.try_into()?)?; let result = asl(cpu, value); cpu.write(address.try_into()?, result)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), } } Opcode::BBR0(mode) => match mode { // These instructions are weird, cause they're relative, except the byte being tested is immediate, // so i'm not sure if what i've already written is going to model this accurately AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_0001 != 0b0000_0001, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBR1(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_0010 != 0b0000_0010, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBR2(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_0100 != 0b0000_0100, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBR3(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_1000 != 0b0000_1000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBR4(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0001_0000 != 0b0001_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBR5(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0010_0000 != 0b0010_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBR6(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0100_0000 != 0b0100_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBR7(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b1000_0000 != 0b1000_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS0(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_0001 == 0b0000_0001, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS1(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_0010 == 0b0000_0010, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS2(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_0100 == 0b0000_0100, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS3(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0000_1000 == 0b0000_1000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS4(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0001_0000 == 0b0001_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS5(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0010_0000 == 0b0010_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS6(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b0100_0000 == 0b0100_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BBS7(mode) => match mode { AddressingMode::ProgramCounterRelativeTest => match get_address(mode, cpu) { Ok(value) => match value { AddressingModeValue::RelativeTest(byte, _address) => { let zero_page_address = byte as Word; let test_byte = cpu.read(zero_page_address)?; branch(cpu, test_byte & 0b1000_0000 == 0b1000_0000, value)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Err(error) => Err(GeorgeErrorKind::Execution(error)), }, _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BCC(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, !cpu.get_flag(StatusFlag::Carry), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BCS(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, cpu.get_flag(StatusFlag::Carry), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BEQ(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, cpu.get_flag(StatusFlag::Zero), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BIT(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX => { let address = get_address(mode, cpu)?; let result = cpu.a & cpu.read(address.try_into()?)?; cpu.set_flag(StatusFlag::Zero, result == 0); cpu.set_flag( StatusFlag::Overflow, result & StatusFlag::Overflow as Byte == StatusFlag::Overflow as Byte, ); cpu.set_flag( StatusFlag::Negative, result & StatusFlag::Negative as Byte == StatusFlag::Negative as Byte, ); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BMI(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, cpu.get_flag(StatusFlag::Negative), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BNE(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, !cpu.get_flag(StatusFlag::Zero), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BPL(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, !cpu.get_flag(StatusFlag::Negative), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BRA(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, true, address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BRK(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Brk, true); Ok(()) //panic!("Interrupts unimplemented!"); } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BVC(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, !cpu.get_flag(StatusFlag::Overflow), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::BVS(mode) => match mode { AddressingMode::ProgramCounterRelative => { let address = get_address(mode, cpu)?; branch(cpu, cpu.get_flag(StatusFlag::Overflow), address)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::CLC(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Carry, false); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::CLD(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Decimal, false); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::CLI(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::IrqDisable, false); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::CLV(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Overflow, false); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::CMP(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; cpu.set_flag(StatusFlag::Carry, cpu.a >= byte); cpu.set_flag(StatusFlag::Zero, cpu.a == byte); cpu.set_flag(StatusFlag::Negative, cpu.a <= byte); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::CPX(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::AbsoluteA => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; 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)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::CPY(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::AbsoluteA => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; println!("{byte:#04x}"); cpu.set_flag(StatusFlag::Carry, cpu.y >= byte); cpu.set_flag(StatusFlag::Zero, cpu.y == byte); cpu.set_flag(StatusFlag::Negative, cpu.y <= byte); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::DEC(mode) => match mode { AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX => { let address = get_address(mode, cpu)?; cpu.write(address.try_into()?, cpu.read(address.try_into()?)? - 1)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::DEX(mode) => match mode { AddressingMode::Implied => { 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)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::DEY(mode) => match mode { AddressingMode::Implied => { 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)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::EOR(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; cpu.a ^= cpu.read(address.try_into()?)?; cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::INC(mode) => match mode { AddressingMode::Accumulator => { 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)); Ok(()) } AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; cpu.set_flag(StatusFlag::Zero, byte.wrapping_add(1) == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(byte.wrapping_add(1))); cpu.write(address.try_into()?, byte.wrapping_add(1))?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::INX(mode) => match mode { AddressingMode::Implied => { 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)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::INY(mode) => match mode { AddressingMode::Implied => { 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)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::JMP(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndirect | AddressingMode::AbsoluteIndexedIndirect => { let address = get_address(mode, cpu)?; cpu.pc = address.try_into()?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::JSR(mode) => match mode { AddressingMode::AbsoluteA => { let return_address = cpu.pc - 1; cpu.push_stack_word(return_address)?; let address = get_address(mode, cpu)?; cpu.pc = address.try_into()?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::LDA(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndexedWithX | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; cpu.a = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::LDX(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithY | AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithY => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; cpu.x = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::LDY(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; cpu.y = byte; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::LSR(mode) => { fn lsr(cpu: &mut Cpu, value: Byte) -> Byte { 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 } match mode { AddressingMode::Accumulator => { cpu.a = lsr(cpu, cpu.a); Ok(()) } AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; let value = cpu.read(address.try_into()?)?; let result = lsr(cpu, value); cpu.write(address.try_into()?, result)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), } } Opcode::NOP(_mode) => { cpu.pc += 1; Ok(()) } Opcode::ORA(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; cpu.a |= cpu.read(address.try_into()?)?; cpu.set_flag(StatusFlag::Zero, cpu.a == 0); cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PHA(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.a)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PHP(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.p)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PHX(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.x)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PHY(mode) => match mode { AddressingMode::Stack => { cpu.push_stack(cpu.y)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PLA(mode) => match mode { AddressingMode::Stack => { cpu.a = cpu.pop_stack()?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PLP(mode) => match mode { AddressingMode::Stack => { cpu.p = cpu.pop_stack()?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PLX(mode) => match mode { AddressingMode::Stack => { cpu.x = cpu.pop_stack()?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::PLY(mode) => match mode { AddressingMode::Stack => { cpu.y = cpu.pop_stack()?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB0(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b1111_1110; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB1(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b1111_1101; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB2(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b1111_1011; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB3(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b1111_0111; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB4(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b1110_1111; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB5(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b1101_1111; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB6(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b1011_1111; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RMB7(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b0111_1111; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::ROL(mode) => { fn rol(cpu: &mut Cpu, value: Byte) -> Byte { let carry = cpu.get_flag(StatusFlag::Carry) as Byte; 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 } match mode { AddressingMode::Accumulator => { cpu.a = rol(cpu, cpu.a); Ok(()) } AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; let value = cpu.read(address.try_into()?)?; let result = rol(cpu, value); cpu.write(address.try_into()?, result)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), } } Opcode::ROR(mode) => { fn ror(cpu: &mut Cpu, value: Byte) -> Byte { let carry = cpu.get_flag(StatusFlag::Carry) as Byte; 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 } match mode { AddressingMode::Accumulator => { cpu.a = ror(cpu, cpu.a); Ok(()) } AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; let value = cpu.read(address.try_into()?)?; let result = ror(cpu, value); cpu.write(address.try_into()?, result)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), } } Opcode::RTI(mode) => match mode { AddressingMode::Implied => { cpu.s = cpu.pop_stack()?; cpu.p = cpu.pop_stack()?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::RTS(mode) => match mode { AddressingMode::Stack => { let return_address = cpu.pop_stack_word()?; cpu.pc = return_address + 3; // Go back to where we jsr'ed, skipping the operand Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SBC(mode) => match mode { AddressingMode::Immediate | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX | AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let carry = cpu.get_flag(StatusFlag::Carry); let result = cpu.a as Word + byte as Word + !carry as Word; cpu.set_flag(StatusFlag::Carry, result > Word::from(u8::max_value())); cpu.set_flag(StatusFlag::Zero, result as Byte == 0); cpu.set_flag( StatusFlag::Overflow, (cpu.a ^ byte) & (cpu.a ^ result as Byte) & 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 Byte)); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SEC(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Carry, true); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SED(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::Decimal, true); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SEI(mode) => match mode { AddressingMode::Implied => { cpu.set_flag(StatusFlag::IrqDisable, true); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB0(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte | 0b0000_0001; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB1(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte | 0b0000_0010; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB2(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte | 0b0000_0100; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB3(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte | 0b0000_1000; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB4(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte | 0b0001_0000; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB5(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte | 0b0010_0000; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB6(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte & 0b0100_0000; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::SMB7(mode) => match mode { AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; let reset_byte = byte | 0b1000_0000; cpu.write(address.try_into()?, reset_byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::STA(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::AbsoluteIndexedWithY | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndirect | AddressingMode::ZeroPageIndexedIndirect | AddressingMode::ZeroPageIndexedWithX | AddressingMode::ZeroPageIndirectIndexedWithY => { let address = get_address(mode, cpu)?; cpu.write(address.try_into()?, cpu.a)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::STP(mode) => match mode { AddressingMode::Implied => { cpu.stop(); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::STX(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithY => { let address = get_address(mode, cpu)?; cpu.write(address.try_into()?, cpu.x)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::STY(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; cpu.write(address.try_into()?, cpu.y)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::STZ(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::AbsoluteIndexedWithX | AddressingMode::ZeroPage | AddressingMode::ZeroPageIndexedWithX => { let address = get_address(mode, cpu)?; cpu.write(address.try_into()?, 0)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TAX(mode) => match mode { AddressingMode::Implied => { cpu.x = cpu.a; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TAY(mode) => match mode { AddressingMode::Implied => { cpu.y = cpu.a; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); cpu.set_flag(StatusFlag::Zero, cpu.y == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TRB(mode) => match mode { // 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 AddressingMode::AbsoluteA | AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; cpu.write(address.try_into()?, !cpu.a & byte)?; cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TSB(mode) => match mode { AddressingMode::AbsoluteA | AddressingMode::ZeroPage => { let address = get_address(mode, cpu)?; let byte = cpu.read(address.try_into()?)?; cpu.write(address.try_into()?, cpu.a | byte)?; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TSX(mode) => match mode { AddressingMode::Implied => { cpu.x = cpu.s; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); cpu.set_flag(StatusFlag::Zero, cpu.x == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TXA(mode) => match mode { AddressingMode::Implied => { cpu.a = cpu.x; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TXS(mode) => match mode { AddressingMode::Implied => { cpu.s = cpu.x; Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::TYA(mode) => match mode { AddressingMode::Implied => { cpu.a = cpu.y; cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); cpu.set_flag(StatusFlag::Zero, cpu.a == 0); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, Opcode::WAI(mode) => match mode { AddressingMode::Implied => { cpu.wait_for_interrupt(); Ok(()) } _ => Err(GeorgeErrorKind::AddrMode( AddressingModeError::IncompatibleAddrMode, )), }, } } }