;-----------------------------------------------------------------------------
;
; This file contains x86 assembly attribute setup.
;
; Copyright (C) Microsoft Corporation, 1997.
;
; WARNING WARNING WARNING
; This assembly file generated from mas file.
; EDIT THE MAS FILE.
; I warned you.
; WARNING WARNING WARNING
;
;-----------------------------------------------------------------------------

include(`m4hdr.mh')dnl
.386p
.MODEL FLAT

INCLUDE offs_acp.inc
INCLUDE profile.inc
INCLUDE texdiff.inc

EXTERN @ComputeTableFog@8:NEAR
EXTERN _g_fOne:DWORD

.CODE
dnl
dnl d_CorrectAttr
dnl
dnl Generates code to compute the DX and DY steps, the
dnl corrected scaled initial value and the NC and CY steps.
dnl
dnl ebx is assumed to be the PSETUPCTX.
dnl $1 is the ATTRSET offset of the value being corrected.
dnl $2 is the name of the uncorrected initial value.
dnl $3 is the name of the DAttr20 value.
dnl $4 is the name of the DAttr10 value.
dnl $5 is the name of the scale to apply to the initial value before
dnl    correction.
dnl
define(`d_CorrectAttr',
`        fld DWORD PTR [ebx+SCTX_fNY10]          ; NY10
        fmul DWORD PTR $3                       ; DXp
        fld DWORD PTR [ebx+SCTX_fNY20]          ; NY20 DXp
        fmul DWORD PTR $4                       ; DXn DXp
        fld DWORD PTR [ebx+SCTX_fNX20]          ; NX20 DXn DXp
        fmul DWORD PTR $4                       ; DYp DXn DXp
        fxch st(2)                              ; DXp DXn DYp
        fld DWORD PTR [ebx+SCTX_fNX10]          ; NX10 DXp DXn DYp
        fmul DWORD PTR $3                       ; DYn DXp DXn DYp
        fxch st(2)                              ; DXn DXp DYn DYp
        fsubp st(1), st(0)                      ; DX DYn DYp
        fld DWORD PTR $2                        ; 0 DX DYn DYp
        fmul DWORD PTR $5                       ; 0 DX DYn DYp
        fxch st(3)                              ; DYp DX DYn 0
        fld DWORD PTR [ebx+SCTX_fDX]            ; fDX DYp DX DYn 0
        fmul st(0), st(2)                       ; cX DYp DX DYn 0
        fxch st(3)                              ; DYn DYp DX cX 0
        fsubp st(1), st(0)                      ; DY DX cX 0
        fxch st(3)                              ; 0 DX cX DY
        fld DWORD PTR [ebx+SCTX_fX20NC]         ; iNC 0 DX cX DY
        fmul st(0), st(2)                       ; XNC 0 DX cX DY
        fxch st(3)                              ; cX 0 DX XNC DY
        faddp st(1), st(0)                      ; c0X DX XNC DY
        fld DWORD PTR [ebx+SCTX_fDY]            ; fDY c0X DX XNC DY
        fmul st(0), st(4)                       ; cY c0X DX XNC DY
        fxch st(3)                              ; XNC c0X DX cY DY
        fadd st(0), st(4)                       ; NC c0X DX cY DY
        fxch st(1)                              ; c0X NC DX cY DY
        fld DWORD PTR [ebx+SCTX_fX20CY]         ; iCY c0X NC DX cY DY
        fmul st(0), st(3)                       ; XCY c0X NC DX cY DY
        fxch st(4)                              ; cY c0X NC DX XCY DY
        faddp st(1), st(0)                      ; c0 NC DX XCY DY
        fxch st(1)                              ; NC c0 DX XCY DY
        fstp DWORD PTR [ebx+SCTX_DAttrNC+$1]    ; c0 DX XCY DY
        fxch st(1)                              ; DX c0 XCY DY
        fstp DWORD PTR [ebx+SCTX_DAttrDX+$1]    ; c0 XCY DY
        fxch st(1)                              ; XCY c0 DY
        fadd st(0), st(2)                       ; CY c0 DY
        fxch st(1)                              ; c0 CY DY
        fstp DWORD PTR [ebx+SCTX_Attr+$1]       ; CY DY
        fxch st(1)                              ; DY CY
        fstp DWORD PTR [ebx+SCTX_DAttrDY+$1]    ; CY
        fstp DWORD PTR [ebx+SCTX_DAttrCY+$1]    ;
')dnl
dnl
dnl d_NegateDeltas
dnl
dnl If X_DEC, negates the given list of deltas in DAttrDX.
dnl
dnl $1 is the name of the block for labels.
dnl Following arguments are ATTRSET offsets in the DAttrDX to process.
dnl
dnl Leaves uFlags in eax.
dnl
define(`d_NegateDeltaLoop',
`        mov eax, [ebx+SCTX_DAttrDX+$1]
        xor eax, 080000000h
        mov [ebx+SCTX_DAttrDX+$1], eax
ifelse(eval($# > 1), `1', `d_NegateDeltaLoop(d_shift($@))')')dnl
dnl
define(`d_NegateDeltas',
`        mov eax, [ebx+SCTX_uFlags]
        test eax, TRIF_X_DEC
        jz L_NDXDZ_$1

        ; X_DEC.  Negate deltas.
d_NegateDeltaLoop(d_shift($@))dnl
        mov eax, [ebx+SCTX_uFlags]
L_NDXDZ_$1:
')dnl
dnl
dnl d_CheckRpDeltas
dnl
dnl Checks given list of deltas against the given limit for
dnl setting of TRIF_RASTPRIM_OVERFLOW.  The delta comparisons
dnl can be done in pure integer because the numbers involved are
dnl always positive.
dnl
dnl RASTPRIM filling is usually done in the non-overflow block so
dnl arbitrary processing can be done there by defining the d_CRPD_Post macro
dnl before invocation of this one.
dnl
dnl $1 is the name of the block for labels.
dnl $2 is the name of the limit.
dnl Following arguments are deltas offsets in the SETUPCTX to check.
dnl
dnl Relies on uFlags being in eax at the start.  Leaves uFlags in eax.
dnl
define(`d_CheckRpDeltaLoop',
`        mov eax, [ebx+$3]
        and eax, 07fffffffh
        cmp eax, $2
        jge L_RPOD_$1
ifelse(eval($# > 3), `1',
       `d_CheckRpDeltaLoop($1, $2, d_shift(d_shift(d_shift($@))))')')dnl
dnl
define(`d_CheckRpDeltas',
`        test eax, TRIF_RASTPRIM_OVERFLOW
        jnz L_RPOD_NoLoad_$1
d_CheckRpDeltaLoop($@)dnl

        ; No overflow.  Do valid delta processing.
d_CRPD_Post(d_shift($@))
        mov eax, [ebx+SCTX_uFlags]
        jmp L_RP_Exit_$1

L_RPOD_$1:
        ; Overflow detected.
        mov eax, [ebx+SCTX_uFlags]
L_RPOD_NoLoad_$1:
        or eax, TRIF_RASTPRIM_OVERFLOW
        mov [ebx+SCTX_uFlags], eax

L_RP_Exit_$1:
')dnl
dnl
dnl d_CheckFxDeltas
dnl
dnl Checks given list of deltas against the given limit for
dnl setting of TRIF_FIXED_OVERFLOW.  The delta comparisons
dnl can be done in pure integer because the numbers involved are
dnl always positive.
dnl
dnl $1 is the name of the block for labels.
dnl $2 is the name of the limit.
dnl Following arguments are ATTRSET offsets in NC and CY to check.
dnl
define(`d_CheckFxDeltaLoop',
`        mov eax, [ebx+SCTX_DAttrNC+$3]
        and eax, 07fffffffh
        cmp eax, $2
        jge L_FXOD_$1
        mov eax, [ebx+SCTX_DAttrCY+$3]
        and eax, 07fffffffh
        cmp eax, $2
        jge L_FXOD_$1
ifelse(eval($# > 3), `1',
       `d_CheckFxDeltaLoop($1, $2, d_shift(d_shift(d_shift($@))))')')dnl
dnl
define(`d_CheckFxDeltas',
`IFDEF STEP_FIXED
        test eax, TRIF_FIXED_OVERFLOW
        jnz L_FXOD_NoLoad_$1
d_CheckFxDeltaLoop($@)dnl

        ; No overflow.
        jmp L_FX_Exit_$1

L_FXOD_$1:
        ; Overflow detected.
        mov eax, [ebx+SCTX_uFlags]
L_FXOD_NoLoad_$1:
        or eax, TRIF_FIXED_OVERFLOW
        mov [ebx+SCTX_uFlags], eax

L_FX_Exit_$1:
ENDIF
')dnl
dnl
dnl d_ZSetup
dnl
dnl Does Z setup.
dnl
dnl $1 is the Z buffer depth.
dnl
define(`d_ZSetup',
`        ;
        ; Slot a is used.
        ;

        ; Compute Z deltas.
        fld DWORD PTR [edx+TL_dvSZ]             ; Z2
        fsub DWORD PTR [edi+TL_dvSZ]            ; DZ20
        fld DWORD PTR [ecx+TL_dvSZ]             ; Z1 DZ20
        fsub DWORD PTR [edi+TL_dvSZ]            ; DZ10 DZ20

        ; Get initial Z and scale Z deltas.
        mov eax, [edi+TL_dvSZ]
        fxch st(1)                              ; DZ20 DZ10
        fmul Z$1_SCALE                          ; DZ20 DZ10
        fxch st(1)                              ; DZ10 DZ20
        fmul Z$1_SCALE                          ; DZ10 DZ20
        mov fVa0, eax
        fxch st(1)                              ; DZ20 DZ10
        fstp fDVa20                             ; DZ10
        fstp fDVa10                             ;

d_CorrectAttr(`ATTRSET_fZ', `fVa0', `fDVa20', `fDVa10', Z$1_SCALE)dnl

d_NegateDeltas(Z$1, `ATTRSET_fZ')dnl

pushdef(`d_CRPD_Post',
`        mov eax, [ebx+SCTX_pPrim]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fZ]
        fistp DWORD PTR [eax+RASTPRIM_iDZDX]
')dnl
d_CheckRpDeltas(Z$1, `C_Z_LIMIT', `SCTX_DAttrDX+ATTRSET_fZ')dnl
popdef(`d_CRPD_Post')dnl

d_CheckFxDeltas(Z$1, `C_Z_LIMIT', `ATTRSET_fZ')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupZEnd]
')dnl
dnl
dnl d_TexSetupStart
dnl
dnl Begins texture coordinate setup.
dnl
dnl $1 is the texcoord index, 1 or 2.
dnl
define(`d_TexSetupStart',
`       mov ecx, DWORD PTR [esi+RS_WRAP0+eval(decr($1) * 4)]           ; Wrap flags
        mov eax, ecx
        and ecx, D3DWRAP_U
        and eax, D3DWRAP_V
        mov bWrapU, ecx
        mov ecx, pV1
        mov bWrapV, eax
')dnl
dnl
dnl d_TexSetupFinish
dnl
dnl Completes texture coordinate setup.
dnl
dnl $1 is the texcoord index, 1 or 2.
dnl
define(`d_TexSetupFinish',
`d_CorrectAttr(ATTRSET_fUoW$1, `fVa0', `fDVa20', `fDVa10', `TEX_SCALE')dnl

d_CorrectAttr(ATTRSET_fVoW$1, `fVb0', `fDVb20', `fDVb10', `TEX_SCALE')dnl

d_NegateDeltas(Tex$1, ATTRSET_fUoW$1, ATTRSET_fVoW$1)dnl

pushdef(`d_CRPD_Post',
`        mov eax, [ebx+SCTX_pPrim]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fUoW$1]
        fistp DWORD PTR [eax+RASTPRIM_iDUoW$1DX]
        fld DWORD PTR [ebx+SCTX_DAttrDY+ATTRSET_fUoW$1]
        fistp DWORD PTR [eax+RASTPRIM_iDUoW$1DY]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fVoW$1]
        fistp DWORD PTR [eax+RASTPRIM_iDVoW$1DX]
        fld DWORD PTR [ebx+SCTX_DAttrDY+ATTRSET_fVoW$1]
        fistp DWORD PTR [eax+RASTPRIM_iDVoW$1DY]
')dnl
d_CheckRpDeltas(Tex$1, `C_TEX_LIMIT',
                SCTX_DAttrDX+ATTRSET_fUoW$1,
                SCTX_DAttrDY+ATTRSET_fUoW$1,
                SCTX_DAttrDX+ATTRSET_fVoW$1,
                SCTX_DAttrDY+ATTRSET_fVoW$1)dnl
popdef(`d_CRPD_Post')dnl

d_CheckFxDeltas(Tex$1, `C_TEX_LIMIT', ATTRSET_fUoW$1, ATTRSET_fVoW$1)dnl
')dnl
dnl
dnl d_PerspTexDeltas
dnl
dnl Computes UoW,VoW texture deltas.
dnl
dnl $1 is the vertex U offset to use.
dnl $2 is the vertex V offset to use.
dnl
define(`d_PerspTexDeltas',
`        ; Get UoW, VoW and scaled deltas.
        fld DWORD PTR [edi+$1]          ; U
        fmul DWORD PTR [edi+TL_dvRHW]   ; UoW
        fld DWORD PTR [edi+$2]          ; V UoW
        fmul DWORD PTR [edi+TL_dvRHW]   ; VoW UoW
        fxch st(1)                      ; UoW VoW
        fstp fVa0                       ; VoW
        fstp fVb0                       ;

        ; DU20.
        TEXTURE_DIFF DWORD PTR [edx+$1], DWORD PTR [edi+$1], bWrapU, fTmp
        fadd DWORD PTR [edi+$1]
        mov ecx, pV1
        fmul DWORD PTR [edx+TL_dvRHW]

        ; DU10.
        TEXTURE_DIFF DWORD PTR [ecx+$1], DWORD PTR [edi+$1], bWrapU, fTmp
        fxch st(1)
        mov ecx, pV1

        ; DU20.
        fsub fVa0
        fmul TEX_SCALE
        fstp fDVa20

        ; DU10.
        fadd DWORD PTR [edi+$1]
        fmul DWORD PTR [ecx+TL_dvRHW]

        ; DV20.
        TEXTURE_DIFF DWORD PTR [edx+$2], DWORD PTR [edi+$2], bWrapV, fTmp
        fxch st(1)
        mov ecx, pV1

        ; DU10.
        fsub fVa0
        fmul TEX_SCALE
        fstp fDVa10

        ; DV20.
        fadd DWORD PTR [edi+$2]
        fmul DWORD PTR [edx+TL_dvRHW]

        ; DV10.
        TEXTURE_DIFF DWORD PTR [ecx+$2], DWORD PTR [edi+$2], bWrapV, fTmp
        fxch st(1)
        mov ecx, pV1

        ; DV20.
        fsub fVb0
        fmul TEX_SCALE
        fstp fDVb20

        ; Finish DV10.
        fadd DWORD PTR [edi+$2]
        fmul DWORD PTR [ecx+TL_dvRHW]
        fsub fVb0
        fmul TEX_SCALE
        fstp fDVb10
')dnl
dnl
dnl d_PerspTexSetup
dnl
dnl Produces perspective-correct texcoord setup code.
dnl
dnl $1 is the vertex U offset to use.
dnl $2 is the vertex V offset to use.
dnl $3 is the texcoord index, 1 or 2.
dnl
define(`d_PerspTexSetup',
`d_TexSetupStart($3)dnl

d_PerspTexDeltas($1, $2)dnl

d_TexSetupFinish($3)dnl
')dnl
dnl
dnl d_AffineTexDeltas
dnl
dnl Computes U,V texture deltas.
dnl
dnl $1 is the vertex U offset to use.
dnl $2 is the vertex V offset to use.
dnl
define(`d_AffineTexDeltas',
`        ; Get scaled deltas.

        ; Start DU20.
        mov eax, [edi+$1]

        ; Save U in fVa0
        mov fVa0, eax

        TEXTURE_DIFF DWORD PTR [edx+$1], DWORD PTR [edi+$1], bWrapU, fTmp
        fmul TEX_SCALE
        mov ecx, pV1
        fstp fDVa20

        ; DU10.
        TEXTURE_DIFF DWORD PTR [ecx+$1], DWORD PTR [edi+$1], bWrapU, fTmp
        fmul TEX_SCALE
        mov ecx, pV1

        ; Start DV20.
        mov eax, [edi+$2]

        ; Save V in fVb0
        mov fVb0, eax

        ; DU10.
        fstp fDVa10

        ; DV20.
        TEXTURE_DIFF DWORD PTR [edx+$2], DWORD PTR [edi+$2], bWrapV, fTmp
        fmul TEX_SCALE
        mov ecx, pV1

        ; DV20.
        fstp fDVb20

        ; DV10.
        TEXTURE_DIFF DWORD PTR [ecx+$2], DWORD PTR [edi+$2], bWrapV, fTmp
        fmul TEX_SCALE
        mov ecx, pV1
        fstp fDVb10
')dnl
dnl
dnl d_AffineTexSetup
dnl
dnl Produces affine-mapped texcoord setup code.
dnl
dnl $1 is the vertex U offset to use.
dnl $2 is the vertex V offset to use.
dnl $3 is the texcoord index, 1 or 2.
dnl
define(`d_AffineTexSetup',
`d_TexSetupStart($3)dnl

d_AffineTexDeltas($1, $2)dnl

d_TexSetupFinish($3)dnl
')dnl
dnl
dnl d_ColorDelta
dnl
dnl Produces color B - A code.
dnl
dnl $1 is the packed color address to use.
dnl $2 is the edge suffix, `2' or `1'.
dnl
define(`d_ColorDelta',
`        ; A0 R0 G0 B0 sit at the end of the stack throughout.
        mov eax, [$1]
        mov ecx, eax
        ; B is in the correct shifted position.
        and eax, 0ffh
        mov fTmp, eax
        fild fTmp                                       ; B
        ; Shift G and store.
        mov eax, ecx
        shr eax, 8
        and eax, 0ffh
        mov fTmp, eax
        fsub st(0), st(4)                               ; dB
        fild fTmp                                       ; G dB
        ; Shift R and store.
        mov eax, ecx
        shr eax, 16
        and eax, 0ffh
        mov fTmp, eax
        fsub st(0), st(4)                               ; dG dB
        fild fTmp                                       ; R dG dB
        ; Shift A and store.
        mov eax, ecx
        shr eax, 24
        mov fTmp, eax
        fsub st(0), st(4)                               ; dR dG dB
        fild fTmp                                       ; A dR dG dB
        ; Scale deltas.
        fxch st(3)                                      ; dB dR dG A
        fmul COLOR_SCALE                                ; dB dR dG A
        fxch st(3)                                      ; A dR dG dB
        fsub st(0), st(4)                               ; dA dR dG dB
        fxch st(2)                                      ; dG dR dA dB
        fmul COLOR_SCALE                                ; dG dR dA dB
        fxch st(1)                                      ; dR dG dA dB
        fmul COLOR_SCALE                                ; dR dG dA dB
        fxch st(2)                                      ; dA dG dR dB
        fmul COLOR_SCALE                                ; dA dG dR dB
        fxch st(3)                                      ; dB dG dR dA
        fstp fDVa$2`'0                                  ; dG dR dA
        fstp fDVb$2`'0                                  ; dR dA
        fstp fDVc$2`'0                                  ; dA
        fstp fDVd$2`'0                                  ;
')dnl
dnl
dnl d_SColorDelta
dnl
dnl Produces specular color B - A code.
dnl
dnl $1 is the packed color address to use.
dnl $2 is the edge suffix, `2' or `1'.
dnl
define(`d_SColorDelta',
`        ; R0 G0 B0 sit at the end of the stack throughout.
        mov eax, [$1]
        mov ecx, eax
        ; B is in the correct shifted position.
        and eax, 0ffh
        mov fTmp, eax
        fild fTmp                                       ; B
        ; Shift G and store.
        mov eax, ecx
        shr eax, 8
        and eax, 0ffh
        mov fTmp, eax
        fsub st(0), st(3)                               ; dB
        fild fTmp                                       ; G dB
        ; Shift R and store.
        mov eax, ecx
        shr eax, 16
        and eax, 0ffh
        mov fTmp, eax
        fsub st(0), st(3)                               ; dG dB
        fild fTmp                                       ; R dG dB
        ; Scale deltas.
        fxch st(2)                                      ; dB dG R
        fmul COLOR_SCALE                                ; dB dG R
        fxch st(2)                                      ; R dG dB
        fsub st(0), st(3)                               ; dR dG dB
        fxch st(1)                                      ; dG dR dB
        fmul COLOR_SCALE                                ; dG dR dB
        fxch st(1)                                      ; dR dG dB
        fmul COLOR_SCALE                                ; dR dG dB
        fxch st(2)                                      ; dB dG dR
        fstp fDVa$2`'0                                  ; dG dR
        fstp fDVb$2`'0                                  ; dR
        fstp fDVc$2`'0                                  ;
')dnl
dnl
dnl d_IdxColorDelta
dnl
dnl Produces indexed color B - A code.
dnl
dnl $1 is the packed color address to use.
dnl $2 is the edge suffix, `2' or `1'.
dnl
define(`d_IdxColorDelta',
`        ; A0 Idx0 sit at the end of the stack throughout.
        mov eax, [$1]
        mov ecx, eax
        ; Mask off alpha.
        and eax, 0ffffffh
        add eax, 128
        mov fTmp, eax
        fild fTmp                                       ; Idx
        ; Shift A and store.
        shr ecx, 24
        mov fTmp, ecx
        fsub st(0), st(2)                               ; dIdx
        fild fTmp                                       ; A dIdx
        ; Scale deltas.
        fxch st(1)                                      ; dIdx A
        fmul INDEX_COLOR_FIXED_SCALE                    ; dIdx A
        fxch st(1)                                      ; A dIdx
        fsub st(0), st(2)                               ; dA dIdx
        fxch st(1)                                      ; dIdx dA
        fstp fDVa$2`'0                                  ; dA
        fmul INDEX_COLOR_SCALE                          ; dA
        fstp fDVb$2`'0                                  ;
')dnl
dnl
dnl d_ColorSetup
dnl
dnl Produces color setup code.
dnl
dnl $1 is the vertex packed color offset to use.
dnl $2 is the color suffix, `' or `S'.
dnl
define(`d_ColorSetup',
`        ; Get colors from pV0.
        mov eax, [edi+$1]
        mov ecx, eax
        ; B is in the correct shifted position.
        and eax, 0ffh
        mov fVa0, eax
        fild fVa0                                       ; B
        ; Shift G and store.
        mov eax, ecx
        shr eax, 8
        and eax, 0ffh
        mov fVb0, eax
        fild fVb0                                       ; G B
        ; Shift R and store.
        mov eax, ecx
        shr eax, 16
        and eax, 0ffh
        mov fVc0, eax
        fild fVc0                                       ; R G B
ifelse($2, `',
`        ; Shift A and store.
        mov eax, ecx
        shr eax, 24
        mov fVd0, eax
        fild fVd0                                       ; A R G B
')dnl

        ; Get colors from pV2 and subtract pV0.
d_$2ColorDelta(edx+$1, `2')dnl

        ; Get colors from pV1 and subtract pV0.
        mov ecx, pV1
d_$2ColorDelta(ecx+$1, `1')dnl
        mov ecx, pV1

        ; Store initial values.
ifelse($2, `',
`        fxch st(3)                                      ; B R G A
        fstp fVa0                                       ; R G A
        fxch st(1)                                      ; G R A
        fstp fVb0                                       ; R A
        fstp fVc0                                       ; A
        fstp fVd0                                       ;
',
`        fxch st(2)                                      ; B G R
        fstp fVa0                                       ; G R
        fstp fVb0                                       ; R
        fstp fVc0                                       ;
')dnl

d_CorrectAttr(ATTRSET_fB$2, `fVa0', `fDVa20', `fDVa10', `COLOR_SCALE')dnl

d_CorrectAttr(ATTRSET_fG$2, `fVb0', `fDVb20', `fDVb10', `COLOR_SCALE')dnl

d_CorrectAttr(ATTRSET_fR$2, `fVc0', `fDVc20', `fDVc10', `COLOR_SCALE')dnl

ifelse($2, `',
`d_CorrectAttr(ATTRSET_fA$2, `fVd0', `fDVd20', `fDVd10', `COLOR_SCALE')dnl

d_NegateDeltas(Col$2, ATTRSET_fB$2, ATTRSET_fG$2,
               ATTRSET_fR$2, ATTRSET_fA$2)dnl

pushdef(`d_CRPD_Post',
`        mov eax, [ebx+SCTX_pPrim]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fB$2]
        fistp DWORD PTR [eax+RASTPRIM_iDB$2DX]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fG$2]
        fistp DWORD PTR [eax+RASTPRIM_iDG$2DX]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fR$2]
        fistp DWORD PTR [eax+RASTPRIM_iDR$2DX]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fA$2]
        fistp DWORD PTR [eax+RASTPRIM_iDA$2DX]
')dnl
d_CheckRpDeltas(Col$2, `C_COLOR_LIMIT',
                SCTX_DAttrDX+ATTRSET_fB$2,
                SCTX_DAttrDX+ATTRSET_fR$2,
                SCTX_DAttrDX+ATTRSET_fG$2,
                SCTX_DAttrDX+ATTRSET_fA$2)dnl
popdef(`d_CRPD_Post')dnl

d_CheckFxDeltas(Col$2, `C_COLOR_LIMIT',
                ATTRSET_fB$2, ATTRSET_fG$2, ATTRSET_fR$2, ATTRSET_fA$2)',
`d_NegateDeltas(Col$2, ATTRSET_fB$2, ATTRSET_fG$2,
               ATTRSET_fR$2)dnl

pushdef(`d_CRPD_Post',
`        mov eax, [ebx+SCTX_pPrim]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fB$2]
        fistp DWORD PTR [eax+RASTPRIM_iDB$2DX]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fG$2]
        fistp DWORD PTR [eax+RASTPRIM_iDG$2DX]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fR$2]
        fistp DWORD PTR [eax+RASTPRIM_iDR$2DX]
')dnl
d_CheckRpDeltas(Col$2, `C_COLOR_LIMIT',
                SCTX_DAttrDX+ATTRSET_fB$2,
                SCTX_DAttrDX+ATTRSET_fR$2,
                SCTX_DAttrDX+ATTRSET_fG$2)dnl
popdef(`d_CRPD_Post')dnl

d_CheckFxDeltas(Col$2, `C_COLOR_LIMIT',
                ATTRSET_fB$2, ATTRSET_fG$2, ATTRSET_fR$2)')dnl
')dnl

;
; Locations of stack variables.
; Done as equates since setup functions don't have real stack frames.
;

; Generic storage slots for initial value and deltas.
fVa0    EQU DWORD PTR [ebp-4]
fDVa20  EQU DWORD PTR [ebp-8]
fDVa10  EQU DWORD PTR [ebp-12]
fVb0    EQU DWORD PTR [ebp-16]
fDVb20  EQU DWORD PTR [ebp-20]
fDVb10  EQU DWORD PTR [ebp-24]
fVc0    EQU DWORD PTR [ebp-28]
fDVc20  EQU DWORD PTR [ebp-32]
fDVc10  EQU DWORD PTR [ebp-36]
fVd0    EQU DWORD PTR [ebp-40]
fDVd20  EQU DWORD PTR [ebp-44]
fDVd10  EQU DWORD PTR [ebp-48]
fTmp    EQU DWORD PTR [ebp-52]
bWrapU  EQU DWORD PTR [ebp-56]
bWrapV  EQU DWORD PTR [ebp-60]

SETUP_LOCALS EQU 60

; Parameters
pV1     EQU DWORD PTR [ebp+20]
pV2     EQU DWORD PTR [ebp+24]

dnl d_DeclTriSetup
dnl
dnl Declare a PFN_SETUPATTR from its attribute name.
dnl
dnl $1 is the attribute name.
dnl
define(`d_DeclTriSetup',
`@TriSetup_$1@16 PROC SYSCALL PUBLIC
        PROF_ENTRY

')dnl
define(`d_EndDeclTriSetup',
`@TriSetup_$1@16 ENDP
')dnl
dnl
;-----------------------------------------------------------------------------
;
; Setup_Start
;
; Establishes the stack frame for all setup routines, including
; preservation of registers and allocation of local storage space.
; Scales deltas by fOoDet.
; Puts pStpCtx in ebx, pV0 in edi, pV1 in ecx, pV2 in edx and
; pdwRenderState in esi.
; Jumps to first attribute setup bead.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Start')dnl
        ; Save registers.
        push ebx
        push esi
        push edi

        ; Set frame after saving registers so saving more or fewer
        ; doesnt alter the locals.
        push ebp
        mov ebp, esp

        ; Allocate local storage space.
        sub esp, SETUP_LOCALS

        ; Put pStpCtx in ebx.
        mov ebx, ecx

        ; Put pV0 in edi, pV1 in ecx and pV2 in edx.
        mov edi, edx
        mov ecx, pV1
        mov edx, pV2

        ; Fold normalization value into deltas.
        fld DWORD PTR [ebx+SCTX_fDX10]          ; DX10
        fmul DWORD PTR [ebx+SCTX_fOoDet]        ; NX10
        fld DWORD PTR [ebx+SCTX_fDX20]          ; DX20 NX10
        fmul DWORD PTR [ebx+SCTX_fOoDet]        ; NX20 NX10

        mov eax, [ebx+SCTX_pCtx]

        fld DWORD PTR [ebx+SCTX_fDY10]          ; DY10 NX20 NX10
        fmul DWORD PTR [ebx+SCTX_fOoDet]        ; NY10 NX20 NX10
        fld DWORD PTR [ebx+SCTX_fDY20]          ; DY20 NY10 NX20 NX10
        fmul DWORD PTR [ebx+SCTX_fOoDet]        ; NY20 NY10 NX20 NX10
        fxch st(3)                              ; NX10 NY10 NX20 NY20
        fstp DWORD PTR [ebx+SCTX_fNX10]         ; NY10 NX20 NY20
        fxch st(1)                              ; NX20 NY10 NY20
        fstp DWORD PTR [ebx+SCTX_fNX20]         ; NY10 NY20

        ; Get pdwRenderState in esi.
        ; Now that pdwRenderState is an array of DWORD declared in the context,
        ; its address is the address of the context plus the offset
        mov esi, eax
        add esi, RCTX_pdwRenderState

        fstp DWORD PTR [ebx+SCTX_fNY10]         ; NY20
        fstp DWORD PTR [ebx+SCTX_fNY20]         ;

        ; Jump to first bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupFirstAttr]
d_EndDeclTriSetup(`Start')dnl

;-----------------------------------------------------------------------------
;
; Setup_Z16
;
; Attribute setup for 16-bit Z.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Z16')dnl
d_ZSetup(`16')dnl
d_EndDeclTriSetup(`Z16')dnl

;-----------------------------------------------------------------------------
;
; Setup_Z32
;
; Attribute setup for 32-bit Z.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Z32')dnl
d_ZSetup(`32')dnl
d_EndDeclTriSetup(`Z32')dnl

;-----------------------------------------------------------------------------
;
; Setup_Persp_Tex1
;
; Attribute setup for OoW and first texture coordinates.
; Coordinates are set up for perspective correction.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Persp_Tex1')dnl
        ;
        ; Setup for OoW.  Slot a is used.
        ;

        ; Compute OoW deltas.
        fld DWORD PTR [edx+TL_dvRHW]            ; OoW2
        fsub DWORD PTR [edi+TL_dvRHW]           ; DOoW20
        fld DWORD PTR [ecx+TL_dvRHW]            ; OoW1 DOoW20
        fsub DWORD PTR [edi+TL_dvRHW]           ; DOoW10 DOoW20

        ; Scale OoW deltas.
        fxch st(1)                              ; DOoW20 DOoW10
        fmul OOW_SCALE                          ; DOoW20 DOoW10
        fxch st(1)                              ; DOoW10 DOoW20
        fmul OOW_SCALE                          ; DOoW10 DOoW20
        fxch st(1)                              ; DOoW20 DOoW10
        fstp fDVa20                             ; DOoW10
        fstp fDVa10                             ;

d_CorrectAttr(`ATTRSET_fOoW', `DWORD PTR [edi+TL_dvRHW]',
              `fDVa20', `fDVa10', `OOW_SCALE')dnl

d_NegateDeltas(`OoW', `ATTRSET_fOoW')dnl

pushdef(`d_CRPD_Post',
`        mov eax, [ebx+SCTX_pPrim]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fOoW]
        fistp DWORD PTR [eax+RASTPRIM_iDOoWDX]
        fld DWORD PTR [ebx+SCTX_DAttrDY+ATTRSET_fOoW]
        fistp DWORD PTR [eax+RASTPRIM_iDOoWDY]
')dnl
d_CheckRpDeltas(`OoW', `C_OOW_LIMIT',
                `SCTX_DAttrDX+ATTRSET_fOoW',
                `SCTX_DAttrDY+ATTRSET_fOoW')dnl
popdef(`d_CRPD_Post')dnl

d_CheckFxDeltas(`OoW', `C_OOW_LIMIT', `ATTRSET_fOoW')dnl

        ;
        ; Setup for tex1.  Slot a is used for U and b for V.
        ;

d_PerspTexSetup(`TL_dvTU', `TL_dvTV', `1')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupTex1End]
d_EndDeclTriSetup(`Persp_Tex1')dnl

;-----------------------------------------------------------------------------
;
; Setup_Affine_Tex1
;
; Attribute setup for OoW and first texture coordinates.
; Coordinates are set up for affine mapping.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Affine_Tex1')dnl
        ;
        ; Setup for OoW.  Slot a is used.
        ;

        mov eax, OOW_SCALE
        mov [ebx+SCTX_Attr+ATTRSET_fOoW], eax

        xor eax, eax
        mov [ebx+SCTX_DAttrDX+ATTRSET_fOoW], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fOoW], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fOoW], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fOoW], eax

        mov ecx, [ebx+SCTX_pPrim]
        mov [ecx+RASTPRIM_iDOoWDX], eax
        mov [ecx+RASTPRIM_iDOoWDY], eax

        mov ecx, pV1

        ;
        ; Setup for tex1.  Slot a is used for U and b for V.
        ;

d_AffineTexSetup(`TL_dvTU', `TL_dvTV', `1')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupTex1End]
d_EndDeclTriSetup(`Affine_Tex1')dnl

;-----------------------------------------------------------------------------
;
; Setup_Persp_Tex2
;
; Attribute setup for second texture coordinates.
; Coordinates are set up for perspective correction.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Persp_Tex2')dnl
        ;
        ; Setup for tex2.  Slot a is for U and slot B is for V.
        ;

d_PerspTexSetup(`TM_dvTU2', `TM_dvTV2', `2')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupTex2End]
d_EndDeclTriSetup(`Persp_Tex2')dnl

;-----------------------------------------------------------------------------
;
; Setup_Affine_Tex2
;
; Attribute setup for second texture coordinates.
; Coordinates are set up for affine mapping.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Affine_Tex2')dnl
        ;
        ; Setup for tex2.  Slot a is for U and slot B is for V.
        ;

d_AffineTexSetup(`TM_dvTU2', `TM_dvTV2', `2')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupTex2End]
d_EndDeclTriSetup(`Affine_Tex2')dnl

;-----------------------------------------------------------------------------
;
; Setup_Diff
;
; Attribute setup for interpolated diffuse color.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Diff')dnl
        ;
        ; Slots a - d are B, G, R and A, respectively.
        ;

d_ColorSetup(`TL_dcColor', `')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupDiffEnd]
d_EndDeclTriSetup(`Diff')dnl

;-----------------------------------------------------------------------------
;
; Setup_DiffFlat
;
; Attribute setup for constant diffuse color.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`DiffFlat')dnl
        ;
        ; Slots a - d are B, G, R and A, respectively.
        ;

        ; Get colors from first input vertex.  Can't just use pV0
        ; because it may have changed due to vertex sorting.
        mov eax, [ebx+SCTX_pFlatVtx]
        mov eax, [eax+TL_dcColor]
        mov ecx, eax
        ; G is in the correct shifted position.
        and eax, 0ff00h
        mov fVb0, eax
        fild fVb0                                       ; G
        ; Shift B and store.
        mov eax, ecx
        shl eax, COLOR_SHIFT
        and eax, 0ff00h
        mov fVa0, eax
        fild fVa0                                       ; B G
        ; Shift R and store.
        mov eax, ecx
        shr eax, (16 - COLOR_SHIFT)
        and eax, 0ff00h
        mov fVc0, eax
        fild fVc0                                       ; R B G
        ; Shift A and store.
        mov eax, ecx
        shr eax, (24 - COLOR_SHIFT)
        and eax, 0ff00h
        mov fVd0, eax
        fild fVd0                                       ; A R B G
        fxch st(3)                                      ; G R B A
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fG]       ; R B A
        fxch st(1)                                      ; B R A
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fB]       ; R A
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fR]       ; A
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fA]       ;

        ; Zero out all deltas.
        xor eax, eax
        mov ecx, [ebx+SCTX_pPrim]
        mov [ebx+SCTX_DAttrDX+ATTRSET_fB], eax
        mov [ebx+SCTX_DAttrDX+ATTRSET_fG], eax
        mov [ebx+SCTX_DAttrDX+ATTRSET_fR], eax
        mov [ebx+SCTX_DAttrDX+ATTRSET_fA], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fB], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fG], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fR], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fA], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fB], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fG], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fR], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fA], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fB], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fG], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fR], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fA], eax
        ; Fills both B and G.
        mov [ecx+RASTPRIM_iDBDX], eax
        ; Fills both R and A.
        mov [ecx+RASTPRIM_iDRDX], eax
        mov ecx, pV1

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupDiffEnd]
d_EndDeclTriSetup(`DiffFlat')dnl

;-----------------------------------------------------------------------------
;
; Setup_DIdx
;
; Attribute setup for interpolated diffuse indexed color.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`DIdx')dnl
        ;
        ; Slots a, b are Idx and A, respectively.
        ;

        ; Get values from pV0.
        mov eax, [edi+TL_dcColor]
        mov ecx, eax
        ; Mask off alpha.
        and eax, 0ffffffh
        add eax, 128
        mov fVa0, eax
        fild fVa0                                       ; Idx
        ; Shift A and store.
        shr ecx, 24
        mov fVb0, ecx
        fild fVb0                                       ; A Idx

        ; Get colors from pV2 and subtract pV0.
d_IdxColorDelta(edx+TL_dcColor, `2')dnl

        ; Get colors from pV1 and subtract pV0.
        mov ecx, pV1
d_IdxColorDelta(ecx+TL_dcColor, `1')dnl
        mov ecx, pV1

        ; Store initial values.
        fstp fVb0                                       ; Idx
        fstp fVa0                                       ;

d_CorrectAttr(ATTRSET_fDIdx, `fVa0', `fDVa20', `fDVa10',
              `INDEX_COLOR_FIXED_SCALE')dnl

d_CorrectAttr(ATTRSET_fDIdxA, `fVb0', `fDVb20', `fDVb10',
              `INDEX_COLOR_SCALE')dnl

d_NegateDeltas(IdxCol, ATTRSET_fDIdx, ATTRSET_fDIdxA)dnl

pushdef(`d_CRPD_Post',
`        mov eax, [ebx+SCTX_pPrim]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fDIdx]
        fistp DWORD PTR [eax+RASTPRIM_iDIdxDX]
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fDIdxA]
        fistp DWORD PTR [eax+RASTPRIM_iDIdxADX]
')dnl
d_CheckRpDeltas(IdxCol, `C_INDEX_COLOR_LIMIT',
                SCTX_DAttrDX+ATTRSET_fDIdx,
                SCTX_DAttrDX+ATTRSET_fDIdxA)dnl
popdef(`d_CRPD_Post')dnl

d_CheckFxDeltas(IdxCol, `C_INDEX_COLOR_LIMIT',
                ATTRSET_fDIdx, ATTRSET_fDIdxA)dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupDiffEnd]
d_EndDeclTriSetup(`DIdx')dnl

;-----------------------------------------------------------------------------
;
; Setup_DIdxFlat
;
; Attribute setup for constant diffuse indexed color.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`DIdxFlat')dnl
        ;
        ; Slots a, b are Idx and A, respectively.
        ;

        ; Get colors from first input vertex.  Can't just use pV0
        ; because it may have changed due to vertex sorting.
        mov eax, [ebx+SCTX_pFlatVtx]
        mov eax, [eax+TL_dcColor]
        mov ecx, eax
        ; Mask off alpha and shift.
        shl eax, INDEX_COLOR_FIXED_SHIFT
        ; Should not be necessary to add .5 to Idx, since
        ; no floating point ops are done to it
        mov fVa0, eax
        fild fVa0                                       ; Idx
        ; Shift A and store.
        shr ecx, (24 - INDEX_COLOR_SHIFT)
        and ecx, 0ffffffh
        mov fVb0, ecx
        fild fVb0                                       ; A Idx
        fxch st(1)                                      ; Idx A
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fDIdx]    ; A
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fDIdxA]   ;

        ; Zero out all deltas.
        xor eax, eax
        mov ecx, [ebx+SCTX_pPrim]
        mov [ebx+SCTX_DAttrDX+ATTRSET_fDIdx], eax
        mov [ebx+SCTX_DAttrDX+ATTRSET_fDIdxA], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fDIdx], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fDIdxA], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fDIdx], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fDIdxA], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fDIdx], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fDIdxA], eax
        mov [ecx+RASTPRIM_iDIdxDX], eax
        mov [ecx+RASTPRIM_iDIdxADX], eax
        mov ecx, pV1

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupDiffEnd]
d_EndDeclTriSetup(`DIdxFlat')dnl

;-----------------------------------------------------------------------------
;
; Setup_Spec
;
; Attribute setup for interpolated specular color.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Spec')dnl
        ;
        ; Slots a - c are B, G and R, respectively.
        ;

d_ColorSetup(`TL_dcSpecular', `S')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupSpecEnd]
d_EndDeclTriSetup(`Spec')dnl

;-----------------------------------------------------------------------------
;
; Setup_SpecFlat
;
; Attribute setup for constant specular color.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`SpecFlat')dnl
        ;
        ; Slots a - c are B, G and R, respectively.
        ;

        ; Get colors from first input vertex.  Can't just use pV0
        ; because it may have changed due to vertex sorting.
        mov eax, [ebx+SCTX_pFlatVtx]
        mov eax, [eax+TL_dcSpecular]
        mov ecx, eax
        ; G is in the correct shifted position.
        and eax, 0ff00h
        mov fVb0, eax
        fild fVb0                                       ; G
        ; Shift B and store.
        mov eax, ecx
        shl eax, 8
        and eax, 0ff00h
        mov fVa0, eax
        fild fVa0                                       ; B G
        ; Shift R and store.
        mov eax, ecx
        shr eax, 8
        and eax, 0ff00h
        mov fVc0, eax
        fild fVc0                                       ; R B G
        fxch st(2)                                      ; G B R
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fGS]      ; B R
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fBS]      ; R
        fstp DWORD PTR [ebx+SCTX_Attr+ATTRSET_fRS]      ;

        ; Zero out all deltas.
        xor eax, eax
        mov ecx, [ebx+SCTX_pPrim]
        mov [ebx+SCTX_DAttrDX+ATTRSET_fBS], eax
        mov [ebx+SCTX_DAttrDX+ATTRSET_fGS], eax
        mov [ebx+SCTX_DAttrDX+ATTRSET_fRS], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fBS], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fGS], eax
        mov [ebx+SCTX_DAttrDY+ATTRSET_fRS], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fBS], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fGS], eax
        mov [ebx+SCTX_DAttrNC+ATTRSET_fRS], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fBS], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fGS], eax
        mov [ebx+SCTX_DAttrCY+ATTRSET_fRS], eax
        ; Fills both B and G.
        mov [ecx+RASTPRIM_iDBSDX], eax
        ; Fills both R and Fog.  Fog will be set up later, if at all,
        ; so it's OK to trash it here.
        mov [ecx+RASTPRIM_iDRSDX], eax
        mov ecx, pV1

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupSpecEnd]
d_EndDeclTriSetup(`SpecFlat')dnl

;-----------------------------------------------------------------------------
;
; Setup_Fog
;
; Attribute setup for vertex fog.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`Fog')dnl
        ;
        ; Slot a is used with some temporaries in b and c.
        ;

IFNDEF PWL_FOG
        ; Check for global-into-local fog.  If global fog is on,
        ; compute the local fog values from table fog rather than
        ; from the vertex.
        mov eax, [ebx+SCTX_uFlags]
        test eax, PRIMSF_GLOBAL_FOG_USED
        jz L_NormalLocalFog

        ;
        ; Compute table fog values for all three vertex Z values.
        ;

        ; pV1
        push DWORD PTR [ecx+TL_dvSZ]
        mov ecx, esi
        call @ComputeTableFog@8
        mov fVb0, eax

        ; pV0
        push DWORD PTR [edi+TL_dvSZ]
        mov ecx, esi
        call @ComputeTableFog@8
        ; Keep V0 fog in fTmp so that we can write the FP value
        ; into fVa0.
        mov fTmp, eax

        ; pV2
        mov edx, pV2
        push DWORD PTR [edx+TL_dvSZ]
        mov ecx, esi
        call @ComputeTableFog@8
        mov fVc0, eax

        ; Restore pV1 and pV2.
        mov ecx, pV1
        mov edx, pV2

        jmp L_ComputeFogDeltas

L_NormalLocalFog:
ENDIF

        ; Extract fog values from specular alpha and shift into position.
        mov eax, [edi+TL_dcSpecular]
        and eax, 0ff000000h
        shr eax, 24 - FOG_SHIFT
        ; Keep V0 fog in fTmp so that we can write the FP value
        ; into fVa0.
        mov fTmp, eax

        mov eax, [ecx+TL_dcSpecular]
        and eax, 0ff000000h
        shr eax, 24 - FOG_SHIFT
        mov fVb0, eax

        mov eax, [edx+TL_dcSpecular]
        and eax, 0ff000000h
        shr eax, 24 - FOG_SHIFT
        mov fVc0, eax

IFNDEF PWL_FOG
L_ComputeFogDeltas:
ENDIF

        fild fTmp

        ; Compute fog deltas from shifted values in fVa?.
        mov eax, fVc0
        sub eax, fTmp
        mov fDVa20, eax

        fstp fVa0
        fild fDVa20

        mov eax, fVb0
        sub eax, fTmp
        mov fDVa10, eax

        fstp fDVa20
        fild fDVa10
        fstp fDVa10

        ; fVa0 is already scaled so the scaling factor is given as 1.0.
        ; This is a little wasteful but it makes the above code simpler
        ; and more integer.
d_CorrectAttr(`ATTRSET_fFog', `fVa0', `fDVa20', `fDVa10', `_g_fOne')dnl

d_NegateDeltas(`Fog', `ATTRSET_fFog')dnl

pushdef(`d_CRPD_Post',
`        ; Fog deltas are given per span rather than per primitive.
        ; Store local fog value in setup context so that it can be
        ; copied into the spans later.
        fld DWORD PTR [ebx+SCTX_DAttrDX+ATTRSET_fFog]
        fistp DWORD PTR [ebx+SCTX_iDLocalFogDX]
')dnl
d_CheckRpDeltas(`Fog', `C_FOG_LIMIT', `SCTX_DAttrDX+ATTRSET_fFog')dnl
popdef(`d_CRPD_Post')dnl

d_CheckFxDeltas(`Fog', `C_FOG_LIMIT', `ATTRSET_fFog')dnl

        ; Jump to next bead.
        jmp DWORD PTR [ebx+SCTX_pfnTriSetupFogEnd]
d_EndDeclTriSetup(`Fog')dnl

;-----------------------------------------------------------------------------
;
; Setup_End
;
; Final bead.  Restores stack and returns.
;
;-----------------------------------------------------------------------------
d_DeclTriSetup(`End')dnl
        ; Give up locals.
        add esp, SETUP_LOCALS

        ; Restore registers.
        pop ebp
        pop edi
        pop esi
        pop ebx

        ret 8
d_EndDeclTriSetup(`End')dnl

END