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

	i68k.c

	MC68000 簡易インタプリタ (調整中)

    以下の命令は未サポート
     MOVEP (to Dn) MOVEP (from Dn)
     PEA LINK UNLK
     CHK TRAP TRAPV
     ABCD NBCD SBCD
     RTR
     特権命令全て

    (サポートしている命令でも実装していないオペレーションあり)

    元々デバッガ用に作成した、処理内容を画面に出力する関数だった為、
    サイクル数等は一切考慮していません。速度もそれなりに遅いです。

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

#include "ncdz.h"

#define VERBOSE 0
#define LOG(x)


/****************************************************************************
	マクロ
 ***************************************************************************/

#define An   	0	// address register
#define Dn   	1	// data register
#define AI   	2	// address register indirect
#define PI		3	// postincrement
#define PI7		4	// postincrement (A7)
#define PD   	5	// predecrement
#define PD7  	6	// predecrement (A7)
#define IX   	7	// indirect + index
#define IM 	 	8	// immidiate
#define AA 	 	9	// absolute addressing
#define MM 		10	// movem register list
#define CCR		11	// condition code register
#define SR 		12	// status register
#define NA 		-1	// N/A

#define SIZE_B	1
#define SIZE_W	2
#define SIZE_L	4

#define NOTFOUND	0
#define SET1		1
#define SET2		2
#define TEST		3
#define INVALID		4
#define OTHER		5

#define OPCODE(x)	(strcmp(opcode, x) == 0)

#define MAKE_INT_8(A)  (s8)((A) & 0xff)
#define MAKE_INT_16(A) (s16)((A) & 0xffff)
#define MAKE_INT_32(A) (s32)((A) & 0xffffffff)

#define HI		(C == 0 && Z == 0)
#define LS		(C != 0 || Z != 0)
#define CC		(C == 0)
#define CS		(C != 0)
#define NE		(Z == 0)
#define EQ		(Z != 0)
#define VC		(V == 0)
#define VS		(V != 0)
#define PL		(N == 0)
#define MI		(N != 0)
#define GE		((N == 0) == (V == 0))
#define LT		((N == 0) != (V == 0))
#define GT		(Z == 0 && (N == 0) == (V == 0))
#define LE		(Z != 0 || (N == 0) != (V == 0))

#define read8(addr)			m68k_read_memory_8(addr)
#define read16(addr)		m68k_read_memory_16(addr)
#define read32(addr)		m68k_read_memory_32(addr)

#define write8(addr, data)	m68k_write_memory_8(addr, data)
#define write16(addr, data)	m68k_write_memory_16(addr, data)
#define write32(addr, data)	m68k_write_memory_32(addr, data)


/****************************************************************************
	プロトタイプ
 ***************************************************************************/

int m68k_disassemble(char *str_buff, int pc);


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

static u32 value1, value2, addr1, addr2;
static u32 a[8], d[8];
static u16 sr;
static int X, N, Z, V, C;


/***************************************************************************
	文字列解析
 ***************************************************************************/

/*--------------------------------------------------------

	buf1の文字列からデータの種類を判別し、計算しやすい
	形に変換してbuf2に格納する

	引  数: char *buf1    元の文字列
	        char *buf2    変換した文字列の格納先
	        u32 pc     現在のPC(OPCODE含む)

	戻り値: 文字列の種類

    アドレスレジスタ及びデータレジスタはbuf[0]に
    レジスタ番号が数値で入ります

 -------------------------------------------------------*/

static int check_type(char *buf1, char *buf2, u32 pc)
{
	int i, j;

	memset(buf2, 0, 256);

	switch (buf1[0])
	{
	case '[':
		// movem - 表記を変更してあります
		for (i = 1; buf1[i] != ']'; i++)
			buf2[i - 1] = buf1[i];
		return MM;

	case 'A':
		// アドレスレジスタ
		buf2[0] = buf1[1] - '0';
		return An;

	case 'D':	// データレジスタ
		buf2[0] = buf1[1] - '0';
		return Dn;

	case 'C':	// CCR
		buf2[0] = 0;
		return SR;

	case 'S':	// ステータスレジスタ
		buf2[0] = 0;
		return SR;

	case '#':	// イミディエイト
		i = 1;
		j = 0;
		while (1)
		{
			if (buf1[i] == '\0'
			||	buf1[i] == ','
			||  buf1[i] == '.')
			{
				break;
			}
			else
			if ((buf1[i] == '-')
			||  (buf1[i] >= '0' && buf1[i] <= '9')
			||	(buf1[i] >= 'a' && buf1[i] <= 'f'))
			{
				buf2[j++] = buf1[i];
			}
			i++;
		}
		return IM;

	case '$':	// アドレス直接指定
		strcpy(buf2, &buf1[1]);
		*strchr(buf2, '.') = '\0';
		return AA;

	case '-':	// プレデクリメント
		buf2[0] = buf1[2];
		buf2[1] = buf1[3];
		return (buf1[3] == '7') ? PD7 : PD;

	case '(':
		if (buf1[1] == 'A' && buf1[3] == ')')
		{
			buf2[0] = buf1[1];
			buf2[1] = buf1[2];

			if (buf1[4] == '+')
			{
				// ポストインクリメント
				return (buf1[2] == '7') ? PI7 : PI;
			}
			else
			{
				// アドレスレジスタ間接参照
				return AI;
			}
		}

		i = 1;
		j = 0;
		while (1)
		{
			if (buf1[i] == ')')
				break;

			else if (buf1[i] == ',')
			{
				buf2[j++] = ',';
			}
			else
			if (buf1[i] == 'P')
			{
				char tmp[16];

				// PCは数値に変換しておく
				sprintf(tmp, "%x", pc + 2);
				strcat(buf2, tmp);
				i++;
				j += strlen(tmp);
			}
			else
			if ((buf1[i] == 'A')
			||	(buf1[i] == 'D')
			||	(buf1[i] == '-')
			||  (buf1[i] >= '0' && buf1[i] <= '9')
			||	(buf1[i] >= 'a' && buf1[i] <= 'f'))
			{
				buf2[j++] = buf1[i];
			}
			i++;
		}
		return IX;

	default:
		return NA;
	}
}


/***************************************************************************
	レジスタ操作
 ***************************************************************************/

/*--------------------------------------------------------

	データレジスタの値を取得

	引  数: int n         レジスタ番号
	        int size      サイズ (バイト)
	戻り値: レジスタのの値

 -------------------------------------------------------*/

static u32 get_d(int n, int size)
{
	switch (size)
	{
	case SIZE_B: return d[n] & 0xff;
	case SIZE_W: return d[n] & 0xffff;
	}
	return d[n];
}


/*--------------------------------------------------------

	データレジスタの値を設定

	引  数: int n         レジスタ番号
	        u32 value  設定する値
	        int size      サイズ (バイト)
	戻り値: なし

 -------------------------------------------------------*/

static void set_d(int n, u32 value, int size)
{
	switch (size)
	{
	case SIZE_B:
		d[n] &= 0xffffff00;
		d[n] |= value & 0xff;
		return;

	case SIZE_W:
		d[n] &= 0xffff0000;
		d[n] |= value & 0xffff;
		return;
	}
	d[n] = value;
}


/*--------------------------------------------------------

	アドレスレジスタの値を取得

	引  数: int n         レジスタ番号
	        int size      サイズ (バイト)
	戻り値: レジスタのの値

  ※現状常にLONGの値を返します

 -------------------------------------------------------*/

static u32 get_a(int n)
{
	return a[n];
}


/*--------------------------------------------------------

	アドレスレジスタの値を設定

	引  数: int n         レジスタ番号
	        u32 value  設定する値
	        int size      サイズ (バイト)
	戻り値: なし

  ※現状常にLONGの値を設定します

 -------------------------------------------------------*/

static void set_a(int n, u32 value)
{
	a[n] = value;
}


/***************************************************************************
	イミディエイト値操作
 ***************************************************************************/

/*--------------------------------------------------------

	即値の文字列を数値に変換して取得

	引  数: char *buf     文字列のバッファ
	        int size      サイズ (バイト)
	戻り値: 値

 -------------------------------------------------------*/

static u32 get_imm(char *buf, int size)
{
	int value;

	if (buf[0] == '-')
	{
		sscanf(&buf[1], "%x", &value);
		value = 0 - value;
	}
	else
	{
		sscanf(buf, "%x", &value);
	}

	switch (size)
	{
	case SIZE_B: return value & 0xff;
	case SIZE_W: return value & 0xffff;
	}
	return value;
}


/***************************************************************************
	エフェクティブアドレス操作
 ***************************************************************************/

/*--------------------------------------------------------

	アドレスの文字列を数値に変換して取得

	引  数: char *buf     文字列のバッファ
	        int size      サイズ (バイト)
	戻り値: アドレスの値

 -------------------------------------------------------*/

static int get_ea(char *buf, int type, int size)
{
	int n, res = 0;
	char *p;

	switch (type)
	{
	case AI:
		n = buf[1] - '0';
		res = a[n];
		break;

	case PD:
	case PD7:
		n = buf[1] - '0';
		a[n] -= size;
		res = a[n];
		break;

	case PI:
	case PI7:
		n = buf[1] - '0';
		res = a[n];
		a[n] += size;
		break;

	case AA:
		sscanf(buf, "%x", &res);
		break;

	case IX:
		p = strtok(buf, ",\n\0");

		while (p)
		{
			if ((p[0] == '-') || (p[0] >= '0' && p[0] <= '9') || (p[0] >= 'a' && p[0] <= 'f'))
			{
				int value;

				if (p[0] == '-')
				{
					sscanf(&p[1], "%x", &value);
					value = 0 - value;
				}
				else
				{
					sscanf(p, "%x", &value);
				}
				res += value;
			}
			else if (p[0] == 'A')
			{
				n = p[1] - '0';
				res += a[n];
			}
			else if (p[0] == 'D')
			{
				n = p[1] - '0';
				res += MAKE_INT_16(d[n]);
			}

			p = strtok(NULL, ",\n\0");
		}
		break;
	}

	return res;
}


/*--------------------------------------------------------

	アドレスのデータを取得

	引  数: int addr      アドレス
	        int size      サイズ (バイト)
	戻り値: アドレスの値

 -------------------------------------------------------*/

static u32 read_ea(int addr, int size)
{
	switch (size)
	{
	case SIZE_B: return read8(addr);
	case SIZE_W: return read16(addr);
	}
	return read32(addr);
}


/*--------------------------------------------------------

	アドレスのデータを設定

	引  数: int addr      アドレス
	        u32 value  設定する値
	        int size      サイズ (バイト)
	戻り値: なし

 -------------------------------------------------------*/

static void write_ea(int addr, u32 value, int size)
{
	switch (size)
	{
	case SIZE_B: write8(addr, value & 0xff); return;
	case SIZE_W: write16(addr, value & 0xffff); return;
	}
	write32(addr, value);
}


/***************************************************************************
	オペレーション & フラグセット
 ***************************************************************************/

/*--------------------------------------------------------

	ネガティブフラグ取得

	引  数: u32 result  オペレーションの結果の値
	        int size       サイズ (バイト)
	戻り値: Nフラグの値

 -------------------------------------------------------*/

static int NFLAG(u32 result, int size)
{
	switch (size)
	{
	case SIZE_B: return (result & 0x80) != 0;
	case SIZE_W: return (result & 0x8000) != 0;
	}
	return (result & 0x80000000) != 0;
}


/*--------------------------------------------------------

	ゼロフラグ取得

	引  数: u32 result  オペレーションの結果の値
	        int size       サイズ (バイト)
	戻り値: Zフラグの値

 -------------------------------------------------------*/

static int ZFLAG(u32 result, int size)
{
	switch (size)
	{
	case SIZE_B: return (result & 0xff) == 0;
	case SIZE_W: return (result & 0xffff) == 0;
	}
	return (result & 0xffffffff) == 0;
}


/*--------------------------------------------------------

	ADDを実行し、演算結果を返す

	引  数: u32 s       ソース
	        u32 d       デスティネーション
	        int size       サイズ (バイト)
	戻り値: 演算結果

  ※同時にフラグも設定します

 -------------------------------------------------------*/

#define VFLAG_ADD(s, d, r) ((s ^ r) & (d ^ r))

static u32 op_add(u32 s, u32 d, int size)
{
	u32 result;

	switch (size)
	{
	case SIZE_B:
		s &= 0x000000ff;
		d &= 0x000000ff;
		break;

	case SIZE_W:
		s &= 0x0000ffff;
		d &= 0x0000ffff;
		break;
	}

	result = d + s;

	switch (size)
	{
	case SIZE_B:
		C = result;
		V = VFLAG_ADD(s, d, result);
		result &= 0x000000ff;
		break;

	case SIZE_W:
		C = result >> 8;
		V = VFLAG_ADD(s, d, result) >> 8;
		result &= 0x0000ffff;
		break;

	case SIZE_L:
		C = ((s & d & 1) + (s >> 1) + (d >> 1)) >> 23;
		V = VFLAG_ADD(s, d, result) >> 24;
		break;
	}

	C = (C & 0x100) != 0;
	V = (V & 0x80) != 0;
	N = NFLAG(result, size);
	Z = ZFLAG(result, size);

#if VERBOSE
	switch (size)
	{
	case SIZE_B: LOG(("%02x + %02x (=%02x) -> ", d, s, result)); break;
	case SIZE_W: LOG(("%04x + %04x (=%04x) -> ", d, s, result)); break;
	case SIZE_L: LOG(("%08x + %08x (=%08x) -> ", d, s, result)); break;
	}
#endif

	return result;
}


/*--------------------------------------------------------

	SUBを実行し、演算結果を返す

	引  数: u32 s       ソース
	        u32 d       デスティネーション
	        int size       サイズ (バイト)
	戻り値: 演算結果

  ※同時にフラグも設定します

 -------------------------------------------------------*/

#define VFLAG_SUB(s, d, r) ((s ^ d) & (r ^ d))

static u32 op_sub(u32 s, u32 d, int size)
{
	u32 result;

	switch (size)
	{
	case SIZE_B:
		s &= 0x000000ff;
		d &= 0x000000ff;
		break;

	case SIZE_W:
		s &= 0x0000ffff;
		d &= 0x0000ffff;
		break;
	}

	result = d - s;

	switch (size)
	{
	case SIZE_B:
		C = result;
		V = VFLAG_SUB(s, d, result);
		result &= 0x000000ff;
		break;

	case SIZE_W:
		C = result >> 8;
		V = VFLAG_SUB(s, d, result) >> 8;
		result &= 0x0000ffff;
		break;

	case SIZE_L:
		C = ((s & result & 1) + (s >> 1) + (result >> 1)) >> 23;
		V = VFLAG_SUB(s, d, result) >> 24;
		break;
	}

	C = (C & 0x100) != 0;
	V = (V & 0x80) != 0;
	N = NFLAG(result, size);
	Z = ZFLAG(result, size);

#if VERBOSE
	switch (size)
	{
	case SIZE_B: LOG(("%02x - %02x (=%02x) -> ", d, s, result)); break;
	case SIZE_W: LOG(("%04x - %04x (=%04x) -> ", d, s, result)); break;
	case SIZE_L: LOG(("%08x - %08x (=%08x) -> ", d, s, result)); break;
	}
#endif

	return result;
}


/*--------------------------------------------------------

	ANDを実行し、演算結果を返す

	引  数: u32 s       ソース
	        u32 d       デスティネーション
	        int size       サイズ (バイト)
	戻り値: 演算結果

  ※同時にフラグも設定します

 -------------------------------------------------------*/

static u32 op_and(u32 s, u32 d, int size)
{
	u32 result;

	switch (size)
	{
	case SIZE_B:
		s &= 0x000000ff;
		d &= 0x000000ff;
		break;

	case SIZE_W:
		s &= 0x0000ffff;
		d &= 0x0000ffff;
		break;
	}

	result = d & s;

	switch (size)
	{
	case SIZE_B:
		result &= 0x000000ff;
		break;

	case SIZE_W:
		result &= 0x0000ffff;
		break;
	}

	C = 0;
	V = 0;
	N = NFLAG(result, size);
	Z = ZFLAG(result, size);

#if VERBOSE
	switch (size)
	{
	case SIZE_B: LOG(("%02x & %02x (=%02x) -> ", d, s, result)); break;
	case SIZE_W: LOG(("%04x & %04x (=%04x) -> ", d, s, result)); break;
	case SIZE_L: LOG(("%08x & %08x (=%08x) -> ", d, s, result)); break;
	}
#endif

	return result;
}


/*--------------------------------------------------------

	EORを実行し、演算結果を返す

	引  数: u32 s       ソース
	        u32 d       デスティネーション
	        int size       サイズ (バイト)
	戻り値: 演算結果

  ※同時にフラグも設定します

 -------------------------------------------------------*/

static u32 op_eor(u32 s, u32 d, int size)
{
	u32 result;

	switch (size)
	{
	case SIZE_B:
		s &= 0x000000ff;
		d &= 0x000000ff;
		break;

	case SIZE_W:
		s &= 0x0000ffff;
		d &= 0x0000ffff;
		break;
	}

	result = d ^ s;

	switch (size)
	{
	case SIZE_B:
		result &= 0x000000ff;
		break;

	case SIZE_W:
		result &= 0x0000ffff;
		break;
	}

	C = 0;
	V = 0;
	N = NFLAG(result, size);
	Z = ZFLAG(result, size);

#if VERBOSE
	switch (size)
	{
	case SIZE_B: LOG(("%02x ^ %02x (=%02x) -> ", d, s, result)); break;
	case SIZE_W: LOG(("%04x ^ %04x (=%04x) -> ", d, s, result)); break;
	case SIZE_L: LOG(("%08x ^ %08x (=%08x) -> ", d, s, result)); break;
	}
#endif

	return result;
}


/*--------------------------------------------------------

	ORを実行し、演算結果を返す

	引  数: u32 s       ソース
	        u32 d       デスティネーション
	        int size       サイズ (バイト)
	戻り値: 演算結果

  ※同時にフラグも設定します

 -------------------------------------------------------*/

static u32 op_or(u32 s, u32 d, int size)
{
	u32 result;

	switch (size)
	{
	case SIZE_B:
		s &= 0x000000ff;
		d &= 0x000000ff;
		break;

	case SIZE_W:
		s &= 0x0000ffff;
		d &= 0x0000ffff;
		break;
	}

	result = d | s;

	switch (size)
	{
	case SIZE_B:
		result &= 0x000000ff;
		break;

	case SIZE_W:
		result &= 0x0000ffff;
		break;
	}

	C = 0;
	V = 0;
	N = NFLAG(result, size);
	Z = ZFLAG(result, size);

#if VERBOSE
	switch (size)
	{
	case SIZE_B: LOG(("%02x | %02x (=%02x) -> ", d, s, result)); break;
	case SIZE_W: LOG(("%04x | %04x (=%04x) -> ", d, s, result)); break;
	case SIZE_L: LOG(("%08x | %08x (=%08x) -> ", d, s, result)); break;
	}
#endif

	return result;
}


/***************************************************************************
	文字列から値を取得
 ***************************************************************************/

/*--------------------------------------------------------

	値1(ソースまたはデスティネーションのみの場合)

	引  数: char *buf       文字列のバッファ
	        int type        check_type()で取得した値
	        int size        サイズ (バイト)
	戻り値: なし

  ※value1に値を、EAの場合はaddr1にアドレスを設定します

 -------------------------------------------------------*/

INLINE void get_value1(char *buf, int type, int size)
{
	switch (type)
	{
	case NA:
		LOG(("(error) "));
		break;

	case CCR:
		value1 = ((X << 4) | (N << 3) | (Z << 2) | (V << 1) | C);
		break;

	case SR:
		value1 = (SR & 0xffe1) | ((X << 4) | (N << 3) | (Z << 2) | (V << 1) | C);
		break;

	case An:
		value1 = a[(int)buf[0]];
		break;

	case Dn:
		value1 = get_d(buf[0], size);
		break;

	case IM:
		value1 = get_imm(buf, size);
		break;

	default:
		addr1 = get_ea(buf, type, size);
		value1 = read_ea(addr1, size);
		break;
	}
}


/*--------------------------------------------------------

	値2 (デスティネーション)

	引  数: char *buf       文字列のバッファ
	        int type        check_type()で取得した値
	        int size        サイズ (バイト)
	戻り値: なし

  ※value2に値を、EAの場合はaddr2にアドレスを設定します

 -------------------------------------------------------*/

INLINE void get_value2(char *buf, int type, int size)
{
	switch (type)
	{
	case NA:
		LOG(("(error) "));
		break;

	case CCR:
		value1 = ((X << 4) | (N << 3) | (Z << 2) | (V << 1) | C);
		break;

	case SR:
		value1 = (SR & 0xffe1) | ((X << 4) | (N << 3) | (Z << 2) | (V << 1) | C);
		break;

	case An:
		value2 = a[(int)buf[0]];
		break;

	case Dn:
		value2 = get_d(buf[0], size);
		break;

	case IM:
		value2 = get_imm(buf, size);
		break;

	default:
		addr2 = get_ea(buf, type, size);
		value2 = read_ea(addr2, size);
		break;
	}
}


/***************************************************************************
	インタプリタコア
 ***************************************************************************/

/*--------------------------------------------------------

	MC68000 簡易インタプリタ

	引  数: u32 start_pc    開始PC
	        u32 break_point ブレークポイント
	戻り値: なし

  ※ブレークポイントが'0'の場合は、開始PCと同じレベルの
    処理が終了した時点で停止します

 -------------------------------------------------------*/

void m68000_interpreter(u32 start_pc, u32 break_point)
{
	u32 pc, nest_pc[256];
	int type1, type2, size;
	char buf[256], src[256], dst[256];
	char *opcode, *p, *tmp1, *tmp2;
	u32 mask, msb, nest_count, result;
	int i, n1, n2, opcode_len, op;

	pc = start_pc;
	nest_count= 0;

	for (i = 0; i < 8; i++)
	{
		a[i] = m68000_get_reg(M68K_A0 + i);
		d[i] = m68000_get_reg(M68K_D0 + i);
	}

	sr = m68000_get_reg(M68K_SR);
	C = sr & (1 << 0);
	V = sr & (1 << 1);
	Z = sr & (1 << 2);
	N = sr & (1 << 3);
	X = sr & (1 << 4);

	// NEOGEO CD用
	a[5] = 0x108000;
	a[7] -= 4 * 8 * 2;	// スタックを破壊しないように念の為

	result = 0;
	n1 = 0;
	n2 = 0;

	LOG(("\n-- start --\n"));

	while (pc != break_point)
	{
		opcode_len = m68k_disassemble(buf, pc);

		if ((p = strchr(buf, ';')) != NULL) *p = '\0';	// コメント削除

		LOG(("%06x: %-32s", pc, buf));

		opcode = strtok(buf, " \n");
		tmp1 = strtok(NULL, " \n");
		tmp2 = strtok(NULL, " \n");

		if (tmp1 != NULL)
			type1 = check_type(tmp1, src, pc);
		else
			type1 = NA;

		if (tmp2 != NULL)
			type2 = check_type(tmp2, dst, pc);
		else
			type2 = NA;

		if ((p = strchr(buf, '.')) != NULL)
		{
			switch (*(p + 1))
			{
			case 'b':
				size = SIZE_B;
				mask = 0x000000ff;
				msb  = 0x00000080;
				break;

			case 'w':
				size = SIZE_W;
				mask = 0x0000ffff;
				msb  = 0x00008000;
				break;

			default:
				size = SIZE_L;
				mask = 0xffffffff;
				msb  = 0x80000000;
				break;
			}
			*p = '\0';
		}
		else
		{
			size = SIZE_L;
			mask = 0xffffffff;
			msb  = 0x80000000;
		}

		if (type2 == An && size == SIZE_B)
		{
			LOG(("%s\tbyte size oparation (illegal).\n"));
			pc += opcode_len;
			continue;
		}

		LOG(("%s\t", opcode));

		if (type1 == An || type1 == Dn) n1 = src[0];
		if (type2 == An || type2 == Dn) n2 = dst[0];

		// 実行
		op = NOTFOUND;

		switch (opcode[0])
		{
		case 'a':
			if (OPCODE("add") || OPCODE("addi"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				result = op_add(value1, value2, size);
				X = C;

				op = SET2;
			}
			else if (OPCODE("adda"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, SIZE_L);

				if (size == SIZE_W && type1 != An)
					value1 = MAKE_INT_16(value1);

				result = value2 + value1;

				LOG(("%06x + %08x (=%06x) -> ", value2, value1, result));

				op = SET2;
			}
			else if (OPCODE("addq"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				if (type2 == An)
				{
					if (size == SIZE_W && type1 != An)
						value1 = MAKE_INT_16(value1);

					result = value2 + value1;

					LOG(("%06x + %08x (=%06x) -> ", value2, value1, result));
				}
				else
				{
					result = op_add(value1, value2, size);
					X = C;
				}

				op = SET2;
			}
			else if (OPCODE("addx"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				result = op_add(value1 + X, value2, size);
				X = C;

				op = SET2;
			}
			else if (OPCODE("and") || OPCODE("andi"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				result = op_and(value1, value2, size);

				op = SET2;
			}
			else if (OPCODE("asl"))
			{
				u32 msb_bit1, msb_bit2;

				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					C = 0;
				}
				else
				{
					msb_bit1 = result & msb;
					X = C = (result & (msb >> (value1 - 1))) != 0;

					msb_bit2 = msb_bit1;

					for (i = 0; i < value1; i++)
					{
						result = (result << 1) & mask;
						V = (msb_bit2 != (result & msb));
						msb_bit2 = result & msb;
					}

					result = (result & ~msb) | msb_bit1;
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x << %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x << %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x << %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);

				op = SET2;
			}
			else if (OPCODE("asr"))
			{
				u32 msb_bit1, msb_bit2;

				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					C = 0;
				}
				else
				{
					msb_bit1 = result & msb;
					X = C = (result & (1 << (value1 - 1))) != 0;

					msb_bit2 = msb_bit1;

					for (i = 0; i < value1; i++)
					{
						result = (result >> 1) & mask;
						V = (msb_bit2 != (result & msb));
						msb_bit2 = result & msb;
					}

					result = (result & ~msb) | msb_bit1;
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x >> %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x >> %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x >> %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);

				op = SET2;
			}
			break;

		case 'b':
			if (OPCODE("bchg") || OPCODE("bclr") || OPCODE("bset") || OPCODE("btst"))
			{
				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);

					value1 %= 32;

					Z = (value2 & (1 << value1)) == 0;

					LOG(("%08x & bit%d -> Z (= %d)", value2, value1, Z));
				}
				else
				{
					size = SIZE_B;
					mask = 0x000000ff;
					msb  = 0x00000080;

					get_value1(src, type1, size);
					get_value2(dst, type2, size);

					value1 %= 8;

					Z = (value2 & (1 << value1)) == 0;

					LOG(("%02x & bit%d -> Z (= %d)", value2, value1, Z));
				}

				if (opcode[2] != 's')
				{
					switch (opcode[2])
					{
					case 'h':	// bchg
						result = value2 ^ (value2 & (1 << value1));
						break;

					case 'l':	// bclr
						result = value2 & ~(value2 & (1 << value1));
						break;

					case 'e':	// bset
						result = value2 | (value2 & (1 << value1));
						break;
					}
					LOG((", %08x -> ", result));

					op = SET2;
				}
				else
				{
					LOG(("\n"));
					op = OTHER;	// TEST
				}
			}
			else if (OPCODE("bsr"))
			{
				nest_pc[nest_count++] = pc + opcode_len;
				pc = (pc + 2) + MAKE_INT_16(get_imm(src, SIZE_W));
				LOG(("(branch to sub routine %06x)\n\n", pc));
				continue;
			}
			else if (OPCODE("bra") || OPCODE("bf"))
			{
				pc = (pc + 2) + MAKE_INT_16(get_imm(src, SIZE_W));
				LOG(("(branch to %06x)\n", pc));
				continue;
			}
			else if (OPCODE("bcc") || OPCODE("bcs") || OPCODE("beq") || OPCODE("bge") ||
					 OPCODE("bgt") || OPCODE("bhi") || OPCODE("ble") || OPCODE("bls") ||
					 OPCODE("blt") || OPCODE("bmi") || OPCODE("bne") || OPCODE("bpl") ||
					 OPCODE("bvc") || OPCODE("bvs") || OPCODE("bt"))
			{
				switch (opcode[1])
				{
				case 'e': result = EQ; break;
				case 'h': result = HI; break;
				case 'm': result = MI; break;
				case 'n': result = NE; break;
				case 'p': result = PL; break;
				case 't': result = 1; break;

				case 'c': result = (opcode[2] == 'c') ? CC : CS; break;
				case 'g': result = (opcode[2] == 'e') ? GE : GT; break;
				case 'v': result = (opcode[2] == 'c') ? VC : VS; break;

				case 'l':
					switch (opcode[2])
					{
					case 'e': result = LE; break;
					case 's': result = LS; break;
					case 't': result = LT; break;
					}
					break;
				}

				if (result)
				{
					pc = (pc + 2) + MAKE_INT_16(get_imm(src, SIZE_W));
					LOG(("(branch to %06x)\n", pc));
					continue;
				}
				LOG(("(pass)\n"));

				op = OTHER;
			}
			break;

		case 'c':
			if (OPCODE("clr"))
			{
				get_value1(src, type1, size);

				result = 0;

				C = 0;
				Z = 1;
				N = 0;
				V = 0;
#if VERBOSE
				switch (size)
				{
				case 1: LOG(("%02x -> %02x ", value1, 0)); break;
				case 2: LOG(("%04x -> %04x ", value1, 0)); break;
				case 4: LOG(("%08x -> %08x ", value1, 0)); break;
				}
#endif
				op = SET1;
			}
			else if (OPCODE("cmp") || OPCODE("cmpi") || OPCODE("cmpm"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				op_sub(value1, value2, size);

				op = TEST;
			}
			else if (OPCODE("cmpa"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				if (size == SIZE_W && type1 != An)
					value1 = MAKE_INT_16(value1);

				op_sub(value1, value2, SIZE_L);

				op = TEST;
			}
			break;

		case 'd':
			if (OPCODE("dbra") || OPCODE("dbf"))
			{
				get_value1(src, type1, SIZE_W);
				value1 -= 1;
				value1 &= 0xffff;
				set_d(n1, value1, SIZE_W);

				if (value1 != 0xffff)
				{
					pc = (pc + 2) + MAKE_INT_8(get_imm(dst, SIZE_B));
					LOG(("--D%d = %04x (branch to %06x)\n", src[0], value1, pc));
					continue;
				}
				LOG(("--D%d = %04x (pass)\n", src[0], value1));

				op = OTHER;
			}
			else if (OPCODE("dbcc") || OPCODE("dbcs") || OPCODE("dbeq") || OPCODE("dbge") ||
					 OPCODE("dbgt") || OPCODE("dbhi") || OPCODE("dble") || OPCODE("dbls") ||
					 OPCODE("dblt") || OPCODE("dbmi") || OPCODE("dbne") || OPCODE("dbpl") ||
					 OPCODE("dbvc") || OPCODE("dbvs") || OPCODE("dbt"))
			{
				switch (opcode[2])
				{
				case 'e': result = EQ; break;
				case 'h': result = HI; break;
				case 'm': result = MI; break;
				case 'n': result = NE; break;
				case 'p': result = PL; break;
				case 't': result = 1; break;

				case 'c': result = (opcode[3] == 'c') ? CC : CS; break;
				case 'g': result = (opcode[3] == 'e') ? GE : GT; break;
				case 'v': result = (opcode[3] == 'c') ? VC : VS; break;

				case 'l':
					switch (opcode[3])
					{
					case 'e': result = LE; break;
					case 's': result = LS; break;
					case 't': result = LT; break;
					}
					break;
				}

				if (!result)
				{
					get_value1(src, type1, SIZE_W);
					value1 -= 1;
					value1 &= 0xffff;
					set_d(n1, value1, SIZE_W);

					if (value1 != 0xffff)
					{
						pc = (pc + 2) + MAKE_INT_8(get_imm(dst, SIZE_B));
						LOG(("--D%d = %04x (branch to %06x)\n", n1, value1, pc));
						continue;
					}

					LOG(("--D%d = %04x (pass)\n", n1, value1));
				}
#if VERBOSE
				else
				{
					LOG(("D%d = %04x (pass)\n", n1, get_d(n1, SIZE_W)));
				}
#endif

				op = OTHER;
			}
			else if (OPCODE("divu"))
			{
				get_value1(src, type1, SIZE_W);
				get_value2(dst, type2, size);

				if (value1 != 0)
				{
					int modulo;

#if VERBOSE
					switch (size)
					{
					case SIZE_W: LOG(("%04x / %04x ", value2, value1)); break;
					case SIZE_L: LOG(("%08x / %04x ", value2, value1)); break;
					}
#endif
					switch (type2)
					{
					case Dn:
						result = (value2 / value1) & 0xffff;
						modulo = (value2 % value1) & 0xffff;
						set_d(n2, (modulo << 16) | result, 4);
						LOG(("(=%04x, modulo=%04x) -> D%d\n", result, modulo, n2));
						break;

					default:
						result = (value2 / value1) & 0xffff;
						modulo = (value2 % value1) & 0xffff;
						write_ea(addr2, (modulo << 16) | result, 4);
						LOG(("(=%04x, modulo=%04x) -> %06x\n", result, modulo, addr2));
						break;
					}

					C = 0;
					Z = ZFLAG(result, 2);
					N = NFLAG(result, 2);
					V = 0;

					op = OTHER;	// SET2
				}
				else
				{
					LOG(("(divide by zero)\n"));
					op = INVALID;
				}
			}
			else if (OPCODE("divs"))
			{
				get_value1(src, type1, SIZE_W);
				get_value2(dst, type2, size);

				value1 = MAKE_INT_16(value1);

				if (value1 != 0)
				{
					int modulo;
#if VERBOSE
					switch (size)
					{
					case SIZE_W: LOG(("%04x / %04x ", value2, value1 & 0xffff)); break;
					case SIZE_L: LOG(("%08x / %04x ", value2, value1 & 0xffff)); break;
					}
#endif
					switch (type2)
					{
					case Dn:
						result = (value2 / value1) & 0xffff;
						modulo = (value2 % value1) & 0xffff;
						set_d(n2, (modulo << 16) | result, 4);
						LOG(("(=%04x, modulo=%04x) -> D%d\n", result, modulo, n2));
						break;

					default:
						result = (value2 / value1) & 0xffff;
						modulo = (value2 % value1) & 0xffff;
						write_ea(addr2, (modulo << 16) | result, 4);
						LOG(("(=%04x, modulo=%04x) -> %06x\n", result, modulo, addr2));
						break;
					}

					C = 0;
					Z = ZFLAG(result, 2);
					N = NFLAG(result, 2);
					V = 0;

					op = OTHER;	// SET2
				}
				else
				{
					LOG(("(divide by zero)\n"));
					op = INVALID;
				}
			}
			break;

		case 'e':
			if (OPCODE("eor") || OPCODE("eori"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				result = op_eor(value1, value2, size);

				op = SET2;
			}
			else if (OPCODE("exg"))
			{
				switch (type2)
				{
				case An:
					switch (type1)
					{
					case An:
						value1 = a[n1];
						a[n1] = a[n2];
						a[n2] = value1;
						LOG(("A%d <--> A%d\n", opcode, n1, n2));
						break;

					case Dn:
						value1 = d[n1];
						d[n1] = a[n2];
						a[n2] = value1;
						LOG(("D%d <--> A%d\n", n1, n2));
						break;
					}
					break;

				case Dn:
					switch (type1)
					{
					case An:
						value1 = a[n1];
						a[n1] = d[n2];
						d[n2] = value1;
						LOG(("A%d <--> D%d\n", opcode, n1, n2));
						break;

					case Dn:
						value1 = d[n1];
						d[n1] = d[n2];
						d[n2] = value1;
						LOG(("D%d <--> D%d\n", n1, n2));
						break;
					}
					break;
				}

				op = OTHER;
			}
			else if (OPCODE("ext"))
			{
				get_value1(src, type1, size);

				switch (size)
				{
				case SIZE_B:
					result = MAKE_INT_8(value1);
					LOG(("%s\t%02x -> %04x ", opcode, value1, result & 0xffff));
					break;

				case SIZE_W:
					result = MAKE_INT_16(value1);
					LOG(("%s\t%04x -> %08x ", opcode, value1, result));
					break;
				}

				size *= 2;

				C = 0;
				Z = ZFLAG(value1, size);
				N = NFLAG(value1, size);
				V = 0;

				op = SET1;
			}
			break;

		case 'j':
			if (OPCODE("jmp"))
			{
				pc = get_ea(src, type1, size);
				LOG(("06x\n", pc));
				continue;
			}
			else if (OPCODE("jsr"))
			{
				nest_pc[nest_count++] = pc + opcode_len;
				pc = get_ea(src, type1, size);
				LOG(("%06x\n\n", pc));
				continue;
			}
			break;

		case 'l':
			if (OPCODE("lea"))
			{
				get_value1(src, type1, SIZE_L);

				result = addr1;

				LOG(("%06x -> ", result));

				op = SET2;
			}
			else if (OPCODE("lsl"))
			{
				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					C = 0;
				}
				else
				{
					X = C = (result & (msb >> (value1 - 1))) != 0;

					for (i = 0; i < value1; i++)
						result = (result << 1) & mask;
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x << %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x << %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x << %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			else if (OPCODE("lsr"))
			{
				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					C = 0;
				}
				else
				{
					X = C = (result & (1 << (value1 - 1))) != 0;

					for (i = 0; i < value1; i++)
						result = (result >> 1) & mask;
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x >> %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x >> %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x >> %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			break;

		case 'm':
			if (OPCODE("move"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				switch (type2)
				{
				case An:
					LOG(("(Illigal)\n"));
					break;

				case Dn:
					set_d(n2, value1, size);
#if VERBOSE
					result = get_d(n2, SIZE_L);
					switch (size)
					{
					case SIZE_B: LOG(("%02x -> D%d (=%08x)\n", value1, n2, result)); break;
					case SIZE_W: LOG(("%04x -> D%d (=%08x)\n", value1, n2, result)); break;
					case SIZE_L: LOG(("%08x -> D%d (=%08x)\n", value1, n2, result)); break;
					}
#endif
					break;

				case CCR:
					C = value1 & (1 << 0);
					V = value1 & (1 << 1);
					Z = value1 & (1 << 2);
					N = value1 & (1 << 3);
					X = value1 & (1 << 4);
					LOG(("%04x -> CCR (XZNVC = %d%d%d%d%d)\n", value1, X, Z, N, V, C));
					break;

				case SR:
					sr = value1;
					C = sr & (1 << 0);
					V = sr & (1 << 1);
					Z = sr & (1 << 2);
					N = sr & (1 << 3);
					X = sr & (1 << 4);
					LOG(("%04x -> SR (=%04x)\n", value1));
					break;

				default:
					write_ea(addr2, value1, size);
#if VERBOSE
					switch (size)
					{
					case SIZE_B: LOG(("%02x -> %06x\n", value1, addr2)); break;
					case SIZE_W: LOG(("%04x -> %06x\n", value1, addr2)); break;
					case SIZE_L: LOG(("%08x -> %06x\n", value1, addr2)); break;
					}
					break;
#endif
				}

				C = 0;
				Z = ZFLAG(value1, size);
				N = NFLAG(value1, size);
				V = 0;

				op = OTHER; // SET2
			}
			else if (OPCODE("movea"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, SIZE_L);

				if (size == SIZE_W)
					value1 = MAKE_INT_16(value1);

				result = value1;

				LOG(("%08x -> ", result));

				op = SET2;
			}
			else if (OPCODE("movem"))
			{
				int i, n;
				char *p;

				p = (type1 == MM) ? src : dst;
				i = 0;

				if (type1 == MM)
				{
					if (type2 == PD7)
					{
						LOG(("%s -> -(A7)\n", src));

						if (p[0] == 'A')
						{
							i = 1;
							while (p[i])
							{
								if (p[i] == '/')
								{
									i++;
									break;
								}

								n = p[i] - '0';
								a[7] -= SIZE_L;
								write_ea(a[7], a[n], SIZE_L);
								i++;
							}
						}
						if (p[i] == 'D')
						{
							i++;
							while (p[i])
							{
								n = p[i] - '0';
								a[7] -= SIZE_L;
								write_ea(a[7], d[n], SIZE_L);
								i++;
							}
						}
					}
					else
					{
						addr2 = get_ea(dst, type2, size);

						LOG(("%s -> %06x\n", src, addr2));

						if (p[0] == 'D')
						{
							i = 1;

							while (p[i])
							{
								if (p[i] == '/')
								{
									i++;
									break;
								}

								n = p[i] - '0';
								write_ea(addr2, d[n], size);
								addr2 += size;
								i++;
							}
						}
						if (p[i] == 'A')
						{
							i++;
							while (p[i])
							{
								n = p[i] - '0';
								write_ea(addr2, a[n], size);
								addr2 += size;
								i++;
							}
						}
					}
				}
				else
				{
					if (type2 == PI7)
					{
						LOG(("(A7)+ -> %s\n", dst));

						if (p[0] == 'D')
						{
							i = 1;
							while (p[i])
							{
								if (p[i] == '/')
								{
									i++;
									break;
								}

								n = p[i] - '0';
								d[n] = read_ea(a[7], SIZE_L);
								a[7] += SIZE_L;
								i++;
							}
						}
						if (p[i] == 'A')
						{
							i++;
							while (p[i])
							{
								n = p[i] - '0';
								a[n] = read_ea(a[7], SIZE_L);
								a[7] += SIZE_L;
								i++;
							}
						}
					}
					else
					{
						addr1 = get_ea(src, type1, size);

						LOG(("%06x -> %s\n",  addr1, dst));

						if (p[0] == 'D')
						{
							i++;
							while (p[i])
							{
								if (p[i] == '/')
								{
									i++;
									break;
								}

								n = p[i] - '0';
								value1 = read_ea(addr1, size);
								d[n] = (d[n] & ~mask) | value1;
								addr1 += size;
								i++;
							}
						}
						if (p[i] == 'A')
						{
							i++;
							while (p[i])
							{
								n = p[i] - '0';
								value1 = read_ea(addr1, size);
								a[n] = (a[n] & ~mask) | value1;
								addr1 += size;
								i++;
							}
						}
					}
				}
				op = OTHER;
			}
			else if (OPCODE("moveq"))
			{
				get_value1(src, type1, size);
				result = MAKE_INT_8(value1);
				set_d(n2, result, SIZE_L);

				C = 0;
				Z = ZFLAG(result, SIZE_L);
				N = NFLAG(result, SIZE_L);
				V = 0;

				LOG(("%02x -> D%d (=%08x)\n", value1, n2, result));

				op = OTHER;	// SET2
			}
			else if (OPCODE("mulu"))
			{
				get_value1(src, type1, SIZE_W);
				get_value2(dst, type2, SIZE_W);

				result = value1 * value2;

				LOG(("%04x * %04x (=%08x) ->", value1, value2, result));

				size *= 2;

				C = 0;
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			else if (OPCODE("muls"))
			{
				get_value1(src, type1, SIZE_W);
				get_value2(dst, type2, SIZE_W);

				value1 = MAKE_INT_16(value1);
				value2 = MAKE_INT_16(value2);

				result = value1 * value2;

				LOG(("%04x * %04x (=%08x) ->", value1 & 0xffff, value2 & 0xffff, result));

				size *= 2;

				C = 0;
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			break;

		case 'n':
			if (OPCODE("nop"))
			{
				LOG(("\n"));
				op = OTHER;
			}
			else if (OPCODE("neg"))
			{
				get_value1(src, type1, size);

				result = op_sub(value1, 0, size);

				op = SET1;
			}
			else if (OPCODE("negx"))
			{
				get_value1(src, type1, size);

				result = op_sub(value1 - X, 0, size);

				op = SET1;
			}
			else if (OPCODE("not"))
			{
				get_value1(src, type1, size);

				result = (~value1) & mask;
#if VERBOSE
				switch (size)
				{
				case 1: LOG(("~%02x (=%02x) -> ", value1, result)); break;
				case 2: LOG(("~%04x (=%02x) -> ", value1, result)); break;
				case 4: LOG(("~%08x (=%02x) -> ", value1, result)); break;
				}
#endif
				C = 0;
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET1;
			}
			break;

		case 'o':
			if (OPCODE("or") || OPCODE("ori"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				result = op_or(value1, value2, size);

				op = SET2;
			}
			break;

		case 'r':
			if (OPCODE("rts"))
			{
				if (nest_count == 0)
				{
					pc = break_point;
					LOG(("(break)\n"));
					continue;
				}
				else
				{
					pc = nest_pc[--nest_count];
					LOG(("(return to %06x)\n\n", pc));
					continue;
				}
			}
			else if (OPCODE("rol"))
			{
				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					C = 0;
				}
				else
				{
					for (i = 0; i < value1; i++)
					{
						C = (result & 1) != 0;
						result = ((result << 1) & mask) | C;
					}
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x << %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x << %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x << %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			else if (OPCODE("ror"))
			{
				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					C = 0;
				}
				else
				{
					for (i = 0; i < value1; i++)
					{
						C = (result & 1) != 0;
						result = ((result >> 1) & mask) | (C * msb);
					}
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x >> %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x >> %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x >> %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			else if (OPCODE("roxl"))
			{
				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					C = 0;
				}
				else
				{
					for (i = 0; i < value1; i++)
					{
						C = (result & 1) != 0;
						result = ((result << 1) & mask) | X;
						X = C;
					}
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x << %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x << %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x << %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			else if (OPCODE("roxr"))
			{
				if (type2 == Dn)
				{
					get_value1(src, type1, size);
					get_value2(dst, type2, size);
				}
				else
				{
					size = SIZE_W;
					mask = 0x0000ffff;
					msb = 0x00008000;
					value1 = 1;
					get_value2(src, type1, size);
				}

				result = value2;

				if (value1 == 0)
				{
					X = C = 0;
				}
				else
				{
					for (i = 0; i < value1; i++)
					{
						C = (result & 1) != 0;
						result = ((result >> 1) & mask) | (X * msb);
						X = C;
					}
				}
#if VERBOSE
				switch (size)
				{
				case SIZE_B: LOG(("%02x >> %d (=%02x) -> ", value2, value1, result)); break;
				case SIZE_W: LOG(("%04x >> %d (=%04x) -> ", value2, value1, result)); break;
				case SIZE_L: LOG(("%08x >> %d (=%08x) -> ", value2, value1, result)); break;
				}
#endif
				Z = ZFLAG(result, size);
				N = NFLAG(result, size);
				V = 0;

				op = SET2;
			}
			break;

		case 's':
			if (OPCODE("sub") || OPCODE("subi"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				result = op_sub(value1, value2, size);
				X = C;

				op = SET2;
			}
			else if (OPCODE("suba"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, SIZE_L);

				if (size == SIZE_W)
					value1 = MAKE_INT_16(value1);

				result = value2 - value1;

				LOG(("%06x - %06x (=%06x) -> ", value2, value1, result));

				op = SET2;
			}
			else if (OPCODE("subq"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				if (type2 == An)
				{
					if (size == SIZE_W)
						value1 = MAKE_INT_16(value1);

					result = value2 - value1;

					LOG(("%06x - %08x (=%06x) -> ", value2, value1, result));
				}
				else
				{
					result = op_sub(value1, value2, size);
					X = C;
				}

				op = SET2;
			}
			else if (OPCODE("subx"))
			{
				get_value1(src, type1, size);
				get_value2(dst, type2, size);

				result = op_sub(value1 - X, value2, size);
				X = C;

				op = SET2;
			}
			else if (OPCODE("swap"))
			{
				get_value1(src, type1, SIZE_L);
				value2 = (value1 & 0xffff0000) >> 16;
				result = (value1 & 0x0000ffff) << 16;
				result |= value2;
				LOG(("D%d %08x -> D%d %08x\n", n1, value1, n1, result));
				set_d(n1, result, SIZE_L);

				op = OTHER;
			}
			else if (OPCODE("scc") || OPCODE("scs") || OPCODE("seq") || OPCODE("sge") ||
					 OPCODE("sgt") || OPCODE("shi") || OPCODE("sle") || OPCODE("sls") ||
					 OPCODE("slt") || OPCODE("smi") || OPCODE("sne") || OPCODE("spl") ||
					 OPCODE("svc") || OPCODE("svs") || OPCODE("st")  || OPCODE("sf"))
			{
				size = SIZE_B;
				get_value1(src, type1, size);

				switch (opcode[1])
				{
				case 'e': result = EQ; break;
				case 'h': result = HI; break;
				case 'm': result = MI; break;
				case 'n': result = NE; break;
				case 'p': result = PL; break;
				case 't': result = 1; break;
				case 'f': result = 0; break;

				case 'c': result = (opcode[2] == 'c') ? CC : CS; break;
				case 'g': result = (opcode[2] == 'e') ? GE : GT; break;
				case 'v': result = (opcode[2] == 'c') ? VC : VS; break;

				case 'l':
					switch (opcode[2])
					{
					case 'e': result = LE; break;
					case 's': result = LS; break;
					case 't': result = LT; break;
					}
					break;
				}

				result = (result != 0) ? 0xff : 0;
				LOG(("%02x -> ", result));

				op = SET1;
			}
			break;

		case 't':
			if (OPCODE("tst"))
			{
				get_value1(src, type1, size);
				op_and(mask, value1, size);
				op = TEST;
			}
			break;
		}

		// 結果
		switch (op)
		{
		case NOTFOUND:
			LOG(("not inplemented.\n"));
			break;

		case SET1:
			switch (type1)
			{
			case An:
				set_a(n1, result);
				LOG(("A%d\n", n1));
				break;

			case Dn:
				set_d(n1, result, size);
				LOG(("D%d\n", n1));
				break;

			default:
				write_ea(addr1, result, size);
				LOG(("$%06x\n", addr1));
				break;
			}
			break;

		case SET2:
			switch (type2)
			{
			case An:
				set_a(n2, result);
				LOG(("A%d\n", n2));
				break;

			case CCR:
				C = value1 & (1 << 0);
				V = value1 & (1 << 1);
				Z = value1 & (1 << 2);
				N = value1 & (1 << 3);
				X = value1 & (1 << 4);
				LOG(("CCR\n"));
				break;

			case Dn:
				set_d(n2, result, size);
				LOG(("D%d\n", n2));
				break;

			default:
				write_ea(addr2, result, size);
				LOG(("$%06x\n", addr2));
				break;
			}
			break;

		case TEST:
			LOG(("CCR (XVNZC = %d%d%d%d%d)\n", X, V, N, Z, C));
			break;

		default:
			break;
		}

		pc += opcode_len;
	}

	LOG(("-- end --\n\n"));
}
