/*static char *SCCSID = "@(#)kmode.h    6.5 91/12/03";*/
/***  KMODE.H
 *
 * SCCSID = @(#)kmode.h 6.5 91/12/03
 *
 * Kernel mode macros, definitions and stack frame structures.
 *
 * IBM/Microsoft Confidential
 *
 * Copyright (c) IBM Corporation 1987, 1989
 * Copyright (c) Microsoft Corporation 1987, 1989
 *
 * All Rights Reserved
 *
 * MODIFICATION HISTORY
 *     10/25/88  MJM    Created
 *     04/11/96  TPL    Added KM_FF_HYBERNATE flag for system hybernation
 */

/***EK+ Global force flag definitions
 */

#define KM_FF_RESCHED      0x000000FF  // Reserved for Resched flags
#define KM_FF_GCTXH        0x00000100  // Global context hook set
#define KM_FF_ATTENDSWAP   0x00000200  // Need to attend to swap file
#define KM_FF_SIGPEND      0x00000400  // Signal pending
#define KM_FF_EVAPALL      0x00000800  // PTDAs or TCBs to evaporate
#define KM_FF_FREECOMPDFS  0x00001000  // free swapper's compressed pages d98488
#define KM_FF_HYBERNATE    0x00002000  // Hybernation event
/*end*/

/***  Misc definitions
 */

/* XLATOFF */
#define KM_GLOBAL_CONTEXT  ((PVOID)-1) // global context hook flag
/* XLATON */

/* ASM
KM_GLOBAL_CONTEXT    equ   -1 ; global context hook flag
*/

/***LP   kmAssert - data structure assertion checking macros
 *
 * ENTRY t - typedef name for structure (i.e. INTH, IOP, etc.)
 *    p - pointer to structure to be checked
 *
 * EXIT  NONE
 *
 * A define "<t>_SIGNATURE" must be defined for each structure
 * where "t" is the typedef name used in the assert macros.  This define
 * is the value for the signature field.  It is a 32 bit value
 * that should be the typedef name (i.e. 0x484F4F4B for "HOOK").
 */

#ifdef   KMSTRICT

#define BAD_SIGNATURE      0x53444142

/* XLATOFF */
#define kmAssertDef(t)     ULONG t##_sig;
#define kmAssertCreate(t, p)  ((p)->t##_sig = t##_SIGNATURE)
#define kmAssertP(t, p)    _assert((p) != NULL && \
              (p)->t##_sig == t##_SIGNATURE)
#define kmAssertDestroy(t, p) ((p)->t##_sig = BAD_SIGNATURE)
#define kmAssertTRUE(e)    _assert(e)
/* XLATON */

/* ASM
kmAssertDef macro t
&t&_sig dd  ?
   endm

kmAssertCreate macro t, p
   mov   [p].&t&_sig,&t&_SIGNATURE
   endm

kmAssertP   macro t, p
   local l1,l2
   or p,p            ;; check if NULL
   jz short l1
   cmp   [p].&t&_sig,&t&_SIGNATURE  ;; check signature
   je short l2
l1:   Panic <"kmAssertP">
l2:
   endm

kmAssertDestroy macro t, p
   mov   [p].&t&_sig,BAD_SIGNATURE
   endm
*/

#else

/* XLATOFF */
#define kmAssertDef(t)
#define kmAssertCreate(t, p)
#define kmAssertDestroy(t, p)
#define kmAssertP(t, p)
#define kmAssertTRUE(e)
/* XLATON */

/* ASM
kmAssertDef    macro t
   endm
kmAssertCreate    macro t, p
   endm
kmAssertDestroy      macro t, p
   endm
kmAssertP      macro t, p
   endm
*/
#endif

/***LT+ DLL - doublely linked list data structure
 *
 * This structure is used to manage doublely linked lists.  It
 * contains a pointer to the Next entry and Previous entry.  The
 * doublely linked lists are circular to simplify the deletion
 * at beginning and end special cases.  The head Next and Prev
 * field must initially point to the Head (itself).
 *
 * The last Next field points to the Head and the first Prev
 * field points to the Head.  In a empty list the Head's Next
 * and Prev point to the Head.  Additions are the same for a
 * empty or full list.  A deletion on a empty list will not
 * hurt anything.
 *
 * If this structure is kept at the begining of the host data
 * structure to be linked together, the head of the list only
 * needs to be a DLL instead of the whole host data structure.
 *
 *         +---------+
 *         v       |
 *    +------------+     |
 *   Head   |    Next -------+  |
 *    +------------+  |  |
 *  +---------- Prev    |   |  |
 *  | +------------+  |  |
 *  |    ^    +------+  |
 *  |  +------+     v       |
 *  |  | +------------+     |
 *  |  | |    Next -------+  |
 *  |  | +------------+  |  |
 *  |  +------- Prev    |   |  |
 *  | +------------+  |  |
 *  |    ^    +------+  |
 *  |  +------+     v       |
 *  |  | +------------+     |
 *  |  | |    Next ----------+
 *  |  | +------------+
 *  |  +------- Prev    |
 *  | +------------+
 *  |    ^
 *  +---------+
 */

typedef struct dll_s {
    struct dll_s *dll_pdllNext;     // pointer to next DLL entry
    struct dll_s *dll_pdllPrev;     // pointer to previous DLL entry
} DLL;

typedef DLL *PDLL;
/*end*/

#define DLL_UNLINKED (NULL)      // dll_pdllNext is NULL is unlinked

/***ET+ HOOK - arm hook service control block
 *
 * This structure contains the information for the arm hook
 * services provided to kernel.  This blocks are allocated
 * and freed with KMAllocHook and KMFreeHook.  "hook_dll"
 * is the link fields used to "arm" a hook by putting the block
 * on the arm hook service's linked list.  "hook_phookOwner" is
 * used to link together all the "per-thread" allocated blocks.
 * "hook_phookOwner" contains -1 for the globally allocated
 * hook blocks.  "hook_pfn" is the hook handler address.
 *
 * NOTE: This structure must end dword aligned so that the
 * reference data tacked on the end starts dword aligned.
 */

typedef struct hook_s {
    struct dll_s hook_dll;    // links to next/prev hook in arm list
    struct hook_s *hook_phookOwner; // link to next hook in owner list
    void *hook_ptcbOwner;     // owning TCB if not global hook
    void *hook_pfn;        // handler address
    BYTE hook_offRefData;     // offset to reference data
    BYTE hook_iType;       // hook type
    BYTE hook_pad[2];         // alignment
#ifdef   KMSTRICT
    ULONG HOOK_sig;        // assertion checking
#endif
} HOOK;

typedef HOOK *PHOOK;       // pointer to hook structure
typedef struct dll_s HOOKHEAD;      // hook head type
typedef HOOKHEAD *PHOOKHEAD;     // pointer to hook head type
/*end*/

/* ASM
.errnz (size hook_s and 011b)    ; make sure HOOK is dword aligned
.errnz (size hook_s gt 255)      ; make sure size can fit in a byte
*/

#define HOOK_SIGNATURE  0x4b4f4f48  // "HOOK"

/* XLATOFF */
typedef VOID (PASCAL *HOOKPFN)(PVOID);
typedef VOID (PASCAL *HOOKPFN2)(PVOID, PVOID);

#define KM_GLOBAL_HOOK     ((PVOID)-1)
/* XLATON */

/* ASM
KM_GLOBAL_HOOK       equ   -1
*/

/* XLATOFF */
/***EP   KMInitHookHead - initialize hook head
 *
 * ENTRY (phookhead) = hook head pointer
 *
 * EXIT  NONE
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * initialize doublely linked list head
 */

#define KMInitHookHead(phookhead) { \
   (phookhead)->dll_pdllNext = (phookhead)->dll_pdllPrev = (phookhead); \
   }
/* XLATON */

/* ASM
KMInitHookHead macro phookhead
   mov   [phookhead].dll_pdllNext,phookhead
   mov   [phookhead].dll_pdllPrev,phookhead
   endm
*/

/* XLATOFF */
/***EP   KMMarkHookUnlinked - marks the hook block "unlinked"
 *
 * ENTRY (phook) = hook to mark unlinked
 *
 * EXIT  NONE
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * pNext link = 0
 */

#define KMMarkHookUnlinked(phook) { \
   ((phook)->hook_dll.dll_pdllNext = DLL_UNLINKED); \
   ((phook)->hook_dll.dll_pdllPrev = DLL_UNLINKED); \
   }
/* XLATON */

/* ASM
KMMarkHookUnlinked   macro phook
   mov   [phook].hook_dll.dll_pdllNext,DLL_UNLINKED
   mov   [phook].hook_dll.dll_pdllPrev,DLL_UNLINKED
   endm
*/

/* XLATOFF */
/***EP   KMIsHookLinked - checks if the hook is already linked
 *
 * ENTRY (phook) = hook to check
 *
 * EXIT  returns TRUE, if already linked
 *    returns FALSE, if not linked to a list
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * return pNext link == 0
 */

#define KMIsHookLinked(phook) ((phook)->hook_dll.dll_pdllNext != DLL_UNLINKED)
/* XLATON */

/* ASM
KMIsHookLinked macro phook
   cmp   [phook].hook_dll.dll_pdllNext,DLL_UNLINKED
   endm
*/

/* XLATOFF */
/***EP   KMQueryHookData - get a pointer to the hook's reference data
 *
 * ENTRY (phook)     = hook block pointer
 *
 * EXIT  (pRefData) = pointer to reference data
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * return pointer to reference data
 */

#ifdef   KMSTRICT

#define KMQueryHookData(phook) \
   ((PVOID)((phook)->hook_offRefData == 0 ? -1 : \
      (ULONG)(phook) + (ULONG)((phook)->hook_offRefData)))
#else

#define KMQueryHookData(phook) \
   ((PVOID)((ULONG)(phook) + (ULONG)((phook)->hook_offRefData)))
#endif
/* XLATON */

/* ASM
KMQueryHookData macro   p,phook
   local qhd10
ifdef KMSTRICT
   mov   p,-1
   cmp   [phook].hook_offRefData,0
   je short qhd10
endif
   movzx p,[phook].hook_offRefData
   add   p,phook
qhd10:
   endm
*/

/* XLATOFF */
/***EP   KMQueryHookType - return hook type
 *
 * Returns the hook type past during hook allocation
 *
 * ENTRY (phook)  = hook block pointer
 *
 * EXIT  (ulType) = hook type value
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * return hook type
 */

#define KMQueryHookType(phook) (((PHOOK)phook)->hook_iType)
/* XLATON */

/* ASM
KMQueryHookType   macro ulType,phook
   movzx ulType,[phook].hook_iType
   endm
*/

/* XLATOFF */
/***EP   KMNextHookLink - get next entry in hook arm list
 *
 * Returns a pointer to the next entry in hook arm linked list.
 *
 * ENTRY (phook) = hook pointer
 *
 * EXIT  (phookNext) = pointer to next hook entry
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 */

#define KMNextHookLink(phook) ((PVOID)((phook)->hook_dll.dll_pdllNext))
/* XLATON */

/* ASM
KMNextHookLink macro phookNext,phook
   mov   phookNext,[phook].hook_dll.dll_pdllNext
   endm
*/

/* XLATOFF */
/***EP   KMCallHook - call hook function
 * KMCallHook2 - call hook function with 2 parameters
 *
 * Calls the hook function passing the specified parameters
 *
 * ENTRY (phook) = hook pointer
 *    (param) = function parameters
 *
 * EXIT  NONE
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 */

#define KMCallHook(phook, param) ((*((HOOKPFN)((phook)->hook_pfn)))param)
#define KMCallHook2(phook, param) ((*((HOOKPFN2)((phook)->hook_pfn)))param)
/* XLATON */

/* ASM
KMCallHook  macro phook,param
   PCall [phook].hook_pfn, <param>
   endm
*/

/* XLATOFF */
/***EP   KMAddHookAfter - add a hook to a doublely linked list after
 *
 * This function adds a new hook to the place on a doublely
 * linked list after the specified hook.
 *
 * If this function is used with the head of the list, it adds
 * the new hook to the beginning of the linked list.
 *
 * ENTRY (phookNew) = hook to add
 *    (phook) = pointer to hook to add phookNew after
 *
 * EXIT  NONE
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * pNew->pNext = p->pNext
 * pNew->pPrev = p
 * p->pNext->pPrev = pNew
 * p->pNext = pNew
 */

#define KMAddHookAfter(phookNew, phook) { \
   (phookNew)->hook_dll.dll_pdllNext = (phook)->hook_dll.dll_pdllNext; \
   (phookNew)->hook_dll.dll_pdllPrev = &(phook)->hook_dll; \
   (phook)->hook_dll.dll_pdllNext->dll_pdllPrev = &(phookNew)->hook_dll; \
   (phook)->hook_dll.dll_pdllNext = &(phookNew)->hook_dll; \
   }

/***EP   KMAddHookBefore - add a hook to a doublely linked list before
 *
 * This function adds a new hook to the place on a doublely
 * linked list before the specified hook.
 *
 * If this function is used with the head of the list, it adds
 * the new hook to the end of the linked list.
 *
 * ENTRY (phookNew) = hook to add
 *    (phook) = pointer to hook to add phookNew before
 *
 * EXIT  NONE
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * pNew->pNext = p
 * pNew->pPrev = p->pPrev
 * p->pPrev->pNext = pNew
 * p->pPrev = pNew
 */

#define KMAddHookBefore(phookNew, phook) { \
   (phookNew)->hook_dll.dll_pdllNext = &(phook)->hook_dll; \
   (phookNew)->hook_dll.dll_pdllPrev = (phook)->hook_dll.dll_pdllPrev; \
   (phook)->hook_dll.dll_pdllPrev->dll_pdllNext = &(phookNew)->hook_dll; \
   (phook)->hook_dll.dll_pdllPrev = &(phookNew)->hook_dll; \
   }

/***EP   KMDeleteHook - delete a hook on a doublely linked
 *
 * This function deletes the specified hook on a doublely
 * linked list.
 *
 * ENTRY (phook) = pointer to hook to delete
 *
 * EXIT  NONE
 *
 * CONTEXT Task
 *    Interrupt
 *
 * PSEUDO-CODE
 *
 * p->pPrev->pNext = p->pNext
 * p->pNext->pPrev = p->pPrev
 */

#define KMDeleteHook(phook) { \
   (phook)->hook_dll.dll_pdllPrev->dll_pdllNext = \
      (phook)->hook_dll.dll_pdllNext; \
   (phook)->hook_dll.dll_pdllNext->dll_pdllPrev = \
      (phook)->hook_dll.dll_pdllPrev; \
   KMMarkHookUnlinked(phook); \
   }
/* XLATON */

/* ASM
KMDeleteHook macro phook,reg1,reg2
   mov   reg1,[phook].hook_dll.dll_pdllNext
   mov   reg2,[phook].hook_dll.dll_pdllPrev
   mov   [reg2].hook_dll.dll_pdllNext,reg1
   mov   reg1,[phook].hook_dll.dll_pdllPrev
   mov   reg2,[phook].hook_dll.dll_pdllNext
   mov   [reg2].hook_dll.dll_pdllPrev,reg1
   KMMarkHookUnlinked phook
   endm
*/

/***  Values for rip_flags
 */

#define KM_RIP_INVALID     0x0001   // invalid register
#define KM_RIP_INVALID_SET 0x0002   // invalid register to set
#define KM_RIP_WORD     0x0004   // word register
#define KM_RIP_TSD_RELATIVE   0x0008   // rip_offset relative to TSD beginning
#define KM_RIP_C32      0x0010   // 32-bit C style call

/***  Register numbers for KMQuery/SetClientRegister
 */

/* XLATOFF */
#define KM_REG_DS    FIELDOFFSET(CRI,cri_ds.rip_flags)
#define KM_REG_ES    FIELDOFFSET(CRI,cri_es.rip_flags)
#define KM_REG_SS    FIELDOFFSET(CRI,cri_ss.rip_flags)
#define KM_REG_CS    FIELDOFFSET(CRI,cri_cs.rip_flags)
#define KM_REG_GS    FIELDOFFSET(CRI,cri_gs.rip_flags)
#define KM_REG_FS    FIELDOFFSET(CRI,cri_fs.rip_flags)
#define KM_REG_EAX      FIELDOFFSET(CRI,cri_eax.rip_flags)
#define KM_REG_EBX      FIELDOFFSET(CRI,cri_ebx.rip_flags)
#define KM_REG_ECX      FIELDOFFSET(CRI,cri_ecx.rip_flags)
#define KM_REG_EDX      FIELDOFFSET(CRI,cri_edx.rip_flags)
#define KM_REG_EBP      FIELDOFFSET(CRI,cri_ebp.rip_flags)
#define KM_REG_ESP      FIELDOFFSET(CRI,cri_esp.rip_flags)
#define KM_REG_ESI      FIELDOFFSET(CRI,cri_esi.rip_flags)
#define KM_REG_EDI      FIELDOFFSET(CRI,cri_edi.rip_flags)
#define KM_REG_EIP      FIELDOFFSET(CRI,cri_eip.rip_flags)
#define KM_REG_EFLAG    FIELDOFFSET(CRI,cri_eflag.rip_flags)
#define KM_REG_CBARGS      FIELDOFFSET(CRI,cri_cbargs.rip_flags)
#define KM_REG_TRAPNUM     FIELDOFFSET(CRI,cri_trapnum.rip_flags)
#define KM_REG_ERRCODE     FIELDOFFSET(CRI,cri_errcode.rip_flags)
#define KM_REG_MAX      FIELDOFFSET(CRI,cri_errcode.rip_flags)
#define KM_REG_RETURN      KM_REG_EIP
#define KM_REG_TOP      FIELDOFFSET(CRI,cri_ulSize)
/* XLATON */

/* ASM
KM_REG_DS         equ   offset cri_ds
KM_REG_ES         equ   offset cri_es
KM_REG_SS         equ   offset cri_ss
KM_REG_CS         equ   offset cri_cs
KM_REG_GS         equ   offset cri_gs
KM_REG_FS         equ   offset cri_fs
KM_REG_EAX        equ   offset cri_eax
KM_REG_EBX        equ   offset cri_ebx
KM_REG_ECX        equ   offset cri_ecx
KM_REG_EDX        equ   offset cri_edx
KM_REG_EBP        equ   offset cri_ebp
KM_REG_ESP        equ   offset cri_esp
KM_REG_ESI        equ   offset cri_esi
KM_REG_EDI        equ   offset cri_edi
KM_REG_EIP        equ   offset cri_eip
KM_REG_EFLAG         equ   offset cri_eflag
KM_REG_CBARGS        equ   offset cri_cbargs
KM_REG_TRAPNUM       equ   offset cri_trapnum
KM_REG_ERRCODE       equ   offset cri_errcode
KM_REG_MAX        equ   offset cri_errcode
KM_REG_RETURN        equ   <KM_REG_EIP>
KM_REG_TOP        equ   offset cri_ulSize
*/

/***EP+ KMREG - Query/Set all register buffer structure
 *
 * This is the structure of the buffer for the KM
 * query/set all register function.
 */

typedef struct kmreg_s {
   ULONG kmreg_eax;
   ULONG kmreg_ebx;
   ULONG kmreg_ecx;
   ULONG kmreg_edx;
   ULONG kmreg_ebp;
   ULONG kmreg_esi;
   ULONG kmreg_edi;
   ULONG kmreg_ds;
   ULONG kmreg_es;
   ULONG kmreg_fs;
   ULONG kmreg_gs;
   ULONG kmreg_cs;
   ULONG kmreg_eip;
   ULONG kmreg_eflag;
   ULONG kmreg_ss;
   ULONG kmreg_esp;
} KMREG;

typedef KMREG *PKMREG;
/*end*/

/***LT+ RIP - Register information packet
 *
 * This is an entry in the CRI structure containing
 * some flags indicating if valid, word size, etc and
 * the offset of the client's register.
 */

typedef struct rip_s {
   USHORT rip_flags;
   USHORT rip_offset;
} RIP;

typedef RIP *PRIP;
/*end*/

/***LT+ CRI - Client register information
 *
 * This data structure contains the offset of each client
 * register relative to the client stack frame base for
 * a particular stack frame type.  "cri_pfnRebuild" is the
 * address of a routine that rebuilds the misc fields (like
 * isf_CurrIntLevel, ssf_sciret, etc) for the stack rebuild
 * function.  "cri_pfpfnKernelExit" is a pointer to a far 32
 * bit address of the kernel exit functions used after a stack
 * is rebuild to exit the kernel.
 */

/* XLATOFF */
typedef VOID (PASCAL *PFNRebuild)(ULONG ulFaultReg,
              struct ksf_s *pksf,
              ULONG cbargs);
/* XLATON */

typedef struct cri_s {
   ULONG cri_ulSize;    // size of stack frame
   struct rip_s cri_eax;
   struct rip_s cri_ebx;
   struct rip_s cri_ecx;
   struct rip_s cri_edx;
   struct rip_s cri_ebp;
   struct rip_s cri_esi;
   struct rip_s cri_edi;
   struct rip_s cri_ds;
   struct rip_s cri_es;
   struct rip_s cri_fs;
   struct rip_s cri_gs;
   struct rip_s cri_cs;
   struct rip_s cri_eip;
   struct rip_s cri_eflag;
   struct rip_s cri_ss;
   struct rip_s cri_esp;
   struct rip_s cri_cbargs;
   struct rip_s cri_trapnum;
   struct rip_s cri_errcode;
/* XLATOFF */
   PFNRebuild cri_pfnRebuild;
   FPVOID *cri_pfpfnKernelExit;
/* XLATON */
/* ASM
cri_pfnRebuild    dd ?
cri_pafpfnKernelExit dd ?
*/
} CRI;

typedef CRI *PCRI;
/*end*/

/***ET+ ISF - Interrupt manager stack frame
 *
 * This is what the stack frame looks like when the system
 * is entered thru the interrupt manager during a hardware
 * interrupt.  For a hardware interrupt in a VDM context, the
 * stack frame always needs to be a "VSF" type so the stack
 * frame base is adjusted by ISF_VSF_START.  The points the
 * stack frame base to "isf_edi" in the regular interrupt
 * frame.    The interrupt stack frame has also been padded
 * (ISF_STACK_PAD) between the general registers (EDI to EAX)
 * and the hardware pushed registers (EIP to SS) with a dummy
 * trap number and error code to look like the VSF stack frame.
 */

#define ISF_STACK_PAD      8

typedef struct isf_s {
   ULONG isf_CurrIntLevel;
   USHORT isf_gs;
   USHORT isf_padgs;
   USHORT isf_fs;
   USHORT isf_padfs;
   USHORT isf_es;
   USHORT isf_pades;
   USHORT isf_ds;
   USHORT isf_padds;
   ULONG  isf_edi;      // start of VDM stack frame
   ULONG  isf_esi;
   ULONG  isf_ebp;
   ULONG  isf_padesp;
   ULONG  isf_ebx;
   ULONG  isf_edx;
   ULONG  isf_ecx;
   ULONG  isf_eax;
   BYTE   isf_pad[ISF_STACK_PAD];
   ULONG  isf_eip;
   USHORT isf_cs;
   USHORT isf_padcs;
   ULONG  isf_eflag;
   ULONG  isf_esp;
   USHORT isf_ss;
   USHORT isf_padss;
} ISF;

typedef ISF *PISF;

#define ISF_VSF_START      isf_edi
#define ISF_SIZE     (sizeof(struct isf_s))
/*end*/

/***ET+ TSF - Trap or exception stack frame
 *
 * This is what the stack frame looks like when
 * the system is entered thru a 386 exception
 * (from protected mode).
 */

typedef struct tsf_s {
   ULONG  tsf_edi;
   ULONG  tsf_esi;
   ULONG  tsf_ebp;
   ULONG  tsf_padesp;
   ULONG  tsf_ebx;
   ULONG  tsf_edx;
   ULONG  tsf_ecx;
   ULONG  tsf_eax;
   USHORT tsf_gs;
   USHORT tsf_padgs;
   USHORT tsf_fs;
   USHORT tsf_padfs;
   USHORT tsf_es;
   USHORT tsf_pades;
   USHORT tsf_ds;
   USHORT tsf_padds;
   ULONG  tsf_trapnum;
   ULONG  tsf_errcode;
   ULONG  tsf_eip;
   USHORT tsf_cs;
   USHORT tsf_padcs;
   ULONG  tsf_eflag;
   ULONG  tsf_esp;
   USHORT tsf_ss;
   USHORT tsf_padss;
} TSF;

typedef TSF *PTSF;

#define TSF_SIZE  (sizeof(struct tsf_s))
/*end*/

/* ASM
tsf_cseip   equ   <fword ptr tsf_eip>
tsf_ssesp   equ   <fword ptr tsf_esp>
*/

/***ET+ KSF - Kernel stack frame
 *
 * This is what the stack frame looks like when
 * the system is re-entered from ring 0.  This is
 * frame used for handling exception while already
 * in kernel mode.
 */

typedef struct ksf_s {
   ULONG  ksf_edi;
   ULONG  ksf_esi;
   ULONG  ksf_ebp;
   ULONG  ksf_padesp;
   ULONG  ksf_ebx;
   ULONG  ksf_edx;
   ULONG  ksf_ecx;
   ULONG  ksf_eax;
   USHORT ksf_gs;
   USHORT ksf_padgs;
   USHORT ksf_fs;
   USHORT ksf_padfs;
   USHORT ksf_es;
   USHORT ksf_pades;
   USHORT ksf_ds;
   USHORT ksf_padds;
   ULONG  ksf_trapnum;
   ULONG  ksf_errcode;
   ULONG  ksf_eip;
   USHORT ksf_cs;
   USHORT ksf_padcs;
   ULONG  ksf_eflag;
} KSF;

typedef KSF *PKSF;

#define KSF_SIZE  (sizeof(struct ksf_s))
/*end*/

/* ASM
ksf_cseip   equ   <fword ptr ksf_eip>
ksf_tos     equ   <dword ptr (KSF_SIZE)>
*/

/***ET+ VSF - VDM process stack frame
 *
 * This is what the stack frame looks like when the system
 * is entered from a VDM thru a exception, software or
 * hardware interrupt.  Most of the 8086 emulation code
 * uses this stack frame directly for performance.  For
 * hardware interrupts taken in a VDM (in either V86 mode
 * or protected mode), the interrupt stack frame (see ISF)
 * is adjusted to look like this frame.
 *
 * The alternate stack frame holds the real or protected
 * mode sensitive registers for the other mode.  So when
 * the VDM is in protected mode, the last V86 mode segment
 * registers and CS:EIP, SS:ESP can be accessed, etc.  Two
 * things happen with we mode switch: 1) the alternate register
 * set is exchanged with the regular set (vsf_eip to vsf_padgs
 * is the exchanged with vsf_alteip to vsf_altpadgs), 2) the
 * TSS's ESP0 value is changed to the appropriate place in the
 * VSF structure.  For V86 mode, ESP0 points to the begining of
 * the segment registers (vsf_gs/vsf_padgs) and for protected
 * mode ESP0 points to the SS register (vsf_ss/vsf_padss).    For
 * protected mode entry, the segments registers are stored in
 * vsf_ds to vsf_gs explictly.   This makes the V86 mode and
 * protected mode stack frames the same for VDDs and the MVDM
 * kernel code.
 */

typedef struct vsf_s {
   ULONG  vsf_edi;
   ULONG  vsf_esi;
   ULONG  vsf_ebp;
   ULONG  vsf_padesp;
   ULONG  vsf_ebx;
   ULONG  vsf_edx;
   ULONG  vsf_ecx;
   ULONG  vsf_eax;
   ULONG  vsf_trapnum;
   ULONG  vsf_errcode;

   ULONG  vsf_eip;         // start of mode sensitive registers
   USHORT vsf_cs;
   USHORT vsf_padcs;
   ULONG  vsf_eflag;
   ULONG  vsf_esp;
   USHORT vsf_ss;
   USHORT vsf_padss;
   USHORT vsf_es;       // TSS points here for protected mode
   USHORT vsf_pades;
   USHORT vsf_ds;
   USHORT vsf_padds;
   USHORT vsf_fs;
   USHORT vsf_padfs;
   USHORT vsf_gs;
   USHORT vsf_padgs;
               // TSS points here for V86 mode
   ULONG vsf_alteip;    // other mode's register set
   USHORT vsf_altcs;
   USHORT vsf_altpadcs;
   ULONG vsf_alteflag;
   ULONG vsf_altesp;
   USHORT vsf_altss;
   USHORT vsf_altpadss;
   USHORT vsf_altes;
   USHORT vsf_altpades;
   USHORT vsf_altds;
   USHORT vsf_altpadds;
   USHORT vsf_altfs;
   USHORT vsf_altpadfs;
   USHORT vsf_altgs;
   USHORT vsf_altpadgs;
} VSF;

typedef VSF *PVSF;

#define VSF_SIZE        (sizeof(struct vsf_s))
/*end*/

/* XLATOFF */
#define VSF_EAX(pvsf)         ((pvsf)->vsf_eax)
#define VSF_EBX(pvsf)         ((pvsf)->vsf_ebx)
#define VSF_ECX(pvsf)         ((pvsf)->vsf_ecx)
#define VSF_EDX(pvsf)         ((pvsf)->vsf_edx)
#define VSF_ESI(pvsf)         ((pvsf)->vsf_esi)
#define VSF_EDI(pvsf)         ((pvsf)->vsf_edi)
#define VSF_EBP(pvsf)         ((pvsf)->vsf_ebp)
#define VSF_ESP(pvsf)         ((pvsf)->vsf_esp)
#define VSF_EIP(pvsf)         ((pvsf)->vsf_eip)
#define VSF_EFLAG(pvsf)       ((pvsf)->vsf_eflag)
#define VSF_CS(pvsf)       ((pvsf)->vsf_cs)
#define VSF_SS(pvsf)       ((pvsf)->vsf_ss)
#define VSF_DS(pvsf)       ((pvsf)->vsf_ds)
#define VSF_ES(pvsf)       ((pvsf)->vsf_es)
#define VSF_FS(pvsf)       ((pvsf)->vsf_fs)
#define VSF_GS(pvsf)       ((pvsf)->vsf_gs)

#define VSF_ALT_ESP(pvsf)     ((pvsf)->vsf_altesp)
#define VSF_ALT_EIP(pvsf)     ((pvsf)->vsf_alteip)
#define VSF_ALT_EFLAG(pvsf)      ((pvsf)->vsf_alteflag)
#define VSF_ALT_CS(pvsf)      ((pvsf)->vsf_altcs)
#define VSF_ALT_SS(pvsf)      ((pvsf)->vsf_altss)
#define VSF_ALT_DS(pvsf)      ((pvsf)->vsf_altds)
#define VSF_ALT_ES(pvsf)      ((pvsf)->vsf_altes)
#define VSF_ALT_FS(pvsf)      ((pvsf)->vsf_altfs)
#define VSF_ALT_GS(pvsf)      ((pvsf)->vsf_altgs)

#define VSF_ALT_SP(pvsf)      WORDOF((pvsf)->vsf_altesp,0)
#define VSF_ALT_IP(pvsf)      WORDOF((pvsf)->vsf_alteip,0)
#define VSF_ALT_FLAG(pvsf)    WORDOF((pvsf)->vsf_alteflag,0)

#define VSF_AX(pvsf)       WORDOF((pvsf)->vsf_eax,0)
#define VSF_AL(pvsf)       BYTEOF((pvsf)->vsf_eax,0)
#define VSF_AH(pvsf)       BYTEOF((pvsf)->vsf_eax,1)
#define VSF_BX(pvsf)       WORDOF((pvsf)->vsf_ebx,0)
#define VSF_BL(pvsf)       BYTEOF((pvsf)->vsf_ebx,0)
#define VSF_BH(pvsf)       BYTEOF((pvsf)->vsf_ebx,1)
#define VSF_CX(pvsf)       WORDOF((pvsf)->vsf_ecx,0)
#define VSF_CL(pvsf)       BYTEOF((pvsf)->vsf_ecx,0)
#define VSF_CH(pvsf)       BYTEOF((pvsf)->vsf_ecx,1)
#define VSF_DX(pvsf)       WORDOF((pvsf)->vsf_edx,0)
#define VSF_DL(pvsf)       BYTEOF((pvsf)->vsf_edx,0)
#define VSF_DH(pvsf)       BYTEOF((pvsf)->vsf_edx,1)
#define VSF_SI(pvsf)       WORDOF((pvsf)->vsf_esi,0)
#define VSF_DI(pvsf)       WORDOF((pvsf)->vsf_edi,0)
#define VSF_BP(pvsf)       WORDOF((pvsf)->vsf_ebp,0)
#define VSF_SP(pvsf)       WORDOF((pvsf)->vsf_esp,0)
#define VSF_IP(pvsf)       WORDOF((pvsf)->vsf_eip,0)
#define VSF_FLAG(pvsf)        WORDOF((pvsf)->vsf_eflag,0)

/***EP   GET_VDM_V86_ESP0 - calculate the v86 mode TSS ESP0 value
 *
 * ENTRY:   ptcb = register containing a pointer to the TCB
 *
 * EXIT: tss_esp0 value
 */

#define  GET_VDM_V86_ESP0(ptcb)  ((ptcb)->TCBCurrTSD + \
              (FIELDOFFSET(tsd_t,TSDPMStack) - \
                (VSF_SIZE - FIELDOFFSET(VSF,vsf_alteip))))

/***EP   GET_VDM_PM_ESP0 - calculate the protected mode TSS ESP0 value
 *
 * ENTRY:   ptcb = register containing a pointer to the TCB
 *
 * EXIT: tss_esp0 value
 */

#define  GET_VDM_PM_ESP0(ptcb)   ((ptcb)->TCBCurrTSD + \
              (FIELDOFFSET(tsd_t,TSDPMStack) - \
                (VSF_SIZE - FIELDOFFSET(VSF,vsf_es))))

// Defines used to work with the VDM stack frame and the alternate reg set

#define VSF_COPY_START     (FIELDOFFSET(VSF,vsf_eip))
#define VSF_ALT_START      (FIELDOFFSET(VSF,vsf_alteip))
#define VSF_ALT_SIZE    (VSF_SIZE - FIELDOFFSET(VSF,vsf_alteip))
#define VSF_GEN_START  (FIELDOFFSET(VSF,vsf_edi))
#define VSF_GEN_SIZE   (FIELDOFFSET(VSF,vsf_trapnum) - FIELDOFFSET(VSF,vsf_edi))
#define VSF_SEG_START  (FIELDOFFSET(VSF,vsf_esp))
#define VSF_ASEG_START (FIELDOFFSET(VSF,vsf_altesp))
#define VSF_SEG_SIZE   (FIELDOFFSET(VSF,vsf_alteip) - FIELDOFFSET(VSF,vsf_esp))

// TSS's ESP0 adjustment value when switching modes (add for V86, sub for PM)

#define  VSF_ESP0_ADJUST      (FIELDOFFSET(VSF,vsf_padgs) - \
              FIELDOFFSET(VSF,vsf_padss))
/* XLATON */

/* ASM
vsf_ax         equ   (word ptr vsf_eax)
vsf_al         equ   (byte ptr vsf_eax)
vsf_ah         equ   (byte ptr (vsf_eax+1))
vsf_bx         equ   (word ptr vsf_ebx)
vsf_bl         equ   (byte ptr vsf_ebx)
vsf_bh         equ   (byte ptr (vsf_ebx+1))
vsf_cx         equ   (word ptr vsf_ecx)
vsf_cl         equ   (byte ptr vsf_ecx)
vsf_ch         equ   (byte ptr (vsf_ecx+1))
vsf_dx         equ   (word ptr vsf_edx)
vsf_dl         equ   (byte ptr vsf_edx)
vsf_dh         equ   (byte ptr (vsf_edx+1))
vsf_si         equ   (word ptr vsf_esi)
vsf_di         equ   (word ptr vsf_edi)
vsf_bp         equ   (word ptr vsf_ebp)
vsf_sp         equ   (word ptr vsf_esp)
vsf_ip         equ   (word ptr vsf_eip)
vsf_flag    equ   (word ptr vsf_eflag)

; Equates used to exchange the alternate register set

VSF_COPY_START    equ   (vsf_eip)
VSF_ALT_START     equ   (vsf_alteip)
VSF_ALT_SIZE      equ   (VSF_SIZE - vsf_alteip)

; TSS's ESP0 adjustment value when switching modes (add for V86, sub for PM)

VSF_ESP0_ADJUST      equ   (vsf_padgs - vsf_padss)
*/

/***ET+ SEF - Kernel mode system entry frame
 *
 * This is the frame put on the by the call gate system
 * entry function (KMEnterKmodeCallGate or KMEnterKmodeAPI32).
 *
 * This frame is used for :
 * 1) 32-bit C APIs, with C callable workers (criSEF)
 * 2) 16-bit PASCAL APIs, with C callable workers  (criPASCALSEF)
 */

typedef struct sef_s {
   ULONG  sef_edi;
   ULONG  sef_esi;
   ULONG  sef_ebp;
   ULONG  sef_padesp;
   ULONG  sef_ebx;
   ULONG  sef_edx;
   ULONG  sef_ecx;
   ULONG  sef_eax;
   USHORT sef_gs;
   USHORT sef_padgs;
   USHORT sef_fs;
   USHORT sef_padfs;
   USHORT sef_es;
   USHORT sef_pades;
   USHORT sef_ds;
   USHORT sef_padds;
   ULONG  sef_retaddr;
   ULONG  sef_cbargs;
   ULONG  sef_eflag;
   ULONG  sef_eip;
   USHORT sef_cs;
   USHORT sef_padcs;
} SEF;

typedef SEF *PSEF;

#define SEF_SIZE  (sizeof(struct sef_s))
/*end*/

/***ET+ THUNKSEF - System entry frame with a thunk return address
 *
 * This is the frame put on the by the call gate system
 * entry function (KMEnterKmodeCallGate) and includes the
 * return address put on by the ring 3 thunk layer.
 *
 * THUNKSEF is compatabile with SEF.
 */

typedef struct thunksef_s {
   struct sef_s thunksef_sef;
   ULONG thunksef_ulret;
} THUNKSEF;

typedef THUNKSEF *PTHUNKSEF;

#define THUNKSEF_SIZE   (sizeof(struct thunksef_s))
/*end*/

/***LT+ SSF - SCI call gate stack frame
 *
 * This is what the stack frame looks like when the system
 * is entered thru SCI via call gate using the KMEnterKmodeSCI
 * function.
 */

typedef struct ssf_s {
   ULONG  ssf_edi;
   ULONG  ssf_esi;
   ULONG  ssf_ebp;
   ULONG  ssf_padesp;
   ULONG  ssf_ebx;
   ULONG  ssf_edx;
   ULONG  ssf_ecx;
   ULONG  ssf_eax;
   USHORT ssf_gs;
   USHORT ssf_padgs;
   USHORT ssf_fs;
   USHORT ssf_padfs;
   USHORT ssf_es;
   USHORT ssf_pades;
   USHORT ssf_ds;
   USHORT ssf_padds;
   USHORT ssf_thopadr;
   ULONG  ssf_cbargs;    /* B729310
                          * The Most Significant Bit of cbargs in an SCI stack
                          * frame is used to denote that a 16 bit callgate is
                          * being used with the SCI mechanism. Used by Dynamic
                          * API's. (_16B_Gate)
                          */
   USHORT ssf_sciret;
   ULONG  ssf_eflag;
   ULONG  ssf_eip;
   USHORT ssf_cs;
   USHORT ssf_padcs;
} SSF;

typedef SSF *PSSF;

#define SSF_SIZE  (sizeof(struct ssf_s))
#define _16B_Gate 0x80000000  /* B729310 */

/*end*/

/***ET+ DHF - Device help stack frame
 *
 * Kernel stack frame looks like this for Device helper
 */

typedef struct dhf_s {
   ULONG  dhf_edi;
   ULONG  dhf_esi;
   ULONG  dhf_ebp;
   ULONG  dhf_padesp;
   ULONG  dhf_ebx;
   ULONG  dhf_edx;
   ULONG  dhf_ecx;
   ULONG  dhf_eax;
   USHORT dhf_gs;
   USHORT dhf_padgs;
   USHORT dhf_fs;
   USHORT dhf_padfs;
   USHORT dhf_es;
   USHORT dhf_pades;
   USHORT dhf_ds;
   USHORT dhf_padds;
   ULONG  dhf_eflag;
   ULONG  dhf_scratch;
   ULONG  dhf_eip;
   USHORT dhf_cs;
   USHORT dhf_padcs;
} DHF;

typedef DHF *PDHF;

#define DHF_SIZE  (sizeof(struct dhf_s))
/*end*/

/***ET+ Hardware exception trap stack frame
 *
 * Stack frame for the trap manager before we go into kernel mode.
 */

typedef struct tf_s {
   ULONG  tf_trapnum;
   ULONG  tf_errcode;
   ULONG  tf_eip;
   USHORT tf_cs;
   USHORT tf_padcs;
   ULONG  tf_eflags;
   ULONG  tf_esp;
   USHORT tf_ss;
   USHORT tf_padss;
} TF;

typedef TF *PTF;
/*end*/

/* XLATOFF */
/***EP   KMQueryStackFrameSize - returns the size of the stack frame
 *
 * (ULONG) cb = KMQueryStackFrameSize(pcri);
 *
 * ENTRY pcri = pointer to CRI structure
 *
 * EXIT  size in bytes of stack frame (ULONG)
 */

#define KMQueryStackFrameSize(pcri) ((pcri)->cri_ulSize)
/* XLATON */

/* ASM

;***EP   KMQueryStackFrameSize - returns the size of the stack frame
;
;  KMQueryStackFrameSize reg, pcri
;
;  ENTRY reg = register to put size (ULONG)
;     pcri = pointer to CRI structure
;
;  EXIT  reg = size in bytes of stack frame
;
;  USES  NONE
;

KMQueryStackFrameSize   macro reg, pcri
   mov   reg,[(pcri).cri_ulSize]
   endm

;***EP   KMEnterExitKmodeInt - enter/exit kernel mode for interrupt manager
;
;  ENTRY (ESP) = interrupt stack frame
;     (DS, ES)  = flat context
;     Interrupts disabled
;
;  EXIT  NONE
;
;  USES  EAX, EBX, ECX, EDX, ESI, EDI, Flags
;
;  NOTE  This macro controls how the kernel/interrupt manager
;     preempts user and kernel code.  All ring 0 code is
;     not preemptable regardless of InDos.   Ring 3 code
;     is not preemptable if we are performing a DynamicTrace
;     single-step.
;

KMEnterExitKmodeInt macro
   local exk10,exk20,exk30,exk40,exk50,exk55,exk60
   ReferFlat _criISF,dword,ifundef
   ReferFlat _criVSF,dword,ifundef
   ReferFlat _TKSSBase,dword,ifundef
   ReferFlat _pTCBCur,dword,ifundef
if RAS
   ReferFlat _pbDYNResetPoint,dword,ifundef
   ReferFlat _DYNNullResetPoint,dword,ifundef
endif
if2
ifndef   KMExitKmodeEvents
   extrn KMExitKmodeEvents:near
endif
endif
ifdef INFOSTRICT        ;; check infosegs
ifndef   _PGVerifyInfoSegs
   extrn _PGVerifyInfoSegs:near
endif
   testb [esp].isf_eflag,F_VM
   jnz   short exk10    ;; jump if v86 mode interrupt
   testb [esp].isf_cs,RPL_MASK   ;; check client CS's RPL
   jz short exk20    ;; jump if ring 0
exk10:   call  _PGVerifyInfoSegs ;; verify all copies are in sync
exk20:
endif
   kmCheckEvents        ;; check for rescheduling
   jz exk60       ;; jump if no rescheduling needed

   testb [esp].isf_eflag,F_VM
   jnz   short exk40    ;; jump if v86 mode interrupt

   testb [esp].isf_cs,RPL_MASK   ;; check client CS's RPL
   jz exk60       ;; jump if ring 0

IF RAS
   cmp   [_pbDYNResetPoint],offset FLAT:_DYNNullResetPoint
   jne   exk60       ;; disable preemption while in DynTrc
ENDIF
   cmp   ss:[TKOPTDA.ptda_ProcType],PT_VDM
   je short exk30    ;; jump if VDM process

   mov   ebx,esp        ;; (EBX) = SS relative base
   SSToDS   ebx         ;; (EBX) = flat rel IntStackFrame base
   mov   esi,[_pTCBCur]    ;; (ESI) = current TCB pointer
   mov   [esi].TCB_pcriFrameType,offset FLAT:_criISF
   mov   [esi].TCB_pFrameBase,ebx
   jmp   short exk50

exk30:   mov   eax,dword ptr [esp].isf_ds
   mov   dword ptr [esp].ISF_VSF_START.vsf_ds,eax
   mov   eax,dword ptr [esp].isf_es
   mov   dword ptr [esp].ISF_VSF_START.vsf_es,eax
   mov   eax,dword ptr [esp].isf_fs
   mov   dword ptr [esp].ISF_VSF_START.vsf_fs,eax
   mov   eax,dword ptr [esp].isf_gs
   mov   dword ptr [esp].ISF_VSF_START.vsf_gs,eax

;  VDMs don't need TCB_pcriFrameType or TCB_pFrameBase set up because
;  the stack frame type and size is always the same.

exk40:   mov   esi,[_pTCBCur]    ;; (ESI) = current TCB pointer
exk50:   mov   [esi].TCBMiscFlags,0 ;; clear miscellaneous flags
   mov   byte ptr [_cInDos],1 ;; in kernel mode now
   sti            ;; enable interrupts
   cld            ;; clear direction flag
   call  KMExitKmodeEvents
   kmExitKmode
   testb [esp].isf_eflag,F_VM
   jz    short exk55    ;; jump if not V86 mode                      ;def 73187

   xor   eax,eax                       ;; v86 mode, insure seg regs are 0
   mov   dword ptr [esp].isf_ds,eax
   mov   dword ptr [esp].isf_es,eax
   mov   dword ptr [esp].isf_fs,eax
   mov   dword ptr [esp].isf_gs,eax
   jmp   short exk60    ;; exit                                      ;def 73187

exk55:
   cmp   ss:[TKOPTDA.ptda_ProcType],PT_VDM
   jne   short exk60    ;; jump if not a VDM

   mov   eax,dword ptr [esp].ISF_VSF_START.vsf_ds
   mov   dword ptr [esp].isf_ds,eax
   mov   eax,dword ptr [esp].ISF_VSF_START.vsf_es
   mov   dword ptr [esp].isf_es,eax
   mov   eax,dword ptr [esp].ISF_VSF_START.vsf_fs
   mov   dword ptr [esp].isf_fs,eax
   mov   eax,dword ptr [esp].ISF_VSF_START.vsf_gs
   mov   dword ptr [esp].isf_gs,eax
exk60:
   endm

.errnz   isf_eax - (ISF_VSF_START + vsf_eax)
.errnz   isf_ebx - (ISF_VSF_START + vsf_ebx)
.errnz   isf_ecx - (ISF_VSF_START + vsf_ecx)
.errnz   isf_edx - (ISF_VSF_START + vsf_edx)
.errnz   isf_ebp - (ISF_VSF_START + vsf_ebp)
.errnz   isf_esi - (ISF_VSF_START + vsf_esi)
.errnz   isf_edi - (ISF_VSF_START + vsf_edi)
.errnz   isf_padesp - (ISF_VSF_START + vsf_padesp)
.errnz   isf_pad - (ISF_VSF_START + vsf_trapnum)
.errnz   isf_eip - (ISF_VSF_START + vsf_eip)
.errnz   isf_cs - (ISF_VSF_START + vsf_cs)
.errnz   isf_eflag - (ISF_VSF_START + vsf_eflag)
.errnz   isf_esp - (ISF_VSF_START + vsf_esp)
.errnz   isf_ss - (ISF_VSF_START + vsf_ss)

;***EP   KMEnterKmodeVDM - enter kernel mode in a context VDM
;
;  ENTRY (ESP) = pointer to protected mode stack frame
;     (mode) = mode entered from:
;        "v86" = entered from V86 mode
;        "vpm" = entered from VDM protected mode
;     (fsreg) = what to do to fs
;        "flatcontext" = load fs with FLAT context
;        "nothing" = don't load fs with anything (no NullContext)
;     (trap)   = non-blank (eg, "trap") to skip misc. TCB initialization;
;        this allows us to save some time on trap entry, because
;        the only misc. flags that need to be cleared for VDMs are
;        on SVC calls, and DemDispatch already does this (JeffPar 25-Jul-90)
;  EXIT  (EAX) = client's EAX register
;     (EDX) = client's EDX register
;     (EBX) = VSF frame pointer (flat relative)
;     (DS, ES) = flat context
;     if fs = "flatcontext", (FS) = flat context
;     Direction flag cleared
;     Interrupts enabled
;
;  USES  EBX, DS, ES, Flags
;

KMEnterKmodeVDM macro mode,fsreg,trap
   ReferFlat _criVSF,dword,ifundef
   cld            ;; clear direction flag
   pushad
ifidni   <mode>,<vpm>
   mov   dword ptr [esp].vsf_ds,ds
   mov   dword ptr [esp].vsf_es,es
   mov   dword ptr [esp].vsf_fs,fs
   mov   dword ptr [esp].vsf_gs,gs
ifidni   <fsreg>,<nothing>
   NullContext <gs>,ebx
else
   NullContext <fs,gs>,ebx
endif
else
ifdifi   <mode>,<v86>
   %out  KMEnterKmodeVDM - bad parameter: mode
endif
endif
;  VDMs don't need TCB_pcriFrameType or TCB_pFrameBase set up because
;  the stack frame type and size is always the same.

ifidni   <fsreg>,<flatcontext>
   FlatContext <ds,es,fs>,ebx
else
ifb   <trap>
   FlatContext <ds,es>,ebx
else
   db 0B9h        ;; opcode for MOV ECX,xxxxxxxx
   dw (seg FLAT:DGROUP),0  ;; this allows us to depend on the high
   mov   ds,ecx         ;; bits of ECX being zero (JeffPar 25-Jul-90)
   mov   es,ecx
   assume   ds:FLAT,es:FLAT
endif
endif
ifb   <trap>
   mov   ebx,[_pTCBCur]    ;; (EBX) = current TCB pointer
   mov   [ebx].TCBMiscFlags,0 ;; clear miscellaneous flags
endif
   mov   ebx,[_pvsfFrameBase] ;; (EBX) = pvsf
   mov   byte ptr [_cInDos],1 ;; in kernel mode now
   sti            ;; enable interrupts
   endm

;***EP   KMExitKmodeVDM - exit kernel mode in a VDM context
;
;  ENTRY (DS) = flat context
;     (mode) = mode exiting to:
;        "v86" = returning to V86 mode
;        "vpm" = returning to VDM protected mode
;        "v86orvpm" = checks return type
;     (exitflag) = determines type of exit:
;        "normal" = normal kernel mode exit to v86 mode
;
;        "noreturn" = exit kernel mode; service the exit
;        kernel events, but don't return to the trapped code.
;
;        "restore" = just restore the registers; don't service
;        exit kmode events or return to the trapped code or
;     (debug) = if not blank, do page fault trace bit check
;
;  EXIT  Return to V86 mode code
;
;  USES  ALL
;
;  NOTE  The page fault trace bit check is so the debugger regains
;     control after a not present page has been swapped in.
;

KMExitKmodeVDM macro mode,exitflag,debug
   local exk10,exk20,exk30,exk40,exk50,nbChk,normal
   normal = 0
   .386p
if2
 ifndef KMExitKmodeEvents
   extrn KMExitKmodeEvents:near
 endif
endif

ifidni   <exitflag>,<noreturn>
   CCALL32  KMExitKmodeEvents,,<noalignesp>
endif

ifdifi   <exitflag>,<restore>
 ifdifi  <exitflag>,<noreturn>
   nbChk = $
   normal = 1
   cli            ;; disable interrupts until iretd
   db MI_SEGDS    ;; throw in 1 DS override to make nbChk
   kmCheckEvents        ;; optimally aligned for trap0d
  ifidni  <mode>,<v86>
   public   KMCHECKEVENTSIZE
   KMCHECKEVENTSIZE = $-nbChk
  endif
   jnz   short exk40    ;; jump if exit kmode events to service
 endif
endif

    exk10:
   kmExitKmode       ;; clear indos

ifidni   <mode>,<v86orvpm>
   testb [esp].vsf_eflag,F_VM
 if normal ne 0
   jz short exk50    ;; execute segreload out of line
 else
   jnz   short exk20
   mov   gs,dword ptr [esp].vsf_gs ;; restore segment registers
   mov   fs,dword ptr [esp].vsf_fs ;; ORDER CRITICAL TO RebuildStackFrame
   mov   es,dword ptr [esp].vsf_es
   mov   ds,dword ptr [esp].vsf_ds
 endif
elseifidni  <mode>,<vpm>
   mov   gs,dword ptr [esp].vsf_gs ;; restore segment registers
   mov   fs,dword ptr [esp].vsf_fs ;; ORDER CRITICAL TO RebuildStackFrame
   mov   es,dword ptr [esp].vsf_es
   mov   ds,dword ptr [esp].vsf_ds
elseifdifi  <mode>,<v86>
   %out  KMExitKmodeVDM - bad exit type parameter: mode
endif

    exk20:
   popad          ;; restore user registers

ifnb  <debug>
 ifdef   PMDEBUG
   movzx esp,sp         ;; just in case
   cmp   [esp].tf_trapnum,EXCPG  ;; if page fault
   jne   short exk30
   testb [esp].tf_eflags,F_TRACE ;; and trace flag is set
   jz short exk30
   mov   [esp].tf_trapnum,EXCSSI ;; make it a trace fault
   JMP16 _Debug_Trap32     ;; stop in debugger
    exk30:
 endif
endif

ifdifi   <exitflag>,<restore>
 ifdifi  <exitflag>,<noreturn>
   add   esp,8       ;; remove trapnum and errcode
   iretd          ;; return to trapped code

    exk40:
   CCALL32 KMExitKmodeEvents,,<noalignesp>
   jmp   short exk10

  ifidni  <mode>,<v86orvpm>
    exk50:
   mov   gs,dword ptr [esp].vsf_gs ;; restore segment registers
   mov   fs,dword ptr [esp].vsf_fs ;; ORDER CRITICAL TO RebuildStackFrame
   mov   es,dword ptr [esp].vsf_es
   mov   ds,dword ptr [esp].vsf_ds
   jmp   exk20
  endif
 endif
endif

   endm

;***EP   KMEnterKmodeTrap - enter kernel mode for protected mode traps
;
;  ENTRY (ESP) = pointer to trap gate stack frame
;
;  EXIT  (DS, ES) = flat context
;     (EBX) = flat relative stack frame base
;     Direction flag cleared
;     Interrupts enabled
;
;  USES  EBX, ESI, DS, ES, Flags
;

KMEnterKmodeTrap macro
   ReferFlat _criTSF,dword,ifundef
   PUSHD ds
   PUSHD es
   PUSHD fs
   PUSHD gs
   pushad
   NullContext <fs,gs>,esi
   kmEnterKmode <offset FLAT:_criTSF>,,nocheck
   endm

;***EP   KMExitKmodeTrap - exit kernel mode for protected mode traps
;
;  ENTRY (DS) = flat context
;     (exitflag) = determines type of exit:
;        "normal" = normal kernel mode exit to protected mode
;
;        "noreturn" = exit kernel mode; service the exit
;        kernel events, but don't return to the trapped code.
;
;        "restore" = just restore the registers; don't service
;        exit kmode events or return to the trapped code
;     (debug) = if not blank, do page fault trace bit check
;
;  EXIT  Return to trapped code (if exitflag != "noreturn" or "restore")
;
;  USES  ALL
;
;  NOTE  The page fault trace bit check is so the debugger regains
;     control after a not present page has been swapped in.

KMExitKmodeTrap macro exitflag,debug
   local exk10,exk15,exk20
if2
ifndef   KMExitKmodeEvents
   extrn KMExitKmodeEvents:near
endif
endif
ifidni   <exitflag>,<noreturn>
   CCALL32  KMExitKmodeEvents,,<noalignesp>
endif
ifdifi   <exitflag>,<restore>
ifdifi   <exitflag>,<noreturn>
   cli            ;; disable interrupts until iretd
   kmCheckEvents
   jnz   short exk20    ;; jump if exit kmode events to service
endif
endif
exk10:   kmExitKmode
ifnb  <debug>
ifdef PMDEBUG
   mov   esi,[_pPTDACur]
   cmp   [esi].ptda_flDbgEnabled,0
endif
endif
   popad          ;; restore user registers
   POPD  gs
   POPD  fs
   POPD  es
   POPD  ds
ifnb  <debug>
ifdef PMDEBUG
   jne   short exk15
   cmp   [esp].tf_trapnum,EXCPG  ;; if page fault
   jne   short exk15
   testb [esp].tf_eflags,F_TRACE ;; and trace flag is set
   jz short exk15
   mov   [esp].tf_trapnum,EXCSSI ;; make it a trace fault
   JMP16 _Debug_Trap32     ;; stop in debugger
exk15:
endif
endif
ifdifi   <exitflag>,<restore>
ifdifi   <exitflag>,<noreturn>
   add   esp,8       ;; remove trapnum and errcode
   iretd          ;; return to trapped code

exk20:   CCALL32  KMExitKmodeEvents,,<noalignesp>
   jmp   short exk10
endif
endif
   endm

;***EP   KMEnterKernelTrap - re-enter the kernel because of a trap
;
;  Re-enter the kernel because of a exception while in kernel mode.
;
;  ENTRY (ESP) = pointer to trap gate stack frame
;
;  EXIT  (DS, ES) = flat context
;     (EBX) = PKSF
;     Direction flag cleared
;     Interrupt flag state preserved
;
;  USES  EBX, ESI, DS, ES, Flags
;

KMEnterKernelTrap macro
   PUSHD ds       ;; save registers
   PUSHD es
   PUSHD fs
   PUSHD gs
   pushad
   NullContext <fs,gs>,esi
   FlatContext <ds,es>,esi    ;; (DS) = (ES) = FLAT
   cld            ;; clear direction flag
   mov   ebx,esp        ;; (EBX) = SS relative base
   SSToDS   ebx         ;; (EBX) = flat relative base
   endm

;***EP   KMEnterKernelTrap_PS - re-enter the kernel because of a trap
;
;  Re-enter the kernel because of a exception while in kernel mode.
;
;  ENTRY (ESP) = pointer to trap gate stack frame
;
;  EXIT  (DS, ES) = flat context
;     (EBX) = PKSF
;     Direction flag cleared
;     Interrupt flag state preserved
;
;  USES  EBX, ESI, DS, ES, Flags
;

KMEnterKernelTrap_PS macro
   PUSHD ds       ;; save registers                               ; whs
   PUSHD es                                                       ; whs
   PUSHD fs                                                       ; whs
   PUSHD gs                                                       ; whs
   pushad                                                         ; whs
   NullContext <fs,gs>,esi                                        ; whs
   FlatContext <ds,es>,esi    ;; (DS) = (ES) = FLAT               ; whs
   cld            ;; clear direction flag                         ; whs
   mov   ebx,esp        ;; (EBX) = SS relative base               ; whs
   SSToDS_PS ebx        ;; (EBX) = flat relative base             ; whs
   endm                                                           ; whs

;***EP   KMExitKernelTrap - exit kernel mode
;
;  Exit the kernel on trap that re-entered the kernel.
;
;  ENTRY (DS) = flat context
;     (exitflag) = determines type of exit:
;        "normal" = normal kernel mode exit to protected mode
;
;        "noreturn" = restore the registers, but don't return
;
;        "restore" = restore the registers, but don't return
;     (debug) = if not blank, do page fault trace bit check
;
;  EXIT  Return to trapped code (if exitflag != "noreturn" or "restore")
;
;  USES  ALL
;
;  NOTE  The page fault trace bit check is so the debugger regains
;     control after a not present page has been swapped in.

KMExitKernelTrap macro exitflag,debug
   local exk10
   popad          ;; restore user registers
   POPD  gs
   POPD  fs
   POPD  es
   POPD  ds
ifnb  <debug>
ifdef PMDEBUG
   cmp   [esp].tf_trapnum,EXCPG  ;; if page fault
   jne   short exk10
   testb [esp].tf_eflags,F_TRACE ;; and trace flag is set
   jz short exk10
   mov   [esp].tf_trapnum,EXCSSI ;; make it a trace fault
   JMP16 _Debug_Trap32     ;; stop in debugger
exk10:
endif
endif
ifdifi   <exitflag>,<restore>
ifdifi   <exitflag>,<noreturn>
   add   esp,8       ;; remove trapnum and errcode
   iretd          ;; return to trapped code
endif
endif
   endm

;***EP   KMEnterKmodeAPI32 - Enter kernal mode for 32-bit APIs
;
;  ENTRY (ESP)   = pointer to call gate stack frame
;     (cbargs)  = number of callgate arguments (in bytes)
;     (pascal)  = if "PASCAL", use pascal call-convention
;     (nothunk) = if blank, there is a thunk address
;
;  EXIT  Direction flag cleared
;     Interrupts enabled
;
;  USES  ESI, Flags
;

KMEnterKmodeAPI32 macro cbargs,pascal,nothunk
    .386p
ifnb <pascal>
ifdif <pascal>,<PASCAL>
   %out KMEnterKmodeAPI32 - the PASCAL keyword must be upper case
   .err
endif
ifb <nothunk>
   %out KMEnterKmodeAPI32 - can't have PASCAL without NOTHUNK
   .err
endif
endif
   pushfd
   PUSHD <cbargs>
ifb <nothunk>
if2
ifndef   KMEnterKmodeThunk&pascal&SEF
   extrn KMEnterKmodeThunk&pascal&SEF:near
endif
endif
   CCALL32  KMEnterKmodeThunk&pascal&SEF,,<nocheck,nocall,noalignesp>
else
if2
ifndef   KMEnterKmode&pascal&SEF
   extrn KMEnterKmode&pascal&SEF:near
endif
endif
   CCALL32  KMEnterKmode&pascal&SEF,,<nocheck,nocall,noalignesp>
endif
    CpuMode reset
   endm

;***EP   KMExitKmodeAPI32 - Exit kernel mode for 32-bit APIs
;
;  ENTRY (ESP) = SEF stack frame
;     (EAX) = client's EAX
;     (cbargs) = number of callgate arguments (in bytes)
;     (pascal) = if "PASCAL", use pascal call-convention
;     (nothunk) = if blank, there is a thunk address
;
;  EXIT  Doesn't return
;

KMExitKmodeAPI32 macro  cbargs,pascal,nothunk
    .386p
ifnb <pascal>
ifdif <pascal>,<PASCAL>
   %out KMExitKmodeAPI32 - the PASCAL keyword must be upper case
   .err
endif
ifb <nothunk>
   %out KMExitKmodeAPI32 - can't have PASCAL without NOTHUNK
   .err
endif
endif
   ?JumpSEFExit %&((cbargs+3) AND (NOT 3)),pascal
    CpuMode reset
   endm

;***LP   ?JumpSEFExit - jump to SEF stack frame kernel exit code
;
;  Jump to appropriate call gate return routine according
;  to the argument bytes
;
;  ENTRY (cbargs) = number of callgate arguments (in bytes)
;     (pascal) = if "PASCAL", use pascal call-convention
;
;  EXIT  Doesn't return
;

?JumpSEFExit macro cbargs,pascal
    .386p
if2
ifndef   KMExitKmode&pascal&SEF&cbargs
   EXTRN32  KMExitKmode&pascal&SEF&cbargs
endif
endif
if (?cstype eq ?CS_32bit)
   jmp   KMExitKmode&pascal&SEF&cbargs
else
   JMP32 KMExitKmode&pascal&SEF&cbargs
endif
endm

;***EP   KMEnterKmodeCallGate - enter kernel mode for call gate frames
;
;  ENTRY (ESP) = pointer to call gate stack frame
;     (cbargs) = number of callgate arguments (in bytes)
;     (enterflag) = if "nosti", interrupts are not enabled
;              if "noinline", the KM routines are called
;     (pascal) = if "PASCAL", _criPASCALSEF is used.
;
;  EXIT  (DS, ES) = flat context
;     (EBX) = flat relative stack frame base
;     Direction flag cleared
;     Interrupts enabled
;
;  USES  EBX, ESI, DS, ES, Flags
;

KMEnterKmodeCallGate macro cbargs,enterflag,pascal
   ReferFlat _cri&pascal&SEF,dword,ifundef
    .386p
ifidni   <enterflag>,<noinline>
   KMEnterKmodeAPI32 <cbargs>,<pascal>,nothunk
else
   pushfd
   PUSHD <cbargs>
   PUSHD 0     ;; dummy sef_retaddr
   PUSHD ds
   PUSHD es
   PUSHD fs
   PUSHD gs
   pushad
   NullContext <fs,gs>,esi
   kmEnterKmode <offset FLAT:_cri&pascal&SEF>,<enterflag>
endif
    CpuMode reset
   endm

;***EP   KMExitKmodeCallGate - exit kernel mode for call gate stack frames
;
;  ENTRY (DS) = flat context
;     (EAX) = client's EAX
;     (cbargs) = number of callgate arguments (in bytes)
;     (enterflag) = if "noretval", don't set the user's EAX
;              if "noinline", the KM routines are called
;     (pascal) = if "PASCAL", _criPASCALSEF is used.
;     (nocheck) = if not blank, the ring 2 stack check is skipped
;
;  EXIT  Return to user code
;
;  USES  ALL
;

KMExitKmodeCallGate macro cbargs,enterflag,pascal,nocheck
   local exk10,exk20,exk30,exk40
   .386p
ifidni   <enterflag>,<noinline>
   KMExitKmodeAPI32 <cbargs>,<pascal>,nothunk
else
if2
ifndef   KMExitKmodeEvents
   extrn KMExitKmodeEvents:near
endif
endif
ifdifi   <enterflag>,<noretval>
   mov   [esp].sef_eax,eax ;; save return code
endif
   kmCheckEvents
   jnz   short exk20    ;; jump if exit kmode events to service

exk10:   kmExitKmode

;; If there are arguments, and this is not a pascal call, then adjust
;; the user ESP for C32.  This adjustment emulates a RETF 0 to ring 3,
;; while still performing a RETF N to pop the kernel stack's arguments.
;; kmRebuildStackFrame will add cbargs back if we have a problem.
;; So, the net effect is to return to the user without changing ESP.

if cbargs
ifb <pascal>
if (cbargs AND 3)
   %out Argument count not dword aligned for a 32-bit C call.
   .err
endif
   sub   dword ptr [esp]+SEF_SIZE+cbargs,cbargs
endif
endif
ifb   <nocheck>
   kmChkStack1 exk40    ;; make stack present, if Ring 2
exk30:               ;; return label from ring 2 check
endif
   popad          ;; restore user registers
   POPD  gs
   POPD  fs
   POPD  es
   POPD  ds
   add   esp,8       ;; pop cbargs/retaddr from stack.
   popfd          ;; popfd and return must be together
   RETFD <(cbargs+3) AND (NOT 3)>;;  for single steping code

exk20:   CCALL32  KMExitKmodeEvents,,<noalignesp>
   jmp   exk10

ifb   <nocheck>
exk40:   kmChkStack2 exk30    ;; make stack present, if Ring 2
endif
endif
    CpuMode reset
   endm

;***EP   KMEnterKmodeSCI - enter kernel mode for sci
;
;  ENTRY (ESP) = pointer to SCI stack frame
;
;  EXIT  (DS, ES) = flat context
;        NOTE: When this macro is invoked by sci, ES will
;              only be set equal to flat context if 32 bit
;              code needed to be called.
;        (EBX) = flat relative stack frame base
;        Direction flag cleared
;        Interrupts enabled
;
;  USES  EBX, ESI, DS, ES, Flags
;
;  NOTE  The sysentry/call gate entry code must be:
;
;        pushfd
;        push  offset SCIReturnX
;        push  cbargs
;        call  sci      ;; 16 bit near call

KMEnterKmodeSCI macro
   local   eksci
   ReferFlat _criSSF,dword,ifundef
    .386p
   PUSHD ds
   PUSHD es
   PUSHD fs
   PUSHD gs
   pushad
   mov     bx,fs                 ;; probability is good that fs & gs are null
   mov     si,gs
   or      si,bx
   jz      short eksci
   NullContext <fs,gs>,esi
eksci:
   kmEnterKmode <offset FLAT:_criSSF>
    CpuMode reset
   endm

;***EP   KMExitKmodeSCI - exit kernel mode for call gate stack frames
;
;  ENTRY NONE
;
;  EXIT  Return to call gate entry code
;
;  USES  ALL
;

KMExitKmodeSCI macro
   local exk10,exk20,exk30,exk40
    .386p
if2
ifndef   KMExitKmodeEvents
   extrn KMExitKmodeEvents:near
endif
endif
   ifdef ?sci_ds
      if ?sci_ds eq ?SCI_UNKNOWN
         FlatContext <ds>,si
      endif
      ?sci_ds = ?SCI_FLAT
      ?sci_es = ?SCI_UNKNOWN
   else
      FlatContext <ds,es>,si
   endif
   kmCheckEvents
   jnz   short exk20    ;; jump if exit kmode events to service
exk10:   kmExitKmode
   kmChkStack1 <short exk40>    ;; make stack present, if Ring 2
exk30:
   popad          ;; restore user registers
   POPD  gs
   POPD  fs
   POPD  es
   POPD  ds
   add   esp,(size ssf_thopadr + size ssf_cbargs)
   ret

exk20:
   ifdef ?sci_es
      FlatContext <es>
      ?sci_es = ?SCI_FLAT
   endif
   CCALL32  KMExitKmodeEvents,,<noalignesp>
   jmp   exk10
exk40:
   kmChkStack2 exk30
   CpuMode reset
   endm

;***EP   KMEnterKmodeDH - enter kernel mode for device help
;
;  ENTRY (ESP) = pointer to device help stack frame
;
;  EXIT  NONE
;
;  USES  NONE

KMEnterKmodeDH macro
   ReferFlat _criDHF,dword,ifundef
    .386p
   pushfd
   PUSHD ds
   PUSHD es
   PUSHD fs
   PUSHD gs
   pushad
   NullContext <fs,gs>,esi
   kmEnterKmode <offset FLAT:_criDHF>,,nocheck

;  Restore register trashed by kmEnterKmode (ESI, EBX, DS, ES, Flags)

   mov   es,[ebx].dhf_es
   mov   esi,[ebx].dhf_esi
   push  [ebx].dhf_eflag
   push  [ebx].dhf_ebx
   push  [ebx].dhf_ds
   pop   ds
   pop   ebx
   popfd
    CpuMode reset
   endm

;***EP   KMExitKmodeDH - exit kernel mode for device help frame
;
;  ENTRY NONE
;
;  EXIT  All Registers are preserved
;
;  USES  NONE

KMExitKmodeDH macro
   local exk10
    .386p
if2
ifndef   KMExitKmodeEvents
   extrn KMExitKmodeEvents:near
endif
endif
   pushfd
   pop   [esp].dhf_eflag
   mov   [esp].dhf_es,es
   mov   [esp].dhf_ds,ds
   mov   [esp].dhf_eax,eax
   mov   [esp].dhf_ebx,ebx
   mov   [esp].dhf_ecx,ecx
   mov   [esp].dhf_edx,edx
   mov   [esp].dhf_edi,edi
   mov   [esp].dhf_esi,esi
   FlatContext <ds,es>,si
   kmCheckEvents
   jz short exk10    ;; jump if no exit kmode events
   CCALL32  KMExitKmodeEvents,,<noalignesp>
exk10:   kmExitKmode
   popad          ;; restore user registers
   POPD  gs
   POPD  fs
   POPD  es
   POPD  ds
   popfd
    CpuMode reset
   endm

;***EP   KMEnterKmodeRebuild - enter kernel mode for stack rebuild
;
;  This macro enters kernel mode after the stack frame has been
;  rebuilt when a fault occurs at ring 0 with InDos == 0 (while
;  exiting the kernel).
;
;  ENTRY (DS, ES) = flat context
;     (EBX) = flat relative stack frame base
;     Direction flag cleared
;     Interrupts disabled
;
;  EXIT  Interrupts enabled
;
;  USES  ESI, Flags
;

KMEnterKmodeRebuild macro
   ReferFlat _cInDos,byte,ifundef
   ReferFlat _pTCBCur,dword,ifundef
    .386p
   mov   byte ptr [_cInDos],1 ;; in kernel mode now
   mov   esi,[_pTCBCur]    ;; (ESI) = current TCB pointer
   mov   [esi].TCBMiscFlags,0 ;; clear miscellaneous flags
   sti            ;; enable interrupts
    CpuMode reset
   endm

;***LP   kmEnterKmode - common enter kernel mode code
;
;  ENTRY (sftype) = stack frame type PCRI
;     (enterflag) = if "nosti", don't do sti at the end of macro
;     (checkflag) = if blank, do page fault CLI check
;
;  EXIT  (EBX) = flat relative stack frame base pointer
;     (DS,ES) = flat context
;     Direction flag cleared
;     Interrupts enabled
;     [InDos] non-zero
;     [TCBMiscFlags] cleared
;
;  USES  EBX, ESI, DS, ES, Flags
;

kmEnterKmode   macro sftype,enterflag,checkflag
   local enk10
   ReferFlat _cInDos,byte,ifundef
   ReferFlat _pTCBCur,dword,ifundef
    .386p
   cli            ;; disable ints until done
   cld            ;; clear the direction flag
   ifdef ?sci_ds
      FlatContext <ds>,esi
      ?sci_ds = ?SCI_FLAT
      ?sci_es = ?SCI_UNKNOWN
   else
      FlatContext <ds,es>,esi
   endif
   mov   ebx,esp        ;; (EBX) = SS relative base
   SSToDS   ebx         ;; (EBX) = flat relative base
   mov   esi,[_pTCBCur]    ;; (ESI) = current TCB pointer
   mov   [esi].TCBMiscFlags,0 ;; clear miscellaneous flags
   mov   [esi].TCB_pcriFrameType,sftype
   mov   [esi].TCB_pFrameBase,ebx
   mov   byte ptr [_cInDos],1 ;; in kernel mode now
ifb   <checkflag>
   kmCheckEntry         ;; check entry actions
endif
ifdifi   <enterflag>,<nosti>
   sti            ;; enable interrupts
endif
   endm

;***LP   kmExitKmode - common exit kernel mode code
;
;  ENTRY (DS) = flat context
;
;  EXIT  [InDos] cleared
;
;  USES  EAX, ECX, ESI, EDI, ES
;

kmExitKmode macro
   ReferFlat _cInDos,byte,ifundef
ifdef INFOSTRICT
   ifndef   _PGVerifyInfoSegs
      extrn _PGVerifyInfoSegs:near
   endif
   ifdef ?sci_es
      FlatContext <es>
      ?sci_es = ?SCI_FLAT
   endif
   CCALL32  _PGVerifyInfoSegs,,<nofar32,noalignesp> ;; verify all copies are in sync
endif
    .386p
   mov   byte ptr [_cInDos],0 ;; not in kernel mode anymore
   endm

;***LP   kmCheckEvents - check for force flag and global events
;
;  ENTRY (DS) = flat context
;
;  EXIT  Z, no exit kmode events
;     NZ, exit kmode events
;
;  USES  EAX, ESI, Flags

kmCheckEvents macro
   ReferFlat _flGlobalActions,dword,ifundef
   ReferFlat _pTCBCur,dword,ifundef
   ReferFlat _pPTDACur,dword,ifundef
    .386p
   mov   eax,[_flGlobalActions]  ;; start with global flag
   mov   esi,[_pTCBCur]    ;; (ESI) = current TCB pointer
   or eax,[esi].TCBForcedActions ;; check thread force flags
   mov   esi,[_pPTDACur]      ;; (ESI) = current PTDA pointer
   or eax,[esi].ptda_ForcedActions  ;; check process force flags
    CpuMode reset
   endm

;***LP   kmChkStack1 - Make user stack page present
;
;  ENTRY (DS) = flat context
;
;  EXIT  Jumps to label that touches stack page if the return
;     stack is ring 2
;
;  USES  EAX

kmChkStack1 macro checklabel
ifndef   _pTSDCur
   ReferFlat _pTSDCur,DWORD
endif
    .386p
   mov   eax,[_pTSDCur]
   test  byte ptr [eax].TSDUserSS,1 ;; ring 2 ?
   jz checklabel        ;; yes, go check stack
   endm

;***LP   kmChkStack2 - Make user stack page present
;
;  ENTRY (DS) = flat context
;     (EAX) = _pTSDCur
;
;  EXIT  None, user stack page touched
;
;  NOTE: This macro assumes that it is called before ANYTHING (including
;     cbargs) is popped off the stack.  RebuildStackFrame in trap.asm
;     assumes this. It passes KM_REG_TOP to KMRebuildStackFrame.
;
;  USES  EAX,EDX

kmChkStack2 macro jmpback
   local kcs10
    .386p
   mov   edx,[eax].TSDUserESP    ;; edx = stack offset
   test  byte ptr [eax].TSDUserSS,SEL_LDTBIT ;; GDT?
   jz short kcs10           ;;   yes, segment base is 0

   movzx edx,dx            ;; zero high-half of ESP!
   movzx eax,[eax].TSDUserSS     ;; [EAX] = user SS

   shl   eax,SEL_LADDR_SHIFT     ;; CRMA  edx,eax
   and   eax,SEL_LADDR_MASK
   add   edx,eax
kcs10:   mov   al,byte ptr [edx-1]     ;; touch stack page
   jmp   short jmpback        ;; jump back inline
   endm

;***LP   kmCheckEntry - check for (and perform) kernel entry actions
;
;  ENTRY (ESI) = pTCBCur
;     (DS,ES) = Flat context
;
;  USES  Flags

kmCheckEntry   macro
   local lpf10
if (?cstype eq ?CS_32bit)
if2
ifndef   KMEnterKmodeEvents
   EXTRN32  KMEnterKmodeEvents
endif
endif
else
if2
ifndef   g_KMEnterKmodeEvents
   EXTRNF32 KMEnterKmodeEvents
endif
endif
endif
   cmp   [esi].TCBEntryActions,0
   jz short lpf10
   ifdef ?sci_es
      if ?sci_es eq ?SCI_UNKNOWN
         FlatContext <es>
         ?sci_es = ?SCI_FLAT
      endif
   endif
   CCALL32  KMEnterKmodeEvents,,<noalignesp>
lpf10:
   endm

ifndef   INCL_NOREFERS
   ReferFlat _flGlobalActions,dword
   ReferFlat _pPTDACur,dword
   ReferFlat _pTCBCur,dword
   ReferFlat _cInDos,byte
   ReferFlat _pvsfFrameBase,dword
   if2
       EXTRNF32 KMExitKmodeEvents
   endif
endif
*/

/***  KM component function prototypes
 */

/* XLATOFF */
INT  PASCAL KMAllocContextHook(PFN, ULONG, PTCB, PHOOK *);
INT  PASCAL KMAllocHook(PFN, BYTE, BYTE, ULONG, PTCB, PHOOK *);
VOID PASCAL KMFreeHook(PHOOK);
BOOL PASCAL KMArmHook(PHOOK, PHOOKHEAD);
BOOL PASCAL KMDisarmHook(PHOOK);
BOOL PASCAL KMArmContextHook(PHOOK, PTCB);
VOID PASCAL KMTerminateThread(VOID);
VOID PASCAL KMRebuildStackFrame(ULONG, PKSF, ULONG);
INT  PASCAL KMQuerySSESPLaddr(PTCB, PVOID *);
INT  PASCAL KMQueryCSEIPLaddr(PTCB, PVOID *);
INT  PASCAL KMQueryAllClientRegisters(PTCB, PKMREG);
INT  PASCAL KMSetAllClientRegisters(PTCB, PKMREG);
INT  PASCAL KMQueryClientRegister(PTCB, ULONG, PULONG);
INT  PASCAL KMSetClientRegister(PTCB, ULONG, ULONG);
INT  PASCAL KMPushClientStack(PTCB, ULONG, PVOID, ULONG);
INT  PASCAL KMPopClientStack(PTCB, ULONG, PVOID, ULONG);
INT  PASCAL KMQueryReturnESPLaddr(PTCB, PVOID *);
INT  PASCAL KMQueryClientReturnESP(PTCB, PULONG);
INT  PASCAL KMSetClientReturnESP(PTCB, ULONG);
INT  PASCAL KMSetFlatDispatch(PTCB, PFN);
/* XLATON */
