/*++

Copyright (c) 2014 Minoca Corp. All Rights Reserved

Module Name:

    armdis.c

Abstract:

    This module implements support for the ARM disassembler.

Author:

    Evan Green 20-Mar-2014

Environment:

    Debug

--*/

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

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

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

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

//
// Define the bitmask of vector list printing flags.
//

#define DBG_ARM_VECTOR_LIST_FLAG_ALL_LANES 0x00000001
#define DBG_ARM_VECTOR_LIST_FLAG_INDEX     0x00000002

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

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

VOID
DbgpArmDecodeUnconditional (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeDataProcessingAndMiscellaneous (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeMediaInstruction (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeBranchAndBlockTransfer (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeCoprocessorSupervisor (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeMemoryHintSimdMisc (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeStoreReturnState (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeReturnFromException (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeBranch (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeUndefined (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeUnpredictable (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeNop (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeChangeProcessorState (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSetEndianness (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodePreloadInstruction (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeClearExclusive (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeDataSynchronizationBarrier (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeDataMemoryBarrier (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeInstructionSynchronizationBarrier (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeParallelAdditionSubtraction (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodePackingInstructions (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeExtensionWithRotation (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSelectBytes (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodePackHalfword (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeReverse (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSaturate (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSumofAbsoluteDifferences (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeBitFieldInstructions (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodePermanentlyUndefined (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeLoadStore (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeExtraLoadStore (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeDataProcessing (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeLoadImmediate (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeMiscellaneous (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeMsrImmediateAndHints (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeMultiply (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSynchronization (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSupervisorCall (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeFloatingPointTwoRegisters (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeFloatingPointThreeRegisters (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeFloatingPointVectorConvert (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeFloatingPointVectorCompare (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdSmallMove (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdSpecialMove (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdDuplicate (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdLoadStoreRegister (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdElementLoadAllLanes (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdElementLoadStoreSingle (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdElementLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdThreeRegistersSameLength (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdOneRegister (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdTwoRegistersWithShift (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdThreeRegistersDifferentLength (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdTwoRegistersWithScalar (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdTwoRegistersMiscellaneous (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdVectorExtract (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdVectorTableLookup (
    PARM_DISASSEMBLY Context
    );

VOID
DbgpArmDecodeSimdVectorDuplicate (
    PARM_DISASSEMBLY Context
    );

PSTR
DbgpArmGetLoadStoreTypeString (
    ULONG Instruction
    );

PSTR
DbgpArmGetBankedRegisterString (
    ULONG Instruction
    );

VOID
DbgpArmPrintStatusRegister (
    PSTR Operand,
    ULONG Instruction
    );

VOID
DbgpArmPrintVectorList (
    PSTR Destination,
    ULONG DestinationSize,
    ULONG VectorStart,
    ULONG VectorCount,
    ULONG VectorIncrement,
    PSTR VectorTypeString,
    ULONG VectorIndex,
    ULONG Flags
    );

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

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

//
// Define decode tables.
//

ARM_DECODE_BRANCH DbgArmTopLevelTable[] = {
    {0xF0000000, 0xF0000000, 0, DbgpArmDecodeUnconditional},
    {0x0C000000, 0x00000000, 0, DbgpArmDecodeDataProcessingAndMiscellaneous},
    {0x0E000010, 0x06000010, 0, DbgpArmDecodeMediaInstruction},
    {0x0C000000, 0x04000000, 0, DbgpArmDecodeLoadStore},
    {0x0C000000, 0x08000000, 0, DbgpArmDecodeBranchAndBlockTransfer},
    {0x0C000000, 0x0C000000, 0, DbgpArmDecodeCoprocessorSupervisor},
};

ARM_DECODE_BRANCH DbgArmUnconditionalTable[] = {
    {0x08000000, 0x00000000, 0, DbgpArmDecodeMemoryHintSimdMisc},
    {0x0E500000, 0x08400000, 0, DbgpArmDecodeStoreReturnState},
    {0x0E500000, 0x08100000, 0, DbgpArmDecodeReturnFromException},
    {0x0E000000, 0x0A000000, 0, DbgpArmDecodeBranch},
    {0x0F000010, 0x0E000000, 0, DbgpArmDecodeCoprocessorMove},
    {0x000000E0, 0x000000A0, 0, DbgpArmDecodeUndefined},
    {0x0FE00000, 0x0C400000, 0, DbgpArmDecodeCoprocessorMoveTwo},
    {0x0FA00000, 0x0C000000, 0, DbgpArmDecodeUndefined},
    {0x0E000000, 0x0C000000, 0, DbgpArmDecodeCoprocessorLoadStore},
    {0x0F100010, 0x0E000010, 0, DbgpArmDecodeCoprocessorMove},
    {0x0F100010, 0x0E100010, 0, DbgpArmDecodeCoprocessorMove},
};

ARM_DECODE_BRANCH DbgArmMemoryHintSimdMiscTable[] = {
    {0x07F10020, 0x01000000, 0, DbgpArmDecodeChangeProcessorState},
    {0x07F100F0, 0x01010000, 0, DbgpArmDecodeSetEndianness},
    {0x06000000, 0x02000000, 0, DbgpArmDecodeSimdDataProcessing},
    {0x07100000, 0x04000000, 0, DbgpArmDecodeSimdElementLoadStore},
    {0x07700000, 0x04100000, 0, DbgpArmDecodeNop},
    {0x07700000, 0x04500000, 0, DbgpArmDecodePreloadInstruction},
    {0x07300000, 0x04300000, 0, DbgpArmDecodeUnpredictable},
    {0x077F0000, 0x051F0000, 0, DbgpArmDecodeUnpredictable},
    {0x07300000, 0x05100000, 0, DbgpArmDecodePreloadInstruction},
    {0x07F00000, 0x05300000, 0, DbgpArmDecodeUnpredictable},
    {0x07F000F0, 0x05700000, 0, DbgpArmDecodeUnpredictable},
    {0x07F000E0, 0x05700020, 0, DbgpArmDecodeUnpredictable},
    {0x07F000F0, 0x05700070, 0, DbgpArmDecodeUnpredictable},
    {0x07F00080, 0x05700080, 0, DbgpArmDecodeUnpredictable},
    {0x07F000F0, 0x05700010, 0, DbgpArmDecodeClearExclusive},
    {0x07F000F0, 0x05700040, 0, DbgpArmDecodeDataSynchronizationBarrier},
    {0x07F000F0, 0x05700050, 0, DbgpArmDecodeDataMemoryBarrier},
    {0x07F000F0, 0x05700060, 0, DbgpArmDecodeInstructionSynchronizationBarrier},
    {0x07B00000, 0x05B00000, 0, DbgpArmDecodeUnpredictable},
    {0x07700010, 0x06100000, 0, DbgpArmDecodeNop},
    {0x07700010, 0x06500000, 0, DbgpArmDecodePreloadInstruction},
    {0x07300010, 0x07100000, 0, DbgpArmDecodePreloadInstruction},
    {0x06300010, 0x06300000, 0, DbgpArmDecodeUnpredictable},
    {0x07F000F0, 0x07F000F0, 0, DbgpArmDecodeUndefined},
};

ARM_DECODE_BRANCH DbgArmSimdDataProcessingTable[] = {
    {0x00800000, 0x00000000, 0, DbgpArmDecodeSimdThreeRegistersSameLength},
    {0x00B80090, 0x00800010, 0, DbgpArmDecodeSimdOneRegister},
    {0x00800010, 0x00800010, 0, DbgpArmDecodeSimdTwoRegistersWithShift},
    {0x00A00050, 0x00800000, 0, DbgpArmDecodeSimdThreeRegistersDifferentLength},
    {0x00B00050, 0x00A00000, 0, DbgpArmDecodeSimdThreeRegistersDifferentLength},
    {0x00A00050, 0x00800040, 0, DbgpArmDecodeSimdTwoRegistersWithScalar},
    {0x00B00050, 0x00A00040, 0, DbgpArmDecodeSimdTwoRegistersWithScalar},
    {0x01B00010, 0x00B00000, 0, DbgpArmDecodeSimdVectorExtract},
    {0x01B00810, 0x01B00000, 0, DbgpArmDecodeSimdTwoRegistersMiscellaneous},
    {0x01B00C10, 0x01B00800, 0, DbgpArmDecodeSimdVectorTableLookup},
    {0x01B00F90, 0x01B00C00, 0, DbgpArmDecodeSimdVectorDuplicate},
};

ARM_DECODE_BRANCH DbgArmSimdElementLoadStoreTable[] = {
    {0x00A00C00, 0x00A00C00, 0, DbgpArmDecodeSimdElementLoadAllLanes},
    {0x00800000, 0x00800000, 0, DbgpArmDecodeSimdElementLoadStoreSingle},
    {0x00800000, 0x00000000, 0, DbgpArmDecodeSimdElementLoadStoreMultiple},
};

ARM_DECODE_BRANCH DbgArmDataProcessingAndMiscellaneousTable[] = {
    {0x03900080, 0x01000000, 0, DbgpArmDecodeMiscellaneous},
    {0x03900090, 0x01000080, 0, DbgpArmDecodeMultiply},
    {0x02000010, 0x00000000, 0, DbgpArmDecodeDataProcessing},
    {0x02000090, 0x00000010, 0, DbgpArmDecodeDataProcessing},
    {0x030000F0, 0x00000090, 0, DbgpArmDecodeMultiply},
    {0x030000F0, 0x01000090, 0, DbgpArmDecodeSynchronization},
    {0x032000F0, 0x002000B0, 0, DbgpArmDecodeExtraLoadStore},
    {0x032000D0, 0x002000D0, 0, DbgpArmDecodeExtraLoadStore},
    {0x020000F0, 0x000000B0, 0, DbgpArmDecodeExtraLoadStore},
    {0x020000D0, 0x000000D0, 0, DbgpArmDecodeExtraLoadStore},
    {0x03F00000, 0x03000000, 0, DbgpArmDecodeLoadImmediate},
    {0x03F00000, 0x03400000, 0, DbgpArmDecodeLoadImmediate},
    {0x03B00000, 0x03200000, 0, DbgpArmDecodeMsrImmediateAndHints},
    {0x02000000, 0x02000000, 0, DbgpArmDecodeDataProcessing},
};

ARM_DECODE_BRANCH DbgArmMediaInstructionTable[] = {
    {0x01800000, 0x00000000, 0, DbgpArmDecodeParallelAdditionSubtraction},
    {0x01800000, 0x00800000, 0, DbgpArmDecodePackingInstructions},
    {0x01800000, 0x01000000, 0, DbgpArmDecodeMultiply},
    {0x01F000E0, 0x01800000, 0, DbgpArmDecodeSumofAbsoluteDifferences},
    {0x01A00060, 0x01A00040, 0, DbgpArmDecodeBitFieldInstructions},
    {0x01E00060, 0x01C00000, 0, DbgpArmDecodeBitFieldInstructions},
    {0xF1F000E0, 0xE1F000E0, 0, DbgpArmDecodePermanentlyUndefined},
};

ARM_DECODE_BRANCH DbgArmPackingInstructionTable[] = {
    {0x000000E0, 0x00000060, 0, DbgpArmDecodeExtensionWithRotation},
    {0x007000E0, 0x000000A0, 0, DbgpArmDecodeSelectBytes},
    {0x00700020, 0x00000000, 0, DbgpArmDecodePackHalfword},
    {0x00300060, 0x00300020, 0, DbgpArmDecodeReverse},
    {0x003000E0, 0x00200020, 0, DbgpArmDecodeSaturate},
    {0x00200020, 0x00200000, 0, DbgpArmDecodeSaturate}
};

ARM_DECODE_BRANCH DbgArmBranchAndBlockTransferTable[] = {
    {0x02000000, 0x02000000, 0, DbgpArmDecodeBranch},
    {0x02000000, 0x00000000, 0, DbgpArmDecodeLoadStoreMultiple},
};

ARM_DECODE_BRANCH DbgArmCoprocessorSupervisorTable[] = {
    {0x03E00000, 0x00000000, 0, DbgpArmDecodeUndefined},
    {0x03000000, 0x03000000, 0, DbgpArmDecodeSupervisorCall},
    {0x03E00E00, 0x00400A00, 0, DbgpArmDecodeSimd64BitTransfers},
    {0x02000E00, 0x00000A00, 0, DbgpArmDecodeSimdLoadStore},
    {0x03000E10, 0x02000A00, 0, DbgpArmDecodeFloatingPoint},
    {0x03000E10, 0x02000A10, 0, DbgpArmDecodeSimdSmallTransfers},
    {0x03E00000, 0x00400000, 0, DbgpArmDecodeCoprocessorMoveTwo},
    {0x02000000, 0x00000000, 0, DbgpArmDecodeCoprocessorLoadStore},
    {0x03000000, 0x02000000, 0, DbgpArmDecodeCoprocessorMove},
};

ARM_DECODE_BRANCH DbgArmFloatingPointTable[] = {
    {0x00B00040, 0x00B00000, 0, DbgpArmDecodeFloatingPointTwoRegisters},
    {0x00BE0040, 0x00B00040, 0, DbgpArmDecodeFloatingPointTwoRegisters},
    {0x00BE0040, 0x00B20040, 0, DbgpArmDecodeFloatingPointVectorConvert},
    {0x00BE0040, 0x00B40040, 0, DbgpArmDecodeFloatingPointVectorCompare},
    {0x00BF00C0, 0x00B700C0, 0, DbgpArmDecodeFloatingPointVectorConvert},
    {0x00BF0040, 0x00B80040, 0, DbgpArmDecodeFloatingPointVectorConvert},
    {0x00BE0040, 0x00BA0040, 0, DbgpArmDecodeFloatingPointVectorConvert},
    {0x00BE0040, 0x00BC0040, 0, DbgpArmDecodeFloatingPointVectorConvert},
    {0x00BE0040, 0x00BE0040, 0, DbgpArmDecodeFloatingPointVectorConvert},
    {0x00B00000, 0x00B00000, 0, DbgpArmDecodeUndefined},
    {0x00B00040, 0x00800040, 0, DbgpArmDecodeUndefined},
    {0x00000000, 0x00000000, 0, DbgpArmDecodeFloatingPointThreeRegisters},
};

ARM_DECODE_BRANCH DbgArmSimdSmallTransferTable[] = {
    {0x00F00100, 0x00000000, 0, DbgpArmDecodeSimdSmallMove},
    {0x00F00100, 0x00E00000, 0, DbgpArmDecodeSimdSpecialMove},
    {0x00900100, 0x00000100, 0, DbgpArmDecodeSimdSmallMove},
    {0x00900140, 0x00800100, 0, DbgpArmDecodeSimdDuplicate},
    {0x00F00100, 0x00100000, 0, DbgpArmDecodeSimdSmallMove},
    {0x00F00100, 0x00F00000, 0, DbgpArmDecodeSimdSpecialMove},
    {0x00100100, 0x00100100, 0, DbgpArmDecodeSimdSmallMove},
};

ARM_DECODE_BRANCH DbgArmSimdLoadStoreTable[] = {
    {0x01200000, 0x01000000, 0, DbgpArmDecodeSimdLoadStoreRegister},
    {0x01800000, 0x00800000, 0, DbgpArmDecodeSimdLoadStoreMultiple},
    {0x01800000, 0x01000000, 0, DbgpArmDecodeSimdLoadStoreMultiple},
};

PSTR DbgArmRegisterNames[] = {
    "r0",
    "r1",
    "r2",
    "r3",
    "r4",
    "r5",
    "r6",
    "r7",
    "r8",
    "r9",
    "r10",
    "fp",
    "ip",
    "sp",
    "lr",
    "pc",
    "f0"
    "f1",
    "f2",
    "f3",
    "f4",
    "f5",
    "f6",
    "f7",
    "fps",
    "cpsr"
};

PSTR DbgArmSpecialRegisterNames[16] = {
    "fpsid",
    "fpscr",
    "<arch>",
    "<arch>",
    "<arch>",
    "<arch>",
    "mvfr1",
    "mvfr0",
    "fpexc",
    "fpinst",
    "fpinst2",
    "<arch>",
    "<arch>",
    "<arch>",
    "<arch>",
    "<arch>",
};

PSTR DbgArmConditionCodes[16] = {
    "eq",
    "ne",
    "cs",
    "cc",
    "mi",
    "pl",
    "vs",
    "vc",
    "hi",
    "ls",
    "ge",
    "lt",
    "gt",
    "le",
    "",
    "",
};

PSTR DbgArmDataProcessingMnemonics[16] = {
    "and",
    "eor",
    "sub",
    "rsb",
    "add",
    "adc",
    "sbc",
    "rsc",
    "tst",
    "teq",
    "cmp",
    "cmn",
    "orr",
    "mov",
    "bic",
    "mvn"
};

PSTR DbgArmSynchronizationMnemonics[8] = {
    "strex",
    "ldrex",
    "strexd",
    "ldrexd",
    "strexb",
    "ldrexb",
    "strexh",
    "ldrexh"
};

PSTR DbgArmBankedRegisters[64] = {
    "r8_usr",
    "r9_usr",
    "r10_usr",
    "r11_usr",
    "r12_usr",
    "sp_usr",
    "lr_usr",
    "UNPREDICTABLE",
    "r8_fiq",
    "r9_fiq",
    "r10_fiq",
    "r11_fiq",
    "r12_fiq",
    "sp_fiq",
    "lr_fiq",
    "UNPREDICTABLE",
    "lr_irq",
    "sp_irq",
    "lr_svc",
    "sp_svc",
    "lr_abr",
    "sp_abt",
    "lr_und",
    "sp_und",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "lr_mon",
    "sp_mon",
    "elr_hyp",
    "sp_hyp",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "spsr_fiq",
    "UNPREDICTABLE",
    "spsr_irq",
    "UNPREDICTABLE",
    "spsr_svc",
    "UNPREDICTABLE",
    "spsr_abt",
    "UNPREDICTABLE",
    "spsr_und",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "UNPREDICTABLE",
    "spsr_mon",
    "spsr_hyp"
};

PSTR DbgArmParallelArithmeticMnemonics[2][24] = {
    {
        "sadd16",
        "sasx",
        "ssax",
        "ssub16",
        "sadd8",
        NULL,
        NULL,
        "ssub8",
        "qadd16",
        "qasx",
        "qsax",
        "qsub16",
        "qadd8",
        NULL,
        NULL,
        "qsub8",
        "shadd16",
        "shasx",
        "shsax",
        "shsub16",
        "shadd8",
        NULL,
        NULL,
        "shsub8"
    },

    {
        "uadd16",
        "uasx",
        "usax",
        "usub16",
        "uadd8",
        NULL,
        NULL,
        "usub8",
        "uqadd16",
        "uqasx"
        "uqsax"
        "uqsub16",
        "uqadd8",
        NULL,
        NULL,
        "uqsub8",
        "uhadd16",
        "uhasx",
        "uhsax",
        "uhsub16",
        "uhadd8",
        NULL,
        NULL,
        "uhsub8"
    }
};

PSTR DbgArmExtensionRotationMnemonics[2][8] = {
    {
        "sxtab16",
        NULL,
        "sxtab",
        "sxtah",
        "uxtab16",
        NULL,
        "uxtab",
        "uxtah"
    },

    {
        "sxtb16",
        NULL,
        "sxtb",
        "sxth",
        "uxtb16",
        NULL,
        "uxtb",
        "uxth"
    }
};

PSTR DbgArmReverseMnemonics[4] = {
    "rev",
    "rbit",
    "rev16",
    "revsh"
};

PSTR DbgArmSimdElementLoadStoreMultipleElementSuffix[] = {
    ARM_SIMD_ELEMENT_LOAD_STORE_4_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_4_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_1_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_2_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_3_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_3_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_1_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_1_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_2_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_2_ELEMENT_SUFFIX,
    ARM_SIMD_ELEMENT_LOAD_STORE_1_ELEMENT_SUFFIX
};

ULONG DbgArmSimdElementLoadStoreMultipleVectorCount[] = {
    4, 4, 4, 4, 3, 3, 1, 2, 2, 2
};

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

BOOL
DbgpArmDisassemble (
    ULONGLONG InstructionPointer,
    PBYTE InstructionStream,
    PSTR Buffer,
    ULONG BufferLength,
    PDISASSEMBLED_INSTRUCTION Disassembly,
    MACHINE_LANGUAGE Language
    )

/*++

Routine Description:

    This routine decodes one instruction from an ARM binary instruction
    stream into a human readable form.

Arguments:

    InstructionPointer - Supplies the instruction pointer for the start of the
        instruction stream.

    InstructionStream - Supplies a pointer to the binary instruction stream.

    Buffer - Supplies a pointer to the buffer where the human
        readable strings will be printed. This buffer must be allocated by the
        caller.

    BufferLength - Supplies the length of the supplied buffer.

    Disassembly - Supplies a pointer to the structure that will receive
        information about the instruction.

    Language - Supplies the machine language to interpret this stream as.

Return Value:

    TRUE on success.

    FALSE if the instruction was unknown.

--*/

{

    ULONG ConditionCode;
    ARM_DISASSEMBLY Context;
    BOOL Decoded;
    BOOL Result;

    RtlZeroMemory(Disassembly, sizeof(DISASSEMBLED_INSTRUCTION));
    if (BufferLength < ARM_INSTRUCTION_SIZE) {
        Result = FALSE;
        goto ArmDisassembleEnd;
    }

    RtlZeroMemory(&Context, sizeof(ARM_DISASSEMBLY));
    Context.Result = Disassembly;
    Context.InstructionPointer = InstructionPointer;

    //
    // Get the instruction word. Always take the max, four bytes, even if the
    // instruction might only end up being two.
    //

    Context.Instruction = *((PULONG)InstructionStream);

    //
    // If this is Thumb, then just call the thumb decode function and skip the
    // rest of this.
    //

    if (Language == MachineLanguageThumb2) {
        DbgpThumbDecode(&Context);
        Result = TRUE;
        goto ArmDisassembleEnd;
    }

    //
    // Use the ARM tables to decode the instruction.
    //

    ASSERT(Language == MachineLanguageArm);

    strcpy(Context.Mnemonic, "Unknown");
    Decoded = ARM_DECODE_WITH_TABLE(&Context, DbgArmTopLevelTable);
    if (Decoded != FALSE) {
        ConditionCode = Context.Instruction >> ARM_CONDITION_SHIFT;
        strcat(Context.Mnemonic, DbgArmConditionCodes[ConditionCode]);
    }

    Result = TRUE;
    Disassembly->BinaryLength = 4;

ArmDisassembleEnd:
    if ((strlen(Context.Mnemonic) +
         strlen(Context.PostConditionMnemonicSuffix) +
         strlen(Context.Operand1) +
         strlen(Context.Operand2) +
         strlen(Context.Operand3) +
         strlen(Context.Operand4) + 5) > BufferLength) {

        Result = FALSE;
    }

    if (Result != FALSE) {
        strcat(Context.Mnemonic, Context.PostConditionMnemonicSuffix);
        strcpy(Buffer, Context.Mnemonic);
        Disassembly->Mnemonic = Buffer;
        Buffer += strlen(Context.Mnemonic) + 1;
        BufferLength -= strlen(Context.Mnemonic) + 1;
        if (strlen(Context.Operand1) != 0) {
            strcpy(Buffer, Context.Operand1);
            Disassembly->DestinationOperand = Buffer;
            Buffer += strlen(Context.Operand1) + 1;
            BufferLength -= strlen(Context.Operand1) + 1;
        }

        if (strlen(Context.Operand2) != 0) {
            strcpy(Buffer, Context.Operand2);
            Disassembly->SourceOperand = Buffer;
            Buffer += strlen(Context.Operand2) + 1;
            BufferLength -= strlen(Context.Operand2) + 1;
        }

        if (strlen(Context.Operand3) != 0) {
            strcpy(Buffer, Context.Operand3);
            Disassembly->ThirdOperand = Buffer;
            Buffer += strlen(Context.Operand3) + 1;
            BufferLength -= strlen(Context.Operand3) + 1;
        }

        if (strlen(Context.Operand4) != 0) {
            strcpy(Buffer, Context.Operand4);
            Disassembly->FourthOperand = Buffer;
        }
    }

    return Result;
}

BOOL
DbgpArmDecodeWithTable (
    PARM_DISASSEMBLY Context,
    PARM_DECODE_BRANCH Table,
    ULONG TableSize
    )

/*++

Routine Description:

    This routine checks the masks and values specified by the given table, and
    calls the appropriate disassembly routine.

Arguments:

    Context - Supplies a pointer to the disassembly context.

    Table - Supplies a pointer to the decode branch table.

    TableSize - Supplies the number of elements in the table.

Return Value:

    TRUE if a match was found.

--*/

{

    ULONG Instruction;
    ULONG Mask;
    ULONG Shift;
    ULONG TableIndex;
    ULONG Value;

    Instruction = Context->Instruction;
    for (TableIndex = 0; TableIndex < TableSize; TableIndex += 1) {
        Shift = Table[TableIndex].Shift;
        Mask = Table[TableIndex].Mask << Shift;
        Value = Table[TableIndex].Value << Shift;
        if ((Instruction & Mask) == Value) {

            //
            // Call the disassembly routine, this table entry matched.
            //

            Table[TableIndex].Disassemble(Context);
            return TRUE;
        }
    }

    //
    // Nothing matched.
    //

    return FALSE;
}

VOID
DbgpArmDecodeCoprocessorMove (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a coprocessor move instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Condition;
    ULONG Coprocessor;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG Opcode1;
    ULONG Opcode2;
    ULONG RegisterD;
    ULONG RegisterM;
    ULONG RegisterN;

    Instruction = Context->Instruction;

    //
    // Get the basic fields for CDP, MRC, and MCR instructions.
    //

    Coprocessor = (Instruction & ARM_COPROCESSOR_NUMBER_MASK) >>
                  ARM_COPROCESSOR_NUMBER_SHIFT;

    RegisterD = (Instruction & ARM_DESTINATION_REGISTER_MASK) >>
                ARM_DESTINATION_REGISTER_SHIFT;

    RegisterN = (Instruction & ARM_COPROCESSOR_RN_MASK) >>
                ARM_COPROCESSOR_RN_SHIFT;

    RegisterM = (Instruction & ARM_COPROCESSOR_RM_MASK) >>
                ARM_COPROCESSOR_RM_SHIFT;

    Opcode2 = (Instruction & ARM_COPROCESSOR_OPCODE2_MASK) >>
              ARM_COPROCESSOR_OPCODE2_SHIFT;

    //
    // CDP has a different opcode 1 shift, so this needs to be adjusted for
    // that instruction.
    //

    Opcode1 = (Instruction & ARM_MCR_MRC_OPCODE1_MASK) >>
              ARM_MCR_MRC_OPCODE1_SHIFT;

    //
    // If the CDP bit is 0, then this instruction is a CDP instruction.
    //

    if ((Instruction & ARM_COPROCESSOR_CDP_BIT) == 0) {
        BaseMnemonic = ARM_CDP_MNEMONIC;
        Opcode1 = (Instruction & ARM_CDP_OPCODE1_MASK) >> ARM_CDP_OPCODE1_SHIFT;
        sprintf(Context->Operand2, "c%d", RegisterD);

    //
    // If it's not a CDP instruction, check the other constant bit, which if
    // set indicates a MRC, if clear indicates MCR.
    //

    } else if ((Instruction & ARM_COPROCESSOR_MRC_BIT) != 0) {
        BaseMnemonic = ARM_MRC_MNEMONIC;
        sprintf(Context->Operand2, "r%d", RegisterD);

    } else {
        BaseMnemonic = ARM_MCR_MNEMONIC;
        sprintf(Context->Operand2, "r%d", RegisterD);
    }

    //
    // If the condition is 0xF, then these are CDP2, MRC2, and MCR2
    // instructions.
    //

    MnemonicSuffix = "";
    Condition = Context->Instruction >> ARM_CONDITION_SHIFT;
    if (Condition == ARM_CONDITION_UNCONDITIONAL) {
        MnemonicSuffix = "2";
    }

    sprintf(Context->Mnemonic, "%s%s", BaseMnemonic, MnemonicSuffix);
    sprintf(Context->Operand1, "p%d, %d", Coprocessor, Opcode1);
    sprintf(Context->Operand3, "c%d, c%d, %d", RegisterN, RegisterM, Opcode2);
    return;
}

VOID
DbgpArmDecodeCoprocessorMoveTwo (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a coprocessor move instruction to/from two ARM
    registers.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Condition;
    ULONG Coprocessor;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG Opcode1;
    ULONG RegisterM;
    ULONG RegisterT;
    ULONG RegisterT2;

    Instruction = Context->Instruction;

    //
    // Get the basic fields for MRRC and MCRR instructions.
    //

    Coprocessor = (Instruction & ARM_COPROCESSOR_NUMBER_MASK) >>
                  ARM_COPROCESSOR_NUMBER_SHIFT;

    RegisterT = (Instruction & ARM_DESTINATION_REGISTER_MASK) >>
                ARM_DESTINATION_REGISTER_SHIFT;

    RegisterT2 = (Instruction & ARM_DESTINATION_REGISTER2_MASK) >>
                ARM_DESTINATION_REGISTER2_SHIFT;

    RegisterM = (Instruction & ARM_COPROCESSOR_RM_MASK) >>
                ARM_COPROCESSOR_RM_SHIFT;

    Opcode1 = (Instruction & ARM_MCRR_MRRC_OPCODE1_MASK) >>
              ARM_MCRR_MRRC_OPCODE1_SHIFT;

    //
    // Check the non-constant bit to determine if this is MRRC or MCRR.
    //

    if ((Instruction & ARM_COPROCESSOR_MRRC_BIT) != 0) {
        BaseMnemonic = ARM_MRRC_MNEMONIC;

    } else {
        BaseMnemonic = ARM_MCRR_MNEMONIC;
    }

    //
    // If the condition is 0xF, then these are MRRC2 and MCRR2 instructions.
    //

    MnemonicSuffix = "";
    Condition = Context->Instruction >> ARM_CONDITION_SHIFT;
    if (Condition == ARM_CONDITION_UNCONDITIONAL) {
        MnemonicSuffix = "2";
    }

    sprintf(Context->Mnemonic, "%s%s", BaseMnemonic, MnemonicSuffix);
    sprintf(Context->Operand1, "p%d, %d", Coprocessor, Opcode1);
    sprintf(Context->Operand2,
            "%s, %s",
            DbgArmRegisterNames[RegisterT],
            DbgArmRegisterNames[RegisterT2]);

    sprintf(Context->Operand3, "c%d", RegisterM);
    return;
}

VOID
DbgpArmDecodeCoprocessorLoadStore (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a coprocessor data instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Condition;
    UCHAR CoprocessorDestination;
    UCHAR CoprocessorNumber;
    PSTR FirstSuffix;
    ULONG Immediate8;
    ULONG Instruction;
    UCHAR Rn;
    PSTR SecondSuffix;
    UCHAR SignCharacter;
    UCHAR WriteBack;

    Instruction = Context->Instruction;

    //
    // Determine whether it's a long load/store or regular.
    //

    SecondSuffix = "";
    if ((Instruction & ARM_COPROCESSOR_DATA_LONG_BIT) != 0) {
        SecondSuffix = ARM_COPROCESSOR_LONG_MNEMONIC;
    }

    //
    // Determine whether the immediate is added or subtracted.
    //

    if ((Instruction & ARM_ADD_SUBTRACT_BIT) != 0) {
        SignCharacter = '+';

    } else {
        SignCharacter = '-';
    }

    //
    // Get the register numbers and values involved.
    //

    CoprocessorDestination =
                    (Instruction & ARM_COPROCESSOR_DATA_DESTINATION_MASK) >>
                    ARM_COPROCESSOR_DATA_DESTINATION_SHIFT;

    Rn = (Instruction & ARM_COPROCESSOR_RN_MASK) >> ARM_COPROCESSOR_RN_SHIFT;
    Immediate8 = (UCHAR)(Instruction & 0xFF);
    CoprocessorNumber = (Instruction & ARM_COPROCESSOR_NUMBER_MASK) >>
                        ARM_COPROCESSOR_NUMBER_SHIFT;

    //
    // Determine the mnemonic.
    //

    if ((Instruction & ARM_LOAD_BIT) != 0) {
        BaseMnemonic = ARM_COPROCESSOR_LOAD_MNEMONIC;

    } else {
        BaseMnemonic = ARM_COPROCESSOR_STORE_MNEMONIC;
    }

    //
    // If the condition is 0xF, then these are MRRC2 and MCRR2 instructions.
    //

    FirstSuffix = "";
    Condition = Context->Instruction >> ARM_CONDITION_SHIFT;
    if (Condition == ARM_CONDITION_UNCONDITIONAL) {
        FirstSuffix = "2";
    }

    sprintf(Context->Mnemonic,
            "%s%s%s",
            BaseMnemonic,
            FirstSuffix,
            SecondSuffix);

    //
    // Write out the first two operands.
    //

    sprintf(Context->Operand1, "p%d", CoprocessorNumber);
    sprintf(Context->Operand2, "c%d", CoprocessorDestination);

    //
    // Depending on the addressing mode, write out the third operand. If the
    // pre-index bit is set, the addressing mode is either pre-indexed or
    // offset.
    //

    if ((Instruction & ARM_PREINDEX_BIT) != 0) {
        if ((Instruction & ARM_WRITE_BACK_BIT) != 0) {
            WriteBack = '!';

        } else {
            WriteBack = ' ';
        }

        sprintf(Context->Operand3,
                "[%s, #%c%d]%c",
                DbgArmRegisterNames[Rn],
                SignCharacter,
                Immediate8 * 4,
                WriteBack);

    //
    // The pre-index bit is not set, so the addressing mode is either post-
    // indexed or unindexed.
    //

    } else {
        if ((Instruction & ARM_WRITE_BACK_BIT) != 0) {
            sprintf(Context->Operand3,
                    "[%s], #%c%d",
                    DbgArmRegisterNames[Rn],
                    SignCharacter,
                    Immediate8 * 4);

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

    return;
}

VOID
DbgpArmDecodeFloatingPoint (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a floating point data processing instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmFloatingPointTable);
    return;
}

VOID
DbgpArmDecodeSimdSmallTransfers (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a transfers between SIMD and floating point 8-bit,
    16-bit, and 32-bit registers and the ARM core.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmSimdSmallTransferTable);
    return;
}

VOID
DbgpArmDecodeSimd64BitTransfers (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a transfers between SIMD and floating point 64-bit
    registers and the ARM core.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR RegisterString;
    ULONG Rt;
    ULONG Rt2;
    ULONG Vector;
    PSTR VectorString;

    Instruction = Context->Instruction;
    Vector = (Instruction & ARM_SIMD_TRANSFER_64_VECTOR_MASK) >>
             ARM_SIMD_TRANSFER_64_VECTOR_SHIFT;

    Rt = (Instruction & ARM_SIMD_TRANSFER_64_RT_MASK) >>
         ARM_SIMD_TRANSFER_64_RT_SHIFT;

    Rt2 = (Instruction & ARM_SIMD_TRANSFER_64_RT2_MASK) >>
          ARM_SIMD_TRANSFER_64_RT2_SHIFT;

    if ((Instruction & ARM_SIMD_TRANSFER_64_TO_REGISTER) != 0) {
        RegisterString = Context->Operand1;
        VectorString = Context->Operand2;

    } else {
        VectorString = Context->Operand1;
        RegisterString = Context->Operand2;
    }

    strcpy(Context->Mnemonic, ARM_VMOV_MNEMONIC);
    sprintf(RegisterString,
            "%s, %s",
            DbgArmRegisterNames[Rt],
            DbgArmRegisterNames[Rt2]);

    if ((Instruction & ARM_SIMD_TRANSFER_64_DOUBLE) != 0) {
        if ((Instruction & ARM_SIMD_TRANSFER_64_VECTOR_BIT) != 0) {
            Vector |= (1 << 4);
        }

        sprintf(VectorString,
                "%s%d",
                ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
                Vector);

    } else {
        Vector <<= 1;
        if ((Instruction & ARM_SIMD_TRANSFER_64_VECTOR_BIT) != 0) {
            Vector |= 1;
        }

        sprintf(VectorString,
                "%s%d, %s%d",
                ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR,
                Vector,
                ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR,
                Vector + 1);
    }

    return;
}

VOID
DbgpArmDecodeSimdLoadStore (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a load/store instructions involving SIMD and floating
    point registers.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmSimdLoadStoreTable);
    return;
}

VOID
DbgpArmDecodeSimdElementLoadStore (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a SIMD element and structure load and store
    instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmSimdElementLoadStoreTable);
    return;
}

VOID
DbgpArmDecodeSimdDataProcessing (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the SIMD data processing instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmSimdDataProcessingTable);
    return;
}

VOID
DbgpArmDecodeRegisterList (
    PSTR Destination,
    ULONG DestinationSize,
    ULONG RegisterList
    )

/*++

Routine Description:

    This routine converts an ARM register list to a string.

Arguments:

    Destination - Supplies a pointer to the destination string.

    DestinationSize - Supplies the size of the destination string.

    RegisterList - Supplies the register bitmask.

Return Value:

    None.

--*/

{

    ULONG CurrentRegister;
    BOOL FirstRegister;

    strcpy(Destination, "{");

    //
    // Loop through the registers, adding the ones specified in the bitfield.
    //

    FirstRegister = TRUE;
    for (CurrentRegister = 0; CurrentRegister < 16; CurrentRegister += 1) {
        if ((RegisterList & 0x00000001) != 0) {
            if (FirstRegister == FALSE) {
                strcat(Destination, ", ");
            }

            strcat(Destination, DbgArmRegisterNames[CurrentRegister]);
            FirstRegister = FALSE;
        }

        RegisterList = RegisterList >> 1;
    }

    strcat(Destination, "}");
    return;
}

VOID
DbgpArmPrintMode (
    PSTR Destination,
    ULONG Mode
    )

/*++

Routine Description:

    This routine prints the given ARM processor mode.

Arguments:

    Destination - Supplies a pointer where the mode will be printed.

    Mode - Supplies the mode to print. Only the bottom 5 bits will be examined.

Return Value:

    None.

--*/

{

    PSTR ModeString;

    Mode &= ARM_MODE_MASK;
    ModeString = NULL;
    if (Mode == ARM_MODE_USER) {
        ModeString = ARM_MODE_USER_STRING;

    } else if (Mode == ARM_MODE_FIQ) {
        ModeString = ARM_MODE_FIQ_STRING;

    } else if (Mode == ARM_MODE_IRQ) {
        ModeString = ARM_MODE_IRQ_STRING;

    } else if (Mode == ARM_MODE_SVC) {
        ModeString = ARM_MODE_SVC_STRING;

    } else if (Mode == ARM_MODE_ABORT) {
        ModeString = ARM_MODE_ABORT_STRING;

    } else if (Mode == ARM_MODE_UNDEF) {
        ModeString = ARM_MODE_UNDEF_STRING;

    } else if (Mode == ARM_MODE_SYSTEM) {
        ModeString = ARM_MODE_SYSTEM_STRING;
    }

    if (ModeString != NULL) {
        sprintf(Destination, "#%s", ModeString);

    } else {
        sprintf(Destination, "%02X", Mode);
    }

    return;
}

VOID
DbgpArmPrintBarrierMode (
    PSTR Destination,
    ULONG Mode
    )

/*++

Routine Description:

    This routine prints the memory barrier (dsb, dmb, isb) type. For full
    system (sy), nothing is printed.

Arguments:

    Destination - Supplies a pointer where the mode will be printed.

    Mode - Supplies the mode to print. Only the bottom 4 bits will be examined.

Return Value:

    None.

--*/

{

    PSTR ModeString;

    ModeString = NULL;
    Mode &= ARM_BARRIER_MODE_MASK;
    if (Mode == ARM_BARRIER_MODE_FULL) {
        ModeString = ARM_BARRIER_MODE_FULL_STRING;

    } else if (Mode == ARM_BARRIER_MODE_ST) {
        ModeString = ARM_BARRIER_MODE_ST_STRING;

    } else if (Mode == ARM_BARRIER_MODE_ISH) {
        ModeString = ARM_BARRIER_MODE_ISH_STRING;

    } else if (Mode == ARM_BARRIER_MODE_ISHST) {
        ModeString = ARM_BARRIER_MODE_ISHST_STRING;

    } else if (Mode == ARM_BARRIER_MODE_NSH) {
        ModeString = ARM_BARRIER_MODE_NSH_STRING;

    } else if (Mode == ARM_BARRIER_MODE_NSHST) {
        ModeString = ARM_BARRIER_MODE_NSHST_STRING;

    } else if (Mode == ARM_BARRIER_MODE_OSH) {
        ModeString = ARM_BARRIER_MODE_OSH_STRING;

    } else if (Mode == ARM_BARRIER_MODE_OSHST) {
        ModeString = ARM_BARRIER_MODE_OSHST_STRING;
    }

    if (ModeString != NULL) {
        strcpy(Destination, ModeString);

    } else {
        sprintf(Destination, "#02X", Mode);
    }

    return;
}

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

VOID
DbgpArmDecodeUnconditional (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes instructions with the unconditional condition code 0xF.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmUnconditionalTable);
    return;
}

VOID
DbgpArmDecodeDataProcessingAndMiscellaneous (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the data processing and miscellaneous instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmDataProcessingAndMiscellaneousTable);
    return;
}

VOID
DbgpArmDecodeMediaInstruction (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an instruction that falls into the Media Extension
    class of instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmMediaInstructionTable);
    return;
}

VOID
DbgpArmDecodeBranchAndBlockTransfer (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes both branch and block transfer instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmBranchAndBlockTransferTable);
    return;
}

VOID
DbgpArmDecodeCoprocessorSupervisor (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a coprocessor move or supervisor instruction. This
    routine also decodes floating point instructions and SIMD to floating point
    transfers.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmCoprocessorSupervisorTable);
    return;
}

VOID
DbgpArmDecodeMemoryHintSimdMisc (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes instructions that are either memory hints, advanced
    SIMD instructions, or miscellaneous instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmMemoryHintSimdMiscTable);
    return;
}

VOID
DbgpArmDecodeStoreReturnState (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the store return state (SRS) instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR MnemonicSuffix;

    MnemonicSuffix = DbgpArmGetLoadStoreTypeString(Context->Instruction);
    sprintf(Context->Mnemonic, "%s%s", ARM_SRS_MNEMONIC, MnemonicSuffix);
    DbgpArmPrintMode(Context->Operand2, Context->Instruction);
    if ((Context->Instruction & ARM_WRITE_BACK_BIT) != 0) {
        sprintf(Context->Operand1, "%s!, %s",
                DbgArmRegisterNames[ARM_STACK_REGISTER],
                Context->Operand2);

    } else {
        sprintf(Context->Operand1, "%s, %s",
                DbgArmRegisterNames[ARM_STACK_REGISTER],
                Context->Operand2);
    }

    Context->Operand2[0] = '\0';
    return;
}

VOID
DbgpArmDecodeReturnFromException (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the return from exception (RFE) instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR MnemonicSuffix;
    ULONG Rn;

    Rn = (Context->Instruction & ARM_UNCONDITIONAL_RN_MASK) >>
         ARM_UNCONDITIONAL_RN_SHIFT;

    MnemonicSuffix = DbgpArmGetLoadStoreTypeString(Context->Instruction);
    sprintf(Context->Mnemonic, "%s%s", ARM_RFE_MNEMONIC, MnemonicSuffix);
    if ((Context->Instruction & ARM_WRITE_BACK_BIT) != 0) {
        sprintf(Context->Operand1, "%s!", DbgArmRegisterNames[Rn]);

    } else {
        sprintf(Context->Operand1, "%s", DbgArmRegisterNames[Rn]);
    }

    return;
}

VOID
DbgpArmDecodeBranch (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the branch instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Condition;
    ULONG Instruction;
    LONG Offset;
    ULONGLONG OperandAddress;

    Instruction = Context->Instruction;
    Offset = (Instruction & ARM_IMMEDIATE24_MASK) << 2;

    //
    // If the condition is set to unconditional, then this is a BLX
    // instruction. The current instruction set is ARM and the target
    // instruction set is Thumb.
    //

    Condition = Instruction >> ARM_CONDITION_SHIFT;
    if (Condition == ARM_CONDITION_UNCONDITIONAL) {
        BaseMnemonic = ARM_BLX_MNEMONIC;
        if ((Instruction & ARM_BLX_H_BIT) != 0) {
            Offset |= 0x2;
        }

        //
        // Or in the bottom bit as this is a transition to Thumb mode and all
        // addresses should off by 1.
        //

        Offset |= 0x1;

    //
    // Otherwise if the link bit is set, then it is a BL instruction with the
    // current and target instruction set both being ARM.
    //

    } else if ((Instruction & ARM_BRANCH_LINK_BIT) != 0) {
        BaseMnemonic = ARM_BL_MNEMONIC;

    //
    // Otherwise it is just a plain branch instruction.
    //

    } else {
        BaseMnemonic = ARM_B_MNEMONIC;
    }

    //
    // Sign-extend the offset.
    //

    if ((Offset & 0x02000000) != 0) {
        Offset |= 0xFC000000;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);

    //
    // The immediate value in the instruction is relative to the PC value of
    // this instruction, which is this instruction's address plus 8.
    //

    OperandAddress = Context->InstructionPointer + 8;
    OperandAddress += (LONGLONG)Offset;
    Context->Result->OperandAddress = OperandAddress;
    Context->Result->AddressIsDestination = TRUE;
    Context->Result->AddressIsValid = TRUE;
    snprintf(Context->Operand1,
             sizeof(Context->Operand1),
             "[0x%08llx]",
             OperandAddress);

    return;
}

VOID
DbgpArmDecodeUndefined (
    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
DbgpArmDecodeUnpredictable (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine catches unpredictable corners of the instruction space.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

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

VOID
DbgpArmDecodeNop (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine handles instructions that are treated as no operation.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

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

VOID
DbgpArmDecodeChangeProcessorState (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the change processor state instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;

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

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

    Context->Operand1[0] = '\0';
    if ((Instruction & ARM_CPS_FLAG_A) != 0) {
        strcat(Context->Operand1, ARM_CPS_FLAG_A_STRING);
    }

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

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

    if ((Instruction & ARM_MODE_MASK) != 0) {
        DbgpArmPrintMode(Context->Operand2, Instruction);
        if ((Instruction &
             (ARM_CPS_FLAG_A | ARM_CPS_FLAG_I | ARM_CPS_FLAG_F)) != 0) {

             strcat(Context->Operand1, ", ");
        }

        strcat(Context->Operand1, Context->Operand2);
        Context->Operand2[0] = '\0';
    }

    return;
}

VOID
DbgpArmDecodeSetEndianness (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the set endianness instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    if ((Context->Instruction & ARM_SETEND_BIG_ENDIAN) != 0) {
        strcpy(Context->Operand1, ARM_SETEND_BE_STRING);

    } else {
        strcpy(Context->Operand1, ARM_SETEND_LE_STRING);
    }

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

VOID
DbgpArmDecodePreloadInstruction (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the preload instructions, both the immediate/literal
    versions and the register based versions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Immediate;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    LONG Offset;
    ULONGLONG OperandAddress;
    ULONG RegisterM;
    ULONG RegisterN;
    CHAR ShiftString[35];
    CHAR SignCharacter;

    //
    // Sort out the bits that are common to the immediate and register preload
    // instructions.
    //

    Instruction = Context->Instruction;
    MnemonicSuffix = "";
    if ((Instruction & ARM_PRELOAD_DATA_BIT) != 0) {
        BaseMnemonic = ARM_PRELOAD_DATA_MNEMONIC;
        if ((Instruction & ARM_PRELOAD_DATA_READ_BIT) == 0) {
            MnemonicSuffix = "w";
        }

    } else {
        BaseMnemonic = ARM_PRELOAD_MNEMONIC;
    }

    RegisterN = (Instruction & ARM_PRELOAD_RN_MASK) >> ARM_PRELOAD_RN_SHIFT;
    if ((Instruction & ARM_PRELOAD_ADD_BIT) != 0) {
        SignCharacter = '+';

    } else {
        SignCharacter = '-';
    }

    sprintf(Context->Mnemonic, "%s%s", BaseMnemonic, MnemonicSuffix);

    //
    // If this is a register preload instruction, get the second register and
    // calculate the shift value.
    //

    if ((Instruction & ARM_PRELOAD_REGISTER_BIT) != 0) {
        Immediate = (Instruction & ARM_PRELOAD_IMMEDIATE5_MASK) >>
                    ARM_PRELOAD_IMMEDIATE5_SHIFT;

        RegisterM = (Instruction & ARM_PRELOAD_RM_MASK) >> ARM_PRELOAD_RM_SHIFT;
        DbgpArmDecodeImmediateShift(ShiftString,
                                    sizeof(ShiftString),
                                    RegisterM,
                                    (Instruction & ARM_SHIFT_TYPE),
                                    Immediate);

        sprintf(Context->Operand1,
                "[%s, %c%s]",
                DbgArmRegisterNames[RegisterN],
                SignCharacter,
                ShiftString);

    //
    // Otherwise build out the immediate/literal value.
    //

    } else {
        Offset = (Instruction & ARM_PRELOAD_IMMEDIATE12_MASK) >>
                 ARM_PRELOAD_IMMEDIATE12_SHIFT;

        //
        // If the register is the PC, then the immediate value in the
        // instruction is relative to the PC value of this instruction, which
        // is this instruction's address plus 8.
        //

        if (RegisterN == ARM_PC_REGISTER) {
            OperandAddress = Context->InstructionPointer + 8;
            if ((Instruction & ARM_PRELOAD_ADD_BIT) == 0) {
                Offset = -Offset;
            }

            OperandAddress += (LONGLONG)Offset;
            Context->Result->OperandAddress = OperandAddress;
            Context->Result->AddressIsDestination = TRUE;
            Context->Result->AddressIsValid = TRUE;
            snprintf(Context->Operand1,
                     sizeof(Context->Operand1),
                     "[0x%08llx]",
                     OperandAddress);

        } else {
            snprintf(Context->Operand1,
                     sizeof(Context->Operand1),
                     "[%s, #%c%d]",
                     DbgArmRegisterNames[RegisterN],
                     SignCharacter,
                     Offset);
        }
    }

    return;
}

VOID
DbgpArmDecodeClearExclusive (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the clear exclusive instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

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

VOID
DbgpArmDecodeDataSynchronizationBarrier (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the data synchronization barrier instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    DbgpArmPrintBarrierMode(Context->Operand1, Context->Instruction);
    strcpy(Context->Mnemonic, ARM_DSB_MNEMONIC);
    return;
}

VOID
DbgpArmDecodeDataMemoryBarrier (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the data memory barrier instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    DbgpArmPrintBarrierMode(Context->Operand1, Context->Instruction);
    strcpy(Context->Mnemonic, ARM_DMB_MNEMONIC);
    return;
}

VOID
DbgpArmDecodeInstructionSynchronizationBarrier (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the instruction synchronization barrier instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    DbgpArmPrintBarrierMode(Context->Operand1, Context->Instruction);
    strcpy(Context->Mnemonic, ARM_ISB_MNEMONIC);
    return;
}

VOID
DbgpArmDecodeParallelAdditionSubtraction (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the parallel addition and subtraction instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG Op2;
    ULONG ParallelOp;
    ULONG Rd;
    ULONG Rm;
    ULONG Rn;
    ULONG Unsigned;

    Instruction = Context->Instruction;
    Unsigned = 0;
    if ((Instruction & ARM_PARALLEL_ARITHMETIC_UNSIGNED) != 0) {
        Unsigned = 1;
    }

    ParallelOp = (Instruction & ARM_PARALLEL_ARITHMETIC_OP1_MASK) >>
                  ARM_PARALLEL_ARITHMETIC_OP1_SHIFT;

    Op2 = (Instruction & ARM_PARALLEL_ARITHMETIC_OP2_MASK) >>
          ARM_PARALLEL_ARITHMETIC_OP2_SHIFT;

    ParallelOp |= ((Op2 - 1) << 2);
    BaseMnemonic = NULL;
    if (ParallelOp < ARM_PARALLEL_ARITHMETIC_OP_MAX) {
        BaseMnemonic = DbgArmParallelArithmeticMnemonics[Unsigned][ParallelOp];
    }

    if (BaseMnemonic == NULL) {
        DbgpArmDecodeUndefined(Context);
        return;
    }

    Rn = (Instruction & ARM_PARALLEL_ARITHMETIC_RN_MASK) >>
         ARM_PARALLEL_ARITHMETIC_RN_SHIFT;

    Rd = (Instruction & ARM_PARALLEL_ARITHMETIC_RD_MASK) >>
         ARM_PARALLEL_ARITHMETIC_RD_SHIFT;

    Rm = (Instruction & ARM_PARALLEL_ARITHMETIC_RM_MASK) >>
         ARM_PARALLEL_ARITHMETIC_RM_SHIFT;

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
    strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    return;
}

VOID
DbgpArmDecodePackingInstructions (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the packing, unpacking, saturation, and reversal
    instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ARM_DECODE_WITH_TABLE(Context, DbgArmPackingInstructionTable);
    return;
}

VOID
DbgpArmDecodeExtensionWithRotation (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes extension with rotation instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG Op1;
    ULONG Rd;
    ULONG Rm;
    PSTR RmString;
    ULONG Rn;
    ULONG Rotation;
    PSTR RotationString;
    BOOL TwoRegisters;

    Instruction = Context->Instruction;
    Rn = (Instruction & ARM_PACKING_RN_MASK) >> ARM_PACKING_RN_SHIFT;
    Rd = (Instruction & ARM_PACKING_RD_MASK) >> ARM_PACKING_RD_SHIFT;
    Rm = (Instruction & ARM_PACKING_RM_MASK) >> ARM_PACKING_RM_SHIFT;
    Op1 = (Instruction & ARM_PACKING_OP1_MASK) >> ARM_PACKING_OP1_SHIFT;
    TwoRegisters = FALSE;
    if (Rn == 0xF) {
        BaseMnemonic = DbgArmExtensionRotationMnemonics[1][Op1];
        RmString = Context->Operand2;
        RotationString = Context->Operand3;
        TwoRegisters = TRUE;

    } else {
        BaseMnemonic = DbgArmExtensionRotationMnemonics[0][Op1];
        RmString = Context->Operand3;
        RotationString = Context->Operand4;
    }

    Rotation = (Instruction & ARM_PACKING_ROTATION_MASK) >>
               ARM_PACKING_ROTATION_SHIFT;

    Rotation <<= 3;

    //
    // If no mnemonic was found for the given op value, then the instruction is
    // undefined.
    //

    if (BaseMnemonic == NULL) {
        DbgpArmDecodeUndefined(Context);
        return;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    if (TwoRegisters == FALSE) {
        strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
    }

    strcpy(RmString, DbgArmRegisterNames[Rm]);
    if (Rotation != 0) {
        sprintf(RotationString, "%s #%d", ARM_ROR_MNEMONIC, Rotation);
    }

    return;
}

VOID
DbgpArmDecodeSelectBytes (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the select byte instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG Rd;
    ULONG Rm;
    ULONG Rn;

    Instruction = Context->Instruction;
    Rn = (Instruction & ARM_PACKING_RN_MASK) >> ARM_PACKING_RN_SHIFT;
    Rd = (Instruction & ARM_PACKING_RD_MASK) >> ARM_PACKING_RD_SHIFT;
    Rm = (Instruction & ARM_PACKING_RM_MASK) >> ARM_PACKING_RM_SHIFT;
    strcpy(Context->Mnemonic, ARM_SEL_MNEMONIC);
    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
    strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    return;
}

VOID
DbgpArmDecodePackHalfword (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the pack halfword instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Immediate;
    ULONG Instruction;
    ULONG Rd;
    ULONG Rm;
    ULONG Rn;
    PSTR ShiftMnemonic;

    Instruction = Context->Instruction;
    Rn = (Instruction & ARM_PACKING_RN_MASK) >> ARM_PACKING_RN_SHIFT;
    Rd = (Instruction & ARM_PACKING_RD_MASK) >> ARM_PACKING_RD_SHIFT;
    Rm = (Instruction & ARM_PACKING_RM_MASK) >> ARM_PACKING_RM_SHIFT;
    Immediate = (Instruction & ARM_PACKING_IMMEDIATE5_MASK) >>
                ARM_PACKING_IMMEDIATE5_SHIFT;

    if ((Instruction & ARM_PACKING_TB_BIT) != 0) {
        BaseMnemonic = ARM_PKHTB_MNEMONIC;
        ShiftMnemonic = ARM_ASR_MNEMONIC;
        if (Immediate == 0) {
            Immediate = 32;
        }

    } else {
        BaseMnemonic = ARM_PKHBT_MNEMONIC;
        ShiftMnemonic = ARM_LSL_MNEMONIC;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    strcpy(Context->Operand2, DbgArmRegisterNames[Rn]);
    strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    if (Immediate != 0) {
        sprintf(Context->Operand4, "%s #%d", ShiftMnemonic, Immediate);
    }

    return;
}

VOID
DbgpArmDecodeReverse (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the reverse instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG Op1;
    ULONG Op2;
    ULONG Rd;
    ULONG ReverseIndex;
    ULONG Rm;

    Instruction = Context->Instruction;
    Rd = (Instruction & ARM_PACKING_RD_MASK) >> ARM_PACKING_RD_SHIFT;
    Rm = (Instruction & ARM_PACKING_RM_MASK) >> ARM_PACKING_RM_SHIFT;
    Op1 = (Instruction & ARM_PACKING_OP1_MASK) >> ARM_PACKING_OP1_SHIFT;
    Op2 = (Instruction & ARM_PACKING_OP2_MASK) >> ARM_PACKING_OP2_SHIFT;
    ReverseIndex = (Op1 & ARM_PACKING_OP1_REV_MASK) >>
                   ARM_PACKING_OP1_REV_SHIFT;

    ReverseIndex |= ((Op2 & ARM_PACKING_OP2_REV_MASK) >>
                     ARM_PACKING_OP2_REV_SHIFT) << 1;

    BaseMnemonic = DbgArmReverseMnemonics[ReverseIndex];
    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    strcpy(Context->Operand2, DbgArmRegisterNames[Rm]);
    return;
}

VOID
DbgpArmDecodeSaturate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the saturate instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR MnemonicPrefix;
    PSTR MnemonicSuffix;
    ULONG Rd;
    ULONG Rm;
    ULONG SaturateImmediate;
    ULONG ShiftImmediate;
    PSTR ShiftMnemonic;

    Instruction = Context->Instruction;
    Rd = (Instruction & ARM_PACKING_RD_MASK) >> ARM_PACKING_RD_SHIFT;
    Rm = (Instruction & ARM_PACKING_RM_MASK) >> ARM_PACKING_RM_SHIFT;
    SaturateImmediate = (Instruction & ARM_PACKING_SAT_IMMEDIATE_MASK) >>
                        ARM_PACKING_SAT_IMMEDIATE_SHIFT;

    MnemonicPrefix = ARM_USAT_MNEMONIC;
    if ((Instruction & ARM_PACKING_SAT_UNSIGNED) == 0) {
        SaturateImmediate += 1;
        MnemonicPrefix = ARM_SSAT_MNEMONIC;
    }

    //
    // If this is a two 16-bit saturate, then there is no shift at the end.
    //

    ShiftImmediate = 0;
    MnemonicSuffix = ARM_SAT16_MNEMONIC;
    if ((Instruction & ARM_PACKING_SAT16_BIT) == 0) {
        MnemonicSuffix = "";
        ShiftImmediate = (Instruction & ARM_PACKING_IMMEDIATE5_MASK) >>
                         ARM_PACKING_IMMEDIATE5_SHIFT;

        ShiftMnemonic = ARM_LSL_MNEMONIC;
        if ((Instruction & ARM_PACKING_SHIFT_BIT) != 0) {
            ShiftMnemonic = ARM_ASR_MNEMONIC;
            if (ShiftImmediate == 0) {
                ShiftImmediate = 32;
            }
        }
    }

    sprintf(Context->Mnemonic,
            "%s%s%s",
            MnemonicPrefix,
            ARM_SAT_MNEMONIC,
            MnemonicSuffix);

    strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
    sprintf(Context->Operand2, "#%d", SaturateImmediate);
    strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    if (ShiftImmediate != 0) {
        sprintf(Context->Operand4, "%s #%d", ShiftMnemonic, ShiftImmediate);
    }

    return;
}

VOID
DbgpArmDecodeSumofAbsoluteDifferences (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the sum of absolute differences instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG RegisterA;
    ULONG RegisterD;
    ULONG RegisterM;
    ULONG RegisterN;
    BOOL ThreeOperands;

    Instruction = Context->Instruction;
    RegisterA = (Instruction & ARM_USAD_RA_MASK) >> ARM_USAD_RA_SHIFT;
    RegisterD = (Instruction & ARM_USAD_RD_MASK) >> ARM_USAD_RD_SHIFT;
    RegisterM = (Instruction & ARM_USAD_RM_MASK) >> ARM_USAD_RM_SHIFT;
    RegisterN = (Instruction & ARM_USAD_RN_MASK) >> ARM_USAD_RN_SHIFT;
    if (RegisterD == 0xF) {
        BaseMnemonic = ARM_USAD_MNEMONIC;
        ThreeOperands = TRUE;

    } else {
        BaseMnemonic = ARM_USADA_MNEMONIC;
        ThreeOperands = FALSE;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->Operand1, DbgArmRegisterNames[RegisterD]);
    strcpy(Context->Operand2, DbgArmRegisterNames[RegisterN]);
    strcpy(Context->Operand3, DbgArmRegisterNames[RegisterM]);
    if (ThreeOperands == FALSE) {
        strcpy(Context->Operand1, DbgArmRegisterNames[RegisterA]);
    }

    return;
}

VOID
DbgpArmDecodeBitFieldInstructions (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the bit field instructions.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG Lsb;
    PSTR LsbString;
    BOOL OneRegister;
    ULONG RegisterD;
    ULONG RegisterN;
    ULONG Width;

    Instruction = Context->Instruction;
    RegisterD = (Instruction & ARM_BIT_FIELD_RD_MASK) >> ARM_BIT_FIELD_RD_SHIFT;
    RegisterN = (Instruction & ARM_BIT_FIELD_RN_MASK) >> ARM_BIT_FIELD_RN_SHIFT;
    Lsb = (Instruction & ARM_BIT_FIELD_LSB_MASK) >> ARM_BIT_FIELD_LSB_SHIFT;
    Width = (Instruction & ARM_BIT_FIELD_WIDTH_MINUS_1_MASK) >>
            ARM_BIT_FIELD_WIDTH_MINUS_1_SHIFT;

    Width += 1;
    OneRegister = FALSE;
    if ((Instruction & ARM_BIT_FIELD_EXTRACT_BIT) != 0) {
        if ((Instruction & ARM_BIT_FIELD_UNSIGNED_BIT) != 0) {
            BaseMnemonic = ARM_UBFX_MNEMONIC;

        } else {
            BaseMnemonic = ARM_SBFX_MNEMONIC;
        }

    } else {
        if (RegisterN == 0xF) {
            BaseMnemonic = ARM_BFC_MNEMONIC;
            OneRegister = TRUE;

        } else {
            BaseMnemonic = ARM_BFI_MNEMONIC;
        }

        Width -= Lsb;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->Operand1, DbgArmRegisterNames[RegisterD]);
    if (OneRegister == FALSE) {
        strcpy(Context->Operand2, DbgArmRegisterNames[RegisterN]);
        LsbString = Context->Operand3;

    } else {
        LsbString = Context->Operand2;
    }

    sprintf(LsbString, "#%d, #%d", Lsb, Width);
    return;
}

VOID
DbgpArmDecodePermanentlyUndefined (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes the permanently undefined instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Value;

    strcpy(Context->Mnemonic, ARM_UNDEFINED_INSTRUCTION_MNEMONIC);
    Value = ARM_SERVICE_BUILD_IMMEDIATE12_4(Context->Instruction);
    sprintf(Context->Operand1, "#%d  ; 0x%x", Value, Value);
    return;
}

VOID
DbgpArmDecodeLoadStore (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a load/store to a word or single byte.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    UCHAR BaseRegister;
    UCHAR DestinationRegister;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG Offset;
    UCHAR OffsetRegister;
    CHAR ShiftString[15];
    UCHAR ShiftValue;
    UCHAR Sign;
    UCHAR WriteBack;

    BaseMnemonic = "";
    MnemonicSuffix = "";
    Instruction = Context->Instruction;

    //
    // Get the destination register.
    //

    DestinationRegister = (Instruction & ARM_DESTINATION_REGISTER_MASK) >>
                          ARM_DESTINATION_REGISTER_SHIFT;

    //
    // Determine the mnemonic.
    //

    if ((Instruction & ARM_LOAD_BIT) != 0) {
        BaseMnemonic = ARM_LOAD_MNEMONIC;

    } else {
        BaseMnemonic = ARM_STORE_MNEMONIC;
    }

    //
    // Determine the suffix. The translate bit only applies if P == 0.
    //

    if ((Instruction & ARM_LOAD_STORE_BYTE_BIT) != 0) {
        if (((Instruction & ARM_PREINDEX_BIT) == 0) &&
            ((Instruction & ARM_LOAD_STORE_TRANSLATE_BIT) != 0)) {

            MnemonicSuffix = ARM_TRANSLATE_BYTE_SUFFIX;

        } else {
            MnemonicSuffix = ARM_BYTE_TRANSFER_SUFFIX;
        }

    } else if (((Instruction & ARM_PREINDEX_BIT) == 0) &&
               ((Instruction & ARM_LOAD_STORE_TRANSLATE_BIT) != 0)) {

        MnemonicSuffix = ARM_TRANSLATE_SUFFIX;
    }

    sprintf(Context->Mnemonic, "%s%s", BaseMnemonic, MnemonicSuffix);

    //
    // For immediate and register offsets, determine the sign of the offset.
    //

    if ((Instruction & ARM_ADD_SUBTRACT_BIT) != 0) {
        Sign = '+';

    } else {
        Sign = '-';
    }

    //
    // For pre-index and offset modes, determine whether a writeback is
    // performed.
    //

    WriteBack = 0;
    if ((Instruction & ARM_WRITE_BACK_BIT) != 0) {
        WriteBack = '!';
    }

    BaseRegister = (Instruction & ARM_LOAD_STORE_BASE_MASK) >>
                   ARM_LOAD_STORE_BASE_SHIFT;

    //
    // Print the operand in the correct addressing form. There are 9 unique
    // forms. Start with the immediate bit, which when 0 means the immediate
    // form is used.
    //

    if ((Instruction & ARM_IMMEDIATE_BIT) == 0) {
        Offset = Instruction & 0x00000FFF;

        //
        // Post-Indexed addressing.
        //

        if ((Instruction & ARM_PREINDEX_BIT) == 0) {
            if (Offset == 0) {
                sprintf(Context->Operand2,
                        "[%s]",
                        DbgArmRegisterNames);

            } else {
                sprintf(Context->Operand2,
                        "[%s], #%c%d",
                        DbgArmRegisterNames[BaseRegister],
                        Sign,
                        Offset);
            }

        //
        // Pre-indexed or offset addressing.
        //

        } else {
            if (Offset == 0) {
                sprintf(Context->Operand2,
                        "[%s]%c",
                        DbgArmRegisterNames[BaseRegister],
                        WriteBack);

            } else {
                sprintf(Context->Operand2,
                        "[%s, #%c%d]%c",
                        DbgArmRegisterNames[BaseRegister],
                        Sign,
                        Offset,
                        WriteBack);
            }
        }

    //
    // Register offset/index or scaled register offset/index.
    //

    } else {

        //
        // Decode the shifted register string.
        //

        OffsetRegister = Instruction & ARM_OFFSET_REGISTER;
        ShiftValue = (Instruction & ARM_LOAD_STORE_SHIFT_VALUE_MASK) >>
                     ARM_LOAD_STORE_SHIFT_VALUE_SHIFT;

        DbgpArmDecodeImmediateShift(ShiftString,
                                    sizeof(ShiftString),
                                    OffsetRegister,
                                    (Instruction & ARM_SHIFT_TYPE),
                                    ShiftValue);

        //
        // Check out the pre-index bit. If it's zero, the addressing mode is
        // post-indexed.
        //

        if ((Instruction & ARM_PREINDEX_BIT) == 0) {
            sprintf(Context->Operand2,
                    "[%s], %c%s",
                    DbgArmRegisterNames[BaseRegister],
                    Sign,
                    ShiftString);

        //
        // Pre-indexed or offset addressing.
        //

        } else {
            sprintf(Context->Operand2,
                    "[%s, %c%s]%c",
                    DbgArmRegisterNames[BaseRegister],
                    Sign,
                    ShiftString,
                    WriteBack);
        }
    }

    //
    // Write the first operand.
    //

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

VOID
DbgpArmDecodeExtraLoadStore (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an "extra load/store" in both the register and
    immediate forms.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    UCHAR BaseRegister;
    UCHAR DestinationRegister;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    UCHAR Offset;
    UCHAR OffsetRegister;
    UCHAR Sign;
    UCHAR WriteBack;

    BaseMnemonic = "ERR";
    MnemonicSuffix = "";
    Instruction = Context->Instruction;

    //
    // Determine whether or not this is a load or store, and what data size it
    // is.
    //

    switch (Instruction & ARM_HALF_WORD_TRANSFER_MASK) {
    case ARM_STORE_HALF_WORD:
        BaseMnemonic = ARM_STORE_MNEMONIC;
        MnemonicSuffix = ARM_HALF_WORD_SUFFIX;
        break;

    case ARM_LOAD_DOUBLE_WORD:
        BaseMnemonic = ARM_LOAD_MNEMONIC;
        MnemonicSuffix = ARM_DOUBLE_WORD_SUFFIX;
        break;

    case ARM_STORE_DOUBLE_WORD:
        BaseMnemonic = ARM_STORE_MNEMONIC;
        MnemonicSuffix = ARM_DOUBLE_WORD_SUFFIX;
        break;

    case ARM_LOAD_UNSIGNED_HALF_WORD:
        BaseMnemonic = ARM_LOAD_MNEMONIC;
        MnemonicSuffix = ARM_HALF_WORD_SUFFIX;
        break;

    case ARM_LOAD_SIGNED_BYTE:
        BaseMnemonic = ARM_LOAD_MNEMONIC;
        MnemonicSuffix = ARM_SIGNED_BYTE_SUFFIX;
        break;

    case ARM_LOAD_SIGNED_HALF_WORD:
        BaseMnemonic = ARM_LOAD_MNEMONIC;
        MnemonicSuffix = ARM_SIGNED_HALF_WORD_SUFFIX;
        break;

    //
    // Invalid configuration.
    //

    default:
        return;
    }

    snprintf(Context->Mnemonic,
             ARM_OPERAND_LENGTH,
             "%s%s",
             BaseMnemonic,
             MnemonicSuffix);

    //
    // Determine whether to add or subtract the offset.
    //

    if ((Instruction & ARM_ADD_SUBTRACT_BIT) != 0) {
        Sign = '+';

    } else {
        Sign = '-';
    }

    //
    // For pre-indexed addressing modes, determine whether or not the calculated
    // address is written back. (If it's not, that's called offset addressing).
    //

    WriteBack = 0;
    if ((Instruction & ARM_WRITE_BACK_BIT) != 0) {
        WriteBack = '!';
    }

    //
    // Print the destination register in the first operand.
    //

    DestinationRegister = (Instruction & ARM_DESTINATION_REGISTER_MASK) >>
                          ARM_DESTINATION_REGISTER_SHIFT;

    sprintf(Context->Operand1, "%s", DbgArmRegisterNames[DestinationRegister]);
    BaseRegister = (Instruction & 0x000F0000) >> 16;

    //
    // Handle the register form.
    //

    if ((Instruction & ARM_HALF_WORD_REGISTER_MASK) ==
        ARM_HALF_WORD_REGISTER_VALUE) {

        OffsetRegister = Instruction & 0x0000000F;

        //
        // If P is 0, then it's post-indexed addressing. W had better be zero
        // in this case. Post-indexed addressing means the base register is
        // used as the address, then the offset register is added to the base
        // and written back to the base. It takes the form of [Rn], +/-Rm.
        //

        if ((Instruction & ARM_PREINDEX_BIT) == 0) {
            if ((Instruction & ARM_WRITE_BACK_BIT) != 0) {
                return;
            }

            sprintf(Context->Operand2,
                    "[%s], %c%s",
                    DbgArmRegisterNames[BaseRegister],
                    Sign,
                    DbgArmRegisterNames[OffsetRegister]);

        //
        // P is 1, which means the addressing form is either pre-indexed or
        // offset based. Pre-indexed means the offset register is added to the
        // base to form the address, and is then written back. Offset addressing
        // is the same but no writeback is performed.
        //

        } else {
            sprintf(Context->Operand2,
                   "[%s, %c%s]%c",
                   DbgArmRegisterNames[BaseRegister],
                   Sign,
                   DbgArmRegisterNames[OffsetRegister],
                   WriteBack);
        }

    //
    // Handle the immediate form.
    //

    } else {
        Offset = ((Instruction & 0x00000F00) >> 4) | (Instruction & 0x0000000F);

        //
        // Like in the register form, P == 0 indicates post-indexed addressing.
        // W must be zero (just don't print if it it's not).
        //

        if ((Instruction & ARM_PREINDEX_BIT) == 0) {
            sprintf(Context->Operand2,
                    "[%s], #%c%d",
                    DbgArmRegisterNames[BaseRegister],
                    Sign,
                    Offset);

        //
        // Like the register case P == 1 means the addressing form is either
        // pre-indexed or offset based, depending on the U bit. If it is
        // offset based (i.e. no write-back) and the offset is zero, don't
        // print the offset.
        //

        } else if ((WriteBack == 0) && (Offset == 0)) {
            sprintf(Context->Operand2,
                   "[%s]",
                   DbgArmRegisterNames[BaseRegister]);

        } else {
            sprintf(Context->Operand2,
                   "[%s, #%c%d]%c",
                   DbgArmRegisterNames[BaseRegister],
                   Sign,
                   Offset,
                   WriteBack);
        }
    }

    return;
}

VOID
DbgpArmDecodeLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a block load or store of multiple registers.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG Operation;
    BOOL PushPop;
    UCHAR Register;
    ULONG RegisterList;
    ULONG RegisterListCount;
    PSTR RegisterListString;

    Instruction = Context->Instruction;
    PushPop = FALSE;
    Operation = Instruction & ARM_LOAD_STORE_OP_MASK;
    Register = (Instruction & ARM_LOAD_STORE_REGISTER_MASK) >>
               ARM_LOAD_STORE_REGISTER_SHIFT;

    RegisterList = Instruction & ARM_REGISTER_LIST_MASK;
    RegisterListCount = RtlCountSetBits32(RegisterList);

    //
    // If the instruction is targeting the stack register, then it may be a
    // push or a pop.
    //

    if ((Register == ARM_STACK_REGISTER) &&
        (RegisterListCount > 1) &&
        ((Operation == ARM_LOAD_STORE_OP_POP) ||
         (Operation == ARM_LOAD_STORE_OP_PUSH))) {

        if ((Instruction & ARM_LOAD_BIT) != 0) {
            BaseMnemonic = ARM_LOAD_POP_MNEMONIC;

        } else {
            BaseMnemonic = ARM_STORE_PUSH_MNEMONIC;
        }

        MnemonicSuffix = "";
        PushPop = TRUE;

    //
    // Otherwise determine if it is a load or a store and get the appropriate
    // suffix.
    //

    } else {
        if ((Instruction & ARM_LOAD_BIT) != 0) {
            BaseMnemonic = ARM_LOAD_MULTIPLE_MNEMONIC;

        } else {
            BaseMnemonic = ARM_STORE_MULTIPLE_MNEMONIC;
        }

        MnemonicSuffix = DbgpArmGetLoadStoreTypeString(Instruction);
    }

    snprintf(Context->Mnemonic,
             ARM_OPERAND_LENGTH,
             "%s%s",
             BaseMnemonic,
             MnemonicSuffix);

    //
    // Write the register (the first operand). Add the ! if the operation does
    // a write back. Push/pop operations are always write back.
    //

    if (PushPop == FALSE) {
        if ((Instruction & ARM_WRITE_BACK_BIT) != 0) {
            sprintf(Context->Operand1, "%s!", DbgArmRegisterNames[Register]);

        } else {
            sprintf(Context->Operand1, "%s", DbgArmRegisterNames[Register]);
        }

        RegisterListString = Context->Operand2;

    } else {
        RegisterListString = Context->Operand1;
    }

    //
    // Get the list of registers to be loaded or stored.
    //

    DbgpArmDecodeRegisterList(RegisterListString,
                              sizeof(RegisterListString),
                              RegisterList);

    //
    // Indicate whether or not the saved PSR (SPSR) should be used instead of
    // the current PSR (CPSR). This is typically only used for returning from
    // exceptions.
    //

    if ((Instruction & ARM_USE_SAVED_PSR_BIT) != 0) {
        strcat(RegisterListString, "^");
    }

    return;
}

VOID
DbgpArmDecodeDataProcessing (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a standard data processing instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    UCHAR DestinationRegister;
    UCHAR ImmediateShift;
    ULONG ImmediateValue;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    UCHAR Opcode;
    UCHAR Operand2Register;
    UCHAR OperandRegister;
    UCHAR ShiftRegister;
    CHAR ShiftString[35];
    PSTR ShiftType;

    BaseMnemonic = "ERR";
    Instruction = Context->Instruction;
    MnemonicSuffix = "";
    ShiftString[0] = '\0';

    //
    // Get the opcode.
    //

    Opcode = (Instruction & ARM_DATA_PROCESSING_OP_MASK) >>
             ARM_DATA_PROCESSING_OP_SHIFT;

    //
    // Ignore the low bit.
    //

    Opcode >>= 1;
    BaseMnemonic = DbgArmDataProcessingMnemonics[Opcode];

    //
    // Determine whether to add the S bit. Compare instructions don't need the
    // S because it's assumed (it's the whole point of a compare to set the
    // flags).
    //

    if (((Instruction & ARM_SET_FLAGS_BIT) != 0) &&
        ((Opcode < ARM_DATA_PROCESSING_COMPARE_INSTRUCTION_MIN) ||
         (Opcode > ARM_DATA_PROCESSING_COMPARE_INSTRUCTION_MAX))) {

        MnemonicSuffix = ARM_SET_FLAGS_MNEMONIC;
    }

    //
    // Build the shift operand string.
    //

    if ((Context->Instruction & ARM_IMMEDIATE_BIT) != 0) {

        //
        // The immediate form takes an 8-bit integer and shifts it by any even
        // number in the shift_imm bits.
        //

        ImmediateValue = Instruction & ARM_DATA_PROCESSING_IMMEDIATE8_MASK;
        ImmediateShift = ((Instruction &
                           ARM_DATA_PROCESSING_IMMEDIATE_ROTATE_MASK) >>
                          ARM_DATA_PROCESSING_IMMEDIATE_ROTATE_SHIFT) * 2;

        //
        // Rotate the value right by the specified number of bits.
        //

        while (ImmediateShift > 0) {
            ImmediateShift -= 1;
            if ((ImmediateValue & 0x1) != 0) {
                ImmediateValue = (ImmediateValue >> 1) | 0x80000000;

            } else {
                ImmediateValue = ImmediateValue >> 1;
            }
        }

        sprintf(ShiftString, "#%d  ; 0x%x", ImmediateValue, ImmediateValue);

    } else {
        Operand2Register = Instruction &
                           ARM_DATA_PROCESSING_OPERAND2_REGISTER_MASK;

        //
        // The register form can be shifted, by either an immediate or another
        // register. Handle the register shift case first.
        //

        if ((Instruction &
             ARM_DATA_PROCESSING_REGISTER_REGISTER_SHIFT_BIT) != 0) {

            ShiftRegister = (Instruction &
                             ARM_DATA_PROCESSING_SHIFT_REGISTER_MASK) >>
                            ARM_DATA_PROCESSING_SHIFT_REGISTER_SHIFT;

            ShiftType = "ERR";
            switch (Instruction & ARM_SHIFT_TYPE) {
            case ARM_SHIFT_LSL:
                ShiftType = ARM_LSL_MNEMONIC;
                break;

            case ARM_SHIFT_LSR:
                ShiftType = ARM_LSR_MNEMONIC;
                break;

            case ARM_SHIFT_ASR:
                ShiftType = ARM_ASR_MNEMONIC;
                break;

            case ARM_SHIFT_ROR:
                ShiftType = ARM_ROR_MNEMONIC;
                break;

            //
            // This case should never hit since all 4 bit combinations were
            // handled.
            //

            default:
                break;
            }

            //
            // If this is the move instruction, then the canonical form
            // actually uses the shift mnemonic for the instruction mnemonic.
            //

            if (Opcode == ARM_DATA_PROCESSING_MOVE_OPCODE) {
                BaseMnemonic = ShiftType;
                sprintf(ShiftString,
                        "%s, %s",
                        DbgArmRegisterNames[Operand2Register],
                        DbgArmRegisterNames[ShiftRegister]);

            } else {
                sprintf(ShiftString,
                        "%s, %s %s",
                        DbgArmRegisterNames[Operand2Register],
                        ShiftType,
                        DbgArmRegisterNames[ShiftRegister]);
            }

        //
        // Shift by an immediate value.
        //

        } else {
            ImmediateValue = (Instruction &
                              ARM_DATA_PROCESSING_SHIFT_IMMEDIATE_MASK) >>
                             ARM_DATA_PROCESSING_SHIFT_IMMEDIATE_SHIFT;

            //
            // If this is a move instruction, then it may have a canonical
            // form.
            //

            if (Opcode == ARM_DATA_PROCESSING_MOVE_OPCODE) {
                ShiftType = NULL;
                switch (Instruction & ARM_SHIFT_TYPE) {
                case ARM_SHIFT_LSL:
                    if (ImmediateValue != 0) {
                        ShiftType = ARM_LSL_MNEMONIC;
                    }

                    break;

                case ARM_SHIFT_LSR:
                    if (ImmediateValue == 0) {
                        ImmediateValue = 32;
                    }

                    ShiftType = ARM_LSR_MNEMONIC;
                    break;

                case ARM_SHIFT_ASR:
                    if (ImmediateValue == 0) {
                        ImmediateValue = 32;
                    }

                    ShiftType = ARM_ASR_MNEMONIC;
                    break;

                case ARM_SHIFT_ROR:
                    if (ImmediateValue == 0) {
                        ShiftType = ARM_RRX_MNEMONIC;

                    } else {
                        ShiftType = ARM_ROR_MNEMONIC;
                    }

                    break;

                //
                // This case should never hit since all 4 bit combinations were
                // handled.
                //

                default:
                    break;
                }

                //
                // If a shift type was set, then use the canonical form and
                // override the base mnemonic.
                //

                if (ShiftType != NULL) {
                    BaseMnemonic = ShiftType;
                }

                //
                // A MOV with no shift and RRX do no print an immediate value.
                // There are the only cases where the immediate value is 0.
                //

                if (ImmediateValue == 0) {
                    sprintf(ShiftString,
                            "%s",
                            DbgArmRegisterNames[Operand2Register]);

                } else {
                    sprintf(ShiftString,
                            "%s, #%d",
                            DbgArmRegisterNames[Operand2Register],
                            ImmediateValue);
                }

            } else {
                DbgpArmDecodeImmediateShift(ShiftString,
                                            sizeof(ShiftString),
                                            Operand2Register,
                                            (Instruction & ARM_SHIFT_TYPE),
                                            ImmediateValue);
            }
        }
    }

    //
    // Print out the mnemonic, it may have been modified while computing the
    // shift string.
    //

    snprintf(Context->Mnemonic,
             ARM_OPERAND_LENGTH,
             "%s%s",
             BaseMnemonic,
             MnemonicSuffix);

    DestinationRegister = (Instruction & ARM_DESTINATION_REGISTER_MASK) >>
                          ARM_DESTINATION_REGISTER_SHIFT;

    OperandRegister = (Instruction &
                       ARM_DATA_PROCESSING_OPERAND_REGISTER_MASK) >>
                      ARM_DATA_PROCESSING_OPERAND_REGISTER_SHIFT;

    //
    // Print the operands depending on the opcode. Compare instructions take
    // the form Rn, <shifter_operand>
    //

    if ((Opcode >= ARM_DATA_PROCESSING_COMPARE_INSTRUCTION_MIN) &&
        (Opcode <= ARM_DATA_PROCESSING_COMPARE_INSTRUCTION_MAX)) {

        strcpy(Context->Operand1, DbgArmRegisterNames[OperandRegister]);
        strcpy(Context->Operand2, ShiftString);

    //
    // Move instructions take the form Rd, <shift_operand>.
    //

    } else if ((Opcode == ARM_DATA_PROCESSING_MOVE_OPCODE) ||
               (Opcode == ARM_DATA_PROCESSING_MOVE_NOT_OPCODE)) {

        strcpy(Context->Operand1, DbgArmRegisterNames[DestinationRegister]);
        strcpy(Context->Operand2, ShiftString);

    //
    // All normal data processing instructions take the form Rd, Rn,
    // <shift_operand>.
    //

    } else {
        strcpy(Context->Operand1, DbgArmRegisterNames[DestinationRegister]);
        strcpy(Context->Operand2, DbgArmRegisterNames[OperandRegister]);
        strcpy(Context->Operand3, ShiftString);
    }

    return;
}

VOID
DbgpArmDecodeLoadImmediate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a 16-bit immediate load instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    UCHAR DestinationRegister;
    ULONG ImmediateValue;
    ULONG Instruction;

    Instruction = Context->Instruction;

    //
    // Get the opcode.
    //

    switch (Instruction & ARM_IMMEDIATE_LOAD_OP_MASK) {
    case ARM_IMMEDIATE_LOAD_OP_MOVW:
        BaseMnemonic = ARM_MOVW_MNEMONIC;
        break;

    case ARM_IMMEDIATE_LOAD_OP_MOVT:
        BaseMnemonic = ARM_MOVT_MNEMONIC;
        break;

    //
    // Invalid configuration.
    //

    default:
        return;
    }

    snprintf(Context->Mnemonic,
             ARM_OPERAND_LENGTH,
             "%s",
             BaseMnemonic);

    //
    // Build the immediate value string.
    //

    ImmediateValue = (Instruction & ARM_IMMEDIATE_LOAD_IMMEDIATE4_MASK) >>
                     ARM_IMMEDIATE_LOAD_IMMEDIATE4_SHIFT;

    ImmediateValue <<= 12;
    ImmediateValue |= ((Instruction & ARM_IMMEDIATE_LOAD_IMMEDIATE12_MASK) >>
                       ARM_IMMEDIATE_LOAD_IMMEDIATE12_SHIFT);

    //
    // Determine the destination register.
    //

    DestinationRegister = (Instruction & ARM_DESTINATION_REGISTER_MASK) >>
                          ARM_DESTINATION_REGISTER_SHIFT;

    //
    // The 16 immediate load instructions take the form Rn, <immediate_operand>
    //

    strcpy(Context->Operand1, DbgArmRegisterNames[DestinationRegister]);
    sprintf(Context->Operand2, "#%d  ; 0x%x", ImmediateValue, ImmediateValue);
    return;
}

VOID
DbgpArmDecodeMiscellaneous (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a miscellaneous instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR Mnemonic;
    ULONG Op;
    ULONG Op2;
    ULONG R0;
    ULONG Rd;
    LONG Value;

    Mnemonic = "ERR";
    Instruction = Context->Instruction;
    Op2 = (Instruction & ARM_MISCELLANEOUS1_OP2_MASK) >>
          ARM_MISCELLANEOUS1_OP2_SHIFT;

    Op = (Instruction & ARM_MISCELLANEOUS1_OP_MASK) >>
         ARM_MISCELLANEOUS1_OP_SHIFT;

    R0 = Instruction & ARM_MOVE_STATUS_R0_MASK;

    //
    // Handle an MSR or MRS instruction.
    //

    if (Op2 == ARM_MISCELLANEOUS1_OP2_STATUS) {
        Rd = (Instruction & ARM_MOVE_STATUS_RD_MASK) >>
             ARM_MOVE_STATUS_RD_SHIFT;

        //
        // Handle an MSR.
        //

        if ((Op & ARM_MISCELLANEOUS1_OP_MSR) != 0) {
            Mnemonic = ARM_MSR_MNEMONIC;
            strcpy(Context->Operand2, DbgArmRegisterNames[R0]);

            //
            // Handle banked MSR vs non-banked.
            //

            if ((Instruction & ARM_MOVE_STATUS_BANKED) != 0) {
                strcpy(Context->Operand1,
                       DbgpArmGetBankedRegisterString(Instruction));

            } else {
                DbgpArmPrintStatusRegister(Context->Operand1, Instruction);
            }

        //
        // This is an MRS instruction.
        //

        } else {
            Mnemonic = ARM_MRS_MNEMONIC;
            strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
            if ((Instruction & ARM_MOVE_STATUS_BANKED) != 0) {
                strcpy(Context->Operand2,
                       DbgpArmGetBankedRegisterString(Instruction));

            } else {
                DbgpArmPrintStatusRegister(Context->Operand2, Instruction);
            }
        }

    //
    // Handle either a BX or CLZ.
    //

    } else if (Op2 == ARM_MISCELLANEOUS1_OP2_BX_CLZ) {
        if (Op == ARM_MISCELLANEOUS1_OP_BX) {
            Mnemonic = ARM_BX_MNEMONIC;

        } else if (Op == ARM_MISCELLANEOUS1_OP_CLZ) {
            Mnemonic = ARM_CLZ_MNEMONIC;
        }

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

    //
    // Handle a BXJ (register).
    //

    } else if (Op2 == ARM_MISCELLANEOUS1_OP2_BXJ) {
        Mnemonic = ARM_BXJ_MNEMONIC;
        strcpy(Context->Operand1, DbgArmRegisterNames[R0]);

    //
    // Handle a BLX (register).
    //

    } else if (Op2 == ARM_MISCELLANEOUS1_OP2_BLX) {
        Mnemonic = ARM_BLX_MNEMONIC;
        strcpy(Context->Operand1, DbgArmRegisterNames[R0]);

    //
    // Handle (or don't) saturating addition or subtraction.
    //

    } else if (Op2 == ARM_MISCELLANEOUS1_OP2_SATURATING_ADDITION) {

    //
    // Handle a simple ERET.
    //

    } else if (Op2 == ARM_MISCELLANEOUS1_OP2_ERET) {
        Mnemonic = ARM_ERET_MNEMONIC;

    //
    // Handle a service call: BKPT, HVC, or SMC.
    //

    } else if (Op2 == ARM_MISCELLANEOUS1_OP2_SERVICE) {
        Value = ARM_SERVICE_BUILD_IMMEDIATE12_4(Instruction);
        if ((Value & 0x00008000) != 0) {
            Value |= 0xFFFF0000;
        }

        if (Op == ARM_MISCELLANEOUS1_OP_BKPT) {
            Mnemonic = ARM_BKPT_MNEMONIC;
            sprintf(Context->Operand1, "#%d", Value);

        } else if (Op == ARM_MISCELLANEOUS1_OP_HVC) {
            Mnemonic = ARM_HVC_MNEMONIC;
            sprintf(Context->Operand1, "#%d", Value);

        } else if (Op == ARM_MISCELLANEOUS1_OP_SMC) {
            Mnemonic = ARM_SMC_MNEMONIC;
            sprintf(Context->Operand1, "#%d", Value & 0xF);
        }
    }

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

VOID
DbgpArmDecodeMsrImmediateAndHints (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an MSR immediate instruction or memory hints.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    LONG Immediate;
    ULONG Instruction;
    PSTR Mnemonic;
    ULONG Op1;
    ULONG Op2;

    Mnemonic = "";
    Instruction = Context->Instruction;
    Op1 = (Instruction & ARM_HINTS_OP1_MASK) >> ARM_HINTS_OP1_SHIFT;
    Op2 = Instruction & ARM_HINTS_OP2_MASK;
    if (Op1 == ARM_HINTS_OP1_HINTS) {
        if (Op2 == ARM_HINTS_OP2_NOP) {
            Mnemonic = ARM_NOP_MNEMONIC;

        } else if (Op2 == ARM_HINTS_OP2_YIELD) {
            Mnemonic = ARM_YIELD_MNEMONIC;

        } else if (Op2 == ARM_HINTS_OP2_WFE) {
            Mnemonic = ARM_WFE_MNEMONIC;

        } else if (Op2 == ARM_HINTS_OP2_WFI) {
            Mnemonic = ARM_WFI_MNEMONIC;

        } else if (Op2 == ARM_HINTS_OP2_SEV) {
            Mnemonic = ARM_SEV_MNEMONIC;

        } else if ((Op2 & ARM_HINTS_OP2_DBG_MASK) == ARM_HINTS_OP2_DBG_VALUE) {
            Mnemonic = ARM_DBG_MNEMONIC;
            sprintf(Context->Operand1,
                    "#%d",
                    Op2 & ARM_HINTS_OP2_DBG_OPTION_MASK);
        }

    //
    // If not hints, then this is an MSR (immediate) instruction.
    //

    } else {
        Mnemonic = ARM_MSR_MNEMONIC;
        DbgpArmPrintStatusRegister(Context->Operand1, Instruction);
        Immediate = Instruction & ARM_MSR_IMMEDIATE12_MASK;
        if ((Immediate & 0x00001000) != 0) {
            Immediate |= 0xFFFFF000;
        }

        sprintf(Context->Operand2, "#%d  ; 0x%x", Immediate, Immediate);
    }

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

VOID
DbgpArmDecodeMultiply (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a multiply or long multiply instruction. This function
    assumes that the instruction is in fact a multiply instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    BOOL LongMultiply;
    PSTR MnemonicSuffix;
    PSTR MultiplyHalves;
    PSTR PreConditionMnemonicSuffix;
    UCHAR Rd;
    UCHAR RdHigh;
    UCHAR RdLow;
    UCHAR Rm;
    UCHAR Rn;
    PSTR Rounded;
    UCHAR Rs;
    BOOL ThreeOperands;
    PSTR XBit;

    MultiplyHalves = NULL;
    LongMultiply = FALSE;
    Rounded = NULL;
    ThreeOperands = FALSE;
    XBit = NULL;
    Instruction = Context->Instruction;
    BaseMnemonic = "ERR";
    PreConditionMnemonicSuffix = "";
    MnemonicSuffix = "";

    //
    // Get the top and bottom bits. These bits aren't actually defined for all
    // multiply instructions, so they'll get ignored in some cases.
    //

    if ((Instruction & ARM_MULTIPLY_SOURCE_HIGH) != 0) {
        if ((Instruction & ARM_MULTIPLY_DESTINATION_HIGH) != 0) {
            MultiplyHalves = ARM_MULTIPLY_TOP_TOP;

        } else {
            MultiplyHalves = ARM_MULTIPLY_TOP_BOTTOM;
        }

    } else {
        if ((Instruction & ARM_MULTIPLY_DESTINATION_HIGH) != 0) {
            MultiplyHalves = ARM_MULTIPLY_BOTTOM_TOP;

        } else {
            MultiplyHalves = ARM_MULTIPLY_BOTTOM_BOTTOM;
        }
    }

    //
    // Get the X bit, which indicates that the multiplications are
    // bottom * top and top * bottom. If X is cleared, the multiplications are
    // bottom * bottom and top * top.
    //

    if ((Instruction & ARM_MULTIPLY_X_BIT) != 0) {
        XBit = ARM_MULTIPLY_X_MNEMONIC;
    }

    //
    // Get the rounding bit, which indicates for a couple of instructions that
    // the multiplication is rounded.
    //

    if ((Instruction & ARM_MULTIPLY_ROUND_BIT) != 0) {
        Rounded = ARM_MULTIPLY_ROUND_MNEMONIC;
    }

    //
    // For a non-long multiply, get the 4 registers.
    //

    Rd = (Instruction & ARM_MULTIPLY_RD_MASK) >> ARM_MULTIPLY_RD_SHIFT;
    Rm = (Instruction & ARM_MULTIPLY_RM_MASK) >> ARM_MULTIPLY_RM_SHIFT;
    Rn = (Instruction & ARM_MULTIPLY_RN_MASK) >> ARM_MULTIPLY_RN_SHIFT;
    Rs = (Instruction & ARM_MULTIPLY_RS_MASK) >> ARM_MULTIPLY_RS_SHIFT;

    //
    // For long multiplies, get the high and low destination registers. Rs and
    // Rm are the same as for non-long multiplies.
    //

    RdHigh = (Instruction & ARM_MULTIPLY_RD_HIGH_MASK) >>
             ARM_MULTIPLY_RD_HIGH_SHIFT;

    RdLow = (Instruction & ARM_MULTIPLY_RD_LOW_MASK) >>
            ARM_MULTIPLY_RD_LOW_SHIFT;

    //
    // Get the mnemonic and characteristics of the instruction.
    //

    switch (Instruction & ARM_MULTIPLY_OPCODE_MASK) {

    //
    // Standard Multiply and accumulate.
    //

    case ARM_MLA_MASK | ARM_SET_FLAGS_BIT:
        MnemonicSuffix = ARM_SET_FLAGS_MNEMONIC;

        //
        // Fall through.
        //

    case ARM_MLA_MASK:
        BaseMnemonic = ARM_MLA_MNEMONIC;
        break;

    //
    // Standard Multiply.
    //

    case ARM_MUL_MASK | ARM_SET_FLAGS_BIT:
        MnemonicSuffix = ARM_SET_FLAGS_MNEMONIC;

        //
        // Fall through.
        //

    case ARM_MUL_MASK:
        BaseMnemonic =ARM_MUL_MNEMONIC;
        ThreeOperands = TRUE;
        break;

    //
    // Signed half word multiply and accumulate.
    //

    case ARM_SMLA_MASK:
        BaseMnemonic = ARM_SMLA_MNEMONIC;
        PreConditionMnemonicSuffix = MultiplyHalves;
        break;

    //
    // Signed half word multiply accumulate, dual,
    // Signed half word multiply subtract, dual,
    // Signed dual multiply add, and
    // Signed dual multiply subtract.
    //

    case ARM_SMLXD_MASK:
        if ((Instruction & ARM_SMLXD_OPCODE2_MASK) == ARM_SMLAD_OPCODE2_VALUE) {
            if (Rn == 0xF) {
                BaseMnemonic = ARM_SMUAD_MNEMONIC;
                ThreeOperands = TRUE;

            } else {
                BaseMnemonic = ARM_SMLAD_MNEMONIC;
            }

        } else if ((Instruction & ARM_SMLXD_OPCODE2_MASK) ==
                   ARM_SMLSD_OPCODE2_VALUE) {

            if (Rn == 0xF) {
                BaseMnemonic = ARM_SMUSD_MNEMONIC;
                ThreeOperands = TRUE;

            } else {
                BaseMnemonic = ARM_SMLSD_MNEMONIC;
            }

        } else {
            return;
        }

        PreConditionMnemonicSuffix = XBit;
        break;

    //
    // Signed half word by word, accumulate, and
    // Signed multiply word B and T.
    //

    case ARM_SMLAW_SMULW_MASK:
        if ((Instruction & ARM_SMULW_DIFFERENT_BIT) != 0) {
            BaseMnemonic = ARM_SMULW_MNEMONIC;
            ThreeOperands = TRUE;

        } else {
            BaseMnemonic = ARM_SMLAW_MNEMONIC;
        }

        if ((Instruction & ARM_MULTIPLY_DESTINATION_HIGH) != 0) {
            PreConditionMnemonicSuffix = ARM_MULTIPLY_TOP;

        } else {
            PreConditionMnemonicSuffix = ARM_MULTIPLY_BOTTOM;
        }

        break;

    //
    // Signed multiply accumulate, long.
    //

    case ARM_SMLAL_MASK | ARM_SET_FLAGS_BIT:
        PreConditionMnemonicSuffix = ARM_SET_FLAGS_MNEMONIC;

        //
        // Fall through.
        //

    case ARM_SMLAL_MASK:
        BaseMnemonic = ARM_SMLAL_MNEMONIC;
        LongMultiply = TRUE;
        break;

    //
    // Signed halfword multiply accumulate, long.
    //

    case ARM_SMLAL_XY_MASK:
        BaseMnemonic = ARM_SMLAL_MNEMONIC;
        PreConditionMnemonicSuffix = MultiplyHalves;
        LongMultiply = TRUE;
        break;

    //
    // Signed divide.
    //

    case ARM_SDIV_MASK:
        BaseMnemonic = ARM_SDIV_MNEMONIC;
        ThreeOperands = TRUE;
        break;

    //
    // Unsigned divide.
    //

    case ARM_UDIV_MASK:
        BaseMnemonic = ARM_UDIV_MNEMONIC;
        ThreeOperands = TRUE;
        break;

    //
    // Signed half word multiply accumulate, long dual, and
    // Signed half word multiply subtract, long dual.
    //

    case ARM_SMLXLD_MASK:
        if ((Instruction & ARM_SMLXLD_OPCODE2_MASK) ==
            ARM_SMLALD_OPCODE2_VALUE) {

            BaseMnemonic = ARM_SMLALD_MNEMONIC;

        } else if ((Instruction & ARM_SMLXLD_OPCODE2_MASK) ==
                   ARM_SMLSLD_OPCODE2_VALUE) {

            BaseMnemonic = ARM_SMLSLD_MNEMONIC;

        } else {
            return;
        }

        PreConditionMnemonicSuffix = XBit;
        LongMultiply = TRUE;
        break;

    //
    // Signed most significant word multiply accumulate, and
    // Signed most significant word multiply subtract, and
    // Signed most significant word multiply.
    //

    case ARM_SMMLX_MASK:
        if ((Instruction & ARM_SMMLX_OPCODE2_MASK) == ARM_SMMLA_OPCODE2_VALUE) {
            if (Rn == 0xF) {
                BaseMnemonic = ARM_SMMUL_MNEMONIC;
                ThreeOperands = TRUE;

            } else {
                BaseMnemonic = ARM_SMMLA_MNEMONIC;
            }

        } else if ((Instruction & ARM_SMMLX_OPCODE2_MASK) ==
                   ARM_SMMLS_OPCODE2_VALUE) {

            BaseMnemonic = ARM_SMMLS_MNEMONIC;

        } else {
            return;
        }

        PreConditionMnemonicSuffix = Rounded;
        break;

    //
    // Signed multiply.
    //

    case ARM_SMUL_MASK:
        BaseMnemonic = ARM_SMUL_MNEMONIC;
        PreConditionMnemonicSuffix = MultiplyHalves;
        ThreeOperands = TRUE;
        break;

    //
    // Signed multiply, long.
    //

    case ARM_SMULL_MASK | ARM_SET_FLAGS_BIT:
        MnemonicSuffix = ARM_SET_FLAGS_MNEMONIC;

        //
        // Fall through.
        //

    case ARM_SMULL_MASK:
        BaseMnemonic = ARM_SMULL_MNEMONIC;
        LongMultiply = TRUE;
        break;

    //
    // Unsigned multiply accumulate accumulate long.
    //

    case ARM_UMAAL_MASK:
        BaseMnemonic = ARM_UMAAL_MNEMONIC;
        LongMultiply = TRUE;
        break;

    //
    // Unsigned multiply accumulate long.
    //

    case ARM_UMLAL_MASK | ARM_SET_FLAGS_BIT:
        MnemonicSuffix = ARM_SET_FLAGS_MNEMONIC;

        //
        // Fall through.
        //

    case ARM_UMLAL_MASK:
        BaseMnemonic = ARM_UMLAL_MNEMONIC;
        LongMultiply = TRUE;
        break;

    //
    // Unsigned multiply long.
    //

    case ARM_UMULL_MASK | ARM_SET_FLAGS_BIT:
        MnemonicSuffix = ARM_SET_FLAGS_MNEMONIC;

        //
        // Fall through.
        //

    case ARM_UMULL_MASK:
        BaseMnemonic = ARM_UMULL_MNEMONIC;
        LongMultiply = TRUE;
        break;

    default:
        return;
    }

    snprintf(Context->Mnemonic,
             ARM_OPERAND_LENGTH,
             "%s%s%s",
             BaseMnemonic,
             PreConditionMnemonicSuffix,
             MnemonicSuffix);

    //
    // Create the operands, depending on whether the instruction was a long
    // multiply or not.
    //

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

    } else {
        strcpy(Context->Operand1, DbgArmRegisterNames[Rd]);
        strcpy(Context->Operand2, DbgArmRegisterNames[Rm]);
        strcpy(Context->Operand3, DbgArmRegisterNames[Rs]);
        if (ThreeOperands == FALSE) {
            strcpy(Context->Operand4, DbgArmRegisterNames[Rn]);
        }
    }

    return;
}

VOID
DbgpArmDecodeSynchronization (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a synchronization primitive instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR Mnemonic;
    ULONG Op;
    ULONG R0;
    ULONG R12;
    ULONG Rn;

    Instruction = Context->Instruction;
    Op = (Instruction & ARM_SYNCHRONIZATION_OPCODE_MASK) >>
         ARM_SYNCHRONIZATION_OPCODE_SHIFT;

    Rn = (Instruction & ARM_SYNCHRONIZATION_RN_MASK) >>
         ARM_SYNCHRONIZATION_RN_SHIFT;

    R0 = Instruction & ARM_SYNCHRONIZATION_R0_MASK;
    R12 = (Instruction & ARM_SYNCHRONIZATION_R12_MASK) >>
          ARM_SYNCHRONIZATION_R12_SHIFT;

    //
    // If the high bit of the op field is not set, then it's a swap instruction.
    //

    if ((Op & ARM_SYNCHRONIZATION_OPCODE_EXCLUSIVE) == 0) {
        if ((Instruction & ARM_SYNCHRONIZATION_SWAP_BYTE) != 0) {
            Mnemonic = ARM_SWPB_MNEMONIC;

        } else {
            Mnemonic = ARM_SWP_MNEMONIC;
        }

        strcpy(Context->Operand1, DbgArmRegisterNames[R12]);
        strcpy(Context->Operand2, DbgArmRegisterNames[R0]);
        sprintf(Context->Operand3, "[%s]", DbgArmRegisterNames[Rn]);

    //
    // It's an ldrex or strex instruction of some kind.
    //

    } else {
        Op &= ~ARM_SYNCHRONIZATION_OPCODE_EXCLUSIVE;
        Mnemonic = DbgArmSynchronizationMnemonics[Op];

        //
        // If the lowest bit of the op region is set, it's an ldrex{b,h,d}.
        //

        if ((Op & ARM_SYNCHRONIZATION_OPCODE_LOAD) != 0) {
            strcpy(Context->Operand1, DbgArmRegisterNames[R12]);
            sprintf(Context->Operand2, "[%s]", DbgArmRegisterNames[Rn]);

        } else {
            strcpy(Context->Operand1, DbgArmRegisterNames[R12]);
            strcpy(Context->Operand2, DbgArmRegisterNames[R0]);
            sprintf(Context->Operand3, "[%s]", DbgArmRegisterNames[Rn]);
        }
    }

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

VOID
DbgpArmDecodeSupervisorCall (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a supervisor call instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate;
    ULONG Instruction;

    Instruction = Context->Instruction;
    Immediate = Instruction & ARM_IMMEDIATE24_MASK;
    strcpy(Context->Mnemonic, ARM_SVC_MNEMONIC);
    sprintf(Context->Operand1, "#%d  ; 0x%x", Immediate, Immediate);
    return;
}

VOID
DbgpArmDecodeFloatingPointTwoRegisters (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a two-register floating point data processing
    instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ARM_IMMEDIATE_DOUBLE Double;
    ARM_IMMEDIATE_FLOAT Float;
    ULONG Immediate8;
    ULONG Instruction;
    ULONG Mask;
    PSTR MnemonicSuffix;
    BOOL TwoRegisters;
    ULONG VectorD;
    ULONG VectorM;
    PSTR VectorTypeString;

    Instruction = Context->Instruction;

    //
    // Collect the vector values. If the double-precision (SZ) bit is set, then
    // the extra bit for each vector is the high bit. If the double-precision
    // bit is not set, then the extra bit is the low bit.
    //

    VectorD = (Instruction & ARM_FLOATING_POINT_VD_MASK) >>
              ARM_FLOATING_POINT_VD_SHIFT;

    VectorM = (Instruction & ARM_FLOATING_POINT_VM_MASK) >>
              ARM_FLOATING_POINT_VM_SHIFT;

    if ((Instruction & ARM_FLOATING_POINT_SZ_BIT) != 0) {
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= (1 << 4);
        }

        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= (1 << 4);
        }

        MnemonicSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;
        VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;

    } else {
        VectorD <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= 1;
        }

        VectorM <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= 1;
        }

        MnemonicSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
        VectorTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
    }

    //
    // If the op bit is not set, then this is actually a VMOV immediate and not
    // a two register instruction.
    //

    if ((Instruction & ARM_FLOATING_POINT_OP_BIT) == 0) {
        BaseMnemonic = ARM_VMOV_MNEMONIC;
        Immediate8 = ARM_FLOATING_POINT_BUILD_IMMEDIATE8(Instruction);
        if ((Instruction & ARM_FLOATING_POINT_SZ_BIT) != 0) {
            Double.Immediate = ARM_FLOATING_POINT_BUILD_IMMEDIATE64(Immediate8);
            sprintf(Context->Operand2,
                    "#%d  ; 0x%I64x %g",
                    Immediate8,
                    Double.Immediate,
                    Double.Double);

        } else {
            Float.Immediate= ARM_FLOATING_POINT_BUILD_IMMEDIATE32(Immediate8);
            sprintf(Context->Operand2,
                    "#%d  ; 0x%x %g",
                    Immediate8,
                    Float.Immediate,
                    Float.Float);
        }

        TwoRegisters = FALSE;

    } else {
        Mask = Instruction & ARM_FLOATING_POINT_TWO_REGISTER_INSTRUCTION_MASK;
        switch (Mask) {
        case ARM_FLOATING_POINT_TWO_REGISTER_INSTRUCTION_VMOV:
            BaseMnemonic = ARM_VMOV_MNEMONIC;
            break;

        case ARM_FLOATING_POINT_TWO_REGISTER_INSTRUCTION_VABS:
            BaseMnemonic = ARM_VABS_MNEMONIC;
            break;

        case ARM_FLOATING_POINT_TWO_REGISTER_INSTRUCTION_VNEG:
            BaseMnemonic = ARM_VNEG_MNEMONIC;
            break;

        case ARM_FLOATING_POINT_TWO_REGISTER_INSTRUCTION_VSQRT:
            BaseMnemonic = ARM_VSQRT_MNEMONIC;
            break;

        default:
            return;
        }

        TwoRegisters = TRUE;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->PostConditionMnemonicSuffix, MnemonicSuffix);
    sprintf(Context->Operand1, "%s%d", VectorTypeString, VectorD);
    if (TwoRegisters != FALSE) {
        sprintf(Context->Operand2, "%s%d", VectorTypeString, VectorM);
    }

    return;
}

VOID
DbgpArmDecodeFloatingPointThreeRegisters (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a three-register floating point data processing
    instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG VectorD;
    ULONG VectorM;
    ULONG VectorN;
    PSTR VectorTypeString;

    Instruction = Context->Instruction;

    //
    // Collect the vector values. If the double-precision (SZ) bit is set, then
    // the extra bit for each vector is the high bit. If the double-precision
    // bit is not set, then the extra bit is the low bit.
    //

    VectorD = (Instruction & ARM_FLOATING_POINT_VD_MASK) >>
              ARM_FLOATING_POINT_VD_SHIFT;

    VectorM = (Instruction & ARM_FLOATING_POINT_VM_MASK) >>
              ARM_FLOATING_POINT_VM_SHIFT;

    VectorN = (Instruction & ARM_FLOATING_POINT_VN_MASK) >>
              ARM_FLOATING_POINT_VN_SHIFT;

    if ((Instruction & ARM_FLOATING_POINT_SZ_BIT) != 0) {
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= (1 << 4);
        }

        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= (1 << 4);
        }

        if ((Instruction & ARM_FLOATING_POINT_N_BIT) != 0) {
            VectorN |= (1 << 4);
        }

        MnemonicSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;
        VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;

    } else {
        VectorD <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= 1;
        }

        VectorM <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= 1;
        }

        VectorN <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_N_BIT) != 0) {
            VectorN |= 1;
        }

        MnemonicSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
        VectorTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
    }

    //
    // Get the instruction based on the value of opcode 1 and the op bit.
    //

    BaseMnemonic = "ERR";
    switch (Instruction & ARM_FLOATING_POINT_INSTRUCTION_MASK) {
    case ARM_FLOATING_POINT_INSTRUCTION_VMLA_VMLS:
        if ((Instruction & ARM_FLOATING_POINT_OP_BIT) != 0) {
            BaseMnemonic = ARM_VMLS_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VMLA_MNEMONIC;
        }

        break;

    case ARM_FLOATING_POINT_INSTRUCTION_VNMLA_VNMLS:
        if ((Instruction & ARM_FLOATING_POINT_OP_BIT) != 0) {
            BaseMnemonic = ARM_VNMLS_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VNMLA_MNEMONIC;
        }

        break;

    case ARM_FLOATING_POINT_INSTRUCTION_VMUL_VNMUL:
        if ((Instruction & ARM_FLOATING_POINT_OP_BIT) != 0) {
            BaseMnemonic = ARM_VNMUL_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VMUL_MNEMONIC;
        }

        break;

    case ARM_FLOATING_POINT_INSTRUCTION_VADD_VSUB:
        if ((Instruction & ARM_FLOATING_POINT_OP_BIT) != 0) {
            BaseMnemonic = ARM_VSUB_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VADD_MNEMONIC;
        }

        break;

    case ARM_FLOATING_POINT_INSTRUCTION_VDIV:
        if ((Instruction & ARM_FLOATING_POINT_OP_BIT) != 0) {
            return;
        }

        BaseMnemonic = ARM_VDIV_MNEMONIC;
        break;

    case ARM_FLOATING_POINT_INSTRUCTION_VFNMA_VFNMS:
        if ((Instruction & ARM_FLOATING_POINT_OP_BIT) != 0) {
            BaseMnemonic = ARM_VFNMA_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VFNMS_MNEMONIC;
        }

        break;

    case ARM_FLOATING_POINT_INSTRUCTION_VFMA_VFMS:
        if ((Instruction & ARM_FLOATING_POINT_OP_BIT) != 0) {
            BaseMnemonic = ARM_VFMS_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VFMA_MNEMONIC;
        }

        break;

    default:
        break;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->PostConditionMnemonicSuffix, MnemonicSuffix);
    sprintf(Context->Operand1, "%s%d", VectorTypeString, VectorD);
    sprintf(Context->Operand2, "%s%d", VectorTypeString, VectorN);
    sprintf(Context->Operand3, "%s%d", VectorTypeString, VectorM);
    return;
}

VOID
DbgpArmDecodeFloatingPointVectorConvert (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a floating point vector convert instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR DestinationHalfSuffix;
    PSTR DestinationSuffix;
    PSTR FixedSuffix;
    ULONG FractionBits;
    ULONG Instruction;
    PSTR PreConditionMnemonicSuffix;
    BOOL RepeatVectorD;
    PSTR SourceHalfSuffix;
    PSTR SourceSuffix;
    ULONG VectorD;
    BOOL VectorDDouble;
    PSTR VectorDTypeString;
    ULONG VectorM;
    BOOL VectorMDouble;
    PSTR VectorMTypeString;

    //
    // Save somem values that are common to most instructions.
    //

    Instruction = Context->Instruction;
    RepeatVectorD = FALSE;
    VectorDDouble = FALSE;
    VectorD = (Instruction & ARM_FLOATING_POINT_VD_MASK) >>
              ARM_FLOATING_POINT_VD_SHIFT;

    VectorMDouble = FALSE;
    VectorM = (Instruction & ARM_FLOATING_POINT_VM_MASK) >>
              ARM_FLOATING_POINT_VM_SHIFT;

    DestinationHalfSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
    SourceHalfSuffix = ARM_FLOATING_POINT_HALF_PRECISION_SUFFIX;
    DestinationSuffix = "";
    PreConditionMnemonicSuffix = "";
    SourceSuffix = "";

    //
    // Compute the fraction bits and suffix for the fixed point instructions.
    // The fixed 32-bit and unsigned bit are the same for float to fixed as
    // they are for fixed to float.
    //

    FractionBits = (Instruction & ARM_FLOATING_POINT_IMMEDIATE4_LOW_MASK) >>
                   ARM_FLOATING_POINT_IMMEDIATE4_LOW_SHIFT;

    FractionBits <<= 1;
    if ((Instruction & ARM_FLOATING_POINT_I_BIT) != 0) {
        FractionBits |= 1;
    }

    if ((Instruction & ARM_VCVT_FIXED_32_TO_FLOAT) != 0) {
        if ((Instruction & ARM_VCVT_FIXED_UNSIGNED_TO_FLOAT) != 0) {
            FixedSuffix = ARM_FLOATING_POINT_UNSIGNED_INTEGER_SUFFIX;

        } else {
            FixedSuffix = ARM_FLOATING_POINT_SIGNED_INTEGER_SUFFIX;
        }

        FractionBits = 32 - FractionBits;

    } else {
        if ((Instruction & ARM_VCVT_FIXED_UNSIGNED_TO_FLOAT) != 0) {
            FixedSuffix = ARM_FLOATING_POINT_UNSIGNED_HALF_SUFFIX;

        } else {
            FixedSuffix = ARM_FLOATING_POINT_SIGNED_HALF_SUFFIX;
        }

        FractionBits = 16 - FractionBits;
    }

    //
    // Determine the suffices and vector sizes baced on the instruction mask.
    //

    switch (Instruction & ARM_VCVT_MASK) {

    //
    // Handle VCVTT single to half.
    //

    case ARM_VCVT_TOP | ARM_VCVT_SINGLE_TO_HALF:
        DestinationHalfSuffix = ARM_FLOATING_POINT_HALF_PRECISION_SUFFIX;
        SourceHalfSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;

        //
        // Fall through.
        //

    //
    // Handle VCVTT half to single.
    //

    case ARM_VCVT_TOP | ARM_VCVT_HALF_TO_SINGLE:
        PreConditionMnemonicSuffix = ARM_FLOATING_POINT_TOP;
        DestinationSuffix = DestinationHalfSuffix;
        SourceSuffix = SourceHalfSuffix;
        break;

    //
    // Handle VCVTB single to half.
    //

    case ARM_VCVT_BOTTOM | ARM_VCVT_SINGLE_TO_HALF:
        DestinationHalfSuffix = ARM_FLOATING_POINT_HALF_PRECISION_SUFFIX;
        SourceHalfSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;

        //
        // Fall through.
        //

    //
    // Handle VCVTB half to single.
    //

    case ARM_VCVT_BOTTOM | ARM_VCVT_HALF_TO_SINGLE:
        PreConditionMnemonicSuffix = ARM_FLOATING_POINT_BOTTOM;
        DestinationSuffix = DestinationHalfSuffix;
        SourceSuffix = SourceHalfSuffix;
        break;

    //
    // Handle VCVT single-precision to double-precision conversions and
    // double-precision to single-precision conversion.
    //

    case ARM_VCVT_FLOAT_TO_FLOAT:

        //
        // Here the double bit indicates that the conversion is from a double.
        //

        if ((Instruction & ARM_VCVT_DOUBLE) != 0) {
            VectorMDouble = TRUE;
            SourceSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;
            DestinationSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;

        } else {
            VectorDDouble = TRUE;
            SourceSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
            DestinationSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;
        }

        break;

    //
    // Handle conversions from floats to integers.
    //

    case ARM_VCVT_FLOAT_TO_INTEGER:
    case ARM_VCVT_FLOAT_TO_INTEGER | ARM_VCVT_FLOAT_TO_INTEGER_SIGNED:
    case ARM_VCVT_FLOAT_TO_INTEGER | ARM_VCVT_FLOAT_TO_INTEGER_ROUND_TO_ZERO:
    case (ARM_VCVT_FLOAT_TO_INTEGER |
          ARM_VCVT_FLOAT_TO_INTEGER_SIGNED |
          ARM_VCVT_FLOAT_TO_INTEGER_ROUND_TO_ZERO):

        if ((Instruction & ARM_VCVT_DOUBLE) != 0) {
            VectorMDouble = TRUE;
            SourceSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;

        } else {
            SourceSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
        }

        if ((Instruction & ARM_VCVT_FLOAT_TO_INTEGER_SIGNED) != 0) {
            DestinationSuffix = ARM_FLOATING_POINT_SIGNED_INTEGER_SUFFIX;

        } else {
            DestinationSuffix = ARM_FLOATING_POINT_UNSIGNED_INTEGER_SUFFIX;
        }

        if ((Instruction & ARM_VCVT_FLOAT_TO_INTEGER_ROUND_TO_ZERO) == 0) {
            PreConditionMnemonicSuffix = ARM_FLOATING_POINT_ROUNDING;
        }

        break;

    //
    // Handle conversions from integers to floats.
    //

    case ARM_VCVT_INTEGER_TO_FLOAT:
    case ARM_VCVT_INTEGER_TO_FLOAT | ARM_VCVT_INTEGER_TO_FLOAT_SIGNED:
        if ((Instruction & ARM_VCVT_DOUBLE) != 0) {
            VectorDDouble = TRUE;
            DestinationSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;

        } else {
            DestinationSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
        }

        if ((Instruction & ARM_VCVT_INTEGER_TO_FLOAT_SIGNED) != 0) {
            SourceSuffix = ARM_FLOATING_POINT_SIGNED_INTEGER_SUFFIX;

        } else {
            SourceSuffix = ARM_FLOATING_POINT_UNSIGNED_INTEGER_SUFFIX;
        }

        break;

    //
    // Handle conversions from floats to fixed point.
    //

    case ARM_VCVT_FLOAT_TO_FIXED:
    case ARM_VCVT_FLOAT_TO_FIXED | ARM_VCVT_FLOAT_TO_FIXED_UNSIGNED:
    case ARM_VCVT_FLOAT_TO_FIXED | ARM_VCVT_FLOAT_TO_FIXED_32:
    case (ARM_VCVT_FLOAT_TO_FIXED |
          ARM_VCVT_FLOAT_TO_FIXED_UNSIGNED |
          ARM_VCVT_FLOAT_TO_FIXED_32):

        if ((Instruction & ARM_VCVT_DOUBLE) != 0) {
            VectorDDouble = TRUE;
            SourceSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;

        } else {
            SourceSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
        }

        DestinationSuffix = FixedSuffix;
        RepeatVectorD = TRUE;
        break;

    //
    // Handle conversions from fixed point to floats.
    //

    case ARM_VCVT_FIXED_TO_FLOAT:
    case ARM_VCVT_FIXED_TO_FLOAT | ARM_VCVT_FIXED_UNSIGNED_TO_FLOAT:
    case ARM_VCVT_FIXED_TO_FLOAT | ARM_VCVT_FIXED_32_TO_FLOAT:
    case (ARM_VCVT_FIXED_TO_FLOAT |
          ARM_VCVT_FIXED_UNSIGNED_TO_FLOAT |
          ARM_VCVT_FIXED_32_TO_FLOAT):

        if ((Instruction & ARM_VCVT_DOUBLE) != 0) {
            VectorDDouble = TRUE;
            DestinationSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;

        } else {
            DestinationSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
        }

        SourceSuffix = FixedSuffix;
        RepeatVectorD = TRUE;
        break;

    default:
        break;
    }

    //
    // Convert the vectors into the correct double-precision or
    // single-precision values.
    //

    if (VectorDDouble != FALSE) {
        VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= (1 << 4);
        }

    } else {
        VectorDTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
        VectorD <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= 1;
        }
    }

    if (VectorMDouble != FALSE) {
        VectorMTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= (1 << 4);
        }

    } else {
        VectorMTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
        VectorM <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= 1;
        }
    }

    sprintf(Context->Mnemonic,
            "%s%s",
            ARM_VCVT_MNEMONIC,
            PreConditionMnemonicSuffix);

    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            DestinationSuffix,
            SourceSuffix);

    sprintf(Context->Operand1, "%s%d", VectorDTypeString, VectorD);
    if (RepeatVectorD != FALSE) {
        sprintf(Context->Operand2, "%s%d", VectorDTypeString, VectorD);

    } else {
        sprintf(Context->Operand2, "%s%d", VectorMTypeString, VectorM);
    }

    return;
}

VOID
DbgpArmDecodeFloatingPointVectorCompare (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a floating point vector compare instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG VectorD;
    ULONG VectorM;
    PSTR VectorTypeString;

    Instruction = Context->Instruction;

    //
    // Collect the vector values. If the double-precision (SZ) bit is set, then
    // the extra bit for each vector is the high bit. If the double-precision
    // bit is not set, then the extra bit is the low bit.
    //

    VectorD = (Instruction & ARM_FLOATING_POINT_VD_MASK) >>
              ARM_FLOATING_POINT_VD_SHIFT;

    VectorM = (Instruction & ARM_FLOATING_POINT_VM_MASK) >>
              ARM_FLOATING_POINT_VM_SHIFT;

    if ((Instruction & ARM_FLOATING_POINT_SZ_BIT) != 0) {
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= (1 << 4);
        }

        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= (1 << 4);
        }

        MnemonicSuffix = ARM_FLOATING_POINT_DOUBLE_PRECISION_SUFFIX;
        VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;

    } else {
        VectorD <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_D_BIT) != 0) {
            VectorD |= 1;
        }

        VectorM <<= 1;
        if ((Instruction & ARM_FLOATING_POINT_M_BIT) != 0) {
            VectorM |= 1;
        }

        MnemonicSuffix = ARM_FLOATING_POINT_SINGLE_PRECISION_SUFFIX;
        VectorTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
    }

    //
    // Get the base mnemonic and fill out the context.
    //

    if ((Instruction & ARM_FLOATING_POINT_VCMP_E_BIT) != 0) {
        BaseMnemonic = ARM_VCMPE_MNEMONIC;

    } else {
        BaseMnemonic = ARM_VCMP_MNEMONIC;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    strcpy(Context->PostConditionMnemonicSuffix, MnemonicSuffix);
    sprintf(Context->Operand1, "%s%d", VectorTypeString, VectorD);
    if ((Instruction & ARM_FLOATING_POINT_VCMP_ZERO) != 0) {
        sprintf(Context->Operand2, "#0.0");

    } else {
        sprintf(Context->Operand2, "%s%d", VectorTypeString, VectorM);
    }

    return;
}

VOID
DbgpArmDecodeSimdSmallMove (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes a floating point to ARM register move instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    ULONG Register;
    PSTR RegisterString;
    ULONG Size;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG Vector;
    BOOL VectorDouble;
    ULONG VectorIndex;
    PSTR VectorString;
    PSTR VectorTypeString;

    Instruction = Context->Instruction;
    Register = (Instruction & ARM_SIMD_TRANSFER_REGISTER_MASK) >>
               ARM_SIMD_TRANSFER_REGISTER_SHIFT;

    VectorDouble = FALSE;
    Vector = (Instruction & ARM_SIMD_TRANSFER_VECTOR_MASK) >>
             ARM_SIMD_TRANSFER_VECTOR_SHIFT;

    //
    // Determine the mnemonic suffices and vector index for the to/from scalar
    // instructions.
    //

    SizeTypeSuffix = "";
    SizeValueSuffix = "";
    VectorIndex = 0;
    if ((Instruction & ARM_SIMD_TRANSFER_MOVE_SCALAR) != 0) {
        VectorDouble = TRUE;
        Size = ARM_SIMD_TRANSFER_SCALAR_BUILD_SIZE_ENCODING(Instruction);
        if ((Size & ARM_SIMD_TRANSFER_SCALAR_SIZE_8_MASK) ==
            ARM_SIMD_TRANSFER_SCALAR_SIZE_8_VALUE) {

            VectorIndex = (Size & ~ARM_SIMD_TRANSFER_SCALAR_SIZE_8_MASK) >>
                          ARM_SIMD_TRANSFER_SCALAR_SIZE_8_SHIFT;

            SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;

        } else if ((Size & ARM_SIMD_TRANSFER_SCALAR_SIZE_16_MASK) ==
                   ARM_SIMD_TRANSFER_SCALAR_SIZE_16_VALUE) {

            VectorIndex = (Size & ~ARM_SIMD_TRANSFER_SCALAR_SIZE_16_MASK) >>
                          ARM_SIMD_TRANSFER_SCALAR_SIZE_16_SHIFT;

            SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;

        } else if ((Size & ARM_SIMD_TRANSFER_SCALAR_SIZE_32_MASK) ==
                   ARM_SIMD_TRANSFER_SCALAR_SIZE_32_VALUE) {

            VectorIndex = (Size & ~ARM_SIMD_TRANSFER_SCALAR_SIZE_32_MASK) >>
                          ARM_SIMD_TRANSFER_SCALAR_SIZE_32_SHIFT;

            if (((Instruction & ARM_SIMD_TRANSFER_TO_REGISTER) != 0) &&
                ((Instruction & ARM_SIMD_TRANSFER_SCALAR_UNSIGNED) != 0)) {

                DbgpArmDecodeUndefined(Context);
                return;
            }

            SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;

        } else {
            DbgpArmDecodeUndefined(Context);
            return;
        }

        if ((Instruction & ARM_SIMD_TRANSFER_TO_REGISTER) != 0) {
            if ((Instruction & ARM_SIMD_TRANSFER_SCALAR_UNSIGNED) != 0) {
                SizeTypeSuffix = ARM_SIMD_DATA_UNSIGNED;

            } else {
                SizeTypeSuffix = ARM_SIMD_DATA_SIGNED;
            }

        } else {
            SizeTypeSuffix = ARM_SIMD_DATA_DEFAULT;
        }
    }

    //
    // Finalize the vector and get its type string.
    //

    if (VectorDouble != FALSE) {
        VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        if ((Instruction & ARM_SIMD_TRANSFER_VECTOR_BIT) != 0) {
            Vector |= (1 << 4);
        }

    } else {
        VectorTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
        Vector <<= 1;
        if ((Instruction & ARM_SIMD_TRANSFER_VECTOR_BIT) != 0) {
            Vector |= 1;
        }
    }

    if ((Instruction & ARM_SIMD_TRANSFER_TO_REGISTER) != 0) {
        RegisterString = Context->Operand1;
        VectorString = Context->Operand2;

    } else {
        VectorString = Context->Operand1;
        RegisterString = Context->Operand2;
    }

    strcpy(Context->Mnemonic, ARM_VMOV_MNEMONIC);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    strcpy(RegisterString, DbgArmRegisterNames[Register]);
    if ((Instruction & ARM_SIMD_TRANSFER_MOVE_SCALAR) != 0) {
        sprintf(VectorString,
                "%s%d[%d]",
                VectorTypeString,
                Vector,
                VectorIndex);

    } else {
        sprintf(VectorString, "%s%d", VectorTypeString, Vector);
    }

    return;
}

VOID
DbgpArmDecodeSimdSpecialMove (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an ARM register to special register move instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG Register;
    PSTR RegisterName;
    ULONG SpecialRegister;

    Instruction = Context->Instruction;
    Register = (Instruction & ARM_SIMD_TRANSFER_REGISTER_MASK) >>
               ARM_SIMD_TRANSFER_REGISTER_SHIFT;

    SpecialRegister = (Instruction & ARM_SIMD_TRANSFER_SPECIAL_MASK) >>
                      ARM_SIMD_TRANSFER_SPECIAL_SHIFT;

    if ((Instruction & ARM_SIMD_TRANSFER_TO_REGISTER) != 0) {
        BaseMnemonic = ARM_VMRS_MNEMONIC;
        if ((Register == 0xF) && (SpecialRegister == 1)) {
            RegisterName = ARM_SIMD_APSR_REGISTER;

        } else {
            RegisterName = DbgArmRegisterNames[Register];
        }

        strcpy(Context->Operand1, RegisterName);
        strcpy(Context->Operand2, DbgArmSpecialRegisterNames[SpecialRegister]);

    } else {
        BaseMnemonic = ARM_VMSR_MNEMONIC;
        strcpy(Context->Operand1, DbgArmSpecialRegisterNames[SpecialRegister]);
        strcpy(Context->Operand2, DbgArmRegisterNames[Register]);
    }

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

VOID
DbgpArmDecodeSimdDuplicate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an ARM register to floating point duplicate
    instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG Register;
    ULONG Size;
    ULONG Vector;
    PSTR VectorTypeString;

    Instruction = Context->Instruction;
    Register = (Instruction & ARM_SIMD_TRANSFER_REGISTER_MASK) >>
               ARM_SIMD_TRANSFER_REGISTER_SHIFT;

    Vector = (Instruction & ARM_SIMD_TRANSFER_VECTOR_MASK) >>
             ARM_SIMD_TRANSFER_VECTOR_SHIFT;

    if ((Instruction & ARM_SIMD_TRANSFER_VECTOR_BIT) != 0) {
        Vector |= (1 << 4);
    }

    //
    // Determine the size of the transfers.
    //

    Size = ARM_SIMD_TRANSFER_DUP_BUILD_SIZE_ENCODING(Instruction);
    switch (Size) {
    case ARM_SIMD_TRANSFER_DUP_SIZE_8:
        MnemonicSuffix = ARM_SIMD_DATA_SIZE_8;
        break;

    case ARM_SIMD_TRANSFER_DUP_SIZE_16:
        MnemonicSuffix = ARM_SIMD_DATA_SIZE_16;
        break;

    case ARM_SIMD_TRANSFER_DUP_SIZE_32:
        MnemonicSuffix = ARM_SIMD_DATA_SIZE_32;
        break;

    default:
        DbgpArmDecodeUndefined(Context);
        return;
    }

    //
    // Get the vector type.
    //

    if ((Instruction & ARM_SIMD_TRANSFER_DUP_QUADWORD) != 0) {
        VectorTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;

    } else {
        VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    }

    strcpy(Context->Mnemonic, ARM_VDUP_MNEMONIC);
    strcpy(Context->PostConditionMnemonicSuffix, MnemonicSuffix);
    sprintf(Context->Operand1, "%s%d", VectorTypeString, Vector);
    strcpy(Context->Operand2, DbgArmRegisterNames[Register]);
    return;
}

VOID
DbgpArmDecodeSimdLoadStoreRegister (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD and floating point register load/store
    instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG Offset;
    ULONG Register;
    CHAR Sign;
    ULONG Vector;
    PSTR VectorTypeString;

    Instruction = Context->Instruction;
    Register = (Instruction & ARM_SIMD_LOAD_STORE_REGISTER_MASK) >>
               ARM_SIMD_LOAD_STORE_REGISTER_SHIFT;

    if ((Instruction & ARM_LOAD_BIT) != 0) {
        BaseMnemonic = ARM_VLD_MNEMONIC;

    } else {
        BaseMnemonic = ARM_VST_MNEMONIC;
    }

    //
    // Get the correct vector value based on whether it is single or double
    // precision.
    //

    Vector = (Instruction & ARM_SIMD_LOAD_STORE_VECTOR_MASK) >>
             ARM_SIMD_LOAD_STORE_VECTOR_SHIFT;

    if ((Instruction & ARM_SIMD_LOAD_STORE_DOUBLE) != 0) {
        if ((Instruction & ARM_SIMD_LOAD_STORE_VECTOR_BIT) != 0) {
            Vector |= (1 << 4);
        }

        VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;

    } else {
        Vector <<= 1;
        if ((Instruction & ARM_SIMD_LOAD_STORE_VECTOR_BIT) != 0) {
            Vector |= 1;
        }

        VectorTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
    }

    //
    // Get the immediate offset and its sign.
    //

    Offset = (Instruction & ARM_SIMD_LOAD_STORE_IMMEDIATE8_MASK) >>
             ARM_SIMD_LOAD_STORE_IMMEDIATE8_SHIFT;

    Offset <<= 2;
    if ((Instruction & ARM_SIMD_LOAD_STORE_ADD_BIT) != 0) {
        Sign = '+';

    } else {
        Sign = '-';
    }

    sprintf(Context->Mnemonic,
            "%s%s",
            BaseMnemonic,
            ARM_FLOATING_POINT_REGISTER);

    sprintf(Context->Operand1, "%s%d", VectorTypeString, Vector);
    if (Offset == 0) {
        sprintf(Context->Operand2, "[%s]", DbgArmRegisterNames[Register]);

    } else {
        sprintf(Context->Operand2,
                "[%s, #%c%d]",
                DbgArmRegisterNames[Register],
                Sign,
                Offset);
    }

    return;
}

VOID
DbgpArmDecodeSimdLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD and floating point multiple register
    load/store instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    PSTR MnemonicSuffix;
    ULONG Operation;
    PSTR PreConditionMnemonicSuffix;
    BOOL PushPop;
    ULONG Register;
    ULONG Vector;
    ULONG VectorCount;
    PSTR VectorListString;
    ULONG VectorListStringSize;
    PSTR VectorTypeString;
    PSTR WriteBack;

    Instruction = Context->Instruction;
    Operation = Instruction & ARM_SIMD_LOAD_STORE_OP_MASK;
    Register = (Instruction & ARM_SIMD_LOAD_STORE_REGISTER_MASK) >>
               ARM_SIMD_LOAD_STORE_REGISTER_SHIFT;

    //
    // Determine if this is a load, store, push or pop.
    //

    PushPop = FALSE;
    if ((Register == ARM_STACK_REGISTER) &&
        ((Operation == ARM_SIMD_LOAD_STORE_OP_VPOP) ||
         (Operation == ARM_SIMD_LOAD_STORE_OP_VPUSH))) {

        if ((Instruction & ARM_LOAD_BIT) != 0) {
            BaseMnemonic = ARM_VPOP_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VPUSH_MNEMONIC;
        }

        PreConditionMnemonicSuffix = "";
        MnemonicSuffix = "";
        PushPop = TRUE;

    } else {
        if ((Instruction & ARM_LOAD_BIT) != 0) {
            BaseMnemonic = ARM_VLD_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VST_MNEMONIC;
        }

        PreConditionMnemonicSuffix = ARM_FLOATING_POINT_MULTIPLE;
        MnemonicSuffix = DbgpArmGetLoadStoreTypeString(Instruction);
    }

    sprintf(Context->Mnemonic,
            "%s%s%s",
            BaseMnemonic,
            PreConditionMnemonicSuffix,
            MnemonicSuffix);

    //
    // Gather the starting vector and the vector count.
    //

    VectorCount = (Instruction & ARM_SIMD_LOAD_STORE_IMMEDIATE8_MASK) >>
                  ARM_SIMD_LOAD_STORE_IMMEDIATE8_SHIFT;

    Vector = (Instruction & ARM_SIMD_LOAD_STORE_VECTOR_MASK) >>
             ARM_SIMD_LOAD_STORE_VECTOR_SHIFT;

    if ((Instruction & ARM_SIMD_LOAD_STORE_DOUBLE) != 0) {
        if ((Instruction & ARM_SIMD_LOAD_STORE_VECTOR_BIT) != 0) {
            Vector |= (1 << 4);
        }

        VectorCount >>= 1;
        VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;

    } else {
        Vector <<= 1;
        if ((Instruction & ARM_SIMD_LOAD_STORE_VECTOR_BIT) != 0) {
            Vector |= 1;
        }

        VectorTypeString = ARM_FLOATING_POINT_SINGLE_PRECISION_VECTOR;
    }

    //
    // Write the register (the first operand) nad add the ! if the operation
    // does a write back. Push/pop operations are always write back.
    //

    if (PushPop == FALSE) {
        WriteBack = "";
        if ((Instruction & ARM_WRITE_BACK_BIT) != 0) {
            WriteBack = "!";
        }

        sprintf(Context->Operand1,
                "%s%s",
                DbgArmRegisterNames[Register],
                WriteBack);

        VectorListString = Context->Operand2;
        VectorListStringSize = sizeof(Context->Operand2);

    } else {
        VectorListString = Context->Operand1;
        VectorListStringSize = sizeof(Context->Operand1);
    }

    //
    // Now print the vector list.
    //

    DbgpArmPrintVectorList(VectorListString,
                           VectorListStringSize,
                           Vector,
                           VectorCount,
                           1,
                           VectorTypeString,
                           0,
                           0);

    return;
}

VOID
DbgpArmDecodeSimdElementLoadAllLanes (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD element load to all lanes instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR AlignString;
    PSTR ElementSuffix;
    ULONG Instruction;
    ULONG Rm;
    ULONG Rn;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG Vector;
    ULONG VectorCount;
    ULONG VectorIncrement;
    PSTR WriteBack;

    Instruction = Context->Instruction;
    Rm = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_RM_MASK) >>
         ARM_SIMD_ELEMENT_LOAD_STORE_RM_SHIFT;

    Rn = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_RN_MASK) >>
         ARM_SIMD_ELEMENT_LOAD_STORE_RN_SHIFT;

    Vector = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_MASK) >>
             ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_SHIFT;

    if ((Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_BIT) != 0) {
        Vector |= (1 << 4);
    }

    //
    // Determine the number of elements in the structure being loaded and the
    // number and spacing of the vectors. Also collect the alignment string,
    // which depends on the size and the element count.
    //

    VectorIncrement = 1;
    if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_TWO_BIT) != 0) {
        VectorIncrement = 2;
    }

    AlignString = "";
    ElementSuffix = "";
    VectorCount = 0;
    switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_MASK) {
    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_1:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_1_ELEMENT_SUFFIX;
        VectorCount = 1;
        VectorIncrement = 1;
        if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_TWO_BIT) != 0) {
            VectorCount = 2;
        }

        switch (Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_MASK) {
        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_16:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_16;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_32:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_32;
            }

            break;

        default:
            break;
        }

        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_2:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_2_ELEMENT_SUFFIX;
        VectorCount = 2;
        switch (Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_MASK) {
        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_8:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_16;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_16:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_32;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_32:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_64;
            }

            break;

        default:
            break;
        }

        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_3:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_3_ELEMENT_SUFFIX;
        VectorCount = 3;
        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_4:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_4_ELEMENT_SUFFIX;
        VectorCount = 4;
        switch (Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_MASK) {
        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_8:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_32;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_16:
        case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_32:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_64;
            }

            break;

        default:
            if ((Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_ALIGN) != 0) {
                AlignString = ARM_SIMD_ALIGN_128;
            }

            break;
        }

        break;

    //
    // This should never hit as all values are accounted for above.
    //

    default:
        break;
    }

    //
    // Get the size suffix.
    //

    SizeValueSuffix = "";
    SizeTypeSuffix = ARM_SIMD_DATA_DEFAULT;
    switch (Instruction & ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_MASK) {
    case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_8:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;
        break;

    case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_16:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        break;

    case ARM_SIMD_ELEMENT_LOAD_ALL_LANES_SIZE_32:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        break;

    default:
        SizeTypeSuffix = "";
        break;
    }

    sprintf(Context->Mnemonic, "%s%s", ARM_VLD_MNEMONIC, ElementSuffix);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    //
    // Assemble the vector list.
    //

    DbgpArmPrintVectorList(Context->Operand1,
                           sizeof(Context->Operand1),
                           Vector,
                           VectorCount,
                           VectorIncrement,
                           ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
                           0,
                           DBG_ARM_VECTOR_LIST_FLAG_INDEX |
                           DBG_ARM_VECTOR_LIST_FLAG_ALL_LANES);

    //
    // Assemble the register operands.
    //

    WriteBack = "";
    if (Rm == ARM_STACK_REGISTER) {
        WriteBack = "!";
    }

    sprintf(Context->Operand2,
            "[%s%s]%s",
            DbgArmRegisterNames[Rn],
            AlignString,
            WriteBack);

    if ((Rm != ARM_STACK_REGISTER) && (Rm != ARM_PC_REGISTER)) {
        strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    }

    return;
}

VOID
DbgpArmDecodeSimdElementLoadStoreSingle (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD element load/store from/to a single structure.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR AlignString;
    ULONG AlignValue;
    PSTR BaseMnemonic;
    PSTR ElementSuffix;
    ULONG Instruction;
    ULONG Rm;
    ULONG Rn;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG Vector;
    ULONG VectorCount;
    ULONG VectorIncrement;
    ULONG VectorIndex;
    PSTR WriteBack;

    AlignString = "";
    ElementSuffix = "";
    SizeValueSuffix = "";
    SizeTypeSuffix = ARM_SIMD_DATA_DEFAULT;

    //
    // The base mnemonic is either vector load or vector store.
    //

    Instruction = Context->Instruction;
    if ((Instruction & ARM_SIMD_ELEMENT_LOAD_BIT) != 0) {
        BaseMnemonic = ARM_VLD_MNEMONIC;

    } else {
        BaseMnemonic = ARM_VST_MNEMONIC;
    }

    Rm = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_RM_MASK) >>
         ARM_SIMD_ELEMENT_LOAD_STORE_RM_SHIFT;

    Rn = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_RN_MASK) >>
         ARM_SIMD_ELEMENT_LOAD_STORE_RN_SHIFT;

    Vector = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_MASK) >>
             ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_SHIFT;

    if ((Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_BIT) != 0) {
        Vector |= (1 << 4);
    }

    //
    // Get the size suffix, vector index, vector increment, and alignment value.
    //

    AlignValue = 0;
    VectorIndex = 0;
    VectorIncrement = 1;
    switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_MASK) {
    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_8:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;
        VectorIndex = (Instruction &
                       ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_8_INDEX_MASK) >>
                      ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_8_INDEX_SHIFT;

        AlignValue = (Instruction &
                      ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_8_ALIGN_MASK) >>
                     ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_8_ALIGN_SHIFT;

        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        VectorIndex = (Instruction &
                       ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16_INDEX_MASK) >>
                      ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16_INDEX_SHIFT;

        if ((Instruction &
             ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16_INCREMENT) != 0) {

            VectorIncrement = 2;
        }

        AlignValue = (Instruction &
                      ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16_ALIGN_MASK) >>
                     ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16_ALIGN_SHIFT;

        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        VectorIndex = (Instruction &
                       ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32_INDEX_MASK) >>
                      ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32_INDEX_SHIFT;

        if ((Instruction &
             ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32_INCREMENT) != 0) {

            VectorIncrement = 2;
        }

        AlignValue = (Instruction &
                      ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32_ALIGN_MASK) >>
                     ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32_ALIGN_SHIFT;

        break;

    default:
        SizeTypeSuffix = "";
        break;
    }

    //
    // Determine the number of elements being loaded/stored and the alignment
    // string.
    //

    VectorCount = 0;
    switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_MASK) {
    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_1:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_1_ELEMENT_SUFFIX;
        VectorCount = 1;
        switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_MASK) {
        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16:
            if (AlignValue != 0) {
                AlignString = ARM_SIMD_ALIGN_16;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32:
            if (AlignValue != 0) {
                AlignString = ARM_SIMD_ALIGN_32;
            }

            break;

        default:
            break;
        }

        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_2:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_2_ELEMENT_SUFFIX;
        VectorCount = 2;
        switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_MASK) {
        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_8:
            if (AlignValue != 0) {
                AlignString = ARM_SIMD_ALIGN_16;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16:
            if (AlignValue != 0) {
                AlignString = ARM_SIMD_ALIGN_32;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32:
            if (AlignValue != 0) {
                AlignString = ARM_SIMD_ALIGN_64;
            }

            break;

        default:
            break;
        }

        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_3:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_3_ELEMENT_SUFFIX;
        VectorCount = 3;
        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_ELEMENT_4:
        ElementSuffix = ARM_SIMD_ELEMENT_LOAD_STORE_4_ELEMENT_SUFFIX;
        VectorCount = 4;
        switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_MASK) {
        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_8:
            if (AlignValue != 0) {
                AlignString = ARM_SIMD_ALIGN_32;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_16:
            if (AlignValue != 0) {
                AlignString = ARM_SIMD_ALIGN_64;
            }

            break;

        case ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32:
            if (AlignValue ==
                ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32_ALIGN_64) {

                AlignString = ARM_SIMD_ALIGN_64;

            } else if (AlignValue ==
                       ARM_SIMD_ELEMENT_LOAD_STORE_SINGLE_SIZE_32_ALIGN_128) {

                AlignString = ARM_SIMD_ALIGN_128;
            }

            break;

        default:
            break;
        }

        break;

    //
    // This should never hit as all values are accounted for above.
    //

    default:
        break;
    }

    sprintf(Context->Mnemonic, "%s%s", BaseMnemonic, ElementSuffix);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    //
    // Assemble the vector list.
    //

    DbgpArmPrintVectorList(Context->Operand1,
                           sizeof(Context->Operand1),
                           Vector,
                           VectorCount,
                           VectorIncrement,
                           ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
                           VectorIndex,
                           DBG_ARM_VECTOR_LIST_FLAG_INDEX);

    //
    // Assemble the register operands.
    //

    WriteBack = "";
    if (Rm == ARM_STACK_REGISTER) {
        WriteBack = "!";
    }

    sprintf(Context->Operand2,
            "[%s%s]%s",
            DbgArmRegisterNames[Rn],
            AlignString,
            WriteBack);

    if ((Rm != ARM_STACK_REGISTER) && (Rm != ARM_PC_REGISTER)) {
        strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    }

    return;
}

VOID
DbgpArmDecodeSimdElementLoadStoreMultiple (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD element load/store from/to multiple structures.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR AlignString;
    PSTR BaseMnemonic;
    PSTR ElementSuffix;
    ULONG Instruction;
    ULONG Rm;
    ULONG Rn;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG Type;
    ULONG Vector;
    ULONG VectorCount;
    ULONG VectorIncrement;
    PSTR WriteBack;

    //
    // The base mnemonic is either vector load or vector store.
    //

    Instruction = Context->Instruction;
    if ((Instruction & ARM_SIMD_ELEMENT_LOAD_BIT) != 0) {
        BaseMnemonic = ARM_VLD_MNEMONIC;

    } else {
        BaseMnemonic = ARM_VST_MNEMONIC;
    }

    Rm = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_RM_MASK) >>
         ARM_SIMD_ELEMENT_LOAD_STORE_RM_SHIFT;

    Rn = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_RN_MASK) >>
         ARM_SIMD_ELEMENT_LOAD_STORE_RN_SHIFT;

    Vector = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_MASK) >>
             ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_SHIFT;

    if ((Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_VECTOR_BIT) != 0) {
        Vector |= (1 << 4);
    }

    //
    // Get the size suffix.
    //

    SizeTypeSuffix = ARM_SIMD_DATA_DEFAULT;
    SizeValueSuffix = "";
    VectorIncrement = 1;
    switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_SIZE_MASK) {
    case ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_SIZE_8:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;
        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_SIZE_16:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_SIZE_32:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_SIZE_64:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_64;
        break;

    default:
        break;
    }

    //
    // Get the alignment string.
    //

    AlignString = "";
    switch (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_ALIGN_MASK) {
    case ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_ALIGN_64:
        AlignString = ARM_SIMD_ALIGN_64;
        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_ALIGN_128:
        AlignString = ARM_SIMD_ALIGN_128;
        break;

    case ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_ALIGN_256:
        AlignString = ARM_SIMD_ALIGN_256;
        break;

    default:
        break;
    }

    //
    // Determine the number of elements being loaded/stored, the vector count,
    // vector increment based on the type field.
    //

    VectorIncrement = 1;
    if ((Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_INCREMENT) != 0) {
        VectorIncrement = 2;
    }

    Type = (Instruction & ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_TYPE_MASK) >>
           ARM_SIMD_ELEMENT_LOAD_STORE_MULTIPLE_TYPE_SHIFT;

    ElementSuffix = DbgArmSimdElementLoadStoreMultipleElementSuffix[Type];
    VectorCount = DbgArmSimdElementLoadStoreMultipleVectorCount[Type];
    sprintf(Context->Mnemonic, "%s%s", BaseMnemonic, ElementSuffix);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    //
    // Assemble the vector list.
    //

    DbgpArmPrintVectorList(Context->Operand1,
                           sizeof(Context->Operand1),
                           Vector,
                           VectorCount,
                           VectorIncrement,
                           ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
                           0,
                           0);

    //
    // Assemble the register operands.
    //

    WriteBack = "";
    if (Rm == ARM_STACK_REGISTER) {
        WriteBack = "!";
    }

    sprintf(Context->Operand2,
            "[%s%s]%s",
            DbgArmRegisterNames[Rn],
            AlignString,
            WriteBack);

    if ((Rm != ARM_STACK_REGISTER) && (Rm != ARM_PC_REGISTER)) {
        strcpy(Context->Operand3, DbgArmRegisterNames[Rm]);
    }

    return;
}

VOID
DbgpArmDecodeSimdThreeRegistersSameLength (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data processing instructions with three
    registers of the same length.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    BOOL FloatSize;
    ULONG Instruction;
    BOOL IntegerSize;
    BOOL NoSizeSuffix;
    BOOL PolynomialSize;
    BOOL SignedSize;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    BOOL TwoVectors;
    ULONG VectorD;
    PSTR VectorDString;
    ULONG VectorM;
    PSTR VectorMString;
    ULONG VectorN;
    PSTR VectorNString;
    PSTR VectorTypeString;

    FloatSize = FALSE;
    IntegerSize = FALSE;
    NoSizeSuffix = FALSE;
    PolynomialSize = FALSE;
    SignedSize = TRUE;
    TwoVectors = FALSE;
    Instruction = Context->Instruction;
    VectorDString = Context->Operand1;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorMString = Context->Operand3;
    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    VectorNString = Context->Operand2;
    VectorN = (Instruction & ARM_SIMD_DATA_PROCESSING_VN_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VN_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VN_BIT) != 0) {
        VectorN |= (1 << 4);
    }

    VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
        VectorTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
    }

    //
    // Determine the base mnemonic. Some instructions ignore the size encodings
    // and others have integer or float encodings rather than the default
    // signed/unsigned. Take note so that the correct size suffix can be
    // calculated.
    //

    BaseMnemonic = NULL;
    switch (Instruction & ARM_SIMD_DATA_PROCESSING_3_SAME_OPERATION_MASK) {
    case ARM_SIMD_VHADD_MASK:
        BaseMnemonic = ARM_VHADD_MNEMONIC;
        break;

    case ARM_SIMD_VQADD_MASK:
        BaseMnemonic = ARM_VQADD_MNEMONIC;
        break;

    case ARM_SIMD_VRHADD_MASK:
        BaseMnemonic = ARM_VRHADD_MNEMONIC;
        break;

    case ARM_SIMD_BITWISE_MASK:
        switch (Instruction & ARM_SIMD_BITWISE_OP_MASK) {
        case ARM_SIMD_BITWISE_VAND_VALUE:
            BaseMnemonic = ARM_VAND_MNEMONIC;
            break;

        case ARM_SIMD_BITWISE_VBIC_VALUE:
            BaseMnemonic = ARM_VBIC_MNEMONIC;
            break;

        case ARM_SIMD_BITWISE_VORR_VALUE:
            if (VectorM == VectorN) {
                BaseMnemonic = ARM_VMOV_MNEMONIC;
                TwoVectors = TRUE;

            } else {
                BaseMnemonic = ARM_VORR_MNEMONIC;
            }

            break;

        case ARM_SIMD_BITWISE_VORN_VALUE:
            BaseMnemonic = ARM_VORN_MNEMONIC;
            break;

        case ARM_SIMD_BITWISE_VEOR_VALUE:
            BaseMnemonic = ARM_VEOR_MNEMONIC;
            break;

        case ARM_SIMD_BITWISE_VBSL_VALUE:
            BaseMnemonic = ARM_VBSL_MNEMONIC;
            break;

        case ARM_SIMD_BITWISE_VBIT_VALUE:
            BaseMnemonic = ARM_VBIT_MNEMONIC;
            break;

        case ARM_SIMD_BITWISE_VBIF_VALUE:
            BaseMnemonic = ARM_VBIF_MNEMONIC;
            break;

        default:
            break;
        }

        NoSizeSuffix = TRUE;
        break;

    case ARM_SIMD_VHSUB_MASK:
        BaseMnemonic = ARM_VHSUB_MNEMONIC;
        break;

    case ARM_SIMD_VQSUB_MASK:
        BaseMnemonic = ARM_VQSUB_MNEMONIC;
        break;

    case ARM_SIMD_VCGT_MASK:
        BaseMnemonic = ARM_VCGT_MNEMONIC;
        break;

    case ARM_SIMD_VCGE_MASK:
        BaseMnemonic = ARM_VCGE_MNEMONIC;
        break;

    case ARM_SIMD_VSHL_REG_MASK:
        BaseMnemonic = ARM_VSHL_MNEMONIC;
        VectorMString = Context->Operand2;
        VectorNString = Context->Operand3;
        break;

    case ARM_SIMD_VQSHL_REG_MASK:
        BaseMnemonic = ARM_VQSHL_MNEMONIC;
        VectorMString = Context->Operand2;
        VectorNString = Context->Operand3;
        break;

    case ARM_SIMD_VRSHL_MASK:
        BaseMnemonic = ARM_VRSHL_MNEMONIC;
        VectorMString = Context->Operand2;
        VectorNString = Context->Operand3;
        break;

    case ARM_SIMD_VQRSHL_MASK:
        BaseMnemonic = ARM_VQRSHL_MNEMONIC;
        VectorMString = Context->Operand2;
        VectorNString = Context->Operand3;
        break;

    case ARM_SIMD_VMAX_INT_MASK:
        BaseMnemonic = ARM_VMAX_MNEMONIC;
        break;

    case ARM_SIMD_VMIN_INT_MASK:
        BaseMnemonic = ARM_VMIN_MNEMONIC;
        break;

    case ARM_SIMD_VABD_MASK:
        BaseMnemonic = ARM_VABD_MNEMONIC;
        break;

    case ARM_SIMD_VABA_MASK:
        BaseMnemonic = ARM_VABA_MNEMONIC;
        break;

    case ARM_SIMD_VADD_INT_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VSUB_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VADD_MNEMONIC;
        }

        IntegerSize = TRUE;
        break;

    case ARM_SIMD_VTST_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VCEQ_MNEMONIC;
            IntegerSize = TRUE;

        } else {
            BaseMnemonic = ARM_VTST_MNEMONIC;
            SignedSize = FALSE;
        }

        break;

    case ARM_SIMD_VMLA_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VMLS_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VMLA_MNEMONIC;
        }

        IntegerSize = TRUE;
        break;

    case ARM_SIMD_VMUL_MASK:
        BaseMnemonic = ARM_VMUL_MNEMONIC;
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            PolynomialSize = TRUE;
        }

        break;

    case ARM_SIMD_VPMAX_INT_MASK:
        BaseMnemonic = ARM_VPMAX_MNEMONIC;
        break;

    case ARM_SIMD_VPMIN_INT_MASK:
        BaseMnemonic = ARM_VPMIN_MNEMONIC;
        break;

    case ARM_SIMD_VQDMULH_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VQRDMULH_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VQDMULH_MNEMONIC;
        }

        Instruction &= ~ARM_SIMD_DATA_PROCESSING_UNSIGNED;
        break;

    case ARM_SIMD_VPADD_INT_MASK:
        BaseMnemonic = ARM_VPADD_MNEMONIC;
        IntegerSize = TRUE;
        break;

    case ARM_SIMD_VFMA_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_VFM_SUBTRACT) != 0) {
            BaseMnemonic = ARM_VFMS_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VFMA_MNEMONIC;
        }

        FloatSize = TRUE;
        break;

    case ARM_SIMD_FP_MATH_MASK:
    case ARM_SIMD_FP_MATH_MASK | ARM_SIMD_FP_MULT:
        switch (Instruction & ARM_SIMD_FP_MATH_OP_MASK) {
        case ARM_SIMD_FP_MATH_VADD_VALUE:
            BaseMnemonic = ARM_VADD_MNEMONIC;
            break;

        case ARM_SIMD_FP_MATH_VSUB_VALUE:
            BaseMnemonic = ARM_VSUB_MNEMONIC;
            break;

        case ARM_SIMD_FP_MATH_VPADD_VALUE:
            BaseMnemonic = ARM_VPADD_MNEMONIC;
            break;

        case ARM_SIMD_FP_MATH_VABD_VALUE:
            BaseMnemonic = ARM_VABD_MNEMONIC;
            break;

        case ARM_SIMD_FP_MATH_VMLA_VALUE:
            BaseMnemonic = ARM_VMLA_MNEMONIC;
            break;

        case ARM_SIMD_FP_MATH_VMLS_VALUE:
            BaseMnemonic = ARM_VMLS_MNEMONIC;
            break;

        case ARM_SIMD_FP_MATH_VMUL_VALUE:
            BaseMnemonic = ARM_VMUL_MNEMONIC;
            break;

        default:
            break;
        }

        FloatSize = TRUE;
        break;

    case ARM_SIMD_COMPARE_MASK:
    case ARM_SIMD_COMPARE_MASK | ARM_SIMD_ABSOLUTE:
        switch (Instruction & ARM_SIMD_COMPARE_OP_MASK) {
        case ARM_SIMD_COMPARE_VCEQ_VALUE:
            BaseMnemonic = ARM_VCEQ_MNEMONIC;
            break;

        case ARM_SIMD_COMPARE_VCGE_VALUE:
            BaseMnemonic = ARM_VCGE_MNEMONIC;
            break;

        case ARM_SIMD_COMPARE_VCGT_VALUE:
            BaseMnemonic = ARM_VCGT_MNEMONIC;
            break;

        case ARM_SIMD_COMPARE_VACGE_VALUE:
            BaseMnemonic = ARM_VACGE_MNEMONIC;
            break;

        case ARM_SIMD_COMPARE_VACGT_VALUE:
            BaseMnemonic = ARM_VACGT_MNEMONIC;
            break;

        default:
            break;
        }

        FloatSize = TRUE;
        break;

    case ARM_SIMD_MIN_MAX_FLOAT_MASK:
        switch (Instruction & ARM_SIMD_MIN_MAX_FLOAT_OP_MASK) {
        case ARM_SIMD_MIN_MAX_FLOAT_VMAX_VALUE:
            BaseMnemonic = ARM_VMAX_MNEMONIC;
            break;

        case ARM_SIMD_MIN_MAX_FLOAT_VMIN_VALUE:
            BaseMnemonic = ARM_VMIN_MNEMONIC;
            break;

        case ARM_SIMD_MIN_MAX_FLOAT_VPMAX_VALUE:
            BaseMnemonic = ARM_VPMAX_MNEMONIC;
            break;

        case ARM_SIMD_MIN_MAX_FLOAT_VPMIN_VALUE:
            BaseMnemonic = ARM_VPMIN_MNEMONIC;
            break;

        default:
            break;
        }

        FloatSize = TRUE;
        break;

    case ARM_SIMD_RECIPROCOL_MASK:
        switch (Instruction & ARM_SIMD_RECIPROCOL_OP_MASK) {
        case ARM_SIMD_RECIPROCOL_VRECPS_VALUE:
            BaseMnemonic = ARM_VRECPS_MNEMONIC;
            break;

        case ARM_SIMD_RECIPROCOL_VRSQRTS_VALUE:
            BaseMnemonic = ARM_VRSQRTS_MNEMONIC;
            break;

        default:
            break;
        }

        FloatSize = TRUE;
        break;

    default:
        break;
    }

    if (BaseMnemonic == NULL) {
        DbgpArmDecodeUndefined(Context);
        return;
    }

    //
    // Parse the instruction assuming it uses the default size suffix.
    //

    SizeTypeSuffix = "";
    SizeValueSuffix = "";
    if (NoSizeSuffix == FALSE) {
        if (FloatSize != FALSE) {
            SizeTypeSuffix = ARM_SIMD_DATA_FLOAT;
            SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;

        } else if (IntegerSize != FALSE) {
            SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;

        } else if (PolynomialSize != FALSE) {
            SizeTypeSuffix = ARM_SIMD_DATA_POLYNOMIAL;

        } else if (SignedSize == FALSE) {
            SizeTypeSuffix = ARM_SIMD_DATA_DEFAULT;

        } else if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            SizeTypeSuffix = ARM_SIMD_DATA_UNSIGNED;

        } else {
            SizeTypeSuffix = ARM_SIMD_DATA_SIGNED;
        }
    }

    if ((NoSizeSuffix == FALSE) && (FloatSize == FALSE)) {
        switch (Instruction & ARM_SIMD_DATA_PROCESSING_3_SAME_SIZE_MASK) {
        case ARM_SIMD_DATA_PROCESSING_3_SAME_SIZE_8:
            SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;
            break;

        case ARM_SIMD_DATA_PROCESSING_3_SAME_SIZE_16:
            SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
            break;

        case ARM_SIMD_DATA_PROCESSING_3_SAME_SIZE_32:
            SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
            break;

        case ARM_SIMD_DATA_PROCESSING_3_SAME_SIZE_64:
            SizeValueSuffix = ARM_SIMD_DATA_SIZE_64;
            break;

        default:
            break;
        }
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    sprintf(VectorDString, "%s%d", VectorTypeString, VectorD);
    sprintf(VectorNString, "%s%d", VectorTypeString, VectorN);
    if (TwoVectors == FALSE) {
        sprintf(VectorMString, "%s%d", VectorTypeString, VectorM);
    }

    return;
}

VOID
DbgpArmDecodeSimdOneRegister (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data processing instrution that uses one
    register and a modified immediate value.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Cmode;
    ARM_IMMEDIATE_FLOAT Float;
    ULONGLONG Immediate;
    ULONG Immediate8;
    ULONG Instruction;
    BOOL PrintFloat;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG Vector;
    PSTR VectorTypeString;

    PrintFloat = FALSE;
    Float.Immediate = 0;
    Instruction = Context->Instruction;

    //
    // Decode the immediate value and the size suffix using the cmode value and
    // the op bit.
    //

    Immediate8 = ARM_SIMD_BUILD_IMMEDIATE8(Instruction);
    Cmode = (Instruction & ARM_SIMD_DATA_PROCESSING_1_REGISTER_CMODE_MASK) >>
            ARM_SIMD_DATA_PROCESSING_1_REGISTER_CMODE_SHIFT;

    switch (Cmode & ARM_SIMD_CMODE_TYPE_MASK) {
    case ARM_SIMD_CMODE_TYPE_I32_NO_SHIFT:
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        Immediate = Immediate8;
        break;

    case ARM_SIMD_CMODE_TYPE_I32_SHIFT_8:
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        Immediate = Immediate8 << 8;
        break;

    case ARM_SIMD_CMODE_TYPE_I32_SHIFT_16:
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        Immediate = Immediate8 << 16;
        break;

    case ARM_SIMD_CMODE_TYPE_I32_SHIFT_24:
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        Immediate = Immediate8 << 24;
        break;

    case ARM_SIMD_CMODE_TYPE_I16_NO_SHIFT:
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        Immediate = Immediate8;
        break;

    case ARM_SIMD_CMODE_TYPE_I16_SHIFT_8:
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        Immediate = Immediate8 << 8;
        break;

    case ARM_SIMD_CMODE_TYPE_I32_SHIFT_ONES:
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        if ((Cmode & ARM_SIMD_CMODE_SHIFT_ONES_16) != 0) {
            Immediate = Immediate8 << 16;
            Immediate |= 0xFFFF;

        } else {
            Immediate = Immediate8 << 8;
            Immediate |= 0xFF;
        }

        break;

    default:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_1_REGISTER_OP_BIT) != 0) {
            if ((Cmode & ARM_SIMD_CMODE_UNDEFINED) != 0) {
                DbgpArmDecodeUndefined(Context);
                return;
            }

            SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
            SizeValueSuffix = ARM_SIMD_DATA_SIZE_64;
            Immediate = ARM_SIMD_BUILD_IMMEDIATE64(Instruction);

        } else {
            if ((Cmode & ARM_SIMD_CMODE_FLOAT_32) != 0) {
                SizeTypeSuffix = ARM_SIMD_DATA_FLOAT;
                SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
                Float.Immediate = ARM_SIMD_BUILD_IMMEDIATE32(Instruction);
                PrintFloat = TRUE;

            } else {
                SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
                SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;
                Immediate = Immediate8;
            }
        }

        break;
    }

    //
    // Get the mnemonic based on the cmode value and the op bit.
    //

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_1_REGISTER_OP_BIT) == 0) {

        //
        // For all modes less than 12, the even modes are vmov and the odds are
        // vorr.
        //

        if ((Cmode < ARM_SIMD_CMODE_NO_OP_VORR_MAX) &&
            ((Cmode & ARM_SIMD_CMODE_NO_OP_VORR_BIT) != 0)) {

            BaseMnemonic = ARM_VORR_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VMOV_MNEMONIC;
        }

    } else {

        //
        // With the two exceptions of cmodes 14 and 15, the odd modes are vbic
        // and the even modes are vmvn.
        //

        if ((Cmode < ARM_SIMD_CMODE_OP_VBIC_MAX) &&
            ((Cmode & ARM_SIMD_CMODE_OP_VBIC_BIT) != 0)) {

            BaseMnemonic = ARM_VBIC_MNEMONIC;

        } else if (Cmode == ARM_SIMD_CMODE_OP_VMOV) {
            BaseMnemonic = ARM_VMOV_MNEMONIC;

        } else if (Cmode == ARM_SIMD_CMODE_OP_UNDEFINED) {
            DbgpArmDecodeUndefined(Context);
            return;

        } else {
            BaseMnemonic = ARM_VMVN_MNEMONIC;
        }
    }

    Vector = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        Vector |= (1 << 4);
    }

    VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
        VectorTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    sprintf(Context->Operand1, "%s%d", VectorTypeString, Vector);
    if (PrintFloat == FALSE) {
        sprintf(Context->Operand2, "#%I64d  ; 0x%I64d", Immediate, Immediate);

    } else {
        sprintf(Context->Operand2,
                "#%d  ; 0x%x %g",
                Immediate8,
                Float.Immediate,
                Float.Float);
    }

    return;
}

VOID
DbgpArmDecodeSimdTwoRegistersWithShift (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data instruction with two registers and a
    shift amount.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    PSTR DestinationSizeSuffix;
    PSTR DestinationTypeSuffix;
    ULONG Immediate;
    ULONG Immediate6;
    ULONG Instruction;
    PSTR SourceSizeSuffix;
    PSTR SourceTypeSuffix;
    ULONG VectorD;
    PSTR VectorDTypeString;
    ULONG VectorM;
    PSTR VectorMTypeString;

    SourceSizeSuffix = "";
    SourceTypeSuffix = "";

    //
    // Gather the information that is shared by most of the two register shift
    // instructions.
    //

    Instruction = Context->Instruction;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    VectorMTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
        VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
    }

    SourceTypeSuffix = "";
    DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
        DestinationTypeSuffix = ARM_SIMD_DATA_UNSIGNED;
    }

    DestinationSizeSuffix = "";
    Immediate6 = (Instruction & ARM_SIMD_2_REGISTER_SHIFT_IMMEDIATE6_MASK) >>
                 ARM_SIMD_2_REGISTER_SHIFT_IMMEDIATE6_SHIFT;

    if ((Instruction & ARM_SIMD_2_REGISTER_SHIFT_64) == 0) {
        if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_32) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            Immediate = 32;
            Immediate -= (Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_32_MASK);

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_16) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_16;
            Immediate = 16;
            Immediate -= (Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_16_MASK);

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_8) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_8;
            Immediate = 8;
            Immediate -= (Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_8_MASK);

        } else {
            DbgpArmDecodeUndefined(Context);
            return;
        }

    } else {
        DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_64;
        Immediate = 64 - Immediate6;
    }

    //
    // Determine the base mnemonic and override any of the size or type
    // information collected above.
    //

    BaseMnemonic = NULL;
    switch (Instruction & ARM_SIMD_2_REGISTER_SHIFT_OPERATION_MASK) {
    case ARM_SIMD_VSHR_MASK:
        BaseMnemonic = ARM_VSHR_MNEMONIC;
        break;

    case ARM_SIMD_VSRA_MASK:
        BaseMnemonic = ARM_VSRA_MNEMONIC;
        break;

    case ARM_SIMD_VRSHR_MASK:
        BaseMnemonic = ARM_VRSHR_MNEMONIC;
        break;

    case ARM_SIMD_VRSRA_MASK:
        BaseMnemonic = ARM_VRSRA_MNEMONIC;
        break;

    case ARM_SIMD_VSRI_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VSRI_MNEMONIC;
        }

        break;

    case ARM_SIMD_VSHL_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VSLI_MNEMONIC;
            DestinationTypeSuffix = "";

        } else {
            BaseMnemonic = ARM_VSHL_MNEMONIC;
            DestinationTypeSuffix = ARM_SIMD_DATA_INTEGER;
        }

        break;

    case ARM_SIMD_VQSHLU_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VQSHLU_MNEMONIC;
            DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
        }

        break;

    case ARM_SIMD_VQSHL_IMM_MASK:
        BaseMnemonic = ARM_VQSHL_MNEMONIC;
        break;

    case ARM_SIMD_VSHRN_MASK:
        switch (Instruction & ARM_SIMD_VSHRN_OP_MASK) {
        case ARM_SIMD_VSHRN_OP_VALUE:
            BaseMnemonic = ARM_VSHRN_MNEMONIC;
            DestinationTypeSuffix = ARM_SIMD_DATA_INTEGER;
            break;

        case ARM_SIMD_VRSHRN_OP_VALUE:
            BaseMnemonic = ARM_VRSHRN_MNEMONIC;
            DestinationTypeSuffix = ARM_SIMD_DATA_INTEGER;
            break;

        case ARM_SIMD_VQSHRUN_OP_VALUE:
            BaseMnemonic = ARM_VQSHRUN_MNEMONIC;
            DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            break;

        case ARM_SIMD_VQRSHRUN_OP_VALUE:
            BaseMnemonic = ARM_VQRSHRUN_MNEMONIC;
            DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            break;

        default:
            break;
        }

        //
        // The size suffix is twice that of the normal encoding.
        //

        if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_32) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_64;

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_16) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_8) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_16;
        }

        VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        break;

    case ARM_SIMD_VQSHRN_MASK:
        switch (Instruction & ARM_SIMD_VQSHRN_OP_MASK) {
        case ARM_SIMD_VQSHRN_OP_VALUE:
            BaseMnemonic = ARM_VQSHRN_MNEMONIC;
            break;

        case ARM_SIMD_VQRSHRN_OP_VALUE:
            BaseMnemonic = ARM_VQRSHRN_MNEMONIC;
            break;

        default:
            break;
        }

        //
        // The size suffix is twice that of the normal encoding.
        //

        if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_32) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_64;

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_16) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_8) != 0) {
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_16;
        }

        VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        break;

    case ARM_SIMD_VSHLL_MASK:
        if ((Instruction & ARM_SIMD_VSHLL_OP_MASK) != ARM_SIMD_VSHLL_OP_VALUE) {
            break;
        }

        //
        // The size suffix is twice that of the normal encoding.
        //

        if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_32) != 0) {
            Immediate = Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_32_MASK;

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_16) != 0) {
            Immediate = Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_16_MASK;

        } else if ((Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_8) != 0) {
            Immediate = Immediate6 & ARM_SIMD_2_REGISTER_SHIFT_SIZE_8_MASK;
        }

        if (Immediate == 0) {
            BaseMnemonic = ARM_VMOVL_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VSHLL_MNEMONIC;
        }

        VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        VectorMTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        break;

    case ARM_SIMD_VCVT_TO_FLOAT_MASK:
        BaseMnemonic = ARM_VCVT_MNEMONIC;
        SourceSizeSuffix = ARM_SIMD_DATA_SIZE_32;
        DestinationTypeSuffix = ARM_SIMD_DATA_FLOAT;
        DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;
        Immediate = 64 - Immediate6;
        break;

    case ARM_SIMD_VCVT_TO_FIXED_MASK:
        BaseMnemonic = ARM_VCVT_MNEMONIC;
        SourceTypeSuffix = ARM_SIMD_DATA_FLOAT;
        SourceSizeSuffix = ARM_SIMD_DATA_SIZE_32;
        DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;
        Immediate = 64 - Immediate6;
        break;

    default:
        break;
    }

    if (BaseMnemonic == NULL) {
        DbgpArmDecodeUndefined(Context);
        return;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s%s%s",
            DestinationTypeSuffix,
            DestinationSizeSuffix,
            SourceTypeSuffix,
            SourceSizeSuffix);

    sprintf(Context->Operand1, "%s%d", VectorDTypeString, VectorD);
    sprintf(Context->Operand2, "%s%d", VectorMTypeString, VectorM);
    sprintf(Context->Operand3, "#%d  ; 0x%x", Immediate, Immediate);
    return;
}

VOID
DbgpArmDecodeSimdThreeRegistersDifferentLength (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data instruction with three registers of
    different lengths.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG Size;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG VectorD;
    PSTR VectorDTypeString;
    ULONG VectorM;
    PSTR VectorMTypeString;
    ULONG VectorN;
    PSTR VectorNTypeString;

    //
    // Gather the values that are common to most of the instructions.
    //

    Instruction = Context->Instruction;
    VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorMTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    VectorNTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    VectorN = (Instruction & ARM_SIMD_DATA_PROCESSING_VN_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VN_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VN_BIT) != 0) {
        VectorN |= (1 << 4);
    }

    SizeTypeSuffix = ARM_SIMD_DATA_SIGNED;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
        SizeTypeSuffix = ARM_SIMD_DATA_UNSIGNED;
    }

    Size = (Instruction & ARM_SIMD_3_DIFF_SIZE_MASK) >>
           ARM_SIMD_3_DIFF_SIZE_SHIFT;

    //
    // Sort out which instruction is actually being decoded and modify the
    // common values as necessary.
    //

    BaseMnemonic = NULL;
    switch (Instruction & ARM_SIMD_3_DIFF_OPERATION_MASK) {
    case ARM_SIMD_VADDL_MASK:
        BaseMnemonic = ARM_VADDL_MNEMONIC;
        break;

    case ARM_SIMD_VADDW_MASK:
        BaseMnemonic = ARM_VADDW_MNEMONIC;
        VectorNTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        break;

    case ARM_SIMD_VSUBL_MASK:
        BaseMnemonic = ARM_VSUBL_MNEMONIC;
        break;

    case ARM_SIMD_VSUBW_MASK:
        BaseMnemonic = ARM_VSUBW_MNEMONIC;
        VectorNTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        break;

    case ARM_SIMD_VADDHN_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VRADDHN_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VADDHN_MNEMONIC;
        }

        //
        // The size is double the normal encoding, so add 1 to the encoding.
        //

        Size += 1;
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        VectorNTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        break;

    case ARM_SIMD_VABAL_MASK:
        BaseMnemonic = ARM_VABAL_MNEMONIC;
        break;

    case ARM_SIMD_VSUBHN_MASK:
        if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
            BaseMnemonic = ARM_VRSUBHN_MNEMONIC;

        } else {
            BaseMnemonic = ARM_VSUBHN_MNEMONIC;
        }

        //
        // The size is double the normal encoding, so add 1 to the encoding.
        //

        Size += 1;
        SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        VectorNTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        break;

    case ARM_SIMD_VABDL_MASK:
        BaseMnemonic = ARM_VABDL_MNEMONIC;
        break;

    case ARM_SIMD_VMLAL_MASK:
        BaseMnemonic = ARM_VMLAL_MNEMONIC;
        break;

    case ARM_SIMD_VMLSL_MASK:
        BaseMnemonic = ARM_VMLSL_MNEMONIC;
        break;

    case ARM_SIMD_VQDMLAL_MASK:
        BaseMnemonic = ARM_VQDMLAL_MNEMONIC;
        break;

    case ARM_SIMD_VQDMLSL_MASK:
        BaseMnemonic = ARM_VQDMLSL_MNEMONIC;
        break;

    case ARM_SIMD_VMULL_INT_MASK:
        BaseMnemonic = ARM_VMULL_MNEMONIC;
        break;

    case ARM_SIMD_VQDMULL_MASK:
        BaseMnemonic = ARM_VQDMULL_MNEMONIC;
        break;

    case ARM_SIMD_VMULL_POLY_MASK:
        BaseMnemonic = ARM_VMULL_MNEMONIC;
        SizeTypeSuffix = ARM_SIMD_DATA_POLYNOMIAL;
        break;

    default:
        break;
    }

    if (BaseMnemonic == NULL) {
        DbgpArmDecodeUndefined(Context);
        return;
    }

    //
    // Get the size suffix now that it has been adjusted for the particular
    // instruction being decoded.
    //

    switch (Size) {
    case ARM_SIMD_3_DIFF_SIZE_8:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;
        break;

    case ARM_SIMD_3_DIFF_SIZE_16:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        break;

    case ARM_SIMD_3_DIFF_SIZE_32:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        break;

    case ARM_SIMD_3_DIFF_SIZE_64:
        SizeValueSuffix = ARM_SIMD_DATA_SIZE_64;
        break;

    //
    // This should never hit as all possible values are accounted for.
    //

    default:
        SizeValueSuffix = "";
        break;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    sprintf(Context->Operand1, "%s%d", VectorDTypeString, VectorD);
    sprintf(Context->Operand2, "%s%d", VectorNTypeString, VectorN);
    sprintf(Context->Operand3, "%s%d", VectorMTypeString, VectorM);
    return;
}

VOID
DbgpArmDecodeSimdTwoRegistersWithScalar (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data instruction with two registers and a
    scalar.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    BOOL FloatValid;
    ULONG Instruction;
    BOOL QuadwordValid;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG VectorD;
    PSTR VectorDTypeString;
    ULONG VectorM;
    ULONG VectorMIndex;
    ULONG VectorN;
    PSTR VectorNTypeString;

    Instruction = Context->Instruction;
    VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorNTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    VectorN = (Instruction & ARM_SIMD_DATA_PROCESSING_VN_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VN_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VN_BIT) != 0) {
        VectorN |= (1 << 4);
    }

    //
    // Vector M stores both the vector and the index. The division of the bits
    // depend on the instruction's encoded size.
    //

    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    switch (Instruction & ARM_SIMD_2_REGISTER_SCALAR_SIZE_MASK) {
    case ARM_SIMD_2_REGISTER_SCALAR_SIZE_16:
        VectorMIndex = (VectorM &
                        ARM_SIMD_2_REGISTER_SCALAR_SIZE_16_VM_INDEX_MASK) >>
                       ARM_SIMD_2_REGISTER_SCALAR_SIZE_16_VM_INDEX_SHIFT;

        VectorM = (VectorM &
                   ARM_SIMD_2_REGISTER_SCALAR_SIZE_16_VM_VECTOR_MASK) >>
                  ARM_SIMD_2_REGISTER_SCALAR_SIZE_16_VM_VECTOR_SHIFT;

        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_SIZE_32:
        VectorMIndex = (VectorM &
                        ARM_SIMD_2_REGISTER_SCALAR_SIZE_32_VM_INDEX_MASK) >>
                       ARM_SIMD_2_REGISTER_SCALAR_SIZE_32_VM_INDEX_SHIFT;

        VectorM = (VectorM &
                   ARM_SIMD_2_REGISTER_SCALAR_SIZE_32_VM_VECTOR_MASK) >>
                  ARM_SIMD_2_REGISTER_SCALAR_SIZE_32_VM_VECTOR_SHIFT;

        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        break;

    default:
        DbgpArmDecodeUndefined(Context);
        return;
    }

    SizeTypeSuffix = ARM_SIMD_DATA_SIGNED;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_UNSIGNED) != 0) {
        SizeTypeSuffix = ARM_SIMD_DATA_UNSIGNED;
    }

    //
    // Get the base mnemonic and finalize the type suffix.
    //

    FloatValid = FALSE;
    QuadwordValid = FALSE;
    BaseMnemonic = NULL;
    switch (Instruction & ARM_SIMD_2_REGISTER_SCALAR_OPERATION_MASK) {
    case ARM_SIMD_2_REGISTER_SCALAR_VMLA_MASK:
    case (ARM_SIMD_2_REGISTER_SCALAR_VMLA_MASK |
          ARM_SIMD_2_REGISTER_SCALAR_FLOAT):

        FloatValid = TRUE;
        QuadwordValid = TRUE;
        BaseMnemonic = ARM_VMLA_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VMLS_MASK:
    case (ARM_SIMD_2_REGISTER_SCALAR_VMLS_MASK |
          ARM_SIMD_2_REGISTER_SCALAR_FLOAT):

        FloatValid = TRUE;
        QuadwordValid = TRUE;
        BaseMnemonic = ARM_VMLS_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VMLAL_MASK:
        BaseMnemonic = ARM_VMLAL_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VMLSL_MASK:
        BaseMnemonic = ARM_VMLSL_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VQDMLAL_MASK:
        BaseMnemonic = ARM_VQDMLAL_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VQDMLSL_MASK:
        BaseMnemonic = ARM_VQDMLSL_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VMUL_MASK:
    case (ARM_SIMD_2_REGISTER_SCALAR_VMUL_MASK |
          ARM_SIMD_2_REGISTER_SCALAR_FLOAT):

        FloatValid = TRUE;
        QuadwordValid = TRUE;
        BaseMnemonic = ARM_VMUL_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VMULL_MASK:
        BaseMnemonic = ARM_VMULL_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VQDMULL_MASK:
        BaseMnemonic = ARM_VQDMULL_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VQDMULH_MASK:
        QuadwordValid = TRUE;
        BaseMnemonic = ARM_VQDMULH_MNEMONIC;
        break;

    case ARM_SIMD_2_REGISTER_SCALAR_VQRDMULH_MASK:
        QuadwordValid = TRUE;
        BaseMnemonic = ARM_VQRDMULH_MNEMONIC;
        break;

    default:
        break;
    }

    if (BaseMnemonic == NULL) {
        DbgpArmDecodeUndefined(Context);
        return;
    }

    //
    // Process the quadword and float bits if they are valid for the
    // instruction being decoded.
    //

    if (QuadwordValid != FALSE) {
        if ((Instruction & ARM_SIMD_2_REGISTER_SCALAR_QUADWORD) != 0) {
            VectorNTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;

        } else {
            VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
        }
    }

    if (FloatValid != FALSE) {
        if ((Instruction & ARM_SIMD_2_REGISTER_SCALAR_FLOAT) != 0) {
            SizeTypeSuffix = ARM_SIMD_DATA_FLOAT;

        } else {
            SizeTypeSuffix = ARM_SIMD_DATA_INTEGER;
        }
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    sprintf(Context->Operand1, "%s%d", VectorDTypeString, VectorD);
    sprintf(Context->Operand2, "%s%d", VectorNTypeString, VectorN);
    sprintf(Context->Operand3,
            "%s%d[%d]",
            ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
            VectorM,
            VectorMIndex);

    return;
}

VOID
DbgpArmDecodeSimdTwoRegistersMiscellaneous (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data vector extract instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    PSTR DestinationSizeSuffix;
    PSTR DestinationTypeSuffix;
    BOOL GetSizeSuffix;
    ULONG Immediate;
    ULONG Instruction;
    BOOL PrintImmediate;
    ULONG Size;
    PSTR SourceSizeSuffix;
    PSTR SourceTypeSuffix;
    ULONG VectorD;
    PSTR VectorDTypeString;
    ULONG VectorM;
    PSTR VectorMTypeString;

    Instruction = Context->Instruction;
    VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorMTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    SourceSizeSuffix = "";
    SourceTypeSuffix = "";
    DestinationTypeSuffix = ARM_SIMD_DATA_DEFAULT;
    DestinationSizeSuffix = "";
    GetSizeSuffix = TRUE;
    Size = (Instruction & ARM_SIMD_2_REGISTER_MISC_SIZE_MASK) >>
            ARM_SIMD_2_REGISTER_MISC_SIZE_SHIFT;

    //
    // Some instructions include an immediate value. Default to not print it.
    //

    Immediate = 0;
    PrintImmediate = FALSE;

    //
    // Determine the base mnemonic and perform and instruction specific
    // modifications to the vector and size information.
    //

    BaseMnemonic = NULL;
    switch (Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_MASK) {
    case ARM_SIMD_2_REGISTER_MISC_TYPE_0:
        switch (Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_0_OP_MASK) {
        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VREV64_MASK:
            BaseMnemonic = ARM_VREV64_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VREV32_MASK:
            BaseMnemonic = ARM_VREV32_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VREV16_MASK:
            BaseMnemonic = ARM_VREV16_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VPADDL_MASK:
        case (ARM_SIMD_2_REGISTER_MISC_TYPE_0_VPADDL_MASK |
              ARM_SIMD_2_REGISTER_MISC_TYPE_0_UNSIGNED):

            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_0_UNSIGNED) != 0) {
                DestinationTypeSuffix = ARM_SIMD_DATA_UNSIGNED;

            } else {
                DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            }

            BaseMnemonic = ARM_VPADDL_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VCLS_MASK:
            DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            BaseMnemonic = ARM_VCLS_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VCLZ_MASK:
            DestinationTypeSuffix = ARM_SIMD_DATA_INTEGER;
            BaseMnemonic = ARM_VCLZ_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VCNT_MASK:
            BaseMnemonic = ARM_VCNT_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VMVN_MASK:
            GetSizeSuffix = FALSE;
            DestinationTypeSuffix = "";
            BaseMnemonic = ARM_VMVN_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VPADAL_MASK:
        case (ARM_SIMD_2_REGISTER_MISC_TYPE_0_VPADAL_MASK |
              ARM_SIMD_2_REGISTER_MISC_TYPE_0_UNSIGNED):

            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_0_UNSIGNED) != 0) {
                DestinationTypeSuffix = ARM_SIMD_DATA_UNSIGNED;

            } else {
                DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            }

            BaseMnemonic = ARM_VPADAL_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VQABS_MASK:
            DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            BaseMnemonic = ARM_VQABS_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_0_VQNEG_MASK:
            DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            BaseMnemonic = ARM_VQNEG_MNEMONIC;
            break;

        default:
            break;
        }

        //
        // All of the type 0 instructions depend on the quadword bit.
        //

        if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
            VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        }

        break;

    case ARM_SIMD_2_REGISTER_MISC_TYPE_1:

        //
        // The majority of these instructions have an immediate 0 value and
        // default to being signed.
        //

        PrintImmediate = TRUE;
        DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
        switch (Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_1_OP_MASK) {
        case ARM_SIMD_2_REGISTER_MISC_TYPE_1_VCGT_MASK:
            BaseMnemonic = ARM_VCGT_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_1_VCGE_MASK:
            BaseMnemonic = ARM_VCGE_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_1_VCEQ_MASK:
            DestinationTypeSuffix = ARM_SIMD_DATA_INTEGER;
            BaseMnemonic = ARM_VCEQ_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_1_VCLE_MASK:
            BaseMnemonic = ARM_VCLE_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_1_VCLT_MASK:
            BaseMnemonic = ARM_VCLT_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_1_VABS_MASK:
            PrintImmediate = FALSE;
            BaseMnemonic = ARM_VABS_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_1_VNEG_MASK:
            PrintImmediate = FALSE;
            BaseMnemonic = ARM_VNEG_MNEMONIC;
            break;

        default:
            break;
        }

        //
        // All of the type 1 instructions depend on the type specific float bit.
        //

        if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_1_FLOAT) != 0) {
            DestinationTypeSuffix = ARM_SIMD_DATA_FLOAT;
        }

        //
        // All of the type 1 instructions depend on the quadword bit.
        //

        if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
            VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        }

        break;

    case ARM_SIMD_2_REGISTER_MISC_TYPE_2:
        switch (Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_2_OP_MASK) {
        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VSWP_MASK:
            GetSizeSuffix = FALSE;
            DestinationTypeSuffix = "";
            if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
                VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
                VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            }

            BaseMnemonic = ARM_VSWP_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VTRN_MASK:
            if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
                VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
                VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            }

            BaseMnemonic = ARM_VTRN_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VUZP_MASK:
            if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
                VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
                VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            }

            BaseMnemonic = ARM_VUZP_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VZIP_MASK:
            if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
                VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
                VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            }

            BaseMnemonic = ARM_VZIP_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VMOVN_MASK:
            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_2_UNSIGNED) != 0) {
                BaseMnemonic = ARM_VQMOVUN_MNEMONIC;

            } else {
                BaseMnemonic = ARM_VMOVN_MNEMONIC;
            }

            DestinationTypeSuffix = ARM_SIMD_DATA_INTEGER;
            VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;

            //
            // The size encodings are doubled, so add 1 to get the correct
            // destination size suffix below.
            //

            Size += 1;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VQMOVN_MASK:
            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_2_UNSIGNED) != 0) {
                DestinationTypeSuffix = ARM_SIMD_DATA_UNSIGNED;

            } else {
                DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            }

            VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;

            //
            // The size encodings are doubled, so add 1 to get the correct
            // destination size suffix below.
            //

            Size += 1;
            BaseMnemonic = ARM_VQMOVN_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VSHLL_MASK:
            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_2_UNSIGNED) != 0) {
                break;
            }

            Immediate = Size;
            VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            BaseMnemonic = ARM_VSHLL_MNEMONIC;
            DestinationTypeSuffix = ARM_SIMD_DATA_INTEGER;
            PrintImmediate = TRUE;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VCVT_HALF_TO_SINGLE_MASK:
            VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            DestinationTypeSuffix = ARM_SIMD_DATA_FLOAT;
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            SourceTypeSuffix = ARM_SIMD_DATA_FLOAT;
            SourceSizeSuffix = ARM_SIMD_DATA_SIZE_16;
            GetSizeSuffix = FALSE;
            BaseMnemonic = ARM_VCVT_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_2_VCVT_SINGLE_TO_HALF_MASK:
            VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            DestinationTypeSuffix = ARM_SIMD_DATA_FLOAT;
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_16;
            SourceTypeSuffix = ARM_SIMD_DATA_FLOAT;
            SourceSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            GetSizeSuffix = FALSE;
            BaseMnemonic = ARM_VCVT_MNEMONIC;
            break;

        default:
            break;
        }

        break;

    case ARM_SIMD_2_REGISTER_MISC_TYPE_3:
        switch (Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_3_OP_MASK) {
        case ARM_SIMD_2_REGISTER_MISC_TYPE_3_VRECPE_MASK:
            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_3_FLOAT) != 0) {
                DestinationTypeSuffix = ARM_SIMD_DATA_FLOAT;

            } else {
                DestinationTypeSuffix = ARM_SIMD_DATA_UNSIGNED;
            }

            BaseMnemonic = ARM_VRECPE_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_3_VRSQRTE_MASK:
            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_3_FLOAT) != 0) {
                DestinationTypeSuffix = ARM_SIMD_DATA_FLOAT;

            } else {
                DestinationTypeSuffix = ARM_SIMD_DATA_UNSIGNED;
            }

            BaseMnemonic = ARM_VRSQRTE_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_3_VCVT_TO_INTEGER_MASK:
            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_3_UNSIGNED) != 0) {
                DestinationTypeSuffix = ARM_SIMD_DATA_UNSIGNED;

            } else {
                DestinationTypeSuffix = ARM_SIMD_DATA_SIGNED;
            }

            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            SourceTypeSuffix = ARM_SIMD_DATA_FLOAT;
            SourceSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            GetSizeSuffix = FALSE;
            BaseMnemonic = ARM_VCVT_MNEMONIC;
            break;

        case ARM_SIMD_2_REGISTER_MISC_TYPE_3_VCVT_FROM_INTEGER_MASK:
            if ((Instruction & ARM_SIMD_2_REGISTER_MISC_TYPE_3_UNSIGNED) != 0) {
                SourceTypeSuffix = ARM_SIMD_DATA_UNSIGNED;

            } else {
                SourceTypeSuffix = ARM_SIMD_DATA_SIGNED;
            }

            SourceSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            DestinationTypeSuffix = ARM_SIMD_DATA_FLOAT;
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            GetSizeSuffix = FALSE;
            BaseMnemonic = ARM_VCVT_MNEMONIC;
            break;

        default:
            break;
        }

        //
        // All of the type 3 instructions depend on the quadword bit.
        //

        if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
            VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
            VectorMTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
        }

        break;

    default:
        break;
    }

    if (BaseMnemonic == NULL) {
        DbgpArmDecodeUndefined(Context);
        return;
    }

    if (GetSizeSuffix != FALSE) {
        switch (Size) {
        case ARM_SIMD_2_REGISTER_MISC_SIZE_8:
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_8;
            break;

        case ARM_SIMD_2_REGISTER_MISC_SIZE_16:
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_16;
            break;

        case ARM_SIMD_2_REGISTER_MISC_SIZE_32:
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_32;
            break;

        case ARM_SIMD_2_REGISTER_MISC_SIZE_64:
            DestinationSizeSuffix = ARM_SIMD_DATA_SIZE_64;
            break;

        default:
            return;
        }
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s%s%s",
            DestinationTypeSuffix,
            DestinationSizeSuffix,
            SourceTypeSuffix,
            SourceSizeSuffix);

    sprintf(Context->Operand1, "%s%d", VectorDTypeString, VectorD);
    sprintf(Context->Operand2, "%s%d", VectorMTypeString, VectorM);
    if (PrintImmediate != FALSE) {
        sprintf(Context->Operand3, "#%d ; 0x%x", Immediate, Immediate);
    }

    return;
}

VOID
DbgpArmDecodeSimdVectorExtract (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data vector extract instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Immediate;
    ULONG Instruction;
    ULONG VectorD;
    ULONG VectorM;
    ULONG VectorN;
    PSTR VectorTypeString;

    Instruction = Context->Instruction;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    VectorN = (Instruction & ARM_SIMD_DATA_PROCESSING_VN_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VN_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VN_BIT) != 0) {
        VectorN |= (1 << 4);
    }

    VectorTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
        VectorTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
    }

    Immediate = (Instruction & ARM_SIMD_VEXT_IMMEDIATE4_MASK) >>
                ARM_SIMD_VEXT_IMMEDIATE4_SHIFT;

    strcpy(Context->Mnemonic, ARM_VEXT_MNEMONIC);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            ARM_SIMD_DATA_DEFAULT,
            ARM_SIMD_DATA_SIZE_8);

    sprintf(Context->Operand1, "%s%d", VectorTypeString, VectorD);
    sprintf(Context->Operand2, "%s%d", VectorTypeString, VectorN);
    sprintf(Context->Operand3, "%s%d", VectorTypeString, VectorM);
    sprintf(Context->Operand4, "#%d  ; 0x%x", Immediate, Immediate);
    return;
}

VOID
DbgpArmDecodeSimdVectorTableLookup (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD vector table lookup instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    PSTR BaseMnemonic;
    ULONG Instruction;
    ULONG VectorCount;
    ULONG VectorD;
    ULONG VectorM;
    ULONG VectorN;

    Instruction = Context->Instruction;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    VectorN = (Instruction & ARM_SIMD_DATA_PROCESSING_VN_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VN_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VN_BIT) != 0) {
        VectorN |= (1 << 4);
    }

    VectorCount = (Instruction & ARM_SIMD_VTB_LENGTH_MASK) >>
                  ARM_SIMD_VTB_LENGTH_SHIFT;

    VectorCount += 1;
    if ((Instruction & ARM_SIMD_VTB_EXTENSION) != 0) {
        BaseMnemonic = ARM_VTBX_MNEMONIC;

    } else {
        BaseMnemonic = ARM_VTBL_MNEMONIC;
    }

    strcpy(Context->Mnemonic, BaseMnemonic);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            ARM_SIMD_DATA_DEFAULT,
            ARM_SIMD_DATA_SIZE_8);

    sprintf(Context->Operand1,
            "%s%d",
            ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
            VectorD);

    DbgpArmPrintVectorList(Context->Operand2,
                           sizeof(Context->Operand2),
                           VectorN,
                           VectorCount,
                           1,
                           ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
                           0,
                           0);

    sprintf(Context->Operand3,
            "%s%d",
            ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
            VectorM);

    return;
}

VOID
DbgpArmDecodeSimdVectorDuplicate (
    PARM_DISASSEMBLY Context
    )

/*++

Routine Description:

    This routine decodes an SIMD data vector duplicate instruction.

Arguments:

    Context - Supplies a pointer to the disassembly context.

Return Value:

    None.

--*/

{

    ULONG Instruction;
    PSTR SizeTypeSuffix;
    PSTR SizeValueSuffix;
    ULONG VectorD;
    PSTR VectorDTypeString;
    ULONG VectorM;
    ULONG VectorMIndex;

    Instruction = Context->Instruction;
    SizeTypeSuffix = ARM_SIMD_DATA_DEFAULT;
    SizeValueSuffix = "";
    VectorMIndex = 0;
    VectorD = (Instruction & ARM_SIMD_DATA_PROCESSING_VD_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VD_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VD_BIT) != 0) {
        VectorD |= (1 << 4);
    }

    VectorM = (Instruction & ARM_SIMD_DATA_PROCESSING_VM_MASK) >>
              ARM_SIMD_DATA_PROCESSING_VM_SHIFT;

    if ((Instruction & ARM_SIMD_DATA_PROCESSING_VM_BIT) != 0) {
        VectorM |= (1 << 4);
    }

    if ((Instruction & ARM_SIMD_VDUP_SIZE_8_MASK) ==
        ARM_SIMD_VDUP_SIZE_8_VALUE) {

        SizeValueSuffix = ARM_SIMD_DATA_SIZE_8;
        VectorMIndex = (Instruction & ARM_SIMD_VDUP_SIZE_8_INDEX_MASK) >>
                       ARM_SIMD_VDUP_SIZE_8_INDEX_SHIFT;

    } else if ((Instruction & ARM_SIMD_VDUP_SIZE_16_MASK) ==
               ARM_SIMD_VDUP_SIZE_16_VALUE) {

        SizeValueSuffix = ARM_SIMD_DATA_SIZE_16;
        VectorMIndex = (Instruction & ARM_SIMD_VDUP_SIZE_16_INDEX_MASK) >>
                       ARM_SIMD_VDUP_SIZE_16_INDEX_SHIFT;

    } else if ((Instruction & ARM_SIMD_VDUP_SIZE_32_MASK) ==
               ARM_SIMD_VDUP_SIZE_32_VALUE) {

        SizeValueSuffix = ARM_SIMD_DATA_SIZE_32;
        VectorMIndex = (Instruction & ARM_SIMD_VDUP_SIZE_32_INDEX_MASK) >>
                       ARM_SIMD_VDUP_SIZE_32_INDEX_SHIFT;

    } else {
        SizeTypeSuffix = "";
    }

    VectorDTypeString = ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR;
    if ((Instruction & ARM_SIMD_DATA_PROCESSING_QUADWORD) != 0) {
        VectorDTypeString = ARM_FLOATING_POINT_QUADWORD_VECTOR;
    }

    strcpy(Context->Mnemonic, ARM_VDUP_MNEMONIC);
    sprintf(Context->PostConditionMnemonicSuffix,
            "%s%s",
            SizeTypeSuffix,
            SizeValueSuffix);

    sprintf(Context->Operand1, "%s%d", VectorDTypeString, VectorD);
    sprintf(Context->Operand2,
            "%s%d[%d]",
            ARM_FLOATING_POINT_DOUBLE_PRECISION_VECTOR,
            VectorM,
            VectorMIndex);

    return;
}

PSTR
DbgpArmGetLoadStoreTypeString (
    ULONG Instruction
    )

/*++

Routine Description:

    This routine returns the push/pop type string.

Arguments:

    Instruction - Supplies the instruction. The push/pop type is always in the
        same bits.

Return Value:

    Returns a static string for the push/pop type.

--*/

{

    switch (Instruction & ARM_LOAD_STORE_TYPE_MASK) {
    case ARM_LOAD_STORE_INCREMENT_AFTER:
        return ARM_INCREMENT_AFTER_SUFFIX;

    case ARM_LOAD_STORE_INCREMENT_BEFORE:
        return ARM_INCREMENT_BEFORE_SUFFIX;

    case ARM_LOAD_STORE_DECREMENT_AFTER:
        return ARM_DECREMENT_AFTER_SUFFIX;

    case ARM_LOAD_STORE_DECREMENT_BEFORE:
        return ARM_DECREMENT_BEFORE_SUFFIX;

    default:
        break;
    }

    return "";
}

PSTR
DbgpArmGetBankedRegisterString (
    ULONG Instruction
    )

/*++

Routine Description:

    This routine returns the banked register/mode string, encoded in
    instructions as the m1 and R fields.

Arguments:

    Instruction - Supplies the instruction.

Return Value:

    Returns a static string for the encoding mode.

--*/

{

    ULONG Index;

    Index = (Instruction & ARM_BANKED_MODE_MASK) >> ARM_BANKED_MODE_SHIFT;
    if ((Instruction & ARM_BANKED_MODE_R_BIT) != 0) {
        Index |= 0x20;
    }

    return DbgArmBankedRegisters[Index];
}

VOID
DbgpArmPrintStatusRegister (
    PSTR Operand,
    ULONG Instruction
    )

/*++

Routine Description:

    This routine prints the status register and flags for a given instruction.

Arguments:

    Operand - Supplies the buffer where the completed string will be returned.

    Instruction - Supplies the instruction.

Return Value:

    None.

--*/

{

    ULONG ExtraFlagCount;
    CHAR ExtraFlags[5];
    PSTR Register;

    memset(ExtraFlags, 0, sizeof(ExtraFlags));
    ExtraFlagCount = 0;
    if ((Instruction & ARM_MOVE_STATUS_SPSR) != 0) {
        Register = ARM_SPSR_STRING;

    } else {
        Register = ARM_CPSR_STRING;
    }

    if ((Instruction & ARM_MSR_MASK_C) != 0) {
        ExtraFlags[ExtraFlagCount] = ARM_MSR_MASK_C_FLAG;
        ExtraFlagCount += 1;
    }

    if ((Instruction & ARM_MSR_MASK_X) != 0) {
        ExtraFlags[ExtraFlagCount] = ARM_MSR_MASK_X_FLAG;
        ExtraFlagCount += 1;
    }

    if ((Instruction & ARM_MSR_MASK_S) != 0) {
        ExtraFlags[ExtraFlagCount] = ARM_MSR_MASK_S_FLAG;
        ExtraFlagCount += 1;
    }

    if ((Instruction & ARM_MSR_MASK_F) != 0) {
        ExtraFlags[ExtraFlagCount] = ARM_MSR_MASK_F_FLAG;
        ExtraFlagCount += 1;
    }

    sprintf(Operand, "%s_%s", Register, ExtraFlags);
    return;
}

VOID
DbgpArmPrintVectorList (
    PSTR Destination,
    ULONG DestinationSize,
    ULONG VectorStart,
    ULONG VectorCount,
    ULONG VectorIncrement,
    PSTR VectorTypeString,
    ULONG VectorIndex,
    ULONG Flags
    )

/*++

Routine Description:

    This routine converts a count of vectors starting at a given vector into
    a string.

Arguments:

    Destination - Supplies a pointer to the destination string.

    DestinationSize - Supplies the size of the destination string.

    VectorStart - Supplies the starting vector.

    VectorCount - Supplies the total number of vectors to convert.

    VectorIncrement - Supplies the interval between vectors.

    VectorTypeString - Supplies the character to use to describe each vector.

    VectorIndex - Supplies the option index into each vector.

    Flags - Supplies a bitmask of flags. See DBG_ARM_VECTOR_LIST_FLAG_* for
        definitions.

Return Value:

    None.

--*/

{

    ULONG CurrentVector;
    PSTR Separator;
    INT Size;
    CHAR VectorString[16];

    if (DestinationSize > 1) {
        strcpy(Destination, "{");
        Size = strlen(Destination);
        Destination += Size;
        DestinationSize -= Size;
    }

    Separator = "";
    for (CurrentVector = VectorStart;
         CurrentVector < VectorStart + VectorCount;
         CurrentVector += VectorIncrement) {

        if ((Flags & DBG_ARM_VECTOR_LIST_FLAG_INDEX) != 0) {
            if ((Flags & DBG_ARM_VECTOR_LIST_FLAG_ALL_LANES) != 0) {
                snprintf(VectorString,
                         sizeof(VectorString),
                         "%s%s%d[]",
                         Separator,
                         VectorTypeString,
                         CurrentVector);

            } else {
                snprintf(VectorString,
                         sizeof(VectorString),
                         "%s%s%d[%d]",
                         Separator,
                         VectorTypeString,
                         CurrentVector,
                         VectorIndex);
            }

        } else {
            snprintf(VectorString,
                     sizeof(VectorString),
                     "%s%s%d",
                     Separator,
                     VectorTypeString,
                     CurrentVector);
        }

        Size = strlen(VectorString);
        if (Size < DestinationSize) {
            strcpy(Destination, VectorString);
            Destination += Size;
            DestinationSize -= Size;
        }

        Separator = ", ";
    }

    if (DestinationSize > 1) {
        strcpy(Destination, "}");
    }

    return;
}

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

/*++

Routine Description:

    This routine converts a register, type and immediate value into a string
    representing the register shifted by the immediate value.

Arguments:

    Destination - Supplies a pointer to the destination string.

    DestinationSize - Supplies the size of the destination string.

    Register - Supplies the register that is to be shifted.

    Type - Supplies the type of shift. See ARM_SHIFT_* for definitions.

    Immediate - Supplies the immediate value by which to shift.

Return Value:

    None.

--*/

{

    PSTR RegisterName;
    PSTR ShiftType;

    ShiftType = NULL;
    switch (Type) {
    case ARM_SHIFT_LSL:
        if (Immediate != 0) {
            ShiftType = ARM_LSL_MNEMONIC;
        }

        break;

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

        ShiftType = ARM_LSR_MNEMONIC;
        break;

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

        ShiftType = ARM_ASR_MNEMONIC;
        break;

    case ARM_SHIFT_ROR:
        if (Immediate == 0) {
            ShiftType = ARM_RRX_MNEMONIC;

        } else {
            ShiftType = ARM_ROR_MNEMONIC;
        }

        break;

    //
    // This case should never hit since all 4 bit combinations are
    // covered.
    //

    default:
        break;
    }

    RegisterName = DbgArmRegisterNames[Register];
    if (Immediate != 0) {
        sprintf(Destination, "%s, %s #%d", RegisterName, ShiftType, Immediate);

    } else if (ShiftType != NULL) {
        sprintf(Destination, "%s, %s", RegisterName, ShiftType);

    } else {
        sprintf(Destination, "%s", RegisterName);
    }

    return;
}

