;	
;	framline.s
;
;	高速Lineルーチン
;
;	CLiP - Common Library for P/ECE
;	Copyright (C) 2001-2004 Naoyuki Sawa
;
;	* Mon Dec 20 19:13:00 JST 2004 Naoyuki Sawa
;	- 1stリリース。
;

; cliprend.hでREND_NOASMの定義有りならば、ここでも定義有りにしてください。
; cliprend.hでREND_NOASMの定義無しならば、ここでも定義無しにしてください。
; cliprend.hでREND_NOASMが定義無しなのにここで定義有りだと、render_lineの二重定義エラーになります。
; cliprend.hでREND_NOASMが定義有りなのにここで定義無しだと、render_lineの定義無しエラーになります。
;#define REND_NOASM
#ifndef REND_NOASM

;
;	SURFACE構造体
;
#define W	 0	; (short)
#define H	 2	; (short)
#define VBUFF	 4	; (unsigned char*)
; これ以降のフィールドはここでは使いません

;
;	RENDER構造体
;
#define SURFACE	 0	; (SURFACE*)
#define LEFT	 4	; (short)
#define TOP	 6	; (short)
#define RIGHT	 8	; (short)
#define BOTTOM	10	; (short)
; これ以降のフィールドはここでは使いません

	.code
	.align 1

; %r10 /= %r11 を行います。
; CLIP0/1マクロから利用する、ローカルサブルーチンです。
div_r10_r11:
	ld.w %alr, %r10
	div0s %r11
	div1 %r11		;  1
	div1 %r11		;  2
	div1 %r11		;  3
	div1 %r11		;  4
	div1 %r11		;  5
	div1 %r11		;  6
	div1 %r11		;  7
	div1 %r11		;  8
	div1 %r11		;  9
	div1 %r11		; 10
	div1 %r11		; 11
	div1 %r11		; 12
	div1 %r11		; 13
	div1 %r11		; 14
	div1 %r11		; 15
	div1 %r11		; 16
	div1 %r11		; 17
	div1 %r11		; 18
	div1 %r11		; 19
	div1 %r11		; 20
	div1 %r11		; 21
	div1 %r11		; 22
	div1 %r11		; 23
	div1 %r11		; 24
	div1 %r11		; 25
	div1 %r11		; 26
	div1 %r11		; 27
	div1 %r11		; 28
	div1 %r11		; 29
	div1 %r11		; 30
	div1 %r11		; 31
	div1 %r11		; 32
	div2s %r11
	div3s
	ret.d
	ld.w %r10, %alr		; *delay* (undoc'd)

; 一軸、一方向のクリッピングを行います。
; [in/out]
;	$1	XA      (始点側の主軸座標)
;	$2	YA      (始点側の副軸座標)
;	$3	XB      (終点側の主軸座標)
;	$4	YB      (終点側の副軸座標)
;	$5	OP      (クリッピング領域の内側判定条件)
;	$6	!OP     (クリッピング領域の外側判定条件)
;	$7	LIM     (クリッピング判定エッジ主軸座標)
;	$8	CLIPPED ((x1,y1)側クリッピングフラグ)
;	$9	EXIT    (完全に領域外だった場合のJump先)
; [use]
;	%r10
;	%r11
;	%alr
;	%ahr
; (x0,y0)側のクリッピングを行います。
#macro CLIP0 $1, $2, $3, $4, $5, $6, $7, $8, $9
	cmp $1, $7			; if(!(XA OP LIM)) {
	xjr$5 $$1			;
	cmp $3, $7			;   if(!(XB OP LIM)) {
	xjr$6 $9			;     return
					;   } else {
	ld.w %r10, $4			;     (YB - YA)
	sub %r10, $2			;
	ld.w %r11, $7			;     (LIM - XA)
	sub %r11, $1			;
	;mlt.w %r10, %r11		;     (YB - YA) * (LIM - XA)
	;↓実用上、(short)×(short)で充分です。
	mlt.h %r10, %r11		;     (YB - YA) * (LIM - XA)
	ld.w %r10, %alr			;
	ld.w %r11, $3			;     (XB - XA)
	xcall.d div_r10_r11		;     (YB - YA) * (LIM - XA) / (XB - XA)
	sub %r11, $1			;     *delay*
	add $2, %r10			;     YA += (YB - YA) * (LIM - XA) / (XB - XA)
	ld.w $1, $7			;     XA = LIM
	;xoor $8, $8, 1			;     CLIPPED |= 1 ★CLIP0/1で異なるのはここだけ★
					;   }
$$1:					; }
#endm
; (x1,y1)側のクリッピングを行います。
#macro CLIP1 $1, $2, $3, $4, $5, $6, $7, $8, $9
	cmp $1, $7			; if(!(XA OP LIM)) {
	xjr$5 $$1			;
	cmp $3, $7			;   if(!(XB OP LIM)) {
	xjr$6 $9			;     return
					;   } else {
	ld.w %r10, $4			;     (YB - YA)
	sub %r10, $2			;
	ld.w %r11, $7			;     (LIM - XA)
	sub %r11, $1			;
	;mlt.w %r10, %r11		;     (YB - YA) * (LIM - XA)
	;↓実用上、(short)×(short)で充分です。
	mlt.h %r10, %r11		;     (YB - YA) * (LIM - XA)
	ld.w %r10, %alr			;
	ld.w %r11, $3			;     (XB - XA)
	xcall.d div_r10_r11		;     (YB - YA) * (LIM - XA) / (XB - XA)
	sub %r11, $1			;     *delay*
	add $2, %r10			;     YA += (YB - YA) * (LIM - XA) / (XB - XA)
	ld.w $1, $7			;     XA = LIM
	xoor $8, $8, 1			;     CLIPPED |= 1 ★CLIP0/1で異なるのはここだけ★
					;   }
$$1:					; }
#endm

; void render_line(RENDER* render, int x0, int y0, int x1, int y1, int color);
; 手順はC言語版と同じです。clipren2.cのrender_line()の実装を参照してください。
	.global render_line
render_line:
	; %r12 = render
	; %r13 = x0
	; %r14 = y0
	; %r15 = x1
	; [%sp+0] = %pc
	; [%sp+1] = y1
	; [%sp+2] = color
	ld.w %r4, [%sp+1]		; y1
	ld.w %r5, [%sp+2]		; color
	; %r4  = y1
	; %r5  = color
	; %r12 = render
	; %r13 = x0
	; %r14 = y0
	; %r15 = x1
	xld.w %r6, 0							; clipped = 0
	xld.h %r7, [%r12+LEFT]						; left
	CLIP0 %r13, %r14, %r15, %r4, ge, lt, %r7, %r6, render_line_EXIT	; CLIP(x, y, 0, 1, >=, render->left)
	CLIP1 %r15, %r4, %r13, %r14, ge, lt, %r7, %r6, render_line_EXIT	; CLIP(x, y, 1, 0, >=, render->left)
	xld.h %r7, [%r12+RIGHT]						; right-1
	xsub %r7, %r7, 1						;
	CLIP1 %r15, %r4, %r13, %r14, le, gt, %r7, %r6, render_line_EXIT	; CLIP(x, y, 1, 0, <=, render->right - 1)
	CLIP0 %r13, %r14, %r15, %r4, le, gt, %r7, %r6, render_line_EXIT	; CLIP(x, y, 0, 1, <=, render->right - 1)
	xld.h %r7, [%r12+TOP]						; top
	CLIP0 %r14, %r13, %r4, %r15, ge, lt, %r7, %r6, render_line_EXIT	; CLIP(y, x, 0, 1, >=, render->top)
	CLIP1 %r4, %r15, %r14, %r13, ge, lt, %r7, %r6, render_line_EXIT	; CLIP(y, x, 1, 0, >=, render->top)
	xld.h %r7, [%r12+BOTTOM]					; bottom-1
	xsub %r7, %r7, 1						;
	CLIP0 %r14, %r13, %r4, %r15, le, gt, %r7, %r6, render_line_EXIT	; CLIP(y, x, 0, 1, <=, render->bottom - 1)
	CLIP1 %r4, %r15, %r14, %r13, le, gt, %r7, %r6, render_line_EXIT	; CLIP(y, x, 1, 0, <=, render->bottom - 1)
	; %r4  = y1
	; %r5  = color
	; %r6  = clipped
	; %r12 = render
	; %r13 = x0
	; %r14 = y0
	; %r15 = x1
	xld.w %r12, [%r12+SURFACE]	; surface
	xld.h %r11, [%r12+W]		; w (あとでまた使います。破壊しないように!!)
	xld.w %r12, [%r12+VBUFF]	; vbuff *anti-interlock*
	;mlt.w %r11, %r14		; w * y0
	;↓実用上、(short)×(short)で充分です。
	mlt.h %r11, %r14		; w * y0
	ld.w %r10, %alr			;
	add %r12, %r10			; vptr = vbuff + w * y0 + x0
	add %r12, %r13
	; %r4  = y1
	; %r5  = color
	; %r6  = clipped
	; %r11 = w
	; %r12 = vptr
	; %r13 = x0
	; %r14 = y0
	; %r15 = x1
	sub %r15, %r13			; nx = x1 - x0, ux = 1
	xjrge.d render_line_L10		; if(x1 < x0) {
	ld.w %r10, 1			; *delay*
	not %r15, %r15			;   nx = x0 - x1
	xadd %r15, %r15, 1		;
	xld.w %r10, -1			;   ux = -1
render_line_L10:			; }
	sub %r4, %r14			; ny = y1 - y0, uy = w
	xjrge render_line_L20		; if(y1 < y0) {
	not %r4, %r4			;   ny = y0 - y1
	xadd %r4, %r4, 1		;
	not %r11, %r11			;   uy = -w
	xadd %r11, %r11, 1		;
render_line_L20:			; }
	; %r4  = ny
	; %r5  = color
	; %r6  = clipped
	; %r10 = ux
	; %r11 = uy
	; %r12 = vptr
	; %r13 = x0 -> もう不要
	; %r14 = y0 -> もう不要
	; %r15 = nx
	cmp %r15, %r4			; if(nx < ny) {
	xjrge render_line_L30		;
	xor %r15, %r4			;   nx <=> ny
	xor %r4, %r15			;
	xor %r15, %r4			;
	xor %r10, %r11			;   ux <=> uy
	xor %r11, %r10			;
	xor %r10, %r11			;
render_line_L30:			; }
	xcmp %r15, 0			; if(nx == 0)
	xjreq render_line_EXIT		;   return
	; %r4  = ny
	; %r5  = color
	; %r6  = clipped
	; %r10 = ux
	; %r11 = uy
	; %r12 = vptr
	; %r15 = nx
	ld.w %r7, %r15			; d = nx
	add %r6, %r15			; n = nx + clipped
	add %r15, %r15			; nx * 2
	add %r4, %r4			; ny * 2
	; %r4  = ny * 2
	; %r5  = color
	; %r6  = n
	; %r7  = d
	; %r10 = ux
	; %r11 = uy
	; %r12 = vptr
	; %r15 = nx * 2
;{{DelaySlotを使わない素直なコード
;render_line_L40:			; do {
;	ld.b [%r12], %r5		;   *vptr = color
;	sub %r7, %r4			;   d -= ny * 2
;	xjrgt.d render_line_L50		;   if(d <= 0) {
;	add %r12, %r10			;   vptr += ux *delay*
;	add %r12, %r11			;     vptr += uy
;	add %r7, %r15			;     d += nx * 2
;render_line_L50:			;   }
;	xsub %r6, %r6, 1		; } while(--n)
;	xjrne render_line_L40
;}}DelaySlotを使わない素直なコード
;{{DelaySlotを使い高速化したコード
	sub %r7, %r4			;   d -= ny * 2 (初回分)
render_line_L40:			; do {
	ld.b [%r12], %r5		;   *vptr = color (sub %r7,%r4の結果フラグを壊さないように!!)
	;sub %r7, %r4			;   d -= ny * 2 (初回分は↑、二回目以降は↓のDelaySlotに分離)
	xjrgt.d render_line_L50		;   if(d <= 0) {
	add %r12, %r10			;   vptr += ux *delay*
	add %r12, %r11			;     vptr += uy
	add %r7, %r15			;     d += nx * 2
render_line_L50:			;   }
	xsub %r6, %r6, 1		; } while(--n)
	xjrne.d render_line_L40		;
	sub %r7, %r4			;   d -= ny * 2 (二回目以降) *delay*
;}}DelaySlotを使い高速化したコード
render_line_EXIT:
	ret

#endif REND_NOASM
