/**
***  Copyright  (C) 1996-1999 Intel Corporation. All rights reserved.
***
*** The information and source code contained herein is the exclusive
*** property of Intel Corporation and may not be disclosed, examined
*** or reproduced in whole or in part without explicit written authorization
*** from the company.
***
****************************************************************************
***
*** WARNING: ntos\rtl\ia64\vunwind.c and sdktools\imagehlp\vwndia64.c are
***          identical. For sake of maintenance and for debug purposes, 
**           please keep them as this. Thank you.
***
****************************************************************************
**/

#if !defined(BUILD_DBGHELP) && !defined(BUILD_IMAGEHLP)

#include "ntrtlp.h"

#else  // !BUILD_DBGHELP && !BUILD_IMAGEHLP

#define TARGET_IA64
#define _CROSS_PLATFORM_
#define _IA64REG_
#include "walk.h"
#include "private.h"
#include <stdlib.h>

#endif // !BUILD_DBGHELP && !BUILD_IMAGEHLP

#ifdef _IMAGEHLP_SOURCE_

#define NOT_IMAGEHLP(E)
#define FUNCTION_ENTRY_IS_IMAGE_STYLE
#define RtlVirtualUnwind VirtualUnwindIa64
#define PRUNTIME_FUNCTION PIMAGE_RUNTIME_FUNCTION_ENTRY
#define RUNTIME_FUNCTION IMAGE_RUNTIME_FUNCTION_ENTRY
#define VUW_DEBUG_PRINT OutputDebugString

#else  // !_IMAGEHLP_SOURCE_

#define NOT_IMAGEHLP(E) E
#define VUW_DEBUG_PRINT DbgPrint

#endif // !_IMAGEHLP_SOURCE_

#ifdef MASK
#undef MASK
#endif // MASK
#define MASK(bp,value)  (value << bp)

//
// ABI values
//

#define SVR4_ABI      0
#define HPUX_ABI      1
#define NT_ABI        2


#ifdef KERNEL_DEBUGGER
#define FUNCTION_ENTRY_IS_IMAGE_STYLE
#define RtlVirtualUnwind VirtualUnwind
#endif

#define STATE_RECORD_STACK_SIZE 32

#define SPILLSIZE_OF_FLOAT128_IN_DWORDS   4
#define SPILLSIZE_OF_ULONGLONG_IN_DWORDS  2

#define REGISTER_SIZE                sizeof(ULONGLONG)
#define STATIC_REGISTER_SET_SIZE     32
#define SLOTS_PER_BUNDLE             3

#define R1_MASK              0xC0
#define R1_PREFIX            0x0
#define R1_REGION_TYPE_MASK  0x20
#define R1_LENGTH_MASK       0x1F

#define R2_MASK              0xE0
#define R2_PREFIX            0x40

#define R3_MASK              0xE0
#define R3_PREFIX            0x60
#define R3_REGION_TYPE_MASK  0x3

#define P1_MASK              0xE0
#define P1_PREFIX            0x80
#define P2_MASK              0xF0
#define P2_PREFIX            0xA0
#define P3_MASK              0xF8
#define P3_PREFIX            0xB0
#define P4_MASK              0xFF
#define P4_PREFIX            0xB8
#define P5_MASK              0xFF
#define P5_PREFIX            0xB9
#define P6_MASK              0xE0
#define P6_PREFIX            0xC0
#define P7_MASK              0xF0
#define P7_PREFIX            0xE0
#define P8_MASK              0xFF
#define P8_PREFIX            0xF0
#define P9_MASK              0xFF
#define P9_PREFIX            0xF1
#define P10_MASK             0xFF
#define P10_PREFIX           0xFF

#define B1_MASK              0xC0
#define B1_PREFIX            0x80
#define B1_TYPE_MASK         0x20
#define B1_LABEL_MASK        0x1F
#define B2_MASK              0xE0
#define B2_PREFIX            0xC0
#define B2_ECOUNT_MASK       0x1F
#define B3_MASK              0xF0
#define B3_PREFIX            0xE0
#define B4_MASK              0xF0
#define B4_PREFIX            0xF0
#define B4_TYPE_MASK         0x08

//
// P3 descriptor type
//

#define PSP_GR               0
#define RP_GR                1
#define PFS_GR               2
#define PREDS_GR             3
#define UNAT_GR              4
#define LC_GR                5
#define RP_BR                6
#define RNAT_GR              7
#define BSP_GR               8
#define BSPSTORE_GR          9
#define FPSR_GR              10
#define PRIUNAT_GR           11

//
// P7 descriptor type
//

#define MEM_STACK_F          0
#define MEM_STACK_V          1
#define SPILL_BASE           2
#define PSP_SPREL            3
#define RP_WHEN              4
#define RP_PSPREL            5
#define PFS_WHEN             6
#define PFS_PSPREL           7
#define PREDS_WHEN           8
#define PREDS_PSPREL         9
#define LC_WHEN              10
#define LC_PSPREL            11
#define UNAT_WHEN            12
#define UNAT_PSPREL          13
#define FPSR_WHEN            14
#define FPSR_PSPREL          15

//
// P8 descriptor type
//

#define PSP_PSPREL           0
#define RP_SPREL             1
#define PFS_SPREL            2
#define PREDS_SPREL          3
#define LC_SPREL             4
#define UNAT_SPREL           5
#define FPSR_SPREL           6
#define BSP_WHEN             7
#define BSP_PSPREL           8
#define BSP_SPREL            9
#define BSPSTORE_WHEN        10
#define BSPSTORE_PSPREL      11
#define BSPSTORE_SPREL       12
#define RNAT_WHEN            13
#define RNAT_PSPREL          14
#define RNAT_SPREL           15
#define PRIUNAT_WHEN         16
#define PRIUNAT_PSPREL       17
#define PRIUNAT_SPREL        18


#define STACK_POINTER_GR     12

#define FIRST_PRESERVED_GR                4
#define LAST_PRESERVED_GR                 7
#define NUMBER_OF_PRESERVED_GR            4
#define NUMBER_OF_SCRATCH_GR              24

#define FIRST_LOW_PRESERVED_FR            2
#define LAST_LOW_PRESERVED_FR             5
#define NUMBER_OF_LOW_PRESERVED_FR        4

#define FIRST_HIGH_PRESERVED_FR           16
#define LAST_HIGH_PRESERVED_FR            31
#define NUMBER_OF_HIGH_PRESERVED_FR       16
#define NUMBER_OF_PRESERVED_FR            (NUMBER_OF_LOW_PRESERVED_FR+NUMBER_OF_HIGH_PRESERVED_FR)

#define FIRST_PRESERVED_BR                1
#define LAST_PRESERVED_BR                 5
#define NUMBER_OF_PRESERVED_BR            5

#define NUMBER_OF_PRESERVED_MISC          8

#define NUMBER_OF_PRESERVED_REGISTERS     (NUMBER_OF_PRESERVED_MISC+NUMBER_OF_PRESERVED_BR)


#define REG_MISC_BASE        0
#define REG_PREDS            (REG_MISC_BASE+0)
#define REG_SP               (REG_MISC_BASE+1)
#define REG_PFS              (REG_MISC_BASE+2)
#define REG_RP               (REG_MISC_BASE+3)
#define REG_UNAT             (REG_MISC_BASE+4)
#define REG_LC               (REG_MISC_BASE+5)
#define REG_NATS             (REG_MISC_BASE+6)
#define REG_FPSR             (REG_MISC_BASE+7)

#define REG_BR_BASE          (REG_MISC_BASE+NUMBER_OF_PRESERVED_MISC)

#define REG_BSP              0xff // REG_MISC_BASE+8
#define REG_BSPSTORE         0xff // REG_MISC_BASE+9
#define REG_RNAT             0xff // REG_MISC_BASE+10

//
// Where is a preserved register saved?
//
//     1. stack general register
//     2. memory stack (pspoff)
//     3. memory stack (spoff)
//     4. branch register
//

#define GENERAL_REG          0
#define PSP_RELATIVE         1
#define SP_RELATIVE          2
#define BRANCH_REG           3


#define ADD_STATE_RECORD(States, RegionLength, DescBeginIndex)       \
    States.Top++;                                                    \
    States.Top->IsTarget = FALSE;                                    \
    States.Top->MiscMask = 0;                                        \
    States.Top->FrMask = 0;                                          \
    States.Top->GrMask = 0;                                          \
    States.Top->Label = (LABEL)0;                                    \
    States.Top->Ecount = 0;                                          \
    States.Top->RegionLen = RegionLength;                            \
    States.Top->RegionBegin = UnwindContext.SlotCount;               \
    States.Top->SpWhen = 0;                                          \
    States.Top->SpAdjustment = 0;                                    \
    States.Top->SpillBase = (States.Top-1)->SpillPtr;                \
    States.Top->SpillPtr = (States.Top-1)->SpillPtr;                 \
    States.Top->Previous = States.Current;                           \
    States.Top->DescBegin = DescBeginIndex;                          \
    States.Current = States.Top


#define VALID_LABEL_BIT_POSITION    15

#define LABEL_REGION(Region, Label)                                    \
    Region->Label = Label;                                             \
    Region->MiscMask |= (1 << VALID_LABEL_BIT_POSITION)

#define IS_REGION_LABELED(Region)  \
    (Region->MiscMask & (1 << VALID_LABEL_BIT_POSITION))

#define CHECK_LABEL(State, Label) \
    ( (IS_REGION_LABELED(State)) && (Label == State->Label) )


#define EXTRACT_NAT_FROM_UNAT(NatBit)  \
    NatBit = (UCHAR)((IntNats >> (((ULONG_PTR)Source & 0x1F8) >> 3)) & 0x1);


#if DBG
int UnwindDebugLevel = 0;
# ifdef _IMAGEHLP_SOURCE_
#  define UW_DEBUG(x) if (UnwindDebugLevel) dbPrint##x
# else
#  define UW_DEBUG(x) if (UnwindDebugLevel) DbgPrint##x
# endif
#else
# define UW_DEBUG(x)
#endif // DBG



typedef struct _REGISTER_RECORD {
    ULONG Where : 2;                  // 2-bit field
    ULONG SaveOffset : 30;            // 30 bits for offset, big enough?
    ULONG When;                       // slot offset relative to region
} REGISTER_RECORD, *PREGISTER_RECORD;

typedef ULONG LABEL;

typedef struct _STATE_RECORD {
    struct _STATE_RECORD *Previous;   // pointer to outer nested prologue
    BOOLEAN IsTarget;       // TRUE if the control pc is in this prologue
    UCHAR GrMask;           // Mask that specifies which GRs to be restored
    USHORT MiscMask;        // Mask that specifies which BRs and misc. registers
                            // are to be restored.
                            // N.B. MSBit indicates Label is valid or not.
    ULONG FrMask;           // Mask that specifies which FRs to be restored
    ULONG SpAdjustment;     // size of stack frame allocated in the prologue
    ULONG SpWhen;           // slot offset relative to region
    ULONG SpillPtr;         // current spill location
    ULONG SpillBase;        // spill base of the region
    ULONG RegionBegin;      // first slot of region relative to function entry
    ULONG RegionLen;        // number of slots in the region
    LABEL Label;            // label that identifies a post-prologue state
    ULONG Ecount;           // number of prologue regions to pop
    ULONG DescBegin;        // first prologue descriptor for the region
    ULONG DescEnd;          // last prologue descriptor for the region
} STATE_RECORD, *PSTATE_RECORD;

typedef struct _UNWIND_CONTEXT {
    REGISTER_RECORD MiscRegs[NUMBER_OF_PRESERVED_REGISTERS];
    REGISTER_RECORD Float[NUMBER_OF_PRESERVED_FR];
    REGISTER_RECORD Integer[NUMBER_OF_PRESERVED_GR];
    BOOLEAN ActiveRegionFound;
    UCHAR AlternateRp;
    USHORT Version;
    PUCHAR Descriptors;               // beginning of descriptor data
    ULONG Size;                       // total size of all descriptors
    ULONG DescCount;                  // number of descriptor bytes processed
    ULONG TargetSlot;
    ULONG SlotCount;
} UNWIND_CONTEXT, *PUNWIND_CONTEXT;

typedef struct _STATE_RECORD_STACK {
    ULONG Size;
    PSTATE_RECORD Current;
    PSTATE_RECORD Top;
    PSTATE_RECORD Base;
} STATE_RECORD_STACK, *PSTATE_RECORD_STACK;

#define OFFSET(type, field) ((ULONG_PTR)(&((type *)0)->field))

static USHORT MiscContextOffset[NUMBER_OF_PRESERVED_REGISTERS] = {
    OFFSET(CONTEXT, Preds),
    OFFSET(CONTEXT, IntSp),
    OFFSET(CONTEXT, RsPFS),
    OFFSET(CONTEXT, BrRp),
    OFFSET(CONTEXT, ApUNAT),
    OFFSET(CONTEXT, ApLC),
    0,
    0,
    OFFSET(CONTEXT, BrS0),
    OFFSET(CONTEXT, BrS1),
    OFFSET(CONTEXT, BrS2),
    OFFSET(CONTEXT, BrS3),
    OFFSET(CONTEXT, BrS4)
};

static USHORT MiscContextPointersOffset[NUMBER_OF_PRESERVED_REGISTERS] = {
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, Preds),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, IntSp),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, RsPFS),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, BrRp),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, ApUNAT),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, ApLC),
    0,
    0,
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, BrS0),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, BrS1),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, BrS2),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, BrS3),
    OFFSET(KNONVOLATILE_CONTEXT_POINTERS, BrS4)
};

static UCHAR P3RecordTypeToRegisterIndex[] =
    {REG_SP, REG_RP, REG_PFS, REG_PREDS, REG_UNAT, REG_LC, REG_RP,
     REG_RNAT, REG_BSP, REG_BSPSTORE, REG_FPSR};

static UCHAR P7RecordTypeToRegisterIndex[] =
    {0, REG_SP, 0, REG_SP, REG_RP, REG_RP, REG_PFS, REG_PFS, REG_PREDS,
     REG_PREDS, REG_LC, REG_LC, REG_UNAT, REG_UNAT, REG_FPSR, REG_FPSR};

static UCHAR P8RecordTypeToRegisterIndex[] =
    {REG_SP, REG_RP, REG_PFS, REG_PREDS, REG_LC, REG_UNAT, REG_FPSR,
     REG_BSP, REG_BSP, REG_BSP, REG_BSPSTORE, REG_BSPSTORE, REG_BSPSTORE,
     REG_RNAT, REG_RNAT, REG_RNAT, REG_NATS, REG_NATS, REG_NATS};

UCHAR
NewParsePrologueRegionPhase0 (
    IN PUNWIND_CONTEXT UwContext,
    IN PSTATE_RECORD StateRecord,
    IN OUT PUCHAR AbiImmContext
    );

VOID
NewParsePrologueRegionPhase1 (
    IN PUNWIND_CONTEXT UwContext,
    IN PSTATE_RECORD StateRecord
    );


VOID
SrInitialize (
    IN PSTATE_RECORD_STACK StateTable,
    IN PSTATE_RECORD StateRecord,
    IN ULONG Size
    )
{
    StateTable->Size = Size;
    StateTable->Base = StateRecord;
    StateTable->Top = StateRecord;
    StateTable->Current = StateRecord;
    RtlZeroMemory(StateTable->Top, sizeof(STATE_RECORD));
}


ULONG
ReadLEB128 (
    IN PUCHAR Descriptors,
    IN OUT PULONG CurrentDescIndex
    )
{
    PUCHAR Buffer;
    ULONG Value;
    ULONG ShiftCount = 7;
    ULONG Count;

    Buffer = Descriptors + *CurrentDescIndex;
    Count = 1;

    Value = Buffer[0] & 0x7F;
    if (Buffer[0] & 0x80) {
        while (TRUE) {
            Value += ((Buffer[Count] & 0x7F) << ShiftCount);
            if (Buffer[Count++] & 0x80) {
                ShiftCount += 7;
            } else {
                break;
            }
        }
    }

    *CurrentDescIndex += Count;

    return Value;
}


ULONGLONG
RestorePreservedRegisterFromGR (
    IN PCONTEXT Context,
    IN SHORT BsFrameSize,
    IN SHORT RNatSaveIndex,
    IN SHORT GrNumber,
#ifdef _IMAGEHLP_SOURCE_
    IN HANDLE hProcess,
    IN PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
    OUT BOOL *Succeed,
#else
    OUT ULONG64 *SourceAddress,
#endif // _IMAGEHLP_SOURCE_
    OUT PUCHAR Nat OPTIONAL
    )
{
    ULONGLONG Result;
    SHORT Offset;
    SHORT Temp;
#ifdef _IMAGEHLP_SOURCE_
    ULONG Size;
#endif // _IMAGEHLP_SOURCE_

#ifdef _IMAGEHLP_SOURCE_
    *Succeed = FALSE;
#endif // _IMAGEHLP_SOURCE_

    if (GrNumber >= STATIC_REGISTER_SET_SIZE) {

        Offset = GrNumber - STATIC_REGISTER_SET_SIZE;
        if ( Offset < BsFrameSize ) {

            Temp = Offset + RNatSaveIndex - NAT_BITS_PER_RNAT_REG;
            while (Temp >= 0) {
                Offset++;
                Temp -= NAT_BITS_PER_RNAT_REG;
            }
            Offset = Offset * sizeof(ULONGLONG);

#ifdef _IMAGEHLP_SOURCE_
            *Succeed = ReadMemory(hProcess, Context->RsBSP + Offset,
                                  &Result, sizeof(ULONGLONG), &Size);
#else
            *SourceAddress = (ULONG64)(Context->RsBSP + Offset);
            Result = *(PULONGLONG)(Context->RsBSP + Offset);
#endif // _IMAGEHLP_SOURCE_

        } else {

            UW_DEBUG(("ERROR: Invalid GR!\n"));
        }

    } else {

        if (GrNumber == 0 || GrNumber == 12) {

            //
            // Invalid GR number -> Invalid Unwind Descriptor
            //

            UW_DEBUG(("ERROR: Invalid GR!\n"));

        } else {

            UW_DEBUG(("WARNING: Target register is not a stacked GR!\n"));
            Offset = GrNumber - 1;
            NOT_IMAGEHLP(*SourceAddress = (ULONG64)(&Context->IntGp + Offset));
            Result = *(&Context->IntGp + Offset);

#ifdef _IMAGEHLP_SOURCE_
            *Succeed = TRUE;
#endif // _IMAGEHLP_SOURCE_

        }
    }

    if (ARGUMENT_PRESENT(Nat)) {

        //
        // TBD: Pick up the corresponding Nat bit
        //

        *Nat = (UCHAR) 0;

    }

    return (Result);
}


UCHAR
ParseBodyRegionDescriptors (
    IN PUNWIND_CONTEXT UnwindContext,
    IN PSTATE_RECORD_STACK StateTable,
    IN ULONG RegionLen
    )
{
    LABEL Label;
    UCHAR FirstByte;
    BOOLEAN EcountDefined;
    BOOLEAN CopyLabel;
    ULONG Ecount;
    ULONG SlotOffset;
    PSTATE_RECORD StateTablePtr;
    PUCHAR Descriptors;

    CopyLabel = EcountDefined = FALSE;
    Descriptors = UnwindContext->Descriptors;

    while (UnwindContext->DescCount < UnwindContext->Size) {

        FirstByte = Descriptors[UnwindContext->DescCount++];

        if ( (FirstByte & B1_MASK) == B1_PREFIX ) {

            Label = (LABEL)(FirstByte & B1_LABEL_MASK);
            if (FirstByte & B1_TYPE_MASK) {

                //
                // copy the entry state
                //

                CopyLabel = TRUE;

            } else {

                //
                // label the entry state
                //

                LABEL_REGION(StateTable->Top, Label);
            }

            UW_DEBUG(("Body region desc B1: copy=%d, label_num=%d\n",
                     FirstByte & B1_TYPE_MASK ? TRUE : FALSE, Label));

        } else if ( (FirstByte & B2_MASK) == B2_PREFIX ) {

            Ecount = FirstByte & B2_ECOUNT_MASK;
            SlotOffset = ReadLEB128(Descriptors, &UnwindContext->DescCount);
            EcountDefined = TRUE;

            UW_DEBUG(("Epilog desc B2: ecount=%d, LEB128(slot)=%d\n",
                      Ecount, SlotOffset));

        } else if ( (FirstByte & B3_MASK) == B3_PREFIX ) {

            SlotOffset = ReadLEB128(Descriptors, &UnwindContext->DescCount);
            Ecount = ReadLEB128(Descriptors, &UnwindContext->DescCount);
            EcountDefined = TRUE;

            UW_DEBUG(("Epilog desc B3: ecount=%d, LEB128 val=%d\n",
                      Ecount, SlotOffset));

        } else if ( (FirstByte & B4_MASK) == B4_PREFIX ) {

            Label = ReadLEB128(Descriptors, &UnwindContext->DescCount);

            if (FirstByte & B4_TYPE_MASK) {

                //
                // copy the entry state
                //

                CopyLabel = TRUE;

            } else {

                //
                // label the current top of stack
                //

                LABEL_REGION(StateTable->Top, Label);
            }

            UW_DEBUG(("Body region desc B4: copy=%d, label_num=%d\n",
                     FirstByte & B4_TYPE_MASK, Label));

        } else {

            //
            // Encounter another region header record
            //

            break;
        }
    }

    if (CopyLabel) {
        StateTablePtr = StateTable->Top;
        while (TRUE) {
            if (CHECK_LABEL(StateTablePtr, Label)) {
                StateTable->Current = StateTablePtr;
                break;
            } else if ((StateTablePtr == StateTable->Base)) {
                UW_DEBUG(("Undefined Label %d\n", Label));
                break;
            }
            StateTablePtr--;
        }
    }

    if (EcountDefined) {

        Ecount++;    // Ecount specifies additional level of prologue
                     // regions to undo (i.e. a value of 0 implies 1
                     // prologue region)

        if (UnwindContext->ActiveRegionFound == FALSE) {
            while (Ecount-- > 0) {
                if (StateTable->Current->Previous) {
                    StateTable->Current = StateTable->Current->Previous;
                }

#if DBG
                else {
                    UW_DEBUG(("WARNING: Ecount is greater than the # of active prologues!\n"));
                }
#endif // DBG

            }
        } else {

            //
            // control PC is in this body/epilog region
            //

            if ((UnwindContext->SlotCount + RegionLen - SlotOffset)
                    <= UnwindContext->TargetSlot)
            {
                PSTATE_RECORD SrPointer;

                StateTable->Current->Ecount = Ecount;
                SrPointer = StateTable->Current;
                while (Ecount > 0) {

                    if (SrPointer->Previous) {
                        SrPointer->Ecount = Ecount;
                        SrPointer->SpWhen = 0;
                        SrPointer->SpAdjustment = 0;
                        SrPointer = SrPointer->Previous;
                    }

#if DBG
                    else {
                        UW_DEBUG(("WARNING: Ecount is greater than the # of active prologues!\n"));
                    }
#endif // DBG
                    Ecount--;

                }
            }
        }
    }

    return FirstByte;
}


ULONGLONG
ProcessInterruptRegion (
#ifdef _IMAGEHLP_SOURCE_
    IN HANDLE hProcess,
    IN PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory,
#else
    IN PKNONVOLATILE_CONTEXT_POINTERS ContextPointers,
#endif _IMAGEHLP_SOURCE_
    IN PUNWIND_CONTEXT UnwindContext,
    IN PCONTEXT Context,
    IN SHORT BsFrameSize,
    IN SHORT RNatSaveIndex,
    IN UCHAR AbiImmContext
    )
{
    //
    // no prologue descriptor in interrupt region.
    //

    PCONTEXT PrevContext;
    ULONGLONG NextPc;
    ULONG Index;
    SHORT TempFrameSize;
    BOOLEAN Success;
#ifdef _IMAGEHLP_SOURCE_
    ULONG Size;
#else
    PVOID *Source;
    PVOID Address;
#endif _IMAGEHLP_SOURCE_


    if (AbiImmContext != CONTEXT_FRAME) {

        PKTRAP_FRAME TrapFrame;
        PKEXCEPTION_FRAME ExFrame;
#ifdef _IMAGEHLP_SOURCE_
        KTRAP_FRAME TF;
        KEXCEPTION_FRAME ExF;
#endif // _IMAGEHLP_SOURCE_

        TrapFrame = (PKTRAP_FRAME) Context->IntSp;
#ifdef _IMAGEHLP_SOURCE_
        if (!ReadMemory(hProcess, Context->IntSp, &TF, sizeof(KTRAP_FRAME), &Size))
        {
            return 0;
        }
        TrapFrame = &TF;
#endif // _IMAGEHLP_SOURCE_

        Context->ApDCR = TrapFrame->ApDCR;
        Context->ApUNAT = TrapFrame->ApUNAT;
        Context->StFPSR = TrapFrame->StFPSR;
        Context->Preds = TrapFrame->Preds;
        Context->IntSp = TrapFrame->IntSp;
        Context->StIPSR = TrapFrame->StIPSR;
        Context->StIFS = TrapFrame->StIFS;
        Context->BrRp = TrapFrame->BrRp;
        Context->RsPFS = TrapFrame->RsPFS;

#ifndef _IMAGEHLP_SOURCE_
        if (ARGUMENT_PRESENT(ContextPointers)) {
            ContextPointers->ApUNAT = &TrapFrame->ApUNAT;
            ContextPointers->IntSp = &TrapFrame->IntSp;
            ContextPointers->BrRp = &TrapFrame->BrRp;
            ContextPointers->RsPFS = &TrapFrame->RsPFS;
            ContextPointers->Preds = &TrapFrame->Preds;
        }
#endif // _IMAGEHLP_SOURCE_

        switch (AbiImmContext) {

        case SYSCALL_FRAME:

            //
            // System Call Handler Frame
            //

            BsFrameSize = (SHORT)(TrapFrame->StIFS >> PFS_SIZE_SHIFT);
            BsFrameSize &= PFS_SIZE_MASK;
            break;

        case INTERRUPT_FRAME:
        case EXCEPTION_FRAME:

            //
            // External Interrupt Frame / Exception Frame
            //

            BsFrameSize = (SHORT)TrapFrame->StIFS & PFS_SIZE_MASK;
            break;

        default:

            break;
        }

        RNatSaveIndex = (SHORT)(TrapFrame->RsBSP >> 3) & NAT_BITS_PER_RNAT_REG;
        TempFrameSize = BsFrameSize - RNatSaveIndex;
        while (TempFrameSize > 0) {
            BsFrameSize++;
            TempFrameSize -= NAT_BITS_PER_RNAT_REG;
        }

        Context->RsBSP = TrapFrame->RsBSP - BsFrameSize * sizeof(ULONGLONG);
        Context->RsBSPSTORE = Context->RsBSP;
        Context->RsRNAT = TrapFrame->RsRNAT;

        NextPc = RtlIa64InsertIPSlotNumber(TrapFrame->StIIP,
                     ((TrapFrame->StIPSR >> PSR_RI) & 0x3));

        return (NextPc);
    }

    //
    // Kernel-to-User thunk, context of the previous frame can be
    // found on the user stack (i.e. context's address = sp+SCRATCH_AREA)
    //

    PrevContext = (PCONTEXT)(Context->IntSp + STACK_SCRATCH_AREA);
#ifdef _IMAGEHLP_SOURCE_
    if (!ReadMemory(hProcess, (DWORD64)PrevContext, Context, sizeof(CONTEXT), &Size))
    {
        return 0;
    }
    NextPc = RtlIa64InsertIPSlotNumber(Context->StIIP,
                                       ((Context->StIPSR >> PSR_RI) & 0x3));
#else

    RtlCopyMemory(&Context->BrRp, &PrevContext->BrRp,
                  (NUMBER_OF_PRESERVED_BR+3) * sizeof(ULONGLONG));
    RtlCopyMemory(&Context->FltS0, &PrevContext->FltS0,
                  NUMBER_OF_LOW_PRESERVED_FR * sizeof(FLOAT128));
    RtlCopyMemory(&Context->FltS4, &PrevContext->FltS4,
                  NUMBER_OF_HIGH_PRESERVED_FR * sizeof(FLOAT128));
    RtlCopyMemory(&Context->IntS0, &PrevContext->IntS0,
                  NUMBER_OF_PRESERVED_GR * sizeof(ULONGLONG));
    RtlCopyMemory(&Context->IntV0, &PrevContext->IntV0,                  
                  NUMBER_OF_SCRATCH_GR * sizeof(ULONGLONG));
    Context->IntT0 = PrevContext->IntT0;    
    Context->IntT1 = PrevContext->IntT1;    
    
    Context->IntSp = PrevContext->IntSp;
    Context->IntNats = PrevContext->IntNats;
    Context->ApUNAT = PrevContext->ApUNAT;
    Context->ApLC = PrevContext->ApLC;
    Context->ApEC = PrevContext->ApEC;
    Context->Preds = PrevContext->Preds;
    Context->RsPFS = PrevContext->RsPFS;
    Context->RsBSP = PrevContext->RsBSP;
    Context->RsBSPSTORE = PrevContext->RsBSPSTORE;
    Context->RsRSC = PrevContext->RsRSC;
    Context->RsRNAT = PrevContext->RsRNAT;
    Context->StIFS = PrevContext->StIFS;
    Context->StIPSR = PrevContext->StIPSR;

    NextPc = RtlIa64InsertIPSlotNumber(PrevContext->StIIP,
                 ((PrevContext->StIPSR >> PSR_RI) & 0x3));

#endif // _IMAGEHLP_SOURCE_

    return(NextPc);
}


ULONGLONG
RtlVirtualUnwind (
#ifdef _IMAGEHLP_SOURCE_
    HANDLE hProcess,
    ULONGLONG ImageBase,
    ULONGLONG ControlPc,
    PRUNTIME_FUNCTION FunctionEntry,
    PCONTEXT ContextRecord,
    PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemory
#define ContextPointers ((PKNONVOLATILE_CONTEXT_POINTERS)0)
#else
    IN ULONGLONG ImageBase,
    IN ULONGLONG ControlPc,
    IN PRUNTIME_FUNCTION FunctionEntry,
    IN OUT PCONTEXT ContextRecord,
    OUT PBOOLEAN InFunction,
    OUT PFRAME_POINTERS EstablisherFrame,
    IN OUT PKNONVOLATILE_CONTEXT_POINTERS ContextPointers OPTIONAL
#endif
    )

/*++

Routine Description:

    This function virtually unwinds the specfified function by executing its
    prologue code backwards.

    If the function is a leaf function, then the address where control left
    the previous frame is obtained from the context record. If the function
    is a nested function, but not an exception or interrupt frame, then the
    prologue code is executed backwards and the address where control left
    the previous frame is obtained from the updated context record.

    Otherwise, an exception or interrupt entry to the system is being unwound
    and an especially coded prologue restores the return address twice. Once
    from the fault instruction address and once from the saved return address
    register. The first restore is returned as the function value and the
    second restore is placed in the updated context record.

    If a context pointers record is specified, then the address where each
    nonvolatile registers is restored from is recorded in the appropriate
    element of the context pointers record.

Arguments:

    ImageBase - Supplies the base address of the module to which the
        function belongs.

    ControlPc - Supplies the address where control left the specified
        function.

    FunctionEntry - Supplies the address of the function table entry for the
        specified function.

    ContextRecord - Supplies the address of a context record.

    InFunction - Supplies a pointer to a variable that receives whether the
        control PC is within the current function.

    EstablisherFrame - Supplies a pointer to a variable that receives the
        the establisher frame pointer value.

    ContextPointers - Supplies an optional pointer to a context pointers
        record.

Return Value:

    The address where control left the previous frame is returned as the
    function value.

--*/

{
#ifdef _IMAGEHLP_SOURCE_
    BOOL Succeed;
#endif // _IMAGEHLP_SOURCE_
    PUCHAR Descriptors = NULL;
    UCHAR AbiImmContext = 0xFF;
    ULONG Mask;
    ULONGLONG NextPc;
    ULONG RegionLen;
    UCHAR FirstByte;
    UCHAR Nat;
    SHORT BsFrameSize;                  // in 8-byte units
    SHORT LocalFrameSize;                  // in 8-byte units
    SHORT TempFrameSize;                // in 8-byte units
    SHORT RNatSaveIndex;
    ULONG i;
    PULONG Buffer;
    BOOLEAN IsPrologueRegion;
    BOOLEAN PspRestored;
    ULONGLONG PreviousIntSp;
    PVOID Destination;
    ULONG64 Source;
    ULONG64 *CtxPtr;
    ULONG64 *NatCtxPtr;
    ULONG64 IntNatsSource;
    ULONG64 IntNats;
    ULONG Size;
    ULONGLONG OldTopRnat;
    ULONGLONG NewTopRnat;
    UNWIND_INFO UnwindInfo;
    ULONG64 UnwindInfoPtr;
    UNWIND_CONTEXT UnwindContext;
    PSTATE_RECORD SrPointer;
    STATE_RECORD_STACK StateTable;
    STATE_RECORD StateRecords[STATE_RECORD_STACK_SIZE];


    BsFrameSize = (SHORT)ContextRecord->StIFS & PFS_SIZE_MASK;
    RNatSaveIndex = (SHORT)(ContextRecord->RsBSP >> 3) & NAT_BITS_PER_RNAT_REG;
    TempFrameSize = RNatSaveIndex + BsFrameSize - NAT_BITS_PER_RNAT_REG;
    while (TempFrameSize >= 0) {
        BsFrameSize++;
        TempFrameSize -= NAT_BITS_PER_RNAT_REG;
    }

    UnwindInfoPtr = ImageBase + FunctionEntry->UnwindInfoAddress;
#ifdef _IMAGEHLP_SOURCE_
    if (!ReadMemory( hProcess, (ULONG64)UnwindInfoPtr,
                     &UnwindInfo, sizeof(UNWIND_INFO), &Size))
    {
        return 0;
    }

    UnwindContext.Version = UnwindInfo.Version;
    Size = UnwindInfo.DataLength * sizeof(ULONGLONG);
    if (Size) {
        Descriptors = (PUCHAR) MemAlloc (Size);
        if (!ReadMemory(hProcess,(ULONG64)(UnwindInfoPtr+sizeof(UNWIND_INFO)), Descriptors, Size, &Size)) {
            return 0;
        }
    }
#else
    UnwindContext.Version = ((PUNWIND_INFO)UnwindInfoPtr)->Version;
    Size = ((PUNWIND_INFO)UnwindInfoPtr)->DataLength * sizeof(ULONGLONG);
    Descriptors = (PUCHAR)UnwindInfoPtr + sizeof(UNWIND_INFO);
#endif // _IMAGEHLP_SOURCE_

    UnwindContext.Size = Size;
    UnwindContext.ActiveRegionFound = FALSE;
    UnwindContext.AlternateRp = 0;
    UnwindContext.DescCount = 0;
    UnwindContext.SlotCount = 0;
    UnwindContext.TargetSlot = (ULONG)(((ControlPc - FunctionEntry->BeginAddress - ImageBase) >> 4) * SLOTS_PER_BUNDLE + ((ControlPc >> 2) & 0x3));
    UnwindContext.Descriptors = Descriptors;

    SrInitialize(&StateTable, StateRecords, STATE_RECORD_STACK_SIZE);

    if (Size) {
        FirstByte = Descriptors[UnwindContext.DescCount++];
    }


    while ( (UnwindContext.DescCount < UnwindContext.Size) &&
            (!UnwindContext.ActiveRegionFound) )
    {

        //
        // Assume a prologue region but not an interrupt region.
        //

        IsPrologueRegion = TRUE;

        //
        // Based on the type of region header, dispatch
        // to the corresponding routine that processes
        // the succeeding descriptors until the next
        // region header record.
        //

        if ((FirstByte & R1_MASK) == R1_PREFIX) {

            //
            // region header record in short format
            //

            RegionLen = FirstByte & R1_LENGTH_MASK;

            if (FirstByte & R1_REGION_TYPE_MASK) {
                IsPrologueRegion = FALSE;
            } else {
                ADD_STATE_RECORD(StateTable, RegionLen, UnwindContext.DescCount);
            }

            UW_DEBUG(("Region R1 format: body=%x, length=%d\n",
                     IsPrologueRegion ? 0 : 1, RegionLen));

        } else if ((FirstByte & R2_MASK) == R2_PREFIX) {

            //
            // general prologue region header
            // N.B. Skip the 2nd byte of the header and proceed to read
            //      the region length; the header descriptors will be
            //      processed again in phase 1.
            //

            ULONG R2DescIndex;

            R2DescIndex = UnwindContext.DescCount - 1;
            UnwindContext.DescCount++;
            RegionLen = ReadLEB128(Descriptors, &UnwindContext.DescCount);
            ADD_STATE_RECORD(StateTable, RegionLen, R2DescIndex);
            UW_DEBUG(("Region R2: body=0, length=%d\n", RegionLen));

        } else if ((FirstByte & R3_MASK) == R3_PREFIX) {

            //
            // region header record in long format
            //

            RegionLen = ReadLEB128(Descriptors, &UnwindContext.DescCount);

            switch (FirstByte & R3_REGION_TYPE_MASK) {

            case 0:      // prologue region header

                ADD_STATE_RECORD(StateTable, RegionLen, UnwindContext.DescCount);
                break;

            case 1:      // body region header

                IsPrologueRegion = FALSE;
                break;

            }

            UW_DEBUG(("Region R3: body=%x, length=%d\n",
                      IsPrologueRegion ? 0 : 1, RegionLen));

        } else {

            //
            // Not a region header record -> Invalid unwind descriptor.
            //

            UW_DEBUG(("Invalid unwind descriptor!\n"));

        }

        if (UnwindContext.TargetSlot < (UnwindContext.SlotCount + RegionLen)) {
            UnwindContext.ActiveRegionFound = TRUE;
            StateTable.Current->IsTarget = IsPrologueRegion;
        }

        if (IsPrologueRegion) {
            FirstByte = NewParsePrologueRegionPhase0(&UnwindContext,
                                                     StateTable.Current,
                                                     &AbiImmContext);
        } else {
            FirstByte = ParseBodyRegionDescriptors(&UnwindContext,
                                                   &StateTable,
                                                   RegionLen);
        }

        UnwindContext.SlotCount += RegionLen;
    }

    //
    // Restore the value of psp and save the current NatCr.
    // N.B. If the value is restored from stack/bstore, turn off the
    //      corresponding sp bit in the saved mask associated with the
    //      prologue region in which psp is saved.
    //

    if (ARGUMENT_PRESENT(ContextPointers)) {
        IntNatsSource = (ULONG64)ContextPointers->ApUNAT;
    } 
    IntNats = ContextRecord->ApUNAT;
    PreviousIntSp = ContextRecord->IntSp;
    PspRestored = FALSE;

    SrPointer = StateTable.Current;
    while (SrPointer != StateTable.Base) {
        NewParsePrologueRegionPhase1(&UnwindContext, SrPointer);

        if (SrPointer->MiscMask & (1 << REG_SP)) {
            if (UnwindContext.MiscRegs[REG_SP].Where == GENERAL_REG) {
                PreviousIntSp = RestorePreservedRegisterFromGR (
                                    ContextRecord,
                                    BsFrameSize,
                                    RNatSaveIndex,
                                    (SHORT)UnwindContext.MiscRegs[REG_SP].SaveOffset,
#ifdef _IMAGEHLP_SOURCE_
                                    hProcess,
                                    ReadMemory,
                                    &Succeed,
#else
                                    &Source,
#endif // _IMAGEHLP_SOURCE_
                                    &Nat
                                    );
#ifdef _IMAGEHLP_SOURCE_
                if (!Succeed) {
                    return 0;
                }
#endif // _IMAGEHLP_SOURCE_

            } else {

                Source = ContextRecord->IntSp + UnwindContext.MiscRegs[REG_SP].SaveOffset*4;
#ifdef _IMAGEHLP_SOURCE_
                if (!ReadMemory(hProcess, (ULONG64)(Source), &PreviousIntSp, sizeof(ULONGLONG), &Size)) {
                    return 0;
                }
#else
                PreviousIntSp = *(PULONGLONG)Source;
#endif // _IMAGEHLP_SOURCE_
                EXTRACT_NAT_FROM_UNAT(Nat);

            }
            ContextRecord->IntNats &= ~(0x1 << STACK_POINTER_GR);
            ContextRecord->IntNats |= (Nat << STACK_POINTER_GR);
            SrPointer->MiscMask &= ~(1 << REG_SP);
            if (ARGUMENT_PRESENT(ContextPointers)) {
                CtxPtr = (ULONG64 *)((ULONG_PTR)ContextPointers +
                                   MiscContextPointersOffset[REG_SP]);
                *CtxPtr = Source;
            }
            PspRestored = TRUE;
        }
        if (PspRestored == FALSE) {
            PreviousIntSp += SrPointer->SpAdjustment * 4;
        }
        SrPointer = SrPointer->Previous;
    }

    if (AbiImmContext != 0xFF) {

        ContextRecord->IntSp = PreviousIntSp;  // trap/context frame address
        NextPc = ProcessInterruptRegion(
#ifdef _IMAGEHLP_SOURCE_
                     hProcess,
                     ReadMemory,
#else
                     ContextPointers,
#endif _IMAGEHLP_SOURCE_
                     &UnwindContext,
                     ContextRecord,
                     BsFrameSize,
                     RNatSaveIndex,
                     AbiImmContext);

        goto FastExit;
    }

    //
    // Restore the contents of any preserved registers saved in this frame.
    //

    SrPointer = StateTable.Current;
    while (SrPointer != StateTable.Base) {

        Mask = SrPointer->MiscMask;
        UW_DEBUG(("MiscMask = 0x%x\n", Mask));

        for (i = 0; i < NUMBER_OF_PRESERVED_REGISTERS; i++) {
            Destination = (PVOID)((ULONG_PTR)ContextRecord + MiscContextOffset[i]);
            if (Mask & 0x1) {

                if (ARGUMENT_PRESENT(ContextPointers)) {
                    CtxPtr = (ULONG64 *)((ULONG_PTR)ContextPointers +
                                       MiscContextPointersOffset[i]);
                    Source = *CtxPtr;
                }

                if (UnwindContext.MiscRegs[i].Where == GENERAL_REG) {

                    *(PULONGLONG)Destination =
                        RestorePreservedRegisterFromGR (
                            ContextRecord,
                            BsFrameSize,
                            RNatSaveIndex,
                            (SHORT)UnwindContext.MiscRegs[i].SaveOffset,
#ifdef _IMAGEHLP_SOURCE_
                            hProcess,
                            ReadMemory,
                            &Succeed,
#else
                            &Source,
#endif // _IMAGEHLP_SOURCE_
                            NULL
                            );
#ifdef _IMAGEHLP_SOURCE_
                    if (!Succeed) {
                        return 0;
                    }
#endif // _IMAGEHLP_SOURCE_

                } else if (UnwindContext.MiscRegs[i].Where == BRANCH_REG) {

                    //
                    // restore return pointer from branch register
                    //

                    USHORT Offset;

                    Offset = (USHORT)UnwindContext.MiscRegs[i].SaveOffset-FIRST_PRESERVED_BR;
                    Source = (ULONG64)(&ContextRecord->BrS0 + Offset);
#ifdef _IMAGEHLP_SOURCE_
                    if (!ReadMemory(hProcess, (ULONG64)(Source), Destination, sizeof(ULONGLONG), &Size)) {
                        return 0;
                    }
#else
                    *(PULONGLONG)Destination = *(PULONGLONG)(Source);
#endif // _IMAGEHLP_SOURCE_

                } else if (UnwindContext.MiscRegs[i].Where == PSP_RELATIVE) {

                    if ((SrPointer->Ecount == 0) || (UnwindContext.MiscRegs[i].SaveOffset <= (STACK_SCRATCH_AREA/sizeof(ULONG)))) {
                        Source = PreviousIntSp + STACK_SCRATCH_AREA
                                     - UnwindContext.MiscRegs[i].SaveOffset*4;

                        if (i == REG_NATS) {
                            Destination = (PVOID)&IntNats;
                            IntNatsSource = Source;
                        }

#ifdef _IMAGEHLP_SOURCE_
                        if (!ReadMemory(hProcess, (ULONG64)(Source), Destination, sizeof(ULONGLONG), &Size)) {
                            return 0;
                        }
#else
                        *(PULONGLONG)Destination = *(PULONGLONG)(Source);
#endif // _IMAGEHLP_SOURCE_
                    }

                } else if (UnwindContext.MiscRegs[i].Where == SP_RELATIVE) {

                    //
                    // Make the necessary adjustment depending on whether
                    // the preserved register is saved before or after the
                    // stack pointer has been adjusted in this prologue.
                    //

                    if (UnwindContext.MiscRegs[i].When >= SrPointer->SpWhen && (SrPointer->RegionLen != 0))
                        Source = ContextRecord->IntSp
                                     + UnwindContext.MiscRegs[i].SaveOffset*4;
                    else
                        Source = ContextRecord->IntSp+SrPointer->SpAdjustment*4
                                     + UnwindContext.MiscRegs[i].SaveOffset*4;

                        if (i == REG_NATS) {
                            Destination = (PVOID)&IntNats;
                            IntNatsSource = Source;
                        }

#ifdef _IMAGEHLP_SOURCE_
                    if (!ReadMemory(hProcess, (ULONG64)(Source), Destination, sizeof(ULONGLONG), &Size)) {
                        return 0;
                    }
#else
                    *(PULONGLONG)Destination = *(PULONGLONG)(Source);
#endif // _IMAGEHLP_SOURCE_
                }

                if (ARGUMENT_PRESENT(ContextPointers) && (i != REG_NATS)) {
                    *CtxPtr = Source;
                }

            } else if (Mask == 0) {

                //
                // No more registers to restore
                //

                break;
            }

            Mask = Mask >> 1;
        }

        //
        // Restore preserved FRs (f2 - f5, f16 - f31)
        //

        Mask = SrPointer->FrMask;
        Destination = (PVOID)&ContextRecord->FltS0;
        CtxPtr = (ULONG64 *)&ContextPointers->FltS0;

        UW_DEBUG(("FrMask = 0x%x\n", Mask));
        for (i = 0; i < NUMBER_OF_PRESERVED_FR; i++) {
            if (Mask & 0x1) {

                if ((SrPointer->Ecount == 0) || (UnwindContext.Float[i].SaveOffset <= (STACK_SCRATCH_AREA/sizeof(ULONG)))) {
                    Source = PreviousIntSp + STACK_SCRATCH_AREA
                                 - UnwindContext.Float[i].SaveOffset*4;
#ifdef _IMAGEHLP_SOURCE_
                    if (!ReadMemory(hProcess, (ULONG64)(Source), Destination, sizeof(FLOAT128), &Size)) {
                        return 0;
                    }
#else
                    *(FLOAT128 *)Destination = *(FLOAT128 *)Source;
#endif // _IMAGEHLP_SOURCE_

                    if (ARGUMENT_PRESENT(ContextPointers)) {
                        *CtxPtr = Source;
                    }
                }

            } else if (Mask == 0) {
                break;
            }

            Mask = Mask >> 1;

            if (i == (NUMBER_OF_LOW_PRESERVED_FR - 1)) {
                Destination = (PVOID)&ContextRecord->FltS4;
                CtxPtr = (ULONG64 *)(&ContextPointers->FltS4);
            } else {
                Destination = (PVOID)((FLOAT128 *)Destination+1);
                CtxPtr++;
            }
        }

        //
        // Restore preserved GRs (r4 - r7)
        //

        Mask = SrPointer->GrMask;
        Destination = (PVOID)&ContextRecord->IntS0;
        CtxPtr = (ULONG64 *)&ContextPointers->IntS0;
        NatCtxPtr = (ULONG64 *)&ContextPointers->IntS0Nat;

        UW_DEBUG(("GrMask = 0x%x\n", Mask));
        for (i = 0; i < NUMBER_OF_PRESERVED_GR; i++)
        {
            if (Mask & 0x1) {

                if ((SrPointer->Ecount == 0) || (UnwindContext.Integer[i].SaveOffset <= (STACK_SCRATCH_AREA/sizeof(ULONG)))) {
                    Source = PreviousIntSp + STACK_SCRATCH_AREA
                                 - UnwindContext.Integer[i].SaveOffset*4;

#ifdef _IMAGEHLP_SOURCE_
                    if (!ReadMemory(hProcess, (ULONG64)(Source), Destination, sizeof(ULONGLONG), &Size)) {
                        return 0;
                    }
#else
                    *(PULONGLONG)Destination = *(PULONGLONG)Source;
#endif // _IMAGEHLP_SOURCE_
                    EXTRACT_NAT_FROM_UNAT(Nat);
                    Nat = (UCHAR)((IntNats >> (((ULONG_PTR)Source & 0x1F8) >> 3)) & 0x1);
                    ContextRecord->IntNats &= ~(0x1 << (i+FIRST_PRESERVED_GR));
                    ContextRecord->IntNats |= (Nat << (i+FIRST_PRESERVED_GR));

#ifndef _IMAGEHLP_SOURCE_
                    if (ARGUMENT_PRESENT(ContextPointers)) {
                        *CtxPtr = Source;
                        *NatCtxPtr = IntNatsSource;
                    }
#endif
                }

            } else if (Mask == 0) {
                break;
            }

            Mask = Mask >> 1;
            Destination = (PVOID)((PULONGLONG)Destination+1);
            CtxPtr++;
            NatCtxPtr++;
        }

        ContextRecord->IntSp += SrPointer->SpAdjustment * 4;
        SrPointer = SrPointer->Previous;
    }

    ContextRecord->IntSp = PreviousIntSp;

    //
    // Restore the value of the epilogue count from the PFS
    //

    ContextRecord->ApEC = (ContextRecord->RsPFS >> PFS_EC_SHIFT) &
                               ~(((ULONGLONG)1 << PFS_EC_SIZE) - 1);
    if (ARGUMENT_PRESENT(ContextPointers)) {
        ContextPointers->ApEC = ContextPointers->RsPFS;
    }


FastExit:

    NOT_IMAGEHLP(*InFunction = TRUE);
    NOT_IMAGEHLP(EstablisherFrame->MemoryStackFp = ContextRecord->IntSp);
    NOT_IMAGEHLP(EstablisherFrame->BackingStoreFp = ContextRecord->RsBSP);

#ifdef _IMAGEHLP_SOURCE_
    if (Descriptors)
        MemFree(Descriptors);
#endif // _IMAGEHLP_SOURCE_

    if (AbiImmContext == 0xFF) {

        NextPc = *(&ContextRecord->BrRp + UnwindContext.AlternateRp);
#ifndef _IMAGEHLP_SOURCE_
        NextPc = RtlIa64InsertIPSlotNumber((NextPc-0x10), 2);
#endif // _IMAGEHLP_SOURCE_

        //
        // determine the local frame size of previous frame and compute
        // the new bsp.
        //

        OldTopRnat = (ContextRecord->RsBSP+(BsFrameSize-1)*8) | RNAT_ALIGNMENT;

        ContextRecord->StIFS = MASK(IFS_V, (ULONGLONG)1) | ContextRecord->RsPFS;
        BsFrameSize = (SHORT)ContextRecord->StIFS & PFS_SIZE_MASK;
        LocalFrameSize = (SHORT)(ContextRecord->StIFS >> PFS_SIZE_SHIFT) & PFS_SIZE_MASK;
        TempFrameSize = LocalFrameSize - RNatSaveIndex;
        while (TempFrameSize > 0) {
            LocalFrameSize++;
            BsFrameSize++;
            TempFrameSize -= NAT_BITS_PER_RNAT_REG;
        }
        ContextRecord->RsBSP -= LocalFrameSize * 8;
        ContextRecord->RsBSPSTORE = ContextRecord->RsBSP;

        //
        // determine if the RNAT field needs to be updated.
        //

        NewTopRnat = (ContextRecord->RsBSP+(BsFrameSize-1)*8) | RNAT_ALIGNMENT;

        if (NewTopRnat < OldTopRnat) {

#ifdef _IMAGEHLP_SOURCE_
            Destination = &ContextRecord->RsRNAT;
            Source = NewTopRnat;
            if (!ReadMemory(hProcess, (ULONG64)Source, Destination, 8, &Size)) {
                return 0;
            }
#else
            ContextRecord->RsRNAT = *(PULONGLONG)(NewTopRnat);
#endif // _IMAGEHLP_SOURCE_

        }
    }

#ifdef _IMAGEHLP_SOURCE_
    UW_DEBUG(("NextPc = 0x%lx, PSP = 0x%lx, BSP = 0x%lx\n",
               (ULONGLONG)NextPc,
               (ULONGLONG)ContextRecord->IntSp,
               (ULONGLONG)ContextRecord->RsBSP));
#else
    UW_DEBUG(("NextPc = 0x%lx, PSP = 0x%lx, BSP = 0x%lx\n",
               (ULONGLONG)NextPc,
               EstablisherFrame->MemoryStackFp,
               EstablisherFrame->BackingStoreFp));
#endif // _IMAGEHLP_SOURCE_
    return (NextPc);
}


UCHAR
NewParsePrologueRegionPhase0 (
    IN PUNWIND_CONTEXT UwContext,
    IN PSTATE_RECORD State,
    IN OUT PUCHAR AbiImmContext
    )
{
    PUCHAR Desc = UwContext->Descriptors;
    ULONG Offset;
    ULONG FrameSize;
    ULONG Index;
    UCHAR RecType;
    UCHAR FirstByte;
    UCHAR SecondByte;
    ULONG GrSave;
    ULONG TempMask;
    ULONG i;

    while (UwContext->DescCount < UwContext->Size) {

        FirstByte = Desc[UwContext->DescCount++];

        if ( (FirstByte & P1_MASK) == P1_PREFIX) {

            continue;

        } else if ( (FirstByte & P2_MASK) == P2_PREFIX ) {

            UwContext->DescCount++;

        } else if ( (FirstByte & P3_MASK) == P3_PREFIX ) {

            UwContext->DescCount++;

        } else if ( (FirstByte & P4_MASK) == P4_PREFIX ) {

            UwContext->DescCount += ((State->RegionLen+3) >> 2);

        } else if ( (FirstByte & P5_MASK) == P5_PREFIX ) {

            UwContext->DescCount += 3;

        } else if ( (FirstByte & P6_MASK) == P6_PREFIX ) {

            continue;

        } else if ( (FirstByte & P7_MASK) == P7_PREFIX ) {

            RecType = FirstByte & ~P7_MASK;

            switch (RecType) {

            case MEM_STACK_F:

                Offset = ReadLEB128(Desc, &UwContext->DescCount);
                FrameSize = ReadLEB128(Desc, &UwContext->DescCount);

                if (UwContext->TargetSlot > (UwContext->SlotCount+Offset) || State->RegionLen == 0)
                {
                    State->SpAdjustment += FrameSize*4;
                    State->SpWhen = Offset;
                }
                break;

            case SPILL_BASE:

                State->SpillBase = ReadLEB128(Desc, &UwContext->DescCount);
                State->SpillPtr = State->SpillBase;
                break;

            case MEM_STACK_V:
            case RP_WHEN:
            case PFS_WHEN:
            case PREDS_WHEN:
            case LC_WHEN:
            case UNAT_WHEN:
            case FPSR_WHEN:

                Offset = ReadLEB128(Desc, &UwContext->DescCount);
                if ((State->IsTarget) &&
                    (UwContext->TargetSlot > (UwContext->SlotCount+Offset)))
                {
                    Index = P7RecordTypeToRegisterIndex[RecType];
                    if (!(State->MiscMask & (1 << Index))) {
                        State->MiscMask |= MASK(Index,1);
                        UwContext->MiscRegs[Index].When = Offset;
                    } else {
                        UW_DEBUG(("Duplicate descriptors,"));
                        UW_DEBUG(("unwinder may produce incorrect result!\n"));
                    }
                }
                UW_DEBUG(("Prolog P7: type=%d slot= %d\n", RecType, Offset));
                break;

            case PSP_SPREL:
            case RP_PSPREL:
            case PFS_PSPREL:
            case PREDS_PSPREL:
            case LC_PSPREL:
            case UNAT_PSPREL:
            case FPSR_PSPREL:

                Offset = ReadLEB128(Desc, &UwContext->DescCount);
                break;

            default:

                UW_DEBUG(("Invalid record type for descriptor P7!\n"));

            }

        } else if ( (FirstByte & P8_MASK) == P8_PREFIX ) {

            RecType = Desc[UwContext->DescCount++];

            switch (RecType) {

            case PSP_PSPREL:
            case RP_SPREL:
            case PFS_SPREL:
            case PREDS_SPREL:
            case LC_SPREL:
            case UNAT_SPREL:
            case FPSR_SPREL:
            case BSP_PSPREL:
            case BSP_SPREL:
            case BSPSTORE_PSPREL:
            case BSPSTORE_SPREL:
            case RNAT_PSPREL:
            case RNAT_SPREL:
            case PRIUNAT_PSPREL:
            case PRIUNAT_SPREL:

                Offset = ReadLEB128(Desc, &UwContext->DescCount);
                UW_DEBUG(("Prolog P8: type=%d slot= %d\n", RecType, Offset));
                break;

            case BSP_WHEN:
            case BSPSTORE_WHEN:
            case RNAT_WHEN:
            case PRIUNAT_WHEN:

                Offset = ReadLEB128(Desc, &UwContext->DescCount);
                if ((State->IsTarget) &&
                    (UwContext->TargetSlot > (UwContext->SlotCount+Offset)))
                {
                    Index = P7RecordTypeToRegisterIndex[RecType];
                    if (!(State->MiscMask & (1 << Index))) {
                        State->MiscMask |= MASK(Index,1);
                        UwContext->MiscRegs[Index].When = Offset;
                    } else {
                        UW_DEBUG(("Duplicate descriptors,"));
                        UW_DEBUG(("unwinder may produce incorrect result!\n"));
                    }
                }
                UW_DEBUG(("Prolog P8: type=%d slot= %d\n", RecType, Offset));
                break;

            default:

                UW_DEBUG(("Invalid record type for descriptor P8!\n"));

            }

        } else if ( (FirstByte & P9_MASK) == P9_PREFIX ) {

            UwContext->DescCount += 2;
            VUW_DEBUG_PRINT("Format P9 not supported yet!\n");

        } else if ( (FirstByte & P10_MASK) == P10_PREFIX ) {

            UCHAR Abi = Desc[UwContext->DescCount++];
            UCHAR Context = Desc[UwContext->DescCount++];

            *AbiImmContext = Context;

            if (Abi != NT_ABI) {
                VUW_DEBUG_PRINT("Unknown ABI unwind descriptor\n");
            }

        } else {

            //
            // Encounter another region header record
            //

            break;
        }
    }

    State->DescEnd = UwContext->DescCount - 2;

    return FirstByte;
}

VOID
NewParsePrologueRegionPhase1 (
    IN PUNWIND_CONTEXT UwContext,
    IN PSTATE_RECORD State
    )
{
    ULONG FrameSize;
    ULONG Offset;
    ULONG GrSave;
    ULONG BrBase;
    ULONG Index;
    ULONG Count;
    UCHAR RecType;
    UCHAR FirstByte, SecondByte;   // 1st & 2nd bytes of a region header record
    ULONG DescIndex;
    ULONG ImaskBegin;
    UCHAR NextBr, NextGr, NextFr;
    USHORT MiscMask;
    ULONG TempMask;
    ULONG FrMask = 0;
    UCHAR BrMask = 0;
    UCHAR GrMask = 0;
    PUCHAR Desc = UwContext->Descriptors;
    BOOLEAN SpillMaskOmitted = TRUE;

    DescIndex = State->DescBegin;

    FirstByte = Desc[DescIndex];

    if ((FirstByte & R2_MASK) == R2_PREFIX) {

        //
        // general prologue region header; need to process it first
        //

        ULONG GrSave, Count;
        UCHAR MiscMask;
        UCHAR SecondByte;
        USHORT i;

        DescIndex++;
        SecondByte = Desc[DescIndex++];
        MiscMask = ((FirstByte & 0x7) << 1) | ((SecondByte & 0x80) >> 7);
        GrSave = SecondByte & 0x7F;
        ReadLEB128(Desc, &DescIndex);    // advance the descriptor index

        if (GrSave < STATIC_REGISTER_SET_SIZE) {
            UW_DEBUG(("Invalid unwind descriptor!\n"));
        }

        UW_DEBUG(("Region R2: rmask=%x,grsave=%d,length=%d\n",
                  MiscMask, GrSave, State->RegionLen));

        Count = 0;
        for (Index = REG_PREDS; Index <= REG_RP; Index++) {
            if (MiscMask & 0x1) {
                if (!(State->IsTarget) ||
                    (State->MiscMask & MASK(Index,1)))
                {
                    UwContext->MiscRegs[Index].Where = GENERAL_REG;
                    UwContext->MiscRegs[Index].SaveOffset = GrSave+Count;
                    UwContext->MiscRegs[Index].When = 0;
                    State->MiscMask |= MASK(Index,1);
                }
                Count++;
            }
            MiscMask = MiscMask >> 1;
        }
    }

    while (DescIndex <= State->DescEnd) {

        FirstByte = Desc[DescIndex++];

        if ( (FirstByte & P1_MASK) == P1_PREFIX) {

            BrMask = FirstByte & ~P1_MASK;
            State->MiscMask |= (BrMask << REG_BR_BASE);

            UW_DEBUG(("Prolog P1: brmask=%x\n", BrMask));

            for (Count = REG_BR_BASE;
                 Count < REG_BR_BASE+NUMBER_OF_PRESERVED_BR;
                 Count++)
            {
                if (BrMask & 0x1) {
                    UwContext->MiscRegs[Count].Where = PSP_RELATIVE;
                    UwContext->MiscRegs[Count].When = State->RegionLen;
                }
                BrMask = BrMask >> 1;
            }

        } else if ( (FirstByte & P2_MASK) == P2_PREFIX ) {

            SecondByte = Desc[DescIndex++];
            GrSave = SecondByte & 0x7F;
            BrMask = ((FirstByte & ~P2_MASK) << 1) | ((SecondByte & 0x80) >> 7);
            UW_DEBUG(("Prolog P2: brmask=%x reg base=%d\n", BrMask, GrSave));

            State->MiscMask |= (BrMask << REG_BR_BASE);

            for (Count = REG_BR_BASE;
                 Count < REG_BR_BASE+NUMBER_OF_PRESERVED_BR;
                 Count++)
            {
                if (BrMask & 0x1) {
                    UwContext->MiscRegs[Count].Where = GENERAL_REG;
                    UwContext->MiscRegs[Count].SaveOffset = GrSave++;
                }
                BrMask = BrMask >> 1;
            }

        } else if ( (FirstByte & P3_MASK) == P3_PREFIX ) {

            SecondByte = Desc[DescIndex++];
            RecType = ((SecondByte & 0x80) >> 7) | ((FirstByte & 0x7) << 1);
            Index = P3RecordTypeToRegisterIndex[RecType];

            if (RecType == RP_BR) 
            {
                UwContext->AlternateRp = SecondByte & 0x7F;
            } 
            else if (!(State->IsTarget) || (State->MiscMask & MASK(Index,1)))
            {
                UwContext->MiscRegs[Index].Where = GENERAL_REG;
                UwContext->MiscRegs[Index].SaveOffset = SecondByte & 0x7F;
                UwContext->MiscRegs[Index].When = 0;
                State->MiscMask |= MASK(Index,1);

                UW_DEBUG(("Prolog P3: type=%d reg=%d\n",
                          RecType, UwContext->MiscRegs[Index].SaveOffset));
            }

        } else if ( (FirstByte & P4_MASK) == P4_PREFIX ) {

            SpillMaskOmitted = FALSE;
            ImaskBegin = DescIndex;
            DescIndex += ((State->RegionLen+3) >> 2);

        } else if ( (FirstByte & P5_MASK) == P5_PREFIX ) {

            GrMask = (Desc[DescIndex] & 0xF0) >> 4;
            FrMask = ((ULONG)(Desc[DescIndex] & 0xF) << 16) |
                         ((ULONG)Desc[DescIndex+1] << 8) |
                         ((ULONG)Desc[DescIndex+2]);

            DescIndex += 3;    // increment the descriptor index

            State->GrMask |= GrMask;
            State->FrMask |= FrMask;

            UW_DEBUG(("Prolog P5: grmask = %x, frmask = %x\n",
                      State->GrMask, State->FrMask));

        } else if ( (FirstByte & P6_MASK) == P6_PREFIX ) {

            if (FirstByte & 0x10) {

                GrMask = FirstByte & 0xF;
                State->GrMask |= GrMask;

            } else {

                FrMask = FirstByte & 0xF;
                State->FrMask |= FrMask;

            }

            UW_DEBUG(("Prolog P6: is_gr = %d, mask = %x\n",
                      (FirstByte & 0x10) ? 1 : 0,
                      (FirstByte & 0x10) ? State->GrMask : State->FrMask));

        } else if ( (FirstByte & P7_MASK) == P7_PREFIX ) {

            RecType = FirstByte & ~P7_MASK;

            switch (RecType) {

            case PSP_SPREL:

                //
                // sp-relative location
                //

                Index = P7RecordTypeToRegisterIndex[RecType];
                Offset = ReadLEB128(Desc, &DescIndex);
                if (!(State->IsTarget) || (State->MiscMask & MASK(Index,1)))
                {
                    UwContext->MiscRegs[Index].Where = SP_RELATIVE;
                    UwContext->MiscRegs[Index].SaveOffset = Offset;
                    if (!(State->MiscMask & MASK(Index,1))) {
                        UwContext->MiscRegs[Index].When = State->RegionLen;
                        State->MiscMask |= MASK(Index,1);
                    }
                }
                UW_DEBUG(("Prolog P7: type=%d spoff = %d\n", RecType, Offset));
                break;


            case RP_PSPREL:
            case PFS_PSPREL:
            case PREDS_PSPREL:
            case LC_PSPREL:
            case UNAT_PSPREL:
            case FPSR_PSPREL:

                //
                // psp-relative location
                //

                Index = P7RecordTypeToRegisterIndex[RecType];
                Offset = ReadLEB128(Desc, &DescIndex);
                if (!(State->IsTarget) || (State->MiscMask & MASK(Index,1)))
                {
                    UwContext->MiscRegs[Index].Where = PSP_RELATIVE;
                    UwContext->MiscRegs[Index].SaveOffset = Offset;
                    UwContext->MiscRegs[Index].When = 0;
                    State->MiscMask |= MASK(Index,1);
                }
                UW_DEBUG(("Prolog P7: type=%d pspoff= %d\n", RecType, Offset));
                break;

            case MEM_STACK_V:
            case RP_WHEN:
            case PFS_WHEN:
            case PREDS_WHEN:
            case LC_WHEN:
            case UNAT_WHEN:
            case FPSR_WHEN:

                //
                // Nevermind processing these descriptors because they
                // have been taken care of in phase 0
                //

                Offset = ReadLEB128(Desc, &DescIndex);
                break;

            case MEM_STACK_F:

                Offset = ReadLEB128(Desc, &DescIndex);
                FrameSize = ReadLEB128(Desc, &DescIndex);

                UW_DEBUG(("Prolog P7: type=%d Slot=%d FrameSize=%d\n",
                          RecType, Offset, FrameSize));
                break;

            case SPILL_BASE:

                State->SpillBase = ReadLEB128(Desc, &DescIndex);
                State->SpillPtr = State->SpillBase;
                UW_DEBUG(("Prolog P7: type=%d, spillbase=%d\n",
                          RecType, State->SpillBase));
                break;

            default:

                UW_DEBUG(("invalid unwind descriptors\n"));

            }

        } else if ( (FirstByte & P8_MASK) == P8_PREFIX ) {

            RecType = Desc[DescIndex++];

            switch (RecType) {

            case PSP_PSPREL:
                 VUW_DEBUG_PRINT("Unsupported Unwind Descriptor!\n");
                 break;

            case RP_SPREL:
            case PFS_SPREL:
            case PREDS_SPREL:
            case LC_SPREL:
            case UNAT_SPREL:
            case FPSR_SPREL:
            case BSP_SPREL:
            case BSPSTORE_SPREL:
            case RNAT_SPREL:
            case PRIUNAT_SPREL:

                //
                // sp-relative location
                //

                Index = P8RecordTypeToRegisterIndex[RecType];
                Offset = ReadLEB128(Desc, &DescIndex);
                if (!(State->IsTarget) || (State->MiscMask & MASK(Index,1)))
                {
                    UwContext->MiscRegs[Index].Where = SP_RELATIVE;
                    UwContext->MiscRegs[Index].SaveOffset = Offset;
                    if (!(State->MiscMask & MASK(Index,1))) {
                        UwContext->MiscRegs[Index].When=State->RegionLen;
                        State->MiscMask |= MASK(Index,1);
                    }
                }
                UW_DEBUG(("Prolog P8: type=%d spoff= %d\n", RecType, Offset));
                break;

            case BSP_PSPREL:
            case BSPSTORE_PSPREL:
            case RNAT_PSPREL:
            case PRIUNAT_PSPREL:

                //
                // psp-relative location
                //

                Index = P8RecordTypeToRegisterIndex[RecType];
                Offset = ReadLEB128(Desc, &DescIndex);
                if (!(State->IsTarget) || (State->MiscMask & MASK(Index,1)))
                {
                    UwContext->MiscRegs[Index].Where = PSP_RELATIVE;
                    UwContext->MiscRegs[Index].SaveOffset = Offset;
                    UwContext->MiscRegs[Index].When = 0;
                    State->MiscMask |= MASK(Index,1);
                }
                UW_DEBUG(("Prolog P8: type=%d pspoff= %d\n", RecType, Offset));
                break;

            case BSP_WHEN:
            case BSPSTORE_WHEN:
            case RNAT_WHEN:
            case PRIUNAT_WHEN:

                //
                // Nevermind processing these descriptors because they
                // have been taken care of in phase 0
                //

                Offset = ReadLEB128(Desc, &DescIndex);
                break;

            default:

                UW_DEBUG(("Invalid record type for descriptor P8!\n"));

            }

        } else if ( (FirstByte & P9_MASK) == P9_PREFIX ) {

            DescIndex += 2;
            VUW_DEBUG_PRINT("Format P9 not supported yet!\n");

        } else if ( (FirstByte & P10_MASK) == P10_PREFIX ) {

            UCHAR Abi = Desc[DescIndex++];
            UCHAR Context = Desc[DescIndex++];

        } else {

            UW_DEBUG(("Invalid descriptor!\n"));

        }
    }

    GrMask = State->GrMask;
    FrMask = State->FrMask;
    BrMask = State->MiscMask >> REG_BR_BASE;

    if (!(GrMask | FrMask | BrMask)) {

        return;

    } else if (SpillMaskOmitted && !(State->IsTarget)) {

        //
        // When spillmask is omitted, floating point registers, general
        // registers, and then branch regisers are spilled in order.
        // They are not modified in the prologue region; therefore, there
        // is no need to restore their contents when the control ip is
        // in this prologue region.
        //

        // 1. floating point registers

        State->SpillPtr &= ~(SPILLSIZE_OF_FLOAT128_IN_DWORDS - 1);
        NextFr = NUMBER_OF_PRESERVED_FR - 1;
        while (FrMask & 0xFFFFF) {
            if (FrMask & 0x80000) {
                State->SpillPtr += SPILLSIZE_OF_FLOAT128_IN_DWORDS;
                UwContext->Float[NextFr].SaveOffset = State->SpillPtr;
            }
            FrMask = FrMask << 1;
            NextFr--;
        }

        // 2. branch registers

        NextBr = REG_BR_BASE + NUMBER_OF_PRESERVED_BR - 1;
        while (BrMask & 0x1F) {
            if (BrMask & 0x10) {
                if (UwContext->MiscRegs[NextBr].Where == PSP_RELATIVE) {
                    State->SpillPtr += SPILLSIZE_OF_ULONGLONG_IN_DWORDS;
                    UwContext->MiscRegs[NextBr].SaveOffset = State->SpillPtr;
                }
            }
            BrMask = BrMask << 1;
            NextBr--;
        }

        // 3. general registers

        NextGr = NUMBER_OF_PRESERVED_GR - 1;
        while (GrMask & 0xF) {
            if (GrMask & 0x8) {
                State->SpillPtr += SPILLSIZE_OF_ULONGLONG_IN_DWORDS;
                UwContext->Integer[NextGr].SaveOffset = State->SpillPtr;
            }
            GrMask = GrMask << 1;
            NextGr--;
        }

    } else if (SpillMaskOmitted && State->IsTarget) {

        State->GrMask = 0;
        State->FrMask = 0;
        State->MiscMask &= MASK(REG_BR_BASE, 1) - 1;

    } else if (SpillMaskOmitted == FALSE) {

        ULONG Length;

        if (State->IsTarget) {

            //
            // control ip is in the prologue region; clear the masks
            // and then process the imask to determine which preserved
            // Gr/Fr/Br have been saved and set the corresponding bits.
            //

            State->GrMask = 0;
            State->FrMask = 0;
            State->MiscMask &= MASK(REG_BR_BASE, 1) - 1;
            Length = UwContext->TargetSlot - State->RegionBegin;
        } else {
            Length = State->RegionLen;
        }

        NextGr = NUMBER_OF_PRESERVED_GR - 1;
        NextBr = NUMBER_OF_PRESERVED_BR - 1;
        NextFr = NUMBER_OF_PRESERVED_FR - 1;
        for (Count = 0; Count < Length; Count++) {

            if ((Count % 4) == 0) {
                FirstByte = Desc[ImaskBegin++];
            } else {
                FirstByte = FirstByte << 2;
            }

            switch (FirstByte & 0xC0) {

            case 0x40:                  // 0x01 - save next fr

                while ( !(FrMask & 0x80000) && (NextFr > 0) ) {
                    NextFr--;
                    FrMask = FrMask << 1;
                }

                UW_DEBUG(("spilled register FS%lx\n", (ULONG)NextFr));

                State->FrMask |= MASK(NextFr,1);
                UwContext->Float[NextFr].When = Count;
                State->SpillPtr += SPILLSIZE_OF_ULONGLONG_IN_DWORDS;
                State->SpillPtr &= ~(SPILLSIZE_OF_FLOAT128_IN_DWORDS - 1);
                State->SpillPtr += SPILLSIZE_OF_FLOAT128_IN_DWORDS;
                UwContext->Float[NextFr].SaveOffset = State->SpillPtr;

                NextFr--;
                FrMask = FrMask << 1;
                break;

            case 0x80:                  // 0x10 - save next gr

                while ( !(GrMask & 0x8) && (NextGr > 0) ) {
                    NextGr--;
                    GrMask = GrMask << 1;
                }

                UW_DEBUG(("spilled register S%lx\n", (ULONG)NextGr));

                State->GrMask |= MASK(NextGr,1);
                UwContext->Integer[NextGr].When = Count;
                State->SpillPtr += SPILLSIZE_OF_ULONGLONG_IN_DWORDS;
                UwContext->Integer[NextGr].SaveOffset = State->SpillPtr;

                NextGr--;
                GrMask = GrMask << 1;
                break;

            case 0xC0:                  // 0x11 - save next br

                while ( !(BrMask & 0x10) && (NextBr > 0) ) {
                    NextBr--;
                    BrMask = BrMask << 1;
                }

                UW_DEBUG(("spilled register BS%lx\n", (ULONG)NextBr));

                Index = REG_BR_BASE + NextBr;
                State->MiscMask |= MASK(Index,1);
                UwContext->MiscRegs[Index].When = Count;
                if (UwContext->MiscRegs[Index].Where == PSP_RELATIVE) {
                    State->SpillPtr += SPILLSIZE_OF_ULONGLONG_IN_DWORDS;
                    UwContext->MiscRegs[Index].SaveOffset = State->SpillPtr;
                }

                NextBr--;
                BrMask = BrMask << 1;
                break;

            default:                    // 0x00 - save no register
                break;

            }
        }
    }
}