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<OpInfo> {
        trace!("DECODING : {bytes:?}");
        let opcode = bytes.get(0).copied()?;
        Some(INSTRUCTION_TABLE[opcode as usize].clone())?
    }

    pub fn decode(bytes: &[u8]) -> Option<Instruction> {
        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);
            }
        }
    }
}
