/*
** THIS SOFTWARE IS SUBJECT TO COPYRIGHT PROTECTION AND IS OFFERED ONLY
** PURSUANT TO THE 3DFX GLIDE GENERAL PUBLIC LICENSE. THERE IS NO RIGHT
** TO USE THE GLIDE TRADEMARK WITHOUT PRIOR WRITTEN PERMISSION OF 3DFX
** INTERACTIVE, INC. A COPY OF THIS LICENSE MAY BE OBTAINED FROM THE 
** DISTRIBUTOR OR BY CONTACTING 3DFX INTERACTIVE INC(info@3dfx.com). 
** THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 
** EXPRESSED OR IMPLIED. SEE THE 3DFX GLIDE GENERAL PUBLIC LICENSE FOR A
** FULL TEXT OF THE NON-WARRANTY PROVISIONS.  
** 
** USE, DUPLICATION OR DISCLOSURE BY THE GOVERNMENT IS SUBJECT TO
** RESTRICTIONS AS SET FORTH IN SUBDIVISION (C)(1)(II) OF THE RIGHTS IN
** TECHNICAL DATA AND COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013,
** AND/OR IN SIMILAR OR SUCCESSOR CLAUSES IN THE FAR, DOD OR NASA FAR
** SUPPLEMENT. UNPUBLISHED RIGHTS RESERVED UNDER THE COPYRIGHT LAWS OF
** THE UNITED STATES.  
** 
** COPYRIGHT 3DFX INTERACTIVE, INC. 1999, ALL RIGHTS RESERVED
**
** $Header: /cvsroot/glide/glide3x/h3/minihwc/minihwc.c,v 1.1.1.1 1999/11/24 21:45:07 joseph Exp $
** $Log: minihwc.c,v $
** Revision 1.1.1.1  1999/11/24 21:45:07  joseph
** Initial checkin for SourceForge
**
** 
** 27    6/24/99 12:45a Stb_mmcclure
** Modifications to fix PRS 6627. Added hwcUnmapMemory9x for revised 9x
** context switching code.
** 
** 26    6/23/99 5:02p Dow
** Workaround for bad context dword & process Id for map memory
** 
** 25    6/21/99 12:32p Sbrooks
** Removed extraneous P6FENCE
** 
** 24    6/21/99 11:17a Sbrooks
** removed CPUType.  Should have never been added!
** 
** 23    6/21/99 8:12a Stb_sbrooks
** hwcAllocWinFifo command fifo surface description error
** 
** 22    6/17/99 3:06p Stb_sbrooks
** Fixes PRS 6682
** 
** 21    6/16/99 1:32p Atai
** report as Banshee if it is an 8M board
** 
** 20    6/11/99 4:00p Stb_sbrooks
** 
** 18    6/10/99 12:14p Stb_mmcclure
** Modifications for per-process lost context for glide in Win9x
** environments. RT3 Fix PRS 6249.
** 
** 17    5/28/99 11:44a Dow
** tm1/fifo page overlap.  Modified by Dow.  Checked in by Brooks.
** 
** 16    5/27/99 2:56p Atai
** subtract tramoffset when we resize fifo
** 
** 15    5/13/99 5:04p Stb_gkincade
** Remove redundant assignment of vidProcCfg before the video filter
** changes in hwcInitVideo( )
** 
** 13    5/06/99 6:37p Dow
** Fix for no alt-tab in driver
** 
** 143   4/25/99 1:04p Atai
** don't call HWCEXT_RELEASECONTEXT when glide release context in NT. The
** driver will restore vidProcCfg and it corrupt alt-entering full screen
** dos mode.
** 
** 142   4/23/99 5:10p Atai
** nt driver returned lostcontext address, not offset
** 
** 141   4/22/99 4:29p Dow
** Alt-Tab on NT
** 
** 140   4/20/99 10:55a Atai
** protect the dos build
** 
** 139   4/16/99 2:41p Kcd
** Mac fixes.
** 
** 138   4/15/99 5:32p Dow
** Alt TAb for NT + palette fix
** 
** 137   4/09/99 4:22p Dow
** Sandbox issues
** 
** 136   4/08/99 6:15p Atai
** added env var FX_GLIDE_REFRESH
** 
** 135   4/05/99 8:24p Dow
** Alt tab happiness--sorta
** 
** 134   4/05/99 10:38a Atai
** protect window only code
** 
** 133   4/04/99 7:13p Atai
** added cast
** 
** 132   4/04/99 7:14p Sreid
** Plumbing for gc->lostcontext flag
** 
** 129   4/01/99 7:52p Peter
** (temp?) fix for query context extescape return value
** 
** 128   4/01/99 10:30a Peter
** check status enum not boolean
** 
** 127   4/01/99 10:08a Peter
** forgot to protect extEscape, doh!
** 
** 126   3/31/99 9:01p Dow
** context check w/ driver
** 
** 125   3/25/99 2:30p Peter
** surface fifo allocation w/ desktop widths < 1024
** 
** 124   3/24/99 10:19a Peter
** required lockabilly
** 
** 123   3/23/99 6:48p Atai
** use max fifo size if 16M board
** 
** 122   3/17/99 3:46p Dow
** Luke Skywalker
** 
** 121   3/11/99 8:37p Peter
** update vidOverlayEndScreenCoord when the overlay dims != screen dims
** (w/ optional scaling)
** 
** 120   3/10/99 11:52a Peter
** initFifo enable hole counting parameter
** 
** 119   3/09/99 12:31p Kcd
** Added MacOS stuff.  Fixed refresh constant table (mis)use.
** 
** 118   3/05/99 10:06p Peter
** allocate independent state buffers in parallel w/ the command buffers
** 
** 117   3/03/99 3:52p Peter
** removed hemorrhaging GetDC and create glide private dc's for ExtEscape
** 
** 116   3/02/99 8:03p Dow
** 
** 115   2/27/99 12:24p Dow
** New resolutions
** 
** 114   2/22/99 5:06p Peter
** cleaned up monitor enum crap
** 
** 113   2/19/99 2:00p Atai
** fixed lfbBuffAddr for 800x600 triple buffering lfb access
** 
** 112   2/17/99 2:36p Peter
** removed extra dd call, fail agp fifo if no dd agp support, release
** window dc after enum
** 
** 111   2/13/99 1:57p Dow
** Added code for new resolutions
** 
** 110   2/08/99 6:42p Atai
** use the active window display as sst 0
** 
** 109   2/02/99 4:37p Peter
** hwGetSurfaceinfo gets depth, releasing memory fifo creation dd object
** 
** 108   1/25/99 6:37p Peter
** removed redundant macros
** 
** 107   1/22/99 4:28p Dow
** Fixed 1600x1200 the right way.
** 
** 106   1/22/99 3:02p Dow
** Turned off 2x2 filtering in 2x mode
** 
** 105   1/21/99 1:43p Dow
** Fencing for Pallette Download
** 
** 104   1/20/99 6:04p Peter
** cleaned up warning
** 
** 103   1/20/99 1:51p Dow
** Pithy workaround for PCI gamma problem
** 
** 102   1/14/99 11:38a Dow
** Left bits alone in vidproccfg
** 
** 101   1/11/99 8:22p Peter
** fixed stride vs width effage that I somehow convinced myself was right
** 
** 100   1/11/99 6:31p Dow
** Added Debugging
** 
** 99    1/04/99 11:58a Peter
** added windowed context support
** 
** 98    12/23/98 12:39p Peter
** hdc cleanup/hwcext_getlinearaddr is not idempotent
** 
** 97    12/22/98 5:43p Atai
** turn on 2x mode for vidProcCfg if greater than or equal to 1280
** 
** 96    12/22/98 3:29p Jeske
** use h3InitGetMemSize() instead so that we don't have to worry about it
** screwing up dramInit1 like h3InitSgram() did in the past...
** 
** 95    12/22/98 2:46p Atai
** fixed for Banshee/DOS draminit1 value
** 
** 94    12/22/98 1:07p Peter
** all dd surface pointers must go through linear map offset to get their
** hw relative offsets
** 
** 93    12/17/98 2:08p Jeske
** comment fix
** 
** 92    12/16/98 7:13p Peter
** query directdraw object for surface memory area caps and set the
** process id when getting the linear addresses via
** 
** 91    12/11/98 1:35p Peter
** window fifo serial # vs pixel cache
** 
** 90    12/09/98 5:17p Dow
** Fixed video scaling for lame resolutions
** 
** 89    12/07/98 2:18p Dow
** Added debug message for process handle
** 
** 88    12/07/98 11:32a Dow
** Fixed 2X resolutions
** 
** 87    12/06/98 11:04a Dow
** 
** 86    12/04/98 8:42a Dow
** Fixed broken DOS build AGAIN
** 
** 85    12/03/98 10:27p Dow
** Added stuff for hwcGetenv
** 
** 84    12/03/98 9:11p Dow
** Fixed resolution check
** 
** 83    12/02/98 9:35p Dow
** Query Resolutions
** 
** 82    12/02/98 2:41p Jeske
** ugh... text badness
** 
** 81    12/02/98 2:38p Jeske
** don't set the clocks unless they use the environment variable
** overrides, even for banshee...
** 
** 80    12/02/98 2:06p Dow
** Gamma Fully Implemented
** 
** 79    11/30/98 6:53p Peter
** video memory fifo's
** 
** 78    11/24/98 12:25p Mikec
** made multi-mon on win95 happy
** 
** 77    11/24/98 9:43a Atai
** detect multiple cards
** 
** 76    11/23/98 3:28p Atai
** fixed the refresh rate stuff
** 
** 75    11/20/98 4:48p Jeske
** avenger-dos, for now, make sure we don't call h3InitPlls() on avenger..
** 
** 74    11/19/98 1:51p Dow
** Fixed refresh/resolution issue
** 
** 73    11/18/98 8:04p Dow
** grxclk
** 
** 72    11/15/98 2:16a Atai
** set device id = 5. Hack for comdex
** 
** 71    11/10/98 6:29p Atai
** added min_tramSize for board information. min_tramSize = 0x200000 if
** device id is 3 or mem size is 4
** 
** 70    10/30/98 3:44p Dow
** Fixed Tiled/Linear color/aux bug
** 
** 69    10/29/98 5:15p Dow
** Fixed 512x384
** 
** 68    10/21/98 11:22a Peter
** dos happiness w/ the new chris pci library (nee tarolli)
** 
** 67    10/14/98 3:37p Dow
** Gamma stuff
** 
** 66    10/13/98 8:48p Dow
** Env Var for Board mem
** 
** 65    10/08/98 10:15a Dow
** Triple buffering fix
** 
** 64    9/18/98 3:08p Dow
** Fixed DOS build
** 
** 63    9/17/98 3:58p Dow
** Vidmode Stuff
** 
** 62    9/11/98 1:07p Peter
** rounding on raw lfb port addr
** 
** 61    9/02/98 1:34p Peter
** watcom warnings
** 
** 60    8/25/98 6:48p Dow
** Added scaling for low-res
** 
** 59    8/25/98 3:07p Dow
** Checked res of hwcRLSEXCLUSIVE
** 
** 58    8/06/98 7:49p Dow
** Moved detection of SDRAM
** 
** 57    8/02/98 5:00p Dow
** Glide Surface Extension
** 
** 56    7/29/98 3:34p Dow
** 
** 55    7/29/98 3:09p Dow
** SDRAM Fixes
** 
** 54    7/24/98 6:34p Dow
** Fixed DOS Build
** 
** 53    7/24/98 2:02p Dow
** AGP Stuff
** 
** 52    7/23/98 1:18a Dow
** Bump & Grind
** 
** 51    7/18/98 12:21a Jdt
** added state abuffer
** 
** 50    7/16/98 10:26p Dow
** GIW Stuff
** 
** 49    7/16/98 2:53p Dow
** Removed useless colBufferAddr frmo surface info
** 
** 48    7/15/98 4:09p Dow
** GIW Stuff & DOS Protection
** 
** 47    7/14/98 7:19p Dow
** protected some win32 stuff
** 
** 46    7/13/98 10:35p Jdt
** Implemented hwcAllocWinFifo, hwcExecuteWinFifo.
** 
** 45    7/08/98 5:45p Dow
** Moved fifo back up 
** 
** 44    7/02/98 12:11p Dow
** LFB fixes
** 
** 43    6/30/98 10:28a Dow
** Fixed fouled checkin
** 
** 41    6/25/98 6:45p Jdt
** Changes to remove the evil of dxdrvr.c
** 
** 40    6/25/98 12:16p Dow
** Added (protected) alternate FIFO layout
** 
** 39    6/16/98 6:11p Dow
** Rearranged texture memory
** 
** 38    6/16/98 9:49a Dow
** Fixed protected stuff
** 
** 37    6/16/98 9:35a Dow
** Comment -> GDBG_INFO conversion
** 
** 36    6/12/98 10:09a Jdt
** Fix broken dos build.
** 
** 35    6/11/98 7:44p Jdt
** Win98/NT5 Mulitmon 1st Pass
** 
** 34    6/10/98 9:49a Peter
** lfb buffer addressing
** 
** 33    6/09/98 10:09a Dow
** Init register performance tweaks.
** 
** 32    6/06/98 11:42a Dow
** Better buffer alignment for all resolutions.
** 
** 31    6/06/98 10:11a Dow
** Changed AUXPAD from 0 to 0x1000 to offset aux buffer.
** 
** 30    6/05/98 7:34p Dow
** Fixed 8x6-12x10 resolutions
** 
** 29    6/05/98 6:21p Jeske
** now everyone will dxClose(), not just windows
** 
** 28    6/05/98 6:37p Dow
** 1600x1200 perf tuning
** 
** 27    6/05/98 5:28p Jeske
** apparently we need to not call h3InitSetVideoMode() for this to work,
** will this work in non640x480 resolutions? we may need to fix that later
** 
** 26    6/05/98 3:27p Dow
** 1600x1200 Works
** 
** 25    6/04/98 9:36p Jeske
** now we leave VGA legacy decoding on in hwcInitRegisters so we can set
** video modes
** 
** 24    6/04/98 6:52p Dow
** Resolutions to 1600x1200
** 
** 23    6/03/98 1:52p Jeske
** added code to convert from Glide's ordinal refresh number to a refresh
** rate in Hz when calling h3InitSetVideoMode(). We really should specify 
** 
** 22    6/01/98 5:48p Jeske
** allow h3InitSetVideoMode to fail and handle it...
** 
** 21    5/31/98 9:02a Dow
** 800x600 Resolution
** 
** 20    5/28/98 6:34p Dow
** Fixed top scanline bug
** 
** 19    5/27/98 3:35p Mc
** Removed blocks of HW_IO_LOAD and HW_IO_STORE that chris put in.
** 
** 18    5/20/98 8:13p Dow
** device rev
** 
** 17    5/20/98 4:16p Dow
** Added env var for FIFO size
** 
** 16    5/15/98 2:46p Dow
** Attempt to patch back regs on NT
** 
** 15    5/13/98 4:26p Dow
** Protected obsolete IO code that was bodging NT
** 
** 14    4/27/98 2:30p Dow
** OpenProcess stuff
** 
** 13    4/22/98 5:29p Dow
** Added calls to HWCEXT_HWCSETEXCLUSIVE and HWCEXT_HWCRLSEXCLUSIVE
** 
** 12    4/16/98 10:14p Dow
** EXT_HWC is default init method
** 
** 11    4/13/98 5:49p Dow
** Turning theory into practice
** 
** 10    4/09/98 9:34p Dow
** synched with change in h3cinit
** 
** 9     4/08/98 12:25p Dow
** LfbMemoryConfig fix
** 
** 8     4/07/98 10:40p Dow
** LFB Fixes
** 
** 7     4/05/98 2:19p Dow
** DOS/WIN32 stepping on each others' dicks
** 
** 6     4/03/98 2:03p Dow
** DOS Glide modes
** 
** 5     3/28/98 10:51a Dow
** Fixes for FIFO bug
** 
** 4     3/20/98 1:11p Dow
** Now checking revision of chip
** 
** 3     3/20/98 11:43a Dow
** fifo placement for windows
** 
** 2     3/11/98 8:27p Dow
** WinGlide
** 
** 1     3/04/98 4:13p Dow
**
*/
#if !defined(GDBG_INFO_ON) || (GDBG_INFO_ON == 0)
#if defined(GDBG_INFO_ON)
#undef GDBG_INFO_ON
#endif /* defined(GDBG_INFO_ON) */
#define GDBG_INFO_ON
#endif /* !defined(GDBG_INFO_ON) || (GDBG_INFO_ON == 0) */

#include <stddef.h>
#include <stdlib.h>
#include <math.h>

#include <3dfx.h>

#ifdef HWC_EXT_INIT
#include "hwcext.h"
#else
#include <fxpci.h>
#endif

#include <h3cinit.h>
#include <minihwc.h>
#include "hwcio.h"
#include "setmode.h"

#ifdef __WIN32__
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <ddraw.h>
#include "qmodes.h"
#define IS_32
#define Not_VxD
#include <minivdd.h>
#include <vmm.h>
#include <configmg.h>

#endif

#ifdef macintosh
#include <GraphicsPrivHwc.h>
#include <mac_sfc.h>
#endif

#ifdef GETENV
#undef GETENV
#endif

#define GETENV hwcGetenv

#define MAXFIFOSIZE        0x40000
#define MAXFIFOSIZE_16MB   0xff000

#if __POWERPC__ && PCI_BUMP_N_GRIND
#undef MAXFIFOSIZE_16MB
#define MAXFIFOSIZE_16MB   MAXFIFOSIZE
#endif

static hwcInfo hInfo;
static char errorString[1024];
static FxU32 fenceVar;

static FxU32 ProcessID;

#if defined(__WATCOMC__)
/*
 *  P6 Fence
 * 
 *  Here's the stuff to do P6 Fencing.  This is required for the
 *  certain things on the P6
 *
 * dpc - 21 may 1997 - FixMe!
 * This was yoinked from sst1/include/sst1init.h, and should be
 * merged back into something if we decide that we need it later.
 */
void 
p6Fence(void);
#pragma aux p6Fence = \
"xchg eax, fenceVar" \
modify [eax];


#define P6FENCE p6Fence()
#elif defined(__MSC__)
#define P6FENCE { __asm xchg eax, fenceVar }
#elif defined(__POWERPC__) && defined(__MWERKS__)
#define P6FENCE __eieio()
#else
#error "P6 Fencing in-line assembler code needs to be added for this compiler"
#endif /* Compiler specific fence commands */

/* Hack */
#if macintosh
#define __DOS32__ 1
#endif

static FxU32
dummyContextDWORD;

static void
lostContext(void);

static FxU32
pow2Round(FxU32 val, FxU32 roundVal);

static FxU32
hwcBufferLfbAddr(FxU32 bufNum, 
                 const hwcBoardInfo* bInfo,
                 FxBool colBufAlignP);

static FxU32
calcBufferSize(FxU32 xres, FxU32 yres, FxBool tiled);  

static FxU32
calcBufferStride(FxU32 xres, FxBool tiles);

static FxU32
calcBufferSizeInTiles(FxU32 xres, FxU32 yres);

static FxU32
calcBufferHeightInTiles(FxU32 yres);

static FxBool resolutionSupported[HWC_MAX_BOARDS][0xF];

/*
**  Function Prototypes
*/
static hwcBoardInfo *curBI;

#ifdef HWC_EXT_INIT

typedef void *HMONITOR;
typedef BOOL (CALLBACK* MONITORENUMPROC)(HMONITOR, HDC, LPRECT, LPARAM);
typedef WINUSERAPI BOOL WINAPI
EnumDisplayMonitors_func( HDC             hdc,
                          LPCRECT         lprcClip,
                          MONITORENUMPROC lpfnEnum,
                          LPARAM          dwData);


typedef struct {
  HDC dc;
  HMONITOR mon;
} DevEnumRec;
static int num_monitor = 0;

extern FxI32 _cpu_detect_asm(void);


static BOOL CALLBACK 
monitorEnum( HMONITOR handle, HDC dc, LPRECT rect, LPARAM param ) 
{
  BOOL rv = TRUE;
  hwcExtRequest_t
    ctxReq;
  hwcExtResult_t
    ctxRes;

  ctxReq.which = HWCEXT_GETDEVICECONFIG;

  GDBG_INFO(80, "monitorEnum:  ExtEscape:HWCEXT_GETDEVICECONFIG\n");
  if (  ExtEscape(dc, EXT_HWC, sizeof(ctxReq), (LPSTR) &ctxReq, sizeof(ctxRes), (LPSTR) &ctxRes) ) {
    if ( ctxRes.optData.deviceConfigRes.vendorID == 0x121a ) {
      DevEnumRec* 
        data = (DevEnumRec*)param;
      LPCSTR
        drvName = "DISPLAY",
        devName = NULL;

      /* If we're on a multi-mon capable system then we may have
       * different display type devices so we have to get the device
       * name explicitly for the CreateDC call.
       */
      if (handle != NULL) {
#define CCHDEVICENAME 32
        typedef struct {
          DWORD cbSize;
          RECT  rcMonitor;
          RECT  rcWork;
          DWORD dwFlags;
          TCHAR szDevice[CCHDEVICENAME];
        } MONITORINFOEX, *LPMONITORINFOEX;
        typedef BOOL (CALLBACK* GetMonitorInfoProc)(HMONITOR, LPMONITORINFOEX);
        static GetMonitorInfoProc monitorInfoProc = NULL;

        if (monitorInfoProc == NULL) {
          HMODULE user32 = GetModuleHandle("user32");

          if (user32 != NULL) {
            monitorInfoProc = (GetMonitorInfoProc)GetProcAddress(user32, "GetMonitorInfoA");
          }
        }

        if (monitorInfoProc != NULL) {
          MONITORINFOEX monInfo;

          monInfo.cbSize = sizeof(monInfo);
          if ((*monitorInfoProc)(handle, &monInfo)) {
            devName = monInfo.szDevice;
            drvName = NULL;
          }
        }
      }

      /* Make a private 'copy' of the dc so that we're not affected by
       * other people dorking with dc's etc. 
       *
       * FixMe: Is there a better way to do this? I did not see a
       * CopyDC or anything like that.  
       */
      dc = CreateDC(drvName,
                    devName,
                    NULL,
                    NULL);

      data[num_monitor].dc = dc;
      data[num_monitor].mon = handle;      

      num_monitor++;
      rv = (num_monitor < HWC_MAX_BOARDS);
    }
  }

  return rv;
} /* monitorEnum */

/*
** Use the active window display as the first sst device
*/
static BOOL CALLBACK 
displayMonitor( HMONITOR handle, HDC dc, LPRECT rect, LPARAM param ) 
{
  BOOL rv = TRUE;
  hwcExtRequest_t
    ctxReq;
  hwcExtResult_t
    ctxRes;

  ctxReq.which = HWCEXT_GETDEVICECONFIG;

  GDBG_INFO(80, "displayMonitor:  ExtEscape:HWCEXT_GETDEVICECONFIG\n");
  if (  ExtEscape(dc, EXT_HWC, sizeof(ctxReq), (LPSTR) &ctxReq, sizeof(ctxRes), (LPSTR) &ctxRes) ) {
    if ( ctxRes.optData.deviceConfigRes.vendorID == 0x121a ) {
      DevEnumRec*
        data   = (DevEnumRec*) param;
      FxI32 
        i;

      for (i = 0; i < num_monitor; i++) {
        HDC      *return_dc   = ( HDC* ) data + (2*i);
        HMONITOR *return_hmon = ( HMONITOR* ) data + (2*i+1);

        if ((data[i].dc == dc) && (data[i].mon == handle)) {
          data[i].dc = data[0].dc;
          data[i].mon = data[0].mon;

          data[0].dc = dc;
          data[0].mon = handle;

          break;
        }
      }
      rv = FALSE;
    }
  } 
  return rv;
} /* displayMonitor */
#endif

hwcInfo *
hwcInit(FxU32 vID, FxU32 dID)
{
#define FN_NAME "hwcInit"
#ifdef HWC_EXT_INIT
  {
    DevEnumRec
      data[HWC_MAX_BOARDS*2];
    int monitor;

    GDBG_INFO(80, "%s\n", FN_NAME);
    errorString[0] = '\0';

    /* find glide compatible devices */
    GDBG_INFO(80, "%s:  Finding Glide compatible devices\n", FN_NAME);
    {
      /* Grab the DC of the Desktop. */
      HDC hdc = GetDC(NULL);
      HMODULE user32 = GetModuleHandle( "user32.dll" );

      for (monitor = 0; monitor < HWC_MAX_BOARDS; monitor++) {
        data[monitor].dc  = NULL;
        data[monitor].mon = NULL;
      }
      num_monitor = 0;

      if ( user32 ) {
        EnumDisplayMonitors_func*
          enumDisplayMonitors = (void*)GetProcAddress( user32, "EnumDisplayMonitors" );
        
        if ( enumDisplayMonitors ) { 
          HWND
            curWindow = GetActiveWindow();
          
          GDBG_INFO(80, "%s:  multi-monitor capable OS ( NT5/W98 )\n", FN_NAME);
          enumDisplayMonitors( hdc, 0, monitorEnum, (LPARAM)data );
          
          /*
          ** use the active window display (if there is one yet
          ** associated w/ the current thread) as sst 0 
          */
          if (curWindow != NULL) {
            HDC curWindowDC = GetDC(curWindow);
            
            if (curWindowDC != NULL) {
              enumDisplayMonitors( curWindowDC, 0, displayMonitor, (LPARAM)data );
              ReleaseDC(curWindow, curWindowDC);
            }
          }
        } else { /* for win95/nt4, assume we have one board */
          monitorEnum(NULL, hdc, NULL, (LPARAM)&data);
        }
      }

      ReleaseDC(NULL, hdc);
    }

    hInfo.nBoards = 0;
    for (monitor = 0; monitor < num_monitor; monitor++) {
      hwcExtRequest_t
        ctxReq;
      hwcExtResult_t
        ctxRes;
      HDC 
        hdc = data[monitor].dc;
      HMONITOR
        hmon = data[monitor].mon;
      int 
        status;

      /* Allocate a context with the Driver */      
      ctxReq.which = HWCEXT_ALLOCCONTEXT;
      ctxReq.optData.allocContextReq.protocolRev = HWCEXT_PROTOCOLREV;
      ctxReq.optData.allocContextReq.appType = HWCEXT_ABAPPTYPE_FSEM;
      
      GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_ALLOCCONTEXT\n");
      status = ExtEscape(hdc, EXT_HWC, 
                         sizeof(ctxReq), (LPSTR) &ctxReq,
                         sizeof(ctxRes), (LPSTR) &ctxRes); 
      
      hInfo.nBoards++;
      hInfo.boardInfo[monitor].boardNum     = monitor;
      hInfo.boardInfo[monitor].hdc          = hdc;
      hInfo.boardInfo[monitor].hMon         = hmon;
      hInfo.boardInfo[monitor].extContextID = ctxRes.optData.allocContextRes.contextID;
      
      ctxReq.which = HWCEXT_GETDEVICECONFIG;

      GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_GETDEVICECONFIG.\n");
      status = ExtEscape(hdc, EXT_HWC, 
                         sizeof(ctxReq), (void *) &ctxReq,
                         sizeof(ctxRes), (void *) &ctxRes); 
      
      hInfo.boardInfo[monitor].pciInfo.initialized = 1;
      hInfo.boardInfo[monitor].pciInfo.vendorID =
        ctxRes.optData.deviceConfigRes.vendorID;

      hInfo.boardInfo[monitor].pciInfo.deviceID = 
        ctxRes.optData.deviceConfigRes.deviceID;
      hInfo.boardInfo[monitor].devRev = 
        ctxRes.optData.deviceConfigRes.chipRev;
      hInfo.boardInfo[monitor].h3Mem = 
        (ctxRes.optData.deviceConfigRes.fbRam >> 20);

      if (hInfo.boardInfo[monitor].h3Mem == 8) {
        hInfo.boardInfo[monitor].pciInfo.deviceID = 3;
      }

      hInfo.boardInfo[monitor].min_tramSize = 
        ((hInfo.boardInfo[monitor].h3Mem == 4 ) ||
         (hInfo.boardInfo[monitor].pciInfo.deviceID == 3)) ? 0x200000 : 0x400000;
      
      if (getenv("FX_GLIDE_FBRAM")) {
        hInfo.boardInfo[monitor].h3Mem = atoi(getenv("FX_GLIDE_FBRAM"));      
      }

      checkResolutions(resolutionSupported[monitor],
                       (void *) hInfo.boardInfo[monitor].hMon);
    }
  }
#elif defined(HWC_GDX_INIT)
  {
    GDHandle screenDeviceHandle;
    CntrlParam  paramBlock;
    OSErr       myErr;
    FxU32 i = 0;
    hwcControl_t control;
    
    hInfo.nBoards = 0;
    
    screenDeviceHandle = DMGetFirstScreenDevice(dmOnlyActiveDisplays);
    while(screenDeviceHandle) {             
      /* First, verify that it's a screen device */
      if(TestDeviceAttribute(screenDeviceHandle,screenDevice)) {
        /* Then verify it actually has a driver, so we know gdRefNum
           is valid. */ 
        if(!TestDeviceAttribute(screenDeviceHandle,noDriver)) {
          /* HLock((Handle)screenDeviceHandle); */
          paramBlock.ioCRefNum = (*screenDeviceHandle)->gdRefNum;
          /* HUnlock((Handle)screenDeviceHandle); */
          /* driver-specific status request */
          paramBlock.csCode = k3DfxGetDeviceConfig; 
          *(hwcControl_t **)(&paramBlock.csParam[0]) = &control;
                        
          myErr = PBControl((ParmBlkPtr)&paramBlock, false);
                        
          if (myErr == noErr) {
            /* Woohoo! Save off the config info for this card */
            hInfo.nBoards++;
            hInfo.boardInfo[i].hMon = screenDeviceHandle;
            hInfo.boardInfo[i].devRev =
              control.res.optData.deviceConfigRes.devRev;
            hInfo.boardInfo[i].h3Mem =
              control.res.optData.deviceConfigRes.h3Mem;
            hInfo.boardInfo[i].pciInfo.vendorID =
              control.res.optData.deviceConfigRes.vendorID;
            hInfo.boardInfo[i].pciInfo.deviceID =
              control.res.optData.deviceConfigRes.deviceID;
            hInfo.boardInfo[i].pciInfo.pciBaseAddr[0] =
              control.res.optData.deviceConfigRes.hwBase; 
            hInfo.boardInfo[i].pciInfo.pciBaseAddr[1] =
              control.res.optData.deviceConfigRes.lfbBase; 
            hInfo.boardInfo[i].pciInfo.pciBaseAddr[2] =
              control.res.optData.deviceConfigRes.ioPortBase;
            hInfo.boardInfo[i].pciInfo.initialized = 1;
                                
            /* No ROM info, don't care */
            i++;
          }
        }
      }
      screenDeviceHandle = DMGetNextScreenDevice(screenDeviceHandle,dmOnlyActiveDisplays);
    }
  }
#else /* HWC_GDX_INIT */
  {
    int i;
    FxU32 bn;

    pciOpen();

    hInfo.nBoards = 0;

    errorString[0] = '\0';

    for (i = 0; i < HWC_MAX_BOARDS; i++) {
      hInfo.boardInfo[i].pciInfo.initialized = 0;
      if (pciFindCardMulti(vID, dID, &bn, i)) {
        hInfo.nBoards++;
        hInfo.boardInfo[i].boardNum = 0;

        hInfo.boardInfo[i].boardNum = 0;

        hInfo.boardInfo[i].pciInfo.initialized = 1;
        hInfo.boardInfo[i].pciInfo.vendorID = vID;
        hInfo.boardInfo[i].pciInfo.deviceID = dID;
        /* 
         * NOTE: in the code above we learn about memsize here:
         *   hInfo.boardInfo[i].h3Mem
         * 
         * However, in DOS, since we have not mapped the board yet, we have
         * to wait until later. (see hwcInitRegisters())  - dwj
         */

        /* Get some board Info */
        pciGetConfigData( PCI_REVISION_ID, bn, &hInfo.boardInfo[i].devRev);

        /* Get all the base addresses */
        pciGetConfigData(PCI_BASE_ADDRESS_0, bn,
                         &hInfo.boardInfo[i].pciInfo.pciBaseAddr[0]);
        pciGetConfigData(PCI_BASE_ADDRESS_1, bn,
                         &hInfo.boardInfo[i].pciInfo.pciBaseAddr[1]);
        pciGetConfigData(PCI_IO_BASE_ADDRESS, bn,
                         &hInfo.boardInfo[i].pciInfo.pciBaseAddr[2]);
        pciGetConfigData(PCI_ROM_BASE_ADDRESS, bn,
                         &hInfo.boardInfo[i].pciInfo.pciBaseAddr[3]);
      }
    }
  
  }
#endif /* HWC_EXT_INIT */
  if (hInfo.nBoards)
    return &hInfo;
  else
    return NULL;

#undef FN_NAME
} /* hwcInit */

FxBool
hwcMapBoard(hwcBoardInfo *bInfo, FxU32 bAddrMask)
{
#define FN_NAME "hwcMapBoard"

  if (bInfo->pciInfo.initialized == FXFALSE) {
    sprintf(errorString, "%s:  Called before hwcInit\n", FN_NAME);
    return FXFALSE;
  }

  bInfo->linearInfo.initialized = FXTRUE;

#ifdef HWC_EXT_INIT
  {
    hwcExtRequest_t req;
    hwcExtResult_t res;
    OSVERSIONINFO ovi;

    ovi.dwOSVersionInfoSize = sizeof ( ovi );
    GetVersionEx ( &ovi );
    if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
      bInfo->osNT = FXTRUE;
      GDBG_INFO(80, FN_NAME ":  OS:  NT\n");
    } else {
      bInfo->osNT = FXFALSE;
      GDBG_INFO(80, FN_NAME ":  OS:  9X\n");
    }
      
    bInfo->procHandle = (FxU32) GetCurrentProcessId();
    GDBG_INFO(80, FN_NAME ":  procHandle:  0x%x\n", bInfo->procHandle);

    req.which = HWCEXT_GETLINEARADDR;
    req.optData.linearAddrReq.devNum = 0;
    req.optData.linearAddrReq.pHandle = bInfo->procHandle;

    GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_GETLINEARADDR\n");
    ExtEscape((HDC) bInfo->hdc, EXT_HWC, sizeof(req), (void *) &req,
      sizeof(res), (void *) &res);

    if (res.resStatus != 1) {
      strcpy(errorString, "HWCEXT_GETLINEARADDR Failed");
      return FXFALSE;
    }
    bInfo->linearInfo.linearAddress[0] =
      res.optData.linearAddressRes.baseAddresses[0];
    bInfo->linearInfo.linearAddress[1] =
      res.optData.linearAddressRes.baseAddresses[1];
    
    /* WTF????? */
    bInfo->pciInfo.pciBaseAddr[2] = 
      res.optData.linearAddressRes.baseAddresses[1];
  }
#elif defined(HWC_GDX_INIT)
  {
    FxU32
      bAddr;

    /* memory mapped register spaces */
    for (bAddr = 0; bAddr < 2; bAddr++) {
      if ((bAddrMask & (0x01UL << bAddr)) != 0x00UL) {
        bInfo->linearInfo.linearAddress[bAddr] = bInfo->pciInfo.pciBaseAddr[bAddr];
      }
    }
    /* FixMe: This gets used to set the pll's so I guess we need it here
     * unconditionally
     */
    bInfo->linearInfo.linearAddress[2] = bInfo->pciInfo.pciBaseAddr[2];
    
    /* Sorry, ROM mapping on MacOS seems whacked out for some reason. */
    bInfo->linearInfo.linearAddress[3] = 0;
    
    /* Kludge.  Pass boardInfo to acceleration stuff. */
#if GLIDE3      
    {
      extern hwcBoardInfo *acceleratorBoardInfo;
      acceleratorBoardInfo = bInfo;
    }
#endif  
  }
#else
  {
    FxU32
      bAddr;

    /* memory mapped register spaces */
    for (bAddr = 0; bAddr < 2; bAddr++) {
      if ((bAddrMask & (0x01UL << bAddr)) != 0x00UL) {
        bInfo->linearInfo.linearAddress[bAddr] = 
          (FxU32)pciMapCardMulti(bInfo->pciInfo.vendorID, bInfo->pciInfo.deviceID,
                                 0x1000000, &bInfo->deviceNum, bInfo->boardNum, bAddr);
      }
    }

    /* FixMe: This gets used to set the pll's so I guess we need it here
     * unconditionally
     */
    bInfo->linearInfo.linearAddress[2] = 
      (FxU32)pciMapCardMulti(bInfo->pciInfo.vendorID, bInfo->pciInfo.deviceID,
                             0x1000000, &bInfo->deviceNum, bInfo->boardNum, 2);

    /* Does the caller want the rom bios? */
    if ((bAddrMask & 0x08UL) != 0x00UL) {
      bInfo->linearInfo.linearAddress[3] = 
        (FxU32)pciMapCardMulti(bInfo->pciInfo.vendorID, bInfo->pciInfo.deviceID,
                               0x1000000, &bInfo->deviceNum, bInfo->boardNum, 3);
    }
  }
#endif  
  
  return FXTRUE;
#undef FN_NAME
} /* hwcMapBoard */

FxBool
hwcInitRegisters(hwcBoardInfo *bInfo) 
{
#define FN_NAME hwcInitRegisters
  FxU32
    grxSpeedInMHz, memSpeedInMHz,
    sgramMode, sgramMask, sgramColor;
  
  if (bInfo->linearInfo.initialized == FXFALSE) {
    printf(errorString, "%s:  Called before hwcMapBoard\n", FN_NAME);
    return FXFALSE;
  }
      
  bInfo->regInfo.initialized = FXTRUE;

  bInfo->regInfo.ioMemBase =
    bInfo->linearInfo.linearAddress[0] + SST_IO_OFFSET;
  bInfo->regInfo.cmdAGPBase =
    bInfo->linearInfo.linearAddress[0] + SST_CMDAGP_OFFSET;
  bInfo->regInfo.waxBase =
    bInfo->linearInfo.linearAddress[0] + SST_2D_OFFSET;
  bInfo->regInfo.sstBase =
    bInfo->linearInfo.linearAddress[0] + SST_3D_OFFSET;
  bInfo->regInfo.lfbBase =
    bInfo->linearInfo.linearAddress[0] + SST_LFB_OFFSET;
  bInfo->regInfo.rawLfbBase =
    bInfo->linearInfo.linearAddress[1];
#if __POWERPC__    
  bInfo->regInfo.ioPortBase = bInfo->pciInfo.pciBaseAddr[2] & ~0x1;
#else
  bInfo->regInfo.ioPortBase = (FxU16) bInfo->pciInfo.pciBaseAddr[2] & ~0x1;
#endif
  /* Figure out if it's SDRAM */
  {
    FxU32 dramInit1;
    
    HWC_IO_LOAD(bInfo->regInfo, dramInit1, dramInit1);
    bInfo->sdRAM = ((dramInit1 & SST_MCTL_TYPE_SDRAM) != 0x00UL);

    if (GETENV("SSTH3_SDRAM"))
      bInfo->sdRAM = FXTRUE;
  }

#if !defined(HWC_EXT_INIT) && !defined(HWC_GDX_INIT)
  {
    FxU32 
      pciInit0,
      pciCommandReg =
        BIT(0) |              /* enable i/o decode */
        BIT(1);               /* enable memory decode */
  
    /* Enable PCI memory and I/O decode */
    pciSetConfigData(PCI_COMMAND, bInfo->deviceNum, &pciCommandReg);
    
    HWC_IO_LOAD(bInfo->regInfo, pciInit0, pciInit0);
    pciInit0 |= SST_PCI_READ_WS | SST_PCI_WRITE_WS;
    HWC_IO_STORE(bInfo->regInfo, pciInit0, pciInit0);  
  }
#endif

  if (GETENV("SSTH3_SGRAM_MODE"))
    sgramMode = atoi(GETENV("SSTH3_SGRAM_MODE"));
  else if (GETENV("SSTH3_SGRAM_222") &&
    (atoi(GETENV("SSTH3_SGRAM_222")) != 0))
    sgramMode = 0x27;
  else
    sgramMode = 0x37;

  if (GETENV("SSTH3_SGRAM_MASK"))
    sgramMask = atoi(GETENV("SSTH3_SGRAM_MASK"));
  else
    sgramMask = 0xFFFFFFFF;

  if (GETENV("SSTH3_SGRAM_COLOR"))
    sgramColor = atoi(GETENV("SSTH3_SGRAM_COLOR"));
  else
    sgramColor = 0;

  if (GETENV("SSTH3_GRXCLOCK"))
    grxSpeedInMHz = atoi(GETENV("SSTH3_GRXCLOCK"));
  else
    grxSpeedInMHz = 100;

  if (GETENV("SSTH3_MEMCLOCK"))
    memSpeedInMHz = atoi(GETENV("SSTH3_MEMCLOCK"));
  else
    memSpeedInMHz = 100;

#if !defined(HWC_ACCESS_DDRAW) && !defined(HWC_GDX_INIT)
  if (GETENV("HAL_NOINIT") == NULL || atoi(GETENV("HAL_NOINIT")) == 0) {

    /* 
     * final DOS initialiation
     */

    /* don't set the clocks unless they used the environment variables */

    if (GETENV("SSTH3_GRXCLOCK") || GETENV("SSTH3_MEMCLOCK")) {
     switch (bInfo->pciInfo.deviceID) {
        case 0x03: /* banshee */
           h3InitPlls(bInfo->regInfo.ioPortBase,  grxSpeedInMHz, memSpeedInMHz);
           break;
        case 0x5: /* voodoo3/avenger */
           break;
        default: /* unknown board type!!! */
           return FXFALSE;
     }
    }

    /* read back the memory size, since we 
     * don't know it under DOS  (see hwcInit) - dwj 
     */
    bInfo->h3Mem = h3InitGetMemSize(bInfo->regInfo.ioPortBase);

    h3InitVga(bInfo->regInfo.ioPortBase, FXTRUE); 
  }
#endif
  
  return FXTRUE;

#undef FN_NAME
} /* hwcInitRegisters */

#define FIFOPAD         0x0000
#define AUXPAD          0x1000

FxBool
hwcAllocBuffers(hwcBoardInfo *bInfo, FxU32 nColBuffers, FxU32 nAuxBuffers) 
{
#define FN_NAME "hwcAllocBuffers"
  FxBool
    bufferAlignP;
  FxU32
    bNum,
    h3Mem = bInfo->h3Mem << 20,
    bufStride, 
    bufSize;
  FxI32
    i,
    tramSize, fifoSize;

  if (bInfo->vidInfo.initialized == FXFALSE) {
    sprintf(errorString, "%s:  Called before video initialization\n", FN_NAME);
    return FXFALSE;
  }
    
  GDBG_INFO(80, "%s(0x%x, 0x%x, 0x%x)\n", FN_NAME, bInfo, nColBuffers, nAuxBuffers);

  /* I've decided on > 2 instead of == 3 because we may support more
    than 3 buffers in the future, and want 4 to set the
    triple-buffering bit in dramInit1, also */
  bInfo->vidInfo.tripleBuffering = (nColBuffers > 2);

  bInfo->vidInfo.stride = bufStride =
    calcBufferStride(bInfo->vidInfo.xRes, bInfo->vidInfo.tiled);   

  /* We want to place the FIFO after the tram but before the color
     buffers with some pad */
  bufSize = calcBufferSize(bInfo->vidInfo.xRes, bInfo->vidInfo.yRes,
    bInfo->vidInfo.tiled); 

  bInfo->buffInfo.bufStride = bufStride;
  bInfo->buffInfo.bufSize = bufSize;

  if (bInfo->vidInfo.tiled) {
    bInfo->buffInfo.bufStrideInTiles = (bufStride >> 7);
    bInfo->buffInfo.bufSizeInTiles =
      calcBufferSizeInTiles(bInfo->vidInfo.xRes, bInfo->vidInfo.yRes);
    bInfo->buffInfo.bufHeightInTiles =
      calcBufferHeightInTiles(bInfo->vidInfo.yRes);
  }

  bInfo->buffInfo.initialized = FXTRUE;
  bInfo->buffInfo.nColBuffers = nColBuffers;
  bInfo->buffInfo.nAuxBuffers = nAuxBuffers;

  /* First, do the buffer allocation */


  if (nAuxBuffers > 0) {
    bInfo->buffInfo.auxBuffEnd  =
    bInfo->buffInfo.auxBuffStart = h3Mem;

    bInfo->buffInfo.auxBuffStart -= bufSize;

    /* auxBuffers start on odd pages, so we need to check to see if
     * it's on an even page and, if so, make it odd. 
     *
     * NB: We need to do the same sort of 'alignment' thing here as for
     * the color buffers.
     */
    bufferAlignP = ((bInfo->buffInfo.auxBuffStart & 0x1000UL) == 0);
    if (bufferAlignP) bInfo->buffInfo.auxBuffStart -= 0x1000;

  }

  for (i = nColBuffers - 1; i >= 0; i--) {
    if ((FxU32) i == (nColBuffers - 1)) {
      FxU32 top;
      if (nAuxBuffers > 0) 
        top = bInfo->buffInfo.auxBuffStart;
      else
        top = h3Mem;
      
      bInfo->buffInfo.colBuffStart[i] = 
        bInfo->buffInfo.colBuffEnd[i] = top;

    } else {
      bInfo->buffInfo.colBuffStart[i] = 
        bInfo->buffInfo.colBuffEnd[i] = 
        bInfo->buffInfo.colBuffStart[i + 1];
    }
    
    bInfo->buffInfo.colBuffStart[i] -= bufSize;

    /* As a memory access optmization colorBuffers start on even
     * pages, while aux buffers start on odd pages.  Thus we must
     * check to see if we're startding on an odd page here and, if so,
     * add a page to the start.  
     */
    bufferAlignP = ((bInfo->buffInfo.colBuffStart[i] & 0x1000UL) != 0);
    if (bufferAlignP) bInfo->buffInfo.colBuffStart[i] -= 0x1000;
  }

  /* Now we can calculate some other stuff... */
  bInfo->fbOffset =  bInfo->buffInfo.colBuffStart[0];
#if 0
  bInfo->tramOffset =  fifoSize = MAXFIFOSIZE;
  tramSize =  bInfo->fbOffset - bInfo->tramOffset;
  if (tramSize < bInfo->min_tramSize) {
    /* Now we have to shrink the FIFO */
    tramSize = bInfo->min_tramSize;
    fifoSize = bInfo->fbOffset - bInfo->min_tramSize;
    if (fifoSize < 0) {
      GDBG_INFO(80, "%s:  Not enough memory for resolution + min texture\n",
        FN_NAME);
      sprintf(errorString, 
        "%s:  Not enough memory for resolution + min texture\n",
        FN_NAME);
      return FXFALSE;
    } 
  }


  bInfo->fifoInfo.fifoLength = (FxU32) fifoSize;
  bInfo->fifoInfo.fifoStart = 0;
  bInfo->tramOffset = (FxU32) fifoSize;
  bInfo->tramSize = (FxU32) tramSize;

#else
  bInfo->tramOffset = 0x0;   /* bottom 1 page reserved */
  fifoSize = (bInfo->h3Mem > 8) ? MAXFIFOSIZE_16MB : MAXFIFOSIZE;
  tramSize =  bInfo->fbOffset - bInfo->tramOffset - fifoSize;
  if (tramSize < (FxI32)bInfo->min_tramSize) {
    /* Now we have to shrink the FIFO */
    tramSize = bInfo->min_tramSize;
    fifoSize = bInfo->fbOffset - bInfo->min_tramSize - bInfo->tramOffset;
    if (fifoSize < 0) {
      GDBG_INFO(80, "%s:  Not enough memory for resolution + min texture\n",
        FN_NAME);
      sprintf(errorString, 
        "%s:  Not enough memory for resolution + min texture\n",
        FN_NAME);
      return FXFALSE;
    } 
  }

  bInfo->fifoInfo.fifoLength = (FxU32) fifoSize;
  bInfo->fifoInfo.fifoStart = bInfo->fbOffset - fifoSize;

  bInfo->fifoInfo.fifoLength -= 0x2000;

  bInfo->tramSize = (FxU32) tramSize;
#endif

#if 1
#define LFBSTRIDE 0x1000
  {
    FxU32 colBuffOffset = 0;
    FxU32 colBufAlignP = 0;

    for (bNum = 0; bNum < nColBuffers; bNum++) {
      if ((colBuffOffset & 0x1000UL) != 0) {
        colBuffOffset += 0x1000UL;
        colBufAlignP++;
      }
      bInfo->buffInfo.lfbBuffAddr[bNum] = 
        hwcBufferLfbAddr(bNum, bInfo, colBufAlignP);
      colBuffOffset += bufSize;
    }

    if (nAuxBuffers > 0) {
      if ((colBuffOffset & 0x1000UL) == 0) {
        colBuffOffset += 0x1000UL;
        colBufAlignP++;
      }
      bInfo->buffInfo.lfbBuffAddr[nColBuffers] =
        hwcBufferLfbAddr(nColBuffers, bInfo, colBufAlignP);
    }
  }
#endif

  bInfo->buffInfo.initialized = FXTRUE;

  GDBG_INFO(80, "%s:  Board Info:\n", FN_NAME);
  GDBG_INFO(80, "\thdc:             0x%x\n", bInfo->hdc);
  GDBG_INFO(80, "\textContextID:    0x%x\n", bInfo->extContextID);
  GDBG_INFO(80, "\tdevRev:          0x%x\n", bInfo->devRev);
  GDBG_INFO(80, "\tfbOffset:        0x%x\n", bInfo->fbOffset);
  GDBG_INFO(80, "\th3Rev:           0x%x\n", bInfo->h3Rev);
  GDBG_INFO(80, "\th3Mem:           0x%x\n", bInfo->h3Mem);
  GDBG_INFO(80, "\tboardNum:        0x%x\n", bInfo->boardNum);
  GDBG_INFO(80, "\tdeviceNum:       0x%x\n", bInfo->deviceNum);

  GDBG_INFO(80, "%s:  Buffer Info:\n", FN_NAME);
  GDBG_INFO(80, "\tbufSize:         0x%x\n", bInfo->buffInfo.bufSize);
  GDBG_INFO(80, "\tbufSizeInTiles:  0x%x\n", bInfo->buffInfo.bufSizeInTiles);
  GDBG_INFO(80, "\tbufStride:       0x%x\n", bInfo->buffInfo.bufStride);
  GDBG_INFO(80, "\tbufStrideInTiles:0x%x\n", bInfo->buffInfo.bufStrideInTiles);
  GDBG_INFO(80, "\tbufHeightInTiles:0x%x\n", bInfo->buffInfo.bufHeightInTiles);
  GDBG_INFO(80, "\tnColBuffers:     0x%x\n", bInfo->buffInfo.nColBuffers);
  for (i = 0; i < (FxI32) nColBuffers; i++) {
    GDBG_INFO(80, "\tcolBuff %d Start: 0x%x\n", i, bInfo->buffInfo.colBuffStart[i]);
    GDBG_INFO(80, "\tcolBuff %d End:   0x%x\n", i, bInfo->buffInfo.colBuffEnd[i]);
  }
  GDBG_INFO(80, "\tnAuxBuffers:     0x%x\n", bInfo->buffInfo.nAuxBuffers);
  GDBG_INFO(80, "\tauxBuffStart:    0x%x\n", bInfo->buffInfo.auxBuffStart);
  GDBG_INFO(80, "\tauxBuffEnd:      0x%x\n", bInfo->buffInfo.auxBuffEnd);

  GDBG_INFO(80, "%s:  FIFO Info:\n", FN_NAME);
  GDBG_INFO(80, "\tfifoStart:       0x%x\n", bInfo->fifoInfo.fifoStart);
  GDBG_INFO(80, "\tfifoLength:      0x%x\n", bInfo->fifoInfo.fifoLength);

  GDBG_INFO(80, "%s:  TRAM Info:\n", FN_NAME);
  GDBG_INFO(80, "\ttramOffset:      0x%x\n", bInfo->tramOffset);
  GDBG_INFO(80, "\ttramSize:        0x%x\n", bInfo->tramSize);
  GDBG_INFO(80, "\tMin TramSize:    0x%x\n", bInfo->min_tramSize);

  return FXTRUE;
#undef FN_NAME
} /* hwcAllocBuffers */

FxBool
hwcInitFifo(hwcBoardInfo *bInfo, FxBool enableHoleCounting)
{
#define FN_NAME "hwcInitFifo"
  FxBool
    agpEnable = FXFALSE;  
  FxU32
    cagpRegs;                   /* pointer to Cmd/AGP regs */

  if (bInfo->regInfo.initialized == FXFALSE) {
    sprintf(errorString, "%s:  Called before hwcMapBoard\n", FN_NAME);
    return FXFALSE;
  }
  cagpRegs = bInfo->regInfo.cmdAGPBase;

  if (bInfo->buffInfo.initialized == FXFALSE) {
    sprintf(errorString, "%s:  Called before hwcInitBuffers\n", FN_NAME);
    return FXFALSE;
  }

  /* disable the CMD fifo */
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.baseSize, 0);
  
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.baseAddrL,
    bInfo->fifoInfo.fifoStart>>12);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.readPtrL, bInfo->fifoInfo.fifoStart);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.readPtrH, 0);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.aMin, bInfo->fifoInfo.fifoStart-4);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.aMax, bInfo->fifoInfo.fifoStart-4);
  
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.depth, 0);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.holeCount, 0);
  /* Fifo LWM /HWM/ THRESHOLD */
  if (bInfo->pciInfo.deviceID == 0x3) { /* banshee */
    HWC_CAGP_STORE(bInfo->regInfo, cmdFifoThresh,
                   (0x09 << SST_HIGHWATER_SHIFT) | 0x2);
  } else {
    HWC_CAGP_STORE(bInfo->regInfo, cmdFifoThresh,
                   (0xf << SST_HIGHWATER_SHIFT) | 0x8);
  }

  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.baseSize, (((bInfo->fifoInfo.fifoLength >> 12) - 1) | 
                                                     SST_EN_CMDFIFO |
                                                     (enableHoleCounting ? 0 : SST_CMDFIFO_DISABLE_HOLES) |
                                                     (agpEnable ?  SST_CMDFIFO_AGP : 0)));
  
  GDBG_INFO(80, "%s:  CMD FIFO placed at physical addr 0x%x\n", 
            FN_NAME,
            bInfo->fifoInfo.fifoStart);

  return FXTRUE;

#undef FN_NAME
} /* hwcInitFifo */

#if 0
/* Currently unused. */
static SstIORegs savedIORegs;
#endif

void
hwcInitVideoOverlaySurface(
  hwcRegInfo *rInfo,
    FxU32 enable,               /* 1=enable Overlay surface (OS), 1=disable */
    FxU32 stereo,               /* 1=enable OS stereo, 0=disable */
    FxU32 horizScaling,         /* 1=enable horizontal scaling, 0=disable */
    FxU32 dudx,                 /* horizontal scale factor (ignored if not */
      /* scaling) */
    FxU32 verticalScaling,      /* 1=enable vertical scaling, 0=disable */
    FxU32 dvdy,                 /* vertical scale factor (ignored if not */
      /* scaling) */
    FxU32 filterMode,           /* duh */
    FxU32 tiled,                /* 0=OS linear, 1=tiled */
    FxU32 pixFmt,               /* pixel format of OS */
    FxU32 clutBypass,           /* bypass clut for OS? */
    FxU32 clutSelect,           /* 0=lower 256 CLUT entries, 1=upper 256 */
    FxU32 startAddress,         /* board address of beginning of OS */
    FxU32 stride)               /* distance between scanlines of the OS, in */
  /* units of bytes for linear OS's and tiles for */
  /* tiled OS's */
{
  FxU32 doStride;
  FxU32 vidProcCfg;

  HWC_IO_LOAD((*rInfo), vidProcCfg, vidProcCfg);

  vidProcCfg &= ~(SST_OVERLAY_TILED_EN |
                  SST_OVERLAY_STEREO_EN |  
                  SST_OVERLAY_HORIZ_SCALE_EN |
                  SST_OVERLAY_VERT_SCALE_EN |
                  SST_OVERLAY_TILED_EN |
                  SST_OVERLAY_PIXEL_FORMAT |
                  SST_OVERLAY_CLUT_BYPASS |
                  SST_OVERLAY_CLUT_SELECT);

  if (enable)
    vidProcCfg |= SST_OVERLAY_EN;

  if (stereo)
    vidProcCfg |= SST_OVERLAY_STEREO_EN;

  if (horizScaling)
    vidProcCfg |= SST_OVERLAY_HORIZ_SCALE_EN;

  if (verticalScaling)
    vidProcCfg |= SST_OVERLAY_VERT_SCALE_EN;

  if (tiled) {
    vidProcCfg |= SST_OVERLAY_TILED_EN;
  }

  vidProcCfg |= SST_OVERLAY_PIXEL_RGB565D; /* Turn on RGB565Dithered */
  vidProcCfg &= ~SST_CURSOR_EN; /* Turn off HW Cursor */
  vidProcCfg |= SST_OVERLAY_TILED_EN; /* Overlay tile space enable */    


  if (clutBypass)
    vidProcCfg |= SST_OVERLAY_CLUT_BYPASS;

  if (clutSelect)
    vidProcCfg |= SST_OVERLAY_CLUT_SELECT;

  HWC_IO_STORE((*rInfo), vidProcCfg, vidProcCfg);

  /* */
  HWC_IO_LOAD((*rInfo), vidDesktopOverlayStride, doStride);
  doStride &= ~(SST_OVERLAY_LINEAR_STRIDE | SST_OVERLAY_TILE_STRIDE);

  stride <<= SST_OVERLAY_STRIDE_SHIFT;
  if (tiled)
    stride &= SST_OVERLAY_TILE_STRIDE;
  else
    stride &= SST_OVERLAY_LINEAR_STRIDE;
  doStride |= stride;

  HWC_IO_STORE((*rInfo), vidDesktopOverlayStride, doStride);

} /* hwcInitVideoOverlaySurface */

FxU32 
hwcInitLookupRefresh(FxU32 ord_refresh) 
{
#define FN_NAME "hwcInitLookupRefresh"
  FxU32 refresh_hz;

  switch(ord_refresh) {
  case(GR_REFRESH_60Hz):
    refresh_hz = 60;
    break;
  case(GR_REFRESH_70Hz):
    refresh_hz = 70;
    break;
  case(GR_REFRESH_72Hz):
    refresh_hz = 72;
    break;
  case(GR_REFRESH_75Hz):
    refresh_hz = 75;
    break;
  case(GR_REFRESH_80Hz):
    refresh_hz = 80;
    break;
  case(GR_REFRESH_85Hz):
    refresh_hz = 85;
    break;
  case(GR_REFRESH_90Hz):
    refresh_hz = 90;
    break;
  case(GR_REFRESH_100Hz):
    refresh_hz = 100;
    break;
  case(GR_REFRESH_120Hz):
    refresh_hz = 120;
    break;
  default:
    GDBG_ERROR(FN_NAME, "Unsupported Refresh Rate -- defaulting to 60hz\n");
    refresh_hz = 60;
    break;
  }
  return (refresh_hz);
#undef FN_NAME
} /* hwcInitLookupRefresh */

#ifdef HWC_ACCESS_DDRAW

#define LINEAR_STRIDE_ALIGN 16UL

static FxBool
_hwcLinear2HWAddr(const FxU32 linearAddr, 
                  const FxU32 linearBaseAddr,
                  const FxU32 linearStride,
                  const hwcBoardInfo* bInfo,
                  hwcSurfaceInfo* ret)
{
#define FN_NAME "_hwcLinear2HWAddr"
  FxU32
    pciStride,                  /* page stride */
    hwStride,                   /* tile stride */
    tileMark,                   /* delineation between linear and tiled */
    lpTileBase;                 /* linear addr of tileMark */
  FxBool
    retVal = FXFALSE,
    isTiled;                    /* is the surface tiled? */

  /* determine lfb baseAddress and hw offset to buffer */
  {
    hwcExtRequest_t
      req;                        /* Request to HWC_EXT */
    hwcExtResult_t
      res;                        /* Result from HWC_EXT */
    
    /* query for tile watermark & compute tile characteristics */
    req.which = HWCEXT_GETDEVICECONFIG;
    GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_GETDEVICECONFIG\n");
    retVal = (ExtEscape(bInfo->hdc, EXT_HWC, 
                        sizeof(req), (LPSTR)&req, 
                        sizeof(res), (LPSTR)&res) > 0);
    if (!retVal) {
      strcpy(errorString, "HWCEXT_GETDEVICECONFIG failed");
      GDBG_INFO(80, "%s: %s.\n", FN_NAME, errorString);
      goto __errExit;
    }
    pciStride = res.optData.deviceConfigRes.pciStride;
    hwStride = res.optData.deviceConfigRes.hwStride;
    tileMark = res.optData.deviceConfigRes.tileMark;

    lpTileBase = linearBaseAddr + tileMark;
    isTiled = (linearAddr >= lpTileBase);
  }

  ret->pciStride = pciStride;
  ret->tileBase = tileMark;
  ret->lpSurface = linearAddr;
  ret->lpLFB = linearBaseAddr;
  ret->hwStride = hwStride;

  /* Compute things that depend on the offset wrt the tile mark */
  ret->isTiled = isTiled;
  if (ret->isTiled) {
    const FxU32
      tileStride = hwStride,
      tilePitch = pciStride,
      tileOffset = linearAddr - lpTileBase,
      y = tileOffset / tilePitch, 
      x = tileOffset - (y * tilePitch),
      tile = ((y / HWC_TILE_HEIGHT) * tileStride) + (x / HWC_TILE_WIDTH);

    ret->fbOffset = (tile << 12UL) + tileMark;
    ret->fbStride = ret->hwStride | SST_BUFFER_MEMORY_TILED;
  } else {
    /* NB: Banshee (and derivatives) have a 16-byte alignment
     * restriction on the stride and offset for color/aux buffers, but
     * it is left to the client to correctly adjust for this when
     * setting the values.  
     */
    ret->fbOffset = linearAddr - linearBaseAddr;
    ret->fbStride = linearStride;
  }

 __errExit:
  return retVal;
#undef FN_NAME
} /* _hwcLinear2HWAddr */

static FxU32
_hwcPixelFormat2Bytes(const DDPIXELFORMAT* pixelFormat)
{
#define FN_NAME "_hwcPixelFormat2Bytes"
  const DWORD
    formatFlags = pixelFormat->dwFlags;
  FxU32 
    retVal = 0x00UL;

  if ((formatFlags & DDPF_PALETTEINDEXED8) == DDPF_PALETTEINDEXED8) {
    retVal = 1;
  } else if ((formatFlags & DDPF_RGB) == DDPF_RGB) {
    retVal = (pixelFormat->dwRGBBitCount >> 0x03UL);
    if ((retVal == 0) || (retVal > 2)) {
      retVal = 0;
      GDBG_INFO(80, FN_NAME": Invalid surface rgb bit count(0x%X)\n",
                pixelFormat->dwRGBBitCount);
    }
  } else {
    GDBG_INFO(80, FN_NAME": Invalid surface pixel format flags(0x%X)\n",
              formatFlags);
  }

  return retVal;
#undef FN_NAME
} /* _hwcPixelFormat2Bytes */

/*-------------------------------------------------------------------
  Function: hwcGetSurfaceInfo
  Date: 14-Jul-98
  Implementor(s): dow
  Description:
  Returns information about a Glide (or DDraw) surface

  Arguments:
  
  Return:
  -------------------------------------------------------------------*/
FxBool
hwcGetSurfaceInfo(const hwcBoardInfo* bInfo,
                  FxU32 *sfc, 
                  hwcSurfaceInfo *ret)
{
#define FN_NAME "hwcGetSurfaceInfo"
  /* AssUMe it's a DDraw surface for now */
  LPDIRECTDRAWSURFACE2
    surf = (LPDIRECTDRAWSURFACE2) sfc;
  DDSURFACEDESC
    desc;
  DWORD
    ddErr;
  FxBool 
    retVal;
  FxU32
    lfbBase,                    /* linear addr for base of LFB */
    lpSurface;                  /* Linear addr for the surface */

  /* Lock the surface and get some info */
  desc.dwSize = sizeof(desc);
  ddErr = IDirectDrawSurface2_Lock(surf, 0, &desc, 
                                   DDLOCK_WAIT | DDLOCK_SURFACEMEMORYPTR, 0);
  retVal = (ddErr == DD_OK);
  if (!retVal) {
    sprintf(errorString, "%s: IDirectDrawSurface2_Lock (0x%X)\n", 
            FN_NAME, ddErr);
    GDBG_INFO(80, "%s", errorString);
    goto __errExit;
  }

  /* Grab the memory pointer */
  lpSurface = (DWORD) desc.lpSurface;

  /* Unlock the surface */
  desc.dwSize = sizeof(desc);
  IDirectDrawSurface2_Unlock(surf, desc.lpSurface);

  /* Get surface dimensions here rather than re-getting the surface
   * description later since these should all be valid here.  
   */
  ret->height = desc.dwHeight;
  ret->width  = desc.dwWidth;
  ret->depth  = _hwcPixelFormat2Bytes(&desc.ddpfPixelFormat);
  retVal = (ret->depth != 0x00UL);
  if (!retVal) {
    sprintf(errorString, "%s: Invalid surface pixel format (0x%X)\n", 
            FN_NAME, desc.ddpfPixelFormat.dwFlags);
    GDBG_INFO(80, "%s", errorString);
    goto __errExit;    
  }

  /* Get base addresses of the board */
  {
    hwcExtRequest_t
      req;                        /* Request to HWC_EXT */
    hwcExtResult_t
      res;                        /* Result from HWC_EXT */

    /* This address was set when the board was mapped in hwcMapBoard */
    lfbBase = bInfo->linearInfo.linearAddress[1];
    
    /* The DirectDraw driver may have re-mapped memory different than
     * the 2d driver's hw mapping. We have to figure out the correct
     * hw address for packet offsets and color buffer stuff.  
     */ 
    if (retVal) {
      req.which = HWCEXT_LINEAR_MAP_OFFSET;
      req.optData.mapInfoReq.mapAddr = lfbBase;
      req.optData.mapInfoReq.remapAddr = lpSurface;
      
      GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_LINEAR_MAP_OFFSET\n");
      retVal = (ExtEscape(bInfo->hdc, EXT_HWC,
                          sizeof(req), (LPCSTR)&req,
                          sizeof(res), (LPSTR)&res) > 0);
      if (!retVal) {
        strcpy(errorString, "HWCEXT_LINEAR_MAP_OFFSET failed");
        GDBG_INFO(80, "%s: %s.\n", FN_NAME, errorString);
      }
      
      /* lpSurface is now an address relative to the 2d driver's base
       * address as returned by the HWCEXT_GETLINEARADDR callback.  
       */
      lpSurface = (lfbBase + res.optData.mapInfoRes.linAddrOffset);
    }
  }
  if (!retVal) goto __errExit;

  /* Get the rest of the information about the hw relative address
   * from the linear address's location in memory.
   */
  retVal = _hwcLinear2HWAddr(lpSurface, 
                             lfbBase,
                             desc.lPitch,
                             bInfo,
                             ret);

 __errExit:  
  return retVal;
#undef FN_NAME
} /* hwcGetSurfaceInfo */

FxU32
hwcAllocWinContext(hwcBoardInfo* bInfo)
{
#define FN_NAME "hwcAllocWinContext"
  FxU32
    retVal = 0x00UL;
  hwcExtRequest_t
    ctxReq;
  hwcExtResult_t
    ctxRes;
  
  /* Allocate a context with the Driver */
  ctxReq.which = HWCEXT_ALLOCCONTEXT;
  ctxReq.optData.allocContextReq.protocolRev = HWCEXT_PROTOCOLREV;
  ctxReq.optData.allocContextReq.appType = HWCEXT_ABAPPTYPE_WIND;
  
  GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_ALLOCCONTEXT\n");
  if (ExtEscape(bInfo->hdc, EXT_HWC, 
                sizeof(ctxReq), (LPSTR)&ctxReq,
                sizeof(ctxRes), (LPSTR)&ctxRes) < 1) {
    strcpy(errorString, FN_NAME": HWCEXT_ALLOCCONTEXT failed");
  } else {
    retVal = ctxRes.optData.allocContextRes.contextID;
  }    

  return retVal;
#undef FN_NAME
} /* hwcAllocWinContext */

FxBool
hwcFreeWinContext(hwcBoardInfo* bInfo,
                  FxU32         winContextId)
{
#define FN_NAME "hwcFreeWinContext"
  hwcExtRequest_t 
    req;
  hwcExtResult_t 
    res;

  if (bInfo->osNT) {
    /*
    ** Don Fowler said this function call will force the driver to restore
    ** vidProcCfg and it is the reason for the full screen dos mode
    ** corruption. This function only called in window mode and we should
    ** be fine not to restore the vidProcCfg.
    */
    return 1;
  }
  else {
    req.which = HWCEXT_RELEASECONTEXT;
    req.optData.releaseContextReq.contextID = winContextId;
    
    GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_RELEASECONTEXT\n");
    return (ExtEscape(bInfo->hdc, EXT_HWC, 
                      sizeof(req), (void*)&req,
                      sizeof(res), (void*)&res) > 0);
  }
#undef FN_NAME
} /* hwcFreeWinContext */

static HRESULT CALLBACK
enumSurfaceCallback(LPDIRECTDRAWSURFACE ddSurface, 
                    LPDDSURFACEDESC ddDesc, 
                    LPVOID procData)
{
  HRESULT retVal = DDENUMRET_OK;

  if (((ddDesc->dwFlags & DDSD_CAPS) != 0x00UL) &&
      ((ddDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE) != 0x00UL)) {

    *(DDSURFACEDESC*)procData = *ddDesc;
    retVal = DDENUMRET_CANCEL;
  }

  /* The surface's refcount is bumped so we need to release since we
   * really only care about the surface's properties.
   */
  IDirectDrawSurface_Release(ddSurface);

  return retVal;
}


FxBool 
hwcAllocWinFifo(hwcBoardInfo* bInfo, 
                HwcWinFifo*   fifo,
                FxU32*        surface)
{
#define FN_NAME "hwcAllocWinFifo"
  FxBool 
    retVal = FXFALSE;
  hwcExtRequest_t      
    req;
  hwcExtResult_t       
    res;

      DDSURFACEDESC
        primaryDesc;


  /* Set undefined fifo type to start, and always clear the serial
   * number for this context.
   */
  fifo->fifoType = 0xFFFFFFFFUL;

  /* Check to see that we have valid surface owner to hang
   * the command fifo surface off of.
   */
  retVal = (surface != NULL);
  if (retVal) {
    LPDIRECTDRAW
      objDD = NULL;
    LPDIRECTDRAWSURFACE
      objSentinal = NULL,
      objFifo = NULL;
    DDSURFACEDESC
      fifoDesc,
      sentinalDesc;
    DWORD
      surfaceWidth;

    /* Get the surface owner so that we can try to allocate a new
     * surface for the fifo. On windows this will be a direct draw
     * object, and we need a dd4 object for agp surfaces.
     */
    {
      LPDIRECTDRAWSURFACE2
        objSurface = NULL;
      
      /* Make sure that this is a Surface2 object so that we can get
       * the DirectDraw object that owns this surface and piggy back
       * our objects ontop of it.
       */
      retVal = (IDirectDrawSurface_QueryInterface((LPDIRECTDRAWSURFACE)surface, 
                                                  &IID_IDirectDrawSurface2,
                                                  &objSurface) == DD_OK);
      if (!retVal) {
        GDBG_INFO(80, "%s: Require atleast IDirectDrawSurface2.\n", FN_NAME);
        goto __errSurfaceFifo;
      }
      
      /* Get the dd object for the surface creation */
      retVal = (IDirectDrawSurface2_GetDDInterface(objSurface, &objDD) == DD_OK);
      if (!retVal) {
        GDBG_INFO(80, "%s: Could not acquire DirectDraw object.\n", FN_NAME);
        goto __errSurfaceDDObj;
      }

  __errSurfaceDDObj:
      if (objSurface != NULL) IDirectDrawSurface_Release(objSurface);
      if (!retVal) goto __errSurfaceFifo;
    }

    /* Is the 2d command fifo in agp or local frame buffer space? */
    memset(&req, 0, sizeof(req));
    memset(&res, 0, sizeof(res));
    
    req.which = HWCEXT_FIFOINFO;
    GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_FIFOINFO\n");
    retVal = (ExtEscape((HDC)bInfo->hdc, EXT_HWC,
                        sizeof(req), (void*)&req,
                        sizeof(res), (void*)&res) > 0);
    if (!retVal) {
      GDBG_INFO(80, "%s: HWCEXT_FIFOINFO failed to determine current 2d fifo type.\n",
                FN_NAME);
      goto __errSurfaceFifo;
    }

    /* Set pixel format to make the allocation be 32-bit words */
#if 0 // SB: pixel format must equal that of the frame buffer
    fifoDesc.ddpfPixelFormat.dwSize = sizeof(fifoDesc.ddpfPixelFormat);
    fifoDesc.ddpfPixelFormat.dwFlags = (DDPF_ALPHAPIXELS |
                                        DDPF_RGB);
    fifoDesc.ddpfPixelFormat.dwRGBBitCount = 32UL;
    fifoDesc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000UL;
    fifoDesc.ddpfPixelFormat.dwRBitMask = 0x00FF0000UL;
    fifoDesc.ddpfPixelFormat.dwGBitMask = 0x0000FF00UL;
    fifoDesc.ddpfPixelFormat.dwBBitMask = 0x000000FFUL;
#endif
    /* We want the command fifo someplace that the hw can get to
     * directly. Whether this is is local to the board or agp is
     * controlled by where the 2d driver has its fifo.  
     */

    {
      DDCAPS
        ddCaps;

      /* Figure out the things that the current DirectDraw driver
       * knows about. We need to know whether it can support creating
       * agp and wider than primary surfaces.  
       */
      memset(&ddCaps, 0, sizeof(ddCaps));
      ddCaps.dwSize = sizeof(ddCaps);
      
      retVal = (IDirectDraw_GetCaps(objDD, &ddCaps, NULL) == DD_OK);
      if (!retVal) {
        GDBG_INFO(80, "%s: Could not determine if DirectDraw handles separate memory types.\n",
                  FN_NAME);
        goto __errSurfaceFifo;
      }
      
      /* Make the fifo type match the 2d fifo type if the driver can
       * handle different memory types.  
       */
      switch (res.optData.fifoInfoRes.fifoType) {        
      case HWCEXT_FIFO_AGP:
        if ((ddCaps.dwCaps2 & DDCAPS2_NONLOCALVIDMEM) != 0x00UL) {
          fifoDesc.ddsCaps.dwCaps |= DDSCAPS_NONLOCALVIDMEM;
        } else {
          GDBG_INFO(80, "%s: AGP fifo w/o DirectDraw support for AGP surfaces.\n", FN_NAME);
          retVal = FXFALSE;
        }
        break;

      case HWCEXT_FIFO_FB:
        if ((ddCaps.ddsCaps.dwCaps & DDSCAPS_LOCALVIDMEM) != 0x00UL) 
          fifoDesc.ddsCaps.dwCaps |= DDSCAPS_LOCALVIDMEM;
        break;
        
      case HWCEXT_FIFO_HOST:
      default:
        GDBG_INFO(80, "%s: Unknown driver fifo type.\n", FN_NAME);
        retVal = FXFALSE;
        break;
      }
      if (!retVal) goto __errSurfaceFifo;

      /* Get the primary surface so that we can figure out other whacked
       * surface creation constraints taht we have to adhere to. 
       */
      primaryDesc.dwSize = 0x00UL;
      retVal = (IDirectDraw_EnumSurfaces(objDD,
                                         DDENUMSURFACES_DOESEXIST | DDENUMSURFACES_ALL,
                                         NULL, 
                                         &primaryDesc,
                                         enumSurfaceCallback) == DD_OK);
      if (!retVal || (primaryDesc.dwSize == 0x00UL)) {
        GDBG_INFO(80, "%s: Could not find primary surface.\n", FN_NAME);
        goto __errSurfaceFifo;
      }

      /* If the DirectDraw driver supports wide surfaces then we can
       * specify what will align well w/ the underlying hw's page
       * size.  Otherwise we need to check the primary surface's
       * dimensions to fit the 'old' rule that no surface's dimensions
       * can exceed the primary's. After we allocate the surface we
       * check to make sure that the width and stride are the 'same'
       * since it is probably not a good thing to use the 'slop' when
       * the stride > width.
       */
      if ((ddCaps.dwCaps2 & DDCAPS2_WIDESURFACES) == 0x00UL) {
        retVal = ((primaryDesc.dwFlags & DDSD_WIDTH) != 0x00UL);
        if (!retVal) {
          GDBG_INFO(80, "%s: Primary surface does not have width.\n", FN_NAME);
          goto __errSurfaceFifo;
        }
        surfaceWidth = primaryDesc.dwWidth;
      } else {
        GDBG_INFO(80, "%s: DirectDraw supports wide surfaces.\n", FN_NAME);
        surfaceWidth = (0x1000UL >> 2UL);
      }
    }

    /* Allocate a sentinal surface so that the client can 'know' when
     * a given fifo segment has actually been executed by reading a
     * client specified serial # out of the buffer. This only needs to
     * be big enough to hold the size of a serial #, but we need to
     * work around some sort of whacked agp bug when reading back. See
     * hwcExecuteStatusWinFifo() for details.
     */
      fifoDesc = primaryDesc;
      fifoDesc.ddsCaps.dwCaps = (DDSCAPS_OFFSCREENPLAIN |
                               DDSCAPS_VIDEOMEMORY);
      fifoDesc.dwFlags = (DDSD_CAPS | 
                        DDSD_WIDTH | DDSD_HEIGHT |
                        DDSD_PITCH | 
                        DDSD_PIXELFORMAT);

      sentinalDesc = fifoDesc;

        {
                int Size;
                switch( sentinalDesc.ddpfPixelFormat.dwRGBBitCount )
                {
                        case 8:
                                Size = 16;
                                break;
                        case 16:
                                Size = 8;
                                break;
                        case 24:
                                Size = 6;
                                break;
                        default:        
                                Size = 4;
                }
            sentinalDesc.dwHeight = Size;
                sentinalDesc.dwWidth  = Size;
        }    
    
    retVal = (IDirectDraw_CreateSurface(objDD,
                                        &sentinalDesc,
                                        &objSentinal,
                                        0) == DD_OK);
    if (!retVal) {
      GDBG_INFO(80, "%s: Could not allocate surface for serial #'s.\n", FN_NAME);
      goto __errSurfaceFifo;
    }

    /* Make stride and width the 'same' so that we get the entire
     * surface as teh command fifo for the hw to just jsr to.
     */
    fifoDesc.dwWidth = surfaceWidth;
    fifoDesc.lPitch  = (fifoDesc.dwWidth*(fifoDesc.ddpfPixelFormat.dwRGBBitCount>>3) );

    /* Try to allocate a surface that contains some # of command
     * stream and persistant state buffers. This is
     * allocated in hw page units (4k) rather than the client
     * allocation units, but that should be transparent since
     * the client will just not use the extra slop.
     */
#define MAX(__a, __b) (((__a) > (__b)) ? (__a) : (__b))
    {
      const FxU32
        allocFifo  = MAX(fifo->cmdBuf.allocUnit, 0x1000UL),
        allocState = MAX(fifo->stateBuf.allocUnit, 0x1000UL),
        allocUnit  = pow2Round(allocFifo + allocState + 0xFFFUL, 0x1000UL);
      const char*
        numAllocStr = GETENV("FX_WINFIFO_INIT_ALLOC");
      FxU32
        numAlloc   = ((numAllocStr == NULL)
                      ? 0x8UL
                      : atoi(numAllocStr));
      DWORD 
        ddErr;

      while(numAlloc > 0) {
        fifoDesc.dwHeight = (allocUnit * numAlloc) / fifoDesc.lPitch;
        ddErr = IDirectDraw_CreateSurface(objDD,
                                          &fifoDesc,
                                          &objFifo,
                                          0);
        retVal = (ddErr == DD_OK);
        if (retVal) break;

        if ((ddErr != DDERR_OUTOFVIDEOMEMORY) ||
            (ddErr != DDERR_OUTOFMEMORY)) break;

        /* Try a smaller allocation */
        numAlloc--;
      }
      if (!retVal) {
        GDBG_INFO(80, "%s: Could not get cmdFifo DirectDraw surface. (0x%X)\n",
                  FN_NAME, ddErr);
        goto __errSurfaceFifo;
      }

      /* Fill in the fifo type for lock. */
      fifo->fifoType = res.optData.fifoInfoRes.fifoType;
      
      /* Do the lock so that we can write the sentinal, but the
       * client is responsible for locking before use.
       */

            fifo->surfaceFifo     = (FxU32*)objFifo;
            fifo->surfaceSentinal = (FxU32*)objSentinal;

      retVal = hwcLockWinFifo(bInfo, fifo);
      if (!retVal) goto __errSurfaceFifo;
      
      /* Write bogus serial #. */
      *(volatile FxU32*)fifo->sentinalBufferAddr = 0x0UL;    
      hwcUnlockWinFifo(bInfo, fifo);
      
      /* Its now safe to overwrite the client allocUnit's to the
       * rounded allocation units because we've finally done all of
       * the stuff that could fail.
       */
      fifo->cmdBuf.size = allocFifo * numAlloc;
      fifo->cmdBuf.allocUnit = allocFifo;
      
      fifo->stateBuf.size = allocState * numAlloc;
      fifo->stateBuf.allocUnit = allocState;      
    }
#undef MAX

    
 __errSurfaceFifo:
    /* Free our directdraw objects if something bad happened. */
    if (!retVal) {
      if (objSentinal != NULL) IDirectDrawSurface_Release(objSentinal);
      if (objFifo != NULL) IDirectDrawSurface_Release(objFifo);
    }
    if (objDD != NULL) IDirectDraw_Release(objDD);
  }

  /* Could not allocate directly accessible fifo so setup the host
   * memory backed fifo which will get dumped at execute time.  
   */
  if (!retVal && (bInfo->hdc != 0x00UL)) {
    fifo->fifoType          = HWCEXT_FIFO_HOST;

    fifo->cmdBuf.hwOffset    = 0x00UL;
    fifo->cmdBuf.size   = fifo->cmdBuf.allocUnit;
    fifo->stateBuf.hwOffset  = 0x00UL;
    fifo->stateBuf.size = fifo->stateBuf.allocUnit;

    /* This can never fail since the client is responsible for
     * allocating this memory.  
     */
    retVal = FXTRUE;
  }

  GDBG_INFO(80, "%s: retVal(0x%X) Type(0x%X)\n", 
            FN_NAME, retVal, fifo->fifoType);

  return retVal;
#undef FN_NAME
} /* hwcAllocWinFifo */

FxBool
hwcLockWinFifo(hwcBoardInfo* bInfo,
               HwcWinFifo* fifo)
{
#define FN_NAME "hwcLockWinFifo"
  FxBool 
    retVal = FXFALSE;

  if (fifo->fifoType == HWCEXT_FIFO_HOST) {
    retVal = FXTRUE;
  } else if ((fifo->fifoType == HWCEXT_FIFO_FB) ||
             (fifo->fifoType == HWCEXT_FIFO_AGP)) {
    DDSURFACEDESC
      fifoDesc,
      sentinalDesc;

    if (fifo->lockCount > 0) goto __alreadyLocked;
    if ((fifo->surfaceFifo == NULL) ||
        (fifo->surfaceSentinal == NULL)) goto __errFifoLock;

    /* Get base pointer to the fifo surfaces.
     *
     * NB: In theory we should leave these locked for the duration of
     * the of the fifo lock and we would need to set the
     * DDLOCK_NOSYSLOCK flag when doing the lock. However, when we do
     * this type of lock the directDraw driver re-maps the physical
     * address to a new set of linear address pages. So we take advantage
     * of the fact taht the current implementation will not move memory
     * behind our backs outside of a surface lock.
     */
#define HWC_DIRECTDRAW_DRIVER_NOSYSLOCK_FLAGS 0 
    fifoDesc.dwSize = sizeof(fifoDesc);
    retVal = (IDirectDrawSurface_Lock((LPDIRECTDRAWSURFACE)fifo->surfaceFifo,
                                      NULL, 
                                      &fifoDesc, 
                                      HWC_DIRECTDRAW_DRIVER_NOSYSLOCK_FLAGS |
                                      DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT,
                                      NULL) == DD_OK);

    /* NB: Since there's no working DDLOCK_NOSYSLOCK we unlock the surface
     * here, and rely on the current implementation to leave the linear 
     * mapping to the hw intact after the unlocking.
     */
    IDirectDrawSurface_Unlock((LPDIRECTDRAWSURFACE)fifo->surfaceFifo, NULL);
    if (!retVal) {
      strcpy(errorString, "Could not lock cmdFifo surface");
      GDBG_INFO(80, "%s: %s.\n", FN_NAME, errorString);
      goto __errFifoLock;
    }

    sentinalDesc.dwSize  = sizeof(sentinalDesc);
    retVal = (IDirectDrawSurface_Lock((LPDIRECTDRAWSURFACE)fifo->surfaceSentinal, 
                                      NULL, 
                                      &sentinalDesc, 
                                      HWC_DIRECTDRAW_DRIVER_NOSYSLOCK_FLAGS |
                                      DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, 
                                      NULL) == DD_OK);

    /* NB: Since there's no working DDLOCK_NOSYSLOCK we unlock the surface
     * here, and rely on the current implementation to leave the linear 
     * mapping to the hw intact after the unlocking.
     */
    IDirectDrawSurface_Unlock((LPDIRECTDRAWSURFACE)fifo->surfaceSentinal, NULL);
    if (!retVal) {
      strcpy(errorString, "Could not lock serial # surface");
      GDBG_INFO(80, "%s: %s.\n", FN_NAME, errorString);
      goto __errFifoLock;
    }

    fifo->cmdBuf.baseAddr   = (FxU32)fifoDesc.lpSurface;
    fifo->stateBuf.baseAddr = (fifo->cmdBuf.baseAddr + 
                                      fifo->cmdBuf.size);
    fifo->sentinalBufferAddr       = (FxU32)sentinalDesc.lpSurface;

    /* The DirectDraw driver may have re-mapped memory different than
     * the 2d driver's hw mapping. We have to figure out the correct
     * hw address for packet offsets and color buffer stuff.  
     */
    {
      hwcExtRequest_t      
        req;
      hwcExtResult_t       
        res;
      const FxU32
        lfbBase = bInfo->linearInfo.linearAddress[1]; /* lfb space */
      FxU32
        tileMark;      
      
      /* query for tile watermark & compute tile characteristics */
      req.which = HWCEXT_GETDEVICECONFIG;
      GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_GETDEVICECONFIG\n");
      retVal = (ExtEscape((HDC)bInfo->hdc, EXT_HWC, 
                          sizeof(req), (LPSTR)&req, 
                          sizeof(res), (LPSTR)&res) > 0);
      if (!retVal) {
        strcpy(errorString, "HWCEXT_GETDEVICECONFIG failed");
        GDBG_INFO(80, "%s: %s.\n", FN_NAME, errorString);
        goto __errFifoLock;
      }
      tileMark = res.optData.deviceConfigRes.tileMark;

      {
        struct remapRec {
          FxU32  remapAddr;
          FxU32* remapOffset;
          FxU32  surfaceType;
        } remapAddrList[] = {
          {    fifo->cmdBuf.baseAddr,      &fifo->cmdBuf.hwOffset, fifo->fifoType },
          {  fifo->stateBuf.baseAddr,    &fifo->stateBuf.hwOffset, fifo->fifoType },
          { fifo->sentinalBufferAddr, &fifo->sentinalBufferOffset, HWCEXT_FIFO_FB }
        };
        FxU32 i;

        for(i = 0; i < sizeof(remapAddrList) / sizeof(remapAddrList[0]); i++) {
          FxU32 hwOffset;

          req.which = HWCEXT_LINEAR_MAP_OFFSET;
          req.optData.mapInfoReq.mapAddr = lfbBase;
          req.optData.mapInfoReq.remapAddr = remapAddrList[i].remapAddr;
          
          GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_LINEAR_MAP_OFFSET\n");
          retVal = (ExtEscape((HDC)bInfo->hdc, EXT_HWC,
                              sizeof(req), (LPCSTR)&req,
                              sizeof(res), (LPSTR)&res) > 0);
          if (!retVal) {
            strcpy(errorString, "HWCEXT_LINEAR_MAP_OFFSET failed");
            GDBG_INFO(80, "%s: %s.\n", FN_NAME, errorString);
            goto __errFifoLock;
          }

          /* Make sure we have the right offset for tiled/linear addresses */
          hwOffset = res.optData.mapInfoRes.linAddrOffset;
          if ((remapAddrList[i].surfaceType == HWCEXT_FIFO_FB) && (hwOffset >= tileMark)) {
            hwcSurfaceInfo surfaceInfo;

            retVal = _hwcLinear2HWAddr(remapAddrList[i].remapAddr, 
                                       remapAddrList[i].remapAddr - hwOffset,
                                       0x00UL,
                                       bInfo,
                                       &surfaceInfo);
            if (!retVal) goto __errFifoLock;
            hwOffset = surfaceInfo.fbOffset;
          }
          *remapAddrList[i].remapOffset = hwOffset;
        }
      }
    }

   __alreadyLocked:
    /* Finally, declare success */
    retVal = FXTRUE;
    
   __errFifoLock:
    ;
  }

  if (retVal) fifo->lockCount++;

  return retVal;
#undef FN_NAME
} /* hwcLockWinFifo */

FxBool
hwcUnlockWinFifo(hwcBoardInfo* bInfo,
                 HwcWinFifo* fifo)
{
#define FN_NAME "hwcUnlockWinFifo"
  FxBool
    retVal = ((fifo->lockCount > 0) &&
              ((fifo->fifoType == HWCEXT_FIFO_HOST) ||
               (fifo->fifoType == HWCEXT_FIFO_FB) ||
               (fifo->fifoType == HWCEXT_FIFO_AGP)));

  if (retVal) {
    fifo->lockCount--;

    if (fifo->lockCount == 0) {
      if ((fifo->fifoType == HWCEXT_FIFO_FB) ||
          (fifo->fifoType == HWCEXT_FIFO_AGP)) {
        /* Do nothing for now since the surfaces were unlocked in
         * hwcLockWinFifo since we're not using the NOSYSLOCK flags
         * when doing the DirectDraw surface lock.  
         */
      }
    }
  }

  return retVal;
#undef FN_NAME
} /* hwcUnlockWinFifo */

FxBool
hwcFreeWinFifo( hwcBoardInfo* bInfo,
                HwcWinFifo* fifo )
{
#define FN_NAME "hwcFreeWinFifo"

  /* What type of fifo do we have? */
  if ((fifo->fifoType == HWCEXT_FIFO_FB) ||
      (fifo->fifoType == HWCEXT_FIFO_AGP)) {
    if (fifo->surfaceFifo != NULL) {
      LPDIRECTDRAWSURFACE
        objFifo = (LPDIRECTDRAWSURFACE)fifo->surfaceFifo;

      IDirectDrawSurface_Release(objFifo);

      fifo->surfaceFifo = NULL;
      fifo->cmdBuf.baseAddr = 
      fifo->cmdBuf.hwOffset = 
      fifo->cmdBuf.size = 
      fifo->stateBuf.baseAddr = 
      fifo->stateBuf.hwOffset = 0x00UL;
    }
    
    if (fifo->surfaceSentinal != NULL) {
      LPDIRECTDRAWSURFACE
        objSentinal = (LPDIRECTDRAWSURFACE)fifo->surfaceSentinal;

      IDirectDrawSurface_Release(objSentinal);

      fifo->surfaceSentinal = NULL;
      fifo->sentinalBufferAddr =
      fifo->sentinalBufferOffset = 0x00UL;
    }
  }
  
  fifo->fifoType = HWCEXT_FIFO_INVALID;

  return FXTRUE;
#undef FN_NAME
} /* hwcFreeWinFifo */

FxBool
hwcExecuteWinFifo(hwcBoardInfo*     bInfo, 
                  const FxU32       winContextId,
                  const HwcWinFifo* fifo,
                  const FxU32       serialNumber)
{
#define FN_NAME "hwcExecuteWinFifo"
  hwcExtRequest_t      req;
  hwcExtResult_t       res;

  memset( &req, 0, sizeof( req ) );
  memset( &res, 0, sizeof( res ) );

  req.which = HWCEXT_EXECUTEFIFO;
  req.contextID = winContextId;

  req.optData.executeFifoReq.fifoType     = fifo->fifoType;

  req.optData.executeFifoReq.fifoPtr      = fifo->cmdBuf.baseAddr;
  req.optData.executeFifoReq.fifoSize     = fifo->cmdBuf.size >> 2UL;
  req.optData.executeFifoReq.statePtr     = fifo->stateBuf.baseAddr;
  req.optData.executeFifoReq.stateSize    = fifo->stateBuf.size >> 2UL;
  req.optData.executeFifoReq.serialNumber = serialNumber;

  if (fifo->fifoType != HWCEXT_FIFO_HOST) {
    req.optData.executeFifoReq.fifoOffset     = fifo->cmdBuf.hwOffset;
    req.optData.executeFifoReq.stateOffset    = fifo->stateBuf.hwOffset;
    req.optData.executeFifoReq.sentinalOffset = fifo->sentinalBufferOffset;
  }
  
  GDBG_INFO(80, FN_NAME": Id(0x%X) state(0x%X) cmd(0x%X)\n",
            winContextId, 
            req.optData.executeFifoReq.stateSize,
            req.optData.executeFifoReq.fifoSize);
  
  GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_EXECUTEFIFO\n");

  return ( ExtEscape( (HDC)bInfo->hdc, EXT_HWC, 
                      sizeof( req ), (void*)&req, 
                      sizeof( res ), (void*)&res )  > 0 );

#undef FN_NAME
} /* hwcExecuteWinFifo */

FxU32
hwcExecuteStatusWinFifo(hwcBoardInfo*     bInfo,
                        const HwcWinFifo* fifo,
                        const FxU32       serialNumber)
{
#define FN_NAME "hwcExecuteStatusWinFifo"
  FxU32
    retVal = ~0x00UL;

  switch(fifo->fifoType) {
  /* Host fifo always executes synchronously */
  case HWCEXT_FIFO_HOST:
    retVal = serialNumber;
    break;

  case HWCEXT_FIFO_FB:
  case HWCEXT_FIFO_AGP:
    {
      volatile FxU32*
        bufAddr = (volatile FxU32*)fifo->sentinalBufferAddr;

      /* We need to do some extra reads here so that we make sure taht
       * the data we're returning is coherent w/ the actual frame
       * buffer due to the intervening pixel cache. The cache is,
       * currently, 4 dwords, and appears to be aligned on this
       * boundary as well so reading 4 dwords away should be flushing.
       *
       * NB: The surface for the serial # was allocated big enough to
       * handle this so we would not have to worry about it here.  
       */
      retVal = bufAddr[0];
      retVal = bufAddr[0 + 4];
      retVal = bufAddr[0];
    }
    break;

  case HWCEXT_FIFO_INVALID:
  default:
    GDBG_ERROR(FN_NAME, "Invalid fifoType");
    break;
  }

  return retVal;
#undef FN_NAME
} /* hwcExecuteStatusWinFifo */

#endif /* HWC_ACCESS_DDRAW */

#ifdef HWC_ACCESS_GDX

FxBool
hwcGetSurfaceInfo(const hwcBoardInfo* bInfo,
                  FxU32 *sfc, 
                  hwcSurfaceInfo *ret)
{
  CntrlParam  paramBlock;
  OSErr       myErr;
  GDXSurfaceDesc_t desc;
  hwcControl_t control;
  
  memset( &control, 0, sizeof( control ) );
  
  /* HLock((Handle)bInfo->hMon); */
  paramBlock.ioCRefNum = (*((GDHandle)bInfo->hMon))->gdRefNum;
  /* HUnlock((Handle)bInfo->hMon); */
  
  paramBlock.csCode = k3DfxGetDeviceConfig; /* driver-specific status request */
  *(hwcControl_t **)(&paramBlock.csParam[0]) = &control;
  
  myErr = PBControl((ParmBlkPtr)&paramBlock, false);      
  if(myErr != noErr)
    return FXFALSE;
  
  ret->tileBase = control.res.optData.deviceConfigRes.tileMark;
  ret->lpLFB = control.res.optData.deviceConfigRes.lfbBase;
  ret->pciStride = control.res.optData.deviceConfigRes.pciStride;
  ret->hwStride = control.res.optData.deviceConfigRes.hwStride;
  
  gdxSurfaceGetDesc(sfc, &desc);
  
  ret->lpSurface = desc.surface;
  ret->width = desc.width;
  ret->height = desc.height;
  ret->depth = desc.bytesPerPixel;
  ret->fbStride = desc.pitch;
  ret->fbOffset = ret->lpSurface - ret->lpLFB;
  ret->isTiled = (ret->fbOffset >= ret->tileBase) ? FXTRUE : FXFALSE;
  
  return FXTRUE;
} /* hwcGetSurfaceInfo */

FxU32
hwcAllocWinContext(hwcBoardInfo* bInfo)
{       
  CntrlParam  paramBlock;
  OSErr       myErr;
  hwcControl_t control;
  
  memset( &control, 0, sizeof( control ) );
  
  /* HLock((Handle)bInfo->hMon); */
  paramBlock.ioCRefNum = (*((GDHandle)bInfo->hMon))->gdRefNum;
  /* HUnlock((Handle)bInfo->hMon); */
  
  paramBlock.csCode = k3DfxAllocWinContext; /* driver-specific status request */
  *(hwcControl_t **)(&paramBlock.csParam[0]) = &control;
  
  
  myErr = PBControl((ParmBlkPtr)&paramBlock, false);
  
  if (myErr == noErr) {
    setLfbSwizzleMode(bInfo->regInfo.rawLfbBase,bInfo->regInfo.ioMemBase,control.res.optData.allocContextRes.depthMode);
    return control.res.optData.allocContextRes.contextID;
  }
  return 0;
} /* hwcAllocWinContext */

FxBool
hwcFreeWinContext(hwcBoardInfo* bInfo,
                  FxU32         winContextId)
{
  CntrlParam  paramBlock;
  OSErr       myErr;
  hwcControl_t control;
  
  memset( &control, 0, sizeof( control ) );
  
  /* HLock((Handle)bInfo->hMon); */
  paramBlock.ioCRefNum = (*((GDHandle)bInfo->hMon))->gdRefNum;
  /* HUnlock((Handle)bInfo->hMon); */
  
  paramBlock.csCode = k3DfxReleaseWinContext; /* driver-specific status request */
  *(hwcControl_t **)(&paramBlock.csParam[0]) = &control;
  control.req.contextID = winContextId;
  
  myErr = PBControl((ParmBlkPtr)&paramBlock, false);
  
  return myErr == noErr ? FXTRUE : FXFALSE;
} /* hwcFreeWinContext */

FxBool 
hwcAllocWinFifo(hwcBoardInfo* bInfo, 
                HwcWinFifo*   fifo,
                FxU32*        surface)
{
#define FN_NAME "hwcAllocWinFifo"
  FxBool 
    retVal = FXFALSE;

  /* Set undefined fifo type to start, and always clear the serial
   * number for this context.
   */
  fifo->fifoType = 0xFFFFFFFFUL;

  /* Check to see that we have valid surface owner to hang
   * the command fifo surface off of.
   */
  retVal = (surface != NULL);
  if (retVal) {
        GDHandle gDevice;
        GDXSurface_t 
                *objSentinal = NULL,
                *objFifo = NULL;
        GDXSurfaceDesc_t
                fifoDesc,
                sentinalDesc,
                surfaceDesc;
                
        /* Get the graphics devices for the surface so we can allocate new
         * surfaces on the same device. */
        
        gdxSurfaceGetDesc(surface, &surfaceDesc);
        
        gDevice = surfaceDesc.owner;

    memset(&fifoDesc, 0, sizeof(fifoDesc));

    /* Allocate a sentinal surface so that the client can 'know' when
     * a given fifo segment has actually been executed by reading a
     * client specified serial # out of the buffer. This only needs to
     * be big enough to hold the size of a serial #, but we need to
     * work around some sort of whacked agp bug when reading back. See
     * hwcExecuteStatusWinFifo() for details.
     */
    sentinalDesc = fifoDesc;

    sentinalDesc.height = 4;
    sentinalDesc.width  = 4;
    sentinalDesc.bytesPerPixel = 4;
    
    objSentinal = gdxSurfaceAlloc(gDevice, &sentinalDesc);
    
    if (!objSentinal) {
      GDBG_INFO(80, "%s: Could not allocate surface for serial #'s.\n", FN_NAME);
      retVal = FXFALSE;
      goto __errSurfaceFifo;
    }

        /* Assume for now that 2D command fifo is in local framebuffer space */
 
    /* Make pitch and width the same so that indexing is easy. 
     * Setup the allocation in hw page unit sizes. (4k bytes)
     */
    fifoDesc.pitch      = 0x1000UL;
    fifoDesc.width  = (fifoDesc.pitch >> 2UL);
    fifoDesc.bytesPerPixel = 4;
    
    /* Try to allocate a surface that contains some # of command
     * stream buffers and one persistant state buffer. This is
     * allocated in hw page units (4k) rather than the client
     * allocation units, but that should be transparent since
     * the client will just not use the extra slop.
     */
#define MAX(__a, __b) (((__a) > (__b)) ? (__a) : (__b))
    {
      const FxU32
        allocFifo  = MAX(fifo->cmdBuf.allocUnit, 0x1000UL),
        allocState = MAX(fifo->stateBuf.allocUnit, 0x1000UL),
        allocUnit  = pow2Round(allocFifo + allocState + 0xFFFUL, 0x1000UL);
      const char*
        numAllocStr = GETENV("FX_WINFIFO_INIT_ALLOC");
      FxU32
        numAlloc   = ((numAllocStr == NULL)
                      ? 0x8UL
                      : atoi(numAllocStr));

      while(numAlloc > 0) {
        fifoDesc.height = (allocUnit * numAlloc) / fifoDesc.pitch;
        /* objFifo = NULL; */
        objFifo = gdxSurfaceAlloc(gDevice,&fifoDesc);
        if(objFifo)
                break;

        /* Try a smaller allocation */
        numAlloc--;
      }
      if(!objFifo) {
        GDBG_INFO(80, "%s: Could not get cmdFifo GDX Surface.\n",
                  FN_NAME);
        retVal = FXFALSE;
        goto __errSurfaceFifo;
      }
      
      /* Its now safe to overwrite the client allocUnit's to the
       * rounded allocation units because we've finally done all of
       * the stuff that could fail.
       */
      fifo->cmdBuf.size = allocFifo * numAlloc;
      fifo->cmdBuf.allocUnit = allocFifo;

      fifo->stateBuf.size = allocState * numAlloc;
      fifo->stateBuf.allocUnit = allocState;
    }
#undef MAX
      
        retVal = FXTRUE;
    /* Fill in some more of the return buffer info, but were not quite
     * done and ready to declare success until we lock the fifo down.  
     */
    fifo->fifoType = HWCEXT_FIFO_FB;

    fifo->surfaceFifo     = (FxU32*)objFifo;
    fifo->surfaceSentinal = (FxU32*)objSentinal;

    /* Do the lock so that we can write the sentinal, but the
     * client is responsible for locking before use.
     */
    if (hwcLockWinFifo(bInfo, fifo)) {
      /* Write bogus serial # */
      *(volatile FxU32*)fifo->sentinalBufferAddr = 0x0UL;
    
      hwcUnlockWinFifo(bInfo, fifo);
    }
    
 __errSurfaceFifo:
    /* Free our directdraw objects if something bad happened. */
    if (!retVal) {
      if (objSentinal != NULL) gdxSurfaceFree(objSentinal);
      if (objFifo != NULL) gdxSurfaceFree(objFifo);
    }
  }

  /* Could not allocate directly accessible fifo so setup the host
   * memory backed fifo which will get dumped at execute time.  
   */
  if (!retVal) {
    fifo->fifoType          = HWCEXT_FIFO_HOST;

    fifo->cmdBuf.hwOffset    = 0x00UL;
    fifo->cmdBuf.size   = fifo->cmdBuf.allocUnit;
    fifo->stateBuf.hwOffset  = 0x00UL;
    fifo->stateBuf.size = fifo->stateBuf.allocUnit;

    /* This can never fail since the client is responsible for
     * allocating this memory.  
     */
    retVal = FXTRUE;
  }

  return retVal;
#undef FN_NAME
}                

FxBool
hwcLockWinFifo(hwcBoardInfo* bInfo,
               HwcWinFifo* fifo)
{
#define FN_NAME "hwcLockWinFifo"
  FxBool 
    retVal = FXFALSE;

  if (fifo->fifoType == HWCEXT_FIFO_HOST) {
    retVal = FXTRUE;
  } else if ((fifo->fifoType == HWCEXT_FIFO_FB) ||
             (fifo->fifoType == HWCEXT_FIFO_AGP)) {
       
    GDXSurfaceDesc_t 
        fifoDesc,
        sentinalDesc;
              
    if (fifo->lockCount > 0) goto __alreadyLocked;
    if ((fifo->surfaceFifo == NULL) ||
        (fifo->surfaceSentinal == NULL)) goto __errFifoLock;

    /* Get base pointer to the fifo surfaces.
     */
     
    gdxSurfaceGetDesc(fifo->surfaceFifo,&fifoDesc);
    gdxSurfaceGetDesc(fifo->surfaceSentinal,&sentinalDesc);
    
    /* Since there's no remapping crap in MacOS, this is easy. */
    fifo->cmdBuf.baseAddr               = fifoDesc.surface; 
    fifo->stateBuf.baseAddr     = fifo->cmdBuf.baseAddr + fifo->cmdBuf.size;
    fifo->sentinalBufferAddr    = sentinalDesc.surface;

        /* Compute hardware offsets as well */
    fifo->cmdBuf.hwOffset =
      fifo->cmdBuf.baseAddr - bInfo->regInfo.rawLfbBase;
    fifo->stateBuf.hwOffset =
      fifo->stateBuf.baseAddr - bInfo->regInfo.rawLfbBase;
    fifo->sentinalBufferOffset =
      fifo->sentinalBufferAddr - bInfo->regInfo.rawLfbBase;
                
    __alreadyLocked:

    /* Finally, declare success */
    retVal = FXTRUE;
    
   __errFifoLock:
    ;
  }
  
  if (retVal) fifo->lockCount++;
  
  return retVal;
#undef FN_NAME
} /* hwcLockWinFifo */

FxBool
hwcUnlockWinFifo(hwcBoardInfo* bInfo,
                 HwcWinFifo* fifo)
{
#define FN_NAME "hwcUnlockWinFifo"
  FxBool
    retVal = ((fifo->lockCount > 0) &&
              ((fifo->fifoType == HWCEXT_FIFO_HOST) ||
               (fifo->fifoType == HWCEXT_FIFO_FB) ||
               (fifo->fifoType == HWCEXT_FIFO_AGP)));

  if (retVal) {
    fifo->lockCount--;

    if (fifo->lockCount == 0) {
      if ((fifo->fifoType == HWCEXT_FIFO_FB) ||
          (fifo->fifoType == HWCEXT_FIFO_AGP)) {
        /* Don't currently do anything since surfaces don't get moved 
         * around yet on MacOS.
         */
      }
    }
  }

  return retVal;
#undef FN_NAME
} /* hwcUnlockWinFifo */

FxBool
hwcFreeWinFifo( hwcBoardInfo* bInfo,
                HwcWinFifo* fifo )
{
#define FN_NAME "hwcFreeWinFifo"

  /* What type of fifo do we have? */
  if ((fifo->fifoType == HWCEXT_FIFO_FB) ||
      (fifo->fifoType == HWCEXT_FIFO_AGP)) {
    if (fifo->surfaceFifo != NULL) {
        gdxSurfaceFree(fifo->surfaceFifo);

      fifo->surfaceFifo = NULL;
      fifo->cmdBuf.baseAddr = 
      fifo->cmdBuf.hwOffset = 
      fifo->cmdBuf.size = 
      fifo->stateBuf.baseAddr = 
      fifo->stateBuf.hwOffset = 0x00UL;
    }
    
    if (fifo->surfaceSentinal != NULL) {
        gdxSurfaceFree(fifo->surfaceSentinal);

      fifo->surfaceSentinal = NULL;
      fifo->sentinalBufferAddr =
      fifo->sentinalBufferOffset = 0x00UL;
    }
  }
  
  fifo->fifoType = HWCEXT_FIFO_INVALID;

  return FXTRUE;
#undef FN_NAME
} /* hwcFreeWinFifo */

FxBool
hwcExecuteWinFifo(hwcBoardInfo*     bInfo, 
                  const FxU32       winContextId,
                  const HwcWinFifo* fifo,
                  const FxU32       serialNumber)
{
  CntrlParam  paramBlock;
  OSErr       myErr;
  hwcControl_t control;
  
  memset( &control, 0, sizeof( control ) );
  
  /* HLock((Handle)bInfo->hMon); */
  paramBlock.ioCRefNum = (*((GDHandle)bInfo->hMon))->gdRefNum;
  /* HUnlock((Handle)bInfo->hMon); */
  
  paramBlock.csCode = k3DfxExecuteFifo; /* driver-specific status request */
  *(hwcControl_t **)(&paramBlock.csParam[0]) = &control;
  
  control.req.contextID = winContextId;
  control.req.optData.executeFifoReq.fifoType     = fifo->fifoType;
  control.req.optData.executeFifoReq.fifoPtr      = fifo->cmdBuf.baseAddr;
  control.req.optData.executeFifoReq.fifoSize     = fifo->cmdBuf.size >> 2UL;
  control.req.optData.executeFifoReq.statePtr     = fifo->stateBuf.baseAddr;
  control.req.optData.executeFifoReq.stateSize    = fifo->stateBuf.size >> 2UL;
  control.req.optData.executeFifoReq.serialNumber = serialNumber;
  
  if (fifo->fifoType != HWCEXT_FIFO_HOST) {
    control.req.optData.executeFifoReq.fifoOffset     = fifo->cmdBuf.hwOffset;
    control.req.optData.executeFifoReq.stateOffset    = fifo->stateBuf.hwOffset;
    control.req.optData.executeFifoReq.sentinalOffset = fifo->sentinalBufferOffset;
  }
  
  myErr = PBControl((ParmBlkPtr)&paramBlock, false);
  
  return myErr == noErr ? FXTRUE : FXFALSE;

  
} /* hwcExecuteWinFifo */

FxU32
hwcExecuteStatusWinFifo(hwcBoardInfo*     bInfo,
                        const HwcWinFifo* fifo,
                        const FxU32       serialNumber)
{
#define FN_NAME "hwcExecuteStatusWinFifo"
  FxU32
    retVal = ~0x00UL;

  switch(fifo->fifoType) {
  /* Host fifo always executes synchronously */
  case HWCEXT_FIFO_HOST:
    retVal = serialNumber;
    break;

  case HWCEXT_FIFO_FB:
  case HWCEXT_FIFO_AGP:
    {
      volatile FxU32*
        bufAddr = (volatile FxU32*)fifo->sentinalBufferAddr;

      /* We need to do some extra reads here so that we make sure taht
       * the data we're returning is coherent w/ the actual frame
       * buffer due to the intervening pixel cache. The cache is,
       * currently, 4 dwords, and appears to be aligned on this
       * boundary as well so reading 4 dwords away should be flushing.
       *
       * NB: The surface for the serial # was allocated big enough to
       * handle this so we would not have to worry about it here.  */
      retVal = (bufAddr[0]);
      retVal = (bufAddr[0 + 4]);
      retVal = (bufAddr[0]);
    }
    break;

  case HWCEXT_FIFO_INVALID:
  default:
    GDBG_ERROR(FN_NAME, "Invalid fifoType");
    break;
  }

  return retVal;
#undef FN_NAME
}                        

/* #define LINEAR_STRIDE_ALIGN 16UL */
/* #define TILE_BIT            0x00008000UL */

#endif /* HWC_ACCESS_GDX */

FxBool
hwcInitVideo(hwcBoardInfo *bInfo, FxBool tiled, FxVideoTimingInfo *vidTiming,
             FxBool overlay)
{
#define FN_NAME "hwcInitVideo"
  FxU32 pixFmt = SST_OVERLAY_PIXEL_RGB565U;
  FxU32
    stride= (tiled) ? bInfo->buffInfo.bufStrideInTiles : bInfo->vidInfo.stride;
  FxU32
    scrWidth, scrHeight,
    ovlWidth, ovlHeight,
    vidProcCfg, vidScreenSize, vidOverlayEndScreenCoord,
    vidOverlayDudx, vidOverlayDvdy,
    dramInit1;

  FxBool
    lfbMemoryConfig,
    miscInit0;

  float
    scale;


#ifdef HWC_EXT_INIT 
  hwcExtRequest_t
    ctxReq;
  hwcExtResult_t
    ctxRes;
#endif
  
  {
    FxU32 refresh;
    static FxU32 refConstToRefreshHz[] = {
      60,                       /* GR_REFRESH_60Hz   */
      70,                       /* GR_REFRESH_70Hz   */
      72,                       /* GR_REFRESH_72Hz   */
      75,                       /* GR_REFRESH_75Hz   */
      80,                       /* GR_REFRESH_80Hz   */
      90,                       /* GR_REFRESH_90Hz   */
      100,                      /* GR_REFRESH_100Hz  */
      85,                       /* GR_REFRESH_85Hz   */
      120,                      /* GR_REFRESH_120Hz  */
      0
    };  

    if (bInfo->vidInfo.vRefresh > GR_REFRESH_120Hz)
      refresh = 0;
    else
      refresh = bInfo->vidInfo.vRefresh;

    refresh = refConstToRefreshHz[refresh]; /* Make sure we use the table, otherwise
                                               we will always pass 0Hz to setVideoMode */
    if (getenv("FX_GLIDE_REFRESH")) {
      refresh = atoi(getenv("FX_GLIDE_REFRESH"));
    }
    
    if ( !setVideoMode( (void*)bInfo->vidInfo.hWnd, 
                        bInfo->vidInfo.xRes,
                        bInfo->vidInfo.yRes,
                        refresh,
                        bInfo->hMon ) ) {
      GDBG_INFO(80, "%s:  dxOpen() failed!\n", FN_NAME);
      return FXFALSE;
    }
#if SET_SWIZZLEHACK
    setLfbSwizzleMode(bInfo->regInfo.rawLfbBase,bInfo->regInfo.ioMemBase,8);
#endif
  }

#ifdef HWC_EXT_INIT
  ctxReq.which = HWCEXT_HWCSETEXCLUSIVE;
  ctxReq.optData.linearAddrReq.devNum = 0;
  
  
  GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_HWCSETEXCLUSIVE\n");
  ExtEscape((HDC) bInfo->hdc, EXT_HWC, sizeof(ctxReq), (void *) &ctxReq,
            sizeof(ctxRes), (void *) &ctxRes);
  
  if (ctxRes.resStatus != 1) {
    strcpy(errorString, "HWCEXT_HWCSETEXCLUSIVE Failed");
    return FXFALSE;
  }

#else
  /* This is off for now until the rest of the alt-tab type things are done. */
#if 0
  /* Before letting glide party on the hw check to see if we're on a
   * system w/ a 2d environment, and make sure taht its happy about us
   * doing this before dorking w/ things that are going to make it
   * unhappy.  
   */
  if (Dpmi2DEnvironmentP()) {
    bInfo->hdc = DpmiDeviceContextGet("3DFXVB");
    if (bInfo->hdc == 0x00UL) return FXFALSE;
    
    if (!DpmiDeviceContextDispatch(bInfo->hdc, FxDCIsFullscreenP)) return FXFALSE;
    if (!DpmiDeviceContextDispatch(bInfo->hdc, FxDCExclusiveLock)) return FXFALSE;
  }
#endif
#endif
  
  HWC_IO_STORE(bInfo->regInfo, vidOverlayDudxOffsetSrcWidth,
    ((bInfo->vidInfo.xRes << 1) << 19));

  /* Video pixel buffer threshold */
  {
    FxU32 vidPixelBufThold;
    FxU32 thold = 32;

    if (getenv("SSTVB_PIXTHOLD")) {
      thold = atoi(getenv("SSTVB_PIXTHOLD"));
    }

    thold &= 0x3f;

    vidPixelBufThold = (thold | (thold << 6) | (thold << 12));

    HWC_IO_STORE(bInfo->regInfo, vidPixelBufThold, vidPixelBufThold);
  }

#ifdef __WIN32__
  if (vidTiming) {
    hwcExtRequest_t req;
    hwcExtResult_t res;

    req.which = HWCEXT_VIDTIMING;
    req.optData.vidTimingReq.vidTiming = (void *) vidTiming;
    
    GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_VIDTIMING\n");
    ExtEscape((HDC) bInfo->hdc, EXT_HWC, sizeof(req), (void *) &req,
      sizeof(res), (void *) &res);

    /* Ignore failure */

  }
#endif

#ifdef __DOS32__
  /* Now call the cinit code */

  h3InitVideoOverlaySurface(
    bInfo->regInfo.ioPortBase,
    FXTRUE,                     /* 1=enable Overlay surface (OS), 1=disable */
    FXFALSE,                    /* 1=enable OS stereo, 0=disable */
    FXFALSE,                    /* 1=enable horizontal scaling, 0=disable */
    0,                          /* horizontal scale factor (ignored if not) */
    FXFALSE,                    /* 1=enable vertical scaling, 0=disable */
    0,                          /* vertical scale factor (ignored if not) */
    0,                          /* Filter mode */
    FXTRUE,                     /* tiled */
    pixFmt,                     /* pixel format of OS */
    FXTRUE,                    /* bypass clut for OS? */
    0,                          /* 0=lower 256 CLUT entries, 1=upper 256 */
    bInfo->buffInfo.colBuffStart[0],/* board address of beginning of OS */
    stride);                    /* distance between scanlines of the OS, in
                                   units of bytes for linear OS's and tiles for
                                   tiled OS's */
  HWC_IO_STORE(bInfo->regInfo, vidOverlayStartCoords, 0);
  HWC_IO_STORE(bInfo->regInfo, vidOverlayEndScreenCoord,
    (bInfo->vidInfo.yRes  << SST_OVERLAY_Y_SHIFT) |
    (bInfo->vidInfo.xRes & SST_OVERLAY_X) );
#else
  hwcInitVideoOverlaySurface(
    &bInfo->regInfo,
    FXTRUE,                     /* 1=enable Overlay surface (OS), 1=disable */
    FXFALSE,                    /* 1=enable OS stereo, 0=disable */
    FXFALSE,                    /* 1=enable horizontal scaling, 0=disable */
    0,                          /* horizontal scale factor (ignored if not) */
    FXFALSE,                    /* 1=enable vertical scaling, 0=disable */
    0,                          /* vertical scale factor (ignored if not) */
    0,                          /* Filter mode */
    FXTRUE,                     /* tiled */
    pixFmt,                     /* pixel format of OS */
    FXFALSE,                    /* bypass clut for OS? */
    0,                          /* 0=lower 256 CLUT entries, 1=upper 256 */
    bInfo->buffInfo.colBuffStart[0],/* board address of beginning of OS */
    stride);                    /* distance between scanlines of the OS, in
                                   units of bytes for linear OS's and tiles for
                                   tiled OS's */
  HWC_IO_STORE(bInfo->regInfo, vidOverlayStartCoords, 0);
  
#endif
  
  /* 
     Setup video scaling for half-modes
   */

  /* Get some important info */
  HWC_IO_LOAD(bInfo->regInfo, vidScreenSize, vidScreenSize);
  HWC_IO_LOAD(bInfo->regInfo, vidProcCfg, vidProcCfg);
  HWC_IO_LOAD(bInfo->regInfo, vidOverlayEndScreenCoord, vidOverlayEndScreenCoord);

  scrWidth = (vidScreenSize >> SST_VIDEO_SCREEN_WIDTH_SHIFT) & 0xfff;
  scrHeight = (vidScreenSize >> SST_VIDEO_SCREEN_HEIGHT_SHIFT) & 0xfff;

  ovlWidth = ((vidOverlayEndScreenCoord) >> SST_OVERLAY_X_SHIFT) & 0xfff;
  ovlHeight = ((vidOverlayEndScreenCoord) >> SST_OVERLAY_Y_SHIFT) & 0xfff;  

  ovlWidth += 1;
  ovlHeight += 1;

  /* Check to see if the screen and overlay dimensions match. 
   * There are two cases that can happen in reality.
   *
   * scrXXX > appXXX: This is a 'real' case, and the overlay dimension
   * needs to mag scaled so that it fits the requested size.
   *
   * (scrXXX == appXXX) && (ovlXXX != scrXXX): This is a somewhat artificial
   * case where someone left the overlay set to some value, and these did
   * not get reset in the setVideoMode processing. (For example, if the user is
   * running an application which bus masters data directly to our video overlay
   * when launching a glide application). In this case we need to fiddle w/ the
   * overlay dimension so that it matches the requested resolution.
   *
   * (scrXXX < appXXX): If setVideoMode is actually working correctly, this cannot
   * happen because that code has to know that we can't do min scaling.
   */

  vidOverlayDudx = 0UL;
  if (scrWidth > bInfo->vidInfo.xRes) {
    vidProcCfg |= SST_OVERLAY_HORIZ_SCALE_EN;

    ovlWidth = scrWidth;

    scale = ((float) bInfo->vidInfo.xRes) / ((float) ovlWidth);

    vidOverlayDudx = (FxU32) (scale * ((float) (1 << 20)));
    HWC_IO_STORE(bInfo->regInfo, vidOverlayDudx, vidOverlayDudx);
  } else if (ovlWidth != scrWidth) {
    ovlWidth = scrWidth;
  }

  HWC_IO_STORE(bInfo->regInfo, vidOverlayDudx, vidOverlayDudx);

  vidOverlayDvdy = 0UL;
  if (scrHeight > bInfo->vidInfo.yRes) {
    vidProcCfg |= SST_OVERLAY_VERT_SCALE_EN;

    ovlHeight = scrHeight;

    scale = ((float) bInfo->vidInfo.yRes) / ((float) ovlHeight);

    vidOverlayDvdy = (FxU32) (scale * ((float) (1 << 20)));
    HWC_IO_STORE(bInfo->regInfo, vidOverlayDvdy, vidOverlayDvdy);
  } else if (ovlHeight != scrHeight) {
    ovlHeight = scrHeight;
  }

  HWC_IO_STORE(bInfo->regInfo, vidOverlayDvdy, vidOverlayDvdy);

  vidOverlayEndScreenCoord = (((ovlHeight - 1) << SST_OVERLAY_Y_SHIFT) |
                              ((ovlWidth - 1) << SST_OVERLAY_X_SHIFT));
  HWC_IO_STORE(bInfo->regInfo, vidOverlayEndScreenCoord, vidOverlayEndScreenCoord);

/* 4/30/99 srogers
   * Adding user support for switching the video filter from 2x2 to 4x1 */
  vidProcCfg &= ~SST_OVERLAY_FILTER_MODE;
  if (NULL == GETENV("SSTH3_OVERLAYMODE")) {
        /* We are in optimal mode by default */
        if(bInfo->vidInfo.xRes < 1024)
      vidProcCfg |= SST_OVERLAY_FILTER_2X2;
        else
          vidProcCfg |= SST_OVERLAY_FILTER_4X4;
  }
  else {
    switch(atoi(GETENV("SSTH3_OVERLAYMODE")))
        {
                default:
                case 1: /* Optimal */
          if(bInfo->vidInfo.xRes < 1024)
                    vidProcCfg |= SST_OVERLAY_FILTER_2X2;
          else
            vidProcCfg |= SST_OVERLAY_FILTER_4X4;
                  break;
                case 2: /* Normal */
            vidProcCfg |= SST_OVERLAY_FILTER_4X4;
                        break;
                case 3: /* High */
                   /* make sure that if 2x video mode is enabled, we use the 4x1 filter. */
                   if(vidProcCfg & SST_VIDEO_2X_MODE_EN)
            vidProcCfg |= SST_OVERLAY_FILTER_4X4;
          else
                    vidProcCfg |= SST_OVERLAY_FILTER_2X2;
                  break;
       }
  }        
  /* Drag down color bits */
  vidProcCfg &= ~SST_OVERLAY_PIXEL_FORMAT;

  /* Set RGB565 */
  vidProcCfg |= SST_OVERLAY_PIXEL_RGB565D;

  HWC_IO_STORE(bInfo->regInfo, vidProcCfg, vidProcCfg);


  GDBG_INFO(80, FN_NAME ":  Video Overlay Info:\n\
            Screen Width =       0x%x (%d) \n\
            Screen Height =      0x%x (%d) \n\
            Render Width =      0x%x (%d) \n\
            Render Height =     0x%x (%d) \n",
            ovlWidth, ovlWidth, ovlHeight, ovlHeight, 
            bInfo->vidInfo.xRes, bInfo->vidInfo.xRes, 
            bInfo->vidInfo.yRes, bInfo->vidInfo.yRes);

  /* Get miscInit0 for y-sub  */
  HWC_IO_LOAD(bInfo->regInfo, miscInit0, miscInit0);

  /* Clear out relavent bits */
  miscInit0 &= ~SST_YORIGIN_TOP;
  miscInit0 |= ((bInfo->vidInfo.yRes - 1)  << SST_YORIGIN_TOP_SHIFT);

  HWC_IO_STORE(bInfo->regInfo, miscInit0, miscInit0);
  
  /* Set up lfbMemoryConfig */
  lfbMemoryConfig =
    (bInfo->fbOffset >> 12) | 
    SST_RAW_LFB_ADDR_STRIDE_4K |
    (bInfo->buffInfo.bufStrideInTiles << SST_RAW_LFB_TILE_STRIDE_SHIFT);

  HWC_IO_STORE(bInfo->regInfo, lfbMemoryConfig, lfbMemoryConfig);

  /* Filthy memclock hack */
  if (getenv("H3_MEM_CLOCK")) {
    int mHz;
    FxU32 pllVal;
    mHz = atoi(getenv("H3_MEM_CLOCK"));

    switch (mHz) {
    case 50:
      pllVal = 0x2806;
      break;
      
    case 75:
      pllVal = 0x7125;
      break;
      
    case 80:
      pllVal = 0x7925;
      break;

    case 100:
      pllVal = 0x2805;
      break;

    default:
      pllVal = 0;
      gdbg_printf("%s:  Bogus MEMCLOCK setting!\n", FN_NAME);
      break;
    }

    if (pllVal) {
      HWC_IO_STORE(bInfo->regInfo, pllCtrl1, pllVal);
      HWC_IO_STORE(bInfo->regInfo, pllCtrl2, pllVal);
      gdbg_printf("%s:  Setting memory clock to %d MHz\n", FN_NAME,
        mHz);
    }

  }

  /* Set up dramInit1 for triple or double buffering */
  HWC_IO_LOAD(bInfo->regInfo, dramInit1, dramInit1);
  if (bInfo->vidInfo.tripleBuffering)
    dramInit1 |= SST_TRIPLE_BUFFER_EN;
  else
    dramInit1 &= ~SST_TRIPLE_BUFFER_EN;

  HWC_IO_STORE(bInfo->regInfo, dramInit1, dramInit1);

  HWC_IO_STORE(bInfo->regInfo, vidMaxRGBDelta, 0x100810);

  HWC_IO_STORE( bInfo->regInfo, vidDesktopOverlayStride,
                ( bInfo->buffInfo.bufStrideInTiles << 16 ) |
                  bInfo->buffInfo.bufStrideInTiles );

  return FXTRUE;

#undef FN_NAME
} /* hwcInitVideo */

FxBool
hwcRestoreVideo(hwcBoardInfo *bInfo)
{
#define FN_NAME "hwcRestoreVideo"
  FxU32 depth;

  /* Disable FIFO */
  do {
    HWC_CAGP_LOAD(bInfo->regInfo, cmdFifo0.depth, depth);
  } while (depth);

  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.baseSize, 0);

#ifdef HWC_EXT_INIT
  {
    hwcExtRequest_t req;
    hwcExtResult_t res;

    req.which = HWCEXT_HWCRLSEXCLUSIVE;
    req.optData.linearAddrReq.devNum = 0;
    GDBG_INFO(90, FN_NAME ":  ExtEscape:HWCEXT_HWCRLSEXCLUSIVE\n");
    ExtEscape((HDC) bInfo->hdc, EXT_HWC, sizeof(req), (void *) &req,
      sizeof(res), (void *) &res);

    GDBG_INFO(80, "%s:  sizeof(res) = %d\n", FN_NAME, sizeof(res));
    GDBG_INFO(80, "%s:  res.resStatus = %d\n", FN_NAME, res.resStatus);
    GDBG_INFO(80, "%s:  &res = 0x%x\n", FN_NAME, &res);

    if (res.resStatus != 1) {
      strcpy(errorString, "HWCEXT_HWCRLSEXCLUSIVE Failed");
      return FXFALSE;
    }
    
  }
#endif /* HWC_EXT_INIT */

  /* Restore display */
  
  resetVideo();

  return FXTRUE;
#undef FN_NAME
} /* hwcRestoreVideo */

char *
hwcGetErrorString()
{
#define FN_NAME "hwcGetErrorString"
  return errorString;
#undef FN_NAME
} /* hwcGetErrorString */

FxBool
hwcCheckMemSize(hwcBoardInfo *bInfo, FxU32 xres, FxU32 yres, FxU32 nColBuffers,
                FxU32 nAuxBuffers, FxBool tiled)
{
#define FN_NAME "hwcCheckMemSize"
  FxU32
    bufSize, totSize;

  bufSize = calcBufferSize(xres, yres, tiled);

  totSize = (nColBuffers + nAuxBuffers) * bufSize;

  if (totSize < ((bInfo->h3Mem << 20) - bInfo->min_tramSize)) /* Need 2M for texture */
    return FXTRUE;
  else
    return FXFALSE;    
#undef FN_NAME
} /* hwcCheckMemSize */

static FxU32
calcBufferStride(FxU32 xres, FxBool tiled)
{
  FxU32
    strideInTiles;

  if (tiled == FXTRUE) {
    /* Calculate tile width stuff */
    strideInTiles = (xres << 1) >> 7;
    if ((xres << 1) & (HWC_TILE_WIDTH - 1))
      strideInTiles++;
    
    return (strideInTiles * HWC_TILE_WIDTH);

  } else {
    return (xres << 1);
  }
} /* calcBufferStride */

static FxU32
calcBufferHeightInTiles(FxU32 yres)
{
  FxU32
    heightInTiles;            /* Height of buffer in tiles */


  /* Calculate tile height stuff */
  heightInTiles = yres >> 5;
  
  if (yres & (HWC_TILE_HEIGHT - 1))
    heightInTiles++;

  return heightInTiles;

} /* calcBufferHeightInTiles */

static FxU32
calcBufferSizeInTiles(FxU32 xres, FxU32 yres) {
  FxU32
    bufSizeInTiles;           /* Size of buffer in tiles */

  bufSizeInTiles =
    calcBufferHeightInTiles(yres) * (calcBufferStride(xres, FXTRUE) >> 7);

  return bufSizeInTiles;

} /* calcBufferSizeInTiles */

static FxU32
calcBufferSize(FxU32 xres, FxU32 yres, FxBool tiled)
{
  FxU32
    stride,
    height,
    bufSize;                  /* Size of buffer in bytes */

  if (tiled) {
    stride = calcBufferStride(xres, tiled);
    height = HWC_TILE_HEIGHT * calcBufferHeightInTiles(yres);
  } else {
    stride = xres << 1;
    height = yres;
  }

  bufSize = stride * height;
  
  return bufSize;

} /* calcBufferSize */

/* How the hw treats lfb accesses are dependent on the 'type' of
 * memory (tiled/linear) that the color/aux buffers are in. We
 * pre-compute the actual lfb address here while we know about the
 * memory space and if we adjusted the page alignment above.
 *
 * NB: If we are in tiled mode then the fact that we align the color
 * buffers on page boundaries means that the y offset of the buffers
 * may not actually be on a boundary for the tile addressing scheme.
 * The 'rounding' done to HWC_TILED_BUFFER_Y_ALIGN adjust for this.
 *
 * NB: The memory optimization of aligning color buffers on even page
 * boundaries will cause the tiled lfb access to be off by a page so
 * we add in the width of a page (HWC_TILED_BUFFER_X_ADJUST) here.
 */
#define HWC_TILED_BUFFER_BYTES    0x1000UL  /* 128 Bytes x 32 lines */
#define HWC_TILED_BUFFER_Y_ALIGN  0x20000UL
#define HWC_TILED_BUFFER_X_ADJUST 0x80UL

static FxU32
hwcBufferLfbAddr(FxU32 bufNum, 
                 const hwcBoardInfo* bInfo,
                 FxBool colBufAlignP)
{
  FxU32 retVal = 0x00UL;

  if (bInfo->vidInfo.tiled) {
    retVal = (bInfo->fbOffset + 
              pow2Round(bufNum * bInfo->vidInfo.yRes * HWC_TILED_BUFFER_BYTES, 
                        HWC_TILED_BUFFER_Y_ALIGN) +
              colBufAlignP * HWC_TILED_BUFFER_X_ADJUST);
  } else if (bufNum < bInfo->buffInfo.nColBuffers) {
    retVal = bInfo->buffInfo.colBuffStart[bufNum];
  } else if (bufNum == bInfo->buffInfo.nColBuffers) {
    retVal = bInfo->buffInfo.auxBuffStart;
  }

  return retVal;
}

static FxU32
pow2Round(FxU32 val, FxU32 pow2Const)
{
  const FxU32 pow2Mask = (pow2Const - 1UL);

  return ((val + pow2Mask) & ~pow2Mask);
}

FxU32 
hwcInitAGPFifo(hwcBoardInfo *bInfo, FxBool enableHoleCounting) 
{
#define FN_NAME "hwcInitAGPFifo"
#if HWC_EXT_INIT
  FxU32
    agpSize, agpLAddr, agpPAddr;
  FxU32
    cagpRegs;                   /* pointer to Cmd/AGP regs */
  hwcExtRequest_t req;
  hwcExtResult_t res;


  if (bInfo->regInfo.initialized == FXFALSE) {
    sprintf(errorString, "%s:  Called before hwcMapBoard\n", FN_NAME);
    return FXFALSE;
  }
  cagpRegs = bInfo->regInfo.cmdAGPBase;

  if (bInfo->buffInfo.initialized == FXFALSE) {
    sprintf(errorString, "%s:  Called before hwcInitBuffers\n", FN_NAME);
    return FXFALSE;
  }

  req.which = HWCEXT_GETAGPINFO;

  GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_GETAGPINFO\n");
  ExtEscape((HDC) bInfo->hdc, EXT_HWC, sizeof(req), (void *) &req,
    sizeof(res), (void *) &res);

  /* If we fail, bail and go to memory fifo */
  if (res.resStatus != 1) {
    return hwcInitFifo(bInfo, enableHoleCounting); 
  }

  agpLAddr = res.optData.agpInfoRes.lAddr;
  agpPAddr = res.optData.agpInfoRes.pAddr;
  agpSize = res.optData.agpInfoRes.size;

  /* disable the CMD fifo */
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.baseSize, 0);

  bInfo->fifoInfo.agpFifo = 1;
  bInfo->fifoInfo.agpVirtAddr = agpLAddr;
  bInfo->fifoInfo.agpPhysAddr = agpPAddr;
  bInfo->fifoInfo.fifoStart = agpPAddr;
  bInfo->fifoInfo.agpSize = agpSize;

  GDBG_INFO(80, "%s AGP linear address = 0x%x\n", FN_NAME, agpLAddr);
  GDBG_INFO(80, "%s AGP physical address = 0x%x\n", FN_NAME, agpPAddr);
  GDBG_INFO(80, "%s AGP Size = 0x%x\n", FN_NAME, agpSize);

#if 0
  From Windows code:

  SETDW(_FF(lpCRegs)->cmdFifo0.baseSize, 0);
  SETDW(_FF(lpCRegs)->cmdFifo0.baseAddrL,
    (_FF(agpPhysAddr) >> 12) );
  SETDW(_FF(lpCRegs)->cmdFifo0.readPtrL, _FF(agpPhysAddr) );
  SETDW(_FF(lpCRegs)->cmdFifo0.readPtrH,  0);
  SETDW(_FF(lpCRegs)->cmdFifo0.aMin,  _FF(agpPhysAddr) - 4);
  SETDW(_FF(lpCRegs)->cmdFifo0.aMax,  _FF(agpPhysAddr) - 4);
  SETDW(_FF(lpCRegs)->cmdFifo0.depth, 0);
  SETDW(_FF(lpCRegs)->cmdFifo0.holeCount, 0);
  SETDW(_FF(lpCRegs)->cmdFifoThresh, 0x122);
  SETDW(_FF(lpCRegs)->cmdFifo0.baseSize, 0x700);
#endif

  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.baseAddrL,
    bInfo->fifoInfo.fifoStart>>12);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.readPtrL, bInfo->fifoInfo.fifoStart);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.readPtrH, 0);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.aMin, bInfo->fifoInfo.fifoStart-4);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.aMax, bInfo->fifoInfo.fifoStart-4);
  
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.depth, 0);
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.holeCount, 0);
  /* Fifo LWM /HWM/ THRESHOLD */
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifoThresh, (0x09 << SST_HIGHWATER_SHIFT) | 0x2); 
  HWC_CAGP_STORE(bInfo->regInfo, cmdFifo0.baseSize,
    ((bInfo->fifoInfo.fifoLength >> 12) - 1) | SST_EN_CMDFIFO |
    SST_CMDFIFO_DISABLE_HOLES | SST_CMDFIFO_AGP);
  
  GDBG_INFO(2,"%s:  CMD FIFO placed at physical addr 0x%x\n", FN_NAME,
    bInfo->fifoInfo.fifoStart);

  return FXTRUE;
#else /* !HWC_EXT_INIT */
  return hwcInitFifo(bInfo, enableHoleCounting);
#endif /* !HWC_EXT_INIT */
#undef FN_NAME
} /* hwcInitAGPFifo */ 


FxBool
hwcAllocAuxRenderingBuffer(hwcBoardInfo *bInfo, hwcBufferDesc *bp, int width,
                           int height)  
{
#define FN_NAME "hwcAllocAuxRenderingBuffer"
  /* For now, these buffers  */
  FxBool
    rVal = FXFALSE;
  
  FxU32
    bufSize,
    offset;

  bufSize = (width * height) << 2;

  offset = bInfo->fbOffset - bufSize;

  if ((offset - bInfo->tramSize) < bInfo->min_tramSize) {
    sprintf(errorString, "%s:  Insufficient memory", FN_NAME);
  } else {
    bp->bufOffset = offset;
    bp->bufStride = width << 2; /* Below tiled memory for now */
    bp->bufBPP = 16;
    bp->tiled = 0;
    bp->bufType = HWC_BUFFER_AUXRENDER;

    bInfo->fbOffset -= bufSize;

    rVal = FXTRUE;
  }

  return rVal;

#undef FN_NAME
} /* hwcAllocAuxRenderingBuffer */

#define RED_SHIFT       16
#define GREEN_SHIFT     8
#define BLUE_SHIFT      0

FxBool
hwcGammaTable(hwcBoardInfo *bInfo, FxU32 nEntries, FxU32 *r, FxU32 *g, FxU32 *b)
{
#define FN_NAME "hwcGammaTable"
  FxU32 gRamp[256];
  FxU32 i;
  FxU32 vidProcCfg;
  FxU32 dacBase;
  FxU32 rDacBase;
  FxU32 rDacData;
  
  /* Load the table into the Display driver as above */
  for (i = 0; i < nEntries; i++) {
    gRamp[i] =
      ((r[i] & 0xff) << RED_SHIFT) |
        ((g[i] & 0xff) << GREEN_SHIFT) |  
          ((b[i] & 0xff) << BLUE_SHIFT);
  }
  
  /*
   **  On W9X/DOS, we can do this ourselves--which is much easier than
   **  mucking about with a bunch of 32-bit data in 16-bit driver
   **  code.
   */
  HWC_IO_LOAD( bInfo->regInfo, vidProcCfg, vidProcCfg);
  
  /* Determin which set of CLUT entries are selected */
  if (vidProcCfg & SST_OVERLAY_CLUT_SELECT)
    dacBase = 256;
  else
    dacBase = 0;
 
  /* Print out some useful info RE the vidProcCfg register */
  GDBG_INFO(80, "%s:  vidProcCFG(SST_OVERLAY_CLUT_SELECT) = %d\n",
            FN_NAME, (vidProcCfg & SST_OVERLAY_CLUT_SELECT) ? 1 : 0);
  GDBG_INFO(80, "%s:  vidProcCFG(SST_OVERLAY_EN) = %d\n",
            FN_NAME, (vidProcCfg & SST_OVERLAY_EN) ? 1 : 0);
  GDBG_INFO(80, "%s:  vidProcCFG(SST_OVERLAY_CLUT_BYPASS) = %d\n",
            FN_NAME, (vidProcCfg & SST_OVERLAY_CLUT_BYPASS) ? 1 : 0);
  
  for (i = 0; i < nEntries; i++) {
    int repeat = 100;
    while (repeat) {
      HWC_IO_STORE( bInfo->regInfo, dacAddr, dacBase + i);
      P6FENCE;
      HWC_IO_STORE( bInfo->regInfo, dacData, gRamp[i]);
      P6FENCE;
      HWC_IO_LOAD( bInfo->regInfo, dacAddr, rDacBase);
      P6FENCE;
      HWC_IO_LOAD( bInfo->regInfo, dacData, rDacData);
      P6FENCE;
      if ((rDacBase == (dacBase + i)) && (rDacData == gRamp[i]))
        break;
      repeat --;
    }
    if (!repeat) {
      GDBG_INFO(0, "%s: Error Writing DacData [%d, %x]. DacBase = %d\n",
                FN_NAME, i, gRamp[i], dacBase);
    }
  }

  return FXTRUE;
  
#undef FN_NAME
} /* hwcGammaTable */

FxBool
hwcGammaRGB(hwcBoardInfo *bInfo, float gammaR, float gammaG, float gammaB)
{
#define FN_NAME "hwcGammaRGB"
  FxU32
    grRamp[256], ggRamp[256], gbRamp[256];
  int
    i;

  GDBG_INFO(80, FN_NAME "(0x%x, %1.2f, %1.2f, %1.2f)\n",
            bInfo, gammaR, gammaG, gammaB);

  /*
  ** NB:  The system eventually devised by Bob and Ken *may* require
  ** separate R, G, and B vectors.
  */

  for (i = 0; i < 256; i++) {
    grRamp[i] = (FxU32)((pow(i/255.0F, 1.0F/gammaR)) * 255.0F + 0.5F);
    ggRamp[i] = (FxU32)((pow(i/255.0F, 1.0F/gammaG)) * 255.0F + 0.5F);
    gbRamp[i] = (FxU32)((pow(i/255.0F, 1.0F/gammaB)) * 255.0F + 0.5F);
  }

  hwcGammaTable(bInfo, 256, grRamp, ggRamp, gbRamp); 

  /*
  **  Now that we have a gamma table, we can give it to the driver via
  **  a call to ExtEscape() when that is defined.....
  */
  return FXFALSE;

#undef FN_NAME
} /* hwcGammaRGB */


#define M 1
#define K 1

FxBool
hwcSetGrxClock(hwcBoardInfo *bInfo, FxU32 speedInMHz)
{
#define FN_NAME "hwcSetGrxClock"
  FxU32
    pllCtrl1,
    dramInit0 = 0xc17ae29, 
    dramInit1 = 0x26c031,
    n,
    m = 1;

  n = (FxU32) ((speedInMHz - 4.76f)/2.38f);

  pllCtrl1 =
    (K << SST_PLL_K_SHIFT) | (m << SST_PLL_M_SHIFT) | (n << SST_PLL_N_SHIFT);

  GDBG_INFO(80, "%s:  Setting Graphics Clock to %d\n", FN_NAME, speedInMHz);

  HWC_IO_STORE( bInfo->regInfo, dramInit0, dramInit0);
  HWC_IO_STORE( bInfo->regInfo, dramInit1, dramInit1);
  HWC_IO_STORE( bInfo->regInfo, pllCtrl1, pllCtrl1);
  
  return FXTRUE;

#undef FN_NAME  
} /* hwcSetGrxClock */

FxBool
hwcSetMemClock(hwcBoardInfo *bInfo, FxU32 speedInMHz)
{
#define FN_NAME "hwcSetMemClock"

  return FXFALSE;
#undef FN_NAME  
} /* hwcSetMemClock */


FxBool
hwcResolutionSupported(hwcBoardInfo *bInfo, GrScreenResolution_t res)
{
#define FN_NAME "hwcResolutionSupported"
  static char *resNames[] = {
    "GR_RESOLUTION_320x200",
    "GR_RESOLUTION_320x240",
    "GR_RESOLUTION_400x256",
    "GR_RESOLUTION_512x384",
    "GR_RESOLUTION_640x200",
    "GR_RESOLUTION_640x350",
    "GR_RESOLUTION_640x400",
    "GR_RESOLUTION_640x480",
    "GR_RESOLUTION_800x600",
    "GR_RESOLUTION_960x720",
    "GR_RESOLUTION_856x480",
    "GR_RESOLUTION_512x256",
    "GR_RESOLUTION_1024x768",
    "GR_RESOLUTION_1280x1024",
    "GR_RESOLUTION_1600x1200",
    "GR_RESOLUTION_400x300",
    "GR_RESOLUTION_1152x864",
    "GR_RESOLUTION_1280x960",
    "GR_RESOLUTION_1600x1024",
    "GR_RESOLUTION_1792x1344",
    "GR_RESOLUTION_1856x1392",
    "GR_RESOLUTION_1920x1440",
    "GR_RESOLUTION_2048x1536",
    "GR_RESOLUTION_2048x2048"
  };

#if 0
  struct WidthHeight_s {
    FxU32 width; 
    FxU32 height;
  } whByRes[] = {
    {320, 200},                 /* GR_RESOLUTION_320x200 */
    {320, 240},                 /* GR_RESOLUTION_320x240 */
    {400, 256},                 /* GR_RESOLUTION_400x256 */
    {512, 384},                 /* GR_RESOLUTION_512x384 */
    {640, 200},                 /* GR_RESOLUTION_640x200 */
    {640, 350},                 /* GR_RESOLUTION_640x350 */
    {640, 400},                 /* GR_RESOLUTION_640x400 */
    {640, 480},                 /* GR_RESOLUTION_640x480 */
    {800, 600},                 /* GR_RESOLUTION_800x600 */
    {960, 720},                 /* GR_RESOLUTION_960x720 */
    {856, 480},                 /* GR_RESOLUTION_856x480 */
    {512, 256},                 /* GR_RESOLUTION_512x256 */
    {1024, 768},                /* GR_RESOLUTION_1024x768 */
    {1280, 1024},               /* GR_RESOLUTION_1280x1024 */
    {1600, 1200},               /* GR_RESOLUTION_1600x1200 */
    {400, 300},                 /* GR_RESOLUTION_400x300 */
    {1152, 864},                /* GR_RESOLUTION_1152x864 */
    {1280, 960},                /* GR_RESOLUTION_1280x960 */
    {1600, 1024},               /* GR_RESOLUTION_1600x1024 */
    {1792, 1344},               /* GR_RESOLUTION_1792x1344 */
    {1856, 1392},               /* GR_RESOLUTION_1856x1392 */
    {1920, 1440},               /* GR_RESOLUTION_1920x1440 */
    {2048, 1536},               /* GR_RESOLUTION_2048x1536 */
    {2048, 2048}                /* GR_RESOLUTION_2048x2048 */
  };
#endif  

  GDBG_INFO(80, FN_NAME ":  res == %s (0x%x), supported == %s\n",
            resNames[res], resolutionSupported[bInfo->boardNum][res],
            resolutionSupported[bInfo->boardNum][res] ? "FXTRUE" : "FXFALSE");
  

  /* Glide has very good checking to see if the memory required is
  available, so we'll just return whether the driver can do it. */
  return resolutionSupported[bInfo->boardNum][res];

#undef FN_NAME
} /* hwcResolutionSupported */

#ifdef __WIN32__
static char *
getRegPath() 
{
  char *retVal = NULL;
  OSVERSIONINFO ovi;
  
  ovi.dwOSVersionInfoSize = sizeof ( ovi );
  GetVersionEx ( &ovi );
  if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT) {
    GDBG_INFO(80, "OS == WNT\n");
    /* It is hardcoded on NT via Display Control code. see:
     * $/devel/swtools/bansheecp2 */
    retVal = "SYSTEM\\CurrentControlSet\\Services\\3Dfx\\Device0\\glide";
  } else {
    QDEVNODE QDevNode;
    QIN Qin;
    int status;

    GDBG_INFO(80, "OS == W9X\n");

    Qin.dwSubFunc = QUERYDEVNODE;
    {
      HDC curDC = GetDC(NULL);

      status = ExtEscape ( curDC, QUERYESCMODE, 
                           sizeof(Qin), (LPCSTR)&Qin, 
                           sizeof(QDevNode), (LPSTR)&QDevNode );
      ReleaseDC(NULL, curDC);
    }

    if ( status > 0 ) {
      static char regPath[255];

      CM_Get_DevNode_Key( QDevNode.dwDevNode, NULL, 
                          &regPath, sizeof(regPath), 
                          CM_REGISTRY_SOFTWARE );
      strcat(regPath, "\\glide");

      retVal = regPath;
    }
  }

  return retVal;
} /* getRegPath */
#endif

char *
hwcGetenv(char *a) 
{
#if __WIN32__
  char *retVal = NULL;
  static char *regPath;
  HKEY hKey;
  DWORD type, szData;
  static char strval[255];

  /* This should work for both NT and Win95/98 (getRegPath works) */
  if (retVal = getenv(a))
    return retVal;
  
  szData = sizeof(strval);

  if (regPath == NULL) {
    regPath = getRegPath();
    
    GDBG_INFO(80, "_grGetEnv:  regPath = %s\n", regPath);

    if (regPath == NULL)
      return NULL;
  } 
  
  if (RegOpenKey(HKEY_CURRENT_USER, regPath, &hKey) == ERROR_SUCCESS) {
    if (RegQueryValueEx(hKey, a, NULL, &type, strval, &szData) ==
        ERROR_SUCCESS) {
      if (type != REG_SZ) {
        retVal = NULL;
      } else {
        retVal = strval;
      }
    }
    RegCloseKey(hKey);
  }
  
  if ((retVal == NULL) && RegOpenKey(HKEY_LOCAL_MACHINE, regPath, &hKey) == ERROR_SUCCESS) {
    
    if (RegQueryValueEx(hKey, a, NULL, &type, strval, &szData) ==
        ERROR_SUCCESS) {
      if (type != REG_SZ) {
        retVal = NULL;
      } else {
        retVal = strval;
      }
    }
    RegCloseKey(hKey);
  }
  return (char*)retVal;
#else
  return getenv(a);
#endif
} /* _grGetenv */

FxU32
hwcQueryContext(hwcBoardInfo *bInfo)
{
#define FN_NAME "hwcQueryContext"
  FxU32
    retVal = FXTRUE;

#if HWC_EXT_INIT
  {
    hwcExtRequest_t
      ctxReq;
    hwcExtResult_t
      ctxRes;

    GDBG_INFO(80, FN_NAME ":  ExtEscape:HWCEXT_QUERYCONTEXT\n");
    
    ctxReq.which = HWCEXT_QUERYCONTEXT;
    ExtEscape((HDC)bInfo->hdc, EXT_HWC, 
                        sizeof(ctxReq), (LPSTR) &ctxReq,
                        sizeof(ctxRes), (LPSTR) &ctxRes);

    retVal = (ctxRes.resStatus != HWC_CONTEXT_LOST);
  }
#endif /* HWC_EXT_INIT */

  return retVal;
#undef FN_NAME
} /* hwcQueryContext */

FxU32
hwcShareContextData(hwcBoardInfo *bInfo, FxU32 **data)
{
#define FN_NAME "hwcShareContextData"
  FxU32
    retVal = FXTRUE;
#if HWC_EXT_INIT
  hwcExtRequest_t
    ctxReq;
  hwcExtResult_t
    ctxRes;

  GDBG_INFO(80, FN_NAME "\n");  

  if (bInfo->osNT) {

    curBI = bInfo;


    ctxReq.optData.contextDwordNTReq.procId = GetCurrentProcessId();


    GDBG_INFO(80, FN_NAME ":  NT Branch\n");  
    {
      FxU32 ohWell;

      ctxReq.which = HWCEXT_CONTEXT_DWORD_NT;


      /* Now for the NASTY stuff: */
      __asm  mov eax, 0;
      __asm  mov ax, cs;
      __asm  mov ohWell, eax;

      ctxReq.optData.contextDwordNTReq.codeSegment = ohWell;

      __asm  mov eax, 0;
      __asm  mov ax, ds;
      __asm  mov ohWell, eax;
    
      ctxReq.optData.contextDwordNTReq.dataSegment = ohWell;

      /* oh, yeah */
    }
    
    
    GDBG_INFO(80, FN_NAME ":  Calling ExtEscape(HWCEXT_CONTEXT_DWORD_NT)\n");  

    ExtEscape((HDC) bInfo->hdc, EXT_HWC, sizeof(ctxReq), (void *) &ctxReq,
              sizeof(ctxRes), (void *) &ctxRes);
    
    
    *data = (FxU32 *) ctxRes.optData.contextDwordNTRes.dwordOffset;

  } else {
    /* context DWORD is rather poorly named now, but oh, well. */
    ctxReq.which = HWCEXT_SHARE_CONTEXT_DWORD;

    /*ctxReq.optData.contextDwordNTReq.procId = GetCurrentProcessId();*/

    /*ProcessID = GetCurrentProcessId ();*/
	bInfo->contextHandle = GetCurrentProcessId ();

	/* Piggy back the context handle on the unmapmemory structure for 
	 * safety 
	 */
	ctxReq.optData.unmapMemoryReq.procHandle = bInfo->contextHandle;

    /* MDM-Richardson - Removed because it corrupts the req structure that
     * gets passed down through the ExtEscape.  Theory is that it messes 
     * with the LPSTR pointer due to a NULL 
     */
	/*ctxReq.optData.shareContextDWORDReq.contextDWORD =
      (FxU32) &bInfo->lostContextDWORD;*/
    
    retVal = ExtEscape((HDC)bInfo->hdc, EXT_HWC, 
                       sizeof(ctxReq), (LPSTR) &ctxReq,
                       sizeof(ctxRes), (LPSTR) &ctxRes);
    
    if (retVal == 0xffffffff)
      *data = &dummyContextDWORD;
    else {
      *data = (FxU32 *) ctxRes.optData.shareContextDWORDRes.contextDWORD;
      if (*data == (FxU32 *) 0xffffffff)
        *data = &dummyContextDWORD;     
    }
  }
    
  GDBG_INFO(80, FN_NAME ":  pointer to context = 0x%x\n",
            ctxRes.optData.shareContextDWORDRes.contextDWORD); 
#endif  
  return retVal;
#undef FN_NAME
} /* hwcShareContextData */

void
hwcUnmapMemory9x(hwcBoardInfo *bInfo) 
{
  hwcExtRequest_t
    ctxReq;
  hwcExtResult_t
    ctxRes;
  
  ctxReq.which = HWCEXT_UNMAP_MEMORY;

  /* Assure that we free up the node by passing the known Process ID that
     was used to open the context
  */
  ctxReq.optData.unmapMemoryReq.procHandle = bInfo->contextHandle;
        
  ExtEscape((HDC)bInfo->hdc, EXT_HWC, 
            sizeof(ctxReq), (LPSTR) &ctxReq,
            sizeof(ctxRes), (LPSTR) &ctxRes);
  
} /* hwcUnmapMemory9x */

#ifdef HWC_EXT_INIT
void
hwcUnmapMemory() 
{
  hwcExtRequest_t
    ctxReq;
  hwcExtResult_t
    ctxRes;
  
  if ( curBI ) {
    if ( curBI->osNT ) {
      ctxReq.which = HWCEXT_UNMAP_MEMORY;

      /* Assure that we free up the node by passing the known Process ID that
         was used to open the context
      */
      ctxReq.optData.unmapMemoryReq.procHandle = GetCurrentProcessId();
      
	  ExtEscape((HDC)curBI->hdc, EXT_HWC, 
                     sizeof(ctxReq), (LPSTR) &ctxReq,
                            sizeof(ctxRes), (LPSTR) &ctxRes);
    }
  }
 
} /* hwcUnmapMemory */

void
hwcClearContextData() 
{
#define FN_NAME "hwcClearContextData"
  return;
#undef FN_NAME
} /* hwcClearContextData */

#endif
