use crate::address_mode::AddressMode; use crate::address_mode::AddressMode::*; use crate::address_mode::AddressMode::*; use crate::instruction_table::INSTRUCTION_TABLE; use crate::op_info::OpInfo; use crate::operand::Operand; use crate::operation::Operation; use crate::operation::Operation::*; use log::trace; #[derive(Debug, PartialEq)] pub struct Instruction { pub op: Operation, pub mode: AddressMode, pub operand: Operand, } impl Instruction { pub fn opinfo(bytes: &[u8]) -> Option { trace!("DECODING : {bytes:?}"); let opcode = bytes.get(0).copied()?; Some(INSTRUCTION_TABLE[opcode as usize].clone())? } pub fn decode(bytes: &[u8]) -> Option { let info = Instruction::opinfo(bytes)?; let operand = match info.length { 2 => Operand::Byte(*bytes.get(1)?), 3 => Operand::Word(u16::from_le_bytes([*bytes.get(1)?, *bytes.get(2)?])), _ => Operand::None, }; let instruction = Instruction { op: info.operation, mode: info.mode, operand, }; trace!("RETURNING: {:?}", instruction); Some(instruction) } } #[cfg(test)] mod test { use super::*; use crate::address_mode::AddressMode::*; use crate::instruction::Instruction; use crate::operation::Operation::*; #[test] fn decode_instruction() { let params = vec![ // ADC ( vec![0x69, 0xab], Instruction { op: ADC, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x65, 0xab], Instruction { op: ADC, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x75, 0xab], Instruction { op: ADC, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x6d, 0xcd, 0xab], Instruction { op: ADC, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x7d, 0xcd, 0xab], Instruction { op: ADC, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0x79, 0xcd, 0xab], Instruction { op: ADC, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0x61, 0xab], Instruction { op: ADC, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0x71, 0xab], Instruction { op: ADC, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // AND ( vec![0x29, 0xab], Instruction { op: AND, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x25, 0xab], Instruction { op: AND, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x35, 0xab], Instruction { op: AND, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x2d, 0xcd, 0xab], Instruction { op: AND, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x3d, 0xcd, 0xab], Instruction { op: AND, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0x39, 0xcd, 0xab], Instruction { op: AND, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0x21, 0xab], Instruction { op: AND, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0x31, 0xab], Instruction { op: AND, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // ASL ( vec![0x0a], Instruction { op: ASL, mode: Accumulator, operand: Operand::None, }, ), ( vec![0x06, 0xab], Instruction { op: ASL, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x16, 0xab], Instruction { op: ASL, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x0e, 0xcd, 0xab], Instruction { op: ASL, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x1e, 0xcd, 0xab], Instruction { op: ASL, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), // BIT ( vec![0x24, 0xab], Instruction { op: BIT, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x2c, 0xcd, 0xab], Instruction { op: BIT, mode: Absolute, operand: Operand::Word(0xabcd), }, ), // BCC, BCS, BEQ, BMI, BNE, BPL, BVC, BVS ( vec![0x10, 0xab], Instruction { op: BPL, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x30, 0xab], Instruction { op: BMI, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x50, 0xab], Instruction { op: BVC, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x70, 0xab], Instruction { op: BVS, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x90, 0xab], Instruction { op: BCC, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xb0, 0xab], Instruction { op: BCS, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xd0, 0xab], Instruction { op: BNE, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xf0, 0xab], Instruction { op: BEQ, mode: Immediate, operand: Operand::Byte(0xab), }, ), // BRK ( vec![0x00], Instruction { op: BRK, mode: Implied, operand: Operand::None, }, ), // CLC, CLD, CLI, CLV ( vec![0x18], Instruction { op: CLC, mode: Implied, operand: Operand::None, }, ), ( vec![0xd8], Instruction { op: CLD, mode: Implied, operand: Operand::None, }, ), ( vec![0x58], Instruction { op: CLI, mode: Implied, operand: Operand::None, }, ), ( vec![0xb8], Instruction { op: CLV, mode: Implied, operand: Operand::None, }, ), // CMP ( vec![0xc9, 0xab], Instruction { op: CMP, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xc5, 0xab], Instruction { op: CMP, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xd5, 0xab], Instruction { op: CMP, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0xcd, 0xcd, 0xab], Instruction { op: CMP, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0xdd, 0xcd, 0xab], Instruction { op: CMP, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0xd9, 0xcd, 0xab], Instruction { op: CMP, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0xc1, 0xab], Instruction { op: CMP, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0xd1, 0xab], Instruction { op: CMP, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // CPX ( vec![0xe0, 0xab], Instruction { op: CPX, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xe4, 0xab], Instruction { op: CPX, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xec, 0xcd, 0xab], Instruction { op: CPX, mode: Absolute, operand: Operand::Word(0xabcd), }, ), // CPY ( vec![0xc0, 0xab], Instruction { op: CPY, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xc4, 0xab], Instruction { op: CPY, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xcc, 0xcd, 0xab], Instruction { op: CPY, mode: Absolute, operand: Operand::Word(0xabcd), }, ), // DEC, DEX, DEY ( vec![0xc6, 0xab], Instruction { op: DEC, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xd6, 0xab], Instruction { op: DEC, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0xce, 0xcd, 0xab], Instruction { op: DEC, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0xde, 0xcd, 0xab], Instruction { op: DEC, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0xca], Instruction { op: DEX, mode: Implied, operand: Operand::None, }, ), ( vec![0x88], Instruction { op: DEY, mode: Implied, operand: Operand::None, }, ), // EOR ( vec![0x49, 0xab], Instruction { op: EOR, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x45, 0xab], Instruction { op: EOR, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x55, 0xab], Instruction { op: EOR, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x4d, 0xcd, 0xab], Instruction { op: EOR, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x5d, 0xcd, 0xab], Instruction { op: EOR, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0x59, 0xcd, 0xab], Instruction { op: EOR, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0x41, 0xab], Instruction { op: EOR, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0x51, 0xab], Instruction { op: EOR, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // INC, INX, INY ( vec![0xe6, 0xab], Instruction { op: INC, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xf6, 0xab], Instruction { op: INC, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0xee, 0xcd, 0xab], Instruction { op: INC, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0xfe, 0xcd, 0xab], Instruction { op: INC, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0xe8], Instruction { op: INX, mode: Implied, operand: Operand::None, }, ), ( vec![0xc8], Instruction { op: INY, mode: Implied, operand: Operand::None, }, ), // JMP, JSR ( vec![0x4c, 0xcd, 0xab], Instruction { op: JMP, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x6c, 0xcd, 0xab], Instruction { op: JMP, mode: Indirect, operand: Operand::Word(0xabcd), }, ), ( vec![0x20], Instruction { op: JSR, mode: Implied, operand: Operand::None, }, ), // LDA ( vec![0xa9, 0xab], Instruction { op: LDA, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xa5, 0xab], Instruction { op: LDA, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xb5, 0xab], Instruction { op: LDA, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0xad, 0xcd, 0xab], Instruction { op: LDA, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0xbd, 0xcd, 0xab], Instruction { op: LDA, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0xb9, 0xcd, 0xab], Instruction { op: LDA, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0xa1, 0xab], Instruction { op: LDA, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0xb1, 0xab], Instruction { op: LDA, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // LDX ( vec![0xa2, 0xab], Instruction { op: LDX, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xa6, 0xab], Instruction { op: LDX, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xb6, 0xab], Instruction { op: LDX, mode: ZeroPageY, operand: Operand::Byte(0xab), }, ), ( vec![0xae, 0xcd, 0xab], Instruction { op: LDX, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0xbe, 0xcd, 0xab], Instruction { op: LDX, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), // LDY ( vec![0xa0, 0xab], Instruction { op: LDY, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xa4, 0xab], Instruction { op: LDY, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xb4, 0xab], Instruction { op: LDY, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0xac, 0xcd, 0xab], Instruction { op: LDY, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0xbc, 0xcd, 0xab], Instruction { op: LDY, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), // LSR ( vec![0x4a], Instruction { op: LSR, mode: Accumulator, operand: Operand::None, }, ), ( vec![0x46, 0xab], Instruction { op: LSR, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x56, 0xab], Instruction { op: LSR, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x4e, 0xcd, 0xab], Instruction { op: LSR, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x5e, 0xcd, 0xab], Instruction { op: LSR, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), // NOP ( vec![0xea], Instruction { op: NOP, mode: Implied, operand: Operand::None, }, ), // ORA ( vec![0x09, 0xab], Instruction { op: ORA, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0x05, 0xab], Instruction { op: ORA, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x15, 0xab], Instruction { op: ORA, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x0d, 0xcd, 0xab], Instruction { op: ORA, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x1d, 0xcd, 0xab], Instruction { op: ORA, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0x19, 0xcd, 0xab], Instruction { op: ORA, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0x01, 0xab], Instruction { op: ORA, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0x11, 0xab], Instruction { op: ORA, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // PHA, PHP, PLA, PLP ( vec![0x48], Instruction { op: PHA, mode: Implied, operand: Operand::None, }, ), ( vec![0x08], Instruction { op: PHP, mode: Implied, operand: Operand::None, }, ), ( vec![0x68], Instruction { op: PLA, mode: Implied, operand: Operand::None, }, ), ( vec![0x28], Instruction { op: PLP, mode: Implied, operand: Operand::None, }, ), // ROL ( vec![0x2a], Instruction { op: ROL, mode: Accumulator, operand: Operand::None, }, ), ( vec![0x26, 0xab], Instruction { op: ROL, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x36, 0xab], Instruction { op: ROL, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x2e, 0xcd, 0xab], Instruction { op: ROL, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x3e, 0xcd, 0xab], Instruction { op: ROL, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), // ROR ( vec![0x6a], Instruction { op: ROR, mode: Accumulator, operand: Operand::None, }, ), ( vec![0x66, 0xab], Instruction { op: ROR, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x76, 0xab], Instruction { op: ROR, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x6e, 0xcd, 0xab], Instruction { op: ROR, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x7e, 0xcd, 0xab], Instruction { op: ROR, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), // RTI, RTS ( vec![0x40], Instruction { op: RTI, mode: Implied, operand: Operand::None, }, ), ( vec![0x60], Instruction { op: RTS, mode: Implied, operand: Operand::None, }, ), // SBC ( vec![0xe9, 0xab], Instruction { op: SBC, mode: Immediate, operand: Operand::Byte(0xab), }, ), ( vec![0xe5, 0xab], Instruction { op: SBC, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0xf5, 0xab], Instruction { op: SBC, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0xed, 0xcd, 0xab], Instruction { op: SBC, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0xfd, 0xcd, 0xab], Instruction { op: SBC, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0xf9, 0xcd, 0xab], Instruction { op: SBC, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0xe1, 0xab], Instruction { op: SBC, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0xf1, 0xab], Instruction { op: SBC, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // SEC, SED, SEI ( vec![0x38], Instruction { op: SEC, mode: Implied, operand: Operand::None, }, ), ( vec![0xf8], Instruction { op: SED, mode: Implied, operand: Operand::None, }, ), ( vec![0x78], Instruction { op: SEI, mode: Implied, operand: Operand::None, }, ), // STA ( vec![0x85, 0xab], Instruction { op: STA, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x95, 0xab], Instruction { op: STA, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x8d, 0xcd, 0xab], Instruction { op: STA, mode: Absolute, operand: Operand::Word(0xabcd), }, ), ( vec![0x9d, 0xcd, 0xab], Instruction { op: STA, mode: AbsoluteX, operand: Operand::Word(0xabcd), }, ), ( vec![0x99, 0xcd, 0xab], Instruction { op: STA, mode: AbsoluteY, operand: Operand::Word(0xabcd), }, ), ( vec![0x81, 0xab], Instruction { op: STA, mode: IndirectX, operand: Operand::Byte(0xab), }, ), ( vec![0x91, 0xab], Instruction { op: STA, mode: IndirectY, operand: Operand::Byte(0xab), }, ), // STX ( vec![0x86, 0xab], Instruction { op: STX, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x96, 0xab], Instruction { op: STX, mode: ZeroPageY, operand: Operand::Byte(0xab), }, ), ( vec![0x8e, 0xcd, 0xab], Instruction { op: STX, mode: Absolute, operand: Operand::Word(0xabcd), }, ), // STY ( vec![0x84, 0xab], Instruction { op: STY, mode: ZeroPage, operand: Operand::Byte(0xab), }, ), ( vec![0x94, 0xab], Instruction { op: STY, mode: ZeroPageX, operand: Operand::Byte(0xab), }, ), ( vec![0x8c, 0xcd, 0xab], Instruction { op: STY, mode: Absolute, operand: Operand::Word(0xabcd), }, ), // TAX, TAY, TSX, TXA, TXS, TYA ( vec![0xaa], Instruction { op: TAX, mode: Implied, operand: Operand::None, }, ), ( vec![0xa8], Instruction { op: TAY, mode: Implied, operand: Operand::None, }, ), ( vec![0xba], Instruction { op: TSX, mode: Implied, operand: Operand::None, }, ), ( vec![0x8a], Instruction { op: TXA, mode: Implied, operand: Operand::None, }, ), ( vec![0x9a], Instruction { op: TXS, mode: Implied, operand: Operand::None, }, ), ( vec![0x98], Instruction { op: TYA, mode: Implied, operand: Operand::None, }, ), ]; for (bytes, instruction) in params { let result1 = Instruction::decode(&bytes); if let Some(instruction1) = result1 { assert_eq!(instruction, instruction1) } else { println!("Failed to decode {:?}", bytes); } } } }