 .386P
 .model FLAT
;
; r_drawa.s
; x86 assembly-language edge clipping and emission code
;

include qasm.inc
include d_if.inc

if	id386

; !!! if these are changed, they must be changed in r_draw.c too !!!
FULLY_CLIPPED_CACHED	equ		080000000h
FRAMECOUNT_MASK			equ		07FFFFFFFh

_DATA SEGMENT	

Ld0 dd 0.0	
Ld1 dd 0.0	
Lstack dd 0	
Lfp_near_clip dd NEAR_CLIP	
Lceilv0 dd 0	
Lv dd 0	
Lu0 dd 0	
Lv0 dd 0	
Lzi0 dd 0	

_DATA ENDS
_TEXT SEGMENT	

;----------------------------------------------------------------------
; edge clipping code
;----------------------------------------------------------------------

pv0		equ		4+12
pv1		equ		8+12
clip	equ		12+12

 align 4	
 public _R_ClipEdge	
_R_ClipEdge:	
 push esi	; preserve register variables
 push edi	
 push ebx	
 mov ds:dword ptr[Lstack],esp	; for clearing the stack later

;	float		d0, d1, f;
;	mvertex_t	clipvert;

 mov ebx,ds:dword ptr[clip+esp]	
 mov esi,ds:dword ptr[pv0+esp]	
 mov edx,ds:dword ptr[pv1+esp]	

;	if (clip)
;	{
 test ebx,ebx	
 jz Lemit	

;		do
;		{

Lcliploop:	

;			d0 = DotProduct (pv0->position, clip->normal) - clip->dist;
;			d1 = DotProduct (pv1->position, clip->normal) - clip->dist;
 fld ds:dword ptr[mv_position+0+esi]	
 fmul ds:dword ptr[cp_normal+0+ebx]	
 fld ds:dword ptr[mv_position+4+esi]	
 fmul ds:dword ptr[cp_normal+4+ebx]	
 fld ds:dword ptr[mv_position+8+esi]	
 fmul ds:dword ptr[cp_normal+8+ebx]	
 fxch st(1)	
 faddp st(2),st(0)	; d0mul2 | d0add0

 fld ds:dword ptr[mv_position+0+edx]	
 fmul ds:dword ptr[cp_normal+0+ebx]	
 fld ds:dword ptr[mv_position+4+edx]	
 fmul ds:dword ptr[cp_normal+4+ebx]	
 fld ds:dword ptr[mv_position+8+edx]	
 fmul ds:dword ptr[cp_normal+8+ebx]	
 fxch st(1)	
 faddp st(2),st(0)	; d1mul2 | d1add0 | d0mul2 | d0add0
 fxch st(3)	; d0add0 | d1add0 | d0mul2 | d1mul2

 faddp st(2),st(0)	; d1add0 | dot0 | d1mul2 
 faddp st(2),st(0)	; dot0 | dot1

 fsub ds:dword ptr[cp_dist+ebx]	; d0 | dot1
 fxch st(1)	; dot1 | d0
 fsub ds:dword ptr[cp_dist+ebx]	; d1 | d0
 fxch st(1)	
 fstp ds:dword ptr[Ld0]	
 fstp ds:dword ptr[Ld1]	

;			if (d0 >= 0)
;			{
 mov eax,ds:dword ptr[Ld0]	
 mov ecx,ds:dword ptr[Ld1]	
 or ecx,eax	
 js Lp2	

; both points are unclipped

Lcontinue:	

;
;				R_ClipEdge (&clipvert, pv1, clip->next);
;				return;
;			}
;		} while ((clip = clip->next) != NULL);
 mov ebx,ds:dword ptr[cp_next+ebx]	
 test ebx,ebx	
 jnz Lcliploop	

;	}

;// add the edge
;	R_EmitEdge (pv0, pv1);
Lemit:	

;
; set integer rounding to ceil mode, set to single precision
;
; FIXME: do away with by manually extracting integers from floats?
; FIXME: set less often
 fldcw ds:word ptr[_fpu_ceil_cw]	

;	edge_t	*edge, *pcheck;
;	int		u_check;
;	float	u, u_step;
;	vec3_t	local, transformed;
;	float	*world;
;	int		v, v2, ceilv0;
;	float	scale, lzi0, u0, v0;
;	int		side;

;	if (r_lastvertvalid)
;	{
 cmp ds:dword ptr[_r_lastvertvalid],0	
 jz LCalcFirst	

;		u0 = r_u1;
;		v0 = r_v1;
;		lzi0 = r_lzi1;
;		ceilv0 = r_ceilv1;
 mov eax,ds:dword ptr[_r_lzi1]	
 mov ecx,ds:dword ptr[_r_u1]	
 mov ds:dword ptr[Lzi0],eax	
 mov ds:dword ptr[Lu0],ecx	
 mov ecx,ds:dword ptr[_r_v1]	
 mov eax,ds:dword ptr[_r_ceilv1]	
 mov ds:dword ptr[Lv0],ecx	
 mov ds:dword ptr[Lceilv0],eax	
 jmp LCalcSecond	

;	}

LCalcFirst:	

;	else
;	{
;		world = &pv0->position[0];

 call near ptr LTransformAndProject	; v0 | lzi0 | u0

 fst ds:dword ptr[Lv0]	
 fxch st(2)	; u0 | lzi0 | v0
 fstp ds:dword ptr[Lu0]	; lzi0 | v0
 fstp ds:dword ptr[Lzi0]	; v0

;		ceilv0 = (int)(v0 - 2000) + 2000; // ceil(v0);
 fistp ds:dword ptr[Lceilv0]	

;	}

LCalcSecond:	

;	world = &pv1->position[0];
 mov esi,edx	

 call near ptr LTransformAndProject	; v1 | lzi1 | u1

 fld ds:dword ptr[Lu0]	; u0 | v1 | lzi1 | u1
 fxch st(3)	; u1 | v1 | lzi1 | u0
 fld ds:dword ptr[Lzi0]	; lzi0 | u1 | v1 | lzi1 | u0
 fxch st(3)	; lzi1 | u1 | v1 | lzi0 | u0
 fld ds:dword ptr[Lv0]	; v0 | lzi1 | u1 | v1 | lzi0 | u0
 fxch st(3)	; v1 | lzi1 | u1 | v0 | lzi0 | u0

;	r_ceilv1 = (int)(r_v1 - 2000) + 2000; // ceil(r_v1);
 fist ds:dword ptr[_r_ceilv1]	

 fldcw ds:word ptr[_fpu_chop_cw]	; put back normal floating-point state

 fst ds:dword ptr[_r_v1]	
 fxch st(4)	; lzi0 | lzi1 | u1 | v0 | v1 | u0

;	if (r_lzi1 > lzi0)
;		lzi0 = r_lzi1;
 fcom st(1)	
 fnstsw ax	
 test ah,1	
 jz LP0	
 fstp st(0)	
 fld st(0)	
LP0:	

 fxch st(1)	; lzi1 | lzi0 | u1 | v0 | v1 | u0
 fstp ds:dword ptr[_r_lzi1]	; lzi0 | u1 | v0 | v1 | u0
 fxch st(1)	
 fst ds:dword ptr[_r_u1]	
 fxch st(1)	

;	if (lzi0 > r_nearzi)	// for mipmap finding
;		r_nearzi = lzi0;
 fcom ds:dword ptr[_r_nearzi]	
 fnstsw ax	
 test ah,045h	
 jnz LP1	
 fst ds:dword ptr[_r_nearzi]	
LP1:	

; // for right edges, all we want is the effect on 1/z
;	if (r_nearzionly)
;		return;
 mov eax,ds:dword ptr[_r_nearzionly]	
 test eax,eax	
 jz LP2	
LPop5AndDone:	
 mov eax,ds:dword ptr[_cacheoffset]	
 mov edx,ds:dword ptr[_r_framecount]	
 cmp eax,07FFFFFFFh	
 jz LDoPop	
 and edx,offset FRAMECOUNT_MASK	
 or edx,offset FULLY_CLIPPED_CACHED	
 mov ds:dword ptr[_cacheoffset],edx	

LDoPop:	
 fstp st(0)	; u1 | v0 | v1 | u0
 fstp st(0)	; v0 | v1 | u0
 fstp st(0)	; v1 | u0
 fstp st(0)	; u0
 fstp st(0)	
 jmp Ldone	

LP2:	

; // create the edge
;	if (ceilv0 == r_ceilv1)
;		return;		// horizontal edge
 mov ebx,ds:dword ptr[Lceilv0]	
 mov edi,ds:dword ptr[_edge_p]	
 mov ecx,ds:dword ptr[_r_ceilv1]	
 mov edx,edi	
 mov esi,ds:dword ptr[_r_pedge]	
 add edx,offset et_size	
 cmp ebx,ecx	
 jz LPop5AndDone	

 mov eax,ds:dword ptr[_r_pedge]	
 mov ds:dword ptr[et_owner+edi],eax	

;	side = ceilv0 > r_ceilv1;
;
;	edge->nearzi = lzi0;
 fstp ds:dword ptr[et_nearzi+edi]	; u1 | v0 | v1 | u0

;	if (side == 1)
;	{
 jc LSide0	

LSide1:	

;	// leading edge (go from p2 to p1)

;		u_step = ((u0 - r_u1) / (v0 - r_v1));
 fsubp st(3),st(0)	; v0 | v1 | u0-u1
 fsub st(0),st(1)	; v0-v1 | v1 | u0-u1
 fdivp st(2),st(0)	; v1 | ustep

;	r_emitted = 1;
 mov ds:dword ptr[_r_emitted],1	

;	edge = edge_p++;
 mov ds:dword ptr[_edge_p],edx	

; pretouch next edge
 mov eax,ds:dword ptr[edx]	

;		v2 = ceilv0 - 1;
;		v = r_ceilv1;
 mov eax,ecx	
 lea ecx,ds:dword ptr[-1+ebx]	
 mov ebx,eax	

;		edge->surfs[0] = 0;
;		edge->surfs[1] = surface_p - surfaces;
 mov eax,ds:dword ptr[_surface_p]	
 mov esi,ds:dword ptr[_surfaces]	
 sub edx,edx	
 sub eax,esi	
 shr eax,offset SURF_T_SHIFT	
 mov ds:dword ptr[et_surfs+edi],edx	
 mov ds:dword ptr[et_surfs+2+edi],eax	

 sub esi,esi	

;		u = r_u1 + ((float)v - r_v1) * u_step;
 mov ds:dword ptr[Lv],ebx	
 fild ds:dword ptr[Lv]	; v | v1 | ustep
 fsubrp st(1),st(0)	; v-v1 | ustep
 fmul st(0),st(1)	; (v-v1)*ustep | ustep
 fadd ds:dword ptr[_r_u1]	; u | ustep

 jmp LSideDone	

;	}

LSide0:	

;	else
;	{
;	// trailing edge (go from p1 to p2)

;		u_step = ((r_u1 - u0) / (r_v1 - v0));
 fsub st(0),st(3)	; u1-u0 | v0 | v1 | u0
 fxch st(2)	; v1 | v0 | u1-u0 | u0
 fsub st(0),st(1)	; v1-v0 | v0 | u1-u0 | u0
 fdivp st(2),st(0)	; v0 | ustep | u0

;	r_emitted = 1;
 mov ds:dword ptr[_r_emitted],1	

;	edge = edge_p++;
 mov ds:dword ptr[_edge_p],edx	

; pretouch next edge
 mov eax,ds:dword ptr[edx]	

;		v = ceilv0;
;		v2 = r_ceilv1 - 1;
 dec ecx	

;		edge->surfs[0] = surface_p - surfaces;
;		edge->surfs[1] = 0;
 mov eax,ds:dword ptr[_surface_p]	
 mov esi,ds:dword ptr[_surfaces]	
 sub edx,edx	
 sub eax,esi	
 shr eax,offset SURF_T_SHIFT	
 mov ds:dword ptr[et_surfs+2+edi],edx	
 mov ds:dword ptr[et_surfs+edi],eax	

 mov esi,1	

;		u = u0 + ((float)v - v0) * u_step;
 mov ds:dword ptr[Lv],ebx	
 fild ds:dword ptr[Lv]	; v | v0 | ustep | u0
 fsubrp st(1),st(0)	; v-v0 | ustep | u0
 fmul st(0),st(1)	; (v-v0)*ustep | ustep | u0
 faddp st(2),st(0)	; ustep | u
 fxch st(1)	; u | ustep

;	}

LSideDone:	

;	edge->u_step = u_step*0x100000;
;	edge->u = u*0x100000 + 0xFFFFF;

 fmul ds:dword ptr[fp_1m]	; u*0x100000 | ustep
 fxch st(1)	; ustep | u*0x100000
 fmul ds:dword ptr[fp_1m]	; ustep*0x100000 | u*0x100000
 fxch st(1)	; u*0x100000 | ustep*0x100000
 fadd ds:dword ptr[fp_1m_minus_1]	; u*0x100000 + 0xFFFFF | ustep*0x100000
 fxch st(1)	; ustep*0x100000 | u*0x100000 + 0xFFFFF
 fistp ds:dword ptr[et_u_step+edi]	; u*0x100000 + 0xFFFFF
 fistp ds:dword ptr[et_u+edi]	

; // we need to do this to avoid stepping off the edges if a very nearly
; // horizontal edge is less than epsilon above a scan, and numeric error
; // causes it to incorrectly extend to the scan, and the extension of the
; // line goes off the edge of the screen
; // FIXME: is this actually needed?
;	if (edge->u < r_refdef.vrect_x_adj_shift20)
;		edge->u = r_refdef.vrect_x_adj_shift20;
;	if (edge->u > r_refdef.vrectright_adj_shift20)
;		edge->u = r_refdef.vrectright_adj_shift20;
 mov eax,ds:dword ptr[et_u+edi]	
 mov edx,ds:dword ptr[_r_refdef+rd_vrect_x_adj_shift20]	
 cmp eax,edx	
 jl LP4	
 mov edx,ds:dword ptr[_r_refdef+rd_vrectright_adj_shift20]	
 cmp eax,edx	
 jng LP5	
LP4:	
 mov ds:dword ptr[et_u+edi],edx	
 mov eax,edx	
LP5:	

; // sort the edge in normally
;	u_check = edge->u;
;
;	if (edge->surfs[0])
;		u_check++;	// sort trailers after leaders
 add eax,esi	

;	if (!newedges[v] || newedges[v]->u >= u_check)
;	{
 mov esi,ds:dword ptr[_newedges+ebx*4]	
 test esi,esi	
 jz LDoFirst	
 cmp ds:dword ptr[et_u+esi],eax	
 jl LNotFirst	
LDoFirst:	

;		edge->next = newedges[v];
;		newedges[v] = edge;
 mov ds:dword ptr[et_next+edi],esi	
 mov ds:dword ptr[_newedges+ebx*4],edi	

 jmp LSetRemove	

;	}

LNotFirst:	

;	else
;	{
;		pcheck = newedges[v];
;
;		while (pcheck->next && pcheck->next->u < u_check)
;			pcheck = pcheck->next;
LFindInsertLoop:	
 mov edx,esi	
 mov esi,ds:dword ptr[et_next+esi]	
 test esi,esi	
 jz LInsertFound	
 cmp ds:dword ptr[et_u+esi],eax	
 jl LFindInsertLoop	

LInsertFound:	

;		edge->next = pcheck->next;
;		pcheck->next = edge;
 mov ds:dword ptr[et_next+edi],esi	
 mov ds:dword ptr[et_next+edx],edi	

;	}

LSetRemove:	

;	edge->nextremove = removeedges[v2];
;	removeedges[v2] = edge;
 mov eax,ds:dword ptr[_removeedges+ecx*4]	
 mov ds:dword ptr[_removeedges+ecx*4],edi	
 mov ds:dword ptr[et_nextremove+edi],eax	

Ldone:	
 mov esp,ds:dword ptr[Lstack]	; clear temporary variables from stack

 pop ebx	; restore register variables
 pop edi	
 pop esi	
 ret	

; at least one point is clipped

Lp2:	
 test eax,eax	
 jns Lp1	

;			else
;			{
;			// point 0 is clipped

;				if (d1 < 0)
;				{
 mov eax,ds:dword ptr[Ld1]	
 test eax,eax	
 jns Lp3	

;				// both points are clipped
;				// we do cache fully clipped edges
;					if (!leftclipped)
 mov eax,ds:dword ptr[_r_leftclipped]	
 mov ecx,ds:dword ptr[_r_pedge]	
 test eax,eax	
 jnz Ldone	

;						r_pedge->framecount = r_framecount;
 mov eax,ds:dword ptr[_r_framecount]	
 and eax,offset FRAMECOUNT_MASK	
 or eax,offset FULLY_CLIPPED_CACHED	
 mov ds:dword ptr[_cacheoffset],eax	

;					return;
 jmp Ldone	

;				}

Lp1:	

;			// point 0 is unclipped
;				if (d1 >= 0)
;				{
;				// both points are unclipped
;					continue;

;			// only point 1 is clipped

;				f = d0 / (d0 - d1);
 fld ds:dword ptr[Ld0]	
 fld ds:dword ptr[Ld1]	
 fsubr st(0),st(1)	

;			// we don't cache partially clipped edges
 mov ds:dword ptr[_cacheoffset],07FFFFFFFh	

 fdivp st(1),st(0)	

 sub esp,offset mv_size	; allocate space for clipvert

;				clipvert.position[0] = pv0->position[0] +
;						f * (pv1->position[0] - pv0->position[0]);
;				clipvert.position[1] = pv0->position[1] +
;						f * (pv1->position[1] - pv0->position[1]);
;				clipvert.position[2] = pv0->position[2] +
;						f * (pv1->position[2] - pv0->position[2]);
 fld ds:dword ptr[mv_position+8+edx]	
 fsub ds:dword ptr[mv_position+8+esi]	
 fld ds:dword ptr[mv_position+4+edx]	
 fsub ds:dword ptr[mv_position+4+esi]	
 fld ds:dword ptr[mv_position+0+edx]	
 fsub ds:dword ptr[mv_position+0+esi]	; 0 | 1 | 2

; replace pv1 with the clip point
 mov edx,esp	
 mov eax,ds:dword ptr[cp_leftedge+ebx]	
 test al,al	

 fmul st(0),st(3)	
 fxch st(1)	; 1 | 0 | 2
 fmul st(0),st(3)	
 fxch st(2)	; 2 | 0 | 1
 fmulp st(3),st(0)	; 0 | 1 | 2
 fadd ds:dword ptr[mv_position+0+esi]	
 fxch st(1)	; 1 | 0 | 2
 fadd ds:dword ptr[mv_position+4+esi]	
 fxch st(2)	; 2 | 0 | 1
 fadd ds:dword ptr[mv_position+8+esi]	
 fxch st(1)	; 0 | 2 | 1
 fstp ds:dword ptr[mv_position+0+esp]	; 2 | 1
 fstp ds:dword ptr[mv_position+8+esp]	; 1
 fstp ds:dword ptr[mv_position+4+esp]	

;				if (clip->leftedge)
;				{
 jz Ltestright	

;					r_leftclipped = true;
;					r_leftexit = clipvert;
 mov ds:dword ptr[_r_leftclipped],1	
 mov eax,ds:dword ptr[mv_position+0+esp]	
 mov ds:dword ptr[_r_leftexit+mv_position+0],eax	
 mov eax,ds:dword ptr[mv_position+4+esp]	
 mov ds:dword ptr[_r_leftexit+mv_position+4],eax	
 mov eax,ds:dword ptr[mv_position+8+esp]	
 mov ds:dword ptr[_r_leftexit+mv_position+8],eax	

 jmp Lcontinue	

;				}

Ltestright:	
;				else if (clip->rightedge)
;				{
 test ah,ah	
 jz Lcontinue	

;					r_rightclipped = true;
;					r_rightexit = clipvert;
 mov ds:dword ptr[_r_rightclipped],1	
 mov eax,ds:dword ptr[mv_position+0+esp]	
 mov ds:dword ptr[_r_rightexit+mv_position+0],eax	
 mov eax,ds:dword ptr[mv_position+4+esp]	
 mov ds:dword ptr[_r_rightexit+mv_position+4],eax	
 mov eax,ds:dword ptr[mv_position+8+esp]	
 mov ds:dword ptr[_r_rightexit+mv_position+8],eax	

;				}
;
;				R_ClipEdge (pv0, &clipvert, clip->next);
;				return;
;			}
 jmp Lcontinue	

;			}

Lp3:	

;			// only point 0 is clipped
;				r_lastvertvalid = false;

 mov ds:dword ptr[_r_lastvertvalid],0	

;				f = d0 / (d0 - d1);
 fld ds:dword ptr[Ld0]	
 fld ds:dword ptr[Ld1]	
 fsubr st(0),st(1)	

;			// we don't cache partially clipped edges
 mov ds:dword ptr[_cacheoffset],07FFFFFFFh	

 fdivp st(1),st(0)	

 sub esp,offset mv_size	; allocate space for clipvert

;				clipvert.position[0] = pv0->position[0] +
;						f * (pv1->position[0] - pv0->position[0]);
;				clipvert.position[1] = pv0->position[1] +
;						f * (pv1->position[1] - pv0->position[1]);
;				clipvert.position[2] = pv0->position[2] +
;						f * (pv1->position[2] - pv0->position[2]);
 fld ds:dword ptr[mv_position+8+edx]	
 fsub ds:dword ptr[mv_position+8+esi]	
 fld ds:dword ptr[mv_position+4+edx]	
 fsub ds:dword ptr[mv_position+4+esi]	
 fld ds:dword ptr[mv_position+0+edx]	
 fsub ds:dword ptr[mv_position+0+esi]	; 0 | 1 | 2

 mov eax,ds:dword ptr[cp_leftedge+ebx]	
 test al,al	

 fmul st(0),st(3)	
 fxch st(1)	; 1 | 0 | 2
 fmul st(0),st(3)	
 fxch st(2)	; 2 | 0 | 1
 fmulp st(3),st(0)	; 0 | 1 | 2
 fadd ds:dword ptr[mv_position+0+esi]	
 fxch st(1)	; 1 | 0 | 2
 fadd ds:dword ptr[mv_position+4+esi]	
 fxch st(2)	; 2 | 0 | 1
 fadd ds:dword ptr[mv_position+8+esi]	
 fxch st(1)	; 0 | 2 | 1
 fstp ds:dword ptr[mv_position+0+esp]	; 2 | 1
 fstp ds:dword ptr[mv_position+8+esp]	; 1
 fstp ds:dword ptr[mv_position+4+esp]	

; replace pv0 with the clip point
 mov esi,esp	

;				if (clip->leftedge)
;				{
 jz Ltestright2	

;					r_leftclipped = true;
;					r_leftenter = clipvert;
 mov ds:dword ptr[_r_leftclipped],1	
 mov eax,ds:dword ptr[mv_position+0+esp]	
 mov ds:dword ptr[_r_leftenter+mv_position+0],eax	
 mov eax,ds:dword ptr[mv_position+4+esp]	
 mov ds:dword ptr[_r_leftenter+mv_position+4],eax	
 mov eax,ds:dword ptr[mv_position+8+esp]	
 mov ds:dword ptr[_r_leftenter+mv_position+8],eax	

 jmp Lcontinue	

;				}

Ltestright2:	
;				else if (clip->rightedge)
;				{
 test ah,ah	
 jz Lcontinue	

;					r_rightclipped = true;
;					r_rightenter = clipvert;
 mov ds:dword ptr[_r_rightclipped],1	
 mov eax,ds:dword ptr[mv_position+0+esp]	
 mov ds:dword ptr[_r_rightenter+mv_position+0],eax	
 mov eax,ds:dword ptr[mv_position+4+esp]	
 mov ds:dword ptr[_r_rightenter+mv_position+4],eax	
 mov eax,ds:dword ptr[mv_position+8+esp]	
 mov ds:dword ptr[_r_rightenter+mv_position+8],eax	

;				}
 jmp Lcontinue	

; %esi = vec3_t point to transform and project
; %edx preserved
LTransformAndProject:	

;	// transform and project
;		VectorSubtract (world, modelorg, local);
 fld ds:dword ptr[mv_position+0+esi]	
 fsub ds:dword ptr[_modelorg_a+0]	
 fld ds:dword ptr[mv_position+4+esi]	
 fsub ds:dword ptr[_modelorg_a+4]	
 fld ds:dword ptr[mv_position+8+esi]	
 fsub ds:dword ptr[_modelorg_a+8]	
 fxch st(2)	; local[0] | local[1] | local[2]

;		TransformVector (local, transformed);
;	
;		if (transformed[2] < NEAR_CLIP)
;			transformed[2] = NEAR_CLIP;
;	
;		lzi0 = 1.0 / transformed[2];
 fld st(0)						; local[0] | local[0] | local[1] | local[2]
 fmul ds:dword ptr[_vpn_a+0]	; zm0 | local[0] | local[1] | local[2]
 fld st(1)						; local[0] | zm0 | local[0] | local[1] | local[2]
 fmul ds:dword ptr[_vright_a+0]	; xm0 | zm0 | local[0] | local[1] | local[2]
 fxch st(2)						; local[0] | zm0 | xm0 | local[1] | local[2]
 fmul ds:dword ptr[_vup_a+0]	; ym0 |  zm0 | xm0 | local[1] | local[2]
 fld st(3)						; local[1] | ym0 |  zm0 | xm0 | local[1] | local[2]
 fmul ds:dword ptr[_vpn_a+4]	; zm1 | ym0 | zm0 | xm0 | local[1] | local[2]
 fld st(4)						; local[1] | zm1 | ym0 | zm0 | xm0 | local[1] | local[2]
 fmul ds:dword ptr[_vright_a+4]	; xm1 | zm1 | ym0 |  zm0 | xm0 | local[1] | local[2]
 fxch st(5)						; local[1] | zm1 | ym0 | zm0 | xm0 | xm1 | local[2]
 fmul ds:dword ptr[_vup_a+4]	; ym1 | zm1 | ym0 | zm0 | xm0 | xm1 | local[2]
 fxch st(1)						; zm1 | ym1 | ym0 | zm0 | xm0 | xm1 | local[2]
 faddp st(3),st(0)				; ym1 | ym0 | zm2 | xm0 | xm1 | local[2]
 fxch st(3)						; xm0 | ym0 | zm2 | ym1 | xm1 | local[2]
 faddp st(4),st(0)				; ym0 | zm2 | ym1 | xm2 | local[2]
 faddp st(2),st(0)				; zm2 | ym2 | xm2 | local[2]
 fld st(3)						; local[2] | zm2 | ym2 | xm2 | local[2]
 fmul ds:dword ptr[_vpn_a+8]	; zm3 | zm2 | ym2 | xm2 | local[2]
 fld st(4)						; local[2] | zm3 | zm2 | ym2 | xm2 | local[2]
 fmul ds:dword ptr[_vright_a+8]	; xm3 | zm3 | zm2 | ym2 | xm2 | local[2]
 fxch st(5)						; local[2] | zm3 | zm2 | ym2 | xm2 | xm3
 fmul ds:dword ptr[_vup_a+8]	; ym3 | zm3 | zm2 | ym2 | xm2 | xm3
 fxch st(1)						; zm3 | ym3 | zm2 | ym2 | xm2 | xm3
 faddp st(2),st(0)				; ym3 | zm4 | ym2 | xm2 | xm3
 fxch st(4)						; xm3 | zm4 | ym2 | xm2 | ym3
 faddp st(3),st(0)				; zm4 | ym2 | xm4 | ym3
 fxch st(1)						; ym2 | zm4 | xm4 | ym3
 faddp st(3),st(0)				; zm4 | xm4 | ym4

 fcom ds:dword ptr[Lfp_near_clip]	
 fnstsw ax	
 test ah,1	
 jz LNoClip	
 fstp st(0)	
 fld ds:dword ptr[Lfp_near_clip]	

LNoClip:	

 fdivr ds:dword ptr[float_1]	; lzi0 | x | y
 fxch st(1)	; x | lzi0 | y

;	// FIXME: build x/yscale into transform?
;		scale = xscale * lzi0;
;		u0 = (xcenter + scale*transformed[0]);
 fld ds:dword ptr[_xscale]		; xscale | x | lzi0 | y
 fmul st(0),st(2)				; scale | x | lzi0 | y
 fmulp st(1),st(0)				; scale*x | lzi0 | y
 fadd ds:dword ptr[_xcenter]	; u0 | lzi0 | y

;		if (u0 < r_refdef.fvrectx_adj)
;			u0 = r_refdef.fvrectx_adj;
;		if (u0 > r_refdef.fvrectright_adj)
;			u0 = r_refdef.fvrectright_adj;
; FIXME: use integer compares of floats?
 fcom ds:dword ptr[_r_refdef+rd_fvrectx_adj]	
 fnstsw ax	
 test ah,1	
 jz LClampP0	
 fstp st(0)	
 fld ds:dword ptr[_r_refdef+rd_fvrectx_adj]	
LClampP0:	
 fcom ds:dword ptr[_r_refdef+rd_fvrectright_adj]	
 fnstsw ax	
 test ah,045h	
 jnz LClampP1	
 fstp st(0)	
 fld ds:dword ptr[_r_refdef+rd_fvrectright_adj]	
LClampP1:	

 fld st(1)	; lzi0 | u0 | lzi0 | y

;		scale = yscale * lzi0;
;		v0 = (ycenter - scale*transformed[1]);
 fmul ds:dword ptr[_yscale]		; scale | u0 | lzi0 | y
 fmulp st(3),st(0)				; u0 | lzi0 | scale*y
 fxch st(2)						; scale*y | lzi0 | u0
 fsubr ds:dword ptr[_ycenter]	; v0 | lzi0 | u0

;		if (v0 < r_refdef.fvrecty_adj)
;			v0 = r_refdef.fvrecty_adj;
;		if (v0 > r_refdef.fvrectbottom_adj)
;			v0 = r_refdef.fvrectbottom_adj;
; FIXME: use integer compares of floats?
 fcom ds:dword ptr[_r_refdef+rd_fvrecty_adj]	
 fnstsw ax	
 test ah,1	
 jz LClampP2	
 fstp st(0)	
 fld ds:dword ptr[_r_refdef+rd_fvrecty_adj]	
LClampP2:	
 fcom ds:dword ptr[_r_refdef+rd_fvrectbottom_adj]	
 fnstsw ax	
 test ah,045h	
 jnz LClampP3	
 fstp st(0)	
 fld ds:dword ptr[_r_refdef+rd_fvrectbottom_adj]	
LClampP3:	
 ret	


_TEXT ENDS
endif	;id386
 END
