/*
 * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
 * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sub license,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include <stdio.h>

#include "main/glheader.h"
#include "main/context.h"
#include "main/macros.h"
#include "main/colormac.h"
#include "main/enums.h"
#include "main/dd.h"
#include "main/mm.h"

#include "via_context.h"
#include "via_state.h"
#include "via_tex.h"
#include "via_ioctl.h"
#include "via_3d_reg.h"

#include "swrast/swrast.h"
#include "vbo/vbo.h"
#include "tnl/tnl.h"
#include "swrast_setup/swrast_setup.h"


static GLuint ROP[16] = {
    HC_HROP_BLACK,    /* GL_CLEAR           0                      	*/
    HC_HROP_DPa,      /* GL_AND             s & d                  	*/
    HC_HROP_PDna,     /* GL_AND_REVERSE     s & ~d  			*/
    HC_HROP_P,        /* GL_COPY            s                       	*/
    HC_HROP_DPna,     /* GL_AND_INVERTED    ~s & d                      */
    HC_HROP_D,        /* GL_NOOP            d  		                */
    HC_HROP_DPx,      /* GL_XOR             s ^ d                       */
    HC_HROP_DPo,      /* GL_OR              s | d                       */
    HC_HROP_DPon,     /* GL_NOR             ~(s | d)                    */
    HC_HROP_DPxn,     /* GL_EQUIV           ~(s ^ d)                    */
    HC_HROP_Dn,       /* GL_INVERT          ~d                       	*/
    HC_HROP_PDno,     /* GL_OR_REVERSE      s | ~d                      */
    HC_HROP_Pn,       /* GL_COPY_INVERTED   ~s                       	*/
    HC_HROP_DPno,     /* GL_OR_INVERTED     ~s | d                      */
    HC_HROP_DPan,     /* GL_NAND            ~(s & d)                    */
    HC_HROP_WHITE     /* GL_SET             1                       	*/
};

/*
 * Compute the 'S5.5' lod bias factor from the floating point OpenGL bias.
 */
static GLuint viaComputeLodBias(GLfloat bias)
{
   int b = (int) (bias * 32.0);
   if (b > 511)
      b = 511;
   else if (b < -512)
      b = -512;
   return (GLuint) b;
}

void viaEmitState(struct via_context *vmesa)
{
   struct gl_context *ctx = vmesa->glCtx;
   GLuint i = 0;
   GLuint j = 0;
   RING_VARS;

   viaCheckDma(vmesa, 0x110);
    
   BEGIN_RING(5);
   OUT_RING( HC_HEADER2 );
   OUT_RING( (HC_ParaType_NotTex << 16) );
   OUT_RING( ((HC_SubA_HEnable << 24) | vmesa->regEnable) );
   OUT_RING( ((HC_SubA_HFBBMSKL << 24) | vmesa->regHFBBMSKL) );    
   OUT_RING( ((HC_SubA_HROP << 24) | vmesa->regHROP) );        
   ADVANCE_RING();
    
   if (vmesa->have_hw_stencil) {
      GLuint pitch, format, offset;
	
      format = HC_HZWBFM_24;	    	
      offset = vmesa->depth.offset;
      pitch = vmesa->depth.pitch;
	
      BEGIN_RING(6);
      OUT_RING( (HC_SubA_HZWBBasL << 24) | (offset & 0xFFFFFF) );
      OUT_RING( (HC_SubA_HZWBBasH << 24) | ((offset & 0xFF000000) >> 24) );	
      OUT_RING( (HC_SubA_HZWBType << 24) | HC_HDBLoc_Local | HC_HZONEasFF_MASK |
	         format | pitch );            
      OUT_RING( (HC_SubA_HZWTMD << 24) | vmesa->regHZWTMD );
      OUT_RING( (HC_SubA_HSTREF << 24) | vmesa->regHSTREF );
      OUT_RING( (HC_SubA_HSTMD << 24) | vmesa->regHSTMD );
      ADVANCE_RING();
   }
   else if (vmesa->hasDepth) {
      GLuint pitch, format, offset;
	
      if (vmesa->depthBits == 16) {
	 format = HC_HZWBFM_16;
      }	    
      else {
	 format = HC_HZWBFM_32;
      }
	    
	    
      offset = vmesa->depth.offset;
      pitch = vmesa->depth.pitch;
	
      BEGIN_RING(4);
      OUT_RING( (HC_SubA_HZWBBasL << 24) | (offset & 0xFFFFFF) );
      OUT_RING( (HC_SubA_HZWBBasH << 24) | ((offset & 0xFF000000) >> 24) );
      OUT_RING( (HC_SubA_HZWBType << 24) | HC_HDBLoc_Local | HC_HZONEasFF_MASK |
	         format | pitch );
      OUT_RING( (HC_SubA_HZWTMD << 24) | vmesa->regHZWTMD );
      ADVANCE_RING();
   }
    
   if (ctx->Color.AlphaEnabled) {
      BEGIN_RING(1);
      OUT_RING( (HC_SubA_HATMD << 24) | vmesa->regHATMD );
      ADVANCE_RING();
      i++;
   }   

   if (ctx->Color.BlendEnabled) {
      BEGIN_RING(11);
      OUT_RING( (HC_SubA_HABLCsat << 24) | vmesa->regHABLCsat );
      OUT_RING( (HC_SubA_HABLCop  << 24) | vmesa->regHABLCop ); 
      OUT_RING( (HC_SubA_HABLAsat << 24) | vmesa->regHABLAsat );        
      OUT_RING( (HC_SubA_HABLAop  << 24) | vmesa->regHABLAop ); 
      OUT_RING( (HC_SubA_HABLRCa  << 24) | vmesa->regHABLRCa ); 
      OUT_RING( (HC_SubA_HABLRFCa << 24) | vmesa->regHABLRFCa );        
      OUT_RING( (HC_SubA_HABLRCbias << 24) | vmesa->regHABLRCbias ); 
      OUT_RING( (HC_SubA_HABLRCb  << 24) | vmesa->regHABLRCb ); 
      OUT_RING( (HC_SubA_HABLRFCb << 24) | vmesa->regHABLRFCb );        
      OUT_RING( (HC_SubA_HABLRAa  << 24) | vmesa->regHABLRAa ); 
      OUT_RING( (HC_SubA_HABLRAb  << 24) | vmesa->regHABLRAb ); 
      ADVANCE_RING();
   }
    
   if (ctx->Fog.Enabled) {
      BEGIN_RING(3);
      OUT_RING( (HC_SubA_HFogLF << 24) | vmesa->regHFogLF ); 
      OUT_RING( (HC_SubA_HFogCL << 24) | vmesa->regHFogCL ); 
      OUT_RING( (HC_SubA_HFogCH << 24) | vmesa->regHFogCH ); 
      ADVANCE_RING();
   }
    
   if (ctx->Line.StippleFlag) {
      BEGIN_RING(2);
      OUT_RING( (HC_SubA_HLP << 24) | ctx->Line.StipplePattern ); 
      OUT_RING( (HC_SubA_HLPRF << 24) | ctx->Line.StippleFactor );
      ADVANCE_RING();
   }

   BEGIN_RING(1);
   OUT_RING( (HC_SubA_HPixGC << 24) | 0x0 ); 
   ADVANCE_RING();
    
   QWORD_PAD_RING();


   if (ctx->Texture._EnabledUnits) {
    
      struct gl_texture_unit *texUnit0 = &ctx->Texture.Unit[0];
      struct gl_texture_unit *texUnit1 = &ctx->Texture.Unit[1];

      {
	 GLuint nDummyValue = 0;

	 BEGIN_RING( 8 );
	 OUT_RING( HC_HEADER2 );
	 OUT_RING( (HC_ParaType_Tex << 16) | (HC_SubType_TexGeneral << 24) );

	 if (texUnit0->Enabled && texUnit1->Enabled) {
	    nDummyValue = (HC_SubA_HTXSMD << 24) | (1 << 3);                
	 }
	 else {
	    nDummyValue = (HC_SubA_HTXSMD << 24) | 0;
	 }

	 if (vmesa->clearTexCache) {
	    vmesa->clearTexCache = 0;
	    OUT_RING( nDummyValue | HC_HTXCHCLR_MASK );
	    OUT_RING( nDummyValue );
	 }
	 else {
	    OUT_RING( nDummyValue );
	    OUT_RING( nDummyValue );
	 }

	 OUT_RING( HC_HEADER2 );
	 OUT_RING( HC_ParaType_NotTex << 16 );
	 OUT_RING( (HC_SubA_HEnable << 24) | vmesa->regEnable );
	 OUT_RING( (HC_SubA_HEnable << 24) | vmesa->regEnable );
	 ADVANCE_RING();
      }

      if (texUnit0->Enabled) {
	 struct gl_texture_object *texObj = texUnit0->_Current;
	 struct via_texture_object *t = (struct via_texture_object *)texObj;
	 GLuint numLevels = t->lastLevel - t->firstLevel + 1;
	 if (VIA_DEBUG & DEBUG_STATE) {
	    fprintf(stderr, "texture0 enabled\n");
	 }		
	 if (numLevels == 8) {
	    BEGIN_RING(27);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Tex << 16) |  (0 << 24) );
	    OUT_RING( t->regTexFM );
	    OUT_RING( (HC_SubA_HTXnL0OS << 24) |
	       ((t->lastLevel) << HC_HTXnLVmax_SHIFT) | t->firstLevel );
	    OUT_RING( t->regTexWidthLog2[0] );
	    OUT_RING( t->regTexWidthLog2[1] );
	    OUT_RING( t->regTexHeightLog2[0] );
	    OUT_RING( t->regTexHeightLog2[1] );
	    OUT_RING( t->regTexBaseH[0] );
	    OUT_RING( t->regTexBaseH[1] );
	    OUT_RING( t->regTexBaseH[2] );
	    OUT_RING( t->regTexBaseAndPitch[0].baseL );
	    OUT_RING( t->regTexBaseAndPitch[0].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[1].baseL );
	    OUT_RING( t->regTexBaseAndPitch[1].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[2].baseL );
	    OUT_RING( t->regTexBaseAndPitch[2].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[3].baseL );
	    OUT_RING( t->regTexBaseAndPitch[3].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[4].baseL );
	    OUT_RING( t->regTexBaseAndPitch[4].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[5].baseL );
	    OUT_RING( t->regTexBaseAndPitch[5].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[6].baseL );
	    OUT_RING( t->regTexBaseAndPitch[6].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[7].baseL );
	    OUT_RING( t->regTexBaseAndPitch[7].pitchLog2 );
	    ADVANCE_RING();
	 }
	 else if (numLevels > 1) {

	    BEGIN_RING(12 + numLevels * 2);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Tex << 16) |  (0 << 24) );
	    OUT_RING( t->regTexFM );
	    OUT_RING( (HC_SubA_HTXnL0OS << 24) |
	       ((t->lastLevel) << HC_HTXnLVmax_SHIFT) | t->firstLevel );
	    OUT_RING( t->regTexWidthLog2[0] );
	    OUT_RING( t->regTexHeightLog2[0] );
		
	    if (numLevels > 6) {
	       OUT_RING( t->regTexWidthLog2[1] );
	       OUT_RING( t->regTexHeightLog2[1] );
	    }
                
	    OUT_RING( t->regTexBaseH[0] );
		
	    if (numLevels > 3) {
	       OUT_RING( t->regTexBaseH[1] );
	    }
	    if (numLevels > 6) {
	       OUT_RING( t->regTexBaseH[2] );
	    }
	    if (numLevels > 9)  {
	       OUT_RING( t->regTexBaseH[3] );
	    }

	    for (j = 0; j < numLevels; j++) {
	       OUT_RING( t->regTexBaseAndPitch[j].baseL );
	       OUT_RING( t->regTexBaseAndPitch[j].pitchLog2 );
	    }

	    ADVANCE_RING_VARIABLE();
	 }
	 else {

	    BEGIN_RING(9);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Tex << 16) |  (0 << 24) );
	    OUT_RING( t->regTexFM );
	    OUT_RING( (HC_SubA_HTXnL0OS << 24) |
	       ((t->lastLevel) << HC_HTXnLVmax_SHIFT) | t->firstLevel );
	    OUT_RING( t->regTexWidthLog2[0] );
	    OUT_RING( t->regTexHeightLog2[0] );
	    OUT_RING( t->regTexBaseH[0] );
	    OUT_RING( t->regTexBaseAndPitch[0].baseL );
	    OUT_RING( t->regTexBaseAndPitch[0].pitchLog2 );
	    ADVANCE_RING();
	 }

	 BEGIN_RING(14);
	 OUT_RING( (HC_SubA_HTXnTB << 24) | vmesa->regHTXnTB[0] );
	 OUT_RING( (HC_SubA_HTXnMPMD << 24) | vmesa->regHTXnMPMD[0] );
	 OUT_RING( (HC_SubA_HTXnTBLCsat << 24) | vmesa->regHTXnTBLCsat[0] );
	 OUT_RING( (HC_SubA_HTXnTBLCop << 24) | vmesa->regHTXnTBLCop[0] );
	 OUT_RING( (HC_SubA_HTXnTBLMPfog << 24) | vmesa->regHTXnTBLMPfog[0] );
	 OUT_RING( (HC_SubA_HTXnTBLAsat << 24) | vmesa->regHTXnTBLAsat[0] );
	 OUT_RING( (HC_SubA_HTXnTBLRCb << 24) | vmesa->regHTXnTBLRCb[0] );
	 OUT_RING( (HC_SubA_HTXnTBLRAa << 24) | vmesa->regHTXnTBLRAa[0] );
	 OUT_RING( (HC_SubA_HTXnTBLRFog << 24) | vmesa->regHTXnTBLRFog[0] );
	 OUT_RING( (HC_SubA_HTXnTBLRCa << 24) | vmesa->regHTXnTBLRCa[0] );
	 OUT_RING( (HC_SubA_HTXnTBLRCc << 24) | vmesa->regHTXnTBLRCc[0] );
	 OUT_RING( (HC_SubA_HTXnTBLRCbias << 24) | vmesa->regHTXnTBLRCbias[0] );
	 OUT_RING( (HC_SubA_HTXnTBC << 24) | vmesa->regHTXnTBC[0] );
	 OUT_RING( (HC_SubA_HTXnTRAH << 24) | vmesa->regHTXnTRAH[0] );
/* 	 OUT_RING( (HC_SubA_HTXnCLODu << 24) | vmesa->regHTXnCLOD[0] ); */
	 ADVANCE_RING();

	 /* KW:  This test never succeeds:
	  */
	 if (t->regTexFM == HC_HTXnFM_Index8) {
	    const struct gl_color_table *table = &texObj->Palette;
	    const GLfloat *tableF = table->TableF;

	    BEGIN_RING(2 + table->Size);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Palette << 16) | (0 << 24) );
	    for (j = 0; j < table->Size; j++) 
	       OUT_RING( tableF[j] );
	    ADVANCE_RING();
	       
	 }

	 QWORD_PAD_RING();
      }
	
      if (texUnit1->Enabled) {
	 struct gl_texture_object *texObj = texUnit1->_Current;
	 struct via_texture_object *t = (struct via_texture_object *)texObj;
	 GLuint numLevels = t->lastLevel - t->firstLevel + 1;
	 int texunit = (texUnit0->Enabled ? 1 : 0);
	 if (VIA_DEBUG & DEBUG_STATE) {
	    fprintf(stderr, "texture1 enabled\n");
	 }		
	 if (numLevels == 8) {
	    BEGIN_RING(27);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Tex << 16) |  (texunit << 24) );
	    OUT_RING( t->regTexFM );
	    OUT_RING( (HC_SubA_HTXnL0OS << 24) |
	       ((t->lastLevel) << HC_HTXnLVmax_SHIFT) | t->firstLevel );
	    OUT_RING( t->regTexWidthLog2[0] );
	    OUT_RING( t->regTexWidthLog2[1] );
	    OUT_RING( t->regTexHeightLog2[0] );
	    OUT_RING( t->regTexHeightLog2[1] );
	    OUT_RING( t->regTexBaseH[0] );
	    OUT_RING( t->regTexBaseH[1] );
	    OUT_RING( t->regTexBaseH[2] );
	    OUT_RING( t->regTexBaseAndPitch[0].baseL );
	    OUT_RING( t->regTexBaseAndPitch[0].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[1].baseL );
	    OUT_RING( t->regTexBaseAndPitch[1].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[2].baseL );
	    OUT_RING( t->regTexBaseAndPitch[2].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[3].baseL );
	    OUT_RING( t->regTexBaseAndPitch[3].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[4].baseL );
	    OUT_RING( t->regTexBaseAndPitch[4].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[5].baseL );
	    OUT_RING( t->regTexBaseAndPitch[5].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[6].baseL );
	    OUT_RING( t->regTexBaseAndPitch[6].pitchLog2 );
	    OUT_RING( t->regTexBaseAndPitch[7].baseL );
	    OUT_RING( t->regTexBaseAndPitch[7].pitchLog2 );
	    ADVANCE_RING();
	 }
	 else if (numLevels > 1) {
	    BEGIN_RING(12 + numLevels * 2);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Tex << 16) |  (texunit << 24) );
	    OUT_RING( t->regTexFM );
	    OUT_RING( (HC_SubA_HTXnL0OS << 24) |
	       ((t->lastLevel) << HC_HTXnLVmax_SHIFT) | t->firstLevel );
	    OUT_RING( t->regTexWidthLog2[0] );
	    OUT_RING( t->regTexHeightLog2[0] );
		
	    if (numLevels > 6) {
	       OUT_RING( t->regTexWidthLog2[1] );
	       OUT_RING( t->regTexHeightLog2[1] );
	       i += 2;
	    }
                
	    OUT_RING( t->regTexBaseH[0] );
		
	    if (numLevels > 3) { 
	       OUT_RING( t->regTexBaseH[1] );
	    }
	    if (numLevels > 6) {
	       OUT_RING( t->regTexBaseH[2] );
	    }
	    if (numLevels > 9)  {
	       OUT_RING( t->regTexBaseH[3] );
	    }
		
	    for (j = 0; j < numLevels; j++) {
	       OUT_RING( t->regTexBaseAndPitch[j].baseL );
	       OUT_RING( t->regTexBaseAndPitch[j].pitchLog2 );
	    }
	    ADVANCE_RING_VARIABLE();
	 }
	 else {
	    BEGIN_RING(9);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Tex << 16) |  (texunit << 24) );
	    OUT_RING( t->regTexFM );
	    OUT_RING( (HC_SubA_HTXnL0OS << 24) |
	       ((t->lastLevel) << HC_HTXnLVmax_SHIFT) | t->firstLevel );
	    OUT_RING( t->regTexWidthLog2[0] );
	    OUT_RING( t->regTexHeightLog2[0] );
	    OUT_RING( t->regTexBaseH[0] );
	    OUT_RING( t->regTexBaseAndPitch[0].baseL );
	    OUT_RING( t->regTexBaseAndPitch[0].pitchLog2 );
	    ADVANCE_RING();
	 }

	 BEGIN_RING(14);
	 OUT_RING( (HC_SubA_HTXnTB << 24) | vmesa->regHTXnTB[1] );
	 OUT_RING( (HC_SubA_HTXnMPMD << 24) | vmesa->regHTXnMPMD[1] );
	 OUT_RING( (HC_SubA_HTXnTBLCsat << 24) | vmesa->regHTXnTBLCsat[1] );
	 OUT_RING( (HC_SubA_HTXnTBLCop << 24) | vmesa->regHTXnTBLCop[1] );
	 OUT_RING( (HC_SubA_HTXnTBLMPfog << 24) | vmesa->regHTXnTBLMPfog[1] );
	 OUT_RING( (HC_SubA_HTXnTBLAsat << 24) | vmesa->regHTXnTBLAsat[1] );
	 OUT_RING( (HC_SubA_HTXnTBLRCb << 24) | vmesa->regHTXnTBLRCb[1] );
	 OUT_RING( (HC_SubA_HTXnTBLRAa << 24) | vmesa->regHTXnTBLRAa[1] );
	 OUT_RING( (HC_SubA_HTXnTBLRFog << 24) | vmesa->regHTXnTBLRFog[1] );
	 OUT_RING( (HC_SubA_HTXnTBLRCa << 24) | vmesa->regHTXnTBLRCa[1] );
	 OUT_RING( (HC_SubA_HTXnTBLRCc << 24) | vmesa->regHTXnTBLRCc[1] );
	 OUT_RING( (HC_SubA_HTXnTBLRCbias << 24) | vmesa->regHTXnTBLRCbias[1] );
	 OUT_RING( (HC_SubA_HTXnTBC << 24) | vmesa->regHTXnTBC[1] );
	 OUT_RING( (HC_SubA_HTXnTRAH << 24) | vmesa->regHTXnTRAH[1] );
/* 	 OUT_RING( (HC_SubA_HTXnCLODu << 24) | vmesa->regHTXnCLOD[1] ); */
	 ADVANCE_RING();

	 /* KW:  This test never succeeds:
	  */
	 if (t->regTexFM == HC_HTXnFM_Index8) {
	    const struct gl_color_table *table = &texObj->Palette;
	    const GLfloat *tableF = table->TableF;

	    BEGIN_RING(2 + table->Size);
	    OUT_RING( HC_HEADER2 );
	    OUT_RING( (HC_ParaType_Palette << 16) | (texunit << 24) );
	    for (j = 0; j < table->Size; j++) {
	       OUT_RING( tableF[j] );
	    }
	    ADVANCE_RING();
	 }

	 QWORD_PAD_RING();
      }
   }
    
#if 0
   /* Polygon stipple is broken - for certain stipple values,
    * eg. 0xf0f0f0f0, the hardware will refuse to accept the stipple.
    * Coincidentally, conform generates just such a stipple.
    */
   if (ctx->Polygon.StippleFlag) {
      GLuint *stipple = &ctx->PolygonStipple[0];
      __DRIdrawable *dPriv = vmesa->driDrawable;
      struct via_renderbuffer *const vrb = 
	(struct via_renderbuffer *) dPriv->driverPrivate;
      GLint i;
        
      BEGIN_RING(38);
      OUT_RING( HC_HEADER2 );             

      OUT_RING( ((HC_ParaType_Palette << 16) | (HC_SubType_Stipple << 24)) );
      for (i = 31; i >= 0; i--) {
	 GLint j;
	 GLuint k = 0;

	 /* Need to flip bits left to right:
	  */
	 for (j = 0 ; j < 32; j++)
	    if (stipple[i] & (1<<j))
	       k |= 1 << (31-j);

	 OUT_RING( k );     
      }

      OUT_RING( HC_HEADER2 );                     
      OUT_RING( (HC_ParaType_NotTex << 16) );
      OUT_RING( (HC_SubA_HSPXYOS << 24) );
      OUT_RING( (HC_SubA_HSPXYOS << 24) );

      ADVANCE_RING();
   }
#endif
   
   vmesa->newEmitState = 0;
}


static INLINE GLuint viaPackColor(GLuint bpp,
                                  GLubyte r, GLubyte g,
                                  GLubyte b, GLubyte a)
{
    switch (bpp) {
    case 16:
        return PACK_COLOR_565(r, g, b);
    case 32:
        return PACK_COLOR_8888(a, r, g, b);        
    default:
       assert(0);
       return 0;
   }
}

static void viaBlendEquationSeparate(struct gl_context *ctx,
				     GLenum rgbMode, 
				     GLenum aMode)
{
    if (VIA_DEBUG & DEBUG_STATE) 
       fprintf(stderr, "%s in\n", __FUNCTION__);

    /* GL_EXT_blend_equation_separate not supported */
    ASSERT(rgbMode == aMode);

    /* Can only do GL_ADD equation in hardware */
    FALLBACK(VIA_CONTEXT(ctx), VIA_FALLBACK_BLEND_EQ, 
	     rgbMode != GL_FUNC_ADD_EXT);

    /* BlendEquation sets ColorLogicOpEnabled in an unexpected
     * manner.
     */
    FALLBACK(VIA_CONTEXT(ctx), VIA_FALLBACK_LOGICOP,
             (ctx->Color.ColorLogicOpEnabled &&
              ctx->Color.LogicOp != GL_COPY));
}

static void viaBlendFunc(struct gl_context *ctx, GLenum sfactor, GLenum dfactor)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    GLboolean fallback = GL_FALSE;
    if (VIA_DEBUG & DEBUG_STATE) 
       fprintf(stderr, "%s in\n", __FUNCTION__);

    switch (ctx->Color.Blend[0].SrcRGB) {
    case GL_SRC_ALPHA_SATURATE:  
    case GL_CONSTANT_COLOR:
    case GL_ONE_MINUS_CONSTANT_COLOR:
    case GL_CONSTANT_ALPHA:
    case GL_ONE_MINUS_CONSTANT_ALPHA:
        fallback = GL_TRUE;
        break;
    default:
        break;
    }

    switch (ctx->Color.Blend[0].DstRGB) {
    case GL_CONSTANT_COLOR:
    case GL_ONE_MINUS_CONSTANT_COLOR:
    case GL_CONSTANT_ALPHA:
    case GL_ONE_MINUS_CONSTANT_ALPHA:
        fallback = GL_TRUE;
        break;
    default:
        break;
    }

    FALLBACK(vmesa, VIA_FALLBACK_BLEND_FUNC, fallback);
}

/* Shouldn't be called as the extension is disabled.
 */
static void viaBlendFuncSeparate(struct gl_context *ctx, GLenum sfactorRGB,
                                 GLenum dfactorRGB, GLenum sfactorA,
                                 GLenum dfactorA)
{
    if (dfactorRGB != dfactorA || sfactorRGB != sfactorA) {
        _mesa_error(ctx, GL_INVALID_OPERATION, "glBlendEquation (disabled)");
    }

    viaBlendFunc(ctx, sfactorRGB, dfactorRGB);
}




/* =============================================================
 * Hardware clipping
 */
static void viaScissor(struct gl_context *ctx, GLint x, GLint y,
                       GLsizei w, GLsizei h)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    if (!vmesa->driDrawable)
       return;

    if (VIA_DEBUG & DEBUG_STATE)
       fprintf(stderr, "%s %d,%d %dx%d, drawH %d\n", __FUNCTION__, 
	       x,y,w,h, vmesa->driDrawable->h);

    if (vmesa->scissor) {
        VIA_FLUSH_DMA(vmesa); /* don't pipeline cliprect changes */
    }

    vmesa->scissorRect.x1 = x;
    vmesa->scissorRect.y1 = vmesa->driDrawable->h - y - h;
    vmesa->scissorRect.x2 = x + w;
    vmesa->scissorRect.y2 = vmesa->driDrawable->h - y;
}

static void viaEnable(struct gl_context *ctx, GLenum cap, GLboolean state)
{
   struct via_context *vmesa = VIA_CONTEXT(ctx);

   switch (cap) {
   case GL_SCISSOR_TEST:
      VIA_FLUSH_DMA(vmesa);
      vmesa->scissor = state;
      break;
   default:
      break;
   }
}



/* Fallback to swrast for select and feedback.
 */
static void viaRenderMode(struct gl_context *ctx, GLenum mode)
{
    FALLBACK(VIA_CONTEXT(ctx), VIA_FALLBACK_RENDERMODE, (mode != GL_RENDER));
}


static void viaDrawBuffer(struct gl_context *ctx, GLenum mode)
{
   struct via_context *vmesa = VIA_CONTEXT(ctx);

   if (VIA_DEBUG & (DEBUG_DRI|DEBUG_STATE)) 
      fprintf(stderr, "%s in\n", __FUNCTION__);

   if (!ctx->DrawBuffer)
      return;

   if (ctx->DrawBuffer->_NumColorDrawBuffers != 1) {
      FALLBACK(vmesa, VIA_FALLBACK_DRAW_BUFFER, GL_TRUE);
      return;
   }

   switch ( ctx->DrawBuffer->_ColorDrawBufferIndexes[0] ) {
   case BUFFER_FRONT_LEFT:
      VIA_FLUSH_DMA(vmesa);
      vmesa->drawBuffer = &vmesa->front;
      FALLBACK(vmesa, VIA_FALLBACK_DRAW_BUFFER, GL_FALSE);
      break;
   case BUFFER_BACK_LEFT:
      VIA_FLUSH_DMA(vmesa);
      vmesa->drawBuffer = &vmesa->back;
      FALLBACK(vmesa, VIA_FALLBACK_DRAW_BUFFER, GL_FALSE);
      break;
   default:
      FALLBACK(vmesa, VIA_FALLBACK_DRAW_BUFFER, GL_TRUE);
      return;
   }


   viaXMesaWindowMoved(vmesa);
}

static void viaClearColor(struct gl_context *ctx, const GLfloat color[4])
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    GLubyte pcolor[4];
    CLAMPED_FLOAT_TO_UBYTE(pcolor[0], color[0]);
    CLAMPED_FLOAT_TO_UBYTE(pcolor[1], color[1]);
    CLAMPED_FLOAT_TO_UBYTE(pcolor[2], color[2]);
    CLAMPED_FLOAT_TO_UBYTE(pcolor[3], color[3]);
    vmesa->ClearColor = viaPackColor(vmesa->viaScreen->bitsPerPixel,
                                     pcolor[0], pcolor[1],
                                     pcolor[2], pcolor[3]);
}

#define WRITEMASK_ALPHA_SHIFT 31
#define WRITEMASK_RED_SHIFT   30
#define WRITEMASK_GREEN_SHIFT 29
#define WRITEMASK_BLUE_SHIFT  28

static void viaColorMask(struct gl_context *ctx,
			 GLboolean r, GLboolean g,
			 GLboolean b, GLboolean a)
{
   struct via_context *vmesa = VIA_CONTEXT( ctx );

   if (VIA_DEBUG & DEBUG_STATE)
      fprintf(stderr, "%s r(%d) g(%d) b(%d) a(%d)\n", __FUNCTION__, r, g, b, a);

   vmesa->ClearMask = (((!r) << WRITEMASK_RED_SHIFT) |
		       ((!g) << WRITEMASK_GREEN_SHIFT) |
		       ((!b) << WRITEMASK_BLUE_SHIFT) |
		       ((!a) << WRITEMASK_ALPHA_SHIFT));
}



/* This hardware just isn't capable of private back buffers without
 * glitches and/or a hefty locking scheme.
 */
void viaCalcViewport(struct gl_context *ctx)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    __DRIdrawable *dPriv = vmesa->driDrawable;
    struct via_renderbuffer *const vrb = 
      (struct via_renderbuffer *) dPriv->driverPrivate;
    const GLfloat *v = ctx->Viewport._WindowMap.m;
    GLfloat *m = vmesa->ViewportMatrix.m;
    
    m[MAT_SX] =   v[MAT_SX];
    m[MAT_TX] =   v[MAT_TX] + vrb->drawX + SUBPIXEL_X;
    m[MAT_SY] = - v[MAT_SY];
    m[MAT_TY] = - v[MAT_TY] + vrb->drawY + SUBPIXEL_Y + vrb->drawH;
    m[MAT_SZ] =   v[MAT_SZ] * (1.0 / vmesa->depth_max);
    m[MAT_TZ] =   v[MAT_TZ] * (1.0 / vmesa->depth_max);
}

static void viaViewport(struct gl_context *ctx,
                        GLint x, GLint y,
                        GLsizei width, GLsizei height)
{
    viaCalcViewport(ctx);
}

static void viaDepthRange(struct gl_context *ctx,
                          GLclampd nearval, GLclampd farval)
{
    viaCalcViewport(ctx);
}

void viaInitState(struct gl_context *ctx)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    vmesa->regCmdB = HC_ACMD_HCmdB;
    vmesa->regEnable = HC_HenCW_MASK;

   /* Mesa should do this for us:
    */

   ctx->Driver.BlendEquationSeparate( ctx, 
				      ctx->Color.Blend[0].EquationRGB,
				      ctx->Color.Blend[0].EquationA);

   ctx->Driver.BlendFuncSeparate( ctx,
				  ctx->Color.Blend[0].SrcRGB,
				  ctx->Color.Blend[0].DstRGB,
				  ctx->Color.Blend[0].SrcA,
				  ctx->Color.Blend[0].DstA);

   ctx->Driver.Scissor( ctx, ctx->Scissor.X, ctx->Scissor.Y,
			ctx->Scissor.Width, ctx->Scissor.Height );

   ctx->Driver.DrawBuffer( ctx, ctx->Color.DrawBuffer[0] );
}

/**
 * Convert S and T texture coordinate wrap modes to hardware bits.
 */
static uint32_t
get_wrap_mode( GLenum sWrap, GLenum tWrap )
{
    uint32_t v = 0;


    switch( sWrap ) {
    case GL_REPEAT:
	v |= HC_HTXnMPMD_Srepeat;
	break;
    case GL_CLAMP:
    case GL_CLAMP_TO_EDGE:
	v |= HC_HTXnMPMD_Sclamp;
	break;
    case GL_MIRRORED_REPEAT:
	v |= HC_HTXnMPMD_Smirror;
	break;
    }

    switch( tWrap ) {
    case GL_REPEAT:
	v |= HC_HTXnMPMD_Trepeat;
	break;
    case GL_CLAMP:
    case GL_CLAMP_TO_EDGE:
	v |= HC_HTXnMPMD_Tclamp;
	break;
    case GL_MIRRORED_REPEAT:
	v |= HC_HTXnMPMD_Tmirror;
	break;
    }
    
    return v;
}

static uint32_t
get_minmag_filter( GLenum min, GLenum mag )
{
    uint32_t v = 0;

    switch (min) {
    case GL_NEAREST:
        v = HC_HTXnFLSs_Nearest |
            HC_HTXnFLTs_Nearest;
        break;
    case GL_LINEAR:
        v = HC_HTXnFLSs_Linear |
            HC_HTXnFLTs_Linear;
        break;
    case GL_NEAREST_MIPMAP_NEAREST:
        v = HC_HTXnFLSs_Nearest |
            HC_HTXnFLTs_Nearest;
        v |= HC_HTXnFLDs_Nearest;
        break;
    case GL_LINEAR_MIPMAP_NEAREST:
        v = HC_HTXnFLSs_Linear |
            HC_HTXnFLTs_Linear;
        v |= HC_HTXnFLDs_Nearest;
        break;
    case GL_NEAREST_MIPMAP_LINEAR:
        v = HC_HTXnFLSs_Nearest |
            HC_HTXnFLTs_Nearest;
        v |= HC_HTXnFLDs_Linear;
        break;
    case GL_LINEAR_MIPMAP_LINEAR:
        v = HC_HTXnFLSs_Linear |
            HC_HTXnFLTs_Linear;
        v |= HC_HTXnFLDs_Linear;
        break;
    default:
        break;
    }

    switch (mag) {
    case GL_LINEAR:
        v |= HC_HTXnFLSe_Linear |
             HC_HTXnFLTe_Linear;
	break;
    case GL_NEAREST:
        v |= HC_HTXnFLSe_Nearest |
             HC_HTXnFLTe_Nearest;
	break;
    default:
        break;
    }

    return v;
}


static GLboolean viaChooseTextureState(struct gl_context *ctx) 
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    struct gl_texture_unit *texUnit0 = &ctx->Texture.Unit[0];
    struct gl_texture_unit *texUnit1 = &ctx->Texture.Unit[1];

    if (texUnit0->_ReallyEnabled || texUnit1->_ReallyEnabled) {
        vmesa->regEnable |= HC_HenTXMP_MASK | HC_HenTXCH_MASK | HC_HenTXPP_MASK;

        if (texUnit0->_ReallyEnabled) {
            struct gl_texture_object *texObj = texUnit0->_Current;
   
	    vmesa->regHTXnTB[0] = get_minmag_filter( texObj->Sampler.MinFilter,
						    texObj->Sampler.MagFilter );

	    vmesa->regHTXnMPMD[0] &= ~(HC_HTXnMPMD_SMASK | HC_HTXnMPMD_TMASK);
	    vmesa->regHTXnMPMD[0] |= get_wrap_mode( texObj->Sampler.WrapS,
						   texObj->Sampler.WrapT );

	    vmesa->regHTXnTB[0] &= ~(HC_HTXnTB_TBC_S | HC_HTXnTB_TBC_T);
            if (texObj->Image[0][texObj->BaseLevel]->Border > 0) {
	       vmesa->regHTXnTB[0] |= (HC_HTXnTB_TBC_S | HC_HTXnTB_TBC_T);
	       vmesa->regHTXnTBC[0] = 
		  PACK_COLOR_888(FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[0]),
				 FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[1]),
				 FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[2]));
	       vmesa->regHTXnTRAH[0] = FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[3]);
            }

	    if (texUnit0->LodBias != 0.0f) {
	       GLuint b = viaComputeLodBias(texUnit0->LodBias);
	       vmesa->regHTXnTB[0] &= ~HC_HTXnFLDs_MASK;
	       vmesa->regHTXnTB[0] |= HC_HTXnFLDs_ConstLOD;
	       vmesa->regHTXnCLOD[0] = (b&0x1f) | (((~b)&0x1f)<<10); /* FIXME */
	    }

	    if (!viaTexCombineState( vmesa, texUnit0->_CurrentCombine, 0 )) {
	       if (VIA_DEBUG & DEBUG_TEXTURE)
		  fprintf(stderr, "viaTexCombineState failed for unit 0\n");
	       return GL_FALSE;
	    }
        }

        if (texUnit1->_ReallyEnabled) {
            struct gl_texture_object *texObj = texUnit1->_Current;

	    vmesa->regHTXnTB[1] = get_minmag_filter( texObj->Sampler.MinFilter,
						    texObj->Sampler.MagFilter );
	    vmesa->regHTXnMPMD[1] &= ~(HC_HTXnMPMD_SMASK | HC_HTXnMPMD_TMASK);
	    vmesa->regHTXnMPMD[1] |= get_wrap_mode( texObj->Sampler.WrapS,
						   texObj->Sampler.WrapT );

	    vmesa->regHTXnTB[1] &= ~(HC_HTXnTB_TBC_S | HC_HTXnTB_TBC_T);
            if (texObj->Image[0][texObj->BaseLevel]->Border > 0) {
	       vmesa->regHTXnTB[1] |= (HC_HTXnTB_TBC_S | HC_HTXnTB_TBC_T);
	       vmesa->regHTXnTBC[1] = 
		  PACK_COLOR_888(FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[0]),
				 FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[1]),
				 FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[2]));
	       vmesa->regHTXnTRAH[1] = FLOAT_TO_UBYTE(texObj->Sampler.BorderColor.f[3]);
            }


	    if (texUnit1->LodBias != 0.0f) {
	       GLuint b = viaComputeLodBias(texUnit1->LodBias);
	       vmesa->regHTXnTB[1] &= ~HC_HTXnFLDs_MASK;
	       vmesa->regHTXnTB[1] |= HC_HTXnFLDs_ConstLOD;
	       vmesa->regHTXnCLOD[1] = (b&0x1f) | (((~b)&0x1f)<<10); /* FIXME */
	    }

	    if (!viaTexCombineState( vmesa, texUnit1->_CurrentCombine, 1 )) {
	       if (VIA_DEBUG & DEBUG_TEXTURE)
		  fprintf(stderr, "viaTexCombineState failed for unit 1\n");
	       return GL_FALSE;
	    }
        }
    }
    else {
        vmesa->regEnable &= ~(HC_HenTXMP_MASK | HC_HenTXCH_MASK | 
			      HC_HenTXPP_MASK);
    }
    
    return GL_TRUE;
}

static void viaChooseColorState(struct gl_context *ctx) 
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    GLenum s = ctx->Color.Blend[0].SrcRGB;
    GLenum d = ctx->Color.Blend[0].DstRGB;

    /* The HW's blending equation is:
     * (Ca * FCa + Cbias + Cb * FCb) << Cshift
     */

    if (ctx->Color.BlendEnabled) {
        vmesa->regEnable |= HC_HenABL_MASK;
        /* Ca  -- always from source color.
         */
        vmesa->regHABLCsat = HC_HABLCsat_MASK | HC_HABLCa_OPC | HC_HABLCa_Csrc;
        /* Aa  -- always from source alpha.
         */
        vmesa->regHABLAsat = HC_HABLAsat_MASK | HC_HABLAa_OPA | HC_HABLAa_Asrc;
        /* FCa -- depend on following condition.
         * FAa -- depend on following condition.
         */
        switch (s) {
        case GL_ZERO:
            /* (0, 0, 0, 0)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_OPC | HC_HABLFCa_HABLRCa;
            vmesa->regHABLAsat |= HC_HABLFAa_OPA | HC_HABLFAa_HABLFRA;
            vmesa->regHABLRFCa = 0x0;
            vmesa->regHABLRAa = 0x0;
            break;
        case GL_ONE:
            /* (1, 1, 1, 1)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_InvOPC | HC_HABLFCa_HABLRCa;
            vmesa->regHABLAsat |= HC_HABLFAa_InvOPA | HC_HABLFAa_HABLFRA;
            vmesa->regHABLRFCa = 0x0;
            vmesa->regHABLRAa = 0x0;
            break;
        case GL_SRC_COLOR:
            /* (Rs, Gs, Bs, As)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_OPC | HC_HABLFCa_Csrc;
            vmesa->regHABLAsat |= HC_HABLFAa_OPA | HC_HABLFAa_Asrc;
            break;
        case GL_ONE_MINUS_SRC_COLOR:
            /* (1, 1, 1, 1) - (Rs, Gs, Bs, As)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_InvOPC | HC_HABLFCa_Csrc;
            vmesa->regHABLAsat |= HC_HABLFAa_InvOPA | HC_HABLFAa_Asrc;
            break;
        case GL_DST_COLOR:
            /* (Rd, Gd, Bd, Ad)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_OPC | HC_HABLFCa_Cdst;
            vmesa->regHABLAsat |= HC_HABLFAa_OPA | HC_HABLFAa_Adst;
            break;
        case GL_ONE_MINUS_DST_COLOR:
            /* (1, 1, 1, 1) - (Rd, Gd, Bd, Ad)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_InvOPC | HC_HABLFCa_Cdst;
            vmesa->regHABLAsat |= HC_HABLFAa_InvOPA | HC_HABLFAa_Adst;
            break;
        case GL_SRC_ALPHA:
            /* (As, As, As, As)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_OPC | HC_HABLFCa_Asrc;
            vmesa->regHABLAsat |= HC_HABLFAa_OPA | HC_HABLFAa_Asrc;
            break;
        case GL_ONE_MINUS_SRC_ALPHA:
            /* (1, 1, 1, 1) - (As, As, As, As)
             */
            vmesa->regHABLCsat |= HC_HABLFCa_InvOPC | HC_HABLFCa_Asrc;
            vmesa->regHABLAsat |= HC_HABLFAa_InvOPA | HC_HABLFAa_Asrc;
            break;
        case GL_DST_ALPHA:
            {
                if (vmesa->viaScreen->bitsPerPixel == 16) {
                    /* (1, 1, 1, 1)
                     */
                    vmesa->regHABLCsat |= (HC_HABLFCa_InvOPC | 
					   HC_HABLFCa_HABLRCa);
                    vmesa->regHABLAsat |= (HC_HABLFAa_InvOPA | 
					   HC_HABLFAa_HABLFRA);
                    vmesa->regHABLRFCa = 0x0;
                    vmesa->regHABLRAa = 0x0;
                }
                else {
                    /* (Ad, Ad, Ad, Ad)
                     */
                    vmesa->regHABLCsat |= HC_HABLFCa_OPC | HC_HABLFCa_Adst;
                    vmesa->regHABLAsat |= HC_HABLFAa_OPA | HC_HABLFAa_Adst;
                }
            }
            break;
        case GL_ONE_MINUS_DST_ALPHA:
            {
                if (vmesa->viaScreen->bitsPerPixel == 16) {
                    /* (1, 1, 1, 1) - (1, 1, 1, 1) = (0, 0, 0, 0)
                     */
                    vmesa->regHABLCsat |= HC_HABLFCa_OPC | HC_HABLFCa_HABLRCa;
                    vmesa->regHABLAsat |= HC_HABLFAa_OPA | HC_HABLFAa_HABLFRA;
                    vmesa->regHABLRFCa = 0x0;
                    vmesa->regHABLRAa = 0x0;
                }
                else {
                    /* (1, 1, 1, 1) - (Ad, Ad, Ad, Ad)
                     */
                    vmesa->regHABLCsat |= HC_HABLFCa_InvOPC | HC_HABLFCa_Adst;
                    vmesa->regHABLAsat |= HC_HABLFAa_InvOPA | HC_HABLFAa_Adst;
                }
            }
            break;
        case GL_SRC_ALPHA_SATURATE:
            {
                if (vmesa->viaScreen->bitsPerPixel == 16) {
                    /* (f, f, f, 1), f = min(As, 1 - Ad) = min(As, 1 - 1) = 0
                     * So (f, f, f, 1) = (0, 0, 0, 1)
                     */
                    vmesa->regHABLCsat |= HC_HABLFCa_OPC | HC_HABLFCa_HABLRCa;
                    vmesa->regHABLAsat |= (HC_HABLFAa_InvOPA | 
					   HC_HABLFAa_HABLFRA);
                    vmesa->regHABLRFCa = 0x0;
                    vmesa->regHABLRAa = 0x0;
                }
                else {
                    /* (f, f, f, 1), f = min(As, 1 - Ad)
                     */
                    vmesa->regHABLCsat |= (HC_HABLFCa_OPC | 
					   HC_HABLFCa_mimAsrcInvAdst);
                    vmesa->regHABLAsat |= (HC_HABLFAa_InvOPA | 
					   HC_HABLFAa_HABLFRA);
                    vmesa->regHABLRFCa = 0x0;
                    vmesa->regHABLRAa = 0x0;
                }
            }
            break;
        }

        /* Op is add.
         */

        /* bias is 0.
         */
        vmesa->regHABLCsat |= HC_HABLCbias_HABLRCbias;
        vmesa->regHABLAsat |= HC_HABLAbias_HABLRAbias;

        /* Cb  -- always from destination color.
         */
        vmesa->regHABLCop = HC_HABLCb_OPC | HC_HABLCb_Cdst;
        /* Ab  -- always from destination alpha.
         */
        vmesa->regHABLAop = HC_HABLAb_OPA | HC_HABLAb_Adst;
        /* FCb -- depend on following condition.
         */
        switch (d) {
        case GL_ZERO:
            /* (0, 0, 0, 0)
             */
            vmesa->regHABLCop |= HC_HABLFCb_OPC | HC_HABLFCb_HABLRCb;
            vmesa->regHABLAop |= HC_HABLFAb_OPA | HC_HABLFAb_HABLFRA;
            vmesa->regHABLRFCb = 0x0;
            vmesa->regHABLRAb = 0x0;
            break;
        case GL_ONE:
            /* (1, 1, 1, 1)
             */
            vmesa->regHABLCop |= HC_HABLFCb_InvOPC | HC_HABLFCb_HABLRCb;
            vmesa->regHABLAop |= HC_HABLFAb_InvOPA | HC_HABLFAb_HABLFRA;
            vmesa->regHABLRFCb = 0x0;
            vmesa->regHABLRAb = 0x0;
            break;
        case GL_SRC_COLOR:
            /* (Rs, Gs, Bs, As)
             */
            vmesa->regHABLCop |= HC_HABLFCb_OPC | HC_HABLFCb_Csrc;
            vmesa->regHABLAop |= HC_HABLFAb_OPA | HC_HABLFAb_Asrc;
            break;
        case GL_ONE_MINUS_SRC_COLOR:
            /* (1, 1, 1, 1) - (Rs, Gs, Bs, As)
             */
            vmesa->regHABLCop |= HC_HABLFCb_InvOPC | HC_HABLFCb_Csrc;
            vmesa->regHABLAop |= HC_HABLFAb_InvOPA | HC_HABLFAb_Asrc;
            break;
        case GL_DST_COLOR:
            /* (Rd, Gd, Bd, Ad)
             */
            vmesa->regHABLCop |= HC_HABLFCb_OPC | HC_HABLFCb_Cdst;
            vmesa->regHABLAop |= HC_HABLFAb_OPA | HC_HABLFAb_Adst;
            break;
        case GL_ONE_MINUS_DST_COLOR:
            /* (1, 1, 1, 1) - (Rd, Gd, Bd, Ad)
             */
            vmesa->regHABLCop |= HC_HABLFCb_InvOPC | HC_HABLFCb_Cdst;
            vmesa->regHABLAop |= HC_HABLFAb_InvOPA | HC_HABLFAb_Adst;
            break;
        case GL_SRC_ALPHA:
            /* (As, As, As, As)
             */
            vmesa->regHABLCop |= HC_HABLFCb_OPC | HC_HABLFCb_Asrc;
            vmesa->regHABLAop |= HC_HABLFAb_OPA | HC_HABLFAb_Asrc;
            break;
        case GL_ONE_MINUS_SRC_ALPHA:
            /* (1, 1, 1, 1) - (As, As, As, As)
             */
            vmesa->regHABLCop |= HC_HABLFCb_InvOPC | HC_HABLFCb_Asrc;
            vmesa->regHABLAop |= HC_HABLFAb_InvOPA | HC_HABLFAb_Asrc;
            break;
        case GL_DST_ALPHA:
            {
                if (vmesa->viaScreen->bitsPerPixel == 16) {
                    /* (1, 1, 1, 1)
                     */
                    vmesa->regHABLCop |= HC_HABLFCb_InvOPC | HC_HABLFCb_HABLRCb;
                    vmesa->regHABLAop |= HC_HABLFAb_InvOPA | HC_HABLFAb_HABLFRA;
                    vmesa->regHABLRFCb = 0x0;
                    vmesa->regHABLRAb = 0x0;
                }
                else {
                    /* (Ad, Ad, Ad, Ad)
                     */
                    vmesa->regHABLCop |= HC_HABLFCb_OPC | HC_HABLFCb_Adst;
                    vmesa->regHABLAop |= HC_HABLFAb_OPA | HC_HABLFAb_Adst;
                }
            }
            break;
        case GL_ONE_MINUS_DST_ALPHA:
            {
                if (vmesa->viaScreen->bitsPerPixel == 16) {
                    /* (1, 1, 1, 1) - (1, 1, 1, 1) = (0, 0, 0, 0)
                     */
                    vmesa->regHABLCop |= HC_HABLFCb_OPC | HC_HABLFCb_HABLRCb;
                    vmesa->regHABLAop |= HC_HABLFAb_OPA | HC_HABLFAb_HABLFRA;
                    vmesa->regHABLRFCb = 0x0;
                    vmesa->regHABLRAb = 0x0;
                }
                else {
                    /* (1, 1, 1, 1) - (Ad, Ad, Ad, Ad)
                     */
                    vmesa->regHABLCop |= HC_HABLFCb_InvOPC | HC_HABLFCb_Adst;
                    vmesa->regHABLAop |= HC_HABLFAb_InvOPA | HC_HABLFAb_Adst;
                }
            }
            break;
        default:
            vmesa->regHABLCop |= HC_HABLFCb_OPC | HC_HABLFCb_HABLRCb;
            vmesa->regHABLAop |= HC_HABLFAb_OPA | HC_HABLFAb_HABLFRA;
            vmesa->regHABLRFCb = 0x0;
            vmesa->regHABLRAb = 0x0;
            break;
        }

        if (vmesa->viaScreen->bitsPerPixel <= 16)
            vmesa->regEnable &= ~HC_HenDT_MASK;

    }
    else {
        vmesa->regEnable &= (~HC_HenABL_MASK);
    }

    if (ctx->Color.AlphaEnabled) {
        vmesa->regEnable |= HC_HenAT_MASK;
        vmesa->regHATMD = FLOAT_TO_UBYTE(ctx->Color.AlphaRef) |
            ((ctx->Color.AlphaFunc - GL_NEVER) << 8);
    }
    else {
        vmesa->regEnable &= (~HC_HenAT_MASK);
    }

    if (ctx->Color.DitherFlag && (vmesa->viaScreen->bitsPerPixel < 32)) {
        if (ctx->Color.BlendEnabled) {
            vmesa->regEnable &= ~HC_HenDT_MASK;
        }
        else {
            vmesa->regEnable |= HC_HenDT_MASK;
        }
    }


    vmesa->regEnable &= ~HC_HenDT_MASK;

    if (ctx->Color.ColorLogicOpEnabled) 
        vmesa->regHROP = ROP[ctx->Color.LogicOp & 0xF];
    else
        vmesa->regHROP = HC_HROP_P;

    vmesa->regHFBBMSKL = PACK_COLOR_888(ctx->Color.ColorMask[0][0],
					ctx->Color.ColorMask[0][1],
					ctx->Color.ColorMask[0][2]);
    vmesa->regHROP |= ctx->Color.ColorMask[0][3];

    if (ctx->Color.ColorMask[0][3])
        vmesa->regEnable |= HC_HenAW_MASK;
    else
        vmesa->regEnable &= ~HC_HenAW_MASK;
}

static void viaChooseFogState(struct gl_context *ctx) 
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    if (ctx->Fog.Enabled) {
        GLubyte r, g, b, a;

        vmesa->regEnable |= HC_HenFOG_MASK;

        /* Use fog equation 0 (OpenGL's default) & local fog.
         */
        vmesa->regHFogLF = 0x0;

        r = (GLubyte)(ctx->Fog.Color[0] * 255.0F);
        g = (GLubyte)(ctx->Fog.Color[1] * 255.0F);
        b = (GLubyte)(ctx->Fog.Color[2] * 255.0F);
        a = (GLubyte)(ctx->Fog.Color[3] * 255.0F);
        vmesa->regHFogCL = (r << 16) | (g << 8) | b;
        vmesa->regHFogCH = a;
    }
    else {
        vmesa->regEnable &= ~HC_HenFOG_MASK;
    }
}

static void viaChooseDepthState(struct gl_context *ctx) 
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    if (ctx->Depth.Test) {
        vmesa->regEnable |= HC_HenZT_MASK;
        if (ctx->Depth.Mask)
            vmesa->regEnable |= HC_HenZW_MASK;
        else
            vmesa->regEnable &= (~HC_HenZW_MASK);
	vmesa->regHZWTMD = (ctx->Depth.Func - GL_NEVER) << 16;
	
    }
    else {
        vmesa->regEnable &= ~HC_HenZT_MASK;
        
        /*=* [DBG] racer : can't display cars in car selection menu *=*/
	/*if (ctx->Depth.Mask)
            vmesa->regEnable |= HC_HenZW_MASK;
        else
            vmesa->regEnable &= (~HC_HenZW_MASK);*/
	vmesa->regEnable &= (~HC_HenZW_MASK);
    }
}

static void viaChooseLineState(struct gl_context *ctx) 
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    if (ctx->Line.StippleFlag) {
        vmesa->regEnable |= HC_HenLP_MASK;
        vmesa->regHLP = ctx->Line.StipplePattern;
        vmesa->regHLPRF = ctx->Line.StippleFactor;
    }
    else {
        vmesa->regEnable &= ~HC_HenLP_MASK;
    }
}

static void viaChoosePolygonState(struct gl_context *ctx) 
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

#if 0
    /* Polygon stipple is broken - see via_state.c
     */
    if (ctx->Polygon.StippleFlag) {
        vmesa->regEnable |= HC_HenSP_MASK;
    }
    else {
        vmesa->regEnable &= ~HC_HenSP_MASK;
    }
#else
    FALLBACK(vmesa, VIA_FALLBACK_POLY_STIPPLE, 
	     ctx->Polygon.StippleFlag);
#endif

    if (ctx->Polygon.CullFlag) {
        vmesa->regEnable |= HC_HenFBCull_MASK;
    }
    else {
        vmesa->regEnable &= ~HC_HenFBCull_MASK;
    }
}

static void viaChooseStencilState(struct gl_context *ctx) 
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);
    
    if (ctx->Stencil._Enabled) {
        GLuint temp;

        vmesa->regEnable |= HC_HenST_MASK;
        temp = (ctx->Stencil.Ref[0] & 0xFF) << HC_HSTREF_SHIFT;
        temp |= 0xFF << HC_HSTOPMSK_SHIFT;
        temp |= (ctx->Stencil.ValueMask[0] & 0xFF);
        vmesa->regHSTREF = temp;

        temp = (ctx->Stencil.Function[0] - GL_NEVER) << 16;

        switch (ctx->Stencil.FailFunc[0]) {
        case GL_KEEP:
            temp |= HC_HSTOPSF_KEEP;
            break;
        case GL_ZERO:
            temp |= HC_HSTOPSF_ZERO;
            break;
        case GL_REPLACE:
            temp |= HC_HSTOPSF_REPLACE;
            break;
        case GL_INVERT:
            temp |= HC_HSTOPSF_INVERT;
            break;
        case GL_INCR:
            temp |= HC_HSTOPSF_INCR;
            break;
        case GL_DECR:
            temp |= HC_HSTOPSF_DECR;
            break;
        }

        switch (ctx->Stencil.ZFailFunc[0]) {
        case GL_KEEP:
            temp |= HC_HSTOPSPZF_KEEP;
            break;
        case GL_ZERO:
            temp |= HC_HSTOPSPZF_ZERO;
            break;
        case GL_REPLACE:
            temp |= HC_HSTOPSPZF_REPLACE;
            break;
        case GL_INVERT:
            temp |= HC_HSTOPSPZF_INVERT;
            break;
        case GL_INCR:
            temp |= HC_HSTOPSPZF_INCR;
            break;
        case GL_DECR:
            temp |= HC_HSTOPSPZF_DECR;
            break;
        }

        switch (ctx->Stencil.ZPassFunc[0]) {
        case GL_KEEP:
            temp |= HC_HSTOPSPZP_KEEP;
            break;
        case GL_ZERO:
            temp |= HC_HSTOPSPZP_ZERO;
            break;
        case GL_REPLACE:
            temp |= HC_HSTOPSPZP_REPLACE;
            break;
        case GL_INVERT:
            temp |= HC_HSTOPSPZP_INVERT;
            break;
        case GL_INCR:
            temp |= HC_HSTOPSPZP_INCR;
            break;
        case GL_DECR:
            temp |= HC_HSTOPSPZP_DECR;
            break;
        }
        vmesa->regHSTMD = temp;
    }
    else {
        vmesa->regEnable &= ~HC_HenST_MASK;
    }
}



static void viaChooseTriangle(struct gl_context *ctx) 
{       
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    if (ctx->Polygon.CullFlag == GL_TRUE) {
        switch (ctx->Polygon.CullFaceMode) {
        case GL_FRONT:
            if (ctx->Polygon.FrontFace == GL_CCW)
                vmesa->regCmdB |= HC_HBFace_MASK;
            else
                vmesa->regCmdB &= ~HC_HBFace_MASK;
            break;
        case GL_BACK:
            if (ctx->Polygon.FrontFace == GL_CW)
                vmesa->regCmdB |= HC_HBFace_MASK;
            else
                vmesa->regCmdB &= ~HC_HBFace_MASK;
            break;
        case GL_FRONT_AND_BACK:
            return;
        }
    }
}

void viaValidateState( struct gl_context *ctx )
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    if (vmesa->newState & _NEW_TEXTURE) {
       GLboolean ok = (viaChooseTextureState(ctx) &&
		       viaUpdateTextureState(ctx));

       FALLBACK(vmesa, VIA_FALLBACK_TEXTURE, !ok);
    }

    if (vmesa->newState & _NEW_COLOR)
        viaChooseColorState(ctx);

    if (vmesa->newState & _NEW_DEPTH)
        viaChooseDepthState(ctx);

    if (vmesa->newState & _NEW_FOG)
        viaChooseFogState(ctx);

    if (vmesa->newState & _NEW_LINE)
        viaChooseLineState(ctx);

    if (vmesa->newState & (_NEW_POLYGON | _NEW_POLYGONSTIPPLE)) {
        viaChoosePolygonState(ctx);
	viaChooseTriangle(ctx);
    }

    if ((vmesa->newState & _NEW_STENCIL) && vmesa->have_hw_stencil)
        viaChooseStencilState(ctx);
    
    if (ctx->_TriangleCaps & DD_SEPARATE_SPECULAR)
        vmesa->regEnable |= HC_HenCS_MASK;
    else
        vmesa->regEnable &= ~HC_HenCS_MASK;

    if (ctx->Point.SmoothFlag ||
	ctx->Line.SmoothFlag ||
	ctx->Polygon.SmoothFlag)
        vmesa->regEnable |= HC_HenAA_MASK;
    else 
        vmesa->regEnable &= ~HC_HenAA_MASK;

    vmesa->newEmitState |= vmesa->newState;
    vmesa->newState = 0;
}

static void viaInvalidateState(struct gl_context *ctx, GLuint newState)
{
    struct via_context *vmesa = VIA_CONTEXT(ctx);

    VIA_FINISH_PRIM( vmesa );
    vmesa->newState |= newState;

    _swrast_InvalidateState(ctx, newState);
    _swsetup_InvalidateState(ctx, newState);
    _vbo_InvalidateState(ctx, newState);
    _tnl_InvalidateState(ctx, newState);
}

void viaInitStateFuncs(struct gl_context *ctx)
{
    /* Callbacks for internal Mesa events.
     */
    ctx->Driver.UpdateState = viaInvalidateState;

    /* API callbacks
     */
    ctx->Driver.BlendEquationSeparate = viaBlendEquationSeparate;
    ctx->Driver.BlendFuncSeparate = viaBlendFuncSeparate;
    ctx->Driver.ClearColor = viaClearColor;
    ctx->Driver.ColorMask = viaColorMask;
    ctx->Driver.DrawBuffer = viaDrawBuffer;
    ctx->Driver.RenderMode = viaRenderMode;
    ctx->Driver.Scissor = viaScissor;
    ctx->Driver.DepthRange = viaDepthRange;
    ctx->Driver.Viewport = viaViewport;
    ctx->Driver.Enable = viaEnable;

    /* XXX this should go away */
    ctx->Driver.ResizeBuffers = viaReAllocateBuffers;
}
