/*	
 *	clipren2.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2010 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()に分割。
 *	* Sat Apr 24 06:00:00 JST 2004 Naoyuki Sawa
 *	- texture_table_init()を追加しました。
 *	* Mon Jun 7 10:43:00 JST 2004 Naoyuki Sawa
 *	- texture_init()にて、4bitデータmask付き、mask無しも有効と見なすよう変更しました。
 *	* Mon Dec 20 06:36:00 JST 2004 Naoyuki Sawa
 *	- render_line()、render_dashes()のDDA処理を僅かに高速化しました。
 *	* Mon Dec 20 19:13:00 JST 2004 Naoyuki Sawa
 *	- render_line()をアセンブラで高速化し、framline.sへ移動しました。
 *	* Wed May  3 16:40:00 JST 2006 Naoyuki Sawa
 *	- render_ellipse()、render_ellipse_fill()を追加しました。
 *	* Tue May 16 17:10:00 JST 2006 Naoyuki Sawa
 *	- render_dashes()を16階調モードでも利用できるよう、透明色の定義を変更しました。
 *	  これまでは4以上を透明色として扱っていましたが、今後は16以上を透明色とします。
 *	* Sun Jul 09 22:53:05 JST 2006 Naoyuki Sawa
 *	- render_rectangle_dark()を追加しました。
 *	- 2006/07/09まで、render_rectangle_fill()の内部に書いていたロジックを、
 *	  render_rectangle_dark()からも利用するため、関数化して取り出しました。
 *	  render_rectangle_fill(),render_rectangle_dark()から共通に利用します。
 *	- ※TODO: render_ellipse_dark()も追加したいところですが、現状では難しいです。
 *	  一見、render_ellipse_fill()が呼び出しているrender_rectangle_fill()の代りに、
 *	  render_rectangle_dark()を呼び出すよう場合分けするだけで済むように見えますが、
 *	  2006/07/09現在のrender_ellipse_fill()の実装は、同じピクセルを何度も重ね塗りしてしまっています。
 *	  この方法のままでrender_ellipse_dark()を実装すると、同じピクセルを何度も輝度変更してしまいます。
 *	  実際には重ね塗りを行う必要は無く、render_ellipse_fill()の実装を簡単にするためだけの理由なので、
 *	  重ね塗りしないようにアルゴリズムを変更すれば、render_ellipse_dark()も実装できるのですが、
 *	  そのためには、render_ellipse()とrender_rectangle_fill()のアルゴリズムを共通化できなくなります。
 *	  2006/07/09現在まだrender_ellipse_dark()は必要ないので、必要になったときに検討することにします。
 *	* Sat Dec 09 00:21:59 JST 2006 Naoyuki Sawa
 *	- render_rectangle()について、高速化失敗のコメントを追記。
 *	  プログラムは変更ありません。
 *	* Wed Jan 10 14:39:19 JST 2007 Naoyuki Sawa
 *	- surface_stretch()を追加しました。
 *	* Wed Jan 10 23:49:58 JST 2007 Naoyuki Sawa
 *	- surface_stretch()のアルゴリズム説明を追記しました。
 *	* Mon Mar 12 00:13:49 JST 2007 Naoyuki Sawa
 *	- render_rectangle_dark()→render_rectangle_shadow()に名前変更しました。
 *	* Wed Oct 17 12:30:13 JST 2007 Naoyuki Sawa
 *	- surface_stretch()をASM化しました。
 *	  使用方法の注意点は、FRAM4領域を使用しているAPI(pceLCDTrans(),pceFileWriteSct()等)と同様です。
 *	  詳細は、ASM版surface_stretch()のコード上のコメントを参照してください。
 *	* Wed Oct 17 16:54:35 JST 2007 Naoyuki Sawa
 *	- ASM版surface_stretch()を高速化しました。
 *	* Wed Oct 17 19:53:44 JST 2007 Naoyuki Sawa
 *	- ASM版surface_stretch()をさらに高速化しました。
 *	* Thu Oct 25 12:18:23 JST 2007 Naoyuki Sawa
 *	- surface_clear()を、ext一命令分だけ節約しました。
 *	  実行結果は同じです。ほとんど、気分の問題です(^^;
 *	* Thu Nov 01 15:46:12 JST 2007 Naoyuki Sawa
 *	- render_point()をASM化しました。
 *	- surface_point()をASM化しました。
 *	* Sun Nov 04 13:05:10 JST 2007 Naoyuki Sawa
 *	- surface_stretch()を拡大フィルタに対応しました。
 *	* Mon Nov 05 15:54:07 JST 2007 Naoyuki Sawa
 *	- soft_focus()を追加しました。
 *	* Fri Dec 28 20:39:14 JST 2007 Naoyuki Sawa
 *	- render_rectangle_clip()をアセンブラ化しました。
 *	- render_rectangle_clip()をglobalに公開しました。
 *	* Sat Dec 29 02:57:41 JST 2007 Naoyuki Sawa
 *	- render_rectangle_shadow()をアセンブラ化しました。
 *	* Thu Jan 10 12:43:35 JST 2008 Naoyuki Sawa
 *	- ASM版surface_stretch()のコメントを追記しました。(「2008/01/10追記」で検索してください。)
 *	  コードには変更有りません。
 *	* Thu Apr 16 00:14:17 JST 2009 Naoyuki Sawa
 *	- texture_copy()を追加し、同時にASM化しました。。
 *	* Sat Apr 18 14:43:08 JST 2009 Naoyuki Sawa
 *	- texture_copy()の仕様を変更しました。
 *	  これまでは、仮想VRAM全体を、同じサイズのテクスチャ全体へ取り込むことしかできませんでしたが、
 *	  今回の変更により、サーフェイスの一部を、テクスチャ全体へ取り込むことしかできるようになりました。
 *	* Mon Apr 20 00:00:08 JST 2009 Naoyuki Sawa
 *	- texture_copy()の仕様を変更しました。
 *	  これまでは、マスク無しテクスチャへの取り込み専用でしたが、今回より、マスク有りにも対応しました。
 *	  テクスチャのマスク有無を判断して、自動的に処理を変えます。
 *	  詳しい内容は、texture_copy()のコメントを参照してください。
 *	* Sat Apr 03 15:04:28 JST 2010 Naoyuki Sawa
 *	- surface_move()を追加し、同時にASM化しました。。
 *	* Sun Apr 25 23:37:45 JST 2010 Naoyuki Sawa
 *	- texture_point()とtexture_pset()を追加し、同時にASM化しました。。
 */
#include "clip.h"

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

TEXTURE*
texture_init(TEXTURE* texture, const void* data)
{
	TEXTUREHEADER* header;

	header = (TEXTUREHEADER*)data;

	/* ヘッダ情報を検査します。
	 * * P/ECEの仕様に「1bitデータmask付き」(bpp=1,mask=1)は存在しませんが、
	 *   本ライブラリでは、有効なテクスチャ形式としてとして利用可能なので、
	 *   エラーにはしません。
	 * * テクセル座標として指定可能なのは0～255に制限されますが、
	 *   テクスチャ自体のサイズは256ドット以上でも構いません。
	 *   従って、ここではサイズの上限は検査していません。
	 * * 2004/06/07
	 *   4bitデータmask付き・mask無し(独自拡張形式)も有効としました。
	 */
	if(strncmp((char*)&header->head, "PMBP", 4) != 0) DIE();
	if(header->bpp != 1 && header->bpp != 2 && header->bpp != 4) DIE();
	if(header->mask != 0 && header->mask != 1) DIE();
	if(header->w < 8 || (header->w & 7) != 0) DIE();
	/* if(header->h < 8 || (header->h & 7) != 0) DIE(); -> 修正：縦サイズは任意でした。 */

	/* テクスチャヘッダとポインタを格納します。 */
	memset(texture, 0, sizeof(TEXTURE));
	texture->header = *header;
	texture->buf  = (unsigned char*)(header + 1);
	if(header->mask) {
		texture->mask = (unsigned char*)(header + 1) +
				header->w * header->h / (8 / header->bpp);
	}

	return texture;
}

void
texture_table_init(TEXTURE texture[], int texture_count, ...)
{
	va_list ap;
	const void* data;

	va_start(ap, texture_count);
	while(texture_count--) {
		data = va_arg(ap, const void*);
		texture_init(texture++, data);
	}
	va_end(ap);
}

void
surface_clear(SURFACE* surface, int color)
{
	int n;

	n = surface->w * surface->h;
	if(surface->vbuff != NULL) memset(surface->vbuff, color, n);
	//if(surface->zbuff != NULL) memset(surface->zbuff, 0xFF, n);
	//↓{{2007/10/25:ext一命令分の節約。実行結果は同じです。}}
	if(surface->zbuff != NULL) memset(surface->zbuff, -1, n);
}

//{{2007/11/01:ASM化に伴い削除
//int
//surface_point(SURFACE* surface, int x, int y)
//{
//	return 0 <= x && x <= surface->w - 1 &&
//	       0 <= y && y <= surface->h - 1 ?
//		surface->vbuff[surface->w * y + x] : -1;
//}
//}}2007/11/01:ASM化に伴い削除
//↓ * Thu Nov 01 15:46:12 JST 2007 Naoyuki Sawa
//↓ - surface_point()をASM化しました。
//↓   ASM版は、C版に較べて約18%高速です。
int surface_point(SURFACE* surface, int x, int y);
asm("
	.code
	.align	1
	.global	surface_point
surface_point:
;//	ld.w	%r10, -1		; %r10 = -1 (戻り値) ----------------------+
	ld.h	%r4, [%r12]+		; %r4  = w                                 |
	cmp	%r13, %r4		; if((unsigned)x >= (unsigned)w) return -1 |
	jruge.d	surface_point_RET	;                                          |
	ld.w	%r10, -1		; <----------------------------------------+	*delay*
	ld.h	%r9, [%r12]+		; %r9  = h
	cmp	%r14, %r9		; if((unsigned)y >= (unsigned)h) return -1
	jruge	surface_point_RET
	;
	ld.w	%r12, [%r12]		; %r12 = vbuff
	mlt.h	%r4, %r14		; %alr =         w * y
	ld.w	%r9, %alr		; %r9  =         w * y
	add	%r12, %r9		; %r12 = vbuff + w * y
	add	%r12, %r13		; %r12 = vbuff + w * y + x
	ld.ub	%r10, [%r12]		; %r10 = pixel (戻り値)
surface_point_RET:
	ret
");

/* * Wed Jan 10 23:49:58 JST 2007 Naoyuki Sawa
 * - 例を使って、アルゴリズムの概略を説明します。
 *   簡単のために、水平方向のみについて説明しますが、垂直方向も同様です。
 *   転送先1ピクセルにかかる範囲の複数の転送元ピクセルの、転送先1ピクセル内で占める面積によって、加重平均を行います。
 * 
 *	<例> 以下では、縮小の例を挙げていますが、拡大の場合も同じアルゴリズムです。
 *	
 *		転送元ピクセル数 sw = 4
 *		転送先ピクセル数 dw = 3
 *	
 *	①
 *		             x1=dw=3
 *		             |
 *		転送元 +-----+-----+-----+-----+ 
 *		       sx=0
 *		       dx=0
 *		転送先 +-------+-------+-------+
 *	②
 *		c1=0
 *		c2=0
 *		             x1=3
 *		             |
 *		転送元 +-----+-----+-----+-----+ 
 *		       sx=0
 *		       dx=0
 *		転送先 +-------+-------+-------+
 *		               |
 *		               x2=sw=4
 *	②
 *		c1=0
 *		c2=0
 *		             x1=3
 *		             |
 *		転送元 +-----+-----+-----+-----+ 
 *		       sx=0
 *		       dx=0
 *		転送先 +-------+-------+-------+
 *		             | |
 *		             | x2=4
 *		             x3=min(x1,x2)=3
 *	③
 *		c1+=x3                                                                                     c1=3
 *		c2+=x3*point(転送元,sx(=0)) ...仮に転送元の1ピクセル目が暗(2)だったとすると... c2+=3*2 ... c2=6
 *		             x1=3
 *		             |
 *		転送元 +-----+-----+-----+-----+ 
 *		       sx=0
 *		       dx=0
 *		転送先 +-------+-------+-------+
 *		             | |
 *		             | x2=4
 *		             x3=3
 *	④
 *		c1=3
 *		c2=6
 *		             x1=0
 *		             |
 *		転送元       +-----+-----+-----+ 
 *		       sx=0
 *		       dx=0
 *		転送先       --+-------+-------+
 *		               |
 *		               x2=1
 *		       ~~~~~~
 *		       処理した分を切り捨てる
 *	⑤
 *		c1=3
 *		c2=6
 *		                   x1+=dw=3
 *		                   |
 *		転送元       +-----+-----+-----+ 転送元の1ピクセル目を使い切ったので、次へ進める。
 *		             sx=1
 *		             dx=0(継続)
 *		転送先       --+-------+-------+
 *		               |
 *		               x2=1
 *	⑥
 *		c1=3
 *		c2=6
 *		               x3=min(x1,x2)=1
 *		               |   x1=3
 *		               |   |
 *		転送元       +-----+-----+-----+
 *		             sx=1
 *		             dx=0
 *		転送先       --+-------+-------+
 *		               |
 *		               x2=1
 *	⑦
 *		c1+=x3                                                                                     c1=4
 *		c2+=x3*point(転送元,sx(=1)) ...仮に転送元の2ピクセル目が黒(3)だったとすると... c2+=1*3 ... c2=9
 *		               x3=1
 *		               |   x1=3
 *		               |   |
 *		転送元       +-----+-----+-----+
 *		             sx=1
 *		             dx=0
 *		転送先       --+-------+-------+
 *		               |
 *		               x2=1
 *	⑧
 *		c1=4
 *		c2=9
 *		                   x1=2
 *		                   |
 *		転送元         ----+-----+-----+
 *		             sx=1
 *		             dx=0
 *		転送先         +-------+-------+
 *		               |
 *		               x2=0
 *		             ~~
 *		             処理した分を切り捨てる
 *	⑧
 *		c1=4
 *		c2=9
 *		                   x1=2
 *		                   |
 *		転送元         ----+-----+-----+
 *		               sx=1(継続)
 *		               dx=1
 *		転送先         +-------+-------+
 *		               |
 *		               x2=0
 *		転送先の1ピクセル目にかかる転送元ピクセルの加重合計が求まったので、平均を取る。
 *		加重平均 = (c2 + c1/2) / c1 = (9 + 2) / 4 = 11 / 4 = 2 ... 転送先1ピクセル目の出力値は、2(暗)と決定した。
 *		                 ~~~~
 *		            四捨五入のため
 *	
 */
//{{2007/10/17:ASM化に伴い削除
//void
//surface_stretch(SURFACE* dst, SURFACE* src)
//{
//	const unsigned dw = dst->w;
//	const unsigned dh = dst->h;
//	const unsigned sw = src->w;
//	const unsigned sh = src->h;
//	      unsigned char*       dp = dst->vbuff;
//	const unsigned char* const sp = src->vbuff;
//	//
//	unsigned dx;
//	unsigned dy;
//	unsigned sx;
//	unsigned sy;
//	unsigned x1;
//	unsigned x2;
//	unsigned x3;
//	unsigned y1;
//	unsigned y2;
//	unsigned y3;
//	unsigned xy;
//	unsigned c1;
//	unsigned c2;
//	unsigned save_sx;
//	unsigned save_sy;
//	unsigned save_x1;
//	unsigned save_y1;
//
//	/*{{転送先の垂直ループ(転送先1ピクセルづつ)*/
//	dy = dh;
//	sy = 0;
//	y1 = dh;
//	do {
//		/*{{転送先の水平ループ(転送先1ピクセルづつ)*/
//		dx = dw;
//		sx = 0;
//		x1 = dw;
//		save_sy = sy;
//		save_y1 = y1;
//		do {
//			sy = save_sy;
//			y1 = save_y1;
//			c1 = 0;
//			c2 = 0;
//			/*{{転送元の垂直ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
//			y2 = sh;
//			save_sx = sx;
//			save_x1 = x1;
//			for(;;) {
//				sx = save_sx;
//				x1 = save_x1;
//				y3 = y1 < y2 ? y1 : y2;
//				/*{{転送元の水平ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
//				x2 = sw;
//				for(;;) {
//					x3 = x1 < x2 ? x1 : x2;
//					xy = x3 * y3;
//					//{{アルゴリズムが正しいことの確認用
//					//if((sx >= sw) || (sy >= sh)) DIE();
//					//}}アルゴリズムが正しいことの確認用
//					c1 += xy;
//					c2 += xy * sp[sw * sy + sx];
//					if(!(x1 -= x3)) {
//						x1 += dw;
//						sx++;
//					}
//					if(!(x2 -= x3)) {
//						break;
//					}
//				}
//				/*}}転送元の水平ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
//				if(!(y1 -= y3)) {
//					y1 += dh;
//					sy++;
//				}
//				if(!(y2 -= y3)) {
//					break;
//				}
//			}
//			/*}}転送元の垂直ループ(転送先1ピクセルにかかる範囲の転送元ピクセルの分)*/
//			*dp++ = (c2 + (c1 / 2)) / c1;
//		} while(--dx);
//		/*}}転送先の水平ループ(転送先1ピクセルづつ)*/
//	} while(--dy);
//	/*}}転送先の垂直ループ(転送先1ピクセルづつ)*/
//}
//}}2007/10/17:ASM化に伴い削除
//↓* Wed Oct 17 12:30:13 JST 2007 Naoyuki Sawa
//↓- surface_stretch()をASM化しました。
//↓- ASM版surface_stretch()は、clipidiv.sで置き換えた__udivsi3を使うことを想定しています。
//↓  clipidiv.s版の__udivsi3は、%r10,%alr,%ahr,%psr以外のレジスタを破壊しません。
//↓  {{2008/01/10追記
//↓    clipidiv.s版の__udivsi3が、%r9も破壊するようになりました。clipidiv.sの本日のコメントを参照してください。
//↓    ASM版surface_stretch()は、%r9の保存を期待していなかったため、ASM版surface_stretch()の修正は不要でした。
//↓  }}2008/01/10追記
//↓  この特性を利用して、レジスタ割り当てを最適化しました。
//↓  置き換える前の、EPSONライブラリ版の__udivsi3は、%r4,%r5も破壊します。
//↓  EPSONライブラリ版の__udivsi3を使うと、ASM版surface_stretch()は動作しません。
//↓  もし今後、clipidiv.sを廃止したり変更する場合は、注意してください。
//↓- ASM版surface_stretch()は、高速化のために、ループ部分をFRAM4領域へ転送して実行します。
//↓  FRAM2領域に常駐するのでなく、呼ばれるたびに毎回FRAM4領域へ転送することにしました。
//↓  このように設計した理由は、以下のとおりです。
//↓  ①本関数は通常、フレーム当り一回しか使用しないので、FRAM4領域へ転送する負荷が小さい。
//↓  ②ループ部分の実行時間が充分に長く、FRAM4領域への転送時間が実行時間に占める割り合いが小さい。
//↓  ③FRAM2領域の消費量を節約できる。
//↓  FRAM4領域へ転送して実行しているので、FRAM4領域を利用する他の機能と併用できません。
//↓  たとえば、stack_switch()を経由して呼び出すことはできません。
//↓  また、割り込み中から呼び出してはいけません。(割り込み外でFRAM4を使用している可能性があるから)
//↓  使用方法の注意点は、FRAM4領域を使用しているAPI(pceLCDTrans(),pceFileWriteSct()等)と同様です。
//↓- 今回のASM化によって、大幅に高速化できましたが、まだリアルタイムゲームには速度不足のようです。
//↓  <例>	224x192 -> 128x88 縮小を一回実行する時間 (TURBO=-1,LDIRECT_INTERVAL=25)
//↓	C版		約 424 ミリ秒
//↓	ASM版		約 132 ミリ秒
//{{2007/10/17:高速化に伴い削除
//	------------	-----	------		--------------------------------------
//	レジスタ割当	定数?	変数名		各ループ固有部分からの、変数使用の有無
//	------------	-----	------		--------------------------------------
//	[%sp+0]		const	dw		LOOP_SX			LOOP_DY	
//	[%sp+2]		const	dh			LOOP_SY			OUTER
//	%r0			dp				LOOP_DX		OUTER
//	[%sp+8]			dx				LOOP_DX	LOOP_DY	
//	[%sp+12]		dy					LOOP_DY	OUTER
//	[%sp+4]		const	sw		LOOP_SX	LOOP_SY			
//	[%sp+6]		const	sh				LOOP_DX		
//	%r1		const	sp		LOOP_SX				
//	%r3			sx		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	
//	%r4			sy		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	OUTER
//	%r5			x1		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	
//	%r6			x2		LOOP_SX	LOOP_SY			
//	%r10			x3		LOOP_SX				
//	%r7			y1			LOOP_SY	LOOP_DX	LOOP_DY	OUTER
//	%r14			y2			LOOP_SY	LOOP_DX		
//	%r15			y3		LOOP_SX	LOOP_SY			
//	%r11			xy		LOOP_SX				
//	%r13			c1		LOOP_SX		LOOP_DX		
//	%r12			c2		LOOP_SX		LOOP_DX		
//	[%sp+16]		save_sx			LOOP_SY	LOOP_DX		
//	[%sp+20]		save_sy				LOOP_DX	LOOP_DY	
//	[%sp+24]		save_x1			LOOP_SY	LOOP_DX		
//	[%sp+28]		save_y1				LOOP_DX	LOOP_DY	
//	%r2			__udivsi3			LOOP_DX		
//	------------	-----	------		--------------------------------------
//void surface_stretch(SURFACE* dst, SURFACE* src);
//asm("
//	.code
//	.align	1
//	.global	surface_stretch
//surface_stretch:
//	pushn	%r3
//	xsub	%sp, %sp, 32
//	ld.w	%r9, [%r12]+			; %r9      := dw,            dh
//	xld.w	[%sp+0], %r9			; [%sp+0]  := dw, [%sp+2] := dh
//	ld.w	%r9, [%r13]+			; %r9      := sw,            sh
//	xld.w	[%sp+4], %r9			; [%sp+4]  := sw, [%sp+6] := sh
//	ld.w	%r0, [%r12]			; %r0      := dp
//	ld.w	%r1, [%r13]			; %r1      := sp
//;//{{リロケータブル化
//	xld.w	%r2, __udivsi3			; %r2      := __udivsi3
//;//}}リロケータブル化
//	xld.w	%r3, 0x0e00			; %r3      := FRAM4_START
//	ld.w	%r12, %r3			; %r12     := FRAM4_START
//	xld.w	%r13, surface_stretch_LOOP_DY	; %r13     := surface_stretch_LOOP_DY
//	xld.w	%r14, surface_stretch_END	; %r14     := surface_stretch_END-surface_stretch_LOOP_DY
//	xcall.d	memcpy				; memcpy(FRAM4_START,surface_stretch_LOOP_DY,surface_stretch_END-surface_stretch_LOOP_DY)
//	 sub	%r14, %r13			; *delay*
//	xld.h	%r7, [%sp+2]			; %r7      := y1 = dh
//	xld.w	%r4, 0				; %r4      := sy = 0				*anti-interlock*
//	xld.w	[%sp+12], %r7			; [%sp+12] := dy = dh
//	jp	%r3				; jp surface_stretch_LOOP_DY@FRAM4_START	'jp.d %rb'は使用不可 (CPU bug)
//;//{{ここからFRAM4領域へ転送
//;//{{LOOP_DY************************************;
//surface_stretch_LOOP_DY:
//	xld.h	%r5, [%sp+0]			; %r5      := x1 = dw
//	xld.w	%r3, 0				; %r3      := sx = 0				*anti-interlock*
//	xld.w	[%sp+8], %r5			; [%sp+8]  := dx = dw
//	xld.w	[%sp+20], %r4			; [%sp+20] := save_sy = sy
//	xld.w	[%sp+28], %r7			; [%sp+28] := save_y1 = y1
//;//{{LOOP_DX++++++++++++++++++++++++++++++++++++;
//surface_stretch_LOOP_DX:
//	xld.w	%r4, [%sp+20]			; %r4      := sy = save_sy
//	xld.w	%r7, [%sp+28]			; %r7      := y1 = save_y1
//	xld.w	%r13, 0				; %r13     := c1 = 0
//	xld.w	%r12, 0				; %r12     := c2 = 0
//	xld.h	%r14, [%sp+6]			; %r14     := y2 = sh
//	xld.w	[%sp+16], %r3			; [%sp+16] := save_sx = sx
//	xld.w	[%sp+24], %r5			; [%sp+24] := save_x1 = x1
//;//{{LOOP_SY====================================;
//surface_stretch_LOOP_SY:
//	xld.w	%r3, [%sp+16]			; %r3  := sx = save_sx
//	xld.w	%r5, [%sp+24]			; %r5  := x1 = save_x1
//	cmp	%r7, %r14			; %r15 := y3 = y1 < y2 ? y1 : y2
//	jrult.d	3
//	 ld.w	%r15, %r7			;						*delay*
//	 ld.w	%r15, %r14
//	xld.h	%r6, [%sp+4]			; %r6  := x2 = sw
//;//{{LOOP_SX------------------------------------;
//surface_stretch_LOOP_SX:
//	cmp	%r5, %r6			; %r10 := x3 = x1 < x2 ? x1 : x2
//	jrult.d	3
//	 ld.w	%r10, %r5			;						*delay*
//	 ld.w	%r10, %r6
//	;
//	mltu.h	%r10, %r15			; %alr := xy = x3 * y3
//;//	ld.w	%r11, %alr			; %r11 := xy ----+
//	;					;                |
//	xld.h	%r9, [%sp+4]			; %r9  :=     sw |
//	ld.w	%r11, %alr			; <--------------+				*anti-interlock*
//	mltu.h	%r9, %r4			; %alr :=     sw * sy
//	ld.w	%r9, %alr			; %r9  :=     sw * sy
//	add	%r9, %r3			; %r9  :=     sw * sy + sx
//	add	%r9, %r1			; %r9  := &sp[sw * sy + sx]
//	ld.ub	%r9, [%r9]			; %r9  :=  sp[sw * sy + sx]
//	sub	%r5, %r10			; <------------------------------------+	*anti-interlock*
//	mltu.h	%r11, %r9			; %alr :=       xy * sp[sw * sy + sx]  |
//	ld.w	%r9, %alr			; %r9  :=       xy * sp[sw * sy + sx]  |
//	;					;                                      |
//;//	add	%r13, %r11			; %r13 := c1 += xy --------------------|-+
//;//	add	%r12, %r9			; %r12 := c2 += xy * sp[sw * sy + sx] -|-|-+
//	;					;                                      | | |
//;//	sub	%r5, %r10			; %r5  := x1 -= x3 --------------------+ | |
//	jrne.d	5				; if(!x1)                                | |
//	 add	%r12, %r9			; <--------------------------------------|-+ 	*delay*
//	 xld.h	%r9, [%sp+0]			;   %r9 :=       dw                      |
//	 add	%r3, 1				;   %r3 := sx++                          |	*anti-interlock*
//	 add	%r5, %r9			;   %r5 := x1 += dw                      |
//	;					;                                        |
//	sub	%r6, %r10			; %r6  := x2 -= x3                       |
//	xjrne.d	surface_stretch_LOOP_SX		;                                        |
//	 add	%r13, %r11			; <--------------------------------------+	*delay*
//;//}}LOOP_SX------------------------------------;
//	sub	%r7, %r15			; %r7  := y1 -= y3
//	jrne	4
//	 xld.h	%r9, [%sp+2]			; %r9  := dh
//	 add	%r4, 1				; %r4  := sy++					*anti-interlock*
//	 add	%r7, %r9			; %r7  := y1 += dh
//	sub	%r14, %r15			; %r14 := y2 -= y3
//	xjrne	surface_stretch_LOOP_SY
//;//}}LOOP_SY====================================;
//	add	%r12, %r12			; %r12   :=          (c2 * 2)
//	add	%r12, %r13			; %r12   :=          (c2 * 2) + c1
//;//{{リロケータブル化
//;//	xcall.d	__udivsi3			; %r10   :=         ((c2 * 2) + c1) / (c1 * 2)
//;//↓リロケータブル化
//	call.d	%r2				; %r10   :=         ((c2 * 2) + c1) / (c1 * 2)
//;//}}リロケータブル化
//	 add	%r13, %r13			; %r13   :=                           (c1 * 2)	*delay*
//	ld.b	[%r0]+, %r10			; [%r0]+ := *dp++ = ((c2 * 2) + c1) / (c1 * 2)
//	xld.w	%r9, [%sp+8]			; while(--dx)
//	xsub	%r9, %r9, 1			;						!INTERLOCK!
//	xld.w	[%sp+8], %r9
//	xjrne	surface_stretch_LOOP_DX
//;//}}LOOP_DX++++++++++++++++++++++++++++++++++++;
//	xld.w	%r9, [%sp+12]			; while(--dy)
//	xsub	%r9, %r9, 1			;						!INTERLOCK!
//	xld.w	[%sp+12], %r9
//	xjrne	surface_stretch_LOOP_DY
//;//}}LOOP_DY************************************;
//	xadd	%sp, %sp, 32
//	popn	%r3
//	ret
//surface_stretch_END:
//;//}}ここまでFRAM4領域へ転送
//");
//}}2007/10/17:高速化に伴い削除
//↓* Wed Oct 17 16:54:35 JST 2007 Naoyuki Sawa
//↓- ASM版surface_stretch()を高速化しました。
//↓  これまでは、ループ最内周で毎回乗算と加算を行って、'&sp[sw*sy+sx]'のピクセルアドレスを計算していました。
//↓  今回、sy(%r1)に実際には'&sp[sw*sy]'の値を保持し、加算のみでピクセルアドレスを計算できるようにしました。
//↓  高速化前のASM版に較べて、約11%速くなりました。
//↓  <例>	224x192 -> 128x88 縮小を一回実行する時間 (TURBO=-1,LDIRECT_INTERVAL=25)
//↓	C版		約 424 ミリ秒
//↓	旧ASM版		約 132 ミリ秒
//↓	新ASM版		約 117 ミリ秒
//{{2007/10/17:さらに高速化に伴い削除
//	------------	-----	------		--------------------------------------
//	レジスタ割当	定数?	変数名		各ループ固有部分からの、変数使用の有無
//	------------	-----	------		--------------------------------------
//	[%sp+0]		const	dw		LOOP_SX			LOOP_DY	OUTER
//	[%sp+2]		const	dh			LOOP_SY			OUTER
//	%r0			dp				LOOP_DX		OUTER
//	%r4			dx				LOOP_DX	LOOP_DY	
//	%r3			dy					LOOP_DY	OUTER
//	[%sp+4]		const	sw			LOOP_SY			OUTER
//	[%sp+6]		const	sh				LOOP_DX		OUTER
//	%r2			sx		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	
//	%r1			sy		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	OUTER		(実際には'&sp[sw*sy]'の値を保持)
//	%r5			x1		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	
//	%r6			x2		LOOP_SX	LOOP_SY			
//	%r10			x3		LOOP_SX				
//	%r7			y1			LOOP_SY	LOOP_DX	LOOP_DY	OUTER
//	%r14			y2			LOOP_SY	LOOP_DX		
//	%r15			y3		LOOP_SX	LOOP_SY			
//	%r11			xy		LOOP_SX				
//	%r13			c1		LOOP_SX		LOOP_DX		
//	%r12			c2		LOOP_SX		LOOP_DX		
//	[%sp+8]			save_sx			LOOP_SY	LOOP_DX		
//	[%sp+12]		save_sy				LOOP_DX	LOOP_DY	
//	[%sp+16]		save_x1			LOOP_SY	LOOP_DX		
//	[%sp+20]		save_y1				LOOP_DX	LOOP_DY	
//	------------	-----	------		--------------------------------------
//void surface_stretch(SURFACE* dst, SURFACE* src);
//asm("
//	.code
//	.align	1
//	.global	surface_stretch
//surface_stretch:
//	pushn	%r3
//	xsub	%sp, %sp, 24
//	ld.w	%r9, [%r12]+			; %r9      := dw,            dh
//	xld.w	[%sp+0], %r9			; [%sp+0]  := dw, [%sp+2] := dh
//	ld.w	%r9, [%r13]+			; %r9      := sw,            sh
//	xld.w	[%sp+4], %r9			; [%sp+4]  := sw, [%sp+6] := sh
//	ld.w	%r0, [%r12]			; %r0      := dp
//	ld.w	%r1, [%r13]			; %r1      := sy				(実際には'&sp[sw*sy]'の値を保持)
//	xld.w	%r2, 0x0e00			; %r2      := FRAM4_START
//	xld.h	%r3, [%sp+2]			; <---------------------------------------------------------------------------------------+
//	ld.w	%r12, %r2			; %r12     := FRAM4_START                                                                 |
//	xld.w	%r13, surface_stretch_LOOP_DY	; %r13     := surface_stretch_LOOP_DY                                                     |
//	xld.w	%r14, surface_stretch_END	; %r14     := surface_stretch_END-surface_stretch_LOOP_DY                                 |
//	xcall.d	memcpy				; memcpy(FRAM4_START,surface_stretch_LOOP_DY,surface_stretch_END-surface_stretch_LOOP_DY) |
//	 sub	%r14, %r13			; *delay*                                                                                 |
//;//	xld.h	%r3, [%sp+2]			; %r3      := dy = dh --------------------------*anti-interlock*--------------------------+
//	ld.w	%r7, %r3			; %r7      := y1 = dh
//	jp	%r2				; jp surface_stretch_LOOP_DY@FRAM4_START	'jp.d %rb'は使用不可 (CPU bug)
//;//{{ここからFRAM4領域へ転送
//;//{{LOOP_DY************************************;
//surface_stretch_LOOP_DY:
//	xld.h	%r5, [%sp+0]			; %r5      := x1 = dw
//	xld.w	%r2, 0				; %r2      := sx = 0				*anti-interlock*
//	ld.w	%r4, %r5			; %r4      := dx = dw
//	xld.w	[%sp+12], %r1			; [%sp+12] := save_sy = sy
//	xld.w	[%sp+20], %r7			; [%sp+20] := save_y1 = y1
//;//{{LOOP_DX++++++++++++++++++++++++++++++++++++;
//surface_stretch_LOOP_DX:
//	xld.w	%r1, [%sp+12]			; %r1      := sy = save_sy
//	xld.w	%r7, [%sp+20]			; %r7      := y1 = save_y1
//	xld.w	%r13, 0				; %r13     := c1 = 0
//	xld.w	%r12, 0				; %r12     := c2 = 0
//	xld.h	%r14, [%sp+6]			; %r14     := y2 = sh
//	xld.w	[%sp+8], %r2			; [%sp+8]  := save_sx = sx
//	xld.w	[%sp+16], %r5			; [%sp+16] := save_x1 = x1
//;//{{LOOP_SY====================================;
//surface_stretch_LOOP_SY:
//	xld.w	%r2, [%sp+8]			; %r2  := sx = save_sx
//	xld.w	%r5, [%sp+16]			; %r5  := x1 = save_x1
//	cmp	%r7, %r14			; %r15 := y3 = y1 < y2 ? y1 : y2
//	jrult.d	3
//	 ld.w	%r15, %r7			;						*delay*
//	 ld.w	%r15, %r14
//	xld.h	%r6, [%sp+4]			; %r6  := x2 = sw
//;//{{LOOP_SX------------------------------------;
//surface_stretch_LOOP_SX:
//	cmp	%r5, %r6			; %r10 := x3 = x1 < x2 ? x1 : x2
//	jrult.d	3
//	 ld.w	%r10, %r5			;						*delay*
//	 ld.w	%r10, %r6
//	;
//	mltu.h	%r10, %r15			; %alr := xy = x3 * y3
//	ld.w	%r11, %alr			; %r11 := xy
//	;
//	add	%r2, %r1			; %r2  :=     &[sx + sy]
//	ld.ub	%r9, [%r2]			; %r9  :=      [sx + sy]
//	sub	%r2, %r1			; %r2  :=       sx				*anti-interlock*
//	mltu.h	%r11, %r9			; %alr := xy * [sx + sy]
//	ld.w	%r9, %alr			; %r9  := xy * [sx + sy]
//	;					;
//;//	add	%r13, %r11			; %r13 := c1 += xy -------------+
//;//	add	%r12, %r9			; %r12 := c2 += xy * [sx + sy] -|-+
//	;					;                               | |
//	sub	%r5, %r10			; %r5  := x1 -= x3              | |
//	jrne.d	5				; if(!x1)                       | |
//	 add	%r12, %r9			; <-----------------------------|-+		*delay*
//	 xld.h	%r9, [%sp+0]			;   %r9 :=       dw             |
//	 add	%r2, 1				;   %r2 := sx++                 |		*anti-interlock*
//	 add	%r5, %r9			;   %r5 := x1 += dw             |
//	;					;                               |
//	sub	%r6, %r10			; %r6  := x2 -= x3              |
//	xjrne.d	surface_stretch_LOOP_SX		;                               |
//	 add	%r13, %r11			; <-----------------------------+		*delay*
//;//}}LOOP_SX------------------------------------;
//	sub	%r7, %r15			; %r7  := y1 -= y3
//	jrne	5
//	 xld.h	%r9, [%sp+2]			; %r9  := dh
//	 xld.h	%r10, [%sp+4]			; %r10 := sw					*anti-interlock*
//	 add	%r7, %r9			; %r7  := y1 += dh				*anti-interlock*
//	 add	%r1, %r10			; %r1  := sy += sw
//	sub	%r14, %r15			; %r14 := y2 -= y3
//	xjrne	surface_stretch_LOOP_SY
//;//}}LOOP_SY====================================;
//	add	%r12, %r12			; %r12   :=          (c2 * 2)
//	add	%r12, %r13			; %r12   :=          (c2 * 2) + c1
//;//{{リロケータブル化
//;//	xcall.d	__udivsi3			; %r10   :=         ((c2 * 2) + c1) / (c1 * 2)
//;//↓リロケータブル化
//	xld.w	%r9, __udivsi3			; %r9    := __udivsi3
//	call.d	%r9				; %r10   :=         ((c2 * 2) + c1) / (c1 * 2)
//;//}}リロケータブル化
//	 add	%r13, %r13			; %r13   :=                           (c1 * 2)	*delay*
//	ld.b	[%r0]+, %r10			; [%r0]+ := *dp++ = ((c2 * 2) + c1) / (c1 * 2)
//	xsub	%r4, %r4, 1			; while(--dx)
//	xjrne	surface_stretch_LOOP_DX
//;//}}LOOP_DX++++++++++++++++++++++++++++++++++++;
//	xsub	%r3, %r3, 1			; while(--dy)
//	xjrne	surface_stretch_LOOP_DY
//;//}}LOOP_DY************************************;
//	xadd	%sp, %sp, 24
//	popn	%r3
//	ret
//surface_stretch_END:
//;//}}ここまでFRAM4領域へ転送
//");
//}}2007/10/17:さらに高速化に伴い削除
//↓* Wed Oct 17 19:53:44 JST 2007 Naoyuki Sawa
//↓- ASM版surface_stretch()をさらに高速化しました。
//↓  これまでは、ループ最内周で毎回、(sx+sy)の合成と分離を行っていました。
//↓  今回、ループ最内周の直前で(sx+sy)の合成を行い、ループ最内周の直後で分離するように変更しました。
//↓  高速化前のASM版に較べて、約2.5%速くなりました。
//↓  <例>	224x192 -> 128x88 縮小を一回実行する時間 (TURBO=-1,LDIRECT_INTERVAL=25)
//↓	C版		約 424 ミリ秒
//↓	旧ASM版		約 132 ミリ秒
//↓	新ASM版		約 117 ミリ秒
//↓	新新ASM版	約 114 ミリ秒
//{{2007/11/04:拡大フィルタ対応に伴い削除
//	------------	-----	------		--------------------------------------
//	レジスタ割当	定数?	変数名		各ループ固有部分からの、変数使用の有無
//	------------	-----	------		--------------------------------------
//	[%sp+0]		const	dw		LOOP_SX			LOOP_DY	OUTER
//	[%sp+2]		const	dh			LOOP_SY			OUTER
//	%r0			dp				LOOP_DX		OUTER
//	%r4			dx				LOOP_DX	LOOP_DY	
//	%r3			dy					LOOP_DY	OUTER
//	[%sp+4]		const	sw			LOOP_SY			OUTER
//	[%sp+6]		const	sh				LOOP_DX		OUTER
//	%r2			sx		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY			(最内周では'sx+sy'、実際には'&sp[sw*sy+sx]'の値を保持)
//	%r1			sy		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	OUTER		(実際には'&sp[sw*sy]'の値を保持)
//	%r5			x1		LOOP_SX	LOOP_SY	LOOP_DX	LOOP_DY	
//	%r6			x2		LOOP_SX	LOOP_SY			
//	%r10			x3		LOOP_SX				
//	%r7			y1			LOOP_SY	LOOP_DX	LOOP_DY	OUTER
//	%r14			y2			LOOP_SY	LOOP_DX		
//	%r15			y3		LOOP_SX	LOOP_SY			
//	%r11			xy		LOOP_SX				
//	%r13			c1		LOOP_SX		LOOP_DX		
//	%r12			c2		LOOP_SX		LOOP_DX		
//	[%sp+8]			save_sx			LOOP_SY	LOOP_DX		
//	[%sp+12]		save_sy				LOOP_DX	LOOP_DY	
//	[%sp+16]		save_x1			LOOP_SY	LOOP_DX		
//	[%sp+20]		save_y1				LOOP_DX	LOOP_DY	
//	------------	-----	------		--------------------------------------
//void surface_stretch(SURFACE* dst, SURFACE* src);
//asm("
//	.code
//	.align	1
//	.global	surface_stretch
//surface_stretch:
//	pushn	%r3
//	xsub	%sp, %sp, 24
//	ld.w	%r9, [%r12]+			; %r9      := dw,            dh
//	xld.w	[%sp+0], %r9			; [%sp+0]  := dw, [%sp+2] := dh
//	ld.w	%r9, [%r13]+			; %r9      := sw,            sh
//	xld.w	[%sp+4], %r9			; [%sp+4]  := sw, [%sp+6] := sh
//	ld.w	%r0, [%r12]			; %r0      := dp
//	ld.w	%r1, [%r13]			; %r1      := sy				(実際には'&sp[sw*sy]'の値を保持)
//	xld.w	%r2, 0x0e00			; %r2      := FRAM4_START
//	xld.h	%r3, [%sp+2]			; <---------------------------------------------------------------------------------------+
//	ld.w	%r12, %r2			; %r12     := FRAM4_START                                                                 |
//	xld.w	%r13, surface_stretch_LOOP_DY	; %r13     := surface_stretch_LOOP_DY                                                     |
//	xld.w	%r14, surface_stretch_END	; %r14     := surface_stretch_END-surface_stretch_LOOP_DY                                 |
//	xcall.d	memcpy				; memcpy(FRAM4_START,surface_stretch_LOOP_DY,surface_stretch_END-surface_stretch_LOOP_DY) |
//	 sub	%r14, %r13			; *delay*                                                                                 |
//;//	xld.h	%r3, [%sp+2]			; %r3      := dy = dh --------------------------*anti-interlock*--------------------------+
//	ld.w	%r7, %r3			; %r7      := y1 = dh
//	jp	%r2				; jp surface_stretch_LOOP_DY@FRAM4_START	'jp.d %rb'は使用不可 (CPU bug)
//;//{{ここからFRAM4領域へ転送
//;//{{LOOP_DY************************************;
//surface_stretch_LOOP_DY:
//	xld.h	%r5, [%sp+0]			; %r5      := x1 = dw
//	xld.w	%r2, 0				; %r2      := sx = 0				*anti-interlock*
//	ld.w	%r4, %r5			; %r4      := dx = dw
//	xld.w	[%sp+12], %r1			; [%sp+12] := save_sy = sy
//	xld.w	[%sp+20], %r7			; [%sp+20] := save_y1 = y1
//;//{{LOOP_DX++++++++++++++++++++++++++++++++++++;
//surface_stretch_LOOP_DX:
//	xld.w	%r1, [%sp+12]			; %r1      := sy = save_sy
//	xld.w	%r7, [%sp+20]			; %r7      := y1 = save_y1
//	xld.w	%r13, 0				; %r13     := c1 = 0
//	xld.w	%r12, 0				; %r12     := c2 = 0
//	xld.h	%r14, [%sp+6]			; %r14     := y2 = sh
//	xld.w	[%sp+8], %r2			; [%sp+8]  := save_sx = sx
//	xld.w	[%sp+16], %r5			; [%sp+16] := save_x1 = x1
//;//{{LOOP_SY====================================;
//surface_stretch_LOOP_SY:
//	xld.w	%r2, [%sp+8]			; %r2  := sx = save_sx
//	xld.w	%r5, [%sp+16]			; %r5  := x1 = save_x1
//	cmp	%r7, %r14			; %r15 := y3 = y1 < y2 ? y1 : y2
//	jrult.d	3
//	 ld.w	%r15, %r7			;						*delay*
//	 ld.w	%r15, %r14
//	xld.h	%r6, [%sp+4]			; %r6  := x2 = sw
//	add	%r2, %r1			; %r2  := sx + sy				(実際には'&sp[sw*sy+sx]'の値を保持)
//;//{{LOOP_SX------------------------------------;
//surface_stretch_LOOP_SX:
//	cmp	%r5, %r6			; %r10 := x3 = x1 < x2 ? x1 : x2
//	jrult.d	3
//	 ld.w	%r10, %r5			;						*delay*
//	 ld.w	%r10, %r6
//	;
//	mltu.h	%r10, %r15			; %alr := xy = x3 * y3
//;//	ld.w	%r11, %alr			; %r11 := xy -------+
//	;					;                   |
//	ld.ub	%r9, [%r2]			; %r9  := [sx + sy] |
//	ld.w	%r11, %alr			; <-----------------+				*anti-interlock*
//	mltu.h	%r11, %r9			; %alr := xy * [sx + sy]
//	ld.w	%r9, %alr			; %r9  := xy * [sx + sy]
//	;					;
//;//	add	%r13, %r11			; %r13 := c1 += xy -------------+
//;//	add	%r12, %r9			; %r12 := c2 += xy * [sx + sy] -|-+
//	;					;                               | |
//	sub	%r5, %r10			; %r5  := x1 -= x3              | |
//	jrne.d	5				; if(!x1)                       | |
//	 add	%r12, %r9			; <-----------------------------|-+		*delay*
//	 xld.h	%r9, [%sp+0]			;   %r9 :=       dw             |
//	 add	%r2, 1				;   %r2 := (sx + sy)++          |		*anti-interlock*
//	 add	%r5, %r9			;   %r5 := x1 += dw             |
//	;					;                               |
//	sub	%r6, %r10			; %r6  := x2 -= x3              |
//	xjrne.d	surface_stretch_LOOP_SX		;                               |
//	 add	%r13, %r11			; <-----------------------------+		*delay*
//;//}}LOOP_SX------------------------------------;
//	sub	%r2, %r1			; %r2  := sx
//	sub	%r7, %r15			; %r7  := y1 -= y3
//	jrne	5
//	 xld.h	%r9, [%sp+2]			; %r9  := dh
//	 xld.h	%r10, [%sp+4]			; %r10 := sw					*anti-interlock*
//	 add	%r7, %r9			; %r7  := y1 += dh				*anti-interlock*
//	 add	%r1, %r10			; %r1  := sy += sw
//	sub	%r14, %r15			; %r14 := y2 -= y3
//	xjrne	surface_stretch_LOOP_SY
//;//}}LOOP_SY====================================;
//	add	%r12, %r12			; %r12   :=          (c2 * 2)
//	add	%r12, %r13			; %r12   :=          (c2 * 2) + c1
//;//{{リロケータブル化
//;//	xcall.d	__udivsi3			; %r10   :=         ((c2 * 2) + c1) / (c1 * 2)
//;//↓リロケータブル化
//	xld.w	%r9, __udivsi3			; %r9    := __udivsi3
//	call.d	%r9				; %r10   :=         ((c2 * 2) + c1) / (c1 * 2)
//;//}}リロケータブル化
//	 add	%r13, %r13			; %r13   :=                           (c1 * 2)	*delay*
//	ld.b	[%r0]+, %r10			; [%r0]+ := *dp++ = ((c2 * 2) + c1) / (c1 * 2)
//	xsub	%r4, %r4, 1			; while(--dx)
//	xjrne	surface_stretch_LOOP_DX
//;//}}LOOP_DX++++++++++++++++++++++++++++++++++++;
//	xsub	%r3, %r3, 1			; while(--dy)
//	xjrne	surface_stretch_LOOP_DY
//;//}}LOOP_DY************************************;
//	xadd	%sp, %sp, 24
//	popn	%r3
//	ret
//surface_stretch_END:
//;//}}ここまでFRAM4領域へ転送
//");
//}}2007/11/04:拡大フィルタ対応に伴い削除
//
// * Sun Nov 04 13:05:10 JST 2007 Naoyuki Sawa
// - surface_stretch()を拡大フィルタに対応しました。
// - これまでのバージョンでは、拡大時は、転送元の1ピクセルが転送先の2ピクセルにまたがる境界以外は、
//   ポイントサンプリングとなっていました。
//   なぜなら、拡大と縮小のアルゴリズムを共通化していたため、拡大時は、転送元の1ピクセルよりも小さな
//   範囲をずらしながら、平均していたからです。
// - 今回の変更によって、拡大時は、転送元の1ピクセル相当の範囲をずらしながら平均するようになって、
//   スムーズな拡大フィルタリングがかかるようになりました。
// - 拡大と縮小の処理を、できるだけ共通化して実装したために、前バージョンとはかなりコードが変わって
//   いますが、縮小のアルゴリズムは前バージョンと同じであり、縮小処理の結果も完全に同じとなります。
// - 縮小のアルゴリズムは前バージョンと同じなので、以下に、拡大のアルゴリズムだけを追加説明します。
//   簡単のために一次元で説明しますが、実際にはこれをXとYそれぞれに適用しています。
//   XとYの処理は独立なので、X=縮小、Y=拡大のような指定でも問題無く、綺麗にフィルタリングされます。
//
//	①3ピクセル→5ピクセル拡大を例として、説明します。
//
//		転送元	|-------|-------|-------|
//
//		転送先	|-------|-------|-------|-------|-------|
//
//
//	②転送元のピクセルを分割します。
//	  転送元ピクセル当りの分割数は、(転送先ピクセル数-1)です。
//
//			|4(=5=1)|
//		転送元	|-+-+-+-|-+-+-+-|-+-+-+-|
//
//		転送先	|-------|-------|-------|-------|-------|
//
//
//	③転送元の最初のピクセル範囲を、転送先へ格納します。
//
//		転送元	|=======|-+-+-+-|-+-+-+-|
//
//		転送先	|=======|-------|-------|-------|-------|
//
//	④転送元のピクセル範囲をずらします。
//	  ずらす幅は、分割単位×(転送元ピクセル数-1)です。
//	  ずらしたピクセル範囲を平均して、次の転送先へ格納します。
//
//			|-->|2=(3-1)
//		転送元	|-+-|=======|-+-|-+-+-+-|
//
//		転送先	|-------|=======|-------|-------|-------|
//
//	⑤④と同じ処理を繰り返します。
//
//			    |-->|2=(3-1)
//		転送元	|-+-+-+-|=======|-+-+-+-|
//
//		転送先	|-------|-------|=======|-------|-------|
//
//	⑥④と同じ処理を繰り返します。
//
//			        |-->|2=(3-1)
//		転送元	|-+-+-+-|-+-|=======|-+-|
//
//		転送先	|-------|-------|-------|=======|-------|
//
//	⑦④と同じ処理を繰り返します。
//	  転送先のすべてのピクセルに格納したら、終了です。
//
//			            |-->|2=(3-1)
//		転送元	|-+-+-+-|-+-+-+-|=======|
//
//		転送先	|-------|-------|-------|-------|=======|
//
// - 速度計測の結果は、以下のとおりです。
//   拡大フィルタ対応の処理が増えた分、拡大処理の速度が少し低下しましたが、許容範囲だと思います。
//   拡大フィルタ対応の処理が増えた分、縮小処理の速度も低下しているかと思いきや、早くなりました。
//   コードを見直して、前バージョンよりもすっきりした実装にした効果だと思います。
//   スタック上の変数を、できるだけHalfに変更したことも、SRAMアクセスが減って早くなった要因です。
//
//				(TURBO=-1,LDIRECT_INTERVAL=25,framldir.o)
//				224x192->128x88縮小	96x66→128x88拡大
//	前バージョンのASM版	約 114 ミリ秒		約  60 ミリ秒
//	拡大フィルタ対応C版	約 347 ミリ秒		約 209 ミリ秒
//	拡大フィルタ対応ASM版	約  98 ミリ秒		約  64 ミリ秒
//
//{{2007/11/04:拡大フィルタ対応C版。ASM版を使いますが、アルゴリズム確認用にC版も残しておきます
//void
//surface_stretch(SURFACE* dst, SURFACE* src)
//{
//	const unsigned dw = dst->w;
//	const unsigned dh = dst->h;
//	const unsigned sw = src->w;
//	const unsigned sh = src->h;
//	unsigned char* dp = dst->vbuff;
//	//
//	int dx;
//	int dy;
//	int sx;
//	int sy;
//	int x1;
//	int x2;
//	int x3;
//	int y1;
//	int y2;
//	int y3;
//	int c1;
//	int init_x1;
//	int init_x2;
//	int init_x3;
//	int init_y1;
//	int init_y2;
//	int init_y3;
//	int init_c2;
//	int save_sx;
//	int save_sy;
//	int save_x1;
//	int save_y1;
//
//	//{{INIT
//	/* - 拡大用セットアップは等倍不可である理由:
//	 *   等倍はdw=1またはsw=1の可能性があり、init_x?=0になってしまう。
//	 *   init_x?=0になると、以下の処理が正しく行えない。
//	 *   従って、等倍は縮小と見なして、縮小セットアップを行う。
//	 */
//	if(dw > sw) {	//拡大(等倍不可)
//		init_x1 = dw - 1;
//		init_x2 = dw - 1;
//		init_x3 = sw - 1;
//	} else {	//縮小または等倍
//		init_x1 = dw;
//		init_x2 = sw;
//		init_x3 = sw;
//	}
//	if(dh > sh) {	//拡大(等倍不可)
//		init_y1 = dh - 1;
//		init_y2 = dh - 1;
//		init_y3 = sh - 1;
//	} else {	//縮小または等倍
//		init_y1 = dh;
//		init_y2 = sh;
//		init_y3 = sh;
//	}
//	init_c2 = init_x2 * init_y2;
//	//}}INIT
//
//	save_sy = (int)src->vbuff;
//	save_y1 = init_y1;
//	dy = dh;
//	//{{LOOP_DY
//	do {
//		save_sx = 0;
//		save_x1 = init_x1;
//		dx = dw;
//		//{{LOOP_DX
//		do {
//			sy = save_sy;
//			y1 = save_y1;
//			y2 = init_y2;
//			c1 = 0;
//			//{{LOOP_SY
//			do {
//				sx = save_sx + sy;
//				x1 = save_x1;
//				x2 = init_x2;
//				y3 = y1 < y2 ? y1 : y2;
//				//{{LOOP_SX
//				do {
//					x3 = x1 < x2 ? x1 : x2;
//					c1 += *(unsigned char*)sx * x3 * y3;
//					if(!(x1 -= x3)) {
//						x1 += init_x1;
//						sx++;
//					}
//				} while(x2 -= x3);
//				//}}LOOP_SX
//				if(!(y1 -= y3)) {
//					y1 += init_y1;
//					sy += sw;
//				}
//			} while(y2 -= y3);
//			//}}LOOP_SY
//			*dp++ = (((c1 << 1) + init_c2) / init_c2) >> 1;
//			save_x1 -= init_x3;
//			while(save_x1 <= 0) {
//				save_x1 += init_x1;
//				save_sx++;
//			}
//		} while(--dx);
//		//}}LOOP_DX
//		save_y1 -= init_y3;
//		while(save_y1 <= 0) {
//			save_y1 += init_y1;
//			save_sy += sw;
//		}
//	} while(--dy);
//	//}}LOOP_DY
//}
//}}2007/11/04:拡大フィルタ対応C版。ASM版を使いますが、アルゴリズム確認用にC版も残しておきます
//
//	----------	-------	-------	-------	-------	-------	-------	-------	-------
//	レジスタ	変数	INIT前	INIT	INIT後	LOOP_DY	LOOP_DX	LOOP_SY	LOOP_SX
//	----------	-------	-------	-------	-------	-------	-------	-------	-------
//	%r0		dp	W				RW		
//	%r12		c1					RW		RW
//	[%sp+0].w	init_c2		W			R		
//	----------	-------	-------	-------	-------	-------	-------	-------	-------
//			dh	W	R	R				
//			sh	W	R					
//	[%sp+8].h	dy			W	RW			
//	%r5		sy					W	RW		(実際には'&sp[sw*sy]'の値を保持)
//	%r6		y1					W	RW	
//	%r7		y2					W	RW	
//	%r14		y3						RW	R
//	[%sp+10].h	init_y1		W	R	R		R	
//	[%sp+12].h	init_y2		W			R		
//	[%sp+14].h	init_y3		W		R			
//	%r1		save_sy			W	RW	R		
//	%r2		save_y1			W	RW	R		
//	----------	-------	-------	-------	-------	-------	-------	-------	-------
//	[%sp+4].h	dw	W	R		R			
//	[%sp+6].h	sw	W	R		R		R	
//	[%sp+22].h	dx					RW		
//	%r10		sx						W	RW	(最内周では'sx+sy'、実際には'&sp[sw*sy+sx]'の値を保持)
//	%r11		x1						W	RW
//	%r13		x2						W	RW
//	%r15		x3							RW
//	[%sp+16].h	init_x1		W		R	R		R
//	[%sp+18].h	init_x2		RW				R	
//	[%sp+20].h	init_x3		W			R		
//	%r3		save_sx				W	RW	R	
//	%r4		save_x1				W	RW	R	
//	----------	-------	-------	-------	-------	-------	-------	-------	-------
//
void surface_stretch(SURFACE* dst, SURFACE* src);
asm("
	.code
	.align	1
	.global	surface_stretch
surface_stretch:
	pushn	%r3
	xsub	%sp, %sp, 28
	ld.h	%r5, [%r12]+			; %r5      := dw = dst->w
	ld.h	%r2, [%r12]+			; %r2      := dh = dst->h
	ld.w	%r0, [%r12]			; %r0      := dp = dst->vbuff
	ld.h	%r6, [%r13]+			; %r6      := sw = src->w
	ld.h	%r3, [%r13]+			; %r3      := sh = src->h
	ld.w	%r1, [%r13]			; %r1      := save_sy = src->vbuff
	xld.h	[%sp+8], %r5			; [%sp+8]  := dw
	xld.h	[%sp+10], %r6			; [%sp+10] := sw
	xld.h	[%sp+12], %r2			; [%sp+12] := dy = dh
	;// %r0      := dp
	;// %r1      := save_sy
	;// %r2      := dh
	;// %r3      := sh
	;// %r5      := dw
	;// %r6      := sw
	;// [%sp+8]  := dw
	;// [%sp+10] := sw
	;// [%sp+12] := dy
	cmp	%r2, %r3			; if(dh > sh)
	jrle.d	5
	 ld.w	%r4, %r3			;   %r4  := sh					*delay*
	 sub	%r2, 1				;   %r2  := init_y1 = dh - 1
	 ld.w	%r3, %r2			;   %r3  := init_y2 = dh - 1
	 sub	%r4, 1				;   %r4  := init_y3 = sh - 1
	cmp	%r5, %r6			; if(dw > sw)
	jrle.d	5
	 ld.w	%r7, %r6			;   %r7  := sw					*delay*
	 sub	%r5, 1				;   %r5  := init_x1 = dw - 1
	 ld.w	%r6, %r5			;   %r6  := init_x2 = dw - 1
	 sub	%r7, 1				;   %r7  := init_x3 = sw - 1
	;// %r0      := dp
	;// %r1      := save_sy
	;// %r2      := init_y1
	;// %r3      := init_y2
	;// %r4      := init_y3
	;// %r5      := init_x1
	;// %r6      := init_x2
	;// %r7      := init_x3
	;// [%sp+8]  := dw
	;// [%sp+10] := sw
	;// [%sp+12] := dy
	mlt.h	%r3, %r6			; %alr     := init_c2 = init_x2 * init_y2
	ld.w	%r9, %alr			; %r9      := init_c2
	xld.w	[%sp+4], %r9			; [%sp+4]  := init_c2
	xld.h	[%sp+14], %r2			; [%sp+14] := init_y1
	xld.h	[%sp+16], %r3			; [%sp+16] := init_y2
	xld.h	[%sp+18], %r4			; [%sp+18] := init_y3
	xld.h	[%sp+20], %r5			; [%sp+20] := init_x1
	xld.h	[%sp+22], %r6			; [%sp+22] := init_x2
	xld.h	[%sp+24], %r7			; [%sp+24] := init_x3
	;// %r0      := dp
	;// %r1      := save_sy
	;// %r2      := save_y1
	;// [%sp+4]  := init_c2
	;// [%sp+8]  := dw
	;// [%sp+10] := sw
	;// [%sp+12] := dy
	;// [%sp+14] := init_y1
	;// [%sp+16] := init_y2
	;// [%sp+18] := init_y3
	;// [%sp+20] := init_x1
	;// [%sp+22] := init_x2
	;// [%sp+24] := init_x3
	xld.w	%r12, 0x0e00			; %r12 := FRAM4_START
	xld.w	%r13, surface_stretch_LOOP_DY	; %r13 := LOOP_DY
	xld.w	%r14, surface_stretch_END	; %r14 := END
	xld.w	[%sp+0], %r12			; retp := FRAM4_START
	xjp.d	memcpy				; memcpy(FRAM4_START, LOOP_DY, END - LOOP_DY)
	sub	%r14, %r13			; %r14 := END - LOOP_DY				*delay*
	;// %r0      := dp
	;// %r1      := save_sy
	;// %r2      := save_y1
	;// [%sp+0]  := init_c2
	;// [%sp+4]  := dw
	;// [%sp+6]  := sw
	;// [%sp+8]  := dy
	;// [%sp+10] := init_y1
	;// [%sp+12] := init_y2
	;// [%sp+14] := init_y3
	;// [%sp+16] := init_x1
	;// [%sp+18] := init_x2
	;// [%sp+20] := init_x3
;//{{ここからFRAM4領域へ転送
;//{{LOOP_DY
surface_stretch_LOOP_DY:
	xld.w	%r3, 0				; %r3      := save_sx = 0
	xld.h	%r9, [%sp+4]			; %r9      := dw
	xld.h	%r4, [%sp+16]			; %r4      := save_x1 = init_x1			*anti-interlock*
	xld.h	[%sp+22], %r9			; [%sp+22] := dx = dw
	;// %r0      := dp
	;// %r1      := save_sy
	;// %r2      := save_y1
	;// %r3      := save_sx
	;// %r4      := save_x1
	;// [%sp+0]  := init_c2
	;// [%sp+4]  := dw
	;// [%sp+6]  := sw
	;// [%sp+8]  := dy
	;// [%sp+10] := init_y1
	;// [%sp+12] := init_y2
	;// [%sp+14] := init_y3
	;// [%sp+16] := init_x1
	;// [%sp+18] := init_x2
	;// [%sp+20] := init_x3
	;// [%sp+22] := dx
;//{{LOOP_DX
surface_stretch_LOOP_DX:
	ld.w	%r5, %r1			; %r5  := sy = save_sy
	ld.w	%r6, %r2			; %r6  := y1 = save_y1
	xld.h	%r7, [%sp+12]			; %r7  := y2 = init_y2
	xld.w	%r12, 0				; %r12 := c1 = 0
	;// %r0      := dp
	;// %r1      := save_sy
	;// %r2      := save_y1
	;// %r3      := save_sx
	;// %r4      := save_x1
	;// %r5      := sy
	;// %r6      := y1
	;// %r7      := y2
	;// %r12     := c1
	;// [%sp+0]  := init_c2
	;// [%sp+4]  := dw
	;// [%sp+6]  := sw
	;// [%sp+8]  := dy
	;// [%sp+10] := init_y1
	;// [%sp+12] := init_y2
	;// [%sp+14] := init_y3
	;// [%sp+16] := init_x1
	;// [%sp+18] := init_x2
	;// [%sp+20] := init_x3
	;// [%sp+22] := dx
;//{{LOOP_SY
surface_stretch_LOOP_SY:
	ld.w	%r10, %r3			; %r10 := sx = save_sx
	add	%r10, %r5			; %r10 := sx = save_sx + sy
	ld.w	%r11, %r4			; %r11 := x1 = save_x1
	xld.h	%r13, [%sp+18]			; %r13 := x2 = init_x2
	cmp	%r6, %r7			; %r14 := y3 = y1 < y2 ? y1 : y2
	jrlt.d	3
	 ld.w	%r14, %r6			;						*delay*
	 ld.w	%r14, %r7
	;// %r0      := dp
	;// %r1      := save_sy
	;// %r2      := save_y1
	;// %r3      := save_sx
	;// %r4      := save_x1
	;// %r5      := sy
	;// %r6      := y1
	;// %r7      := y2
	;// %r10     := sx
	;// %r11     := x1
	;// %r12     := c1
	;// %r13     := x2
	;// %r14     := y3
	;// [%sp+0]  := init_c2
	;// [%sp+4]  := dw
	;// [%sp+6]  := sw
	;// [%sp+8]  := dy
	;// [%sp+10] := init_y1
	;// [%sp+12] := init_y2
	;// [%sp+14] := init_y3
	;// [%sp+16] := init_x1
	;// [%sp+18] := init_x2
	;// [%sp+20] := init_x3
	;// [%sp+22] := dx
;//{{LOOP_SX
surface_stretch_LOOP_SX:
	ld.ub	%r9, [%r10]			; <------------------------------+
	cmp	%r11, %r13			; %r15 := x3 = x1 < x2 ? x1 : x2 |
	jrlt.d	3				;                                |
	 ld.w	%r15, %r11			;                                |		*delay*
	 ld.w	%r15, %r13			;                                |
;//	ld.ub	%r9, [%r10]			; %r9  := [sx] ------------------+		*anti-interlock*
	mlt.h	%r9, %r15			; %alr := [sx] * x3
	ld.w	%r9, %alr			; %r9  := [sx] * x3
;//	mlt.h	%r9, %r14			; %alr := [sx] * x3 * y3 -------+
;//	ld.w	%r9, %alr			; %r9  := [sx] * x3 * y3 -------|-+
;//	add	%r12, %r9			; %r12 := c1 += [sx] * x3 * y3 -|-|-+
	sub	%r11, %r15			; %r11 := x1 -= x3              | | |
	jrne.d	5				;                               | | |
	 mlt.h	%r9, %r14			; <-----------------------------+ | |		*delay*
	 xld.h	%r9, [%sp+16]			; %r9  := init_x1                 | |
	 add	%r10, 1				; %r10 := sx++                    | |		*anti-interlock*
	 add	%r11, %r9			; %r11 := x1 += init_x1           | |
	ld.w	%r9, %alr			; <-------------------------------+ |
	sub	%r13, %r15			; %r13 := x2 -= x3                  |
	xjrne.d	surface_stretch_LOOP_SX		; while(x2 -= x3)                   |
	add	%r12, %r9			; <---------------------------------+		*delay*
;//}}LOOP_SX
	sub	%r6, %r14			; %r6  := y1 -= y3
	jrne	5
	 xld.h	%r9, [%sp+10]			; %r9  := init_y1
	 xld.h	%r15, [%sp+6]			; %r15 := sw					*anti-interlock*
	 add	%r6, %r9			; %r6  := y1 += init_y1				*anti-interlock*
	 add	%r5, %r15			; %r5  := sy += sw
	sub	%r7, %r14			; %r7  := y2 -= y3
	xjrne	surface_stretch_LOOP_SY		; while(y2 -= y3)
;//}}LOOP_SY
	sla	%r12, 1				; %r12 := c1<<1
	xld.w	%r13, [%sp+0]			; %r13 := init_c2
	xld.w	%r10, __udivsi3			; %r10 := __udivsi3
	call.d	%r10				; %r10 := ((c1<<1)+init_c2)/init_c2
	add	%r12, %r13			; %r12 := (c1<<1)+init_c2			*delay*
;//	sra	%r10, 1				; %r10 := ((c1<<1)+init_c2)>>1 --+
;//	ld.b	[%r0]+, %r10			; *dp++ = ((c1<<1)+init_c2)>>1 --|-+
	xld.h	%r9, [%sp+20]			; %r9  := init_x3                | |
	xld.h	%r14, [%sp+22]			; <------------------------------|-|-+		*anti-interlock*
	sub	%r4, %r9			; %r4  := save_x1 -= init_x3     | | |
	jrgt.d	6				; if(save_x1 <= 0)               | | |
	 sra	%r10, 1				; <------------------------------+ | |		*delay*
	 xld.h	%r9, [%sp+16]			;   %r9  := init_x1                | |
	  add	%r4, %r9			;     %r4  := save_x1 += init_x1   | |
	 jrle.d	-1				;   while(save_x1 <= 0)            | |
	 add	%r3, 1				;   %r3  := save_sx++              | |		*delay*
	ld.b	[%r0]+, %r10			; <--------------------------------+ |
;//	xld.h	%r14, [%sp+22]			; %r14     := dx --------------------+
	xsub	%r14, %r14, 1			; %r14     := --dx
	xld.h	[%sp+22], %r14			; [%sp+22] := --dx
	xjrne	surface_stretch_LOOP_DX		; while(--dx)
;//}}LOOP_DX
	xld.h	%r9, [%sp+14]			; %r9  := init_y3
	xld.h	%r14, [%sp+8]			; <------------------------------+		*anti-interlock*
	sub	%r2, %r9			; %r2  := save_y1 -= init_y3     |
	jrgt	6				; if(save_y1 <= 0)               |
	 xld.h	%r9, [%sp+10]			;   %r9  := init_y1              |
	 xld.h	%r15, [%sp+6]			;   %r15 := sw                   |
	  add	%r2, %r9			;     %r2  := save_y1 += init_y1 |
	 jrle.d	-1				;   while(save_y1 <= 0)          |
	 add	%r1, %r15			;   %r1  := save_sy += sw        |		*delay*
;//	xld.h	%r14, [%sp+8]			; %r14    := dy -----------------+
	xsub	%r14, %r14, 1			; %r14    := --dy
	xld.h	[%sp+8], %r14			; [%sp+8] := --dy
	xjrne	surface_stretch_LOOP_DY		; while(--dy)
;//}}LOOP_DY
	xadd	%sp, %sp, 24
	popn	%r3
	ret
surface_stretch_END:
;//}}ここまでFRAM4領域へ転送
");

void
soft_focus(SURFACE* surface, double x, double y)
{
	SURFACE tmp;

	tmp.w = surface->w * x + 0.5;
	if(tmp.w < 1) tmp.w = 1;
	if(tmp.w > surface->w) tmp.w = surface->w;

	tmp.h = surface->h * y + 0.5;
	if(tmp.h < 1) tmp.h = 1;
	if(tmp.h > surface->h) tmp.h = surface->h;

	if(tmp.w == surface->w &&
	   tmp.h == surface->h) return;	/* 高速化 */
	if(tmp.w * tmp.h > sizeof _def_vbuff) DIE();
	tmp.vbuff = _def_vbuff;

	surface_stretch(&tmp, surface);	/* 縮小 */
	surface_stretch(surface, &tmp);	/* 拡大 */
}

void texture_copy(TEXTURE* texture, SURFACE* surface, int x, int y);
#ifdef REND_NOASM
void
texture_copy(TEXTURE* texture, SURFACE* surface, int x, int y)
{
	const unsigned char* vbuff = surface->vbuff + surface->w * y + x;
	unsigned char* buf = texture->buf;
	int gap = surface->w - texture->header.w;
	int bpp = texture->header.bpp;	/* 1,2,4 */
	int w = texture->header.w;	/* 8N */
	int i = texture->header.h;
	if(!texture->header.mask) {
		w *= bpp;		/* 8N * {1,2,4} */
		do {
			int j = w;
			do {
				int c = -1U >> 8;
				do {
					c = (c << bpp) | *vbuff++;
				} while(c >= 0);
				*buf++ = c;
			} while(j -= 8);
			vbuff += gap;
		} while(i -= 1);
	} else {
		unsigned char* mask = texture->mask;
		int trans = 1 << bpp;
		do {
			int j = w;
			do {
				int m = -1U >> 8;
				do {
					int c = -1U >> 8;
					do {
						int s = *vbuff++;
						c <<= bpp;
						m <<= 1;
						if(s < trans) {
							c |= s;
							m |= 1;
						}
					} while(c >= 0);
					*buf++ = c;
				} while(m >= 0);
				*mask++ = m;
			} while(j -= 8);
			vbuff += gap;
		} while(i -= 1);
	}
}
#else /*REND_NOASM*/
asm("
		.code
		.align		1
		.global		texture_copy
texture_copy:
		pushn		%r3
		;// %r12 := texture
		;// %r13 := surface
		;// %r14 := x
		;// %r15 := y
		ld.w		%r0, %r12				;// %r0  := texture
		ld.w		%r1, %r13				;// %r1  := surface
		ld.w		%r2, %r14				;// %r2  := x
;//		ld.w		%r3, %r15				;// %r3  := y ----------------------------------+
		xld.w		%r12, 0x0e00				;// %r12 := FRAM4_START				|
		xld.w		%r13, texture_copy_NOMASK_START		;// %r13 := start = texture_copy_NOMASK_START	|
		xld.w		%r14, texture_copy_NOMASK_END		;// %r14 := end   = texture_copy_NOMASK_END	|
		xbtst		[%r0+9], 0				;// %psr[Z] := !texture->header.mask		|
		jreq.d		texture_copy_MEMCPY			;// if(texture->header.mask) {			|
		ld.w		%r3, %r15				;// %r3  := y <---------------------------------+			*delay*
		xld.w		%r13, texture_copy_MASK_START		;//   %r13 := start = texture_copy_MASK_START
		xld.w		%r14, texture_copy_MASK_END		;//   %r14 := end   = texture_copy_MASK_END
texture_copy_MEMCPY:							;// }
		xcall.d		memcpy					;// %r10 := memcpy(FRAM4_START, start, size)
		sub		%r14, %r13				;// %r14 := size  = end - start						*delay*
		;// %r0  := texture
		;// %r1  := surface
		;// %r2  := x
		;// %r3  := y
		;// %r10 := FRAM4_START
		xld.ub		%r4, [%r0+8]				;// %r4  := bpp   = texture->header.bpp
		xld.h		%r5, [%r0+10]				;// %r5  := w     = texture->header.w
		xld.h		%r6, [%r0+12]				;// %r6  := i     = texture->header.h
		xld.w		%r7, [%r0+20]				;// %r7  := buf   = texture->buf
		xld.h		%r11, [%r1+0]				;// %r11 :=                          surface->w
		xld.w		%r12, [%r1+4]				;// %r12 :=         surface->vbuff
		mlt.h		%r11, %r3				;// %alr :=                          surface->w * y
		ld.w		%r9, %alr				;// %r9  :=                          surface->w * y
		add		%r12, %r9				;// %r12 :=         surface->vbuff + surface->w * y
		add		%r12, %r2				;// %r12 := vbuff = surface->vbuff + surface->w * y + x
		sub		%r11, %r5				;// %r11 := gap   = surface->w - w, %psr[V] := 0
		ld.w		%r13, -1				;// %r13  :=        -1U
		srl		%r13, 8					;// %r13  := c    = -1U >> 8
		;// %r0  := texture
		;// %r4  := bpp
		;// %r5  := w
		;// %r6  := i
		;// %r7  := buf
		;// %r10 := FRAM4_START
		;// %r11 := gap
		;// %r12 := vbuff
		;// %r13 := c
		;// %psr[V] := 0
		jp		%r10					;// goto FRAM4_START							(jp.d %rbは不可)
;//{{ここからFRAM4領域へ転送--------------------------------------------;
texture_copy_NOMASK_START:
		mlt.h		%r5, %r4				;// %alr := w *= bpp
texture_copy_NOMASK_LOOP_I:						;// do {
		ld.w		%r14, %alr				;//   %r14 := j = w
texture_copy_NOMASK_LOOP_J:						;//   do {
texture_copy_NOMASK_LOOP_C:						;//     do {
		;// %r4  := bpp
		;// %r6  := i
		;// %r7  := buf
		;// %r11 := gap
		;// %r12 := vbuff
		;// %r13 := c
		;// %r14 := j
		;// %psr[V] := 0
		;// %alr := w
		ld.ub		%r9, [%r12]+				;//       %r9  := s = *vbuff++
		sll		%r13, %r4				;//       %r13 := c <<= bpp, %psr[N] := c[31]
		jrge.d		texture_copy_NOMASK_LOOP_C		;//     } while(!(%psr[N] ^ %psr[V]))
		or		%r13, %r9				;//       %r13 := c | s							*delay*
		ld.b		[%r7]+, %r13				;//     *buf++ = c
		sub		%r14, 8					;//     %r14 := j -= 8, %psr[V] := 0
		jrne.d		texture_copy_NOMASK_LOOP_J		;//   } while(j != 0)
		srl		%r13, 8					;//     %r13 := c = -1U >> 8						*delay*
		sub		%r6, 1					;//   %r6  := i -= 1, %psr[V] := 0
		jrne.d		texture_copy_NOMASK_LOOP_I		;// } while(i != 0)
		add		%r12, %r11				;//   %r12 := vbuff += gap, %psr[V] := 0				*delay*
		popn		%r3
		ret
texture_copy_NOMASK_END:
;//}}ここまでFRAM4領域へ転送--------------------------------------------;
;//{{ここからFRAM4領域へ転送--------------------------------------------;
texture_copy_MASK_START:
		xld.w		%r0, [%r0+24]				;// %r0  := mask  = texture->mask
		ld.w		%r1, 1					;// %r1  :=         1
		sll		%r1, %r4				;// %r1  := trans = 1 << bpp
		ld.w		%r2, -1					;// %r2  :=         -1U
		srl		%r2, 7					;// %r2  :=       = -1U >> 7
		ld.w		%r3, %r2				;// %r3  := m     = -1U >> 7
texture_copy_MASK_LOOP_I:						;// do {
		ld.w		%r14, %r5				;//   %r14 := j = w
texture_copy_MASK_LOOP_J:						;//   do {
texture_copy_MASK_LOOP_M:						;//     do {
texture_copy_MASK_LOOP_C:						;//       do {
		;// %r0  := mask
		;// %r1  := trans
		;// %r2  := -1U >> 7
		;// %r3  := m
		;// %r4  := bpp
		;// %r6  := i
		;// %r7  := buf
		;// %r11 := gap
		;// %r12 := vbuff
		;// %r13 := c
		;// %r14 := j
		ld.ub		%r9, [%r12]+				;//         %r9  := s = *vbuff++
		cmp		%r9, %r1				;//         %psr[C] := s < trans, %psr[V] := 0
		jrge.d		3					;//         if(s < trans)
		 sll		%r13, %r4				;//         %r13 := c <<= bpp, %psr[N] := c[31]				*delay*
		 or		%r13, %r9				;//           %r13 := c | s
		jrge.d		texture_copy_MASK_LOOP_C		;//       } while(!(%psr[N] ^ %psr[V]))
		adc		%r3, %r3				;//         %r3  := m = (m << 1) | %psr[C], %psr[C] := m[32]		*delay*
		ld.b		[%r7]+, %r13				;//       *buf++ = c
		jruge.d		texture_copy_MASK_LOOP_M		;//     } while(!%psr[C])
		srl		%r13, 8					;//       %r13 := c = -1U >> 8						*delay*
		ld.b		[%r0]+, %r3				;//     *mask++ = m
		sub		%r14, 8					;//     %r14 := j -= 8
		jrne.d		texture_copy_MASK_LOOP_J		;//   } while(j != 0)
		ld.w		%r3, %r2				;//     %r3 := m = -1U >> 7						*delay*
		sub		%r6, 1					;//   %r6  := i -= 1
		jrne.d		texture_copy_MASK_LOOP_I		;// } while(i != 0)
		add		%r12, %r11				;//   %r12 := vbuff += gap						*delay*
		popn		%r3
		ret
texture_copy_MASK_END:
;//}}ここまでFRAM4領域へ転送--------------------------------------------;
");
#endif /*REND_NOASM*/

#ifdef REND_NOASM
void
surface_move(SURFACE* dst, int dx, int dy, SURFACE* src, int sx, int sy, int w, int h)
{
	unsigned char* dptr;
	unsigned char* sptr;
	int x;
	int y;

	/* 転送先矩形の水平クリッピングを行います。 */
	if(dx < 0) { /* 左クリッピング */
		w  += dx;
		sx -= dx; /* 転送元矩形の左端も調整 */
		dx  =  0;
	}
	if(w > dst->w - dx) { /* 右クリッピング */
		w = dst->w - dx;
	}
	if(w <= 0) {
		return; /* 完全に画面外 */
	}

	/* 転送先矩形の垂直クリッピングを行います。 */
	if(dy < 0) { /* 上クリッピング */
		h  += dy;
		sy -= dy; /* 転送元矩形の上端も調整 */
		dy  =  0;
	}
	if(h > dst->h - dy) { /* 下クリッピング */
		h = dst->h - dy;
	}
	if(h <= 0) {
		return; /* 完全に画面外 */
	}

	/* 転送先矩形と転送元矩形の、左上先頭ピクセルアドレスを求めます。 */
	dptr = &dst->vbuff[dst->w * dy + dx];
	sptr = &src->vbuff[src->w * sy + sx];

	/* 転送先矩形が転送元矩形の前方ならば、左上先頭からコピーします。 */
	if(dptr <= sptr) {
		y = h;
		do {
			x = w;
			do {
				*dptr++ = *sptr++;
			} while(--x);
			dptr += dst->w - w;
			sptr += src->w - w;
		} while(--y);

	/* 転送先矩形が転送元矩形の後方ならば、右下末尾からコピーします。 */
	} else {
		dptr += dst->w * (h - 1) + (w - 1); /* 右下末尾のアドレス */
		sptr += src->w * (h - 1) + (w - 1); /* 右下末尾のアドレス */
		y = h;
		do {
			x = w;
			do {
				*dptr-- = *sptr--;
			} while(--x);
			dptr -= dst->w - w;
			sptr -= src->w - w;
		} while(--y);
	}
}
#else /*REND_NOASM*/
void surface_move(SURFACE* dst, int dx, int dy, SURFACE* src, int sx, int sy, int w, int h);
asm("
		.code
		.align		1
		.global		surface_move
surface_move:
		;// %r12     := dst
		;// %r13     := dx
		;// %r14     := dy
		;// %r15     := src
		;// [%sp+ 4] := sx
		;// [%sp+ 8] := sy
		;// [%sp+12] := w
		;// [%sp+16] := h
		xld.w		%r4, [%sp+ 4]		;// %r4  := sx
		xld.w		%r5, [%sp+ 8]		;// %r5  := sy
		xld.w		%r6, [%sp+12]		;// %r6  := w
		xld.w		%r7, [%sp+16]		;// %r7  := h
		ld.h		%r10, [%r12]+		;// %r10 := dst->w
		ld.h		%r11, [%r12]+		;// %r11 := dst->h, %r12 := &dst->vbuff
		ld.w		%r12, [%r12]		;// %r12 := dst->vbuff
		;// %r4  := sx
		;// %r5  := sy
		;// %r6  := w
		;// %r7  := h
		;// %r10 := dst->w
		;// %r11 := dst->h
		;// %r12 := dst->vbuff
		;// %r13 := dx
		;// %r14 := dy
		;// %r15 := src
		;// 転送先矩形の水平クリッピングを行います。
		cmp		%r13, 0			;// if(dx < 0) {
		jrge		surface_move_CLIP_L
		add		%r6, %r13		;//   %r6  := w  += dx
		sub		%r4, %r13		;//   %r4  := sx -= dx
		ld.w		%r13, 0			;//   %r13 := dx  =  0
surface_move_CLIP_L:					;// }
		;//
		sub		%r10, %r13		;// %r10 := dst->w - dx
		cmp		%r6, %r10		;// if(w >  dst->w - dx) {
		jrle		surface_move_CLIP_R
		ld.w		%r6, %r10		;//   %r6  := w = dst->w - dx
surface_move_CLIP_R:
		add		%r10, %r13		;// %r10 := dst->w
		;//
		cmp		%r6, 0			;// if(w <= 0) return
		jrle		surface_move_RET
		;// 転送先矩形の垂直クリッピングを行います。
		cmp		%r14, 0			;// if(dy < 0) {
		jrge		surface_move_CLIP_T
		add		%r7, %r14		;//   %r7  := h  += dy
		sub		%r5, %r14		;//   %r5  := sy -= dy
		ld.w		%r14, 0			;//   %r14 := dy  =  0
surface_move_CLIP_T:					;// }
		;//
		sub		%r11, %r14		;// %r11 := dst->h - dy
		cmp		%r7, %r11		;// if(h >  dst->h - dy) {
		jrle		surface_move_CLIP_B
		ld.w		%r7, %r11		;//   %r7  := h = dst->h - dy
surface_move_CLIP_B:
		add		%r11, %r14		;// %r11 := dst->h
		;//
		cmp		%r7, 0			;// if(h <= 0) return
		jrle		surface_move_RET
		;// 転送先矩形と転送元矩形の、左上先頭ピクセルアドレスを求めます。
		mlt.h		%r10, %r14		;// %alr :=                    dst->w * dy
		ld.w		%r9, %alr		;// %r9  :=                    dst->w * dy
		add		%r9, %r13		;// %r9  :=                    dst->w * dy + dx
		add		%r12, %r9		;// %r12 := dptr = &dst->vbuff[dst->w * dy + dx]
		;//
		ld.h		%r11, [%r15]		;// %r11 := src->w
		xld.w		%r15, [%r15+4]		;// %r15 := src->vbuff
		;//
		mlt.h		%r11, %r5		;// %alr :=                    src->w * sy
		ld.w		%r9, %alr		;// %r9  :=                    src->w * sy
		add		%r9, %r4		;// %r9  :=                    src->w * sy + sx
		add		%r15, %r9		;// %r12 := sptr = &src->vbuff[src->w * sy + sx]
		;// %r6  := w
		;// %r7  := h
		;// %r10 := dst->w
		;// %r11 := src->w
		;// %r12 := dptr
		;// %r15 := sptr
		cmp		%r12, %r15
		jrgt		surface_move_B
		;// 転送先矩形が転送元矩形の前方ならば、左上先頭からコピーします。
		sub		%r10, %r6		;// %r10 := dst->w - w
		sub		%r11, %r6		;// %r11 := src->w - w
		;// %r6  := w
		;// %r7  := y = h
		;// %r10 := dst->w - w
		;// %r11 := src->w - w
		;// %r12 := dptr
		;// %r15 := sptr
surface_move_F_LOOP_Y:					;// do {
		ld.w		%r4, %r6		;//   %r4  := x = w
surface_move_F_LOOP_X:					;//   do {
		ld.b		%r9, [%r15]+		;//     *dptr++ = *sptr++
		ld.b		[%r12]+, %r9
		sub		%r4, 1			;//     %r4  := x--
		jrne		surface_move_F_LOOP_X	;//   } while(x)
		add		%r12, %r10		;//   %r12 := dptr += dst->w - w
;//		add		%r15, %r11		;//   %r15 := sptr += src->w - w -------+
		sub		%r7, 1			;//   %r7  := y--			|
		jrne.d		surface_move_F_LOOP_Y	;// } while(y)				|
		add		%r15, %r11		;//   %r15 := sptr += src->w - w <------+
		jp		surface_move_RET
surface_move_B:
		;// 転送先矩形が転送元矩形の後方ならば、右下末尾からコピーします。
		sub		%r6, 1			;// %r6  := w - 1
		sub		%r7, 1			;// %r7  := h - 1
		;//
		mlt.h		%r10, %r7		;// %alr :=         dst->w * (h - 1)
		ld.w		%r9, %alr		;// %r9  :=         dst->w * (h - 1)
		add		%r9, %r6		;// %r9  :=         dst->w * (h - 1) + (w - 1)
		add		%r12, %r9		;// %r12 := dptr += dst->w * (h - 1) + (w - 1)
		;//
		mlt.h		%r11, %r7		;// %alr :=         src->w * (h - 1)
		ld.w		%r9, %alr		;// %r9  :=         src->w * (h - 1)
		add		%r9, %r6		;// %r9  :=         src->w * (h - 1) + (w - 1)
		add		%r15, %r9		;// %r15 := sptr += src->w * (h - 1) + (w - 1)
		;//
		add		%r6, 1			;// %r6  := w
		add		%r7, 1			;// %r7  := h
		;//
		sub		%r10, %r6		;// %r10 := dst->w - w
		sub		%r11, %r6		;// %r11 := src->w - w
		;// %r6  := w
		;// %r7  := y = h
		;// %r10 := dst->w - w
		;// %r11 := src->w - w
		;// %r12 := dptr
		;// %r15 := sptr
surface_move_B_LOOP_Y:					;// do {
		ld.w		%r4, %r6		;//   %r4  := x = w
surface_move_B_LOOP_X:					;//   do {
		ld.b		%r9, [%r15]		;//     *dptr-- = *sptr--
		sub		%r15, 1			;//						*anti-interlock*
		ld.b		[%r12], %r9
;//		sub		%r12, 1			;// --------------------+
		sub		%r4, 1			;//     %r4  := x--	|
		jrne.d		surface_move_B_LOOP_X	;//   } while(x)	|
		sub		%r12, 1			;// <-------------------+			*delay*
		sub		%r12, %r10		;//   %r12 := dptr -= dst->w - w
;//		sub		%r15, %r11		;//   %r15 := sptr -= src->w - w -------+
		sub		%r7, 1			;//   %r7  := y--			|
		jrne.d		surface_move_B_LOOP_Y	;// } while(y)				|
		sub		%r15, %r11		;//   %r15 := sptr -= src->w - w <------+	*delay*
surface_move_RET:
		ret
");
#endif /*REND_NOASM*/

#ifdef REND_NOASM
int texture_point(TEXTURE* texture, int x, int y) {
	int texel_offset;
	int texel_mask;
	int byte_offset;
	int shift_count;

	/* テクスチャの範囲外ならば、透明色と見なします。 */
	if(((unsigned)x >= (unsigned)texture->header.w) ||
	   ((unsigned)y >= (unsigned)texture->header.h)) {
		return -1;
	}

	/* 左上原点からの、テクセルオフセットを求めます。 */
	texel_offset = ((texture->header.w + 7) & ~7) * y + x;
	/* 不透明色の最大値(==テクセルマスク)を求めます。 */
	texel_mask = (1 << texture->header.bpp) - 1;

	/* マスク付きテクスチャならば… */
	if(texture->header.mask) {
		/* マスクへのバイトオフセットと、シフト数を求めます。 */
		byte_offset =  texel_offset >> 3;
		shift_count = ~texel_offset &  7;
		/* マスクがセットされていなければ、透明色を返します。 */
		if(!((texture->mask[byte_offset] >> shift_count) & 1)) {
			return -1;
		}
	}

	/* テクセルへのバイトオフセットと、シフト数を求めます。 */
	byte_offset = ( texel_offset * texture->header.bpp) >> 3;
	shift_count = (~texel_offset * texture->header.bpp) &  7;
	/* テクセル値を返します。 */
	return (texture->buf[byte_offset] >> shift_count) & texel_mask;
}
#else /*REND_NOASM*/
int texture_point(TEXTURE* texture, int x, int y);
asm("
		.code
		.align		1
		.global		texture_point
texture_point:
		;// %r12 := texture
		;// %r13 := x
		;// %r14 := y
		add		%r12, 8			;// %r12 :=        &texture->header.bpp
		ld.ub		%r4, [%r12]+		;// %r4  := bpp   = texture->header.bpp
		ld.ub		%r5, [%r12]+		;// %r5  := mask  = texture->header.mask
		ld.h		%r6, [%r12]+		;// %r6  := w     = texture->header.w
		ld.h		%r7, [%r12]		;// %r7  := h     = texture->header.h
		cmp		%r13, %r6		;// if((unsigned)x >= (unsigned)w)
		jruge		texture_point_RET_M1	;//   return -1
		cmp		%r14, %r7		;// if((unsigned)y >= (unsigned)h)
		jruge		texture_point_RET_M1	;//   return -1
		add		%r12, 8			;// %r12 :=        &texture->buf
		ld.w		%r10, [%r12]+		;// %r10 := pbuf  = texture->buf
		ld.w		%r11, [%r12]		;// %r11 := pmask = texture->mask
		;// %r4  := bpp
		;// %r5  := mask
		;// %r6  := w
		;// %r7  := h
		;// %r10 := pbuf
		;// %r11 := pmask
		;// %r13 := x
		;// %r14 := y
		add		%r6, 7			;// %r6  :=                  w + 7
		and		%r6, -8			;// %r6  :=                 (w + 7) & ~7
		mlt.h		%r6, %r14		;// %alr :=                ((w + 7) & ~7) * y
		ld.w		%r6, %alr		;// %r6  :=                ((w + 7) & ~7) * y
		add		%r6, %r13		;// %r6  := texel_offset = ((w + 7) & ~7) * y + x
		ld.w		%r7, 1			;// %r7  :=               1
		sla		%r7, %r4		;// %r7  :=               1 << bpp
;//		sub		%r7, 1			;// %r7  := texel_mask = (1 << bpp) - 1 --------+
		;// %r4  := bpp										|
		;// %r5  := mask									|
		;// %r6  := texel_offset								|
		;// %r7  := texel_mask									|
		;// %r10 := pbuf									|
		;// %r11 := pmask									|
		cmp		%r5, 0			;// if(mask) {					|
		jreq.d		texture_point_NO_MASK	;//						|
		sub		%r7, 1			;// <-------------------------------------------+	*delay*
		ld.w		%r12, %r6		;//   %r12 :=                texel_offset
		sra		%r12, 3			;//   %r12 := byte_offset =  texel_offset >> 3
		not		%r13, %r6		;//   %r13 :=               ~texel_offset
;//		and		%r13, 7			;//   %r13 := shift_count = ~texel_offset &  7 -+
		add		%r11, %r12		;//   %r11 :=    &pmask[byte_offset]		|
		ld.ub		%r11, [%r11]		;//   %r11 := m = pmask[byte_offset]		|
		and		%r13, 7			;// <-------------------------------------------+	*anti-interlock*
		sra		%r11, %r13		;//   %r11 := m >>= shift_count
		and		%r11, 1			;//   if(!(m & 1))
		jreq		texture_point_RET_M1	;//     return -1
texture_point_NO_MASK:					;// }
		;// %r4  := bpp
		;// %r6  := texel_offset
		;// %r7  := texel_mask
		;// %r10 := pbuf
		mlt.h		%r6, %r4		;// %alr :=                 texel_offset * bpp
		ld.w		%r12, %alr		;// %r12 :=                 texel_offset * bpp
		sra		%r12, 3			;// %r12 := byte_offset = ( texel_offset * bpp) >> 3
		not		%r13, %r6		;// %r13 :=                ~texel_offset
		mlt.h		%r13, %r4		;// %alr :=                ~texel_offset * bpp
		ld.w		%r13, %alr		;// %r13 :=                ~texel_offset * bpp
;//		and		%r13, 7			;// %r13 := shift_count = (~texel_offset * bpp) &  7 ---+
		add		%r10, %r12		;// %r10 :=    &pbuf[byte_offset]			|
		ld.ub		%r10, [%r10]		;// %r10 := c = pbuf[byte_offset]			|
		and		%r13, 7			;// <---------------------------------------------------+	*anti-interlock*
		sra		%r10, %r13		;// %r10 := c >>= shift_count
		ret.d
		and		%r10, %r7		;// %r10 := c &= texel_mask	*delay*
texture_point_RET_M1:
		ret.d
		ld.w		%r10, -1		;// %r10 := -1	*delay*
");
#endif /*REND_NOASM*/

#ifdef REND_NOASM
void texture_pset(TEXTURE* texture, int x, int y, int color) {
	int texel_offset;
	int texel_mask;
	int byte_offset;
	int shift_count;

	/* テクスチャの範囲外ならば、何もせずに帰ります。 */
	if(((unsigned)x >= (unsigned)texture->header.w) ||
	   ((unsigned)y >= (unsigned)texture->header.h)) {
		return;
	}

	/* 左上原点からの、テクセルオフセットを求めます。 */
	texel_offset = ((texture->header.w + 7) & ~7) * y + x;
	/* 不透明色の最大値(==テクセルマスク)を求めます。 */
	texel_mask = (1 << texture->header.bpp) - 1;

	/* マスク付きテクスチャならば… */
	if(texture->header.mask) {
		/* マスクへのバイトオフセットと、シフト数を求めます。 */
		byte_offset =  texel_offset >> 3;
		shift_count = ~texel_offset &  7;
		/* 不透明色ならば… */
		if((unsigned)color <= (unsigned)texel_mask) {
			/* マスクビットをセットします。 */
			texture->mask[byte_offset] |=  (1 << shift_count);
		/* 透明色ならば… */
		} else {
			/* マスクビットをクリアします。 */
			texture->mask[byte_offset] &= ~(1 << shift_count);
			/* 透明色のテクセル値はゼロとします。
			 * 仕様ではありませんが、コンバータで変換したデータは通常そうなっているので、合わせることにしました。
			 */
			color = 0;
		}
	}
	/* マスク無しテクスチャの場合、指定されたカラー値の補正は行っていません。
	 * テクスチャのビット深度に応じた、最大値以上のカラー値を指定した場合の結果は、未定義です。
	 * おそらく、隣のテクセルにも、誤ったテクセル値を書き込むことになります。
	 * ただし、テクスチャメモリ外への書き込みを行わないことだけは保証します。
	 * テクスチャイメージの生成結果が重要で無い処理ならば、不正な不透明カラー値を指定しても、プログラム自体は動作します。
	 */

	/* テクセルへのバイトオフセットと、シフト数を求めます。 */
	byte_offset = ( texel_offset * texture->header.bpp) >> 3;
	shift_count = (~texel_offset * texture->header.bpp) &  7;
	/* テクセル値を変更します。 */
	texture->buf[byte_offset] &= ~(texel_mask << shift_count);
	texture->buf[byte_offset] |=  (color      << shift_count);
}
#else /*REND_NOASM*/
void texture_pset(TEXTURE* texture, int x, int y, int color);
asm("
		.code
		.align		1
		.global		texture_pset
texture_pset:
		;// %r12 := texture
		;// %r13 := x
		;// %r14 := y
		;// %r15 := color
		add		%r12, 8			;// %r12 :=        &texture->header.bpp
		ld.ub		%r4, [%r12]+		;// %r4  := bpp   = texture->header.bpp
		ld.ub		%r5, [%r12]+		;// %r5  := mask  = texture->header.mask
		ld.h		%r6, [%r12]+		;// %r6  := w     = texture->header.w
		ld.h		%r7, [%r12]		;// %r7  := h     = texture->header.h
		cmp		%r13, %r6		;// if((unsigned)x >= (unsigned)w)
		jruge		texture_pset_RET	;//   return
		cmp		%r14, %r7		;// if((unsigned)y >= (unsigned)h)
		jruge		texture_pset_RET	;//   return
		add		%r12, 8			;// %r12 :=        &texture->buf
		ld.w		%r10, [%r12]+		;// %r10 := pbuf  = texture->buf
		ld.w		%r11, [%r12]		;// %r11 := pmask = texture->mask
		;// %r4  := bpp
		;// %r5  := mask
		;// %r6  := w
		;// %r7  := h
		;// %r10 := pbuf
		;// %r11 := pmask
		;// %r13 := x
		;// %r14 := y
		;// %r15 := color
		add		%r6, 7			;// %r6  :=                  w + 7
		and		%r6, -8			;// %r6  :=                 (w + 7) & ~7
		mlt.h		%r6, %r14		;// %alr :=                ((w + 7) & ~7) * y
		ld.w		%r6, %alr		;// %r6  :=                ((w + 7) & ~7) * y
		add		%r6, %r13		;// %r6  := texel_offset = ((w + 7) & ~7) * y + x
		ld.w		%r7, 1			;// %r7  :=               1
		sla		%r7, %r4		;// %r7  :=               1 << bpp
;//		sub		%r7, 1			;// %r7  := texel_mask = (1 << bpp) - 1	--------+
		;// %r4  := bpp										|
		;// %r5  := mask									|
		;// %r6  := texel_offset								|
		;// %r7  := texel_mask									|
		;// %r10 := pbuf									|
		;// %r11 := pmask									|
		;// %r15 := color									|
		cmp		%r5, 0			;// if(mask) {					|
		jreq.d		texture_pset_NO_MASK	;//						|
		sub		%r7, 1			;// <-------------------------------------------+	*delay*
		ld.w		%r12, %r6		;//   %r12 :=                texel_offset
		sra		%r12, 3			;//   %r12 := byte_offset =  texel_offset >> 3
		not		%r13, %r6		;//   %r13 :=               ~texel_offset
;//		and		%r13, 7			;//   %r13 := shift_count = ~texel_offset &  7 -+
		add		%r11, %r12		;//   %r11 :=    &pmask[byte_offset]		|
		ld.ub		%r12, [%r11]		;//   %r12 := m = pmask[byte_offset]		|
		and		%r13, 7			;// <-------------------------------------------+	*anti-interlock*
		rr		%r12, %r13		;//   %r12 := m (rr)= shift_count
		cmp		%r15, %r7		;//   if((unsigned)color > (unsigned)texel_mask) {
		jrule.d		4			;//
		or		%r12, 1			;//   %r12 := m |= 1	*delay*
		 and		%r12, -2		;//     %r12 := m &= ~1
		 ld.w		%r15, 0			;//     %r15 := color = 0
		rl		%r12, %r13		;//   %r12 := m (rl)= shift_count
		ld.b		[%r11], %r12		;//   pmask[byte_offset] = m
texture_pset_NO_MASK:					;// }
		;// %r4  := bpp
		;// %r6  := texel_offset
		;// %r7  := texel_mask
		;// %r10 := pbuf
		;// %r15 := color
		mlt.h		%r6, %r4		;// %alr :=                 texel_offset * bpp
		ld.w		%r12, %alr		;// %r12 :=                 texel_offset * bpp
		sra		%r12, 3			;// %r12 := byte_offset = ( texel_offset * bpp) >> 3
		not		%r13, %r6		;// %r13 :=                ~texel_offset
		mlt.h		%r13, %r4		;// %alr :=                ~texel_offset * bpp
		ld.w		%r13, %alr		;// %r13 :=                ~texel_offset * bpp
;//		and		%r13, 7			;// %r13 := shift_count = (~texel_offset * bpp) &  7 ---+
		add		%r10, %r12		;// %r10 :=    &pbuf[byte_offset]			|
		ld.ub		%r11, [%r10]		;// %r11 := c = pbuf[byte_offset]			|
		and		%r13, 7			;// <---------------------------------------------------+	*anti-interlock*
		rr		%r11, %r13		;// %r10 := c (rr)= shift_count
		not		%r7, %r7		;// %r7  :=      ~texel_mask
		and		%r11, %r7		;// %r11 := c &= ~texel_mask
		or		%r11, %r15		;// %r11 := c |=  color
		rl		%r11, %r13		;// %r11 := c (rl)= shift_count
		ld.b		[%r10], %r11		;// pbuf[byte_offset] = c
texture_pset_RET:
		ret
");
#endif /*REND_NOASM*/

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

void
render_init_2d(RENDER* render, SURFACE* surface, int x, int y, int width, int height)
{
	/* まずクリアします。 */
	memset(render, 0, sizeof(RENDER));

	/* サーフェイスを設定します。 */
	render->surface = surface;

	/* ビューポート矩形を設定します。 */
	if(width == 0) width = surface->w;
	if(height == 0) height = surface->h;
	render->left = x;
	render->top = y;
	render->right = x + width;
	render->bottom = y + height;
	if(render->left < 0 ) render->left = 0;
	if(render->top < 0 ) render->top = 0;
	if(render->right > surface->w) render->right = surface->w;
	if(render->bottom > surface->h) render->bottom = surface->h;

	/* コンテキストスタックを初期化します。 */
	render->context = render->context_stack;
}

void
render_save(RENDER* render)
{
	/* スタックトップを複製します。 */
	if(++render->context == render->context_stack + RENDER_CONTEXT_STACK_SIZE) DIE();
	memcpy(render->context, render->context - 1, sizeof(RENDERCONTEXT));
}

void
render_restore(RENDER* render)
{
	/* スタックトップを捨てます。 */
	if(render->context-- == render->context_stack) DIE();
}

//{{2007/11/01:ASM化に伴い削除
//void
//render_point(RENDER* render, int x, int y, int color)
//{
//	SURFACE* surface;
//	unsigned char* vptr;
//	int ofs;
//
//	/* サーフェイスを取り出しておきます。 */
//	surface = render->surface;
//
//	/* ビューポート矩形外なら、描きません。 */
//	if(x < render->left || render->right  <= x ||
//	   y < render->top  || render->bottom <= y) return;
//
//	/* ピクセルアドレスを求めます。 */
//	ofs = surface->w * y + x;
//	vptr = surface->vbuff + ofs;
//
//	/* ピクセルを描きます。 */
//	*vptr = color;
//}
//}}2007/11/01:ASM化に伴い削除
//↓ * Thu Nov 01 15:46:12 JST 2007 Naoyuki Sawa
//↓ - render_point()をASM化しました。
//↓   ASM版は、C版に較べて約7%高速です。
void render_point(RENDER* render, int x, int y, int color);
asm("
	.code
	.align	1
	.global	render_point
render_point:
	ld.w	%r4, [%r12]+		; %r4  = surface
	;
	ld.h	%r9, [%r12]+		; %r9  = left
	cmp	%r13, %r9		; if(x < left) return
	jrlt	render_point_RET
	ld.h	%r9, [%r12]+		; %r9  = top
	cmp	%r14, %r9		; if(y < top) return
	jrlt	render_point_RET
	ld.h	%r9, [%r12]+		; %r9  = right
	cmp	%r13, %r9		; if(x >= right) return
	jrge	render_point_RET
	ld.h	%r9, [%r12]		; %r9  = bottom
	cmp	%r14, %r9		; if(y >= bottom) return	!INTERLOCK!
	jrge	render_point_RET
	;
	ld.w	%r5, [%r4]+		; %r5  = w | (h<<16)		(h<<16)はmlt.hで無視される
	ld.w	%r4, [%r4]		; %r4  = vbuff
	mlt.h	%r5, %r14		; %alr =         w * y
	ld.w	%r9, %alr		; %r9  =         w * y
	add	%r4, %r9		; %r4  = vbuff + w * y
	add	%r4, %r13		; %r4  = vbuff + w * y + x
	ld.b	[%r4], %r15
render_point_RET:
	ret
");

/* 全く高速化していません。遅いです。
 * ゲームでは、高速塗りつぶしが要求される場面は少ないと思うので、
 * 現状のままでも特に問題はないと思います。
 */
int
render_paint(RENDER* render, int x0, int y0, int color)
{
/* 塗りつぶし点スタックサイズ：
 * 実験したところ、かなり複雑な塗りつぶしでも24程度で充分でしたが、
 * 念のため、余裕を見込んで、大きめに取っておくことにします。
 * あまり大きすぎると、P/ECEではスタックがあふれるので、ご注意ください。
 */
#define RENDER_PAINT_STACK_SIZE	64

	SURFACE* surface;
	short stack[RENDER_PAINT_STACK_SIZE][2]; /* メモリ節約のためshortで */
	int i_stack, ofs, x, y, x1, x2;
	unsigned char c;
	int cnt = 0; /* ここに塗ったピクセル数をカウントします */

	/* サーフェイスを取り出しておきます。 */
	surface = render->surface;

	/* 塗りつぶし開始点がビューポート外なら、何もしません。 */
	if(x0 < render->left || render->right  - 1 < x0) return 0;
	if(y0 < render->top  || render->bottom - 1 < y0) return 0;

	/* 塗りつぶされる色を取得します。 */
	ofs = surface->w * y0 + x0;
	c = surface->vbuff[ofs];

	/* 塗りつぶし開始点が既に塗りつぶす色なら、何もしません。 */
	/* if(c == color) return 0; */
	/* ->よく考えたら、このチェックは不要でした。
	 *   もしも塗りつぶし開始点が既に塗りつぶす色なら、
	 *   1. スタックに最初の(x0,y0)を積む。
	 *   2. while()ループの中で(x0,y0)を取り出す。
	 *   3. 既に塗られているので、continueで飛ばされる。
	 *   4. スタックは空なので、終了する。
	 *   となり、何もせずに終了します。
	 */

	/* 塗りつぶし開始点をスタックに積みます。 */
	stack[0][0] = x0;
	stack[0][1] = y0;
	i_stack = 1;

	/* 塗りつぶし点がなくなるまで... */
	while(i_stack != 0) {
		/* 塗りつぶし点を取り出します。 */
		i_stack--;
		x0 = stack[i_stack][0];
		y0 = stack[i_stack][1];

		/* 既に塗られていたら、飛ばします。 */
		ofs = surface->w * y0 + x0;
		if(surface->vbuff[ofs] == color) continue;

		/* 塗られる色の左端を探します。 */
		x1 = x0;
		for(;;) {
			x1--;
			if(x1 < render->left) break; /* ビューポート外 */
			ofs = surface->w * y0 + x1;
			if(surface->vbuff[ofs] != c) break; /* 別色 */
		}
		x1++; /* 一つ行き過ぎているので戻す */

		/* 塗られる色の右端を探します。 */
		x2 = x0;
		for(;;) {
			x2++;
			if(x2 > render->right - 1) break; /* ビューポート外 */
			ofs = surface->w * y0 + x2;
			if(surface->vbuff[ofs] != c) break; /* 別色 */
		}
		x2--; /* 一つ行き過ぎているので戻す */

		/* 左右の間を塗りつぶします。 */
		for(x = x1; x <= x2; x++) {
			ofs = surface->w * y0 + x;
			surface->vbuff[ofs] = color;
			cnt++; /* 塗ったピクセル数をカウント */
			/* ここでpceLCDTrans()を呼ぶと、塗りつぶしの様子を1ピクセル単位で観察できます。 */
			/* pceLCDTrans() */
		}
		/* ここでpceLCDTrans()を呼ぶと、塗りつぶしの様子を1ライン単位で観察できます。 */
		/* pceLCDTrans() */

		/* 上のラインの調査。 */
		if(y0 != render->top) {
			y = y0 - 1;
			x = x1;
			for(;;) {
				/* 塗られる色の始まりを探します。 */
				for(;;) {
					if(x > x2) break;
					ofs = surface->w * y + x;
					if(surface->vbuff[ofs] == c) break;
					x++;
				}
				if(x > x2) break; /* もうない */

				/* 塗られる色の終わりを探します。 */
				for(;;) {
					if(x > x2) break;
					ofs = surface->w * y + x;
					if(surface->vbuff[ofs] != c) break;
					x++;
				}

				/* 塗られる色の終わりの位置をスタックに積みます。 */
				if(i_stack < RENDER_PAINT_STACK_SIZE) {
					stack[i_stack][0] = x - 1; /* 一つ行き過ぎているので戻す */
					stack[i_stack][1] = y;
					i_stack++;
				}
			}
		}

		/* 下のラインの調査。 */
		if(y0 != render->bottom - 1) {
			y = y0 + 1;
			x = x1;
			for(;;) {
				/* 塗られる色の始まりを探します。 */
				for(;;) {
					if(x > x2) break;
					ofs = surface->w * y + x;
					if(surface->vbuff[ofs] == c) break;
					x++;
				}
				if(x > x2) break; /* もうない */

				/* 塗られる色の終わりを探します。 */
				for(;;) {
					if(x > x2) break;
					ofs = surface->w * y + x;
					if(surface->vbuff[ofs] != c) break;
					x++;
				}

				/* 塗られる色の終わりの位置をスタックに積みます。 */
				if(i_stack < RENDER_PAINT_STACK_SIZE) {
					stack[i_stack][0] = x - 1; /* 一つ行き過ぎているので戻す */
					stack[i_stack][1] = y;
					i_stack++;
				}
			}
		}
	}

	return cnt;

#undef RENDER_PAINT_STACK_SIZE
}

/* * Mon Dec 20 19:13:00 JST 2004 Naoyuki Sawa
 * - render_line()をアセンブラで高速化し、framline.sへ移動しました。
 */
#ifdef REND_NOASM
void
render_line(RENDER* render, int x0, int y0, int x1, int y1, int color)
{
	/* 一方向クリッピング用マクロ。 */
#define CLIP(X, Y, A, B, OP, LIM) \
	if(!((X##A) OP (LIM))) { \
		if(!((X##B) OP (LIM))) { \
			return; \
		} else { \
			(Y##A) = (Y##A) + \
			((Y##B) - (Y##A)) * ((LIM) - (X##A)) / ((X##B) - (X##A)); \
			(X##A) = (LIM); \
			clipped |= (A); /* (x1,y1)側をクリップしたらclipped=1 */ \
		} \
	}

	SURFACE* surface;
	unsigned char* vptr;
	int nx, ny;
	int ux, uy;
	int d, n;
	int tmp, ofs;
	/* 終点側のクリッピングフラグ。
	 * 初期値は0で、(x1,y1)側がクリップされると1になります。
	 * (x0,y0)側がクリップされたかどうかには影響されません。
	 * 線は常に、(x0,y0)から(x1,y1)に向かって描かれます。
	 * 連続線を想定しているので、通常、終点は描きませんが、
	 * (x1,y1)側がクリップされた場合のみ、終点も描きます。
	 */
	int clipped = 0;

	/* サーフェイスを取り出しておきます。 */
	surface = render->surface;

	/* 各頂点を、左右上下でクリップします。 */
#if 0	//{{2004/12/20以前のコード、安定して動いていたので念のため保存
	//CLIP(x, y, 0, 1, >=, render->left);
	//CLIP(x, y, 0, 1, <=, render->right - 1);
	//CLIP(x, y, 1, 0, >=, render->left);
	//CLIP(x, y, 1, 0, <=, render->right - 1);
	//CLIP(y, x, 0, 1, >=, render->top);
	//CLIP(y, x, 0, 1, <=, render->bottom - 1);
	//CLIP(y, x, 1, 0, >=, render->top);
	//CLIP(y, x, 1, 0, <=, render->bottom - 1);
	//}}2004/12/20以前のコード、安定して動いていたので念のため保存
#else	//↓2004/12/20 Naoyuki Sawa アセンブラ版の処理順序に合わせるため変更しました。
	//{{2004/12/20以降のコード
	// * Mon Dec 20 18:19:00 JST 2004 Naoyuki Sawa
	// - クリッピング除算時の誤差が、処理順序によってわずかに異なるため、
	//   これまでの描画結果とは数ピクセル異なる場合があります。
	//   “一方の処理順序の方がより正確”というものではないので、問題ありません。
	CLIP(x, y, 0, 1, >=, render->left);
	CLIP(x, y, 1, 0, >=, render->left);
	CLIP(x, y, 0, 1, <=, render->right - 1);
	CLIP(x, y, 1, 0, <=, render->right - 1);
	CLIP(y, x, 0, 1, >=, render->top);
	CLIP(y, x, 1, 0, >=, render->top);
	CLIP(y, x, 0, 1, <=, render->bottom - 1);
	CLIP(y, x, 1, 0, <=, render->bottom - 1);
#endif	//}}2004/12/20以降のコード

	/* 始点アドレスを求めます。 */
	ofs = surface->w * y0 + x0;
	vptr = surface->vbuff + ofs;

	/* X軸のDDAパラメタを計算します。 */
	if(x1 >= x0) {
		nx = x1 - x0;
		ux =  1;
	} else {
		nx = x0 - x1;
		ux = -1;
	}

	/* Y軸のDDAパラメタを計算します。 */
	if(y1 >= y0) {
		ny = y1 - y0;
		uy =  surface->w;
	} else {
		ny = y0 - y1;
		uy = -surface->w;
	}

	/* 長い方を主軸(X)、もう一方を副軸(Y)とします。 */
	if(nx < ny) {
		//tmp = x0; x0 = y0; y0 = tmp; → 不要でした。2004/12/20削除
		//tmp = x1; x1 = y1; y1 = tmp; → 不要でした。2004/12/20削除
		tmp = nx; nx = ny; ny = tmp;
		tmp = ux; ux = uy; uy = tmp;
	}
	if(nx == 0) return; /* 点なら描きません。 */

	/* DDAループ。 */
#if 0	//{{2004/12/20以前のコード、安定して動いていたので念のため保存
	//d = -nx;
	//n = nx + clipped; /* 終点側がクリップされたら、終点も描く。 */
	//do {
	//	*vptr = color;
	//	vptr += ux;
	//	d += ny * 2;
	//	if(d >= 0) {
	//		vptr += uy;
	//		d -= nx * 2;
	//	}
	//} while(--n); /* 終点は描かないので、プリデクリメントです。   */
	//	      /* 終点側がクリップされたら、終点も描きますが、 */
	//	      /* その場合は、ループ前に予め+1されているので、 */
	//	      /* いずれにせよここではプリデクリメントです。   */
	//}}2004/12/20以前のコード、安定して動いていたので念のため保存
#else	//↓2004/12/20 Naoyuki Sawa 高速化のため変更しました。
	//{{2004/12/20以降のコード
	// * Mon Dec 20 06:36:00 JST 2004 Naoyuki Sawa
	// - DDAの誤差項(変数d)を、マイナスから0以上への増加方向でカウントするよりも、
	//   プラスから0以下への減少方向でカウントする方が僅かに高速化できます。
	//   S1C33にはNeg命令が無く、"d=-nx"よりも"d=nx"の方が一命令少なくて済むからです。
	//   セットアップ時だけのごく僅かな違いに過ぎず、ループ中の処理速度は同じなので、
	//   実質、ほとんど違いはないのですけれど、一応、高速化しておくことにしました。
	//   2004/12/20現時点では、他の関数のDDAセットアップ(ポリゴン描画など)の実装も、
	//   DDAの誤差項を、マイナスから0以上への増加方向でカウントしているので、
	//   同様の方法で僅かずつ高速化可能だと思います。(いずれ、行う予定です)
	d = nx;
	n = nx + clipped; /* 終点側がクリップされたら、終点も描く。 */
	do {
		*vptr = color;
		vptr += ux;
		d -= ny * 2;
		if(d <= 0) {
			vptr += uy;
			d += nx * 2;
		}
	} while(--n); /* 終点は描かないので、プリデクリメントです。   */
		      /* 終点側がクリップされたら、終点も描きますが、 */
		      /* その場合は、ループ前に予め+1されているので、 */
		      /* いずれにせよここではプリデクリメントです。   */
#endif	//}}2004/12/20以降のコード

#undef CLIP
}
#endif /*REND_NOASM*/

/* render_line()をベースに、破線パターンの取り出しだけ追加しました。
 * render_line()を変更・修正したときは、render_dashes()も追従してください。
 */
int
render_dashes(RENDER* render, int x0, int y0, int x1, int y1, unsigned i_pat, const unsigned char* pat, unsigned n_pat)
{
	/* 一方向クリッピング用マクロ。 */
#define CLIP(X, Y, A, B, OP, LIM) \
	if(!((X##A) OP (LIM))) { \
		if(!((X##B) OP (LIM))) { \
			return i_pat; \
		} else { \
			(Y##A) = (Y##A) + \
			((Y##B) - (Y##A)) * ((LIM) - (X##A)) / ((X##B) - (X##A)); \
			(X##A) = (LIM); \
			clipped |= (A); /* (x1,y1)側をクリップしたらclipped=1 */ \
		} \
	}

	SURFACE* surface;
	unsigned char *vptr, color;
	int nx, ny;
	int ux, uy;
	int d, n;
	int tmp, ofs;
	/* 終点側のクリッピングフラグ。
	 * 初期値は0で、(x1,y1)側がクリップされると1になります。
	 * (x0,y0)側がクリップされたかどうかには影響されません。
	 * 線は常に、(x0,y0)から(x1,y1)に向かって描かれます。
	 * 連続線を想定しているので、通常、終点は描きませんが、
	 * (x1,y1)側がクリップされた場合のみ、終点も描きます。
	 */
	int clipped = 0;

	/* サーフェイスを取り出しておきます。 */
	surface = render->surface;

	/* 各頂点を、左右上下でクリップします。 */
#if 0	//{{2004/12/20以前のコード、安定して動いていたので念のため保存
	//CLIP(x, y, 0, 1, >=, render->left);
	//CLIP(x, y, 0, 1, <=, render->right - 1);
	//CLIP(x, y, 1, 0, >=, render->left);
	//CLIP(x, y, 1, 0, <=, render->right - 1);
	//CLIP(y, x, 0, 1, >=, render->top);
	//CLIP(y, x, 0, 1, <=, render->bottom - 1);
	//CLIP(y, x, 1, 0, >=, render->top);
	//CLIP(y, x, 1, 0, <=, render->bottom - 1);
	//}}2004/12/20以前のコード、安定して動いていたので念のため保存
#else	//↓2004/12/20 Naoyuki Sawa アセンブラ版の処理順序に合わせるため変更しました。
	//{{2004/12/20以降のコード
	// * Mon Dec 20 18:19:00 JST 2004 Naoyuki Sawa
	// - クリッピング除算時の誤差が、処理順序によってわずかに異なるため、
	//   これまでの描画結果とは数ピクセル異なる場合があります。
	//   “一方の処理順序の方がより正確”というものではないので、問題ありません。
	CLIP(x, y, 0, 1, >=, render->left);
	CLIP(x, y, 1, 0, >=, render->left);
	CLIP(x, y, 0, 1, <=, render->right - 1);
	CLIP(x, y, 1, 0, <=, render->right - 1);
	CLIP(y, x, 0, 1, >=, render->top);
	CLIP(y, x, 1, 0, >=, render->top);
	CLIP(y, x, 0, 1, <=, render->bottom - 1);
	CLIP(y, x, 1, 0, <=, render->bottom - 1);
#endif	//}}2004/12/20以降のコード

	/* 始点アドレスを求めます。 */
	ofs = surface->w * y0 + x0;
	vptr = surface->vbuff + ofs;

	/* X軸のDDAパラメタを計算します。 */
	if(x1 >= x0) {
		nx = x1 - x0;
		ux =  1;
	} else {
		nx = x0 - x1;
		ux = -1;
	}

	/* Y軸のDDAパラメタを計算します。 */
	if(y1 >= y0) {
		ny = y1 - y0;
		uy =  surface->w;
	} else {
		ny = y0 - y1;
		uy = -surface->w;
	}

	/* 長い方を主軸(X)、もう一方を副軸(Y)とします。 */
	if(nx < ny) {
		//tmp = x0; x0 = y0; y0 = tmp; → 不要でした。2004/12/20削除
		//tmp = x1; x1 = y1; y1 = tmp; → 不要でした。2004/12/20削除
		tmp = nx; nx = ny; ny = tmp;
		tmp = ux; ux = uy; uy = tmp;
	}
	if(nx == 0) return i_pat; /* 点なら描きません。 */

	/* 最初のパターンインデクスを確実に有効範囲内にします。 */
	i_pat %= n_pat;

	/* DDAループ。 */
#if 0	//{{2004/12/20以前のコード、安定して動いていたので念のため保存
	//d = -nx;
	//n = nx + clipped; /* 終点側がクリップされたら、終点も描く。 */
	//do {
	//	color = pat[i_pat++];
	//	if(i_pat == n_pat) i_pat = 0;
	//	if(color < 4) *vptr = color;
	//	vptr += ux;
	//	d += ny * 2;
	//	if(d >= 0) {
	//		vptr += uy;
	//		d -= nx * 2;
	//	}
	//} while(--n); /* 終点は描かないので、プリデクリメントです。   */
	//	      /* 終点側がクリップされたら、終点も描きますが、 */
	//	      /* その場合は、ループ前に予め+1されているので、 */
	//	      /* いずれにせよここではプリデクリメントです。   */
	//}}2004/12/20以前のコード、安定して動いていたので念のため保存
#else	//↓2004/12/20 Naoyuki Sawa 高速化のため変更しました。
	//{{2004/12/20以降のコード
	// * Mon Dec 20 06:36:00 JST 2004 Naoyuki Sawa
	// - DDAの誤差項(変数d)を、マイナスから0以上への増加方向でカウントするよりも、
	//   プラスから0以下への減少方向でカウントする方が僅かに高速化できます。
	//   S1C33にはNeg命令が無く、"d=-nx"よりも"d=nx"の方が一命令少なくて済むからです。
	//   セットアップ時だけのごく僅かな違いに過ぎず、ループ中の処理速度は同じなので、
	//   実質、ほとんど違いはないのですけれど、一応、高速化しておくことにしました。
	//   2004/12/20現時点では、他の関数のDDAセットアップ(ポリゴン描画など)の実装も、
	//   DDAの誤差項を、マイナスから0以上への増加方向でカウントしているので、
	//   同様の方法で僅かずつ高速化可能だと思います。(いずれ、行う予定です)
	d = nx;
	n = nx + clipped; /* 終点側がクリップされたら、終点も描く。 */
	do {
		color = pat[i_pat++];
		if(i_pat == n_pat) i_pat = 0;
		//if(color < 4) *vptr = color;
		//↓* Tue May 16 17:10:00 JST 2006 Naoyuki Sawa
		//↓- render_dashes()を16階調モードでも利用できるよう、透明色の定義を変更しました。
		//↓  これまでは4以上を透明色として扱っていましたが、今後は16以上を透明色とします。
		if(color < 16) *vptr = color;
		vptr += ux;
		d -= ny * 2;
		if(d <= 0) {
			vptr += uy;
			d += nx * 2;
		}
	} while(--n); /* 終点は描かないので、プリデクリメントです。   */
		      /* 終点側がクリップされたら、終点も描きますが、 */
		      /* その場合は、ループ前に予め+1されているので、 */
		      /* いずれにせよここではプリデクリメントです。   */
#endif	//}}2004/12/20以降のコード

	return i_pat;

#undef CLIP
}

void
render_rectangle(RENDER* render, int x, int y, int w, int h, int color)
{
#if 1
	int x0, y0, x1, y1;

	/* サイズが0x0なら何もしません。 */
	if(w == 0 || h == 0) return;

	/* 正規化します。(左上～右下の形) */
	if(w < 0) { x += w; w = -w; }
	if(h < 0) { y += h; h = -h; }

	/* 点の場合のみ、render_line()では描けないので、別処理です。 */
	if(w == 1 && h == 1) {
		render_point(render, x, y, color);
		return;
	}

	/* 矩形を描きます。
	 * * 現在の実装は簡単のためにrender_line()を使っていますが、
	 *   水平線・垂直線しか描かないので、もっと高速化可能です。
	 * * Sat Dec 09 00:21:59 JST 2006 Naoyuki Sawa
	 * - 試しに以下の「高速化に失敗した例」のように変更してみたところ、二倍以上遅くなってしまいました。
	 *   render_rectangle_fill()は意外と遅く、素直にrender_line()を使った方が速いみたいです。
	 *   結局、元のままのこちらのブロックのコードに戻しました。
	 */
	x0 = x;
	y0 = y;
	x1 = x + w - 1;
	y1 = y + h - 1;
	render_line(render, x0, y0, x1, y0, color); /* 左上～右上 */
	render_line(render, x1, y0, x1, y1, color); /* 右上～右下 */
	render_line(render, x1, y1, x0, y1, color); /* 右下～左下 */
	render_line(render, x0, y1, x0, y0, color); /* 左下～左上 */
#else	/*{{2006/12/09 高速化に失敗した例*/
//	/* サイズが0x0なら何もしません。 */
//	if(w == 0 || h == 0) return;
//
//	/* 正規化します。(左上～右下の形) */
//	if(w < 0) { x += w; w = -w; }
//	if(h < 0) { y += h; h = -h; }
//
//	/* 点の場合のみ、以下のアルゴリズムでは描けないので、別処理です。 */
//	if(w == 1 && h == 1) {
//		render_point(render, x, y, color);
//		return;
//	}
//
//	/* 矩形を描きます。 */
//	w--;
//	h--;
//	render_rectangle_fill(render, x  , y  , w, 1, color);	// ─①→│
//	render_rectangle_fill(render, x  , y+1, 1, h, color);	// │　　④
//	render_rectangle_fill(render, x+1, y+h, w, 1, color);	// ②　　↓
//	render_rectangle_fill(render, x+w, y  , 1, h, color);	// ↓─③→
#endif	/*}}2006/12/09 高速化に失敗した例*/
}

/* 矩形座標の正規化とクリッピングを行います。
 * [in]
 *	(px,py,pw,ph)		変更前の座標値へのポインタ。
 * [out]
 *	戻り値			0 = 完全に画面外。
 *				    描画を行う必要はありません。
 *				    (*px,*py,*pw,*ph)は変更しません。
 *				1 = 少なくとも一部が画面内。
 *				    描画を行う必要があります。
 *				    (*px,*py,*pw,*ph)は変更されます。
 *	(*px,*py,*pw,*ph)	変更後の座標値を格納します。
 *				戻り値が0の場合は、格納しません。
 * [note]
 *	- 2006/07/09まで、render_rectangle_fill()の内部に書いていたロジックを、
 *	  render_rectangle_dark()からも利用するため、関数化して取り出しました。
 *	  render_rectangle_fill(),render_rectangle_dark()から共通に利用します。
 */
//{{2007/12/28:アセンブラ化に伴い削除
//static int
//render_rectangle_clip(RENDER* render, int* px, int* py, int* pw, int* ph)
//{
//	int n;
//
//	/* 変更前の座標値を取り出します。 */
//	int x = *px;
//	int y = *py;
//	int w = *pw;
//	int h = *ph;
//
//	/* 正規化します。(左上～右下の形) */
//	if(w < 0) { x += w; w = -w; }
//	if(h < 0) { y += h; h = -h; }
//
//	/* クリッピングします。 */
//	if(x < render->left) {
//		n = render->left - x; /* 左にはみ出たピクセル数 */
//		x = render->left;
//		w -= n;
//	}
//	if(x + w > render->right) {
//		w = render->right - x;
//	}
//	if(w <= 0) {
//		return 0; /* 完全に画面外 */
//	}
//	if(y < render->top) {
//		n = render->top - y; /* 上にはみ出たピクセル数 */
//		y = render->top;
//		h -= n;
//	}
//	if(y + h > render->bottom) {
//		h = render->bottom - y;
//	}
//	if(h <= 0) {
//		return 0; /* 完全に画面外 */
//	}
//
//	/* 変更後の座標値を格納します。 */
//	*px = x;
//	*py = y;
//	*pw = w;
//	*ph = h;
//	return 1; /* 少なくとも一部が画面内 */
//}
//}}2007/12/28:アセンブラ化に伴い削除
//↓ * Fri Dec 28 20:39:14 JST 2007 Naoyuki Sawa
//↓ - render_rectangle_clip()をアセンブラ化しました。
//↓   クリッピングアルゴリズム効率化は、new_pceLCDDrawObject()をアセンブラ化したときに工夫したのと同じ方法を用いました。
//↓   clipldirモジュールの、アセンブラ版new_pceLCDDrawObject()を参照してください。
//↓ - アセンブラ化による、省メモリ化の効果は、以下のとおりです。
//↓		C言語版		124バイト
//↓		アセンブラ版	 96バイト	28バイト削減
//↓ - アセンブラ化による、高速化の効果は、以下のとおりです。
//↓	①既に正規化されており、かつ、クリッピングが発生しない場合の比較
//↓		x =   0;
//↓		y =   0;
//↓		w = 128;
//↓		h =  88;
//↓		r = render_rectangle_clip(&render, &x, &y, &w, &h);
//↓	  上記コードを1000000回実行して、時間を計測。(CODE=SRAM,STACK=SRAM,TURBO=-1)
//↓		C言語版		11693ミリ秒
//↓		アセンブラ版	10483ミリ秒	約10%高速化
//↓	②正規化が必要で、かつ、全方向にクリッピングが発生する場合の比較
//↓		x =  128+10;
//↓		y =   88+10;
//↓		w = -128-20;
//↓		h =  -88-20;
//↓		r = render_rectangle_clip(&render, &x, &y, &w, &h);
//↓	  上記コードを1000000回実行して、時間を計測。(CODE=SRAM,STACK=SRAM,TURBO=-1)
//↓		C言語版		12966ミリ秒
//↓		アセンブラ版	11247ミリ秒	約13%高速化
//↓ - render_rectangle_clip()をglobalに公開しました。
//↓   これまでのC言語版はstaticでしたが、アセンブラ化にあわせてglobalにしました。
//↓   2007/12/28現在のところ、まだ、他のモジュールから利用していないので、globalに公開した意味は無いのですけれど、
//↓   比較的汎用性の高い関数だと思うので、今後、他のモジュールから利用できるよう、globalにしておくことにしました。
/* int render_rectangle_clip(RENDER* render, int* px, int* py, int* pw, int* ph) */
asm("
	.code
	.align	1
	.global render_rectangle_clip
render_rectangle_clip:
	;// %r12    := render
	;// %r13    := px
	;// %r14    := py
	;// %r15    := pw
	;// [%sp+4] := ph
	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
	xld.w	%r12, [%sp+4]			; %r12 := ph
	;// %r4  := left
	;// %r5  := top
	;// %r6  := right
	;// %r7  := bottom
	;// %r12 := ph
	;// %r13 := px
	;// %r14 := py
	;// %r15 := pw
	;---------------------------------------;
;//	ld.w	%r10, [%r13]			; %r10 := x = *px -+
	ld.w	%r11, [%r15]			; %r11 := w = *pw  |
	ld.w	%r10, [%r13]			; <----------------+	*anti-interlock*
	cmp	%r11, 0				; if(w < 0)
	jrge	4
	 add	%r10, %r11			;   x += w
	 not	%r11, %r11			;   w = -w
	 add	%r11, 1
	sub	%r4, %r10			; %r4 := n = left - x
	jrle	3				; if(n > 0)		// if(x < left)
	 add	%r10, %r4			;   %r10 := x += n	//   x += (left - x) = left
	 sub	%r11, %r4			;   %r11 := w -= n
	sub	%r6, %r10			; %r6 := right - x
	cmp	%r11, %r6			; if(w > right - x)	// if(x + w > right)
	jrle	2
	 ld.w	%r11, %r6			;   %r11 := w = right - x
	cmp	%r11, 0				; if(w <= 0)
	jrle	render_rectangle_clip_RET0	;   return 0
	;// %r5  := top
	;// %r7  := bottom
	;// %r10 := x
	;// %r11 := w
	;// %r12 := ph
	;// %r13 := px
	;// %r14 := py
	;// %r15 := pw
	;---------------------------------------;
;//	ld.w	%r4, [%r14]			; %r4 := y = *py -+
	ld.w	%r6, [%r12]			; %r6 := h = *ph  |
	ld.w	%r4, [%r14]			; <---------------+	*anti-interlock*
	cmp	%r6, 0				; if(h < 0)
	jrge	4
	 add	%r4, %r6			;   y += h
	 not	%r6, %r6			;   h = -h
	 add	%r6, 1
	sub	%r5, %r4			; %r5 := n = top - y
	jrle	3				; if(n > 0)		// if(y < top)
	 add	%r4, %r5			;   %r4 := y += n	//   y += (top - y) = top
	 sub	%r6, %r5			;   %r6 := h -= n
	sub	%r7, %r4			; %r7 := bottom - y
	cmp	%r6, %r7			; if(h > bottom - y)	// if(y + h > bottom)
	jrle	2
	 ld.w	%r6, %r7			;   %r6 := h = bottom - y
	cmp	%r6, 0				; if(h <= 0)
	jrle	render_rectangle_clip_RET0	;   return 0
	;// %r4  := y
	;// %r6  := h
	;// %r10 := x
	;// %r11 := w
	;// %r12 := ph
	;// %r13 := px
	;// %r14 := py
	;// %r15 := pw
	;---------------------------------------;
	ld.w	[%r13], %r10 			; *px := x
	ld.w	[%r14], %r4 			; *py := y
	ld.w	[%r15], %r11 			; *pw := w
	ld.w	[%r12], %r6 			; *ph := h
	ret.d					; return 1
	ld.w	%r10, 1				;			*delay
render_rectangle_clip_RET0:
	ret.d					; return 0
	ld.w	%r10, 0				;			*delay
");

//void
//render_rectangle_fill(RENDER* render, int x, int y, int w, int h, int color)
//{
//	SURFACE* surface;
//	unsigned char* p;
//	int i, n;
//
//	/* サーフェイスを取り出しておきます。 */
//	surface = render->surface;
//
//	/* 正規化します。(左上～右下の形) */
//	if(w < 0) { x += w; w = -w; }
//	if(h < 0) { y += h; h = -h; }
//
//	/* クリッピングします。 */
//	if(x < render->left) {
//		n = render->left - x; /* 左にはみ出たピクセル数 */
//		x = render->left;
//		w -= n;
//	}
//	if(x + w > render->right) {
//		w = render->right - x;
//	}
//	if(w <= 0) return; /* 画面外 */
//	if(y < render->top) {
//		n = render->top - y; /* 上にはみ出たピクセル数 */
//		y = render->top;
//		h -= n;
//	}
//	if(y + h > render->bottom) {
//		h = render->bottom - y;
//	}
//	if(h <= 0) return; /* 画面外 */
//
//	/* 塗りつぶします。 */
//	for(i = 0, p = &surface->vbuff[surface->w * y + x]; i < h; i++, p += surface->w) {
//		memset(p, color, w);
//	}
//}
//↓2006/07/09 正規化とクリッピングの処理を、render_rectangle_clip()に分離しました。
void
render_rectangle_fill(RENDER* render, int x, int y, int w, int h, int color)
{
	SURFACE* surface;
	unsigned char* p;

	/* 正規化とクリッピングを行います。 */
	if(!render_rectangle_clip(render, &x, &y, &w, &h)) {
		return; /* 完全に画面外 */
	}

	/* サーフェイスを取り出しておきます。 */
	surface = render->surface;

	/* 矩形領域を塗りつぶします。 */
	p = &surface->vbuff[surface->w * y + x];
	do {
		memset(p, color, w);
		p += surface->w;
	} while(--h);
}

//{{2007/12/29:アセンブラ化に伴い削除
//void
//render_rectangle_shadow(RENDER* render, int x, int y, int w, int h, int adjust, int limit)
//{
//	SURFACE* surface;
//	unsigned char* p;
//	int c;
//
//	/* 正規化とクリッピングを行います。 */
//	if(!render_rectangle_clip(render, &x, &y, &w, &h)) {
//		return; /* 完全に画面外 */
//	}
//
//	/* サーフェイスを取り出しておきます。 */
//	surface = render->surface;
//
//	/* 矩形領域のピクセル値を増減します。 */
//	p = &surface->vbuff[surface->w * y + x];
//	do {
//		x = w;
//		do {
//			c = *p + adjust;
//			if(((adjust < 0) && (c < limit)) || /* ピクセル値減少、下限アンダーフロー */
//			   ((adjust > 0) && (c > limit))) { /* ピクセル値増加、上限オーバーフロー */
//				c = limit;
//			}
//			*p++ = c;
//		} while(--x);
//		p += surface->w - w;
//	} while(--h);
//}
//}}2007/12/29:アセンブラ化に伴い削除
//↓ * Sat Dec 29 02:57:41 JST 2007 Naoyuki Sawa
//↓ - render_rectangle_shadow()をアセンブラ化しました。
//↓ - アセンブラ化による、省メモリ化の効果は、有りません。
//↓		C言語版		128バイト
//↓		アセンブラ版	128バイト	変化無し
//↓ - アセンブラ化による、高速化の効果は、以下のとおりです。
//↓	①全画面shadowを行い、ほとんどlimitオーバーしない場合の比較
//↓		surf.w = 128;
//↓		surf.h =  88;
//↓		surf.vbuff = (unsigned char*)0xc00000; // ROM領域をランダムな内容のvbuffとして使用
//↓		render_init_2d(&rend, &surf, 0, 0, 0, 0);
//↓		render_rectangle_shadow(&rend, 0, 0, 128, 88, 127, 255);
//↓	  上記コードを1000回実行して、時間を計測。(CODE=SRAM,STACK=SRAM,TURBO=-1)
//↓		C言語版		23127ミリ秒
//↓		アセンブラ版	10103ミリ秒	約56%高速化
//↓	②全画面shadowを行い、約半分の確率でlimitオーバーする場合の比較
//↓		surf.w = 128;
//↓		surf.h =  88;
//↓		surf.vbuff = (unsigned char*)0xc00000; // ROM領域をランダムな内容のvbuffとして使用
//↓		render_init_2d(&rend, &surf, 0, 0, 0, 0);
//↓		render_rectangle_shadow(&rend, 0, 0, 128, 88, 127, 255);
//↓	  上記コードを1000回実行して、時間を計測。(CODE=SRAM,STACK=SRAM,TURBO=-1)
//↓		C言語版		23127ミリ秒
//↓		アセンブラ版	10461ミリ秒	約54%高速化
/* void render_rectangle_shadow(RENDER* render, int x, int y, int w, int h, int adjust, int limit) */
asm("
	.code
	.align	1
	.global render_rectangle_shadow
render_rectangle_shadow:
	;// %r12     := render
	;// %r13     := x
	;// %r14     := y
	;// %r15     := w
	;// [%sp+0]  := retp
	;// [%sp+4]  := h
	;// [%sp+8]  := adjust
	;// [%sp+12] := limit
	pushn	%r0
	xsub	%sp, %sp, 16
	;// %r4      := h
	;// %r12     := render
	;// %r13     := x
	;// %r14     := y
	;// %r15     := w
	;// [%sp+16] := %r0
	;// [%sp+20] := retp
	;// [%sp+24] := h
	;// [%sp+28] := adjust
	;// [%sp+32] := limit
	ld.w	%r0, [%r12]			; %r0      := surface
	xld.w	[%sp+4], %r13			; [%sp+4]  := x
	xld.w	[%sp+8], %r14			; [%sp+8]  := y
	xld.w	[%sp+12], %r15			; [%sp+12] := w
	xadd	%r13, %sp, 4			; %r13     := &x
	xadd	%r14, %sp, 8			; %r14     := &y
;//	xadd	%r15, %sp, 12			; %r15     := &w -+
	xadd	%r4, %sp, 24			; %r4      := &h  |
	xld.w	[%sp+0], %r4			; [%sp+0]  := &h  |
	;// %r0      := surface			;                 |
	;// %r12     := render			;                 |
	;// %r13     := &x			;                 |
	;// %r14     := &y			;                 |
	;// %r15     := &w			;                 |
	;// [%sp+0]  := &h			;                 |
	;// [%sp+4]  := x			;                 |
	;// [%sp+8]  := y			;                 |
	;// [%sp+12] := w			;                 |
	;// [%sp+16] := %r0			;                 |
	;// [%sp+20] := retp			;                 |
	;// [%sp+24] := h			;                 |
	;// [%sp+28] := adjust			;                 |
	;// [%sp+32] := limit			;                 |
	ld.w	%r15, %sp			; <---------------+
	xcall.d	render_rectangle_clip		;                 |
	add	%r15, 12			; <---------------+		*delay*
	cmp	%r10, 0
	jreq	render_rectangle_shadow_RET	; 完全に画面外
	;// %r0      := surface
	;// [%sp+4]  := x
	;// [%sp+8]  := y
	;// [%sp+12] := w
	;// [%sp+16] := %r0
	;// [%sp+20] := retp
	;// [%sp+24] := h
	;// [%sp+28] := adjust
	;// [%sp+32] := limit
	xld.h	%r4, [%r0+0]			; %r4  := surface->w
	xld.w	%r5, [%r0+4]			; %r5  := surface->vbuff
	xld.w	%r6, [%sp+4]			; %r6  := x
	xld.w	%r7, [%sp+8]			; %r7  := y
	xld.w	%r10, [%sp+12]			; %r10 := w
	xld.w	%r11, [%sp+24]			; %r11 := h
	xld.w	%r12, [%sp+28]			; %r12 := adjust
	xld.w	%r13, [%sp+32]			; %r13 := limit
	mlt.h	%r4, %r7			; %alr :=                     surface->w * y
	ld.w	%r9, %alr			; %r9  :=                     surface->w * y
	add	%r9, %r6			; %r9  :=                     surface->w * y + x
	add	%r5, %r9			; %r5  := p = &surface->vbuff[surface->w * y + x]
	sub	%r4, %r10			; %r4  := surface->w - w
	;// %r4      := surface->w - w
	;// %r5      := p
	;// %r10     := w
	;// %r11     := h
	;// %r12     := adjust
	;// %r13     := limit
	;// [%sp+16] := %r0
	;// [%sp+20] := retp
	;// [%sp+28] := adjust
	;// [%sp+32] := limit
	cmp	%r12, 0				; if adjust = 0 : goto RET
	jreq	render_rectangle_shadow_RET	; if adjust > 0 : goto INC
	jrlt	render_rectangle_shadow_DEC_DOY	; if adjust < 0 : goto DEC
	;---------------------------------------;
render_rectangle_shadow_INC_DOY:		; do {
	ld.w	%r14, %r10			;   %r14 := x = w
render_rectangle_shadow_INC_DOX:		;   do {
	ld.ub	%r15, [%r5]			;     %r15 := c = *p
	add	%r15, %r12			;     %r15 := c += adjust	!INTERLOCK!
	cmp	%r15, %r13			;     if(c > limit) {
	jrle.d	3
	 sub	%r14, 1				;     %r14 := x--		*delay*
	 ld.w	%r15, %r13			;       %r15 := c = limit
	ld.b	[%r5]+, %r15			;   } while(x)
	jrne	render_rectangle_shadow_INC_DOX
	sub	%r11, 1				; } while(--h)
	jrne.d	render_rectangle_shadow_INC_DOY
	add	%r5, %r4			; %r5  := p += surface->w - w	*delay*
	jp	render_rectangle_shadow_RET
	;---------------------------------------;
render_rectangle_shadow_DEC_DOY:		; do {
	ld.w	%r14, %r10			;   %r14 := x = w
render_rectangle_shadow_DEC_DOX:		;   do {
	ld.ub	%r15, [%r5]			;     %r15 := c = *p
	add	%r15, %r12			;     %r15 := c += adjust	!INTERLOCK!
	cmp	%r15, %r13			;     if(c < limit) {
	jrge.d	3
	 sub	%r14, 1				;     %r14 := x--		*delay*
	 ld.w	%r15, %r13			;       %r15 := c = limit
	ld.b	[%r5]+, %r15			;   } while(x)
	jrne	render_rectangle_shadow_DEC_DOX
	sub	%r11, 1				; } while(--h)
	jrne.d	render_rectangle_shadow_DEC_DOY
	add	%r5, %r4			; %r5  := p += surface->w - w	*delay*
	;---------------------------------------;
render_rectangle_shadow_RET:
	xadd	%sp, %sp, 16
	popn	%r0
	ret
");

static void
render_ellipse_sub2(RENDER* render, int ax, int ay, int cx, int cy, int ix, int iy, int jx, int jy, int color, int fill)
{
	int x0 = cx      - ix;
	int x1 = cx + ax + ix;
	int x2 = cx      - iy;
	int x3 = cx + ax + iy;
	int y0 = cy      - jy;
	int y1 = cy + ay + jy;
	int y2 = cy      - jx;
	int y3 = cy + ay + jx;

	if(!fill) {
		render_point(render, x0, y0, color);
		render_point(render, x1, y0, color);
		render_point(render, x0, y1, color);
		render_point(render, x1, y1, color);
		render_point(render, x2, y2, color);
		render_point(render, x3, y2, color);
		render_point(render, x2, y3, color);
		render_point(render, x3, y3, color);
	} else {
		/* render_ellipse()とrender_ellipse_fill()のアルゴリズムを共通化し、
		 * 実装を簡単にするために、速度の効率を犠牲にした実装となっています。
		 * 同じ領域を何度も重ね塗りするので、render_ellipse_fill()は遅いです。
		 */
		render_rectangle_fill(render, x0, y0, (x1 - x0) + 1, 1, color);
		render_rectangle_fill(render, x0, y1, (x1 - x0) + 1, 1, color);
		render_rectangle_fill(render, x2, y2, (x3 - x2) + 1, 1, color);
		render_rectangle_fill(render, x2, y3, (x3 - x2) + 1, 1, color);
	}
}

static void
render_ellipse_sub1(RENDER* render, int x, int y, int w, int h, int color, int fill)
{
	int ax, ay;
	int cx, cy;
	int rx, ry;
	int ix, iy;
	int jx, jy;
	int f;

	if(w <= 0) {
		if(w) {
			w = -w;
			x -= w;
		} else {
			return;
		}
	}
	ax = !(w & 1);		/* 左半円の中心座標から右半円の中心座標への補正値 */
	cx = x + w / 2 - ax;	/* 左半円の中心座標 */
	rx = cx - x;		/* 横半径 (「rx = cx - x + 1」としてはダメです!!) */

	if(h <= 0) {
		if(h) {
			h = -h;
			y -= h;
		} else {
			return;
		}
	}
	ay = !(h & 1);		/* 上半円の中心座標から下半円の中心座標への補正値 */
	cy = y + h / 2 - ay;	/* 上半円の中心座標 */
	ry = cy - y;		/* 縦半径 (「ry = cy - y + 1」としてはダメです!!) */

	if(rx >= ry) {		/* 横長楕円、または、真円 */
		ix = rx;
		iy = 0;
		f = 3 - ix * 2;
		do {
			jx = ix * (ry + 1) / (rx + 1);
			jy = iy * (ry + 1) / (rx + 1);
			render_ellipse_sub2(render, ax, ay, cx, cy, ix, iy, jx, jy, color, fill);
			if(f >= 0) {
				f -= ix * 4;
				ix--;
			}
			f += iy * 4 + 2;
			iy++;
		} while(ix >= iy);
	} else {		/* 縦長楕円 */
		jx = 0;
		jy = ry;
		f = 3 - jy * 2;
		do {
			ix = jx * (rx + 1) / (ry + 1);
			iy = jy * (rx + 1) / (ry + 1);
			render_ellipse_sub2(render, ax, ay, cx, cy, ix, iy, jx, jy, color, fill);
			if(f >= 0) {
				f -= jy * 4;
				jy--;
			}
			f += jx * 4 + 2;
			jx++;
		} while(jy >= jx);
	}
}

void
render_ellipse(RENDER* render, int x, int y, int w, int h, int color)
{
	render_ellipse_sub1(render, x, y, w, h, color, 0);
}

void
render_ellipse_fill(RENDER* render, int x, int y, int w, int h, int color)
{
	render_ellipse_sub1(render, x, y, w, h, color, 1);
}

