
#[derive(Clone, Copy, Debug)]
pub enum TargetRegister { A, B, C, D, E, H, L, }
#[derive(Clone, Copy, Debug)]
pub enum TargetU16Register {AF, BC, DE, HL, SP, PC}

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Condition {
    NZ, // Not Zero
    Z,  // Zero
    NC, // Not Carry
    C,  // Carry
    None,
}
#[derive(Clone, Copy, Debug)]
pub enum Target {
    U8Register(TargetRegister),
    U16Register(TargetU16Register),
    Immediate(u8)
}

#[derive(Clone, Copy, Debug)]
pub enum LoadTarget{
    CopyR8R8(TargetRegister, TargetRegister),
    CopyR8N8(TargetRegister, u8),
    CopyR16N16(TargetU16Register, u16),
    CopyHLR8(TargetRegister),
    CopyHLN8(u8),
    CopyR8HL(TargetRegister),
    CopyR16A(TargetU16Register),
    CopyN16A(u16),
    CopyAR16(TargetU16Register),
    CopyAN16(u16),
    CopyHLIA,
    CopyHLDA,
    CopyAHLD,
    CopyAHLI,
    CopySPN16(u16),
    CopyN16SP(u16),
    CopyHLSPE8(i8),
    CopySPHL,
    CopyPortA(Target),
    CopyAPort(Target)
}
#[derive(Debug)]
pub enum Instruction {
    ADC(Target),
    ADD(Target),
    ADDHL(TargetU16Register),
    ADDSP(i8),
    AND(Target),
    BIT(u8, Target),
    CALL(Condition, u16),
    CCF,
    CP(Target),
    CPL,
    DAA,
    DEC(Target),
    DECU16(TargetU16Register),
    DI,
    EI,
    HALT,
    INC(Target),
    INCU16(TargetU16Register),
    JP(Condition, u16),
    JPHL,
    JR(Condition, i8),
    LD(LoadTarget),
    NOP,
    OR(Target),
    POP(TargetU16Register),
    PUSH(TargetU16Register),
    RES(u8, Target),
    RET(Condition),
    RETI,
    RL(Target),
    RLC(Target),
    RR(Target),
    RRC(Target),
    RST(u8),
    SBC(Target),
    SCF,
    SET(u8, Target),
    SLA(Target),
    SRA(Target),
    SRL(Target),
    STOP(u8),
    SUB(Target),
    SWAP(Target),
    XOR(Target),
}


fn get_u16(low: u8, high: u8) -> u16 {
    ((high as u16) << 8) | low as u16
}

pub fn parse_instruction(opcode: u8, arg1: u8, arg2: u8) -> Instruction {
    match opcode {
        0x00 => { Instruction::NOP }
        0x01 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::BC, get_u16(arg1, arg2))) }
        0x02 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::BC)) }
        0x03 => { Instruction::INCU16(TargetU16Register::BC) }
        0x04 => { Instruction::INC(Target::U8Register(TargetRegister::B)) }
        0x05 => { Instruction::DEC(Target::U8Register(TargetRegister::B)) }
        0x06 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::B, arg1)) }
        0x07 => { Instruction::RLC(Target::U8Register(TargetRegister::A)) }
        0x08 => { Instruction::LD(LoadTarget::CopyN16SP(get_u16(arg1, arg2))) }
        0x09 => { Instruction::ADDHL(TargetU16Register::BC) }
        0x0A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::BC)) }
        0x0B => { Instruction::DECU16(TargetU16Register::BC) }
        0x0C => { Instruction::INC(Target::U8Register(TargetRegister::C)) }
        0x0D => { Instruction::DEC(Target::U8Register(TargetRegister::C)) }
        0x0E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::C, arg1)) }
        0x0F => { Instruction::RRC(Target::U8Register(TargetRegister::A)) }
        0x10 => { Instruction::STOP(arg1) }
        0x11 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::DE, get_u16(arg1, arg2))) }
        0x12 => { Instruction::LD(LoadTarget::CopyR16A(TargetU16Register::DE)) }
        0x13 => { Instruction::INCU16(TargetU16Register::DE) }
        0x14 => { Instruction::INC(Target::U8Register(TargetRegister::D)) }
        0x15 => { Instruction::DEC(Target::U8Register(TargetRegister::D)) }
        0x16 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::D, arg1)) }
        0x17 => { Instruction::RL(Target::U8Register(TargetRegister::A)) }
        0x18 => { Instruction::JR(Condition::None, arg1 as i8) }
        0x19 => { Instruction::ADDHL(TargetU16Register::DE) }
        0x1A => { Instruction::LD(LoadTarget::CopyAR16(TargetU16Register::DE)) }
        0x1B => { Instruction::DECU16(TargetU16Register::DE) }
        0x1C => { Instruction::INC(Target::U8Register(TargetRegister::E)) }
        0x1D => { Instruction::DEC(Target::U8Register(TargetRegister::E)) }
        0x1E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::E, arg1)) }
        0x1F => { Instruction::RR(Target::U8Register(TargetRegister::A)) }
        0x20 => { Instruction::JR(Condition::NZ, arg1 as i8) }
        0x21 => { Instruction::LD(LoadTarget::CopyR16N16(TargetU16Register::HL, get_u16(arg1, arg2))) }
        0x22 => { Instruction::LD(LoadTarget::CopyHLIA) }
        0x23 => { Instruction::INCU16(TargetU16Register::HL) }
        0x24 => { Instruction::INC(Target::U8Register(TargetRegister::H)) }
        0x25 => { Instruction::DEC(Target::U8Register(TargetRegister::H)) }
        0x26 => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::H, arg1)) }
        0x27 => { Instruction::DAA }
        0x28 => { Instruction::JR(Condition::Z, arg1 as i8) }
        0x29 => { Instruction::ADDHL(TargetU16Register::HL) }
        0x2A => { Instruction::LD(LoadTarget::CopyAHLI) }
        0x2B => { Instruction::DECU16(TargetU16Register::HL) }
        0x2C => { Instruction::INC(Target::U8Register(TargetRegister::L)) }
        0x2D => { Instruction::DEC(Target::U8Register(TargetRegister::L)) }
        0x2E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::L, arg1)) }
        0x2F => { Instruction::CPL }
        0x30 => { Instruction::JR(Condition::NC, arg1 as i8) }
        0x31 => { Instruction::LD(LoadTarget::CopySPN16(get_u16(arg1, arg2))) }
        0x32 => { Instruction::LD(LoadTarget::CopyHLDA) }
        0x33 => { Instruction::INCU16(TargetU16Register::SP) }
        0x34 => { Instruction::INC(Target::U16Register(TargetU16Register::HL)) }
        0x35 => { Instruction::DEC(Target::U16Register(TargetU16Register::HL)) }
        0x36 => { Instruction::LD(LoadTarget::CopyHLN8(arg1)) }
        0x37 => { Instruction::SCF }
        0x38 => { Instruction::JR(Condition::C, arg1 as i8) }
        0x39 => { Instruction::ADDHL(TargetU16Register::SP) }
        0x3A => { Instruction::LD(LoadTarget::CopyAHLD)}
        0x3B => { Instruction::DECU16(TargetU16Register::SP) }
        0x3C => { Instruction::INC(Target::U8Register(TargetRegister::A)) }
        0x3D => { Instruction::DEC(Target::U8Register(TargetRegister::A)) }
        0x3E => { Instruction::LD(LoadTarget::CopyR8N8(TargetRegister::A, arg1)) }
        0x3F => { Instruction::CCF }
        0x40 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::B)) }
        0x41 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::C)) }
        0x42 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::D)) }
        0x43 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::E)) }
        0x44 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::H)) }
        0x45 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::L)) }
        0x46 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::B)) }
        0x47 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::B, TargetRegister::A)) }
        0x48 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::B)) }
        0x49 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::C)) }
        0x4A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::D)) }
        0x4B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::E)) }
        0x4C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::H)) }
        0x4D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::L)) }
        0x4E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::C)) }
        0x4F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::C, TargetRegister::A)) }
        0x50 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::B)) }
        0x51 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::C)) }
        0x52 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::D)) }
        0x53 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::E)) }
        0x54 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::H)) }
        0x55 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::L)) }
        0x56 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::D)) }
        0x57 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::D, TargetRegister::A)) }
        0x58 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::B)) }
        0x59 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::C)) }
        0x5A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::D)) }
        0x5B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::E)) }
        0x5C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::H)) }
        0x5D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::L)) }
        0x5E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::E)) }
        0x5F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::E, TargetRegister::A)) }
        0x60 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::B)) }
        0x61 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::C)) }
        0x62 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::D)) }
        0x63 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::E)) }
        0x64 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::H)) }
        0x65 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::L)) }
        0x66 => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::H)) }
        0x67 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::H, TargetRegister::A)) }
        0x68 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::B)) }
        0x69 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::C)) }
        0x6A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::D)) }
        0x6B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::E)) }
        0x6C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::H)) }
        0x6D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::L)) }
        0x6E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::L)) }
        0x6F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::L, TargetRegister::A)) }
        0x70 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::B)) }
        0x71 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::C)) }
        0x72 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::D)) }
        0x73 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::E)) }
        0x74 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::H)) }
        0x75 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::L)) }
        0x76 => { Instruction::HALT }
        0x77 => { Instruction::LD(LoadTarget::CopyHLR8(TargetRegister::A)) }
        0x78 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::B)) }
        0x79 => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::C)) }
        0x7A => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::D)) }
        0x7B => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::E)) }
        0x7C => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::H)) }
        0x7D => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::L)) }
        0x7E => { Instruction::LD(LoadTarget::CopyR8HL(TargetRegister::A)) }
        0x7F => { Instruction::LD(LoadTarget::CopyR8R8(TargetRegister::A, TargetRegister::A)) }
        0x80 => { Instruction::ADD(Target::U8Register(TargetRegister::B)) }
        0x81 => { Instruction::ADD(Target::U8Register(TargetRegister::C)) }
        0x82 => { Instruction::ADD(Target::U8Register(TargetRegister::D)) }
        0x83 => { Instruction::ADD(Target::U8Register(TargetRegister::E)) }
        0x84 => { Instruction::ADD(Target::U8Register(TargetRegister::H)) }
        0x85 => { Instruction::ADD(Target::U8Register(TargetRegister::L)) }
        0x86 => { Instruction::ADD(Target::U16Register(TargetU16Register::HL)) }
        0x87 => { Instruction::ADD(Target::U8Register(TargetRegister::A)) }
        0x88 => { Instruction::ADC(Target::U8Register(TargetRegister::B)) }
        0x89 => { Instruction::ADC(Target::U8Register(TargetRegister::C)) }
        0x8A => { Instruction::ADC(Target::U8Register(TargetRegister::D)) }
        0x8B => { Instruction::ADC(Target::U8Register(TargetRegister::E)) }
        0x8C => { Instruction::ADC(Target::U8Register(TargetRegister::H)) }
        0x8D => { Instruction::ADC(Target::U8Register(TargetRegister::L)) }
        0x8E => { Instruction::ADC(Target::U16Register(TargetU16Register::HL)) }
        0x8F => { Instruction::ADC(Target::U8Register(TargetRegister::A)) }
        0x90 => { Instruction::SUB(Target::U8Register(TargetRegister::B)) }
        0x91 => { Instruction::SUB(Target::U8Register(TargetRegister::C)) }
        0x92 => { Instruction::SUB(Target::U8Register(TargetRegister::D)) }
        0x93 => { Instruction::SUB(Target::U8Register(TargetRegister::E)) }
        0x94 => { Instruction::SUB(Target::U8Register(TargetRegister::H)) }
        0x95 => { Instruction::SUB(Target::U8Register(TargetRegister::L)) }
        0x96 => { Instruction::SUB(Target::U16Register(TargetU16Register::HL)) }
        0x97 => { Instruction::SUB(Target::U8Register(TargetRegister::A)) }
        0x98 => { Instruction::SBC(Target::U8Register(TargetRegister::B)) }
        0x99 => { Instruction::SBC(Target::U8Register(TargetRegister::C)) }
        0x9A => { Instruction::SBC(Target::U8Register(TargetRegister::D)) }
        0x9B => { Instruction::SBC(Target::U8Register(TargetRegister::E)) }
        0x9C => { Instruction::SBC(Target::U8Register(TargetRegister::H)) }
        0x9D => { Instruction::SBC(Target::U8Register(TargetRegister::L)) }
        0x9E => { Instruction::SBC(Target::U16Register(TargetU16Register::HL)) }
        0x9F => { Instruction::SBC(Target::U8Register(TargetRegister::A)) }
        0xA0 => { Instruction::AND(Target::U8Register(TargetRegister::B)) }
        0xA1 => { Instruction::AND(Target::U8Register(TargetRegister::C)) }
        0xA2 => { Instruction::AND(Target::U8Register(TargetRegister::D)) }
        0xA3 => { Instruction::AND(Target::U8Register(TargetRegister::E)) }
        0xA4 => { Instruction::AND(Target::U8Register(TargetRegister::H)) }
        0xA5 => { Instruction::AND(Target::U8Register(TargetRegister::L)) }
        0xA6 => { Instruction::AND(Target::U16Register(TargetU16Register::HL)) }
        0xA7 => { Instruction::AND(Target::U8Register(TargetRegister::A)) }
        0xA8 => { Instruction::XOR(Target::U8Register(TargetRegister::B)) }
        0xA9 => { Instruction::XOR(Target::U8Register(TargetRegister::C)) }
        0xAA => { Instruction::XOR(Target::U8Register(TargetRegister::D)) }
        0xAB => { Instruction::XOR(Target::U8Register(TargetRegister::E)) }
        0xAC => { Instruction::XOR(Target::U8Register(TargetRegister::H)) }
        0xAD => { Instruction::XOR(Target::U8Register(TargetRegister::L)) }
        0xAE => { Instruction::XOR(Target::U16Register(TargetU16Register::HL)) }
        0xAF => { Instruction::XOR(Target::U8Register(TargetRegister::A)) }
        0xB0 => { Instruction::OR(Target::U8Register(TargetRegister::B)) }
        0xB1 => { Instruction::OR(Target::U8Register(TargetRegister::C)) }
        0xB2 => { Instruction::OR(Target::U8Register(TargetRegister::D)) }
        0xB3 => { Instruction::OR(Target::U8Register(TargetRegister::E)) }
        0xB4 => { Instruction::OR(Target::U8Register(TargetRegister::H)) }
        0xB5 => { Instruction::OR(Target::U8Register(TargetRegister::L)) }
        0xB6 => { Instruction::OR(Target::U16Register(TargetU16Register::HL)) }
        0xB7 => { Instruction::OR(Target::U8Register(TargetRegister::A)) }
        0xB8 => { Instruction::CP(Target::U8Register(TargetRegister::B)) }
        0xB9 => { Instruction::CP(Target::U8Register(TargetRegister::C)) }
        0xBA => { Instruction::CP(Target::U8Register(TargetRegister::D)) }
        0xBB => { Instruction::CP(Target::U8Register(TargetRegister::E)) }
        0xBC => { Instruction::CP(Target::U8Register(TargetRegister::H)) }
        0xBD => { Instruction::CP(Target::U8Register(TargetRegister::L)) }
        0xBE => { Instruction::CP(Target::U16Register(TargetU16Register::HL)) }
        0xBF => { Instruction::CP(Target::U8Register(TargetRegister::A)) }
        0xC0 => { Instruction::RET(Condition::NZ) }
        0xC1 => { Instruction::POP(TargetU16Register::BC) }
        0xC2 => { Instruction::JP(Condition::NZ, get_u16(arg1, arg2)) }
        0xC3 => { Instruction::JP(Condition::None, get_u16(arg1, arg2)) }
        0xC4 => { Instruction::CALL(Condition::NZ, get_u16(arg1, arg2)) }
        0xC5 => { Instruction::PUSH(TargetU16Register::BC) }
        0xC6 => { Instruction::ADD(Target::Immediate(arg1)) }
        0xC7 => { Instruction::RST(0x00) }
        0xC8 => { Instruction::RET(Condition::Z) }
        0xC9 => { Instruction::RET(Condition::None) }
        0xCA => { Instruction::JP(Condition::Z, get_u16(arg1, arg2)) }
        0xCB => {match_cb_instruction(arg1)},
        0xCC => { Instruction::CALL(Condition::Z, get_u16(arg1, arg2)) }
        0xCD => { Instruction::CALL(Condition::None, get_u16(arg1, arg2)) }
        0xCE => { Instruction::ADC(Target::Immediate(arg1)) }
        0xCF => { Instruction::RST(0x01) }
        0xD0 => { Instruction::RET(Condition::NC) }
        0xD1 => { Instruction::POP(TargetU16Register::DE) }
        0xD2 => { Instruction::JP(Condition::NC, get_u16(arg1, arg2)) }
        0xD4 => { Instruction::CALL(Condition::NC, get_u16(arg1, arg2)) }
        0xD5 => { Instruction::PUSH(TargetU16Register::DE) }
        0xD6 => { Instruction::SUB(Target::Immediate(arg1)) }
        0xD7 => { Instruction::RST(0x02) }
        0xD8 => { Instruction::RET(Condition::C) }
        0xD9 => { Instruction::RETI }
        0xDA => { Instruction::JP(Condition::C, get_u16(arg1, arg2)) }
        0xDC => { Instruction::CALL(Condition::C, get_u16(arg1, arg2)) }
        0xDE => { Instruction::SBC(Target::Immediate(arg1)) }
        0xDF => { Instruction::RST(0x03) }
        0xE0 => { Instruction::LD(LoadTarget::CopyPortA(Target::Immediate(arg1))) }
        0xE1 => { Instruction::POP(TargetU16Register::HL) }
        0xE2 => { Instruction::LD(LoadTarget::CopyPortA(Target::U8Register(TargetRegister::C))) }
        0xE5 => { Instruction::PUSH(TargetU16Register::HL) }
        0xE6 => { Instruction::AND(Target::Immediate(arg1)) }
        0xE7 => { Instruction::RST(0x04) }
        0xE8 => { Instruction::ADDSP(arg1 as i8) }
        0xE9 => { Instruction::JPHL }
        0xEA => { Instruction::LD(LoadTarget::CopyN16A(get_u16(arg1, arg2))) }
        0xEE => { Instruction::XOR(Target::Immediate(arg1)) }
        0xEF => { Instruction::RST(0x05) }
        0xF0 => { Instruction::LD(LoadTarget::CopyAPort(Target::Immediate(arg1))) }
        0xF1 => { Instruction::POP(TargetU16Register::AF) }
        0xF2 => { Instruction::LD(LoadTarget::CopyAPort(Target::U8Register(TargetRegister::C))) }
        0xF3 => { Instruction::DI }
        0xF5 => { Instruction::PUSH(TargetU16Register::AF) }
        0xF6 => { Instruction::OR(Target::Immediate(arg1)) }
        0xF7 => { Instruction::RST(0x06) }
        0xF8 => { Instruction::LD(LoadTarget::CopyHLSPE8(arg1 as i8)) }
        0xF9 => { Instruction::LD(LoadTarget::CopySPHL) }
        0xFA => { Instruction::LD(LoadTarget::CopyAN16(get_u16(arg1, arg2))) }
        0xFB => { Instruction::EI }
        0xFE => { Instruction::CP(Target::Immediate(arg1)) }
        0xFF => { Instruction::RST(0x07) }
        _ => { panic!("Invalid u8 opcode: {:02X}", opcode); }
    }
}

fn match_cb_instruction(opcode: u8) -> Instruction {
    match opcode {
        0x00 => { Instruction::RLC(Target::U8Register(TargetRegister::B)) }
        0x01 => { Instruction::RLC(Target::U8Register(TargetRegister::C)) }
        0x02 => { Instruction::RLC(Target::U8Register(TargetRegister::D)) }
        0x03 => { Instruction::RLC(Target::U8Register(TargetRegister::E)) }
        0x04 => { Instruction::RLC(Target::U8Register(TargetRegister::H)) }
        0x05 => { Instruction::RLC(Target::U8Register(TargetRegister::L)) }
        0x06 => { Instruction::RLC(Target::U16Register(TargetU16Register::HL)) }
        0x07 => { Instruction::RLC(Target::U8Register(TargetRegister::A)) }
        0x08 => { Instruction::RRC(Target::U8Register(TargetRegister::B)) }
        0x09 => { Instruction::RRC(Target::U8Register(TargetRegister::C)) }
        0x0A => { Instruction::RRC(Target::U8Register(TargetRegister::D)) }
        0x0B => { Instruction::RRC(Target::U8Register(TargetRegister::E)) }
        0x0C => { Instruction::RRC(Target::U8Register(TargetRegister::H)) }
        0x0D => { Instruction::RRC(Target::U8Register(TargetRegister::L)) }
        0x0E => { Instruction::RRC(Target::U16Register(TargetU16Register::HL)) }
        0x0F => { Instruction::RRC(Target::U8Register(TargetRegister::A)) }
        0x10 => { Instruction::RL(Target::U8Register(TargetRegister::B)) }
        0x11 => { Instruction::RL(Target::U8Register(TargetRegister::C)) }
        0x12 => { Instruction::RL(Target::U8Register(TargetRegister::D)) }
        0x13 => { Instruction::RL(Target::U8Register(TargetRegister::E)) }
        0x14 => { Instruction::RL(Target::U8Register(TargetRegister::H)) }
        0x15 => { Instruction::RL(Target::U8Register(TargetRegister::L)) }
        0x16 => { Instruction::RL(Target::U16Register(TargetU16Register::HL)) }
        0x17 => { Instruction::RL(Target::U8Register(TargetRegister::A)) }
        0x18 => { Instruction::RR(Target::U8Register(TargetRegister::B)) }
        0x19 => { Instruction::RR(Target::U8Register(TargetRegister::C)) }
        0x1A => { Instruction::RR(Target::U8Register(TargetRegister::D)) }
        0x1B => { Instruction::RR(Target::U8Register(TargetRegister::E)) }
        0x1C => { Instruction::RR(Target::U8Register(TargetRegister::H)) }
        0x1D => { Instruction::RR(Target::U8Register(TargetRegister::L)) }
        0x1E => { Instruction::RR(Target::U16Register(TargetU16Register::HL)) }
        0x1F => { Instruction::RR(Target::U8Register(TargetRegister::A)) }
        0x20 => { Instruction::SLA(Target::U8Register(TargetRegister::B)) }
        0x21 => { Instruction::SLA(Target::U8Register(TargetRegister::C)) }
        0x22 => { Instruction::SLA(Target::U8Register(TargetRegister::D)) }
        0x23 => { Instruction::SLA(Target::U8Register(TargetRegister::E)) }
        0x24 => { Instruction::SLA(Target::U8Register(TargetRegister::H)) }
        0x25 => { Instruction::SLA(Target::U8Register(TargetRegister::L)) }
        0x26 => { Instruction::SLA(Target::U16Register(TargetU16Register::HL)) }
        0x27 => { Instruction::SLA(Target::U8Register(TargetRegister::A)) }
        0x28 => { Instruction::SRA(Target::U8Register(TargetRegister::B)) }
        0x29 => { Instruction::SRA(Target::U8Register(TargetRegister::C)) }
        0x2A => { Instruction::SRA(Target::U8Register(TargetRegister::D)) }
        0x2B => { Instruction::SRA(Target::U8Register(TargetRegister::E)) }
        0x2C => { Instruction::SRA(Target::U8Register(TargetRegister::H)) }
        0x2D => { Instruction::SRA(Target::U8Register(TargetRegister::L)) }
        0x2E => { Instruction::SRA(Target::U16Register(TargetU16Register::HL)) }
        0x2F => { Instruction::SRA(Target::U8Register(TargetRegister::A)) }
        0x30 => { Instruction::SWAP(Target::U8Register(TargetRegister::B)) }
        0x31 => { Instruction::SWAP(Target::U8Register(TargetRegister::C)) }
        0x32 => { Instruction::SWAP(Target::U8Register(TargetRegister::D)) }
        0x33 => { Instruction::SWAP(Target::U8Register(TargetRegister::E)) }
        0x34 => { Instruction::SWAP(Target::U8Register(TargetRegister::H)) }
        0x35 => { Instruction::SWAP(Target::U8Register(TargetRegister::L)) }
        0x36 => { Instruction::SWAP(Target::U16Register(TargetU16Register::HL)) }
        0x37 => { Instruction::SWAP(Target::U8Register(TargetRegister::A)) }
        0x38 => { Instruction::SRL(Target::U8Register(TargetRegister::B)) }
        0x39 => { Instruction::SRL(Target::U8Register(TargetRegister::C)) }
        0x3A => { Instruction::SRL(Target::U8Register(TargetRegister::D)) }
        0x3B => { Instruction::SRL(Target::U8Register(TargetRegister::E)) }
        0x3C => { Instruction::SRL(Target::U8Register(TargetRegister::H)) }
        0x3D => { Instruction::SRL(Target::U8Register(TargetRegister::L)) }
        0x3E => { Instruction::SRL(Target::U16Register(TargetU16Register::HL)) }
        0x3F => { Instruction::SRL(Target::U8Register(TargetRegister::A)) }
        0x40 => { Instruction::BIT(0, Target::U8Register(TargetRegister::B)) }
        0x41 => { Instruction::BIT(0, Target::U8Register(TargetRegister::C)) }
        0x42 => { Instruction::BIT(0, Target::U8Register(TargetRegister::D)) }
        0x43 => { Instruction::BIT(0, Target::U8Register(TargetRegister::E)) }
        0x44 => { Instruction::BIT(0, Target::U8Register(TargetRegister::H)) }
        0x45 => { Instruction::BIT(0, Target::U8Register(TargetRegister::L)) }
        0x46 => { Instruction::BIT(0, Target::U16Register(TargetU16Register::HL)) }
        0x47 => { Instruction::BIT(0, Target::U8Register(TargetRegister::A)) }
        0x48 => { Instruction::BIT(1, Target::U8Register(TargetRegister::B)) }
        0x49 => { Instruction::BIT(1, Target::U8Register(TargetRegister::C)) }
        0x4A => { Instruction::BIT(1, Target::U8Register(TargetRegister::D)) }
        0x4B => { Instruction::BIT(1, Target::U8Register(TargetRegister::E)) }
        0x4C => { Instruction::BIT(1, Target::U8Register(TargetRegister::H)) }
        0x4D => { Instruction::BIT(1, Target::U8Register(TargetRegister::L)) }
        0x4E => { Instruction::BIT(1, Target::U16Register(TargetU16Register::HL)) }
        0x4F => { Instruction::BIT(1, Target::U8Register(TargetRegister::A)) }
        0x50 => { Instruction::BIT(2, Target::U8Register(TargetRegister::B)) }
        0x51 => { Instruction::BIT(2, Target::U8Register(TargetRegister::C)) }
        0x52 => { Instruction::BIT(2, Target::U8Register(TargetRegister::D)) }
        0x53 => { Instruction::BIT(2, Target::U8Register(TargetRegister::E)) }
        0x54 => { Instruction::BIT(2, Target::U8Register(TargetRegister::H)) }
        0x55 => { Instruction::BIT(2, Target::U8Register(TargetRegister::L)) }
        0x56 => { Instruction::BIT(2, Target::U16Register(TargetU16Register::HL)) }
        0x57 => { Instruction::BIT(2, Target::U8Register(TargetRegister::A)) }
        0x58 => { Instruction::BIT(3, Target::U8Register(TargetRegister::B)) }
        0x59 => { Instruction::BIT(3, Target::U8Register(TargetRegister::C)) }
        0x5A => { Instruction::BIT(3, Target::U8Register(TargetRegister::D)) }
        0x5B => { Instruction::BIT(3, Target::U8Register(TargetRegister::E)) }
        0x5C => { Instruction::BIT(3, Target::U8Register(TargetRegister::H)) }
        0x5D => { Instruction::BIT(3, Target::U8Register(TargetRegister::L)) }
        0x5E => { Instruction::BIT(3, Target::U16Register(TargetU16Register::HL)) }
        0x5F => { Instruction::BIT(3, Target::U8Register(TargetRegister::A)) }
        0x60 => { Instruction::BIT(4, Target::U8Register(TargetRegister::B)) }
        0x61 => { Instruction::BIT(4, Target::U8Register(TargetRegister::C)) }
        0x62 => { Instruction::BIT(4, Target::U8Register(TargetRegister::D)) }
        0x63 => { Instruction::BIT(4, Target::U8Register(TargetRegister::E)) }
        0x64 => { Instruction::BIT(4, Target::U8Register(TargetRegister::H)) }
        0x65 => { Instruction::BIT(4, Target::U8Register(TargetRegister::L)) }
        0x66 => { Instruction::BIT(4, Target::U16Register(TargetU16Register::HL)) }
        0x67 => { Instruction::BIT(4, Target::U8Register(TargetRegister::A)) }
        0x68 => { Instruction::BIT(5, Target::U8Register(TargetRegister::B)) }
        0x69 => { Instruction::BIT(5, Target::U8Register(TargetRegister::C)) }
        0x6A => { Instruction::BIT(5, Target::U8Register(TargetRegister::D)) }
        0x6B => { Instruction::BIT(5, Target::U8Register(TargetRegister::E)) }
        0x6C => { Instruction::BIT(5, Target::U8Register(TargetRegister::H)) }
        0x6D => { Instruction::BIT(5, Target::U8Register(TargetRegister::L)) }
        0x6E => { Instruction::BIT(5, Target::U16Register(TargetU16Register::HL)) }
        0x6F => { Instruction::BIT(5, Target::U8Register(TargetRegister::A)) }
        0x70 => { Instruction::BIT(6, Target::U8Register(TargetRegister::B)) }
        0x71 => { Instruction::BIT(6, Target::U8Register(TargetRegister::C)) }
        0x72 => { Instruction::BIT(6, Target::U8Register(TargetRegister::D)) }
        0x73 => { Instruction::BIT(6, Target::U8Register(TargetRegister::E)) }
        0x74 => { Instruction::BIT(6, Target::U8Register(TargetRegister::H)) }
        0x75 => { Instruction::BIT(6, Target::U8Register(TargetRegister::L)) }
        0x76 => { Instruction::BIT(6, Target::U16Register(TargetU16Register::HL)) }
        0x77 => { Instruction::BIT(6, Target::U8Register(TargetRegister::A)) }
        0x78 => { Instruction::BIT(7, Target::U8Register(TargetRegister::B)) }
        0x79 => { Instruction::BIT(7, Target::U8Register(TargetRegister::C)) }
        0x7A => { Instruction::BIT(7, Target::U8Register(TargetRegister::D)) }
        0x7B => { Instruction::BIT(7, Target::U8Register(TargetRegister::E)) }
        0x7C => { Instruction::BIT(7, Target::U8Register(TargetRegister::H)) }
        0x7D => { Instruction::BIT(7, Target::U8Register(TargetRegister::L)) }
        0x7E => { Instruction::BIT(7, Target::U16Register(TargetU16Register::HL)) }
        0x7F => { Instruction::BIT(7, Target::U8Register(TargetRegister::A)) }
        0x80 => { Instruction::RES(0, Target::U8Register(TargetRegister::B)) }
        0x81 => { Instruction::RES(0, Target::U8Register(TargetRegister::C)) }
        0x82 => { Instruction::RES(0, Target::U8Register(TargetRegister::D)) }
        0x83 => { Instruction::RES(0, Target::U8Register(TargetRegister::E)) }
        0x84 => { Instruction::RES(0, Target::U8Register(TargetRegister::H)) }
        0x85 => { Instruction::RES(0, Target::U8Register(TargetRegister::L)) }
        0x86 => { Instruction::RES(0, Target::U16Register(TargetU16Register::HL)) }
        0x87 => { Instruction::RES(0, Target::U8Register(TargetRegister::A)) }
        0x88 => { Instruction::RES(1, Target::U8Register(TargetRegister::B)) }
        0x89 => { Instruction::RES(1, Target::U8Register(TargetRegister::C)) }
        0x8A => { Instruction::RES(1, Target::U8Register(TargetRegister::D)) }
        0x8B => { Instruction::RES(1, Target::U8Register(TargetRegister::E)) }
        0x8C => { Instruction::RES(1, Target::U8Register(TargetRegister::H)) }
        0x8D => { Instruction::RES(1, Target::U8Register(TargetRegister::L)) }
        0x8E => { Instruction::RES(1, Target::U16Register(TargetU16Register::HL)) }
        0x8F => { Instruction::RES(1, Target::U8Register(TargetRegister::A)) }
        0x90 => { Instruction::RES(2, Target::U8Register(TargetRegister::B)) }
        0x91 => { Instruction::RES(2, Target::U8Register(TargetRegister::C)) }
        0x92 => { Instruction::RES(2, Target::U8Register(TargetRegister::D)) }
        0x93 => { Instruction::RES(2, Target::U8Register(TargetRegister::E)) }
        0x94 => { Instruction::RES(2, Target::U8Register(TargetRegister::H)) }
        0x95 => { Instruction::RES(2, Target::U8Register(TargetRegister::L)) }
        0x96 => { Instruction::RES(2, Target::U16Register(TargetU16Register::HL)) }
        0x97 => { Instruction::RES(2, Target::U8Register(TargetRegister::A)) }
        0x98 => { Instruction::RES(3, Target::U8Register(TargetRegister::B)) }
        0x99 => { Instruction::RES(3, Target::U8Register(TargetRegister::C)) }
        0x9A => { Instruction::RES(3, Target::U8Register(TargetRegister::D)) }
        0x9B => { Instruction::RES(3, Target::U8Register(TargetRegister::E)) }
        0x9C => { Instruction::RES(3, Target::U8Register(TargetRegister::H)) }
        0x9D => { Instruction::RES(3, Target::U8Register(TargetRegister::L)) }
        0x9E => { Instruction::RES(3, Target::U16Register(TargetU16Register::HL)) }
        0x9F => { Instruction::RES(3, Target::U8Register(TargetRegister::A)) }
        0xA0 => { Instruction::RES(4, Target::U8Register(TargetRegister::B)) }
        0xA1 => { Instruction::RES(4, Target::U8Register(TargetRegister::C)) }
        0xA2 => { Instruction::RES(4, Target::U8Register(TargetRegister::D)) }
        0xA3 => { Instruction::RES(4, Target::U8Register(TargetRegister::E)) }
        0xA4 => { Instruction::RES(4, Target::U8Register(TargetRegister::H)) }
        0xA5 => { Instruction::RES(4, Target::U8Register(TargetRegister::L)) }
        0xA6 => { Instruction::RES(4, Target::U16Register(TargetU16Register::HL)) }
        0xA7 => { Instruction::RES(4, Target::U8Register(TargetRegister::A)) }
        0xA8 => { Instruction::RES(5, Target::U8Register(TargetRegister::B)) }
        0xA9 => { Instruction::RES(5, Target::U8Register(TargetRegister::C)) }
        0xAA => { Instruction::RES(5, Target::U8Register(TargetRegister::D)) }
        0xAB => { Instruction::RES(5, Target::U8Register(TargetRegister::E)) }
        0xAC => { Instruction::RES(5, Target::U8Register(TargetRegister::H)) }
        0xAD => { Instruction::RES(5, Target::U8Register(TargetRegister::L)) }
        0xAE => { Instruction::RES(5, Target::U16Register(TargetU16Register::HL)) }
        0xAF => { Instruction::RES(5, Target::U8Register(TargetRegister::A)) }
        0xB0 => { Instruction::RES(6, Target::U8Register(TargetRegister::B)) }
        0xB1 => { Instruction::RES(6, Target::U8Register(TargetRegister::C)) }
        0xB2 => { Instruction::RES(6, Target::U8Register(TargetRegister::D)) }
        0xB3 => { Instruction::RES(6, Target::U8Register(TargetRegister::E)) }
        0xB4 => { Instruction::RES(6, Target::U8Register(TargetRegister::H)) }
        0xB5 => { Instruction::RES(6, Target::U8Register(TargetRegister::L)) }
        0xB6 => { Instruction::RES(6, Target::U16Register(TargetU16Register::HL)) }
        0xB7 => { Instruction::RES(6, Target::U8Register(TargetRegister::A)) }
        0xB8 => { Instruction::RES(7, Target::U8Register(TargetRegister::B)) }
        0xB9 => { Instruction::RES(7, Target::U8Register(TargetRegister::C)) }
        0xBA => { Instruction::RES(7, Target::U8Register(TargetRegister::D)) }
        0xBB => { Instruction::RES(7, Target::U8Register(TargetRegister::E)) }
        0xBC => { Instruction::RES(7, Target::U8Register(TargetRegister::H)) }
        0xBD => { Instruction::RES(7, Target::U8Register(TargetRegister::L)) }
        0xBE => { Instruction::RES(7, Target::U16Register(TargetU16Register::HL)) }
        0xBF => { Instruction::RES(7, Target::U8Register(TargetRegister::A)) }
        0xC0 => { Instruction::SET(0, Target::U8Register(TargetRegister::B)) }
        0xC1 => { Instruction::SET(0, Target::U8Register(TargetRegister::C)) }
        0xC2 => { Instruction::SET(0, Target::U8Register(TargetRegister::D)) }
        0xC3 => { Instruction::SET(0, Target::U8Register(TargetRegister::E)) }
        0xC4 => { Instruction::SET(0, Target::U8Register(TargetRegister::H)) }
        0xC5 => { Instruction::SET(0, Target::U8Register(TargetRegister::L)) }
        0xC6 => { Instruction::SET(0, Target::U16Register(TargetU16Register::HL)) }
        0xC7 => { Instruction::SET(0, Target::U8Register(TargetRegister::A)) }
        0xC8 => { Instruction::SET(1, Target::U8Register(TargetRegister::B)) }
        0xC9 => { Instruction::SET(1, Target::U8Register(TargetRegister::C)) }
        0xCA => { Instruction::SET(1, Target::U8Register(TargetRegister::D)) }
        0xCB => { Instruction::SET(1, Target::U8Register(TargetRegister::E)) }
        0xCC => { Instruction::SET(1, Target::U8Register(TargetRegister::H)) }
        0xCD => { Instruction::SET(1, Target::U8Register(TargetRegister::L)) }
        0xCE => { Instruction::SET(1, Target::U16Register(TargetU16Register::HL)) }
        0xCF => { Instruction::SET(1, Target::U8Register(TargetRegister::A)) }
        0xD0 => { Instruction::SET(2, Target::U8Register(TargetRegister::B)) }
        0xD1 => { Instruction::SET(2, Target::U8Register(TargetRegister::C)) }
        0xD2 => { Instruction::SET(2, Target::U8Register(TargetRegister::D)) }
        0xD3 => { Instruction::SET(2, Target::U8Register(TargetRegister::E)) }
        0xD4 => { Instruction::SET(2, Target::U8Register(TargetRegister::H)) }
        0xD5 => { Instruction::SET(2, Target::U8Register(TargetRegister::L)) }
        0xD6 => { Instruction::SET(2, Target::U16Register(TargetU16Register::HL)) }
        0xD7 => { Instruction::SET(2, Target::U8Register(TargetRegister::A)) }
        0xD8 => { Instruction::SET(3, Target::U8Register(TargetRegister::B)) }
        0xD9 => { Instruction::SET(3, Target::U8Register(TargetRegister::C)) }
        0xDA => { Instruction::SET(3, Target::U8Register(TargetRegister::D)) }
        0xDB => { Instruction::SET(3, Target::U8Register(TargetRegister::E)) }
        0xDC => { Instruction::SET(3, Target::U8Register(TargetRegister::H)) }
        0xDD => { Instruction::SET(3, Target::U8Register(TargetRegister::L)) }
        0xDE => { Instruction::SET(3, Target::U16Register(TargetU16Register::HL)) }
        0xDF => { Instruction::SET(3, Target::U8Register(TargetRegister::A)) }
        0xE0 => { Instruction::SET(4, Target::U8Register(TargetRegister::B)) }
        0xE1 => { Instruction::SET(4, Target::U8Register(TargetRegister::C)) }
        0xE2 => { Instruction::SET(4, Target::U8Register(TargetRegister::D)) }
        0xE3 => { Instruction::SET(4, Target::U8Register(TargetRegister::E)) }
        0xE4 => { Instruction::SET(4, Target::U8Register(TargetRegister::H)) }
        0xE5 => { Instruction::SET(4, Target::U8Register(TargetRegister::L)) }
        0xE6 => { Instruction::SET(4, Target::U16Register(TargetU16Register::HL)) }
        0xE7 => { Instruction::SET(4, Target::U8Register(TargetRegister::A)) }
        0xE8 => { Instruction::SET(5, Target::U8Register(TargetRegister::B)) }
        0xE9 => { Instruction::SET(5, Target::U8Register(TargetRegister::C)) }
        0xEA => { Instruction::SET(5, Target::U8Register(TargetRegister::D)) }
        0xEB => { Instruction::SET(5, Target::U8Register(TargetRegister::E)) }
        0xEC => { Instruction::SET(5, Target::U8Register(TargetRegister::H)) }
        0xED => { Instruction::SET(5, Target::U8Register(TargetRegister::L)) }
        0xEE => { Instruction::SET(5, Target::U16Register(TargetU16Register::HL)) }
        0xEF => { Instruction::SET(5, Target::U8Register(TargetRegister::A)) }
        0xF0 => { Instruction::SET(6, Target::U8Register(TargetRegister::B)) }
        0xF1 => { Instruction::SET(6, Target::U8Register(TargetRegister::C)) }
        0xF2 => { Instruction::SET(6, Target::U8Register(TargetRegister::D)) }
        0xF3 => { Instruction::SET(6, Target::U8Register(TargetRegister::E)) }
        0xF4 => { Instruction::SET(6, Target::U8Register(TargetRegister::H)) }
        0xF5 => { Instruction::SET(6, Target::U8Register(TargetRegister::L)) }
        0xF6 => { Instruction::SET(6, Target::U16Register(TargetU16Register::HL)) }
        0xF7 => { Instruction::SET(6, Target::U8Register(TargetRegister::A)) }
        0xF8 => { Instruction::SET(7, Target::U8Register(TargetRegister::B)) }
        0xF9 => { Instruction::SET(7, Target::U8Register(TargetRegister::C)) }
        0xFA => { Instruction::SET(7, Target::U8Register(TargetRegister::D)) }
        0xFB => { Instruction::SET(7, Target::U8Register(TargetRegister::E)) }
        0xFC => { Instruction::SET(7, Target::U8Register(TargetRegister::H)) }
        0xFD => { Instruction::SET(7, Target::U8Register(TargetRegister::L)) }
        0xFE => { Instruction::SET(7, Target::U16Register(TargetU16Register::HL)) }
        0xFF => { Instruction::SET(7, Target::U8Register(TargetRegister::A)) }
        _ => { panic!("Invalid u16 opcode: {:02X}", opcode); }
    }
}
