/*
 *	clipdda.c
 *
 *	汎用DDA処理
 *
 *	* Sun Jun 04 22:03:50 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	  当モジュールを作成した理由は、以下の通りです。
 *	- これまでは、clipmisc.hにてDDA構造体だけを定義し、各モジュールにて個別に処理を実装していました。
 *	  そうしていた理由は、レジスタの退避を減らしたりレジスタの使い回しを工夫して高速化するためでした。
 *	  しかし、極端に高速化が要求されない場合は、汎用DDA処理が有る方が手間が省けて便利だと思いました。
 *	  そこで、汎用DDA処理として当モジュールを作成する事にしました。
 *	- 今後、極端な高速化が不要で、特別な計算方法も必要無い場合は、当モジュールを使うようにして下さい。
 *	  ちなみに、極端な高速化が必要な場合とは、既存のframds15.cモジュール等です。
 *	  また、特別な計算方法が必要な場合とは、既存のframrodg.cのRodGen_DrawLine1()です。(誤差項の初期値をずらしています)
 *	  上記のような特別な場合は、当モジュールを使わずに、これまで通り各モジュール個別にDDA処理を実装する必要が有ります。
 */
#include "clip.h"
//*****************************************************************************
//	グローバル関数
//*****************************************************************************
//DDAセットアップ処理を行います。
//[in]
//	dda		管理構造体。
//	dx		主軸長。		(dx> 0)でも(dx< 0)でも(dx== 0)でも構いません。
//	y1		副軸の開始値。
//	y2		副軸の目標値。		(y1>y2)でも(y1<y2)でも(y1==y2)でも構いません。
//[mod]
//	- アセンブラ版では、下記のレジスタだけを破壊します。
//	  │%r9,%r10,%r11,%r12,%r13,%r14,%r15,%alr,%ahr,%psr
//	  上記以外のレジスタが維持される事を前提として、呼び出し側の処理を作成して構いません。
//	  具体的には、呼び出し側の処理において、%r4〜%r7を退避・復帰する処理が不要となります。
//[note]
//	- 主軸長の正負は、結果に影響しません。
//	  例えば、(dx=10)を指定しても、(dx=-10)を指定しても、同じ結果になります。
//	- 主軸長に0が指定された場合は、dda_step()が常に副軸の目標値を返すように、管理構造体を初期化します。
//	  実際には主軸長が0の場合にDDAを行う事は無いはずですが、もしDDAを行った場合上記の動作が適切だと思ったからです。
#ifndef PIECE
void dda_setup(struct dda* dda, int dx, int y1, int y2) {
	if(!(dx = abs(dx))) {		//主軸長(+)
	     dx = 1;			//┬主軸長が0だったならば、dda_step()常にy2を返すように管理構造体を初期化する。
	     y1 = y2; }			//┘
	{ int     dy = y2 - y1;		//副軸長(+/-)
	dda->i =            y1;		//現在値(+/-)
	dda->e =       dx;		//誤差項
	dda->u =  dy / dx;		//現在値の固定変化量(+/-)
	dda->n = (dy % dx) << 1;	//誤差項の固定減少量				未決定	─┐
	dda->v =          1;		//誤差項が0以下になった時の現在値の変化量	未決定	─│┐
	dda->f =       dx  << 1;	//誤差項が0以下になった時の誤差項の増加量(+)		　││
	if(dy < 0) {			//副軸長が(-)だったならば…				　││
	     dda->n = -dda->n;		//誤差項の固定減少量(+)				決定	←┘│
	     dda->v = -1; } }		//誤差項が0以下になった時の現在値の変化量(+/-)	決定	←─┘
}
#else //PIECE
void dda_setup(struct dda* dda, int dx, int y1, int y2);
asm("
		.code
		.align		1
		.global		dda_setup
dda_setup:
		;//%r12 := dda
		;//%r13 := dx
		;//%r14 := y1
		;//%r15 := y2
		cmp		%r13, 1				;//if(dx < 1) {						%psr(C) := !dx
		jrge.d		6				;//
		 ld.w		%r11, 1				;//%r11 := v = 1			*delay*
		 jruge.d	3				;//  if(!dx) {
		  not		%r13, %r13			;//           %r13 := dx = -dx + 1 = 1	*delay*
		  ld.w		%r14, %r15			;//           %r14 := y1 =  y2
		 adc		%r13, %r11			;//  } else { %r13 := dx = -dx }
		ld.w		[%r12]+, %r14			;//dda->i := y1
		ld.w		[%r12]+, %r13			;//dda->e := dx
		sub		%r15, %r14			;//%r15   :=      dy = y2 - y1
		ld.w		%r14, %r12			;//%r14   := dda
		xcall.d		__divsi3			;//%r10   := u =  dy / dx				%ahr := dy % dx
		ld.w		%r12, %r15			;//%r12   :=      dy			*delay*
		ld.w		[%r14]+, %r10			;//dda->u := u
		ld.w		%r10, %ahr			;//%r10   :=      dy % dx
		sla		%r10, 1				;//%r10   := n = (dy % dx) << 1
		cmp		%r15, 0				;//if(dy < 0) {
		jrge.d		5				;//
		 sla		%r13, 1				;//%r13   := f = dx << 1		*delay*
		 not		%r10, %r10			;//  %r10 := n = -n
		 add		%r10, 1				;//  
		 ld.w		%r11, -1			;//  %r11 := v = -1 }
		ld.w		[%r14]+, %r10			;//dda->n := n
		ld.w		[%r14]+, %r11			;//dda->v := v
		ld.w		[%r14], %r13			;//dda->f := f
		ret
");
#endif//PIECE
//-----------------------------------------------------------------------------
//DDAステップ実行を行います。
//[in]
//	dda		管理構造体。
//[out]
//	戻り値		変化前の現在値。
//[mod]
//	- アセンブラ版では、下記のレジスタだけを破壊します。
//	  │%r9,%r10,%r11,%r12,%r13,%r14,%r15,%alr,%ahr,%psr
//	  上記以外のレジスタが維持される事を前提として、呼び出し側の処理を作成して構いません。
//	  具体的には、呼び出し側の処理において、%r4〜%r7を退避・復帰する処理が不要となります。
//[note]
//	- これまで各モジュールで個別にDDAを実装していた箇所では、今回の現在値を使用した後にDDAステップ実行を行う事が多かったのですが、
//	  当関数を使用する場合は、DDAステップ実行と同時に変化前の現在値を取得する事に注意して下さい。
//	  これまで各モジュールで個別にDDAを実装していた箇所では、ループの最後でDDAステップ実行を行っていましたが、
//	  当関数を使用する場合は、ループの先頭で当関数を呼び出してDDAステップ実行を行う形式になります。
#ifndef PIECE
int dda_step(struct dda* dda) {
	int save_i = dda->i;		//変化前の現在値を退避しておく。
	dda->i += dda->u;		//現在値に固定変化量を加算する。
	if((dda->e -= dda->n) <= 0) {	//誤差項から固定減少量を減算し、0以下になったら…
	    dda->e += dda->f;		//誤差項が0以下になった時の誤差項の増加量を加算する。
	    dda->i += dda->v; }		//誤差項が0以下になった時の現在値の変化量を加算する。
	return save_i;			//変化前の現在値を返す。
}
#else //PIECE
int dda_step(struct dda* dda);
asm("
		.code
		.align		1
		.global		dda_step
dda_step:
		;//%r12 := dda
		ld.w		%r9, %r12			;//%r9    :=               dda
		ld.w		%r10, [%r9]+			;//%r10   :=      save_i = dda->i
		ld.w		%r11, %r10			;//%r11   := i := save_i
		ld.w		%r13, [%r9]+			;//%r13   := e :=          dda->e
		ld.w		%r14, [%r9]+			;//%r14   := u :=          dda->u
		ld.w		%r15, [%r9]+			;//%r15   := n :=          dda->n
		sub		%r13, %r15			;//%r13   := e -= n
		jrgt.d		6				;//if(e <= 0) {
		 add		%r11, %r14			;//  %r11 := i += u			*delay*
		 ld.w		%r14, [%r9]+			;//  %r14 := v :=          dda->v
		 ld.w		%r15, [%r9]			;//  %r15 := f :=          dda->f
		 add		%r11, %r14			;//  %r11 := i += v
		 add		%r13, %r15			;//  %r13 := e += f }
		ld.w		[%r12]+, %r11			;//dda->i := i
		ld.w		[%r12], %r13			;//dda->e := e
		ret						;//return         save_i
");
#endif//PIECE
//*****************************************************************************
//	使用例
//*****************************************************************************
#if 0
int app_main(int argc, char* argv[]) {
	struct dda dda; int x;
	dda_setup(&dda,  5, -6,  7); for(x = 0; x < 5; x++) { printf("%2d ", dda_step(&dda)); } puts(""); //-6 -3 -1  2  4
	dda_setup(&dda,  5,  7, -6); for(x = 0; x < 5; x++) { printf("%2d ", dda_step(&dda)); } puts(""); // 7  4  2 -1 -3
	dda_setup(&dda, -5, -6,  7); for(x = 0; x < 5; x++) { printf("%2d ", dda_step(&dda)); } puts(""); //-6 -3 -1  2  4
	dda_setup(&dda, -5,  7, -6); for(x = 0; x < 5; x++) { printf("%2d ", dda_step(&dda)); } puts(""); // 7  4  2 -1 -3
	dda_setup(&dda,  0, -6,  7); for(x = 0; x < 5; x++) { printf("%2d ", dda_step(&dda)); } puts(""); // 7  7  7  7  7
	dda_setup(&dda,  0,  7, -6); for(x = 0; x < 5; x++) { printf("%2d ", dda_step(&dda)); } puts(""); //-6 -6 -6 -6 -6
	return 0;
}
#endif
