/*++

Copyright (c) 2014 Minoca Corp. All Rights Reserved

Module Name:

    thm32dis.c

Abstract:

    This module implements support for disassembling 32-bit Thumb-2
    instructions.

Author:

    Evan Green 27-Apr-2014

Environment:

    Debug

--*/

//
// ------------------------------------------------------------------- Includes
//

#include "dbgrtl.h"
#include "disasm.h"
#include "armdis.h"
#include "thmdis.h"

#include <assert.h>
#include <string.h>
#include <stdio.h>

//
// ---------------------------------------------------------------- Definitions
//

//
// ------------------------------------------------------ Data Type Definitions
//

//
// ----------------------------------------------- Internal Function Prototypes
//

VOID
DbgpThumb32DecodeLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLoadStoreDualExclusive (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLdrexStrex (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLdrdStrd (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLoadStoreExclusiveFunkySize (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeTableBranch (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeDataProcessingShiftedRegister (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeCoprocessorSimdFloatingPoint (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeUndefined (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeSimdDataProcessing (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeDataModifiedImmediate (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeDataPlainImmediate (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeBranchAndMiscellaneous (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeMsr (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeCpsAndHints (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeMiscellaneousControl (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeBxj (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeExceptionReturn (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeMrs (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeHvc (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeSmc (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeBranch (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeUdf (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeBranchWithLink (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLoadStoreSingleItem (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLoadStoreImmediate (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLoadStoreRegister (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeDataProcessingRegister (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeMultiplyAccumulate (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumb32DecodeLongMultiplyDivide (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpThumbDecodeImmediateShift (
    PSTR Destination,
    ULONG DestinationSize,
    ULONG Register,
    ULONG Type,
    ULONG Immediate
    );

ULONG
DbgpThumb32DecodeModifiedImmediate (
    ULONG Immediate12
    );

//
// -------------------------------------------------------------------- Globals
//

//
// Define mnemonic tables.
//

PSTR DbgThumb32DataProcessingMnemonics[2][16] = {
    {
        "and.w",
        "bic.w",
        "orr.w",
        "orn.w",
        "eor.w",
        "",
        "",
        "",
        "add.w",
        "",
        "adc.w",
        "sbc.w",
        "",
        "sub.w",
        "rsb.w",
        "",
    },
    {
        "ands.w",
        "bics.w",
        "orrs.w",
        "orns.w",
        "eors.w",
        "",
        "",
        "",
        "adds.w",
        "",
        "adcs.w",
        "sbcs.w",
        "",
        "subs.w",
        "rsbs.w",
        "",
    },
};

PSTR DbgThumb32DataProcessingShiftMnemonics[2][5] = {
    {
        "lsl.w",
        "lsr.w",
        "asr.w",
        "ror.w",
        "rrx.w"
    },
    {
        "lsls.w",
        "lsrs.w",
        "asrs.w",
        "rors.w",
        "rrxs.w"
    }
};

PSTR DbgThumb32MovMnemonics[2] = {
    "mov.w",
    "movs.w"
};

PSTR DbgThumb32MvnwMnemonics[2] = {
    "mvn.w",
    "mvns.w"
};

PSTR DbgThumb32HintMnemonics[] = {
    "nop.w",
    "yield.w",
    "wfe.w",
    "wfi.w",
    "sev.w"
};

PSTR DbgThumb32LoadStoreMnemonics[2][4] = {
    {
        "strb.w",
        "strh.w",
        "str.w",
        "Undef str.w"
    },
    {
        "ldrb.w",
        "ldrh.w",
        "ldr.w",
        "Undef ldr.w"
    }
};

PSTR DbgThumb32LoadSetFlagsMnemonics[4] = {
    "ldsrb.w",
    "ldrsh.w",
    "ldrs.w",
    "Undef ldrs.w"
};

PSTR DbgThumb32LoadStoreUnprivilegedMnemonics[2][4] = {
    {
        "strbt",
        "strht",
        "strt",
        "Undef strt"
    },
    {
        "ldrbt",
        "ldrht",
        "ldrt",
        "Undef ldrt"
    }
};

PSTR DbgThumb32LoadSetFlagsUnprivilegedMnemonics[4] = {
    "ldrsbt",
    "ldrsht",
    "ldrst",
    "Undef ldrst"
};

PSTR DbgThumb32PreloadMnemonics[4] = {
    "pli",
    "pldw",
    "pld",
    "Undef pld"
};

PSTR DbgThumb32ExtendAndAddMnemonics[2][6] = {
    {
        "sxtah",
        "uxtah",
        "sxtab16",
        "uxtab16",
        "sxtab",
        "uxtab"
    },
    {
        "sxth",
        "uxth",
        "sxtb16",
        "uxtb16",
        "sxtb",
        "uxtb"
    }
};

PSTR DbgThumb32ParallelArithmeticMnemonics[2][24] = {
    {
        "sadd8",
        "sadd16",
        "sasx",
        "",
        "ssub8",
        "ssub16",
        "ssax",
        "",
        "qadd8",
        "qadd16",
        "qasx",
        "",
        "qsub8",
        "qsub16",
        "qsax",
        "",
        "shadd8",
        "shadd16",
        "shasx",
        "",
        "shsub8",
        "shsub16",
        "shsax",
        "",
    },
    {
        "uadd8",
        "uadd16",
        "uasx",
        "",
        "usub8",
        "usub16",
        "usax",
        "",
        "uqadd8",
        "uqadd16",
        "uqasx",
        "",
        "uqsub8",
        "uqsub16",
        "uqsax",
        "",
        "uhadd8",
        "uhadd16",
        "uhasx",
        "",
        "uhsub8",
        "uhsub16",
        "uhsax",
        "",
    },
};

PSTR DbgThumb32DataProcessingMiscellaneousMnemonics[] = {
    "qadd",
    "qdadd",
    "qsub",
    "qdsub",
    "rev.w",
    "rev16.w",
    "rbit",
    "revsh.w",
    "sel",
    "",
    "",
    "",
    "clz",
    "",
    "",
    ""
};

PSTR DbgThumb32MultiplyMnemonics[2][8] = {
    {
        "mla",
        "smla",
        "smlad",
        "smlaw",
        "smlsd",
        "smmla",
        "smmls",
        "usada8"
    },
    {
        "mul",
        "smul",
        "smuad",
        "smulw",
        "smusd",
        "smmul",
        "smmls",
        "usad8"
    }
};

PSTR DbgThumb32MultiplyTopBottomMnemonics[2] = {
    "b",
    "t",
};

PSTR DbgThumb32LongMultiplyMnemonics[8] = {
    "smull",
    "sdiv",
    "umull",
    "udiv",
    "smlal",
    "smlsld",
    "umlal",
    ""
};

//
// Define decode tables.
//

THUMB_DECODE_BRANCH DbgThumb32TopLevelTable[] = {
    {0x1E400000, 0x08000000, 0, DbgpThumb32DecodeLoadStoreMultiple},
    {0x1E400000, 0x08400000, 0, DbgpThumb32DecodeLoadStoreDualExclusive},
    {0x1E000000, 0x0A000000, 0, DbgpThumb32DecodeDataProcessingShiftedRegister},
    {0x1C000000, 0x0C000000, 0, DbgpThumb32DecodeCoprocessorSimdFloatingPoint},
    {0x1A008000, 0x10000000, 0, DbgpThumb32DecodeDataModifiedImmediate},
    {0x1A008000, 0x12000000, 0, DbgpThumb32DecodeDataPlainImmediate},
    {0x18008000, 0x10008000, 0, DbgpThumb32DecodeBranchAndMiscellaneous},
    {0x1F100000, 0x18000000, 0, DbgpThumb32DecodeLoadStoreSingleItem},
    {0x1E700000, 0x18100000, 0, DbgpThumb32DecodeLoadStoreSingleItem},
    {0x1E700000, 0x18300000, 0, DbgpThumb32DecodeLoadStoreSingleItem},
    {0x1E700000, 0x18500000, 0, DbgpThumb32DecodeLoadStoreSingleItem},
    {0x1E700000, 0x18700000, 0, DbgpThumb32DecodeUndefined},
    {0x1F100000, 0x19000000, 0, DbgpArmDecodeSimdElementLoadStore},
    {0x1F000000, 0x1A000000, 0, DbgpThumb32DecodeDataProcessingRegister},
    {0x1F800000, 0x1B000000, 0, DbgpThumb32DecodeMultiplyAccumulate},
    {0x1F800000, 0x1B800000, 0, DbgpThumb32DecodeLongMultiplyDivide},
    {0x1C000000, 0x1C000000, 0, DbgpThumb32DecodeCoprocessorSimdFloatingPoint},
};

THUMB_DECODE_BRANCH DbgThumb32LoadStoreDualExclusiveTable[] = {
    {0x01B00000, 0x00000000, 0, DbgpThumb32DecodeLdrexStrex},
    {0x01B00000, 0x00100000, 0, DbgpThumb32DecodeLdrexStrex},
    {0x01300000, 0x00200000, 0, DbgpThumb32DecodeLdrdStrd},
    {0x01100000, 0x01000000, 0, DbgpThumb32DecodeLdrdStrd},
    {0x01300000, 0x00300000, 0, DbgpThumb32DecodeLdrdStrd},
    {0x01100000, 0x01100000, 0, DbgpThumb32DecodeLdrdStrd},
    {0x01B000F0, 0x00800040, 0, DbgpThumb32DecodeLoadStoreExclusiveFunkySize},
    {0x01B000F0, 0x00800050, 0, DbgpThumb32DecodeLoadStoreExclusiveFunkySize},
    {0x01B000F0, 0x00800070, 0, DbgpThumb32DecodeLoadStoreExclusiveFunkySize},
    {0x01B000F0, 0x00900000, 0, DbgpThumb32DecodeTableBranch},
    {0x01B000F0, 0x00900010, 0, DbgpThumb32DecodeTableBranch},
    {0x01B000F0, 0x00900040, 0, DbgpThumb32DecodeLoadStoreExclusiveFunkySize},
    {0x01B000F0, 0x00900050, 0, DbgpThumb32DecodeLoadStoreExclusiveFunkySize},
    {0x01B000F0, 0x00900070, 0, DbgpThumb32DecodeLoadStoreExclusiveFunkySize},
};

THUMB_DECODE_BRANCH DbgThumb32CoprocessorSimdFloatingPointTable[] = {
    {0x03E00000, 0x00000000, 0, DbgpThumb32DecodeUndefined},
    {0x03000000, 0x03000000, 0, DbgpThumb32DecodeSimdDataProcessing},
    {0x03E00E00, 0x00400A00, 0, DbgpArmDecodeSimd64BitTransfers},
    {0x02000E00, 0x00000A00, 0, DbgpArmDecodeSimdLoadStore},
    {0x03000E10, 0x02000A00, 0, DbgpArmDecodeFloatingPoint},
    {0x03000E10, 0x02000A10, 0, DbgpArmDecodeSimdSmallTransfers},
    {0x03E00000, 0x00400000, 0, DbgpArmDecodeCoprocessorMoveTwo},
    {0x02000000, 0x00000000, 0, DbgpArmDecodeCoprocessorLoadStore},
    {0x03000010, 0x02000000, 0, DbgpArmDecodeCoprocessorMove},
    {0x03000010, 0x02000010, 0, DbgpArmDecodeCoprocessorMove},
};

THUMB_DECODE_BRANCH DbgThumb32BranchAndMiscellaneousTable[] = {
    {0x07E05000, 0x03800000, 0, DbgpThumb32DecodeMsr},
    {0x07F05000, 0x03A00000, 0, DbgpThumb32DecodeCpsAndHints},
    {0x07F05000, 0x03B00000, 0, DbgpThumb32DecodeMiscellaneousControl},
    {0x07F05000, 0x03C00000, 0, DbgpThumb32DecodeBxj},
    {0x07F05000, 0x03D00000, 0, DbgpThumb32DecodeExceptionReturn},
    {0x07E05000, 0x03E00000, 0, DbgpThumb32DecodeMrs},
    {0x07F07000, 0x07E00000, 0, DbgpThumb32DecodeHvc},
    {0x07F07000, 0x07F00000, 0, DbgpThumb32DecodeSmc},
    {0x00005000, 0x00001000, 0, DbgpThumb32DecodeBranch},
    {0x00005000, 0x00000000, 0, DbgpThumb32DecodeBranch},
    {0x07F07000, 0x07F02000, 0, DbgpThumb32DecodeUdf},
    {0x00004000, 0x00004000, 0, DbgpThumb32DecodeBranchWithLink},
};

//
// ------------------------------------------------------------------ Functions
//

VOID
DbgpThumb32Decode (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit portion of the Thumb-2 instruction set.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    //
    // Swap the half words, then decode using the table.
    //

    Context->Instruction = ((Context->Instruction << 16) & 0xFFFF0000) |
                           ((Context->Instruction >> 16) & 0x0000FFFF);

    THUMB_DECODE_WITH_TABLE(Context, DbgThumb32TopLevelTable);
    return;
}

//
// --------------------------------------------------------- Internal Functions
//

VOID
DbgpThumb32DecodeLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit load/store multiple instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG Mode;
    ULONG Op;
    ULONG RegisterList;
    ULONG Rn;

    Instruction = Context->Instruction;
    Op = (Instruction >> THUMB32_LOAD_STORE_MULTIPLE_OP_SHIFT) &
         THUMB32_LOAD_STORE_MULTIPLE_OP_MASK;

    Rn = (Instruction >> THUMB32_LOAD_STORE_MULTIPLE_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    //
    // The instruction is either rfe/srs, or ldm/stm.
    //

    switch (Op) {
    case THUMB32_LOAD_STORE_RETURN_STATE_OP:
    case THUMB32_LOAD_STORE_RETURN_STATE_OP2:
        Mode = Instruction & THUMB32_LOAD_STORE_MODE_MASK;
        if ((Instruction & THUMB32_LOAD_BIT) != 0) {
            strcpy(Context->Mnemonic, THUMB_RFE_MNEMONIC);

        } else {
            strcpy(Context->Mnemonic, THUMB_SRS_MNEMONIC);
            DbgpArmPrintMode(Context->Operand2, Mode);
        }

        break;

    case THUMB32_LOAD_STORE_MULTIPLE_OP:
    case THUMB32_LOAD_STORE_MULTIPLE_OP2:
    default:
        if ((Instruction & THUMB32_LOAD_BIT) != 0) {
            strcpy(Context->Mnemonic, THUMB_LDM_MNEMONIC);

        } else {
            strcpy(Context->Mnemonic, THUMB_STM_MNEMONIC);
        }

        RegisterList = Instruction & THUMB_REGISTER16_LIST;
        DbgpArmDecodeRegisterList(Context->Operand2,
                                  sizeof(Context->Operand2),
                                  RegisterList);

        break;
    }

    //
    // Add the decrement-before or increment-after suffix.
    //

    if ((Instruction & THUMB32_LOAD_STORE_INCREMENT) != 0) {
        strcat(Context->Mnemonic, THUMB_IA_SUFFIX);

    } else {
        strcat(Context->Mnemonic, THUMB_DB_SUFFIX);
    }

    //
    // Print operand one, the register.
    //

    if ((Instruction & THUMB32_LOAD_STORE_MULTIPLE_WRITE_BACK_BIT) != 0) {
        snprintf(Context->Operand1,
                 sizeof(Context->Operand1),
                 "%s!",
                 DbgArmRegisterNames[Rn]);

    } else {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
    }

    return;
}

VOID
DbgpThumb32DecodeLoadStoreDualExclusive (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit load/store dual, load/store exclusive,
    and table branch instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    THUMB_DECODE_WITH_TABLE(Context, DbgThumb32LoadStoreDualExclusiveTable);
    return;
}

VOID
DbgpThumb32DecodeLdrexStrex (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit load/store exclusive (32-bit data)
    instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate8;
    ULONG Instruction;
    ULONG Rd;
    ULONG Rn;
    PSTR RnOperand;
    ULONG Rt;
    PSTR RtOperand;

    Instruction = Context->Instruction;
    Rd = (Instruction >> THUMB32_EXCLUSIVE_RD_SHIFT) & THUMB_REGISTER16_MASK;
    Rn = (Instruction >> THUMB32_EXCLUSIVE_RN_SHIFT) & THUMB_REGISTER16_MASK;
    Rt = (Instruction >> THUMB32_EXCLUSIVE_RT_SHIFT) & THUMB_REGISTER16_MASK;
    Immediate8 = (Instruction >> THUMB32_EXCLUSIVE_IMMEDIATE8_SHIFT) &
                 THUMB_IMMEDIATE8_MASK;

    Immediate8 <<= 2;
    if ((Instruction & THUMB32_LOAD_BIT) != 0) {
        strcpy(Context->Mnemonic, THUMB_LDREX_MNEMONIC);
        RtOperand = &(Context->Operand1[0]);
        RnOperand = &(Context->Operand2[0]);

    } else {
        strcpy(Context->Mnemonic, THUMB_STREX_MNEMONIC);
        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        RtOperand = &(Context->Operand2[0]);
        RnOperand = &(Context->Operand3[0]);
    }

    strcpy(RtOperand, DbgArmRegisterNames[Rt]);
    if (Immediate8 == 0) {
        sprintf(RnOperand, "[%s]", DbgArmRegisterNames[Rn]);

    } else {
        sprintf(RnOperand,
                "[%s, #%d]",
                DbgArmRegisterNames[Rn],
                Immediate8 * 4);
    }

    return;
}

VOID
DbgpThumb32DecodeLdrdStrd (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit load/store dual (64-bit data).

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate8;
    ULONG Instruction;
    ULONG Rn;
    ULONG Rt;
    ULONG Rt2;

    Instruction = Context->Instruction;
    if ((Instruction & THUMB32_LOAD_BIT) != 0) {
        strcpy(Context->Mnemonic, THUMB_LDRD_MNEMONIC);

    } else {
        strcpy(Context->Mnemonic, THUMB_STRD_MNEMONIC);
    }

    Rn = (Instruction >> THUMB32_DUAL_RN_SHIFT) & THUMB_REGISTER16_MASK;
    Rt = (Instruction >> THUMB32_DUAL_RT_SHIFT) & THUMB_REGISTER16_MASK;
    Rt2 = (Instruction >> THUMB32_DUAL_RT2_SHIFT) & THUMB_REGISTER16_MASK;
    Immediate8 = Instruction & THUMB_IMMEDIATE8_MASK;
    Immediate8 <<= 2;
    strcpy(Context->Operand1, DbgArmRegisterNames[Rt]);
    strcpy(Context->Operand2, DbgArmRegisterNames[Rt2]);
    if ((Instruction & THUMB32_PREINDEX_BIT) != 0) {
        if ((Instruction & THUMB32_WRITE_BACK_BIT) != 0) {
            snprintf(Context->Operand3,
                     sizeof(Context->Operand3),
                     "[%s, #%d]!",
                     DbgArmRegisterNames[Rn],
                     Immediate8);

        } else {
            if (Immediate8 != 0) {
                snprintf(Context->Operand3,
                         sizeof(Context->Operand3),
                         "[%s, #%d]",
                         DbgArmRegisterNames[Rn],
                         Immediate8);

            } else {
                snprintf(Context->Operand3,
                         sizeof(Context->Operand3),
                         "[%s]",
                         DbgArmRegisterNames[Rn]);
            }
        }

    //
    // If pre-index is not set, then update is assumed to be set.
    //

    } else {
        snprintf(Context->Operand3,
                 sizeof(Context->Operand3),
                 "[%s] #%d",
                 DbgArmRegisterNames[Rn],
                 Immediate8);
    }

    return;
}

VOID
DbgpThumb32DecodeLoadStoreExclusiveFunkySize (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit load/store exclusive instructions for
    non-native sizes (8, 16 and 64 bits).

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR Mnemonic;
    ULONG Op;
    ULONG Rd;
    ULONG Rn;
    PSTR RnRegister;
    ULONG Rt;
    ULONG Rt2;

    Instruction = Context->Instruction;
    Rd = (Instruction >> THUMB32_EXCLUSIVE_FUNKY_RD_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rn = (Instruction >> THUMB32_EXCLUSIVE_FUNKY_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rt = (Instruction >> THUMB32_EXCLUSIVE_FUNKY_RT_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rt2 = (Instruction >> THUMB32_EXCLUSIVE_FUNKY_RT2_SHIFT) &
          THUMB_REGISTER16_MASK;

    //
    // Get the mnemonic. Load instructions look like ldr Rt, [Rn]. Store
    // instructions look like str Rd, Rt, [Rn]. Dual instructions stick Rt2
    // after Rt.
    //

    Op = (Instruction >> THUMB32_EXCLUSIVE_FUNKY_OP_SHIFT) &
         THUMB32_EXCLUSIVE_FUNKY_OP_MASK;

    if ((Instruction & THUMB32_LOAD_BIT) != 0) {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rt]);
        RnRegister = &(Context->Operand2[0]);
        if (Op == THUMB32_EXCLUSIVE_FUNKY_OP_BYTE) {
            Mnemonic = THUMB_LDREXB_MNEMONIC;

        } else if (Op == THUMB32_EXCLUSIVE_FUNKY_OP_HALF_WORD) {
            Mnemonic = THUMB_LDREXH_MNEMONIC;

        } else {

            assert(Op == THUMB32_EXCLUSIVE_FUNKY_OP_DUAL);

            Mnemonic = THUMB_LDREXD_MNEMONIC;
            strcpy(Context->Operand2, DbgArmRegisterNames[Rt2]);
            RnRegister = &(Context->Operand3[0]);
        }

    } else {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        strcpy(Context->Operand2, DbgArmRegisterNames[Rt]);
        RnRegister = &(Context->Operand3[0]);
        if (Op == THUMB32_EXCLUSIVE_FUNKY_OP_BYTE) {
            Mnemonic = THUMB_STREXB_MNEMONIC;

        } else if (Op == THUMB32_EXCLUSIVE_FUNKY_OP_HALF_WORD) {
            Mnemonic = THUMB_STREXH_MNEMONIC;

        } else {

            assert(Op == THUMB32_EXCLUSIVE_FUNKY_OP_DUAL);

            Mnemonic = THUMB_STREXD_MNEMONIC;
            strcpy(Context->Operand3, DbgArmRegisterNames[Rt2]);
            RnRegister = &(Context->Operand4[0]);
        }
    }

    strcpy(Context->Mnemonic, Mnemonic);
    sprintf(RnRegister, "[%s]", DbgArmRegisterNames[Rn]);
    return;
}

VOID
DbgpThumb32DecodeTableBranch (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit load/store exclusive instructions for
    non-native sizes (8, 16 and 64 bits).

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG Rm;
    ULONG Rn;

    Instruction = Context->Instruction;
    Rm = (Instruction >> THUMB32_TABLE_BRANCH_RM_SHIFT) & THUMB_REGISTER16_MASK;
    Rn = (Instruction >> THUMB32_TABLE_BRANCH_RN_SHIFT) & THUMB_REGISTER16_MASK;
    if ((Instruction & THUMB32_TABLE_BRANCH_HALF_WORD) != 0) {
        strcpy(Context->Mnemonic, THUMB_TBH_MNEMONIC);
        snprintf(Context->Operand1,
                 sizeof(Context->Operand1),
                 "[%s, %s, lsl #1]",
                 DbgArmRegisterNames[Rn],
                 DbgArmRegisterNames[Rm]);

    } else {
        strcpy(Context->Mnemonic, THUMB_TBB_MNEMONIC);
        snprintf(Context->Operand1,
                 sizeof(Context->Operand1),
                 "[%s, %s]",
                 DbgArmRegisterNames[Rn],
                 DbgArmRegisterNames[Rm]);

    }

    return;
}

VOID
DbgpThumb32DecodeDataProcessingShiftedRegister (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the 32-bit data processing (shifted register)
    instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate5;
    ULONG Instruction;
    PSTR Mnemonic;
    ULONG Op;
    ULONG Rd;
    ULONG Rm;
    ULONG Rn;
    ULONG SetFlags;
    BOOL StandardParameters;
    ULONG Type;

    Instruction = Context->Instruction;
    Rd = (Instruction >> THUMB32_DATA_SHIFTED_REGISTER_RD_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rm = (Instruction >> THUMB32_DATA_SHIFTED_REGISTER_RM_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rn = (Instruction >> THUMB32_DATA_SHIFTED_REGISTER_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    Type = (Instruction >> THUMB32_DATA_SHIFTED_REGISTER_TYPE_SHIFT) &
           THUMB32_DATA_SHIFTED_REGISTER_TYPE_MASK;

    Immediate5 = (Instruction >>
                  THUMB32_DATA_SHIFTED_REGISTER_IMMEDIATE2_SHIFT) &
                 THUMB32_DATA_SHIFTED_REGISTER_IMMEDIATE2_MASK;

    Immediate5 |= ((Instruction >>
                    THUMB32_DATA_SHIFTED_REGISTER_IMMEDIATE3_SHIFT) &
                   THUMB32_DATA_SHIFTED_REGISTER_IMMEDIATE3_MASK) << 2;

    SetFlags = Instruction & THUMB32_DATA_SET_FLAGS;
    Op = (Instruction >> THUMB32_DATA_SHIFTED_REGISTER_OP_SHIFT) &
         THUMB32_DATA_SHIFTED_REGISTER_OP_MASK;

    StandardParameters = TRUE;
    if (SetFlags != 0) {
        SetFlags = 1;
    }

    //
    // This decoding follows a standard pattern, but there are several
    // exceptions that kick in when 1111 is specified for one of the registers.
    // The exceptions are listed below in this switch statement.
    //

    Mnemonic = DbgThumb32DataProcessingMnemonics[SetFlags][Op];
    switch (Op) {
    case THUMB32_DATA_AND:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_TST_W_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            DbgpThumbDecodeImmediateShift(Context->Operand2,
                                          sizeof(Context->Operand2),
                                          Rm,
                                          Type,
                                          Immediate5);
        }

        break;

    case THUMB32_DATA_ORR:
        if (Rn == 0xF) {
            StandardParameters = FALSE;
            Mnemonic = DbgThumb32DataProcessingShiftMnemonics[SetFlags][Type];
            switch (Type) {
            case THUMB_SHIFT_TYPE_LSL:
                if (Immediate5 == 0) {
                    Mnemonic = DbgThumb32MovMnemonics[SetFlags];
                }

                break;

            case THUMB_SHIFT_TYPE_ROR:
                if (Immediate5 == 0) {
                    Type += 1;
                    Mnemonic =
                        DbgThumb32DataProcessingShiftMnemonics[SetFlags][Type];

                }

                break;

            default:
                break;
            }

            strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
            strcpy(Context->Operand2, DbgArmRegisterNames[Rm]);
            if (Immediate5 != 0) {
                snprintf(Context->Operand3,
                         sizeof(Context->Operand3),
                         "#%d",
                         Immediate5);
            }
        }

        break;

    case THUMB32_DATA_ORN:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = DbgThumb32MvnwMnemonics[SetFlags];
            strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
            DbgpThumbDecodeImmediateShift(Context->Operand2,
                                          sizeof(Context->Operand2),
                                          Rm,
                                          Type,
                                          Immediate5);
        }

        break;

    case THUMB32_DATA_EOR:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_TEQ_W_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            DbgpThumbDecodeImmediateShift(Context->Operand2,
                                          sizeof(Context->Operand2),
                                          Rm,
                                          Type,
                                          Immediate5);
        }

        break;

    case THUMB32_DATA_PKH:
        Type &= ~0x1;
        Mnemonic = THUMB_PKHBT_MNEMONIC;
        if ((Instruction & THUMB32_PACK_HALF_WORD_TB) != 0) {
            Mnemonic = THUMB_PKHTB_MNEMONIC;
        }

        break;

    case THUMB32_DATA_ADD:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_CMN_W_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            DbgpThumbDecodeImmediateShift(Context->Operand2,
                                          sizeof(Context->Operand2),
                                          Rm,
                                          Type,
                                          Immediate5);
        }

        break;

    case THUMB32_DATA_SUB:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_CMP_W_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            DbgpThumbDecodeImmediateShift(Context->Operand2,
                                          sizeof(Context->Operand2),
                                          Rm,
                                          Type,
                                          Immediate5);
        }

        break;

    default:
        break;
    }

    strcpy(Context->Mnemonic, Mnemonic);

    //
    // If the switch statement didn't apply, copy in the regular parameters.
    // The pack half-word is a special case, it changed the opcode but still
    // follows the standard parameters.
    //

    if (StandardParameters != FALSE) {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
        DbgpThumbDecodeImmediateShift(Context->Operand3,
                                      sizeof(Context->Operand3),
                                      Rm,
                                      Type,
                                      Immediate5);
    }

    return;
}

VOID
DbgpThumb32DecodeCoprocessorSimdFloatingPoint (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes coprocessor, advanced SIMD, and floating point
    instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    THUMB_DECODE_WITH_TABLE(Context,
                            DbgThumb32CoprocessorSimdFloatingPointTable);

    return;
}

VOID
DbgpThumb32DecodeUndefined (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine catches undefined corners of the instruction space.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    strcpy(Context->Mnemonic, "Undefined");
    return;
}

VOID
DbgpThumb32DecodeSimdDataProcessing (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes SIMD data processing instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;

    //
    // The 32-bit Thumb instruction and the ARM instruction only differ by one
    // bit. Move the bit in ths 32-bit Thumb instruction and use the ARM
    // decoder.
    //

    Instruction = Context->Instruction;
    if ((Instruction & THUMB32_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
        Context->Instruction |= ARM_SIMD_DATA_PROCESSING_UNSIGNED;

    } else {
        Context->Instruction &= ~ARM_SIMD_DATA_PROCESSING_UNSIGNED;
    }

    DbgpArmDecodeSimdDataProcessing(Context);
    Context->Instruction = Instruction;
    return;
}

VOID
DbgpThumb32DecodeDataModifiedImmediate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes data processing (modified immediate) instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate12;
    ULONG Instruction;
    PSTR Mnemonic;
    ULONG ModifiedImmediate;
    ULONG Op;
    ULONG Rd;
    ULONG Rn;
    ULONG SetFlags;
    BOOL StandardParameters;

    Instruction = Context->Instruction;
    Immediate12 = (Instruction >>
                   THUMB32_DATA_MODIFIED_IMMEDIATE_IMMEDIATE8_SHIFT) &
                  THUMB_IMMEDIATE8_MASK;

    Immediate12 |= ((Instruction >>
                     THUMB32_DATA_MODIFIED_IMMEDIATE_IMMEDIATE3_SHIFT) &
                    THUMB_IMMEDIATE3_MASK) << 8;

    if ((Instruction & THUMB32_DATA_MODIFIED_IMMEDIATE_IMMEDIATE12) != 0) {
        Immediate12 |= 1 << 11;
    }

    Rd = (Instruction >> THUMB32_DATA_MODIFIED_IMMEDIATE_RD_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rn = (Instruction >> THUMB32_DATA_MODIFIED_IMMEDIATE_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    SetFlags = 0;
    if ((Instruction & THUMB32_DATA_SET_FLAGS) != 0) {
        SetFlags = 1;
    }

    Op = (Instruction >> THUMB32_DATA_MODIFIED_IMMEDIATE_OP_SHIFT) &
         THUMB32_DATA_MODIFIED_IMMEDIATE_OP_MASK;

    ModifiedImmediate = DbgpThumb32DecodeModifiedImmediate(Immediate12);
    StandardParameters = TRUE;
    Mnemonic = DbgThumb32DataProcessingMnemonics[SetFlags][Op];
    switch (Op) {
    case THUMB32_DATA_AND:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_TST_W_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "#%d",
                     ModifiedImmediate);
        }

        break;

    case THUMB32_DATA_ORR:
        if (Rn == 0xF) {
            StandardParameters = FALSE;
            Mnemonic = DbgThumb32MovMnemonics[SetFlags];
            strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "#%d",
                     ModifiedImmediate);
        }

        break;

    case THUMB32_DATA_ORN:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = DbgThumb32MvnwMnemonics[SetFlags];
            strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "#%d",
                     ModifiedImmediate);
        }

        break;

    case THUMB32_DATA_EOR:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_TEQ_W_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "#%d",
                     ModifiedImmediate);
        }

        break;

    case THUMB32_DATA_ADD:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_CMN_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "#%d",
                     ModifiedImmediate);
        }

        break;

    case THUMB32_DATA_SUB:
        if ((Rd == 0xF) && (SetFlags != 0)) {
            StandardParameters = FALSE;
            Mnemonic = THUMB_CMP_W_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "#%d",
                     ModifiedImmediate);
        }

        break;

    default:
        break;
    }

    strcpy(Context->Mnemonic, Mnemonic);

    //
    // If the switch statement didn't apply, copy in the regular parameters.
    // The pack half-word is a special case, it changed the opcode but still
    // follows the standard parameters.
    //

    if (StandardParameters != FALSE) {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
        snprintf(Context->Operand3,
                 sizeof(Context->Operand3),
                 "#%d",
                 ModifiedImmediate);
    }

    return;
}

VOID
DbgpThumb32DecodeDataPlainImmediate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes data processing (plain Jane immediate) instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate;
    ULONG Immediate12;
    ULONG Immediate3;
    ULONG Immediate5;
    ULONG Instruction;
    PSTR LsbString;
    PSTR Mnemonic;
    PSTR *Mnemonics;
    ULONG Op;
    ULONGLONG OperandAddress;
    ULONG Rd;
    ULONG Rn;
    ULONG SetFlags;
    PSTR ShiftMnemonic;
    LONG SignedImmediate;
    ULONG Width;
    PSTR WidthString;

    Instruction = Context->Instruction;
    Rd = (Instruction >> THUMB32_DATA_PLAIN_IMMEDIATE_RD_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rn = (Instruction >> THUMB32_DATA_PLAIN_IMMEDIATE_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    Op = (Instruction >> THUMB32_DATA_PLAIN_IMMEDIATE_OP_SHIFT) &
         THUMB32_DATA_PLAIN_IMMEDIATE_OP_MASK;

    Immediate3 = (Instruction >>
                  THUMB32_DATA_MODIFIED_IMMEDIATE_IMMEDIATE3_SHIFT) &
                 THUMB_IMMEDIATE3_MASK;

    Immediate5 = (Instruction >>
                  THUMB32_DATA_PLAIN_IMMEDIATE_IMMEDIATE2_SHIFT) &
                 THUMB_IMMEDIATE2_MASK;

    Immediate5 |= Immediate3 << 2;
    Immediate12 = (Instruction >>
                   THUMB32_DATA_MODIFIED_IMMEDIATE_IMMEDIATE8_SHIFT) &
                  THUMB_IMMEDIATE8_MASK;

    Immediate12 |= Immediate3 << 8;
    if ((Instruction & THUMB32_DATA_MODIFIED_IMMEDIATE_IMMEDIATE12) != 0) {
        Immediate12 |= 1 << 11;
    }

    SetFlags = 0;
    if ((Instruction & THUMB32_DATA_SET_FLAGS) != 0) {
        SetFlags = 1;
    }

    Mnemonic = "Unknown thumb.";
    switch (Op) {
    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_ADD:
    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_SUB:
        if (Rn == 0xF) {
            Mnemonic = THUMB_ADR_W_MNEMONIC;
            SignedImmediate = Immediate12;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rn]);
            if (Op == THUMB32_DATA_PLAIN_IMMEDIATE_OP_SUB) {
                SignedImmediate = -SignedImmediate;
            }

            //
            // Calculate the operand address. The immediate is relative to the
            // current PC aligned down to a four-byte boundary.
            //

            OperandAddress = Context->InstructionPointer + 4;
            OperandAddress = THUMB_ALIGN_4(OperandAddress);
            OperandAddress += (LONGLONG)SignedImmediate;
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "[0x%08llx]",
                     OperandAddress);

            Context->Result->OperandAddress = OperandAddress;
            Context->Result->AddressIsDestination = FALSE;
            Context->Result->AddressIsValid = TRUE;

        } else {
            Mnemonics = DbgThumb32DataProcessingMnemonics[SetFlags];
            if (Op == THUMB32_DATA_PLAIN_IMMEDIATE_OP_ADD) {
                Mnemonic = Mnemonics[THUMB32_DATA_ADD];

            } else {
                Mnemonic = Mnemonics[THUMB32_DATA_SUB];
            }

            strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
            strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
            snprintf(Context->Operand3,
                     sizeof(Context->Operand3),
                     "#%d",
                     Immediate12);
        }

        break;

    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_MOV:
    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_MOVT:
        if (Op == THUMB32_DATA_PLAIN_IMMEDIATE_OP_MOV) {
            Mnemonic = THUMB_MOVW_MNEMONIC;

        } else {
            Mnemonic = THUMB_MOVT_MNEMONIC;
        }

        Immediate = Immediate12 |
                    (((Instruction >>
                       THUMB32_DATA_PLAIN_IMMEDIATE_IMMEDIATE4_SHIFT) &
                      THUMB_IMMEDIATE4_MASK) << 12);

        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        snprintf(Context->Operand2,
                 sizeof(Context->Operand2),
                 "#%d",
                 Immediate);

        break;

    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_SSAT:
    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_SSAT16:
    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_USAT:
    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_USAT16:
        Immediate = (Instruction >>
                     THUMB32_DATA_PLAIN_IMMEDIATE_SAT_IMMEDIATE_SHIFT);

        if (Immediate5 == 0) {
            Immediate &= THUMB32_DATA_PLAIN_IMMEDIATE_SAT_IMMEDIATE4_MASK;

        } else {
            Immediate &= THUMB32_DATA_PLAIN_IMMEDIATE_SAT_IMMEDIATE5_MASK;
        }

        if ((Instruction & THUMB32_DATA_PLAIN_IMMEDIATE_UNSIGNED) != 0) {
            if (Immediate5 == 0) {
                Mnemonic = THUMB_USAT16_MNEMONIC;

            } else {
                Mnemonic = THUMB_USAT_MNEMONIC;
            }

        } else {
            if (Immediate5 == 0) {
                Mnemonic = THUMB_SSAT16_MNEMONIC;

            } else {
                Mnemonic = THUMB_SSAT_MNEMONIC;
            }

            Immediate += 1;
        }

        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        sprintf(Context->Operand2, "#%d", Immediate);
        strcpy(Context->Operand3, DbgArmRegisterNames[Rn]);
        if (Immediate5 != 0) {
            ShiftMnemonic = ARM_LSL_MNEMONIC;
            if ((Instruction & THUMB32_DATA_PLAIN_IMMEDIATE_SHIFT_RIGHT) != 0) {
                ShiftMnemonic = ARM_ASR_MNEMONIC;
            }

            sprintf(Context->Operand4, "%s #%d", ShiftMnemonic, Immediate5);
        }

        break;

    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_BFIC:
        if (Rn == 0xF) {
            Mnemonic = THUMB_BFC_MNEMONIC;
            LsbString = Context->Operand2;
            WidthString = Context->Operand3;

        } else {
            Mnemonic = THUMB_BFI_MNEMONIC;
            strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
            LsbString = Context->Operand3;
            WidthString = Context->Operand4;
        }

        Width = (Instruction >> THUMB32_DATA_PLAIN_IMMEDIATE_MSB_SHIFT) &
                THUMB32_DATA_PLAIN_IMMEDIATE_MSB_MASK;

        Width = Width + 1 - Immediate5;
        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        sprintf(LsbString, "#%d", Immediate5);
        sprintf(WidthString, "#%d", Width);
        break;

    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_SBFX:
    case THUMB32_DATA_PLAIN_IMMEDIATE_OP_UBFX:
        if ((Instruction & THUMB32_DATA_PLAIN_IMMEDIATE_UNSIGNED) != 0) {
            Mnemonic = THUMB_UBFX_MNEMONIC;

        } else {
            Mnemonic = THUMB_SBFX_MNEMONIC;
        }

        Width = (Instruction >>
                 THUMB32_DATA_PLAIN_IMMEDIATE_WIDTH_MINUS_1_SHIFT) &
                THUMB32_DATA_PLAIN_IMMEDIATE_WIDTH_MINUS_1_MASK;

        Width += 1;
        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
        sprintf(Context->Operand3, "#%d", Immediate5);
        sprintf(Context->Operand4, "#%d", Width);
        break;

    default:
        break;
    }

    strcpy(Context->Mnemonic, Mnemonic);
    return;
}

VOID
DbgpThumb32DecodeBranchAndMiscellaneous (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes branch and miscellaneous instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    THUMB_DECODE_WITH_TABLE(Context, DbgThumb32BranchAndMiscellaneousTable);
    return;
}

VOID
DbgpThumb32DecodeMsr (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes MSR (move to status from ARM) instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG Mask;
    ULONG Mode;
    PSTR Register;
    ULONG Rn;

    Instruction = Context->Instruction;
    Rn = (Instruction >> THUMB32_MSR_RN_SHIFT) & THUMB_REGISTER16_MASK;
    strcpy(Context->Mnemonic, THUMB_MSR_MNEMONIC);
    if ((Instruction & THUMB32_MSR_BANKED_REGISTER) != 0) {
        Mode = (Instruction >> THUMB32_MSR_MODE_SHIFT) & THUMB32_MSR_MODE_MASK;
        if ((Instruction & THUMB32_MSR_MODE4) != 0) {
            Mode |= 1 << 4;
        }

        if ((Instruction & THUMB32_MSR_SPSR) != 0) {
            Mode |= 1 << 5;
        }

        strcpy(Context->Operand1, DbgArmBankedRegisters[Mode]);

    } else {
        Mask = (Instruction >> THUMB32_MSR_MASK_SHIFT) & THUMB32_MSR_MASK_MASK;
        Register = THUMB_CPSR_STRING;
        if ((Instruction & THUMB32_MSR_SPSR) != 0) {
            Register = THUMB_SPSR_STRING;
        }

        strcpy(Context->Operand1, Register);
        strcat(Context->Operand1, "_");
        if ((Mask & THUMB32_MSR_MASK_C) != 0) {
            strcat(Context->Operand1, "c");
        }

        if ((Mask & THUMB32_MSR_MASK_X) != 0) {
            strcat(Context->Operand1, "x");
        }

        if ((Mask & THUMB32_MSR_MASK_S) != 0) {
            strcat(Context->Operand1, "s");
        }

        if ((Mask & THUMB32_MSR_MASK_F) != 0) {
            strcat(Context->Operand1, "f");
        }
    }

    strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
    return;
}

VOID
DbgpThumb32DecodeCpsAndHints (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the CPS (change processor state) instruction, as well
    as memory hints.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG HintOp;
    ULONG Instruction;
    ULONG Mode;
    ULONG Option;

    Instruction = Context->Instruction;

    //
    // If bits 8:6 are zero, then this is CPS.
    //

    if ((Instruction & THUMB32_CPS_MASK) == THUMB32_CPS_VALUE) {
        Mode = Instruction & THUMB32_CPS_MODE_MASK;
        if ((Instruction & THUMB32_CPS_DISABLE) != 0) {
            strcpy(Context->Mnemonic, THUMB_CPS_DISABLE_W_MNEMONIC);

        } else {
            strcpy(Context->Mnemonic, THUMB_CPS_ENABLE_W_MNEMONIC);
        }

        strcpy(Context->Operand1, "");
        if ((Instruction & THUMB32_CPS_FLAG_A) != 0) {
            strcat(Context->Operand1, ARM_CPS_FLAG_A_STRING);
        }

        if ((Instruction & THUMB32_CPS_FLAG_I) != 0) {
            strcat(Context->Operand1, ARM_CPS_FLAG_I_STRING);
        }

        if ((Instruction & THUMB32_CPS_FLAG_F) != 0) {
            strcat(Context->Operand1, ARM_CPS_FLAG_F_STRING);
        }

        if ((Instruction & THUMB32_CPS_CHANGE_MODE) != 0) {
            DbgpArmPrintMode(Context->Operand2, Mode);
        }

    //
    // This is a hint instruction.
    //

    } else {
        HintOp = Instruction & THUMB32_HINT_MASK;
        if ((HintOp & THUMB32_HINT_DBG_MASK) == THUMB32_HINT_DBG_VALUE) {
            Option = Instruction & THUMB32_DBG_OPTION_MASK;
            strcpy(Context->Mnemonic, THUMB_DBG_MNEMONIC);
            snprintf(Context->Operand1,
                     sizeof(Context->Operand1),
                     "#%d",
                     Option);

        } else {
            if (HintOp >= THUMB32_HINT_OP_COUNT) {
                strcpy(Context->Mnemonic, "Undef hint");

            } else {
                strcpy(Context->Mnemonic, DbgThumb32HintMnemonics[HintOp]);
            }
        }
    }

    return;
}

VOID
DbgpThumb32DecodeMiscellaneousControl (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb miscellaneous control instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR Mnemonic;
    ULONG Mode;
    ULONG Op;

    Instruction = Context->Instruction;
    Op = (Instruction >> THUMB32_MISCELLANEOUS_CONTROL_OP_SHIFT) &
         THUMB32_MISCELLANEOUS_CONTROL_OP_MASK;

    Mode = Instruction & THUMB32_BARRIER_MODE_MASK;
    if (Op == THUMB32_MISCELLANEOUS_CONTROL_OP_ENTERX) {
        Mnemonic = THUMB_ENTERX_MNEMONIC;

    } else if (Op == THUMB32_MISCELLANEOUS_CONTROL_OP_LEAVEX) {
        Mnemonic = THUMB_LEAVEX_MNEMONIC;

    } else if (Op == THUMB32_MISCELLANEOUS_CONTROL_OP_CLREX) {
        Mnemonic = THUMB_CLREX_MNEMONIC;

    } else if (Op == THUMB32_MISCELLANEOUS_CONTROL_OP_DSB) {
        Mnemonic = THUMB_DSB_MNEMONIC;
        DbgpArmPrintBarrierMode(Context->Operand1, Mode);

    } else if (Op == THUMB32_MISCELLANEOUS_CONTROL_OP_DMB) {
        Mnemonic = THUMB_DMB_MNEMONIC;
        DbgpArmPrintBarrierMode(Context->Operand1, Mode);

    } else if (Op == THUMB32_MISCELLANEOUS_CONTROL_OP_ISB) {
        Mnemonic = THUMB_ISB_MNEMONIC;
        DbgpArmPrintBarrierMode(Context->Operand1, Mode);

    } else {
        Mnemonic = "Undef Misc control";
    }

    strcpy(Context->Mnemonic, Mnemonic);
    return;
}

VOID
DbgpThumb32DecodeBxj (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb BXJ instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Rm;

    Rm = (Context->Instruction >> THUMB32_BXJ_RM_SHIFT) & THUMB_REGISTER16_MASK;
    strcpy(Context->Mnemonic, THUMB_BXJ_MNEMONIC);
    strcpy(Context->Operand1, DbgArmRegisterNames[Rm]);
    return;
}

VOID
DbgpThumb32DecodeExceptionReturn (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb ERET (exception return) and SUBS pc, lr.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate8;

    Immediate8 = Context->Instruction & THUMB_IMMEDIATE8_MASK;
    if (Immediate8 == 0) {
        strcpy(Context->Mnemonic, THUMB_ERET_MNEMONIC);

    } else {
        strcpy(Context->Mnemonic, THUMB_SUBS_MNEMONIC);
        strcpy(Context->Operand1, DbgArmRegisterNames[15]);
        strcpy(Context->Operand2, DbgArmRegisterNames[13]);
        snprintf(Context->Operand3,
                 sizeof(Context->Operand3),
                 "#%d",
                 Immediate8);
    }

    return;
}

VOID
DbgpThumb32DecodeMrs (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb MRS (Move to ARM from Status register)
    instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG Mode;
    ULONG Rd;
    PSTR Register;

    Instruction = Context->Instruction;
    Rd = (Instruction >> THUMB32_MRS_RD_SHIFT) & THUMB_REGISTER16_MASK;
    strcpy(Context->Mnemonic, THUMB_MRS_MNEMONIC);
    if ((Instruction & THUMB32_MRS_BANKED_REGISTER) != 0) {
        Mode = (Instruction >> THUMB32_MRS_MODE_SHIFT) & THUMB32_MRS_MODE_MASK;
        if ((Instruction & THUMB32_MRS_MODE4) != 0) {
            Mode |= 1 << 4;
        }

        if ((Instruction & THUMB32_MRS_SPSR) != 0) {
            Mode |= 1 << 5;
        }

        strcpy(Context->Operand2, DbgArmBankedRegisters[Mode]);

    } else {
        Register = THUMB_CPSR_STRING;
        if ((Instruction & THUMB32_MRS_SPSR) != 0) {
            Register = THUMB_SPSR_STRING;
        }

        strcpy(Context->Operand2, Register);
    }

    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    return;
}

VOID
DbgpThumb32DecodeHvc (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb HVC (hypervisor call) instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate16;
    ULONG Instruction;

    Instruction = Context->Instruction;
    Immediate16 = (Instruction & THUMB32_HVC_IMMEDIATE12_MASK) |
                  ((Instruction >> THUMB32_HVC_IMMEDIATE4_SHIFT) &
                   THUMB32_HVC_IMMEDIATE4_MASK);

    strcpy(Context->Mnemonic, THUMB_HVC_MNEMONIC);
    snprintf(Context->Operand1,
             sizeof(Context->Operand1),
             "#%d",
             Immediate16);

    return;
}

VOID
DbgpThumb32DecodeSmc (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb SMC (secure monitor call) instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate4;
    ULONG Instruction;

    Instruction = Context->Instruction;
    Immediate4 = (Instruction >> THUMB32_SMC_IMMEDIATE4_SHIFT) &
                  THUMB32_SMC_IMMEDIATE4_MASK;

    strcpy(Context->Mnemonic, THUMB_SMC_MNEMONIC);
    snprintf(Context->Operand1,
             sizeof(Context->Operand1),
             "#%d",
             Immediate4);

    return;
}

VOID
DbgpThumb32DecodeBranch (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb branch (both conditional and
    unconditional) instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Bit;
    ULONG Condition;
    PSTR ConditionString;
    LONG Immediate;
    ULONG Instruction;
    ULONGLONG OperandAddress;
    ULONG SBit;

    Instruction = Context->Instruction;
    Immediate = (Instruction >> THUMB32_B_IMMEDIATE11_SHIFT) &
                 THUMB32_B_IMMEDIATE11_MASK;

    Condition = (Instruction >> THUMB32_B_CONDITION_SHIFT) &
                THUMB32_B_CONDITION_MASK;

    ConditionString = "";

    //
    // Handle an unconditional branch, which has a larger range.
    //

    if ((Instruction & THUMB32_B_UNCONDITIONAL_MASK) ==
        THUMB32_B_UNCONDITIONAL_VALUE) {

        Immediate |= ((Instruction >> THUMB32_B_IMMEDIATE10_SHIFT) &
                      THUMB_IMMEDIATE10_MASK) << 11;

        //
        // The next two bits are NOT(J2 EOR S) and NOT(J1 EOR S).
        //

        SBit = 0;
        if ((Instruction & THUMB32_B_S_BIT) != 0) {
            SBit = 1;
        }

        Bit = 0;
        if ((Instruction & THUMB32_B_J1_BIT) != 0) {
            Bit = 1;
        }

        Bit = !(Bit ^ SBit);
        if (Bit != 0) {
            Immediate |= 1 << 21;
        }

        Bit = 0;
        if ((Instruction & THUMB32_B_J2_BIT) != 0) {
            Bit = 1;
        }

        Bit = !(Bit ^ SBit);
        if (Bit != 0) {
            Immediate |= 1 << 22;
        }

        if (SBit != 0) {
            Immediate |= 1 << 23;
        }

        Immediate <<= 1;

        //
        // Sign extend.
        //

        if ((Immediate & 0x01000000) != 0) {
            Immediate |= 0xFE000000;
        }

    //
    // Conditional branches sacrifice some range for the encoded condition.
    //

    } else {
        ConditionString = DbgArmConditionCodes[Condition];
        Immediate |= ((Instruction >> THUMB32_B_IMMEDIATE6_SHIFT) &
                      THUMB_IMMEDIATE6_MASK) << 11;

        if ((Instruction & THUMB32_B_J1_BIT) != 0) {
            Immediate |= (1 << 17);
        }

        if ((Instruction & THUMB32_B_J2_BIT) != 0) {
            Immediate |= (1 << 18);
        }

        if ((Instruction & THUMB32_B_S_BIT) != 0) {
            Immediate |= (1 << 19);
        }

        Immediate <<= 1;

        //
        // Sign extend.
        //

        if ((Immediate & 0x00100000) != 0) {
            Immediate |= 0xFFE00000;
        }
    }

    snprintf(Context->Mnemonic,
             sizeof(Context->Mnemonic),
             THUMB_B_W_MNEMONIC_FORMAT,
             ConditionString);

    //
    // All of these branches are relative to the PC, which is 4 ahead of the
    // instruction pointer. Calculate the absolute operand address.
    //

    OperandAddress = Context->InstructionPointer + 4;
    OperandAddress += (LONGLONG)Immediate;
    snprintf(Context->Operand1,
             sizeof(Context->Operand1),
             "[0x%08llx]",
             OperandAddress);

    Context->Result->OperandAddress = OperandAddress;
    Context->Result->AddressIsDestination = TRUE;
    Context->Result->AddressIsValid = TRUE;
    return;
}

VOID
DbgpThumb32DecodeUdf (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb undefined instruction (like THE undefined
    instruction).

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate20;
    ULONG Instruction;

    Instruction = Context->Instruction;
    Immediate20 = (Instruction & THUMB_IMMEDIATE12_MASK) |
                  ((Instruction >> THUMB32_UDF_IMMEDIATE4_SHIFT) &
                   THUMB_IMMEDIATE4_MASK);

    strcpy(Context->Mnemonic, THUMB_UDF_W_MNEMONIC);
    snprintf(Context->Operand1,
             sizeof(Context->Operand1),
             "#%d",
             Immediate20);

    return;
}

VOID
DbgpThumb32DecodeBranchWithLink (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb branch with link instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Bit;
    LONG Immediate25;
    ULONG Instruction;
    ULONGLONG OperandAddress;
    ULONG SBit;

    Instruction = Context->Instruction;
    Immediate25 = ((Instruction >> THUMB32_BL_IMMEDIATE11_SHIFT) &
                   THUMB_IMMEDIATE11_MASK) |
                  (((Instruction >> THUMB32_BL_IMMEDIATE10_SHIFT) &
                    THUMB_IMMEDIATE10_MASK) << 11);

    if ((Instruction & THUMB32_BL_X_BIT) == 0) {
        Immediate25 &= ~THUMB32_BL_THUMB_BIT;
    }

    //
    // The next two bits are NOT(J1 EOR S) and NOT(J2 EOR S).
    //

    SBit = 0;
    if ((Instruction & THUMB32_B_S_BIT) != 0) {
        SBit = 1;
    }

    Bit = 0;
    if ((Instruction & THUMB32_B_J2_BIT) != 0) {
        Bit = 1;
    }

    Bit = !(Bit ^ SBit);
    if (Bit != 0) {
        Immediate25 |= 1 << 21;
    }

    Bit = 0;
    if ((Instruction & THUMB32_B_J1_BIT) != 0) {
        Bit = 1;
    }

    Bit = !(Bit ^ SBit);
    if (Bit != 0) {
        Immediate25 |= 1 << 22;
    }

    if (SBit != 0) {
        Immediate25 |= 1 << 23;
    }

    Immediate25 <<= 1;

    //
    // Sign extend.
    //

    if ((Immediate25 & 0x00200000) != 0) {
        Immediate25 |= 0xFFC00000;
    }

    //
    // For the BLX encoding, the immediate is relative to "Align(PC, 4)". The
    // PC is four bytes ahead of the instruction pointer and it is an align
    // down operation. The align-down action also strips the low bit from the
    // Thumb instruction point, resulting in the correct ARM address. This is
    // necessay because the destination mode of BLX is ARM.
    //

    OperandAddress = Context->InstructionPointer + 4;
    if ((Instruction & THUMB32_BL_X_BIT) == 0) {
        strcpy(Context->Mnemonic, THUMB_BLX_MNEMONIC);
        OperandAddress = THUMB_ALIGN_4(OperandAddress);

    //
    // BL is relative to the PC.
    //

    } else {
        strcpy(Context->Mnemonic, THUMB_BL_MNEMONIC);
    }

    OperandAddress += (LONGLONG)Immediate25;
    snprintf(Context->Operand1,
             sizeof(Context->Operand1),
             "[0x%08llx]",
             OperandAddress);

    Context->Result->OperandAddress = OperandAddress;
    Context->Result->AddressIsDestination = TRUE;
    Context->Result->AddressIsValid = TRUE;
    return;
}

VOID
DbgpThumb32DecodeLoadStoreSingleItem (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb load/store instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;

    Instruction = Context->Instruction;
    if ((Instruction & THUMB32_LOAD_STORE_REGISTER_MASK) ==
         THUMB32_LOAD_STORE_REGISTER_VALUE) {

        DbgpThumb32DecodeLoadStoreRegister(Context);

    } else {
        DbgpThumb32DecodeLoadStoreImmediate(Context);
    }

    return;
}

VOID
DbgpThumb32DecodeLoadStoreImmediate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb load/store immediate instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    LONG Immediate;
    ULONG Instruction;
    ULONG Load;
    ULONG Op;
    ULONGLONG OperandAddress;
    ULONG Rn;
    ULONG Rt;

    Instruction = Context->Instruction;
    Op = (Instruction >> THUMB32_LOAD_STORE_OP_SHIFT) &
         THUMB32_LOAD_STORE_OP_MASK;

    Rn = (Instruction >> THUMB32_LOAD_STORE_IMMEDIATE_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rt = (Instruction >> THUMB32_LOAD_STORE_IMMEDIATE_RT_SHIFT) &
         THUMB_REGISTER16_MASK;

    Load = 0;
    if ((Instruction & THUMB32_LOAD_BIT) != 0) {
        Load = 1;
    }

    //
    // Assume the mnemonic is not unprivileged. This may get altered later.
    //

    if ((Load != 0) && ((Instruction & THUMB32_LOAD_SET_FLAGS) != 0)) {
        strcpy(Context->Mnemonic, DbgThumb32LoadSetFlagsMnemonics[Op]);

    } else {
        strcpy(Context->Mnemonic,
               DbgThumb32LoadStoreMnemonics[Load][Op]);
    }

    //
    // If bit 23 is set, then the pre-index is an immediate12.
    //

    if ((Instruction & THUMB32_LOAD_STORE_IMMEDIATE_LARGE) != 0) {
        Immediate = Instruction & THUMB_IMMEDIATE12_MASK;
        snprintf(Context->Operand2,
                 sizeof(Context->Operand2),
                 "[%s, #%d]",
                 DbgArmRegisterNames[Rn],
                 Immediate);

    //
    // There are a few addressing modes, and an immediate8.
    //

    } else {
        Immediate = Instruction & THUMB_IMMEDIATE8_MASK;
        if ((Instruction & THUMB32_LOAD_STORE_IMMEDIATE_ADD) == 0) {
            Immediate = -Immediate;
        }

        if ((Instruction & THUMB32_LOAD_STORE_IMMEDIATE_PREINDEX) != 0) {
            if ((Instruction & THUMB32_LOAD_STORE_IMMEDIATE_WRITE_BACK) != 0) {
                snprintf(Context->Operand2,
                         sizeof(Context->Operand2),
                         "[%s, #%d]!",
                         DbgArmRegisterNames[Rn],
                         Immediate);

            } else {
                snprintf(Context->Operand2,
                         sizeof(Context->Operand2),
                         "[%s, #%d]",
                         DbgArmRegisterNames[Rn],
                         Immediate);
            }

        } else {
            snprintf(Context->Operand2,
                     sizeof(Context->Operand2),
                     "[%s], #%d",
                     DbgArmRegisterNames[Rn],
                     Immediate);
        }

        //
        // It's an unprivileged instruction if both the P (preindex) and U (add)
        // bits are set.
        //

        if (((Instruction & THUMB32_LOAD_STORE_IMMEDIATE_PREINDEX) != 0) &&
            ((Instruction & THUMB32_LOAD_STORE_IMMEDIATE_ADD) != 0)) {

            if ((Load != 0) && ((Instruction & THUMB32_LOAD_SET_FLAGS) != 0)) {
                strcpy(Context->Mnemonic,
                       DbgThumb32LoadSetFlagsUnprivilegedMnemonics[Op]);

            } else {
                strcpy(Context->Mnemonic,
                       DbgThumb32LoadStoreUnprivilegedMnemonics[Load][Op]);
            }
        }
    }

    //
    // If this is a load relative to the PC, then calculate the absolute
    // operand address and override the second operand with the absolute
    // address.
    //

    if ((Load != 0) && (Rn == 15)) {

        //
        // The address is relative to the PC aligned down to a 4-byte boundary.
        //

        OperandAddress = Context->InstructionPointer + 4;
        OperandAddress = THUMB_ALIGN_4(OperandAddress);
        OperandAddress += Immediate;
        Context->Result->OperandAddress = OperandAddress;
        Context->Result->AddressIsDestination = FALSE;
        Context->Result->AddressIsValid = TRUE;
        snprintf(Context->Operand2,
                 sizeof(Context->Operand2),
                 "[0x%08llx]",
                 OperandAddress);
    }

    //
    // If Rt is 15, then this is actually a preload operation. Copy the second
    // operand to the first.
    //

    if (Rt == 15) {
        strcpy(Context->Mnemonic, DbgThumb32PreloadMnemonics[Op]);
        strcpy(Context->Operand1, Context->Operand2);
        Context->Operand2[0] = '\0';

    } else {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rt]);
    }

    return;
}

VOID
DbgpThumb32DecodeLoadStoreRegister (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb load/store register instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate2;
    ULONG Instruction;
    ULONG Load;
    ULONG Op;
    ULONG Rm;
    ULONG Rn;
    ULONG Rt;

    Instruction = Context->Instruction;
    Op = (Instruction >> THUMB32_LOAD_STORE_OP_SHIFT) &
         THUMB32_LOAD_STORE_OP_MASK;

    Rm = (Instruction >> THUMB32_LOAD_STORE_REGISTER_RM_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rn = (Instruction >> THUMB32_LOAD_STORE_REGISTER_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rt = (Instruction >> THUMB32_LOAD_STORE_REGISTER_RT_SHIFT) &
         THUMB_REGISTER16_MASK;

    Immediate2 = (Instruction >> THUMB32_LOAD_STORE_REGISTER_IMMEDIATE2_SHIFT) &
                 THUMB_IMMEDIATE2_MASK;

    Load = 0;
    if ((Instruction & THUMB32_LOAD_BIT) != 0) {
        Load = 1;
    }

    if ((Load != 0) && ((Instruction & THUMB32_LOAD_SET_FLAGS) != 0)) {
        strcpy(Context->Mnemonic, DbgThumb32LoadSetFlagsMnemonics[Op]);

    } else {
        strcpy(Context->Mnemonic, DbgThumb32LoadStoreMnemonics[Load][Op]);
    }

    if (Immediate2 == 0) {
        snprintf(Context->Operand2,
                 sizeof(Context->Operand2),
                 "[%s, %s]",
                 DbgArmRegisterNames[Rn],
                 DbgArmRegisterNames[Rm]);

    } else {
        snprintf(Context->Operand2,
                 sizeof(Context->Operand2),
                 "[%s, %s, %s #%d]",
                 DbgArmRegisterNames[Rn],
                 THUMB_SHIFT_TYPE_LSL_STRING,
                 DbgArmRegisterNames[Rm],
                 Immediate2);
    }

    //
    // If Rt is 15, then this is actually a preload operation. Copy the second
    // operand to the first.
    //

    if (Rt == 15) {
        strcpy(Context->Mnemonic, DbgThumb32PreloadMnemonics[Op]);
        strcpy(Context->Operand1, Context->Operand2);
        strcpy(Context->Operand2, "");

    } else {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rt]);
    }

    return;
}

VOID
DbgpThumb32DecodeDataProcessingRegister (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb data processing (register) instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG MiscellaneousOp;
    ULONG Op1;
    ULONG ParallelOp;
    ULONG Rd;
    ULONG Rm;
    ULONG Rn;
    ULONG Rotate;
    ULONG SetFlags;
    ULONG Unsigned;

    Instruction = Context->Instruction;
    SetFlags = 0;
    if ((Instruction & THUMB32_DATA_SET_FLAGS) != 0) {
        SetFlags = 1;
    }

    Op1 = (Instruction >> THUMB32_DATA_PROCESSING_REGISTER_OP1_SHIFT) &
          THUMB32_DATA_PROCESSING_REGISTER_OP1_MASK;

    Rd = (Instruction >> THUMB32_DATA_PROCESSING_REGISTER_RD_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rm = (Instruction >> THUMB32_DATA_PROCESSING_REGISTER_RM_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rn = (Instruction >> THUMB32_DATA_PROCESSING_REGISTER_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);

    //
    // Handle shift/rotate instructions.
    //

    if ((Instruction & THUMB32_DATA_PROCESSING_REGISTER_SHIFT_MASK) ==
        THUMB32_DATA_PROCESSING_REGISTER_SHIFT_VALUE) {

        strcpy(Context->Mnemonic,
               DbgThumb32DataProcessingShiftMnemonics[SetFlags][Op1 >> 1]);

        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
        strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);

    //
    // Handle signed and unsigned extend and add.
    //

    } else if ((Op1 & THUMB32_DATA_PROCESSING_REGISTER_OP1_EXTEND) == 0) {
        Rotate = (Instruction >>
                  THUMB32_DATA_PROCESSING_REGISTER_ROTATE_SHIFT) &
                 THUMB32_DATA_PROCESSING_REGISTER_ROTATE_MASK;

        Rotate <<= 3;
        if (Op1 < THUMB32_DATA_PROCESSING_REGISTER_OP1_EXTEND_COUNT) {
            if (Rn == 15) {
                strcpy(Context->Mnemonic,
                       DbgThumb32ExtendAndAddMnemonics[1][Op1]);

                strcpy(Context->Operand2, DbgArmRegisterNames[Rm]);
                if (Rotate != 0) {
                    snprintf(Context->Operand3,
                             sizeof(Context->Operand4),
                             "ror #%d",
                             Rotate);
                }

            } else {
                strcpy(Context->Mnemonic,
                       DbgThumb32ExtendAndAddMnemonics[0][Op1]);

                strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
                strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
                if (Rotate != 0) {
                    snprintf(Context->Operand4,
                             sizeof(Context->Operand4),
                             "ror #%d",
                             Rotate);
                }
            }
        }

    //
    // Handle parallel addition and subtraction, both signed and unsigned.
    //

    } else if ((Instruction & THUMB32_DATA_PROCESSING_REGISTER_PARALLEL) == 0) {
        Unsigned = 0;
        if ((Instruction & THUMB32_DATA_PROCESSING_REGISTER_UNSIGNED) != 0) {
            Unsigned = 1;
        }

        ParallelOp = (Instruction >>
                      THUMB32_DATA_PROCESSING_PARALLEL_OP1_SHIFT) &
                     THUMB32_DATA_PROCESSING_PARALLEL_OP1_MASK;

        ParallelOp |= ((Instruction >>
                        THUMB32_DATA_PROCESSING_PARALLEL_OP2_SHIFT) &
                       THUMB32_DATA_PROCESSING_PARALLEL_OP2_MASK) << 3;

        if (ParallelOp < THUMB32_DATA_PROCESSING_PARALLEL_OP_COUNT) {
            strcpy(Context->Mnemonic,
                   DbgThumb32ParallelArithmeticMnemonics[Unsigned][ParallelOp]);
        }

        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
        strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);

    //
    // Handle miscellaneous instructions.
    //

    } else {
        MiscellaneousOp = (Instruction >>
                           THUMB32_DATA_PROCESSING_MISCELLANEOUS_OP2_SHIFT) &
                          THUMB32_DATA_PROCESSING_MISCELLANEOUS_OP2_MASK;

        MiscellaneousOp |= ((Instruction >>
                             THUMB32_DATA_PROCESSING_MISCELLANEOUS_OP1_SHIFT) &
                            THUMB32_DATA_PROCESSING_MISCELLANEOUS_OP1_MASK) <<
                           2;

        strcpy(Context->Mnemonic,
               DbgThumb32DataProcessingMiscellaneousMnemonics[MiscellaneousOp]);

        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
        if (Rn != Rm) {
            strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
        }
    }

    return;
}

VOID
DbgpThumb32DecodeMultiplyAccumulate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb multiply and multiply/accumulate
    instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG Op1;
    ULONG Op2;
    ULONG Ra;
    ULONG Rd;
    ULONG Rm;
    ULONG Rn;
    ULONG Top;

    Instruction = Context->Instruction;
    Ra = (Instruction >> THUMB32_MULTIPLY_RA_SHIFT) & THUMB_REGISTER16_MASK;
    Rd = (Instruction >> THUMB32_MULTIPLY_RD_SHIFT) & THUMB_REGISTER16_MASK;
    Rm = (Instruction >> THUMB32_MULTIPLY_RN_SHIFT) & THUMB_REGISTER16_MASK;
    Rn = (Instruction >> THUMB32_MULTIPLY_RM_SHIFT) & THUMB_REGISTER16_MASK;
    Op1 = (Instruction >> THUMB32_MULTIPLY_OP1_SHIFT) &
          THUMB32_MULTIPLY_OP1_MASK;

    Op2 = (Instruction >> THUMB32_MULTIPLY_OP2_SHIFT) &
           THUMB32_MULTIPLY_OP2_MASK;

    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
    strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    if (Ra != 15) {
        strcpy(Context->Mnemonic, DbgThumb32MultiplyMnemonics[0][Op1]);
        strcpy(Context->Operand4, DbgArmRegisterNames[Ra]);

    } else {
        strcpy(Context->Mnemonic, DbgThumb32MultiplyMnemonics[1][Op1]);
    }

    if ((Op1 == THUMB32_MULTIPLY_OP1_MLS) &&
        (Op2 == THUMB32_MULTIPLY_OP2_MLS)) {

        strcpy(Context->Mnemonic, THUMB_MLS_MNEMONIC);
    }

    //
    // Instructions that operate on only the top or bottom half of some
    // registers (Rn and maybe Rm) get endings for top or bottom.
    //

    if (Op1 == THUMB32_MULTIPLY_OP1_HALF_HALF) {
        Top = 0;
        if ((Instruction & THUMB32_MULTIPLY_RN_TOP) != 0) {
            Top = 1;
        }

        strcat(Context->Mnemonic, DbgThumb32MultiplyTopBottomMnemonics[Top]);
    }

    if ((Op1 == THUMB32_MULTIPLY_OP1_HALF_HALF) ||
        (Op1 == THUMB32_MULTIPLY_OP1_WORD_HALF)) {

        Top = 0;
        if ((Instruction & THUMB32_MULTIPLY_RM_TOP) != 0) {
            Top = 1;
        }

        strcat(Context->Mnemonic, DbgThumb32MultiplyTopBottomMnemonics[Top]);
    }

    //
    // A couple of instructions have an optional X or R tagged on the end.
    //

    if ((Op1 == THUMB32_MULTIPLY_OP1_SMAD) ||
        (Op1 == THUMB32_MULTIPLY_OP1_SMSD)) {

        if ((Instruction & THUMB32_MULTIPLY_DUAL_CROSS) != 0) {
            strcat(Context->Mnemonic, THUMB_MULTIPLY_CROSS_MNEMONIC);
        }

    } else if (Op1 == THUMB32_MULTIPLY_OP1_SMML) {
        if ((Instruction & THUMB32_MULTIPLY_ROUND) != 0) {
            strcat(Context->Mnemonic, THUMB_MULTIPLY_ROUND_MNEMONIC);
        }
    }

    return;
}

VOID
DbgpThumb32DecodeLongMultiplyDivide (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes 32-bit Thumb long multiply and divide instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Cross;
    ULONG Instruction;
    ULONG Op1;
    ULONG Op2;
    ULONG RdHigh;
    ULONG RdLow;
    ULONG Rm;
    ULONG Rn;
    ULONG Top;

    Instruction = Context->Instruction;
    RdHigh = (Instruction >> THUMB32_LONG_MULTIPLY_RD_HIGH_SHIFT) &
             THUMB_REGISTER16_MASK;

    RdLow = (Instruction >> THUMB32_LONG_MULTIPLY_RD_LOW_SHIFT) &
             THUMB_REGISTER16_MASK;

    Rm = (Instruction >> THUMB32_LONG_MULTIPLY_RM_SHIFT) &
         THUMB_REGISTER16_MASK;

    Rn = (Instruction >> THUMB32_LONG_MULTIPLY_RN_SHIFT) &
         THUMB_REGISTER16_MASK;

    Op1 = (Instruction >> THUMB32_LONG_MULTIPLY_OP1_SHIFT) &
          THUMB32_LONG_MULTIPLY_OP1_MASK;

    Op2 = (Instruction >> THUMB32_LONG_MULTIPLY_OP2_SHIFT) &
          THUMB32_LONG_MULTIPLY_OP2_MASK;

    Cross = 0;
    strcpy(Context->Mnemonic, DbgThumb32LongMultiplyMnemonics[Op1]);
    if (Op1 == THUMB32_LONG_MULTIPLY_OP1_SMLA) {
        if ((Op2 & THUMB32_LONG_MULTIPLY_OP2_SMLA_HALF_MASK) ==
            THUMB32_LONG_MULTIPLY_OP2_SMLA_HALF_VALUE) {

            Top = 0;
            if ((Instruction & THUMB32_MULTIPLY_RN_TOP) != 0) {
                Top = 1;
            }

            strcat(Context->Mnemonic,
                   DbgThumb32MultiplyTopBottomMnemonics[Top]);

            Top = 0;
            if ((Instruction & THUMB32_MULTIPLY_RM_TOP) != 0) {
                Top = 1;
            }

            strcat(Context->Mnemonic,
                   DbgThumb32MultiplyTopBottomMnemonics[Top]);

        } else if ((Op2 & THUMB32_LONG_MULTIPLY_OP2_SMLALD_MASK) ==
                   THUMB32_LONG_MULTIPLY_OP2_SMLALD_VALUE) {

            strcpy(Context->Mnemonic, THUMB_SMLALD_MNEMONIC);
            Cross = Instruction & THUMB32_MULTIPLY_DUAL_CROSS;
        }

    } else if (Op1 == THUMB32_LONG_MULTIPLY_OP1_SMLSLD) {
        Cross = Instruction & THUMB32_MULTIPLY_DUAL_CROSS;
    }

    if (Cross != 0) {
        strcat(Context->Mnemonic, THUMB_MULTIPLY_CROSS_MNEMONIC);
    }

    strcpy(Context->Operand1, DbgArmRegisterNames[RdHigh]);
    if (RdLow != 15) {
        strcpy(Context->Operand2, DbgArmRegisterNames[RdLow]);
        strcpy(Context->Operand3, DbgArmRegisterNames[Rn]);
        strcpy(Context->Operand4, DbgArmRegisterNames[Rm]);

    } else {
        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
        strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    }

    return;
}

VOID
DbgpThumbDecodeImmediateShift (
    PSTR Destination,
    ULONG DestinationSize,
    ULONG Register,
    ULONG Type,
    ULONG Immediate
    )

/*++

Routine Description:

    This routine performs the operation known in the ARM ARM as
    DecodeImmShift().

Arguments:

    Destination - Supplies the destination to write to.

    DestinationSize - Supplies the size of the destination.

    Register - Supplies the base register.

    Type - Supplies the shift type.

    Immediate - Supplies the shift value.

Return Value:

    None.

--*/

{

    PSTR ShiftTypeString;

    switch (Type) {
    case THUMB_SHIFT_TYPE_LSL:
        if (Immediate == 0) {
            snprintf(Destination,
                     DestinationSize,
                     "%s",
                     DbgArmRegisterNames[Register]);

        } else {
            snprintf(Destination,
                     DestinationSize,
                     "%s, %s #%d",
                     DbgArmRegisterNames[Register],
                     THUMB_SHIFT_TYPE_LSL_STRING,
                     Immediate);
        }

        break;

    case THUMB_SHIFT_TYPE_LSR:
        if (Immediate == 0) {
            Immediate = 32;
        }

        snprintf(Destination,
                 DestinationSize,
                 "%s, %s #%d",
                 DbgArmRegisterNames[Register],
                 THUMB_SHIFT_TYPE_LSR_STRING,
                 Immediate);

        break;

    case THUMB_SHIFT_TYPE_ASR:
        if (Immediate == 0) {
            Immediate = 32;
        }

        snprintf(Destination,
                 DestinationSize,
                 "%s, %s #%d",
                 DbgArmRegisterNames[Register],
                 THUMB_SHIFT_TYPE_ASR_STRING,
                 Immediate);

        break;

    case THUMB_SHIFT_TYPE_ROR:
    default:
        ShiftTypeString = THUMB_SHIFT_TYPE_ROR_STRING;
        if (Immediate == 0) {
            Immediate = 1;
            ShiftTypeString = THUMB_SHIFT_TYPE_RRX_STRING;
        }

        snprintf(Destination,
                 DestinationSize,
                 "%s, %s #%d",
                 DbgArmRegisterNames[Register],
                 ShiftTypeString,
                 Immediate);

        break;
    }

    return;
}

ULONG
DbgpThumb32DecodeModifiedImmediate (
    ULONG Immediate12
    )

/*++

Routine Description:

    This routine performs the operation known in the ARM ARM as
    ThumbExpandImm(), expanding a modified immediate.

Arguments:

    Immediate12 - Supplies the 12 bit immediate.

Return Value:

    Returns the expanded immediate.

--*/

{

    ULONG Result;
    ULONG RotateCount;

    if ((Immediate12 & THUMB32_MODIFIED_IMMEDIATE_OP_MASK) ==
        THUMB32_MODIFIED_IMMEDIATE_OP_NO_ROTATE) {

        Result = Immediate12 & THUMB_IMMEDIATE8_MASK;
        switch ((Immediate12 >> 8) & 0x3) {

        //
        // 00000000 00000000 00000000 abcdefgh
        //

        case 0x0:
            break;

        //
        // 00000000 abcdefgh 00000000 abcdefgh
        //

        case 0x1:
            Result |= Result << 16;
            break;

        //
        // abcdefgh 00000000 abcdefgh 00000000
        //

        case 0x2:
            Result |= Result << 16;
            Result <<= 8;
            break;

        //
        // abcdefgh abcdefgh abcdefgh abcdefgh
        //

        case 0x3:
            Result |= Result << 16;
            Result |= Result << 8;
            break;

        default:
            break;
        }

    //
    // Rotate bits 6:0 (with a 1 tacked on the MSB) by the amount specified in
    // bits 7-11.
    //

    } else {
        Result = (Immediate12 & THUMB32_MODIFIED_IMMEDIATE_CONSTANT_MASK) |
                 THUMB32_MODIFIED_IMMEDIATE_EXTRA_ONE;

        RotateCount = (Immediate12 >> THUMB32_MODIFIED_IMMEDIATE_ROTATE_SHIFT) &
                      THUMB32_MODIFIED_IMMEDIATE_ROTATE_MASK;

        //
        // Perform the rotate.
        //

        Result = (Result >> RotateCount) | (Result << (32 - RotateCount));
    }

    return Result;
}

