/*
** Copyright (c) 1996, 3Dfx Interactive, Inc.
** All Rights Reserved.
**
** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.;
** the contents of this file may not be disclosed to third parties, copied or
** duplicated in any form, in whole or in part, without the prior written
** permission of 3Dfx Interactive, Inc.
**
** RESTRICTED RIGHTS LEGEND:
** 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.
**
*/
#define INIT_AT3D_LIB

#include <3dfx.h>

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef __linux__
#include <conio.h>
#endif
#include <assert.h>
#include <time.h>

#ifdef __DOS32__
#ifdef __DJGPP__
#include <dos.h>
#else
#include <i86.h>
#endif
#endif

#ifdef __WIN32__
#include <windows.h>
#endif /* __WIN32__ */

#if defined(__WATCOMC__)
#define _inp inp
#define _outp outp
#endif

#ifdef __DJGPP__
#include <fxdpmi.h>
#endif

#if defined(__linux__)
#define _inp(port) pioInByte(port)
#define _outp(port, data) pioOutByte(port, data)
#endif

#define AT3D_BASE 0xffec00

#define FX_DLL_DEFINITION
#include <fxdll.h>
#include <gdebug.h>
#include <init.h>

#include "fxinit96.h"

#include "initat3d.h"
#include "atvga.h"
#include "initvga.h"
#include "physical.h"

/*
**  Static Globals   
*/

/* Stuff for programming the memory clock */
typedef struct _CLOCK_TABLE {
  int iMclk;
  unsigned char wN;
  unsigned char wM;
  unsigned char wL;
  unsigned char wFR;
} CLOCK_TABLE, *LPCLOCK_TABLE;


/* This table is for AT3D, other parts need different tables. */ 
CLOCK_TABLE ClockTableAT3D[] =
{
  { 25, 0x1b, 0x1, 0x3, 0x6 },
  { 40, 0x2c, 0x1, 0x3, 0x3 },
  { 45, 0x32, 0x1, 0x3, 0x1 },
  { 50, 0x1b, 0x1, 0x2, 0x6 },
  { 55, 0x2d, 0x2, 0x2, 0x6 },
  { 60, 0x21, 0x1, 0x2, 0x5 },
  { 62, 0x44, 0x3, 0x2, 0x5 },
  { 65, 0x5a, 0x4, 0x2, 0x4 },
  { 67, 0x37, 0x2, 0x2, 0x4 },
  { 70, 0x26, 0x1, 0x2, 0x4 },
  { 72, 0x27, 0x1, 0x2, 0x4 },
  { 75, 0x29, 0x1, 0x2, 0x3 },
  { 80, 0x2c, 0x1, 0x2, 0x3 },
  { 85, 0x2f, 0x1, 0x2, 0x2 },
  { 90, 0x31, 0x1, 0x2, 0x1 },
  { 95, 0x34, 0x1, 0x2, 0x1 },
  { 100, 0x1b, 0x1, 0x1, 0x6},
  { 0,  0,    0,   0,   0 } /* used for end of table mark */
};

typedef struct _REGVALS {
  unsigned char E8;
  unsigned char E9;
  unsigned char EA;
} REGVALS, *LPREGVALS;

typedef struct _REGINFO {
  REGVALS RegVals;
  int   bFreqFound;
} REGINFO, *LPREGINFO;

/* MCLK Constants */
const unsigned char bBypass = 0;
const unsigned char bHighSpeed = 1;
const unsigned char bPowerOff = 0;


/* HALData required for each partner */
static Init96HALData at3dHALData = {
  0x1000000,
  initAT3DAperture,
  initAT3DSetVideo,    
  initAT3DEnableRegs,
  initAT3DDisableRegs,
  NULL,
  initAT3DUseTiles,
  initAT3DGetInfo,
  NULL, /* SwapTiles */
  NULL
};

/* Random Kruft */
static FxBool tiling;           /* Well, are we? */
static FxU32 workaround;        /* Some registers have to be read
                                   after they're written, and we store
                                   that read here*/
static LPCOP at3dRegPtr = NULL;

/* Stuff we squirrel away and put back when we've finished */
static FxU16 tileRegSave[12*8];
FxU16
  regWindowSequenceControlSave,
  regWindow0BaseAddressSave,
  regWindow0StretchFactorHor1Save,
  regWindow0StretchFactorHor2Save,
  regWindow0StretchFactorVer1Save,
  regWindow0StretchFactorVer2Save,
  regWindow0ControlSave;

/* Some Constants */
#define GINTERVAL 100


/*
 **   Assignment Macros/Debugging Support Routines
 **
 **   OK, So these just print out the byte offset of the register, but
 **   before you come bitch at me, look at physical.h to see how much
 **   work it is to generate register names, then look at this file and
 **   see how few of them we actually use.
 **
 **   CHD
 **
 **   Fixme!!! need workaround = d, not workaround = s!  Do this after 2.4
 **
 */
#ifdef GDBG_INFO_ON
#define SETAT3D(d,s) \
{\
GDBG_INFO((80, "%s:  Setting AT3D Register 0x%x with value 0x%x\n", FN_NAME, (FxU32) &d - (FxU32) at3dRegPtr,  s));\
d = s; workaround = (FxU32) s ;}
#define GETAT3D(s) readDebugAT3D(&s, FN_NAME)
#define GET8AT3D(s) read8DebugAT3D(&s, FN_NAME)
#define GET16AT3D(s) read16DebugAT3D(&s, FN_NAME)
#else
#define SETAT3D(d,s) d = s; workaround = (FxU32) s
#define GETAT3D(s) s
#define GET8AT3D(s) s
#define GET16AT3D(s) s
#endif

#ifdef GDBG_INFO_ON
static FxU32
readDebugAT3D(volatile FxU32 *s, char *fName) {
  FxU32 ret;

  ret = *s;

  GDBG_INFO((80, "%s:  Read 0x%x from register 0x%x\n", fName, ret,
             (FxU32) s - (FxU32) at3dRegPtr));

  return ret;

} /* readDebugAT3D */

static FxU8
read8DebugAT3D(volatile FxU8 *s, char *fName) {

  FxU8
    ret;

  ret = *s;

  GDBG_INFO((80, "%s:  Read 0x%x from register 0x%x\n", fName, ret,
             (FxU32) s - (FxU32) at3dRegPtr));

  return ret;
  
} /* read8DebugAT3D */

static FxU16
read16DebugAT3D(volatile FxU16 *s, char *fName) {

  FxU16
    ret;

  ret = *s;

  GDBG_INFO((80, "%s:  Read 0x%x from register 0x%x\n", fName, ret,
             (FxU32) s - (FxU32) at3dRegPtr));

  return ret;
  
} /* read16DebugAT3D */
#endif

/*
 **  AT3D Utility Routines
 **
 **  These routines blah blah blah
 */

#ifdef H3D
void pageflippingSwapWait(void) {
#define FN_NAME "pageflippingSwapWait"
  int bailCount = 150000, pending = 1;
  FxU32 status;

  if (!at3dRegPtr) return;

  status = (FxU32)(GETAT3D(at3dRegPtr->Extended_Status) & 0x00400000L);
  if (!status) {
    do {
      status = (FxU32)(GETAT3D(at3dRegPtr->Extended_Status) & 0x00400000L);
    } while (!status && --bailCount);  // wait for it to go low
    bailCount = 150000;
  }
  if (status) { // is swap pending?
    do {
      status = (FxU32)(GETAT3D(at3dRegPtr->Extended_Status) & 0x00400000L);
    } while (status && --bailCount);  // wait for it to go low
  }
#undef FN_NAME
}
#endif /* H3D */

/*-------------------------------------------------------------------
  Function: initAT3DGetRegVals
  Date: 20-May-97
  Implementor(s): someone from ALSC, ganked by dow
  Library: Init Code for AT3D
  Description:
  This routine was shamelessly ganked out of the DirectDraw driver
  for AT3D/Voodoo Rush.  
  
  Basically, it takes a frequency (in MHz), and returns some
  mystical values needed for programming the PLL.
  
  Arguments:
  
  Return:
  FXTRUE if successful.
  -------------------------------------------------------------------*/
REGINFO
initAT3DGetRegVals(int iFreq)
{
#define FN_NAME "initAT3DGetRegVals"
  /* Get the E8,E9, and EA values for given frequency. */
  LPCLOCK_TABLE lpClockTable = (LPCLOCK_TABLE)ClockTableAT3D;
  int i;
  REGINFO RegInfo; 
  BYTE bN, bM, bL, bFreqRange;
  
  /* Scan for MCLK value from array. */
  i = 0;
  while( lpClockTable[i].iMclk != 0 ) {
    if( lpClockTable[i].iMclk == iFreq )
      break;
    i++;
  }
  
  /* Check if there is no MCLK matched. */
  if( lpClockTable[i].iMclk == 0 )  {
    RegInfo.bFreqFound = FXFALSE;
    i=0;
  } else {
    RegInfo.bFreqFound = FXTRUE;
  }
  
  bN = lpClockTable[i].wN;
  bM = lpClockTable[i].wM;
  bL = lpClockTable[i].wL;
  bFreqRange = lpClockTable[i].wFR;
  
  /* Assign values to E8-EA... */
  RegInfo.RegVals.E8 = bBypass +
    (bPowerOff << 1) +
      (bL << 2) + 
        (bFreqRange << 4) +
          (bHighSpeed << 7);
  RegInfo.RegVals.E9 = bM;
  RegInfo.RegVals.EA = bN;
  
  return(RegInfo);

#undef FN_NAME
} /* GetRegVals */

/*-------------------------------------------------------------------
  Function: setmclk32
  Date: 20-May-97
  Implementor(s): someone from ALSC, ganked by dow
  Library: Init Code for AT3D
  Description:
  This routine sets the memory clock for the AT3D chip.  It was
  shamelessly ganked out of the DirectDraw driver for
  AT3D/Voodoo Rush.  
  
  Arguments:
  
  Return:
  FXTRUE if successful.
  -------------------------------------------------------------------*/
static FxBool
initAT3DSetMClock(int iMclk, LPCOP at3dRegs)
{
#define FN_NAME "initAT3DSetMClock"
  REGINFO RegInfo;
  FxU8 oE8, oE9, oEA;
  FxU8 nE8, nE9, nEA;
  
  GDBG_INFO((80, "%s:\n", FN_NAME));

  /* Get values for E8, E9, EA etc. based on iMclk. */
  RegInfo = initAT3DGetRegVals(iMclk);
  
  if(!RegInfo.bFreqFound)
    return(FXFALSE);
  
  /* Read in the current 3  Bytes starting at 0E8 */
  oE8 = GET8AT3D(at3dRegs->MCLK_Ctrl);
  oE9 = GET8AT3D(at3dRegs->MCLK_M);
  oEA = GET8AT3D(at3dRegs->MCLK_N);
  
  /*
   ** Set new values....
   **
   ** Assign new E8-EA... (with L=3, and high speed bit set)
   ** This is done to avoid a temporary illegal setting of the mclk!
   */
  nE8 = (oE8 | 0x8C);
  nE9 = oE9;
  nEA = oEA;
  SETAT3D(at3dRegs->MCLK_Ctrl, nE8);
  SETAT3D(at3dRegs->MCLK_M, nE9);
  SETAT3D(at3dRegs->MCLK_N, nEA);
  
  /*
   **   Assign new E8-EA... (with desired M and N values)
   ** This is done to avoid a temporary illegal setting of the mclk!
   */
  nE8 = nE8;
  nE9 = RegInfo.RegVals.E9;
  nEA = RegInfo.RegVals.EA;
  SETAT3D(at3dRegs->MCLK_Ctrl, nE8);
  SETAT3D(at3dRegs->MCLK_M, nE9);
  SETAT3D(at3dRegs->MCLK_N, nEA);
  
  /* Assign new E8-EA... (with correct L value) */
  nE8 = RegInfo.RegVals.E8;
  nE9 = nE9;
  nEA = nEA;
  SETAT3D(at3dRegs->MCLK_Ctrl,nE8);
  SETAT3D(at3dRegs->MCLK_M, nE9);
  SETAT3D(at3dRegs->MCLK_N, nEA);
  
  GDBG_INFO((80, "%s:  Returning FXTRUE\n", FN_NAME));

  return(FXTRUE);
  
#undef FN_NAME
} /* initAT3DSetMClock */

/*-------------------------------------------------------------------
  Function: initAT3DWait
  Date: 21-May-97
  Implementor(s): dow
  Library: Init Code for AT3D
  Description:
  Waits for the specified number of milliseconds.  This works
  because CLOCKS_PER_SEC is equal to 1000 in time.h;
  Arguments:
  
  Return:
  Nothing ever.
  -------------------------------------------------------------------*/
static void
initAT3DWait( FxI32 mSec)
{
#define FN_NAME "initAT3DWait"
  clock_t t0, t1;

  t0 = clock();

  do {
    t1 = clock();
  } while ((t1 - t0) < mSec);
#undef FN_NAME
} /* initAT3DWait */

/*-------------------------------------------------------------------
  Function: initAT3D2DIdle
  Date: 21-May-97
  Implementor(s): someone from ALSC, dow
  Library: Init Code for AT3D
  Description:
  Waits for the AT3D chip to be idle;
  Arguments:
  
  Return:
  Nothing ever.
  -------------------------------------------------------------------*/
static void
initAT3D2DIdle(LPCOP at3dRegs) 
{
#define FN_NAME "initAT3D2DIdle"
  volatile FxU32 val;

  while ((val = GETAT3D(at3dRegs->Extended_Status)) & EXT_STATUS_DWG_ENG_BUSY);

#undef FN_NAME
} /* initAT3D2DIdle */

/*-------------------------------------------------------------------
  Function: initAT3DDisableMemoryRefresh
  Date: 09-May-97
  Implementor(s): someone from ALSC, dow
  Library: Init Code for AT3D
  Description:
  Disables memory refresh on AT3D.
  
  Notes:
  Shamelessly ganked from the AT3D DDRAW code.
  
  Arguments:
  
  Return:
  pointer to at3dHALData
  -------------------------------------------------------------------*/
static void
initAT3DDisableMemoryRefresh(LPCOP at3dRegs) 
{
#define FN_NAME "initAT3DDisableMemoryRefresh"
  FxU32 vgaSeq, data;
  FxU16 vgaOverride;

  /* Make sure the 2D is not making any requests for memory */
  initAT3D2DIdle(at3dRegs);
  
  GDBG_INFO((80, "%s:  Turning off video refresh\n", FN_NAME));
  INP(SC_INDEX, vgaSeq);

  OUTP(SC_INDEX, SC_CLOCKING_MODE);
  INP(SC_INDEX, data);

  data |= VGA_SEQUENCER_SCREEN_OFF;
  OUTP(SC_DATA, data);
  OUTP(SC_INDEX, vgaSeq);

  GDBG_INFO((80, "%s:  Turning off DRAM refresh\n", FN_NAME));
  GDBG_INFO((80, "%s:  VGAOverride\n", FN_NAME));
  vgaOverride = GET16AT3D(at3dRegs->VGAOverride);
  vgaOverride |= VGA_OVERRIDE_DISABLE_DRAM_REFRESH;
  SETAT3D(at3dRegs->VGAOverride, vgaOverride);

#undef FN_NAME
} /* initAT3DDisableMemoryRefresh */

/*-------------------------------------------------------------------
  Function: initAT3DEnableMemoryRefresh
  Date: 09-May-97
  Implementor(s): Someone from ALSC, dow
  Library: Init Code for AT3D
  Description:
  
  Arguments:
  
  Return:
  pointer to at3dHALData
  -------------------------------------------------------------------*/
static void
initAT3DEnableMemoryRefresh(LPCOP at3dRegs) 
{
#define FN_NAME "initAT3DEnableMemoryRefresh"

  FxU32 vgaSeq, data;
  FxU16 vgaOverride;

  GDBG_INFO((80, "%s:  Turning on video refresh\n", FN_NAME));
  INP(SC_INDEX, vgaSeq);

  OUTP(SC_INDEX, SC_CLOCKING_MODE);
  INP(SC_DATA, data);

  data &= ~VGA_SEQUENCER_SCREEN_OFF;

  OUTP(SC_DATA, data);
  OUTP(SC_INDEX, vgaSeq);

  GDBG_INFO((80, "%s:  Turning on DRAM refresh\n", FN_NAME));
  GDBG_INFO((80, "%s:  VGAOverride\n", FN_NAME));
  vgaOverride = GET16AT3D(at3dRegs->VGAOverride);
  vgaOverride &= !VGA_OVERRIDE_DISABLE_DRAM_REFRESH;
  SETAT3D(at3dRegs->VGAOverride, vgaOverride);

#undef FN_NAME
} /* initAT3DEnableMemoryRefresh */


/*
 **  Entry Points
 */

/*-------------------------------------------------------------------
  Function: initAT3DGetHALData
  Date: 09-May-97
  Implementor(s): dow
  Library: Init Code for AT3D
  Description:
  
  Arguments:
  
  Return:
  pointer to at3dHALData
  -------------------------------------------------------------------*/
INITAT3DENTRY(initAT3DGetHALData, Init96HALData *, (void) )
{
#define FN_NAME "initAT3DGetHALData"
  return &at3dHALData;
#undef FN_NAME
} /* initAT3DGetHALData */

/*-------------------------------------------------------------------
  Function: initAT3DAperture
  Date: 19-Feb-97
  Implementor(s): dow
  Library: Init Code for AT3D
  Description:
  Initialize rd pointers to access apertures
  Arguments:
  
  Return:
  -------------------------------------------------------------------*/
INITAT3DENTRY(initAT3DAperture, FxBool , (InitRegisterDesc *rd) )
{
#define FN_NAME "initAT3DAperture"
  FxBool
    rVal = FXTRUE;

  GDBG_INFO((80, "%s:\n", FN_NAME));

  rd->hwDep.VG96RegDesc.partnerRegPtr =
    &rd->hwDep.VG96RegDesc.baseAddress[AT3D_BASE >> 2];
  GDBG_INFO((80, "%s:  Setting partnerRegPtr to 0x%x\n", FN_NAME, rd->hwDep.VG96RegDesc.partnerRegPtr));

  at3dRegPtr = (LPCOP) rd->hwDep.VG96RegDesc.partnerRegPtr;

  rd->hwDep.VG96RegDesc.serialStatus = (FxU32 *) &at3dRegPtr->SerialInput;
  GDBG_INFO((80, "%s:  Setting serialStatus to 0x%x\n", FN_NAME, rd->hwDep.VG96RegDesc.serialStatus));

  /* This is not important, but it might be tested for NULL somewhere */
  rd->hwDep.VG96RegDesc.fifoApertureBase = rd->hwDep.VG96RegDesc.baseAddress;

  GDBG_INFO((80, "%s:  returning FXTRUE\n", FN_NAME));

  return rVal;

#undef FN_NAME
} /* initAT3DAperture */


INITAT3DENTRY(initAT3DSetVideo, FxBool, (GrScreenResolution_t res, GrScreenRefresh_t refresh, FxU32 *xr, FxU32 *yr )) 
{
#define FN_NAME "initAT3DSetVideo"
#if defined(__DOS32__)
  union REGS r, rOut;
#endif
  FxBool
    fixVsync = FXFALSE,
    rv = FXTRUE;
  
  GDBG_INFO((80, "%s: \n", FN_NAME));

#if defined(INIT_ACCESS_DIRECT) && defined( __DOS32__ )
  /* Wait half a sec */
  initAT3DWait( 100);
#endif

  switch( res ) {
  case GR_RESOLUTION_320x200:
#if defined(__DOS32__)
    /* Set up VESA Mode 0x136 - 320x200x16 */
    GDBG_INFO((80, "initAT3DSetVideo:  Setting up VESA mode 320x200x16\n"));
    r.w.ax = 0x4f02;
    r.w.bx = 0x136;
    int386( 0x10, &r, &rOut );
#endif
    *xr = 320;
    *yr = 200;
    rv = FXTRUE;
    break;
  case GR_RESOLUTION_320x240:
#if defined(__DOS32__)
    /* Set up VESA Mode 0x137 - 320x240x16 */
    GDBG_INFO((80, "initAT3DSetVideo:  Setting up VESA mode 320x240x16\n"));
    r.w.ax = 0x4f02;
    r.w.bx = 0x137;
    int386( 0x10, &r, &rOut );
#endif
    *xr = 320;
    *yr = 240;
    rv = FXTRUE;
    break;
  case GR_RESOLUTION_512x384:
    fixVsync = FXTRUE;
#if defined(__DOS32__)
    /* Set up VESA Mode 0x139 - 512x384x16 */
    GDBG_INFO((80, "initAT3DSetVideo:  Setting up VESA mode 512x384x16\n"));
    r.w.ax = 0x4f02;
    r.w.bx = 0x139;
    int386( 0x10, &r, &rOut );
#endif
    *xr = 512;
    *yr = 384;
    rv = FXTRUE;
    break;
  case GR_RESOLUTION_640x400:
    fixVsync = FXTRUE;
    rv = FXTRUE;
    break;

  case GR_RESOLUTION_640x480:
#if defined(__DOS32__)
    /* Set up VESA Mode 0x111 - 640x480x16 */
    GDBG_INFO((80, "initAT3DSetVideo:  Setting up VESA mode 640x480x16\n"));
    r.w.ax = 0x4f02;
    r.w.bx = 0x111;
    int386( 0x10, &r, &rOut );
#endif
    *xr = 640;
    *yr = 480;
    rv = FXTRUE;
    break;

#ifdef H3D
  case GR_RESOLUTION_640x240_AUTOFLIPPED:
    *xr = 640;
    *yr = 480;
    rv = FXTRUE;
    break;
  case GR_RESOLUTION_800x300_AUTOFLIPPED:
    *xr = 800;
    *yr = 600;
    rv = FXTRUE;
    break;
#endif

  case GR_RESOLUTION_800x600:
    fixVsync = FXTRUE;
#if defined(__DOS32__)
    /* Set up VESA Mode 0x114 - 800x600x16 */
    GDBG_INFO((80, "initAT3DSetVideo:  Setting up VESA mode 800x600x16\n"));
    r.w.ax = 0x4f02;
    r.w.bx = 0x114;
    int386( 0x10, &r, &rOut );
#endif
    *xr = 800;
    *yr = 600;
    rv = FXTRUE;
    break;
  default:
    rv = FXFALSE;
    break;
  }

  if (rv) {
    /* For some reason, AT3D reverses the polarity of  VSYNC for
    512x384 & 800x600, so we need to fix that (NOTE: this code also
    sets the polarity of HSYNC, hence c0 instead of 80)

    See page 342 of _Programmers_Guide_To_The_EGA_And_VGA_Cards, 2nd
    Ed. by Richard F. Ferraro for details on the Miscellaneous Output
    Register. 
    */
    if (fixVsync) {
      FxU16 vgaMiscOutput;

      GDBG_INFO((80, "initAT3DSetVideo:  Setting VSYNC Polarity\n"));

      INP(VGA_MISC_OUTPUT_READ, vgaMiscOutput);
      vgaMiscOutput |= 0xc0;

      OUTP(VGA_MISC_OUTPUT_WRITE, vgaMiscOutput);
    }
  }

  
  if (myGetenv("INIT96_AT3D_VSYNC_END")) {
    FxU16 vSyncEnd = 3;
    GDBG_INFO((80, "%s: Setting VSync End\n", FN_NAME));
    vSyncEnd = atoi(myGetenv("INIT96_AT3D_VSYNC_END"));
    CRTC_SET(CC_VSYNC_END, vSyncEnd);
  }
  
  if (myGetenv("INIT96_AT3D_VSYNC_START")) {
    FxU16 vSyncStart = 0;
    GDBG_INFO((80, "%s: Setting VSync Start\n", FN_NAME));
    vSyncStart = atoi(myGetenv("INIT96_AT3D_VSYNC_START"));
    CRTC_SET(CC_VSYNC_START, vSyncStart);
  }
  
  GDBG_INFO((80, "%s: returning %s\n", FN_NAME, rv?"FXTRUE":"FXFALSE"));  
  return rv;
#undef FN_NAME  
} /* initAT3DSetVideo */

/*-------------------------------------------------------------------
  Function: initAT3DEnableRegs
  Date: 07-Feb-97
  Implementor(s): dow
  Library: Init Code for AT3D
  Description:
  
  Do all setup necessary to enable direct writes to JR Space
  
  Arguments:
  rd - pointer to register description
  
  Return:
  FXTRUE if success, FXFALSE if Failure
  -------------------------------------------------------------------*/
INITAT3DENTRY(initAT3DEnableRegs,FxBool,(InitRegisterDesc *rd))  
{
#define FN_NAME "initAT3DEnableRegs"
#if !defined( __WIN32__) || defined(INIT_ACCESS_DIRECT)
  LPCOP
    at3dRegs = (LPCOP) rd->hwDep.VG96RegDesc.partnerRegPtr;

  FxU8 tmp;

  GDBG_INFO((80, "%s:\n", FN_NAME));

  GDBG_INFO((80, "%s:  Enabling writes to AT3D registers\n", FN_NAME)); 
  GDBG_INFO((80, "%s:  Setting VGA sequencer to point at flat model control\n", FN_NAME)); 
  GDBG_INFO((80, "%s:  6MB Memory Aperture\n", FN_NAME));
  SEQU_SET(0x1c, 0x27);

  GDBG_INFO((80, "%s:  VGA sequencer to remap control\n", FN_NAME));
  GDBG_INFO((80, "%s:  Map Control Registers into end of Flat Space\n", FN_NAME));
  SEQU_SET(0x1b, 0x24);

  GDBG_INFO((80, "%s:  Waiting for 2D Idle", FN_NAME));
  initAT3D2DIdle(at3dRegs);

  /*
     VG96_MCLKFrequency=50
     DualCycleThreshold=65
     */

  GDBG_INFO((80, "%s:  Setting Graphics clock to 50MHz\n", FN_NAME));
  /* FIXME!!  Add a myGetenv for SST96_MEMCLOCK */
  initAT3DSetMClock(50, at3dRegs);

  /* Wait two VSYNCs in order to give a few milliseconds for the
     on-board MCLK VCO to settle. (LouP)  */
  while(AT3D_IN_VSYNC);
  while(!AT3D_IN_VSYNC); 
  while(AT3D_IN_VSYNC);
  while(!AT3D_IN_VSYNC); 
  
  GDBG_INFO((80, "%s:  Turning on PUMA\n", FN_NAME));
  GDBG_INFO((80, "%s:  UMAControl\n", FN_NAME));
  SETAT3D(at3dRegs->UMAControl, PUMA_CONTROL_ON);

  GDBG_INFO((80, "%s:  GPIOControl\n", FN_NAME));  
  tmp = GET8AT3D(at3dRegs->GPIOControl);
  tmp |= GPIO_PUMA_SRESET_N;
  SETAT3D(at3dRegs->GPIOControl, 0xd0); /* tmp */

  GDBG_INFO((80, "%s:  GPIODirection\n", FN_NAME));
  tmp = GET8AT3D(at3dRegs->GPIODirection);
  tmp |= GPIO_PUMA_SRESET_N;
  SETAT3D(at3dRegs->GPIODirection, 0x10);

  GDBG_INFO((80, "%s:  GPIOLevel\n", FN_NAME));
  tmp = GET8AT3D(at3dRegs->GPIOLevel);
  tmp |= GPIO_PUMA_SRESET_N;    /* active low signal */
  SETAT3D(at3dRegs->GPIOLevel, tmp);

  GDBG_INFO((80, "%s:  FeatureCnctrDisable\n", FN_NAME));
  SETAT3D(at3dRegs->FeatureCnctrDisable, 0x80);

  GDBG_INFO((80, "%s:  GPIOLevel\n", FN_NAME));
  SETAT3D(at3dRegs->GPIOLevel, 0x10);

  GDBG_INFO((80, "%s:  GenFeatureCnctrCtl\n", FN_NAME));
  SETAT3D(at3dRegs->GenFeatureCnctrCtl, 0x10);

  GDBG_INFO((80, "%s:  Setting up AT3D Pixel FIFO Request Points\n", FN_NAME));

  GDBG_INFO((80, "%s:  HiReqMiss\n", FN_NAME));
  SETAT3D(at3dRegs->HiReqMiss, 0x04);

  GDBG_INFO((80, "%s:  HiReqNoMiss\n", FN_NAME));
  SETAT3D(at3dRegs->HiReqNoMiss, 0x04);

  GDBG_INFO((80, "%s:  LoReq\n", FN_NAME));
  SETAT3D(at3dRegs->LoReq, 0x1b);
  
  GDBG_INFO((80, "%s:  Enabling FBI Jr. Space\n", FN_NAME));

  GDBG_INFO((80, "%s:  ExtendedMemoryEnable\n", FN_NAME));
  SETAT3D(at3dRegs->ExtendedMemoryEnable, 0x04);

  GDBG_INFO((80, "%s:  GPIOControl", FN_NAME));
  SETAT3D(at3dRegs->GPIOControl, 0x03);

  GDBG_INFO((80, "%s:  returning FXTRUE\n", FN_NAME));

#endif /* __WIN32__ */

  return FXTRUE;

#undef FN_NAME
} /* initAT3DEnableRegs */

/* FIXME!!! jdt - puma/ram timing fixes need to go here */
INITAT3DENTRY(initAT3DDisableRegs,FxBool,(InitRegisterDesc *rd)) 
{
#define FN_NAME "initAT3DDisableRegs"
  int i,j;
#if !defined( __WIN32__) || defined(INIT_ACCESS_DIRECT)
  FxU32 status;
  FxU8
    gpioControl, gpioDirection, gpioLevel;
#endif  
  
#if 0 /* Fixme! delete after 2.4 */
  LPCOP
    lAT3DRegPtr = (LPCOP) rd->hwDep.VG96RegDesc.partnerRegPtr;
#endif 
  
  GDBG_INFO((80, "%s\n", FN_NAME));
  
#if !defined( __WIN32__) || defined(INIT_ACCESS_DIRECT)

  GDBG_INFO((80, "%s:  Waiting for 3D Idle", FN_NAME));
  do {
    status = GETAT3D(*rd->hwDep.VG96RegDesc.serialStatus);
  } while (status & 1);

  GDBG_INFO((80, "%s:  Disabling PUMA\n", FN_NAME));
  
  GDBG_INFO((80, "%s:  Disabling Memory Refresh\n", FN_NAME));
  initAT3DDisableMemoryRefresh(at3dRegPtr);
  
  GDBG_INFO((80, "%s:  Waiting for %d msec\n", FN_NAME, GINTERVAL));
  initAT3DWait(GINTERVAL);
  
  SETAT3D(at3dRegPtr->UMAControl, PUMA_CONTROL_OFF);
  
  GDBG_INFO((80, "%s:  GPIOControl\n", FN_NAME));
  gpioControl = GET8AT3D(at3dRegPtr->GPIOControl);
  gpioControl |= GPIO_PUMA_SRESET_N;
  SETAT3D(at3dRegPtr->GPIOControl, gpioControl);

  GDBG_INFO((80, "%s:  GPIODirection\n", FN_NAME));
  gpioDirection = GET8AT3D(at3dRegPtr->GPIODirection);
  gpioDirection |= GPIO_PUMA_SRESET_N;
  SETAT3D(at3dRegPtr->GPIODirection, gpioDirection);

  GDBG_INFO((80, "%s:  GPIOLevel\n", FN_NAME));
  gpioLevel = GET8AT3D(at3dRegPtr->GPIOLevel);
  gpioLevel &= ~ GPIO_PUMA_SRESET_N;
  SETAT3D(at3dRegPtr->GPIOLevel, gpioLevel);
  
  GDBG_INFO((80, "%s:  Enabling Memory refresh\n", FN_NAME));
  initAT3DEnableMemoryRefresh(at3dRegPtr);
  
#endif
  /* Undo tile programming regardless */
  if ( tiling ) {
    GDBG_INFO((80, "%s:  Restoring tile registers\n", FN_NAME));
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
                      regWindowSequenceControlSave);

    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidDataOffset, 
                      regWindow0BaseAddressSave);
    
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorHor, 
                      regWindow0StretchFactorHor1Save);

    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetHor, 
                      regWindow0StretchFactorHor2Save);

    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorVer, 
                      regWindow0StretchFactorVer1Save);
    
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetVer, 
                      regWindow0StretchFactorVer2Save);
    
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidControl, 
                      regWindow0ControlSave);

    GDBG_INFO((80, "Restoring all tiles\n"));
    for (i=0, j=0; i<MAX_TILES; i++) {
      int k;
      volatile FxU16 *tileRegPtr = (FxU16 *) &at3dRegPtr->TileRegsSet[i];
      for (k=0; k<8; k++, j++) {
        SETAT3D(tileRegPtr[k], tileRegSave[j]);
      }
    } /* end for i */
    tiling = 0;
  }

  GDBG_INFO((80, "%s:  returning FXTRUE\n", FN_NAME));
  
  return FXTRUE;
#undef FN_NAME
} /* initAT3DDisableRegs */

/*-------------------------------------------------------------------
  Function: initAT3DUseTiles
  Date: 07-Feb-97 (Moved into initat3d from init96)
  Implementor(s): jdt, paik, murali, dow
  Library: Init Code for AT3D
  Description:
  
  Arguments:
  frontBuffer:          FB offset of front buffer
  backBuffer:           FB offset of back buffer
  strideInBytes:        Tile width in bytes

  Return:
  1 if successful
  -------------------------------------------------------------------*/
#ifdef H3D
  // save framebufferinfo for enabling/disabling h3d
static InitRegisterDesc *h3drd=0;
static FxU32 h3dbuffers[3], h3dnBuffers, h3dstrideInBytes, h3dWidth, h3dHeight, h3dleft, h3dright, h3dbottom;

INITAT3DENTRY(initAT3DUseTiles, int, (InitRegisterDesc *rd,
                                      FxU32 *buffers,
                                      FxU32 nBuffers,
                                                                                            FxU32 strideInBytes,
                                                                                            FxU32 Width, FxU32 Height,
                                                                                            FxU32 PageFlipping))
#else
INITAT3DENTRY(initAT3DUseTiles, int, (InitRegisterDesc *rd,
                                      FxU32 *buffers,
                                      FxU32 nBuffers,
                                      FxU32 strideInBytes,
                                      FxU32 Width, FxU32 Height)) 
#endif
{
#define FN_NAME "initAT3DUseTiles"

  FxU32 left   = 0; 
  FxU32 right  = Width;
  FxU32 bottom = Height-1;
  unsigned int   i, j, ntimes;

  GDBG_INFO((80, "%s:\n", FN_NAME)); 
  GDBG_INFO((80, "%s:  Setting up overlays!\n", FN_NAME));

#ifdef H3D
  h3drd = rd;
  for(i=0;i<nBuffers;i++)
    h3dbuffers[i]=buffers[i];
  h3dnBuffers=nBuffers, h3dstrideInBytes=strideInBytes, h3dWidth=Width, h3dHeight=Height;
  h3dleft = left, h3dright = right, h3dbottom = bottom;
#endif
  tiling = 1;

#ifdef GDBG_INFO_ON
  for (i = 0; i < nBuffers; i++)
    GDBG_INFO((80, "%s:  Buffer %d = 0x%x\n", FN_NAME, i, buffers[i]));
#endif

  GDBG_INFO((80, "%s:  Saving all tiles\n", FN_NAME));
  for (i=0, j=0; i<MAX_TILES; i++) {
    int k;
    volatile FxU16 *tileRegPtr = (FxU16 *) &at3dRegPtr->TileRegsSet[i];
    for (k=0; k<8; k++) {
      tileRegSave[j++] = GET16AT3D(tileRegPtr[k]);
    }
  } /* end for i */

  GDBG_INFO((80, "%s:  Zeroing out all tiles\n", FN_NAME));
    for (i=0; i<MAX_TILES; i++) {
      int k;
      volatile FxU16 *tileRegPtr = (FxU16 *) &at3dRegPtr->TileRegsSet[i];
      for (k=0; k<8; k++) {
        SETAT3D(tileRegPtr[k], 0);
      }
    } /* end for i */
  
  /*---------------------------------
    VIDEO WINDOW
    ---------------------------------*/
  
  
  /* Set up tile sequence control register 
     [12]    - stereo enable                 - 0x0 - disable
     [11:10] - buffercount                   - 0x1 - single buffer
     [9]     - swap buffer on 3D swap signal - 0x1 - enable
     [8]     - buffer swap using TV input    - 0x0 - disable
     [7:4]   - tile sequence length          - 0x1 - magic
     [3:0]   - tile sequence base            - 0x0 - magic
     */
  GDBG_INFO((80, "%s:  SEtting up tile sequence control register\n", FN_NAME));
  {
    int a = 0, b = 0;
    ntimes = 0;
    while( !( a && !b ) ) {     /* pass on falling edge of VRETRACE_ACTIVE */
      a = b;
      b = _inp( VGA_INPUT_STATUS_1C ) & VIS1C_VERTICAL_RETRACE_ACTIVE;
      if (ntimes++ > 1000) {
        GDBG_INFO((80, "%s:  Bailing out after %d checks of VRETRACE check\n", FN_NAME, ntimes));
        break;
      } 
    }
  }
  
  while (GET16AT3D(at3dRegPtr->CurrentVerticalPosition) > 20) {
    if (ntimes++ > 1000) {
      GDBG_INFO((80, "%s:  Bailing out after %d checks of register 0x1fa\n", FN_NAME, ntimes));
      break;
    }
  }
  
  GDBG_INFO((80, "%s, Setting Window Sequence Control Register to %x\n", FN_NAME, 0x0610)); 
  regWindow0ControlSave =
        GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl);
#ifdef H3D
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping?(FxU16)(0x1610):(FxU16)(0x0610)));
#else
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
                (FxU16)(0x0610));
#endif
  
  /* 1: video data stride 
   * Set stride of data buffer in DWORDS
   */
  GDBG_INFO((80, "%s:  Setting Window 0 Video Data Stride to 0x%x\n", FN_NAME, strideInBytes >> 2));
  regWindow0BaseAddressSave =
        GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidDataOffset);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidDataOffset,
                (FxU16)(strideInBytes>>2));
  
  /* 2: video scale factor horizontal
   * A value of zero represents a factor of 1.0
   */
  GDBG_INFO((80, "%s:  Setting Window 0 Horizontal Video Scale Factor to 0x%x \n", FN_NAME, 0x0000));
  regWindow0StretchFactorHor1Save =
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorHor);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorHor, (FxU16)0x0000);
  
  /* 3: video offset factor horizontal 
   * 0xFFF - 0x0 * (X) & 0xFFFh = 0xFFF - set for no scale
   */
  GDBG_INFO((80, "%s:  Setting Window 0 Horizontal Video Offset Factor to 0x%x\n", FN_NAME, 0xfff));

  regWindow0StretchFactorHor2Save = 
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetHor);
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetHor, (FxU16)0xFFF);
  
  /* 4: video scale factor vertical
   * A value of zero represents a factor of 1.0
   */
  GDBG_INFO((80, "%s:  Setting Window 0  Vertical Video Scale Factor to 0x%x\n", FN_NAME, 0x0000));
#if 0 /* Fixme I think this line is bogus, remove after 2.4 !!! XXX ??? */
  at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorVer = (FxU16)0x0000;
#endif
  regWindow0StretchFactorVer1Save =
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorVer);
#ifdef H3D
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorVer, (PageFlipping?(FxU16)0x07FA:(FxU16)0x0000));
#else
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorVer, (FxU16)0x0000);
#endif

  /* 5: video offset factor vertical 
   * 0xFFF - 0x0 * (X) & 0xFFFh = 0xFFF - set for no scale
   */
  GDBG_INFO((80, "%s:  Setting  Window 0 Vertical Video Offset Factor \n", FN_NAME, 0xfff));
  regWindow0StretchFactorVer2Save =
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetVer);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetVer, (FxU16)0xFFF);

  
  /* 6: control reg, pixel depth, window format, enable bits 
     [14]  - Chromakey     - 0x0 - Disable
     [13]  - Reservered    - 0x0
     [12]  - Smoothing     - 0x0 - disable
     [11]  - VStretch lerp - 0x0 - disable
     [10]  - HStretch lerp - 0x0 - disable
     [9]   - Stretch       - 0x0 - disable
     [8]   - YUV-RGB       - 0x0 - disable
     [7]   - Reservered    - 0x0
     [6:4] - color format  - 0x2 - RGB
     [3:1] - Pixel Depth   - 0x5 - 16 BPP
     [0]   - enable window - 0x1 - enable
   */
  GDBG_INFO((80, "%s:  Window 0 Control to 0x%x\n", FN_NAME, 0x20 | 0xa | 0x1));

  regWindow0ControlSave =
        GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidControl);
#ifdef H3D
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidControl, (PageFlipping ? (0x0200|0x0020|0x000A|0x0001) : (0x0020|0x000A|0x0001)));
#else
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidControl,
                 0x0020 | 0x000A | 0x0001);
#endif
  
#ifdef H3D  
  /*---------------------------------
    CONFIGURE TILES 0, 1, 2 & 3
    ---------------------------------*/
  for (i=0; i < nBuffers*2; i++) {
    FxU32 buf;
    if (PageFlipping) {
                buf = buffers[i>>1];
                if (i & 1)      // bottom half of page
                        buf += (strideInBytes*((Height)>>1));
    } else {
      buf = buffers[i];
      if (i == nBuffers) break;
    }
#else
  /*---------------------------------
    CONFIGURE TILES
    ---------------------------------*/
  for (i=0; i < nBuffers; i++) {
    FxU32
      buf = buffers[i];
#endif

    GDBG_INFO((80, "%s:  Configuring tile %d ======================\n", FN_NAME, i));
  
    /* display position left register 
     * Sets the leftmost column of pixels in the tile
     * this column lies inside the tile
     * [10:0] - integer column - 0
     */
    GDBG_INFO((80, "%s:  Setting Tile %d Left Register to 0x%x\n", FN_NAME, i, left));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DisplayPositionLeft, (FxU16)left);
  
    /* display position right register
     * sets the rightmost column of pixels in the tile
     * this column lies outside the tile
     * [10:0] - integer column - 640
     */
    GDBG_INFO((80, "%s:  Setting Tile %d Right Register to 0x%x\n", FN_NAME, i, right));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DisplayPositionRight, (FxU16)right);
  
    /* display position bottom register
     * sets the bottom row of pixels in the tile
     * this row of pixels lies inside the tile
     * [10:0] - bottomost tile - 480-1;
     */    
    GDBG_INFO((80, "%s:  Setting Tile %d Bottom Register to 0x%x\n", FN_NAME, i, bottom));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DisplayPositionBottom, (FxU16)bottom);
  
    /* tile data width register
     * sets the width of the source tile data, not the display
     * [10:0] - width in pixels( not bytes ) - 640
     */
    GDBG_INFO((80, "%s:  Setting Tile %d Data Width register to 0x%x\n", FN_NAME, i, strideInBytes >> 1));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DataWidth, (FxU16)(strideInBytes>>1));
  
    /* tile data location 
     * sets the address in bytes of the top-left corner
     * of the video tile data where it is stored, not the
     * display window.
     * [21:0] - byte address - offset - 0
     */
    GDBG_INFO((20, "%s:  Setting Tile %d Data location to 0x%x\n", FN_NAME, i, buf));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DataLocationLo, (FxU16)(buf & 0xFFFF));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DataLocationHi,
                                                 (FxU8)((buf >> 16) & 0xFF));
  
    /* tile control register 
     * [4]   - rightmost tile  - 0x1 - only
     * [3]   - reserved        - 0 
     * [2:0] - vWindow select  - 0x1 - window 1
     */
    GDBG_INFO((80, "%s: Setting Tile %d Control Register to 0x%x\n", FN_NAME, i, 0x11));
    SETAT3D(at3dRegPtr->TileRegsSet[i].Control, 0x11);
  } /* end for i */
  
  /*  set window sequence control to disable tile swap on 3d request 
      [12]    - stereo enable                 - 0x0 - disable
      [11:10] - buffercount                   - 0x1 - single buffer
      [9]     - swap buffer on 3D swap signal - 0x0 - enable
      [8]     - buffer swap using TV input    - 0x0 - disable
      [7:4]   - tile sequence length          - 0x1 - magic
      [3:0]   - tile sequence base            - 0x0 - magic
   */
#ifdef H3D
  GDBG_INFO((80, "%s:  Disabling swap on 3d request\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping ? (FxU16)(0x1410) : (FxU16)(0x0410)));
#else
  GDBG_INFO((80, "%s:  Disabling swap on 3d request\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
                                         (FxU16)(0x0410));
#endif
  
  /*  set window sequence control to single tile 
      [12]    - stereo enable                 - 0x0 - disable
      [11:10] - buffercount                   - 0x1 - single buffer
      [9]     - swap buffer on 3D swap signal - 0x0 - enable
      [8]     - buffer swap using TV input    - 0x0 - disable
      [7:4]   - tile sequence length          - 0x1 - magic
      [3:0]   - tile sequence base            - 0x0 - magic
   */
#ifdef H3D
  GDBG_INFO((80, "%s:  Setting window sequence control to single tile\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping ? (FxU16)(0x1010) : (FxU16)(0x0010)));
#else
  GDBG_INFO((80, "%s:  Setting Tile Sequence Control to single tile\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
                                (FxU16)(0x0010));
#endif
  
  /*  set window sequence control to enable tile swap on 3d request 
      [12]    - stereo enable                 - 0x0 - disable
      [11:10] - buffercount                   - 0x0 - single buffer
      [9]     - swap buffer on 3D swap signal - 0x1 - enable
      [8]     - buffer swap using TV input    - 0x0 - disable
      [7:4]   - tile sequence length          - 0x1 - magic
      [3:0]   - tile sequence base            - 0x0 - magic
   */
#ifdef H3D
  GDBG_INFO((80, "%s:  Enabling swap on 3d request\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping ? (FxU16)(0x1210) : (FxU16)(0x0210)));
#else
  GDBG_INFO((80, "%s:  Enabling swap on 3d request\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
                                 (FxU16)(0x0210));
#endif
 
  GDBG_INFO((80, "%s:  Enabling %s buffering\n", FN_NAME,
             nBuffers > 2 ? "triple" : "double"));

    /* set window sequence control to enable double buffering 
       [12]    - stereo enable                 - 0x0 - disable
       [11:10] - buffercount                   - 0x1 or 0x2
                          (double or triple buffer, respectively)
       [9]     - swap buffer on 3D swap signal - 0x1 - enable
       [8]     - buffer swap using TV input    - 0x0 - disable
       [7:4]   - tile sequence length          - 0x1 - magic
       [3:0]   - tile sequence base            - 0x0 - magic
       */
  GDBG_INFO((80, "******* nBuffers = %d\n", nBuffers));
#ifdef H3D
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
      (
      ((FxU16)(0x0010)) |
      (PageFlipping ? (FxU16)(0x1000) : (FxU16)(0x0000)) |
      ((nBuffers>2) ? (FxU16)(0x0a00) : (FxU16)(0x0600))
      ));
#else
  if (nBuffers > 2) {
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
            (FxU16)(0x0a10));
  } else 
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
            (FxU16)(0x0610));
#endif

  /* wait a few vsyncs */
  /*           VVVVV--WTF? (CHD) */
  for ( i = 0; i < 1; i++ ) {
    int a = 0, b = 0;
    ntimes = 0;
    while( !( a && !b ) ) {     /* pass on falling edge of VRETRACE_ACTIVE */
      if (ntimes >= 1000) {
        GDBG_INFO((80, "Bailing out of vsync check after %d tries\n", FN_NAME, ntimes));
        break;
      }
      a = b;
      b = _inp( VGA_INPUT_STATUS_1C ) & VIS1C_VERTICAL_RETRACE_ACTIVE;
      
    }
  }
  
  GDBG_INFO((80, "%s:  returning %d\n", FN_NAME, 1));

  return 1;

#undef FN_NAME
} /* initAT3DUseTiles */
 

/*-------------------------------------------------------------------
  Function: initAT3DGetInfo
  Date: 28-Apr-97
  Implementor(s): pgj
  Library: Init Code for AT3D
  Description:

  get information about at3d
  
  Arguments:
  info - pointer to info struct
  
  Return:
  FXTRUE if success, FXFALSE if Failure
  -------------------------------------------------------------------*/
INITAT3DENTRY(initAT3DGetInfo,FxBool,(VG96Info *info))  
{
#define FN_NAME "initAT3DGetInfo"
#ifdef __DOS32__
    union REGS inR, outR;
#endif /* __DOS32__ */
    FxU32 bytes;

    GDBG_INFO((80, "%s:\n", FN_NAME));

    info->vgaChip = 0;
    info->vg96Rev = 0;

#ifdef __DOS32__
    inR.w.ax = 0x3002;
    int386(0x10, &inR, &outR);
    bytes = outR.h.al;
#else
#ifdef __linux__
    bytes = myGetenv("SST_VRAM_4MB") ? 0x40 : 0x20;
#else
#define PROMOTIONESCAPE                 0x80DE
    {
        HDC hdc = GetDC(GetActiveWindow());
        bytes = ExtEscape(hdc, PROMOTIONESCAPE, sizeof("MS"), "MS", 0, NULL);
    }
#endif
#endif /* __DOS32__ */

    bytes <<= 16;
    info->vg96Ram = bytes;

    info->nTFX    = 1;
    info->tfxRev  = 1;
    info->tfxRam  = 2;
    GDBG_INFO((135, "%s: %8.8x bytes\n", FN_NAME, bytes));

    return FXTRUE;

#undef FN_NAME
} /* initAT3DGetInfo */

#ifdef H3D
/*-------------------------------------------------------------------
  Function: initAT3DSetTiles
  Date: 28-Apr-97
  Implementor(s):
  Library: Init Code for AT3D
  Description:

  get information about at3d
  
  Arguments:
  info - pointer to info struct
  
  Return:
  FXTRUE if success, FXFALSE if Failure
  -------------------------------------------------------------------*/
void initAT3DSetTiles(FxU32 PageFlipping, FxU32 nBuffers) 
{
#define FN_NAME "initAT3DSetTiles"
  FxU32   i, ntimes;

  if (!h3drd) return; // not inited yet

#ifdef GDBG_INFO_ON
  for (i = 0; i < nBuffers; i++)
    GDBG_INFO((80, "%s:  Buffer %d = 0x%x\n", FN_NAME, i, h3dbuffers[i]));
#endif

  GDBG_INFO((80, "%s:  Zeroing out all tiles\n", FN_NAME));
    for (i=0; i<MAX_TILES; i++) {
      int k;
      volatile FxU16 *tileRegPtr = (FxU16 *) &at3dRegPtr->TileRegsSet[i];
      for (k=0; k<8; k++) {
        SETAT3D(tileRegPtr[k], 0);
      }
    } /* end for i */
  
  /*---------------------------------
    VIDEO WINDOW
    ---------------------------------*/
  
  
  /* Set up tile sequence control register 
     [12]    - stereo enable                 - 0x0 - disable
     [11:10] - buffercount                   - 0x1 - single buffer
     [9]     - swap buffer on 3D swap signal - 0x1 - enable
     [8]     - buffer swap using TV input    - 0x0 - disable
     [7:4]   - tile sequence length          - 0x1 - magic
     [3:0]   - tile sequence base            - 0x0 - magic
     */
  GDBG_INFO((80, "%s:  SEtting up tile sequence control register\n", FN_NAME));
  {
    int a = 0, b = 0;
    ntimes = 0;
    while( !( a && !b ) ) {     /* pass on falling edge of VRETRACE_ACTIVE */
      a = b;
      b = _inp( VGA_INPUT_STATUS_1C ) & VIS1C_VERTICAL_RETRACE_ACTIVE;
      if (ntimes++ > 1000) {
        GDBG_INFO((80, "%s:  Bailing out after %d checks of VRETRACE check\n", FN_NAME, ntimes));
        break;
      } 
    }
  }
  
  while (GET16AT3D(at3dRegPtr->CurrentVerticalPosition) > 20) {
    if (ntimes++ > 1000) {
      GDBG_INFO((80, "%s:  Bailing out after %d checks of register 0x1fa\n", FN_NAME, ntimes));
      break;
    }
  }
  
  GDBG_INFO((80, "%s, Setting Window Sequence Control Register to %x\n", FN_NAME, 0x0610)); 
  regWindow0ControlSave =
        GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping?(FxU16)(0x1610):(FxU16)(0x0610)));
  
  /* 1: video data stride 
   * Set stride of data buffer in DWORDS
   */
  GDBG_INFO((80, "%s:  Setting Window 0 Video Data Stride to 0x%x\n", FN_NAME, h3dstrideInBytes >> 2));
  regWindow0BaseAddressSave =
        GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidDataOffset);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidDataOffset,
                (FxU16)(h3dstrideInBytes>>2));
  
  /* 2: video scale factor horizontal
   * A value of zero represents a factor of 1.0
   */
  GDBG_INFO((80, "%s:  Setting Window 0 Horizontal Video Scale Factor to 0x%x \n", FN_NAME, 0x0000));
  regWindow0StretchFactorHor1Save =
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorHor);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorHor, (FxU16)0x0000);
  
  /* 3: video offset factor horizontal 
   * 0xFFF - 0x0 * (X) & 0xFFFh = 0xFFF - set for no scale
   */
  GDBG_INFO((80, "%s:  Setting Window 0 Horizontal Video Offset Factor to 0x%x\n", FN_NAME, 0xfff));

  regWindow0StretchFactorHor2Save = 
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetHor);
    SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetHor, (FxU16)0xFFF);
  
  /* 4: video scale factor vertical
   * A value of zero represents a factor of 1.0
   */
  GDBG_INFO((80, "%s:  Setting Window 0  Vertical Video Scale Factor to 0x%x\n", FN_NAME, 0x0000));
  regWindow0StretchFactorVer1Save =
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorVer);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleFactorVer, (PageFlipping?(FxU16)0x07FA:(FxU16)0x0000));

  /* 5: video offset factor vertical 
   * 0xFFF - 0x0 * (X) & 0xFFFh = 0xFFF - set for no scale
   */
  GDBG_INFO((80, "%s:  Setting  Window 0 Vertical Video Offset Factor \n", FN_NAME, 0xfff));
  regWindow0StretchFactorVer2Save =
    GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetVer);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidScaleOffsetVer, (FxU16)0xFFF);

  
  /* 6: control reg, pixel depth, window format, enable bits 
     [14]  - Chromakey     - 0x0 - Disable
     [13]  - Reservered    - 0x0
     [12]  - Smoothing     - 0x0 - disable
     [11]  - VStretch lerp - 0x0 - disable
     [10]  - HStretch lerp - 0x0 - disable
     [9]   - Stretch       - 0x0 - disable
     [8]   - YUV-RGB       - 0x0 - disable
     [7]   - Reservered    - 0x0
     [6:4] - color format  - 0x2 - RGB
     [3:1] - Pixel Depth   - 0x5 - 16 BPP
     [0]   - enable window - 0x1 - enable
   */
  GDBG_INFO((80, "%s:  Window 0 Control to 0x%x\n", FN_NAME, 0x20 | 0xa | 0x1));

  regWindow0ControlSave =
        GET16AT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidControl);
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].VidControl, (PageFlipping ? (0x0200|0x0020|0x000A|0x0001) : (0x0020|0x000A|0x0001)));
  /*---------------------------------
    CONFIGURE TILES 0, 1, 2 & 3
    ---------------------------------*/
  for (i=0; i < nBuffers*2; i++) {
    FxU32 buf;
    if (PageFlipping) {
                buf = h3dbuffers[i>>1];
                if (i & 1)      // bottom half of page
                        buf += (h3dstrideInBytes*((h3dHeight)>>1));
    } else {
      buf = h3dbuffers[i];
      if (i == nBuffers) break;
    }

    GDBG_INFO((80, "%s:  Configuring tile %d ======================\n", FN_NAME, i));
  
    /* display position left register 
     * Sets the leftmost column of pixels in the tile
     * this column lies inside the tile
     * [10:0] - integer column - 0
     */
    GDBG_INFO((80, "%s:  Setting Tile %d Left Register to 0x%x\n", FN_NAME, i, h3dleft));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DisplayPositionLeft, (FxU16)h3dleft);
  
    /* display position right register
     * sets the rightmost column of pixels in the tile
     * this column lies outside the tile
     * [10:0] - integer column - 640
     */
    GDBG_INFO((80, "%s:  Setting Tile %d Right Register to 0x%x\n", FN_NAME, i, h3dright));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DisplayPositionRight, (FxU16)h3dright);
  
    /* display position bottom register
     * sets the bottom row of pixels in the tile
     * this row of pixels lies inside the tile
     * [10:0] - bottomost tile - 480-1;
     */    
    GDBG_INFO((80, "%s:  Setting Tile %d Bottom Register to 0x%x\n", FN_NAME, i, h3dbottom));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DisplayPositionBottom, (FxU16)h3dbottom);
  
    /* tile data width register
     * sets the width of the source tile data, not the display
     * [10:0] - width in pixels( not bytes ) - 640
     */
    GDBG_INFO((80, "%s:  Setting Tile %d Data Width register to 0x%x\n", FN_NAME, i, h3dstrideInBytes >> 1));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DataWidth, (FxU16)(h3dstrideInBytes>>1));
  
    /* tile data location 
     * sets the address in bytes of the top-left corner
     * of the video tile data where it is stored, not the
     * display window.
     * [21:0] - byte address - offset - 0
     */
    GDBG_INFO((20, "%s:  Setting Tile %d Data location to 0x%x\n", FN_NAME, i, buf));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DataLocationLo, (FxU16)(buf & 0xFFFF));
    SETAT3D(at3dRegPtr->TileRegsSet[i].DataLocationHi,
                                                 (FxU8)((buf >> 16) & 0xFF));
  
    /* tile control register 
     * [4]   - rightmost tile  - 0x1 - only
     * [3]   - reserved        - 0 
     * [2:0] - vWindow select  - 0x1 - window 1
     */
    GDBG_INFO((80, "%s: Setting Tile %d Control Register to 0x%x\n", FN_NAME, i, 0x11));
    SETAT3D(at3dRegPtr->TileRegsSet[i].Control, 0x11);
  } /* end for i */
  
  /*  set window sequence control to disable tile swap on 3d request 
      [12]    - stereo enable                 - 0x0 - disable
      [11:10] - buffercount                   - 0x1 - single buffer
      [9]     - swap buffer on 3D swap signal - 0x0 - enable
      [8]     - buffer swap using TV input    - 0x0 - disable
      [7:4]   - tile sequence length          - 0x1 - magic
      [3:0]   - tile sequence base            - 0x0 - magic
   */
  GDBG_INFO((80, "%s:  Disabling swap on 3d request\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping ? (FxU16)(0x1410) : (FxU16)(0x0410)));
  
  /*  set window sequence control to single tile 
      [12]    - stereo enable                 - 0x0 - disable
      [11:10] - buffercount                   - 0x1 - single buffer
      [9]     - swap buffer on 3D swap signal - 0x0 - enable
      [8]     - buffer swap using TV input    - 0x0 - disable
      [7:4]   - tile sequence length          - 0x1 - magic
      [3:0]   - tile sequence base            - 0x0 - magic
   */
  GDBG_INFO((80, "%s:  Setting window sequence control to single tile\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping ? (FxU16)(0x1010) : (FxU16)(0x0010)));
  
  /*  set window sequence control to enable tile swap on 3d request 
      [12]    - stereo enable                 - 0x0 - disable
      [11:10] - buffercount                   - 0x0 - single buffer
      [9]     - swap buffer on 3D swap signal - 0x1 - enable
      [8]     - buffer swap using TV input    - 0x0 - disable
      [7:4]   - tile sequence length          - 0x1 - magic
      [3:0]   - tile sequence base            - 0x0 - magic
   */
  GDBG_INFO((80, "%s:  Enabling swap on 3d request\n", FN_NAME));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl, (PageFlipping ? (FxU16)(0x1210) : (FxU16)(0x0210)));
 
  GDBG_INFO((80, "%s:  Enabling %s buffering\n", FN_NAME,
             nBuffers > 2 ? "triple" : "double"));

    /* set window sequence control to enable double buffering 
       [12]    - stereo enable                 - 0x0 - disable
       [11:10] - buffercount                   - 0x1 or 0x2
                          (double or triple buffer, respectively)
       [9]     - swap buffer on 3D swap signal - 0x1 - enable
       [8]     - buffer swap using TV input    - 0x0 - disable
       [7:4]   - tile sequence length          - 0x1 - magic
       [3:0]   - tile sequence base            - 0x0 - magic
       */
  GDBG_INFO((80, "******* nBuffers = %d\n", nBuffers));
  SETAT3D(at3dRegPtr->V2.VidWindowV2Set[0].TileSequenceControl,
    (
    ((FxU16)(0x0010)) |
    (PageFlipping ? (FxU16)(0x1000) : (FxU16)(0x0000)) |
    ((nBuffers>2) ? (FxU16)(0x0a00) : (FxU16)(0x0600))
    ));
  
  /* wait a few vsyncs */
  /*           VVVVV--WTF? (CHD) */
  for ( i = 0; i < 1; i++ ) {
    int a = 0, b = 0;
    ntimes = 0;
    while( !( a && !b ) ) {     /* pass on falling edge of VRETRACE_ACTIVE */
      if (ntimes >= 1000) {
        GDBG_INFO((80, "Bailing out of vsync check after %d tries\n", FN_NAME, ntimes));
        break;
      }
      a = b;
      b = _inp( VGA_INPUT_STATUS_1C ) & VIS1C_VERTICAL_RETRACE_ACTIVE;
      
    }
  }
  
  GDBG_INFO((80, "%s:  returning %d\n", FN_NAME, 1));

#undef FN_NAME
} /* initAT3DSetTiles */

#endif /* H3D */
