text: /******************************Module*Header**********************************\
  *
  *                           *******************
  *                           * D3D SAMPLE CODE *
  *                           *******************
  *
  * Module Name: d3dtxt.c
  *
  *  Content: D3D texture setup
  *
  * Copyright (c) 1994-1999 3Dlabs Inc. Ltd. All rights reserved.
  * Copyright (c) 1995-2003 Microsoft Corporation.  All rights reserved.
  \*****************************************************************************/
  
  #include "glint.h"
  #include "dma.h"
  #include "chroma.h"
  #include "tag.h"
  
  //-----------------------------------------------------------------------------
  
  // Some variables shared through this module (not globals)
  // These are set up in _D3DChangeTextureP3RX.
  
  // P3 has 16 texture map base address slots, numbered 0 to 15 hence ...
  #define P3_TEX_MAP_MAX_LEVEL    15
  
  typedef struct
  {
      DWORD dwTex0MipBase;
      DWORD dwTex0MipMax;
      DWORD dwTex0ActMaxLevel; // Controlled by D3DTSS_MAXMIPLEVEL, default 0
      DWORD dwTex1MipBase;
      DWORD dwTex1MipMax;
      DWORD dwTex1ActMaxLevel; // Same for Texture 1
  } P3_MIP_BASES;
  
  #define TSSTATE(stageno,argno)        \
                          ( pContext->TextureStageState[stageno].m_dwVal[argno] )
  #define TSSTATESELECT(stageno,argno)  \
                              ( TSSTATE(stageno,argno) & D3DTA_SELECTMASK )
  #define TSSTATEINVMASK(stageno,argno) \
                              ( TSSTATE(stageno,argno) & ~D3DTA_COMPLEMENT )
  #define TSSTATEALPHA(stageno,argno)   \
                              ( TSSTATE(stageno,argno)  & ~D3DTA_ALPHAREPLICATE )
  
  #define IS_ALPHA_ARG 1
  #define IS_COLOR_ARG 0
  
  
  #if DX8_DDI
  //-----------------------------------------------------------------------------
  //
  // __TXT_MapDX8toDX6TexFilter
  //
  // map DX8 enums into DX6(&7) texture filtering enums 
  //
  //-----------------------------------------------------------------------------
  DWORD
  __TXT_MapDX8toDX6TexFilter( DWORD dwStageState, DWORD dwValue )
  {
      switch (dwStageState)
      {
      case D3DTSS_MAGFILTER:
          switch (dwValue)
          {
          case D3DTEXF_POINT            : return D3DTFG_POINT;
          case D3DTEXF_LINEAR           : return D3DTFG_LINEAR;
          case D3DTEXF_FLATCUBIC        : return D3DTFG_FLATCUBIC;
          case D3DTEXF_GAUSSIANCUBIC    : return D3DTFG_GAUSSIANCUBIC;
          case D3DTEXF_ANISOTROPIC      : return D3DTFG_ANISOTROPIC;
          }
          break;
      case D3DTSS_MINFILTER:
          switch (dwValue)
          {
          case D3DTEXF_POINT            : return D3DTFN_POINT;
          case D3DTEXF_LINEAR           : return D3DTFN_LINEAR;
          case D3DTEXF_FLATCUBIC        : return D3DTFN_ANISOTROPIC;
          }
          break;
      case D3DTSS_MIPFILTER:
          switch (dwValue)
          {
          case D3DTEXF_NONE             : return D3DTFP_NONE;
          case D3DTEXF_POINT            : return D3DTFP_POINT;
          case D3DTEXF_LINEAR           : return D3DTFP_LINEAR;
          }
          break;
      }
      return 0x0;
  } // __TXT_MapDX8toDX6TexFilter
  #endif // DX8_DDI
  
  //-----------------------------------------------------------------------------
  //
  // _D3D_TXT_ParseTextureStageStates
  //
  // Parse the texture state stages command token and update our context state
  //
  // Note : bTranslateDX8FilterValueToDX6 will only be FALSE when it is called
  //        from _D3D_SB_ExecuteStateSet if that state set's value has been 
  //        changes by _D3D_SB_CaptureStateSet (Basically DX6 filter values are
  //        stored in the state set directly, thus no need to translate them.)
  //
  //-----------------------------------------------------------------------------
  void 
  _D3D_TXT_ParseTextureStageStates(
      P3_D3DCONTEXT* pContext, 
      D3DHAL_DP2TEXTURESTAGESTATE *pState, 
      DWORD dwCount,
      BOOL bTranslateDX8FilterValueToDX6)
  {
      DWORD i;
      DWORD dwStage, dwState, dwValue;
      
      DISPDBG((DBGLVL,"*** In _D3D_TXT_ParseTextureStageStates"));
  
      for (i = 0; i < dwCount; i++, pState++)
      {
          dwStage = pState->wStage;
          dwState = pState->TSState;
          dwValue = pState->dwValue;
        
          // check for range before continuing
          if ( (dwStage < D3DHAL_TSS_MAXSTAGES) &&
               (dwState < D3DTSS_MAX))
          {
  
  #if DX7_D3DSTATEBLOCKS 
              if (pContext->bStateRecMode)
              {
                  // Record this texture stage state into the 
                  //current state set being recorded 
                  _D3D_SB_RecordStateSetTSS(pContext, dwStage, dwState, dwValue);
  
                  // skip any further processing and go to the next TSS
                  continue;
              }
  #endif //DX7_D3DSTATEBLOCKS       
  
  #if DX7_TEXMANAGEMENT
              if ((D3DTSS_TEXTUREMAP == dwState) && (0 != dwValue))
              {
                  P3_SURF_INTERNAL* pTexture;
  
                  pTexture = GetSurfaceFromHandle(pContext, dwValue);
  
                  // If this is a valid managed texture
                  if (CHECK_SURF_INTERNAL_AND_DDSURFACE_VALIDITY(pTexture) &&
                      (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)) 
                  {
                      // Update stats
                      _D3D_TM_STAT_Inc_NumTexturesUsed(pContext);
                      _D3D_TM_STAT_Inc_NumUsedTexInVid(pContext, pTexture);
                  }
              }
  #endif // DX7_TEXMANAGEMENT  
  
              DISPDBG((DBGLVL,"  Stage = %d, State = 0x%x, Value = 0x%x", 
                                 dwStage, dwState, dwValue));
  
              // Special case a texture handle change and the address update
              switch ( dwState )
              {
              case D3DTSS_TEXTUREMAP:
                  DISPDBG((DBGLVL,"  D3DTSS_TEXTUREMAP: Handle=0x%x", dwValue));
  
                  if (pContext->TextureStageState[dwStage].m_dwVal[dwState] != 
                                                                         dwValue)
                  {
                      pContext->TextureStageState[dwStage].m_dwVal[dwState] = 
                                                                          dwValue;
                      DIRTY_TEXTURE(pContext);
                  }
                  break;
  
              case D3DTSS_ADDRESS:
                  DISPDBG((DBGLVL,"  D3DTSS_ADDRESS"));
                  // map single set ADDRESS to both U and V controls
                  pContext->TextureStageState[dwStage].m_dwVal[D3DTSS_ADDRESSU] = 
                  pContext->TextureStageState[dwStage].m_dwVal[D3DTSS_ADDRESSV] = 
                  pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
  
                  DIRTY_TEXTURE(pContext);
                  break;
                  
              case D3DTSS_COLOROP:
              case D3DTSS_ALPHAOP:
              case D3DTSS_COLORARG1:
              case D3DTSS_COLORARG2:
              case D3DTSS_ALPHAARG1:
              case D3DTSS_ALPHAARG2:
                  pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                  pContext->Flags &= ~SURFACE_MODULATE;
                  DIRTY_TEXTURESTAGEBLEND(pContext);
                  DIRTY_TEXTURE(pContext);
                  break;
                  
              case D3DTSS_TEXCOORDINDEX:
                  DISPDBG((DBGLVL,"  D3DTSS_TEXCOORDINDEX: stage %d, value %d", 
                              dwStage, dwValue ));
                  pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                  
                  // Update the offsets to the texture coordinates                                         
                  // NOTE: The texture coordinate index can contain various flags
                  // in addition to the actual value. These flags are:
                  //     D3DTSS_TCI_PASSTHRU (default - resolves to zero)
                  //     D3DTSS_TCI_CAMERASPACENORMAL 
                  //     D3DTSS_TCI_CAMERASPACEPOSITION 
                  //     D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR 
                  // and are used for texture coordinate generation.
                  //
                  // These flags are not relevant when considering the offset of
                  // texture coordinates in the vertex stream. These flags appear
                  // in the high word of the index value DWORD. Only the low word
                  // contains actual index data. Therefore, we will mask of the
                  // low word when looking up the offset table for this texture
                  // coordinate index.
                  pContext->FVFData.dwTexOffset[dwStage] = 
                      pContext->FVFData.dwTexCoordOffset[dwValue & 0x0000FFFFul];
                      
                  DIRTY_TEXTURE(pContext);
                  break;
                  
              case D3DTSS_MIPMAPLODBIAS:
                  DISPDBG((DBGLVL,"  D3DTSS_MIPMAPLODBIAS: stage %d, value %d", 
                              dwStage, dwValue ));
                  pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                  DIRTY_TEXTURE(pContext);
                  break;
                  
              case D3DTSS_MAGFILTER:        
              case D3DTSS_MINFILTER:
              case D3DTSS_MIPFILTER:
  #if DX8_DDI        
                  if((!IS_DX7_OR_EARLIER_APP(pContext)) && 
                     bTranslateDX8FilterValueToDX6)
                  {
                      // filtering values are somewhat different in DX8 
                      // so translate them before using them.
                      dwValue = __TXT_MapDX8toDX6TexFilter(dwState, dwValue);
                  }
  #endif DX8_DDI            
                  
                  pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                  DIRTY_TEXTURE(pContext);        
                  break;
                  
              case D3DTSS_MAXMIPLEVEL:
                  DISPDBG((DBGLVL,"  D3DTSS_MAXMIPLEVEL: stage %d, value %d", 
                              dwStage, dwValue ));
                  pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                  DIRTY_TEXTURE(pContext);
                  break;     
                  
              default:
                  pContext->TextureStageState[dwStage].m_dwVal[dwState] = dwValue;
                  DIRTY_TEXTURE(pContext);
                  break;
              } // switch
          } 
          else
          {
              DISPDBG((WRNLVL,"Out of range stage/state %d %d",dwStage,dwState));
          }// if
      } // for
  } // _D3D_TXT_ParseTextureStageStates
  
  
  
  //-----------------------------------------------------------------------------
  //
  // SETARG
  //
  // dwArg = the argument.
  // num = argument number (1 or 2).
  // bIsAlpha = TRUE if this is the alpha channel, 
  //            FALSE if this is the colour channel.
  // iD3DStage = D3D stage number.
  // iChipStageNo = chip stage number (should only be 0 or 1 on P3)
  //
  //-----------------------------------------------------------------------------
  void
  SETARG(
      P3_D3DCONTEXT *pContext, 
      struct TextureCompositeRGBAMode *pMode,
      DWORD dwArg, 
      DWORD num,
      BOOL bIsAlpha,    
      DWORD iD3DStage, 
      DWORD iChipStageNo)    
  {                       
      BOOL bSetArgToggleInvert;
      DWORD dwArgValue, dwInvertArgValue;
      BOOL bArgValueAssigned = FALSE,
           bInvertArgValueAssigned;
  
      bSetArgToggleInvert = FALSE;                            
      switch (dwArg & D3DTA_SELECTMASK)                       
      {                                                       
          case D3DTA_TEXTURE:                                 
              if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
              {                                                   
                  dwArgValue = ( pContext->iStageTex[iD3DStage] == 0 ) ?   
                                                           P3RX_TEXCOMP_T0A :   
                                                           P3RX_TEXCOMP_T1A;  
                  bArgValueAssigned = TRUE;
                  DISPDBG((DBGLVL,"  Tex%dA", pContext->iStageTex[iD3DStage] ));     
              }                                                   
              else                                                
              {                                                   
                  dwArgValue = ( pContext->iStageTex[iD3DStage] == 0 ) ?   
                                                           P3RX_TEXCOMP_T0C :   
                                                           P3RX_TEXCOMP_T1C; 
                  bArgValueAssigned = TRUE;                                                         
                  DISPDBG((DBGLVL,"  Tex%dC", pContext->iStageTex[iD3DStage] ));   
              }                                                   
              break;                
              
          case D3DTA_DIFFUSE:                                     
              if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
              {                                                   
                  dwArgValue = P3RX_TEXCOMP_CA;      
                  bArgValueAssigned = TRUE;
                  DISPDBG((DBGLVL,"  DiffA" ));                        
              }                                                   
              else                                                
              {                                                   
                  dwArgValue = P3RX_TEXCOMP_CC;              
                  bArgValueAssigned = TRUE;
                  DISPDBG((DBGLVL,"  DiffC" ));                        
              }                                                   
              break;        
              
          case D3DTA_CURRENT:                                     
              // Cope with a "current" argument in texcomp0 
              if ( iChipStageNo == 0 )                            
              {                                                                           
                  // This is texcomp0
                  if ( pContext->bBumpmapEnabled )                                        
                  {                                                                       
                      // Emboss bumpmapping is on
                      if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))                   
                      {                                                                   
                          // This is the alpha-channel, where the D3D stages 0 & 1
                          // should have put the heightfield info.
                          dwArgValue = P3RX_TEXCOMP_HEIGHTA;                         
                          bArgValueAssigned = TRUE;
                          DISPDBG((DBGLVL,"  BumpA" ));                                        
                          // And cope with inverted bumpmaps.
                          bSetArgToggleInvert = pContext->bBumpmapInverted;               
                      }                                                                   
                      else                                                                
                      {                                                                   
                          // Colour channel - this will hold the diffuse colour.
                          dwArgValue = P3RX_TEXCOMP_CC;                              
                          bArgValueAssigned = TRUE;
                          DISPDBG((DBGLVL,"  DiffC" ));                                        
                      }                                                                   
                  }                                                                       
                  else                                                    
                  {                                                       
                      // Embossing is off - default to diffuse.
                      if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
                      {                                                   
                          dwArgValue = P3RX_TEXCOMP_CA;              
                          bArgValueAssigned = TRUE;
                          DISPDBG((DBGLVL,"  DiffA" ));                        
                      }                                                   
                      else                                                
                      {                                                   
                          dwArgValue = P3RX_TEXCOMP_CC;              
                          bArgValueAssigned = TRUE;
                          DISPDBG((DBGLVL,"  DiffC" ));                        
                      }                                                   
                  }                                                       
              }                                                           
              else                                                        
              {                                                           
                  // texcomp stage 1
                  if ( pContext->bStage0DotProduct )                      
                  {                                                       
                      // Need to take the dotproduct sum result,
                      // even in the alpha channel, according to the docs.
                      dwArgValue = P3RX_TEXCOMP_SUM;                 
                      bArgValueAssigned = TRUE;
                  }                                                       
                  else                                                    
                  {                                                       
                      if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
                      {                                                   
                          dwArgValue = P3RX_TEXCOMP_OA;              
                          bArgValueAssigned = TRUE;
                          DISPDBG((DBGLVL,"  CurrA" ));                        
                      }                                                   
                      else                                                
                      {                                                   
                          dwArgValue = P3RX_TEXCOMP_OC;              
                          bArgValueAssigned = TRUE;
                          DISPDBG((DBGLVL,"  CurrC" ));                        
                      }                                                   
                  }                                                       
              }                                                           
              break;      
              
          case D3DTA_TFACTOR:                                     
              if ((dwArg & D3DTA_ALPHAREPLICATE) || (bIsAlpha))   
              {                                                   
                  dwArgValue = P3RX_TEXCOMP_FA;              
                  bArgValueAssigned = TRUE;
                  DISPDBG((DBGLVL,"  TfactA" ));                       
              }                                                   
              else                                                
              {                                                   
                  dwArgValue = P3RX_TEXCOMP_FC;              
                  bArgValueAssigned = TRUE;
                  DISPDBG((DBGLVL,"  TfactC" ));                       
              }                                                   
              break;                                              
              
          default:                                                
              if ( bIsAlpha )                                     
              {                                                   
                  DISPDBG((ERRLVL,"ERROR: Invalid AlphaArgument"));
                  SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_ARG );
              }
              else
              {
                  DISPDBG((ERRLVL,"ERROR: Invalid ColorArgument"));
                  SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_ARG );
              }
              break;
      } // switch
      
      if ( ( (dwArg & D3DTA_COMPLEMENT) == 0 ) == bSetArgToggleInvert )
      {                                                           
          dwInvertArgValue= __PERMEDIA_ENABLE;   
          bInvertArgValueAssigned = TRUE;
          DISPDBG((DBGLVL,"    inverted" ));                           
      }                                                           
      else                                                        
      {                                                           
          dwInvertArgValue = __PERMEDIA_DISABLE;   
          bInvertArgValueAssigned = TRUE;        
      }                                                           
                                                                  
      // Set up the I input for MODULATExxx_ADDxxx modes.
      if ( num == 1 )                                             
      {                                                           
          switch (dwArg & D3DTA_SELECTMASK)                       
          {                                                       
              case D3DTA_TEXTURE:                                 
                  pMode->I = ( pContext->iStageTex[iD3DStage] == 0 ) ?  
                                                  P3RX_TEXCOMP_I_T0A :  
                                                  P3RX_TEXCOMP_I_T1A;   
                  DISPDBG((DBGLVL,"  I: Tex%dA", pContext->iStageTex[iD3DStage] ));    
                  break;    
                  
              case D3DTA_DIFFUSE:                                     
                  pMode->I = P3RX_TEXCOMP_I_CA;                       
                  DISPDBG((DBGLVL,"  I: DiffA" ));                         
                  break;                                              
                  
              case D3DTA_CURRENT:                                     
                  if ( iChipStageNo == 0 )                            
                  {                                                   
                      if ( pContext->bBumpmapEnabled )                
                      {                                               
                          // Bumpmapping mode. 
                          pMode->I = P3RX_TEXCOMP_I_HA;               
                          DISPDBG((DBGLVL,"  I: BumpA" ));                 
                      }                                               
                      else                                            
                      {                                               
                          pMode->I = P3RX_TEXCOMP_I_CA;               
                          DISPDBG((DBGLVL,"  I: DiffA" ));                 
                      }                                               
                  }                                                   
                  else                                                
                  {                                                   
                      pMode->I = P3RX_TEXCOMP_I_OA;                   
                      DISPDBG((DBGLVL,"  I: CurrA" ));                     
                  }                                                   
                  break;                                              
                  
              case D3DTA_TFACTOR:                                     
                  pMode->I = P3RX_TEXCOMP_I_FA;                       
                  DISPDBG((DBGLVL,"  I: TfactA" ));                        
                  break;                                              
                  
              default:                                                
                  if ( bIsAlpha )                                     
                  {                                                   
                      DISPDBG((ERRLVL,"ERROR: Invalid AlphaArgument"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_ARG );    
                  }                                                   
                  else                                                
                  {                                                   
                      DISPDBG((ERRLVL,"ERROR: Invalid ColorArgument"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_ARG );    
                  }                                                   
                  break;                                              
          } // switch
          
          if ( ( (dwArg & D3DTA_COMPLEMENT) == 0 ) == bSetArgToggleInvert )
          {                                                           
              pMode->InvertI = __PERMEDIA_ENABLE;                        
          }                                                           
          else                                                        
          {                                                           
              pMode->InvertI = __PERMEDIA_DISABLE;                       
          }                                                           
      } // if ( num == 1 )          
  
      if (bArgValueAssigned)
      {
          if (num == 1)
          {
              pMode->Arg1 = dwArgValue;
          }
          else
          {
              pMode->Arg2 = dwArgValue;        
          }
      }
  
      if (bInvertArgValueAssigned)
      {
          if (num == 1)
          {
              pMode->InvertArg1 = dwInvertArgValue;
          }
          else
          {
              pMode->InvertArg2 = dwInvertArgValue;        
          }
      }    
  } // SETARG
  
  //-----------------------------------------------------------------------------
  //
  // SETTAARG_ALPHA
  //
  // TexApp blend mode for the alpha channel.
  //
  //-----------------------------------------------------------------------------
  void
  SETTAARG_ALPHA(
      P3_D3DCONTEXT *pContext, 
      struct TextureApplicationMode *pMode,
      DWORD dwArg, 
      DWORD num) 
  {                                                          
      switch (dwArg & D3DTA_SELECTMASK)                               
      {                                                               
          case D3DTA_TEXTURE:                                         
              DISPDBG((ERRLVL,"ERROR: Invalid TA AlphaArgument"));
              SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );    
              break;                                                  
          case D3DTA_DIFFUSE:                                         
              if ( (num) == 1 )                                       
              {                                                       
                  pMode->AlphaA = P3RX_TEXAPP_A_CA;          
                  DISPDBG((DBGLVL,"  DiffA" ));                        
              }                                                       
              else                                                    
              {                                                       
                  DISPDBG((ERRLVL,"ERROR: Invalid TA AlphaArgument"));
                  SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );
              }                                                       
              break;                                                  
          case D3DTA_CURRENT:                                         
              if ( (num) == 2 )                                       
              {                                                       
                  pMode->AlphaB = P3RX_TEXAPP_B_TA;          
                  DISPDBG((DBGLVL,"  CurrA" ));                        
              }                                                       
              else                                                    
              {                                                       
                  // Can't do
                  DISPDBG((ERRLVL,"ERROR: Invalid TA AlphaArgument"));
                  SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );
              }                                                       
              break;                                                  
          case D3DTA_TFACTOR:                                         
              if ( (num) == 1 )                                       
              {                                                       
                  pMode->AlphaA = P3RX_TEXAPP_A_KA;          
                  DISPDBG((DBGLVL,"  TfactA" ));                       
              }                                                       
              else                                                    
              {                        
                  if ( (num) != 2)
                  {
                      DISPDBG((ERRLVL," ** SETTAARG: num must be 1 or 2"));
                  }
                  pMode->AlphaB = P3RX_TEXAPP_B_KA;          
                  DISPDBG((DBGLVL,"  TfactA" ));                       
              }                                                       
              break;                                                  
          default:                                                    
              DISPDBG((ERRLVL,"ERROR: Unknown TA AlphaArgument"));
              SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_ARG );        
              break;                                                  
      }         
      
      if ( (dwArg & D3DTA_COMPLEMENT) != 0 )                          
      {                                                               
          // Can't do COMPLEMENT on the args.
          DISPDBG((ERRLVL,"ERROR: Can't do COMPLEMENT in TA unit"));
          SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_ARG_HERE );            
      }                                                               
  } // SETTAARG_ALPHA
  
  //-----------------------------------------------------------------------------
  //
  // SETTAARG_COLOR
  //
  // TexApp blend mode for the color channel.
  //
  //-----------------------------------------------------------------------------
  void 
  SETTAARG_COLOR(
      P3_D3DCONTEXT *pContext, 
      struct TextureApplicationMode *pMode,
      DWORD dwArg, 
      DWORD num) 
  {                                                                   
      switch (dwArg & D3DTA_SELECTMASK)                               
      {                                                               
              DISPDBG((ERRLVL,"ERROR: Invalid TA ColorArgument"));
              SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );    
              break;                                                  
          case D3DTA_DIFFUSE:                                         
              if ( (num) == 1 )                                       
              {                                                       
                  if ( (dwArg & D3DTA_ALPHAREPLICATE) != 0 )      
                  {                                               
                      pMode->ColorA = P3RX_TEXAPP_A_CA;      
                      DISPDBG((DBGLVL,"  DiffA" ));                    
                  }                                               
                  else                                            
                  {                                               
                      pMode->ColorA = P3RX_TEXAPP_A_CC;      
                      DISPDBG((DBGLVL,"  DiffC" ));                    
                  }                                               
                  // Set up the I input for MODULATExxx_ADDxxx modes
                  pMode->ColorI = P3RX_TEXAPP_I_CA;          
                  DISPDBG((DBGLVL,"  I: DiffA" ));                     
              }                                                       
              else                                                    
              {                                                       
                  DISPDBG((ERRLVL,"ERROR: Invalid TA ColorArgument"));
                  SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );
              }                                                       
              break;                                                  
          case D3DTA_CURRENT:                                         
              if ( (num) == 2 )                                       
              {                                                       
                  if (dwArg & D3DTA_ALPHAREPLICATE)   
                  {                                                   
                      pMode->ColorB = P3RX_TEXAPP_B_TA;          
                      DISPDBG((DBGLVL,"  CurrA" ));                        
                  }                                                   
                  else                                                
                  {                                                   
                      pMode->ColorB = P3RX_TEXAPP_B_TC;          
                      DISPDBG((DBGLVL,"  CurrC" ));                        
                  }                                                   
              }                                                       
              else                                                    
              {                                                       
                  // Can't do.
                  DISPDBG((ERRLVL,"ERROR: Invalid TA ColorArgument"));
                  SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );
              }                                                       
              break;                                                  
          case D3DTA_TFACTOR:                                         
              if ( (num) == 1 )                                       
              {                                                       
                  if ( (dwArg & D3DTA_ALPHAREPLICATE) != 0 )      
                  {                                               
                      pMode->ColorA = P3RX_TEXAPP_A_KA;      
                      DISPDBG((DBGLVL,"  TfactA" ));                   
                  }                                               
                  else                                            
                  {                                               
                      pMode->ColorA = P3RX_TEXAPP_A_KC;      
                      DISPDBG((DBGLVL,"  TfactC" ));                   
                  }                                               
                  // Set up the I input for MODULATExxx_ADDxxx modes. 
                  pMode->ColorI = P3RX_TEXAPP_I_KA;          
                  DISPDBG((DBGLVL,"  I: TfactA" ));                    
              }                                                       
              else                                                    
              {                           
                  if ( (num) != 2)
                  {
                      DISPDBG((ERRLVL," ** SETTAARG: num must be 1 or 2"));    
                  }
                  
                  if (dwArg & D3DTA_ALPHAREPLICATE)   
                  {                                                   
                      pMode->ColorB = P3RX_TEXAPP_B_KA;          
                      DISPDBG((DBGLVL,"  TfactA" ));                       
                  }                                                   
                  else                                                
                  {                                                   
                      pMode->ColorB = P3RX_TEXAPP_B_KC;          
                      DISPDBG((DBGLVL,"  TfactC" ));                       
                  }                                                   
              }                                                       
              break;                                                  
          default:                                                    
              DISPDBG((ERRLVL,"ERROR: Unknown TA ColorArgument"));
              SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_ARG );        
              break;                                                  
      }                                                               
      if ( (dwArg & D3DTA_COMPLEMENT) != 0 )                          
      {                                                               
          // Can't do COMPLEMENT on the args.
          DISPDBG((ERRLVL,"ERROR: Can't do COMPLEMENT in TA unit"));
          SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_ARG_HERE );            
      }                                                               
  } // SETTAARG_COLOR
  
  //-----------------------------------------------------------------------------
  //
  // SETOP
  //
  // Note - SETOP must be done after SETARG for DISABLE to work.
  //
  //-----------------------------------------------------------------------------
  void 
  SETOP(
      P3_D3DCONTEXT *pContext, 
      struct TextureCompositeRGBAMode* pMode, 
      DWORD dwOperation, 
      DWORD iD3DStage, 
      DWORD iChipStageNo, 
      BOOL bIsAlpha)
  {                                                                   
      pMode->Enable = __PERMEDIA_ENABLE;                                 
      pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;                
      pMode->InvertI = __PERMEDIA_DISABLE;                               
      pMode->A = P3RX_TEXCOMP_ARG1;                                   
      pMode->B = P3RX_TEXCOMP_ARG2;                                   
      switch (dwOperation)                                            
      {                                                               
          case D3DTOP_DISABLE:                                        
              if ( bIsAlpha )                                         
              {                                                       
                  // Just pass through "current"
                  pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A;   
                  if ( iChipStageNo == 0 )                            
                  {                                                   
                      if ( pContext->bBumpmapEnabled )                
                      {                                               
                          // Embossing is on.
                          pMode->Arg1 = P3RX_TEXCOMP_HEIGHTA;    
                      }                                               
                      else                                            
                      {                                               
                          // Current = diffuse in stage0.
                          pMode->Arg1 = P3RX_TEXCOMP_CA;         
                      }                                               
                  }                                                   
                  else                                                
                  {                                                   
                      if ( pContext->bStage0DotProduct )              
                      {                                               
                          pMode->Arg1 = P3RX_TEXCOMP_SUM;        
                      }                                               
                      else                                            
                      {                                               
                          pMode->Arg1 = P3RX_TEXCOMP_OA;         
                      }                                               
                  }                                                   
              }                                                       
              else                                                    
              {                                                       
                  DISPDBG((ERRLVL,"SETOP: Colour op was DISABLE"
                                       " - should never have got here."));
              }                                                       
              break;                                                  
              
          case D3DTOP_SELECTARG1:                                     
              DISPDBG((DBGLVL,"  D3DTOP_SELECTARG1"));                     
              pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A;       
              break;                                                  
              
          case D3DTOP_SELECTARG2:                                     
              DISPDBG((DBGLVL,"  D3DTOP_SELECTARG2"));                     
              pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A; // No Pass B  
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              break;                                                  
              
          case D3DTOP_MODULATE:                                       
              DISPDBG((DBGLVL,"  D3DTOP_MODULATE"));                       
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;  
              break;                                                  
              
          case D3DTOP_MODULATE2X:                                     
              DISPDBG((DBGLVL,"  D3DTOP_MODULATE2X"));                     
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;  
              pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_TWO;        
              break;                                                  
              
          case D3DTOP_MODULATE4X:                                     
              DISPDBG((DBGLVL,"  D3DTOP_MODULATE4X"));                     
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;  
              pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_FOUR;       
              break;                                                  
              
          case D3DTOP_ADD:                                            
              DISPDBG((DBGLVL,"  D3DTOP_ADD"));                            
              pMode->Operation = P3RX_TEXCOMP_OPERATION_ADD_AB;       
              break;                                                  
              
          case D3DTOP_ADDSIGNED:                                      
              DISPDBG((DBGLVL,"  D3DTOP_ADDSIGNED"));                      
              pMode->Operation = P3RX_TEXCOMP_OPERATION_ADDSIGNED_AB; 
              break;                                                  
              
          case D3DTOP_ADDSIGNED2X:                                    
              DISPDBG((DBGLVL,"  D3DTOP_ADDSIGNED2X"));                    
              pMode->Operation = P3RX_TEXCOMP_OPERATION_ADDSIGNED_AB; 
              pMode->Scale = P3RX_TEXCOMP_OPERATION_SCALE_TWO;        
              break;                                                  
              
          case D3DTOP_SUBTRACT:                                       
              DISPDBG((DBGLVL,"  D3DTOP_SUBTRACT"));                       
              pMode->Operation = P3RX_TEXCOMP_OPERATION_SUBTRACT_AB;  
              break;                                                  
              
          case D3DTOP_ADDSMOOTH:                                      
              DISPDBG((DBGLVL,"  D3DTOP_ADDSMOOTH"));                      
              pMode->Operation = P3RX_TEXCOMP_OPERATION_ADD_AB_SUB_MODULATE_AB;
              break;                                                  
              
          case D3DTOP_BLENDDIFFUSEALPHA:                              
              DISPDBG((DBGLVL,"  D3DTOP_BLENDDIFFUSEALPHA"));              
              pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              pMode->I = P3RX_TEXCOMP_I_CA;                           
              break;                                                  
              
          case D3DTOP_BLENDTEXTUREALPHA:                              
              DISPDBG((DBGLVL,"  D3DTOP_BLENDTEXTUREALPHA"));              
              pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              pMode->I = ( pContext->iStageTex[iD3DStage] == 0 ) ? 
                                                          P3RX_TEXCOMP_I_T0A : 
                                                          P3RX_TEXCOMP_I_T1A; 
              DISPDBG((DBGLVL,"    alpha: Tex%dA", pContext->iStageTex[iD3DStage] ));  
              break;                                                  
              
          case D3DTOP_BLENDFACTORALPHA:                               
              DISPDBG((DBGLVL,"  D3DTOP_BLENDFACTORALPHA"));               
              pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              pMode->I = P3RX_TEXCOMP_I_FA;                           
              break;                                                  
              
          case D3DTOP_BLENDCURRENTALPHA:                              
              DISPDBG((DBGLVL,"  D3DTOP_BLENDCURRENTALPHA"));              
              pMode->Operation = P3RX_TEXCOMP_OPERATION_LERP_ABI;     
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              pMode->I = P3RX_TEXCOMP_I_OA;                           
              break;                                                  
              
          case D3DTOP_BLENDTEXTUREALPHAPM:                            
              DISPDBG((DBGLVL,"  D3DTOP_BLENDTEXTUREALPHAPM"));            
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B;    
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              pMode->I = ( pContext->iStageTex[iD3DStage] == 0 ) ? 
                                                             P3RX_TEXCOMP_I_T0A : 
                                                             P3RX_TEXCOMP_I_T1A; 
              DISPDBG((DBGLVL,"    alpha: Tex%dA", pContext->iStageTex[iD3DStage] ));  
              pMode->InvertI = __PERMEDIA_ENABLE;                        
              break;                                                  
              
          case D3DTOP_PREMODULATE:                                                                            
              DISPDBG((DBGLVL,"  D3DTOP_PREMODULATE"));                                                            
              // result = current_tex * next_stage_tex - ignore arguments.
              if ( ( pContext->iStageTex[iD3DStage] != -1 ) && 
                   ( pContext->iStageTex[iD3DStage+1] != -1 ) )                       
              {                                                                                               
                  pMode->Arg1 = ( pContext->iStageTex[iD3DStage] == 0 ) ? 
                                                              P3RX_TEXCOMP_T0C : 
                                                              P3RX_TEXCOMP_T1C;            
                  DISPDBG((DBGLVL,"    Arg1: Tex%d", pContext->iStageTex[iD3DStage] ));                                        
                  pMode->Arg2 = ( pContext->iStageTex[iD3DStage+1] == 0 ) ? 
                                                              P3RX_TEXCOMP_T0C : 
                                                              P3RX_TEXCOMP_T1C;      
                  DISPDBG((DBGLVL,"    Arg2: Tex%d", pContext->iStageTex[iD3DStage+1] ));                                  
              }                                                                                               
              else                                                                                            
              {                                                                                               
                  // Not enough textures
                  DISPDBG((ERRLVL,"** SETOP: PREMODULATE didn't have two "
                                       "textures to play with."));
                  if ( bIsAlpha )                                                                             
                  {                                                                                           
                      SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_OP_HERE );                                         
                  }                                                                                           
                  else                                                                                        
                  {                                                                                           
                      SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_OP_HERE );                                         
                  }                                                                                           
                  pMode->Arg1 = P3RX_TEXCOMP_CC;                                                              
                  pMode->Arg2 = P3RX_TEXCOMP_CC;                                                              
              }                                                                                               
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;                                          
              pMode->A = P3RX_TEXCOMP_ARG2;                                                                   
              pMode->B = P3RX_TEXCOMP_ARG1;                                                                   
              break;                                                                                          
              
          case D3DTOP_MODULATEALPHA_ADDCOLOR:                         
              DISPDBG((DBGLVL,"  D3DTOP_MODULATEALPHA_ADDCOLOR"));         
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B; 
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              // I input set up by SETARG
              break;                                                  
              
          case D3DTOP_MODULATECOLOR_ADDALPHA:                         
              DISPDBG((DBGLVL,"  D3DTOP_MODULATECOLOR_ADDALPHA"));         
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB_ADD_I; 
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              // I input set up by SETARG
              break;                                                  
              
          case D3DTOP_MODULATEINVALPHA_ADDCOLOR:                      
              DISPDBG((DBGLVL,"  D3DTOP_MODULATEINVALPHA_ADDCOLOR"));      
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B; 
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              // I input set up by SETARG
              pMode->InvertI = 1 - pMode->InvertI;                    
              break;                                                  
              
          case D3DTOP_MODULATEINVCOLOR_ADDALPHA:                      
              DISPDBG((DBGLVL,"  D3DTOP_MODULATEINVCOLOR_ADDALPHA"));      
              pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB_ADD_I; 
              pMode->A = P3RX_TEXCOMP_ARG2;                           
              pMode->B = P3RX_TEXCOMP_ARG1;                           
              pMode->InvertArg1 = 1 - pMode->InvertArg1;              
              // I input set up by SETARG
              break;                                                  
              
          case D3DTOP_DOTPRODUCT3:                                    
              DISPDBG((DBGLVL,"  D3DTOP_DOTPRODUCT3"));                    
              if ( iChipStageNo == 0 )                                
              {                                                       
                  pMode->Operation = P3RX_TEXCOMP_OPERATION_MODULATE_SIGNED_AB;   
                  // Signal that the special input to stage 1 is needed.
                  pContext->bStage0DotProduct = TRUE;                         
              }                                                       
              else                                                    
              {                                                       
                  // Can't do stage 1 dotproduct. Fail.
                  DISPDBG((ERRLVL,"** SETOP: Can't do DOTPRODUCT3 in second stage."));
                  if ( bIsAlpha )                                     
                  {                                                   
                      SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_OP_HERE ); 
                  }                                                   
                  else                                                
                  {                                                   
                      SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_OP_HERE ); 
                  }                                                   
                  pMode->Operation = P3RX_TEXCOMP_OPERATION_PASS_A;   
              }                                                       
              break;                                                  
              
          case D3DTOP_BUMPENVMAP:                                     
          case D3DTOP_BUMPENVMAPLUMINANCE:                            
              DISPDBG((ERRLVL,"** SETOP: Unsupported operation.")); 
              if ( bIsAlpha )                                         
              {                                                       
                  SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_ALPHA_OP );       
              }                                                       
              else                                                    
              {                                                       
                  SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_COLOR_OP );       
              }                                                       
              break;                                                  
              
          default:                                                    
              DISPDBG((ERRLVL,"** SETOP: Unknown operation."));
              if ( bIsAlpha )                                         
              {                                                       
                  SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_OP );         
              }                                                       
              else                                                    
              {                                                       
                  SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_OP );         
              }                                                       
              break;                                                  
      } // switch                                                              
  } // SETOP
  
  //-----------------------------------------------------------------------------
  //
  // SETTAOP
  //
  // Must be done after SETTAARG to set up DISABLE properly.
  //
  //-----------------------------------------------------------------------------
  void 
  SETTAOP(
      P3_D3DCONTEXT *pContext, 
      struct TextureApplicationMode* pMode, 
      DWORD dwOperand, 
      BOOL bIsAlpha,    
      DWORD iD3DStage, 
      DWORD iChipStageNo)
  {                     
      DWORD dwInvertI, dwOperation, dwI = 0, dwA = 0;
      BOOL bOperation = FALSE,
           bI         = FALSE,
           bA         = FALSE;
  
      pMode->Enable = __PERMEDIA_ENABLE;                                         
      dwInvertI = __PERMEDIA_DISABLE;                             
      switch (dwOperand)                                                    
      {                                                                       
          case D3DTOP_DISABLE:                                                
              if ( bIsAlpha )                                                 
              {                                                               
                  // Just pass through "current"
                  dwOperation = P3RX_TEXAPP_OPERATION_PASS_A;  
                  bOperation = TRUE;
                  dwA = P3RX_TEXAPP_A_CA;                
                  bA = TRUE;
              }                                                               
              else                                                            
              {                                                               
                  DISPDBG((ERRLVL,"SETTAOP: Colour op was DISABLE "      
                               " should never have got here."));
              }                                                               
              break;                                                          
          case D3DTOP_SELECTARG1:                                             
              DISPDBG((DBGLVL,"  D3DTOP_SELECTARG1"));                             
              dwOperation = P3RX_TEXAPP_OPERATION_PASS_A;   
              bOperation = TRUE;
              break;                                                          
          case D3DTOP_SELECTARG2:                                             
              DISPDBG((DBGLVL,"  D3DTOP_SELECTARG2"));                             
              dwOperation = P3RX_TEXAPP_OPERATION_PASS_B;   
              bOperation = TRUE;
              break;                                                          
          case D3DTOP_MODULATE:                                               
              DISPDBG((DBGLVL,"  D3DTOP_MODULATE"));                               
              dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_AB; 
              bOperation = TRUE;
              break;                                                          
          case D3DTOP_ADD:                                                    
              DISPDBG((DBGLVL,"  D3DTOP_ADD"));                                    
              dwOperation = P3RX_TEXAPP_OPERATION_ADD_AB;    
              bOperation = TRUE;
              break;                                                          
          case D3DTOP_BLENDDIFFUSEALPHA:                                      
              DISPDBG((DBGLVL,"  D3DTOP_BLENDDIFFUSEALPHA"));                      
              dwOperation = P3RX_TEXAPP_OPERATION_LERP_ABI;   
              bOperation = TRUE;
              dwInvertI = 1 - dwInvertI;    
              dwI = P3RX_TEXAPP_I_CA;    
              bI = TRUE;
              break;                                                          
          case D3DTOP_BLENDFACTORALPHA:                                       
              DISPDBG((DBGLVL,"  D3DTOP_BLENDFACTORALPHA"));                       
              dwOperation = P3RX_TEXAPP_OPERATION_LERP_ABI; 
              bOperation = TRUE;
              dwInvertI = 1 - dwInvertI;  
              dwI = P3RX_TEXAPP_I_KA;             
              bI = TRUE;
              break;                                                          
          case D3DTOP_BLENDCURRENTALPHA:                                      
              DISPDBG((DBGLVL,"  D3DTOP_BLENDCURRENTALPHA"));                      
              dwOperation = P3RX_TEXAPP_OPERATION_LERP_ABI;   
              bOperation = TRUE;
              dwInvertI = 1 - dwInvertI;    
              dwI = P3RX_TEXAPP_I_TA;         
              bI = TRUE;
              break;                                                          
          case D3DTOP_MODULATEALPHA_ADDCOLOR:                                 
              DISPDBG((DBGLVL,"  D3DTOP_MODULATEALPHA_ADDCOLOR"));                 
              dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_BI_ADD_A; 
              bOperation = TRUE;
              // I should have been set up by SETTAARG.
              // dwI = P3RX_TEXAPP_I_TA;         
              break;                                                          
          case D3DTOP_MODULATECOLOR_ADDALPHA:                                 
              DISPDBG((DBGLVL,"  D3DTOP_MODULATECOLOR_ADDALPHA"));                 
              dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_AB_ADD_I; 
              bOperation = TRUE;
              // I should have been set up by SETTAARG.
              // dwI = P3RX_TEXAPP_I_TA; 
              break;                                                          
          case D3DTOP_MODULATEINVALPHA_ADDCOLOR:                              
              DISPDBG((DBGLVL,"  D3DTOP_MODULATEINVALPHA_ADDCOLOR"));              
              dwOperation = P3RX_TEXAPP_OPERATION_MODULATE_BI_ADD_A; 
              bOperation = TRUE;
              dwInvertI = 1 - dwInvertI;   
              // I should have been set up by SETTAARG.
              // dwI = P3RX_TEXAPP_I_TA;
              break;                                                          
          case D3DTOP_MODULATE2X:                                             
          case D3DTOP_MODULATE4X:                                             
          case D3DTOP_ADDSIGNED:                                              
          case D3DTOP_ADDSIGNED2X:                                            
          case D3DTOP_SUBTRACT:                                               
          case D3DTOP_ADDSMOOTH:                                              
          case D3DTOP_BLENDTEXTUREALPHA:                                      
          case D3DTOP_BLENDTEXTUREALPHAPM:                                    
          case D3DTOP_PREMODULATE:                                            
          case D3DTOP_MODULATEINVCOLOR_ADDALPHA:                              
          case D3DTOP_DOTPRODUCT3:                                            
              DISPDBG((ERRLVL,"** SETTAOP: Unsupported operation in TA unit."));
              if ( bIsAlpha )                                                 
              {                                                               
                  SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_ALPHA_OP_HERE );             
              }                                                               
              else                                                            
              {                                                               
                  SET_BLEND_ERROR ( pContext,  BSF_CANT_USE_COLOR_OP_HERE );             
              }                                                               
              break;                                                          
          case D3DTOP_BUMPENVMAP:                                             
          case D3DTOP_BUMPENVMAPLUMINANCE:                                    
              DISPDBG((ERRLVL,"** SETTAOP: Unsupported operation."));
              if ( bIsAlpha )                                                 
              {                                                               
                  SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_ALPHA_OP );               
              }                                                               
              else                                                            
              {                                                               
                  SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_COLOR_OP );               
              }                                                               
              break;                                                          
          default:                                                            
              // What is this?. //azn
              DISPDBG((ERRLVL,"** SETTAOP: Unknown operation."));
              if ( bIsAlpha )                                                 
              {                                                               
                  SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_ALPHA_OP );                 
              }                                                               
              else                                                            
              {                                                               
                  SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_COLOR_OP );                 
              }                                                               
              break;                                                          
      }       
  
      if(bIsAlpha)
      {
          pMode->AlphaInvertI = dwInvertI;
          if (bOperation)
          {
              pMode->AlphaOperation = dwOperation;    
          }
  
          if (bI)
          {
              pMode->AlphaI = dwI;      
          }
  
          if (bA)
          {
              pMode->AlphaA = dwA;          
          }
      }
      else
      {
          pMode->ColorInvertI = dwInvertI;
          
          if (bOperation)
          {
              pMode->ColorOperation = dwOperation;    
          }
  
          if (bI)
          {
              pMode->ColorI = dwI;  
          }
          
          if (bA)
          {
              pMode->ColorA = dwA;          
          }
      }
  } // SETTAOP
  
  //-----------------------------------------------------------------------------
  //
  // _D3DDisplayWholeTSSPipe
  //
  // Dumps the whole TSS pipe state out to the debug stream.
  // Also dumps fog, specular and alpha-blend state out.
  //
  //-----------------------------------------------------------------------------
  void _D3DDisplayWholeTSSPipe ( P3_D3DCONTEXT* pContext, int iDebugNumber )
  {
  #if DBG
      int i;
      char *pszTemp, *pszTempPre, *pszTempPost;
      char *pszOp;
      char *pszArg1Pre, *pszArg1, *pszArg1Post;
      char *pszArg2Pre, *pszArg2, *pszArg2Post;
      char *pszSrc, *pszDest;
      P3_SURF_INTERNAL* pTexture;
      P3_SURF_FORMAT* pFormatSurface;
  
      DISPDBG((iDebugNumber,"TSS dump:"));
  
      #define SWITCH_ARG(prefix,opname) \
              case prefix##_##opname##: pszTemp = #opname; break
  
      i = 0;
      while ( ( i < 8 ) && ( TSSTATE ( i, D3DTSS_COLOROP ) != D3DTOP_DISABLE ) )
      {
  
          switch ( TSSTATE ( i, D3DTSS_COLOROP ) )
          {
              SWITCH_ARG ( D3DTOP,DISABLE );
              SWITCH_ARG ( D3DTOP,SELECTARG1 );
              SWITCH_ARG ( D3DTOP,SELECTARG2 );
              SWITCH_ARG ( D3DTOP,MODULATE );
              SWITCH_ARG ( D3DTOP,MODULATE2X );
              SWITCH_ARG ( D3DTOP,MODULATE4X );
              SWITCH_ARG ( D3DTOP,ADD );
              SWITCH_ARG ( D3DTOP,ADDSIGNED );
              SWITCH_ARG ( D3DTOP,ADDSIGNED2X );
              SWITCH_ARG ( D3DTOP,SUBTRACT );
              SWITCH_ARG ( D3DTOP,ADDSMOOTH );
              SWITCH_ARG ( D3DTOP,BLENDDIFFUSEALPHA );
              SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHA );
              SWITCH_ARG ( D3DTOP,BLENDFACTORALPHA );
              SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHAPM );
              SWITCH_ARG ( D3DTOP,BLENDCURRENTALPHA );
              SWITCH_ARG ( D3DTOP,PREMODULATE );
              SWITCH_ARG ( D3DTOP,MODULATEALPHA_ADDCOLOR );
              SWITCH_ARG ( D3DTOP,MODULATECOLOR_ADDALPHA );
              SWITCH_ARG ( D3DTOP,MODULATEINVALPHA_ADDCOLOR );
              SWITCH_ARG ( D3DTOP,MODULATEINVCOLOR_ADDALPHA );
              SWITCH_ARG ( D3DTOP,BUMPENVMAP );
              SWITCH_ARG ( D3DTOP,BUMPENVMAPLUMINANCE );
              SWITCH_ARG ( D3DTOP,DOTPRODUCT3 );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          pszOp = pszTemp;
  
          switch ( TSSTATESELECT ( i, D3DTSS_COLORARG1 ) )
          {
              SWITCH_ARG ( D3DTA,DIFFUSE );
              SWITCH_ARG ( D3DTA,CURRENT );
              SWITCH_ARG ( D3DTA,TEXTURE );
              SWITCH_ARG ( D3DTA,TFACTOR );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          if ( ( TSSTATE ( i, D3DTSS_COLORARG1 ) & D3DTA_ALPHAREPLICATE ) != 0 )
          {
              pszTempPost = ".A";
          }
          else
          {
              pszTempPost = ".C";
          }
          if ( ( TSSTATE ( i, D3DTSS_COLORARG1 ) & D3DTA_COMPLEMENT ) != 0 )
          {
              pszTempPre = "1-";
          }
          else
          {
              pszTempPre = "";
          }
          pszArg1Pre = pszTempPre;
          pszArg1Post = pszTempPost;
          pszArg1 = pszTemp;
  
  
          switch ( TSSTATESELECT ( i, D3DTSS_COLORARG2 ) )
          {
              SWITCH_ARG ( D3DTA,DIFFUSE );
              SWITCH_ARG ( D3DTA,CURRENT );
              SWITCH_ARG ( D3DTA,TEXTURE );
              SWITCH_ARG ( D3DTA,TFACTOR );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          if ( ( TSSTATE ( i, D3DTSS_COLORARG2 ) & D3DTA_ALPHAREPLICATE ) != 0 )
          {
              pszTempPost = ".A";
          }
          else
          {
              pszTempPost = ".C";
          }
          if ( ( TSSTATE ( i, D3DTSS_COLORARG2 ) & D3DTA_COMPLEMENT ) != 0 )
          {
              pszTempPre = "1-";
          }
          else
          {
              pszTempPre = "";
          }
          pszArg2Pre = pszTempPre;
          pszArg2Post = pszTempPost;
          pszArg2 = pszTemp;
  
  
          DISPDBG((iDebugNumber," C%i: %s: %s%s%s, %s%s%s",
                   i, pszOp, pszArg1Pre, pszArg1, pszArg1Post, 
                   pszArg2Pre, pszArg2, pszArg2Post ));
  
  
          switch ( TSSTATE ( i, D3DTSS_ALPHAOP ) )
          {
              SWITCH_ARG ( D3DTOP,DISABLE );
              SWITCH_ARG ( D3DTOP,SELECTARG1 );
              SWITCH_ARG ( D3DTOP,SELECTARG2 );
              SWITCH_ARG ( D3DTOP,MODULATE );
              SWITCH_ARG ( D3DTOP,MODULATE2X );
              SWITCH_ARG ( D3DTOP,MODULATE4X );
              SWITCH_ARG ( D3DTOP,ADD );
              SWITCH_ARG ( D3DTOP,ADDSIGNED );
              SWITCH_ARG ( D3DTOP,ADDSIGNED2X );
              SWITCH_ARG ( D3DTOP,SUBTRACT );
              SWITCH_ARG ( D3DTOP,ADDSMOOTH );
              SWITCH_ARG ( D3DTOP,BLENDDIFFUSEALPHA );
              SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHA );
              SWITCH_ARG ( D3DTOP,BLENDFACTORALPHA );
              SWITCH_ARG ( D3DTOP,BLENDTEXTUREALPHAPM );
              SWITCH_ARG ( D3DTOP,BLENDCURRENTALPHA );
              SWITCH_ARG ( D3DTOP,PREMODULATE );
              SWITCH_ARG ( D3DTOP,MODULATEALPHA_ADDCOLOR );
              SWITCH_ARG ( D3DTOP,MODULATECOLOR_ADDALPHA );
              SWITCH_ARG ( D3DTOP,MODULATEINVALPHA_ADDCOLOR );
              SWITCH_ARG ( D3DTOP,MODULATEINVCOLOR_ADDALPHA );
              SWITCH_ARG ( D3DTOP,BUMPENVMAP );
              SWITCH_ARG ( D3DTOP,BUMPENVMAPLUMINANCE );
              SWITCH_ARG ( D3DTOP,DOTPRODUCT3 );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          pszOp = pszTemp;
  
  
          switch ( TSSTATESELECT ( i, D3DTSS_ALPHAARG1 ) )
          {
              SWITCH_ARG ( D3DTA,DIFFUSE );
              SWITCH_ARG ( D3DTA,CURRENT );
              SWITCH_ARG ( D3DTA,TEXTURE );
              SWITCH_ARG ( D3DTA,TFACTOR );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          if ( ( TSSTATE ( i, D3DTSS_ALPHAARG1 ) & D3DTA_ALPHAREPLICATE ) != 0 )
          {
              // Alpharep doesn't mean much in the alpha channel.
              pszTempPost = ".AR???";
          }
          else
          {
              pszTempPost = ".A";
          }
          if ( ( TSSTATE ( i, D3DTSS_ALPHAARG1 ) & D3DTA_COMPLEMENT ) != 0 )
          {
              pszTempPre = "1-";
          }
          else
          {
              pszTempPre = "";
          }
          pszArg1Pre = pszTempPre;
          pszArg1Post = pszTempPost;
          pszArg1 = pszTemp;
  
  
          switch ( TSSTATESELECT ( i, D3DTSS_ALPHAARG2 ) )
          {
              SWITCH_ARG ( D3DTA,DIFFUSE );
              SWITCH_ARG ( D3DTA,CURRENT );
              SWITCH_ARG ( D3DTA,TEXTURE );
              SWITCH_ARG ( D3DTA,TFACTOR );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          if ( ( TSSTATE ( i, D3DTSS_ALPHAARG2 ) & D3DTA_ALPHAREPLICATE ) != 0 )
          {
              pszTempPost = ".AR???";
          }
          else
          {
              pszTempPost = ".A";
          }
          if ( ( TSSTATE ( i, D3DTSS_ALPHAARG2 ) & D3DTA_COMPLEMENT ) != 0 )
          {
              pszTempPre = "1-";
          }
          else
          {
              pszTempPre = "";
          }
          pszArg2Pre = pszTempPre;
          pszArg2Post = pszTempPost;
          pszArg2 = pszTemp;
  
          DISPDBG((iDebugNumber," A%i: %s: %s%s%s, %s%s%s", 
                      i, pszOp, pszArg1Pre, pszArg1, pszArg1Post, 
                         pszArg2Pre, pszArg2, pszArg2Post ));
  
  
          if ( TSSTATE ( i, D3DTSS_TEXTUREMAP ) != 0 )
          {
              char szTemp[4];
              // Setup texture 0.
              pTexture = GetSurfaceFromHandle(pContext, 
                                              TSSTATE(i, D3DTSS_TEXTUREMAP));
              if ( pTexture == NULL )
              {
                  DISPDBG((iDebugNumber," Tex%i: 0x%x, TCI: %i, INVALID TEXTURE",
                           i, TSSTATE ( i, D3DTSS_TEXTUREMAP ), 
                              TSSTATE ( i, D3DTSS_TEXCOORDINDEX ) ));
              }
              else
              {
                  pFormatSurface = pTexture->pFormatSurface;
                  ASSERTDD ( pFormatSurface != NULL, 
                             "** _D3DDisplayWholeTSSPipe: "
                             "Surface had NULL format!" );
  
                  // Find the filtering mode.
                  szTemp[3] = '\0';
                  switch ( TSSTATE ( i, D3DTSS_MINFILTER ) )
                  {
                      case D3DTFN_POINT:
                          szTemp[0] = 'P';
                          break;
                      case D3DTFN_LINEAR:
                          szTemp[0] = 'L';
                          break;
                      case D3DTFN_ANISOTROPIC:
                          szTemp[0] = 'A';
                          break;
                      default:
                          szTemp[0] = '?';
                          break;
                  }
                  switch ( TSSTATE ( i, D3DTSS_MIPFILTER ) )
                  {
                      case D3DTFP_NONE:
                          szTemp[1] = 'x';
                          break;
                      case D3DTFP_POINT:
                          szTemp[1] = 'P';
                          break;
                      case D3DTFP_LINEAR:
                          szTemp[1] = 'L';
                          break;
                      default:
                          szTemp[1] = '?';
                          break;
                  }
                  switch ( TSSTATE ( i, D3DTSS_MAGFILTER ) )
                  {
                      case D3DTFG_POINT:
                          szTemp[2] = 'P';
                          break;
                      case D3DTFG_LINEAR:
                          szTemp[2] = 'L';
                          break;
                      case D3DTFG_FLATCUBIC:
                          szTemp[2] = 'F';
                          break;
                      case D3DTFG_GAUSSIANCUBIC:
                          szTemp[2] = 'G';
                          break;
                      case D3DTFG_ANISOTROPIC:
                          szTemp[2] = 'A';
                          break;
                      default:
                          szTemp[2] = '?';
                          break;
                  }
                  
                  DISPDBG((iDebugNumber," Tex%i: 0x%x, TCI: %i, %s:%dx%d %s", 
                           i, TSSTATE ( i, D3DTSS_TEXTUREMAP ), 
                              TSSTATE ( i, D3DTSS_TEXCOORDINDEX ), 
                              pFormatSurface->pszStringFormat, 
                              pTexture->wWidth, 
                              pTexture->wHeight, szTemp ));
              }
          }
          else
          {
              DISPDBG((iDebugNumber," Tex%i: NULL, TCI: %i", 
                       i, TSSTATE ( i, D3DTSS_TEXCOORDINDEX ) ));
          }
          
  
          i++;
      }
  
      // Alpha-test.
      if ( pContext->RenderStates[D3DRENDERSTATE_ALPHATESTENABLE] != 0 )
      {
          switch ( pContext->RenderStates[D3DRENDERSTATE_ALPHAFUNC] )
          {
              SWITCH_ARG ( D3DCMP,NEVER );
              SWITCH_ARG ( D3DCMP,LESS );
              SWITCH_ARG ( D3DCMP,EQUAL );
              SWITCH_ARG ( D3DCMP,LESSEQUAL );
              SWITCH_ARG ( D3DCMP,GREATER );
              SWITCH_ARG ( D3DCMP,NOTEQUAL );
              SWITCH_ARG ( D3DCMP,GREATEREQUAL );
              SWITCH_ARG ( D3DCMP,ALWAYS );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          DISPDBG((iDebugNumber,"Alpha-test: %s:0x%x.", 
                   pszTemp, pContext->RenderStates[D3DRENDERSTATE_ALPHAREF] ));
      }
      else
      {
          DISPDBG((iDebugNumber,"No alpha-test."));
      }
  
  
      // Alpha-blend.
      if ( pContext->RenderStates[D3DRENDERSTATE_BLENDENABLE] != 0 )
      {
          switch ( pContext->RenderStates[D3DRENDERSTATE_SRCBLEND] )
          {
              SWITCH_ARG ( D3DBLEND,ZERO );
              SWITCH_ARG ( D3DBLEND,ONE );
              SWITCH_ARG ( D3DBLEND,SRCCOLOR );
              SWITCH_ARG ( D3DBLEND,INVSRCCOLOR );
              SWITCH_ARG ( D3DBLEND,SRCALPHA );
              SWITCH_ARG ( D3DBLEND,INVSRCALPHA );
              SWITCH_ARG ( D3DBLEND,DESTALPHA );
              SWITCH_ARG ( D3DBLEND,INVDESTALPHA );
              SWITCH_ARG ( D3DBLEND,DESTCOLOR );
              SWITCH_ARG ( D3DBLEND,INVDESTCOLOR );
              SWITCH_ARG ( D3DBLEND,SRCALPHASAT );
              SWITCH_ARG ( D3DBLEND,BOTHSRCALPHA );
              SWITCH_ARG ( D3DBLEND,BOTHINVSRCALPHA );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          pszSrc = pszTemp;
  
          switch ( pContext->RenderStates[D3DRENDERSTATE_DESTBLEND] )
          {
              SWITCH_ARG ( D3DBLEND,ZERO );
              SWITCH_ARG ( D3DBLEND,ONE );
              SWITCH_ARG ( D3DBLEND,SRCCOLOR );
              SWITCH_ARG ( D3DBLEND,INVSRCCOLOR );
              SWITCH_ARG ( D3DBLEND,SRCALPHA );
              SWITCH_ARG ( D3DBLEND,INVSRCALPHA );
              SWITCH_ARG ( D3DBLEND,DESTALPHA );
              SWITCH_ARG ( D3DBLEND,INVDESTALPHA );
              SWITCH_ARG ( D3DBLEND,DESTCOLOR );
              SWITCH_ARG ( D3DBLEND,INVDESTCOLOR );
              SWITCH_ARG ( D3DBLEND,SRCALPHASAT );
              SWITCH_ARG ( D3DBLEND,BOTHSRCALPHA );
              SWITCH_ARG ( D3DBLEND,BOTHINVSRCALPHA );
              default:
                  pszTemp = "Unknown";
                  break;
          }
          pszDest = pszTemp;
          DISPDBG((iDebugNumber,"Blend %s:%s", pszSrc, pszDest));
      }
      else
      {
          DISPDBG((iDebugNumber,"No alpha-blend."));
      }
  
      #undef SWITCH_ARG
  
  #endif //DBG
  } // _D3DDisplayWholeTSSPipe
  
  //-----------------------------------------------------------------------------
  //
  // __TXT_TranslateToChipBlendMode
  //
  // Translates the blend mode from D3D into what the chip understands
  //
  //-----------------------------------------------------------------------------
  void 
  __TXT_TranslateToChipBlendMode( 
      P3_D3DCONTEXT *pContext, 
      TexStageState* pState,
      P3_SOFTWARECOPY* pSoftP3RX, 
      int iTSStage, 
      int iChipStageNo )
  {
      struct TextureCompositeRGBAMode* pColorMode;
      struct TextureCompositeRGBAMode* pAlphaMode;
      struct TextureApplicationMode* pTAMode;
  
      switch(iChipStageNo)
      {
          default:
              DISPDBG((ERRLVL,"ERROR: Invalid texture stage!"));
              // Fall through and treat as #0 in order not to AV anything
          case 0:
              pColorMode = &pSoftP3RX->P3RXTextureCompositeColorMode0;
              pAlphaMode = &pSoftP3RX->P3RXTextureCompositeAlphaMode0;
              pTAMode = NULL;
              break;
          case 1:
              pColorMode = &pSoftP3RX->P3RXTextureCompositeColorMode1;
              pAlphaMode = &pSoftP3RX->P3RXTextureCompositeAlphaMode1;
              pTAMode = NULL;
              break;
          case 2:
              pColorMode = NULL;
              pAlphaMode = NULL;
              pTAMode = &pSoftP3RX->P3RXTextureApplicationMode;
              break;
  
      }
  
      DISPDBG((DBGLVL,"*** In __TXT_TranslateToChipBlendMode: "
                 "Chip Stage %d, D3D TSS Stage %d", 
                 iChipStageNo, iTSStage ));
  
      // Setup the arguments
      if ( ( iChipStageNo == 0 ) || ( iChipStageNo == 1 ) )
      {
          // Texture composite unit.
          DISPDBG((DBGLVL,"TexComp%d:", iChipStageNo ));
          DISPDBG((DBGLVL,"Arg1:" ));
          
          SETARG(pContext,
                 pColorMode, 
                 pState->m_dwVal[D3DTSS_COLORARG1], 
                 1, 
                 IS_COLOR_ARG, 
                 iTSStage, 
                 iChipStageNo);
                 
          SETARG(pContext,
                 pAlphaMode, 
                 pState->m_dwVal[D3DTSS_ALPHAARG1], 
                 1, 
                 IS_ALPHA_ARG, 
                 iTSStage, 
                 iChipStageNo);
  
          DISPDBG((DBGLVL,"Arg2:" ));
          
          SETARG(pContext,
                 pColorMode, 
                 pState->m_dwVal[D3DTSS_COLORARG2], 
                 2, 
                 IS_COLOR_ARG, 
                 iTSStage, 
                 iChipStageNo);
                 
          SETARG(pContext,
                 pAlphaMode, 
                 pState->m_dwVal[D3DTSS_ALPHAARG2], 
                 2, 
                 IS_ALPHA_ARG, 
                 iTSStage, 
                 iChipStageNo);
  
          DISPDBG((DBGLVL,"Op:" ));
          SETOP(pContext, 
                pColorMode, 
                pState->m_dwVal[D3DTSS_COLOROP], 
                iTSStage, 
                iChipStageNo, 
                IS_COLOR_ARG);
                
          SETOP(pContext, 
                pAlphaMode, 
                pState->m_dwVal[D3DTSS_ALPHAOP], 
                iTSStage, 
                iChipStageNo, 
                IS_ALPHA_ARG);
      }
      else if ( iChipStageNo == 2 ) 
      {
          DISPDBG((DBGLVL,"TexApp:" ));
          DISPDBG((DBGLVL,"Arg1:" ));
          
          SETTAARG_COLOR(pContext, 
                         pTAMode, 
                         pState->m_dwVal[D3DTSS_COLORARG1], 
                         1 );
                   
          if ( ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_DISABLE ) && 
               ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_SELECTARG2 ) )
          {
              SETTAARG_ALPHA(pContext,
                             pTAMode, 
                             pState->m_dwVal[D3DTSS_ALPHAARG1], 1
                             );
          }
  
          DISPDBG((DBGLVL,"Arg2:" ));
          SETTAARG_COLOR(pContext, 
                         pTAMode, 
                         pState->m_dwVal[D3DTSS_COLORARG2], 
                         2 );
  
          if ( ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_DISABLE ) && 
               ( pState->m_dwVal[D3DTSS_ALPHAOP] != D3DTOP_SELECTARG1 ) )
          {
              SETTAARG_ALPHA(pContext, 
                             pTAMode, 
                             pState->m_dwVal[D3DTSS_ALPHAARG2], 
                             2);
          }
  
          DISPDBG((DBGLVL,"Op:" ));
          SETTAOP(pContext,
                  pTAMode, 
                  pState->m_dwVal[D3DTSS_COLOROP], 
                  IS_COLOR_ARG, 
                  iTSStage, 
                  iChipStageNo);
                  
          SETTAOP(pContext,
                  pTAMode, 
                  pState->m_dwVal[D3DTSS_ALPHAOP], 
                  IS_ALPHA_ARG, 
                  iTSStage, 
                  iChipStageNo);
      } 
      else
      {
          DISPDBG(( ERRLVL,"** __TXT_TranslateToChipBlendMode: "
                           "iChipStage must be 0 to 2" ));    
      }
  } // __TXT_TranslateToChipBlendMode
  
  //-----------------------------------------------------------------------------
  //
  // __TXT_ValidateTextureUnitStage
  //
  // Validate the texture which we're trying to set up in stage iChipStage of
  // the hardware, iTSStage of the D3D TSS.
  //
  //-----------------------------------------------------------------------------
  BOOL
  __TXT_ValidateTextureUnitStage(
      P3_D3DCONTEXT* pContext,
      int iChipStage, 
      int iTSStage,
      P3_SURF_INTERNAL* pTexture)
  {
      DWORD dwTexHandle = 
              pContext->TextureStageState[iTSStage].m_dwVal[D3DTSS_TEXTUREMAP];
              
      if( CHECK_SURF_INTERNAL_AND_DDSURFACE_VALIDITY(pTexture) &&
          (pTexture->Location != SystemMemory)                 &&  
          (dwTexHandle != 0)                                     )
      {
          // Texture is valid. Mark pCurrentTexturep[iChipStage] to point
          // to its P3_SURF_INTERNAL structure.
          pContext->pCurrentTexture[iChipStage] = pTexture;
  
          DISPDBG((DBGLVL, "__TXT_ValidateTextureUnitStage: valid texture %x "
                           "(handle %d) for iChipStage= %d iTSStage= %d",
                           pTexture, dwTexHandle, iChipStage, iTSStage));                                                                                    
      }
      else
      {
          // Set texture as invalid & force texturing off
          pContext->bTextureValid = FALSE;
          pContext->pCurrentTexture[iChipStage] = NULL;
          pContext->dwDirtyFlags |= CONTEXT_DIRTY_TEXTURE;
  
          // Setup error if we're asked to validate the TSS setup
          SET_BLEND_ERROR ( pContext,  BSF_INVALID_TEXTURE );
  
          DISPDBG((WRNLVL, "__TXT_ValidateTextureUnitStage: INVALID texture %x "
                           "(handle %d) for iChipStage= %d iTSStage= %d "
                           "Location=%d",
                           pTexture, dwTexHandle, iChipStage, iTSStage,
                           (pTexture !=NULL)?pTexture->Location:0));      
      }
  
      return ( (BOOL)pContext->bTextureValid );
      
  } // __TXT_ValidateTextureUnitStage
  
  //-----------------------------------------------------------------------------
  //
  // __TXT_ConsiderSrcChromaKey
  //
  // Setup chromakeying for a certain texture bound to a certain stage.
  // Note - "stage" is the chip stage, not the D3D stage.
  //-----------------------------------------------------------------------------
  static void
  __TXT_ConsiderSrcChromaKey(
      P3_D3DCONTEXT *pContext, 
      P3_SURF_INTERNAL* pTexture, 
      int stage )
  {
      P3_THUNKEDDATA * pThisDisplay = pContext->pThisDisplay;
      P3_SOFTWARECOPY* pSoftP3RX = &pContext->SoftCopyGlint;
      P3_DMA_DEFS();
  
      if ((pTexture->dwFlagsInt & DDRAWISURF_HASCKEYSRCBLT) &&
          pContext->RenderStates[D3DRENDERSTATE_COLORKEYENABLE])
      {
          DWORD LowerBound = 0x00000000;
          DWORD UpperBound = 0xFFFFFFFF;
          DWORD* pPalEntries = NULL;
          DWORD dwPalFlags = 0;
  
          DISPDBG((DBGLVL,"    Can Chroma Key texture stage %d", stage));
  
          pContext->bCanChromaKey = TRUE;
  
  #if DX7_PALETTETEXTURE
          // Get the palette entries
          if (pTexture->pixFmt.dwFlags & DDPF_PALETTEINDEXED8)
          {
              D3DHAL_DP2UPDATEPALETTE *pPalette = NULL;
          
              pPalette = GetPaletteFromHandle(pContext,
                                              pTexture->dwPaletteHandle);
              if (pPalette)
              {
                  pPalEntries = (LPDWORD)(pPalette + 1);
              }
              else
              {
                  SET_BLEND_ERROR(pContext, BSF_INVALID_TEXTURE);
              }
  
              dwPalFlags = pTexture->dwPaletteFlags;
          }
  #endif        
  
          // Get the correct chroma value for the texture map to send to the chip.
          Get8888ScaledChroma(pThisDisplay, 
                              pTexture->dwFlagsInt, 
                              &pTexture->pixFmt,
                              pTexture->dwCKLow,
                              pTexture->dwCKHigh,
                              &LowerBound, 
                              &UpperBound, 
                              pPalEntries,
                              dwPalFlags & DDRAWIPAL_ALPHA, 
                              FALSE);
  
          P3_DMA_GET_BUFFER_ENTRIES( 8);
  
          // Send the upper and lower bounds for the alpha-map filtering
          if( stage == 0 )
          {
              SEND_P3_DATA(TextureChromaLower0, LowerBound );
              SEND_P3_DATA(TextureChromaUpper0, UpperBound );
              pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable0 = 
                                                              __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureFilterMode.AlphaMapSense0 = 
                                                      P3RX_ALPHAMAPSENSE_INRANGE;
          }
          else
          {
              ASSERTDD ( stage == 1, 
                         "** __TXT_ConsiderSrcChromaKey: stage must be 0 or 1" );
          }
  
          // If we are mipmapping, we need to set up texture1's chromakey as well.
          // If not, then this will be overridden when this gets called for tex1.
          SEND_P3_DATA(TextureChromaLower1, LowerBound );
          SEND_P3_DATA(TextureChromaUpper1, UpperBound );
          pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable1 = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RXTextureFilterMode.AlphaMapSense1 = 
                                                      P3RX_ALPHAMAPSENSE_INRANGE;
  
          P3_DMA_COMMIT_BUFFER();
  
          pSoftP3RX->P3RXTextureFilterMode.AlphaMapFiltering = __PERMEDIA_ENABLE;
      }
      else
      {
          DISPDBG((DBGLVL,"    Can't Chroma Key texture stage %d", stage));
  
          if( stage == 0 )
          {
              pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable0 = 
                                                              __PERMEDIA_DISABLE;
          }
          else
          {
              ASSERTDD ( stage == 1, 
                         "** __TXT_ConsiderSrcChromaKey: stage must be 0 or 1" );
          }
          
          // If we are mipmapping, we need to set up texture1's chromakey (or 
          // lack of it) as well. If not, then this will be overridden when 
          // this gets called for tex1.
          pSoftP3RX->P3RXTextureFilterMode.AlphaMapEnable1 = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureFilterMode.AlphaMapFiltering = __PERMEDIA_DISABLE;
      }
  } // __TXT_ConsiderSrcChromaKey
  
  
  //-----------------------------------------------------------------------------
  //
  // __TXT_SetupTexture
  //
  // This is the new all-singing all-dancing texture setup code.
  // Return is TRUE if setup succeeded, FALSE if it failed (for ValidateDevice)
  // This sets up either texture 0 or texture 1, taking its wrapping, etc,
  // info from iTSStage.
  //
  //-----------------------------------------------------------------------------
  BOOL __TXT_SetupTexture (
          P3_THUNKEDDATA * pThisDisplay,
          int iTexNo,
          int iTSStage,
          P3_D3DCONTEXT* pContext,
          P3_SURF_INTERNAL* pTexture,
          P3_SOFTWARECOPY* pSoftP3RX,
          BOOL bBothTexturesValid,
          P3_MIP_BASES *pMipBases)
  {
      P3_SURF_FORMAT* pFormatSurface;
      int iT0MaxLevel, iT1MaxLevel;
   
      P3_DMA_DEFS();
  
      ASSERTDD ( ( iTexNo >= 0 ) && ( iTexNo <= 1 ), 
                 "**__TXT_SetupTexture: we only have two texture units!" );
  
      if ( pTexture != NULL )
      {
          pFormatSurface = pTexture->pFormatSurface;
      }
      else
      {
          // Suceeded, but should never have got here!
          DISPDBG((ERRLVL,"**__TXT_SetupTexture: should never "
                               "be called with handle of NULL"));
          return ( TRUE );
      }
  
      P3_DMA_GET_BUFFER();
      P3_ENSURE_DX_SPACE(32);
      WAIT_FIFO(32);
  
      // Set up the texture-relevant things.
  
      switch ( iTexNo )
      {
          case 0:
          {
              // Set both bits in case we are mipmapping
  
              pSoftP3RX->P3RXTextureFilterMode.ForceAlphaToOne0 = 
                                              pFormatSurface->bAlpha ? 
                                                      __PERMEDIA_DISABLE : 
                                                      __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureFilterMode.ForceAlphaToOne1 = 
                                              pFormatSurface->bAlpha ? 
                                                      __PERMEDIA_DISABLE : 
                                                      __PERMEDIA_ENABLE;
  
              // D3D UV Wrapping
              if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] 
                                                              & D3DWRAP_U)
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapS = 1;
              }
              else
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapS = 0;
              }
  
              if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] 
                                                              & D3DWRAP_V)
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapT = 1;
              }
              else
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapT = 0;
              }
  
              // U Wrapping
              switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSU ))
              {
                  case D3DTADDRESS_CLAMP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapS = 
                                              __GLINT_TEXADDRESS_WRAP_CLAMP;
                      pSoftP3RX->P3RXTextureIndexMode0.WrapU = 
                                              P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                      break;
                  case D3DTADDRESS_WRAP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapS = 
                                              __GLINT_TEXADDRESS_WRAP_REPEAT;
                      pSoftP3RX->P3RXTextureIndexMode0.WrapU = 
                                              P3RX_TEXINDEXMODE_WRAP_REPEAT;
                      break;
                  case D3DTADDRESS_MIRROR:
                      pSoftP3RX->P3RXTextureCoordMode.WrapS = 
                                              __GLINT_TEXADDRESS_WRAP_MIRROR;
                      pSoftP3RX->P3RXTextureIndexMode0.WrapU = 
                                              P3RX_TEXINDEXMODE_WRAP_MIRROR;
                      break;
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSU!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                      break;
              }
  
              // V Wrapping
              switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSV ))
              {
                  case D3DTADDRESS_CLAMP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapT = 
                                              __GLINT_TEXADDRESS_WRAP_CLAMP;
                      pSoftP3RX->P3RXTextureIndexMode0.WrapV = 
                                              P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                      break;
                  case D3DTADDRESS_WRAP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapT = 
                                              __GLINT_TEXADDRESS_WRAP_REPEAT;
                      pSoftP3RX->P3RXTextureIndexMode0.WrapV = 
                                              P3RX_TEXINDEXMODE_WRAP_REPEAT;
                      break;
                  case D3DTADDRESS_MIRROR:
                      pSoftP3RX->P3RXTextureCoordMode.WrapT = 
                                              __GLINT_TEXADDRESS_WRAP_MIRROR;
                      pSoftP3RX->P3RXTextureIndexMode0.WrapV = 
                                              P3RX_TEXINDEXMODE_WRAP_MIRROR;
                      break;
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSV!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                      break;
              }
  
  #if DX8_3DTEXTURES
              if (pTexture->b3DTexture)
              {
                  // W Wrapping
                  switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSW ))
                  {
                      case D3DTADDRESS_CLAMP:
                          pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                  __GLINT_TEXADDRESS_WRAP_CLAMP;
                          pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                              P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                          break;
                      
                      case D3DTADDRESS_WRAP:
                          pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                  __GLINT_TEXADDRESS_WRAP_REPEAT;
                          pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                  P3RX_TEXINDEXMODE_WRAP_REPEAT;
                          break;
                      
                      case D3DTADDRESS_MIRROR:
                          pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                  __GLINT_TEXADDRESS_WRAP_MIRROR;
                          pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                  P3RX_TEXINDEXMODE_WRAP_MIRROR;
                          break;
                      
                      default:
                          DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSW!"));
                          SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                          break;
                  }
              }
  #endif // DX8_3DTEXTURES
  
              if(( TSSTATE( iTSStage, D3DTSS_ADDRESSU ) == D3DTADDRESS_CLAMP ) ||
                 ( TSSTATE( iTSStage, D3DTSS_ADDRESSV ) == D3DTADDRESS_CLAMP ))
              {
                  if( (TSSTATE( iTSStage, D3DTSS_ADDRESSU ) != D3DTADDRESS_CLAMP) ||
                      (TSSTATE( iTSStage, D3DTSS_ADDRESSV ) != D3DTADDRESS_CLAMP))
                  {
                      DISPDBG((ERRLVL,"Warning: One texture coord clamped, but not "
                                  "the other - can't appply TextureShift"));
                  } 
  
                  pSoftP3RX->P4DeltaFormatControl.TextureShift = 
                                                          __PERMEDIA_DISABLE;
              }
              else
              {
                  pSoftP3RX->P4DeltaFormatControl.TextureShift = 
                                                          __PERMEDIA_ENABLE;
              }
  
              ASSERTDD ( pFormatSurface != NULL, 
                         "** SetupTextureUnitStage: logic error: "
                         "pFormatSurace is NULL" );
              switch (pFormatSurface->DeviceFormat)
              {
                  case SURF_CI8:
                      pSoftP3RX->P3RXTextureReadMode0.TextureType = 
                                      P3RX_TEXREADMODE_TEXTURETYPE_8BITINDEXED;
                      break;
                      
                  case SURF_YUV422:
                      pSoftP3RX->P3RXTextureReadMode0.TextureType = 
                                      P3RX_TEXREADMODE_TEXTURETYPE_422_YVYU;
                      break;
                      
                  default:
                      pSoftP3RX->P3RXTextureReadMode0.TextureType = 
                                      P3RX_TEXREADMODE_TEXTURETYPE_NORMAL;
                      break;
              }
              
              // MAG Filter
              switch(TSSTATE ( iTSStage, D3DTSS_MAGFILTER ))
              {
                  case D3DTFG_POINT:
                      pSoftP3RX->P3RXTextureIndexMode0.MagnificationFilter = 
                                      __GLINT_TEXTUREREAD_FILTER_NEAREST;
                      break;
                      
                  case D3DTFG_LINEAR:
                      pSoftP3RX->P3RXTextureIndexMode0.MagnificationFilter = 
                                      __GLINT_TEXTUREREAD_FILTER_LINEAR;
                      break;
                      
                  case D3DTFG_FLATCUBIC:
                  case D3DTFG_GAUSSIANCUBIC:
                  case D3DTFG_ANISOTROPIC:
                      DISPDBG((ERRLVL,"ERROR: Unsupported MAGFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                      break;
                      
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown MAGFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                      break;
              }
  
              switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
              {
                  case D3DTFN_POINT:
                      pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter = 
                                              __GLINT_TEXTUREREAD_FILTER_NEAREST;
                      break;
                      
                  case D3DTFN_LINEAR:
                      pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter = 
                                              __GLINT_TEXTUREREAD_FILTER_LINEAR;
                      break;
                      
                  case D3DTFN_ANISOTROPIC:
                      DISPDBG((ERRLVL,"ERROR: Unsupported MINFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                      break;
                      
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown MINFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                      break;
              }
  
              switch(TSSTATE ( iTSStage, D3DTSS_MIPFILTER ))
              {
                  case D3DTFP_NONE:
                      // No need to set the minification filter, it was done above
                      break;
                      
                  case D3DTFP_POINT:
                      switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                      {
                          case D3DTFN_POINT:
                              // Point Min, Point Mip
                              pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                          __GLINT_TEXTUREREAD_FILTER_NEARMIPNEAREST;
                              break;
                          case D3DTFN_LINEAR:
                              // Linear Min, Point Mip
                              pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                       __GLINT_TEXTUREREAD_FILTER_LINEARMIPNEAREST;
                              break;
                      }
                      break;
                      
                  case D3DTFP_LINEAR:
                      if( bBothTexturesValid )
                      {
                          // We can only do per-poly mipmapping while 
                          // multi-texturing, so don't enable inter-map filtering.
  
                          // Non-fatal error - drop back to nearest 
                          // mipmap filtering.
                          SET_BLEND_ERROR ( pContext,  BS_INVALID_FILTER );
  
                          switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                          {
                              case D3DTFN_POINT:
                                  // Point Min, Point Mip
                                  pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                      __GLINT_TEXTUREREAD_FILTER_NEARMIPNEAREST;
                                  break;
                              case D3DTFN_LINEAR:
                                  // Linear Min, Point Mip
                                  pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                      __GLINT_TEXTUREREAD_FILTER_LINEARMIPNEAREST;
                                  break;
                          }
                      }
                      else
                      {
                          // Single texture - do inter-map filtering
  
                          switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                          {
                              case D3DTFN_POINT:
                                  // Point Min, Linear Mip
                                  pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                      __GLINT_TEXTUREREAD_FILTER_NEARMIPLINEAR;
                                  break;
                              case D3DTFN_LINEAR:
                                  // Linear Min, Linear Mip
                                  pSoftP3RX->P3RXTextureIndexMode0.MinificationFilter =
                                                                      __GLINT_TEXTUREREAD_FILTER_LINEARMIPLINEAR;
                                  break;
                          }
                      }
                      break;
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown MIPFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                      break;
              }
  
              // MipMapping
              if( (TSSTATE ( iTSStage, D3DTSS_MIPFILTER ) != D3DTFP_NONE) && 
                  (pTexture->bMipMap))
              {
                  int iLOD;
                  INT iTexLOD;
  
                  DISPDBG(( DBGLVL, "Multiple texture levels" ));
  
                  // Load the mipmap levels for texture 0
                  // Mip level from pMipBases->dwTex0ActMaxLevel to 
                  // pTexture->iMipLevels will be mapped to base address slot
                  // from pMipBases->dwTex0Mipbase to dwTex0MipMax
                  ASSERTDD ( pMipBases->dwTex0MipBase == 0, 
                            "** __TXT_SetupTexture: "
                            "Texture 0 mipmap base is not 0" );
                            
                  iLOD = pMipBases->dwTex0MipBase;
                  iTexLOD = pMipBases->dwTex0ActMaxLevel;
                  iT0MaxLevel = iTexLOD;
  
                  while(( iTexLOD < pTexture->iMipLevels ) && 
                        ( iLOD <= (int)pMipBases->dwTex0MipMax ))
                  {
                      DISPDBG((DBGLVL, "  Setting Texture Base Address %d to 0x%x", 
                                  iLOD, pTexture->MipLevels[iLOD].dwOffsetFromMemoryBase));
                                  
                      pSoftP3RX->P3RXTextureMapWidth[iLOD] = 
                                  pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
  
  #if DX7_TEXMANAGEMENT
                      // If this is a driver managed texture surface, we need 
                      // to use our privately allocated mem ptr
                      if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                      {                        
                          SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                              (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                              iLOD);
                      }
                      else
  #endif
                      {
                          SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                              pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase, 
                                              iLOD);
                      }
  
                      iLOD++;
                      iTexLOD++;
                  }
  
                  // If both textures are enabled we can't do per-pixel 
                  // mipmapping because that uses both sets of texcoord 
                  // DDAs to generate the LOD level. So we must do per-poly 
                  // mipmapping. Per-poly mipmapping can only be done in 
                  // hardware on P4 - we use a Delta renderer on P3 when 
                  // mipmapping with both textures enabled.
  
                  if( bBothTexturesValid )
                  {
                      DISPDBG(( DBGLVL, "Both textures valid" ));
  
                      // Do per-poly mipmapping in the P4 DeltaFormat unit
  
                      pSoftP3RX->P3RXTextureCoordMode.EnableLOD = 
                                                              __PERMEDIA_DISABLE;
                      pSoftP3RX->P3RXTextureCoordMode.EnableDY = 
                                                              __PERMEDIA_DISABLE;
                      pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                              __PERMEDIA_ENABLE;
  
                      {
                          DWORD d;
  
                          *(float *)&d = 
                                  pContext->MipMapLODBias[TEXSTAGE_0] *
                                  pTexture->dwPixelPitch *
                                  pTexture->wHeight;
  
                          SEND_P3_DATA(TextureLODScale, d);                       
                      }
                  }
                  else
                  {
                      DISPDBG(( DBGLVL, "Single texture only" ));
  
                      // Do per-pixel mipmapping
  
                  
                      pSoftP3RX->P3RXTextureCoordMode.EnableLOD = 
                                                              __PERMEDIA_ENABLE;
                      pSoftP3RX->P3RXTextureCoordMode.EnableDY = 
                                                              __PERMEDIA_ENABLE;
                      pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                              __PERMEDIA_DISABLE;
  
  //@@BEGIN_DDKSPLIT
  #if 0
  // azn this would disable "automatic" mip mapping and we would be using the
  // values supplied by writing into LOD and LOD1 (d3dprim.c) - don't forget!
                      pSoftP3RX->P3RXTextureCoordMode.EnableLOD = 
                                                              __PERMEDIA_DISABLE;
                      pSoftP3RX->P3RXTextureCoordMode.EnableDY = 
                                                              __PERMEDIA_DISABLE;
                      pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                              __PERMEDIA_ENABLE;
                      SEND_P3_DATA(TextureLODScale, 0);
  #endif
  //@@END_DDKSPLIT
                      {
                          float bias;
                          DWORD b;
  
                          bias = pContext->TextureStageState[TEXSTAGE_0].m_fVal[D3DTSS_MIPMAPLODBIAS];
  
                          // Convert LOD bias from float to 6.8
  
                          myFtoi( &b, bias * 256.0f );
  
                          SEND_P3_DATA(TextureLODBiasS, b);
                          SEND_P3_DATA(TextureLODBiasT, b);
                      }
                  }
  
                  pSoftP3RX->P3RXTextureIndexMode0.MipMapEnable = 
                                                              __PERMEDIA_ENABLE;
              }
              else
              {
                  int iTexLOD;
  
                  // No mipmapping.
                  DISPDBG(( DBGLVL, "Single texture level" ));
  
                  pSoftP3RX->P3RXTextureCoordMode.EnableLOD = __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureCoordMode.EnableDY = __PERMEDIA_DISABLE;
                  pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap = 
                                                              __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureIndexMode0.MipMapEnable = 
                                                              __PERMEDIA_DISABLE;
  
                  ASSERTDD ( pMipBases->dwTex0MipBase == 0, 
                            "** __TXT_SetupTexture: "
                            "Texture 0 mipmap base is not 0" );
                            
                  // Use the maximum level indicated by D3DTSS_MAXMIPLEVEL
                  iTexLOD = pMipBases->dwTex0ActMaxLevel;
                  iT0MaxLevel = iTexLOD;
  
  #if DX7_TEXMANAGEMENT
                  // If this is a driver managed texture surface, we need 
                  // to use our privately allocated mem ptr
                  if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                  {                        
                      SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                          (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                          0);
                  }
                  else
  #endif                
                  {
                      SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                          pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase, 
                                          0);
                  }
  
                  // No mipmapping, but could be combining the caches.
                  pSoftP3RX->P3RXTextureMapWidth[0] = 
                                      pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
                  pSoftP3RX->P3RXTextureMapWidth[1] = 
                                      pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
              }
  
  //@@BEGIN_DDKSPLIT
              // There is only one TextureCoordMode, it is possible that it is
              // not used at all
  //@@END_DDKSPLIT
              // Set maximum dimension of the texture
              pSoftP3RX->P3RXTextureCoordMode.Width = pTexture->MipLevels[iT0MaxLevel].logWidth;
              pSoftP3RX->P3RXTextureCoordMode.Height = pTexture->MipLevels[iT0MaxLevel].logHeight;
  #if DX7_PALETTETEXTURE
              // If it is a palette indexed texture, we simply follow the chain
              // down from the surface to its palette and pull out the LUT values
              // from the PALETTEENTRY's in the palette.
              ASSERTDD ( pFormatSurface != NULL, "** SetupTextureUnitStage: logic error: pFormatSurace is NULL" );
              if (pFormatSurface->DeviceFormat == SURF_CI8)
              {
                  WAIT_FIFO(8);
  
                  pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_ENABLE;
                  pSoftP3RX->P3RXLUTMode.InColorOrder = __PERMEDIA_ENABLE;        
                  SEND_P3_DATA(LUTAddress, 0);
                  SEND_P3_DATA(LUTTransfer, 0);
                  SEND_P3_DATA(LUTIndex, 0);
                  COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode);
  
                  // In this case simply download the 256 entries each time the 
                  // texture handle changes.
                  {
                      DWORD dwCount1, dwCount2;
                      D3DHAL_DP2UPDATEPALETTE *pPalette;  // associated palette
                      LPDWORD lpColorTable;           // array of palette entries
          
                      pPalette = GetPaletteFromHandle(pContext, 
                                                      pTexture->dwPaletteHandle);
                      if (pPalette) // If palette can be found
                      {
                          lpColorTable = (LPDWORD)(pPalette + 1);
                          
                          if (pTexture->dwPaletteFlags & DDRAWIPAL_ALPHA)
                          {
                              for (dwCount1 = 0; dwCount1 < 16; dwCount1++)
                              {
                                  P3_ENSURE_DX_SPACE(17);
                                  WAIT_FIFO(17);
                                  P3RX_HOLD_CMD(LUTData, 16);
                                  for (dwCount2 = 0; dwCount2 < 16; dwCount2++)
                                  {
                                      *dmaPtr++ = *lpColorTable++;
                                      CHECK_FIFO(1);
                                  }
                              }
                          }
                          else
                          {
                              for (dwCount1 = 0; dwCount1 < 16; dwCount1++)
                              {
                                  P3_ENSURE_DX_SPACE(17);
                                  WAIT_FIFO(17);
                                  P3RX_HOLD_CMD(LUTData, 16);
                                  for (dwCount2 = 0; dwCount2 < 16; dwCount2++)
                                  {
                                      *dmaPtr++ = CHROMA_UPPER_ALPHA(*(DWORD*)lpColorTable++);
                                      CHECK_FIFO(1);
                                  }
                              }
                          }
                      }
                      else
                      {
                          DISPDBG((ERRLVL,"Palette handle is missing for CI8 surf!"));
                      }
                  }
  
                  // Make sure there is room left over for the rest of the routine
                  P3_ENSURE_DX_SPACE(2);
                  WAIT_FIFO(2);
                  SEND_P3_DATA(LUTIndex, 0);
  
              }
              else
  #endif // DX7_PALETTETEXTURE
              {
                  // No LUT.
                  P3_ENSURE_DX_SPACE(4);
                  WAIT_FIFO(4);
                  
                  pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_DISABLE;
                  SEND_P3_DATA(LUTTransfer, __PERMEDIA_DISABLE);
                  COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode)
              }
  
  #if DX8_3DTEXTURES
              P3_ENSURE_DX_SPACE(4);
              WAIT_FIFO(4);
  
              if (pTexture->b3DTexture)
              {
                  //
                  // Set size of each 2D texture slice in texel size to TextureMapSize.
                  //
                  SEND_P3_DATA(TextureMapSize, pTexture->dwSliceInTexel);
              }
              else
              {
                  SEND_P3_DATA(TextureMapSize, 0);
              }
  #endif // DX8_3DTEXTURES
  
              P3_DMA_COMMIT_BUFFER();
              __TXT_ConsiderSrcChromaKey( pContext, pTexture, 0 );
              P3_DMA_GET_BUFFER();
  
              // Setup TextureReadMode
              pSoftP3RX->P3RXTextureReadMode0.MapBaseLevel = 
                                                      pMipBases->dwTex0MipBase;
              pSoftP3RX->P3RXTextureReadMode0.MapMaxLevel = 
                                                      pMipBases->dwTex0MipMax;
              pSoftP3RX->P3RXTextureReadMode0.Width = pTexture->MipLevels[iT0MaxLevel].logWidth;
              pSoftP3RX->P3RXTextureReadMode0.Height = pTexture->MipLevels[iT0MaxLevel].logHeight;
              pSoftP3RX->P3RXTextureReadMode0.TexelSize = pTexture->dwPixelSize;
  
              pSoftP3RX->P3RXTextureReadMode0.LogicalTexture = 
                                                              __PERMEDIA_DISABLE;
  
              // Enable stage 0
              pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_ENABLE;
  
              // Never set CombineCaches - chip bug
              pSoftP3RX->P3RXTextureReadMode0.CombineCaches = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureFilterMode.CombineCaches = __PERMEDIA_DISABLE;
  
              // Always copy TRM0 to TRM1 in case we are combining the caches
              pSoftP3RX->P3RXTextureReadMode1 = pSoftP3RX->P3RXTextureReadMode0;
  
              // Enable the texture index unit 
              // (this is a bit like the the texture read)
              pSoftP3RX->P3RXTextureIndexMode0.Width = pTexture->MipLevels[iT0MaxLevel].logWidth;
              pSoftP3RX->P3RXTextureIndexMode0.Height = pTexture->MipLevels[iT0MaxLevel].logHeight;
  
              // Set both formats to be equal for texture 0 - this will be correct 
              // for single-texture per-pixel mipmap or non-mipmapped with a 
              // combined cache. If the second texture is valid it's setup below 
              // will set Format1 appropriately.
  
              ASSERTDD ( pFormatSurface != NULL, 
                         "** SetupTextureUnitStage: logic error: "
                         "pFormatSurace is NULL" );
  
              pSoftP3RX->P3RXTextureFilterMode.Format0 = 
                                                  pFormatSurface->FilterFormat;
              pSoftP3RX->P3RXTextureFilterMode.Format1 = 
                                                  pFormatSurface->FilterFormat;
  
  #if DX8_3DTEXTURES
              if (pTexture->b3DTexture)
              {
                  //
                  // Enable 3D Texture registers.
                  //
                  pSoftP3RX->P3RX_P3DeltaMode.Texture3DEnable = __PERMEDIA_ENABLE;
                  pSoftP3RX->P3RXTextureReadMode0.Texture3D = __PERMEDIA_ENABLE;
                  pSoftP3RX->P3RXTextureIndexMode0.Texture3DEnable = 
                                                              __PERMEDIA_ENABLE;
  
                  //
                  // ReadMode1 and IndexMode1 should have same data as 0.
                  //
                  pSoftP3RX->P3RXTextureReadMode1 = pSoftP3RX->P3RXTextureReadMode0;
                  pSoftP3RX->P3RXTextureIndexMode1 = pSoftP3RX->P3RXTextureIndexMode0;
  
                  //
                  // And put logDepth into IndexMode1.Width. 
                  //
                  pSoftP3RX->P3RXTextureIndexMode1.Width = pTexture->logDepth;
              }
              else
              {
                  pSoftP3RX->P3RXTextureReadMode0.Texture3D = __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureIndexMode0.Texture3DEnable = 
                                                              __PERMEDIA_DISABLE;
              }
  #endif // DX8_3DTEXTURES
              break;
          }
  
          // Texture Stage 1
          case 1:
          {
              pSoftP3RX->P3RXTextureFilterMode.ForceAlphaToOne1 = 
                                              pFormatSurface->bAlpha ? 
                                                      __PERMEDIA_DISABLE : 
                                                      __PERMEDIA_ENABLE;
  
              // D3D UV Wrapping
              if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] & 
                                                                      D3DWRAP_U)
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapS1 = 1;
              }
              else
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapS1 = 0;
              }
  
              if (pContext->RenderStates[D3DRENDERSTATE_WRAP0+iTSStage] & 
                                                                      D3DWRAP_V)
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapT1 = 1;
              }
              else
              {
                  pSoftP3RX->P4DeltaFormatControl.WrapT1 = 0;
              }
  
              // U Addressing
              switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSU ))
              {
                  case D3DTADDRESS_CLAMP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                  __GLINT_TEXADDRESS_WRAP_CLAMP;
                      pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                              P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                      break;
                      
                  case D3DTADDRESS_WRAP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                  __GLINT_TEXADDRESS_WRAP_REPEAT;
                      pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                  P3RX_TEXINDEXMODE_WRAP_REPEAT;
                      break;
                      
                  case D3DTADDRESS_MIRROR:
                      pSoftP3RX->P3RXTextureCoordMode.WrapS1 = 
                                                  __GLINT_TEXADDRESS_WRAP_MIRROR;
                      pSoftP3RX->P3RXTextureIndexMode1.WrapU = 
                                                  P3RX_TEXINDEXMODE_WRAP_MIRROR;
                      break;
                      
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSU!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                      break;
              }
  
              // V Addressing
              switch (TSSTATE ( iTSStage, D3DTSS_ADDRESSV ))
              {
                  case D3DTADDRESS_CLAMP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapT1 = 
                                                  __GLINT_TEXADDRESS_WRAP_CLAMP;
                      pSoftP3RX->P3RXTextureIndexMode1.WrapV = 
                                              P3RX_TEXINDEXMODE_WRAP_CLAMPEDGE;
                      break;
                      
                  case D3DTADDRESS_WRAP:
                      pSoftP3RX->P3RXTextureCoordMode.WrapT1 = 
                                                  __GLINT_TEXADDRESS_WRAP_REPEAT;
                      pSoftP3RX->P3RXTextureIndexMode1.WrapV = 
                                                  P3RX_TEXINDEXMODE_WRAP_REPEAT;
                      break;
                      
                  case D3DTADDRESS_MIRROR:
                      pSoftP3RX->P3RXTextureCoordMode.WrapT1 = 
                                                  __GLINT_TEXADDRESS_WRAP_MIRROR;
                      pSoftP3RX->P3RXTextureIndexMode1.WrapV = 
                                                  P3RX_TEXINDEXMODE_WRAP_MIRROR;
                      break;
                      
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown ADDRESSV!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_STATE );
                      break;
              }
  
              if(( TSSTATE( iTSStage, D3DTSS_ADDRESSU ) == D3DTADDRESS_CLAMP ) ||
                 ( TSSTATE( iTSStage, D3DTSS_ADDRESSV ) == D3DTADDRESS_CLAMP ))
              {
                  if ((TSSTATE( iTSStage, D3DTSS_ADDRESSU ) != D3DTADDRESS_CLAMP) ||
                      (TSSTATE( iTSStage, D3DTSS_ADDRESSV ) != D3DTADDRESS_CLAMP))
                  {
                      DISPDBG((ERRLVL,"Warning: One texture coord clamped, but not "
                                  "the other - can't appply TextureShift"));
                  }
                   
                  pSoftP3RX->P4DeltaFormatControl.TextureShift1 = 
                                                              __PERMEDIA_DISABLE;
              }
              else
              {
                  pSoftP3RX->P4DeltaFormatControl.TextureShift1 = 
                                                              __PERMEDIA_ENABLE;
              }
  
              ASSERTDD ( pFormatSurface != NULL, 
                         "** SetupTextureUnitStage: logic error: "
                         "pFormatSurace is NULL" );
              switch (pFormatSurface->DeviceFormat)
              {
                  case SURF_CI8:
                      pSoftP3RX->P3RXTextureReadMode1.TextureType = 
                                      P3RX_TEXREADMODE_TEXTURETYPE_8BITINDEXED;
                      break;
                      
                  case SURF_YUV422:
                      pSoftP3RX->P3RXTextureReadMode1.TextureType = 
                                      P3RX_TEXREADMODE_TEXTURETYPE_422_YVYU;
                      break;
                      
                  default:
                      pSoftP3RX->P3RXTextureReadMode1.TextureType = 
                                      P3RX_TEXREADMODE_TEXTURETYPE_NORMAL;
                      break;
              }
              
              // MAG Filter
              switch(TSSTATE ( iTSStage, D3DTSS_MAGFILTER ))
              {
                  case D3DTFG_POINT:
                      pSoftP3RX->P3RXTextureIndexMode1.MagnificationFilter = 
                                              __GLINT_TEXTUREREAD_FILTER_NEAREST;
                      break;
                      
                  case D3DTFG_LINEAR:
                      pSoftP3RX->P3RXTextureIndexMode1.MagnificationFilter = 
                                              __GLINT_TEXTUREREAD_FILTER_LINEAR;
                      break;
                      
                  case D3DTFG_FLATCUBIC:
                  case D3DTFG_GAUSSIANCUBIC:
                  case D3DTFG_ANISOTROPIC:
                      DISPDBG((ERRLVL,"ERROR: Unsupported MAGFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                      break;
                      
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown MAGFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                      break;
              }
  
              switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
              {
                  case D3DTFN_POINT:
                      pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter = 
                                              __GLINT_TEXTUREREAD_FILTER_NEAREST;
                      break;
                  case D3DTFN_LINEAR:
                      pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter = 
                                              __GLINT_TEXTUREREAD_FILTER_LINEAR;
                      break;
                  case D3DTFN_ANISOTROPIC:
                      DISPDBG((ERRLVL,"ERROR: Unsupported MINFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNSUPPORTED_FILTER );
                      break;
                  default:
                      DISPDBG((ERRLVL,"ERROR: Unknown MINFILTER!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                      break;
              }
  
              switch(TSSTATE ( iTSStage, D3DTSS_MIPFILTER ))
              {
                  case D3DTFP_NONE:
                      // No need to set the minification filter
                      // it was done above
                      break;
                      
                  case D3DTFP_LINEAR:
                  case D3DTFP_POINT:
                      if( bBothTexturesValid )
                      {
                          if ( TSSTATE ( iTSStage, D3DTSS_MIPFILTER ) == 
                                                                  D3DTFP_LINEAR )
                          {
                              // Can't do trilinear with both textures 
                              // - fall back to per-poly.
                              SET_BLEND_ERROR ( pContext,  BS_INVALID_FILTER );
                          }
  
                          // We can only do per-poly mipmapping while 
                          // multi-texturing, so don't enable 
                          //inter-map filtering.
  
                          switch(TSSTATE ( iTSStage, D3DTSS_MINFILTER ))
                          {
                              case D3DTFN_POINT:
                                  // Point Min, Point Mip
                                  pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter =
                                                                          __GLINT_TEXTUREREAD_FILTER_NEARMIPNEAREST;
                                  break;
                                  
                              case D3DTFN_LINEAR:
                                  // Linear Min, Point Mip
                                  pSoftP3RX->P3RXTextureIndexMode1.MinificationFilter =
                                                                          __GLINT_TEXTUREREAD_FILTER_LINEARMIPNEAREST;
                                  break;
                          }
                      }
                      else
                      {
                          DISPDBG((ERRLVL,"** Setting up the second stage, but "
                                       "only one texture is valid"));
                      }
                      break;
                      
                  default:
                      DISPDBG((ERRLVL,"ERROR: Invalid Mip filter!"));
                      SET_BLEND_ERROR ( pContext,  BSF_UNDEFINED_FILTER );
                      break;
              }
  
              // MipMapping
              // If the app chooses to have two mip-mapped textures or a 
              // single mip-mapped texture in stage 1 they only get 
              // per-poly mipmapping.
              if( (TSSTATE ( iTSStage, D3DTSS_MIPFILTER ) != D3DTFP_NONE) && 
                   pTexture->bMipMap )
              {
                  int iLOD, iTexLOD;
  
                  // Load the mipmap levels for texture 1
                  // Mip level from pMipBases->dwTex1ActMaxLevel to
                  // pTexture->iMipLevels will be mapped to base address slot
                  // from pMipBases->dwTex1Mipbase to dwTex1MipMax
                  iLOD = pMipBases->dwTex1MipBase;
                  iTexLOD = pMipBases->dwTex1ActMaxLevel;
                  iT1MaxLevel = iTexLOD;
   
                  P3_ENSURE_DX_SPACE(32);
                  WAIT_FIFO(32);
  
                  while(( iTexLOD < pTexture->iMipLevels ) && 
                        ( iLOD <= (int)pMipBases->dwTex1MipMax ))
                  {
                      DISPDBG((DBGLVL, "  Setting Texture Base Address %d to 0x%x", 
                                  iLOD, 
                                  pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase));
                                  
                      pSoftP3RX->P3RXTextureMapWidth[iLOD] = 
                                  pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
  
  #if DX7_TEXMANAGEMENT
                      // If this is a driver managed texture surface, we need 
                      // to use our privately allocated mem ptr
                      if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                      {                        
                          SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                              (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                              iLOD);            
                      }
                      else
  #endif   
                      {
                          SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                              pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase, 
                                              iLOD);
                      }   
                      
                      iLOD++;
                      iTexLOD++;
                  }
  
                  pSoftP3RX->P3RXTextureCoordMode.EnableLOD = __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureCoordMode.EnableDY = __PERMEDIA_DISABLE;
                  pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap1 = 
                                                              __PERMEDIA_ENABLE;
                  pSoftP3RX->P3RXTextureIndexMode1.MipMapEnable = 
                                                              __PERMEDIA_ENABLE;
  
                  P3_ENSURE_DX_SPACE(2);
                  WAIT_FIFO(2);
                  {
                      DWORD d;
  
                      *(float *)&d = 
                              pContext->MipMapLODBias[TEXSTAGE_1] *
                              pTexture->dwPixelPitch *
                              pTexture->wHeight;
  
                      SEND_P3_DATA(TextureLODScale1, d);
                  }
              }
              else
              {
                  int iTexLOD;
  
                  // Use the maximum level indicated by D3DTSS_MAXMIPLEVEL
                  iTexLOD = pMipBases->dwTex0ActMaxLevel;
                  iT1MaxLevel = iTexLOD;
  
                  pSoftP3RX->P3RXTextureCoordMode.EnableLOD = __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureCoordMode.EnableDY = __PERMEDIA_DISABLE;
                  pSoftP3RX->P4DeltaFormatControl.PerPolyMipMap1 = 
                                                              __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureIndexMode1.MipMapEnable = 
                                                              __PERMEDIA_DISABLE;
  
                  P3_ENSURE_DX_SPACE(2);
                  WAIT_FIFO(2);
  
  #if DX7_TEXMANAGEMENT
                      // If this is a driver managed texture surface, we need 
                      // to use our privately allocated mem ptr
                      if (pTexture->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
                      {                        
                          SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                              (DWORD)D3DTMMIPLVL_GETOFFSET(pTexture->MipLevels[iTexLOD], pThisDisplay),
                                              pMipBases->dwTex1MipBase);            
                      }
                      else
  #endif
                      {
                          SEND_P3_DATA_OFFSET(TextureBaseAddr0, 
                                              pTexture->MipLevels[iTexLOD].dwOffsetFromMemoryBase,
                                              pMipBases->dwTex1MipBase);
                      }
                      
                  // No mipmapping.
                  pSoftP3RX->P3RXTextureMapWidth[pMipBases->dwTex1MipBase] = 
                                      pTexture->MipLevels[iTexLOD].P3RXTextureMapWidth;
              }
  
  
              ASSERTDD ( pFormatSurface != NULL, 
                         "** SetupTextureUnitStage: logic error: "
                         "pFormatSurace is NULL" );
                         
              if (pFormatSurface->DeviceFormat == SURF_CI8)
              {
                  // In the future, this will work as long as texture 0 isn't
                  // palettised, or if they share the palette.
                  // But that needs some restructuring - the whole LUT setup 
                  // should be in a single bit of code in _D3DChangeTextureP3RX, 
                  // since it is a shared resource.
                  DISPDBG((ERRLVL,"** SetupTextureUnitStage: allow second texture "
                               "to use LUTs"));
                                
                  // For now, fail.
                  SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_PALETTES );
              }
  
              P3_DMA_COMMIT_BUFFER();
              __TXT_ConsiderSrcChromaKey( pContext, pTexture, 1 );
              P3_DMA_GET_BUFFER();
  
              // Setup TextureReadMode
              pSoftP3RX->P3RXTextureReadMode1.MapBaseLevel = 
                                                      pMipBases->dwTex1MipBase;
              pSoftP3RX->P3RXTextureReadMode1.MapMaxLevel = 
                                                      pMipBases->dwTex1MipMax;
              pSoftP3RX->P3RXTextureReadMode1.Width = pTexture->MipLevels[iT1MaxLevel].logWidth;
              pSoftP3RX->P3RXTextureReadMode1.Height = pTexture->MipLevels[iT1MaxLevel].logHeight;
              pSoftP3RX->P3RXTextureReadMode1.TexelSize = pTexture->dwPixelSize;
  
              pSoftP3RX->P3RXTextureReadMode1.LogicalTexture = 
                                                              __PERMEDIA_DISABLE;
              
              // Enable the texture index unit (this is a bit like the 
              // the texture read)
              pSoftP3RX->P3RXTextureIndexMode1.Width = pTexture->MipLevels[iT1MaxLevel].logWidth;
              pSoftP3RX->P3RXTextureIndexMode1.Height = pTexture->MipLevels[iT1MaxLevel].logHeight;
              ASSERTDD ( pFormatSurface != NULL, 
                         "** SetupTextureUnitStage: logic error: "
                         "pFormatSurace is NULL" );
              pSoftP3RX->P3RXTextureFilterMode.Format1 = 
                                                  pFormatSurface->FilterFormat;
  
              // Enable stage 1
              pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_ENABLE;
  
  #if DX7_PALETTETEXTURE
  #if 0
              // D3DValidateDeviceP3() will return error code for this case
              ASSERTDD((pFormatSurface->DeviceFormat != SURF_CI8 && 
                        pFormatSurface->DeviceFormat != SURF_CI4),
                       "Texture surface can't be palettized when using a "
                       "second map!");
  #endif
  #endif
  
              break;
          }
      }
  
      P3_DMA_COMMIT_BUFFER();
  
      return TRUE;
  } // __TXT_SetupTexture
  
  //-----------------------------------------------------------------------------
  //
  // __bD3DTexturesMatch
  //
  //
  // A function to compare the two textures in two D3D stages, and determine
  // if they could be satisfied by the same on-chip texture.
  //
  // int iStage1              D3D stage number of first texture.
  // int iStage2              D3D stage number of second texture.
  // *pContext                The context.
  //
  // result:                  TRUE if the textures match, FALSE if they don't.
  //
  // An ASSERT is triggered if either stage is not using a texture. In the 
  // release build, the result will be TRUE, meaning that we could pack both 
  // textures stages requirements into one texture (because one or both do 
  // not use a texture).
  //
  //-----------------------------------------------------------------------------
  BOOL 
  __bD3DTexturesMatch ( 
      int iStage1, 
      int iStage2, 
      P3_D3DCONTEXT* pContext )
  {
      ASSERTDD ( iStage1 != iStage2, 
                 "** __bD3DTexturesMatch: both stages are the same "
                 "- pointless comparison!" );
                 
      if ( TSSTATE ( iStage1, D3DTSS_TEXTUREMAP ) == 0 )
      {
          DISPDBG((ERRLVL,"** __bD3DTexturesMatch: first considered stage's "
                       "texture is NULL"));
                        
          return ( TRUE );
      }
      else if ( TSSTATE ( iStage2, D3DTSS_TEXTUREMAP ) == 0 )
      {
          DISPDBG((ERRLVL,"** __bD3DTexturesMatch: second considered stage's "
                        "texture is NULL"));
          return ( TRUE );
      }
      else
      {
          #define CHECK_EQUALITY(name) ( TSSTATE ( iStage1, name ) == TSSTATE ( iStage2, name ) )
          if (CHECK_EQUALITY ( D3DTSS_TEXTUREMAP ) &&
              CHECK_EQUALITY ( D3DTSS_TEXCOORDINDEX ) )
          {
              // Yes, the textures have the same handle and coord set. Do 
              // some further checks.
  
              // If the pointers are different, or the texcoord sets are 
              // different (for bumpmapping), this is a common occurrance, 
              // and need not be flagged. However, if they are the same, 
              // but a filter mode or something like that is different,
              // it is likely to be an app bug, so flag it.
  
              if (
                  // Should not need to check ADDRESS 
                  // - should have been mirrored to ADDRESS[UV].
                  CHECK_EQUALITY ( D3DTSS_ADDRESSU ) &&
                  CHECK_EQUALITY ( D3DTSS_ADDRESSV ) &&
                  CHECK_EQUALITY ( D3DTSS_MAGFILTER ) &&
                  CHECK_EQUALITY ( D3DTSS_MINFILTER ) &&
                  CHECK_EQUALITY ( D3DTSS_MIPFILTER ) )
                  // I should also check all the other variables like 
                  // MIPMAPLODBIAS, but they rely on mipmapping being 
                  // enabled, etc, so it's more of a hassle. If an app 
                  // really does manage to be this perverse, it's doing well!
              {
                  // Looks good.
                  return ( TRUE );
              }
              else
              {
                  // Well, the texcoords agree and the handle agree, but the 
                  // others don't. I bet this is an app bug - you are unlikely 
                  // to do this deliberately.
                  _D3DDisplayWholeTSSPipe ( pContext, WRNLVL );
                  DISPDBG((ERRLVL,"** __bD3DTexturesMatch: textures agree in "
                                "handle and texcoord, but not other things - "
                                "likely app bug."));
                  return ( FALSE );
              }
          }
          else
          {
              // No, different textures.
              return ( FALSE );
          }
          #undef CHECK_EQUALITY
      }
      return TRUE;
  } // __bD3DTexturesMatch  
  
  //-----------------------------------------------------------------------------
  //
  // _D3DChangeTextureP3RX
  //
  // This function does whole setup of necessary texturing state  according to
  // the current renderestates and texture stage states. Disables texturing
  // accordingly if this is needed.
  //
  //-----------------------------------------------------------------------------
  
  void 
  _D3DChangeTextureP3RX(
      P3_D3DCONTEXT* pContext)
  {
      P3_SURF_INTERNAL* pTexture0 = NULL;
      P3_SURF_INTERNAL* pTexture1 = NULL;
      P3_THUNKEDDATA * pThisDisplay = pContext->pThisDisplay;
      P3_SOFTWARECOPY* pSoftP3RX = &pContext->SoftCopyGlint;
      P3_MIP_BASES mipBases;
      DWORD* pFlags = &pContext->Flags;   
      INT i, iLastChipStage;
      DWORD dwT0MipLevels, 
            dwT1MipLevels,
            dwTexAppTfactor, 
            dwTexComp0Tfactor, 
            dwTexComp1Tfactor;
      BOOL bBothTexturesValid,
           bProcessChipStage0, 
           bProcessChipStage1, 
           bProcessChipStage2,
           bAlphaBlendDouble;
  
      P3_DMA_DEFS();
  
      DBG_ENTRY(_D3DChangeTextureP3RX);  
  
      pContext->iTexStage[0] = -1;
      pContext->iTexStage[1] = -1;
      // This is checked against the current state at the end of the routine.
      bAlphaBlendDouble = FALSE;
  
      // Verify if texturing should be disabled
      if ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_DISABLE ) ||
           ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_TEXTUREMAP ) == 0 ) &&
             ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) == D3DTOP_DISABLE ) &&
             ( ( ( TSSTATESELECT ( TEXSTAGE_0, D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) &&
                 ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ) ||
               ( ( TSSTATESELECT ( TEXSTAGE_0, D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) &&
                 ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ) )
           ) )
      {
          // Stage 0 is disabled, so they just want the diffuse colour.
          // Or, the texture handle is 0 , stage 1 is D3DTOP_DISABLE and in stage 
          // 0 we are selecting an arg that is not a D3DTA_TEXTURE
         
          DISPDBG((DBGLVL, "All composite units disabled - setting diffuse colour"));
          
          P3_DMA_GET_BUFFER_ENTRIES(20);
  
          // Turn off texture address generation
          pSoftP3RX->P3RXTextureCoordMode.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureCoordMode, pSoftP3RX->P3RXTextureCoordMode);
      
          // Turn off texture reads
          pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureReadMode0, pSoftP3RX->P3RXTextureReadMode0);
          COPY_P3_DATA(TextureReadMode1, pSoftP3RX->P3RXTextureReadMode1);
          pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureIndexMode0, pSoftP3RX->P3RXTextureIndexMode0);
          COPY_P3_DATA(TextureIndexMode1, pSoftP3RX->P3RXTextureIndexMode1);
  
          // Turn off the texture filter mode unit
          pSoftP3RX->P3RXTextureFilterMode.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureFilterMode, pSoftP3RX->P3RXTextureFilterMode);
          
          // Turn off texture color mode unit
          pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureApplicationMode, 
                       pSoftP3RX->P3RXTextureApplicationMode);
  
          // Not compositing
          SEND_P3_DATA(TextureCompositeMode, __PERMEDIA_DISABLE);
  
          *pFlags &= ~SURFACE_TEXTURING;
  
          pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode);
  
          // Specular texture can be enabled without texturing on
          COPY_P3_DATA(DeltaMode, pSoftP3RX->P3RX_P3DeltaMode);
  
          P3_DMA_COMMIT_BUFFER();
      
          // Turn off texturing in the render command
          RENDER_TEXTURE_DISABLE(pContext->RenderCommand);
  
          pContext->bTextureValid = TRUE;
          pContext->pCurrentTexture[0] = NULL;
          pContext->pCurrentTexture[1] = NULL;
  
          // Track just for debugging purpouses
          pContext->bTexDisabled = TRUE;
  
          bAlphaBlendDouble = FALSE;
          if ( bAlphaBlendDouble != pContext->bAlphaBlendMustDoubleSourceColour )
          {
              pContext->bAlphaBlendMustDoubleSourceColour = bAlphaBlendDouble;
              DIRTY_ALPHABLEND(pContext);
          }
  
          DBG_EXIT(_D3DChangeTextureP3RX,1);  
          return;
      }
  
      if ( TSSTATE ( TEXSTAGE_0, D3DTSS_TEXTUREMAP ) == 0 )
      if ( ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG1 ) == D3DTA_TFACTOR ) &&
             ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) ) ||
           ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG2 ) == D3DTA_TFACTOR ) &&
             ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_SELECTARG2 ) ) )
      {
          // This is an unusual way to set up the diffuse color : take
          // it from the the D3DTA_TFACTOR. But some apps use it.
          // we need to treat it separately for the Perm3 setup because
          // it might not be binded with any texture
  
          DISPDBG((DBGLVL, "Diffuse color comes from D3DTA_TFACTOR"));
  
          P3_DMA_GET_BUFFER_ENTRIES(30);
  
          // Turn off texture address generation
          pSoftP3RX->P3RXTextureCoordMode.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureCoordMode, pSoftP3RX->P3RXTextureCoordMode);
      
          // Turn off texture reads
          pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureReadMode0, pSoftP3RX->P3RXTextureReadMode0);
          COPY_P3_DATA(TextureReadMode1, pSoftP3RX->P3RXTextureReadMode1);
          pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureIndexMode0, pSoftP3RX->P3RXTextureIndexMode0);
          COPY_P3_DATA(TextureIndexMode1, pSoftP3RX->P3RXTextureIndexMode1);
  
          // Turn off the texture filter mode unit
          pSoftP3RX->P3RXTextureFilterMode.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(TextureFilterMode, pSoftP3RX->P3RXTextureFilterMode);
  
          // Setup texture color mode unit            
          pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RXTextureApplicationMode.ColorA = P3RX_TEXAPP_A_KC;
          pSoftP3RX->P3RXTextureApplicationMode.ColorOperation = P3RX_TEXAPP_OPERATION_PASS_A; 
          pSoftP3RX->P3RXTextureApplicationMode.AlphaA = P3RX_TEXAPP_A_KA;                   
          pSoftP3RX->P3RXTextureApplicationMode.AlphaOperation = P3RX_TEXAPP_OPERATION_PASS_A; 
                  
          COPY_P3_DATA(TextureApplicationMode, 
                       pSoftP3RX->P3RXTextureApplicationMode);
  
          // Setup compositing
  
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_FA;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE; 
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;  
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;          
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;          
          COPY_P3_DATA(TextureCompositeAlphaMode0, 
                       pSoftP3RX->P3RXTextureCompositeAlphaMode0);
  
          pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_FC;
          pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE; 
          pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;  
          pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;          
          pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;          
          COPY_P3_DATA(TextureCompositeColorMode0, 
                       pSoftP3RX->P3RXTextureCompositeColorMode0);
                       
          pSoftP3RX->P3RXTextureCompositeAlphaMode1.Enable = __PERMEDIA_DISABLE;                
          COPY_P3_DATA(TextureCompositeAlphaMode1, 
                       pSoftP3RX->P3RXTextureCompositeAlphaMode1);
  
          pSoftP3RX->P3RXTextureCompositeColorMode1.Enable = __PERMEDIA_DISABLE;                
          COPY_P3_DATA(TextureCompositeColorMode1, 
                       pSoftP3RX->P3RXTextureCompositeColorMode1);
                       
          SEND_P3_DATA(TextureCompositeMode, __PERMEDIA_ENABLE);
  
          *pFlags &= ~SURFACE_TEXTURING;
  
          pSoftP3RX->P3RXLUTMode.Enable = __PERMEDIA_DISABLE;
          COPY_P3_DATA(LUTMode, pSoftP3RX->P3RXLUTMode);
  
          // Specular texture can be enabled without texturing on
          COPY_P3_DATA(DeltaMode, pSoftP3RX->P3RX_P3DeltaMode);
  
          P3_DMA_COMMIT_BUFFER();
  
      
      
          // Turn off texturing in the render command
          // RENDER_TEXTURE_DISABLE(pContext->RenderCommand);
          RENDER_TEXTURE_ENABLE(pContext->RenderCommand);
  
          pContext->bTextureValid = TRUE;
          pContext->pCurrentTexture[0] = NULL;
          pContext->pCurrentTexture[1] = NULL;
  
          // Track just for debugging purpouses
          pContext->bTexDisabled = FALSE;
  
          
          DBG_EXIT(_D3DChangeTextureP3RX,1);  
          return;
      }
  
      // Track just for debugging purpouses
      pContext->bTexDisabled = FALSE;
  
      // Dump to the debugger our current TSS setup
      _D3DDisplayWholeTSSPipe(pContext, DBGLVL);
  
      // Deal with the textures.
  
      // Find the texture mappings. If D3D stage 0 uses a texture, it must 
      // always be chip texture 0 to keep the bumpmap working. Fortunately, 
      // this is the only non-orthogonal case, so everything else can cope 
      // with this restriction.
      
      for ( i = TEXSTAGE_0; i < D3DTSS_MAX; i++ )
      {
          if ( TSSTATE ( i, D3DTSS_COLOROP ) == D3DTOP_DISABLE )
          {
              // Finished processing.
              break;
          }
  
          // This code could be slightly optimised - if a texture is set up, 
          // but none of the relevant arguments are TEXTURE (with additional 
          // flags), then of course we don't need to set the texture up at all.
          // Normally, both arguments are "relevant", but with SELECTARG1 and 
          // SELECTARG2, one of them is not. Also, watch out for PREMODULATE - 
          // it is an implicit reference to a stage's texture.
  
          if (
              ( TSSTATE ( i, D3DTSS_TEXTUREMAP ) == 0 ) ||
              ( (
                  ( ( TSSTATESELECT ( i, D3DTSS_COLORARG1 ) != D3DTA_TEXTURE ) || 
                    ( TSSTATE ( i, D3DTSS_COLOROP ) == D3DTOP_SELECTARG2     ) ) &&
                  ( ( TSSTATESELECT ( i, D3DTSS_COLORARG2 ) != D3DTA_TEXTURE ) || 
                    ( TSSTATE ( i, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1     ) ) &&
                  ( ( TSSTATESELECT ( i, D3DTSS_ALPHAARG1 ) != D3DTA_TEXTURE ) || 
                    ( TSSTATE ( i, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2     ) ) &&
                  ( ( TSSTATESELECT ( i, D3DTSS_ALPHAARG2 ) != D3DTA_TEXTURE ) || 
                    ( TSSTATE ( i, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1     ) )
                ) &&
                ( TSSTATE ( i, D3DTSS_COLOROP ) != D3DTOP_PREMODULATE ) &&
                ( TSSTATE ( i-1, D3DTSS_COLOROP ) != D3DTOP_PREMODULATE )
              ) )
          {
              // This D3D stage doesn't use a texture.
              pContext->iStageTex[i] = -1;
          }
          else
          {
              // Note that the below code should be put into a little loop
              // for any future devices that have more than 2 textures, otherwise
              // the code will get big, nested and crufty. But for only 2, it's
              // manageable, and slightly faster this way.
  
              // A texture is used - is texture 0 free?
              if ( pContext->iTexStage[0] == -1 )
              {
                  // Texture 0 is free - make it this stage.
                  ASSERTDD ( pContext->iTexStage[1] == -1, 
                             "** _D3DChangeTextureP3RX: pContext->iTexStage[1] "
                             "should be -1 if pContext->iTexStage[0] is" );
                  pContext->iTexStage[0] = i;
                  pContext->iStageTex[i] = 0;
              }
              else
              {
                  // Texture 0 is assigned - see if this is the same as it.
                  if ( __bD3DTexturesMatch ( i, 
                                             pContext->iTexStage[0], 
                                             pContext ) )
                  {
                      // Yes, they match - no need to use texture 1.
                      pContext->iStageTex[i] = 0;
                  }
                  else
                  {
                      // No, they don't match. Is texture 1 free?
                      if ( pContext->iTexStage[1] == -1 )
                      {
                          // Texture 1 is free - make it this stage.
                          ASSERTDD ( pContext->iTexStage[0] != -1, 
                                     "** _D3DChangeTextureP3RX: "
                                     "pContext->iTexStage[0] should not be "
                                     "-1 if pContext->iTexStage[1] is not." );
                          pContext->iTexStage[1] = i;
                          pContext->iStageTex[i] = 1;
                      }
                      else
                      {
                          // Texture 1 is assigned - see if this is the same 
                          // as it.
                          if ( __bD3DTexturesMatch ( i, 
                                                     pContext->iTexStage[1], 
                                                     pContext ) )
                          {
                              // Yes, they match - mark it.
                              pContext->iStageTex[i] = 1;
                          }
                          else
                          {
                              // No, they don't match, and both chip textures 
                              // have been assigned. Fail a ValidateDevice().
                              DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: app tried "
                                           "to use more than two textures."));
                              SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_TEXTURES );
                              pContext->iStageTex[i] = -1;
                          }
                      }
                  }
              }
          }
  
          // A quick sanity check.
  #if DBG
          if ( TSSTATE ( i, D3DTSS_TEXTUREMAP ) == 0 )
          {
              // That's fine, then.
              ASSERTDD ( pContext->iStageTex[i] == -1, 
                         "** _D3DChangeTextureP3RX: something failed with the "
                         "texture-assignment logic" );
          }
          else if ( pContext->iStageTex[i] == -1 )
          {
              // That's fine - texture may have been set up but not referenced.
          }
          else if ( pContext->iTexStage[pContext->iStageTex[i]] == -1 )
          {
              // Oops.
              DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: something failed with "
                            "the texture-assignment logic"));
          }
          else if ( pContext->iTexStage[pContext->iStageTex[i]] == i )
          {
              // That's fine, then.
          }
          else if ( __bD3DTexturesMatch ( i, 
                                          pContext->iTexStage[pContext->iStageTex[i]], 
                                          pContext ) )
          {
              // That's fine, then.
          }
          else
          {
              // Oops.
              DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: something failed with "
                            "the texture-assignment logic"));
          }
  #endif // DBG
      }
      
      // And a few more gratuitous sanity checks at the end of the loop.
      ASSERTDD ( ( pContext->iTexStage[0] == -1 ) || 
                 ( pContext->iStageTex[pContext->iTexStage[0]] == 0 ), 
                 "** _D3DChangeTextureP3RX: something failed with the "
                 "texture-assignment logic" );
                 
      ASSERTDD ( ( pContext->iTexStage[1] == -1 ) || 
                 ( pContext->iStageTex[pContext->iTexStage[1]] == 1 ), 
                 "** _D3DChangeTextureP3RX: something failed with the "
                 "texture-assignment logic" );
  
  #if DBG
      if ( pContext->iTexStage[0] != -1 )
      {
          DISPDBG((DBGLVL, "Setting new texture0 data, Handle: 0x%x", 
                      TSSTATE ( pContext->iTexStage[0], D3DTSS_TEXTUREMAP )));
      }
      else
      {
          DISPDBG((DBGLVL, "Texture0 not used" ));
      }
      
      if ( pContext->iTexStage[1] != -1 )
      {
          DISPDBG((DBGLVL, "Setting new texture1 data, Handle: 0x%x", 
                      TSSTATE ( pContext->iTexStage[1], D3DTSS_TEXTUREMAP )));
      }
      else
      {
          DISPDBG((DBGLVL, "Texture1 not used" ));
      }
  #endif //DBG
      
      // Set the texture valid flag to true.  
      // If anything resets it then the texture state is invalid.
      pContext->bTextureValid = TRUE;
      pContext->bCanChromaKey = FALSE;
      pContext->bTex0Valid = FALSE;
      pContext->bTex1Valid = FALSE;
      pContext->bStage0DotProduct = FALSE;
  
      // Set up the textures.
      if ( pContext->iTexStage[0] != -1 )
      {
          // Setup texture 0.
          pTexture0 = GetSurfaceFromHandle(pContext, 
                                           TSSTATE(pContext->iTexStage[0], 
                                                   D3DTSS_TEXTUREMAP) );
          if (NULL == pTexture0)
          {
              DISPDBG((ERRLVL, "ERROR: Texture Surface (0) is NULL"));
              DBG_EXIT(_D3DChangeTextureP3RX,1);  
              return;            
          }
          
  #if DX7_TEXMANAGEMENT  
          if (pTexture0->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
          {
              if (!_D3D_TM_Preload_Tex_IntoVidMem(pContext, pTexture0))
              {
                  return; // something bad happened !!!
              }
  
              _D3D_TM_TimeStampTexture(pContext->pTextureManager,
                                       pTexture0);        
          }
  #endif // DX7_TEXMANAGEMENT                                                 
  
          pContext->bTex0Valid = 
                      __TXT_ValidateTextureUnitStage(pContext, 
                                                     0, 
                                                     pContext->iTexStage[0],
                                                     pTexture0 );
          if ( !pContext->bTex0Valid )
          {
              SET_BLEND_ERROR ( pContext,  BSF_INVALID_TEXTURE );
              // Pretend that no texture was set.
              pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
              pContext->bTex0Valid = FALSE;
              pTexture0 = NULL;
          }
      }
      else
      {
          pSoftP3RX->P3RXTextureReadMode0.Enable = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureIndexMode0.Enable = __PERMEDIA_DISABLE;
          pContext->bTex0Valid = FALSE;
          pTexture0 = NULL;
      }
  
      if ( pContext->iTexStage[1] != -1 )
      {
          // Setup texture 1.
          if ( pContext->iTexStage[0] == -1 )
          {
              DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: Should not be "
                            "using tex1 if tex0 not used."));
              SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_BLEND_STAGES );
          }
          
          pTexture1 = GetSurfaceFromHandle(pContext, 
                                           TSSTATE ( pContext->iTexStage[1],
                                                     D3DTSS_TEXTUREMAP ) );
          if (NULL == pTexture1)
          {
              DISPDBG((ERRLVL, "ERROR: Texture Surface (1) is NULL"));
              DBG_EXIT(_D3DChangeTextureP3RX,1);  
              return;            
          }
                                                     
  
  #if DX7_TEXMANAGEMENT  
          if (pTexture1->dwCaps2 & DDSCAPS2_TEXTUREMANAGE)
          {
              if (!_D3D_TM_Preload_Tex_IntoVidMem(pContext, pTexture1))
              {
                  return; // something bad happened !!!
              }
  
              _D3D_TM_TimeStampTexture(pContext->pTextureManager,
                                       pTexture1);        
          }
  #endif // DX7_TEXMANAGEMENT                                                     
  
          pContext->bTex1Valid = 
                      __TXT_ValidateTextureUnitStage(pContext, 
                                                     1, 
                                                     pContext->iTexStage[1], 
                                                     pTexture1 );
          if ( !pContext->bTex1Valid )
          {
              SET_BLEND_ERROR ( pContext,  BSF_INVALID_TEXTURE );
              // Pretend that no texture was set.
              pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
              pContext->bTex1Valid = FALSE;
              pTexture1 = NULL;
          }
      }
      else
      {
          pSoftP3RX->P3RXTextureReadMode1.Enable = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureIndexMode1.Enable = __PERMEDIA_DISABLE;
          pContext->bTex1Valid = FALSE;
          pTexture1 = NULL;
      }
  
      bBothTexturesValid = pContext->bTex0Valid && pContext->bTex1Valid;
  
      if( pContext->bTex0Valid )
      {
          dwT0MipLevels = pContext->TextureStageState[0].m_dwVal[D3DTSS_MAXMIPLEVEL];
  #if DX7_TEXMANAGEMENT        
          if ( dwT0MipLevels < pTexture0->m_dwTexLOD)
          {
              dwT0MipLevels = pTexture0->m_dwTexLOD;
          }
  #endif // DX7_TEXMANAGEMENT
          if (dwT0MipLevels > ((DWORD)(pTexture0->iMipLevels - 1))) 
          {
              // Set the actuall maximum mip level that will be used in rendering
              mipBases.dwTex0ActMaxLevel = pTexture0->iMipLevels - 1;
  
              dwT0MipLevels = 1;
          }
          else
          {
              // Set the actuall maximum mip level that will be used in rendering
              mipBases.dwTex0ActMaxLevel = dwT0MipLevels;
  
              dwT0MipLevels = pTexture0->iMipLevels - dwT0MipLevels;
          }
      }
  
      if( pContext->bTex1Valid )
      {
          ASSERTDD ( pContext->bTex0Valid, 
                     "** _D3DChangeTextureP3RX: tex1 should not be used "
                     "unless tex0 is used as well" );
  
          dwT1MipLevels = pContext->TextureStageState[1].m_dwVal[D3DTSS_MAXMIPLEVEL];
  #if DX7_TEXMANAGEMENT        
          if ( dwT1MipLevels < pTexture1->m_dwTexLOD)
          {
              dwT1MipLevels = pTexture1->m_dwTexLOD;
          }
  #endif // DX7_TEXMANAGEMENT        
          if (dwT1MipLevels > ((DWORD)(pTexture1->iMipLevels - 1))) 
          {
              // Set the actuall maximum mip level that will be used in rendering
              mipBases.dwTex1ActMaxLevel = pTexture1->iMipLevels - 1;
  
              dwT1MipLevels = 1;
          }
          else
          {
              // Set the actuall maximum mip level that will be used in rendering
              mipBases.dwTex1ActMaxLevel = dwT1MipLevels;
  
              dwT1MipLevels = pTexture1->iMipLevels - dwT1MipLevels;
          }
  
          // Enable generation of the second set of texture coordinates.
          // Strictly, we should check whether texture 0 is being used, and
          // if not move the second texture to the first (thus enabling 
          // mipmapping, etc) but that's for later.
          pSoftP3RX->P3RX_P3DeltaMode.TextureEnable1 = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RX_P3DeltaControl.ShareQ = 1;
      }
      else
      {
          // Turn off generation of the second set of texture coordinates
          pSoftP3RX->P3RX_P3DeltaMode.TextureEnable1 = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RX_P3DeltaControl.ShareQ = 0;
      }
  
      if( bBothTexturesValid )
      {
          float totBases, baseRatio;
          DWORD t0Count, res;
  
          totBases = (float)dwT0MipLevels + dwT1MipLevels;
  
          // Adjust mip levels to fit in N - 2 slots as each texture
          // needs at least one slot.
  
          baseRatio = ( P3_TEX_MAP_MAX_LEVEL - 1 ) / totBases;
  
          // Calculate number of slots for texture 0, texture 1 then
          // gets the remainder.
  
          myFtoi( &res, dwT0MipLevels * baseRatio );
          t0Count = 1 + res;
  
          ASSERTDD( t0Count > 0, "No slots for texture 0" );
          ASSERTDD( t0Count <= P3_TEX_MAP_MAX_LEVEL, "No slots for texture 1" );
  
  #define FIXED_ALLOC 0
  #if FIXED_ALLOC
          mipBases.dwTex0MipBase = 0;
          mipBases.dwTex0MipMax  = min( dwT0MipLevels - 1, 7 );
          mipBases.dwTex1MipBase = 8;
          mipBases.dwTex1MipMax  = 8 + min( dwT1MipLevels - 1, 
                                            P3_TEX_MAP_MAX_LEVEL - 8 );
  #else
          mipBases.dwTex0MipBase = 0;
          mipBases.dwTex0MipMax  = min( dwT0MipLevels - 1, t0Count - 1 );
          mipBases.dwTex1MipBase = t0Count;
          mipBases.dwTex1MipMax  = t0Count + min( dwT1MipLevels - 1, 
                                                  P3_TEX_MAP_MAX_LEVEL - t0Count );
  #endif
      }
      else
      {
          if( pContext->bTex0Valid )
          {
              mipBases.dwTex0MipBase = 0;
              mipBases.dwTex0MipMax  = min( dwT0MipLevels - 1, 
                                            P3_TEX_MAP_MAX_LEVEL );
              mipBases.dwTex1MipBase = 0;
              mipBases.dwTex1MipMax  = min( dwT0MipLevels - 1, 
                                            P3_TEX_MAP_MAX_LEVEL );
          }
  
          if( pContext->bTex1Valid )
          {
              mipBases.dwTex0MipBase = 0;
              mipBases.dwTex0MipMax  = min( dwT1MipLevels - 1, 
                                            P3_TEX_MAP_MAX_LEVEL );
              mipBases.dwTex1MipBase = 0;
              mipBases.dwTex1MipMax  = min( dwT1MipLevels - 1, 
                                            P3_TEX_MAP_MAX_LEVEL );
          }
      }
  
      DISPDBG(( DBGLVL, "tex0 base %d", mipBases.dwTex0MipBase ));
      DISPDBG(( DBGLVL, "tex0 max  %d", mipBases.dwTex0MipMax ));
      DISPDBG(( DBGLVL, "tex1 base %d", mipBases.dwTex1MipBase ));
      DISPDBG(( DBGLVL, "tex1 max  %d", mipBases.dwTex1MipMax ));
  
      // Recalculate the LOD biases for per-poly mipmapping
      pContext->MipMapLODBias[TEXSTAGE_0] =
               pow4( pContext->TextureStageState[TEXSTAGE_0].
                                                  m_fVal[D3DTSS_MIPMAPLODBIAS] );
  
      pContext->MipMapLODBias[TEXSTAGE_1] = 
               pow4( pContext->TextureStageState[TEXSTAGE_1].
                                                  m_fVal[D3DTSS_MIPMAPLODBIAS] );
  
      if ( pTexture0 != NULL )
      {
          __TXT_SetupTexture ( pThisDisplay, 
                               0, 
                               pContext->iTexStage[0], 
                               pContext, 
                               pTexture0, 
                               pSoftP3RX, 
                               bBothTexturesValid, 
                               &mipBases);
      }
      
      if ( pTexture1 != NULL )
      {
          __TXT_SetupTexture ( pThisDisplay, 
                               1, 
                               pContext->iTexStage[1], 
                               pContext, 
                               pTexture1, 
                               pSoftP3RX, 
                               bBothTexturesValid, 
                               &mipBases);
  
  #if DX7_PALETTETEXTURE
          if (GET_BLEND_ERROR(pContext) == BSF_TOO_MANY_PALETTES)
          {
              if (pTexture0 && 
                  (pTexture0->dwPaletteHandle == pTexture1->dwPaletteHandle)) 
              {
                  RESET_BLEND_ERROR(pContext);
              }
          }
  #endif
      }
  
      // Fix up the D3DRENDERSTATE_MODULATE case.
      if( pTexture0 != NULL )
      {
          if( pContext->Flags & SURFACE_MODULATE )
          {
              // If SURFACE_MODULATE is set then we must have seen a 
              // DX5-style texture blend
              // Note : bAlpha is true for CI8 and CI4 textures
  
              BOOL bSelectArg1 = pTexture0->pFormatSurface->bAlpha;
  
  #if DX7_PALETTETEXTURE
              if( pTexture0->pixFmt.dwFlags & DDPF_PALETTEINDEXED8 )
              {
                  bSelectArg1 = pTexture0->dwPaletteFlags & DDRAWIPAL_ALPHA;
              }
  #endif            
  
              if( bSelectArg1 )
              {
                  TSSTATE( pContext->iChipStage[0], D3DTSS_ALPHAOP ) = 
                                                              D3DTOP_SELECTARG1;
              }
              else
              {
                  TSSTATE( pContext->iChipStage[0], D3DTSS_ALPHAOP ) = 
                                                              D3DTOP_SELECTARG2;
              }
          }
      }
  
      P3_DMA_GET_BUFFER();
  
      // Textures set up - now do the blending.
  
      // These might be overidden later for special blends.
      dwTexAppTfactor = pContext->RenderStates[D3DRENDERSTATE_TEXTUREFACTOR];
      dwTexComp0Tfactor = pContext->RenderStates[D3DRENDERSTATE_TEXTUREFACTOR];
      dwTexComp1Tfactor = pContext->RenderStates[D3DRENDERSTATE_TEXTUREFACTOR];
  
      // Detect the stage 0 & 1 bumpmap setup code.
      if (( TSSTATE ( TEXSTAGE_0, D3DTSS_TEXTUREMAP ) != 0 ) &&
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_TEXTUREMAP ) != 0 ) &&
          ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) != D3DTOP_DISABLE ) )
      {
          // Looking good for a bumpmap. Now find various special cases.
          // First of all, do they want anything in the stage 2 current colour?
          if (
              ( ( ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG1 ) != D3DTA_CURRENT ) &&
                  ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG2 ) != D3DTA_CURRENT ) ) ||
                ( ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
                  ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG1 ) != D3DTA_CURRENT ) ) ||
                ( ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) == D3DTOP_SELECTARG2 ) &&
                  ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_COLORARG2 ) != D3DTA_CURRENT ) ) ) &&
  
                ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) != D3DTOP_DOTPRODUCT3 ) &&
                ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) != D3DTOP_DOTPRODUCT3 ) )
          {
              // Nope - they don't care what the current colour channel is, and
              // no dotproducts are used in stages 0 and 1 (they affect the alpha 
              // channel) so ignore what is in the colour channel - this is a 
              // bumpmap so far.
  
              // Now see if they want a bumpmap or an inverted bumpmap. People 
              // are so fussy.
              
              // Check first stage.
              if (( ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) ) ||
                    ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) ) ) &&
                  ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) )
              {
                  // First stage fine and not inverted. Check second stage.
                  if (( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) ||
                      ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) )
                  {
                      // Fine, not inverted.
                      pContext->bBumpmapEnabled = TRUE;
                      pContext->bBumpmapInverted = FALSE;
                  }
                  else if (
                      ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) ) ||
                      ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) ) )
                  {
                      // Fine, inverted.
                      pContext->bBumpmapEnabled = TRUE;
                      pContext->bBumpmapInverted = TRUE;
                  }
                  else
                  {
                      // Nope, second stage is no good.
                      pContext->bBumpmapEnabled = FALSE;
                      pContext->bBumpmapInverted = FALSE;
                  }
              }
              else if (
                  ( ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == (D3DTA_TEXTURE | D3DTA_COMPLEMENT) ) ) ||
                    ( ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
                      ( TSSTATEALPHA ( TEXSTAGE_0, D3DTSS_ALPHAARG2 ) == (D3DTA_TEXTURE | D3DTA_COMPLEMENT) ) ) ) &&
                  ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) )
              {
                  // First stage fine and inverted. Check second stage.
                  if (( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) ||
                      ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) )
                  {
                      // Fine, inverted.
                      pContext->bBumpmapEnabled = TRUE;
                      pContext->bBumpmapInverted = TRUE;
                  }
                  else if (
                      ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) ) ||
                      ( ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) &&
                        ( TSSTATEALPHA ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) ) )
                  {
                      // Fine, not inverted.
                      pContext->bBumpmapEnabled = TRUE;
                      pContext->bBumpmapInverted = FALSE;
                  }
                  else
                  {
                      // Nope, second stage is no good.
                      pContext->bBumpmapEnabled = FALSE;
                      pContext->bBumpmapInverted = FALSE;
                  }
              }
              else
              {
                  // Nope, first stage is no good.
                  pContext->bBumpmapEnabled = FALSE;
                  pContext->bBumpmapInverted = FALSE;
              }
          }
          else
          {
              // Could do some more checking, e.g. is all they want in the current colour
              // channel easily available from a single input, e.g. tex0.c, in which case
              // that's fine. A non-bumpmap variant also needs to sense that the first
              // stage is simply a selectarg1/2 and thus can ignore the first stage as
              // a texcomp stage.
              // But that's for later.
              pContext->bBumpmapEnabled = FALSE;
              pContext->bBumpmapInverted = FALSE;
          }
  
      }
      else
      {
          pContext->bBumpmapEnabled = FALSE;
          pContext->bBumpmapInverted = FALSE;
      }
  
      if ( pContext->bBumpmapEnabled )
      {
          DISPDBG((DBGLVL,"Enabling emboss bumpmapping"));
          // Remap stages 1 & 2 out of existence.
          pContext->iChipStage[0] = TEXSTAGE_2;
          pContext->iChipStage[1] = TEXSTAGE_3;
          pContext->iChipStage[2] = TEXSTAGE_4;
          pContext->iChipStage[3] = TEXSTAGE_5;
      }
      else
      {
          // Normal mapping.
          pContext->iChipStage[0] = TEXSTAGE_0;
          pContext->iChipStage[1] = TEXSTAGE_1;
          pContext->iChipStage[2] = TEXSTAGE_2;
          pContext->iChipStage[3] = TEXSTAGE_3;
      }
  
      iLastChipStage = 0;
      // Set these flags to FALSE as the stages are processed.
      bProcessChipStage0 = TRUE;
      bProcessChipStage1 = TRUE;
      bProcessChipStage2 = TRUE;
  
      // Turn on the basic enables.
      pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_ENABLE;
  //  pSoftP3RX->P3RXTextureApplicationMode.EnableKs = __PERMEDIA_DISABLE;
      pSoftP3RX->P3RXTextureApplicationMode.EnableKd = __PERMEDIA_DISABLE;
      pSoftP3RX->P3RXTextureApplicationMode.MotionCompEnable = __PERMEDIA_DISABLE;
  
  
      // Handle chip stage 0.
  
      // Detect the very special-case glossmap+bumpmap code. There is no easy way
      // to generalise it, so the whole chunk gets checked here.
      if ( bProcessChipStage0 && bProcessChipStage1 && bProcessChipStage2 && pContext->bTex0Valid && pContext->bTex1Valid &&
          // Colour channel of stage 0 can be whatever you want.
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) == D3DTOP_MODULATEALPHA_ADDCOLOR ) &&  // Early-out test - nothing uses this!
          ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
          ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == D3DTA_DIFFUSE ) &&
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) &&
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
          ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
          ( TSSTATE ( TEXSTAGE_2, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
          ( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
          ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
          ( TSSTATEINVMASK ( TEXSTAGE_2, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) &&
          ( TSSTATE ( TEXSTAGE_3, D3DTSS_COLOROP ) == D3DTOP_MODULATE2X ) &&
          ( TSSTATE ( TEXSTAGE_3, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
          ( TSSTATE ( TEXSTAGE_3, D3DTSS_COLORARG2 ) == (D3DTA_CURRENT | D3DTA_ALPHAREPLICATE) ) &&
          ( TSSTATE ( TEXSTAGE_3, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
          ( TSSTATE ( TEXSTAGE_3, D3DTSS_ALPHAARG1 ) == D3DTA_TFACTOR ) &&
          ( TSSTATE ( TEXSTAGE_4, D3DTSS_COLOROP ) == D3DTOP_DISABLE ) &&
          ( pContext->iStageTex[0] == 0 ) &&
          ( pContext->iStageTex[1] == 1 ) &&
          ( pContext->iStageTex[2] == 0 )
          )
      {
          int iMode;
          // OK, looks good. Check which way round the bumpmapping is being done.
          if (( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
              ( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG2 ) == (D3DTA_CURRENT | D3DTA_COMPLEMENT) ) )
          {
              // Standard emboss.
              iMode = 0;
          }
          else if (( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG1 ) == (D3DTA_TEXTURE | D3DTA_COMPLEMENT) ) &&
                   ( TSSTATE ( TEXSTAGE_2, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) )
          {
              // Inverted emboss.
              iMode = 1;
          }
          else
          {
              // No good - can't do it.
              iMode = -1;
          }
  
          if ( iMode == -1 )
          {
              // Nope.
              SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_BLEND_STAGES );
              bProcessChipStage0 = FALSE;
              bProcessChipStage1 = FALSE;
              bProcessChipStage2 = FALSE;
              iLastChipStage = 3;
          }
          else
          {
              // Set up the colour channel of tc0.
              // Alpha channel will be overridden later.
              __TXT_TranslateToChipBlendMode(pContext, 
                                             &pContext->TextureStageState[0], 
                                             pSoftP3RX, 
                                             0, 
                                             0);
  
              // Pass through bump.a, maybe inverted.
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_HEIGHTA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_CA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.I = P3RX_TEXCOMP_I_CA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.B = P3RX_TEXCOMP_ARG2;
              if ( iMode )
              {
                  // Inverted bumpmap.
                  pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_ENABLE;
              }
              else
              {
                  // Non-inverted bumpmap.
                  pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
              }
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg2 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertI = __PERMEDIA_DISABLE;
  
              // Do tex1.c * diff.a + current.c
              pSoftP3RX->P3RXTextureCompositeColorMode1.Enable = __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureCompositeColorMode1.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B;
              pSoftP3RX->P3RXTextureCompositeColorMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
              pSoftP3RX->P3RXTextureCompositeColorMode1.Arg1 = P3RX_TEXCOMP_T1C;
              pSoftP3RX->P3RXTextureCompositeColorMode1.Arg2 = P3RX_TEXCOMP_OC;
              pSoftP3RX->P3RXTextureCompositeColorMode1.I = P3RX_TEXCOMP_I_CA;
              pSoftP3RX->P3RXTextureCompositeColorMode1.A = P3RX_TEXCOMP_ARG1;
              pSoftP3RX->P3RXTextureCompositeColorMode1.B = P3RX_TEXCOMP_ARG2;
              pSoftP3RX->P3RXTextureCompositeColorMode1.InvertArg1 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeColorMode1.InvertArg2 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeColorMode1.InvertI = __PERMEDIA_DISABLE;
  
              // Pass through bump.a again.
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Enable = __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg1 = P3RX_TEXCOMP_OA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg2 = P3RX_TEXCOMP_CA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.I = P3RX_TEXCOMP_I_CA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.A = P3RX_TEXCOMP_ARG1;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.B = P3RX_TEXCOMP_ARG2;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertArg1 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertArg2 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertI = __PERMEDIA_DISABLE;
  
              // Do current.c * current.a, by doing B*I+A. A=black, B=current.c, I=current.a
              pSoftP3RX->P3RXTextureApplicationMode.ColorA = P3RX_TEXAPP_A_KC;
              pSoftP3RX->P3RXTextureApplicationMode.ColorB = P3RX_TEXAPP_B_TC;
              pSoftP3RX->P3RXTextureApplicationMode.ColorI = P3RX_TEXAPP_I_TA;
              pSoftP3RX->P3RXTextureApplicationMode.ColorInvertI = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureApplicationMode.ColorOperation = P3RX_TEXAPP_OPERATION_MODULATE_BI_ADD_A;
              // Set the colour channel to black (allow the alpha channel to be preserved).
              dwTexAppTfactor &= 0xff000000;
  
              // Alpha channel selects the constant color.
              pSoftP3RX->P3RXTextureApplicationMode.AlphaA = P3RX_TEXAPP_A_KA;
              pSoftP3RX->P3RXTextureApplicationMode.AlphaB = P3RX_TEXAPP_B_KA;
              pSoftP3RX->P3RXTextureApplicationMode.AlphaInvertI = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureApplicationMode.AlphaOperation = P3RX_TEXAPP_OPERATION_PASS_B;
              
              // Do *2 in alpha-blend unit.
              bAlphaBlendDouble = TRUE;
  
              // We don't actually need the remap (and it doesn't mean much),
              // but it stops erroneous errors being flagged.
              pContext->iChipStage[0] = TEXSTAGE_0;
              pContext->iChipStage[1] = TEXSTAGE_1;
              pContext->iChipStage[2] = TEXSTAGE_3;
              pContext->iChipStage[3] = TEXSTAGE_4;
  
              bProcessChipStage0 = FALSE;
              bProcessChipStage1 = FALSE;
              bProcessChipStage2 = FALSE;
              iLastChipStage = 3;
          }
      }
  
  
  
      // Detect the special-case 3-blend-unit bumpmapping mode.
      // Third stage will be set up by the standard routines - only the first
      // two are special-cased and shoehorned into TexComp0.
      if ( bProcessChipStage0 && !pContext->bBumpmapEnabled && pContext->bTex0Valid && pContext->bTex1Valid &&
          ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLOROP ) == D3DTOP_MODULATE ) &&
          ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) &&
          ( TSSTATE ( TEXSTAGE_0, D3DTSS_COLORARG2 ) == D3DTA_DIFFUSE ) &&
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
  
          ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
          ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
          // ( TSSTATE ( TEXSTAGE_0, D3DTSS_ALPHAARG2 ) == D3DTA_DIFFUSE ) dont care && 
  
          ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
          (
            ( ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == ( D3DTA_TEXTURE | D3DTA_COMPLEMENT ) ) &&
              ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) ||
            ( ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
              ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) )
          ) )
      {
          // Yep, looks good. Set it up.
          ASSERTDD ( pContext->iTexStage[0] == 0, "** _D3DChangeTextureP3RX: textures not correct for special bumpmapping" );
          ASSERTDD ( pContext->iTexStage[1] == 1, "** _D3DChangeTextureP3RX: textures not correct for special bumpmapping" );
  
          pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;
          pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
          pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_T0C;
          pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_CC;
          pSoftP3RX->P3RXTextureCompositeColorMode0.I = P3RX_TEXCOMP_I_CA;
          pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;
          pSoftP3RX->P3RXTextureCompositeColorMode0.B = P3RX_TEXCOMP_ARG2;
          pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_DISABLE;
  
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_HEIGHTA;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_CA;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.I = P3RX_TEXCOMP_I_CA;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.B = P3RX_TEXCOMP_ARG2;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg2 = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertI = __PERMEDIA_DISABLE;
  
          if ( ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) &&
               ( TSSTATE ( TEXSTAGE_1, D3DTSS_ALPHAARG2 ) == ( D3DTA_CURRENT | D3DTA_COMPLEMENT ) ) )
          {
              // Inverted bumpmap.
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_ENABLE;
          }
          else
          {
              // Normal bumpmap.
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
          }
  
          // Done chip stage 0, TSS stage 0 & 1. Move chip stage 1 on a notch.
          pContext->iChipStage[0] = TEXSTAGE_0;
          pContext->iChipStage[1] = TEXSTAGE_2;
          pContext->iChipStage[2] = TEXSTAGE_3;
          pContext->iChipStage[3] = TEXSTAGE_4;
          iLastChipStage = 1;
          bProcessChipStage0 = FALSE;
      }
  
      // Detect a chipstage 0 MODULATE+ADD concatenation. Used by lightmaps.
      // This compresses two stages into texcomp0. The alpha channel has
      // two modes - either one of the two stages just does a selectarg1 (current),
      // and the other gets set up as normal, or (for specular stuff) they
      // both do ADDSIGNED (cur, cur), in which case it's special-cased.
      if ( bProcessChipStage0 && pContext->bBumpmapEnabled &&
          ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) == D3DTOP_MODULATE ) &&
          ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLORARG1 ) == ( D3DTA_CURRENT | D3DTA_ALPHAREPLICATE ) ) &&
          ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLORARG2 ) == D3DTA_DIFFUSE ) &&
          ( ( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_ADD ) &&
              ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) &&
              ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) ) ||
            ( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_SELECTARG1 ) &&
              ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLORARG1 ) == D3DTA_CURRENT ) ) ) )
      {
          // Colour channel is correct and can be squashed down to one stage.
          // Check that the alpha channel is OK.
          int bOK;
          if (( ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
                ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) ||
              ( ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
                ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) )
          {
              // Stage 0 is set to pass-through - set up texcomp0 as stage 1.
              // Colour channel will be overridden later.
              __TXT_TranslateToChipBlendMode(pContext, 
                                             &pContext->TextureStageState[pContext->iChipStage[1]], 
                                             pSoftP3RX, 
                                             pContext->iChipStage[1], 
                                             0);
              bOK = TRUE;
          }
          else if (( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG1 ) &&
                     ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) ) ||
                   ( ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) == D3DTOP_SELECTARG2 ) &&
                     ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) ) )
          {
              // Stage 1 is set to pass-through - set up texcomp0 as stage 0.
              // Colour channel will be overridden later.
              __TXT_TranslateToChipBlendMode(pContext, 
                                             &pContext->TextureStageState[pContext->iChipStage[0]], 
                                             pSoftP3RX, 
                                             pContext->iChipStage[0], 
                                             0);
              bOK = TRUE;
          }
          else if (( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
                   ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) &&
                   ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) &&
                   ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) == D3DTOP_ADDSIGNED ) &&
                   ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG1 ) == D3DTA_CURRENT ) &&
                   ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAARG2 ) == D3DTA_CURRENT ) )
          {
              // Set up to do ( 4 * cur.a - 1.5 ), or rather 4 * ( cur.a - 0.375 )
              dwTexComp0Tfactor = 0x60606060;     // All channels set to (0.375)
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_SUBTRACT_AB;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_FOUR;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg1 = P3RX_TEXCOMP_HEIGHTA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_FA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.I = P3RX_TEXCOMP_I_CA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG1;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.B = P3RX_TEXCOMP_ARG2;
              if ( pContext->bBumpmapInverted )
              {
                  pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_ENABLE;
              }
              else
              {
                  pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
              }
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg2 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertI = __PERMEDIA_DISABLE;
              bOK = TRUE;
          }
          else
          {
              bOK = FALSE;
          }
  
          if ( bOK )
          {
              // OK, the alpha channel is fine - set up the colour channel now.
              pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
              if ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_ADD )
              {
                  // Yes, this is the ((diff.c*cur.a)+tex.c) case.
                  pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AI_ADD_B;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_CC;
                  if ( pContext->iStageTex[pContext->iChipStage[1]] == 0 )
                  {
                      pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_T0C;
                  }
                  else
                  {
                      pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_T1C;
                  }
                  pSoftP3RX->P3RXTextureCompositeColorMode0.I = P3RX_TEXCOMP_I_HA;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.B = P3RX_TEXCOMP_ARG2;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
                  if ( pContext->bBumpmapInverted )
                  {
                      pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_ENABLE;
                  }
                  else
                  {
                      pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_DISABLE;
                  }
              }
              else
              {
                  // Yes, this is just the (diff.c*cur.a) case.
                  pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_MODULATE_AB;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.Arg1 = P3RX_TEXCOMP_CC;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_HEIGHTA;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.I = P3RX_TEXCOMP_I_OA;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG1;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.B = P3RX_TEXCOMP_ARG2;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg1 = __PERMEDIA_DISABLE;
                  pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
                  if ( pContext->bBumpmapInverted )
                  {
                      pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_ENABLE;
                  }
                  else
                  {
                      pSoftP3RX->P3RXTextureCompositeColorMode0.InvertI = __PERMEDIA_DISABLE;
                  }
              }
  
              // Done chip stage 0, TSS stage 0 & 1. Move chip stage 1 on a notch.
              pContext->iChipStage[1]++;
              pContext->iChipStage[2]++;
              pContext->iChipStage[3]++;
              iLastChipStage = 1;
              bProcessChipStage0 = FALSE;
          }
      }
  
  
      if ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) == D3DTOP_DISABLE )
      {
          // Nothing more to do.
          bProcessChipStage0 = FALSE;
          bProcessChipStage1 = FALSE;
          bProcessChipStage2 = FALSE;
      }
  
      if ( pContext->iStageTex[pContext->iChipStage[0]] == -1 )
      {
          // This stage has no texture - is anyone trying to use it?
          if (( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG2 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[0], D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[0], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG1 ) )
          {
              // Panic! In future, we should feed white to the argument using the TFACTOR thing,
              // but for now just disable the rest of the pipeline.
              bProcessChipStage0 = FALSE;
              bProcessChipStage1 = FALSE;
              bProcessChipStage2 = FALSE;
          }
      }
  
      if ( bProcessChipStage0 )
      {
          // Set up stage 0
          DISPDBG((DBGLVL,"Texture Stage 0 is valid - setting it up"));
          __TXT_TranslateToChipBlendMode(pContext, 
                                         &pContext->TextureStageState[pContext->iChipStage[0]], 
                                         pSoftP3RX, 
                                         pContext->iChipStage[0], 
                                         0);
          iLastChipStage = 1;
          bProcessChipStage0 = FALSE;
      }
  
  
      // Handle chip stage 1.
  
  
      if ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) == D3DTOP_DISABLE )
      {
          // Nothing more to do.
          bProcessChipStage1 = FALSE;
          bProcessChipStage2 = FALSE;
      }
  
      if ( pContext->iStageTex[pContext->iChipStage[1]] == -1 )
      {
          // This stage has no texture - is anyone trying to use it?
          if (( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG2 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[1], D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[1], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG1 ) )
          {
              // Panic! In future, we should feed white to the argument using the TFACTOR thing,
              // but for now just disable the rest of the pipeline.
              bProcessChipStage1 = FALSE;
              bProcessChipStage2 = FALSE;
          }
      }
  
  
      if ( bProcessChipStage1 )
      {
          // Set up stage 1
          DISPDBG((DBGLVL,"Texture Stage 1 is valid - setting it up"));
          __TXT_TranslateToChipBlendMode(pContext, 
                                         &pContext->TextureStageState[pContext->iChipStage[1]],
                                         pSoftP3RX, 
                                         pContext->iChipStage[1], 
                                         1);
  
          iLastChipStage = 2;
          bProcessChipStage1 = FALSE;
      }
  
  
  
      // Handle chip stage 2.
  
  
      if ( TSSTATE ( pContext->iChipStage[2], D3DTSS_COLOROP ) == D3DTOP_DISABLE )
      {
          // Nothing more to do.
          bProcessChipStage2 = FALSE;
      }
  
      if ( pContext->iStageTex[pContext->iChipStage[2]] == -1 )
      {
          // This stage has no texture - is anyone trying to use it?
          if (( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_COLORARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_COLOROP ) != D3DTOP_SELECTARG2 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_COLORARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_COLOROP ) != D3DTOP_SELECTARG1 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_ALPHAARG1 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG2 ) ||
              ( TSSTATESELECT ( pContext->iChipStage[2], D3DTSS_ALPHAARG2 ) == D3DTA_TEXTURE ) && ( TSSTATE ( pContext->iChipStage[2], D3DTSS_ALPHAOP ) != D3DTOP_SELECTARG1 ) )
          {
              // Panic! In future, we should feed white to the argument using the TFACTOR thing,
              // but for now just disable the rest of the pipeline.
              bProcessChipStage2 = FALSE;
          }
      }
  
      if ( bProcessChipStage2 )
      {
          // Set up chip stage 2 - texapp.
          DISPDBG((DBGLVL,"Texture Stage 2 is valid - setting it up"));
          DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: Cool - an app is using the "
                       "TexApp unit - tell someone!"));
          __TXT_TranslateToChipBlendMode(pContext, 
                                         &pContext->TextureStageState[pContext->iChipStage[2]],
                                         pSoftP3RX, 
                                         pContext->iChipStage[2], 
                                         2);
          iLastChipStage = 3;
          bProcessChipStage2 = FALSE;
      }
  
      // This must be last.
      if ( TSSTATE ( pContext->iChipStage[3], D3DTSS_COLOROP ) != D3DTOP_DISABLE )
      {
          // Oops - ran out of stages to set up.
          SET_BLEND_ERROR ( pContext,  BSF_TOO_MANY_BLEND_STAGES );
          iLastChipStage = 3;
      }
  
      switch ( iLastChipStage )
      {
          case 0:
              DISPDBG((DBGLVL,"Texture Composite 0 is disabled"));
              // This should have been caught ages ago.
              pSoftP3RX->P3RXTextureCompositeColorMode0.Arg2 = P3RX_TEXCOMP_CC;
              pSoftP3RX->P3RXTextureCompositeColorMode0.InvertArg2 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeColorMode0.A = P3RX_TEXCOMP_ARG2;
              pSoftP3RX->P3RXTextureCompositeColorMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
              pSoftP3RX->P3RXTextureCompositeColorMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
              pSoftP3RX->P3RXTextureCompositeColorMode0.Enable = __PERMEDIA_ENABLE;
              
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Arg2 = P3RX_TEXCOMP_CA;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.InvertArg1 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.A = P3RX_TEXCOMP_ARG2;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode0.Enable = __PERMEDIA_ENABLE;
              // fall through
          case 1:
              DISPDBG((DBGLVL,"Texture Composite 1 is disabled"));
              // Make sure the second stage passes the texel that the first stage generated
              if ( pContext->bStage0DotProduct )
              {
                  // First stage was a dot-product - do the summing (even in the alpha channel).
                  pSoftP3RX->P3RXTextureCompositeColorMode1.Arg2 = P3RX_TEXCOMP_SUM;
                  pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg2 = P3RX_TEXCOMP_SUM;
              }
              else
              {
                  pSoftP3RX->P3RXTextureCompositeColorMode1.Arg2 = P3RX_TEXCOMP_OC;
                  pSoftP3RX->P3RXTextureCompositeAlphaMode1.Arg2 = P3RX_TEXCOMP_OA;
              }
              pSoftP3RX->P3RXTextureCompositeColorMode1.InvertArg2 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeColorMode1.A = P3RX_TEXCOMP_ARG2;
              pSoftP3RX->P3RXTextureCompositeColorMode1.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
              pSoftP3RX->P3RXTextureCompositeColorMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
              pSoftP3RX->P3RXTextureCompositeColorMode1.Enable = __PERMEDIA_ENABLE;
              
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.InvertArg2 = __PERMEDIA_DISABLE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.A = P3RX_TEXCOMP_ARG2;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Operation = P3RX_TEXCOMP_OPERATION_PASS_A;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Scale = P3RX_TEXCOMP_OPERATION_SCALE_ONE;
              pSoftP3RX->P3RXTextureCompositeAlphaMode1.Enable = __PERMEDIA_ENABLE;
              // fall through
          case 2:
              // Texapp to passthrough.
              DISPDBG((DBGLVL,"Texture Application is disabled"));
              pSoftP3RX->P3RXTextureApplicationMode.ColorB = P3RX_TEXAPP_B_TC;
              pSoftP3RX->P3RXTextureApplicationMode.ColorOperation = P3RX_TEXAPP_OPERATION_PASS_B;
              pSoftP3RX->P3RXTextureApplicationMode.ColorInvertI = __PERMEDIA_DISABLE;
  
              pSoftP3RX->P3RXTextureApplicationMode.AlphaB = P3RX_TEXAPP_B_TC;
              pSoftP3RX->P3RXTextureApplicationMode.AlphaOperation = P3RX_TEXAPP_OPERATION_PASS_B;
              pSoftP3RX->P3RXTextureApplicationMode.AlphaInvertI = __PERMEDIA_DISABLE;
              // fall through
          case 3:
              // Nothing else in the pipeline to disable.
              // fall through
              break;
          default:
              DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: iLastChipStage was > 3 - oops."));
              break;
      }
  
  
      // Set up the alpha-map filtering to reflect the single/multi/mip-mapped texturing status
      // All the other colour-key stuff has already been set up.
      if( pContext->bCanChromaKey )
      {
          ASSERTDD ( pTexture0 != NULL, "** _D3DChangeTextureP3RX: pTexture was NULL" );
          if( pTexture0->bMipMap )
          {
              pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit0 = 4;
              pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit1 = 4;
              if ( pContext->bTex0Valid )
              {
                  // Filter mode is irrelevant - this just works!
                  pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit01 = 7;
              }
              else
              {
                  DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: Trying to mipmap without a valid texture."));
                  pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit01 = 8;
              }
              ASSERTDD ( !pContext->bTex1Valid, "** _D3DChangeTextureP3RX: Trying to mipmap with too many textures." );
          }
          else
          {
              // No mipmapping.
              if ( pContext->bTex0Valid )
              {
                  // Don't care about filter mode - this just works.
                  pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit0 = 7;
              }
              else
              {
                  pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit0 = 4;
              }
              if ( pContext->bTex1Valid )
              {
                  // Don't care about filter mode - this just works.
                  pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit1 = 7;
              }
              else
              {
                  pSoftP3RX->P3RXTextureFilterMode.AlphaMapFilterLimit1 = 4;
              }
          }
      }
  
      // Enable Texture Address calculation
      pSoftP3RX->P3RXTextureCoordMode.Enable = __PERMEDIA_ENABLE;
  
      // Enable filtering
      pSoftP3RX->P3RXTextureFilterMode.Enable = __PERMEDIA_ENABLE;
  
  //  // Enable Texel color generation
  //  pSoftP3RX->P3RXTextureApplicationMode.Enable = __PERMEDIA_ENABLE;
  
      // Do we need to share the texture coordinates ?
      if ( pContext->bTex0Valid && pContext->bTex1Valid &&
          ( TSSTATE ( pContext->iTexStage[0], D3DTSS_TEXCOORDINDEX ) ==
            TSSTATE ( pContext->iTexStage[1], D3DTSS_TEXCOORDINDEX ) ) )
      {
          pSoftP3RX->P3RX_P3DeltaControl.ShareS = __PERMEDIA_ENABLE;
          pSoftP3RX->P3RX_P3DeltaControl.ShareT = __PERMEDIA_ENABLE;
      }
      else
      {
          pSoftP3RX->P3RX_P3DeltaControl.ShareS = __PERMEDIA_DISABLE;
          pSoftP3RX->P3RX_P3DeltaControl.ShareT = __PERMEDIA_DISABLE;
      }
  
      P3_ENSURE_DX_SPACE((P3_LOD_LEVELS*2));
      WAIT_FIFO((P3_LOD_LEVELS*2));
      for (i = 0; i < P3_LOD_LEVELS; i++)
      {
          COPY_P3_DATA_OFFSET(TextureMapWidth0, pSoftP3RX->P3RXTextureMapWidth[i], i);
      }
  
      if ( ( GET_BLEND_ERROR(pContext) & BLEND_STATUS_FATAL_FLAG ) != 0 )
      {
          // Got a fatal blend error - signal it to the user.
  
          DISPDBG((ERRLVL,"** _D3DChangeTextureP3RX: invalid blend mode"));
          
          _D3DDisplayWholeTSSPipe ( pContext, WRNLVL );
  
          // And make sure this is re-evaluated next time we render,
          // so that this (probably very munged) invalid setup doesn't cripple
          // any subsequent valid renderstates.
          DIRTY_EVERYTHING(pContext);
      }
  
  
      P3_ENSURE_DX_SPACE(32);
      WAIT_FIFO(32);
      SEND_P3_DATA(TextureCompositeMode, __PERMEDIA_ENABLE);
      COPY_P3_DATA(TextureFilterMode, pSoftP3RX->P3RXTextureFilterMode);
  
      COPY_P3_DATA(TextureApplicationMode, pSoftP3RX->P3RXTextureApplicationMode);
      COPY_P3_DATA(TextureCoordMode, pSoftP3RX->P3RXTextureCoordMode);
      COPY_P3_DATA(DeltaControl, pSoftP3RX->P3RX_P3DeltaControl);
  
      // Copy the current TFACTOR values.
      SEND_P3_DATA ( TextureEnvColor, FORMAT_8888_32BIT_BGR(dwTexAppTfactor) );
      SEND_P3_DATA ( TextureCompositeFactor0, FORMAT_8888_32BIT_BGR(dwTexComp0Tfactor) );
      SEND_P3_DATA ( TextureCompositeFactor1, FORMAT_8888_32BIT_BGR(dwTexComp1Tfactor) );
      DISPDBG((DBGLVL,"Current TFACTOR values. %x %x %x",
                      dwTexAppTfactor,
                      dwTexComp0Tfactor,
                      dwTexComp1Tfactor));
  
      COPY_P3_DATA(DeltaMode, pSoftP3RX->P3RX_P3DeltaMode);
  
      P3_ENSURE_DX_SPACE(32);
      WAIT_FIFO(32);
  
      COPY_P3_DATA(TextureCompositeColorMode0, pSoftP3RX->P3RXTextureCompositeColorMode0);
      COPY_P3_DATA(TextureCompositeColorMode1, pSoftP3RX->P3RXTextureCompositeColorMode1);
      COPY_P3_DATA(TextureCompositeAlphaMode0, pSoftP3RX->P3RXTextureCompositeAlphaMode0);
      COPY_P3_DATA(TextureCompositeAlphaMode1, pSoftP3RX->P3RXTextureCompositeAlphaMode1);
  
      COPY_P3_DATA(TextureReadMode1, pSoftP3RX->P3RXTextureReadMode1);
      COPY_P3_DATA(TextureIndexMode1, pSoftP3RX->P3RXTextureIndexMode1);
  
      COPY_P3_DATA(TextureReadMode0, pSoftP3RX->P3RXTextureReadMode0);
      COPY_P3_DATA(TextureIndexMode0, pSoftP3RX->P3RXTextureIndexMode0);
  
      // Make sure the texture cache is invalidated
      P3RX_INVALIDATECACHE(__PERMEDIA_ENABLE, __PERMEDIA_DISABLE);
      
      SEND_P3_DATA(LOD, 0);
      SEND_P3_DATA(LOD1, 0);
  
      {
          struct LodRange range;
  
          // Clear down whole register
  
          *(DWORD *)&range = 0;
  
          // Each of the Min and Max LODs are in 4.8 format. We only deal 
          // with integer LODs in the range (0, N) so we just compute the 
          // upper value N and shift it up 8 bits.
  
          range.Min = 0;
          range.Max = ( mipBases.dwTex0MipMax - mipBases.dwTex0MipBase ) << 8;
          COPY_P3_DATA( LodRange0, range );
  
          range.Min = 0;
          range.Max = ( mipBases.dwTex1MipMax - mipBases.dwTex1MipBase ) << 8;
          COPY_P3_DATA( LodRange1, range );
      }
  
      *pFlags |= SURFACE_TEXTURING;
  
      // Turn texturing on in the render command
      RENDER_TEXTURE_ENABLE(pContext->RenderCommand);
   
      P3_DMA_COMMIT_BUFFER();
  
      // See if the alpha-blend unit needs to be updated.
      if ( bAlphaBlendDouble != pContext->bAlphaBlendMustDoubleSourceColour )
      {
          pContext->bAlphaBlendMustDoubleSourceColour = bAlphaBlendDouble;
          DIRTY_ALPHABLEND(pContext);
      }
  
      DBG_EXIT(_D3DChangeTextureP3RX,0);  
      
  } // _D3DChangeTextureP3RX
  
  