/*	
 *	framdprm.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2008 Naoyuki Sawa
 *
 *	* Sun Jan 13 13:34:20 JST 2008 Naoyuki Sawa
 *	- 新規作成。
 *	- これまでは、clipren3.cの中で定義していたdraw_primitive_wz()を、少し変更して移動してきました。
 *	  今後、アセンブラ化するための準備です。
 *	- これまでは、スキャンライン描画関数(draw_scanline_*())だけを高速化していましたが、計測してみると、
 *	  スキャンラインセットアップ関数(draw_primitive_*())もかなりの処理時間を費やしているようだからです。
 *	* Mon Jan 14 23:18:06 JST 2008 Naoyuki Sawa
 *	- draw_primitive_woz()を廃止し、draw_primitive_wz()に統合し、関数名をdraw_primitive()に変更しました。
 *	  draw_primitive_wz()のモジュールframdp1.cと、draw_primitive_woz()のモジュールframdp2.cを統合し、framdprm.cとしました。
 *	- これまで、draw_primitive_wz()とdraw_primitive_woz()の違いは、z成分に対するDDA実行の有無だけでした。
 *	  draw_primitive_woz()を廃止したことにより、render_polygon()から呼ばれた場合も、z成分に対するDDAを行うようになりました。
 *	  これは無駄ですが、実際の性能低下はわずかなので、コードサイズ削減と単純化を優先して、統合することにしました。
 *	* Mon Jan 14 23:29:39 JST 2008 Naoyuki Sawa
 *	- draw_primitive()をアセンブラ化しました。
 *	- C言語版と較べて、速度はあまり変わっていませんが、サイズが小さくなりました。(C版 = 約1KB -> ASM版 = 400バイト)
 *	  サイズが小さくなったので、高速RAMに転送して、高速化し易くなりました。
 *	- draw_primitive()は、スタックが高速RAMにあるかSRAMにあるかによっても、速度がかなり違います。
 *	  できれば、コードだけでなく、スタックも高速RAMに配置すると、最大の高速化が得られます。
 *	* Tue Jan 15 17:25:14 JST 2008 Naoyuki Sawa
 *	- アセンブラ版draw_primitive()を、インターロック回避と遅延スロットによって、高速化しました。
 *	  高速化の効果は、平均的な描画処理全体に対して、0.5%程度のわずかな向上です。
 */
#include "clip.h"

/*****************************************************************************
 *	
 *****************************************************************************/

#ifdef REND_NOASM
void
draw_primitive(RENDVERTEX vertices[/*n_vertices*/], int n_vertices, DSLPARAM* param, DSLPROC draw_scanline)
{
	struct {
		int iv;		/* 現在の辺の終点インデクス            */
		int ny;		/* 現在の辺の高さの変位                */
		int n;		/* 現在の辺の残りライン数              */
		RENDVERTEX cur;	/* 現在値                              */
		DDA x, z;	/* 頂点座標DDA(yは左右共通で1づつ進む) */
		DDA s, t;	/* テクスチャ座標DDA                   */
	} sides[2], *side;

	int i, itop, y, top, bottom;
	RENDVERTEX *v, *v0, *v1;

	/* 上下端の位置と、上端の頂点を求めます。 */
	itop = 0;
	top = bottom = vertices[0].y;
	for(i = 1; i < n_vertices; i++) {
		v = &vertices[i];
		if(v->y < top) {
			itop = i;
			top = v->y;
		}
		if(v->y > bottom) {
			bottom = v->y;
		}
	}
	if(top == bottom) return; /* 水平ポリゴンは描きません。 */

	/* 左右の辺を初期化します。 */
	for(i = 0; i < 2; i++) {
		side = &sides[i];
		side->iv = itop;
		side->n = 0; /* 初回に本当の初期化を行うように */
	}

	/* 下端を除く、各スキャンラインについて... */
	for(y = top; y < bottom; y++) {
		/* 左右の辺について... */
		for(i = 0; i < 2; i++) {
			side = &sides[i];
			/* 次の辺に達したら... */
			if(side->n == 0) {
				do {
					/* 次の辺の両端の頂点を求めます。 */
					v0 = &vertices[side->iv];
					if(i == 0) {
						side->iv--;
						if(side->iv == -1) {
							side->iv = n_vertices - 1;
						}
					} else {
						side->iv++;
						if(side->iv == n_vertices) {
							side->iv = 0;
						}
					}
					v1 = &vertices[side->iv];
					/* この辺の高さの変位を求めます。 */
					side->ny = v1->y - v0->y;
					/* V字ポリゴンを検出したら、残りの描画はキャンセルします。 */
					if(side->ny < 0) return;
				} while(side->ny == 0); /* 水平な辺は無視します。 */
				/* DDAセットアップ。 */
				DRAW_PRIMITIVE_SETUP(x);
				DRAW_PRIMITIVE_SETUP(z); /* render_polygon()から呼ばれた場合は無駄 */
				DRAW_PRIMITIVE_SETUP(s);
				DRAW_PRIMITIVE_SETUP(t);
				/* 次の辺までのカウンタを初期化。 */
				side->n = side->ny;
			}
		}
		/* 左右の辺の間に、水平線を描きます。 */
		sides[0].cur.y = sides[1].cur.y = y;
		draw_scanline(&sides[0].cur, &sides[1].cur, param);
		/* 左右の辺について... */
		for(i = 0; i < 2; i++) {
			side = &sides[i];
			/* 次の辺までのライン数を減らします。 */
			side->n--;
			/* DDAステップ実行。 */
			DRAW_PRIMITIVE_STEP(x)
			DRAW_PRIMITIVE_STEP(z) /* render_polygon()から呼ばれた場合は無駄 */
			DRAW_PRIMITIVE_STEP(s)
			DRAW_PRIMITIVE_STEP(t)
		}
	}
	/* 下端のスキャンラインは描きません。 */
}
#else /*REND_NOASM*/
void draw_primitive(RENDVERTEX vertices[/*n_vertices*/], int n_vertices, DSLPARAM* param, DSLPROC draw_scanline);
asm("
	.code
	.align	1
	.global	draw_primitive
draw_primitive:
	;// %r12 := vertices
	;// %r13 := n_vertices (>=3)
	;// %r14 := param
	;// %r15 := draw_scanline
	pushn	%r3
	xsub	%sp, %sp, 168
	;// [%sp+  0] := side[0].iv	SIDE(0)
	;// [%sp+  2] := side[0].n
	;// [%sp+  4] := side[0].ny2
	;// [%sp+  6] := side[0].x	RENDVERTEX
	;// [%sp+  8] := side[0].y
	;// [%sp+ 10] := side[0].z
	;// [%sp+ 12] := side[0].s
	;// [%sp+ 14] := side[0].t
	;// [%sp+ 16] := side[0].nx2	DDA(x)
	;// [%sp+ 20] := side[0].ux
	;// [%sp+ 24] := side[0].vx
	;// [%sp+ 28] := side[0].dx
	;// [%sp+ 32] := side[0].nz2	DDA(z)
	;// [%sp+ 36] := side[0].uz
	;// [%sp+ 40] := side[0].vz
	;// [%sp+ 44] := side[0].dz
	;// [%sp+ 48] := side[0].ns2	DDA(s)
	;// [%sp+ 52] := side[0].us
	;// [%sp+ 56] := side[0].vs
	;// [%sp+ 60] := side[0].ds
	;// [%sp+ 64] := side[0].nt2	DDA(t)
	;// [%sp+ 68] := side[0].ut
	;// [%sp+ 72] := side[0].vt
	;// [%sp+ 76] := side[0].dt
	;// [%sp+ 80] := side[0].iv	SIDE(1)
	;// [%sp+ 82] := side[0].n
	;// [%sp+ 84] := side[0].ny2
	;// [%sp+ 86] := side[0].x	RENDVERTEX
	;// [%sp+ 88] := side[0].y
	;// [%sp+ 90] := side[0].z
	;// [%sp+ 92] := side[0].s
	;// [%sp+ 94] := side[0].t
	;// [%sp+ 96] := side[0].nx2	DDA(x)
	;// [%sp+100] := side[0].ux
	;// [%sp+104] := side[0].vx
	;// [%sp+108] := side[0].dx
	;// [%sp+112] := side[0].nz2	DDA(z)
	;// [%sp+116] := side[0].uz
	;// [%sp+120] := side[0].vz
	;// [%sp+124] := side[0].dz
	;// [%sp+128] := side[0].ns2	DDA(s)
	;// [%sp+132] := side[0].us
	;// [%sp+136] := side[0].vs
	;// [%sp+140] := side[0].ds
	;// [%sp+144] := side[0].nt2	DDA(t)
	;// [%sp+148] := side[0].ut
	;// [%sp+152] := side[0].vt
	;// [%sp+156] := side[0].dt
	;// [%sp+160] := param
	;// [%sp+164] := draw_scanline
	ld.w	%r0, %r12					; %r0       := vertices
	ld.w	%r1, %r13					; %r1       := n_vertices
	xld.w	[%sp+160], %r14					; [%sp+160] := param
	xld.w	[%sp+164], %r15					; [%sp+164] := draw_scanline
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r12 := vertices
	;// %r13 := n_vertices (>=3)

	;/* 上下端の位置と、上端の頂点を求めます。 */
	ld.w	%r2, -1						; %r2  :=          0xFFFFFFFF
	srl	%r2, 1						; %r2  := top    = 0x7FFFFFFF (=INT_MAX)
	not	%r3, %r2					; %r3  := bottom = 0x80000000 (=INT_MIN)
								; %r4  := itop
	ld.w	%r5, 0						; %r5  := i = 0
	add	%r12, 2						; %r12 := p = &vertices[0].y
								; %r13 := n = n_vertices
draw_primitive_TOP_BOTTOM:					; do {
	ld.uh	%r9, [%r12]					;   %r9  := y = p->y
	add	%r12, 10					;   %r12 := p++						*anti-interlock*
	cmp	%r9, %r2					;   if(y < top)
	jrge.d	4
	 cmp	%r9, %r3					;   <------------------+				*delay*
	 ld.w	%r2, %r9					;     %r2  := top  = y |
	 ld.w	%r4, %r5					;     %r4  := itop = i |
;//	cmp	%r9, %r3					;   if(y > bottom) ----+
	jrle.d	3
	 sub	%r13, 1						;   <--------------------+				*delay*
	 ld.w	%r3, %r9					;     %r3  := bottom = y |
;//	sub	%r13, 1						;   %r13 := n-- ---------+
	jrne.d	draw_primitive_TOP_BOTTOM			; } while(n)
	 add	%r5, 1						;   %r5  := i++						*delay*
	sub	%r3, %r2					; %r3  := lines = bottom - top
	jreq	draw_primitive_EXIT				; if(lines == 0) return
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines
	;// %r4  := itop

	;/* 左右の辺を初期化します。 */
	xld.w	[%sp+ 0], %r4					; [%sp+ 0] := side[0].iv = itop, [%sp+ 2] := side[0].n = 0
	xld.w	[%sp+80], %r4					; [%sp+80] := side[0].iv = itop, [%sp+82] := side[0].n = 0
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines

	;/* 下端を除く、各スキャンラインについて... */
draw_primitive_SCANLINE:					; do {
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines
	;/* 左の辺について... */
	;/* 次の辺に達したら... */
	xld.uh	%r7, [%sp+2]					;   %r7  := n = side[0].n
	sub	%r7, 1						;   if(--n <= 0) {					!INTERLOCK!
	jrgt	draw_primitive_SIDE0_SKIP
	xld.uh	%r4, [%sp+0]					;     %r4  := iv = side[0].iv
draw_primitive_SIDE0_LOOP:					;     do {
	;/* 次の辺の両端の頂点を求めます。 */
	ld.w	%r9, 10						;       %r9  := sizeof(RENDVERTEX)
	mlt.h	%r4, %r9					;       %alr := iv * sizeof(RENDVERTEX)
	ld.w	%r5, %alr					;       %r5  := iv * sizeof(RENDVERTEX)
;//	add	%r5, %r0					;       %r5  := v0 = &vertices[iv] -+
	sub	%r4, 1						;       %r4  := iv--                |
	jrge.d	3						;       if(iv < 0)                  |
	 add	%r5, %r0					;       <---------------------------+			*delay*
	 add	%r4, %r1					;         %r4  := iv += n_vertices
	mlt.h	%r4, %r9					;       %alr := iv * sizeof(RENDVERTEX)
	ld.w	%r6, %alr					;       %r6  := iv * sizeof(RENDVERTEX)
	add	%r6, %r0					;       %r6  := v1 = &vertices[iv]
	;/* この辺の高さの変位を求めます。 */
	xld.uh	%r7, [%r6+2]					;       %r7  := v1->y
	xld.uh	%r9, [%r5+2]					;       %r9  := v0->y
	sub	%r7, %r9					;       %r7  := ny = v1->y - v0->y
	jrlt	draw_primitive_EXIT				;       if(ny < 0) return
	jreq	draw_primitive_SIDE0_LOOP			;     } while(ny == 0)
	;/* 次の辺までのカウンタを初期化。 */
	xld.h	[%sp+0], %r4					;     [%sp+0] := side[0].iv = iv
	sla	%r7, 1						;     %r7  := ny * 2
	xld.h	[%sp+4], %r7					;     [%sp+4] := side[0].ny2 = ny * 2
;//	sra	%r7, 1						;     %r7  := ny -------------+
	;/* DDAセットアップ。 */				;                             |
	ld.w	%r4, %sp					;     %r4  := side = &side[0] |
	;// %r0  := vertices					;                             |
	;// %r1  := n_vertices (>=3)				;                             |
	;// %r2  := y						;                             |
	;// %r3  := lines					;                             |
	;// %r4  := side					;                             |
	;// %r5  := v0						;                             |
	;// %r6  := v1						;                             |
	;// %r7  := ny						;                             |
	call.d	draw_primitive_SETUP				;                             |
	sra	%r7, 1						;     <-----------------------+				*delay*
draw_primitive_SIDE0_SKIP:					;   }
	xld.h	[%sp+2], %r7					;   [%sp+2] := side[0].n = ny
	;/* 右の辺について... */
	;/* 次の辺に達したら... */
	xld.uh	%r7, [%sp+82]					;   %r7  := n = side[1].n
	sub	%r7, 1						;   if(--n <= 0) {					!INTERLOCK!
	jrgt	draw_primitive_SIDE1_SKIP
	xld.uh	%r4, [%sp+80]					;     %r4  := iv = side[1].iv
draw_primitive_SIDE1_LOOP:					;     do {
	;/* 次の辺の両端の頂点を求めます。 */
	ld.w	%r9, 10						;       %r9  := sizeof(RENDVERTEX)
	mlt.h	%r4, %r9					;       %alr := iv * sizeof(RENDVERTEX)
	ld.w	%r5, %alr					;       %r5  := iv * sizeof(RENDVERTEX)
;//	add	%r5, %r0					;       %r5  := v0 = &vertices[iv] -+
	add	%r4, 1						;       %r4  := iv++                |
	cmp	%r4, %r1					;       if(iv >= n_vertices)        |
	jrlt.d	3						;                                   |
	 add	%r5, %r0					;       <---------------------------+			*delay*
	 sub	%r4, %r1					;         %r4  := iv -= n_vertices
	mlt.h	%r4, %r9					;       %alr := iv * sizeof(RENDVERTEX)
	ld.w	%r6, %alr					;       %r6  := iv * sizeof(RENDVERTEX)
	add	%r6, %r0					;       %r6  := v1 = &vertices[iv]
	;/* この辺の高さの変位を求めます。 */
	xld.uh	%r7, [%r6+2]					;       %r7  := v1->y
	xld.uh	%r9, [%r5+2]					;       %r9  := v0->y
	sub	%r7, %r9					;       %r7  := ny = v1->y - v0->y
	jrlt	draw_primitive_EXIT				;       if(ny < 0) return
	jreq	draw_primitive_SIDE1_LOOP			;     } while(ny == 0)
	;/* 次の辺までのカウンタを初期化。 */
	xld.h	[%sp+80], %r4					;     [%sp+80] := side[1].iv = iv
	sla	%r7, 1						;     %r7  := ny * 2
	xld.h	[%sp+84], %r7					;     [%sp+84] := side[1].ny2 = ny * 2
;//	sra	%r7, 1						;     %r7  := ny -------------+
	;/* DDAセットアップ。 */				;                             |
	xadd	%r4, %sp, 80					;     %r4  := side = &side[1] |
	;// %r0  := vertices					;                             |
	;// %r1  := n_vertices (>=3)				;                             |
	;// %r2  := y						;                             |
	;// %r3  := lines					;                             |
	;// %r4  := side					;                             |
	;// %r5  := v0						;                             |
	;// %r6  := v1						;                             |
	;// %r7  := ny						;                             |
	call.d	draw_primitive_SETUP				;                             |
	sra	%r7, 1						;     <-----------------------+				*delay*
draw_primitive_SIDE1_SKIP:					;   }
	xld.h	[%sp+82], %r7					;   [%sp+82] := side[1].n = ny
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines
	;/* 左右の辺の間に、水平線を描きます。 */
	xld.h	[%sp+ 8], %r2					;   [%sp+ 8] := side[0].y = y
	xld.h	[%sp+88], %r2					;   [%sp+88] := side[1].y = y
	xadd	%r12, %sp,  6					;   %r12 := &side[0].x
	xadd	%r13, %sp, 86					;   %r13 := &side[1].x
;//	xld.w	%r14, [%sp+160]					;   %r14 := param --------+
	xld.w	%r9, [%sp+164]					;   %r9  := draw_scanline |
	xld.w	%r14, [%sp+160]					;   <---------------------+				*anti-interlock*
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines
	;// %r12 := &side[0].x
	;// %r13 := &side[1].x
	;// %r14 := param
	call	%r9						;   draw_scanline(&side[0].x, &side[1].x, param)
	;/* 左の辺について... */
	;/* DDAステップ実行。 */
	ld.w	%r4, %sp					;   %r4  := side = &side[0]
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines
	;// %r4  := side
	call	draw_primitive_STEP
	;/* 右の辺について... */
	;/* DDAステップ実行。 */
;//	xadd	%r4, %sp, 80					;   %r4  := side = &side[1] -+
	ld.w	%r4, %sp					;   <------------------------+
	add	%r4, 63						;   <------------------------+
	;// %r0  := vertices					;                            |
	;// %r1  := n_vertices (>=3)				;                            |
	;// %r2  := y						;                            |
	;// %r3  := lines					;                            |
	;// %r4  := side					;                            |
	call.d	draw_primitive_STEP				;                            |
	add	%r4, 17						;   <------------------------+				*delay*

	sub	%r3, 1						;   lines--
	jrne.d	draw_primitive_SCANLINE				; } while(lines)
	add	%r2, 1						;   %r2  := y++						*delay*
draw_primitive_EXIT:
	xadd	%sp, %sp, 168
	popn	%r3
	ret

	;/* DDAセットアップ。 */
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines
	;// %r4  := side
	;// %r5  := v0
	;// %r6  := v1
	;// %r7  := ny
draw_primitive_SETUP:
	xadd	%r10, %r4, 6					; %r10 := &side->x
	xadd	%r11, %r4, 16					; %r11 := &side->nx2
	not	%r15, %r7					; %r15 := d = -ny
;//	add	%r15, 1						; --+
	call.d	draw_primitive_SETUP_ONE			; x |
	add	%r15, 1						; <-+							*delay*
	add	%r5, 2						; y (v0->y skip)
	add	%r6, 2						;   (v1->y skip)
;//	add	%r10, 2						;   (side->y skip) -+
	call.d	draw_primitive_SETUP_ONE			; z                 |
	add	%r10, 2						; <-----------------+					*delay*
	call	draw_primitive_SETUP_ONE			; s
	;/* FALLTHRU */						; t
draw_primitive_SETUP_ONE:
	ld.uh	%r12, [%r5]+					; %r12 := v0->i
	ld.uh	%r13, [%r6]+					; %r13 := v1->i
	ld.h	[%r10]+, %r12					; side->i = v0->i
	sub	%r13, %r12					; %r13 := n = v1->i - v0->i
	jrge.d	5						; if(n < 0)
	 ld.w	%r14, 1						; %r14 := v = 1						*delay*
	 not	%r13, %r13					;   %r13 := n = v0->i - v1->i
	 add	%r13, 1
	 ld.w	%r14, -1					;   %r14 := v = -1
	xsla	%r13, 16
	ld.w	%alr, %r13
	div0u	%r7
	div1	%r7						;//  1
	div1	%r7						;//  2
	div1	%r7						;//  3
	div1	%r7						;//  4
	div1	%r7						;//  5
	div1	%r7						;//  6
	div1	%r7						;//  7
	div1	%r7						;//  8
	div1	%r7						;//  9
	div1	%r7						;// 10
	div1	%r7						;// 11
	div1	%r7						;// 12
	div1	%r7						;// 13
	div1	%r7						;// 14
	div1	%r7						;// 15
	div1	%r7						;// 16
	ld.w	%r12, %ahr					; %r12 := n = (n % ny)
	add	%r12, %r12					; %r12 := n * 2
	ld.w	%r13, %alr					; %r13 := u = (n / ny) * v
	mlt.h	%r13, %r14
	ld.w	%r13, %alr
	ld.w	[%r11]+, %r12					; side->ni2 = n * 2
	ld.w	[%r11]+, %r13					; side->ui = u
	ld.w	[%r11]+, %r14					; side->vi = v
	ld.w	[%r11]+, %r15					; side->di = d
	ret

	;/* DDAステップ実行。 */
	;// %r0  := vertices
	;// %r1  := n_vertices (>=3)
	;// %r2  := y
	;// %r3  := lines
	;// %r4  := side
draw_primitive_STEP:
	xld.uh	%r5, [%r4+4]					; %r5  := ny * 2 = side->ny2
	xadd	%r6, %r4, 6					; %r6  := &side->x
;//	xadd	%r7, %r4, 16					; %r7  := &side->nx2 -+
	ld.w	%r7, %r4					; <-------------------+
	call.d	draw_primitive_STEP_ONE				; x                   |
	add	%r7, 16						; <-------------------+					*delay*
;//	add	%r6, 2						; y (side->y skip) -+
	call.d	draw_primitive_STEP_ONE				; z                 |
	add	%r6, 2						; <-----------------+					*delay*
	call	draw_primitive_STEP_ONE				; s
	;/* FALLTHRU */						; t
draw_primitive_STEP_ONE:
;//	ld.uh	%r10, [%r6]					; %r10 := i = side->i ------+
	ld.w	%r11, [%r7]+					; %r11 := n * 2 = side->ni2 |
	ld.w	%r12, [%r7]+					; %r12 := u = side->ui      |
	ld.w	%r13, [%r7]+					; %r13 := v = side->vi      |
	ld.w	%r14, [%r7]					; %r14 := d = side->di      |
	ld.uh	%r10, [%r6]					; <-------------------------+				*anti-interlock*
	add	%r14, %r11					; %r14 := d += n * 2
	jrlt.d	4						; if(d >= 0)
	 add	%r10, %r12					; %r10 := i += u					*delay*
	 add	%r10, %r13					;   %r10 := i += v
	 sub	%r14, %r5					;   %r14 := d -= ny * 2
	ld.h	[%r6]+, %r10					; side->i  = i
	ld.w	[%r7]+, %r14					; side->di = d
	ret
");
#endif /*REND_NOASM*/
