/*	
 *	clipdobj.c
 *
 *	高速版pceLCDDrawObject
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2002 Naoyuki Sawa
 *
 *	* Sun Jun  2 20:07:00 JST 2002 Naoyuki Sawa
 *	- clippce.cとclipwin.cに同じコードをコピーして使っていたのを抽出しました。
 *	  このファイルは、直接コンパイルしません。
 *	  clippce.cまたはclipwin.cにインクルードしてコンパイルします。
 */

/****************************************************************************
 *	高速版pceLCDDrawObject
 ****************************************************************************/

/* * 高速版pceLCDDrawObjectには、次のような制限があります。
 * - 転送方法がDRW_NOMAL、かつ、転送元ビットマップが2bitデータmask付きである場合以外は、
 *   本物のpceLCDDrawObjectに処理を任せます。呼び出しが増える分、少し遅くなります。
 * - 転送元矩形に、転送元ビットマップをはみ出す領域を指定すると、正しく動作しません。
 * - 転送元ビットマップは、横8ピクセル単位のサイズで作成されていなければいけません。
 *   この制限は、P/ECEビットマップ形式の制限でもあるので、特に注意する必要はありません。
 * * アプリケーション起動時に一度だけhook_LCDDrawObject()を呼び、
 *   アプリケーション終了時には必ずunhook_LCDDrawObject()を読んでください。
 */

#define KSNO_LCDDrawObject 100	/* pceLCDDrawObjectのカーネルサービス番号 */

static int (*old_LCDDrawObject)(DRAW_OBJECT);	/* 本物のpceLCDDrawObjectを保存 */
static unsigned int draw_table[256] = {
	0x00000000,0x01000000,0x02000000,0x03000000,0x00010000,0x01010000,0x02010000,0x03010000,0x00020000,0x01020000,0x02020000,0x03020000,0x00030000,0x01030000,0x02030000,0x03030000,
	0x00000100,0x01000100,0x02000100,0x03000100,0x00010100,0x01010100,0x02010100,0x03010100,0x00020100,0x01020100,0x02020100,0x03020100,0x00030100,0x01030100,0x02030100,0x03030100,
	0x00000200,0x01000200,0x02000200,0x03000200,0x00010200,0x01010200,0x02010200,0x03010200,0x00020200,0x01020200,0x02020200,0x03020200,0x00030200,0x01030200,0x02030200,0x03030200,
	0x00000300,0x01000300,0x02000300,0x03000300,0x00010300,0x01010300,0x02010300,0x03010300,0x00020300,0x01020300,0x02020300,0x03020300,0x00030300,0x01030300,0x02030300,0x03030300,
	0x00000001,0x01000001,0x02000001,0x03000001,0x00010001,0x01010001,0x02010001,0x03010001,0x00020001,0x01020001,0x02020001,0x03020001,0x00030001,0x01030001,0x02030001,0x03030001,
	0x00000101,0x01000101,0x02000101,0x03000101,0x00010101,0x01010101,0x02010101,0x03010101,0x00020101,0x01020101,0x02020101,0x03020101,0x00030101,0x01030101,0x02030101,0x03030101,
	0x00000201,0x01000201,0x02000201,0x03000201,0x00010201,0x01010201,0x02010201,0x03010201,0x00020201,0x01020201,0x02020201,0x03020201,0x00030201,0x01030201,0x02030201,0x03030201,
	0x00000301,0x01000301,0x02000301,0x03000301,0x00010301,0x01010301,0x02010301,0x03010301,0x00020301,0x01020301,0x02020301,0x03020301,0x00030301,0x01030301,0x02030301,0x03030301,
	0x00000002,0x01000002,0x02000002,0x03000002,0x00010002,0x01010002,0x02010002,0x03010002,0x00020002,0x01020002,0x02020002,0x03020002,0x00030002,0x01030002,0x02030002,0x03030002,
	0x00000102,0x01000102,0x02000102,0x03000102,0x00010102,0x01010102,0x02010102,0x03010102,0x00020102,0x01020102,0x02020102,0x03020102,0x00030102,0x01030102,0x02030102,0x03030102,
	0x00000202,0x01000202,0x02000202,0x03000202,0x00010202,0x01010202,0x02010202,0x03010202,0x00020202,0x01020202,0x02020202,0x03020202,0x00030202,0x01030202,0x02030202,0x03030202,
	0x00000302,0x01000302,0x02000302,0x03000302,0x00010302,0x01010302,0x02010302,0x03010302,0x00020302,0x01020302,0x02020302,0x03020302,0x00030302,0x01030302,0x02030302,0x03030302,
	0x00000003,0x01000003,0x02000003,0x03000003,0x00010003,0x01010003,0x02010003,0x03010003,0x00020003,0x01020003,0x02020003,0x03020003,0x00030003,0x01030003,0x02030003,0x03030003,
	0x00000103,0x01000103,0x02000103,0x03000103,0x00010103,0x01010103,0x02010103,0x03010103,0x00020103,0x01020103,0x02020103,0x03020103,0x00030103,0x01030103,0x02030103,0x03030103,
	0x00000203,0x01000203,0x02000203,0x03000203,0x00010203,0x01010203,0x02010203,0x03010203,0x00020203,0x01020203,0x02020203,0x03020203,0x00030203,0x01030203,0x02030203,0x03030203,
	0x00000303,0x01000303,0x02000303,0x03000303,0x00010303,0x01010303,0x02010303,0x03010303,0x00020303,0x01020303,0x02020303,0x03020303,0x00030303,0x01030303,0x02030303,0x03030303,
};
static unsigned int draw_table_revx[256] = {
	0x00000000,0x00000001,0x00000002,0x00000003,0x00000100,0x00000101,0x00000102,0x00000103,0x00000200,0x00000201,0x00000202,0x00000203,0x00000300,0x00000301,0x00000302,0x00000303,
	0x00010000,0x00010001,0x00010002,0x00010003,0x00010100,0x00010101,0x00010102,0x00010103,0x00010200,0x00010201,0x00010202,0x00010203,0x00010300,0x00010301,0x00010302,0x00010303,
	0x00020000,0x00020001,0x00020002,0x00020003,0x00020100,0x00020101,0x00020102,0x00020103,0x00020200,0x00020201,0x00020202,0x00020203,0x00020300,0x00020301,0x00020302,0x00020303,
	0x00030000,0x00030001,0x00030002,0x00030003,0x00030100,0x00030101,0x00030102,0x00030103,0x00030200,0x00030201,0x00030202,0x00030203,0x00030300,0x00030301,0x00030302,0x00030303,
	0x01000000,0x01000001,0x01000002,0x01000003,0x01000100,0x01000101,0x01000102,0x01000103,0x01000200,0x01000201,0x01000202,0x01000203,0x01000300,0x01000301,0x01000302,0x01000303,
	0x01010000,0x01010001,0x01010002,0x01010003,0x01010100,0x01010101,0x01010102,0x01010103,0x01010200,0x01010201,0x01010202,0x01010203,0x01010300,0x01010301,0x01010302,0x01010303,
	0x01020000,0x01020001,0x01020002,0x01020003,0x01020100,0x01020101,0x01020102,0x01020103,0x01020200,0x01020201,0x01020202,0x01020203,0x01020300,0x01020301,0x01020302,0x01020303,
	0x01030000,0x01030001,0x01030002,0x01030003,0x01030100,0x01030101,0x01030102,0x01030103,0x01030200,0x01030201,0x01030202,0x01030203,0x01030300,0x01030301,0x01030302,0x01030303,
	0x02000000,0x02000001,0x02000002,0x02000003,0x02000100,0x02000101,0x02000102,0x02000103,0x02000200,0x02000201,0x02000202,0x02000203,0x02000300,0x02000301,0x02000302,0x02000303,
	0x02010000,0x02010001,0x02010002,0x02010003,0x02010100,0x02010101,0x02010102,0x02010103,0x02010200,0x02010201,0x02010202,0x02010203,0x02010300,0x02010301,0x02010302,0x02010303,
	0x02020000,0x02020001,0x02020002,0x02020003,0x02020100,0x02020101,0x02020102,0x02020103,0x02020200,0x02020201,0x02020202,0x02020203,0x02020300,0x02020301,0x02020302,0x02020303,
	0x02030000,0x02030001,0x02030002,0x02030003,0x02030100,0x02030101,0x02030102,0x02030103,0x02030200,0x02030201,0x02030202,0x02030203,0x02030300,0x02030301,0x02030302,0x02030303,
	0x03000000,0x03000001,0x03000002,0x03000003,0x03000100,0x03000101,0x03000102,0x03000103,0x03000200,0x03000201,0x03000202,0x03000203,0x03000300,0x03000301,0x03000302,0x03000303,
	0x03010000,0x03010001,0x03010002,0x03010003,0x03010100,0x03010101,0x03010102,0x03010103,0x03010200,0x03010201,0x03010202,0x03010203,0x03010300,0x03010301,0x03010302,0x03010303,
	0x03020000,0x03020001,0x03020002,0x03020003,0x03020100,0x03020101,0x03020102,0x03020103,0x03020200,0x03020201,0x03020202,0x03020203,0x03020300,0x03020301,0x03020302,0x03020303,
	0x03030000,0x03030001,0x03030002,0x03030003,0x03030100,0x03030101,0x03030102,0x03030103,0x03030200,0x03030201,0x03030202,0x03030203,0x03030300,0x03030301,0x03030302,0x03030303,
};

static int
new_LCDDrawObject(DRAW_OBJECT obj)
{
	PIECE_VRAM dest;
	PIECE_BMP src;
	int n, x, y, step, dest_stride, src_stride;
	unsigned char *dest_buf, *src_mask, mask;
	unsigned short *src_buf, pixel;
	unsigned int *dest_buf4; /* dest_bufをキャストして使い回すと、コンパイラの最適化が弱くなります */

	/* 反転方向によって、処理を分けます。(※DRW_NOMAL=0と仮定)
	 * 反転方向以外のフラグが指定されていたら、本物に任せます。
	 */
	switch(obj.param) {
	case 0/*反転なし*/:
		/* 転送元ビットマップ情報によって、処理を分けます。
		 * 2bitデータまたは2bitデータmask付きでなければ、本物に任せます。
		 */
		src = *obj.src;
		if(src.header.bpp != 2) break; /* 本物に任せます */
		switch(src.header.mask) {
		case 0/*2bitデータ*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				obj.dw = obj.clip.right - obj.dx;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				obj.dh = obj.clip.bottom - obj.dy;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			if((obj.dx & 3) == 0 && (obj.dw & 3) == 0 && (obj.sx & 3) == 0) {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf4 = (unsigned int*)dest.buf + (dest.w * obj.dy + obj.dx) / 4;
				dest_stride = (dest.w - obj.dw) / 4;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_00: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf4++ = draw_table[pixel >> (p) * 2 & 255]; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if((x -= 4) == 0) break;
					case 0: PIXEL(0);
					default/*4*/: PIXEL(4); goto LOOP_00;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf4 += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			} else {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf = dest.buf + dest.w * obj.dy + obj.dx;
				dest_stride = dest.w - obj.dw;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_01: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf++ = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_01;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			}
			return 1; /* 描画完了 */

		case 1/*2bitデータmask付き*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				obj.dw = obj.clip.right - obj.dx;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				obj.dh = obj.clip.bottom - obj.dy;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * obj.dy + obj.dx;
			dest_stride = dest.w - obj.dw;

			/* 転送元ピクセルアドレスを求めます。 */
			n = (src.header.w * obj.sy + obj.sx) / 8;
			src_buf = (unsigned short*)src.buf + n;
			src_mask = src.mask + n;
			src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
			/*                                                     ~~~      */
			/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

			y = obj.dh; /* 残りライン数 */
			do {
				/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
				step = obj.sx & 7;

				/* 開始点から始まるか途中から始まるかわからないので、
				 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
				 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
				 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
				 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
				 */
				pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
				mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

				/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
				x = obj.dw; /* 残りピクセル数 */
				switch(step) {
LOOP_02: /* 初回はここは飛び越します */
					/* 8ピクセル単位の開始点で、次のデータを取得します。 */
					pixel = *++src_buf;
					mask = *++src_mask;
#define PIXEL(p, m) \
					/* 不透明ピクセルなら、描きます。 */ \
					if(mask >> (m) & 1) *dest_buf = pixel >> (p) * 2 & 3; \
					dest_buf++; \
					/* 1ライン転送完了したら、抜けます。 */ \
					if(--x == 0) break;
				case 0: PIXEL(3, 7);
				case 1: PIXEL(2, 6);
				case 2: PIXEL(1, 5);
				case 3: PIXEL(0, 4);
				case 4: PIXEL(7, 3);
				case 5: PIXEL(6, 2);
				case 6: PIXEL(5, 1);
				default/*7*/: PIXEL(4, 0); goto LOOP_02;
#undef PIXEL
				}

				/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
				dest_buf += dest_stride;
				src_buf += src_stride;
				src_mask += src_stride;
			} while(--y != 0);
			return 1; /* 描画完了 */
		}
		break; /* 本物に任せます */

	case DRW_REVX:
		/* 転送元ビットマップ情報によって、処理を分けます。
		 * 2bitデータまたは2bitデータmask付きでなければ、本物に任せます。
		 */
		src = *obj.src;
		if(src.header.bpp != 2) break; /* 本物に任せます */
		switch(src.header.mask) {
		case 0/*2bitデータ*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				n = obj.dx + obj.dw - obj.clip.right; /* 右にはみ出たピクセル数 */
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				obj.dh = obj.clip.bottom - obj.dy;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			if((obj.dx & 3) == 0 && (obj.dw & 3) == 0 && (obj.sx & 3) == 0) {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf4 = (unsigned int*)dest.buf + (dest.w * obj.dy + obj.dx + obj.dw - 1) / 4;
				dest_stride = (dest.w + obj.dw) / 4;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_10: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf4-- = draw_table_revx[pixel >> (p) * 2 & 255]; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if((x -= 4) == 0) break;
					case 0: PIXEL(0);
					default/*4*/: PIXEL(4); goto LOOP_10;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf4 += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			} else {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf = dest.buf + dest.w * obj.dy + obj.dx + obj.dw - 1;
				dest_stride = dest.w + obj.dw;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_11: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf-- = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_11;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			}
			return 1; /* 描画完了 */

		case 1/*2bitデータmask付き*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				n = obj.dx + obj.dw - obj.clip.right; /* 右にはみ出たピクセル数 */
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				obj.dh = obj.clip.bottom - obj.dy;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * obj.dy + obj.dx + obj.dw - 1;
			dest_stride = dest.w + obj.dw;

			/* 転送元ピクセルアドレスを求めます。 */
			n = (src.header.w * obj.sy + obj.sx) / 8;
			src_buf = (unsigned short*)src.buf + n;
			src_mask = src.mask + n;
			src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
			/*                                                     ~~~      */
			/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

			y = obj.dh; /* 残りライン数 */
			do {
				/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
				step = obj.sx & 7;

				/* 開始点から始まるか途中から始まるかわからないので、
				 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
				 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
				 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
				 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
				 */
				pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
				mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

				/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
				x = obj.dw; /* 残りピクセル数 */
				switch(step) {
LOOP_12: /* 初回はここは飛び越します */
					/* 8ピクセル単位の開始点で、次のデータを取得します。 */
					pixel = *++src_buf;
					mask = *++src_mask;
#define PIXEL(p, m) \
					/* 不透明ピクセルなら、描きます。 */ \
					if(mask >> (m) & 1) *dest_buf = pixel >> (p) * 2 & 3; \
					dest_buf--; \
					/* 1ライン転送完了したら、抜けます。 */ \
					if(--x == 0) break;
				case 0: PIXEL(3, 7);
				case 1: PIXEL(2, 6);
				case 2: PIXEL(1, 5);
				case 3: PIXEL(0, 4);
				case 4: PIXEL(7, 3);
				case 5: PIXEL(6, 2);
				case 6: PIXEL(5, 1);
				default/*7*/: PIXEL(4, 0); goto LOOP_12;
#undef PIXEL
				}

				/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
				dest_buf += dest_stride;
				src_buf += src_stride;
				src_mask += src_stride;
			} while(--y != 0);
			return 1; /* 描画完了 */
		}
		break; /* 本物に任せます */

	case DRW_REVY:
		/* 転送元ビットマップ情報によって、処理を分けます。
		 * 2bitデータまたは2bitデータmask付きでなければ、本物に任せます。
		 */
		src = *obj.src;
		if(src.header.bpp != 2) break; /* 本物に任せます */
		switch(src.header.mask) {
		case 0/*2bitデータ*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				obj.dw = obj.clip.right - obj.dx;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				n = obj.dy + obj.dh - obj.clip.bottom; /* 下にはみ出たピクセル数 */
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			if((obj.dx & 3) == 0 && (obj.dw & 3) == 0 && (obj.sx & 3) == 0) {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf4 = (unsigned int*)dest.buf + (dest.w * (obj.dy + obj.dh - 1) + obj.dx) / 4;
				dest_stride = (-dest.w - obj.dw) / 4;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_20: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf4++ = draw_table[pixel >> (p) * 2 & 255]; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if((x -= 4) == 0) break;
					case 0: PIXEL(0);
					default/*4*/: PIXEL(4); goto LOOP_20;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf4 += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			} else {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf = dest.buf + dest.w * (obj.dy + obj.dh - 1) + obj.dx;
				dest_stride = -dest.w - obj.dw;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_21: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf++ = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_21;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			}
			return 1; /* 描画完了 */

		case 1/*2bitデータmask付き*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				obj.dw = obj.clip.right - obj.dx;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				n = obj.dy + obj.dh - obj.clip.bottom; /* 下にはみ出たピクセル数 */
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * (obj.dy + obj.dh - 1) + obj.dx;
			dest_stride = -dest.w - obj.dw;

			/* 転送元ピクセルアドレスを求めます。 */
			n = (src.header.w * obj.sy + obj.sx) / 8;
			src_buf = (unsigned short*)src.buf + n;
			src_mask = src.mask + n;
			src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
			/*                                                     ~~~      */
			/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

			y = obj.dh; /* 残りライン数 */
			do {
				/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
				step = obj.sx & 7;

				/* 開始点から始まるか途中から始まるかわからないので、
				 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
				 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
				 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
				 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
				 */
				pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
				mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

				/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
				x = obj.dw; /* 残りピクセル数 */
				switch(step) {
LOOP_22: /* 初回はここは飛び越します */
					/* 8ピクセル単位の開始点で、次のデータを取得します。 */
					pixel = *++src_buf;
					mask = *++src_mask;
#define PIXEL(p, m) \
					/* 不透明ピクセルなら、描きます。 */ \
					if(mask >> (m) & 1) *dest_buf = pixel >> (p) * 2 & 3; \
					dest_buf++; \
					/* 1ライン転送完了したら、抜けます。 */ \
					if(--x == 0) break;
				case 0: PIXEL(3, 7);
				case 1: PIXEL(2, 6);
				case 2: PIXEL(1, 5);
				case 3: PIXEL(0, 4);
				case 4: PIXEL(7, 3);
				case 5: PIXEL(6, 2);
				case 6: PIXEL(5, 1);
				default/*7*/: PIXEL(4, 0); goto LOOP_22;
#undef PIXEL
				}

				/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
				dest_buf += dest_stride;
				src_buf += src_stride;
				src_mask += src_stride;
			} while(--y != 0);
			return 1; /* 描画完了 */
		}
		break; /* 本物に任せます */

	case DRW_REVX | DRW_REVY:
		/* 転送元ビットマップ情報によって、処理を分けます。
		 * 2bitデータまたは2bitデータmask付きでなければ、本物に任せます。
		 */
		src = *obj.src;
		if(src.header.bpp != 2) break; /* 本物に任せます */
		switch(src.header.mask) {
		case 0/*2bitデータ*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				n = obj.dx + obj.dw - obj.clip.right; /* 右にはみ出たピクセル数 */
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				n = obj.dy + obj.dh - obj.clip.bottom; /* 下にはみ出たピクセル数 */
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			if((obj.dx & 3) == 0 && (obj.dw & 3) == 0 && (obj.sx & 3) == 0) {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf4 = (unsigned int*)dest.buf + (dest.w * (obj.dy + obj.dh - 1) + obj.dx + obj.dw - 1) / 4;
				dest_stride = (-dest.w + obj.dw) / 4;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_30: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf4-- = draw_table_revx[pixel >> (p) * 2 & 255]; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if((x -= 4) == 0) break;
					case 0: PIXEL(0);
					default/*4*/: PIXEL(4); goto LOOP_30;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf4 += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			} else {
				/* 転送先ピクセルアドレスを求めます。 */
				dest_buf = dest.buf + dest.w * (obj.dy + obj.dh - 1) + obj.dx + obj.dw - 1;
				dest_stride = -dest.w + obj.dw;

				/* 転送元ピクセルアドレスを求めます。 */
				n = (src.header.w * obj.sy + obj.sx) / 8;
				src_buf = (unsigned short*)src.buf + n;
				src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
				/*                                                     ~~~      */
				/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

				y = obj.dh; /* 残りライン数 */
				do {
					/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
					step = obj.sx & 7;

					/* 開始点から始まるか途中から始まるかわからないので、
					 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
					 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
					 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
					 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
					 */
					pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */

					/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
					x = obj.dw; /* 残りピクセル数 */
					switch(step) {
LOOP_31: /* 初回はここは飛び越します */
						/* 8ピクセル単位の開始点で、次のデータを取得します。 */
						pixel = *++src_buf;
#define PIXEL(p) \
						/* ピクセルを、描きます。 */ \
						*dest_buf-- = pixel >> (p) * 2 & 3; \
						/* 1ライン転送完了したら、抜けます。 */ \
						if(--x == 0) break;
					case 0: PIXEL(3);
					case 1: PIXEL(2);
					case 2: PIXEL(1);
					case 3: PIXEL(0);
					case 4: PIXEL(7);
					case 5: PIXEL(6);
					case 6: PIXEL(5);
					default/*7*/: PIXEL(4); goto LOOP_31;
#undef PIXEL
					}

					/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
					dest_buf += dest_stride;
					src_buf += src_stride;
				} while(--y != 0);
			}
			return 1; /* 描画完了 */

		case 1/*2bitデータmask付き*/:
			/* 転送先VRAM情報を取得。 */
			if(obj.dest != NULL) {
				dest = *obj.dest;
			} else {
				dest.w = DISP_X;
				dest.h = DISP_Y;
				dest.buf = pceLCDSetBuffer(INVALIDPTR);
			}

			/* 転送先クリッピング。 */
			if(obj.dx < obj.clip.left) {
				n = obj.clip.left - obj.dx; /* 左にはみ出たピクセル数 */
				obj.dx = obj.clip.left;
				obj.dw -= n;
			}
			if(obj.dx + obj.dw > obj.clip.right) {
				n = obj.dx + obj.dw - obj.clip.right; /* 右にはみ出たピクセル数 */
				obj.dw -= n;
				obj.sx += n;
			}
			if(obj.dw <= 0) return 0; /* 画面外 */
			if(obj.dy < obj.clip.top) {
				n = obj.clip.top - obj.dy; /* 上にはみ出たピクセル数 */
				obj.dy = obj.clip.top;
				obj.dh -= n;
			}
			if(obj.dy + obj.dh > obj.clip.bottom) {
				n = obj.dy + obj.dh - obj.clip.bottom; /* 下にはみ出たピクセル数 */
				obj.dh -= n;
				obj.sy += n;
			}
			if(obj.dh <= 0) return 0; /* 画面外 */

			/* 転送元クリッピングは行いません。
			 * 元々指定された転送元矩形が転送元ビットマップ内に収まっていたならば、
			 * 転送先クリッピングによって転送元がはみ出すことはありえないからです。
			 * しかし本物は転送元クリッピングも行っているので、この点は非互換です。
			 */

			/* 転送先ピクセルアドレスを求めます。 */
			dest_buf = dest.buf + dest.w * (obj.dy + obj.dh - 1) + obj.dx + obj.dw - 1;
			dest_stride = -dest.w + obj.dw;

			/* 転送元ピクセルアドレスを求めます。 */
			n = (src.header.w * obj.sy + obj.sx) / 8;
			src_buf = (unsigned short*)src.buf + n;
			src_mask = src.mask + n;
			src_stride = src.header.w / 8 - ((obj.sx & 7) + obj.dw - 1) / 8;
			/*                                                     ~~~      */
			/* 8ステップ完了時に進むのではなく、9ステップ目の先頭で進むから */

			y = obj.dh; /* 残りライン数 */
			do {
				/* 8ピクセル単位のうち、何ピクセル目から開始するか？ */
				step = obj.sx & 7;

				/* 開始点から始まるか途中から始まるかわからないので、
				 * 初回はたとえstep=0でも、ここで最初のデータを取得することにします。
				 * ※P/ECEビットマップのピクセル並びは、MSB側が左のピクセルなんですね。
				 *   S1C33はリトルエンディアンなんだから、LSB側を左のピクセルにした方が扱いやすいと思うのですが。
				 *   WindowsBMPはMSB側が左のピクセルなのかな？もしそうならば、BMPの仕様に合わせたのかも知れません。
				 */
				pixel = *src_buf;	/* ピクセル並びは MSB:45670123:LSB の順です */
				mask = *src_mask;	/* ピクセル並びは MSB:01234567:LSB の順です */

				/* 何ピクセル目から開始するかによって、適切な位置へ飛び込みます。 */
				x = obj.dw; /* 残りピクセル数 */
				switch(step) {
LOOP_32: /* 初回はここは飛び越します */
					/* 8ピクセル単位の開始点で、次のデータを取得します。 */
					pixel = *++src_buf;
					mask = *++src_mask;
#define PIXEL(p, m) \
					/* 不透明ピクセルなら、描きます。 */ \
					if(mask >> (m) & 1) *dest_buf = pixel >> (p) * 2 & 3; \
					dest_buf--; \
					/* 1ライン転送完了したら、抜けます。 */ \
					if(--x == 0) break;
				case 0: PIXEL(3, 7);
				case 1: PIXEL(2, 6);
				case 2: PIXEL(1, 5);
				case 3: PIXEL(0, 4);
				case 4: PIXEL(7, 3);
				case 5: PIXEL(6, 2);
				case 6: PIXEL(5, 1);
				default/*7*/: PIXEL(4, 0); goto LOOP_32;
#undef PIXEL
				}

				/* 転送先・転送元ピクセルアドレスを次のラインへ進めます。 */
				dest_buf += dest_stride;
				src_buf += src_stride;
				src_mask += src_stride;
			} while(--y != 0);
			return 1; /* 描画完了 */
		}
		break; /* 本物に任せます */
	}

	/* 高速ルーチンが対応していない描画方式なら、本物に任せます。 */
	return old_LCDDrawObject(obj);
}

static void
hook_LCDDrawObject()
{
	/* pceLCDDrawObjectをフックします。 */
	old_LCDDrawObject = pceVectorSetKs(KSNO_LCDDrawObject, new_LCDDrawObject);
}

static void
unhook_LCDDrawObject()
{
	/* pceLCDDrawObjectをフック解除します。 */
	pceVectorSetKs(KSNO_LCDDrawObject, old_LCDDrawObject);
}
