
#define SCCSID "@(#)em86vpm.c   6.11 92/04/24"

/***    EM86VPM.C
 *
 *       Version 1.21.1.2, for src/dos/mvdm/em86vpm.c, em86, mer.krnl, 960610.1
 *
 *      IBM/Microsoft Confidential
 *
 *      Copyright (c) IBM Corporation 1987, 1989
 *      Copyright (c) Microsoft Corporation 1987, 1989
 *
 *      All Rights Reserved
 *
 *      Title:
 *
 *      DOS Protected mode VDM support
 *
 *      Description:
 *
 *      This file contains the routines to support start and ending
 *      maintaining DOS protected mode execution.
 *
 *      IDT design:
 *
 *       OS/2 Protected
 *        or V86 Mode
 *      +-------------+  0
 *      |             |
 *      |  Trap       |  Exceptions
 *      |  Handlers   |
 *      |             |  1f
 *      +-------------+  20
 *      |             |
 *      |  Invalid    |  Software ints
 *      |             |
 *      |             |  4f
 *      +-------------+  50
 *      |  Hardware   |
 *      |  Interrupt  |  Hardware ints
 *      |  Handlers   |
 *      |             |
 *      +-------------+  7f
 *
 *      The OS/2 protected mode/VDM V86 mode IDT is allocated
 *      staticly.  The trap entries are runtime initialized (before
 *      going into protected mode) because flat selector/offset can
 *      not be staticly initialized in a IDT descriptor entry.  The
 *      hardware interrupt handlers are initialized by interrupt
 *      manager.  These will be the same in all IDTs.
 *
 *       VDM Protected
 *            Mode
 *      +-------------+  0      IDT_START_EXCEPTIONS
 *      |             |
 *      |  Trap       |  Exceptions
 *      |  Handlers   |
 *      |             |  1f     IDT_END_EXCEPTIONS
 *      +-------------+  20     IDT_START_SOFTWARE_INTS
 *      |             |
 *      |  DPMI App   |  Software ints
 *      |  Handlers   |
 *      |             |  4f     IDT_END_SOFTWARE_INTS
 *      +-------------+  50     IDT_START_HARDWARE_INTS
 *      |  Hardware   |
 *      |  Interrupt  |  Hardware ints
 *      |  Handlers   |
 *      |             |  7f     IDT_END_HARDWARE_INTS
 *      +-------------+  80     IDT_START_VPM_SOFTWARE_INTS
 *      |             |
 *      |  DPMI App   |  DOS protected mode software ints
 *      |  Handlers   |
 *      |             |
 *      +-------------+  ff     IDT_END_VPM_SOFTWARE_INTS
 *
 *      The VDM protected mode IDT is allocated when a VDM first
 *      enters protected on a per VDM basis and is freed when the
 *      the last protected mode app in a VDM terminates.  The
 *      exception and the hardware interrupt entries are copied
 *      from the OS/2 protected mode IDT.  The software interrupt
 *      and DOS protected mode software ints are initialized to
 *      point to v86 mode reflection handlers.  When a DPMI app
 *      installs a software interrupt handler these entries are
 *      modified directly.  A shadow table is maintained and
 *      referenced to route the GP faults on the INT instructions
 *      in the exception and hardware interrupt regions.
 *
 *      Public routines:
 *
 *      EM86StartVPMExecution
 *      vpmInitData
 *      em86VPMDestroyVDM
 *      em86EndVPMExecution
 *      VDHRegisterDPMI
 *
 *      Local routines:
 *
 *      int2fGetVPMSwitchAddr
 *      vpmFreeTaskResources
 *      em86AllocVPM
 *      em86UpdateIDT
 *      em86FreeVPM
 *      em86AllocLDTIDT
 *      em86FreeLDTIDT
 *      em86AllocSel
 *      em86FreeSel
 *
 *      Modification History
 *
 *    - Bx001   Bill Madden, IBM        08/23/91
 *      CP20 PTR B726179  Changed vpmInitData to initialize variables for
 *                        when a second app goes into VPM mode.
 *    - Bx002   Bill Madden, IBM        10/18/91
 *      CP20 PTR B728103  Added EM86GetPSP to get the app's PSP before entering
 *                        protect mode, instead of using pPDTACur.
 *    - Bx003   Bill Madden, IBM        11/14/91
 *      CP20 PTR B729855  Changed em86StartVPMExecution so that DPX works with
 *                        DOS owns UMBs.
 *    - Bx004   Bill Madden, IBM        12/18/91
 *      CP20 PTR B730951  Changed em86AllocSel and em86FreeSel to use LDT sels
 *                        so DPMI exception handlers have a ring 3 stack.
 *    - Bx005   Bill Madden, IBM        09/28/92
 *      CP20 FEA 53758    Added multiple LDT/IDT support for DPMI apps.
 *    - Bx006   Bill Madden, IBM        11/10/92
 *      CP20 DEF 56890    Modified CTRL-C handling for DPMI apps.
 *    - Bx007   Bill Madden, IBM        01/14/93
 *      CP21 DEF 59705    Cleaned up DPMI termination code
 *    - Bx008  Ken Luo    03/19/93    DEF 63248
 *      Added Dekko Hooks
 *    - Bx009  Lisa Primm     04/21/93    DEF 64131
 *      Fixed problems in multiple DPMI client support.
 *    - Bx010  Bill Madden    07/01/93    DEF 71398
 *      Restore default VDM LDT after last DPMI client exits.
 *    - Bx011  Change Team    08/23/93    Def 73188
 *      Changed SelSwitchVdmLDT to accept additional paramater
 *    - Bx012  Change Team    11/01/93    Def 72717
 *      Changed to reflect int23 to real mode if we don't have it hooked.
 *    - 81988  Rob Rose       06/07/94    Def 81988
 *      Added check for NULL hhookGetPSPRetHook to avoid excessive hook allocs.
 *      (Change team fix brought over because of potential memory savings)
 *    - WIN32  Liren Ji       06/16/94    Def 86206
 *      Added VDHInstallFaultHandler
 *    -        bjn            09/19/94
 *      Changed segment names for page tuning.
 *
 *    -     Gordon Kuo        03/06/96       149007
 *      Add the initialization stuff for VDPMI 1.0 subset
 *
 *    -     John M. Hare      07/10/96       158244
 *      Fix 686 limitation from 149007 implementation
 *      Added default of 386 processor
 */
#define FNO_NUM  FNO_EM86VPM_C
#include <fnomvdm.h>

#define INCL_NONE               // for mvdm.h
#define INCL_TYPES              // include base types
#define INCL_MVDMTYPES          // include mvdm types
#define INCL_VDHALL             // include VDH services

#include <vdmvdh.h>
#include <seldesc.h>
#include <sft.h>
#include <kmode.h>
#include <em86.h>
#include <vpm.h>
#include <vpmxport.h>
#include <dpmi.h>
#include <pdb.h>
#include <int2f.h>

#ifdef INCDEBUG
#include <dbgex.h>
#endif /* DEBUG */

#ifdef MMIOPH
   #include <mmph.h>
#endif

// BUGBUG- wayneca 7/31/90
/***   Things wrong:
    1. hw IF disabled, but isn't changing when vpic changes location
       of vectors
    2. 60h-6fh is treated as hw interrupt, shouldn't be
    3. 1ch, 23h, 24h should be reflected to protect mode from V86 if
       hooked
    4. mikem thinks he set up 0-7 wrong
    5. only 0-7 should have IF disabled for software ints in protect
       mode
    6. vnpx reflecting exception 7h needs new service
***/

// BUGBUG- wayneca 7/31/90 - should contain only startup/end code.
// code for interrupt simulation should be moved.

/* External and misc definitions */

extern INT em86PGMoveStub(ULONG,PVOID,ULONG,PVOID,INT,USHORT);
extern INT em86SelDestroyVdmLDT(PVOID);
extern INT em86PGGetAccessStub(ULONG,PVOID,INT,INT,PCHAR);
extern INT em86DPMITaskSwitch(PVOID);
extern struct gate PASCAL IDT[];
extern PVSF pvsfFrameBase;
extern VPVOID vpVdmBpArpl;
extern ULONG CpuType;
extern UCHAR flrestart;
extern FNHOOK VPMIntDispatcher;
extern FNARM EM86Int23Hook;
extern FNARM em86VPMReflectionHook;
extern FNARM EM86VPMExcRetHook;
extern FNARM EM86VPMExpExcRetHook;    /* 149007 */
extern FNARM EM86V86ExpExcRetHook;    /* 149007 */
extern FNARM EM86V86ExpExc9RetHook;   /* 149007 */
extern FNARM EM86BacktoV86RetHook;    /* 149007 */
extern BOOL em86VPMNpxSet(ULONG);
extern BOOL em86VPMNpxStatus(PULONG);
extern VOID PASCAL VDMMVPMBeginEnd(ULONG);                              //BN005
extern VOID PASCAL em86VPMInt2FHdlr(VOID);
extern BOOL VPMPgMapPhys(VMAC *, ULONG, ULONG);


/* Internal routine prototypes */

// em86 routines
VOID em86UpdateIDT(ULONG);
INT  em86AllocVPM(VOID);
VOID em86FreeVPM(VOID);
BOOL em86AllocSel(ULONG, ULONG, PSEL, BYTE, BYTE);
VOID em86FreeSel(SEL);
PDHD PASCAL vpmFreeTaskResources(VOID);
INT  PASCAL vpmInitData(PVSF);
VOID PASCAL EM86GetPSP(PVOID, PVSF);                                    //BN002
INT  em86AllocLDTIDT(VOID);                                             //BN005
VOID em86FreeLDTIDT(VOID);                                              //BN005



#pragma BEGIN_MVDM_SWAP_DATA

/***    fVpmInitOK - DPM VDD initialization status
 *
 *      True if DPM VDD registered successfully.
 */
BOOL fVpmInitOK = FALSE;      // DPM VDD registered successfully

/***    DPM VDD entry points
 *
 *      Set up by VDHRegisterDPMI.  If registration did not
 *      succeed, DOS apps will never receive protect mode entry
 *      points and so the code that uses these functions is never
 *      called.
 *
 *          INT 31h router
 *          DPMI task creation
 *          DPMI task termination
 */
PFNDPMRTE   pfndpmInt31Rte   = NULL;
PFNDPMSTART pfndpmCreateTask = NULL;
PFNDPMEND   pfndpmEndTask    = NULL;
PFNDPMQUERY pfndpmQuery      = NULL;  /* 149007 */
            /* ULONG (PASCAL *PFNDPMQUERY)(ULONG,PULONG) */


#pragma END_MVDM_SWAP_DATA


#pragma BEGIN_MVDM_SWAP_INSTANCE

/***    flVdmStatus - VDM status flags
 *
 *      This variable contains various VDM status flags.  There
 *      are flags indicating the switching into protected mode
 *      was enabled (dpmi was initialized), what bitness (16 or 32)
 *      the protected mode execution is and what the current execution
 *      mode is (V86 or protected).
 */

ULONG   flVdmStatus = 0;

/***    pdhdHostData - DPMI host data pointer
 *
 *      The pdhdHostData variable contains the pointer to the mvdm
 *      per task data.  This structure is shared with the DPMI
 *      API VDD.  In the structure, both the kernel and the VDD
 *      have pointers to private per task data.  Multiple DPMI
 *      tasks can run in the same VDM.  Each is linked into
 *      a list of mvdm task data structures.
 */

PDHD pdhdHostData = NULL;
PDHD pdhdHostLast = NULL;

/***    pvpmData - EM86 current DPMI task data pointer
 *
 *      The pvpmData variable contains the pointer to the em86 per DPMI task
 *      data.  This structure is not shared with the DPMI API VDD.  This is
 *      "shortcut" way for EM86 to get the current data block.
 */

PVPMD pvpmData = NULL;

/***    flVpmIRQHooks - Hooked protect mode IRQ's
 *
 *      This variable contains bit flags that indicate which IRQ's
 *      are hooked by the application in protect mode.
 *      The low byte corresponds to the master and the second
 *      byte the slave.
 */

ULONG   flVpmIRQHooks = 0;

/***    cVPMExecution - Count of the number of start PM executions
 *
 *      This is the count of the number of times a protected mode
 *      program has started.  It is incremented when an application
 *      calls the start protected mode execution function.  It is
 *      decremented every time a protected mode app terminates.
 */

ULONG   cVPMExecution = 0;

/***    cVPMHWInt - VDM protected hardware interrupt count
 *
 *      This is the count of begin hardware interrupts for the VDM.
 */
ULONG   cVPMHWInt = 0;

/***    lidtV86IDT - limit/base of VDM v86 mode IDT
 *
 *      This is the limit/base of the VDM V86 mode IDT in the
 *      lidt/sidt instruction format.
 */

struct lidt_s lidtV86IDT;

/***    hhookV86ReflectionHook - V86 reflection call back return hook
 *
 *      This return hook is used to regain control after switching
 *      to V86 mode on a protected mode interrupt.
 */

HHOOK   hhookV86ReflectionHook = NULL;
HHOOK   hhookVPMReflectionHook = NULL;
HHOOK   hhookVPMExcRetHook = NULL;
HHOOK   hhookVPMExpExcRetHook  = NULL;  /* GK Hook for Expanded stack return */
                                        /* 149007 */
HHOOK   hhookV86ExpExcRetHook  = NULL;  /* GK Hook V86 Expanded stack return */
                                        /* 149007 */
HHOOK   hhookV86ExpExc9RetHook = NULL;  /* GK Hook V86 Expanded stack return */
                                        /* 149007 */
HHOOK   hhookBacktoV86RetHook  = NULL;  /* GK Hook V86 int 1c,23,24 to VPM n */
                                        /* 149007 */
HHOOK   hhookGetPSPRetHook = NULL;                                      //BN003
HHOOK   hhookV86Int23Hook = NULL;                                       //BN006

/***    pVPMBP - VDM protected mode breakpoint table address
 ***    selVPMBP - VDM protected mode breakpoint selector (CS)
 *
 *      These variables point to the ring 3 read-only table of
 *      HLT instructions used has breakpoint address to regain
 *      control in VDM protected mode.  Each DPMI task in a VDM
 *      will use the same LDT selector.
 */

PVOID pVPMBP = NULL;
SEL selVPMBP = -1;

/***    papfnVPMBP - pointer to VDM protected mode breakpoint functions
 *
 *      This is a pointer to the array of VPM BP function pointers indexed
 *      by the offset value of the VPM BP address.
 */

VOID (PASCAL *(*papfnVPMBP)[VPM_NUM_BPS])(VOID) = NULL;

/***    vpVPMSwitchAddr - breakpoint to cause start of protect mode
 *
 *      This breakpoint is given to the client through an int 2fh
 *      interface.  When executed, a new dpmi execution begins.
 */

VPVOID  vpVPMSwitchAddr = 0;

/***    vpV86Int23vect - initial V86 Int 23 vector
 *
 *      This is the initial int 23 vector from the IVT so that we can chain to
 *      the original handler.
 */

VPVOID  vpV86Int23vect = 0;                                             //BN006

/***    vpOurV86Int23vect - V86 Int 23 vector set entering protect mode apps.
 *
 *      This is the initial int 23 vector that we set, so later we can check
 *      (somewhat) to see if command.com is hooking it. (em86int.asm) DEF 72717
 */

VPVOID  vpOurV86Int23vect = 0;                                          //72717

/***     usEIPincr - increment amount used when emulating an instruction
 *
 *       This value is used when we have to reissue an instruction that
 *       caused a trap while examining a user buffer.
 */

USHORT   usEIPincr = 0;
ULONG    ulVSFEAX;
ULONG    ulVSFEBX;

/***     V86SelSWInfo - default LDT info for VDM                        //BN010
 *
 *       This value is used when we have to restore the LDT to it's original
 *       value when the last DPMI task exits.
 */
SELSWINFO V86SelSWInfo;                                                 //BN010

/***     pFaultHandler - VDD registered ring-0 fault handler                     //BN010
 *
 *       This value is used to call a VDD fault handler
 *       when an exception occurs.
 */
PVOID    pFaultHandler = NULL;          /* 149007 */                  //WIN32

#pragma END_MVDM_SWAP_INSTANCE

#pragma BEGIN_MVDM_SWAP_CODE


/*** High level DPMI control routines -- ***/

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_26","CODE")

/***EP  EM86StartVPMExecution - start DOS protected mode execution
 *
 *      This routine gains control when the vpVPMSwitchAddr is called
 *      in V86 mode.  The VDM is initialized for protected mode operation
 *      and if successful, switches the VDM into protected mode.
 *
 *      ENTRY   (pvsf) = pointer to client register stack frame
 *              (p) = reference data (unused)
 *              VSF(AX) = flags
 *                  bit 0 = 1 if the program is 32-bit application
 *              VSF(ES) = real mode segment to DPMI host data area
 *
 *      EXIT    if VSF's carry clear,
 *                  program is now executing in protected mode
 *                  VSF(CS) = selector with base of V86 mode CS and a 64K limit
 *                  VSF(SS) = selector with base of V86 mode SS and a 64K limit
 *                  VSF(DS) = selector with base of V86 mode DS and a 64K limit
 *                  VSF(ES) = selector to program's PSP with a 100h byte limit
 *                  VSF(FS) and VSF(GS) = 0
 *                  if the program is a 32 bit app, the high word of ESP is 0
 *                  all other registers preserved
 *              if VSF's carry set,
 *                  mode switch failed
 *                  program is still executing in V86 mode
 *
 *      NOTE    The return address put on the stack by the calling
 *              V86 code has already been popped off into VSF(CS:IP)
 *              by the VDM BP dispatch code.
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *      allocate and initialize the mvdm task data
 *      save segment pdb and env values in mvdmtask data
 *      initialize the V86 stack supplied by client
 *      link mvdm task data into task list for this VDM
 *      use vpmInitData to complete em86 setup
 *      call VDD for its task creation
 *         (VDD sets up client protect mode selectors)
 *      fill in ENV selector in V86 pdb
 *      use VDHSwitchToVPM to switch client to protect mode
 *      if anything fails
 *          use em86EndVPMExecution to clean up
 *      endif
 */

VOID PASCAL EM86StartVPMExecution(PVOID p, register PVSF pvsf)
{
    register PDHD pdhd;
    PPDB   ppdb;
    VMKHBI khbi;
    ULONG  ulinit;

    p;  // remove warning

#if VDM_STATUS_VPM_32 != DPMI_32BIT_PROGRAM
#error EM86StartVPMExecution - VDM_STATUS_VPM_32 != DPMI_32BIT_PROGRAM
#endif

    // Allocate mvdm dpmi task instance data
    if(VMAllocKHB(VM_HKH_PUB_SWAPRW, sizeof(DHD),
       (VMHOB)EM86MALLOCOWNER, NA, SSToDS(&khbi)) != NO_ERROR) {
        VSF_EFLAG(pvsf) |= F_CARRY;             // set client's carry
        return;
    }
    pdhd = (PDHD)khbi.khbi_pbData;

    // Initialize the host data block
    memset(pdhd, 0, sizeof(DHD));
    em86AssertCreate(DHD,pdhd);                  // initialize signature

    // Save PDB seg in host area (PDB segment is in client's BX)
    if (VSF_EFLAG(pvsf) == F_CARRY) {                                   //BN003
        pdhd->dhd_segPDB = pPTDACur->CurrentPDB;
    } else {                                                            //BN003
        pdhd->dhd_segPDB = VSF_BX(pvsf);                                //BN003
    }
    ppdb = (PPDB)(pdhd->dhd_segPDB << 4);
    VSF_EAX(pvsf) = ulVSFEAX;
    VSF_EBX(pvsf) = ulVSFEBX;

    // Save ENV seg in host area
    pdhd->dhd_segEnviron = ppdb->PDB_environ;

    // Get the V86 stack segment
    pdhd->dhd_segStack = VSF_ES(pvsf);
    memset((PBYTE)(pdhd->dhd_segStack << 4), 0, DHD_V86_STACK_SIZE);

    // Add this host data block to the linked list.
    pdhd->dhd_pdhdParent = pdhdHostData;
    pdhdHostLast = pdhdHostData = pdhd;

    if ( (vpmInitData(pvsf) == NO_ERROR)
        && (ulinit = (*pfndpmCreateTask)((PVOID)pvsf)) )  {

        // VDD has set up protect mode selectors

        // set env variable to selector
        ppdb->PDB_environ = pdhd->dhd_selEnviron;

        VDMMVPMBeginEnd(VDM_BEGIN_VPM_TASK);    // Call begin VPM hooks //BN005

        // Switch the VDM to protect mode if DPX doesn't tell us to leave it in
        // V86 mode.  DPX reflects requests to DOS for some info, so it doesn't
        // want us messing with the state of the VDM.
        if (!(ulinit & DPXLEAVEV86)) {                                  //BN003

           VDHSwitchToVPM();

           // Protected mode switch successful
           VSF_EFLAG(pvsf) &= ~F_CARRY;         // clear client's carry
        }

    }else  {
        // Clean up
        em86EndVPMExecution();
        VSF_EFLAG(pvsf) |= F_CARRY;             // set client's carry
    } // endif

} // end EM86StartVPMExecution


#pragma code_seg("SWAP32_26","CODE")

/***LP  int2fGetVPMSwitchAddr - returns DPMI mode switch address
 *
 *      Returns the DPMI protected mode switch address and
 *      configuration information.
 *
 *      ENTRY   (pvsf) = pointer to client register stack frame
 *
 *      EXIT    VSF(AX) = 0
 *              VSF(BX) = flags
 *                  bit 0 = 1 if 32 bit programs supported
 *              VSF(CL) = processor type
 *              VSF(DH) = major DPMI version number
 *              VSF(DL) = minor DPMI version number
 *              VSF(ES:DI) = address of routine to call to enter protected mode
 *              VSF(SI) = number of paragraphs required for DPMI host data
 *
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *      fail if VDD has not registered or DPMI not allowed in this VDD
 *          (VDD sets bit in flVDMStatus when DPMI is allowed)
 *      if DOS protected mode switch address isn't allocated
 *         allocate a VDM BP hook
 *      set ax = 0
 *      set "32 bit programs allowed" flag
 *      set processor type
 *      set DPMI version number
 *      set DOS protected mode switch address
 *      set DPMI host data size (for V86 stack)
 */

BOOL int2fGetVPMSwitchAddr(pvsf)
register PVSF pvsf;
{
    register PVDMBP pvdmbp;

    // Make sure VDD registered and DPMI allowed in this VDM
    if (!fVpmInitOK || !(flVdmStatus & VDM_STATUS_VPM_PERM))
        return(TRUE);                           // return ERROR (AX!=0)

    // if the PM switch address isn't allocated
    if(vpVPMSwitchAddr == NULL) {
        pvdmbp = (PVDMBP)VDHAllocHook(VDH_BP_HOOK,
                                      (PFNARM)EM86GetPSP,               //BC002
                                      0);
        if(pvdmbp == NULL) {
            return(TRUE);                       // return ERROR (AX!=0)
        }
        em86AssertP(VDMBP, pvdmbp);

        // This is hack so the ARPL dispatch code will automaticly pop off
        // the return address from the user's stack and put it into the
        // CS:IP registers.  It happens that the recursive return hooks
        // work this way.
        pvdmbp->vdmbp_pfnDispatch = (PFN)em86VDMBPRecursiveDispatcher;
        vpVPMSwitchAddr = VDHArmBPHook((HHOOK)pvdmbp);
    }
    VSF_AX(pvsf) = 0;
    VSF_BX(pvsf) = INT2F_32BIT_PROGRAMS_SUPPORTED;
    switch( CpuType )                                /* 149007 */
    {                                                /* 149007 */
       case 1:   /* CPU_386 in protmode.h */         /* 149007 */
          VSF_CL(pvsf) = INT2F_386_PROCESSOR;        /* 149007 */
          break;                                     /* 149007 */
       case 2:   /* CPU_486 in protmode.h */         /* 149007 */
          VSF_CL(pvsf) = INT2F_486_PROCESSOR;        /* 149007 */
          break;                                     /* 149007 */
       case 3:  /* CPU_PENTIUM in protmode.h */      /* 149007 */
          VSF_CL(pvsf) = INT2F_586_PROCESSOR;        /* 149007 */
          break;                                     /* 149007 */
       case 4:  /* CPU_PENTIUM_PRO in protmode.h */  /* 158244 */
          VSF_CL(pvsf) = INT2F_686_PROCESSOR;        /* 158244 */
          break;                                     /* 158244 */
       default: /* CPU_386 in protmode.h  */         /* 158244 */
          VSF_CL(pvsf) = INT2F_386_PROCESSOR;        /* 158244 */
          break;                                     /* 158244 */
                                                     /* 149007 */
    }                                                /* 149007 */
    VSF_DX(pvsf) = DPMI_VERSION;
    VSF_DI(pvsf) = OFFSETOF16(vpVPMSwitchAddr);
    VSF_ES(pvsf) = SEGMENTOF16(vpVPMSwitchAddr);

    // Calculate and return the number of DPMI host data paragraphs needed
    VSF_SI(pvsf) = ((DHD_V86_STACK_SIZE + 0x0f) >> 4);
    return(TRUE);
}


#pragma code_seg("SWAP32_26","CODE")

/***EP  vpmInitData - initialize DPMI LDT/IDT and status
 *
 *      This routine initializes the IDT, LDT and status.
 *
 *      ENTRY   (pvsf) = pointer to client register stack frame
 *              VSF(AX) = flags
 *                  bit 0 = 1 if the program is 32-bit application
 *
 *      EXIT    if returns NO_ERROR
 *                  LDT, IDT, and flVdmStatus set
 *              else
 *                  cannot enter protect mode
 *
 *      NOTE    The return address put on the stack by the calling
 *              V86 code has already been popped off into VSF(CS:IP)
 *              by the VDM BP dispatch code.
 *
 *      NOTE    This needs to change for 1.0.  Should have
 *              both versions in conditional compilation.
 *
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *      allocate and initialize em86 per task data area
 *      put pointer to em86 data in mvdm per task data structure
 *      copy old tasks protect mode registers into new em86 task area
 *      increment protected mode execution counter
 *      if this the first PM app
 *         save away bitness flag
 *         set the VPM enabled status
 *         allocate protected mode stack, idt, etc. (em86AllocVPM)
 *         allocate LDT (em86AllocLDT)
 *      else
 *         fail if different bitness
 *      endif
 */

INT PASCAL vpmInitData(register PVSF pvsf)
{
    INT rc = NO_ERROR;
    VMKHBI khbi;

    // If already in a DPMI app, save it's current status
    if (pvpmData) {
       pvpmData->vpmd_flSaveVdmStatus = flVdmStatus;
       memcpy(&pvpmData->vpmd_aulVPMCurrentState,                       //BN009
              DHD_VPM_SAVE_START(pvsf),                                 //BN009
              VSF_ALT_SIZE);                                            //BN009
    }

    // Allocate vpm dpmi task instance data
    if ((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, sizeof(VPMD),
         (VMHOB)EM86MALLOCOWNER, NA, SSToDS(&khbi))) != NO_ERROR) {
        return(rc);
    }
    pvpmData = (PVPMD)khbi.khbi_pbData;

    // Initialize the VPM data block
    memset(pvpmData, 0, sizeof(VPMD));
    em86AssertCreate(VPMD,pvpmData);            // initialize signature

    pdhdHostData->dhd_pvpmData = pvpmData;      // VPM instance data

    /* Initialize the VPM stack selector for this task */
    pvpmData->vpmd_selVPMStack = -1;                                    //BN009

    // Save the old DOS PM client registers in the new app's VPM data
    // block.  These values will be restored when the app terminates.
//BD009  memcpy(&pvpmData->vpmd_aulVPMPreviousState,
//BD009         DHD_VPM_SAVE_START(pvsf),
//BD009         VSF_ALT_SIZE);

    // Initialize VDM status
    flVdmStatus &= ~(VDM_STATUS_VPM_32 | VDM_STATUS_VPM_APP |
                     VDM_STATUS_VPM_1C_HOOK | VDM_STATUS_VPM_08_HOOK);

    // Set status flags
    flVdmStatus |= VSF_EAX(pvsf) & DPMI_32BIT_PROGRAM;
    flVdmStatus |= VDM_STATUS_VPM_APP;

    if (cVPMExecution++ == 0) {     // if "first" DPMI task in VDM
        flrestart = 0;              // Clear restart flag

        rc = em86AllocVPM();        // Allocate per VDM data
    }

    if (rc == NO_ERROR) {

       // If this is the first DPMI application in the VDM, hook int 23 and 24
       if (selVPMBP == 0xffff) {
          VDHInstallIntHook(0, 0x23, VPMIntDispatcher,
                            VDH_PRE_HOOK | VDH_ASM_HOOK);
          VDHInstallIntHook(0, 0x24, VPMIntDispatcher,
                            VDH_PRE_HOOK | VDH_ASM_HOOK);
       }
       rc = em86AllocLDTIDT();      // Allocate new LDT and IDT
    }

    return(rc);

} // end vpmInitData

#pragma code_seg("SWAP32_27","CODE")
/***EP  em86VPMDestroyVDM - VDM process termination cleanup
 *
 *      This procedure is called when a VDM is terminated.
 *
 *      VOID em86VPMDestroyVDM(VOID)
 *
 *      ENTRY   NONE
 *
 *      EXIT    NONE
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *      free whole pdhdHostData chain (vpmFreeTaskResources)
 *      free protected mode tables (em86FreeVPM)
 */

VOID PASCAL em86VPMDestroyVDM()
{
    // Switch the VDM to V86 mode, alternate registers are protected mode
    VDHSwitchToV86();

    // Check for counter underflow
    while (pdhdHostData)  {
        pdhdHostData = vpmFreeTaskResources();     // free resources
    }  // endwhile

    // 1.0 NOTE - following needs to change for 1.0
    em86FreeVPM();
}

#pragma code_seg("SWAP32_28","CODE")
/***LP  em86EndVPMExecution - end this VDM's execution of protected mode
 *
 *      This procedure is called when the protected mode application
 *      terminates.
 *
 *      VOID em86EndVPMExecution(VOID)
 *
 *      ENTRY   NONE
 *
 *      EXIT    NONE
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *      switch to V86 mode
 *      restore environment segment
 *      restore parents protect mode segment registers
 *      call vpmFreeTaskResources to free resources
 *         (unlink mvdm task data from list)
 *      decrement protected mode execution counter
 *      if count goes to 0
 *          free idt, etc. (em86FreeVPM)
 *          free ldt (em86FreeLDT)
 *          clear flVdmStatus
 *      clear DOS protected mode app and 32 bit execution bit
 */

VOID PASCAL em86EndVPMExecution()
{
    PVSF pvsf = (PVSF)pTCBCur->TCB_pFrameBase;
    register PDHD pdhd;
    PPDB ppdb;

    // Check for counter underflow
    em86AssertTRUE(cVPMExecution != 0);

    // Get per task data area
    pdhd = pdhdHostLast;
    em86AssertP(DHD,pdhd);

    // Switch the VDM to V86 mode, alternate registers are protected mode
    VDHSwitchToV86();

    // Restore old environment segment value in PDB
    if(pdhd->dhd_selPDB) {
        for (; pdhd && (pdhdHostData->dhd_segPDB != pdhd->dhd_segPDB);  //BC002
             pdhd = pdhd->dhd_pdhdParent) {                             //BC007
        }
        if (pdhd) {                                                     //BN007
           em86AssertTRUE(pdhdHostData->dhd_segPDB == pdhd->dhd_segPDB);
           ppdb = (PPDB)(pdhd->dhd_segPDB << 4);
           ppdb->PDB_environ = pdhd->dhd_segEnviron;
        }
    } // endif

    // Restore parent program's protected mode registers
//BD009  memcpy(DHD_VPM_SAVE_START(pvsf),
//BD009         pvpmData->vpmd_aulVPMPreviousState,
//BD009         VSF_ALT_SIZE);

    // Frees task data area ***NOTE: pdhd set to parent at return ***
    pdhdHostData = vpmFreeTaskResources(); // Free selectors, allocations

    // Decrement count of DOS protected mode apps in the VDM.  If this is the
    // last one free the LDT and stack.
    if (--cVPMExecution == 0) {

        // Free protected mode stuff
        em86FreeVPM();

        // Clear status bits
        flVdmStatus &= ~(VDM_STATUS_VPM_32 | VDM_STATUS_VPM_APP |
                         VDM_STATUS_VPM_1C_HOOK | VDM_STATUS_VPM_08_HOOK);

        cVPMHWInt = 0;              // Clear HW Interrupt count         //BN009

    } else {
        flVdmStatus = pvpmData->vpmd_flSaveVdmStatus;
        memcpy(DHD_VPM_SAVE_START(pvsf),                                //BN009
               &pvpmData->vpmd_aulVPMCurrentState,                      //BN009
               VSF_ALT_SIZE);                                           //BN009
    }
}


#pragma code_seg("SWAP32_28","CODE")

/***LP  vpmFreeTaskResources - free per dpmi task resources
 *
 *      This procedure frees all per dpmi task allocations.
 *      Must be called with client switched to V86 mode!!!
 *
 *      PDHD vpmFreeTaskResources()
 *
 *      ENTRY   NONE
 *
 *      EXIT    pointer to new task data area or NULL
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *      save new task mvdm task data pointer from *pdhdHostData
 *      call DPMI API VDD to clean up
 *      if 1.0
 *          free LDT, IDT, shadow tables
 *          replace with previous tasks data
 *      free mvdm and em86 per task data areas
 */

PDHD PASCAL vpmFreeTaskResources()
{
    register PDHD pdhd = pdhdHostData;
    PDHD pdhdNew;

    if (!pdhd)
        return(NULL);

    em86AssertP(DHD,pdhd);

    // If this isn't the last app in the list, remove from it from the list
    if (pdhd != pdhdHostLast) {                                         //BN007 begin
       pdhdNew = pdhdHostLast;

       // Look for host data block in list
       while (pdhdNew && (pdhdNew->dhd_pdhdParent != pdhd)) {
          pdhdNew = pdhdNew->dhd_pdhdParent;
       }

       // Remove this block from the list
       if (pdhdNew) {
          pdhdNew->dhd_pdhdParent = pdhd->dhd_pdhdParent;
       }

       // If this app has a parent, make the parent the current process
       if (pdhd->dhd_pdhdParent) {
          pdhdNew = pdhd->dhd_pdhdParent;
       }

    // This is the last task data area in the list, update last pointer
    } else {                                                            //BN007 end
       pdhdNew = pdhd->dhd_pdhdParent;
       pdhdHostLast = pdhdNew;
    }

    // Call INT 31h API DPMI VDD to clean up
    (*pfndpmEndTask)();

    VDMMVPMBeginEnd(VDM_END_VPM_TASK); // Call VDD end VPM user hooks   //BN005

    em86FreeLDTIDT();                  // Free present LDT and IDT

    if (pdhdNew) {                        // If there is another DPMI task
       em86DPMITaskSwitch(pdhdNew);       // Set LDT and IDT to previous task
       pvpmData = pdhdNew->dhd_pvpmData;  // update pvpmData with current task
    } else {
       SELSwitchVdmLDT(&V86SelSWInfo,NULL);  // set to default VDM LDT //BN010 //Bx011
       pvpmData = NULL;                   // no more dpmi tasks
    }

    // free per dpmi task data areas
    if (pdhd->dhd_pvpmData)
        VMFreeKHB(VM_HKH_PUB_SWAPRW, pdhd->dhd_pvpmData);

    VMFreeKHB(VM_HKH_PUB_SWAPRW, pdhd);

    return(pdhdNew);

}  // end vpmFreeTaskResources


#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***EP+ VDHRegisterDPMI - special DPMI support
 *
 *      This service is for protect mode DPMI support
 *      only.  Other VDD's should not use this service.
 *
 *      ENTRY
 *          ulVersion
 *              Version requested
 *                  90h for 0.9
 *                 100h for 1.0
 *          pInBuf
 *              pointer to DPMI VD entry point structure
 *              (see dpmxport.h for details)
 *          pOutBuf
 *              pointer to kernel entry point structure
 *              (see dpmxport.h for details)
 *
 *      EXIT-SUCCESS
 *          returns TRUE
 *
 *      EXIT-FAILURE
 *          returns FALSE
 *
 *      CONTEXT
 *          VDM Task
 *
 *      PSEUDO-CODE
 *      if version requested is supportable
 *      (1.0 kernels must behave as 0.9 if requested to)
 *          Copy VDD taskcreate, taskend, and INT 31h entry points
 *          Copy kernel entry points to output structure
 *          Set fVpmInitOK to indicate VDD initialized
 */
BOOL VDHENTRY VDHRegisterDPMI (ULONG ulVersion, PDPMX pdpmx, PVPMX pvpmx)
/*end*/
{
#ifdef MMIOPH
    MMPHPreVDHRegisterDPMI()
#endif

    if (ulVersion <= DPMI_VERSION && pdpmx && pvpmx)  {

        pfndpmInt31Rte   = pdpmx->dpmx_pfnINT31Rte;
        pfndpmCreateTask = pdpmx->dpmx_pfnCreateTask;
        pfndpmEndTask    = pdpmx->dpmx_pfnEndTask;
        pfndpmQuery      = pdpmx->dpmx_pfnQuery;     /* 149007 */

#ifdef INCDEBUG
        pvpmx->vpmx_DBGWPAllocVDM = DBGWPAllocVDM;
        pvpmx->vpmx_DBGWPFreeVDM = DBGWPFreeVDM;
        pvpmx->vpmx_DBGDR6QueryVDM = DBGDR6QueryVDM;
#endif /* DEBUG */

        pvpmx->vpmx_SELCreateVdmLDT = SELCreateVdmLDT;
        pvpmx->vpmx_SELDestroyVdmLDT = em86FreeLDTIDT;
        pvpmx->vpmx_SELAllocVdm = SELAllocVdmSels;
        pvpmx->vpmx_SELFreeVdm = SELFreeVdmSels;
        pvpmx->vpmx_SELGetVdmInfo = (PFNVPMSELGETVDMINFO)SELGetVdmInfo;
        pvpmx->vpmx_PGMove = em86PGMoveStub;
        pvpmx->vpmx_PGGetAccess = em86PGGetAccessStub;
        pvpmx->vpmx_PGAllocSizeQuery = PGAllocSizeQuery;
        pvpmx->vpmx_VMAllocMem = (PFNVPMVMALLOCMEM)VMAllocMem;
        pvpmx->vpmx_VMSetMem = VMSetMem;
        pvpmx->vpmx_VMFreeMem = VMFreeMem;
        pvpmx->vpmx_em86DPMITaskSwitch = em86DPMITaskSwitch;
        pvpmx->vpmx_SEL_VDM_LDT_INCR = SEL_VDM_LDT_INCR;
        pvpmx->vpmx_CpuType = CpuType;
        pvpmx->vpmx_MaxAppLinAddr = MAXCRSIZE;
        pvpmx->vpmx_V86StackSize = DHD_V86_STACK_SIZE;
        pvpmx->vpmx_pfnVPMNpxStatus = em86VPMNpxStatus;
        pvpmx->vpmx_pfnVPMNpxSet = em86VPMNpxSet;
        pvpmx->vpmx_pfnVPMPgMapPhys = (PFNVPMPGMAPPHYS)VPMPgMapPhys;

        fVpmInitOK = TRUE;
    }  // endif

#ifdef MMIOPH
    MMPHPostVDHRegisterDPMI()
#endif
    return(fVpmInitOK);
}  // end VDHRegisterDPMI


/*** end of high level DPMI routines ***/

#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_26","CODE")

/*** EM86 dpmi creation support routines ***/

/***LP  em86AllocLDTIDT - allocate VDM protected mode per-task data     BN005
 *
 *      This functions allocates the VDM's protected mode LDT, IDT and
 *      and IDT shadow tables.
 *
 *      ENTRY   NONE
 *
 *      EXIT    returns error status
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *
 */

INT em86AllocLDTIDT()                                                   //BN005
{
    register int i;
    int rc;

    if((rc = SELCreateVdmLDT((PVOID*)&(pdhdHostData->dhd_laddrLDT)))
       != NO_ERROR) {
         pdhdHostData->dhd_laddrLDT = NULL;
         goto error;
    }

    pvpmData->vpmd_LDTswinfo.ssv_ldtaddr = (ULONG)pdhdHostData->dhd_laddrLDT;
    pvpmData->vpmd_LDTswinfo.ssv_ldthandle = pPTDACur->ptda_ldthandle;
    pvpmData->vpmd_LDTswinfo.ssv_ldtpgmap  = pPTDACur->ptda_ldtpgmap;

    SELSwitchVdmLDT(&(pvpmData->vpmd_LDTswinfo), NULL);  // set to new LDT  //Bx011

    // Allocate VDM protected mode IDT
    if((rc = VMAllocKHB(VM_HKH_PUB_RESRW, VPM_IDT_SIZE, (VMHOB)EM86MALLOCOWNER,
                        NA, &(pvpmData->vpmd_pagateVPMIDT)))
       != NO_ERROR) {
        goto error;
    }

    // Initialize VDM protected mode IDT
    memset(pvpmData->vpmd_pagateVPMIDT, 0, VPM_IDT_SIZE);
    memcpy((PBYTE)pvpmData->vpmd_pagateVPMIDT +
            (IDT_START_EXCEPTIONS * GATE_SIZE),
           (PBYTE)IDT + (IDT_START_EXCEPTIONS * GATE_SIZE),
           VPM_EXCEPTION_INTS_SIZE);

    memcpy((PBYTE)pvpmData->vpmd_pagateVPMIDT +
            (IDT_START_HARDWARE_INTS * GATE_SIZE),
           (PBYTE)IDT + (IDT_START_HARDWARE_INTS * GATE_SIZE),
           VPM_HARDWARE_INTS_SIZE);

    // Setup lidt variable for the mode switching functions
    pvpmData->vpmd_lidtVPMIDT.lidt_limit = VPM_IDT_SIZE - 1;
    pvpmData->vpmd_lidtVPMIDT.lidt_base =
       (ULONG)pvpmData->vpmd_pagateVPMIDT;

    // Allocate IDT shadow table
    if((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, VPMINT_TABLE_SIZE,
      (VMHOB)EM86MALLOCOWNER, NA, &(pvpmData->vpmd_pavpmint))) != NO_ERROR) {
        goto error;
    }

    // Allocate VDM protected exception table
    if((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, VPMEXC_TABLE_SIZE,
      (VMHOB)EM86MALLOCOWNER, NA, &(pvpmData->vpmd_pavpmexc))) != NO_ERROR) {
        goto error;
    }

    // Allocate VDM V86 exception table  GK         149007
    if((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, VPMEXC_TABLE_SIZE,   /* 149007 */
                        (VMHOB)EM86MALLOCOWNER, NA,             /* 149007 */
                        &(pvpmData->vpmd_pav86exc))) != NO_ERROR) {/* 149007 */
        goto error;                                                /* 149007 */
    }                                                              /* 149007 */

    // Allocate ring 3 VDM protected mode stack                         //BN009
    if (!(pvpmData->vpmd_pVPMStack = VDHAllocPages(NULL,                //BN009
              (VPM_STACK_SIZE + PAGESIZE - 1)/PAGESIZE,                 //BN009
              0))) {                                                    //BN009
        goto error;                                                     //BN009
    }

    // Create a VPM stack selector
    if (em86AllocSel((ULONG)(pvpmData->vpmd_pVPMStack),                 //BC009
            VPM_STACK_SIZE,
            &(pvpmData->vpmd_selVPMStack),                              //BC009
            (BYTE)D_DATA3,
            (BYTE)((flVdmStatus & VDM_STATUS_VPM_32) ? D_DBIG : 0))
                == NULL) {
        goto error;
    }

    // Create a breakpoint region code selector
    if (em86AllocSel((ULONG)pVPMBP,
                     VPM_BP_SIZE,
                     &selVPMBP,
                     (BYTE)D_CODE3,
                     0) == NULL) {
        goto error;
    }
    pdhdHostData->dhd_selvpmbp = selVPMBP;           // set BP selector

    /*  Initialize the IDT and IDT shadow table
     *
     *  VPM breakpoints address are put into each entry.  The tricky part
     *  is that the first 0 - 256 VPM BPs are reserved for interrupts so
     *  the BP number (EIP of the breakpoint address) indicates which
     *  interrupt it is when it is time to reflect it to V86 mode.
     */
    for(i = 0; i < VPM_NUM_INTS; i++) {
         em86AssertCreate(VPMINT, &((*pvpmData->vpmd_pavpmint)[i]));
         (*pvpmData->vpmd_pavpmint)[i].vpmint_sel = selVPMBP;
         (*pvpmData->vpmd_pavpmint)[i].vpmint_off = i;
         (*pvpmData->vpmd_pavpmint)[i].vpmint_usFlagMask = ~F_TRACE;

         //BUGBUG wayneca 7/31/90 - need to find where base ints are
         if(i >= 0x8 && i <= 0xf || i >= 0x70 && i <= 0x77) {
             // clear IF so interrupts will be disabled
             (*pvpmData->vpmd_pavpmint)[i].vpmint_usFlagMask &= ~F_INTERRUPT;
         }
         em86UpdateIDT(i);
    }

    /*  Initialize the VPM exception table
     *
     *  Same thing as interrupts, but the VPM BPs are reserved starting at
     *  257 to 257 + 1f.
     *
     */
    for(i = 0; i < VPM_NUM_EXCEPTIONS; i++) {
         em86AssertCreate(VPMEXC, &((*pvpmData->vpmd_pavpmexc)[i]));
         (*pvpmData->vpmd_pavpmexc)[i].vpmexc_sel = selVPMBP;
         (*pvpmData->vpmd_pavpmexc)[i].vpmexc_off = VPM_EXC_BP_START + i;
         (*pvpmData->vpmd_pavpmexc)[i].vpmexc_usFlags = 0;
    }

    /*  Initialize the V86 exception table  GK          149007
     *                                                  149007
     *  Same thing as VPM Exception, but the V86 BPs are reserved starting at
     *  257 + 1f to 257 + 1f * 2.                       149007
     *                                                  149007
     */
    for(i = 0; i < VPM_NUM_EXCEPTIONS; i++) {           /* 149007 */
       /* em86AssertCreate(V86EXC, &((*pvpmData->vpmd_pav86exc)[i]));      */

       (*pvpmData->vpmd_pav86exc)[i].vpmexc_sel     = selVPMBP; /* 149007 */
       (*pvpmData->vpmd_pav86exc)[i].vpmexc_off     = VPM_V86EXC_BP_START + i;
                                                                /* 149007 */
       (*pvpmData->vpmd_pav86exc)[i].vpmexc_usFlags = 0;        /* 149007 */
    }

    return(NO_ERROR);

error:
    em86FreeVPM();              // clean up
    return(rc);
}

#pragma code_seg("SWAP32_26","CODE")

/***LP  em86AllocVPM - allocate VDM protected mode tables
 *
 *      This functions allocates the VDM's protected mode stack, IDT
 *      and IDT shadow tables.
 *
 *      ENTRY   NONE
 *
 *      EXIT    returns error status
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *
 *      allocate ring 3 VDM private arena 4K stack
 *      create selector for the stack
 *      allocate ring 3 VDM protected mode breakpoint region
 *      create a breakpoint region code selector
 *      allocate VPM breakpoint function table
 *      allocate VDM protected mode IDT
 *      initialize VDM protected mode IDT
 *      allocate IDT shadow table
 *      allocate V86 mode reflection return hook
 *      allocate VDM protected mode exception table
 *      initialize VPM breakpoint table
 *      initialize IDT shadow table
 *      initialize VPM exception table
 */

INT em86AllocVPM()
{
    register int i;
    int rc;

//BD009    // Allocate ring 3 VDM protected mode stack
//BD009    if (!(pVPMStack = VDHAllocPages(NULL,
//BD009                      (VPM_STACK_SIZE + PAGESIZE - 1)/PAGESIZE,
//BD009                        0))) {
//BD009        goto error;
//BD009    }

    // Allocate ring 3 VDM protected mode breakpoint region

    if (!(pVPMBP = VDHAllocPages(NULL,
                                 (VPM_BP_SIZE + PAGESIZE - 1)/PAGESIZE,
                                 0))) {
        goto error;
    }

    memset(pVPMBP, MI_HLT, VPM_BP_SIZE);

    // Set breakpoint region read-only
    if((rc = VMSetMem((ULONG)pVPMBP, VPM_BP_SIZE, HOBNULL, VPM_BP_SET_FLAGS,
      0)) != NO_ERROR) {
        goto error;
    }

    // Allocate VPM breakpoint function table
    if((rc = VMAllocKHB(VM_HKH_PUB_SWAPRW, (VPM_NUM_BPS * sizeof(PFN)),
      (VMHOB)EM86MALLOCOWNER, NA, &papfnVPMBP)) != NO_ERROR) {
        goto error;
    }

    // Allocate V86 mode reflection return hook
    if((hhookV86ReflectionHook = VDHAllocHook(VDH_RETURN_HOOK,
      em86V86ReflectionHook, 0)) == NULL) {
        rc = VDHGetError();
        goto error;
    }

    // Allocate VPM mode reflection return hook
    if((hhookVPMReflectionHook = VDHAllocHook(VDH_RETURN_HOOK,
      em86VPMReflectionHook, 0)) == NULL) {
        rc = VDHGetError();
        goto error;
    }
    if((hhookVPMExcRetHook = VDHAllocHook(VDH_RETURN_HOOK,
        EM86VPMExcRetHook, 0)) == NULL) {
        rc = VDHGetError();
        goto error;
    }
    if((hhookVPMExpExcRetHook = VDHAllocHook(VDH_RETURN_HOOK,   /* 149007 */
        EM86VPMExpExcRetHook, 0)) == NULL) {                    /* 149007 */
        rc = VDHGetError();                                     /* 149007 */
        goto error;                                             /* 149007 */
    }                                                           /* 149007 */
    if((hhookV86ExpExcRetHook = VDHAllocHook(VDH_RETURN_HOOK,   /* 149007 */
        EM86V86ExpExcRetHook, 0)) == NULL) {                    /* 149007 */
        rc = VDHGetError();                                     /* 149007 */
        goto error;                                             /* 149007 */
    }                                                           /* 149007 */
    if((hhookV86ExpExc9RetHook = VDHAllocHook(VDH_RETURN_HOOK,  /* 149007 */
        EM86V86ExpExc9RetHook, 0)) == NULL) {                   /* 149007 */
        rc = VDHGetError();                                     /* 149007 */
        goto error;                                             /* 149007 */
    }                                                           /* 149007 */
    if((hhookBacktoV86RetHook = VDHAllocHook(VDH_RETURN_HOOK,   /* 149007 */
        EM86BacktoV86RetHook, 0)) == NULL) {                    /* 149007 */
        rc = VDHGetError();                                     /* 149007 */
        goto error;                                             /* 149007 */
    }                                                           /* 149007 */

    // Allocate V86 int 23 hook
    if((hhookV86Int23Hook = VDHAllocHook(VDH_BP_HOOK,
                                         EM86Int23Hook,
                                         0)) == NULL) {                 //BN006
        rc = VDHGetError();                                             //BN006
        goto error;                                                     //BN006
    }                                                                   //BN006

    // Get the current int 23 vector from the IVT
    vpV86Int23vect = *((PVPVOID)(0x23*sizeof(ULONG)));                  //BN006

    // Put our int 23 hook into the IVT and store the pointer.          //72717
    vpOurV86Int23vect =                                                 //72717
    *((PVPVOID)(0x23*sizeof(ULONG))) = VDHArmBPHook(hhookV86Int23Hook); //BN006

    /* Initialize the VPM breakpoint table */

    // Interrupts are reflected to V86 mode
    for(i = VPM_INT_BP_START; i <= VPM_INT_BP_END; i++) {
        (*papfnVPMBP)[i] = em86VPMIntBPHdlr;
    }

    // INT 21, 2F, and 31 have their own handlers
    (*papfnVPMBP)[VPM_INT_BP_START + 0x21] = em86VPMInt21Hdlr;
    (*papfnVPMBP)[VPM_INT_BP_START + 0x2F] = em86VPMInt2FHdlr;
    (*papfnVPMBP)[VPM_INT_BP_START + 0x31] = em86VPMInt31Hdlr;

    // Exceptions 0-5 are reflected to V86 mode
    for(i = VPM_EXC_BP_START; i <= VPM_EXC_BP_START + 5; i++) {
        (*papfnVPMBP)[i] = em86VPMExcBPReflect;
    }

    // Exception 6 terminates the VDM
    (*papfnVPMBP)[VPM_EXC_BP_START + 6] = em86VPMExcBPTerminate;

    // Exception 7 is reflected to V86 mode
    (*papfnVPMBP)[VPM_EXC_BP_START + 7] = em86VPMExcBPReflect;

    // Exceptions 8-1f terminate the VDM
    for(i = VPM_EXC_BP_START + 8; i <= VPM_EXC_BP_END; i++) {
        (*papfnVPMBP)[i] = em86VPMExcBPTerminate;
    }

    /* GK: Set up default exception handler for V86 Exception 149007 */
    // Exceptions 0-5 are reflected to V86 mode                 /* 149007 */
    for(i = VPM_V86EXC_BP_START; i <= VPM_V86EXC_BP_START + 5; i++) {/* 149007 */
        (*papfnVPMBP)[i] = em86VPMExcBPReflect;                      /* 149007 */
    }                                                                /* 149007 */
                                                                     /* 149007 */
    // Exception 6 terminates the VDM                                /* 149007 */
    (*papfnVPMBP)[VPM_V86EXC_BP_START + 6] = em86V86ExcBPTerminate;  /* 149007 */
                                                                     /* 149007 */
    // Exception 7 is reflected to V86 mode                          /* 149007 */
    (*papfnVPMBP)[VPM_V86EXC_BP_START + 7] = em86VPMExcBPReflect;    /* 149007 */
                                                                     /* 149007 */
    // Exceptions 8-1f terminate the VDM                             /* 149007 */
    for(i = VPM_V86EXC_BP_START + 8; i <= VPM_V86EXC_BP_END; i++) {  /* 149007 */
        (*papfnVPMBP)[i] = em86V86ExcBPTerminate;                    /* 149007 */
    }                                                                /* 149007 */



    // The VDMBP range is initialize to the VDMBP dispatcher
    for(i = VPM_VDMBP_START; i <= VPM_VDMBP_END; i++) {
        (*papfnVPMBP)[i] = em86DispatchVPMVDMBP;
    }

    // Get default LDT information
    V86SelSWInfo.ssv_ldtaddr = pPTDACur->ptda_ldtaddr;                  //BN010
    V86SelSWInfo.ssv_ldthandle = pPTDACur->ptda_ldthandle;              //BN010
    V86SelSWInfo.ssv_ldtpgmap  = pPTDACur->ptda_ldtpgmap;               //BN010

    return(NO_ERROR);

error:
    em86FreeVPM();              // clean up
    return(rc);
}


#pragma code_seg("SWAP32_25","CODE")
/***LP  em86UpdateIDT - Update the IDT from the shadow table
 *
 *      This function copies the CS:EIP from the shadow table
 *      to the IDT entry and sets the appropriate gate type (INT
 *      or TRAP) for the IDT entries that are not reserved by
 *      the system (exceptions 0-1f, hardware interrupts 50-5f
 *      and 70-7fh).
 *
 *      ENTRY   (IntNum)
 *
 *      EXIT    NONE
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *
 *      if a system reserved interrupt
 *         return
 *      set the selector and offset in the IDT gate
 *      if VPM breakpoint for this interrupt is being set
 *         set ring 0 trap gate to force faulting
 *           so we don't have to pop the iret frame
 *      else
 *         set the appropriate access byte in the IDT gate
 */

VOID em86UpdateIDT(IntNum)
register ULONG IntNum;
{
    // BUGBUG wayneca 10/22/90 this incorrectly assumes hardware
    // interrupts are fixed and contiguous
    if((IntNum <= IDT_END_EXCEPTIONS) ||
      (IntNum >= IDT_START_HARDWARE_INTS && IntNum <= IDT_END_HARDWARE_INTS)) {
        return;
    }

    (*pvpmData->vpmd_pagateVPMIDT)[IntNum].g_sel =
       (*pvpmData->vpmd_pavpmint)[IntNum].vpmint_sel;
    (*pvpmData->vpmd_pagateVPMIDT)[IntNum].g_lowoffset =
      LOUSHORT((*pvpmData->vpmd_pavpmint)[IntNum].vpmint_off);
    (*pvpmData->vpmd_pagateVPMIDT)[IntNum].g_extoffset =
      HIUSHORT((*pvpmData->vpmd_pavpmint)[IntNum].vpmint_off);

    if(((*pvpmData->vpmd_pavpmint)[IntNum].vpmint_sel == selVPMBP) &&
       ((*pvpmData->vpmd_pavpmint)[IntNum].vpmint_off == IntNum)) {
        (*pvpmData->vpmd_pagateVPMIDT)[IntNum].g_access = D_TRAP032;
    }
    else {
        // if the interrupt flag is set in the mask, use a trap gate
        if((*pvpmData->vpmd_pavpmint)[IntNum].vpmint_usFlagMask &
            F_INTERRUPT) {
           (*pvpmData->vpmd_pagateVPMIDT)[IntNum].g_access = D_TRAP3;
        } else {
           (*pvpmData->vpmd_pagateVPMIDT)[IntNum].g_access = D_INT3;
        }

        // if 32 bit app, make it a 32 bit gate
        if(flVdmStatus & VDM_STATUS_VPM_32) {
            (*pvpmData->vpmd_pagateVPMIDT)[IntNum].g_access |= D_32;
        }
    }
}

#pragma code_seg("SWAP32_27","CODE")
/***LP  em86FreeVPM - free VDM protected mode tables
 *
 *      This functions frees the VDM's protected mode stack, IDT and
 *      IDT shadow tables.
 *
 *      ENTRY   NONE
 *
 *      EXIT    returns error status
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *
 *      free ring 3 stack
 *      free VDM protected mode IDT
 *      free VDM protected mode breakpoint ring 3 region
 *      free VDM protected mode breakpoint selector
 *      free VPM breakpoint table
 *      free IDT shadow table
 *      free VPM exception table
 *      free V86 mode reflection return hook
 */

VOID em86FreeVPM()
{
    // Switch the VDM to V86 mode, alternate registers are protected mode
    VDHSwitchToV86();

//BD009    // free VDM protected mode ring 3 stack
//BD009    if(pVPMStack != NULL) {
//BD009        VDHFreePages(pVPMStack);
//BD009        pVPMStack = NULL;
//BD009    }

    // free VDM protected mode breakpoint ring 3 region
    if(pVPMBP != NULL) {
        VDHFreePages(pVPMBP);
        pVPMBP = NULL;
    }

    // free VPM breakpoint table
    if(papfnVPMBP != NULL) {
        VMFreeKHB(VM_HKH_PUB_SWAPRW, papfnVPMBP);
        papfnVPMBP = NULL;
    }

    // free V86 mode reflection return hook
    if(hhookV86ReflectionHook != NULL) {
        VDHFreeHook(hhookV86ReflectionHook);
        hhookV86ReflectionHook = NULL;
    }

    // free get PSP return hook
    if(hhookGetPSPRetHook != NULL) {                                    //BN003
        VDHFreeHook(hhookGetPSPRetHook);                                //BN003
        hhookGetPSPRetHook = NULL;                                      //BN003
    }                                                                   //BN003

    // free VPM mode reflection return hook
    if(hhookVPMReflectionHook != NULL) {
        VDHFreeHook(hhookVPMReflectionHook);
        hhookVPMReflectionHook = NULL;
    }

    // free VPM mode exception reflection return hook
    if(hhookVPMExcRetHook != NULL) {
        VDHFreeHook(hhookVPMExcRetHook);
        hhookVPMExcRetHook = NULL;
    }
    if(hhookVPMExpExcRetHook != NULL) {               /* 149007 */
        VDHFreeHook(hhookVPMExpExcRetHook);           /* 149007 */
        hhookVPMExpExcRetHook = NULL;                 /* 149007 */
    }                                                 /* 149007 */
    if(hhookV86ExpExcRetHook != NULL) {               /* 149007 */
        VDHFreeHook(hhookV86ExpExcRetHook);           /* 149007 */
        hhookV86ExpExcRetHook = NULL;                 /* 149007 */
    }                                                 /* 149007 */
    if(hhookV86ExpExc9RetHook != NULL) {              /* 149007 */
        VDHFreeHook(hhookV86ExpExc9RetHook);          /* 149007 */
        hhookV86ExpExc9RetHook = NULL;                /* 149007 */
    }                                                 /* 149007 */
    if(hhookBacktoV86RetHook != NULL) {               /* 149007 */
        VDHFreeHook(hhookBacktoV86RetHook);           /* 149007 */
        hhookBacktoV86RetHook = NULL;                 /* 149007 */
    }                                                 /* 149007 */

    // Free int 23 hook
    if (hhookV86Int23Hook != NULL) {                                    //BN006
        VDHFreeHook(hhookV86Int23Hook);                                 //BN006
        hhookV86Int23Hook = NULL;                                       //BN006
    }                                                                   //BN006
    // Restore the int 23 vector in the IVT
    *((PVPVOID)(0x23*sizeof(ULONG))) = vpV86Int23vect;                  //BN006
}

#pragma code_seg("SWAP32_28","CODE")
/***LP  em86FreeLDTIDT - free VDM protected mode per-task data strucs   //BN005
 *
 *      This functions frees the VDM's protected mode LDT and IDT.
 *
 *      ENTRY   NONE
 *
 *      EXIT    NONE
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 */

VOID em86FreeLDTIDT()                                                   //BN005
{
   SELDestroyVdmLDT(pdhdHostData->dhd_laddrLDT);
   pdhdHostData->dhd_laddrLDT = NULL;

   // free VDM protected mode ring 3 stack                              //BN009
   if(pvpmData->vpmd_pVPMStack != NULL) {                               //BN009
       VDHFreePages(pvpmData->vpmd_pVPMStack);                          //BN009
       pvpmData->vpmd_pVPMStack = NULL;                                 //BN009
   }                                                                    //BN009

   // free VDM protected mode IDT
   if (pvpmData->vpmd_pagateVPMIDT != NULL) {
      VMFreeKHB(VM_HKH_PUB_RESRW, pvpmData->vpmd_pagateVPMIDT);
      pvpmData->vpmd_pagateVPMIDT = NULL;
   }

   // free IDT shadow table
   if (pvpmData->vpmd_pavpmint != NULL) {
       VMFreeKHB(VM_HKH_PUB_SWAPRW, pvpmData->vpmd_pavpmint);
       pvpmData->vpmd_pavpmint = NULL;
   }

   // free VPM exception table
   if (pvpmData->vpmd_pavpmexc != NULL) {
       VMFreeKHB(VM_HKH_PUB_SWAPRW, pvpmData->vpmd_pavpmexc);
       pvpmData->vpmd_pavpmexc = NULL;
   }
   // free V86 exception table  GK  149007
   if (pvpmData->vpmd_pav86exc != NULL) {                    /* 149007 */
       VMFreeKHB(VM_HKH_PUB_SWAPRW, pvpmData->vpmd_pav86exc);/* 149007 */
       pvpmData->vpmd_pav86exc = NULL;                       /* 149007 */
   }                                                         /* 149007 */
}

#pragma code_seg("SWAP32_26","CODE")
/***LP  em86AllocSel - allocate and initalize LDT selector
 *
 *      ENTRY   (laddr) = base address
 *              (size) = limit + 1 of descriptor
 *              (psel) = pointer to selector (-1 means allocate new selector)
 *              (acc) = access byte
 *              (attr) = attribute byte
 *
 *      EXIT    if successful
 *                  returns TRUE
 *                  psel = selector
 *              if failure
 *                  returns FALSE
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 */
BOOL em86AllocSel(ULONG laddr, ULONG size, PSEL psel, BYTE acc, BYTE attr)
{
   USHORT selmin=1;
    // Would be better to allocate same LDT descriptor in each LDT
//BD004  if(SELAllocGDT(SSToDS(&sel)) != NO_ERROR) {

    // Allocate an LDT selector
    if(SELAllocVdmSels(1, SSToDS(&selmin), psel) != NO_ERROR) {         //BN004
        return(NULL);
    }

    SELSetDescInfo(SELSD_ALL, (USHORT)((ULONG)attr << 8 | (ULONG)acc),
                   *psel, size, laddr, HOBNULL, HOBNULL, 0);

    return(TRUE);
}


#pragma code_seg()
#pragma BEGIN_MVDM_SWAP_CODE

/***LP  em86FreeSel - free LDT selector
 *
 *      ENTRY   (sel) = selector to free
 *
 *      EXIT    NONE
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 */

VOID em86FreeSel(sel)
SEL sel;
{
    // allocate in GDT so present when multiple LDTs
//BD004  SELFreeGDT(sel);
    SELFreeVdmSels(sel,1);                                              //BN004
}


#pragma END_MVDM_SWAP_CODE
#pragma code_seg("SWAP32_26","CODE")
/***LP  EM86GetPSP - get PSP selector
 *
 *      ENTRY   (pvsf) = client register frame
 *
 *      EXIT    NONE
 *
 *      CONTEXT VDM Task
 *
 *      PSEUDO-CODE
 *          Allocate the return hook
 *          Arm the return hook
 *          Save user registers
 *          Issue the Int 21h, function 62 to get the PSP
 *
 */
VOID PASCAL EM86GetPSP(PVOID p, register PVSF pvsf)
{
   p;                                        // get rid of warning

   if((hhookGetPSPRetHook == NULL) &&        // check for NULL hook     //81988
      ((hhookGetPSPRetHook = VDHAllocHook(VDH_RETURN_HOOK,
      (PFNARM)EM86StartVPMExecution, 0)) == NULL)) {
      VSF_EFLAG(pvsf) |= F_CARRY;             // set client's carry
   } else {
      VDHArmReturnHook(hhookGetPSPRetHook, VDHARH_RECURSIVE_CSEIP_HOOK);
      ulVSFEAX = VSF_EAX(pvsf);
      ulVSFEBX = VSF_EBX(pvsf);
      VSF_AX(pvsf) = 0x6200;
      VDHPushInt(0x21);
   }
}


/*** End of EM86 dpmi creation support routines ***/


#pragma code_seg("SWAP32_21","CODE")

/***LP  VDMInstallFaultHandler - Install a ring-0 fault handler
 *
 *      ENTRY
 *             PVOID handler address (32 bit address)
 *
 *      EXIT   TRUE,   if successful
 *
 *
 *      CONTEXT VDM Task
 *
 *
 */
BOOL VDHENTRY VDHInstallFaultHandler(PVOID pHandler)
{
  pFaultHandler = pHandler;                                             // WIN32
  return TRUE;                                                          // WIN32
}


#pragma END_MVDM_SWAP_CODE
