/*	
 *	clipren2.c
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2008 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追記」で検索してください。)
 *	  コードには変更有りません。
 */
#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);	/* 拡大 */
}

/*****************************************************************************
 *	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);
}

