1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19*/
20//
21// r_drawa.s
22// x86 assembly-language edge clipping and emission code
23//
24
25#include "asm_i386.h"
26#include "quakeasm.h"
27#include "asm_draw.h"
28#include "d_ifacea.h"
29
30#if	id386
31
32// !!! if these are changed, they must be changed in r_draw.c too !!!
33#define FULLY_CLIPPED_CACHED	0x80000000
34#define FRAMECOUNT_MASK			0x7FFFFFFF
35
36	.data
37
38Ld0:			.single		0.0
39Ld1:			.single		0.0
40Lstack:			.long		0
41Lfp_near_clip:	.single		NEAR_CLIP
42Lceilv0:		.long		0
43Lv:				.long		0
44Lu0:			.long		0
45Lv0:			.long		0
46Lzi0:			.long		0
47
48	.text
49
50//----------------------------------------------------------------------
51// edge clipping code
52//----------------------------------------------------------------------
53
54#define pv0		4+12
55#define pv1		8+12
56#define clip	12+12
57
58	.align 4
59.globl C(R_ClipEdge)
60C(R_ClipEdge):
61	pushl	%esi				// preserve register variables
62	pushl	%edi
63	pushl	%ebx
64	movl	%esp,Lstack			// for clearing the stack later
65
66//	float		d0, d1, f;
67//	mvertex_t	clipvert;
68
69	movl	clip(%esp),%ebx
70	movl	pv0(%esp),%esi
71	movl	pv1(%esp),%edx
72
73//	if (clip)
74//	{
75	testl	%ebx,%ebx
76	jz		Lemit
77
78//		do
79//		{
80
81Lcliploop:
82
83//			d0 = DotProduct (pv0->position, clip->normal) - clip->dist;
84//			d1 = DotProduct (pv1->position, clip->normal) - clip->dist;
85	flds	mv_position+0(%esi)
86	fmuls	cp_normal+0(%ebx)
87	flds	mv_position+4(%esi)
88	fmuls	cp_normal+4(%ebx)
89	flds	mv_position+8(%esi)
90	fmuls	cp_normal+8(%ebx)
91	fxch	%st(1)
92	faddp	%st(0),%st(2)		// d0mul2 | d0add0
93
94	flds	mv_position+0(%edx)
95	fmuls	cp_normal+0(%ebx)
96	flds	mv_position+4(%edx)
97	fmuls	cp_normal+4(%ebx)
98	flds	mv_position+8(%edx)
99	fmuls	cp_normal+8(%ebx)
100	fxch	%st(1)
101	faddp	%st(0),%st(2)		// d1mul2 | d1add0 | d0mul2 | d0add0
102	fxch	%st(3)				// d0add0 | d1add0 | d0mul2 | d1mul2
103
104	faddp	%st(0),%st(2)		// d1add0 | dot0 | d1mul2
105	faddp	%st(0),%st(2)		// dot0 | dot1
106
107	fsubs	cp_dist(%ebx)		// d0 | dot1
108	fxch	%st(1)				// dot1 | d0
109	fsubs	cp_dist(%ebx)		// d1 | d0
110	fxch	%st(1)
111	fstps	Ld0
112	fstps	Ld1
113
114//			if (d0 >= 0)
115//			{
116	movl	Ld0,%eax
117	movl	Ld1,%ecx
118	orl		%eax,%ecx
119	js		Lp2
120
121// both points are unclipped
122
123Lcontinue:
124
125//
126//				R_ClipEdge (&clipvert, pv1, clip->next);
127//				return;
128//			}
129//		} while ((clip = clip->next) != NULL);
130	movl	cp_next(%ebx),%ebx
131	testl	%ebx,%ebx
132	jnz		Lcliploop
133
134//	}
135
136//// add the edge
137//	R_EmitEdge (pv0, pv1);
138Lemit:
139
140//
141// set integer rounding to ceil mode, set to single precision
142//
143// FIXME: do away with by manually extracting integers from floats?
144// FIXME: set less often
145	fldcw	ceil_cw
146
147//	edge_t	*edge, *pcheck;
148//	int		u_check;
149//	float	u, u_step;
150//	vec3_t	local, transformed;
151//	float	*world;
152//	int		v, v2, ceilv0;
153//	float	scale, lzi0, u0, v0;
154//	int		side;
155
156//	if (r_lastvertvalid)
157//	{
158	cmpl	$0,C(r_lastvertvalid)
159	jz		LCalcFirst
160
161//		u0 = r_u1;
162//		v0 = r_v1;
163//		lzi0 = r_lzi1;
164//		ceilv0 = r_ceilv1;
165	movl	C(r_lzi1),%eax
166	movl	C(r_u1),%ecx
167	movl	%eax,Lzi0
168	movl	%ecx,Lu0
169	movl	C(r_v1),%ecx
170	movl	C(r_ceilv1),%eax
171	movl	%ecx,Lv0
172	movl	%eax,Lceilv0
173	jmp		LCalcSecond
174
175//	}
176
177LCalcFirst:
178
179//	else
180//	{
181//		world = &pv0->position[0];
182
183	call	LTransformAndProject	// v0 | lzi0 | u0
184
185	fsts	Lv0
186	fxch	%st(2)					// u0 | lzi0 | v0
187	fstps	Lu0						// lzi0 | v0
188	fstps	Lzi0					// v0
189
190//		ceilv0 = (int)(v0 - 2000) + 2000; // ceil(v0);
191	fistpl	Lceilv0
192
193//	}
194
195LCalcSecond:
196
197//	world = &pv1->position[0];
198	movl	%edx,%esi
199
200	call	LTransformAndProject	// v1 | lzi1 | u1
201
202	flds	Lu0						// u0 | v1 | lzi1 | u1
203	fxch	%st(3)					// u1 | v1 | lzi1 | u0
204	flds	Lzi0					// lzi0 | u1 | v1 | lzi1 | u0
205	fxch	%st(3)					// lzi1 | u1 | v1 | lzi0 | u0
206	flds	Lv0						// v0 | lzi1 | u1 | v1 | lzi0 | u0
207	fxch	%st(3)					// v1 | lzi1 | u1 | v0 | lzi0 | u0
208
209//	r_ceilv1 = (int)(r_v1 - 2000) + 2000; // ceil(r_v1);
210	fistl	C(r_ceilv1)
211
212	fldcw	single_cw				// put back normal floating-point state
213
214	fsts	C(r_v1)
215	fxch	%st(4)					// lzi0 | lzi1 | u1 | v0 | v1 | u0
216
217//	if (r_lzi1 > lzi0)
218//		lzi0 = r_lzi1;
219	fcom	%st(1)
220	fnstsw	%ax
221	testb	$1,%ah
222	jz		LP0
223	fstp	%st(0)
224	fld		%st(0)
225LP0:
226
227	fxch	%st(1)					// lzi1 | lzi0 | u1 | v0 | v1 | u0
228	fstps	C(r_lzi1)				// lzi0 | u1 | v0 | v1 | u0
229	fxch	%st(1)
230	fsts	C(r_u1)
231	fxch	%st(1)
232
233//	if (lzi0 > r_nearzi)	// for mipmap finding
234//		r_nearzi = lzi0;
235	fcoms	C(r_nearzi)
236	fnstsw	%ax
237	testb	$0x45,%ah
238	jnz		LP1
239	fsts	C(r_nearzi)
240LP1:
241
242// // for right edges, all we want is the effect on 1/z
243//	if (r_nearzionly)
244//		return;
245	movl	C(r_nearzionly),%eax
246	testl	%eax,%eax
247	jz		LP2
248LPop5AndDone:
249	movl	C(cacheoffset),%eax
250	movl	C(r_framecount),%edx
251	cmpl	$0x7FFFFFFF,%eax
252	jz		LDoPop
253	andl	$(FRAMECOUNT_MASK),%edx
254	orl		$(FULLY_CLIPPED_CACHED),%edx
255	movl	%edx,C(cacheoffset)
256
257LDoPop:
258	fstp	%st(0)			// u1 | v0 | v1 | u0
259	fstp	%st(0)			// v0 | v1 | u0
260	fstp	%st(0)			// v1 | u0
261	fstp	%st(0)			// u0
262	fstp	%st(0)
263	jmp		Ldone
264
265LP2:
266
267// // create the edge
268//	if (ceilv0 == r_ceilv1)
269//		return;		// horizontal edge
270	movl	Lceilv0,%ebx
271	movl	C(edge_p),%edi
272	movl	C(r_ceilv1),%ecx
273	movl	%edi,%edx
274	movl	C(r_pedge),%esi
275	addl	$(et_size),%edx
276	cmpl	%ecx,%ebx
277	jz		LPop5AndDone
278
279	movl	C(r_pedge),%eax
280	movl	%eax,et_owner(%edi)
281
282//	side = ceilv0 > r_ceilv1;
283//
284//	edge->nearzi = lzi0;
285	fstps	et_nearzi(%edi)		// u1 | v0 | v1 | u0
286
287//	if (side == 1)
288//	{
289	jc		LSide0
290
291LSide1:
292
293//	// leading edge (go from p2 to p1)
294
295//		u_step = ((u0 - r_u1) / (v0 - r_v1));
296	fsubrp	%st(0),%st(3)		// v0 | v1 | u0-u1
297	fsub	%st(1),%st(0)		// v0-v1 | v1 | u0-u1
298	fdivrp	%st(0),%st(2)		// v1 | ustep
299
300//	r_emitted = 1;
301	movl	$1,C(r_emitted)
302
303//	edge = edge_p++;
304	movl	%edx,C(edge_p)
305
306// pretouch next edge
307	movl	(%edx),%eax
308
309//		v2 = ceilv0 - 1;
310//		v = r_ceilv1;
311	movl	%ecx,%eax
312	leal	-1(%ebx),%ecx
313	movl	%eax,%ebx
314
315//		edge->surfs[0] = 0;
316//		edge->surfs[1] = surface_p - surfaces;
317	movl	C(surface_p),%eax
318	movl	C(surfaces),%esi
319	subl	%edx,%edx
320	subl	%esi,%eax
321	shrl	$(SURF_T_SHIFT),%eax
322	movl	%edx,et_surfs(%edi)
323	movl	%eax,et_surfs+2(%edi)
324
325	subl	%esi,%esi
326
327//		u = r_u1 + ((float)v - r_v1) * u_step;
328	movl	%ebx,Lv
329	fildl	Lv					// v | v1 | ustep
330	fsubp	%st(0),%st(1)		// v-v1 | ustep
331	fmul	%st(1),%st(0)		// (v-v1)*ustep | ustep
332	fadds	C(r_u1)				// u | ustep
333
334	jmp		LSideDone
335
336//	}
337
338LSide0:
339
340//	else
341//	{
342//	// trailing edge (go from p1 to p2)
343
344//		u_step = ((r_u1 - u0) / (r_v1 - v0));
345	fsub	%st(3),%st(0)		// u1-u0 | v0 | v1 | u0
346	fxch	%st(2)				// v1 | v0 | u1-u0 | u0
347	fsub	%st(1),%st(0)		// v1-v0 | v0 | u1-u0 | u0
348	fdivrp	%st(0),%st(2)		// v0 | ustep | u0
349
350//	r_emitted = 1;
351	movl	$1,C(r_emitted)
352
353//	edge = edge_p++;
354	movl	%edx,C(edge_p)
355
356// pretouch next edge
357	movl	(%edx),%eax
358
359//		v = ceilv0;
360//		v2 = r_ceilv1 - 1;
361	decl	%ecx
362
363//		edge->surfs[0] = surface_p - surfaces;
364//		edge->surfs[1] = 0;
365	movl	C(surface_p),%eax
366	movl	C(surfaces),%esi
367	subl	%edx,%edx
368	subl	%esi,%eax
369	shrl	$(SURF_T_SHIFT),%eax
370	movl	%edx,et_surfs+2(%edi)
371	movl	%eax,et_surfs(%edi)
372
373	movl	$1,%esi
374
375//		u = u0 + ((float)v - v0) * u_step;
376	movl	%ebx,Lv
377	fildl	Lv					// v | v0 | ustep | u0
378	fsubp	%st(0),%st(1)		// v-v0 | ustep | u0
379	fmul	%st(1),%st(0)		// (v-v0)*ustep | ustep | u0
380	faddp	%st(0),%st(2)		// ustep | u
381	fxch	%st(1)				// u | ustep
382
383//	}
384
385LSideDone:
386
387//	edge->u_step = u_step*0x100000;
388//	edge->u = u*0x100000 + 0xFFFFF;
389
390	fmuls	fp_1m				// u*0x100000 | ustep
391	fxch	%st(1)				// ustep | u*0x100000
392	fmuls	fp_1m				// ustep*0x100000 | u*0x100000
393	fxch	%st(1)				// u*0x100000 | ustep*0x100000
394	fadds	fp_1m_minus_1		// u*0x100000 + 0xFFFFF | ustep*0x100000
395	fxch	%st(1)				// ustep*0x100000 | u*0x100000 + 0xFFFFF
396	fistpl	et_u_step(%edi)		// u*0x100000 + 0xFFFFF
397	fistpl	et_u(%edi)
398
399// // we need to do this to avoid stepping off the edges if a very nearly
400// // horizontal edge is less than epsilon above a scan, and numeric error
401// // causes it to incorrectly extend to the scan, and the extension of the
402// // line goes off the edge of the screen
403// // FIXME: is this actually needed?
404//	if (edge->u < r_refdef.vrect_x_adj_shift20)
405//		edge->u = r_refdef.vrect_x_adj_shift20;
406//	if (edge->u > r_refdef.vrectright_adj_shift20)
407//		edge->u = r_refdef.vrectright_adj_shift20;
408	movl	et_u(%edi),%eax
409	movl	C(r_refdef)+rd_vrect_x_adj_shift20,%edx
410	cmpl	%edx,%eax
411	jl		LP4
412	movl	C(r_refdef)+rd_vrectright_adj_shift20,%edx
413	cmpl	%edx,%eax
414	jng		LP5
415LP4:
416	movl	%edx,et_u(%edi)
417	movl	%edx,%eax
418LP5:
419
420// // sort the edge in normally
421//	u_check = edge->u;
422//
423//	if (edge->surfs[0])
424//		u_check++;	// sort trailers after leaders
425	addl	%esi,%eax
426
427//	if (!newedges[v] || newedges[v]->u >= u_check)
428//	{
429	movl	C(newedges)(,%ebx,4),%esi
430	testl	%esi,%esi
431	jz		LDoFirst
432	cmpl	%eax,et_u(%esi)
433	jl		LNotFirst
434LDoFirst:
435
436//		edge->next = newedges[v];
437//		newedges[v] = edge;
438	movl	%esi,et_next(%edi)
439	movl	%edi,C(newedges)(,%ebx,4)
440
441	jmp		LSetRemove
442
443//	}
444
445LNotFirst:
446
447//	else
448//	{
449//		pcheck = newedges[v];
450//
451//		while (pcheck->next && pcheck->next->u < u_check)
452//			pcheck = pcheck->next;
453LFindInsertLoop:
454	movl	%esi,%edx
455	movl	et_next(%esi),%esi
456	testl	%esi,%esi
457	jz		LInsertFound
458	cmpl	%eax,et_u(%esi)
459	jl		LFindInsertLoop
460
461LInsertFound:
462
463//		edge->next = pcheck->next;
464//		pcheck->next = edge;
465	movl	%esi,et_next(%edi)
466	movl	%edi,et_next(%edx)
467
468//	}
469
470LSetRemove:
471
472//	edge->nextremove = removeedges[v2];
473//	removeedges[v2] = edge;
474	movl	C(removeedges)(,%ecx,4),%eax
475	movl	%edi,C(removeedges)(,%ecx,4)
476	movl	%eax,et_nextremove(%edi)
477
478Ldone:
479	movl	Lstack,%esp			// clear temporary variables from stack
480
481	popl	%ebx				// restore register variables
482	popl	%edi
483	popl	%esi
484	ret
485
486// at least one point is clipped
487
488Lp2:
489	testl	%eax,%eax
490	jns		Lp1
491
492//			else
493//			{
494//			// point 0 is clipped
495
496//				if (d1 < 0)
497//				{
498	movl	Ld1,%eax
499	testl	%eax,%eax
500	jns		Lp3
501
502//				// both points are clipped
503//				// we do cache fully clipped edges
504//					if (!leftclipped)
505	movl	C(r_leftclipped),%eax
506	movl	C(r_pedge),%ecx
507	testl	%eax,%eax
508	jnz		Ldone
509
510//						r_pedge->framecount = r_framecount;
511	movl	C(r_framecount),%eax
512	andl	$(FRAMECOUNT_MASK),%eax
513	orl		$(FULLY_CLIPPED_CACHED),%eax
514	movl	%eax,C(cacheoffset)
515
516//					return;
517	jmp		Ldone
518
519//				}
520
521Lp1:
522
523//			// point 0 is unclipped
524//				if (d1 >= 0)
525//				{
526//				// both points are unclipped
527//					continue;
528
529//			// only point 1 is clipped
530
531//				f = d0 / (d0 - d1);
532	flds	Ld0
533	flds	Ld1
534	fsubr	%st(1),%st(0)
535
536//			// we don't cache partially clipped edges
537	movl	$0x7FFFFFFF,C(cacheoffset)
538
539	fdivrp	%st(0),%st(1)
540
541	subl	$(mv_size),%esp			// allocate space for clipvert
542
543//				clipvert.position[0] = pv0->position[0] +
544//						f * (pv1->position[0] - pv0->position[0]);
545//				clipvert.position[1] = pv0->position[1] +
546//						f * (pv1->position[1] - pv0->position[1]);
547//				clipvert.position[2] = pv0->position[2] +
548//						f * (pv1->position[2] - pv0->position[2]);
549	flds	mv_position+8(%edx)
550	fsubs	mv_position+8(%esi)
551	flds	mv_position+4(%edx)
552	fsubs	mv_position+4(%esi)
553	flds	mv_position+0(%edx)
554	fsubs	mv_position+0(%esi)		// 0 | 1 | 2
555
556// replace pv1 with the clip point
557	movl	%esp,%edx
558	movl	cp_leftedge(%ebx),%eax
559	testb	%al,%al
560
561	fmul	%st(3),%st(0)
562	fxch	%st(1)					// 1 | 0 | 2
563	fmul	%st(3),%st(0)
564	fxch	%st(2)					// 2 | 0 | 1
565	fmulp	%st(0),%st(3)			// 0 | 1 | 2
566	fadds	mv_position+0(%esi)
567	fxch	%st(1)					// 1 | 0 | 2
568	fadds	mv_position+4(%esi)
569	fxch	%st(2)					// 2 | 0 | 1
570	fadds	mv_position+8(%esi)
571	fxch	%st(1)					// 0 | 2 | 1
572	fstps	mv_position+0(%esp)		// 2 | 1
573	fstps	mv_position+8(%esp)		// 1
574	fstps	mv_position+4(%esp)
575
576//				if (clip->leftedge)
577//				{
578	jz		Ltestright
579
580//					r_leftclipped = true;
581//					r_leftexit = clipvert;
582	movl	$1,C(r_leftclipped)
583	movl	mv_position+0(%esp),%eax
584	movl	%eax,C(r_leftexit)+mv_position+0
585	movl	mv_position+4(%esp),%eax
586	movl	%eax,C(r_leftexit)+mv_position+4
587	movl	mv_position+8(%esp),%eax
588	movl	%eax,C(r_leftexit)+mv_position+8
589
590	jmp		Lcontinue
591
592//				}
593
594Ltestright:
595//				else if (clip->rightedge)
596//				{
597	testb	%ah,%ah
598	jz		Lcontinue
599
600//					r_rightclipped = true;
601//					r_rightexit = clipvert;
602	movl	$1,C(r_rightclipped)
603	movl	mv_position+0(%esp),%eax
604	movl	%eax,C(r_rightexit)+mv_position+0
605	movl	mv_position+4(%esp),%eax
606	movl	%eax,C(r_rightexit)+mv_position+4
607	movl	mv_position+8(%esp),%eax
608	movl	%eax,C(r_rightexit)+mv_position+8
609
610//				}
611//
612//				R_ClipEdge (pv0, &clipvert, clip->next);
613//				return;
614//			}
615	jmp		Lcontinue
616
617//			}
618
619Lp3:
620
621//			// only point 0 is clipped
622//				r_lastvertvalid = false;
623
624	movl	$0,C(r_lastvertvalid)
625
626//				f = d0 / (d0 - d1);
627	flds	Ld0
628	flds	Ld1
629	fsubr	%st(1),%st(0)
630
631//			// we don't cache partially clipped edges
632	movl	$0x7FFFFFFF,C(cacheoffset)
633
634	fdivrp	%st(0),%st(1)
635
636	subl	$(mv_size),%esp			// allocate space for clipvert
637
638//				clipvert.position[0] = pv0->position[0] +
639//						f * (pv1->position[0] - pv0->position[0]);
640//				clipvert.position[1] = pv0->position[1] +
641//						f * (pv1->position[1] - pv0->position[1]);
642//				clipvert.position[2] = pv0->position[2] +
643//						f * (pv1->position[2] - pv0->position[2]);
644	flds	mv_position+8(%edx)
645	fsubs	mv_position+8(%esi)
646	flds	mv_position+4(%edx)
647	fsubs	mv_position+4(%esi)
648	flds	mv_position+0(%edx)
649	fsubs	mv_position+0(%esi)		// 0 | 1 | 2
650
651	movl	cp_leftedge(%ebx),%eax
652	testb	%al,%al
653
654	fmul	%st(3),%st(0)
655	fxch	%st(1)					// 1 | 0 | 2
656	fmul	%st(3),%st(0)
657	fxch	%st(2)					// 2 | 0 | 1
658	fmulp	%st(0),%st(3)			// 0 | 1 | 2
659	fadds	mv_position+0(%esi)
660	fxch	%st(1)					// 1 | 0 | 2
661	fadds	mv_position+4(%esi)
662	fxch	%st(2)					// 2 | 0 | 1
663	fadds	mv_position+8(%esi)
664	fxch	%st(1)					// 0 | 2 | 1
665	fstps	mv_position+0(%esp)		// 2 | 1
666	fstps	mv_position+8(%esp)		// 1
667	fstps	mv_position+4(%esp)
668
669// replace pv0 with the clip point
670	movl	%esp,%esi
671
672//				if (clip->leftedge)
673//				{
674	jz		Ltestright2
675
676//					r_leftclipped = true;
677//					r_leftenter = clipvert;
678	movl	$1,C(r_leftclipped)
679	movl	mv_position+0(%esp),%eax
680	movl	%eax,C(r_leftenter)+mv_position+0
681	movl	mv_position+4(%esp),%eax
682	movl	%eax,C(r_leftenter)+mv_position+4
683	movl	mv_position+8(%esp),%eax
684	movl	%eax,C(r_leftenter)+mv_position+8
685
686	jmp		Lcontinue
687
688//				}
689
690Ltestright2:
691//				else if (clip->rightedge)
692//				{
693	testb	%ah,%ah
694	jz		Lcontinue
695
696//					r_rightclipped = true;
697//					r_rightenter = clipvert;
698	movl	$1,C(r_rightclipped)
699	movl	mv_position+0(%esp),%eax
700	movl	%eax,C(r_rightenter)+mv_position+0
701	movl	mv_position+4(%esp),%eax
702	movl	%eax,C(r_rightenter)+mv_position+4
703	movl	mv_position+8(%esp),%eax
704	movl	%eax,C(r_rightenter)+mv_position+8
705
706//				}
707	jmp		Lcontinue
708
709// %esi = vec3_t point to transform and project
710// %edx preserved
711LTransformAndProject:
712
713//	// transform and project
714//		VectorSubtract (world, modelorg, local);
715	flds	mv_position+0(%esi)
716	fsubs	C(modelorg)+0
717	flds	mv_position+4(%esi)
718	fsubs	C(modelorg)+4
719	flds	mv_position+8(%esi)
720	fsubs	C(modelorg)+8
721	fxch	%st(2)				// local[0] | local[1] | local[2]
722
723//		TransformVector (local, transformed);
724//
725//		if (transformed[2] < NEAR_CLIP)
726//			transformed[2] = NEAR_CLIP;
727//
728//		lzi0 = 1.0 / transformed[2];
729	fld		%st(0)				// local[0] | local[0] | local[1] | local[2]
730	fmuls	C(vpn)+0			// zm0 | local[0] | local[1] | local[2]
731	fld		%st(1)				// local[0] | zm0 | local[0] | local[1] |
732								//  local[2]
733	fmuls	C(vright)+0			// xm0 | zm0 | local[0] | local[1] | local[2]
734	fxch	%st(2)				// local[0] | zm0 | xm0 | local[1] | local[2]
735	fmuls	C(vup)+0			// ym0 |  zm0 | xm0 | local[1] | local[2]
736	fld		%st(3)				// local[1] | ym0 |  zm0 | xm0 | local[1] |
737								//  local[2]
738	fmuls	C(vpn)+4			// zm1 | ym0 | zm0 | xm0 | local[1] |
739								//  local[2]
740	fld		%st(4)				// local[1] | zm1 | ym0 | zm0 | xm0 |
741								//  local[1] | local[2]
742	fmuls	C(vright)+4			// xm1 | zm1 | ym0 |  zm0 | xm0 |
743								//  local[1] | local[2]
744	fxch	%st(5)				// local[1] | zm1 | ym0 | zm0 | xm0 |
745								//  xm1 | local[2]
746	fmuls	C(vup)+4			// ym1 | zm1 | ym0 | zm0 | xm0 |
747								//  xm1 | local[2]
748	fxch	%st(1)				// zm1 | ym1 | ym0 | zm0 | xm0 |
749								//  xm1 | local[2]
750	faddp	%st(0),%st(3)		// ym1 | ym0 | zm2 | xm0 | xm1 | local[2]
751	fxch	%st(3)				// xm0 | ym0 | zm2 | ym1 | xm1 | local[2]
752	faddp	%st(0),%st(4)		// ym0 | zm2 | ym1 | xm2 | local[2]
753	faddp	%st(0),%st(2)		// zm2 | ym2 | xm2 | local[2]
754	fld		%st(3)				// local[2] | zm2 | ym2 | xm2 | local[2]
755	fmuls	C(vpn)+8			// zm3 | zm2 | ym2 | xm2 | local[2]
756	fld		%st(4)				// local[2] | zm3 | zm2 | ym2 | xm2 | local[2]
757	fmuls	C(vright)+8			// xm3 | zm3 | zm2 | ym2 | xm2 | local[2]
758	fxch	%st(5)				// local[2] | zm3 | zm2 | ym2 | xm2 | xm3
759	fmuls	C(vup)+8			// ym3 | zm3 | zm2 | ym2 | xm2 | xm3
760	fxch	%st(1)				// zm3 | ym3 | zm2 | ym2 | xm2 | xm3
761	faddp	%st(0),%st(2)		// ym3 | zm4 | ym2 | xm2 | xm3
762	fxch	%st(4)				// xm3 | zm4 | ym2 | xm2 | ym3
763	faddp	%st(0),%st(3)		// zm4 | ym2 | xm4 | ym3
764	fxch	%st(1)				// ym2 | zm4 | xm4 | ym3
765	faddp	%st(0),%st(3)		// zm4 | xm4 | ym4
766
767	fcoms	Lfp_near_clip
768	fnstsw	%ax
769	testb	$1,%ah
770	jz		LNoClip
771	fstp	%st(0)
772	flds	Lfp_near_clip
773
774LNoClip:
775
776	fdivrs	float_1				// lzi0 | x | y
777	fxch	%st(1)				// x | lzi0 | y
778
779//	// FIXME: build x/yscale into transform?
780//		scale = xscale * lzi0;
781//		u0 = (xcenter + scale*transformed[0]);
782	flds	C(xscale)			// xscale | x | lzi0 | y
783	fmul	%st(2),%st(0)		// scale | x | lzi0 | y
784	fmulp	%st(0),%st(1)		// scale*x | lzi0 | y
785	fadds	C(xcenter)			// u0 | lzi0 | y
786
787//		if (u0 < r_refdef.fvrectx_adj)
788//			u0 = r_refdef.fvrectx_adj;
789//		if (u0 > r_refdef.fvrectright_adj)
790//			u0 = r_refdef.fvrectright_adj;
791// FIXME: use integer compares of floats?
792	fcoms	C(r_refdef)+rd_fvrectx_adj
793	fnstsw	%ax
794	testb	$1,%ah
795	jz		LClampP0
796	fstp	%st(0)
797	flds	C(r_refdef)+rd_fvrectx_adj
798LClampP0:
799	fcoms	C(r_refdef)+rd_fvrectright_adj
800	fnstsw	%ax
801	testb	$0x45,%ah
802	jnz		LClampP1
803	fstp	%st(0)
804	flds	C(r_refdef)+rd_fvrectright_adj
805LClampP1:
806
807	fld		%st(1)				// lzi0 | u0 | lzi0 | y
808
809//		scale = yscale * lzi0;
810//		v0 = (ycenter - scale*transformed[1]);
811	fmuls	C(yscale)			// scale | u0 | lzi0 | y
812	fmulp	%st(0),%st(3)		// u0 | lzi0 | scale*y
813	fxch	%st(2)				// scale*y | lzi0 | u0
814	fsubrs	C(ycenter)			// v0 | lzi0 | u0
815
816//		if (v0 < r_refdef.fvrecty_adj)
817//			v0 = r_refdef.fvrecty_adj;
818//		if (v0 > r_refdef.fvrectbottom_adj)
819//			v0 = r_refdef.fvrectbottom_adj;
820// FIXME: use integer compares of floats?
821	fcoms	C(r_refdef)+rd_fvrecty_adj
822	fnstsw	%ax
823	testb	$1,%ah
824	jz		LClampP2
825	fstp	%st(0)
826	flds	C(r_refdef)+rd_fvrecty_adj
827LClampP2:
828	fcoms	C(r_refdef)+rd_fvrectbottom_adj
829	fnstsw	%ax
830	testb	$0x45,%ah
831	jnz		LClampP3
832	fstp	%st(0)
833	flds	C(r_refdef)+rd_fvrectbottom_adj
834LClampP3:
835	ret
836
837#endif	// id386
838
839