/*	
 *	clipsti.c
 *
 *	P/ECE STIC Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2005 Naoyuki Sawa
 *
 *	* Wed May 18 20:00:00 JST 2005 Naoyuki Sawa
 *	- 作成開始。
 */
#include "clip.h"

/****************************************************************************
 *	グローバル変数
 ****************************************************************************/

STIC stic;

#ifdef STIC_GRAYSCALE
const unsigned char stic_grayscale_table[16/*Color*/] = {
#define STIC_RGB(r,g,b) ((int)((r*0.30+g*0.59+b*0.11)*15/255+0.5)^15)
#else /*STIC_GRAYSCALE*/
const unsigned char stic_color_table[16/*Color*/][3/*R,G,B*/] = {
#define STIC_RGB(r,g,b) {r,g,b}
#endif /*STIC_GRAYSCALE*/
STIC_RGB(  0,  0,  0),	/*  0 Black        */
STIC_RGB(  0, 45,255),	/*  1 Blue         */
STIC_RGB(255, 62,  0),	/*  2 Red          */
STIC_RGB(201,212,100),	/*  3 Tan          */
STIC_RGB(  0,120, 16),	/*  4 Dark Green   */
STIC_RGB(  0,167, 32),	/*  5 Green        */
STIC_RGB(250,234, 39),	/*  6 Yellow       */
STIC_RGB(255,252,255),	/*  7 White        */
STIC_RGB(168,168,168),	/*  8 Grey         */
STIC_RGB( 90,203,255),	/*  9 Cyan         */
STIC_RGB(255,166,  0),	/* 10 Orange       */
STIC_RGB( 60, 88,  0),	/* 11 Brown        */
STIC_RGB(255, 50,118),	/* 12 Pink         */
STIC_RGB(189,149,255),	/* 13 Light Blue   */
STIC_RGB(108,205, 48),	/* 14 Yellow-Green */
STIC_RGB(200, 26,125),	/* 15 Purple       */
#undef STIC_RGB
};

/****************************************************************************
 *	ローカル変数
 ****************************************************************************/

/* MOBのグラフィックデータ展開用バッファです。
 * MOBの描画と、MOB×MOB、MOB×Background Card衝突判定に利用します。
 * 16×64ピクセル、2セット分です。(衝突判定のため2セット必要)
 */
static unsigned short stic_mob_graphics[2][64];

/* Background Cardのマスクデータ展開用バッファです。
 * MOB×Background Card衝突判定に利用します。
 * 16×64ピクセル、1セット分です。(MOB側はstic_mob_graphics[0]を使用)
 * 16×64ピクセルのうち、左上8×8ピクセル部分だけを使います。
 * 左:MSB<->LSB:右なので、16bitの上位8bit側に詰めます。
 * 9ライン目以降にはプログラムから値を格納せず、常に0のままとします。
 */
static unsigned short stic_background_card_mask[64];

/* Borderのマスクパターンです。
 * MOB×Border衝突判定に利用します。
 * 16×64ピクセル×2パターンです。(MOB側はstic_mob_graphics[0]を使用)
 * 16×64ピクセルのうち、左上8×8ピクセル部分だけを使います。
 * 左:MSB<->LSB:右なので、16bitの上位8bit側に詰めます。
 * パターン[0]は、画面外、および、左と上のBorder extensionに相当します。
 * パターン[1]は、画面右端(X=159,Y=0..95)の縦1ラインBorderに相当します。
 */
static const unsigned short stic_border_mask[2][64] = {
	{ 0xff00,0xff00,0xff00,0xff00,0xff00,0xff00,0xff00,0xff00 },
	{ 0x0100,0x0100,0x0100,0x0100,0x0100,0x0100,0x0100,0x0100 },
};

/****************************************************************************
 *	内部関数
 ****************************************************************************/

void
stic_fill_background_card(int x, int y, int color)
{
	unsigned char* vbuff;
	int ix;
	int iy;

	/* 完全に画面外ならば、何もしません。 */
	if((x <= 0 - 8) || (x >= 160) ||
	   (y <= 0 - 8) || (y >=  96)) {
		return;
	}

	vbuff = &stic.vbuff[160 * y + x];

	/* 完全に画面内ならば、クリッピングは不要です。 */
	if(((unsigned)x <= 160 - 8) &&
	   ((unsigned)y <=  96 - 8)) {
		iy = 8;
		do {
			ix = 8;
			do {
				*vbuff++ = color;
			} while(--ix);
			vbuff += 160 - 8;
		} while(--iy);

	/* 画面をはみ出す場合、クリッピングが必要です。 */
	} else {
		iy = 8;
		do {
			if((unsigned)y <= 96 - 1) {
				ix = 8;
				do {
					if((unsigned)x <= 160 - 1) {
						*vbuff = color;
					}
					vbuff++;
					x++;
				} while(--ix);
				vbuff -= 8;
				x -= 8;
			}
			vbuff += 160;
			y++;
		} while(--iy);
	}
}

void
stic_draw_background_card(int x, int y, int card, int color)
{
	const unsigned char* graphics;
	unsigned char* vbuff;
	int ix;
	int iy;
	int bits;

	/* 完全に画面外ならば、何もしません。 */
	if((x <= 0 - 8) || (x >= 160) ||
	   (y <= 0 - 8) || (y >=  96)) {
		return;
	}

	vbuff = &stic.vbuff[160 * y + x];
	graphics = &stic.graphics[card * 8];

	/* 完全に画面内ならば、クリッピングは不要です。 */
	if(((unsigned)x <= 160 - 8) &&
	   ((unsigned)y <=  96 - 8)) {
		iy = 8;
		do {
			bits = *graphics++;
			ix = 8;
			do {
				if(bits & 0x80) {
					*vbuff = color;
				}
				bits <<= 1;
				vbuff++;
			} while(--ix);
			vbuff += 160 - 8;
		} while(--iy);

	/* 画面をはみ出す場合、クリッピングが必要です。 */
	} else {
		iy = 8;
		do {
			bits = *graphics++;
			if((unsigned)y <= 96 - 1) {
				ix = 8;
				do {
					if(bits & 0x80) {
						if((unsigned)x <= 160 - 1) {
							*vbuff = color;
						}
					}
					bits <<= 1;
					vbuff++;
					x++;
				} while(--ix);
				vbuff -= 8;
				x -= 8;
			}
			vbuff += 160;
			y++;
		} while(--iy);
	}
}

void
stic_draw_colored_squares(int x, int y, const char colors[/*4*/])
{
	unsigned char* vbuff;
	int ix;
	int iy;
	int color;

	/* 完全に画面外ならば、何もしません。 */
	if((x <= 0 - 8) || (x >= 160) ||
	   (y <= 0 - 8) || (y >=  96)) {
		return;
	}

	vbuff = &stic.vbuff[160 * y + x];

	/* 完全に画面内ならば、クリッピングは不要です。 */
	if(((unsigned)x <= 160 - 8) &&
	   ((unsigned)y <=  96 - 8)) {
		iy = 2;
		do {
			ix = 2;
			do {
				color = *colors++;
				if(color != -1) {
					vbuff[160 * 0 + 0] = vbuff[160 * 0 + 1] = vbuff[160 * 0 + 2] = vbuff[160 * 0 + 3] =
					vbuff[160 * 1 + 0] = vbuff[160 * 1 + 1] = vbuff[160 * 1 + 2] = vbuff[160 * 1 + 3] =
					vbuff[160 * 2 + 0] = vbuff[160 * 2 + 1] = vbuff[160 * 2 + 2] = vbuff[160 * 2 + 3] =
					vbuff[160 * 3 + 0] = vbuff[160 * 3 + 1] = vbuff[160 * 3 + 2] = vbuff[160 * 3 + 3] = color;
				}
				vbuff += 4;
			} while(--ix);
			vbuff += 160 * 4 - 8;
		} while(--iy);

	/* 画面をはみ出す場合、クリッピングが必要です。 */
	} else {
		for(iy = 0; iy < 8; iy++) {
			if((unsigned)y <= 96 - 1) {
				for(ix = 0; ix < 8; ix++) {
					if((unsigned)x <= 160 - 1) {
						color = colors[(iy / 4) * 2 + (ix / 4)];
						if(color != -1) {
							*vbuff = color;
						}
					}
					vbuff++;
					x++;
				}
				vbuff -= 8;
				x -= 8;
			}
			vbuff += 160;
			y++;
		}
	}
}

void
stic_draw_mob(const STICMOB* mob)
{
	const unsigned short* graphics;
	unsigned char* vbuff;
	int color;
	int x;
	int y;
	int ix;
	int iy;
	int bits;

	/* 次の条件のどれか一つでも満たされる場合、描画処理全体を省略します。
	 * - GROM Card #0 (完全透過グラフィック) が選択されている。
	 * - STICリセット後、MOB X-positionレジスタに一度も書き込んでいない。
	 * - STICリセット後、MOB Y-positionレジスタに一度も書き込んでいない。
	 */
	if(!mob->card || !mob->w || !mob->h) {
		return;
	}

	x = mob->x + stic.x_delay;
	y = mob->y + stic.y_delay;

	/* 完全に画面外ならば、何もしません。 */
	if((x <= 0 - mob->w) || (x >= 160) ||
	   (y <= 0 - mob->h) || (y >=  96)) {
		return;
	}

	/* グラフィックデータを取得します。 */
	stic_make_mob_graphics(mob, stic_mob_graphics[0]);
	graphics = stic_mob_graphics[0];

	vbuff = &stic.vbuff[160 * y + x];
	color = mob->color;

	/* 完全に画面内ならば、クリッピングは不要です。 */
	if(((unsigned)x <= (unsigned)(160 - mob->w)) &&
	   ((unsigned)y <= (unsigned)( 96 - mob->h))) {
		iy = mob->h;
		do {
			bits = *graphics++;
			ix = mob->w;
			do {
				if(bits & 0x8000) {
					*vbuff = color;
				}
				bits <<= 1;
				vbuff++;
			} while(--ix);
			vbuff += 160 - mob->w;
		} while(--iy);

	/* 画面をはみ出す場合、クリッピングが必要です。 */
	} else {
		iy = mob->h;
		do {
			bits = *graphics++;
			if((unsigned)y <= 96 - 1) {
				ix = mob->w;
				do {
					if(bits & 0x8000) {
						if((unsigned)x <= 160 - 1) {
							*vbuff = color;
						}
					}
					bits <<= 1;
					vbuff++;
					x++;
				} while(--ix);
				vbuff -= mob->w;
				x -= mob->w;
			}
			vbuff += 160;
			y++;
		} while(--iy);
	}
}

void
stic_make_mob_graphics(const STICMOB* mob, unsigned short graphics[/*64*/])
{
	const unsigned char* src1;
	const unsigned short* src;
	unsigned short* dst;
	int card;
	int size;
	int a;
	int i;

	/* グラフィックデータの取得と、垂直反転を行います。 */
	if(mob->flags & STICMOB_RESOLUTION) {
		/* MOB resolution = 8x16 */
		card = mob->card & ~1;
		size = 16;
	} else {
		/* MOB resolution = 8x8 */
		card = mob->card;
		size = 8;
	}
	if(mob->flags & STICMOB_Y_FLIP) {
		src1 = &stic.graphics[card * 8 + size];
		dst = graphics;
		i = size;
		do {
			*dst++ = *--src1 << 8;
		} while(--i);
	} else {
		src1 = &stic.graphics[card * 8];
		dst = graphics;
		i = size;
		do {
			*dst++ = *src1++ << 8;
		} while(--i);
	}

	/* 水平反転を行います。 */
	if(mob->flags & STICMOB_X_FLIP) {
		dst = graphics;
		i = size;
		do {
			*dst = MIRROR(*dst >> 8) << 8;
			dst++;
		} while(--i);
	}

	/* 水平拡大を行います。 */
	if(mob->x_size) {
		dst = graphics;
		i = size;
		do {
			*dst = XSIZE2(*dst >> 8);
			dst++;
		} while(--i);
	}

	/* 垂直拡大、縮小を行います。 */
	switch(mob->y_size) {
	case 0:
		/* displayed height = x1/2 */
		size /= 2;
		src = graphics;
		dst = graphics;
		i = size;
		do {
			a  = *src++;
			a |= *src++;
			*dst++ = a;
		} while(--i);
		break;
	case 2:
		/* displayed height = x2 */
		src = &graphics[size    ];
		dst = &graphics[size * 2];
		i = size;
		do {
			a = *--src;
			*--dst = a;
			*--dst = a;
		} while(--i);
		size *= 2;
		break;
	case 3:
		/* displayed height = x4 */
		src = &graphics[size    ];
		dst = &graphics[size * 4];
		i = size;
		do {
			a = *--src;
			*--dst = a;
			*--dst = a;
			*--dst = a;
			*--dst = a;
		} while(--i);
		size *= 4;
		break;
	}

	/* グラフィックデータの垂直余白をクリアします。 */
	if(size < 64) {
		dst = &graphics[size];
		i = 64 - size;
		do {
			*dst++ = 0;
		} while(--i);
	}
}

void
stic_make_background_card_mask(int card, unsigned short mask[/*8*/])
{
	const unsigned char* src;
	int i;

	/* GROM/GRAMの8bitデータを、そのまま16bitに変換します。
	 * 左:MSB<->LSB:右なので、16bitの上位8bit側に詰めます。
	 */
	src = &stic.graphics[card * 8];
	i = 8;
	do {
		*mask++ = *src++ << 8;
	} while(--i);
}

void
stic_make_colored_squares_mask(const char colors[/*4*/], unsigned short mask[/*8*/])
{
	int i;
	int v;

	/* (4x4ピクセル1単位)x2x2で、透過色でない部分だけのマスクを作ります。
	 * 左:MSB<->LSB:右なので、16bitの上位8bit側に詰めます。
	 */
	i = 2; /* Top,Bottom */
	do {
		v = 0;
		if(*colors++ != -1) { /* Pixel0(TopLeft ),Pixel2(BottomLeft ) */
			v |= 0xf0 << 8;
		}
		if(*colors++ != -1) { /* Pixel1(TopRight),Pixel3(BottomRight) */
			v |= 0x0f << 8;
		}
		mask[0] = mask[1] = mask[2] = mask[3] = v;
		mask += 4;
	} while(--i);
}

int
stic_collision_graphics(int x1, int y1, const unsigned short* g1/*[64]*/, int x2, int y2, const unsigned short* g2/*[64]*/)
{
	int i;
	int v1;
	int v2;

	if(x1 + 16/*Graphics1Width */ <= x2) return 0;
	if(x2 + 16/*Graphics2Width */ <= x1) return 0;
	if(y1 + 64/*Graphics1Height*/ <= y2) return 0;
	if(y2 + 64/*Graphics2Height*/ <= y1) return 0;

	if(y1 <= y2) {
		i = y2 - y1;
		g1 += i;
	} else {
		i = y1 - y2;
		g2 += i;
	}
	if(x1 <= x2) {
		x2 -= x1;
		x1 = 0;
	} else {
		x1 -= x2;
		x2 = 0;
	}

	do {
		v1 = *g1++ << x2; /* 左:MSB<->LSB:右 なので、 */
		v2 = *g2++ << x1; /* 相手の座標で左シフトする */
		if(v1 & v2) {
			return 1;
		}
	} while(++i < 64/*Graphics1,2Height*/);

	return 0;
}

void
stic_collision()
{
	int retval;
	int i_mob1;
	int i_mob2;
	STICMOB* mob1;
	STICMOB* mob2;
	int i;
	int x1;
	int y1;
	int x2;
	int y2;
	int col;
	int row;
	int info;
	int card;
	int color;
	char colors[4];

	for(i_mob1 = 0; i_mob1 < 8; i_mob1++) {
		mob1 = &stic.mob[i_mob1];
		if(!(mob1->flags & STICMOB_INTERACT)) {
			continue;
		}
		stic_make_mob_graphics(mob1, stic_mob_graphics[0]);

		/*---------- MOB×MOB ----------*/

		for(i_mob2 = i_mob1 + 1; i_mob2 < 8; i_mob2++) {
			mob2 = &stic.mob[i_mob2];
			if(!(mob2->flags & STICMOB_INTERACT)) {
				continue;
			}
			stic_make_mob_graphics(mob2, stic_mob_graphics[1]);

			retval = stic_collision_graphics(
				mob1->x, mob1->y, stic_mob_graphics[0],
				mob2->x, mob2->y, stic_mob_graphics[1]);
			if(retval) {
				mob1->collision |= (1 << i_mob2);
				mob2->collision |= (1 << i_mob1);
				/* 相手方MOBを全て走査する必要があるため、一つ衝突しても継続です!! */
			}
		}

		/*---------- MOB×Background Card ----------*/

		y1 = mob1->y;
		//row = y1 / 8/*CardHeight*/;
		//↓非除数が負の場合の丸め方向が異なるので"/"は不可。">>"を使用。(必須!!)
		row = y1 >> 3;
		y1 &= 7/*CardHeight-1*/;
		y2 = 0;
		do {
			if((unsigned)row < 12) {
				x1 = mob1->x;
				//col = x1 / 8/*CardWidth*/;
				//↓非除数が負の場合の丸め方向が異なるので"/"は不可。">>"を使用。(必須!!)
				col = x1 >> 3;
				x1 &= 7/*CardWidth-1*/;
				x2 = 0;
				do {
					if((unsigned)col < 20) {
						info = stic.backtab[row * 20 + col];
						if(stic.flags & STIC_MODE) { /* Foreground/Background Mode */
							card = (info >> 3) & 0x13f;
							stic_make_background_card_mask(card, stic_background_card_mask);
						} else { /* Color Stack & Colored Squares Mode */
							if((info & 0x1800) == 0x1000) { /* Colored Squares Mode */
								info = ((info >> 2) & 0x800) | (info & 0x7ff);
								for(i = 0; i < 4; i++) {
									color = info & 7;
									if(color == 7) {
										color = -1; /* カラー7=背景扱い */
									} else {
									}
									colors[i] = color;
									info >>= 3;
								}
								stic_make_colored_squares_mask(colors, stic_background_card_mask);
							} else { /* Color Stack Mode */
								card = (info >> 3) & 0x1ff;
								if(card & 0x100) {
									card &= 0x13f;
								}
								stic_make_background_card_mask(card, stic_background_card_mask);
							}
						}
						retval = stic_collision_graphics(
							x1, y1, stic_mob_graphics[0],
							x2, y2, stic_background_card_mask);
						if(retval) {
							mob1->collision |= (1 << 8);
							goto MOB_CARD_EXIT; /* 一つ衝突したら、それ以降のCardは走査不要 */
						}
					}
					col++;
					x2 += 8/*CardWidth*/;
				} while(x2 < x1 + mob1->w);
			}
			row++;
			y2 += 8/*CardHeight*/;
		} while(y2 < y1 + mob1->h);
MOB_CARD_EXIT:

		/*---------- MOB×Border ----------*/

		y1 = mob1->y;
		//row = y1 / 8/*CardHeight*/;
		//↓非除数が負の場合の丸め方向が異なるので"/"は不可。">>"を使用。(必須!!)
		row = y1 >> 3;
		y1 &= 7/*CardHeight-1*/;
		y2 = 0;
		do {
			x1 = mob1->x;
			//col = x1 / 8/*CardWidth*/;
			//↓非除数が負の場合の丸め方向が異なるので"/"は不可。">>"を使用。(必須!!)
			col = x1 >> 3;
			x1 &= 7/*CardWidth-1*/;
			x2 = 0;
			do {
				if(((unsigned)col >= 20) || /* 画面外 */
				   ((unsigned)row >= 12) || /* 画面外 */
				   (!col && (stic.flags & STIC_BORDER_EXTENSION_LEFT)) || /* 左Border extension */
				   (!row && (stic.flags & STIC_BORDER_EXTENSION_TOP))) {  /* 上Border extension */
					retval = stic_collision_graphics(
						x1, y1, stic_mob_graphics[0],
						x2, y2, stic_border_mask[0/*8x8ベタのマスクパターン*/]);
				} else if(col == 19) { /* 画面右端(X=159,Y=0..95)の縦1ラインBorder */
					retval = stic_collision_graphics(
						x1, y1, stic_mob_graphics[0],
						x2, y2, stic_border_mask[1/*右端縦1ラインのパターン*/]);
				} else { /* 画面内 */
					retval = 0;
				}
				if(retval) {
					mob1->collision |= (1 << 9);
					goto MOB_BORDER_EXIT; /* 一つ衝突したら、それ以降のBorderは走査不要 */
				}
				col++;
				x2 += 8/*CardWidth*/;
			} while(x2 < x1 + mob1->w);
			row++;
			y2 += 8/*CardHeight*/;
		} while(y2 < y1 + mob1->h);
MOB_BORDER_EXIT:
		; /* ←VisualC++はgotoラベルの後に文を要求するので、ダミーのセミコロンが必要です。 */
	}
}

/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/

void
stic_reset(const unsigned short backtab[/*$0200-$02EF*/], const unsigned char graphics[/*$3000-$3AFF*/])
{
	memset(&stic, 0, sizeof(STIC));
	stic.backtab = backtab;
	stic.graphics = graphics;
}

int
stic_read(int addr)
{
	ASSERT((addr >= 0) && (addr <= 0x3f));

	switch(addr) {
	case 0x18:
	case 0x19:
	case 0x1a:
	case 0x1b:
	case 0x1c:
	case 0x1d:
	case 0x1e:
	case 0x1f: /* MOB Collision */
		return stic.mob[addr & 7].collision;
	case 0x21: /* Mode select */
		stic.flags &= ~STIC_MODE; /* Color Stack & Colored Squares Mode */
		break;
	}
	return 0;
}

void
stic_write(int addr, int data)
{
	STICMOB* mob;

	ASSERT((addr >= 0) && (addr <= 0x3f));
	ASSERT((data >= 0) && (data <= 0xffff));

	switch(addr) {
	case 0x00:
	case 0x01:
	case 0x02:
	case 0x03:
	case 0x04:
	case 0x05:
	case 0x06:
	case 0x07: /* MOB X-position */
		mob = &stic.mob[addr & 7];
		mob->x = (data & 0xff) - 8/*仕様*/;
		if(data & (1<<8)) {
			mob->flags |=  STICMOB_INTERACT;
		} else {
			mob->flags &= ~STICMOB_INTERACT;
		}
		if(data & (1<<9)) {
			mob->flags |=  STICMOB_VISIBLE;
		} else {
			mob->flags &= ~STICMOB_VISIBLE;
		}
		mob->x_size = (data >> 10) & 1;
		mob->w = 8 << mob->x_size;
		return;
	case 0x08:
	case 0x09:
	case 0x0a:
	case 0x0b:
	case 0x0c:
	case 0x0d:
	case 0x0e:
	case 0x0f: /* MOB Y-position */
		mob = &stic.mob[addr & 7];
		mob->y = (data & 0x7f) - 8/*仕様*/;
		if(data & (1<<7)) {
			mob->flags |=  STICMOB_RESOLUTION;
			mob->y_size = (data >> 8) & 3;
			mob->h = (16 / 2) << mob->y_size;
		} else {
			mob->flags &= ~STICMOB_RESOLUTION;
			mob->y_size = (data >> 8) & 3;
			mob->h = ( 8 / 2) << mob->y_size;
		}
		if(data & (1<<10)) {
			mob->flags |=  STICMOB_X_FLIP;
		} else {
			mob->flags &= ~STICMOB_X_FLIP;
		}
		if(data & (1<<11)) {
			mob->flags |=  STICMOB_Y_FLIP;
		} else {
			mob->flags &= ~STICMOB_Y_FLIP;
		}
		return;
	case 0x10:
	case 0x11:
	case 0x12:
	case 0x13:
	case 0x14:
	case 0x15:
	case 0x16:
	case 0x17: /* MOB Attribute */
		mob = &stic.mob[addr & 7];
		mob->color = ((data >> 9) & 4) | (data & 3);
#ifdef STIC_GRAYSCALE
		mob->color = stic_grayscale_table[mob->color];
#endif /*STIC_GRAYSCALE*/
		mob->card = (data >> 3) & 0x1ff;
		if(mob->card & 0x100) {
			mob->card &= 0x13f;
		}
		if(data & (1<<13)) {
			mob->flags |=  STICMOB_PRIORITY;
		} else {
			mob->flags &= ~STICMOB_PRIORITY;
		}
		return;
	case 0x18:
	case 0x19:
	case 0x1a:
	case 0x1b:
	case 0x1c:
	case 0x1d:
	case 0x1e:
	case 0x1f: /* MOB Collision */
		addr &= 7;		/* addr=MOB番号 */
		data &= 0x3ff;		/* 未使用ビットは0固定(?) */
		data &= ~(1 << addr);	/* MOBx<->MOBxの自己衝突ビットは0固定 */
		stic.mob[addr].collision &= data;
		return;
	//case 0x20: /* Display enable */
	case 0x21: /* Mode select */
		stic.flags |= STIC_MODE; /* Foreground/Background Mode */
		return;
	//case 0x22:
	//case 0x23:
	//case 0x24:
	//case 0x25:
	//case 0x26:
	//case 0x27: /* (reserved) */
	case 0x28:
	case 0x29:
	case 0x2a:
	case 0x2b: /* Color Stack */
		data &= 15;
#ifdef STIC_GRAYSCALE
		data = stic_grayscale_table[data];
#endif /*STIC_GRAYSCALE*/
		stic.color_stack[addr & 3] = data;
		return;
	case 0x2c: /* Display border color */
		data &= 15;
#ifdef STIC_GRAYSCALE
		data = stic_grayscale_table[data];
#endif /*STIC_GRAYSCALE*/
		stic.border_color = data;
		return;
	//case 0x2d:
	//case 0x2e:
	//case 0x2f: /* (reserved) */
	case 0x30: /* Horizontal Delay */
		stic.x_delay = data & 7;
		return;
	case 0x31: /* Vertical Delay */
		stic.y_delay = data & 7;
		return;
	case 0x32: /* Border extension */
		if(data & 1) {
			stic.flags |=  STIC_BORDER_EXTENSION_LEFT;
		} else {
			stic.flags &= ~STIC_BORDER_EXTENSION_LEFT;
		}
		if(data & 2) {
			stic.flags |=  STIC_BORDER_EXTENSION_TOP;
		} else {
			stic.flags &= ~STIC_BORDER_EXTENSION_TOP;
		}
		return;
	//case 0x33:
	//case 0x34:
	//case 0x35:
	//case 0x36:
	//case 0x37:
	//case 0x38:
	//case 0x39:
	//case 0x3a:
	//case 0x3b:
	//case 0x3c:
	//case 0x3d:
	//case 0x3e:
	//case 0x3f: /* (reserved) */
	}
}

void
stic_update(unsigned char vbuff[/*160*96*/])
{
	const unsigned short* backtab;
	int color_stack;
	STICMOB* mob;
	int i;
	int x;
	int y;
	int col;
	int row;
	int info;
	int card;
	int color;
	char colors[4];

	/* 実描画有りならば... */
	if(vbuff) {
		/* 仮想画面を格納します。 */
		stic.vbuff = vbuff;

		/* Color Stackをリセットします。(Foreground/Background Modeの場合はダミー処理) */
		color_stack = 0;
		color = stic.color_stack[color_stack];

		/* Background Cardの背景ピクセルを描きます。 */
		backtab = stic.backtab;
		y = stic.y_delay;
		for(row = 0; row < 12; row++) {
			x = stic.x_delay;
			for(col = 0; col < 20; col++) {
				info = *backtab++;
				if(stic.flags & STIC_MODE) { /* Foreground/Background Mode */
					color = ((info >> 9) & 8) | ((info >> 11) & 4) | ((info >> 9) & 3);
					stic_fill_background_card(x, y, color);
				} else { /* Color Stack & Colored Squares Mode */
					/* Color Stack ModeとColored Squares Modeは、どちらも背景色としてColor Stackを用います。
					 * Colored Squares Modeの場合、カラー7のピクセルが背景扱いとなります。(当り判定も無し)
					 * 従って、背景色塗りぶつし処理においては、どちらのModeのセルも同じ処理となります。
					 * ただし、Color Stackを進めることができるのは、Color Stack Modeのセルだけです。
					 */
					switch(info & 0x3800) {
					//case 0x0000:	/* Color Stack Mode, No Advance */
					//case 0x0800:	/* Color Stack Mode, No Advance */
					//case 0x1000:	/* Colored Square Mode */
					//case 0x1800:	/* Color Stack Mode, No Advance */
					case 0x2000:	/* Color Stack Mode, Advance */
					case 0x2800:	/* Color Stack Mode, Advance */
					//case 0x3000:	/* Colored Square Mode */
					case 0x3800:	/* Color Stack Mode, Advance */
						color_stack = (color_stack + 1) & 3;
						color = stic.color_stack[color_stack];
						break;
					}
					stic_fill_background_card(x, y, color);
				}
				x += 8;
			}
			y += 8;
		}

		/* 低優先度のMOBを描きます。 */
		for(i = 7; i >= 0; i--) {
			mob = &stic.mob[i];
			if((mob->flags & STICMOB_VISIBLE) &&
			   (mob->flags & STICMOB_PRIORITY)) {
				stic_draw_mob(mob);
			}
		}

		/* Background Cardの前景ピクセルを描きます。 */
		backtab = stic.backtab;
		y = stic.y_delay;
		for(row = 0; row < 12; row++) {
			x = stic.x_delay;
			for(col = 0; col < 20; col++) {
				info = *backtab++;
				if(stic.flags & STIC_MODE) { /* Foreground/Background Mode */
					card = (info >> 3) & 0x13f;
					color = info & 7;
#ifdef STIC_GRAYSCALE
					color = stic_grayscale_table[color];
#endif /*STIC_GRAYSCALE*/
					stic_draw_background_card(x, y, card, color);
				} else { /* Color Stack & Colored Squares Mode */
					if((info & 0x1800) == 0x1000) { /* Colored Squares Mode */
						info = ((info >> 2) & 0x800) | (info & 0x7ff);
						for(i = 0; i < 4; i++) {
							color = info & 7;
							if(color == 7) {
								color = -1; /* カラー7=背景扱い */
							} else {
#ifdef STIC_GRAYSCALE
								color = stic_grayscale_table[color];
#endif /*STIC_GRAYSCALE*/
							}
							colors[i] = color;
							info >>= 3;
						}
						stic_draw_colored_squares(x, y, colors);
					} else { /* Color Stack Mode */
						card = (info >> 3) & 0x1ff;
						color = info & 7;
						if(card & 0x100) {
							card &= 0x13f;
							color |= (info >> 9) & 8;
						}
#ifdef STIC_GRAYSCALE
						color = stic_grayscale_table[color];
#endif /*STIC_GRAYSCALE*/
						stic_draw_background_card(x, y, card, color);
					}
				}
				x += 8;
			}
			y += 8;
		}

		/* 高優先度のMOBを描きます。 */
		for(i = 7; i >= 0; i--) {
			mob = &stic.mob[i];
			if((mob->flags & STICMOB_VISIBLE) &&
			  !(mob->flags & STICMOB_PRIORITY)) {
				stic_draw_mob(mob);
			}
		}

		/* ボーダーを塗りつぶします。 */
		color = stic.border_color;
		/* STICの可視領域は159x96なので、右1ピクセルを無条件でマスク。 */
		vbuff = &stic.vbuff[160 * 0 + 159];
		y = 96;
		do {
			*vbuff = color;
			vbuff += 160;
		} while(--y);
		/* Border extension(Left edge)=1ならば、左8ピクセルをマスク。 */
		if(stic.flags & STIC_BORDER_EXTENSION_LEFT) {
			vbuff = &stic.vbuff[160 * 0 + 0];
			y = 96;
			do {
				memset(vbuff, color, 8);
				vbuff += 160;
			} while(--y);
		}
		/* Border extension(Top edge)=1ならば、上8ピクセルをマスク。 */
		if(stic.flags & STIC_BORDER_EXTENSION_TOP) {
			memset(&stic.vbuff[160 * 0 + 0], color, 160 * 8);
		}
	}

	/* 衝突検出を行います。 */
	stic_collision();
}

