/*	
 *	clipren3.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2009 Naoyuki Sawa
 *
 *	* Wed Feb 13 06:00:00 JST 2002 Naoyuki Sawa
 *	- 作成開始。
 *	* Fri Mar  8 07:00:00 JST 2002 Naoyuki Sawa
 *	- render_line()で終点側がクリッピングされた時に、
 *	  画面端に1ピクセル隙間が出来てしまう問題を修正。
 *	* Wed Apr 24 10:11:00 JST 2002 Naoyuki Sawa
 *	- render_polygon()を追加。
 *	* Wed Jun  5 13:51:00 JST 2002 Naoyuki Sawa
 *	- render_point()を追加。
 *	* Wed Jun 18 21:39:00 JST 2002 Naoyuki Sawa
 *	- render_fill()を追加。
 *	* Sat Jul  6 08:42:00 JST 2002 Naoyuki Sawa
 *	- surface_point()を追加。
 *	* Sat Jul  6 17:51:00 JST 2002 Naoyuki Sawa
 *	- render_fill()の関数名をrender_paint()に変更。
 *	- render_rectangle()を追加。
 *	* Thu Jul 25 04:07:00 JST 2002 Naoyuki Sawa
 *	- render_rectangle_fill()を追加。
 *	* Sun Jul 28 16:56:00 JST 2002 Naoyuki Sawa
 *	- render_paint()に戻り値(塗ったピクセル数)を追加。
 *	* Sat Sep 21 23:02:00 JST 2002 Naoyuki Sawa
 *	- render_dashes()追加。
 *	* Sat Oct 21 15:58:00 JST 2002 Naoyuki Sawa
 *	- render_dashes()に戻り値を追加。
 *	* Sat Mar  8 06:23:00 JST 2003 Naoyuki Sawa
 *	- 描画用ワーク(xformed_vectors[],clip_vertices[]など)を_def_vbuff[]上に確保。
 *	* Wed Mar 26 05:00:00 JST 2003 Naoyuki Sawa
 *	- 浮動小数→固定小数変更につき、ジオメトリステージを大幅に改造。
 *	  浮動小数版の旧ソースは keep/浮動小数版演算描画コード保存 に保存してあります。
 *	  固定小数版もロジックは浮動小数版と同じで、可読性は浮動小数版の方が高いので、
 *	  浮動小数版の旧ソースは破棄しないで下さい。
 *	* Sun Mar 30 19:00:00 JST 2003 Naoyuki Sawa
 *	- スキャンライン描画関数をアセンブラで高速化しました。40%ぐらい速くなりました。
 *	  NOASMシンボルの定義/未定義によって、Cルーチンとアセンブラルーチンを切り替えられます。
 *	- まずは使用頻度の高い、Ｚ比較ありの、color・2bit・2bitmaskだけをアセンブラ化しました。
 *	  その他のスキャンライン描画関数も、今後必要に応じてアセンブラ化する予定です。
 *	  draw_scanline_wz_2bitmaskが全機能バージョンですので、これを基に削ればOKだと思います。
 *	* Tue Apr  1 05:13:00 JST 2003 Naoyuki Sawa
 *	- クリッピングを少し高速化。
 *	  始点・終点座標インデクスの計算に剰余演算が使われていたのを、条件分岐に変更。
 *	* Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa
 *	- スキャンライン描画関数の一部を高速RAM転送候補としてframds??.cへ分離。
 *	  それに伴い、構造体の定義をcliprend.hへ移動。
 *	  また、c/asm切り換えシンボルをNOASM→REND_NOASMに改名し、cliprend.hへ移動。
 *	* Tue Apr  2 08:00:00 JST 2003 Naoyuki Sawa
 *	- clip_z(),clip_xy()にクリッピング不要検査を追加し、高速化。
 *	* Sun Sep 14 18:44:00 JST 2003 Naoyuki Sawa
 *	- cliprend.c→clipren2.c(2D描画)/clipren3.d(3D描画)に分割。
 *	- render_init()をrender_init_2d()/render_init_3d()に分割。
 *	* Thu Jul  8 06:00:00 JST 2004 Naoyuki Sawa
 *	- fcom()とficom()の引数順序変更に追従。
 *	* Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa
 *	- draw_scanline_wz_1bit()をframds12.cへ移動しました。
 *	- draw_scanline_wz_1bitmask()をframds13.cへ移動しました。
 *	- draw_scanline_woz_color()をframds21.cへ移動しました。
 *	- draw_scanline_woz_1bit()をframds22.cへ移動しました。
 *	- draw_scanline_woz_1bitmask()をframds23.cへ移動しました。
 *	- draw_scanline_woz_2bit()をframds24.cへ移動しました。
 *	- draw_scanline_woz_2bitmask()をframds25.cへ移動しました。
 *	* Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
 *	- select_draw_scanline_wz()とselect_draw_scanline_woz()に、以下の変更を行いました。
 *	- colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
 *	  必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
 *	- テクスチャ無しポリゴンの描画色を、背景色でなく、前景色に変更しました。
 *	  前述の入れ替えと相殺して、下位ニブルで描画色を指定することに変わりありません。
 *	* Sun Jan 13 13:34:20 JST 2008 Naoyuki Sawa
 *	- draw_primitive_wz()をframdp1.cへ、draw_primitive_woz()をframdp2.cへ、移動しました。
 *	  draw_primitive_wz()とdraw_primitive_woz()を、今後、アセンブラ化するための準備です。
 *	- この変更に伴い、いくつかの細かな変更を行いました。
 *	  変更箇所は、cliprend.hとclipren3.cを、「2008/01/13」で検索してください。
 *	* Mon Jan 14 23:18:06 JST 2008 Naoyuki Sawa
 *	- draw_primitive_woz()とdraw_primitive_wz()の統合に追従しました。
 *	  変更箇所は、本モジュールを、「2008/01/14」で検索してください。
 *	* Tue Apr 01 12:56:34 JST 2008 Naoyuki Sawa
 *	- render_mesh()の中の、全頂点座標を変換する処理を分離し、以下の方法で高速化しました。
 *	  ・vxform()はスタック上のmatrixを破壊しない性質を利用して、コピー回数を節約します。
 *	  ・vxform()からの戻り値をxformed_vectors[]に直接格納して、コピー回数を節約します。
 *	* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
 *	- render_mesh()の中の、クリッピング用頂点リストを組み立てる処理を分離し、アセンブラ化して高速化しました。
 *	* Thu Apr 03 15:14:06 JST 2008 Naoyuki Sawa
 *	- render_mesh()の中の、描画用頂点リストを組み立てる処理を分離し、アセンブラ化して高速化しました。
 *	* Fri Apr 04 00:06:18 JST 2008 Naoyuki Sawa
 *	- クリッピングが必要かどうかおおまかに判定する処理と、実際のクリッピング処理を、別々の関数に分離しました。
 *	  おおまかに判定するだけの関数が単純化されるため、実際のクリッピング処理が不要だった場合に高速となります。
 *	* Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
 *	- render_mesh()の中の、透視変換のためのx=x/z,y=y/zを行う処理を分離し、アセンブラ化して高速化しました。
 *	- render_mesh()の中の、表裏判定を行う処理を分離し、アセンブラ化して高速化しました。
 *	* Sun Apr 06 14:51:21 JST 2008 Naoyuki Sawa
 *	- clip_xy_sub()をアセンブラ化して高速化しました。
 *	- clip_z_sub()をアセンブラ化して高速化しました。
 *	* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
 *	- 描画用一時ワークの配置を変更し、clip_vertices[][]とrend_vertices[]をFRAM4領域へ移動しました。
 *	  これらの配列に対するアクセス頻度が高いので、実行速度を大幅に高速化できました。
 *	* Wed Jul 30 22:07:23 JST 2008 Naoyuki Sawa
 *	  CLIPVERTEXの定義を、cliprend.hからclipren3.cへ移動しました。
 *	  clipren3.cの内部でしか使っていなかったので、他のモジュールへの影響は有りません。
 *	  今後、浮動小数点版描画モジュールを作成する予定で、その際、浮動小数点版描画モジュール内では、
 *	  同じCLIPVERTEXという名前で、浮動小数点のクリッピング用頂点構造体を定義する予定です。
 *	  それに備えて、固定小数版のCLIPVERTEXをclipren3.cローカルとしておくために、移動を行いました。
 *	* Sat Apr 11 05:29:02 JST 2009 Naoyuki Sawa
 *	- render_mesh()とrender_polygon()の中で行っていた処理の一部を、select_draw_scanline()に分離しました。
 *	  この変更理由は、select_draw_scanline()のコメントを参照してください。
 *	* Wed May 06 20:58:21 JST 2009 Naoyuki Sawa
 *	- render_edge()を追加しました。
 *	* Fri May 08 00:00:02 JST 2009 Naoyuki Sawa
 *	- render_edge()内部構造体メンバの、threshold1とthreshold2を、unsigned char→shortに変更しました。
 *	  render_edge()関数の引数thresholdに、背面クリッピング距離に近いような大きな値を指定すると、threshold1が128以上の値となり、
 *	  threshold1の二倍の値であるthreshold2が、unsigned charをオーバーフローしてしまうからです。
 *	  現実には、引数thresholdにそのような大きな値を指定することはまず無いと思うのですが、念のために修正しておくことにしました。
 *	* Sat May 09 13:21:42 JST 2009 Naoyuki Sawa
 *	- render_edge()に、以下二点の変更を行いました。
 *	  引数thresholdに小さな値が指定された場合でも、[0..255]にマップしたエッジ閾値(threshold1)は2以上とするよう修正しました。
 *	  Zバッファ値ひとつ分の差で、エッジ閾値(threshold1)を超えた際、エッジ閾値未満の差分(threshold1-1)距離を格納することにしました。
 *	* Sun May 10 16:19:02 JST 2009 Naoyuki Sawa
 *	- render_edge()のバグ修正。
 *	  ±の差を同時に比較するためには、threshold1とthreshold2を1小さい値にしておくことが必要でした。
 */
#include "clip.h"

/*****************************************************************************
 *	RENDER
 *****************************************************************************/

//{{2008/07/30:cliprend.hから移動
/* クリッピング用頂点 */
typedef struct _CLIPVERTEX {
	fixed x, y, z;		/* + 0,12 頂点座標       */
	fixed s, t;		/* +12, 8 テクスチャ座標 */
				/* =20                   */
} CLIPVERTEX;
//}}2008/07/30:cliprend.hから移動

//{{2008/04/06:変更
///* 静的なワークエリアを、_def_vbuff[]上に確保するための構造体です。 */
//#define N_VERTICES_MAX	8				/* 1ポリゴン当たりの最大頂点数 */
//typedef struct _RENDER_WORK {
//	vector xformed_vectors[255];			/* 12*255=3060: 変換済み頂点座標バッファ（MESH.nvが符号無し8ビットなので、最大255頂点です） */
//	CLIPVERTEX clip_vertices[2][N_VERTICES_MAX];	/* 20*2*8= 320: クリッピング用頂点バッファ         */
//	RENDVERTEX rend_vertices[N_VERTICES_MAX];	/* 10*8  =  80: 描画用頂点バッファ                 */
//} RENDER_WORK;						/* 合計 = 3460: _def_vbuff[128*88=11264]のサイズを超えないこと！（まだ3倍以上余裕あります） */
//#define render_work	((RENDER_WORK*)_def_vbuff)	/* 描画用一時ワークは、_def_vbuff[]上に確保します。 */
//							/* これらのワークはローカル関数間の連絡のみに利用され、グローバル関数間で保持する必要はないので、 */
//							/* 基本的にユーザーアプリケーションから意識する必要はありません。 */
//							/* ただし、直接_def_vbuff[]に描画しようとすると、描画中にワークが破壊され、正しく動作しません。 */
//							/* 描画ターゲットに_def_vbuff[]を指定しないよう、ご注意ください。 */
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置を変更し、clip_vertices[][]とrend_vertices[]をFRAM4領域へ移動しました。
//↓  これらの配列に対するアクセス頻度が高いので、実行速度を大幅に高速化できました。
/* 静的な描画用一時ワークを_def_vbuff[]上に確保するための構造体です。
 * - これらのワークはローカル関数間の連絡のみに利用され、グローバル関数間で保持する必要はないので、
 *   基本的に、ユーザーアプリケーションから意識する必要はありません。
 *   ただし、直接_def_vbuff[]に描画しようとすると、描画中にワークが破壊されて、正しく動作しません。
 *   描画ターゲットに_def_vbuff[]を指定しないよう、注意してください。
 */
#define N_VERTICES_MAX		8			/* 1ポリゴン当たりの最大頂点数 */
typedef struct _RENDER_WORK_VBUFF {			/* _def_vbuff[]上に確保する、描画用一時ワークの構造体 */
	vector xformed_vectors[255];			/* 12*255=3060: 変換済み頂点座標バッファ（MESH.nvが符号無し8ビットなので、最大255頂点です） */
} RENDER_WORK_VBUFF;					/* 合計  =3060: _def_vbuff[128*88=11264]のサイズを超えないこと！（まだ3倍以上余裕あります） */
#define _render_work_vbuff	((RENDER_WORK_VBUFF*)_def_vbuff)
#define _xformed_vectors	(_render_work_vbuff->xformed_vectors)
typedef struct _RENDER_WORK_FRAM4 {			/* FRAM4領域内に確保する、描画用一時ワークの構造体 */
	CLIPVERTEX clip_vertices[2][N_VERTICES_MAX];	/* 20*2*8= 320: クリッピング用頂点バッファ         */
	RENDVERTEX rend_vertices[N_VERTICES_MAX];	/* 10*8  =  80: 描画用頂点バッファ                 */
} RENDER_WORK_FRAM4;					/* 合計  = 400: FRAM4領域のサイズ(FRAM4_SIZE=512)を超えないこと！（あと少し余裕があります） */
#define _render_work_fram4	((RENDER_WORK_FRAM4*)FRAM4_START)
#define _clip_vertices		(_render_work_fram4->clip_vertices)
#define _rend_vertices		(_render_work_fram4->rend_vertices)
//}}2008/04/06:変更

/* スキャンライン描画関数の選択と呼び出し */
/* (Ｚコンペアあり) */
//static void draw_primitive_wz(RENDER* render, int n_vertices, int color);		{{2008/01/13:削除->framdp1.cへ移動しました。}}
//static DSLPROC select_draw_scanline_wz(DSLPARAM* param, RENDER* render, int color);	{{2008/01/13:削除->render_mesh()内に処理を含めました。}}
/* (Ｚコンペアなし) */
//static void draw_primitive_woz(RENDER* render, int n_vertices, int color);		{{2008/01/13:削除->framdp2.cへ移動しました。}}
//static DSLPROC select_draw_scanline_woz(DSLPARAM* param, RENDER* render, int color);	{{2008/01/13:削除->render_polygon()内に処理を含めました。}}

/* スキャンライン描画関数の選択 */
static DSLPROC select_draw_scanline(RENDER* render, DSLPARAM* param);	/*{{2009/04/11:追加}}*/

void
render_init_3d(RENDER* render, SURFACE* surface, int x, int y, int width, int height, fixed front, fixed back, fixed fovy)
{
	/* まず2D用として初期化します。 */
	render_init_2d(render, surface, x, y, width, height);

	/* クリッピング距離を設定します。 */
	render->front = front;
	render->back = back;

	/* 重要！render_init_2d()での処理内容と重複しますが、こちらでもこの処理が必要です！
	 *       これを忘れると、このあと変換行列の初期化で、ゼロ除算エラーが発生します！！
	 */
	if(width == 0) width = surface->w;
	if(height == 0) height = surface->h;

	/* 変換行列を初期化します。(コンテキストスタックはrender_init_2d()で初期化済み) */
	render->context->matrix = MATRIX_I;
	render->context->matrix = mviewportz(render->context->matrix, x, y, width, height);
	render->context->matrix = mperspect (render->context->matrix, fovy, fidiv(fld(width), height), front);
}

//{{2008/04/04,2008/04/06:変更
///* 平面クリッピングを行います。
// * [in]
// *	render				レンダラーへのポインタ。
// *	nsrc				クリッピングする頂点の数。
// *	render_work->clip_vertices[0]	クリッピングする頂点リスト。(グローバル変数)
// * [out]
// *	戻り値				クリッピング後の頂点の数。
// *	render_work->clip_vertices[0]	クリッピング後の頂点リスト。(グローバル変数)
// * [note]
// *	* 作業領域として、render_work->clip_vertices[1]も使います。
// */
//static int clip_xy(RENDER* render, int nsrc) {
//#define CLIP(SRC, DST, X, Y, OP, LIM) \
//	src = SRC; \
//	dst = DST; \
//	ndst = 0; \
//	for(i = 0, v0 = src; i < nsrc; i++, v0++) { \
//		v1 = i < nsrc - 1 ? v0 + 1 : src; \
//		if(fcom(v0->X, OP, LIM)) { \
//			*dst = *v0; \
//			dst++; \
//			ndst++; \
//			if(ndst == N_VERTICES_MAX) break; \
//			if(fcom(v1->X, OP, LIM)) { \
//				continue; \
//			} else { \
//				/* go clip */ \
//			} \
//		} else { \
//			if(fcom(v1->X, OP, LIM)) { \
//				/* go clip */ \
//			} else { \
//				continue; \
//			} \
//		} \
//		/* do clip */ \
//		dst->X = LIM; \
//		n0 = fsub(LIM, v0->X); \
//		n1 = fsub(v1->X, LIM); \
//		n01 = fadd(n0, n1); \
//		n001 = fdiv(n0, n01); \
//		dst->Y = fadd(v0->Y, fmul(fsub(v1->Y, v0->Y), n001)); \
//		n0 = fmul(n0, v0->z); \
//		n1 = fmul(n1, v1->z); \
//		n01 = fadd(n0, n1); \
//		n001 = fdiv(n0, n01); \
//		dst->z = fadd(v0->z, fmul(fsub(v1->z, v0->z), n001)); \
//		dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)); \
//		dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001)); \
//		dst++; \
//		ndst++; \
//		if(ndst == N_VERTICES_MAX) break; \
//	} \
//	if(ndst < 3) return 0; \
//
//	CLIPVERTEX *src, *dst;
//	CLIPVERTEX *v0, *v1;
//	int i, ndst;
//	fixed n0, n1, n01, n001;
//	fixed left   = fild(render->left  );
//	fixed right  = fild(render->right );
//	fixed top    = fild(render->top   );
//	fixed bottom = fild(render->bottom);
//
//	/* 実際のクリッピング処理の前に、クリッピングが必要かどうかおおまかに判定します。
//	 * 全ての頂点がレンジ内に入っていれば、クリッピングは必要ありません。
//	 * 実際のクリッピング処理よりも単純な比較処理ですので、クリッピング不要なら高速化が期待できます。
//	 * 判定の結果、クリッピングが必要だったら、おおまかな判定処理が増えたぶん遅くなってしまいます。
//	 * オブジェクト単位のカリング等を行って、なるべく画面外のメッシュは描かないようにしてください。
//	 */
//	v0 = render_work->clip_vertices[0];
//	v1 = v0 + nsrc;
//	while(v0 < v1) {
//		if(!fcom(v0->x, >=, left  )) break;
//		if(!fcom(v0->x, <=, right )) break;
//		if(!fcom(v0->y, >=, top   )) break;
//		if(!fcom(v0->y, <=, bottom)) break;
//		v0++;
//	}
//	if(v0 == v1) return nsrc;
//
//	/* ※TODO: 大半のメッシュが完全に画面内（上で判定済み）か、または完全に画面外だと仮定すると、
//	 *         実際のクリッピング処理に入る前に完全画面外の判断を行った方が速くなるかも知れません。
//	 */
//
//	/* レンジ最大値（right,bottom）との比較が「<」ではなく「<=」である理由について説明します。
//	 * draw_primitive_*()とdraw_scanline_*_*()内の縦横ループ処理をご覧いただければわかるように、
//	 * 下端と右端のピクセルは描きません。
//	 * すなわち、画面上のポリゴン頂点座標は、グリッド座標系で表されます。
//	 * 従って、下端・右端との比較は「<」ではなく「<=」となります。
//	 */
//	CLIP(render_work->clip_vertices[0], render_work->clip_vertices[1], x, y, >=, left  ); nsrc = ndst;
//	CLIP(render_work->clip_vertices[1], render_work->clip_vertices[0], x, y, <=, right ); nsrc = ndst;
//	CLIP(render_work->clip_vertices[0], render_work->clip_vertices[1], y, x, >=, top   ); nsrc = ndst;
//	CLIP(render_work->clip_vertices[1], render_work->clip_vertices[0], y, x, <=, bottom);
//	return ndst;
//
//#undef CLIP
//}
//↓* Fri Apr 04 00:06:18 JST 2008 Naoyuki Sawa
//↓- クリッピングが必要かどうかおおまかに判定する処理と、実際のクリッピング処理を、別々の関数に分離しました。
//↓  おおまかに判定するだけの関数が単純化されるため、実際のクリッピング処理が不要だった場合に高速となります。
//↓* Sun Apr 06 14:51:21 JST 2008 Naoyuki Sawa
//↓- clip_xy_sub()をアセンブラ化して高速化しました。
#ifdef REND_NOASM
static int clip_xy_sub(RENDER* render, CLIPVERTEX* clip_vertices_0, CLIPVERTEX* clip_vertices_1, int nsrc) {
#define CLIP(SRC, DST, X, Y, OP, LIM) \
	src = SRC; \
	dst = DST; \
	ndst = 0; \
	for(i = 0, v0 = src; i < nsrc; i++, v0++) { \
		v1 = i < nsrc - 1 ? v0 + 1 : src; \
		if(fcom(v0->X, OP, LIM)) { \
			*dst = *v0; \
			dst++; \
			ndst++; \
			if(ndst == N_VERTICES_MAX) break; \
			if(fcom(v1->X, OP, LIM)) { \
				continue; \
			} else { \
				/* go clip */ \
			} \
		} else { \
			if(fcom(v1->X, OP, LIM)) { \
				/* go clip */ \
			} else { \
				continue; \
			} \
		} \
		/* do clip */ \
		dst->X = LIM; \
		n0 = fsub(LIM, v0->X); \
		n1 = fsub(v1->X, LIM); \
		n01 = fadd(n0, n1); \
		n001 = fdiv(n0, n01); \
		dst->Y = fadd(v0->Y, fmul(fsub(v1->Y, v0->Y), n001)); \
		n0 = fmul(n0, v0->z); \
		n1 = fmul(n1, v1->z); \
		n01 = fadd(n0, n1); \
		n001 = fdiv(n0, n01); \
		dst->z = fadd(v0->z, fmul(fsub(v1->z, v0->z), n001)); \
		dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)); \
		dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001)); \
		dst++; \
		ndst++; \
		if(ndst == N_VERTICES_MAX) break; \
	} \
	if(ndst < 3) return 0; \

	CLIPVERTEX *src, *dst;
	CLIPVERTEX *v0, *v1;
	int i, ndst;
	fixed n0, n1, n01, n001;
	fixed left   = fild(render->left  );
	fixed right  = fild(render->right );
	fixed top    = fild(render->top   );
	fixed bottom = fild(render->bottom);

	/* ※TODO: 大半のメッシュが完全に画面内（上で判定済み）か、または完全に画面外だと仮定すると、
	 *         実際のクリッピング処理に入る前に完全画面外の判断を行った方が速くなるかも知れません。
	 */

	/* レンジ最大値（right,bottom）との比較が「<」ではなく「<=」である理由について説明します。
	 * draw_primitive_*()とdraw_scanline_*_*()内の縦横ループ処理をご覧いただければわかるように、
	 * 下端と右端のピクセルは描きません。
	 * すなわち、画面上のポリゴン頂点座標は、グリッド座標系で表されます。
	 * 従って、下端・右端との比較は「<」ではなく「<=」となります。
	 */
	CLIP(clip_vertices_0, clip_vertices_1, x, y, >=, left  ); nsrc = ndst;
	CLIP(clip_vertices_1, clip_vertices_0, x, y, <=, right ); nsrc = ndst;
	CLIP(clip_vertices_0, clip_vertices_1, y, x, >=, top   ); nsrc = ndst;
	CLIP(clip_vertices_1, clip_vertices_0, y, x, <=, bottom);
	return ndst;

#undef CLIP
}
static int clip_xy(RENDER* render, CLIPVERTEX* clip_vertices_0, CLIPVERTEX* clip_vertices_1, int nsrc) {
	/* 実際のクリッピング処理の前に、クリッピングが必要かどうかおおまかに判定します。
	 * 全ての頂点がレンジ内に入っていれば、クリッピングは必要ありません。
	 * 実際のクリッピング処理よりも単純な比較処理ですので、クリッピング不要なら高速化が期待できます。
	 * 判定の結果、クリッピングが必要だったら、おおまかな判定処理が増えたぶん遅くなってしまいます。
	 * オブジェクト単位のカリング等を行って、なるべく画面外のメッシュは描かないようにしてください。
	 */
	fixed left   = fild(render->left  );
	fixed right  = fild(render->right );
	fixed top    = fild(render->top   );
	fixed bottom = fild(render->bottom);
	CLIPVERTEX* v = clip_vertices_0;
	int i = nsrc;
	do {
		if(!fcom(v->x, >=, left  )) goto L_CLIP;
		if(!fcom(v->x, <=, right )) goto L_CLIP;
		if(!fcom(v->y, >=, top   )) goto L_CLIP;
		if(!fcom(v->y, <=, bottom)) goto L_CLIP;
		v++;
	} while(--i);
	return nsrc;
L_CLIP:
	return clip_xy_sub(render, clip_vertices_0, clip_vertices_1, nsrc);
}
#else /*REND_NOASM*/
/*static*/ int clip_xy(RENDER* render, CLIPVERTEX* clip_vertices_0, CLIPVERTEX* clip_vertices_1, int nsrc);
asm("
	.code
	.align	1
	;// %r12 := render
	;// %r13 := clip_vertices_0
	;// %r14 := clip_vertices_1
	;// %r15 := nsrc
clip_xy:
	add	%r12, 4			; %r12 := &render->left
	ld.h	%r4, [%r12]+		; %r4  := left   = render->left
	ld.h	%r5, [%r12]+		; %r5  := top    = render->top
	ld.h	%r6, [%r12]+		; %r6  := right  = render->right
	ld.h	%r7, [%r12]		; %r7  := bottom = render->bottom
	xsla	%r4, 10			; %r4  := left   = fild(left  )
	xsla	%r5, 10			; %r5  := top    = fild(top   )
	xsla	%r6, 10			; %r6  := right  = fild(right )
	xsla	%r7, 10			; %r7  := bottom = fild(bottom)
	ld.w	%r10, %r13		; %r10 := v = clip_vertices_0
	ld.w	%r11, %r15		; %r11 := i = nsrc
clip_xy_LOOP:
	ld.w	%r9, [%r10]+		; %r10 := x = v->x
	cmp	%r9, %r4		; if(x < left  ) goto CLIP
	jrlt	clip_xy_CLIP
	cmp	%r9, %r6		; if(x > right ) goto CLIP
;//	jrgt	clip_xy_CLIP		; -----------------+
	ld.w	%r9, [%r10]		; %r10 := y = v->y |
	jrgt	clip_xy_CLIP		; <----------------+			*anti-interlock*
	cmp	%r9, %r5		; if(x < top   ) goto CLIP
	jrlt	clip_xy_CLIP
	cmp	%r9, %r7		; if(x > bottom) goto CLIP
	jrgt	clip_xy_CLIP
	sub	%r11, 1			; i--
	jrne.d	clip_xy_LOOP
	add	%r10, 16		; %r10 := v++				*delay*
	ret.d				; return nsrc
	ld.w	%r10, %r15		; %r10 := nsrc				*delay*
clip_xy_CLIP:
	xjp.d	clip_xy_sub		
	sub	%r12, 10		; %r12 := render			*delay*
");
asm("
	.code
	.align	1
	;// %r12 := render
	;// %r13 := clip_vertices_0
	;// %r14 := clip_vertices_1
	;// %r15 := nsrc
clip_xy_sub:
	pushn	%r3
	xsub	%sp, %sp, 36
	;//
	add	%r12, 4				; %r12     := &render->left
	ld.h	%r4, [%r12]+			; %r4      := left
	ld.h	%r5, [%r12]+			; %r5      := top
	ld.h	%r6, [%r12]+			; %r6      := right
	ld.h	%r7, [%r12]			; %r7      := bottom
	xsla	%r4, 10				; %r4      := left   = fild(left  )
	xsla	%r5, 10				; %r5      := top    = fild(top   )
	xsla	%r6, 10				; %r6      := right  = fild(right )
	xsla	%r7, 10				; %r7      := bottom = fild(bottom)
	xld.w	[%sp+0], %r4			; [%sp+0]  := left
	xld.w	[%sp+4], %r5			; [%sp+4]  := top
	xld.w	[%sp+8], %r6			; [%sp+8]  := right
	xld.w	[%sp+12], %r7			; [%sp+12] := bottom
	xld.w	[%sp+16], %r13			; [%sp+16] := clip_vertices_0
	xld.w	[%sp+20], %r14			; [%sp+20] := clip_vertices_1
	;//
	ld.w	%r0, %r15			; %r0  := i = nsrc
	xld.w	%r1, [%sp+16]			; %r1  := v0 = clip_vertices_0
	xld.w	%r2, [%sp+20]			; %r2  := dst = clip_vertices_1
	ld.w	%r3, 8				; %r3  := (8-ndst) = 8
	;//-------------------------------------;
clip_xy_sub_LEFT_FOR:
	ld.w	%r4, %r1			; %r4  := v1 = v0 + 1
;//	add	%r4, 20				; -----------+
	cmp	%r0, 1				; if(i == 1) |
	jrne.d	3				;            |
	 add	%r4, 20				; <----------+							*delay*
	 xld.w	%r4, [%sp+16]			;   %r4  := v1 = clip_vertices_0
	;//
	xld.w	%r5, [%r1+0]			; %r5  := v0->x
	xld.w	%r6, [%sp+0]			; %r6  := left
	xld.w	%r7, [%r4+0]			; %r7  := v1->x							*anti-interlock*
	cmp	%r5, %r6			; if(v0->x >= left)
	jrlt	clip_xy_sub_LEFT_ELSE
	;//
	ld.w	%r9, [%r1]+			;   *dst++ = *v0
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]
	sub	%r1, 16				; <-----------+							*anti-interlock*
	ld.w	[%r2]+, %r9			;             |
;//	sub	%r1, 16				; %r1  := v0 -+
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_LEFT_BREAK
	;//
	cmp	%r7, %r6			; if(v1->x >= left) { continue } else { go clip }
	jrlt	clip_xy_sub_LEFT_CLIP
	jp	clip_xy_sub_LEFT_CONTINUE
	;//
clip_xy_sub_LEFT_ELSE:
	cmp	%r7, %r6			; if(v1->x >= left) { go clip } else { continue }
	jrlt	clip_xy_sub_LEFT_CONTINUE
	;//
clip_xy_sub_LEFT_CLIP:
	xcall	clip_xy_sub_CLIP_SUB1		; %r10  := n001, %r11  := m001
	;//
	xld.w	%r9, [%sp+0]			; %r9   := left
	add	%r1, 4				; %r1   := &v0->y						*anti-interlock*
	ld.w	[%r2]+, %r9			; dst->x = left
;//	add	%r4, 4				; %r4   := &v1->y -------------------------------------+
	xcall.d	clip_xy_sub_CLIP_SUB2		; dst->y = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001)) |
	add	%r4, 4				; <----------------------------------------------------+	*delay*
;//	ld.w	%r10, %r11			; %r10  := m001 ---------------------------------------+
	xcall.d	clip_xy_sub_CLIP_SUB2		; dst->z = fadd(v0->z, fmul(fsub(v1->z, v0->z), m001)) |
	ld.w	%r10, %r11			; <----------------------------------------------------+	*delay*
	xcall	clip_xy_sub_CLIP_SUB2		; dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), m001))
	xcall	clip_xy_sub_CLIP_SUB2		; dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), m001))
	sub	%r1, 20				; %r1   := v0
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_LEFT_BREAK
	;//
clip_xy_sub_LEFT_CONTINUE:
	sub	%r0, 1				; %r0  := i--
	jrne.d	clip_xy_sub_LEFT_FOR
	add	%r1, 20				; %r1  := v0++							*delay*
	;//
clip_xy_sub_LEFT_BREAK:
	ld.w	%r0, 8				; %r0  := i = nsrc = ndst = 8 - (8-ndst)
	sub	%r0, %r3
	cmp	%r0, 3
	xjrlt	clip_xy_sub_RET
	xld.w	%r1, [%sp+20]			; %r1  := v0 = clip_vertices_1
	xld.w	%r2, [%sp+16]			; %r2  := dst = clip_vertices_0
	ld.w	%r3, 8				; %r3  := (8-ndst) = 8
	;//-------------------------------------;
clip_xy_sub_RIGHT_FOR:
	ld.w	%r4, %r1			; %r4  := v1 = v0 + 1
;//	add	%r4, 20				; -----------+
	cmp	%r0, 1				; if(i == 1) |
	jrne.d	3				;            |
	 add	%r4, 20				; <----------+							*delay*
	 xld.w	%r4, [%sp+20]			;   %r4  := v1 = clip_vertices_1
	;//
	xld.w	%r5, [%r1+0]			; %r5  := v0->x
	xld.w	%r6, [%sp+8]			; %r6  := right
	xld.w	%r7, [%r4+0]			; %r7  := v1->x							*anti-interlock*
	cmp	%r5, %r6			; if(v0->x <= right)
	jrgt	clip_xy_sub_RIGHT_ELSE
	;//
	ld.w	%r9, [%r1]+			;   *dst++ = *v0
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]
	sub	%r1, 16				; <-----------+							*anti-interlock*
	ld.w	[%r2]+, %r9			;             |
;//	sub	%r1, 16				; %r1  := v0 -+
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_RIGHT_BREAK
	;//
	cmp	%r7, %r6			; if(v1->x <= right) { continue } else { go clip }
	jrgt	clip_xy_sub_RIGHT_CLIP
	jp	clip_xy_sub_RIGHT_CONTINUE
	;//
clip_xy_sub_RIGHT_ELSE:
	cmp	%r7, %r6			; if(v1->x <= right) { go clip } else { continue }
	jrgt	clip_xy_sub_RIGHT_CONTINUE
	;//
clip_xy_sub_RIGHT_CLIP:
	xcall	clip_xy_sub_CLIP_SUB1		; %r10  := n001, %r11  := m001
	;//
	xld.w	%r9, [%sp+8]			; %r9   := right
	add	%r1, 4				; %r1   := &v0->y						*anti-interlock*
	ld.w	[%r2]+, %r9			; dst->x = right
;//	add	%r4, 4				; %r4   := &v1->y -------------------------------------+
	xcall.d	clip_xy_sub_CLIP_SUB2		; dst->y = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001)) |
	add	%r4, 4				; <----------------------------------------------------+	*delay*
;//	ld.w	%r10, %r11			; %r10  := m001 ---------------------------------------+
	xcall.d	clip_xy_sub_CLIP_SUB2		; dst->z = fadd(v0->z, fmul(fsub(v1->z, v0->z), m001)) |
	ld.w	%r10, %r11			; <----------------------------------------------------+	*delay*
	xcall	clip_xy_sub_CLIP_SUB2		; dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), m001))
	xcall	clip_xy_sub_CLIP_SUB2		; dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), m001))
	sub	%r1, 20				; %r1   := v0
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_RIGHT_BREAK
	;//
clip_xy_sub_RIGHT_CONTINUE:
	sub	%r0, 1				; %r0  := i--
	jrne.d	clip_xy_sub_RIGHT_FOR
	add	%r1, 20				; %r1  := v0++							*delay*
	;//
clip_xy_sub_RIGHT_BREAK:
	ld.w	%r0, 8				; %r0  := i = nsrc = ndst = 8 - (8-ndst)
	sub	%r0, %r3
	cmp	%r0, 3
	xjrlt	clip_xy_sub_RET
	xld.w	%r1, [%sp+16]			; %r1  := v0 = clip_vertices_0
	xld.w	%r2, [%sp+20]			; %r2  := dst = clip_vertices_1
	ld.w	%r3, 8				; %r3  := (8-ndst) = 8
	;//-------------------------------------;
clip_xy_sub_TOP_FOR:
	ld.w	%r4, %r1			; %r4  := v1 = v0 + 1
;//	add	%r4, 20				; -----------+
	cmp	%r0, 1				; if(i == 1) |
	jrne.d	3				;            |
	 add	%r4, 20				; <----------+							*delay*
	 xld.w	%r4, [%sp+16]			;   %r4  := v1 = clip_vertices_0
	;//
	xld.w	%r5, [%r1+4]			; %r5  := v0->y
	xld.w	%r6, [%sp+4]			; %r6  := top
	xld.w	%r7, [%r4+4]			; %r7  := v1->y							*anti-interlock*
	cmp	%r5, %r6			; if(v0->y >= top)
	jrlt	clip_xy_sub_TOP_ELSE
	;//
	ld.w	%r9, [%r1]+			;   *dst++ = *v0
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]
	sub	%r1, 16				; <-----------+							*anti-interlock*
	ld.w	[%r2]+, %r9			;             |
;//	sub	%r1, 16				; %r1  := v0 -+
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_TOP_BREAK
	;//
	cmp	%r7, %r6			; if(v1->y >= top) { continue } else { go clip }
	jrlt	clip_xy_sub_TOP_CLIP
	jp	clip_xy_sub_TOP_CONTINUE
	;//
clip_xy_sub_TOP_ELSE:
	cmp	%r7, %r6			; if(v1->y >= top) { go clip } else { continue }
	jrlt	clip_xy_sub_TOP_CONTINUE
	;//
clip_xy_sub_TOP_CLIP:
	xcall	clip_xy_sub_CLIP_SUB1		; %r10  := n001, %r11  := m001
	;//
	xcall	clip_xy_sub_CLIP_SUB2		; dst->x = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001))
	xld.w	%r9, [%sp+4]			; %r9   := top
	add	%r1, 4				; %r1   := &v0->z						*anti-interlock*
	ld.w	[%r2]+, %r9			; dst->y = top
	add	%r4, 4				; %r4   := &v1->z
;//	ld.w	%r10, %r11			; %r10  := m001 ---------------------------------------+
	xcall.d	clip_xy_sub_CLIP_SUB2		; dst->z = fadd(v0->z, fmul(fsub(v1->z, v0->z), m001)) |
	ld.w	%r10, %r11			; <----------------------------------------------------+	*delay*
	xcall	clip_xy_sub_CLIP_SUB2		; dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), m001))
	xcall	clip_xy_sub_CLIP_SUB2		; dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), m001))
	sub	%r1, 20				; %r1   := v0
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_TOP_BREAK
	;//
clip_xy_sub_TOP_CONTINUE:
	sub	%r0, 1				; %r0  := i--
	jrne.d	clip_xy_sub_TOP_FOR
	add	%r1, 20				; %r1  := v0++							*delay*
	;//
clip_xy_sub_TOP_BREAK:
	ld.w	%r0, 8				; %r0  := i = nsrc = ndst = 8 - (8-ndst)
	sub	%r0, %r3
	cmp	%r0, 3
	xjrlt	clip_xy_sub_RET
	xld.w	%r1, [%sp+20]			; %r1  := v0 = clip_vertices_1
	xld.w	%r2, [%sp+16]			; %r2  := dst = clip_vertices_0
	ld.w	%r3, 8				; %r3  := (8-ndst) = 8
	;//-------------------------------------;
clip_xy_sub_BOTTOM_FOR:
	ld.w	%r4, %r1			; %r4  := v1 = v0 + 1
;//	add	%r4, 20				; -----------+
	cmp	%r0, 1				; if(i == 1) |
	jrne.d	3				;            |
	 add	%r4, 20				; <----------+							*delay*
	 xld.w	%r4, [%sp+20]			;   %r4  := v1 = clip_vertices_1
	;//
	xld.w	%r5, [%r1+4]			; %r5  := v0->y
	xld.w	%r6, [%sp+12]			; %r6  := bottom
	xld.w	%r7, [%r4+4]			; %r7  := v1->y							*anti-interlock*
	cmp	%r5, %r6			; if(v0->y <= bottom)
	jrgt	clip_xy_sub_BOTTOM_ELSE
	;//
	ld.w	%r9, [%r1]+			;   *dst++ = *v0
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]
	sub	%r1, 16				; <-----------+							*anti-interlock*
	ld.w	[%r2]+, %r9			;             |
;//	sub	%r1, 16				; %r1  := v0 -+
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_BOTTOM_BREAK
	;//
	cmp	%r7, %r6			; if(v1->y <= bottom) { continue } else { go clip }
	jrgt	clip_xy_sub_BOTTOM_CLIP
	jp	clip_xy_sub_BOTTOM_CONTINUE
	;//
clip_xy_sub_BOTTOM_ELSE:
	cmp	%r7, %r6			; if(v1->y <= bottom) { go clip } else { continue }
	jrgt	clip_xy_sub_BOTTOM_CONTINUE
	;//
clip_xy_sub_BOTTOM_CLIP:
	xcall	clip_xy_sub_CLIP_SUB1		; %r10  := n001, %r11  := m001
	;//
	xcall	clip_xy_sub_CLIP_SUB2		; dst->x = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001))
	xld.w	%r9, [%sp+12]			; %r9   := bottom
	add	%r1, 4				; %r1   := &v0->z						*anti-interlock*
	ld.w	[%r2]+, %r9			; dst->y = bottom
	add	%r4, 4				; %r4   := &v1->z
;//	ld.w	%r10, %r11			; %r10  := m001 ---------------------------------------+
	xcall.d	clip_xy_sub_CLIP_SUB2		; dst->z = fadd(v0->z, fmul(fsub(v1->z, v0->z), m001)) |
	ld.w	%r10, %r11			; <----------------------------------------------------+	*delay*
	xcall	clip_xy_sub_CLIP_SUB2		; dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), m001))
	xcall	clip_xy_sub_CLIP_SUB2		; dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), m001))
	sub	%r1, 20				; %r1   := v0
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_xy_sub_BOTTOM_BREAK
	;//
clip_xy_sub_BOTTOM_CONTINUE:
	sub	%r0, 1				; %r0  := i--
	jrne.d	clip_xy_sub_BOTTOM_FOR
	add	%r1, 20				; %r1  := v0++							*delay*
	;//
clip_xy_sub_BOTTOM_BREAK:
	ld.w	%r0, 8				; %r0  := ndst = 8 - (8-ndst)
	sub	%r0, %r3
	;//-------------------------------------;
clip_xy_sub_RET:
	ld.w	%r10, %r0			; return ndst
	;//
	xadd	%sp, %sp, 36
	popn	%r3
	ret
	;//-------------------------------------;
clip_xy_sub_CLIP_SUB2:
	ld.w	%r5, [%r1]+			; %r5   :=                              v0->i
	ld.w	%r6, [%r4]+			; %r6   :=                       v1->i
	sub	%r6, %r5			; %r6   :=                  fsub(v1->i, v0->i)
	mlt.w	%r6, %r10			; %r6   :=             fmul(fsub(v1->i, v0->i), n001)
	ld.w	%r6, %alr
	ld.w	%r9, %ahr
	xsrl	%r6, 10
	xsll	%r9, 22
	or	%r6, %r9
	add	%r5, %r6			; %r5   := fadd(v0->i, fmul(fsub(v1->i, v0->i), n001))
	ld.w	[%r2]+, %r5			; dst->i = fadd(v0->i, fmul(fsub(v1->i, v0->i), n001))
	ret
	;//-------------------------------------;
clip_xy_sub_CLIP_SUB1:
	xld.w	[%sp+28], %r4			; [%sp+28] := v1					(clip_xy_subのスタックフレームの[%sp+24])
	xld.w	%r12, [%r1+8]			; %r12     := v0->z
	xld.w	%r13, [%r4+8]			; %r13     := v1->z
	sub	%r7, %r6			; %r7      := n1 = fsub(v1->i, limit)
	sub	%r6, %r5			; %r6      := n0 = fsub(limit, v0->i)
	mlt.w	%r12, %r6			; %r12     := m0 = fmul(n0, v0->z)
	ld.w	%r12, %alr
	ld.w	%r9, %ahr
	xsrl	%r12, 10
	xsll	%r9, 22
	or	%r12, %r9
	mlt.w	%r13, %r7			; %r13     := m1 = fmul(n1, v1->z)
	ld.w	%r13, %alr
	ld.w	%r9, %ahr
	xsrl	%r13, 10
	xsll	%r9, 22
	or	%r13, %r9
	xld.w	[%sp+32], %r6			; [%sp+32] := n0					(clip_xy_subのスタックフレームの[%sp+28])
	xld.w	[%sp+36], %r7			; [%sp+36] := n1					(clip_xy_subのスタックフレームの[%sp+32])
;//	add	%r13, %r12			; %r13     := m01 = fadd(m0, m1) --+
	xcall.d	fdiv				; %r10     := m001 = fdiv(m0, m01) |
	add	%r13, %r12			; <--------------------------------+			*delay*
	xld.w	%r12, [%sp+32]			; %r12     := n0					(clip_xy_subのスタックフレームの[%sp+28])
	xld.w	%r13, [%sp+36]			; %r13     := n1					(clip_xy_subのスタックフレームの[%sp+32])
	xld.w	[%sp+32], %r10			; [%sp+32] := m001					(clip_xy_subのスタックフレームの[%sp+28])
;//	add	%r13, %r12			; %r13     := n01 = fadd(n0, n1) --+
	xcall.d	fdiv				; %r10     := n001 = fdiv(n0, n01) |
	add	%r13, %r12			; <--------------------------------+			*delay*
	xld.w	%r11, [%sp+32]			; %r11     := m001					(clip_xy_subのスタックフレームの[%sp+28])
	xld.w	%r4, [%sp+28]			; %r4      := v1					(clip_xy_subのスタックフレームの[%sp+24])
	ret
");
#endif /*REND_NOASM*/
//}}2008/04/04,2008/04/06:変更

/* * ポリゴン描画は全て整数で処理できますが、現在の実装ではそうなっていません。
 *   メッシュ描画のコードを流用したため、クリッピングに浮動小数を使っています。→2003/03/25 固定小数になりました
 *   この部分を独自の整数処理に書き直せば、大幅に高速化できるはずです。(※TODO)
 * * と思いましたが、クリッピングの改良だけでは、3%程度しか速くならないようです。
 *   ポリゴン描画ではＺコンペアが不要なので、これを省略すると25%程度速くなります。
 *   高速化のために、Ｚコンペア無しのスキャンライン描画を追加するのが良さそうです。
 * * ->やってみました。
 */
void
render_polygon(RENDER* render, const VERTEX* vertices, int n_vertices, int color)
{
	int i;
	const VERTEX* vertex;
	CLIPVERTEX *clip_vertex;
	RENDVERTEX *rend_vertex;

//{{2009/04/11:変更
////{{2008/01/13:追加。これまではselect_draw_scanline_woz()で行っていた処理です。
//	DSLPARAM param;
//	DSLPROC draw_scanline;
//	SURFACE* surface = render->surface;
//	TEXTURE* texture = render->context->texture;
//	param.surface_w = surface->w;
//	param.surface_h = surface->h;
//	param.surface_vbuff = surface->vbuff;
//	if(!texture) {
//		draw_scanline = draw_scanline_woz_color;
//	} else {
//		param.texture_w = texture->header.w;
//		param.texture_h = texture->header.h;
//		param.texture_buf = texture->buf;
//		switch(texture->header.bpp) {
//		case 1:
//			if(!texture->header.mask) {
//				draw_scanline = draw_scanline_woz_1bit;
//			} else {
//				param.texture_mask = texture->mask;
//				draw_scanline = draw_scanline_woz_1bitmask;
//			}
//			break;
//		default/*2*/:
//			if(!texture->header.mask) {
//				draw_scanline = draw_scanline_woz_2bit;
//			} else {
//				param.texture_mask = texture->mask;
//				draw_scanline = draw_scanline_woz_2bitmask;
//			}
//			break;
//		}
//	}
////}}2008/01/13:追加。これまではselect_draw_scanline_woz()で行っていた処理です。
//↓2009/04/11:スキャンライン描画関数の選択処理を、再度、select_draw_scanline()に分離しました。
	DSLPARAM param;
	DSLPROC draw_scanline = select_draw_scanline(render, &param);
//}}2009/04/11:変更

	/* クリッピング用頂点リストを組み立てます。 */
	if(n_vertices > N_VERTICES_MAX) n_vertices = N_VERTICES_MAX;
//{{2008/04/06:変更
//	for(i = 0, vertex = vertices, clip_vertex = render_work->clip_vertices[0];
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
	for(i = 0, vertex = vertices, clip_vertex = _clip_vertices[0];
//}}2008/04/06:変更
	    i < n_vertices;
	    i++, vertex++, clip_vertex++) {
		clip_vertex->x = fild(vertex->x);
		clip_vertex->y = fild(vertex->y);
		clip_vertex->z = fild(1); /* 平面クリッピングのためのダミー。0以外なら何でもOKです。 */
		clip_vertex->s = fadd(fild(vertex->s), fld(0.5)); /* テクセルの中心をポイント */
		clip_vertex->t = fadd(fild(vertex->t), fld(0.5)); /* テクセルの中心をポイント */
	}
	if(n_vertices < 3) return;

	/* 平面クリッピングを行います。 */
//{{2008/04/04,2008/04/06:変更
//	n_vertices = clip_xy(render, n_vertices);
//↓* Fri Apr 04 00:06:18 JST 2008 Naoyuki Sawa
//↓- clip_xy()の仕様変更に伴って、呼び出し引数を変更しました。
//	n_vertices = clip_xy(render, render_work->clip_vertices[0], render_work->clip_vertices[1], n_vertices);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
	n_vertices = clip_xy(render, _clip_vertices[0], _clip_vertices[1], n_vertices);
//}}2008/04/04,2008/04/06:変更
	if(n_vertices < 3) return;

	/* 描画用頂点リストを組み立てます。 */
//{{2008/04/06:変更
//	for(i = 0, clip_vertex = render_work->clip_vertices[0], rend_vertex = render_work->rend_vertices;
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
	for(i = 0, clip_vertex = _clip_vertices[0], rend_vertex = _rend_vertices;
//}}2008/04/06:変更
	    i < n_vertices;
	    i++, clip_vertex++, rend_vertex++) {
		rend_vertex->x = fist(clip_vertex->x);
		rend_vertex->y = fist(clip_vertex->y);
//{{2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
//		/* draw_primitive_woz()はrend_vertex->zを使わないので、不定値のままで大丈夫です。 */
//↓ * Mon Jan 14 23:18:06 JST 2008 Naoyuki Sawa
//↓ - draw_primitive()はrend_vertex->zに対してDDAを行うので、z=0を設定しておくことにします。
//↓   不定値でも描画結果には影響しませんが、DDAの条件分岐パスが不定となり、実行速度がばらつくおそれがあります。
//↓   そこで、確実のために、z=0としておくことにしました。
		rend_vertex->z = 0;
//}}2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
		rend_vertex->s = fist(fimul(clip_vertex->s, 1 << 8)); /* 8.8bit */
		rend_vertex->t = fist(fimul(clip_vertex->t, 1 << 8)); /* 8.8bit */
	}

//{{2008/01/13:変更
//	/* プリミティブを描きます。 */
//	draw_primitive_woz(render, n_vertices, color);
//}}2008/01/13:変更{{
	/* プリミティブを描きます。 */
	param.fore_color = color      & 15;
	param.back_color = color >> 4 & 15;
	//draw_primitive_woz(render_work->rend_vertices, n_vertices, &param, draw_scanline);
	//↓2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
//{{2008/04/06:変更
//	draw_primitive(render_work->rend_vertices, n_vertices, &param, draw_scanline);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
	draw_primitive(_rend_vertices, n_vertices, &param, draw_scanline);
//}}2008/04/06:変更
//}}2008/01/13:変更
}

/* * Tue Apr 01 12:56:34 JST 2008 Naoyuki Sawa
 * - render_mesh()の中の、全頂点座標を変換する処理を分離し、以下の方法で高速化しました。
 *   ・vxform()はスタック上のmatrixを破壊しない性質を利用して、コピー回数を節約します。
 *   ・vxform()からの戻り値をxformed_vectors[]に直接格納して、コピー回数を節約します。
 * - 上記の効果が得られるのは、アセンブラ版のみです。
 *   C言語版は、動作確認と処理内容を示すためだけに残してあります。
 */
#ifdef REND_NOASM
static void vxform_all(vector* v_out/*[nv]*/, const matrix* m, const vector* v_in/*[nv]*/, int nv) {
	do {
		*v_out++ = vxform(*m, *v_in++);
	} while(--nv);
}
#else /*REND_NOASM*/
/*static*/ void vxform_all(vector* v_out/*[nv]*/, const matrix* m, const vector* v_in/*[nv]*/, int nv);
asm("
	.code
	.align	1
	;// %r12 := v_out
	;// %r13 := m
	;// %r14 := v_in
	;// %r15 := nv
vxform_all:
	pushn	%r1
	xsub	%sp, %sp, 60
	;//
	ld.w	%r4, [%r13]+		; %r4      := m->a00
	ld.w	%r5, [%r13]+		; %r5      := m->a01
	ld.w	%r6, [%r13]+		; %r6      := m->a02
	ld.w	%r7, [%r13]+		; %r7      := m->a03
	xld.w	[%sp+0], %r4		; [%sp+0]  := m->a00
	xld.w	[%sp+4], %r5		; [%sp+4]  := m->a01
	xld.w	[%sp+8], %r6		; [%sp+8]  := m->a02
	xld.w	[%sp+12], %r7		; [%sp+12] := m->a03
	ld.w	%r4, [%r13]+		; %r4      := m->a10
	ld.w	%r5, [%r13]+		; %r5      := m->a11
	ld.w	%r6, [%r13]+		; %r6      := m->a12
	ld.w	%r7, [%r13]+		; %r7      := m->a13
	xld.w	[%sp+16], %r4		; [%sp+16] := m->a10
	xld.w	[%sp+20], %r5		; [%sp+20] := m->a11
	xld.w	[%sp+24], %r6		; [%sp+24] := m->a12
	xld.w	[%sp+28], %r7		; [%sp+28] := m->a13
	ld.w	%r4, [%r13]+		; %r4      := m->a20
	ld.w	%r5, [%r13]+		; %r5      := m->a21
	ld.w	%r6, [%r13]+		; %r6      := m->a22
	ld.w	%r7, [%r13]+		; %r7      := m->a23
	xld.w	[%sp+32], %r4		; [%sp+32] := m->a20
	xld.w	[%sp+36], %r5		; [%sp+36] := m->a21
	xld.w	[%sp+40], %r6		; [%sp+40] := m->a22
	xld.w	[%sp+44], %r7		; [%sp+44] := m->a23
	;//
	ld.w	%r0, %r14		; %r0 := v_in
	ld.w	%r1, %r15		; %r1 := nv
vxform_all_LOOP:
	ld.w	%r4, [%r0]+		; %r4      := v_in->x
	ld.w	%r5, [%r0]+		; %r5      := v_in->y
	ld.w	%r6, [%r0]+		; %r6      := v_in->z
	xld.w	[%sp+48], %r4		; [%sp+48] := v_in->x
	xld.w	[%sp+52], %r5		; [%sp+52] := v_in->y
	xld.w	[%sp+56], %r6		; [%sp+56] := v_in->z
	xcall	vxform			; 結果格納ポインタの%r12は%r10にそのまま返されるので、(S1C33 ABI仕様)
	ld.w	%r12, %r10		; 次回のために、再び%r12に戻しておきます。
	sub	%r1, 1			; %r1  := nv--
	jrne.d	vxform_all_LOOP
	add	%r12, 12		;			*delay*
	;//
	xadd	%sp, %sp, 60
	popn	%r1
	ret
");
#endif /*REND_NOASM*/

/* * Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
 * - render_mesh()の中の、クリッピング用頂点リストを組み立てる処理を分離し、アセンブラ化して高速化しました。
 * - C言語版は、動作確認と処理内容を示すためだけに残してあります。
 */
#ifdef REND_NOASM
static int pick_vertices(CLIPVERTEX* cv, const vector* v, const TCOORD* c, const FACE* f) {
	int i;
	int iv;
	int ic;
	int nfv;
	FACEVERTEX* fv;
	const vector* pv;
	const TCOORD* pc;

	nfv = f->nfv;
	if(nfv > N_VERTICES_MAX) {
		nfv = N_VERTICES_MAX;
	}
	fv = (FACEVERTEX*)(f + 1);

	i = nfv;
	do {
		iv = fv->iv;
		ic = fv->ic;
		pv = &v[iv];
		pc = &c[ic];
		cv->x = pv->x;
		cv->y = pv->y;
		cv->z = pv->z;
		cv->s = fadd(fild(pc->s), fld(0.5)); /* テクセルの中心をポイント */
		cv->t = fadd(fild(pc->t), fld(0.5)); /* テクセルの中心をポイント */
		fv++;
		cv++;
	} while(--i);

	return nfv;
}
#else /*REND_NOASM*/
/*static*/ int pick_vertices(CLIPVERTEX* cv, const vector* v, const TCOORD* c, const FACE* f);
asm("
	.code
	.align	1
	;// %r12 := cv
	;// %r13 := v
	;// %r14 := c
	;// %r15 := f
pick_vertices:
	ld.ub	%r10, [%r15]		; %r10 := nfv
	ld.w	%r9, 12			; %r9  := sizeof(vector)		*anti-interlock*
	cmp	%r10, 8			; if(nfv > N_VERTICES_MAX)
	jrle.d	3
	 add	%r15, 2			; %r15 := fv				*delay*
	 ld.w	%r10, 8			;   %r10 := nfv = N_VERTICES_MAX
	ld.w	%r11, %r10		; %r11 := i = nfv
pick_vertices_LOOP:
	ld.ub	%r4, [%r15]+		; %r4  := iv = fv->iv
	mlt.h	%r4, %r9		; %alr := iv * sizeof(vector)
	ld.w	%r4, %alr		; %r4  := iv * sizeof(vector)
	add	%r4, %r13		; %r4  := pv = &v[iv]
	ld.w	%r5, [%r4]+		; %r5  := x = pv->x
	ld.w	%r6, [%r4]+		; %r6  := y = pv->y
	ld.w	%r7, [%r4]		; %r7  := z = pv->z
	ld.w	[%r12]+, %r5		; cv->x = x
	ld.w	[%r12]+, %r6		; cv->y = y
	ld.w	[%r12]+, %r7		; cv->z = z
	ld.ub	%r4, [%r15]+		; %r4  := ic = fv->ic
	sla	%r4, 1			; %r4  := ic * sizeof(TCOORD)
	add	%r4, %r14		; %r4  := pc = &c[ic]
	ld.ub	%r5, [%r4]+		; %r5  := s = pc->s
	ld.ub	%r6, [%r4]		; %r6  := t = pc->t
	sla	%r5, 2			; %r5  := s = fadd(fild(s), fld(0.5)) = (s * 4 + 2) * 256
	sla	%r6, 2			; %r6  := t = fadd(fild(t), fld(0.5)) = (t * 4 + 2) * 256
	add	%r5, 2
	add	%r6, 2
	sla	%r5, 8
	sla	%r6, 8
	ld.w	[%r12]+, %r5		; cv->s = s
	ld.w	[%r12]+, %r6		; cv->t = t
	sub	%r11, 1			; %r11 := i--
	jrne	pick_vertices_LOOP
	ret
");
#endif /*REND_NOASM*/

/* * Thu Apr 03 15:14:06 JST 2008 Naoyuki Sawa
 * - render_mesh()の中の、描画用頂点リストを組み立てる処理を分離し、アセンブラ化して高速化しました。
 * - C言語版は、動作確認と処理内容を示すためだけに残してあります。
 */
#ifdef REND_NOASM
static void init_vertices(RENDER* r, RENDVERTEX* rv, const CLIPVERTEX* cv, int nv) {
	do {
		rv->x = fist(cv->x);
		rv->y = fist(cv->y);
		rv->z = fist(fimul(fdiv(fmul(fsub(cv->z, r->front), r->back),
					fmul(fsub(r->back, r->front), cv->z)), 0xffff)); /* 8.8bit */
		rv->s = fist(fimul(cv->s, 1 << 8)); /* 8.8bit */
		rv->t = fist(fimul(cv->t, 1 << 8)); /* 8.8bit */
		cv++;
		rv++;
	} while(--nv);
}
#else /*REND_NOASM*/
/*static*/ void init_vertices(RENDER* r, RENDVERTEX* rv, const CLIPVERTEX* cv, int nv);
asm("
	.code
	.align	1
	;// %r12 := r
	;// %r13 := rv
	;// %r14 := cv
	;// %r15 := nv
init_vertices:
	pushn	%r3
	ld.w	%r0, %r12		; %r0  := r
	ld.w	%r1, %r13		; %r1  := rv
	ld.w	%r2, %r14		; %r2  := cv
	ld.w	%r3, %r15		; %r3  := nv
	;//
	add	%r0, 12			; %r0  := &r->front
init_vertices_LOOP:
	ld.w	%r4, [%r2]+		; %r4  :=      cv->x
	xsra	%r4, 10			; %r4  := fist(cv->x)
	ld.h	[%r1]+, %r4		; rv->x = fist(cv->x)
	;//
	ld.w	%r4, [%r2]+		; %r4  :=      cv->y
	xsra	%r4, 10			; %r4  := fist(cv->y)
	ld.h	[%r1]+, %r4		; rv->y = fist(cv->y)
	;//
	ld.w	%r4, [%r0]+		; %r4  := F = r->front
	ld.w	%r5, [%r0]		; %r5  := B = r->back
	ld.w	%r6, [%r2]+		; %r6  := Z = cv->z					*anti-interlock*
	;//
	mlt.w	%r5, %r6		; {%r10,%r11} :=             BZ              << 10
	ld.w	%r10, %alr
	ld.w	%r11, %ahr
	;//
	mlt.w	%r4, %r5		; {%r12,%r14} :=        FB                   << 10
	ld.w	%r12, %alr
	ld.w	%r14, %ahr
	sub	%r12, %r10		; {%r12,%r14} :=       (FB - BZ)             << 10
	sbc	%r14, %r11
	xsrl	%r12, 10		; %r12        :=        FB - BZ
	xsll	%r14, 22
	or	%r12, %r14
	;//
	mlt.w	%r4, %r6		; {%r13,%r15} :=                    FZ       << 10
	ld.w	%r13, %alr
	ld.w	%r15, %ahr
	sub	%r13, %r10		; {%r13,%r15} :=                   (FZ - BZ) << 10
	sbc	%r15, %r11
	xsrl	%r13, 10		; %r13        :=                    FZ - BZ
	xsll	%r15, 22
;//	or	%r13, %r15		; -------------------------------------------+
	;//				;                                            |
	xcall.d	fdiv			; %r10        :=       (FB - BZ) / (FZ - BZ) |	0x0000〜0x0400(0.0〜1.0)
	or	%r13, %r15		; <------------------------------------------+		*delay*
	ld.w	%r4, -1			; %r4[31:15]  :=                                0xffff
	mltu.h	%r4, %r10		; %alr        :=      ((FB - BZ) / (FZ - BZ)) * 0xffff
	ld.w	%r4, %alr
	xsra	%r4, 10
	ld.h	[%r1]+, %r4		; rv->z       := fist(((FB - BZ) / (FZ - BZ)) * 0xffff)
	;//
	ld.w	%r4, [%r2]+		; %r4  :=            cv->s
	sra	%r4, 2			; %r4  := fist(fimul(cv->s, 1 << 8))
	ld.h	[%r1]+, %r4		; rv->s = fist(fimul(cv->s, 1 << 8))
	;//
	ld.w	%r4, [%r2]+		; %r4  :=            cv->t
	sra	%r4, 2			; %r4  := fist(fimul(cv->t, 1 << 8))
	ld.h	[%r1]+, %r4		; rv->t = fist(fimul(cv->t, 1 << 8))
	;//
	sub	%r3, 1			; %r3  := nv--
	jrne.d	init_vertices_LOOP
	sub	%r0, 4			; %r0  := &r->front					*delay*
	;//
	popn	%r3
	ret
");
#endif /*REND_NOASM*/

//{{2008/04/04,2008/04/06:変更
///* Ｚクリッピングを行います。
// * [in]
// *	render				レンダラーへのポインタ。
// *	nsrc				クリッピングする頂点の数。
// *	render_work->clip_vertices[0]	クリッピングする頂点リスト。(グローバル変数)
// * [out]
// *	戻り値				クリッピング後の頂点の数。
// *	render_work->clip_vertices[0]	クリッピング後の頂点リスト。(グローバル変数)
// * [note]
// *	* 作業領域として、render_work->clip_vertices[1]も使います。
// */
//static int clip_z(RENDER* render, int nsrc) {
//#define CLIP(SRC, DST, OP, LIM) \
//	src = SRC; \
//	dst = DST; \
//	ndst = 0; \
//	for(i = 0, v0 = src; i < nsrc; i++, v0++) { \
//		v1 = i < nsrc - 1 ? v0 + 1 : src; \
//		if(fcom(v0->z, OP, LIM)) { \
//			*dst = *v0; \
//			dst++; \
//			ndst++; \
//			if(ndst == N_VERTICES_MAX) break; \
//			if(fcom(v1->z, OP, LIM)) { \
//				continue; \
//			} else { \
//				/* go clip */ \
//			} \
//		} else { \
//			if(fcom(v1->z, OP, LIM)) { \
//				/* go clip */ \
//			} else { \
//				continue; \
//			} \
//		} \
//		/* do clip */ \
//		dst->z = LIM; \
//		n0 = fsub(LIM, v0->z); \
//		n1 = fsub(v1->z, LIM); \
//		n01 = fadd(n0, n1); \
//		n001 = fdiv(n0, n01); \
//		dst->x = fadd(v0->x, fmul(fsub(v1->x, v0->x), n001)); \
//		dst->y = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001)); \
//		dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)); \
//		dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001)); \
//		dst++; \
//		ndst++; \
//		if(ndst == N_VERTICES_MAX) break; \
//	} \
//	if(ndst < 3) return 0; \
//
//	CLIPVERTEX *src, *dst;
//	CLIPVERTEX *v0, *v1;
//	int i, ndst;
//	fixed n0, n1, n01, n001;
//	fixed front = render->front;
//	fixed back  = render->back ;
//
//	/* 実際のクリッピング処理の前に、クリッピングが必要かどうかおおまかに判定します。
//	 * 全ての頂点がレンジ内に入っていれば、クリッピングは必要ありません。
//	 * 実際のクリッピング処理よりも単純な比較処理ですので、クリッピング不要なら高速化が期待できます。
//	 * 判定の結果、クリッピングが必要だったら、おおまかな判定処理が増えたぶん遅くなってしまいます。
//	 * オブジェクト単位のカリング等を行って、なるべく画面外のメッシュは描かないようにしてください。
//	 */
//	v0 = render_work->clip_vertices[0];
//	v1 = v0 + nsrc;
//	while(v0 < v1) {
//		if(!fcom(v0->z, >=, front)) break;
//		if(!fcom(v0->z, <=, back )) break;
//		v0++;
//	}
//	if(v0 == v1) return nsrc;
//
//	/* ※TODO: 大半のメッシュが完全に画面内（上で判定済み）か、または完全に画面外だと仮定すると、
//	 *         実際のクリッピング処理に入る前に完全画面外の判断を行った方が速くなるかも知れません。
//	 */
//
//	/* レンジ最大値（back）との比較が「<」ではなく「<=」である理由について説明します。
//	 * render_mesh()内で描画用頂点リストを組み立てている処理をご覧いただければわかるように、
//	 * z=back の頂点を Zバッファ値=0xff（8.8bitでは0xffff） にマップしています。
//	 * z=back の頂点も可視です。従って、z=back もクリッピング内として「<=」で判定しています。
//	 */
//	CLIP(render_work->clip_vertices[0], render_work->clip_vertices[1], >=, front); nsrc = ndst;
//	CLIP(render_work->clip_vertices[1], render_work->clip_vertices[0], <=, back );
//	return ndst;
//
//#undef CLIP
//}
//↓* Fri Apr 04 00:06:18 JST 2008 Naoyuki Sawa
//↓- クリッピングが必要かどうかおおまかに判定する処理と、実際のクリッピング処理を、別々の関数に分離しました。
//↓  おおまかに判定するだけの関数が単純化されるため、実際のクリッピング処理が不要だった場合に高速となります。
//↓* Sun Apr 06 14:51:21 JST 2008 Naoyuki Sawa
//↓- clip_z_sub()をアセンブラ化して高速化しました。
#ifdef REND_NOASM
static int clip_z_sub(RENDER* render, CLIPVERTEX* clip_vertices_0, CLIPVERTEX* clip_vertices_1, int nsrc) {
#define CLIP(SRC, DST, OP, LIM) \
	src = SRC; \
	dst = DST; \
	ndst = 0; \
	for(i = 0, v0 = src; i < nsrc; i++, v0++) { \
		v1 = i < nsrc - 1 ? v0 + 1 : src; \
		if(fcom(v0->z, OP, LIM)) { \
			*dst = *v0; \
			dst++; \
			ndst++; \
			if(ndst == N_VERTICES_MAX) break; \
			if(fcom(v1->z, OP, LIM)) { \
				continue; \
			} else { \
				/* go clip */ \
			} \
		} else { \
			if(fcom(v1->z, OP, LIM)) { \
				/* go clip */ \
			} else { \
				continue; \
			} \
		} \
		/* do clip */ \
		dst->z = LIM; \
		n0 = fsub(LIM, v0->z); \
		n1 = fsub(v1->z, LIM); \
		n01 = fadd(n0, n1); \
		n001 = fdiv(n0, n01); \
		dst->x = fadd(v0->x, fmul(fsub(v1->x, v0->x), n001)); \
		dst->y = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001)); \
		dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)); \
		dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001)); \
		dst++; \
		ndst++; \
		if(ndst == N_VERTICES_MAX) break; \
	} \
	if(ndst < 3) return 0; \

	CLIPVERTEX *src, *dst;
	CLIPVERTEX *v0, *v1;
	int i, ndst;
	fixed n0, n1, n01, n001;
	fixed front = render->front;
	fixed back  = render->back ;

	/* ※TODO: 大半のメッシュが完全に画面内（上で判定済み）か、または完全に画面外だと仮定すると、
	 *         実際のクリッピング処理に入る前に完全画面外の判断を行った方が速くなるかも知れません。
	 */

	/* レンジ最大値（back）との比較が「<」ではなく「<=」である理由について説明します。
	 * render_mesh()内で描画用頂点リストを組み立てている処理をご覧いただければわかるように、
	 * z=back の頂点を Zバッファ値=0xff（8.8bitでは0xffff） にマップしています。
	 * z=back の頂点も可視です。従って、z=back もクリッピング内として「<=」で判定しています。
	 */
	CLIP(clip_vertices_0, clip_vertices_1, >=, front); nsrc = ndst;
	CLIP(clip_vertices_1, clip_vertices_0, <=, back );
	return ndst;

#undef CLIP
}
static int clip_z(RENDER* render, CLIPVERTEX* clip_vertices_0, CLIPVERTEX* clip_vertices_1, int nsrc) {
	/* 実際のクリッピング処理の前に、クリッピングが必要かどうかおおまかに判定します。
	 * 全ての頂点がレンジ内に入っていれば、クリッピングは必要ありません。
	 * 実際のクリッピング処理よりも単純な比較処理ですので、クリッピング不要なら高速化が期待できます。
	 * 判定の結果、クリッピングが必要だったら、おおまかな判定処理が増えたぶん遅くなってしまいます。
	 * オブジェクト単位のカリング等を行って、なるべく画面外のメッシュは描かないようにしてください。
	 */
	fixed front = render->front;
	fixed back  = render->back ;
	CLIPVERTEX* v = clip_vertices_0;
	int i = nsrc;
	do {
		if(!fcom(v->z, >=, front)) goto L_CLIP;
		if(!fcom(v->z, <=, back )) goto L_CLIP;
		v++;
	} while(--i);
	return nsrc;
L_CLIP:
	return clip_z_sub(render, clip_vertices_0, clip_vertices_1, nsrc);
}
#else /*REND_NOASM*/
/*static*/ int clip_z(RENDER* render, CLIPVERTEX* clip_vertices_0, CLIPVERTEX* clip_vertices_1, int nsrc);
asm("
	.code
	.align	1
	;// %r12 := render
	;// %r13 := clip_vertices_0
	;// %r14 := clip_vertices_1
	;// %r15 := nsrc
clip_z:
	add	%r12, 12		; %r12 := &render->front
	ld.w	%r4, [%r12]+		; %r4  := front = render->front
	ld.w	%r5, [%r12]		; %r5  := back  = render->back
	ld.w	%r6, %r13		; %r6  := v = clip_vertices_0
	ld.w	%r7, %r15		; %r7  := i = nsrc
	add	%r6, 8			; %r6  := &v->z
clip_z_LOOP:
	ld.w	%r9, [%r6]		; %r9  := z = v->z
	cmp	%r9, %r4		; if(z < front) goto CLIP		!INTERLOCK!
	jrlt	clip_z_CLIP
	cmp	%r9, %r5		; if(z > back ) goto CLIP
	jrgt	clip_z_CLIP
	sub	%r7, 1			; i--
	jrne.d	clip_z_LOOP
	add	%r6, 20			; %r6  := v++				*delay*
	ret.d				; return nsrc
	ld.w	%r10, %r15		; %r10 := nsrc				*delay*
clip_z_CLIP:
	xjp.d	clip_z_sub		
	sub	%r12, 16		; %r12 := render			*delay*
");
asm("
	.code
	.align	1
	;// %r12 := render
	;// %r13 := clip_vertices_0
	;// %r14 := clip_vertices_1
	;// %r15 := nsrc
clip_z_sub:
	pushn	%r3
	xsub	%sp, %sp, 20
	;//
	add	%r12, 12			; %r12     := &render->front
	ld.w	%r4, [%r12]+			; %r4      := front
	ld.w	%r5, [%r12]			; %r5      := back
	xld.w	[%sp+0], %r4			; [%sp+0]  := front
	xld.w	[%sp+4], %r5			; [%sp+4]  := back
	xld.w	[%sp+8], %r13			; [%sp+8]  := clip_vertices_0
	xld.w	[%sp+12], %r14			; [%sp+12] := clip_vertices_1
	;//
	ld.w	%r0, %r15			; %r0  := i = nsrc
	xld.w	%r1, [%sp+8]			; %r1  := v0 = clip_vertices_0
	xld.w	%r2, [%sp+12]			; %r2  := dst = clip_vertices_1
	ld.w	%r3, 8				; %r3  := (8-ndst) = 8
	;//-------------------------------------;
clip_z_sub_FRONT_FOR:
	ld.w	%r4, %r1			; %r4  := v1 = v0 + 1
;//	add	%r4, 20				; -----------+
	cmp	%r0, 1				; if(i == 1) |
	jrne.d	3				;            |
	 add	%r4, 20				; <----------+							*delay*
	 xld.w	%r4, [%sp+8]			;   %r4  := v1 = clip_vertices_0
	;//
	xld.w	%r5, [%r1+8]			; %r5  := v0->z
	xld.w	%r12, [%sp+0]			; %r12 := front
	xld.w	%r13, [%r4+8]			; %r13 := v1->z							*anti-interlock*
	cmp	%r5, %r12			; if(v0->z >= front)
	jrlt	clip_z_sub_FRONT_ELSE
	;//
	ld.w	%r9, [%r1]+			;   *dst++ = *v0
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]
	sub	%r1, 16				; <-----------+							*anti-interlock*
	ld.w	[%r2]+, %r9			;             |
;//	sub	%r1, 16				; %r1  := v0 -+
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_z_sub_FRONT_BREAK
	;//
	cmp	%r13, %r12			; if(v1->z >= front) { continue } else { go clip }
	jrlt	clip_z_sub_FRONT_CLIP
	jp	clip_z_sub_FRONT_CONTINUE
	;//
clip_z_sub_FRONT_ELSE:
	cmp	%r13, %r12			; if(v1->z >= front) { go clip } else { continue }
	jrlt	clip_z_sub_FRONT_CONTINUE
	;//
clip_z_sub_FRONT_CLIP:
	xld.w	[%sp+16], %r4			; [%sp+16] := v1
	sub	%r12, %r5			; %r12     := n0  = fsub(front, v0->z)
;//	sub	%r13, %r5			; %r13     := n01 = fsub(v1->z, v0->z) -+
	xcall.d	fdiv				; %r10     := n001 = fdiv(n0, n01)      |
	sub	%r13, %r5			; <-------------------------------------+			*delay*
	xld.w	%r4, [%sp+16]			; %r4      := v1
	;//
	call	clip_z_sub_CLIP_SUB		; dst->x = fadd(v0->x, fmul(fsub(v1->x, v0->x), n001))
	call	clip_z_sub_CLIP_SUB		; dst->y = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001))
	xld.w	%r9, [%sp+0]			; %r9   := front
	add	%r1, 4				; %r1   := &v0->s						*anti-interlock*
	ld.w	[%r2]+, %r9			; dst->z = front
;//	add	%r4, 4				; %r4   := &v1->s -------------------------------------+
	call.d	clip_z_sub_CLIP_SUB		; dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)) |
	add	%r4, 4				; <----------------------------------------------------+	*delay*
	call	clip_z_sub_CLIP_SUB		; dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001))
	sub	%r1, 20				; %r1   := v0
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_z_sub_FRONT_BREAK
	;//
clip_z_sub_FRONT_CONTINUE:
	sub	%r0, 1				; %r0  := i--
	jrne.d	clip_z_sub_FRONT_FOR
	add	%r1, 20				; %r1  := v0++							*delay*
	;//
clip_z_sub_FRONT_BREAK:
	ld.w	%r0, 8				; %r0  := i = nsrc = ndst = 8 - (8-ndst)
	sub	%r0, %r3
	cmp	%r0, 3
	jrlt	clip_z_sub_RET
	xld.w	%r1, [%sp+12]			; %r1  := v0 = clip_vertices_1
	xld.w	%r2, [%sp+8]			; %r2  := dst = clip_vertices_0
	ld.w	%r3, 8				; %r3  := (8-ndst) = 8
	;//-------------------------------------;
clip_z_sub_BACK_FOR:
	ld.w	%r4, %r1			; %r4  := v1 = v0 + 1
;//	add	%r4, 20				; -----------+
	cmp	%r0, 1				; if(i == 1) |
	jrne.d	3				;            |
	 add	%r4, 20				; <----------+							*delay*
	 xld.w	%r4, [%sp+12]			;   %r4  := v1 = clip_vertices_1
	;//
	xld.w	%r5, [%r1+8]			; %r5  := v0->z
	xld.w	%r12, [%sp+4]			; %r12 := back
	xld.w	%r13, [%r4+8]			; %r13 := v1->z							*anti-interlock*
	cmp	%r5, %r12			; if(v0->z <= back)
	jrgt	clip_z_sub_BACK_ELSE
	;//
	ld.w	%r9, [%r1]+			;   *dst++ = *v0
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]+
	ld.w	[%r2]+, %r9
	ld.w	%r9, [%r1]
	sub	%r1, 16				; <-----------+							*anti-interlock*
	ld.w	[%r2]+, %r9			;             |
;//	sub	%r1, 16				; %r1  := v0 -+
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_z_sub_BACK_BREAK
	;//
	cmp	%r13, %r12			; if(v1->z <= back) { continue } else { go clip }
	jrgt	clip_z_sub_BACK_CLIP
	jp	clip_z_sub_BACK_CONTINUE
	;//
clip_z_sub_BACK_ELSE:
	cmp	%r13, %r12			; if(v1->z <= back) { go clip } else { continue }
	jrgt	clip_z_sub_BACK_CONTINUE
	;//
clip_z_sub_BACK_CLIP:
	xld.w	[%sp+16], %r4			; [%sp+16] := v1
	sub	%r12, %r5			; %r12     := n0  = fsub( back, v0->z)
;//	sub	%r13, %r5			; %r13     := n01 = fsub(v1->z, v0->z) -+
	xcall.d	fdiv				; %r10     := n001 = fdiv(n0, n01)      |
	sub	%r13, %r5			; <-------------------------------------+			*delay*
	xld.w	%r4, [%sp+16]			; %r4      := v1
	;//
	call	clip_z_sub_CLIP_SUB		; dst->x = fadd(v0->x, fmul(fsub(v1->x, v0->x), n001))
	call	clip_z_sub_CLIP_SUB		; dst->y = fadd(v0->y, fmul(fsub(v1->y, v0->y), n001))
	xld.w	%r9, [%sp+4]			; %r9   := back
	add	%r1, 4				; %r1   := &v0->s						*anti-interlock*
	ld.w	[%r2]+, %r9			; dst->z = back
;//	add	%r4, 4				; %r4   := &v1->s -------------------------------------+
	call.d	clip_z_sub_CLIP_SUB		; dst->s = fadd(v0->s, fmul(fsub(v1->s, v0->s), n001)) |
	add	%r4, 4				; <----------------------------------------------------+	*delay*
	call	clip_z_sub_CLIP_SUB		; dst->t = fadd(v0->t, fmul(fsub(v1->t, v0->t), n001))
	sub	%r1, 20				; %r1   := v0
	;//
	sub	%r3, 1				;   %r3  := (8-ndst)--
	jreq	clip_z_sub_BACK_BREAK
	;//
clip_z_sub_BACK_CONTINUE:
	sub	%r0, 1				; %r0  := i--
	jrne.d	clip_z_sub_BACK_FOR
	add	%r1, 20				; %r1  := v0++							*delay*
	;//
clip_z_sub_BACK_BREAK:
	ld.w	%r0, 8				; %r0  := ndst = 8 - (8-ndst)
	sub	%r0, %r3
	;//-------------------------------------;
clip_z_sub_RET:
	ld.w	%r10, %r0			; return ndst
	;//
	xadd	%sp, %sp, 20
	popn	%r3
	ret
	;//-------------------------------------;
clip_z_sub_CLIP_SUB:
	ld.w	%r5, [%r1]+			; %r5   :=                              v0->i
	ld.w	%r6, [%r4]+			; %r6   :=                       v1->i
	sub	%r6, %r5			; %r6   :=                  fsub(v1->i, v0->i)
	mlt.w	%r6, %r10			; %r6   :=             fmul(fsub(v1->i, v0->i), n001)
	ld.w	%r6, %alr
	ld.w	%r9, %ahr
	xsrl	%r6, 10
	xsll	%r9, 22
	or	%r6, %r9
	add	%r5, %r6			; %r5   := fadd(v0->i, fmul(fsub(v1->i, v0->i), n001))
	ld.w	[%r2]+, %r5			; dst->i = fadd(v0->i, fmul(fsub(v1->i, v0->i), n001))
	ret
");
#endif /*REND_NOASM*/
//}}2008/04/04,2008/04/06:変更

/* * Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
 * - render_mesh()の中の、透視変換のためのx=x/z,y=y/zを行う処理を分離し、アセンブラ化して高速化しました。
 * - C言語版は、動作確認と処理内容を示すためだけに残してあります。
 */
#ifdef REND_NOASM
static void inhomogenize(CLIPVERTEX* cv, int nv) { /* 非同次座標化 */
	do {
		cv->x = fdiv(cv->x, cv->z);
		cv->y = fdiv(cv->y, cv->z);
		cv++;
	} while(--nv);
}
#else /*REND_NOASM*/
/*static*/ void inhomogenize(CLIPVERTEX* cv, int nv); /* 非同次座標化 */
asm("
	.code
	.align	1
	;// %r12 := cv
	;// %r13 := nv
inhomogenize:
	pushn	%r2
	ld.w	%r0, %r12		; %r0  := cv
	ld.w	%r1, %r13		; %r1  := nv
	;//
inhomogenize_LOOP:
	xld.w	%r2, [%r0+8]		; %r2  := cv->z
	;//
	ld.w	%r12, [%r0]		; %r12 := cv->x
	xcall.d	fdiv
	ld.w	%r13, %r2		; %r13 := cv->z			*delay*
	ld.w	[%r0]+, %r10		; cv->x = fdiv(cv->x, cv->z)
	;//
	ld.w	%r12, [%r0]		; %r12 := cv->y
	xcall.d	fdiv
	ld.w	%r13, %r2		; %r13 := cv->z			*delay*
	ld.w	[%r0], %r10		; cv->y = fdiv(cv->y, cv->z)
	;//
	sub	%r1, 1			; %r1  := nv--
	jrne.d	inhomogenize_LOOP
	add	%r0, 16			; %r0  := cv++			*delay*
	;//
	popn	%r2
	ret
");
#endif /*REND_NOASM*/

/* * Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
 * - render_mesh()の中の、表裏判定を行う処理を分離し、アセンブラ化して高速化しました。
 * - C言語版は、動作確認と処理内容を示すためだけに残してあります。
 */
#ifdef REND_NOASM
static int cull_back(const CLIPVERTEX* cv, int nv) { /* バックフェースカリング */
	fixed xN0 = fsub(cv[0].x, cv[nv - 1].x);
	fixed yN0 = fsub(cv[0].y, cv[nv - 1].y);
	fixed x01 = fsub(cv[1].x, cv[     0].x);
	fixed y01 = fsub(cv[1].y, cv[     0].y);
	fixed s = fsub(fmul(xN0, y01), fmul(x01, yN0));
	return ficom(s, <=, 0);
}
#else /*REND_NOASM*/
/*static*/ int cull_back(const CLIPVERTEX* cv, int nv); /* バックフェースカリング */
asm("
	.code
	.align	1
	;// %r12 := cv
	;// %r13 := nv
cull_back:
	ld.w	%r4, [%r12]+		; %r4  := cv[0].x
	ld.w	%r5, [%r12]		; %r5  := cv[0].y
	add	%r12, 16		; %r12 := &cv[1]
	;//
	ld.w	%r6, [%r12]+		; %r6  := cv[1].x
	ld.w	%r7, [%r12]		; %r7  := cv[1].y
	sub	%r12, 44		; %r12 := &cv[-1]
	;//
	ld.w	%r9, 20			; %r9  := sizeof(CLIPVERTEX)
	mlt.h	%r9, %r13		; %alr := sizeof(CLIPVERTEX) * nv
	ld.w	%r9, %alr		; %r9  := sizeof(CLIPVERTEX) * nv
	add	%r12, %r9		; %r12 := &cv[nv-1]
	ld.w	%r10, [%r12]+		; %r10 := cv[nv-1].x
	ld.w	%r11, [%r12]		; %r11 := cv[nv-1].y
	;//
	sub	%r6, %r4		; %r6  := x01 = fsub(cv[1].x, cv[0].x)
	sub	%r7, %r5		; %r7  := y01 = fsub(cv[1].y, cv[0].y)
	;//
	sub	%r4, %r10		; %r4  := xN0 = fsub(cv[0].x, cv[nv-1].x)
	sub	%r5, %r11		; %r5  := yN0 = fsub(cv[0].y, cv[nv-1].y)
	;//
	mlt.w	%r4, %r7		; {%alr,%ahr} := fmul(xN0, y01) << 10
	ld.w	%r12, %alr		; {%alr,%ahr} := fmul(xN0, y01) << 10
	ld.w	%r13, %ahr
	;//
	mlt.w	%r6, %r5		; {%ahr,%ahr} := fmul(x01, yN0) << 10
	ld.w	%r14, %alr		; {%r14,%r15} := fmul(x01, yN0) << 10
	ld.w	%r15, %ahr
	;//
	cmp	%r13, %r15		; return ((fmul(xN0, y01) << 10) <= (fmul(x01, yN0) << 10)) ? 1 : 0
	jrgt	cull_back_RET0
	jrlt	cull_back_RET1
	cmp	%r12, %r14
	jrugt	cull_back_RET0		; (下位Wordは符号bitを含まないので、符号無しで比較することに注意!!)
cull_back_RET1:
	ret.d				; return 1
	ld.w	%r10, 1			;		*delay*
cull_back_RET0:
	ret.d				; return 0
	ld.w	%r10, 0			;		*delay*
");
#endif /*REND_NOASM*/

void
render_mesh(RENDER* render, const MESH* mesh)
{
//{{2008/04/02,2008/04/05:変更
//	int i, j, n_vectors, n_tcoords, n_faces, n_face_vertices, n_vertices;
//↓* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
//↓- クリッピング用頂点リストを組み立てる処理を分離したため、n_face_verticesは不要になりました。
//	int i, j, n_vectors, n_tcoords, n_faces, n_vertices;
//↓* Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
//↓- 透視変換のためのx=x/z,y=y/zを行う処理を分離したため、jは不要になりました。
	int i, n_vectors, n_tcoords, n_faces, n_vertices;
//}}2008/04/02,2008/04/05:変更
//{{2008/04/05:削除
//	fixed s, xN0, yN0, x01, y01;
//→* Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
//→- 表裏判定を行う処理を分離したため、s,xN0,yN0,x01,y01は不要になりました。
//}}2008/04/05:削除
//{{2008/04/01,2008/04/02,:変更
//	struct _vector *vectors, *vector, *vector_src, *vector_dst; /* 変数名vectorとの衝突回避のため、構造体名にはstruct_vectorを使用 */
//↓* Tue Apr 01 12:56:34 JST 2008 Naoyuki Sawa
//↓- 全頂点座標を変換する処理を分離したため、vector_srcとvector_dstは不要になりました。
//	struct _vector *vectors, *vector; /* 変数名vectorとの衝突回避のため、構造体名にはstruct_vectorを使用 */
//↓* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
//↓- クリッピング用頂点リストを組み立てる処理を分離したため、vectorは不要になりました。
//↓- 変数vectorを削除したため、構造体名との衝突回避は不要になりました。
	vector* vectors;
//}}2008/04/01,2008/04/02:変更
//{{2008/04/02:変更
//	TCOORD *tcoords, *tcoord;
//↓* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
//↓- クリッピング用頂点リストを組み立てる処理を分離したため、tcoordは不要になりました。
	TCOORD* tcoords;
//}}2008/04/02:変更
	FACE *face;
//{{2008/04/02:削除
//	FACEVERTEX *face_vertices, *face_vertex;
//→* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
//→- クリッピング用頂点リストを組み立てる処理を分離したため、face_verticesとface_vertexは不要になりました。
//}}2008/04/02:削除
//{{2008/04/05:削除
//	CLIPVERTEX *clip_vertex;
//→* Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
//→- 透視変換のためのx=x/z,y=y/zを行う処理を分離したため、clip_vertexは不要になりました。
//}}2008/04/05:削除
//{{2008/04/03:削除
//	RENDVERTEX *rend_vertex;
//→* Thu Apr 03 15:14:06 JST 2008 Naoyuki Sawa
//→- 描画用頂点リストを組み立てる処理を分離したため、rend_vertexは不要になりました。
//}}2008/04/03:削除

//{{2009/04/11:変更
////{{2008/01/13:追加。これまではselect_draw_scanline_wz()で行っていた処理です。
//	DSLPARAM param;
//	DSLPROC draw_scanline;
//	SURFACE* surface = render->surface;
//	TEXTURE* texture = render->context->texture;
//	param.surface_w = surface->w;
//	param.surface_h = surface->h;
//	param.surface_vbuff = surface->vbuff;
//	param.surface_zbuff = surface->zbuff;
//	if(!texture) {
//		draw_scanline = draw_scanline_wz_color;
//	} else {
//		param.texture_w = texture->header.w;
//		param.texture_h = texture->header.h;
//		param.texture_buf = texture->buf;
//		switch(texture->header.bpp) {
//		case 1:
//			if(!texture->header.mask) {
//				draw_scanline = draw_scanline_wz_1bit;
//			} else {
//				param.texture_mask = texture->mask;
//				draw_scanline = draw_scanline_wz_1bitmask;
//			}
//			break;
//		default/*2*/:
//			if(!texture->header.mask) {
//				draw_scanline = draw_scanline_wz_2bit;
//			} else {
//				param.texture_mask = texture->mask;
//				draw_scanline = draw_scanline_wz_2bitmask;
//			}
//			break;
//		}
//	}
////}}2008/01/13:追加。これまではselect_draw_scanline_wz()で行っていた処理です。
//↓2009/04/11:スキャンライン描画関数の選択処理を、再度、select_draw_scanline()に分離しました。
	DSLPARAM param;
	DSLPROC draw_scanline = select_draw_scanline(render, &param);
//}}2009/04/11:変更

	/* リストの先頭アドレスを計算します。 */
	n_vectors = mesh->nv;
	n_tcoords = mesh->nc;
	n_faces = mesh->nf;
//{{2008/04/02:変更
//	vectors = (struct _vector*)(mesh + 1); /* 変数名vectorとの衝突回避のため、構造体名にはstruct_vectorを使用 */
//↓* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
//↓- 変数vectorを削除したため、構造体名との衝突回避は不要になりました。
	vectors = (vector*)(mesh + 1);
//}}2008/04/02:変更
	tcoords = (TCOORD*)(vectors + n_vectors);
	face = (FACE*)(tcoords + n_tcoords);

	/* 全頂点座標を変換します。 */
//{{2008/04/01,2008/04/06:変更
//	for(i = 0, vector_src = vectors, vector_dst = render_work->xformed_vectors;
//	    i < n_vectors;
//	    i++, vector_src++, vector_dst++) {
//		*vector_dst = vxform(render->context->matrix, *vector_src);
//	}
//↓* Tue Apr 01 12:56:34 JST 2008 Naoyuki Sawa
//↓- render_mesh()の中の、全頂点座標を変換する処理を、vxform_all()に分離して、高速化しました。
//↓  詳細は、vxform_all()のコメントを参照してください。
//	vxform_all(render_work->xformed_vectors, &render->context->matrix, vectors, n_vectors);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
	vxform_all(_xformed_vectors, &render->context->matrix, vectors, n_vectors);
//}}2008/04/01,2008/04/06:変更

	/* 各フェイスを描きます。 */
	for(i = 0;
	    i < n_faces;
//{{2008/04/02:変更
//	    i++, face = (FACE*)(face_vertices + n_face_vertices)) {
//		n_face_vertices = face->nfv;
//		face_vertices = (FACEVERTEX*)(face + 1);
//↓* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
//↓- 変数n_face_verticesとface_verticesの削除に伴って、ループ送り処理を変更しました。
	    i++, face = (FACE*)((FACEVERTEX*)(face + 1) + face->nfv)) {
//}}2008/04/02:変更

		/* クリッピング用頂点リストを組み立てます。
		 * * テクスチャ無しの場合分けを行っていませんが、動作に問題ないはずです。
		 *   テクスチャ無しならmesh->nc=0なので、tcoordsは最初のフェイスを指しています。
		 *   各フェイス頂点のface_vertex->ic=0なので、テクスチャ座標として最初のフェイスのm,nを取得します。
		 *   mesh->nf=0の場合のみ最初のフェイスが存在しませんが、その場合はこのループ自体が回りません。
		 *   取得したテクスチャ座標は描画時に無視されるので、テクスチャ座標値は関係ありません。
		 *   上記の通り、テクスチャ座標取得時に不正なメモリ領域にアクセスしないことが保証されます。
		 *   従って、テクスチャ無しの場合分けを行わなくても、動作に問題はないと思います。
		 */
//{{2008/04/02,2008/04/06:変更
//		n_vertices = 0;
//		for(j = 0, face_vertex = face_vertices, clip_vertex = render_work->clip_vertices[0];
//		    j < n_face_vertices;
//		    j++, face_vertex++, clip_vertex++) {
//			vector = &render_work->xformed_vectors[face_vertex->iv];
//			tcoord = &tcoords[face_vertex->ic];
//			clip_vertex->x = vector->x;
//			clip_vertex->y = vector->y;
//			clip_vertex->z = vector->z;
//			clip_vertex->s = fadd(fild(tcoord->s), fld(0.5)); /* テクセルの中心をポイント */
//			clip_vertex->t = fadd(fild(tcoord->t), fld(0.5)); /* テクセルの中心をポイント */
//			if(++n_vertices == N_VERTICES_MAX) break;
//		}
//↓* Wed Apr 02 15:08:35 JST 2008 Naoyuki Sawa
//↓- render_mesh()の中の、クリッピング用頂点リストを組み立てる処理を、pick_vertices()に分離して、高速化しました。
//↓  詳細は、pick_vertices()のコメントを参照してください。
//		n_vertices = pick_vertices(render_work->clip_vertices[0], render_work->xformed_vectors, tcoords, face);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
		n_vertices = pick_vertices(_clip_vertices[0], _xformed_vectors, tcoords, face);
//}}2008/04/02,2008/04/06:変更
		if(n_vertices < 3) continue;

		/* Ｚクリッピングを行います。 */
//{{2008/04/04,2008/04/06:変更
//		n_vertices = clip_z(render, n_vertices);
//↓* Fri Apr 04 00:06:18 JST 2008 Naoyuki Sawa
//↓- clip_z()の仕様変更に伴って、呼び出し引数を変更しました。
//		n_vertices = clip_z(render, render_work->clip_vertices[0], render_work->clip_vertices[1], n_vertices);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
		n_vertices = clip_z(render, _clip_vertices[0], _clip_vertices[1], n_vertices);
//}}2008/04/04,2008/04/06:変更
		if(n_vertices < 3) continue;

		/* 透視変換のための x=x/z,y=y/z を行います。 */
//{{2008/04/05,2008/04/06:変更
//		for(j = 0, clip_vertex = render_work->clip_vertices[0];
//		    j < n_vertices;
//		    j++, clip_vertex++) {
//			clip_vertex->x = fdiv(clip_vertex->x, clip_vertex->z);
//			clip_vertex->y = fdiv(clip_vertex->y, clip_vertex->z);
//		}
//↓* Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
//↓- render_mesh()の中の、透視変換のためのx=x/z,y=y/zを行う処理を、inhomogenize()に分離して、高速化しました。
//↓  詳細は、inhomogenize()のコメントを参照してください。
//		inhomogenize(render_work->clip_vertices[0], n_vertices); /* 非同次座標化 */
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
		inhomogenize(_clip_vertices[0], n_vertices); /* 非同次座標化 */
//}}2008/04/05,2008/04/06:変更

		/* 表裏判定を行います。
		 * 右回り(=表)記述なので、左回り(=裏)なら描きません。
		 */
//{{2008/04/05,2008/04/06:変更
//		xN0 = fsub(render_work->clip_vertices[0][0].x, render_work->clip_vertices[0][n_vertices - 1].x);
//		yN0 = fsub(render_work->clip_vertices[0][0].y, render_work->clip_vertices[0][n_vertices - 1].y);
//		x01 = fsub(render_work->clip_vertices[0][1].x, render_work->clip_vertices[0][             0].x);
//		y01 = fsub(render_work->clip_vertices[0][1].y, render_work->clip_vertices[0][             0].y);
//		s = fsub(fmul(xN0, y01), fmul(x01, yN0));
//		if(ficom(s, <=, 0)) continue;
//↓* Sat Apr 05 10:56:32 JST 2008 Naoyuki Sawa
//↓- render_mesh()の中の、表裏判定を行う処理を、cull_back()に分離して、高速化しました。
//↓  詳細は、cull_back()のコメントを参照してください。
//		if(cull_back(render_work->clip_vertices[0], n_vertices)) continue;
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
		if(cull_back(_clip_vertices[0], n_vertices)) continue;
//}}2008/04/05,2008/04/06:変更

		/* 平面クリッピングを行います。 */
//{{2008/04/04,2008/04/06:変更
//		n_vertices = clip_xy(render, n_vertices);
//↓* Fri Apr 04 00:06:18 JST 2008 Naoyuki Sawa
//↓- clip_xy()の仕様変更に伴って、呼び出し引数を変更しました。
//		n_vertices = clip_xy(render, render_work->clip_vertices[0], render_work->clip_vertices[1], n_vertices);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
		n_vertices = clip_xy(render, _clip_vertices[0], _clip_vertices[1], n_vertices);
//}}2008/04/04,2008/04/06:変更
		if(n_vertices < 3) continue;

		/* 描画用頂点リストを組み立てます。 */
//{{2008/04/03,2008/04/06:変更
//		for(j = 0, clip_vertex = render_work->clip_vertices[0], rend_vertex = render_work->rend_vertices;
//		    j < n_vertices;
//		    j++, clip_vertex++, rend_vertex++) {
//			rend_vertex->x = fist(clip_vertex->x);
//			rend_vertex->y = fist(clip_vertex->y);
//			rend_vertex->z = fist(fimul(fdiv(fmul(fsub(clip_vertex->z, render->front), render->back  ),
//							 fmul(fsub(render->back  , render->front), clip_vertex->z)), 0xffff)); /* 8.8bit */
//			rend_vertex->s = fist(fimul(clip_vertex->s, 1 << 8)); /* 8.8bit */
//			rend_vertex->t = fist(fimul(clip_vertex->t, 1 << 8)); /* 8.8bit */
//		}
//↓* Thu Apr 03 15:14:06 JST 2008 Naoyuki Sawa
//↓- render_mesh()の中の、描画用頂点リストを組み立てる処理を、init_vertices()に分離して、高速化しました。
//↓  詳細は、init_vertices()のコメントを参照してください。
//		init_vertices(render, render_work->rend_vertices, render_work->clip_vertices[0], n_vertices);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
		init_vertices(render, _rend_vertices, _clip_vertices[0], n_vertices);
//}}2008/04/03,2008/04/06:変更

//{{2008/01/13:変更
//		/* プリミティブを描きます。 */
//		draw_primitive_wz(render, n_vertices, face->c);
//}}2008/01/13:変更{{
		/* プリミティブを描きます。 */
		param.fore_color = face->c      & 15;
		param.back_color = face->c >> 4 & 15;
		//draw_primitive_wz(render_work->rend_vertices, n_vertices, &param, draw_scanline);
		//↓2008/01/14:draw_primitive_woz()とdraw_primitive_wz()の統合に追従
//{{2008/04/06:変更
//		draw_primitive(render_work->rend_vertices, n_vertices, &param, draw_scanline);
//↓* Sun Apr 06 21:06:57 JST 2008 Naoyuki Sawa
//↓- 描画用一時ワークの配置変更に追従しました。
		draw_primitive(_rend_vertices, n_vertices, &param, draw_scanline);
//}}2008/04/06:変更
//}}2008/01/13:変更
	}
}

/****************************************************************************
 *	Ｚコンペアあり
 ****************************************************************************/

//{{2008/01/13:削除->framdp1.cへ移動しました。
///* プリミティブを描きます。
// * [in]
// *	render				レンダラーへのポインタ。
// *	n_vertices			頂点の数。
// *	render_work->rend_vertices	頂点リスト。(グローバル変数)
// *	color				カラー。
// */
//static void
//draw_primitive_wz(RENDER* render, int n_vertices, int color)
//{
//	struct {
//		int iv;		/* 現在の辺の終点インデクス            */
//		int ny;		/* 現在の辺の高さの変位                */
//		int n;		/* 現在の辺の残りライン数              */
//		RENDVERTEX cur;	/* 現在値                              */
//		DDA x, z;	/* 頂点座標DDA(yは左右共通で1づつ進む) */
//		DDA s, t;	/* テクスチャ座標DDA                   */
//	} sides[2], *side;
//
//	DSLPARAM param;
//	int i, itop, y, top, bottom;
//	DSLPROC draw_scanline;
//	RENDVERTEX *v, *v0, *v1;
//
//	/* スキャンライン描画パラメータを初期化します。 */
//	draw_scanline = select_draw_scanline_wz(&param, render, color);
//
//	/* 上下端の位置と、上端の頂点を求めます。 */
//	itop = 0;
//	top = bottom = render_work->rend_vertices[0].y;
//	for(i = 1; i < n_vertices; i++) {
//		v = &render_work->rend_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 = &render_work->rend_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 = &render_work->rend_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);
//				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)
//			DRAW_PRIMITIVE_STEP(s)
//			DRAW_PRIMITIVE_STEP(t)
//		}
//	}
//	/* 下端のスキャンラインは描きません。 */
//}
//}}2008/01/13:削除->framdp1.cへ移動しました。

//{{2008/01/13:削除->render_mesh()内に処理を含めました。
///* スキャンライン描画パラメータを初期化します。
// * [in]
// *	param		初期化するパラメータ構造体へのポインタ。
// *	render		レンダラーへのポインタ。
// * [out]
// *	戻り値		スキャンライン描画関数へのポインタ。
// * [note]
// *	* 使わないメンバは初期化しません。（ゼロクリアもしません）
// */
//static DSLPROC
//select_draw_scanline_wz(DSLPARAM* param, RENDER* render, int color)
//{
//	SURFACE* surface = render->surface;
//	TEXTURE* texture = render->context->texture;
//	DSLPROC draw_scanline;
//
//	/* サーフェイス */
//	param->surface_w     = surface->w;
//	param->surface_h     = surface->h;
//	param->surface_vbuff = surface->vbuff;
//	param->surface_zbuff = surface->zbuff;
//
//	/* テクスチャ・カラー */
//	if(texture == NULL) {
//		//param->texture_w    = 使わない
//		//param->texture_h    = 使わない
//		//param->texture_buf  = 使わない
//		//param->texture_mask = 使わない
////{{2008/01/01:変更
////		param->back_color     = color & 15;
////		//param->fore_color   = 使わない
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
////↓ - テクスチャ無しポリゴンの描画色を、背景色でなく、前景色に変更しました。
////↓   前述の入れ替えと相殺して、下位ニブルで描画色を指定することに変わりありません。
//		param->fore_color     = color & 15;
//		//param->back_color   = 使わない
////}}2008/01/01:変更
//		draw_scanline = draw_scanline_wz_color;
//	} else {
//		param->texture_w   = texture->header.w;
//		param->texture_h   = texture->header.h;
//		param->texture_buf = texture->buf;
//		switch(texture->header.bpp) {
//		case 1:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_wz_1bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_wz_1bitmask;
//			}
////{{2008/01/01:変更
////			param->back_color = color      & 15;
////			param->fore_color = color >> 4 & 15;
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
//			param->fore_color = color      & 15;
//			param->back_color = color >> 4 & 15;
////}}2008/01/01:変更
//			break;
//		default/*2*/:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_wz_2bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_wz_2bitmask;
//			}
//			//param->fore_color = 使わない
//			//param->back_color = 使わない
//			break;
//		}
//	}
//
//	return draw_scanline;
//}
//}}2008/01/13:削除->render_mesh()内に処理を含めました。

/* * Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa            */
/* - draw_scanline_wz_color()はframds11.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa           */
/* - draw_scanline_wz_1bit()はframds12.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa               */
/* - draw_scanline_wz_1bitmask()はframds13.cへ移動しました。 */

/* * Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa           */
/* - draw_scanline_wz_2bit()はframds14.cへ移動しました。 */

/* * Tue Apr  2 05:53:00 JST 2003 Naoyuki Sawa               */
/* - draw_scanline_wz_2bitmask()はframds15.cへ移動しました。 */

/****************************************************************************
 *	Ｚコンペアなし
 ****************************************************************************/

//{{2008/01/13:削除->framdp2.cへ移動しました。
///* プリミティブを描きます。
// * [in]
// *	render				レンダラーへのポインタ。
// *	n_vertices			頂点の数。
// *	render_work->rend_vertices	頂点リスト。(グローバル変数)
// *	color				カラー。
// */
//static void
//draw_primitive_woz(RENDER* render, int n_vertices, int color)
//{
//	struct {
//		int iv;		/* 現在の辺の終点インデクス            */
//		int ny;		/* 現在の辺の高さの変位                */
//		int n;		/* 現在の辺の残りライン数              */
//		RENDVERTEX cur;	/* 現在値                              */
//		DDA x;		/* 頂点座標DDA(yは左右共通で1づつ進む) */
//		DDA s, t;	/* テクスチャ座標DDA                   */
//	} sides[2], *side;
//
//	int i, itop, y, top, bottom;
//	RENDVERTEX *v, *v0, *v1;
//	DSLPROC draw_scanline;
//	DSLPARAM param;
//
//	/* スキャンライン描画パラメータを初期化します。 */
//	draw_scanline = select_draw_scanline_woz(&param, render, color);
//
//	/* 上下端の位置と、上端の頂点を求めます。 */
//	itop = 0;
//	top = bottom = render_work->rend_vertices[0].y;
//	for(i = 1; i < n_vertices; i++) {
//		v = &render_work->rend_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 = &render_work->rend_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 = &render_work->rend_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(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(s)
//			DRAW_PRIMITIVE_STEP(t)
//		}
//	}
//	/* 下端のスキャンラインは描きません。 */
//}
//}}2008/01/13:削除->framdp2.cへ移動しました。

//{{2008/01/13:削除->render_polygon()内に処理を含めました。
///* スキャンライン描画パラメータを初期化します。
// * [in]
// *	param		初期化するパラメータ構造体へのポインタ。
// *	render		レンダラーへのポインタ。
// * [out]
// *	戻り値		スキャンライン描画関数へのポインタ。
// * [note]
// *	* 使わないメンバは初期化しません。（ゼロクリアもしません）
// */
//static DSLPROC
//select_draw_scanline_woz(DSLPARAM* param, RENDER* render, int color)
//{
//	SURFACE* surface = render->surface;
//	TEXTURE* texture = render->context->texture;
//	DSLPROC draw_scanline;
//
//	/* サーフェイス */
//	param->surface_w       = surface->w;
//	param->surface_h       = surface->h;
//	param->surface_vbuff   = surface->vbuff;
//	//param->surface_zbuff = 使わない
//
//	/* テクスチャ・カラー */
//	if(texture == NULL) {
//		//param->texture_w    = 使わない
//		//param->texture_h    = 使わない
//		//param->texture_buf  = 使わない
//		//param->texture_mask = 使わない
////{{2008/01/01:変更
////		param->back_color     = color & 15;
////		//param->fore_color   = 使わない
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
////↓ - テクスチャ無しポリゴンの描画色を、背景色でなく、前景色に変更しました。
////↓   前述の入れ替えと相殺して、下位ニブルで描画色を指定することに変わりありません。
//		param->fore_color     = color & 15;
//		//param->back_color   = 使わない
////}}2008/01/01:変更
//		draw_scanline = draw_scanline_woz_color;
//	} else {
//		param->texture_w   = texture->header.w;
//		param->texture_h   = texture->header.h;
//		param->texture_buf = texture->buf;
//		switch(texture->header.bpp) {
//		case 1:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_woz_1bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_woz_1bitmask;
//			}
////{{2008/01/01:変更
////			param->back_color = color      & 15;
////			param->fore_color = color >> 4 & 15;
////↓ * Tue Jan 01 21:30:56 JST 2008 Naoyuki Sawa
////↓ - colorパラメータの下位ニブルを前景色、上位ニブルを背景色に入れ替えました。
////↓   必須では有りませんが、DRW_CLR()のパラメータ順と一致させて、解り易くするためです。
//			param->fore_color = color      & 15;
//			param->back_color = color >> 4 & 15;
////}}2008/01/01:変更
//			break;
//		default/*2*/:
//			if(!texture->header.mask) {
//				//param->texture_mask = 使わない
//				draw_scanline = draw_scanline_woz_2bit;
//			} else {
//				param->texture_mask = texture->mask;
//				draw_scanline = draw_scanline_woz_2bitmask;
//			}
//			//param->fore_color = 使わない
//			//param->back_color = 使わない
//			break;
//		}
//	}
//
//	return draw_scanline;
//}
//}}2008/01/13:削除->render_polygon()内に処理を含めました。

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa             */
/* - draw_scanline_woz_color()はframds21.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa            */
/* - draw_scanline_woz_1bit()はframds22.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa                */
/* - draw_scanline_woz_1bitmask()はframds23.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa            */
/* - draw_scanline_woz_2bit()はframds24.cへ移動しました。 */

/* * Mon Aug  2 05:00:00 JST 2004 Naoyuki Sawa                */
/* - draw_scanline_woz_2bitmask()はframds25.cへ移動しました。 */

/* * Sat Apr 11 05:29:02 JST 2009 Naoyuki Sawa
 * - render_mesh()とrender_polygon()の中で行っていた処理の一部を、select_draw_scanline()に分離しました。
 *   2008/01/13よりも以前には、select_draw_scanline_wz()とselect_draw_scanline_woz()で行っていた処理を、
 *   いったんrender_mesh()とrender_polygon()の中に含めていたのですが、それらの処理を再度分離して、統合したものです。
 * - この変更を行った理由は、Z比較無しのrender_mesh()を利用可能とするためです。
 *   これまでは、render_mesh()はZ比較有り、render_polygon()はZ比較無しと、固定的にDSLPROCを選択していました。
 *   しかし、投影シャドウ用のテクスチャレンダリングを行う場合などに、Z比較無しでrender_mesh()を実行したい場合があります。
 *   そこで、select_draw_scanline_wz()とselect_draw_scanline_woz()の中の、DSLPROC選択処理を統合し、
 *   surfaceにZバッファが設定されているか否かによって、Z比較有り/無しのDSLPROCを選択するようにしました。
 *   今後は、surfaceにZバッファを設定せずにrender_mesh()を呼べば、Z比較無しでrender_mesh()を実行することが可能です。
 * - 逆に、surfaceにZバッファを設定してrender_polygon()を呼ぶと、Z比較有りのrender_polygon()になってしまいます。
 *   render_polygon()の実装は、全ての頂点をz=0に設定して描画を行っているので、Z比較有りでも描画結果は同じになるのですが、
 *   無意味なZ比較であり、速度低下を引き起こすことになってしまいます。
 *   通常、Zバッファを設定したsurfaceに対してrender_polygon()を使用することは少ないと思いますが、注意してください。
 *   たとえば、Zバッファを設定したsurfaceに対して3D描画を行った後に、回転スプライトを描画した場合などがそれに相当します。
 */
static DSLPROC
select_draw_scanline(RENDER* render, DSLPARAM* param)
{
	static const DSLPROC proc_table[/*i*/] = {
		draw_scanline_woz_color,	/* 0 */
		draw_scanline_woz_1bit,		/* 1 */
		draw_scanline_woz_2bit,		/* 2 */
		draw_scanline_woz_1bitmask,	/* 3 */
		draw_scanline_woz_2bitmask,	/* 4 */
		draw_scanline_wz_color,		/* 5 */
		draw_scanline_wz_1bit,		/* 6 */
		draw_scanline_wz_2bit,		/* 7 */
		draw_scanline_wz_1bitmask,	/* 8 */
		draw_scanline_wz_2bitmask,	/* 9 */
	};
	int i = 0;
	SURFACE* surface = render->surface;
	TEXTURE* texture = render->context->texture;

	param->surface_w = surface->w;
	param->surface_h = surface->h;
	param->surface_vbuff = surface->vbuff;
	if(surface->zbuff) {
		i += 5;
		param->surface_zbuff = surface->zbuff;
	}
	if(texture) {
		i += texture->header.bpp; /* 1 or 2 */
		param->texture_w = texture->header.w;
		param->texture_h = texture->header.h;
		param->texture_buf = texture->buf;
		if(texture->header.mask) {
			i += 2;
			param->texture_mask = texture->mask;
		}
	}

	return proc_table[i];
}

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

void
render_edge(RENDER* render, fixed threshold)
{
	static struct {
		fixed front;			/* 前面クリップ距離の変化を検出するため */
		fixed back;			/* 背面クリップ距離の変化を検出するため */
		fixed threshold;		/* エッジ閾値の変化を検出するため。実距離において、隣のピクセルとの間にこのエッジ閾値以上の差があれば、エッジを描画する */
//{{2009/05/08:修正
//		unsigned char threshold1;	/* エッジ閾値(実距離)を、[0..255]のスケールで表した値。以下のtable[]で変換した値で比較するために使う */
//		unsigned char threshold2;	/* threshold1の二倍の値。±の差を同時に比較するために使う */
//↓ * Fri May 08 00:00:02 JST 2009 Naoyuki Sawa
//↓ - render_edge()内部構造体メンバの、threshold1とthreshold2を、unsigned char→shortに変更しました。
//↓   render_edge()関数の引数thresholdに、背面クリッピング距離に近いような大きな値を指定すると、threshold1が128以上の値となり、
//↓   threshold1の二倍の値であるthreshold2が、unsigned charをオーバーフローしてしまうからです。
//↓   現実には、引数thresholdにそのような大きな値を指定することはまず無いと思うのですが、念のために修正しておくことにしました。
		short threshold1;		/* エッジ閾値(実距離)を、[0..255]のスケールで表した値。以下のtable[]で変換した値で比較するために使う */
		short threshold2;		/* threshold1の二倍の値。±の差を同時に比較するために使う */
//}}2009/05/08:修正
		unsigned char table[256];	/* Zバッファ値(実距離に対してリニアでない)を、実距離に対してリニアにし、[front..back]の範囲で[0..255]に変換するテーブル */
	} *p;

	/* テーブル等のメモリをまだ確保していなければ、確保します。 */
	if(!p) {
		p = calloc(sizeof p[0], 1);
		if(!p) {
			DIE();
		}
	}

	/* 前面クリップ距離、背面クリップ距離、エッジ閾値が変化したら、テーブルを再計算します。 */
	if((p->front != render->front) || (p->back != render->back) || (p->threshold != threshold)) {
		int i;
		int x;
		double f;
		double b;
		double t;
		double z;

		/* 次回以降の、前面クリップ距離、背面クリップ距離、エッジ閾値の変化を検出するために、今回の値を記憶しておきます。 */
		p->front = render->front;
		p->back = render->back;
		p->threshold = threshold;

		/* 演算誤差を低減し、実装を簡単にするために、テーブルの計算は浮動小数点で行うことにします。
		 * - float演算を使うと、framflt1.oがリンクされてしまうので、double演算で行ことにします。
		 *   clipren3.cはfixed演算が前提で、float演算を使わないアプリケーションから使う場合、framflt1.oが無駄だからです。
		 *   double演算のライブラリモジュールは、clipprnt.oから常にリンクされているため、無駄が増えることにはなりません。
		 */
		f = (double)(int)render->front / FIXED_SCALE;	// fst()を使うとfloat演算が含まれるのでダメ!
		b = (double)(int)render->back / FIXED_SCALE;	// fst()を使うとfloat演算が含まれるのでダメ!
		t = (double)(int)threshold / FIXED_SCALE;	// fst()を使うとfloat演算が含まれるのでダメ!

		/* エッジ閾値(実距離)を、[front..back]の範囲でリニアに[0..255]にマップした値を計算します。 */
		x = t / (b - f) * 255.0;
//{{2009/05/09:変更
//		x = (x < 0) ? 0 : (x > 255) ? 255 : x;
//↓ * Sat May 09 13:21:42 JST 2009 Naoyuki Sawa
//↓ - 引数thresholdに小さな値が指定された場合でも、[0..255]にマップしたエッジ閾値(threshold1)は2以上とするよう修正しました。
//↓   もし、threshold1が0だと、すべてのピクセルにエッジが発生してしまいます。
//↓   もし、threshold1が1だと、わずかでも斜めの平面にエッジが発生してしまいます。
//↓   上記二点のような問題を避けるために、threshold1の最小値を2とすることにしました。
		x = (x < 2) ? 2 : (x > 255) ? 255 : x;
//}}2009/05/09:変更
		p->threshold1 = x;
		p->threshold2 = x + x;

		/* Zバッファ値[1..255]に対して... (Zバッファ値0は、テーブル値0に変換されることが自明なので、計算不要) */
		for(i = 1; i <= 255; i++) {
			/* init_vertices()の逆の計算を行って、Zバッファ値を、視点からの実距離に変換します。 */
			z = f / (1.0 - (i / 255.0) * (b - f) / b);
			/* 実距離を、[front..back]の範囲でリニアに[0..255]にマップした値を計算します。 */
			x = (z - f) / (b - f) * 255.0;
			x = (x < 0) ? 0 : (x > 255) ? 255 : x;
			p->table[i] = x;
			/* - 遠方ではZバッファ精度が下がるため、Zバッファ値ひとつ分の差で、エッジ閾値を超えてしまう可能性があります。
			 *   そうなった場合、かたむいたポリゴン上に誤ったエッジ描画が多発して、真っ黒になってしまうおそれれがあります。
			 * - そこで、上述の問題が発生する距離以遠では、Zバッファ値が違っても同じ距離に変換するよう、テーブルを調整します。→この2行の内容は、もう古くなりました。
			 *   遠方でエッジ描画が行われなくなる弊害がありますが、誤ったエッジ描画で真っ黒になっるよりはマシと考えました。   →{{2009/05/09:変更}}のコメントを参照。
			 * - 上述の対策を行っても、やはり、かたむきの大きいポリゴン上に、誤ったエッジ描画が発生してしまう場合があります。
			 *   かたむきの大きいポリゴンは、隣り合ったピクセル間で、Zバッファ値が大きく変化するためです。特に遠方で顕著です。
			 *   2009/05/06現在のところ、回避方法が思いつかず、未対策のままです。あまり目立たないので、このままでも良いかな…
			 */
			if((p->table[i] - p->table[i - 1]) >= p->threshold1) {	/* Zバッファ値ひとつ分の差で、エッジ閾値を超えた? */
//{{2009/05/09:変更
//				int j;
//				for(j = i; j <= 255; j++) {
//					p->table[j] = p->table[i - 1];		/* 残りのテーブル値を、同じ距離に変換するよう設定 */
//				}
//				break;						/* ここまで */
//↓ * Sat May 09 13:21:42 JST 2009 Naoyuki Sawa
//↓ - Zバッファ値ひとつ分の差で、エッジ閾値(threshold1)を超えた際、エッジ閾値未満の差分(threshold1-1)距離を格納することにしました。
//↓   これまでは、Zバッファ値ひとつ分の差で、エッジ閾値を超えたエントリ以降は、同じ距離を格納して、エッジ生成を打ち切っていました。
//↓   そのため、遠方でエッジ描画が行われなくなる弊害がありました。
//↓   今回の変更により、遠方でも「Zバッファ値が2以上変化した場合」は、「(threshold1-1)×2の距離差」となり、エッジ生成が行われます。
//↓   遠方での、エッジ描画が改善しました。
				p->table[i] = p->table[i - 1] + (p->threshold1 - 1);
//}}2009/05/09:変更
			}
		}

//{{2009/05/09:追加
		/* * Sun May 10 16:19:02 JST 2009 Naoyuki Sawa
		 * - ±の差を同時に比較するためには、threshold1とthreshold2を1小さい値にしておくことが必要でした。
		 *   なぜ1小さい値にしておくことが必要なのかを、距離の閾値が2(最小値)の場合を例に挙げて、説明します。
		 * - 距離の閾値が2ならば、距離の差が-2以下、または、+2以上ならばエッジを描きます。
		 *   これまでは、距離の閾値が2の場合に、threshold1=2、threshold2=4としていました。
		 *   距離の差を求めて、threshold1を加算し、符号無しで比較し、threshold2以上ならばエッジと判断します。
		 *	if((unsigned)(距離の差 + threshold1) >= (unsigned)threshold2) { エッジと判定; }
		 *   距離の差が+2側はこれで良いのですが、-2の場合に誤った判定で、エッジを見落としてしまっていました。
		 *	if((unsigned)(+2 + 2) >= (unsigned)4) { エッジと判定; } -> (4 >= 4) -> TRUE  エッジと判定
		 *	if((unsigned)(-2 + 2) >= (unsigned)4) { エッジと判定; } -> (0 >= 4) -> FALSE エッジを見落とし!!
		 * - 正しくは、距離の閾値が2の場合に、threshold1=1、threshold2=3としなければなりません。
		 *	if((unsigned)(+2 + 1) >= (unsigned)3) { エッジと判定; } -> (  3 >= 3) -> TRUE エッジと判定
		 *	if((unsigned)(-2 + 1) >= (unsigned)3) { エッジと判定; } -> (-1U >= 3) -> TRUE エッジと判定
		 * - 以上説明した理由により、threshold1とthreshold2を1小さい値にしておくことが必要です。
		 */
		p->threshold1--;
		p->threshold2--;
//}}2009/05/09:追加
	}

	/* ここより上は、初期化処理で、最初に一回(またはパラメータ変化時に)実行するだけなので、速度は重要で有りません。
	 * ここより下が、エッジ描画の本体処理で、速度が重要なので、アセンブラで最適化しやすいよう、別関数に分けました。
	 */
	{
		static void render_edge_sub(RENDER* render, unsigned threshold1, unsigned threshold2, const unsigned char table[/*256*/]);
		render_edge_sub(render, p->threshold1, p->threshold2, p->table);
	}
}

#ifdef REND_NOASM
static void render_edge_sub(RENDER* render, unsigned threshold1, unsigned threshold2, const unsigned char table[/*256*/]) {
	SURFACE* const surface = render->surface;		/* サーフェイス */
	      unsigned char* pv = surface->vbuff;		/* カラーバッファポインタ */
	const unsigned char* pz = surface->zbuff;		/* Zバッファポインタ */
	int const w = surface->w;				/* サーフェイスの幅 */
	int const w_1 = w - 1;					/* サーフェイスの幅 - 1 (1ピクセル右へ進んだZバッファポインタから、左下のピクセルを指すために使う) */
	int const lr = render->right - render->left;		/* クリッピング領域の幅 */
	int const lr_1 = lr - 1;				/* クリッピング領域の幅 - 1 (水平ループ回数として使う) */
	int const tb = render->bottom - render->top;		/* クリッピング領域の高さ */
	int const tb_1 = tb - 1;				/* クリッピング領域の高さ - 1 (垂直ループ回数として使う) */
	int const offset = w * render->top + render->left;	/* クリッピング領域の左上への、バッファポインタのオフセット */
	int const gap = w - lr;					/* [クリッピング領域の右端 + 1]から、次のラインのクリッピング領域の左端へのギャップ */
	int x;
	int y;
	unsigned a;
	unsigned b;

	/* バッファポインタを、クリッピング領域の左上へ進めます。 */
	pv += offset;
	pz += offset;

	/* 処理内容は、以下のとおりです。
	 * 1. クリッピング領域内の全ピクセルを、「(x,y) = [(left,top)-(right,bottom)]」と表現することにします。
	 *    ただし、上記はグリッド座標系であり、「rightの列」「bottomの行」のピクセルは含まないものとします。
	 * 2. エッジを描く対象とする全ピクセルは、[(left+1,top)-(right,bottom-1)]とします。
	 *    「leftの列」「bottom-2の行」には、(たとえ、実際にはエッジであったとしても)、エッジを描きません。
	 *    このため、画面端に絶対にエッジが描かれない部分が発生してしまいますが、ほぼ目立たないと思います。
	 * 3. エッジを描く対象とする各ピクセル(x,y)について、「Z(x,y)」、「Z(x-1,y)」、「Z(x,y+1)」を求めます。
	 *    Zとは、そのピクセルに対応するZバッファ値を、テープルを用いて、リニアな[0..255]に変換した値です。
	 *    「Z(x,y)」と「Z(x-1,y)」、または、「Z(x,y)」と「Z(x,y+1)」の差が閾値以上なら、エッジを描きます。
	 */
	y = tb_1;			/* 水平ループカウンタ (「bottom-2の行」はZ比較対象としてだけ用い、エッジ描画対象に含めないので「-1」) */
	do {
		/* 「Z(1,y)」との比較のために、「Z(0,y)」を求めておきます。(0,y)のピクセルは、エッジ描画対象としません。 */
		a = table[*pz++];	/* Zバッファポインタを、(1,y)へ進めます。 */
		pv++;			/* カラーバッファポインタを、(1,y)へ進めます。 */

		x = lr_1;		/* 垂直ループカウンタ (「leftの列」はZ比較対象としてだけ用い、エッジ描画対象に含めないので「-1」) */
		do {
			/* 「Z(x-1,y)」を、比較対象として保存します。 */
			b = a;
			/* 「Z(x,y)」を求めます。 */
			a = table[*pz++];			/* Zバッファポインタを、(x+1,y)へ進めます。 */
			/* 「Z(x,y)」と「Z(x-1,y)」の差が、閾値以上かを判断します。 */
			b = (b - a) + threshold1;
			if(b < threshold2) {
				/* 「Z(x,y)」と「Z(x-1,y)」の差が閾値未満ならば、比較対象を「Z(x,y+1)」に切り替えます。 */
				b = table[pz[w_1]];		/* Zバッファポインタは(x+1,y)へ進んでいるので、左下が元のピクセルの真下です。 */
				b = (b - a) + threshold1;
			}
			/* 「Z(x,y)」と「Z(x-1,y)」、または、「Z(x,y)」と「Z(x,y+1)」の差が閾値以上なら、エッジを描きます。 */
			if(b >= threshold2) {
				*pv = 3;
			}
			pv++;					/* カラーバッファポインタを、(x+1,y)へ進めます。 */
		} while(--x);

		/* バッファポインタを、次のラインのクリッピング領域の左端へ進めます。 */
		pv += gap;
		pz += gap;
	} while(--y);
}
#else /*REND_NOASM*/
asm("
		.align		1
		.global		render_edge_sub
render_edge_sub:
		pushn		%r3
		ld.w		%r0, %r12			;// %r0  := render
		ld.w		%r1, %r13			;// %r1  := threshold1
		ld.w		%r2, %r14			;// %r2  := threshold2
		ld.w		%r3, %r15			;// %r3  := table
		xld.w		%r12, 0x0e00			;// %r12 := FRAM4
		xld.w		%r13, render_edge_sub_LOOP_Y	;// %r13 := START
		xld.w		%r14, render_edge_sub_END	;// %r14 := END
		xcall.d		memcpy				;// %r10 := FRAM4 = memcpy(FRAM4, LOOP_Y, END - LOOP_Y)
		sub		%r14, %r13			;// %r14 :=                               END - LOOP_Y		*delay*
		;//
		ld.w		%r9, [%r0]+			;// %r9  := surface = render->surface
		ld.h		%r4, [%r9]			;// %r4  := w = surface->w
		add		%r9, 4
		ld.w		%r5, [%r9]+			;// %r5  := pv = surface->vbuff
		ld.w		%r6, [%r9]			;// %r6  := pz = surface->zbuff
		ld.h		%r7, [%r0]+			;// %r7  := left = render->left
		ld.h		%r11, [%r0]+			;// %r11 := top = render->top
		ld.h		%r12, [%r0]+			;// %r12 := right = render->right
		ld.h		%r13, [%r0]			;// %r13 := bottom = render->bottom
		;//
		mlt.h		%r4, %r11			;// %alr :=                w * top
		ld.w		%r9, %alr			;// %r9  :=                w * top
		add		%r9, %r7			;// %r9  :=       offset = w * top + left
		add		%r5, %r9			;// %r5  := pv += offset
		add		%r6, %r9			;// %r6  := pz += offset
		;//
		sub		%r12, %r7			;// %r12 := lr   = right - left
		sub		%r12, 1				;// %r12 := lr_1 = lr - 1
		sub		%r13, %r11			;// %r13 :=     tb   = bottom - top
		sub		%r13, 1				;// %r13 := y = tb_1 = tb - 1
		;//
		sub		%r4, 1				;// %r4  := w_1 = w - 1
		ld.w		%r14, %r4			;// %r14 :=                                     w_1
		sub		%r14, %r12			;// %r14 := gap = w - lr = (w - 1) - (lr - 1) = w_1 - lr_1
		;//
		ld.w		%r15, 3				;// %r15 := 3
		jp		%r10				;// goto FRAM4 (jp.d %rbは不可)
;//{{ここからFRAM4領域へ転送
render_edge_sub_LOOP_Y:						;// do {
		ld.ub		%r0, [%r6]+			;//   %r0  :=           *pz++
		add		%r0, %r3			;//   %r0  :=    &table[*pz++]
		ld.ub		%r0, [%r0]			;//   %r0  := a = table[*pz++]
		add		%r5, 1				;//   %r5  := pv++
		;//
		ld.w		%r7, %r12			;//   %r7 := x = lr_1
render_edge_sub_LOOP_X:						;//   do {
		ld.w		%r10, %r0			;//     %r10 := b = a
		;// %r0  := a
		;// %r1  := threshold1
		;// %r2  := threshold2
		;// %r3  := table
		;// %r4  := w_1
		;// %r5  := pv
		;// %r6  := pz
		;// %r7  := x
		;// %r10 := b
		;// %r12 := lr_1
		;// %r13 := y
		;// %r14 := gap
		;// %r15 := 3
		ld.ub		%r0, [%r6]+			;//     %r0  :=           *pz++
		add		%r0, %r3			;//     %r0  :=    &table[*pz++]
		ld.ub		%r0, [%r0]			;//     %r0  := a = table[*pz++]
		sub		%r10, %r0			;//     %r10 :=      b - a					!INTERLOCK!
		add		%r10, %r1			;//     %r10 := b = (b - a) + threshold1
		cmp		%r10, %r2			;//     if(b < threshold2)
		jruge		8
		 add		%r6, %r4			;//       %r2  :=          &pz[w_1]
		 ld.ub		%r10, [%r6]			;//       %r10 :=           pz[w_1]
		 sub		%r6, %r4			;//       %r2  :=           pz					*anti-interlock*
		 add		%r10, %r3			;//       %r10 :=    &table[pz[w_1]]
		 ld.ub		%r10, [%r10]			;//       %r10 := b = table[pz[w_1]]
		 sub		%r10, %r0			;//       %r10 :=      b - a					!INTERLOCK!
		 add		%r10, %r1			;//       %r10 := b = (b - a) + threshold1
		cmp		%r10, %r2			;//     if(b >= threshold2)
		jrult.d		3
		 sub		%r7, 1				;//     %r10 := x--						*delay*
		 ld.b		[%r5], %r15			;//       *pv = 3
		jrne.d		render_edge_sub_LOOP_X		;//   } while(x)
		add		%r5, 1				;//     %r5  := pv++						*delay*
		;//
		add		%r5, %r14			;//   %r5  := pv += gap
		sub		%r13, 1				;//   %r13 := y--
		jrne.d		render_edge_sub_LOOP_Y		;// } while(y)
		add		%r6, %r14			;//   %r6  := pz += gap						*delay*
		;//
		popn		%r3
		ret
render_edge_sub_END:
;//}}ここまでFRAM4領域へ転送
");
#endif /*REND_NOASM*/

