use crate::ArithmeticOutput;
use core::fmt::{Display, Error, Formatter};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Instruction {
ADC,
ADCnd,
AND,
ASL,
BCC,
BCS,
BEQ,
BIT,
BMI,
BNE,
BPL,
BRA,
BRK,
BRKcld,
BVC,
BVS,
CLC,
CLD,
CLI,
CLV,
CMP,
CPX,
CPY,
DEC,
DEX,
DEY,
EOR,
INC,
INX,
INY,
JMP,
JSR,
LDA,
LDX,
LDY,
LSR,
NOP,
ORA,
PHA,
PHX,
PHY,
PHP,
PLA,
PLX,
PLY,
PLP,
ROL,
ROR,
RTI,
RTS,
SBC,
SBCnd,
SEC,
SED,
SEI,
STA,
STX,
STY,
STZ,
SAX,
TAX,
TAY,
TRB,
TSB,
TSX,
TXA,
TXS,
TYA,
WAI,
STP,
BBR(u8),
BBS(u8),
RMB(u8),
SMB(u8),
XAA,
ALR,
ANC,
ARR,
DCP,
ISC,
JAM,
LAS,
LAX,
NOP1,
NOPI,
NOPZ,
NOPZX,
NOPA,
NOPAX,
NOPAX8,
RLA,
RRA,
SBX,
SLO,
SRE,
USBC,
}
impl Instruction {
#[must_use]
pub fn base_cycles(self, mode: AddressingMode) -> u8 {
#[allow(clippy::enum_glob_use)]
use AddressingMode::*;
#[allow(clippy::enum_glob_use)]
use Instruction::*;
match (self, mode) {
(ADC | ADCnd, Immediate) => 2,
(ADC | ADCnd, ZeroPage) => 3,
(ADC | ADCnd, ZeroPageX) => 4,
(ADC | ADCnd, Absolute) => 4,
(ADC | ADCnd, AbsoluteX) => 4, (ADC | ADCnd, AbsoluteY) => 4, (ADC | ADCnd, IndexedIndirectX) => 6,
(ADC | ADCnd, IndirectIndexedY) => 5, (ADC | ADCnd, ZeroPageIndirect) => 5,
(AND, Immediate) => 2,
(AND, ZeroPage) => 3,
(AND, ZeroPageX) => 4,
(AND, Absolute) => 4,
(AND, AbsoluteX) => 4, (AND, AbsoluteY) => 4, (AND, IndexedIndirectX) => 6,
(AND, IndirectIndexedY) => 5, (AND, ZeroPageIndirect) => 5,
(ASL, Accumulator) => 2,
(ASL, ZeroPage) => 5,
(ASL, ZeroPageX) => 6,
(ASL, Absolute) => 6,
(ASL, AbsoluteX) => 7,
(BCC | BCS | BEQ | BMI | BNE | BPL | BVC | BVS, Relative) => 2,
(BRA, Relative) => 3,
(BIT, ZeroPage) => 3,
(BIT, Absolute) => 4,
(BIT, ZeroPageX) => 4, (BIT, AbsoluteX) => 4, (BIT, Immediate) => 2,
(BRK | BRKcld, Implied) => 7,
(CMP, Immediate) => 2,
(CMP, ZeroPage) => 3,
(CMP, ZeroPageX) => 4,
(CMP, Absolute) => 4,
(CMP, AbsoluteX) => 4, (CMP, AbsoluteY) => 4, (CMP, IndexedIndirectX) => 6,
(CMP, IndirectIndexedY) => 5, (CMP, ZeroPageIndirect) => 5,
(CPX, Immediate) => 2,
(CPX, ZeroPage) => 3,
(CPX, Absolute) => 4,
(CPY, Immediate) => 2,
(CPY, ZeroPage) => 3,
(CPY, Absolute) => 4,
(DEC, Accumulator) => 2, (DEC, ZeroPage) => 5,
(DEC, ZeroPageX) => 6,
(DEC, Absolute) => 6,
(DEC, AbsoluteX) => 7,
(DEX | DEY, Implied) => 2,
(EOR, Immediate) => 2,
(EOR, ZeroPage) => 3,
(EOR, ZeroPageX) => 4,
(EOR, Absolute) => 4,
(EOR, AbsoluteX) => 4, (EOR, AbsoluteY) => 4, (EOR, IndexedIndirectX) => 6,
(EOR, IndirectIndexedY) => 5, (EOR, ZeroPageIndirect) => 5,
(CLC | CLD | CLI | CLV | SEC | SED | SEI, Implied) => 2,
(INC, Accumulator) => 2, (INC, ZeroPage) => 5,
(INC, ZeroPageX) => 6,
(INC, Absolute) => 6,
(INC, AbsoluteX) => 7,
(INX | INY, Implied) => 2,
(JMP, Absolute) => 3,
(JMP, BuggyIndirect) => 5, (JMP, Indirect) => 6, (JMP, AbsoluteIndexedIndirect) => 6,
(JSR, Absolute) => 6,
(LDA, Immediate) => 2,
(LDA, ZeroPage) => 3,
(LDA, ZeroPageX) => 4,
(LDA, Absolute) => 4,
(LDA, AbsoluteX) => 4, (LDA, AbsoluteY) => 4, (LDA, IndexedIndirectX) => 6,
(LDA, IndirectIndexedY) => 5, (LDA, ZeroPageIndirect) => 5,
(LDX, Immediate) => 2,
(LDX, ZeroPage) => 3,
(LDX, ZeroPageY) => 4,
(LDX, Absolute) => 4,
(LDX, AbsoluteY) => 4,
(LDY, Immediate) => 2,
(LDY, ZeroPage) => 3,
(LDY, ZeroPageX) => 4,
(LDY, Absolute) => 4,
(LDY, AbsoluteX) => 4,
(LSR, Accumulator) => 2,
(LSR, ZeroPage) => 5,
(LSR, ZeroPageX) => 6,
(LSR, Absolute) => 6,
(LSR, AbsoluteX) => 7,
(NOP, Implied) => 2,
(ORA, Immediate) => 2,
(ORA, ZeroPage) => 3,
(ORA, ZeroPageX) => 4,
(ORA, Absolute) => 4,
(ORA, AbsoluteX) => 4, (ORA, AbsoluteY) => 4, (ORA, IndexedIndirectX) => 6,
(ORA, IndirectIndexedY) => 5, (ORA, ZeroPageIndirect) => 5,
(PHA | PHP, Implied) => 3,
(PHX | PHY, Implied) => 3,
(PLA | PLP, Implied) => 4,
(PLX | PLY, Implied) => 4,
(ROL, Accumulator) => 2,
(ROL, ZeroPage) => 5,
(ROL, ZeroPageX) => 6,
(ROL, Absolute) => 6,
(ROL, AbsoluteX) => 7,
(ROR, Accumulator) => 2,
(ROR, ZeroPage) => 5,
(ROR, ZeroPageX) => 6,
(ROR, Absolute) => 6,
(ROR, AbsoluteX) => 7,
(RTI, Implied) => 6,
(RTS, Implied) => 6,
(SBC | SBCnd, Immediate) => 2,
(SBC | SBCnd, ZeroPage) => 3,
(SBC | SBCnd, ZeroPageX) => 4,
(SBC | SBCnd, Absolute) => 4,
(SBC | SBCnd, AbsoluteX) => 4, (SBC | SBCnd, AbsoluteY) => 4, (SBC | SBCnd, IndexedIndirectX) => 6,
(SBC | SBCnd, IndirectIndexedY) => 5, (SBC | SBCnd, ZeroPageIndirect) => 5,
(STA, ZeroPage) => 3,
(STA, ZeroPageX) => 4,
(STA, Absolute) => 4,
(STA, AbsoluteX) => 5,
(STA, AbsoluteY) => 5,
(STA, IndexedIndirectX) => 6,
(STA, IndirectIndexedY) => 6,
(STA, ZeroPageIndirect) => 5,
(STX, ZeroPage) => 3,
(STX, ZeroPageY) => 4,
(STX, Absolute) => 4,
(STY, ZeroPage) => 3,
(STY, ZeroPageX) => 4,
(STY, Absolute) => 4,
(STZ, ZeroPage) => 3,
(STZ, ZeroPageX) => 4,
(STZ, Absolute) => 4,
(STZ, AbsoluteX) => 5,
(TAX | TAY | TSX | TXA | TXS | TYA, Implied) => 2,
(TRB | TSB, ZeroPage) => 5,
(TRB | TSB, Absolute) => 6,
(STP, Implied) => 3,
(WAI, Implied) => 3,
(BBR(_), ZeroPageRelative) => 5,
(BBS(_), ZeroPageRelative) => 5,
(RMB(_), ZeroPage) => 5,
(SMB(_), ZeroPage) => 5,
(SAX, ZeroPage) => 3,
(SAX, ZeroPageY) => 4,
(SAX, Absolute) => 4,
(SAX, IndexedIndirectX) => 6,
(XAA, Immediate) => 2,
(ALR, Immediate) => 2,
(ANC, Immediate) => 2,
(ARR, Immediate) => 2,
(DCP, ZeroPage) => 5,
(DCP, ZeroPageX) => 6,
(DCP, Absolute) => 6,
(DCP, AbsoluteX) => 7,
(DCP, AbsoluteY) => 7,
(DCP, IndexedIndirectX) => 8,
(DCP, IndirectIndexedY) => 8,
(ISC, ZeroPage) => 5,
(ISC, ZeroPageX) => 6,
(ISC, Absolute) => 6,
(ISC, AbsoluteX) => 7,
(ISC, AbsoluteY) => 7,
(ISC, IndexedIndirectX) => 8,
(ISC, IndirectIndexedY) => 8,
(JAM, Implied) => 2,
(LAS, AbsoluteY) => 4,
(LAX, ZeroPage) => 3,
(LAX, ZeroPageY) => 4,
(LAX, Absolute) => 4,
(LAX, AbsoluteY) => 4, (LAX, IndexedIndirectX) => 6,
(LAX, IndirectIndexedY) => 5,
(NOP1, Implied) => 1,
(NOPI, Immediate) => 2,
(NOPZ, ZeroPage) => 3,
(NOPZX, ZeroPageX) => 4,
(NOPA, Absolute) => 4,
(NOPAX, AbsoluteX) => 4, (NOPAX8, AbsoluteX) => 8,
(RLA, ZeroPage) => 5,
(RLA, ZeroPageX) => 6,
(RLA, Absolute) => 6,
(RLA, AbsoluteX) => 7,
(RLA, AbsoluteY) => 7,
(RLA, IndexedIndirectX) => 8,
(RLA, IndirectIndexedY) => 8,
(RRA, ZeroPage) => 5,
(RRA, ZeroPageX) => 6,
(RRA, Absolute) => 6,
(RRA, AbsoluteX) => 7,
(RRA, AbsoluteY) => 7,
(RRA, IndexedIndirectX) => 8,
(RRA, IndirectIndexedY) => 8,
(SBX, Immediate) => 2,
(SLO, ZeroPage) => 5,
(SLO, ZeroPageX) => 6,
(SLO, Absolute) => 6,
(SLO, AbsoluteX) => 7,
(SLO, AbsoluteY) => 7,
(SLO, IndexedIndirectX) => 8,
(SLO, IndirectIndexedY) => 8,
(SRE, ZeroPage) => 5,
(SRE, ZeroPageX) => 6,
(SRE, Absolute) => 6,
(SRE, AbsoluteX) => 7,
(SRE, AbsoluteY) => 7,
(SRE, IndexedIndirectX) => 8,
(SRE, IndirectIndexedY) => 8,
(USBC, Immediate) => 2,
_ => unreachable!("undecoded instruction"),
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum OpInput {
UseImplied,
UseImmediate(u8),
UseRelative(u16),
UseAddress { address: u16, page_crossed: bool },
UseBitBranch { zp_address: u8, relative: u16 },
}
impl OpInput {
#[must_use]
pub const fn page_crossed(&self) -> bool {
match self {
OpInput::UseAddress { page_crossed, .. } => *page_crossed,
_ => false,
}
}
}
impl Display for OpInput {
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
match self {
OpInput::UseImplied => write!(f, ""),
OpInput::UseImmediate(v) => write!(f, "#${v:02X}"),
OpInput::UseRelative(v) => write!(f, "${v:04X}"),
OpInput::UseAddress { address, .. } => write!(f, "${address:04X}"),
OpInput::UseBitBranch {
zp_address,
relative,
} => {
write!(f, "${zp_address:02X},${relative:04X}")
}
}
}
}
#[derive(Copy, Clone, Debug)]
pub enum AddressingMode {
Accumulator,
Implied,
Immediate,
ZeroPage,
ZeroPageX,
ZeroPageY,
Relative,
Absolute,
AbsoluteX,
AbsoluteY,
BuggyIndirect,
Indirect,
IndexedIndirectX,
IndirectIndexedY,
ZeroPageIndirect,
AbsoluteIndexedIndirect,
ZeroPageRelative,
}
impl AddressingMode {
#[must_use]
pub const fn extra_bytes(self) -> u16 {
match self {
AddressingMode::Accumulator => 0,
AddressingMode::Implied => 0,
AddressingMode::Immediate => 1,
AddressingMode::ZeroPage => 1,
AddressingMode::ZeroPageX => 1,
AddressingMode::ZeroPageY => 1,
AddressingMode::Relative => 1,
AddressingMode::Absolute => 2,
AddressingMode::AbsoluteX => 2,
AddressingMode::AbsoluteY => 2,
AddressingMode::Indirect => 2,
AddressingMode::BuggyIndirect => 2,
AddressingMode::IndexedIndirectX => 1,
AddressingMode::IndirectIndexedY => 1,
AddressingMode::ZeroPageIndirect => 1,
AddressingMode::AbsoluteIndexedIndirect => 2,
AddressingMode::ZeroPageRelative => 2,
}
}
}
pub type DecodedInstr = (Instruction, AddressingMode, OpInput);
#[derive(Copy, Clone, Debug, Default)]
pub struct Nmos6502;
impl crate::Variant for Nmos6502 {
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
match opcode {
0x00 => Some((Instruction::BRK, AddressingMode::Implied)),
0x01 => Some((Instruction::ORA, AddressingMode::IndexedIndirectX)),
0x02 => Some((Instruction::JAM, AddressingMode::Implied)),
0x03 => Some((Instruction::SLO, AddressingMode::IndexedIndirectX)),
0x04 => Some((Instruction::NOPZ, AddressingMode::ZeroPage)),
0x05 => Some((Instruction::ORA, AddressingMode::ZeroPage)),
0x06 => Some((Instruction::ASL, AddressingMode::ZeroPage)),
0x07 => Some((Instruction::SLO, AddressingMode::ZeroPage)),
0x08 => Some((Instruction::PHP, AddressingMode::Implied)),
0x09 => Some((Instruction::ORA, AddressingMode::Immediate)),
0x0a => Some((Instruction::ASL, AddressingMode::Accumulator)),
0x0b => Some((Instruction::ANC, AddressingMode::Immediate)),
0x0c => Some((Instruction::NOPA, AddressingMode::Absolute)),
0x0d => Some((Instruction::ORA, AddressingMode::Absolute)),
0x0e => Some((Instruction::ASL, AddressingMode::Absolute)),
0x0f => Some((Instruction::SLO, AddressingMode::Absolute)),
0x10 => Some((Instruction::BPL, AddressingMode::Relative)),
0x11 => Some((Instruction::ORA, AddressingMode::IndirectIndexedY)),
0x12 => Some((Instruction::JAM, AddressingMode::Implied)),
0x13 => Some((Instruction::SLO, AddressingMode::IndirectIndexedY)),
0x14 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x15 => Some((Instruction::ORA, AddressingMode::ZeroPageX)),
0x16 => Some((Instruction::ASL, AddressingMode::ZeroPageX)),
0x17 => Some((Instruction::SLO, AddressingMode::ZeroPageX)),
0x18 => Some((Instruction::CLC, AddressingMode::Implied)),
0x19 => Some((Instruction::ORA, AddressingMode::AbsoluteY)),
0x1a => Some((Instruction::NOP, AddressingMode::Implied)),
0x1b => Some((Instruction::SLO, AddressingMode::AbsoluteY)),
0x1c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x1d => Some((Instruction::ORA, AddressingMode::AbsoluteX)),
0x1e => Some((Instruction::ASL, AddressingMode::AbsoluteX)),
0x1f => Some((Instruction::SLO, AddressingMode::AbsoluteX)),
0x20 => Some((Instruction::JSR, AddressingMode::Absolute)),
0x21 => Some((Instruction::AND, AddressingMode::IndexedIndirectX)),
0x22 => Some((Instruction::JAM, AddressingMode::Implied)),
0x23 => Some((Instruction::RLA, AddressingMode::IndexedIndirectX)),
0x24 => Some((Instruction::BIT, AddressingMode::ZeroPage)),
0x25 => Some((Instruction::AND, AddressingMode::ZeroPage)),
0x26 => Some((Instruction::ROL, AddressingMode::ZeroPage)),
0x27 => Some((Instruction::RLA, AddressingMode::ZeroPage)),
0x28 => Some((Instruction::PLP, AddressingMode::Implied)),
0x29 => Some((Instruction::AND, AddressingMode::Immediate)),
0x2a => Some((Instruction::ROL, AddressingMode::Accumulator)),
0x2b => Some((Instruction::ANC, AddressingMode::Immediate)),
0x2c => Some((Instruction::BIT, AddressingMode::Absolute)),
0x2d => Some((Instruction::AND, AddressingMode::Absolute)),
0x2e => Some((Instruction::ROL, AddressingMode::Absolute)),
0x2f => Some((Instruction::RLA, AddressingMode::Absolute)),
0x30 => Some((Instruction::BMI, AddressingMode::Relative)),
0x31 => Some((Instruction::AND, AddressingMode::IndirectIndexedY)),
0x32 => Some((Instruction::JAM, AddressingMode::Implied)),
0x33 => Some((Instruction::RLA, AddressingMode::IndirectIndexedY)),
0x34 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x35 => Some((Instruction::AND, AddressingMode::ZeroPageX)),
0x36 => Some((Instruction::ROL, AddressingMode::ZeroPageX)),
0x37 => Some((Instruction::RLA, AddressingMode::ZeroPageX)),
0x38 => Some((Instruction::SEC, AddressingMode::Implied)),
0x39 => Some((Instruction::AND, AddressingMode::AbsoluteY)),
0x3a => Some((Instruction::NOP, AddressingMode::Implied)),
0x3b => Some((Instruction::RLA, AddressingMode::AbsoluteY)),
0x3c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x3d => Some((Instruction::AND, AddressingMode::AbsoluteX)),
0x3e => Some((Instruction::ROL, AddressingMode::AbsoluteX)),
0x3f => Some((Instruction::RLA, AddressingMode::AbsoluteX)),
0x40 => Some((Instruction::RTI, AddressingMode::Implied)),
0x41 => Some((Instruction::EOR, AddressingMode::IndexedIndirectX)),
0x42 => Some((Instruction::JAM, AddressingMode::Implied)),
0x43 => Some((Instruction::SRE, AddressingMode::IndexedIndirectX)),
0x44 => Some((Instruction::NOPZ, AddressingMode::ZeroPage)),
0x45 => Some((Instruction::EOR, AddressingMode::ZeroPage)),
0x46 => Some((Instruction::LSR, AddressingMode::ZeroPage)),
0x47 => Some((Instruction::SRE, AddressingMode::ZeroPage)),
0x48 => Some((Instruction::PHA, AddressingMode::Implied)),
0x49 => Some((Instruction::EOR, AddressingMode::Immediate)),
0x4a => Some((Instruction::LSR, AddressingMode::Accumulator)),
0x4b => Some((Instruction::ALR, AddressingMode::Immediate)),
0x4c => Some((Instruction::JMP, AddressingMode::Absolute)),
0x4d => Some((Instruction::EOR, AddressingMode::Absolute)),
0x4e => Some((Instruction::LSR, AddressingMode::Absolute)),
0x4f => Some((Instruction::SRE, AddressingMode::Absolute)),
0x50 => Some((Instruction::BVC, AddressingMode::Relative)),
0x51 => Some((Instruction::EOR, AddressingMode::IndirectIndexedY)),
0x52 => Some((Instruction::JAM, AddressingMode::Implied)),
0x53 => Some((Instruction::SRE, AddressingMode::IndirectIndexedY)),
0x54 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x55 => Some((Instruction::EOR, AddressingMode::ZeroPageX)),
0x56 => Some((Instruction::LSR, AddressingMode::ZeroPageX)),
0x57 => Some((Instruction::SRE, AddressingMode::ZeroPageX)),
0x58 => Some((Instruction::CLI, AddressingMode::Implied)),
0x59 => Some((Instruction::EOR, AddressingMode::AbsoluteY)),
0x5a => Some((Instruction::NOP, AddressingMode::Implied)),
0x5b => Some((Instruction::SRE, AddressingMode::AbsoluteY)),
0x5c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x5d => Some((Instruction::EOR, AddressingMode::AbsoluteX)),
0x5e => Some((Instruction::LSR, AddressingMode::AbsoluteX)),
0x5f => Some((Instruction::SRE, AddressingMode::AbsoluteX)),
0x60 => Some((Instruction::RTS, AddressingMode::Implied)),
0x61 => Some((Instruction::ADC, AddressingMode::IndexedIndirectX)),
0x62 => Some((Instruction::JAM, AddressingMode::Implied)),
0x63 => Some((Instruction::RRA, AddressingMode::IndexedIndirectX)),
0x64 => Some((Instruction::NOPZ, AddressingMode::ZeroPage)),
0x65 => Some((Instruction::ADC, AddressingMode::ZeroPage)),
0x66 => Some((Instruction::ROR, AddressingMode::ZeroPage)),
0x67 => Some((Instruction::RRA, AddressingMode::ZeroPage)),
0x68 => Some((Instruction::PLA, AddressingMode::Implied)),
0x69 => Some((Instruction::ADC, AddressingMode::Immediate)),
0x6a => Some((Instruction::ROR, AddressingMode::Accumulator)),
0x6b => Some((Instruction::ARR, AddressingMode::Immediate)),
0x6c => Some((Instruction::JMP, AddressingMode::BuggyIndirect)),
0x6d => Some((Instruction::ADC, AddressingMode::Absolute)),
0x6e => Some((Instruction::ROR, AddressingMode::Absolute)),
0x6f => Some((Instruction::RRA, AddressingMode::Absolute)),
0x70 => Some((Instruction::BVS, AddressingMode::Relative)),
0x71 => Some((Instruction::ADC, AddressingMode::IndirectIndexedY)),
0x72 => Some((Instruction::JAM, AddressingMode::Implied)),
0x73 => Some((Instruction::RRA, AddressingMode::IndirectIndexedY)),
0x74 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0x75 => Some((Instruction::ADC, AddressingMode::ZeroPageX)),
0x76 => Some((Instruction::ROR, AddressingMode::ZeroPageX)),
0x77 => Some((Instruction::RRA, AddressingMode::ZeroPageX)),
0x78 => Some((Instruction::SEI, AddressingMode::Implied)),
0x79 => Some((Instruction::ADC, AddressingMode::AbsoluteY)),
0x7a => Some((Instruction::NOP, AddressingMode::Implied)),
0x7b => Some((Instruction::RRA, AddressingMode::AbsoluteY)),
0x7c => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0x7d => Some((Instruction::ADC, AddressingMode::AbsoluteX)),
0x7e => Some((Instruction::ROR, AddressingMode::AbsoluteX)),
0x7f => Some((Instruction::RRA, AddressingMode::AbsoluteX)),
0x80 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x81 => Some((Instruction::STA, AddressingMode::IndexedIndirectX)),
0x82 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x83 => Some((Instruction::SAX, AddressingMode::IndexedIndirectX)),
0x84 => Some((Instruction::STY, AddressingMode::ZeroPage)),
0x85 => Some((Instruction::STA, AddressingMode::ZeroPage)),
0x86 => Some((Instruction::STX, AddressingMode::ZeroPage)),
0x87 => Some((Instruction::SAX, AddressingMode::ZeroPage)),
0x88 => Some((Instruction::DEY, AddressingMode::Implied)),
0x89 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x8a => Some((Instruction::TXA, AddressingMode::Implied)),
0x8b => Some((Instruction::XAA, AddressingMode::Immediate)),
0x8c => Some((Instruction::STY, AddressingMode::Absolute)),
0x8d => Some((Instruction::STA, AddressingMode::Absolute)),
0x8e => Some((Instruction::STX, AddressingMode::Absolute)),
0x8f => Some((Instruction::SAX, AddressingMode::Absolute)),
0x90 => Some((Instruction::BCC, AddressingMode::Relative)),
0x91 => Some((Instruction::STA, AddressingMode::IndirectIndexedY)),
0x92 => Some((Instruction::JAM, AddressingMode::Implied)),
0x93 => None, 0x94 => Some((Instruction::STY, AddressingMode::ZeroPageX)),
0x95 => Some((Instruction::STA, AddressingMode::ZeroPageX)),
0x96 => Some((Instruction::STX, AddressingMode::ZeroPageY)),
0x97 => Some((Instruction::SAX, AddressingMode::ZeroPageY)),
0x98 => Some((Instruction::TYA, AddressingMode::Implied)),
0x99 => Some((Instruction::STA, AddressingMode::AbsoluteY)),
0x9a => Some((Instruction::TXS, AddressingMode::Implied)),
0x9b => None, 0x9c => None, 0x9d => Some((Instruction::STA, AddressingMode::AbsoluteX)),
0x9e => None, 0x9f => None, 0xa0 => Some((Instruction::LDY, AddressingMode::Immediate)),
0xa1 => Some((Instruction::LDA, AddressingMode::IndexedIndirectX)),
0xa2 => Some((Instruction::LDX, AddressingMode::Immediate)),
0xa3 => Some((Instruction::LAX, AddressingMode::IndexedIndirectX)),
0xa4 => Some((Instruction::LDY, AddressingMode::ZeroPage)),
0xa5 => Some((Instruction::LDA, AddressingMode::ZeroPage)),
0xa6 => Some((Instruction::LDX, AddressingMode::ZeroPage)),
0xa7 => Some((Instruction::LAX, AddressingMode::ZeroPage)),
0xa8 => Some((Instruction::TAY, AddressingMode::Implied)),
0xa9 => Some((Instruction::LDA, AddressingMode::Immediate)),
0xaa => Some((Instruction::TAX, AddressingMode::Implied)),
0xab => None, 0xac => Some((Instruction::LDY, AddressingMode::Absolute)),
0xad => Some((Instruction::LDA, AddressingMode::Absolute)),
0xae => Some((Instruction::LDX, AddressingMode::Absolute)),
0xaf => Some((Instruction::LAX, AddressingMode::Absolute)),
0xb0 => Some((Instruction::BCS, AddressingMode::Relative)),
0xb1 => Some((Instruction::LDA, AddressingMode::IndirectIndexedY)),
0xb2 => Some((Instruction::JAM, AddressingMode::Implied)),
0xb3 => Some((Instruction::LAX, AddressingMode::IndirectIndexedY)),
0xb4 => Some((Instruction::LDY, AddressingMode::ZeroPageX)),
0xb5 => Some((Instruction::LDA, AddressingMode::ZeroPageX)),
0xb6 => Some((Instruction::LDX, AddressingMode::ZeroPageY)),
0xb7 => Some((Instruction::LAX, AddressingMode::ZeroPageY)),
0xb8 => Some((Instruction::CLV, AddressingMode::Implied)),
0xb9 => Some((Instruction::LDA, AddressingMode::AbsoluteY)),
0xba => Some((Instruction::TSX, AddressingMode::Implied)),
0xbb => Some((Instruction::LAS, AddressingMode::AbsoluteY)),
0xbc => Some((Instruction::LDY, AddressingMode::AbsoluteX)),
0xbd => Some((Instruction::LDA, AddressingMode::AbsoluteX)),
0xbe => Some((Instruction::LDX, AddressingMode::AbsoluteY)),
0xbf => Some((Instruction::LAX, AddressingMode::AbsoluteY)),
0xc0 => Some((Instruction::CPY, AddressingMode::Immediate)),
0xc1 => Some((Instruction::CMP, AddressingMode::IndexedIndirectX)),
0xc2 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0xc3 => Some((Instruction::DCP, AddressingMode::IndexedIndirectX)),
0xc4 => Some((Instruction::CPY, AddressingMode::ZeroPage)),
0xc5 => Some((Instruction::CMP, AddressingMode::ZeroPage)),
0xc6 => Some((Instruction::DEC, AddressingMode::ZeroPage)),
0xc7 => Some((Instruction::DCP, AddressingMode::ZeroPage)),
0xc8 => Some((Instruction::INY, AddressingMode::Implied)),
0xc9 => Some((Instruction::CMP, AddressingMode::Immediate)),
0xca => Some((Instruction::DEX, AddressingMode::Implied)),
0xcb => Some((Instruction::SBX, AddressingMode::Immediate)),
0xcc => Some((Instruction::CPY, AddressingMode::Absolute)),
0xcd => Some((Instruction::CMP, AddressingMode::Absolute)),
0xce => Some((Instruction::DEC, AddressingMode::Absolute)),
0xcf => Some((Instruction::DCP, AddressingMode::Absolute)),
0xd0 => Some((Instruction::BNE, AddressingMode::Relative)),
0xd1 => Some((Instruction::CMP, AddressingMode::IndirectIndexedY)),
0xd2 => Some((Instruction::JAM, AddressingMode::Implied)),
0xd3 => Some((Instruction::DCP, AddressingMode::IndirectIndexedY)),
0xd4 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0xd5 => Some((Instruction::CMP, AddressingMode::ZeroPageX)),
0xd6 => Some((Instruction::DEC, AddressingMode::ZeroPageX)),
0xd7 => Some((Instruction::DCP, AddressingMode::ZeroPageX)),
0xd8 => Some((Instruction::CLD, AddressingMode::Implied)),
0xd9 => Some((Instruction::CMP, AddressingMode::AbsoluteY)),
0xda => Some((Instruction::NOP, AddressingMode::Implied)),
0xdb => Some((Instruction::DCP, AddressingMode::AbsoluteY)),
0xdc => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0xdd => Some((Instruction::CMP, AddressingMode::AbsoluteX)),
0xde => Some((Instruction::DEC, AddressingMode::AbsoluteX)),
0xdf => Some((Instruction::DCP, AddressingMode::AbsoluteX)),
0xe0 => Some((Instruction::CPX, AddressingMode::Immediate)),
0xe1 => Some((Instruction::SBC, AddressingMode::IndexedIndirectX)),
0xe2 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0xe3 => Some((Instruction::ISC, AddressingMode::IndexedIndirectX)),
0xe4 => Some((Instruction::CPX, AddressingMode::ZeroPage)),
0xe5 => Some((Instruction::SBC, AddressingMode::ZeroPage)),
0xe6 => Some((Instruction::INC, AddressingMode::ZeroPage)),
0xe7 => Some((Instruction::ISC, AddressingMode::ZeroPage)),
0xe8 => Some((Instruction::INX, AddressingMode::Implied)),
0xe9 => Some((Instruction::SBC, AddressingMode::Immediate)),
0xea => Some((Instruction::NOP, AddressingMode::Implied)),
0xeb => Some((Instruction::USBC, AddressingMode::Immediate)),
0xec => Some((Instruction::CPX, AddressingMode::Absolute)),
0xed => Some((Instruction::SBC, AddressingMode::Absolute)),
0xee => Some((Instruction::INC, AddressingMode::Absolute)),
0xef => Some((Instruction::ISC, AddressingMode::Absolute)),
0xf0 => Some((Instruction::BEQ, AddressingMode::Relative)),
0xf1 => Some((Instruction::SBC, AddressingMode::IndirectIndexedY)),
0xf2 => Some((Instruction::JAM, AddressingMode::Implied)),
0xf3 => Some((Instruction::ISC, AddressingMode::IndirectIndexedY)),
0xf4 => Some((Instruction::NOPZX, AddressingMode::ZeroPageX)),
0xf5 => Some((Instruction::SBC, AddressingMode::ZeroPageX)),
0xf6 => Some((Instruction::INC, AddressingMode::ZeroPageX)),
0xf7 => Some((Instruction::ISC, AddressingMode::ZeroPageX)),
0xf8 => Some((Instruction::SED, AddressingMode::Implied)),
0xf9 => Some((Instruction::SBC, AddressingMode::AbsoluteY)),
0xfa => Some((Instruction::NOP, AddressingMode::Implied)),
0xfb => Some((Instruction::ISC, AddressingMode::AbsoluteY)),
0xfc => Some((Instruction::NOPAX, AddressingMode::AbsoluteX)),
0xfd => Some((Instruction::SBC, AddressingMode::AbsoluteX)),
0xfe => Some((Instruction::INC, AddressingMode::AbsoluteX)),
0xff => Some((Instruction::ISC, AddressingMode::AbsoluteX)),
}
}
fn adc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let result_16 = u16::from(accumulator) + u16::from(value) + u16::from(carry);
let did_carry = result_16 > 0xFF;
let result = result_16.to_le_bytes()[0];
let overflow = (!(accumulator ^ value) & (accumulator ^ result)) & 0x80 != 0;
let negative = (result & 0x80) != 0;
let zero = result == 0;
ArithmeticOutput {
result,
did_carry,
overflow,
negative,
zero,
}
}
fn adc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let temp_result = accumulator.wrapping_add(value).wrapping_add(carry);
let mut low_nibble = (accumulator & 0x0f)
.wrapping_add(value & 0x0f)
.wrapping_add(carry);
let mut high_nibble = (accumulator >> 4).wrapping_add(value >> 4);
let mut carry_to_high = false;
if low_nibble > 9 {
low_nibble = low_nibble.wrapping_sub(10);
carry_to_high = true;
}
high_nibble = high_nibble.wrapping_add(u8::from(carry_to_high));
let (adjusted_high, did_carry) = if high_nibble > 9 {
(high_nibble.wrapping_sub(10), true)
} else {
(high_nibble, false)
};
let result = (adjusted_high << 4) | (low_nibble & 0x0f);
let overflow = (!(accumulator ^ value) & (accumulator ^ temp_result)) & 0x80 != 0;
let negative = (result & 0x80) != 0;
let zero = result == 0;
ArithmeticOutput {
result,
did_carry,
overflow,
negative,
zero,
}
}
fn sbc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let temp_result = accumulator.wrapping_sub(value).wrapping_sub(1 - carry);
let did_borrow = u16::from(accumulator) < (u16::from(value) + u16::from(1 - carry));
let did_carry = !did_borrow;
let overflow = (accumulator ^ value) & (accumulator ^ temp_result) & 0x80 != 0;
let negative = (temp_result & 0x80) != 0;
let zero = temp_result == 0;
ArithmeticOutput {
result: temp_result,
did_carry,
overflow,
negative,
zero,
}
}
fn sbc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let temp_result = accumulator.wrapping_sub(value).wrapping_sub(1 - carry);
let mut low_nibble = (accumulator & 0x0f)
.wrapping_sub(value & 0x0f)
.wrapping_sub(1 - carry);
let mut high_nibble = (accumulator >> 4).wrapping_sub(value >> 4);
let mut borrow = false;
if (low_nibble & 0x10) != 0 {
low_nibble = (low_nibble.wrapping_add(10)) & 0x0f;
borrow = true;
}
high_nibble = high_nibble.wrapping_sub(u8::from(borrow));
if (high_nibble & 0x10) != 0 {
high_nibble = (high_nibble.wrapping_add(10)) & 0x0f;
borrow = true;
} else {
borrow = false;
}
let result = (high_nibble << 4) | low_nibble;
let did_carry = !borrow;
let overflow = (accumulator ^ value) & (accumulator ^ temp_result) & 0x80 != 0;
let negative = (result & 0x80) != 0;
let zero = result == 0;
ArithmeticOutput {
result,
did_carry,
overflow,
negative,
zero,
}
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Ricoh2a03;
impl crate::Variant for Ricoh2a03 {
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
match Nmos6502::decode(opcode) {
Some((Instruction::ADC, addressing_mode)) => {
Some((Instruction::ADCnd, addressing_mode))
}
Some((Instruction::SBC, addressing_mode)) => {
Some((Instruction::SBCnd, addressing_mode))
}
other_instruction => other_instruction,
}
}
#[inline]
fn adc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Nmos6502::adc_binary(accumulator, value, carry_set)
}
#[inline]
fn adc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Self::adc_binary(accumulator, value, carry_set)
}
#[inline]
fn sbc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Nmos6502::sbc_binary(accumulator, value, carry_set)
}
#[inline]
fn sbc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Self::sbc_binary(accumulator, value, carry_set)
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct RevisionA;
impl crate::Variant for RevisionA {
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
match Nmos6502::decode(opcode) {
Some((Instruction::ROR, _)) => None,
other_instruction => other_instruction,
}
}
#[inline]
fn adc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Nmos6502::adc_binary(accumulator, value, carry_set)
}
#[inline]
fn adc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Nmos6502::adc_decimal(accumulator, value, carry_set)
}
#[inline]
fn sbc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Nmos6502::sbc_binary(accumulator, value, carry_set)
}
#[inline]
fn sbc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
Nmos6502::sbc_decimal(accumulator, value, carry_set)
}
}
#[derive(Copy, Clone, Debug, Default)]
pub struct Mos65C02<const ROCKWELL: bool, const WDC: bool>;
pub type Cmos6502 = Mos65C02<false, true>;
#[allow(non_upper_case_globals)]
pub const Cmos6502: Cmos6502 = Cmos6502 {};
pub type W65C02S = Mos65C02<true, true>;
pub const W65C02S: W65C02S = W65C02S {};
const fn cmos_base_decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
match opcode {
0x00 => Some((Instruction::BRKcld, AddressingMode::Implied)),
0x02 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x22 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x42 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x62 => Some((Instruction::NOPI, AddressingMode::Immediate)),
0x03 | 0x13 | 0x23 | 0x33 | 0x43 | 0x53 | 0x63 | 0x73 | 0x83 | 0x93 | 0xa3 | 0xb3
| 0xc3 | 0xd3 | 0xe3 | 0xf3 | 0x0b | 0x1b | 0x2b | 0x3b | 0x4b | 0x5b | 0x6b | 0x7b
| 0x8b | 0x9b | 0xab | 0xbb | 0xeb | 0xfb => {
Some((Instruction::NOP1, AddressingMode::Implied))
}
0x5c => Some((Instruction::NOPAX8, AddressingMode::AbsoluteX)),
0x1a => Some((Instruction::INC, AddressingMode::Accumulator)),
0x3a => Some((Instruction::DEC, AddressingMode::Accumulator)),
0x6c => Some((Instruction::JMP, AddressingMode::Indirect)),
0x7c => Some((Instruction::JMP, AddressingMode::AbsoluteIndexedIndirect)),
0x80 => Some((Instruction::BRA, AddressingMode::Relative)),
0x64 => Some((Instruction::STZ, AddressingMode::ZeroPage)),
0x74 => Some((Instruction::STZ, AddressingMode::ZeroPageX)),
0x9c => Some((Instruction::STZ, AddressingMode::Absolute)),
0x9e => Some((Instruction::STZ, AddressingMode::AbsoluteX)),
0x7a => Some((Instruction::PLY, AddressingMode::Implied)),
0xfa => Some((Instruction::PLX, AddressingMode::Implied)),
0x5a => Some((Instruction::PHY, AddressingMode::Implied)),
0xda => Some((Instruction::PHX, AddressingMode::Implied)),
0x04 => Some((Instruction::TSB, AddressingMode::ZeroPage)),
0x14 => Some((Instruction::TRB, AddressingMode::ZeroPage)),
0x0c => Some((Instruction::TSB, AddressingMode::Absolute)),
0x1c => Some((Instruction::TRB, AddressingMode::Absolute)),
0x12 => Some((Instruction::ORA, AddressingMode::ZeroPageIndirect)),
0x32 => Some((Instruction::AND, AddressingMode::ZeroPageIndirect)),
0x34 => Some((Instruction::BIT, AddressingMode::ZeroPageX)),
0x3c => Some((Instruction::BIT, AddressingMode::AbsoluteX)),
0x52 => Some((Instruction::EOR, AddressingMode::ZeroPageIndirect)),
0x72 => Some((Instruction::ADC, AddressingMode::ZeroPageIndirect)),
0x92 => Some((Instruction::STA, AddressingMode::ZeroPageIndirect)),
0xb2 => Some((Instruction::LDA, AddressingMode::ZeroPageIndirect)),
0xd2 => Some((Instruction::CMP, AddressingMode::ZeroPageIndirect)),
0xf2 => Some((Instruction::SBC, AddressingMode::ZeroPageIndirect)),
0x89 => Some((Instruction::BIT, AddressingMode::Immediate)),
_ => None,
}
}
const fn wdc_decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
match opcode {
0xcb => Some((Instruction::WAI, AddressingMode::Implied)),
0xdb => Some((Instruction::STP, AddressingMode::Implied)),
_ => None,
}
}
const fn rockwell_decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
let bit = (opcode >> 4) & 0x07;
if opcode & 0x0f == 0x0f {
if opcode < 0x80 {
Some((Instruction::BBR(bit), AddressingMode::ZeroPageRelative))
} else {
Some((Instruction::BBS(bit), AddressingMode::ZeroPageRelative))
}
} else if opcode & 0x0f == 0x07 {
if opcode < 0x80 {
Some((Instruction::RMB(bit), AddressingMode::ZeroPage))
} else {
Some((Instruction::SMB(bit), AddressingMode::ZeroPage))
}
} else {
None
}
}
impl<const ROCKWELL: bool, const WDC: bool> crate::Variant for Mos65C02<ROCKWELL, WDC> {
fn decode(opcode: u8) -> Option<(Instruction, AddressingMode)> {
cmos_base_decode(opcode)
.or_else(|| if WDC { wdc_decode(opcode) } else { None })
.or_else(|| {
if ROCKWELL {
rockwell_decode(opcode)
} else {
None
}
})
.or_else(|| Nmos6502::decode(opcode))
}
fn adc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let result_16 = u16::from(accumulator) + u16::from(value) + u16::from(carry);
let did_carry = result_16 > 0xFF;
#[allow(clippy::cast_possible_truncation)]
let result = result_16 as u8;
let overflow = (!(accumulator ^ value) & (accumulator ^ result)) & 0x80 != 0;
let negative = (result & 0x80) != 0;
let zero = result == 0;
ArithmeticOutput {
result,
did_carry,
overflow,
negative,
zero,
}
}
fn adc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let temp_result = accumulator.wrapping_add(value).wrapping_add(carry);
let mut low_nibble = (accumulator & 0x0f)
.wrapping_add(value & 0x0f)
.wrapping_add(carry);
let mut high_nibble = (accumulator >> 4).wrapping_add(value >> 4);
let mut carry_to_high = false;
if low_nibble > 9 {
low_nibble = low_nibble.wrapping_sub(10);
carry_to_high = true;
}
high_nibble = high_nibble.wrapping_add(u8::from(carry_to_high));
let (adjusted_high, did_carry) = if high_nibble > 9 {
(high_nibble.wrapping_sub(10), true)
} else {
(high_nibble, false)
};
let result = (adjusted_high << 4) | (low_nibble & 0x0f);
let overflow = (!(accumulator ^ value) & (accumulator ^ temp_result)) & 0x80 != 0;
let negative = (result & 0x80) != 0;
let zero = result == 0;
ArithmeticOutput {
result,
did_carry,
overflow,
negative,
zero,
}
}
fn sbc_binary(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let temp_result = accumulator.wrapping_sub(value).wrapping_sub(1 - carry);
let did_borrow = u16::from(accumulator) < (u16::from(value) + u16::from(1 - carry));
let did_carry = !did_borrow;
let overflow = (accumulator ^ value) & (accumulator ^ temp_result) & 0x80 != 0;
let negative = (temp_result & 0x80) != 0;
let zero = temp_result == 0;
ArithmeticOutput {
result: temp_result,
did_carry,
overflow,
negative,
zero,
}
}
fn sbc_decimal(accumulator: u8, value: u8, carry_set: bool) -> ArithmeticOutput {
let carry = u8::from(carry_set);
let temp_result = accumulator.wrapping_sub(value).wrapping_sub(1 - carry);
let mut low_nibble = (accumulator & 0x0f)
.wrapping_sub(value & 0x0f)
.wrapping_sub(1 - carry);
let mut high_nibble = (accumulator >> 4).wrapping_sub(value >> 4);
let mut borrow = false;
if (low_nibble & 0x10) != 0 {
low_nibble = (low_nibble.wrapping_add(10)) & 0x0f;
borrow = true;
}
high_nibble = high_nibble.wrapping_sub(u8::from(borrow));
if (high_nibble & 0x10) != 0 {
high_nibble = (high_nibble.wrapping_add(10)) & 0x0f;
borrow = true;
} else {
borrow = false;
}
let result = (high_nibble << 4) | low_nibble;
let did_carry = !borrow;
let overflow = (accumulator ^ value) & (accumulator ^ temp_result) & 0x80 != 0;
let negative = (result & 0x80) != 0;
let zero = result == 0;
ArithmeticOutput {
result,
did_carry,
overflow,
negative,
zero,
}
}
fn penalty_cycles_for_decimal_mode() -> u8 {
1 }
fn penalty_cycles_for_indirect_jmp() -> u8 {
1 }
}