3442 lines
121 KiB
Rust
3442 lines
121 KiB
Rust
#![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<u8, Instruction> = 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<AddressingModeValue> for Word {
|
|
type Error = AddressingModeError;
|
|
fn try_from(value: AddressingModeValue) -> Result<Self, Self::Error> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
// a
|
|
let address = cpu.read_word(cpu.pc)?;
|
|
cpu.pc += 2;
|
|
Ok(AddressingModeValue::Absolute(address))
|
|
}
|
|
|
|
fn zero_page(cpu: &mut Cpu) -> Result<AddressingModeValue, ExecutionError> {
|
|
// 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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
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<AddressingModeValue, ExecutionError> {
|
|
let byte: Byte = cpu.read(cpu.pc)?;
|
|
cpu.pc += 1;
|
|
Ok(AddressingModeValue::Relative(byte))
|
|
}
|
|
|
|
fn relative_test(
|
|
// r
|
|
cpu: &mut Cpu,
|
|
) -> Result<AddressingModeValue, ExecutionError> {
|
|
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,
|
|
)),
|
|
},
|
|
}
|
|
}
|
|
}
|