/*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 = VV8514.C
 *
 * DESCRIPTIVE NAME = Virtual Video Device Driver for the 8514/A
 *
 *
 * VERSION = V2.0
 *
 * DATE      02/07/90
 *
 * DESCRIPTION  This module contains the all 8514/A VDD's code and data.
 *
 *
 * FUNCTIONS    VDDInit()                  V8514 initialization
 *              V8514Create()              VDM creation notification
 *              V8514SetFgnd()             VDM foreground notification
 *              V8514SetBgnd()             VDM background notification
 *              V8514Destroy()             VDM termination notification
 *              V8514VDMContext()          Finish screen-switch in VDM's context
 *              V8514SetIOTrapping         Set per-VDM I/O trapping flag
 *              v8514SetIOHooks()          Install/remove I/O hooks
 *              v8514UpdateScreenState()   Redetermine current screen state
 *              v8514AddEvent()            Add video event to the "event queue"
 *              v8514DeleteEvent()         Delete video event from the "event queue"
 *              v8514RestoreIOState()      Transfer virtual I/O state to hardware
 *              v8514TransferBuffer()      Transfer all pages to/from physical VRAM
 *              v8514DisableDisplay()      Disable display
 *              v8514EnableDisplay()       Enable display
 *              v8514GrowBuffer()          Grow specified VDM's virtual buffer
 *              v8514ShrinkBuffer()        Shrink specified VDM's virtual buffer
 *              V8514SysReqProc()          DosRequestVDD router
 *              v8514SysQueryMode()        Query mode data
 *              v8514SysQueryCursor()      Query cursor data
 *              v8514SysQueryPalette()     Query color data
 *              v8514SysCopyLVB()          Copy rectangle from VDM LVB
 *              v8514SysCopyBitmap()       Copy rectangle from VDM bitmap
 *              V8514DevReqProc()          VDHRequestVDD router
 *              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
 *              v8514CheckDisplayOwner()   Determine if 8514/A display status changing
 *              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
 *              V8514ReadDACMask()         Read 8514/A DAC mask register
 *              V8514WriteDACMask()        Write 8514/A DAC mask register
 *              V8514ReadDACReadIndx()     Read 8514/A DAC read-index register
 *              V8514WriteDACReadIndx()    Write 8514/A DAC read-index register
 *              V8514ReadDACWriteIndx()    Read 8514/A DAC write-index register
 *              V8514WriteDACWriteIndx()   Write 8514/A DAC write-index register
 *              V8514ReadDACData()         Read 8514/A DAC data register
 *              V8514WriteDACData()        Write 8514/A DAC data register
 *              v8514AdvanceDACReadState() Advance DAC read state
 *              V8514Int2FHandler()        INT 2Fh handler
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


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

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif
#define  INCL_DOSERRORS
#include <bseerr.h>
#pragma  BEGIN_SWAP_DATA

 /* Note there are three basic monitor types, and the following limitations:
 **
 ** MONITOR_8503    (3)      640x480, 16/64 colors
 ** MONITOR_8512_3  (4)      640x480, 16/256 colors
 ** MONITOR_8514    (9)      1024x768, 16/256 colors (256 w/memory exp. only)
 */

BOOL ulMachineType;             /*            */
BOOL ulMonitor = MONITOR_8512_3;
BOOL flDualDisplayConf = FALSE; /*            */

 /*
 **  Number of planes is 4 or 8, depending on memory expansion
 */

INT nPlanes = 4;                                 /* physical plane
                                                    configuration           */
INT npgBufferMax;                                /* maximum possible size
                                                    of the save/restore
                                                    buffer                  */

 /*
 ** 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 vv8514p.h, as well as the format of the aawModeInit table (see below).
 */

WORD aportWReg[] =
{
  A8514_HORZTOTAL,                               /* (W/O)                   */
  A8514_HORZDSP,                                 /* (W/O)                   */
  A8514_HORZSSTART,                              /* (W/O)                   */
  A8514_HORZSWIDTH,                              /* (W/O)                   */
  A8514_VERTTOTAL,                               /* (W/O)                   */
  A8514_VERTDSP,                                 /* (W/O)                   */
  A8514_VERTSSTART,                              /* (W/O)                   */
  A8514_VERTSWIDTH,                              /* (W/O)                   */
  A8514_DSPCTRL,                                 /* (W/O)                   */
  A8514_SUBSYSCTRL,                              /* (W/O)                   */
  A8514_ROMPAGESELECT,                           /* (W/O)                   */
  A8514_ADVCTRL,                                 /* (W/O)                   */
  A8514_COMMAND,                                 /* (W/O)                   */
  A8514_CURRENTY,                                /* (R/W)                   */
  A8514_CURRENTX,                                /* (R/W)                   */
  A8514_DESTY_AXSTP,                             /* (W/O)                   */
  A8514_DESTX_DIASTP,                            /* (W/O)                   */
  A8514_ERRORTERM,                               /* (R/W)                   */
  A8514_MAJORAXISCNT,                            /* (W/O)                   */
  A8514_BGNDCOLOR,                               /* (W/O)                   */
  A8514_FGNDCOLOR,                               /* (W/O)                   */
  A8514_WRITEMASK,                               /* (W/O)                   */
  A8514_READMASK,                                /* (W/O)                   */
  A8514_COLORCOMPARE,                            /* (W/O)                   */
  A8514_BGNDMIX,                                 /* (W/O)                   */
  A8514_FGNDMIX,                                 /* (W/O)                   */
  A8514_SHORTSTROKE,                             /* (W/O)                   */
  A8514_PIXELTRANSFER,                           /* (R/W)                   */
} ;

 /*
 ** The following is a complete list of byte registers, which use the
 ** I/O handlers in the corresponding ioh array (aiohBReg).
 */

WORD aportBReg[] =
{
  A8514_DACMASK,                                 /* (R/W-byte)              */
  A8514_DACREADINDEX,                            /* (R/W-byte)              */
  A8514_DACWRITEINDEX,                           /* (R/W-byte)              */
  A8514_DACDATA,                                 /* (R/W-byte-indexed)      */
} ;

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,
} ;

IOH aiohBReg[] =
{
  {
    V8514ReadDACMask,   V8514WriteDACMask
  } ,
  {
    V8514ReadDACReadIndx,                   V8514WriteDACReadIndx
  } ,
  {
    V8514ReadDACWriteIndx,                  V8514WriteDACWriteIndx
  } ,
  {
    V8514ReadDACData,   V8514WriteDACData
  } ,
} ;

 /*
 ** These tables define the register settings for a set of private modes;
 ** the first 9 entries of each table correspond to the first 9 registers
 ** defined in aportWReg, and the 10th entry is for the A8514_ADVCTRL port.
 */

#ifdef   SETMODE_SUPPORT
WORD aawModeInit[MAX_MODES][MAX_INITREGS] =
{
  {
    0x9D,               0x7F,               0x81,               0x16,
    0x660,              0x5FB,              0x600,              0x08,
    0x53,               0x07
  } ,
  {
    0x63,               0x4F,               0x52,               0x2C,
    0x830,              0x779,              0x7A8,              0x22,
    0x41,               0x03
  } ,
  {
    0x63,               0x4F,               0x52,               0x2C,
    0x418,              0x3BB,              0x3D2,              0x22,
    0x43,               0x03
  }
} ;

#endif

 /*
 ** hvddVideo is a handle to all other VDDs registered under the same name.
 */

HVDD hvddVideo = NULL;
PSYSFUN apfnSysReq[] =
{
  v8514SysQueryMode,  v8514SysQueryCursor,
  v8514SysQueryPalette,                   v8514SysCopyLVB,
  v8514SysCopyBitmap
} ;

#ifdef   PROPERTIES
SZ szPropIOTrap = PROP_NAME_8514A_IOTRAP;
SZ szPropInt2F = PROP_NAME_INT2F;                /*                         */
#endif

#ifdef   VDDSTRICT
CHAR szAssertMsg[] = "V8514 assertion failure in %s, line %d\n";
#endif

#ifdef   VDDDEBUG
CHAR szModule[] = "V8514: ";
#endif
#pragma  END_SWAP_DATA
#pragma  BEGIN_SWAP_INSTANCE
PERVDMDATA VDMData;
#pragma  END_SWAP_INSTANCE
#pragma  BEGIN_INIT_CODE

/***********************************************************************
 *
 * FUNCTION NAME = VDDInit()
 *
 * DESCRIPTION   = V8514 initialization
 *
 *                 This routine is defined as the device driver entry
 *                 point, and is called during system initialization.
 *
 * INPUT         = None
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE (ie, hardware missing)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 verify existence of physical hardware
 *                 install event hooks (create/fgnd/bgnd)
 *                 define DosRequestVDD/VDHRequestVDD procedure
 *
 ***********************************************************************/

BOOL EXPENTRY VDDInit(PSZ psz)
{
  INT flCRT;
  register INT i;

  while (psz = STRTOKEN(psz))                           /*            */

    if (STRCOMPARE(psz,
                   "/dual",
                   FALSE) == 0)
      flDualDisplayConf = TRUE;                         /*            */
/*  INT3();                                                                 */
    /*
    ** Begin equipment check
    */
/*  OUTW(A8514_ERRORTERM, 0x5A5A);                                          */
/*  if (INW(A8514_ERRORTERM) != 0x5A5A)                                     */
    _asm                                /*"inpw, outpw" workaround                  */
    {                                                                  /*           */
    mov  dx,A8514_ERRORTERM             ;Error Term Register            ;@V2.0TPL01
    mov  ax,5A5Ah                       ;Equipment check signature      ;@V2.0TPL01
    out  dx,ax                          ;                               ;@V2.0TPL01
    in   ax,dx                          ;                               ;@V2.0TPL01
    cmp  ax,5A5Ah                       ;Found 8514/A?                  ;@V2.0TPL01
    je   DspChk                         ;                               ;@V2.0TPL01
    }                                                                  /*           */

  return  FALSE;

DspChk:                                          /*                         */

  /*
  ** Determine monitor type
  */

  for (i = 0; i < 20; i++)
  {

/*      flCRT = INW(A8514_SUBSYSSTATUS);                                    */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  dx,A8514_SUBSYSSTATUS      ;Status Register                ;@V2.0TPL01
        sub  eax,eax                    ;                               ;@V2.0TPL01
        in   ax,dx                      ;Get monitor type               ;@V2.0TPL01
        mov  flCRT,eax                  ;Save it in flCRT               ;@V2.0TPL01
        }                                                              /*           */

    if (!(flCRT&SUBSTATUS_NOT8514))
    {
      ulMonitor = MONITOR_8514;
      break;
    }
  }

  if (flCRT&SUBSTATUS_8503)
    ulMonitor = MONITOR_8503;

  /*
  ** Determine memory configuration
  */

  if (flCRT&SUBSTATUS_MEMORYOPT)
    nPlanes = 8;
  npgBufferMax = PAGESFROMBYTES(MAX_VRES *MAX_HRES *nPlanes/8);

  /*
  ** Install user event hooks
  */

  VDHInstallUserHook(VDM_CREATE,
                     (PUSERHOOK)V8514Create);
  VDHInstallUserHook(VDM_FOREGROUND,
                     (PUSERHOOK)V8514SetFgnd);
  VDHInstallUserHook(VDM_BACKGROUND,
                     (PUSERHOOK)V8514SetBgnd);
  VDHInstallUserHook(VDM_TERMINATE,
                     (PUSERHOOK)V8514Destroy);

#ifdef   PROPERTIES

     VDHRegisterProperty(szPropIOTrap,           /* property name           */
                           NULL,                 /* no help file            */
                           0,                    /* no help id              */
                           VDMP_BOOL,            /* type                    */
                           VDMP_ORD_OTHER,       /* no ordinal              */

      #ifdef OLD_PROPERTIES
                           0,                    /* modifiable after
                                                    creation                */

   #else
                           VDMP_CREATE,          /* not modifiable after
                                                    creation                */
   #endif
                           (VOID *)TRUE,         /* default                 */
                           NULL,                 /* validation info         */

   #ifdef   OLD_PROPERTIES
                           V8514SetIOTrapping    /* function                */

   #else
       NULL                                      /* no function             */
   #endif
       );
//              return  FALSE;
#endif

  /*
  ** Register as a co-primary Video VDD
  */

  VDHRegisterVDD(VVD_NAME1,
                 V8514SysReqProc,
                 V8514DevReqProc);
  /*
  ** Determine the machine type for future handling of the ROMPAGESELECT register.
  **           
  */
  if (!(ulMachineType = VDHQuerySysValue(0L,VDHGSV_MACHINETYPE)))
        ulMachineType = MACHINE_TYPE_PS2;

  return  TRUE;
}

#pragma  END_INIT_CODE
#pragma  BEGIN_SWAP_CODE

/***********************************************************************
 *
 * FUNCTION NAME = V8514Create()
 *
 * DESCRIPTION   = VDM creation notification
 *
 *                 This registered subroutine is called each time a new
 *                 VDM is created (see VDHInstallUserHook for complete
 *                 semantics).
 *
 *                 Virtual memory buffer(s) are fully allocated at this
 *                 time to insure the system *could* bring the VDM
 *                 foreground later.  If the VDM is being started in the
 *                 foreground, then when V8514SetFgnd grows the buffers,
 *                 effectively nothing happens, since they were already
 *                 grown here;  if the VDM is being started in the
 *                 background, then when V8514SetBgnd shrinks the
 *                 buffers, they will be shrunken normally.  "Spiking"
 *                 the system's memory overcommit in this way should help
 *                 eliminate the chance of an interactive DOS application,
 *                 started in the background, beginning some critical
 *                 operation which must be brought foreground and yet can't.
 *                 Forcing the user to kill off such a VDM is highly
 *                 undesirable.
 *
 *
 * INPUT         = hvdm -> new VDM
 *
 * OUTPUT        = SUCCESS
 *                     TRUE (new VDM accepted)
 *                 FAILURE
 *                     FALSE (ie, not enough memory)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 allocate maximum background storage for VDM
 *                 if error
 *                   fail the creation
 *
 ***********************************************************************/

BOOL EXPENTRY V8514Create(HVDM hvdm)
{

  /*
  ** Allocate all per-VDM semaphores
  */

  if (!CreateMutexSem(&pVDMData(hvdm)->hmx8514State))
    return  FALSE;


  if (!CreateEventSem(&pVDMData(hvdm)->hev8514WakeUp))
    return  FALSE;

  /*
  ** Allocate context hook handle
  */

  if ((VDMData.hhookVDMContext = VDHAllocHook(VDH_CONTEXT_HOOK,
                                              (PFNARM)V8514VDMContext,
                                              0)) == NULL)
    return  FALSE;

  /*
  ** Stash the handle and SGID away immediately
  */

  VDMData.hvdm8514 = hvdm;
  VDMData.sgID8514 = VDHQuerySysValue(hvdm,
                                      VDHLSV_SESSIONID);

#ifdef   PROPERTIES

  /*
  ** Obtain I/O trap property settings
  */

  if (VDHQueryProperty(szPropIOTrap))
#endif
       VDMData.flVDM8514 |= VDM_IOTRAPPED;

#ifdef   PROPERTIES                              /*                         */

  /*
  ** Obtain INT 2F property settings                                        
  */

  if (VDHQueryProperty(szPropInt2F))             /*                         */
    VDMData.flVDM8514 |= VDM_INT2F;              /*                         */
#endif                                           /*                         */

  /*
  ** Because the default state of an 8514/A is
  ** "pass-thru" mode, we are not initially the DSPOWNER
  */

  VDMData.flVDM8514 |= VDM_DEVOWNER;
  VDMData.awreg8514Data[WREG_ADVCTRL] = !ADVCTRL_VGADISABLE|ADVCTRL_OUTPUTPIN|
     !ADVCTRL_DOTCLOCK;

  /*
  ** Provide other minimal default state info
  */

  VDMData.regDACMask8514 = 0xFF;

  /*
  ** Allocate maximum buffer storage for VDM; if fails, fail creation
  */

  if (!v8514GrowBuffer(hvdm))
    return  FALSE;

  /*
  ** Claim the IOOWNED bit
  */

  v8514LockIO(hvdm);

  /*
  ** Install all I/O handlers
  */

  v8514SetIOHooks();

  /*
  ** Get VGA VDD handle, so that we can post video events to it
  */

  if (!hvddVideo)
  {                                              /*                         */
    hvddVideo = VDHOpenVDD(VVD_NAME1);

    /*
    ** register INT 2F notifications                                        
    */

    VDHRegisterInt2FProc((VDHVVD_NOTIFY_ON_OFF|  /*                         */
       VDHVVD_NOTIFY_START|                      /*                         */
       VDHVVD_NOTIFY_END),                       /*                         */
                         V8514Int2FHandler);     /*                         */
    /*
    ** Tell VVGA we are here now                                            
    */

    VDHRequestVDD(hvddVideo,                     /*                         */
                  hvdm,
                  VVDDEVREQ_8514PRESENT,
                  (PVOID)TRUE,
                  NULL);

  }                                              /*                         */
  return  TRUE;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514SetFgnd()
 *
 * DESCRIPTION   = VDM foreground notification
 *
 *                 This registered subroutine is called each time a VDM
 *                 is switched to foreground.  It adjusts the level of
 *                 virtualization so that I/O commands are directed to
 *                 the hardware instead of being virtualized (see
 *                 VDHInstallUserHook for complete semantics).
 *
 * INPUT         = hvdm -> new foreground VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 if no ownership
 *                   return
 *                 set foreground mode
 *                 disable video signal
 *                 update physical video memory with virtual
 *                 update physical registers with virtual state
 *                 enable video signal
 *                 thaw VDM if previously frozen
 *                   due to unsupported background access
 *
 ***********************************************************************/

VOID EXPENTRY V8514SetFgnd(register HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** If we own the device and the VDM is not foreground
  */

  if (pvd->flVDM8514&VDM_DEVOWNER && !vdhBTS(&pvd->flVDM8514,
                                             LOG2(VDM_FGND)))
  {
    RequestMutexSem(pvd->hmx8514State);          /*                         */

    if (!v8514GrowBuffer(hvdm))
    {
      v8514AddEvent(hvdm,
                    VVDEVENT_SWITCHERROR,
                    NULL,
                    0);
      ReleaseMutexSem(pvd->hmx8514State);        /*                         */
      return ;
    }

    if ((pvd->flVDM8514&(VDM_IOINIT|VDM_IOTRAPPED)) == /*                   */
       (VDM_IOINIT|VDM_IOTRAPPED))
    {                                            /*                         */

      /*
      ** Turn the screen off while we're updating things
      */

      v8514DisableDisplay(hvdm);

      /*
      ** Update physical video memory with virtual memory
      */

      v8514TransferBuffer(hvdm,
                          TRUE);

      /*
      ** Update physical registers with virtual I/O state
      */

      v8514RestoreIOState(hvdm);

      /*
      ** Enable the screen
      */

      v8514EnableDisplay(hvdm);
    }

      /*           
      ** Don't restore the ADVCTRL port in dual display configuration
      */
    else if (!flDualDisplayConf)

      /*
      ** Even when the VDM has not modified the 8514/A state, we
      ** must ALWAYS restore the defined setting of the ADVCTRL port
      */

      OUTW(A8514_ADVCTRL,
           pvd->awreg8514Data[WREG_ADVCTRL]);

    /*
    ** allow pre INT 2F notification to change I/O access               
    */

    if (!(pvd->flVDM8514&VDM_INT2F))
    {                                            /*                         */

      /*
      ** Allow the VDM to party direct I/O registers now
      */

      if (!vdhBTS(&pvd->flVDM8514,
                  LOG2(VDM_CONTEXTHOOK)))
        VDHArmContextHook(pvd->hhookVDMContext,
                          hvdm);
    }                                            /*                         */

    /*
    ** Release the IOOWNED bit
    */

    v8514UnlockIO(hvdm);
    ReleaseMutexSem(pvd->hmx8514State);          /*                         */
  }

  /*
  ** Thaw VDM if previously blocked in the background
  */

  if (vdhBTR(&pvd->flVDM8514,
             LOG2(VDM_BLOCKED)))
  {

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

    v8514UpdateScreenState(hvdm);
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514SetBgnd()
 *
 * DESCRIPTION   = VDM background notification
 *
 *                 This registered subroutine is called each time a VDM
 *                 is switched to background.  It adjusts the level of
 *                 virtualization so that I/O commands are virtualized
 *                 instead of being allowed to go to the hardware (see
 *                 VDHInstallUserHook for complete semantics).
 *
 *                 Note that we also leave the video signal OFF after we
 *                 are done exchanging VRAM contents.  This is because,
 *                 although VRAM for the previous session may be fully
 *                 restored, we do not know what the I/O state for that
 *                 session was.  Furthermore, that session may not be
 *                 the one returning to the foreground anyway.  Hence,
 *                 it is best to leave the screen disabled.
 *
 * INPUT         = hvdm -> new background VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 if no ownership
 *                   return
 *                 disable video signal
 *                 save physical video memory to virtual
 *                 set background mode
 *                 thaw VDM if previously frozen
 *                   due to error to growing background buffer
 *
 ***********************************************************************/

VOID EXPENTRY V8514SetBgnd(register HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);

    /*
    ** If we own the device and the VDM is foreground
    */

  if (pvd->flVDM8514&VDM_DEVOWNER && pvd->flVDM8514&VDM_FGND)
  {                                              /*                         */

    /*
    ** Make sure VVGA's INT 2F completes                                    
    */

    pvd->flVDM8514 &= ~VDM_INT2FWAKEME;          /*                         */

    if ((pvd->flVDM8514&(VDM_DSPOWNER|VDM_INT2F)) == /*                     */
       (VDM_DSPOWNER|VDM_INT2F))
    {                                            /*                         */

      if (pvd->flVDM8514&VDM_IOINIT)
      {                                          /*                         */
        ResetEventSem(pvd->hev8514WakeUp);       /*                         */
        VDHWaitEventSem(pvd->hev8514WakeUp,
                        MAX_VDM_WAIT);           /*                         */
      }                                          /*                         */
    }

    else
    {                                            /*                         */

      /*
      ** Trap Pixel Transfer reg. I/O
      */

      if (!vdhBTS(&pvd->flVDM8514,
                  LOG2(VDM_CONTEXTHOOK)))
        VDHArmContextHook(pvd->hhookVDMContext,
                          hvdm);
    }                                            /*                         */
    RequestMutexSem(pvd->hmx8514State);          /*                         */
    pvd->flVDM8514 &= ~VDM_FGND;                 /*                         */

    /*
    ** Claim the IOOWNED bit
    */

    v8514LockIO(hvdm);

    if (pvd->flVDM8514&VDM_IOINIT)
    {

      /*
      ** Redetermine the current screen dimensions
      ** (and we may even decide to reset VDM_IOINIT)
      */

      v8514UpdateScreenState(hvdm);

      if (pvd->flVDM8514&VDM_IOINIT)
      {

        /*
        ** Turn the screen off while we're updating things
        */

        v8514DisableDisplay(hvdm);

        /*
        ** Update virtual video memory with physical memory
        */

        v8514TransferBuffer(hvdm,
                            FALSE);
      }
    }
    ReleaseMutexSem(pvd->hmx8514State);          /*                         */
  }

  /*
  ** Shrink memory buffer down now
  */

  v8514ShrinkBuffer(hvdm);
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514Destroy()
 *
 * DESCRIPTION   = VDM termination notification
 *
 *                 This registered subroutine is called each time an
 *                 existing VDM is destroyed (see VDHInstallUserHook
 *                 for complete semantics).
 *
 * INPUT         = hvdm -> dying VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID EXPENTRY V8514Destroy(HVDM hvdm)
{

  /*
  ** Ignore Int 2F notification                                          
  */

  VDMData.flVDM8514 &= ~VDM_INT2F;               /*                         */

  /*
  ** Flip to background if not already, and free buffer
  */

  VDMData.npgBufferReq = 0;
  V8514SetBgnd(hvdm);

  /*
  ** Deallocate all per-VDM semaphores
  */

  DestroyMutexSem(VDMData.hmx8514State);
  DestroyEventSem(VDMData.hev8514WakeUp);
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514VDMContext()
 *
 * DESCRIPTION   = Finish screen-switch in VDM's context
 *
 *                 This is a context hook to perform I/O trap                           T
 *                 enabling/disabling in the VDM's context.
 *
 * INPUT         = p    == undefined
 *                 pcrf -> VDM register frame (NULL if called from VDD)
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514VDMContext(PVOID p,PCRF pcrf)
{
  BOOL fTrap;

  VDMData.flVDM8514 &= ~VDM_CONTEXTHOOK;

  /*
  ** serialize access to the video state                                    
  */

  RequestMutexSem(VDMData.hmx8514State);         /*                         */
  fTrap = TRUE;

  if (VDMData.flVDM8514&VDM_FGND)
  {                                              /*                         */
    fTrap = FALSE;

    /*
    ** tell VVGA to setup for appropriate repaint flag                      
    */

    if (hvddVideo)                               /*                         */
      VDHRequestVDD(hvddVideo,
                    CURRENT_VDM,                 /*                         */
                    VVDDEVREQ_REPAINT,
                    (PVOID)TRUE,
                    NULL);                       /*                         */
  }                                              /*                         */
  VDHSetIOHookState(CURRENT_VDM,
                    A8514_PIXELTRANSFER,
                    1,
                    &ioh8514wreglo,
                    fTrap);
  VDHSetIOHookState(CURRENT_VDM,
                    A8514_PIXELTRANSFER+1,
                    1,
                    &ioh8514wreghi,
                    fTrap);
  ReleaseMutexSem(VDMData.hmx8514State);         /*                         */
}

#ifdef   OLD_PROPERTIES

 /*
 ** NOTE:  This property has been changed to a create-time only
 **        property, because it does not seem worth the effort at this
 **        time to write the necessary routines to re-enable trapping
 **        for all the ports on the fly...
 */

/***********************************************************************
 *
 * FUNCTION NAME = V8514SetIOTrapping
 *
 * DESCRIPTION   = Set per-VDM I/O trapping flag
 *
 *                 This routine is called when the user alters an
 *                 existing VDM's I/O trapping "advanced property"
 *                 setting.
 *
 * INPUT         = op - operation to perform (set)
 *                 hvdm - target VDM
 *                 cb - length of value
 *                 psz - ptr to BOOL value
 *
 * OUTPUT        = returns 0 (no error)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

ULONGEXPENTRY V8514SetIOTrapping(ULONG op,HVDM hvdm,ULONG cb,PSZ psz)
{
  AssertTRUE(op == VDHPROP_SET);
  SETBIT(pVDMData(hvdm)->flVDM8514,
         VDM_IOTRAPPED,
         (BOOL)psz);
  return 0;
}

#endif

/***********************************************************************
 *
 * FUNCTION NAME = v8514SetIOHooks()
 *
 * DESCRIPTION   = Install/remove I/O hooks
 *
 *                 This routine walks installs all the appropriate I/O
 *                 handlers for the current VDM.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514SetIOHooks()
{
  FLAGS fl;
  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));                 /*                         */

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

  fl = 0;                                        /*                         */

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

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

  /*
  ** Last but not least, install I/O handlers for all byte-size ports
  */

  for (i = 0; i < NElements(aportBReg); i++)
  {
    AssertRC(VDHInstallIOHook(CURRENT_VDM,
                              aportBReg[i],
                              1,
                              &aiohBReg[i],
                              fl));              /*                         */
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514UpdateScreenState()
 *
 * DESCRIPTION   = Redetermine current screen state
 *
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

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

  RequestMutexSem(pvd->hmx8514State);
  pvd->npgBufferReq = 0;

  if (!(pvd->awreg8514Data[WREG_ADVCTRL]&ADVCTRL_VGADISABLE))
  {
    pvd->flVDM8514 &= ~VDM_IOINIT;

    if (pvd->flVDM8514&VDM_BLOCKED)

    /*
    ** We just need to tell the other VDD that we're frozen now
    */

      v8514AddEvent(hvdm,
                    VVDEVENT_MODE,
                    NULL,
                    0);
  }

  else
  {
    pvd->npgBufferReq = npgBufferMax;
    pvd->vvm8514.vvm_ulAdapter = ADAPTER_8514A;
    pvd->vvm8514.vvm_ulFormat = FORMAT_BITMAP;
    pvd->vvm8514.vvm_ulDDFormat = 0;
    pvd->vvm8514.vvm_flMode = 0;
    pvd->vvm8514.vvm_nRows = 480;
    pvd->vvm8514.vvm_nCols = 640;
    pvd->vvm8514.vvm_nPlanes = 1;
    pvd->vvm8514.vvm_nBitCount = 8;
    pvd->vvm8514.vvm_ulCellWidth = 1;
    pvd->vvm8514.vvm_ulCellHeight = 1;
    pvd->vvm8514.vvm_fSuspended = SUSPEND_NONE;

    if (pvd->flVDM8514&VDM_BLOCKED)
      pvd->vvm8514.vvm_fSuspended = SUSPEND_UNSUPPORTED_MODE;

    if (pvd->awreg8514Data[WREG_DSPCTRL]&DSPCTRL_INTERLACE)
    {
      pvd->vvm8514.vvm_nRows = 768;
      pvd->vvm8514.vvm_nCols = 1024;
    }

    if ((pvd->awreg8514Data[WREG_DSPCTRL]&DSPCTRL_CASMASK) == DSPCTRL_CAS2)
    {
      pvd->vvm8514.vvm_nBitCount = 4;
    }

    /*
    ** Refresh mode data for Shield
    */

    v8514AddEvent(hvdm,
                  VVDEVENT_MODE,
                  &pvd->vvm8514,
                  0);
  }
  ReleaseMutexSem(pvd->hmx8514State);
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514UpdateInitialState()
 *
 * DESCRIPTION   = Early detection for enhanced modes for notification
 *                 of mouse and pen related components
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

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

  RequestMutexSem(pvd->hmx8514State);
  pvd->npgBufferReq = 0;

  pvd->npgBufferReq = npgBufferMax;
  pvd->vvm8514.vvm_ulAdapter = ADAPTER_8514A;
  pvd->vvm8514.vvm_ulFormat = FORMAT_BITMAP;
  pvd->vvm8514.vvm_ulDDFormat = 0;
  pvd->vvm8514.vvm_flMode = 0;
  pvd->vvm8514.vvm_nRows = 480;
  pvd->vvm8514.vvm_nCols = 640;
  pvd->vvm8514.vvm_nPlanes = 1;
  pvd->vvm8514.vvm_nBitCount = 8;
  pvd->vvm8514.vvm_ulCellWidth = 1;
  pvd->vvm8514.vvm_ulCellHeight = 1;
  pvd->vvm8514.vvm_fSuspended = SUSPEND_NONE;

  if (pvd->flVDM8514&VDM_BLOCKED)
    pvd->vvm8514.vvm_fSuspended = SUSPEND_UNSUPPORTED_MODE;

  if (pvd->awreg8514Data[WREG_DSPCTRL]&DSPCTRL_INTERLACE)
  {
    pvd->vvm8514.vvm_nRows = 768;
    pvd->vvm8514.vvm_nCols = 1024;
  }

  if ((pvd->awreg8514Data[WREG_DSPCTRL]&DSPCTRL_CASMASK) == DSPCTRL_CAS2)
  {
    pvd->vvm8514.vvm_nBitCount = 4;
  }

  /*
  ** Refresh mode data for Shield
  */

  v8514AddEvent(hvdm,
                  VVDEVENT_MODE,
                  &pvd->vvm8514,
                  0);

  ReleaseMutexSem(pvd->hmx8514State);
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514AddEvent()
 *
 * DESCRIPTION   = Add video event to the "event queue"
 *
 *
 * INPUT         = hvdm   -> VDM
 *                 iEvent == event ordinal
 *                 pEvent -> event data (varies from one event to another)
 *                 flAdd  == Event flags (eg, POSTEVENT_FLUSH)
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514AddEvent(HVDM hvdm,INT iEvent,register PVOID pEvent,FLAGS
                              flAdd)
{
  VVPOST vvp;


  if (hvddVideo)
  {
    vvp.vvp_iEvent = iEvent;
    vvp.vvp_pEvent = pEvent;
    vvp.vvp_flEvent = flAdd|POSTEVENT_ADD;
    VDHRequestVDD(hvddVideo,
                  hvdm,
                  VVDDEVREQ_POSTEVENT,
                  SSToDS(&vvp),
                  NULL);
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514DeleteEvent()
 *
 * DESCRIPTION   = Delete video event from the "event queue"
 *
 *
 * INPUT         = hvdm    -> hvdm
 *                 iEvent  == event ordinal
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514DeleteEvent(HVDM hvdm,INT iEvent)
{
  VVPOST vvp;


  if (hvddVideo)
  {
    vvp.vvp_iEvent = iEvent;
    vvp.vvp_pEvent = NULL;
    vvp.vvp_flEvent = POSTEVENT_DELETE;
    VDHRequestVDD(hvddVideo,
                  hvdm,
                  VVDDEVREQ_POSTEVENT,
                  SSToDS(&vvp),
                  NULL);
  }
}

/***********************************************************************
 *
 * 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;
  register INT i;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** Restore all the word registers
  */

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

    if (i == WREG_COMMAND)
      w &= ~COMMAND_FUNMASK;

    else

      if (i == WREG_DSPCTRL)

        w &= ~DSPCTRL_DSPCTRLMASK;

    if (i != WREG_SHORTSTROKE && i != WREG_PIXELTRANSFER &&
        !((i == WREG_ROMPAGESELECT) && (ulMachineType == MACHINE_TYPE_AT))) //          
      OUTW(aportWReg[i],                                      //          
           w);
  }

  /*
  ** Then restore all the multi-function registers
  */

  for (i = 0; i < MFINDX_INDXTOTAL; i++)
  {
    OUTW(A8514_MULTIFUNCTION,
         pvd->awreg8514MFData[i] | (i << MFINDX_INDXSHIFT));    /*            */
/*       pvd->awreg8514MFData[i]);                                            */
  }

  /*
  ** Lastly, restore the color lookup table and all DAC registers
  */

  OUTB(A8514_DACMASK,
       pvd->regDACMask8514);
  OUTB(A8514_DACWRITEINDEX,
       0);

  for (i = 0; i < MAX_DACREGS; i++)
  {
    OUTB(A8514_DACDATA,
         pvd->adacDACData8514[i].dac_bRed);
    OUTB(A8514_DACDATA,
         pvd->adacDACData8514[i].dac_bGreen);
    OUTB(A8514_DACDATA,
         pvd->adacDACData8514[i].dac_bBlue);
  }
  i = pvd->regDACIndx8514;

  if (pvd->flVDM8514&VDM_DACREADING)
  {
    OUTB(A8514_DACREADINDEX,
         i);

    if (pvd->stateDAC8514 > DAC_DATA0)
      INB(A8514_DACDATA);

    if (pvd->stateDAC8514 > DAC_DATA1)
      INB(A8514_DACDATA);
  }

  else
  {
    OUTB(A8514_DACWRITEINDEX,
         i);
    i &= MAX_DACREGS-1;                          /* insurance! (never
                                                    assume the VDM          */

                                        /* always uses index values in-range)*/

    if (pvd->stateDAC8514 > DAC_DATA0)
      OUTB(A8514_DACDATA,
           pvd->dacDACDataCur8514.dac_bRed);

    if (pvd->stateDAC8514 > DAC_DATA1)
      OUTB(A8514_DACDATA,
           pvd->dacDACDataCur8514.dac_bGreen);
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514TransferBuffer()
 *
 * DESCRIPTION   = Transfer all pages to/from physical VRAM
 *
 *                 This is one of the major steps in moving a VDM from
 *                 background to foreground, or vice versa.  It
 *                 transfers the entire contents of the save/restore
 *                 buffer either to or from 8514/A VRAM.
 *
 * INPUT         = hvdm  -> VDM
 *                 fWrite == TRUE to write to VRAM, FALSE to read
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514TransferBuffer(HVDM hvdm,BOOL fWrite)
{
  INT x,y;
  WORD wCmd,wTmp;
  register PWORD pwData;
  register PVDMDATA pvd = pVDMData(hvdm);

    /*
    ** If I/O trapping is disabled, assume the app will restore itself
    */

  if (!(pvd->flVDM8514&VDM_IOTRAPPED))
    return ;
  pwData = (PWORD)(pvd->pBuffer);

  /*
  ** Wait for previous command to complete
  */
/*  while (INW(A8514_STATUS) & STATUS_COMMAND_ACTIVE)                       */
/*      ;                                                                   */
    _asm                                /*"inpw, outpw" workaround                  */
    {                                                                  /*           */
        mov  dx,A8514_STATUS            ;Command/Flag Register          ;@V2.0TPL01
    CmdWait:                                                            ;@V2.0TPL01
        in   ax,dx                      ;Get command status             ;@V2.0TPL01
        test ax,STATUS_COMMAND_ACTIVE   ;Command still active?          ;@V2.0TPL01
        jnz  CmdWait                    ;Wait until 8514/A is done      ;@V2.0TPL01
    }                                                                  /*           */
/*  OUTW(A8514_MAJORAXISCNT, MAX_HRES-1);                                   */
    _asm                                /*"inpw, outpw" workaround                  */
    {                                                                  /*           */
    mov  dx,A8514_MAJORAXISCNT          ;LX Register                    ;@V2.0TPL01
    mov  ax,MAX_HRES-1                  ;Horizontal limit is 1023       ;@V2.0TPL01
    out  dx,ax                          ;                               ;@V2.0TPL01
    }                                                                  /*           */
/*  OUTW(A8514_WRITEMASK, 0xFFFF);                                          */
    _asm                                /*"inpw, outpw" workaround                  */
    {                                                                  /*           */
    mov  dx,A8514_WRITEMASK             ;Plane WE Register              ;@V2.0TPL01
    mov  ax,0FFFFh                      ;                               ;@V2.0TPL01
    out  dx,ax                          ;                               ;@V2.0TPL01
    }                                                                  /*           */
/*  OUTW(A8514_BGNDMIX, 0x0001);                                            */
/*  OUTW(A8514_FGNDMIX, 0x0001);                                            */
    _asm                                /*"inpw, outpw" workaround                  */
    {                                                                  /*           */
    mov  dx,A8514_BGNDMIX               ;Function 0 Register            ;@V2.0TPL01
    mov  ax,00001h                      ;                               ;@V2.0TPL01
    out  dx,ax                          ;Setup background mix           ;@V2.0TPL01
    mov  dx,A8514_FGNDMIX               ;Function 1 Register            ;@V2.0TPL01
    out  dx,ax                          ;Setup foreground mix           ;@V2.0TPL01
    }                                                                  /*           */
/*  OUTW(A8514_MULTIFUNCTION, MFINDX_MINORAXISCNT | MAX_VRES-1);            */
/*  OUTW(A8514_MULTIFUNCTION, MFINDX_TOPSCISSORS | 0);                      */
/*  OUTW(A8514_MULTIFUNCTION, MFINDX_LEFTSCISSORS | 0);                     */
/*  OUTW(A8514_MULTIFUNCTION, MFINDX_BOTTOMSCISSORS | MAX_VRES-1);          */
/*  OUTW(A8514_MULTIFUNCTION, MFINDX_RIGHTSCISSORS | MAX_HRES-1);           */
/*  OUTW(A8514_MULTIFUNCTION, MFINDX_MEMORYCTRL | MEMCTRL_WRITE8 | MEMCTRL_CAS4);*/
/*  OUTW(A8514_MULTIFUNCTION, MFINDX_PIXELCTRL | 0x0000);                   */
    _asm                                        /*inpw, outpw" workaround */
    {                                                                  /*           */
    mov  dx,A8514_MULTIFUNCTION                 ;Secondary Decodes Reg. ;@V2.0TPL01
    mov  ax,MFINDX_MINORAXISCNT+MAX_VRES-1      ;Vertical limit is 1023 ;@V2.0TPL01
    out  dx,ax                                  ;                       ;@V2.0TPL01
    mov  ax,MFINDX_TOPSCISSORS+0                ;YMIN is 0              ;@V2.0TPL01
    out  dx,ax                                  ;                       ;@V2.0TPL01
    mov  ax,MFINDX_LEFTSCISSORS+0               ;XMIN is 0              ;@V2.0TPL01
    out  dx,ax                                  ;                       ;@V2.0TPL01
    mov  ax,MFINDX_BOTTOMSCISSORS+MAX_VRES-1    ;YMAX is 1023           ;@V2.0TPL01
    out  dx,ax                                  ;                       ;@V2.0TPL01
    mov  ax,MFINDX_RIGHTSCISSORS+MAX_HRES-1     ;XMAX is 1023           ;@V2.0TPL01
    out  dx,ax                                  ;                       ;@V2.0TPL01
    mov  ax,MFINDX_MEMORYCTRL+MEMCTRL_WRITE8+MEMCTRL_CAS4 ;Config =     ;@V2.0TPL01
    out  dx,ax                                  ;interleave and 1K lines;@V2.0TPL01
    mov  ax,MFINDX_PIXELCTRL+0                  ;Mode Register          ;@V2.0TPL01
    out  dx,ax                                  ;                       ;@V2.0TPL01
    }                                                                  /*           */

  wCmd = COMMAND_STROKEALG|COMMAND_DRAW|COMMAND_PCDATAXFER|COMMAND_PCDATA16BIT
     |COMMAND_SWAPBYTES|COMMAND_FUN_LINE;

  if (fWrite)
  {
    wCmd |= COMMAND_WRITE;

/*      OUTW(A8514_FGNDMIX, FGNDMIX_SOURCE | FGNDMIX_PIXELDATA);            */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                               ;@V2.0TPL01
        mov  dx,A8514_FGNDMIX           ;Function 1 Register            ;@V2.0TPL01
        mov  ax,FGNDMIX_SOURCE+FGNDMIX_PIXELDATA                        ;@V2.0TPL01
        out  dx,ax                      ;                               ;@V2.0TPL01
        }                                                              /*           */

  }

  for (y = 0; y < MAX_VRES; y++)
  {

/*      x = 0;                                                              */
/*      OUTW(A8514_CURRENTX, x & (MAX_HRES*2-1));                           */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  dx,A8514_CURRENTX          ;X0 Register                    ;@V2.0TPL01
        sub  ax,ax                      ;X = 0;                         ;@V2.0TPL01
        out  dx,ax                      ;                               ;@V2.0TPL01
        }                                                              /*           */
/*      OUTW(A8514_CURRENTY, y & (MAX_VRES*2-1));                           */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  dx,A8514_CURRENTY          ;Y0 Register                    ;@V2.0TPL01
        mov  eax,y                      ;                               ;@V2.0TPL01
        out  dx,ax                      ;                               ;@V2.0TPL01
        }                                                              /*           */
        /*
        ** Issue command
                                                                            */
/*      OUTW(A8514_COMMAND, wCmd);                                          */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  dx,A8514_COMMAND           ;                               ;@V2.0TPL01
        mov  ax,wCmd                    ;                               ;@V2.0TPL01
        out  dx,ax                      ;                               ;@V2.0TPL01
        }                                                              /*           */

    if (!fWrite)
    {

            /*
            ** Wait for data availability
            */
/*          while (!(INW(A8514_STATUS) & STATUS_DATA_AVAILABLE))            */
/*              ;                                                           */
            _asm                        /*"inpw, outpw" workaround                  */
            {                                                          /*           */
                 mov  dx,A8514_STATUS   ;                               ;@V2.0TPL01
            DataWait:                   ;                               ;@V2.0TPL01
                 in   ax,dx             ;                               ;@V2.0TPL01
                 test ax,STATUS_DATA_AVAILABLE                          ;@V2.0TPL01
                 jz   DataWait          ;Wait until data available      ;@V2.0TPL01
            }                                                          /*           */

      if (nPlanes == 8)
      {

                /*
                ** Read 8-plane data
                */
/*              while (x++ < MAX_HRES/2) {                                  */
/*                  *pwData++ = INW(A8514_PIXELTRANSFER);                   */
/*              }                                                           */
                _asm                    /*"inpw, outpw" workaround                  */
                {                                                      /*           */
                     mov  cx,MAX_HRES/2                 ;512 x 2 pixels ;@V2.0TPL01
                     mov  dx,A8514_PIXELTRANSFER        ;Color 0 Reg.   ;@V2.0TPL01
                n8rPixel:                                               ;@V2.0TPL01
                     in   ax,dx                         ;Read 2 pixels  ;@V2.0TPL01
                     mov  word ptr [pwData],ax          ;Save them      ;@V2.0TPL01
                     add  pwData, 2                     ;Next word      ;@V2.0TPL01
                     dec  cx                            ;Next group of  ;@V2.0TPL01
                     jnz  n8rPixel                      ;  pixels       ;@V2.0TPL01
                }                                                      /*           */

      }

      else
      {

                /*
                ** Read 4-plane data
                */
/*              while (x++ < MAX_HRES/2) {                                  */
/*                  wTmp = INW(A8514_PIXELTRANSFER);                        */
/*                  *(PBYTE)pwData++ = (wTmp & 0x0F) | ((wTmp >> 4) & 0xF0);*/
/*              }                                                           */
                _asm                    /*"inpw, outpw" workaround                  */
                {                                                      /*           */
                    mov  cx,MAX_HRES/2                  ;512 x 2 pixels ;@V2.0TPL01
                    mov  dx,A8514_PIXELTRANSFER         ;               ;@V2.0TPL01
                n4rPixel:                                               ;@V2.0TPL01
                    in   ax,dx                          ;Read 2 pixels  ;@V2.0TPL01
                    mov  wTmp,ax                        ;               ;@V2.0TPL01
                    and  wTmp,0Fh                       ;Preserve pixel low
                    shr  ax,4                           ;Pack pixel high
                    and  ax,0F0h                        ;Preserve pixel high
                    or   ax,wTmp                        ;               ;@V2.0TPL01
                    mov  byte ptr [pwData],al           ;Save it        ;@V2.0TPL01
                    inc  pwData                         ;Next byte      ;@V2.0TPL01
                    dec  cx                             ;Next group of  ;@V2.0TPL01
                    jnz  n4rPixel                       ;  pixels       ;@V2.0TPL01
                }                                                      /*           */

      }
    }

    else
    {

      if (nPlanes == 8)
      {

                /*
                ** Write 8-plane data
                */
/*              while (x++ < MAX_HRES/2) {                                  */
/*                  OUTW(A8514_PIXELTRANSFER, *pwData++);                   */
/*              }                                                           */
                _asm                    /*"inpw, outpw" workaround                  */
                {                                                       ;@V2.0TPL01
                    mov  cx,MAX_HRES/2                  ;Write 2 pixels ;@V2.0TPL01
                    mov  dx,A8514_PIXELTRANSFER         ;Color 0 Reg.   ;@V2.0TPL01
                n8wPixel:                                               ;@V2.0TPL01
                    mov  ax,word ptr [pwData]           ;Get 2 pixels   ;@V2.0TPL01
                    out  dx,ax                          ;               ;@V2.0TPL01
                    add  pwData, 2                      ;Next word      ;@V2.0TPL01
                    dec  cx                             ;Next group of  ;@V2.0TPL01
                    jnz  n8wPixel                       ;  pixels       ;@V2.0TPL01
                }                                                      /*           */

      }

      else
      {

                /*
                ** Write 4-plane data
                */
/*              while (x++ < MAX_HRES/2) {                                  */
/*                  wTmp = *(PBYTE)pwData++;                                */
/*                  OUTW(A8514_PIXELTRANSFER, (wTmp & 0x0F) | ((wTmp & 0xF0) << 4));*/
/*              }                                                           */
                _asm                    /*"inpw, outpw" workaround                  */
                {                                                      /*           */
                    mov  cx,MAX_HRES/2                  ;Write 2 pixel  ;@V2.0TPL01
                    mov  dx,A8514_PIXELTRANSFER         ;               ;@V2.0TPL01
                    sub  ax,ax                          ;               ;@V2.0TPL01
                n4wPixel:                                               ;@V2.0TPL01
                    mov  al,byte ptr [pwData]           ;Get 2 pixels   ;@V2.0TPL01
                    mov  wTmp,ax                        ;               ;@V2.0TPL01
                    and  wTmp,0Fh                       ;preserve pixel low
                    and  ax,0F0h                        ;preserve pixel high
                    shl  ax,4                           ;Unpack pixel high
                    or   ax,wTmp                        ;               ;@V2.0TPL01
                    out  dx,ax                          ;Write two 4-bit pixels
                    inc  pwData                         ;Next byte      ;@V2.0TPL01
                    dec  cx                             ;Next group of  ;@V2.0TPL01
                    jnz  n4wPixel                       ;  pixels       ;@V2.0TPL01
                }                                                      /*           */

      }
    }
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514DisableDisplay()
 *
 * DESCRIPTION   = Disable display
 *
 *                 This turns the video signal off, helping to hide
 *                 unsightly adapter reprogramming.
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514DisableDisplay(HVDM hvdm)
{
  OUTW(A8514_DSPCTRL,
       pVDMData(hvdm)->awreg8514Data[WREG_DSPCTRL]|DSPCTRL_DSPDISABLE);
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514EnableDisplay()
 *
 * DESCRIPTION   = Enable display
 *
 *                 This turns the video signal on, after all video
 *                 adapter reprogramming is complete.  Also, since
 *                 v8514RestoreIOState cannot properly update the
 *                 particular register involved (lest it re-enable the
 *                 screen before we're ready), we take care of that last
 *                 little "bit" of virtual-to-physical restoration here.
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514EnableDisplay(HVDM hvdm)
{
  OUTW(A8514_DSPCTRL,
       pVDMData(hvdm)->awreg8514Data[WREG_DSPCTRL]);
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514GrowBuffer()
 *
 * DESCRIPTION   = Grow specified VDM's virtual buffer
 *
 *                 This routine's purpose is to grow the virtual buffer
 *                 to the maximum possible size needed.
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = SUCCESS
 *                     TRUE
 *                 FAILURE
 *                     FALSE (not enough memory)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

BOOL PRIVENTRY v8514GrowBuffer(HVDM hvdm)
{
  PVOID p;
  BOOL fSuccess = FALSE;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** If I/O trapping is disabled, assume the app will restore itself
  */

  if (!(pvd->flVDM8514&VDM_IOTRAPPED))
    return  TRUE;

  /*
  ** If no plane buffer at all
  */

  if (!pvd->pBuffer)
  {

    /*
    ** Allocate a maximum size buffer
    */

    p = VDHAllocPages(NULL,
                      npgBufferMax,
                      VDHAP_SYSTEM);
  }

  else
  {

    /*
    ** Or grow existing buffer to maximum size
    */

    if (pvd->npgBuffer >= npgBufferMax)
      fSuccess++;

    else
      p = VDHReallocPages(pvd->pBuffer,
                          npgBufferMax,
                          NULL);
  }

  /*
  ** If no error
  */

  if (!fSuccess && p)
  {

    /*
    ** Record (possibly new) address and maxed-out size
    */

    pvd->pBuffer = p;
    pvd->npgBuffer = npgBufferMax;
    fSuccess++;
  }
  return  fSuccess;
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514ShrinkBuffer()
 *
 * DESCRIPTION   = Shrink specified VDM's virtual buffer
 *
 *                This routine's purpose is to shrink the virtual buffer
 *                to a minimum possible size.
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514ShrinkBuffer(HVDM hvdm)
{
  PVOID p;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** If new size is zero
  */

  if (pvd->npgBufferReq == 0 && pvd->pBuffer)
  {

    /*
    ** Free this plane buffer entirely
    */

    VDHFreePages(pvd->pBuffer);

    /*
    ** Record NULL address and zero size
    */

    pvd->pBuffer = NULL;
    pvd->npgBuffer = 0;
  }

  /*
  ** If there's still valid data
  */

  else

    if (pvd->npgBufferReq < pvd->npgBuffer)
    {

    /*
    ** Shrink the plane buffer
    */

      p = VDHReallocPages(pvd->pBuffer,
                          pvd->npgBufferReq,
                          NULL);
      AssertNONZERO(p);

      /*
      ** Record (possibly new) address and minimum size
      */

      pvd->pBuffer = p;
      pvd->npgBuffer = pvd->npgBufferReq;
    }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514SysReqProc()
 *
 * DESCRIPTION   = DosRequestVDD router
 *
 *                 This subroutine is registered during VDD
 *                 initialization via VDHRegisterVDD, and receives
 *                 requests from OS/2 applications.
 *
 * INPUT         = sgid   == screen group
 *                 ulFunc == function code
 *                 nbIn   -> input buffer size (0 if none)
 *                 pIn    -> input buffer
 *                 nbOut  -> output buffer size (0 if none)
 *                 pOut   -> output buffer
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid function, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

LONG EXPENTRY V8514SysReqProc(SGID sgid,ULONG ulFunc,ULONG nbIn,PVOID pIn,
                               ULONG nbOut,PVOID pOut)
{
  HVDM hvdm = INVALID_HVDM;


  if (sgid)
  {
    hvdm = VDHHandleFromSGID(sgid);

    if (hvdm)
    {

      if ((pVDMData(hvdm)->flVDM8514&(VDM_DEVOWNER|VDM_DSPOWNER)) ==
         (VDM_DEVOWNER|VDM_DSPOWNER))

        if (ulFunc >= VVDSYSREQ_QUERYMODE && ulFunc <= VVDSYSREQ_COPYBITMAP)
          return (apfnSysReq[ulFunc-VVDSYSREQ_QUERYMODE])(hvdm,
                                                          nbIn,
                                                          pIn,
                                                          nbOut,
                                                          pOut);
    }
  }
  return  VDDREQ_PASS;
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514SysQueryMode()
 *
 * DESCRIPTION   = Query mode data
 *
 *                 This subroutine processes the VVDSYSREQ_QUERYMODE
 *                 function, filling in the given VVMODE structure.
 *
 * INPUT         = hvdm -> VDM
 *                 pvvm -> video mode packet
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

LONG PRIVENTRY v8514SysQueryMode(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,
                                  PVVMODE pvvm)
{
  register PVDMDATA pvd = pVDMData(hvdm);


  if (ul2 < sizeof(VVMODE) || !pvvm)
    return  ERROR_INVALID_PARAMETER;


  if (!(pvd->flVDM8514&VDM_IOTRAPPED))
    return  ERROR_BAD_FORMAT;

  RequestMutexSem(pvd->hmx8514State);
  *pvvm = pvd->vvm8514;
  v8514DeleteEvent(hvdm,
                   VVDEVENT_MODE);
  ReleaseMutexSem(pvd->hmx8514State);
  return  NO_ERROR;
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514SysQueryCursor()
 *
 * DESCRIPTION   = Query cursor data
 *
 *                 This subroutine processes the VVDSYSREQ_QUERYCURSOR
 *                 function, filling in the given VVCURSOR structure.
 *
 * INPUT         = hvdm -> VDM
 *                 pvvc -> query cursor packet
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

LONG PRIVENTRY v8514SysQueryCursor(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,
                                    PVVCURSOR pvvc)
{

  if (ul2 < sizeof(VVCURSOR) || !pvvc)
    return  ERROR_INVALID_PARAMETER;

  memset(pvvc,
         0,
         sizeof(VVCURSOR));
  return  NO_ERROR;
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514SysQueryPalette()
 *
 * DESCRIPTION   = Query color data
 *
 *                 This subroutine processes the VVDSYSREQ_QUERYPALETTE
 *                 function, filling in the given RGB array.
 *
 * INPUT         = hvdm -> VDM
 *                 prgb -> RGB array
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

LONG PRIVENTRY v8514SysQueryPalette(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,
                                     register PRGB prgb)
{
  LONG rc = NO_ERROR;
  ULONG nColors;
  register INT i;
  register PVDMDATA pvd = pVDMData(hvdm);

  RequestMutexSem(pvd->hmx8514State);
  nColors = 1 << pvd->vvm8514.vvm_nBitCount;

  if (!prgb || ul2 < nColors *sizeof(RGB))
    rc = ERROR_INVALID_PARAMETER;

  else
  {

    /*
    ** A simple memcpy will not suffice, because the
    ** DAC data is stored RGB and required order is BGR
    */

    for (i = 0; i < nColors; i++,
         prgb++)
    {
      prgb->bBlue = pvd->adacDACData8514[i].dac_bBlue;
      prgb->bGreen = pvd->adacDACData8514[i].dac_bGreen;
      prgb->bRed = pvd->adacDACData8514[i].dac_bRed;
    }
    v8514DeleteEvent(hvdm,
                     VVDEVENT_PALETTE);
  }
  ReleaseMutexSem(pvd->hmx8514State);
  return  rc;
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514SysCopyLVB()
 *
 * DESCRIPTION   = Copy rectangle from VDM LVB
 *
 *                 This subroutine processes the VVDSYSREQ_COPYLVB
 *                 function.
 *
 * INPUT         = hvdm -> VDM
 *                 ul1  == size of rect. desc.
 *                 prcl -> rectangle description
 *                 ul2  == size of shadow LVB buffer
 *                 pb   -> shadow LVB buffer
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

LONG PRIVENTRY v8514SysCopyLVB(HVDM hvdm,ULONG ul1,PRECTL prcl,ULONG ul2,PBYTE
                                pb)
{
  return  ERROR_BAD_FORMAT;
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514SysCopyBitmap()
 *
 * DESCRIPTION   = Copy rectangle from VDM bitmap
 *
 *                 This subroutine processes the VVDSYSREQ_COPYBITMAP
 *                 function.
 *
 * INPUT         = hvdm  -> VDM
 *                 ul1   == size of rect. desc.
 *                 pvvr  -> DD format and rectangle
 *                 ul2   == size of shadow LVB buffer
 *                 pbDst -> shadow LVB buffer
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

LONG PRIVENTRY v8514SysCopyBitmap(HVDM hvdm,ULONG ul1,PVVRECT pvvr,ULONG ul2,
                                   register PBYTE pbDst)
{
  INT x,y,nbRow;
  register PBYTE pbSrc;
  PVDMDATA pvd = pVDMData(hvdm);


  if (ul1 < sizeof(VVRECT) || !pvvr)
    return  ERROR_INVALID_PARAMETER;


  if (pvvr->vvr_ulDDFormat || !(pvd->flVDM8514&VDM_IOTRAPPED))
    return  ERROR_BAD_FORMAT;

  RequestMutexSem(pvd->hmx8514State);
  pbSrc = pvd->pBuffer;
  nbRow = pvd->vvm8514.vvm_nCols *pvd->vvm8514.vvm_nBitCount/8;
  y = pvvr->vvr_rcl.yBottom;
  pbSrc += nbRow *y;

  while (y >= pvvr->vvr_rcl.yTop)
  {

    for (x = 0; x < nbRow; x++)
      *pbDst++ = *pbSrc++;
    pbSrc -= nbRow *2;
    y--;
  }
  ReleaseMutexSem(pvd->hmx8514State);
  return  NO_ERROR;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514DevReqProc()
 *
 * DESCRIPTION   = VDHRequestVDD router
 *
 *                 This subroutine is registered during VDD
 *                 initialization via VDHRegisterVDD, and receives
 *                 requests from other VDDs that have have registered
 *                 under the same name and use VDHRequestVDD.  See VVD.H
 *                 for a description of the input/output buffers, if any.
 *
 * INPUT         = hvdm    -> VDM
 *                 ulFunc  == function code
 *                 pbufIn  -> input buffer (not used here)
 *                 pbufOut -> output buffer (not used here)
 *
 * OUTPUT        = SUCCESS
 *                     True
 *                 FAILURE
 *                     False (error code set via VDHSetError)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

LONG EXPENTRY V8514DevReqProc(HVDM hvdm,ULONG ulFunc,PVOID pIn,PVOID pOut)
{
  if (ulFunc == VVDDEVREQ_PM_WINDOW)   /*                                   */
    v8514DevWindowsDeskTop(hvdm,
                          (BOOL)pIn);  /*                                   */
  return  VDDREQ_PASS;         /* pass control to the next VDD in the chain */
}

/***************************************************************************
 *
 * FUNCTION NAME = v8514DevWindowsDeskTop()
 *
 * DESCRIPTION   = Make a windowed VDM Seamless
 *
 *                 This subroutine processes the VVDDEVREQ_PM_WINDOWS
 *                 function, make sure the VDM has full access to the
 *                 8514A hardware and then pass the request down the chain.
 *
 * INPUT         = hvdm -> VDM
 *                 fSet == TRUE to make a windowed VDM SEAMLESS,
 *                         FALSE to release (currently not supported)
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY v8514DevWindowsDeskTop(HVDM hvdm,BOOL fSet)
{                                                     /*                    */
  register PVDMDATA pvd = pVDMData(hvdm);             /*                    */
  INT i;                                              /*                    */

  /*
  ** no-op if VDM is not currently background
  */

  if (!(pvd->flVDM8514 & VDM_FGND))
  {                                                   /*                    */

    /*
    ** mark VDM SEAMLESS
    */

    if (fSet)
    {                                                 /*                    */

      for (i = 0; i < (NElements(aportWReg)); i++)
      {                                               /*                    */

        VDHSetIOHookState(CURRENT_VDM,
                          aportWReg[i],
                          1,
                          &ioh8514wreglo,
                          FALSE);                     /*                    */

        VDHSetIOHookState(CURRENT_VDM,
                          aportWReg[i]+1,
                          1,
                          &ioh8514wreghi,
                          FALSE);                     /*                    */
      }                                               /*                    */

      VDHSetIOHookState(CURRENT_VDM,
                        A8514_MULTIFUNCTION,
                        1,
                        &ioh8514mfreglo,
                        FALSE);                       /*                    */

      VDHSetIOHookState(CURRENT_VDM,
                        A8514_MULTIFUNCTION+1,
                        1,
                        &ioh8514mfreghi,
                        FALSE);                       /*                    */

      /*
      ** Last but not least, install I/O handlers for all byte-size ports
      */

      for (i = 0; i < NElements(aportBReg); i++)
      {                                               /*                    */
        VDHSetIOHookState(CURRENT_VDM,
                          aportBReg[i],
                          1,
                          &aiohBReg[i],
                          FALSE);                     /*                    */
      }                                               /*                    */
    }                                                 /*                    */
  }                                                   /*                    */
}                                                     /*                    */

/***********************************************************************
 *
 * FUNCTION NAME = v8514LockIO()
 *
 * DESCRIPTION   = Prevent VDM from doing further I/O
 *
 * INPUT         = hvdm -> VDM
 *
 * OUTPUT        = I/O inhibited
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

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


  if ((pvd->flVDM8514&(VDM_DSPOWNER|VDM_FROZEN|VDM_IOTRAPPED|VDM_INT2FWAKEME))
     == VDM_DSPOWNER)
  {
    VDHFreezeVDM(hvdm);
    pvd->flVDM8514 |= VDM_FROZEN;
  }

  /*
  ** Claim the IOOWNED bit
  */

  while (vdhBTS(&pvd->flVDM8514,
                LOG2(VDM_IOOWNED)))
  {
    ResetEventSem(pvd->hev8514WakeUp);
    pvd->flVDM8514 |= VDM_IOWAKEME;

    if (pvd->flVDM8514&VDM_IOOWNED)
    {

      if (!VDHWaitEventSem(pvd->hev8514WakeUp,
                           MAX_VDM_WAIT))
        break;           /* FLAG: -- assume we got ERROR_TIMEOUT           */
    }
    pvd->flVDM8514 &= ~VDM_IOWAKEME;
  }
}

/***********************************************************************
 *
 * 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->flVDM8514 &= ~VDM_IOOWNED;

  if (pvd->flVDM8514&VDM_FROZEN)
  {
    VDHThawVDM(hvdm);
    pvd->flVDM8514 &= ~VDM_FROZEN;
  }

  if (pvd->flVDM8514&VDM_IOWAKEME)
    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.flVDM8514,
                LOG2(VDM_IOOWNED)))
  {
    ResetEventSem(VDMData.hev8514WakeUp);
    VDMData.flVDM8514 |= (VDM_IOWAKEME|VDM_BLOCKED);

    /*
    ** 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.flVDM8514&VDM_IOOWNED)
    {

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

      v8514UpdateScreenState(CURRENT_VDM);

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

                                /*           due to DosKillProcess by Shell */

    }

    VDMData.flVDM8514 &= ~VDM_IOWAKEME;
  }

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

  if ((VDMData.flVDM8514&(VDM_FGND|VDM_IOTRAPPED)) == VDM_FGND)
  {
    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;
    }


    for (i = 0; i < NElements(aportBReg); i++)
    {

      if (port == aportBReg[i])
      {
        VDHSetIOHookState(CURRENT_VDM,
                          port,
                          1,
                          &aiohBReg[i],
                          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.flVDM8514 &= ~VDM_IOOWNED;

  if (VDMData.flVDM8514&VDM_IOWAKEME)
    PostEventSem(VDMData.hev8514WakeUp);
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514CheckDisplayOwner()
 *
 * DESCRIPTION   = Determine if 8514/A display status changing
 *
 * INPUT         = wData == new ADVCTRL register
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514CheckDisplayOwner(WORD wData)
{

  if ((wData&ADVCTRL_VGADISABLE) != (VDMData.awreg8514Data[WREG_ADVCTRL]
     &ADVCTRL_VGADISABLE))
  {

    if (hvddVideo)
    {

      if (wData&ADVCTRL_VGADISABLE)
      {

        /*
        ** We're enabling the 8514/A
        */

        VDMData.flVDM8514 |= VDM_DSPOWNER;
        VDHRequestVDD(hvddVideo,
                      CURRENT_VDM,
                      VVDDEVREQ_DSPRELEASE,
                      NULL,
                      NULL);
      }

      else
      {

        /*
        ** We're disabling the 8514/A
        */

        VDHRequestVDD(hvddVideo,
                      CURRENT_VDM,
                      VVDDEVREQ_DSPACCEPT,
                      NULL,
                      NULL);
        VDMData.flVDM8514 &= ~VDM_DSPOWNER;
      }
    }
  }
}

/***********************************************************************
 *
 * 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.flVDM8514&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)
    {
      return 0;                                  /* this limited
                                                    virtualization says
                                                    that                    */

                                /* the 8514/A is never busy in the background*/

    }
  }
  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.flVDM8514&VDM_FGND))
  {

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


      /*
      ** This is a workaround to a Windowed VDM startup problem
      ** on a system with a Western Digital VGA and a Paradise8514/A.
      ** An OUT to the ROM Page Register by the ROM BIOS with a value
      ** of0Eh to enable VGA addrress decode(This sequence has no
      ** effect on Family2machines).So it is safe to just shadow
      ** the value while  theVDM is in background without causing any
      ** harm.
      */

      if (port == A8514_ROMPAGESELECT)
      {                                          /*                         */
        OUTB(port,
             bLow);                              /*                         */

        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));

    if (i == WREG_DSPCTRL)
    {

      if (!(bLow&DSPCTRL_DSPCTRLMASK))
        bLow |= BYTEOF(VDMData.awreg8514Data[i],
                       0)&DSPCTRL_DSPCTRLMASK;
    }

    if (i == WREG_ADVCTRL)
      v8514CheckDisplayOwner((WORD)bLow);
    BYTEOF(VDMData.awreg8514Data[i],
           0) = bLow;
    VDMData.flVDM8514 |= VDM_IOINIT;
    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.flVDM8514&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)
    {
      return 0;                                  /* this limited
                                                    virtualization says
                                                    that                    */

    /*
    ** the 8514/A is never busy in the background
    */

    }
  }
  b = 0xFF;

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

/***********************************************************************
 *
 * 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.flVDM8514&VDM_FGND))
  {

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

  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));

    if (i == WREG_SUBSYSCTRL)
    {

      if (!(bHigh&(SUBCTRL_PIXCTRLMASK >> 8)))
        bHigh |= BYTEOF(VDMData.awreg8514Data[i],
                        1)&(SUBCTRL_PIXCTRLMASK >> 8);

      if (!(bHigh&(SUBCTRL_SEQCTRLMASK >> 8)))
        bHigh |= BYTEOF(VDMData.awreg8514Data[i],
                        1)&(SUBCTRL_SEQCTRLMASK >> 8);
    }
    BYTEOF(VDMData.awreg8514Data[i],
           1) = bHigh;
    VDMData.flVDM8514 |= VDM_IOINIT;
    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;

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

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

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


    if (port == A8514_SUBSYSSTATUS)
    {

/*          return (INW(port) & (SUBSTATUS_MONITORID | SUBSTATUS_MEMORYOPT));*/
            _asm                        /*"inpw, outpw" workaround                  */
            {                                                          /*           */
            mov  edx,port                                               ;@V2.0TPL01
            in   ax,dx                                                  ;@V2.0TPL01
            and  ax,SUBSTATUS_MONITORID+SUBSTATUS_MEMORYOPT             ;@V2.0TPL01
            mov  w,ax                                                   ;@V2.0TPL01
            }                                                          /*           */

      return  w;                                 /*                         */
    }


    if (port == A8514_STATUS)
    {
      return 0;                                  /* this limited
                                                    virtualization says
                                                    that                    */

                                /* the 8514/A is never busy in the background*/

    }
  }
  w = 0xFFFF;

  if (v8514EnterIO(port,
                   2))
  {

/*      w = INW(port);                                                      */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  edx,port                                                   ;@V2.0TPL01
        in   ax,dx                                                      ;@V2.0TPL01
        mov  w,ax                                                       ;@V2.0TPL01
        }                                                              /*           */

#ifdef   FAKE_4PLANE

    if (port == A8514_SUBSYSSTATUS)
    {
      w &= ~(SUBSTATUS_MEMORYOPT|SUBSTATUS_MONITORID);
      w |= SUBSTATUS_8512_3;
    }
#endif
    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.flVDM8514&VDM_FGND))
  {

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

  if (v8514EnterIO(port,
                   2))
  {

/*      OUTW(port, wData);                                                  */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  edx,port                                                   ;@V2.0TPL01
        mov  ax,wData                                                   ;@V2.0TPL01
        out  dx,ax                                                      ;@V2.0TPL01
        }                                                              /*           */
        /*
        ** Now shadow the data
        */

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

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

    if (i == WREG_DSPCTRL)
    {

      if (!(wData&DSPCTRL_DSPCTRLMASK))
        wData |= VDMData.awreg8514Data[WREG_DSPCTRL]&DSPCTRL_DSPCTRLMASK;
    }

    if (i == WREG_SUBSYSCTRL)
    {

      if (!(wData&SUBCTRL_PIXCTRLMASK))
        wData |= VDMData.awreg8514Data[WREG_SUBSYSCTRL]&SUBCTRL_PIXCTRLMASK;

      if (!(wData&SUBCTRL_SEQCTRLMASK))
        wData |= VDMData.awreg8514Data[WREG_SUBSYSCTRL]&SUBCTRL_SEQCTRLMASK;
    }

    if (i == WREG_ADVCTRL)
      v8514CheckDisplayOwner(wData);
    VDMData.awreg8514Data[i] = wData;
    VDMData.flVDM8514 |= VDM_IOINIT;

    if (port == A8514_ADVCTRL)                     /* @82093 */
    {                                              /* @82093 */
    v8514UpdateInitialState(CURRENT_VDM);          /* @82093 */
    }                                              /* @82093 */

    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 (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 (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 (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 (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 (v8514EnterIO(port,
                   2))
  {

/*      w = INW(port);                                                      */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  edx,port                                                   ;@V2.0TPL01
        in   ax,dx                                                      ;@V2.0TPL01
        mov  w,ax                                                       ;@V2.0TPL01
        }                                                              /*           */

    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 (v8514EnterIO(port,
                   2))
  {

/*      OUTW(port, wData);                                                  */
        _asm                            /*"inpw, outpw" workaround                  */
        {                                                              /*           */
        mov  edx,port                                                   ;@V2.0TPL01
        mov  ax,wData                                                   ;@V2.0TPL01
        out  dx,ax                                                      ;@V2.0TPL01
        }                                                              /*           */
        /*
        ** Now shadow the data
        */

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

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadDACMask()
 *
 * DESCRIPTION   = Read 8514/A DAC mask register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads from the 8514/A DAC mask register
 *                 (A8514_DACMASK).
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

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

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

  if (!(VDMData.flVDM8514&VDM_FGND))
  {
    return  VDMData.regDACMask8514;
  }

  b = 0xFF;

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

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteDACMask()
 *
 * DESCRIPTION   = Write 8514/A DAC mask register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes to the 8514/A DAC mask register
 *                 (A8514_DACMASK).
 *
 * INPUT         = bData == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteDACMask(BYTE bData,ULONG port,PCRF pcrf)
{

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

        /*
        ** Now shadow the data
        */

    VDMData.regDACMask8514 = bData;
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadDACReadIndx()
 *
 * DESCRIPTION   = Read 8514/A DAC read-index register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads from the 8514/A DAC read-index register
 *                 (A8514_DACREADINDEX).
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

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

  b = 0xFF;

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

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteDACReadIndx()
 *
 * DESCRIPTION   = Write 8514/A DAC read-index register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes to the 8514/A DAC read-index register
 *                 (A8514_DACREADINDEX).
 *
 * INPUT         = bData == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteDACReadIndx(BYTE bData,ULONG port,PCRF pcrf)
{

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

    /*
    ** Now shadow the data
    */

    VDMData.regDACIndx8514 = bData;
    VDMData.stateDAC8514 = DAC_DATA2;
    VDMData.flVDM8514 |= VDM_DACREADING;
    v8514AdvanceDACReadState();
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadDACWriteIndx()
 *
 * DESCRIPTION   = Read 8514/A DAC write-index register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads from the 8514/A DAC write-index register
 *                 (A8514_DACWRITEINDEX).
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

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

  b = 0xFF;

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

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteDACWriteIndx()
 *
 * DESCRIPTION   = Write 8514/A DAC write-index register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes to the 8514/A DAC write-index register
 *                 (A8514_DACWRITEINDEX).
 *
 * INPUT         = bData == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteDACWriteIndx(BYTE bData,ULONG port,PCRF pcrf)
{

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

        /*
        ** Now shadow the data
        */

    VDMData.regDACIndx8514 = bData;
    VDMData.stateDAC8514 = DAC_DATA0;
    VDMData.flVDM8514 &= ~VDM_DACREADING;
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514ReadDACData()
 *
 * DESCRIPTION   = Read 8514/A DAC data register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 reads from the 8514/A DAC data register
 *                 (A8514_DACDATA).
 *
 * INPUT         = port == port address to read
 *                 pcrf -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

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

  b = 0xFF;

  if (v8514EnterIO(port,
                   1))
  {

    /*
    ** Logically, AdvanceDACReadState happens after the hardware is read,
    **  but since we're only shadowing and not virtualizing, this is OK
    */

    v8514AdvanceDACReadState();
    b = INB(port);
    v8514ExitIO();
  }
  return  b;
}

/***********************************************************************
 *
 * FUNCTION NAME = V8514WriteDACData()
 *
 * DESCRIPTION   = Write 8514/A DAC data register
 *
 *                 This registered subroutine is called whenever a VDM
 *                 writes to the 8514/A DAC data register
 *                 (A8514_DACDATA).
 *
 * INPUT         = bData == output data
 *                 port  == port address to write
 *                 pcrf  -> VDM register frame
 *
 * OUTPUT        = Returns data from port
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID HOOKENTRY V8514WriteDACData(BYTE bData,ULONG port,PCRF pcrf)
{
  register INT i;


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

        /*
        ** Now shadow the data
        */

    BYTEOF(VDMData.dacDACDataCur8514,
           VDMData.stateDAC8514) = bData;

    if (++VDMData.stateDAC8514 > DAC_DATA2)
    {
      i = VDMData.regDACIndx8514++&(MAX_DACREGS-1);
      VDMData.adacDACData8514[i] = VDMData.dacDACDataCur8514;
      VDMData.stateDAC8514 = DAC_DATA0;
    }
    v8514ExitIO();
  }
}

/***********************************************************************
 *
 * FUNCTION NAME = v8514AdvanceDACReadState()
 *
 * DESCRIPTION   = Advance DAC read state
 *
 *                 This adjusts the palette read state to the next
 *                 appropriate state.
 *
 * INPUT         = None
 *
 * OUTPUT        = None
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ***********************************************************************/

VOID PRIVENTRY v8514AdvanceDACReadState()
{
  register INT i;


  if (++VDMData.stateDAC8514 > DAC_DATA2)
  {
    i = VDMData.regDACIndx8514++&(MAX_DACREGS-1);
    VDMData.dacDACDataCur8514 = VDMData.adacDACData8514[i];
    VDMData.stateDAC8514 = DAC_DATA0;
  }
}

/***********************************************************************            
 *                                                                                  
 * FUNCTION NAME = V8514INT2FHANDLER()                                              
 *                                                                                  
 * DESCRIPTION   = INT 2FH HANDLER                                                  
 *                                                                                  
 * INPUT         = HVDM  == VDM HANDLE                                              
 *                 T     == NOTIFICATION TYPE                                       
 *                 PCRF  -> VDM REGISTER FRAME                                      
 *                                                                                  
 * OUTPUT        = NONE                                                             
 *                                                                                  
 * RETURN-NORMAL =                                                                  
 * RETURN-ERROR  =                                                                  
 *
 ***********************************************************************            */
                                                                        /*          */

VOID EXPENTRY V8514Int2FHandler(HVDM hvdm,ULONG t,PCRF pcrf)/*              */
{                                                /*                         */
  register PVDMDATA pvd = pVDMData(hvdm);        /*                         
                                                                            */


  switch (t)
  {                                              /*                         */
    case  VDHVVD_INT2F_OFF :                     /* at OS/2 task time
                                                                            */
      pvd->flVDM8514 &= ~VDM_INT2F;              /*                         */
      break;                                     /*                         */
    case  VDHVVD_INT2F_ON :                      /* at OS/2 task time
                                                                            */
      pvd->flVDM8514 |= VDM_INT2F;               /*                         */
      break;                                     /*                         */
    case  VDHVVD_INT2F_FG_START :                /* at VDM task time
                                                                            */

      /*
      ** grant I/O access 1st on foreground notification                     
      */

      if (!vdhBTS(&pvd->flVDM8514,
                  LOG2(VDM_CONTEXTHOOK)))        /*                         */
        V8514VDMContext(NULL,
                        pcrf);                   /*                         */
      break;                                     /*                         */
    case  VDHVVD_INT2F_BG_END :                  /* at VDM task time
                                                                            */

      /*
      ** Indicate successful return from Int 2F notification                 
      */

      pvd->flVDM8514 &= ~VDM_FGND;               /* need to force it
                                                                            */
      V8514VDMContext(NULL,
                      pcrf);                     /*                         */
      pvd->flVDM8514 |= VDM_INT2FWAKEME;         /*                         */
      PostEventSem(pvd->hev8514WakeUp);          /*                         */
      break;                                     /*                         */
  }                                              /*                         */
}                                                /*                         */
#pragma  END_SWAP_CODE

