/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = VV8514SV.C
 *
 * DESCRIPTIVE NAME = Virtual Video SVGA Specific Processing for the 8514 compatibles
 *
 *
 * VERSION = V1.0
 *
 * DATE      04/30/93
 *
 * DESCRIPTION  This module contains all 8514 specific SVGA code and data.
 *
 *
 * FUNCTIONS
 *              v8514ReturnLineLen()       Return screen width in bytes
 *              v8514Accelerated()         Return TRUE if adapter in accelerator mode
 *              v8514SetIOHooks()          Install/remove I/O hooks
 *              v8514SetPixelIOHooks()     Install/remove pixel transfer I/O hooks
 *              v8514UpdateModeData()      Redetermine current screen state
 *              v8514RestoreIOState()      Transfer virtual I/O state to hardware
 *              v8514EnableBankAdd()       Enable direct linear access to the VRAM
 *              v8514EnableVGA()           Put adapter in VGA mode
 *              v8514WaitOnEngine()        Wait until current accelerator operation finished
 *              v8514LockIO()              Prevent VDM from doing further I/O
 *              v8514UnlockIO()            Allow VDM I/O to proceed
 *              v8514EnterIO()             Verify that VDM is allowed to do I/O
 *              v8514ExitIO()              Mark VDM I/O completed
 *              V8514ReadWRegByteL()       Read low byte of 8514/A word register
 *              V8514WriteWRegByteL()      Write low byte of 8514/A word register
 *              V8514ReadWRegByteH()       Read high byte of 8514/A word register
 *              V8514WriteWRegByteH()      Write high byte of 8514/A word register
 *              V8514ReadWRegWord()        Read 8514/A word register
 *              V8514WriteWRegWord()       Write 8514/A word register
 *              V8514ReadMFRegByteL()      Read low byte of 8514/A multi-function register
 *              V8514WriteMFRegByteL()     Write low byte of 8514/A multi-function register
 *              V8514ReadMFRegByteH()      Read high byte of 8514/A multi-function register
 *              V8514WriteMFRegByteH()     Write high byte of 8514/A multi-function register
 *              V8514ReadMFRegWord()       Read 8514/A multi-function register
 *              V8514WriteMFRegWord()      Write 8514/A multi-function register
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#define  IO8BIT           /* CL386 Version 6.00.054 FLAG:              */
#include <mvdm.h>
#include <vvd.h>
#include <vvdp.h>

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif
#define  INCL_DOSERRORS
#include <bseerr.h>
/*
**     Externals
*/
/*
** Global data
*/
extern ULONG ulSVGAVRAMSize;

#pragma  BEGIN_SWAP_DATA
/*
** Future 8514 compatible adapters. Add accelerator port defines here and
** allocate entries into aportWReg array. Define WREG_ index into the entry
** in vvdp.h
*/
#define A8514_PIXELTRANSFER_EXT 0xE2EA
 /*
 ** The following is a complete list of word registers, all of which use
 ** the following I/O handlers:
 **
 **     V8514ReadWRegByteL      V8514ReadWRegByteH      V8514ReadWRegWord
 **     V8514WriteWRegByteL     V8514WriteWRegByteH     V8514WriteWRegWord
 **
 ** Only one word register is excluded (A8514_MULTIFUNCTION), which actually
 ** indexes into an array of internal registers, so shadowing of the port is
 ** handled by a special set of routines, and which are specially installed by
 ** v8514SetIOHooks:
 **
 **     V8514ReadMFRegByteL     V8514ReadMFRegByteH     V8514ReadMFRegWord
 **     V8514WriteMFRegByteL    V8514WriteMFRegByteH    V8514WriteMFRegWord
 **
 ** NOTE: this list of registers MUST be kept in sync with WREG_* constants
 ** in vvdp.h.
 */

WORD aportWReg[] =
{
  A8514_SUBSYSCTRL,                              /* (W/O) 00                */
  A8514_ADVCTRL,                                 /* (W/O) 01                */
  A8514_COMMAND,                                 /* (W/O) 02                */
  A8514_CURRENTY,                                /* (R/W) 03                */
  A8514_CURRENTX,                                /* (R/W) 04                */
  A8514_DESTY_AXSTP,                             /* (W/O) 05                */
  A8514_DESTX_DIASTP,                            /* (W/O) 06                */
  A8514_ERRORTERM,                               /* (R/W) 07                */
  A8514_MAJORAXISCNT,                            /* (W/O) 08                */
  A8514_BGNDCOLOR,                               /* (W/O) 09                */
  A8514_FGNDCOLOR,                               /* (W/O) 10                */
  A8514_WRITEMASK,                               /* (W/O) 11                */
  A8514_READMASK,                                /* (W/O) 12                */
  A8514_COLORCOMPARE,                            /* (W/O) 13                */
  A8514_BGNDMIX,                                 /* (W/O) 14                */
  A8514_FGNDMIX,                                 /* (W/O) 15                */
  A8514_SHORTSTROKE,                             /* (W/O) 16                */
  A8514_PIXELTRANSFER,                           /* (R/W) 17                */
  A8514_PIXELTRANSFER_EXT,                       /* (R/W) 18                */
} ;

IOH ioh8514wreglo =
{
  V8514ReadWRegByteL, V8514WriteWRegByteL,
  V8514ReadWRegWord,  V8514WriteWRegWord, NULL,
} ;

IOH ioh8514wreghi =
{
  V8514ReadWRegByteH, V8514WriteWRegByteH, NULL,
  NULL,               NULL,
} ;

IOH ioh8514mfreglo =
{
  V8514ReadMFRegByteL,V8514WriteMFRegByteL,
  V8514ReadMFRegWord, V8514WriteMFRegWord, NULL,
} ;

IOH ioh8514mfreghi =
{
  V8514ReadMFRegByteH,V8514WriteMFRegByteH,
  NULL,               NULL,               NULL,
} ;

#pragma  END_SWAP_DATA
#pragma  BEGIN_SWAP_CODE
/***********************************************************************
 *
 * FUNCTION NAME = v8514ReturnLineLen()
 *
 * DESCRIPTION   = Return line len in bytes for accelerated modes, depending on
 *                 the memory map organization.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL = TRUE/FALSE
 * RETURN-ERROR  =
 *
 ***********************************************************************/
ULONG   PRIVENTRY v8514ReturnLineLen(HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG ulLineLen;
  switch((pvd->aregCRTData[0x50]&0xC0) >> 6)   //bits 6-7
  {
    case 0x3:
      ulLineLen=1280;
      break;
    case 0x2:
      ulLineLen=800;
      break;
    case 0x1:
      ulLineLen=640;
      break;
    default:
      if ((ulSVGAVRAMSize == 0x200000) &&
          (pvd->mstateVideo == MEMORY_GRFX256) &&
          (pvd->vvMode.vvm_nCols > 1024))
         ulLineLen=2048;
       else
         ulLineLen=1024;
  }
  return ulLineLen;
}
/***********************************************************************
 *
 * FUNCTION NAME = v8514Accelerated()
 *
 * DESCRIPTION   = Is 8514 in accelerator mode
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL = TRUE/FALSE
 * RETURN-ERROR  =
 *
 ***********************************************************************/
BOOL    PRIVENTRY v8514Accelerated(HVDM hvdm)
{
  return (pVDMData(hvdm)->awreg8514Data[WREG_ADVCTRL]&ADVCTRL_VGADISABLE);
}
/***********************************************************************
 *
 * FUNCTION NAME = v8514SetPixelIOHooks()
 *
 * DESCRIPTION   = Install/remove I/O hooks
 *
 *                 This routine walks installs the appropriate I/O
 *                 handlers for the current VDM.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/
VOID PRIVENTRY v8514SetPixelIOHooks(FLAGS fl)
{
  VDHSetIOHookState(CURRENT_VDM,
                    A8514_PIXELTRANSFER,
                    1,
                    &ioh8514wreglo,
                    fl);
  VDHSetIOHookState(CURRENT_VDM,
                    A8514_PIXELTRANSFER+1,
                    1,
                    &ioh8514wreghi,
                    fl);
  VDHSetIOHookState(CURRENT_VDM,
                    A8514_PIXELTRANSFER_EXT,
                    1,
                    &ioh8514wreglo,
                    fl);
  VDHSetIOHookState(CURRENT_VDM,
                    A8514_PIXELTRANSFER_EXT+1,
                    1,
                    &ioh8514wreghi,
                    fl);
}
/***********************************************************************
 *
 * FUNCTION NAME = v8514InstallIOHooks()
 *
 * DESCRIPTION   = Install I/O hooks and set the initial state to trapped.
 *
 *                 This routine installs all the appropriate I/O
 *                 handlers for the current VDM.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514InstallIOHooks(VOID)
{
  register INT i;

  /*
  ** Don't trap Pixel Transfer Registers - for performance reasons
  */

  AssertRC(VDHInstallIOHook(CURRENT_VDM,
                            aportWReg[WREG_PIXELTRANSFER],
                            1,
                            &ioh8514wreglo,
                            VDHIIH_NO_SIMULATE));
  AssertRC(VDHInstallIOHook(CURRENT_VDM,
                            aportWReg[WREG_PIXELTRANSFER]+1,
                            1,
                            &ioh8514wreghi,
                            0));

  AssertRC(VDHInstallIOHook(CURRENT_VDM,
                            aportWReg[WREG_PIXELTRANSFER_EXT],
                            1,
                            &ioh8514wreglo,
                            VDHIIH_NO_SIMULATE));
  AssertRC(VDHInstallIOHook(CURRENT_VDM,
                            aportWReg[WREG_PIXELTRANSFER_EXT]+1,
                            1,
                            &ioh8514wreghi,
                            0));

  /*
  ** Default to I/O trap on installation
  */

  for (i = 0; i < (NElements(aportWReg)-1); i++)
  {

    AssertRC(VDHInstallIOHook(CURRENT_VDM,
                              aportWReg[i],
                              1,
                              &ioh8514wreglo,
                              VDHIIH_NO_SIMULATE));
    AssertRC(VDHInstallIOHook(CURRENT_VDM,
                              aportWReg[i]+1,
                              1,
                              &ioh8514wreghi,
                              NULL));
  }
  AssertRC(VDHInstallIOHook(CURRENT_VDM,
                            A8514_MULTIFUNCTION,
                            1,
                            &ioh8514mfreglo,
                            VDHIIH_NO_SIMULATE));
  AssertRC(VDHInstallIOHook(CURRENT_VDM,
                            A8514_MULTIFUNCTION+1,
                            1,
                            &ioh8514mfreghi,
                            NULL));

}

/***********************************************************************
 *
 * FUNCTION NAME = v8514SetIOHooks()
 *
 * DESCRIPTION   = Set/reset I/O hooks
 *
 *                 This routine installs all the appropriate I/O
 *                 handlers for the current VDM, except pixel transfer port.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514SetIOHooks(FLAGS fl)
{
  register INT i;

  for (i = 0; i < (NElements(aportWReg)); i++)
  {
    if ((i == WREG_PIXELTRANSFER) || (i == WREG_PIXELTRANSFER_EXT))
      continue;
    VDHSetIOHookState(CURRENT_VDM,
                      aportWReg[i],
                      1,
                      &ioh8514wreglo,
                      fl);

    VDHSetIOHookState(CURRENT_VDM,
                      aportWReg[i]+1,
                      1,
                      &ioh8514wreghi,
                      fl);
  }

  VDHSetIOHookState(CURRENT_VDM,
                    A8514_MULTIFUNCTION,
                    1,
                    &ioh8514mfreglo,
                    fl);

  VDHSetIOHookState(CURRENT_VDM,
                    A8514_MULTIFUNCTION+1,
                    1,
                    &ioh8514mfreghi,
                    fl);
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514UpdateModeData()
 *
 * DESCRIPTION   = Update current mode state
 *
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL = TRUE if 8514 mode enabled, FALSE else
 * RETURN-ERROR  =
 *
 ***********************************************************************/

BOOL PRIVENTRY v8514UpdateModeData(HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);

  if (!v8514Accelerated(hvdm))
  {
    pvd->flVDMX2Video &= ~VDMX2_8514IOINIT;

    /*
    ** VGA mode data is to be evaluated.
    */
    return(FALSE);
  }

  else
  {
    pvd->vvMode.vvm_ulAdapter = ADAPTER_8514A;
    pvd->vvMode.vvm_ulFormat = FORMAT_BITMAP;
    pvd->vvMode.vvm_ulDDFormat = 0;
    pvd->vvMode.vvm_flMode = 0;
    pvd->vvMode.vvm_nPlanes = 1;
    pvd->vvMode.vvm_ulCellWidth = 1;
    pvd->vvMode.vvm_ulCellHeight = 1;
    pvd->vvMode.vvm_fSuspended = SUSPEND_NONE;

    if (pvd->flVDMX2Video & VDMX2_8514BLOCKED)
      pvd->vvMode.vvm_fSuspended = SUSPEND_UNSUPPORTED_MODE;

    /*
    ** Perform any mode fixup if needed.
    */
    if (pvd->awreg8514Data[WREG_ADVCTRL]&ADVCTRL_DOTCLOCK)
    {
      if (pvd->vvMode.vvm_nCols == 1024)
        pvd->vvMode.vvm_nRows = 768;
      else if (pvd->vvMode.vvm_nCols == 800)
        pvd->vvMode.vvm_nRows = 600;
    }
    return(TRUE);
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514EnableBankAdd()
 *
 * DESCRIPTION   = Enable Bank addressing. Linear addressing should be
 *                 enabled as well as default aperture at A0000. This
 *                 requires the engine addressability to be disabled for
 *                 older chipsets (<928).
 *
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514EnableBankAdd(HVDM hvdm)
{
  BYTE bData, bIndx;
  register PVDMDATA pvd = pVDMData(hvdm);

  bIndx = INB(PORT_COLRCRTINDX);                // get current contents
  OUTB(PORT_COLRCRTINDX,0x31);                  // memory configuration register
  bData = (BYTE) INB(PORT_COLRCRTDATA) & 0xF6;
  /*
  ** Definition of enhanced mode on dumb-frame buffer adapters is linear
  ** memory configuration using more than 256K of VRAM. On accelerators,
  ** it is that + any memory configuration when engine is enabled.
  */
  if(!(pvd->flVDMXVideo & VDMX_ENHANCEDMODE))
    bData |= 0x1;                               // bit 0 should be 1, bit 3 0
  else
  {
    bData |= 0x9;                               // bit 0 and 3 should be 1
    OUTB(PORT_COLRCRTDATA,bData);
    OUTB(PORT_COLRCRTINDX,0x40);                // system configuration register
    bData = (BYTE) INB(PORT_COLRCRTDATA) & 0xFE;// disable engine reg addressing
    bData |= 0x8;                               // enable fast write buffer
    OUTB(PORT_COLRCRTDATA,bData);
    OUTB(PORT_COLRCRTINDX,0x58);                // linear address control reg
    bData = (BYTE) INB(PORT_COLRCRTDATA) |0x10; // enable linear addressing
  }
  OUTB(PORT_COLRCRTDATA,bData);
  OUTB(PORT_COLRCRTINDX,bIndx);                 // restore the index
}
/***********************************************************************
 *
 * FUNCTION NAME = v8514EnableVGA()
 *
 * DESCRIPTION   = Program ADVANCED FUNCTION register to enable VGA
 *
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514EnableVGA(VOID)
{
  BYTE bData, bIndx;                                            /*          */
  /*
  ** Enable 8514 register addressing.
  */
  bIndx = INB(PORT_COLRCRTINDX);        /* get current index */ /*          */
  OUTB(PORT_COLRCRTINDX,0x40);
  bData = (BYTE) INB(PORT_COLRCRTDATA) | 0x1;
  OUTB(PORT_COLRCRTDATA,bData);
  OUTW(A8514_ADVCTRL,0x2);
  OUTB(PORT_COLRCRTINDX,bIndx);         /* restore the index */ /*          */
}
/***********************************************************************
 *
 * FUNCTION NAME = v8514RestoreIOState()
 *
 * DESCRIPTION   = Transfer virtual I/O state to hardware
 *
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514RestoreIOState(HVDM hvdm)
{
  WORD w, port;
  BYTE bData, bIndx;                                            /*          */
  register INT i;
  register PVDMDATA pvd = pVDMData(hvdm);

  if (!(pvd->flVDMVideo&VDM_FGND))
    return;
  /*
  ** Enable 8514 addressing first.
  */
  bIndx = INB(PORT_COLRCRTINDX);        /* get current index */ /*          */
  OUTB(PORT_COLRCRTINDX,0x40);
  bData = (BYTE) INB(PORT_COLRCRTDATA) | 0x1;
  OUTB(PORT_COLRCRTDATA,bData);
  OUTB(PORT_COLRCRTINDX,bIndx);         /* restore the index */ /*          */
  /*
  ** If no IO trapping, or 8514 not enabled restore only ADVANCE CTRL reg.
  */
  if((pvd->flVDMXVideo & VDMX_NOIOTRAPPING) ||
      !v8514Accelerated(hvdm))
  {
    OUTW(aportWReg[WREG_ADVCTRL],
         pvd->awreg8514Data[WREG_ADVCTRL]);
  }
  else
  {
    /*
    ** 8514 enabled, restore all trapped 8514 registers.
    ** Restore all the word registers.
    */

    for (i = 0; i < NElements(aportWReg); i++)
    {
       w = pvd->awreg8514Data[i];
       port = aportWReg[i];

       if (i == WREG_COMMAND)
          w &= 0x1FFF;                                  // nop command

       if (i != WREG_SHORTSTROKE && i != WREG_PIXELTRANSFER &&
            i != WREG_PIXELTRANSFER_EXT)
       {
          _asm
          {
              mov  dx, word ptr port
              mov  ax,w
              out  dx,ax
          }
       }
    }

    /*
    ** Then restore all the multi-function registers. It is important to
    ** do outw, as index and data have to be kept together. Don't use OUTW macro!
    */

    for (i = 0; i < MFINDX_INDXTOTAL; i++)
    {
       w = pvd->awreg8514MFData[i] | (i << MFINDX_INDXSHIFT);
       _asm
       {
          mov    dx, A8514_MULTIFUNCTION
          mov    ax, w
          out    dx, ax
       }
    }
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514WaitOnEngine()
 *
 * DESCRIPTION   = If 8514 enabled, wait until all pending commands
 *                 have completed. VDM assumed unfrozen.
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        =
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 * PSEUDO CODE
 *  if (Engine Busy)
 *    Mark VDM instance as WAIT_ON_ENGINE
 *    if 8514 ports not trapped
 *      Set8514IO hooks
 *    endif
 *    ResetEventSemaphore (hevEngineBusy)
 *    while (EngineBusy and TotalTimeout < MAX))
 *      WaitEventSemaphore (hevEngineBusy, TimeOut)
 *      Add TimeOut to TotalTimeout
 *    endwhile
 *    if EngineBusy
 *      Reset Engine
 *    endif
 *    Mark VDM instance as not WAIT_ON_ENGINE
 *  endif
 *
 ***********************************************************************/

VOID    PRIVENTRY v8514WaitOnEngine(HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  BYTE bData, bIndx;                                            /*          */
  WORD wData;                                                   /*          */
  ULONG ulTimeOut = 0;


  if (v8514Accelerated(hvdm))
  {
    vvUnLockSVGARegisters();
    /*
    ** Enable 8514 register addressing.
    */
    bIndx = INB(PORT_COLRCRTINDX);      /* get current index */ /*          */
    OUTB(PORT_COLRCRTINDX,0x40);
    bData = (BYTE) INB(PORT_COLRCRTDATA) | 0x1;
    OUTB(PORT_COLRCRTDATA,bData);
    OUTB(PORT_COLRCRTINDX,bIndx);       /* restore the index */ /*          */
    /*
    ** Busy flag should be reset when engine has completed all the pending
    ** commands. However, on some chip revisions, due to the linear addressing being
    ** enabled, FIFO status will not be empty. Our code should never be
    ** checking for the FIFO status.
    ** The VDM will be given a chance to continue running for a while and
    ** clear the engine. This thread blocks on a semaphore, which gets released
    ** by VDM accessing either the status port and finding the engine not busy
    ** or attempting to program the command register.
    ** As there is a chance that VDM will not be doing any of the port access,
    ** once the block times out, engine is checked again and then blocked.
    ** If VDM has accessed the command port and released our semaphore and
    ** the engine is still found busy, that means that an operation which is not
    ** dependant on the app's involvment is in progress, so VDM will be blocked
    ** and we will keep checking the busy flag.
    ** If the time-out accumulated and the engine has not cleared yet,
    ** the state would be assumed beyond help and the engine will be reset.
    */
    while(ulTimeOut < MAX_VDM_WAIT)
    {
        _asm
        {
           mov  dx,A8514_STATUS            ;Command/Flag Register
           in   ax,dx                      ;Get command status
           test ax,STATUS_COMMAND_ACTIVE   ;Command still active?
           jz   EngineClear                ;Engine is not busy
        }
        if (!(vdhBTS(&pvd->flVDMX2Video,
                LOG2(VDMX2_8514ENGBUSY))))
        {
          /*
          ** If ports are not being trapped, setup the hooks.
          ** It has to be done out of the VDM context, so
          ** setup the UpdateContext hook. Carefull with VDM_UPDATEHOOK
          ** flag, do not use it.
          */
          if (pvd->flVDMXVideo & VDMX_NOIOTRAPPING)
            VDHArmContextHook(pvd->hhookUpdateContext,
                              pvd->hvdmVideo);
        }
        ResetEventSem(pvd->hev8514Busy);
        VDHWaitEventSem(pvd->hev8514Busy,
                        MAX_ENGINE_WAIT);  // we don't care if it times out!
        ulTimeOut += MAX_ENGINE_WAIT;
    }
    /*
    ** Being here means that the engine is still not free. The application
    ** may have trapped in its engine servicing code or it trashed the engine
    ** beyond belief. The reset we are attempting is not guaranteed to work,
    ** but this is the best we can do.
    */
    wData = (pvd->awreg8514Data[WREG_SUBSYSCTRL] & 0xBFFF) | 0x8000;
    _asm
    {
        mov  dx, A8514_SUBSYSCTRL          ;Command/Flag Register
        mov  ax, wData                     ; reset data
        out  dx, ax
    }
EngineClear:                               // all clear, switch the VDM into bgnd
    vdhBTR(&pvd->flVDMX2Video,             // reset the flag
            LOG2(VDMX2_8514ENGBUSY));
  }
}
/***********************************************************************
 *
 * FUNCTION NAME = v8514LockIO()
 *
 * DESCRIPTION   = Prevent VDM from doing further I/O. This function is
 *                 different from 8514 one in the sense that it doesn't
 *                 freeze or block the VDM if the IOOWNED bit was already
 *                 set. It instead just sets the bit, so that next port hooks
 *                 EnterIO function can block the VDM.
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = I/O inhibited
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514LockIO(HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);


  /*
  ** Claim the IOOWNED bit
  */
  vdhBTS(&pvd->flVDMX2Video,
         LOG2(VDMX2_8514IOOWNED));
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514UnlockIO()
 *
 * DESCRIPTION   = Allow VDM I/O to proceed
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = I/O may proceed
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514UnlockIO(HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** Release the IOOWNED bit
  */

  pvd->flVDMX2Video &= ~VDMX2_8514IOOWNED;

  if (pvd->flVDMX2Video&VDMX2_8514IOWAKEME)
    PostEventSem(pvd->hev8514WakeUp);
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514EnterIO()
 *
 * DESCRIPTION   = Verify that VDM is allowed to do I/O
 *
 * INPUT         = port == port being accessed
 *                 size == size of port being accessed (1 or 2)
 * OUTPUT        =
 *                 I/O may proceed
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

BOOL PRIVENTRY v8514EnterIO(register ULONG port,ULONG n)
{

  while (vdhBTS(&VDMData.flVDMX2Video,
                LOG2(VDMX2_8514IOOWNED)))
  {
    ResetEventSem(VDMData.hev8514WakeUp);
    VDMData.flVDMX2Video |= (VDMX2_8514IOWAKEME|VDMX2_8514BLOCKED);

    /*
    ** Now that we've armed the WAKEUP semaphore and set the WAKEME
    ** bit, make sure IOOWNED still set;  if it isn't, then we may have
    ** missed the chance to be awakened, not to mention the fact that it
    ** may be moot now anyway
    */

    if (VDMData.flVDMX2Video&VDMX2_8514IOOWNED)
    {

      /*
      ** This will convey to the Shield that the VDM is suspended
      */

      vvUpdateModeData(CURRENT_VDM);

      if (!WaitEventSem(VDMData.hev8514WakeUp))
        return  FALSE;   /* FLAG: -- assume we got ERROR_INTERRUPT  */
                         /*          due to DosKillProcess by Shell */

    }

    VDMData.flVDMX2Video &= ~VDMX2_8514IOWAKEME;
  }

  /*
  ** Check to see if we should disable trapping after first access now
  */

  if ((VDMData.flVDMVideo & VDM_FGND) &&
      (VDMData.flVDMXVideo & VDMX_NOIOTRAPPING))
  {
    ULONG portTry;
    register INT i;


    for (i = 0; i < NElements(aportWReg); i++)
    {
      portTry = aportWReg[i];

      /*
      ** Note that we can never let go of the ADVCTRL port,
      ** otherwise the display ownership logic will be broken
      */

      if (port == portTry && i != WREG_ADVCTRL)
      {
        VDHSetIOHookState(CURRENT_VDM,
                          port,
                          1,
                          &ioh8514wreglo,
                          FALSE);

        if (n > 1)
          ++port;

        else
          return  TRUE;
      }


      if (port == portTry+1)
      {
        VDHSetIOHookState(CURRENT_VDM,
                          port,
                          1,
                          &ioh8514wreghi,
                          FALSE);
        return  TRUE;
      }
    }


    if (port == A8514_MULTIFUNCTION)
    {
      VDHSetIOHookState(CURRENT_VDM,
                        port,
                        1,
                        &ioh8514mfreglo,
                        FALSE);

      if (n > 1)
        ++port;

      else
        return  TRUE;
    }


    if (port == A8514_MULTIFUNCTION+1)
    {
      VDHSetIOHookState(CURRENT_VDM,
                        port,
                        1,
                        &ioh8514mfreghi,
                        FALSE);
      return  TRUE;
    }


  }
  return  TRUE;
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514ExitIO()
 *
 * DESCRIPTION   = Mark VDM I/O completed
 *
 * INPUT         = None
 *
 * OUTPUT        = I/O completed
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514ExitIO()
{
  VDMData.flVDMX2Video &= ~VDMX2_8514IOOWNED;

  if (VDMData.flVDMX2Video&VDMX2_8514IOWAKEME)
    PostEventSem(VDMData.hev8514WakeUp);
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadWRegByteL()
 *
 * DESCRIPTION   = Read low byte of 8514/A word register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads a byte from the lower half of an 8514/A word
 *                 register (ie, one of those listed in aportWReg).
 *                 Note that most of these registers are W/O, so we
 *                 don't expect apps to try this very often.  We may
 *                 also decide to make the W/O ports readable.
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

BYTE HOOKENTRY V8514ReadWRegByteL(ULONG port,PCRF pcrf)
{
  BYTE b;

  /*
  ** Allow some minimal kinds of background access
  */

  if (!(VDMData.flVDMVideo&VDM_FGND))
  {

    if (port == A8514_ERRORTERM)
    {
      return  BYTEOF(VDMData.awreg8514Data[WREG_ERRORTERM],
                     0);
    }


    if (port == A8514_SUBSYSSTATUS)
    {
      return (INB(port)&(SUBSTATUS_MONITORID|SUBSTATUS_MEMORYOPT));
    }

    if (port == A8514_STATUS)
    {
      /*
      ** this limited virtualization says that
      ** the 8514/A is never busy in the background
      */
      return 0;
    }
    if (VDMData.flVDMXVideo & VDMX_INT2F)
    {
      /*
      ** If 2F is registered, VDM is not supposed to be
      ** addressing the hardware, but if they do, let them run
      ** and operate with garbage rather than suspend.
      */
      return 0xFF;
    }
  }
  b = 0xFF;

  if (v8514EnterIO(port,
                   1))
  {
    b = INB(port);
    v8514ExitIO();
  }
  return  b;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteWRegByteL()
 *
 * DESCRIPTION   = Write low byte of 8514/A word register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes a byte to the lower half of an 8514/A word
 *                 register (ie, one of those listed in aportWReg).
 *
 * INPUT         = bLow == output data
 *                 port == port address to write
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteWRegByteL(BYTE bLow,ULONG port,PCRF pcrf)
{
  register INT i;

  /*
  ** Allow some minimal kinds of background access
  */

  if (!(VDMData.flVDMVideo&VDM_FGND))
  {

    if (port == A8514_ERRORTERM)
    {
      BYTEOF(VDMData.awreg8514Data[WREG_ERRORTERM],
             0) = bLow;
      return;
    }
    /*
    ** If BIOS is enabling VGA in the background, don't lock it.
    */
    if((port == A8514_ADVCTRL) && (bLow == 0x2))
    {
      return;
    }
    if (VDMData.flVDMXVideo & VDMX_INT2F)
    {
      /*
      ** If 2F is registered, VDM is not supposed to be
      ** addressing the hardware, but if they do, let them run
      ** and shadow their access.
      */
      for (i = 0; i < NElements(aportWReg); i++)
        if (aportWReg[i] == port)
          break;
      AssertTRUE(i < NElements(aportWReg));

      BYTEOF(VDMData.awreg8514Data[i],
             0) = bLow;
      return;
    }
  }
  if (v8514EnterIO(port,
                   1))
  {
    OUTB(port,
         bLow);

    /*
    ** Now shadow the data
    */

    for (i = 0; i < NElements(aportWReg); i++)

      if (aportWReg[i] == port)
        break;
    AssertTRUE(i < NElements(aportWReg));

    BYTEOF(VDMData.awreg8514Data[i],
           0) = bLow;
    VDMData.flVDMX2Video |= VDMX2_8514IOINIT;
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadWRegByteH()
 *
 * DESCRIPTION   = Read high byte of 8514/A word register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads a byte from the upper half of an 8514/A word
 *                 register (ie, one of those listed in aportWReg).
 *
 *                 Note that most of these registers are W/O, so we
 *                 don't expect apps to try this very often.  We may
 *                 also decide to make the W/O ports readable.
 *
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

BYTE HOOKENTRY V8514ReadWRegByteH(ULONG port,PCRF pcrf)
{
  BYTE b;

    /*
    ** Allow some minimal kinds of background access
    */

  if (!(VDMData.flVDMVideo&VDM_FGND))
  {

    if (port == A8514_ERRORTERM+1)
    {
      return  BYTEOF(VDMData.awreg8514Data[WREG_ERRORTERM],
                     1);
    }


    if (port == A8514_SUBSYSSTATUS+1)
    {
      return 0;                                  /* nothing interesting in
                                                    the high byte           */
    }

    if (port == A8514_STATUS+1)
    {
      /*
      ** Limited virtualization: 8514/A is never busy in the background
      */
      return 0;
    }
    if (VDMData.flVDMXVideo & VDMX_INT2F)
    {
      /*
      ** If 2F is registered, VDM is not supposed to be
      ** addressing the hardware, but if they do, let them run
      ** and operate with garbage rather than suspend.
      */
      return 0xFF;
    }
  }
  else if ((port == A8514_STATUS+1) && (VDMData.flVDMX2Video & VDMX2_8514ENGBUSY))
  {
      b = INB(port);
      /*
      ** VDM is being switched into the background while an
      ** accelerator operation was in the progress. The assumption is
      ** that application will not be programming the accelerator unless
      ** the engine busy flag is reset. So, read access to the status
      ** register is trapped and as soon as the engine busy is reset,
      ** VDM is blocked from doing further IO, as not to have a chance
      ** to program the accelerator again. The shell switching thread is
      ** released by posting the event semaphore.
      */
      if (!(b & 0x2))
      {
         PostEventSem(VDMData.hev8514Busy);
         v8514LockIO(CURRENT_VDM);
      }
  }

  if (v8514EnterIO(port,
                   1))
  {
    b = INB(port);
    v8514ExitIO();
    return  b;
  }
  return(0xFF);

}

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteWRegByteH()
 *
 * DESCRIPTION   = Write high byte of 8514/A word register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes a byte to the upper half of an 8514/A word
 *                 register (ie, one of those listed in aportWReg).
 *
 * INPUT         = bHigh == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteWRegByteH(BYTE bHigh,ULONG port,PCRF pcrf)
{
  register INT i;

  /*
  ** Allow some minimal kinds of background access
  */

  if (!(VDMData.flVDMVideo&VDM_FGND))
  {

    if (port == A8514_ERRORTERM+1)
    {
      BYTEOF(VDMData.awreg8514Data[WREG_ERRORTERM],
             1) = bHigh;
      return ;
    }
    if (VDMData.flVDMXVideo & VDMX_INT2F)
    {
      /*
      ** If 2F is registered, VDM is not supposed to be
      ** addressing the hardware, but if they do, let them run
      ** and shadow their access.
      */
      for (i = 0,port--; i < NElements(aportWReg); i++)
        if (aportWReg[i] == port)
          break;
      AssertTRUE(i < NElements(aportWReg));

      BYTEOF(VDMData.awreg8514Data[i],
             0) = bHigh;
      return;
    }
  }
  else if ((port == A8514_COMMAND+1) && (VDMData.flVDMX2Video & VDMX2_8514ENGBUSY))
  {
      /*
      ** VDM is being switched into the background while an
      ** accelerator operation was in the progress. The application is
      ** programming the engine again without checking the busy status,
      ** possibly because its previous command did not require its involvement.
      ** VDM is blocked from doing further IO, as not to have a chance
      ** to program the accelerator again. The shell switching thread is
      ** released by posting the event semaphore.
      */
      for (i = 0,port--; i < NElements(aportWReg); i++)
        if (aportWReg[i] == port)
          break;
      AssertTRUE(i < NElements(aportWReg));

      BYTEOF(VDMData.awreg8514Data[i],
             0) = bHigh;
      PostEventSem(VDMData.hev8514Busy);
      v8514LockIO(CURRENT_VDM);
  }

  if (v8514EnterIO(port,
                   1))
  {
    OUTB(port,
         bHigh);

    /*
    ** Now shadow the data
    */

    for (i = 0,port--; i < NElements(aportWReg); i++)
      if (aportWReg[i] == port)
        break;
    AssertTRUE(i < NElements(aportWReg));

    BYTEOF(VDMData.awreg8514Data[i],
           1) = bHigh;
    VDMData.flVDMX2Video |= VDMX2_8514IOINIT;
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadWRegWord()
 *
 * DESCRIPTION   = Read 8514/A word register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads from an 8514/A word register (ie, one of those
 *                 listed in aportWReg).
 *
 *                 Note that most of these registers are W/O, so we don't
 *                 expect apps to try this very often.  We may also decide
 *                 to make the W/O ports readable.
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

WORD HOOKENTRY V8514ReadWRegWord(ULONG port,PCRF pcrf)
{
  WORD w = 0xFFFF;

  /*
  ** Allow some minimal kinds of background access
  */

  if (!(VDMData.flVDMVideo&VDM_FGND))
  {

    if (port == A8514_ERRORTERM)
    {
      return  VDMData.awreg8514Data[WREG_ERRORTERM];
    }


    if (port == A8514_SUBSYSSTATUS)
    {

      _asm
      {
         mov  dx,word ptr port
         in   ax,dx
         and  ax,SUBSTATUS_MONITORID+SUBSTATUS_MEMORYOPT
         mov  w,ax
      }
      return  w;
    }


    if (port == A8514_STATUS)
    {
      /*
      ** Limited virtualization: 8514/A is never busy in the background
      */
      return 0;
    }
    if (VDMData.flVDMXVideo & VDMX_INT2F)
    {
      /*
      ** If 2F is registered, VDM is not supposed to be
      ** addressing the hardware, but if they do, let them run
      ** and operate with garbage rather than suspend.
      */
      return 0xFFFF;
    }
  }
  else if ((port == A8514_STATUS) && (VDMData.flVDMX2Video & VDMX2_8514ENGBUSY))
  {
      _asm
      {
         mov  dx,word ptr port
         in   ax,dx
         mov  w,ax
      }
      /*
      ** VDM is being switched into the background while an
      ** accelerator operation was in the progress. The assumption is
      ** that application will not be programming the accelerator again unless
      ** the engine busy flag is reset. So, read access to the status
      ** register is trapped and as soon as the engine busy is reset,
      ** VDM is blocked from doing further IO, as not to have a chance
      ** to program the accelerator again. The shell switching thread is
      ** released by posting the event semaphore.
      */
      if (!(w & STATUS_COMMAND_ACTIVE))
      {
         PostEventSem(VDMData.hev8514Busy);
         v8514LockIO(CURRENT_VDM);
      }
  }

  if (v8514EnterIO(port,
                   2))
  {

    _asm
    {
        mov  dx,word ptr port
        in   ax,dx
        mov  w,ax
    }
    v8514ExitIO();
  }
  return  w;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteWRegWord()
 *
 * DESCRIPTION   = Write 8514/A word register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes to an 8514/A word register (ie, one of those
 *                 listed in aportWReg).
 *
 * INPUT         = wData == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteWRegWord(WORD wData,ULONG port,PCRF pcrf)
{
  register INT i;

  /*
  ** Allow some minimal kinds of background access
  */

  if (!(VDMData.flVDMVideo&VDM_FGND))
  {

    if (port == A8514_ERRORTERM)
    {
      VDMData.awreg8514Data[WREG_ERRORTERM] = wData;
      return ;
    }
    /*
    ** If BIOS is enabling VGA in the background, don't lock it.
    */
    if((port == A8514_ADVCTRL) && (wData == 0x2))
    {
      return ;
    }
    if (VDMData.flVDMXVideo & VDMX_INT2F)
    {
      /*
      ** If 2F is registered, VDM is not supposed to be
      ** addressing the hardware, but if they do, let them run
      ** and shadow their access.
      */
      for (i = 0; i < NElements(aportWReg); i++)
        if (aportWReg[i] == port)
          break;
      AssertTRUE(i < NElements(aportWReg));
      VDMData.awreg8514Data[i] = wData;
      return;
    }
  }
  else if ((port == A8514_COMMAND) && (VDMData.flVDMX2Video & VDMX2_8514ENGBUSY))
  {
      /*
      ** VDM is being switched into the background while an
      ** accelerator operation was in the progress. The application is
      ** programming the engine again without checking the busy status,
      ** possibly because its previous command did not require its involvement.
      ** VDM is blocked from doing further IO, as not to have a chance
      ** to program the accelerator again. The shell switching thread is
      ** released by posting the event semaphore.
      */
      for (i = 0; i < NElements(aportWReg); i++)
        if (aportWReg[i] == port)
          break;
      AssertTRUE(i < NElements(aportWReg));
      VDMData.awreg8514Data[i] = wData;
      PostEventSem(VDMData.hev8514Busy);
      v8514LockIO(CURRENT_VDM);
  }

  if (v8514EnterIO(port,
                   2))
  {

    _asm
    {
        mov  dx,word ptr port
        mov  ax,wData
        out  dx,ax
    }
    /*
    ** Now shadow the data
    */

    for (i = 0; i < NElements(aportWReg); i++)

      if (aportWReg[i] == port)
        break;
    AssertTRUE(i < NElements(aportWReg));

    VDMData.awreg8514Data[i] = wData;
    VDMData.flVDMX2Video |= VDMX2_8514IOINIT;
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadMFRegByteL()
 *
 * DESCRIPTION   = Read low byte of 8514/A multi-function register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads a byte from the lower half of the 8514/A
 *                 multi-function register (ie, one of those listed under
 *                 A8514_MULTIFUNCTION).
 *
 *                 Note that all of the multi-function registers are W/O,
 *                 so we don't expect apps to try this very often.  We may
 *                 also decide to make the W/O ports readable.
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

BYTE HOOKENTRY V8514ReadMFRegByteL(ULONG port,PCRF pcrf)
{
  BYTE b;

  b = 0xFF;

  if(!(VDMData.flVDMVideo & VDM_FGND) && (VDMData.flVDMXVideo & VDMX_INT2F))
  {
    /*
    ** If 2F is registered, VDM is not supposed to be
    ** addressing the hardware, but if they do, let them run
    ** and operate with garbage rather than suspend.
    */
    return b;
  }
  if (v8514EnterIO(port,
                   1))
  {
    b = INB(port);
    v8514ExitIO();
  }
  return  b;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteMFRegByteL()
 *
 * DESCRIPTION   = Write low byte of 8514/A multi-function register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes a byte to the lower half of the 8514/A
 *                 multi-function register (ie, one of those listed
 *                 under A8514_MULTIFUNCTION).
 *
 * INPUT         = bLow == output data
 *                 port == port address to write
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteMFRegByteL(BYTE bLow,ULONG port,PCRF pcrf)
{

  if(!(VDMData.flVDMVideo & VDM_FGND) && (VDMData.flVDMXVideo & VDMX_INT2F))
  {
    /*
    ** If 2F is registered, VDM is not supposed to be
    ** addressing the hardware, but if they do, let them run
    ** and shadow the data only.
    */
    BYTEOF(VDMData.wreg8514MFIndx,
           0) = bLow;
    return;
  }
  if (v8514EnterIO(port,
                   1))
  {
    OUTB(port,
         bLow);

    /*
    ** Now shadow the data
    */

    BYTEOF(VDMData.wreg8514MFIndx,
           0) = bLow;
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadMFRegByteH()
 *
 * DESCRIPTION   = Read high byte of 8514/A multi-function register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads a byte from the upper half of the 8514/A
 *                 multi-function register (ie, one of those listed
 *                 under A8514_MULTIFUNCTION).
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

BYTE HOOKENTRY V8514ReadMFRegByteH(ULONG port,PCRF pcrf)
{
  BYTE b;

  b = 0xFF;

  if(!(VDMData.flVDMVideo & VDM_FGND) && (VDMData.flVDMXVideo & VDMX_INT2F))
  {
    /*
    ** If 2F is registered, VDM is not supposed to be
    ** addressing the hardware, but if they do, let them run
    ** and operate with garbage rather than suspend.
    */
    return b;
  }
  if (v8514EnterIO(port,
                   1))
  {
    b = INB(port);
    v8514ExitIO();
  }
  return  b;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteMFRegByteH()
 *
 * DESCRIPTION   = Write high byte of 8514/A multi-function register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes a byte to the upper half of the 8514/A
 *                 multi-function register (ie, one of those listed under
 *                 A8514_MULTIFUNCTION).
 *
 * INPUT         = bHigh == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteMFRegByteH(BYTE bHigh,ULONG port,PCRF pcrf)
{

  if(!(VDMData.flVDMVideo & VDM_FGND) && (VDMData.flVDMXVideo & VDMX_INT2F))
  {
    /*
    ** If 2F is registered, VDM is not supposed to be
    ** addressing the hardware, but if they do, let them run
    ** and shadow the data only.
    */
    BYTEOF(VDMData.wreg8514MFIndx,
           0) = bHigh;
    VDMData.awreg8514MFData[VDMData.wreg8514MFIndx >> MFINDX_INDXSHIFT] =
       VDMData.wreg8514MFIndx&MFINDX_DATAMASK;
    return;
  }
  if (v8514EnterIO(port,
                   1))
  {
    OUTB(port,
         bHigh);

    /*
    ** Now shadow the data
    */

    BYTEOF(VDMData.wreg8514MFIndx,
           1) = bHigh;
    VDMData.awreg8514MFData[VDMData.wreg8514MFIndx >> MFINDX_INDXSHIFT] =
       VDMData.wreg8514MFIndx&MFINDX_DATAMASK;
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadMFRegWord()
 *
 * DESCRIPTION   = Read 8514/A multi-function register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads from the 8514/A multi-function register (ie,
 *                 one of those listed under A8514_MULTIFUNCTION).
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

WORD HOOKENTRY V8514ReadMFRegWord(ULONG port,PCRF pcrf)
{
  WORD w;

  w = 0xFFFF;

  if(!(VDMData.flVDMVideo & VDM_FGND) && (VDMData.flVDMXVideo & VDMX_INT2F))
  {
    /*
    ** If 2F is registered, VDM is not supposed to be
    ** addressing the hardware, but if they do, let them run
    ** and operate with garbage rather than suspend.
    */
    return w;
  }
  if (v8514EnterIO(port,
                   2))
  {
    _asm
    {
        mov  dx,word ptr port
        in   ax,dx
        mov  w,ax
    }

    v8514ExitIO();
  }
  return  w;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteMFRegWord()
 *
 * DESCRIPTION   = Write 8514/A multi-function register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes to the 8514/A multi-function register (ie, one
 *                 of those listed under A8514_MULTIFUNCTION).
 *
 * INPUT         = wData == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteMFRegWord(WORD wData,ULONG port,PCRF pcrf)
{

  if(!(VDMData.flVDMVideo & VDM_FGND) && (VDMData.flVDMXVideo & VDMX_INT2F))
  {
    /*
    ** If 2F is registered, VDM is not supposed to be
    ** addressing the hardware, but if they do, let them run
    ** and shadow the data only.
    */
    VDMData.wreg8514MFIndx = wData;
    VDMData.awreg8514MFData[VDMData.wreg8514MFIndx >> MFINDX_INDXSHIFT] =
       VDMData.wreg8514MFIndx&MFINDX_DATAMASK;
    return;
  }
  if (v8514EnterIO(port,
                   2))
  {

    _asm
    {
        mov  dx,word ptr port
        mov  ax,wData
        out  dx,ax
    }
    /*
    ** Now shadow the data
    */

    VDMData.wreg8514MFIndx = wData;
    VDMData.awreg8514MFData[VDMData.wreg8514MFIndx >> MFINDX_INDXSHIFT] =
       VDMData.wreg8514MFIndx&MFINDX_DATAMASK;
    v8514ExitIO();
  }
}

#pragma  END_SWAP_CODE



