
#include "sysconfig.h"
#include "sysdeps.h"

#include "main.h"
#include "options_cpu.h"
#include "debug.h"
#include "memory.h"
#include "newcpu.h"
#include "fpp.h"
#include "debugmem.h"
#include "disasm.h"
#include "profile.h"

int disasm_flags = DISASM_FLAG_LC_MNEMO | DISASM_FLAG_LC_REG | DISASM_FLAG_LC_SIZE | DISASM_FLAG_LC_HEX | DISASM_FLAG_CC | DISASM_FLAG_EA | DISASM_FLAG_VAL | DISASM_FLAG_WORDS;
int disasm_min_words = 5;
int disasm_max_words = 16;
TCHAR disasm_hexprefix[3] = { '$', 0 };

static TCHAR disasm_areg, disasm_dreg;
static TCHAR disasm_pcreg[3], disasm_fpreg[3];

void disasm_init(void)
{
	_tcscpy(disasm_pcreg, _T("PC"));
	_tcscpy(disasm_fpreg, _T("FP"));
	if (disasm_flags & DISASM_FLAG_LC_REG) {
		_tcscpy(disasm_pcreg, _T("pc"));
		_tcscpy(disasm_fpreg, _T("fp"));
	}
	disasm_areg = (disasm_flags & DISASM_FLAG_LC_REG) ? 'a' : 'A';
	disasm_dreg = (disasm_flags & DISASM_FLAG_LC_REG) ? 'd' : 'D';

}

static void disasm_lc_mnemo(TCHAR *s)
{
	if (!(disasm_flags & DISASM_FLAG_LC_MNEMO) && !(disasm_flags & DISASM_FLAG_LC_SIZE)) {
		return;
	}
	if ((disasm_flags & DISASM_FLAG_LC_MNEMO) && (disasm_flags & DISASM_FLAG_LC_SIZE)) {
		to_lower(s, -1);
		return;
	}
	TCHAR *s2 = _tcschr(s, '.');
	if (s2) {
		if (disasm_flags & DISASM_FLAG_LC_SIZE) {
			to_lower(s2, -1);
		}
		if (disasm_flags & DISASM_FLAG_LC_MNEMO) {
			s2[0] = 0;
			to_lower(s, -1);
			s2[0] = '.';
		}
	} else {
		if (disasm_flags & DISASM_FLAG_LC_MNEMO) {
			to_lower(s, -1);
			return;
		}
	}
}

static const TCHAR *disasm_lc_size(const TCHAR *s)
{
	static TCHAR tmp[32];
	if (disasm_flags & DISASM_FLAG_LC_SIZE) {
		_tcscpy(tmp, s);
		to_lower(tmp, -1);
		return tmp;
	}
	return s;
}

static const TCHAR *disasm_lc_reg(const TCHAR *s)
{
	static TCHAR tmp[32];
	if (disasm_flags & DISASM_FLAG_LC_REG) {
		_tcscpy(tmp, s);
		to_lower(tmp, -1);
		return tmp;
	}
	return s;
}

static const TCHAR *disasm_lc_hex2(const TCHAR *s, bool noprefix)
{
	static TCHAR tmp[32];
	bool copied = false;
	if (disasm_flags & DISASM_FLAG_LC_HEX) {
		const TCHAR *s2 = _tcschr(s, 'X');
		if (s2) {
			_tcscpy(tmp, s);
			copied = true;
			tmp[s2 - s] = 'x';
			for (;;) {
				s2 = _tcschr(tmp, 'X');
				if (!s2) {
					break;
				}
				tmp[s2 - tmp] = 'x';
			}
		}
	}
	if (!noprefix) {
		if (disasm_hexprefix[0] != '$' || disasm_hexprefix[1] != 0 || s[0] != '$') {
			if (!copied) {
				_tcscpy(tmp, s);
				copied = true;
			}
			const TCHAR *s2 = _tcschr(tmp, '%');
			if (s2) {
				int len = _tcslen(disasm_hexprefix);
				if (s2 > tmp && s2[-1] == '$') {
					len--;
					s2--;
				}
				if (len < 0) {
					memmove(tmp + (s2 - tmp), tmp + (s2 - tmp) - len, (_tcslen(tmp + (s2 - tmp) - len) + 1) * sizeof(TCHAR));
				} else {
					if (len > 0) {
						memmove(tmp + (s2 - tmp) + len, s2, (_tcslen(s2) + 1) * sizeof(TCHAR));
					}
					memcpy(tmp + (s2 - tmp), disasm_hexprefix, _tcslen(disasm_hexprefix) * sizeof(TCHAR));
				}
			}
			return tmp;
		}
	}
	if (copied) {
		return tmp;
	}
	return s;
}

static const TCHAR *disasm_lc_hex(const TCHAR *s)
{
	return disasm_lc_hex2(s, false);
}
static const TCHAR *disasm_lc_nhex(const TCHAR *s)
{
	return disasm_lc_hex2(s, true);
}


struct cpum2c m2cregs[] = {
	{ 0, 31, _T("SFC") },
	{ 1, 31, _T("DFC") },
	{ 2, 30, _T("CACR") },
	{ 3, 24, _T("TC") },
	{ 4, 24, _T("ITT0") },
	{ 5, 24, _T("ITT1") },
	{ 6, 24, _T("DTT0") },
	{ 7, 24, _T("DTT1") },
	{ 8, 16, _T("BUSC") },
	{ 0x800, 31, _T("USP") },
	{ 0x801, 31, _T("VBR") },
	{ 0x802,  6, _T("CAAR") },
	{ 0x803, 15, _T("MSP") },
	{ 0x804, 31, _T("ISP") },
	{ 0x805,  8, _T("MMUS") },
	{ 0x806, 12, _T("URP") },
	{ 0x807, 12, _T("SRP") },
	{ 0x808, 16, _T("PCR") },
    { -1, 0, NULL }
};

const TCHAR *fpsizes[] = {
	_T("L"),
	_T("S"),
	_T("X"),
	_T("P"),
	_T("W"),
	_T("D"),
	_T("B"),
	_T("P")
};
static const wordsizes fpsizeconv[] = {
	sz_long,
	sz_single,
	sz_extended,
	sz_packed,
	sz_word,
	sz_double,
	sz_byte,
	sz_packed
};
static const int datasizes[] = {
	1,
	2,
	4,
	4,
	8,
	12,
	12
};

static void showea_val(TCHAR *buffer, uae_u16 opcode, uaecptr addr, int size)
{
	struct mnemolookup *lookup;
	struct instr *table = &table68k[opcode];

#ifndef WINUAE_FOR_HATARI
#ifndef CPU_TESTER
#if UAE
	if (addr >= 0xe90000 && addr < 0xf00000)
		goto skip;
	if (addr >= 0xdff000 && addr < 0xe00000)
		goto skip;
#endif
#endif
#endif

	if (!(disasm_flags & DISASM_FLAG_VAL)) {
		goto skip;
	}
 
	for (lookup = lookuptab; lookup->mnemo != table->mnemo; lookup++)
		;
	if (!(lookup->flags & 1))
		goto skip;
	buffer += _tcslen(buffer);
	if (debug_safe_addr(addr, datasizes[size])) {
		bool cached = false;
		switch (size)
		{
			case sz_byte:
			{
				uae_u8 v = get_byte_cache_debug(addr, &cached);
				uae_u8 v2 = v;
				if (cached)
					v2 = get_byte_debug(addr);
				if (v != v2) {
					_stprintf(buffer, _T(" [%02x:%02x]"), v, v2);
				} else {
					_stprintf(buffer, _T(" [%s%02x]"), cached ? _T("*") : _T(""), v);
				}
			}
			break;
			case sz_word:
			{
				uae_u16 v = get_word_cache_debug(addr, &cached);
				uae_u16 v2 = v;
				if (cached)
					v2 = get_word_debug(addr);
				if (v != v2) {
					_stprintf(buffer, _T(" [%04x:%04x]"), v, v2);
				} else {
					_stprintf(buffer, _T(" [%s%04x]"), cached ? _T("*") : _T(""), v);
				}
			}
			break;
			case sz_long:
			{
				uae_u32 v = get_long_cache_debug(addr, &cached);
				uae_u32 v2 = v;
				if (cached)
					v2 = get_long_debug(addr);
				if (v != v2) {
					_stprintf(buffer, _T(" [%08x:%08x]"), v, v2);
				} else {
					_stprintf(buffer, _T(" [%s%08x]"), cached ? _T("*") : _T(""), v);
				}
			}
			break;
			case sz_single:
			{
				fpdata fp;
				fpp_to_single(&fp, get_long_debug(addr));
				_stprintf(buffer, _T("[%s]"), fpp_print(&fp, 0));
			}
			break;
			case sz_double:
			{
				fpdata fp;
				fpp_to_double(&fp, get_long_debug(addr), get_long_debug(addr + 4));
				_stprintf(buffer, _T("[%s]"), fpp_print(&fp, 0));
			}
			break;
			case sz_extended:
			{
				fpdata fp;
				fpp_to_exten(&fp, get_long_debug(addr), get_long_debug(addr + 4), get_long_debug(addr + 8));
				_stprintf(buffer, _T("[%s]"), fpp_print(&fp, 0));
				break;
			}
			case sz_packed:
				_stprintf(buffer, _T("[%08x%08x%08x]"), get_long_debug(addr), get_long_debug(addr + 4), get_long_debug(addr + 8));
				break;
		}
	}
skip:
	for (int i = 0; i < size; i++) {
#ifndef WINUAE_FOR_HATARI
		TCHAR name[256];
		if (debugmem_get_symbol(addr + i, name, sizeof(name) / sizeof(TCHAR))) {
#else
		const char *name;
		if ((name = Symbols_GetByCpuAddress(addr + i, SYMTYPE_TEXT))) {
#endif
			_stprintf(buffer + _tcslen(buffer), _T(" %s"), name);
		}
	}
}

uaecptr ShowEA_disp(uaecptr *pcp, uaecptr base, TCHAR *buffer, const TCHAR *name, bool pcrel)
{
	uaecptr addr;
	uae_u16 dp;
	int r;
	uae_u32 dispreg;
	uaecptr pc = *pcp;
	TCHAR mult[20];
	TCHAR *p = NULL;

	dp = get_iword_debug(pc);
	pc += 2;

	r = (dp & 0x7000) >> 12; // REGISTER

	dispreg = dp & 0x8000 ? m68k_areg(regs, r) : m68k_dreg(regs, r);
	if (!(dp & 0x800)) { // W/L
		dispreg = (uae_s32)(uae_s16)(dispreg);
	}

	int m = 1 << ((dp >> 9) & 3);
	mult[0] = 0;

	if (currprefs.cpu_model >= 68020) {
		dispreg <<= (dp >> 9) & 3; // SCALE
	}

	if (buffer)
		buffer[0] = 0;
	if ((dp & 0x100) && currprefs.cpu_model >= 68020) {
		TCHAR dr[20];
		uae_s32 outer = 0, disp = 0;

		// Full format extension (68020+)

		if (m > 1 && buffer) {
			_stprintf(mult, _T("*%d"), m);
		}

		if (dp & 0x80) { // BS (base register suppress)
			base = 0;
			if (buffer)
				name = NULL;
		}
		if (buffer)
			_stprintf(dr, _T("%c%d.%c"), dp & 0x8000 ? 'A' : 'D', (int)r, dp & 0x800 ? 'L' : 'W');
		if (dp & 0x40) { // IS (index suppress)
			dispreg = 0;
			dr[0] = 0;
		}

		if (buffer) {
			_tcscpy(buffer, _T("("));
			p = buffer + _tcslen(buffer);

			if (dp & 3) {
				// indirect
				_stprintf(p, _T("["));
				p += _tcslen(p);
			} else {
				// (an,dn,word/long)
				if (name) {
					_stprintf(p, _T("%s,"), name);
					p += _tcslen(p);
				}
				if (dr[0]) {
					_stprintf(p, _T("%s%s,"), dr, mult);
					p += _tcslen(p);
				}
			}
		}

		if ((dp & 0x30) == 0x20) { // BD SIZE = 2 (WORD)
			disp = (uae_s32)(uae_s16)get_iword_debug(pc);
			if (buffer) {
				_stprintf(p, disasm_lc_hex(_T("$%04X,")), (uae_s16)disp);
				p += _tcslen(p);
			}
			pc += 2;
			base += disp;
		} else if ((dp & 0x30) == 0x30) { // BD SIZE = 3 (LONG)
			disp = get_ilong_debug(pc);
			if (buffer) {
				_stprintf(p, disasm_lc_hex(_T("$%08X,")), disp);
				p += _tcslen(p);
			}
			pc += 4;
			base += disp;
		}

		if ((dp & 3) && buffer) {
			if (name) {
				_stprintf(p, _T("%s,"), name);
				p += _tcslen(p);
			}

			if (!(dp & 0x04)) {
				if (dr[0]) {
					_stprintf(p, _T("%s%s,"), dr, mult);
					p += _tcslen(p);
				}
			}

			if (p[-1] == ',')
				p--;
			_stprintf(p, _T("],"));
			p += _tcslen(p);

			if ((dp & 0x04)) {
				if (dr[0]) {
					_stprintf(p, _T("%s%s,"), dr, mult);
					p += _tcslen(p);
				}
			}

		}

		if ((dp & 0x03) == 0x02) {
			outer = (uae_s32)(uae_s16)get_iword_debug(pc);
			if (buffer) {
				_stprintf(p, disasm_lc_hex(_T("$%04X,")), (uae_s16)outer);
				p += _tcslen(p);
			}
			pc += 2;
		} else 	if ((dp & 0x03) == 0x03) {
			outer = get_ilong_debug(pc);
			if (buffer) {
				_stprintf(p, disasm_lc_hex(_T("$%08X,")), outer);
				p += _tcslen(p);
			}
			pc += 4;
		}

		if (buffer) {
			if (p[-1] == ',')
				p--;
			_stprintf(p, _T(")"));
			p += _tcslen(p);
		}

		if ((dp & 0x4) == 0)
			base += dispreg;
		if (dp & 0x3)
			base = get_long_debug(base);
		if (dp & 0x4)
			base += dispreg;

		addr = base + outer;

		if (buffer) {
			_stprintf(p, disasm_lc_hex(_T(" == $%08X")), addr);
			p += _tcslen(p);
		}

	} else {
		// Brief format extension
		TCHAR regstr[20];
		uae_s8 disp8 = dp & 0xFF;

		if (m > 1 && buffer) {
			if (currprefs.cpu_model < 68020) {
				_stprintf(mult, _T("[*%d]"), m);
			} else {
				_stprintf(mult, _T("*%d"), m);
			}
		}
		regstr[0] = 0;
		_stprintf(regstr, _T(",%c%d.%c"), dp & 0x8000 ? disasm_areg : disasm_dreg, (int)r, dp & 0x800 ? 'L' : 'W');
		addr = base + (uae_s32)((uae_s8)disp8) + dispreg;
		if (buffer) {
			if (pcrel) {
				_stprintf(buffer, _T("(%s%s%s,$%02x=$%08x) == $%08x"), name, regstr, mult, (uae_u8)disp8, (*pcp) += disp8, addr);
			} else {
				_stprintf(buffer, _T("(%s%s%s,$%02x) == $%08x"), name, regstr, mult, (uae_u8)disp8, addr);
			}
			if (((dp & 0x0100) || m != 1) && currprefs.cpu_model < 68020) {
				_tcscat(buffer, _T(" (68020+)"));
			}
		}
	}

	*pcp = pc;
	return addr;
}

uaecptr ShowEA(void *f, uaecptr pc, uae_u16 opcode, int reg, amodes mode, wordsizes size, TCHAR *buf, uae_u32 *eaddr, int *actualea, int safemode)
{
	uaecptr addr = pc;
	uae_s16 disp16;
	uae_s32 offset = 0;
	TCHAR buffer[80];

	if (actualea)
		*actualea = 1;
	if ((opcode & 0xf0ff) == 0x60ff && currprefs.cpu_model < 68020) {
		// bcc.l is bcc.s if 68000/68010
		mode = immi;
	}

	buffer[0] = 0;
	switch (mode){
	case Dreg:
		_stprintf(buffer, _T("%c%d"), disasm_dreg, reg);
		if (actualea)
			*actualea = 0;
		break;
	case Areg:
		_stprintf(buffer, _T("%c%d"), disasm_areg, reg);
		if (actualea)
			*actualea = 0;
		break;
	case Aind:
		_stprintf(buffer, _T("(%c%d)"), disasm_areg, reg);
		addr = regs.regs[reg + 8];
		showea_val(buffer, opcode, addr, size);
		break;
	case Aipi:
		_stprintf(buffer, _T("(%c%d)+"), disasm_areg, reg);
		addr = regs.regs[reg + 8];
		showea_val(buffer, opcode, addr, size);
		break;
	case Apdi:
		_stprintf(buffer, _T("-(%c%d)"), disasm_areg, reg);
		addr = regs.regs[reg + 8] - datasizes[size];
		showea_val(buffer, opcode, addr, size);
		break;
	case Ad16:
		{
			TCHAR offtxt[32];
			disp16 = get_iword_debug (pc); pc += 2;
			if (disp16 < 0)
				_stprintf (offtxt, disasm_lc_hex(_T("-$%04X")), -disp16);
			else
				_stprintf (offtxt, disasm_lc_hex(_T("$%04X")), disp16);
			addr = m68k_areg (regs, reg) + disp16;
			_stprintf(buffer, _T("(%c%d,%s)"), disasm_areg, reg, offtxt);
			if (disasm_flags & DISASM_FLAG_VAL) {
				_stprintf(buffer + _tcslen(buffer), disasm_lc_hex(_T(" == $%08X")), addr);
			}
			showea_val(buffer, opcode, addr, size);
		}
		break;
	case Ad8r:
		{
			TCHAR name[10];
			_stprintf(name, _T("%c%d"), disasm_areg, reg);
			addr = ShowEA_disp(&pc, m68k_areg(regs, reg), buffer, name, false);
			showea_val(buffer, opcode, addr, size);
		}
		break;
	case PC16:
		disp16 = get_iword_debug (pc); pc += 2;
		addr += (uae_s16)disp16;
		_stprintf(buffer, _T("(%s"), disasm_pcreg);
		_stprintf(buffer + _tcslen(buffer), disasm_lc_hex(_T(",$%04X)")), disp16 & 0xffff);
		if (disasm_flags & DISASM_FLAG_VAL) {
			_stprintf(buffer + _tcslen(buffer), disasm_lc_hex(_T(" == $%08X")), addr);
		}
		showea_val(buffer, opcode, addr, size);
		break;
	case PC8r:
		{
			addr = ShowEA_disp(&pc, addr, buffer, disasm_pcreg, true);
			showea_val(buffer, opcode, addr, size);
		}
		break;
	case absw:
		addr = (uae_s32)(uae_s16)get_iword_debug (pc);
		_stprintf (buffer, disasm_lc_hex(_T("$%04X")), (uae_u16)addr);
		pc += 2;
		showea_val(buffer, opcode, addr, size);
		break;
	case absl:
		addr = get_ilong_debug (pc);
		_stprintf (buffer, disasm_lc_hex(_T("$%08X")), addr);
		pc += 4;
		showea_val(buffer, opcode, addr, size);
		break;
	case imm:
		if (actualea)
			*actualea = 0;
		switch (size){
		case sz_byte:
			_stprintf (buffer, disasm_lc_hex(_T("#$%02X")), (get_iword_debug (pc) & 0xff));
			pc += 2;
			break;
		case sz_word:
			_stprintf (buffer, disasm_lc_hex(_T("#$%04X")), (get_iword_debug (pc) & 0xffff));
			pc += 2;
			break;
		case sz_long:
			_stprintf(buffer, disasm_lc_hex(_T("#$%08X")), (get_ilong_debug(pc)));
			pc += 4;
			break;
		case sz_single:
			{
				fpdata fp;
				fpp_to_single(&fp, get_ilong_debug(pc));
				_stprintf(buffer, _T("#%s"), fpp_print(&fp, 0));
				pc += 4;
			}
			break;
		case sz_double:
			{
				fpdata fp;
				fpp_to_double(&fp, get_ilong_debug(pc), get_ilong_debug(pc + 4));
				_stprintf(buffer, _T("#%s"), fpp_print(&fp, 0));
				pc += 8;
			}
			break;
		case sz_extended:
		{
			fpdata fp;
			fpp_to_exten(&fp, get_ilong_debug(pc), get_ilong_debug(pc + 4), get_ilong_debug(pc + 8));
			_stprintf(buffer, _T("#%s"), fpp_print(&fp, 0));
			pc += 12;
			break;
		}
		case sz_packed:
			_stprintf(buffer, disasm_lc_hex(_T("#$%08X%08X%08X")), get_ilong_debug(pc), get_ilong_debug(pc + 4), get_ilong_debug(pc + 8));
			pc += 12;
			break;
		default:
			break;
		}
		break;
	case imm0:
		offset = (uae_s32)(uae_s8)get_iword_debug (pc);
		_stprintf (buffer, disasm_lc_hex(_T("#$%02X")), (uae_u32)(offset & 0xff));
		addr = pc + 2 + offset;
		if ((opcode & 0xf000) == 0x6000) {
			showea_val(buffer, opcode, addr, 1);
		}
		pc += 2;
		if (actualea)
			*actualea = 0;
		break;
	case imm1:
		offset = (uae_s32)(uae_s16)get_iword_debug (pc);
		buffer[0] = 0;
		_stprintf (buffer, disasm_lc_hex(_T("#$%04X")), (uae_u32)(offset & 0xffff));
		addr = pc + offset;
		if ((opcode & 0xf000) == 0x6000) {
			showea_val(buffer, opcode, addr, 2);
		}
		pc += 2;
		if (actualea)
			*actualea = 0;
		break;
	case imm2:
		offset = (uae_s32)get_ilong_debug (pc);
		_stprintf (buffer, disasm_lc_hex(_T("#$%08X")), (uae_u32)offset);
		addr = pc + offset;
		if ((opcode & 0xf000) == 0x6000) {
			showea_val(buffer, opcode, addr, 4);
		}
		pc += 4;
		if (actualea)
			*actualea = 0;
		break;
	case immi:
		offset = (uae_s32)(uae_s8)(reg & 0xff);
		_stprintf (buffer, disasm_lc_hex(_T("#$%02X")), (uae_u8)offset);
		addr = pc + offset;
		if (actualea)
			*actualea = 0;
		break;
	default:
		break;
	}
	if (buf == NULL)
		f_out (f, _T("%s"), buffer);
	else
		_tcscat (buf, buffer);
	if (eaddr)
		*eaddr = addr;
	return pc;
}

static const TCHAR *ccnames[] =
{
	_T("T"), _T("F"), _T("HI"),_T("LS"),_T("CC"),_T("CS"),_T("NE"),_T("EQ"),
	_T("VC"),_T("VS"),_T("PL"),_T("MI"),_T("GE"),_T("LT"),_T("GT"),_T("LE")
};
static const TCHAR *fpccnames[] =
{
	_T("F"),
	_T("EQ"),
	_T("OGT"),
	_T("OGE"),
	_T("OLT"),
	_T("OLE"),
	_T("OGL"),
	_T("OR"),
	_T("UN"),
	_T("UEQ"),
	_T("UGT"),
	_T("UGE"),
	_T("ULT"),
	_T("ULE"),
	_T("NE"),
	_T("T"),
	_T("SF"),
	_T("SEQ"),
	_T("GT"),
	_T("GE"),
	_T("LT"),
	_T("LE"),
	_T("GL"),
	_T("GLE"),
	_T("NGLE"),
	_T("NGL"),
	_T("NLE"),
	_T("NLT"),
	_T("NGE"),
	_T("NGT"),
	_T("SNE"),
	_T("ST")
};
const TCHAR *fpuopcodes[] =
{
	_T("FMOVE"),
	_T("FINT"),
	_T("FSINH"),
	_T("FINTRZ"),
	_T("FSQRT"),
	NULL,
	_T("FLOGNP1"),
	NULL,
	_T("FETOXM1"),
	_T("FTANH"),
	_T("FATAN"),
	NULL,
	_T("FASIN"),
	_T("FATANH"),
	_T("FSIN"),
	_T("FTAN"),
	_T("FETOX"),	// 0x10
	_T("FTWOTOX"),
	_T("FTENTOX"),
	NULL,
	_T("FLOGN"),
	_T("FLOG10"),
	_T("FLOG2"),
	NULL,
	_T("FABS"),
	_T("FCOSH"),
	_T("FNEG"),
	NULL,
	_T("FACOS"),
	_T("FCOS"),
	_T("FGETEXP"),
	_T("FGETMAN"),
	_T("FDIV"),		// 0x20
	_T("FMOD"),
	_T("FADD"),
	_T("FMUL"),
	_T("FSGLDIV"),
	_T("FREM"),
	_T("FSCALE"),
	_T("FSGLMUL"),
	_T("FSUB"),
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	_T("FSINCOS"),	// 0x30
	_T("FSINCOS"),
	_T("FSINCOS"),
	_T("FSINCOS"),
	_T("FSINCOS"),
	_T("FSINCOS"),
	_T("FSINCOS"),
	_T("FSINCOS"),
	_T("FCMP"),
	NULL,
	_T("FTST"),
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	_T("FSMOVE"),	// 0x40
	_T("FSSQRT"),
	NULL,
	NULL,
	_T("FDMOVE"),
	_T("FDSQRT"),
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,	// 0x50
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	_T("FSABS"),
	NULL,
	_T("FSNEG"),
	NULL,
	_T("FDABS"),
	NULL,
	_T("FDNEG"),
	NULL,
	_T("FSDIV"), // 0x60
	NULL,
	_T("FSADD"),
	_T("FSMUL"),
	_T("FDDIV"),
	NULL,
	_T("FDADD"),
	_T("FDMUL"),
	_T("FSSUB"),
	NULL,
	NULL,
	NULL,
	_T("FDSUB"),
	NULL,
	NULL,
	NULL,
	NULL,  // 0x70
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	_T("ILLG") // 0x80
};

static const TCHAR *movemregs[] =
{
	_T("D0"),
	_T("D1"),
	_T("D2"),
	_T("D3"),
	_T("D4"),
	_T("D5"),
	_T("D6"),
	_T("D7"),
	_T("A0"),
	_T("A1"),
	_T("A2"),
	_T("A3"),
	_T("A4"),
	_T("A5"),
	_T("A6"),
	_T("A7"),
	_T("FP0"),
	_T("FP1"),
	_T("FP2"),
	_T("FP3"),
	_T("FP4"),
	_T("FP5"),
	_T("FP6"),
	_T("FP7"),
	_T("FPCR"),
	_T("FPSR"),
	_T("FPIAR")
};

static void addmovemreg(TCHAR *out, int *prevreg, int *lastreg, int *first, int reg, int fpmode)
{
	TCHAR s[10];
	TCHAR *p = out + _tcslen (out);
	if (*prevreg < 0) {
		*prevreg = reg;
		*lastreg = reg;
		return;
	}
	if (reg < 0 || fpmode == 2 || (*prevreg) + 1 != reg || (reg & 8) != ((*prevreg & 8))) {
		_tcscpy(s, movemregs[*lastreg]);
		if (disasm_flags & DISASM_FLAG_LC_REG) {
			to_lower(s, -1);
		}
		_stprintf (p, _T("%s%s"), (*first) ? _T("") : _T("/"), s);
		p = p + _tcslen (p);
		if (*lastreg != *prevreg) {
			_tcscpy(s, movemregs[*prevreg]);
			if (disasm_flags & DISASM_FLAG_LC_REG) {
				to_lower(s, -1);
			}
			if ((*lastreg) + 2 == reg) {
				_stprintf(p, _T("/%s"), s);
			} else if ((*lastreg) != (*prevreg)) {
				_stprintf(p, _T("-%s"), s);
			}
		}
		*lastreg = reg;
		*first = 0;
	}
	*prevreg = reg;
}

static bool movemout(TCHAR *out, uae_u16 mask, int mode, int fpmode, bool dst)
{
	unsigned int dmask, amask;
	int prevreg = -1, lastreg = -1, first = 1;

	if (mode == Apdi && !fpmode) {
		uae_u8 dmask2;
		uae_u8 amask2;
		
		amask2 = mask & 0xff;
		dmask2 = (mask >> 8) & 0xff;
		dmask = 0;
		amask = 0;
		for (int i = 0; i < 8; i++) {
			if (dmask2 & (1 << i))
				dmask |= 1 << (7 - i);
			if (amask2 & (1 << i))
				amask |= 1 << (7 - i);
		}
	} else {
		dmask = mask & 0xff;
		amask = (mask >> 8) & 0xff;
		if (fpmode == 1 && mode != Apdi) {
			uae_u8 dmask2 = dmask;
			dmask = 0;
			for (int i = 0; i < 8; i++) {
				if (dmask2 & (1 << i))
					dmask |= 1 << (7 - i);
			}
		}
	}
	bool dataout = dmask != 0 || amask != 0;
	if (dst && dataout)
		_tcscat(out, _T(","));
	if (fpmode) {
		while (dmask) { addmovemreg(out, &prevreg, &lastreg, &first, movem_index1[dmask] + (fpmode == 2 ? 24 : 16), fpmode); dmask = movem_next[dmask]; }
	} else {
		while (dmask) { addmovemreg (out, &prevreg, &lastreg, &first, movem_index1[dmask], fpmode); dmask = movem_next[dmask]; }
		while (amask) { addmovemreg (out, &prevreg, &lastreg, &first, movem_index1[amask] + 8, fpmode); amask = movem_next[amask]; }
	}
	addmovemreg(out, &prevreg, &lastreg, &first, -1, fpmode);
	return dataout;
}

static void disasm_size(TCHAR *instrname, struct instr *dp)
{
	int size = dp->size;
	if (dp->unsized) {
		_tcscat(instrname, _T(" "));
		return;
	}
	int m = dp->mnemo;
	if (dp->suse && dp->smode == immi &&
		(m == i_MOVE || m == i_ADD || m == i_ADDA || m == i_SUB || m == i_SUBA)) {
		_tcscat(instrname, disasm_lc_size(_T("Q")));
		if (m == i_MOVE)
			size = -1;
	}
	// EXT.B -> EXTB.L
	if (m == i_EXT && dp->size == sz_byte) {
		_tcscat(instrname, disasm_lc_size(_T("B")));
		size = sz_long;
	}

	switch (size)
	{
	case sz_byte:
		_tcscat(instrname, disasm_lc_size(_T(".B ")));
		break;
	case sz_word:
		_tcscat(instrname, disasm_lc_size(_T(".W ")));
		break;
	case sz_long:
		_tcscat(instrname, disasm_lc_size(_T(".L ")));
		break;
	default:
		_tcscat(instrname, disasm_lc_size(_T(" ")));
		break;
	}
}

static void asm_add_extensions(uae_u16 *data, int *dcntp, int mode, uae_u32 v, int extcnt, uae_u16 *ext, uaecptr pc, int size)
{
	int dcnt = *dcntp;
	if (mode < 0)
		return;
	if (mode == Ad16) {
		data[dcnt++] = v;
	}
	if (mode == PC16) {
		data[dcnt++] = v - (pc + 2);
	}
	if (mode == Ad8r || mode == PC8r) {
		for (int i = 0; i < extcnt; i++) {
			data[dcnt++] = ext[i];
		}
	}
	if (mode == absw) {
		data[dcnt++] = (uae_u16)v;
	}
	if (mode == absl) {
		data[dcnt++] = (uae_u16)(v >> 16);
		data[dcnt++] = (uae_u16)v;
	}
	if ((mode == imm && size == 0) || mode == imm0) {
		data[dcnt++] = (uae_u8)v;
	}
	if ((mode == imm && size == 1) || mode == imm1) {
		data[dcnt++] = (uae_u16)v;
	}
	if ((mode == imm && size == 2) || mode == imm2) {
		data[dcnt++] = (uae_u16)(v >> 16);
		data[dcnt++] = (uae_u16)v;
	}
	*dcntp = dcnt;
}

static int asm_isdreg(const TCHAR *s)
{
	if (s[0] == 'D' && s[1] >= '0' && s[1] <= '7')
		return s[1] - '0';
	return -1;
}
static int asm_isareg(const TCHAR *s)
{
	if (s[0] == 'A' && s[1] >= '0' && s[1] <= '7')
		return s[1] - '0';
	if (s[0] == 'S' && s[1] == 'P')
		return 7;
	return -1;
}
static int asm_ispc(const TCHAR *s)
{
	if (s[0] == 'P' && s[1] == 'C')
		return 1;
	return 0;
}

static uae_u32 asmgetval(const TCHAR *s)
{
	TCHAR *endptr;
	if (s[0] == '$') {
		s++;
	}
	if (s[0] == '-') {
		return _tcstol(s, &endptr, 16);
	}
	return _tcstoul(s, &endptr, 16);
}

static int asm_parse_mode020(TCHAR *s, uae_u8 *reg, uae_u32 *v, int *extcnt, uae_u16 *ext)
{
	return -1;
}

static int asm_parse_mode(TCHAR *s, uae_u8 *reg, uae_u32 *v, int *extcnt, uae_u16 *ext)
{
	TCHAR *ss = s;
	*reg = -1;
	*v = 0;
	*ext = 0;
	*extcnt = 0;
	if (s[0] == 0)
		return -1;
	// Dn
	if (asm_isdreg(s) >= 0 && s[2] == 0) {
		*reg = asm_isdreg(s);
		return Dreg;
	}
	// An
	if (asm_isareg(s) >= 0 && s[2] == 0) {
		*reg = asm_isareg(s);
		return Areg;
	}
	// (An) and (An)+
	if (s[0] == '(' && asm_isareg(s + 1) >= 0 && s[3] == ')') {
		*reg = asm_isareg(s + 1);
		if (s[4] == '+' && s[5] == 0)
			return Aipi;
		if (s[4] == 0)
			return Aind;
		return -1;
	}
	// -(An)
	if (s[0] == '-' && s[1] == '(' && asm_isareg(s + 2) >= 0 && s[4] == ')' && s[5] == 0) {
		*reg = asm_isareg(s + 2);
		return Apdi;
	}
	// Immediate
	if (s[0] == '#') {
		if (s[1] == '!') {
			*v = _tstol(s + 2);
		} else {
			*v = asmgetval(s + 1);
		}
		return imm;
	}
	// Value
	if (s[0] == '!') {
		*v = _tstol(s + 1);
	} else {
		*v = asmgetval(s);
	}
	int dots = 0;
	int fullext = 0;
	for (int i = 0; i < _tcslen(s); i++) {
		if (s[i] == ',') {
			dots++;
		} else if (s[i] == '[') {
			if (fullext > 0)
				fullext = -1;
			else
				fullext = 1;
		} else if (s[i] == ']') {
			if (fullext != 1)
				fullext = -1;
			else
				fullext = 2;
			fullext++;
		}
	}
	if (fullext < 0 || fullext == 1)
		return -1;
	if (fullext == 2) {
		return asm_parse_mode020(s, reg, v, extcnt, ext);
	}
	while (*s != 0) {
		// d16(An)
		if (dots == 0 && s[0] == '(' && asm_isareg(s + 1) >= 0 && s[3] == ')' && s[4] == 0) {
			*reg = asm_isareg(s + 1);
			return Ad16;
		}
		// d16(PC)
		if (dots == 0 && s[0] == '(' && asm_ispc(s + 1) && s[3] == ')' && s[4] == 0) {
			*reg = 2;
			return PC16;
		}
		// (d16,An) / (d16,PC)
		if (dots == 1 && s[0] == '(' && !asm_ispc(s + 1) && asm_isareg(s + 1) < 0 && asm_isdreg(s + 1) < 0) {
			TCHAR *startptr, *endptr;
			if (s[1] == '!') {
				startptr = s + 2;
				*v = _tcstol(startptr, &endptr, 10);
			} else {
				if (s[1] == '$')
					startptr = s + 2;
				else
					startptr = s + 1;
				*v = _tcstol(startptr, &endptr, 16);
			}
			if (endptr == startptr || endptr[0] != ',')
				return -1;
			if (asm_ispc(endptr + 1) && endptr[3] == ')') {
				*reg = 2;
				return PC16;
			}
			if (asm_isareg(endptr + 1) >= 0 && endptr[3] == ')') {
				*reg = asm_isareg(endptr + 1);
				return Ad16;
			}
			return -1;
		}
		// Ad8r PC8r
		if (s[0] == '(') {
			TCHAR *s2 = s;
			if (!asm_ispc(s + 1) && asm_isareg(s + 1) < 0 && asm_isdreg(s + 1) < 0) {
				if (dots != 2)
					return -1;
				TCHAR *startptr, *endptr;
				if (s[1] == '!') {
					startptr = s + 2;
					*v = _tcstol(startptr, &endptr, 10);
				} else {
					if (s[1] == '$')
						startptr = s + 2;
					else
						startptr = s + 1;
					*v = _tcstol(startptr, &endptr, 16);
				}
				if (endptr == startptr || endptr[0] != ',')
					return -1;
				s2 = endptr + 1;
			} else if (((asm_isareg(s + 1) >= 0 || asm_ispc(s + 1)) && s[3] == ',') || (asm_isdreg(s + 4) >= 0 || asm_isareg(s + 4) >= 0)) {
				if (dots != 1)
					return -1;
				s2 = s + 1;
			} else {
				return -1;
			}
			uae_u8 reg2;
			bool ispc = asm_ispc(s2);
			if (ispc) {
				*reg = 3;
			} else {
				*reg = asm_isareg(s2);
			}
			*extcnt = 1;
			s2 += 2;
			if (*s2 != ',')
				return -1;
			s2++;
			if (asm_isdreg(s2) >= 0) {
				reg2 = asm_isdreg(s2);
			} else {
				reg2 = asm_isareg(s2);
				*ext |= 1 << 15;
			}
			s2 += 2;
			*ext |= reg2 << 12;
			*ext |= (*v) & 0xff;
			if (s2[0] == '.' && s2[1] == 'W') {
				s2 += 2;
			} else if (s2[0] == '.' && s2[1] == 'L') {
				*ext |= 1 << 11;
				s2 += 2;
			}
			if (s2[0] == '*') {
				TCHAR scale = s2[1];
				if (scale == '2')
					*ext |= 1 << 9;
				else if (scale == '4')
					*ext |= 2 << 9;
				else if (scale == '8')
					*ext |= 3 << 9;
				else
					return -1;
				s2 += 2;
			}
			if (s2[0] == ')' && s2[1] == 0) {
				return ispc ? PC8r : Ad8r;
			}
			return -1;
		}
		s++;
	}
	// abs.w
	if (s - ss > 2 && s[-2] == '.' && s[-1] == 'W') {
		*reg = 0;
		return absw;
	}
	// abs.l
	*reg = 1;
	return absl;
}

static TCHAR *asm_parse_parm(TCHAR *parm, TCHAR *out)
{
	TCHAR *p = parm;
	bool quote = false;

	for (;;) {
		if (*p == '(') {
			quote = true;
		}
		if (*p == ')') {
			if (!quote)
				return NULL;
			quote = false;
		}
		if ((*p == ',' || *p == 0) && !quote) {
			TCHAR c = *p;
			p[0] = 0;
			_tcscpy(out, parm);
			my_trim(out);
			if (c)
				p++;
			return p;
		}
		p++;
	}
}

static bool m68k_asm_parse_movec(TCHAR *s, TCHAR *d)
{
	for (int i = 0; m2cregs[i].regname; i++) {
		if (!_tcscmp(s, m2cregs[i].regname)) {
			uae_u16 v = m2cregs[i].regno;
			if (asm_isareg(d) >= 0)
				v |= 0x8000 | (asm_isareg(d) << 12);
			else if (asm_isdreg(d) >= 0)
				v |= (asm_isdreg(d) << 12);
			else
				return false;
			_stprintf(s, _T("#%X"), v);
			return true;
		}
	}
	return false;
}

static bool m68k_asm_parse_movem(TCHAR *s, int dir)
{
	TCHAR *d = s;
	uae_u16 regmask = 0;
	uae_u16 mask = dir ? 0x8000 : 0x0001;
	bool ret = false;
	while(*s) {
		int dreg = asm_isdreg(s);
		int areg = asm_isareg(s);
		if (dreg < 0 && areg < 0)
			break;
		int reg = dreg >= 0 ? dreg : areg + 8;
		regmask |= dir ? (mask >> reg) : (mask << reg);
		s += 2;
		if (*s == 0) {
			ret = true;
			break;
		} else if (*s == '/') {
			s++;
			continue;
		} else if (*s == '-') {
			s++;
			int dreg2 = asm_isdreg(s);
			int areg2 = asm_isareg(s);
			if (dreg2 < 0 && areg2 < 0)
				break;
			int reg2 = dreg2 >= 0 ? dreg2 : areg2 + 8;
			if (reg2 < reg)
				break;
			while (reg2 >= reg) {
				regmask |= dir ? (mask >> reg) : (mask << reg);
				reg++;
			}
			s += 2;
			if (*s == 0) {
				ret = true;
				break;
			}
		} else {
			break;
		}
	}
	if (ret)
		_stprintf(d, _T("#%X"), regmask);
	return ret;
}

int m68k_asm(TCHAR *sline, uae_u16 *out, uaecptr pc)
{
	TCHAR *p;
	const TCHAR *cp1;
	TCHAR ins[256], parms[256];
	TCHAR line[256];
	TCHAR srcea[256], dstea[256];
	uae_u16 data[16], sexts[8], dexts[8];
	int sextcnt, dextcnt;
	int dcnt = 0;
	int cc = -1;
	int quick = 0;
	bool immrelpc = false;

	if (_tcslen(sline) > 100)
		return -1;

	srcea[0] = dstea[0] = 0;
	parms[0] = 0;

	// strip all white space except first space
	p = line;
	bool firstsp = true;
	for (int i = 0; sline[i]; i++) {
		TCHAR c = sline[i];
		if (c == 32 && firstsp) {
			firstsp = false;
			*p++ = 32;
		}
		if (c <= 32)
			continue;
		*p++ = c;
	}
	*p = 0;

	to_upper(line, _tcslen(line));

	p = line;
	while (*p && *p != ' ')
		p++;
	if (*p == ' ') {
		*p = 0;
		_tcscpy(parms, p + 1);
		my_trim(parms);
	}
	_tcscpy(ins, line);
	
	if (_tcslen(ins) == 0)
		return 0;

	int size = 1;
	int inssize = -1;
	cp1 = _tcschr(line, '.');
	if (cp1) {
		size = cp1[1];
		if (size == 'W')
			size = 1;
		else if (size == 'L')
			size = 2;
		else if (size == 'B')
			size = 0;
		else
			return 0;
		inssize = size;
		line[cp1 - line] = 0;
		_tcscpy(ins, line);
	}

	TCHAR *parmp = parms;
	parmp = asm_parse_parm(parmp, srcea);
	if (!parmp)
		return 0;
	if (srcea[0]) {
		parmp = asm_parse_parm(parmp, dstea);
		if (!parmp)
			return 0;
	}

	int smode = -1;
	int dmode = -1;
	uae_u8 sreg = -1;
	uae_u8 dreg = -1;
	uae_u32 sval = 0;
	uae_u32 dval = 0;
	int ssize = -1;
	int dsize = -1;
	struct mnemolookup *lookup;

	dmode = asm_parse_mode(dstea, &dreg, &dval, &dextcnt, dexts);


	// Common alias
	if (!_tcscmp(ins, _T("BRA"))) {
		_tcscpy(ins, _T("BT"));
	} else if (!_tcscmp(ins, _T("BSR"))) {
		immrelpc = true;
	} else if (!_tcscmp(ins, _T("MOVEM"))) {
		if (dmode >= Aind && _tcschr(dstea, '-') == NULL && _tcschr(dstea, '/') == NULL) {
			_tcscpy(ins, _T("MVMLE"));
			if (!m68k_asm_parse_movem(srcea, dmode == Apdi))
				return -1;
		} else {
			TCHAR tmp[256];
			_tcscpy(ins, _T("MVMEL"));
			_tcscpy(tmp, srcea);
			_tcscpy(srcea, dstea);
			_tcscpy(dstea, tmp);
			if (!m68k_asm_parse_movem(srcea, 0))
				return -1;
			dmode = asm_parse_mode(dstea, &dreg, &dval, &dextcnt, dexts);
		}
	} else if (!_tcscmp(ins, _T("MOVEC"))) {
		if (dmode == Dreg || dmode == Areg) {
			_tcscpy(ins, _T("MOVEC2"));
			if (!m68k_asm_parse_movec(srcea, dstea))
				return -1;
		} else {
			TCHAR tmp[256];
			_tcscpy(ins, _T("MOVE2C"));
			_tcscpy(tmp, srcea);
			_tcscpy(srcea, dstea);
			dstea[0] = 0;
			if (!m68k_asm_parse_movec(srcea, tmp))
				return -1;
		}
		dmode = -1;
	}
	
	if (dmode == Areg) {
		int l = _tcslen(ins);
		if (l <= 2)
			return -1;
		TCHAR last = ins[l- 1];
		if (last == 'Q') {
			last = ins[l - 2];
			if (last != 'A') {
				ins[l - 1] = 'A';
				ins[l] = 'Q';
				ins[l + 1] = 0;
			}
		} else if (last != 'A') {
			TCHAR insa[256];
			_tcscpy(insa, ins);
			_tcscat(insa, _T("A"));
			for (lookup = lookuptab; lookup->name; lookup++) {
				if (!_tcscmp(insa, lookup->name)) {
					_tcscpy(ins, insa);
					break;
				}
			}
		}
	}

	bool fp = ins[0] == 'F';
	int tsize = size;

	if (ins[_tcslen(ins) - 1] == 'Q' && _tcslen(ins) > 3 && !fp) {
		quick = 1;
		ins[_tcslen(ins) - 1] = 0;
		if (inssize < 0)
			tsize = 2;
	}

	for (lookup = lookuptab; lookup->name; lookup++) {
		if (!_tcscmp(ins, lookup->name))
			break;
	}
	if (!lookup->name) {
		// Check cc variants
		for (lookup = lookuptab; lookup->name; lookup++) {
			const TCHAR *ccp = _tcsstr(lookup->name, _T("cc"));
			if (ccp) {
				TCHAR tmp[256];
				for (int i = 0; i < (fp ? 32 : 16); i++) {
					const TCHAR *ccname = fp ? fpccnames[i] : ccnames[i];
					_tcscpy(tmp, lookup->name);
					_tcscpy(tmp + (ccp - lookup->name), ccname);
					if (tmp[_tcslen(tmp) - 1] == ' ')
						tmp[_tcslen(tmp) - 1] = 0;
					if (!_tcscmp(tmp, ins)) {
						_tcscpy(ins, lookup->name);
						cc = i;
						if (lookup->mnemo == i_DBcc || lookup->mnemo == i_Bcc) {
							// Bcc.B uses same encoding mode as MOVEQ
							immrelpc = true;
						}
						if (size == 0) {
							quick = 2;
						}
						break;
					}
				}
			}
			if (cc >= 0)
				break;
		}
	}

	if (!lookup->name)
		return 0;

	int mnemo = lookup->mnemo;

	int found = 0;
	int sizemask = 0;
	int unsized = 0;

	for (int round = 0; round < 9; round++) {

		if (!found && round == 8)
			return 0;

		if (round == 3) {
			bool isimm = srcea[0] == '#';
			if (immrelpc && !isimm) {
				TCHAR tmp[256];
				_tcscpy(tmp, srcea);
				srcea[0] = '#';
				_tcscpy(srcea + 1, tmp);
			}
			smode = asm_parse_mode(srcea, &sreg, &sval, &sextcnt, sexts);
			if (immrelpc && !isimm) {
				sval = sval - (pc + 2);
			}
			if (quick) {
				smode = immi;
				sreg = sval & 0xff;
			}
		}

		if (round == 1) {
			if (!quick && (sizemask == 1 || sizemask == 2 || sizemask == 4)) {
				tsize = 0;
				if (sizemask == 2)
					tsize = 1;
				else if (sizemask == 4)
					tsize = 2;
			} else {
				continue;
			}
		}
		if (round == 2 && !found) {
			unsized = 1;
		}

		if (round == 4 && smode == imm) {
			smode = imm0;
		} else if (round == 5 && smode == imm0) {
			smode = imm1;
		} else if (round == 6 && smode == imm1) {
			smode = imm2;
		} else if (round == 7 && smode == imm2) {
			smode = immi;
			sreg = sval & 0xff;
		} else if (round == 4) {
			round += 5 - 1;
		}

		for (int opcode = 0; opcode < 65536; opcode++) {
			struct instr *table = &table68k[opcode];
			if (table->mnemo != mnemo)
				continue;
			if (cc >= 0 && table->cc != cc)
				continue;

#if 0
			if (round == 0) {
				console_out_f(_T("%s OP=%04x S=%d SR=%d SM=%d SU=%d SP=%d DR=%d DM=%d DU=%d DP=%d SDU=%d\n"), lookup->name, opcode, table->size,
					table->sreg, table->smode, table->suse, table->spos,
					table->dreg, table->dmode, table->duse, table->dpos,
					table->sduse);
			}
#endif

			if (table->duse && !(table->dmode == dmode || (dmode >= imm && dmode <= imm2 && table->dmode >= imm && table->dmode <= imm2)))
				continue;
			if (round == 0) {
				sizemask |= 1 << table->size;
			}
			if (unsized > 0 && !table->unsized) {
				continue;
			}

			found++;

			if (round >= 3) {

				if (
					((table->size == tsize || table->unsized)) &&
					((!table->suse && smode < 0) || (table->suse && table->smode == smode)) &&
					((!table->duse && dmode < 0) || (table->duse && (table->dmode == dmode || (dmode == imm && (table->dmode >= imm && table->dmode <= imm2))))) &&
					((table->sreg == sreg || (table->smode >= absw && table->smode != immi))) &&
					((table->dreg == dreg || table->dmode >= absw))
					)
				{
					if (inssize >= 0 && tsize != inssize)
						continue;


					data[dcnt++] = opcode;
					asm_add_extensions(data, &dcnt, smode, sval, sextcnt, sexts, pc, tsize);
					if (smode >= 0)
						asm_add_extensions(data, &dcnt, dmode, dval, dextcnt, dexts, pc, tsize);
					for (int i = 0; i < dcnt; i++) {
						out[i] = data[i];
					}
					return dcnt;
				}

			}
		}
	}

	return 0;
}

static void resolve_if_jmp(TCHAR *s, uae_u32 addr)
{
	uae_u16 opcode = get_word_debug(addr);
	if (opcode == 0x4ef9) { // JMP x.l
		TCHAR *p = s + _tcslen(s);
		uae_u32 addr2 = get_long_debug(addr + 2);
		_stprintf(p, disasm_lc_hex(_T(" == $%08X ")), addr2);
		showea_val(p + _tcslen(p), opcode, addr2, 4);
		TCHAR txt[256];
		bool ext;
		if (debugmem_get_segment(addr2, NULL, &ext, NULL, txt)) {
			if (ext) {
				_tcscat(p, _T(" "));
				_tcscat(p, txt);
			}
		}
	}
}

static bool mmu_op30_helper_get_fc(uae_u16 extra, TCHAR *out)
{
	switch (extra & 0x0018) {
	case 0x0010:
		_stprintf(out, _T("#%d"), extra & 7);
		return true;
	case 0x0008:
		_stprintf(out, _T("%c%d"), disasm_dreg, extra & 7);
		return true;
	case 0x0000:
		if (extra & 1) {
			_tcscpy(out, disasm_lc_reg(_T("DFC")));
		} else {
			_tcscpy(out, disasm_lc_reg(_T("SFC")));
		}
		return true;
	default:
		return false;
	}
}

static bool mmu_op30_invea(uae_u32 opcode)
{
	int eamode = (opcode >> 3) & 7;
	int rreg = opcode & 7;

	// Dn, An, (An)+, -(An), immediate and PC-relative not allowed
	if (eamode == 0 || eamode == 1 || eamode == 3 || eamode == 4 || (eamode == 7 && rreg > 1))
		return true;
	return false;
}

static uaecptr disasm_mmu030(uaecptr pc, uae_u16 opcode, uae_u16 extra, struct instr *dp, TCHAR *instrname, uae_u32 *seaddr2, int *actualea, int safemode)
{
	int type = extra >> 13;

	_tcscpy(instrname, _T("F-LINE (MMU 68030)"));
	pc += 2;

	switch (type)
	{
	case 0:
	case 2:
	case 3:
	{
		// PMOVE
		int preg = (extra >> 10) & 31;
		int rw = (extra >> 9) & 1;
		int fd = (extra >> 8) & 1;
		int unused = (extra & 0xff);
		const TCHAR *r = NULL;

		if (mmu_op30_invea(opcode))
			break;
		if (unused)
			break;
		if (rw && fd)
			break;
		switch (preg)
		{
		case 0x10:
			r = _T("TC");
			break;
		case 0x12:
			r = _T("SRP");
			break;
		case 0x13:
			r = _T("CRP");
			break;
		case 0x18:
			r = _T("MMUSR");
			break;
		case 0x02:
			r = _T("TT0");
			break;
		case 0x03:
			r = _T("TT1");
			break;
		}
		if (!r)
			break;

		_tcscpy(instrname, _T("PMOVE"));
		if (fd)
			_tcscat(instrname, _T("FD"));
		_tcscat(instrname, _T(" "));
		disasm_lc_mnemo(instrname);

		if (!rw) {
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, seaddr2, actualea, safemode);
			_tcscat(instrname, _T(","));
		}
		_tcscat(instrname, disasm_lc_reg(r));
		if (rw) {
			_tcscat(instrname, _T(","));
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, seaddr2, actualea, safemode);
		}
		break;
	}
	case 1:
	{
		// PLOAD/PFLUSH
		uae_u16 mode = (extra >> 8) & 31;
		int unused = (extra & (0x100 | 0x80 | 0x40 | 0x20));
		uae_u16 fc_mask = (extra & 0x00E0) >> 5;
		uae_u16 fc_bits = extra & 0x7f;
		TCHAR fc[10];

		if (unused)
			break;

		switch (mode) {
		case 0x00: // PLOAD W
		case 0x02: // PLOAD R
			if (mmu_op30_invea(opcode))
				break;
			_stprintf(instrname, _T("PLOAD%c"), mode == 0 ? 'W' : 'R');
			disasm_lc_mnemo(instrname);
			_stprintf(instrname + _tcslen(instrname), _T(" %s,"), fc);
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, seaddr2, actualea, safemode);
			break;
		case 0x04: // PFLUSHA
			if (fc_bits)
				break;
			_tcscpy(instrname, _T("PFLUSHA"));
			disasm_lc_mnemo(instrname);
			break;
		case 0x10: // FC
			if (!mmu_op30_helper_get_fc(extra, fc))
				break;
			_tcscpy(instrname, _T("PFLUSH"));
			disasm_lc_mnemo(instrname);
			_stprintf(instrname + _tcslen(instrname), _T(" %s,%d"), fc, fc_mask);
			break;
		case 0x18: // FC + EA
			if (mmu_op30_invea(opcode))
				break;
			if (!mmu_op30_helper_get_fc(extra, fc))
				break;
			_tcscpy(instrname, _T("PFLUSH"));
			disasm_lc_mnemo(instrname);
			_stprintf(instrname + _tcslen(instrname), _T(" %s,%d"), fc, fc_mask);
			_tcscat(instrname, _T(","));
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, seaddr2, actualea, safemode);
			break;
		}
		break;
	}
	case 4:
	{
		// PTEST
		int level = (extra & 0x1C00) >> 10;
		int rw = (extra >> 9) & 1;
		int a = (extra >> 8) & 1;
		int areg = (extra & 0xE0) >> 5;
		TCHAR fc[10];

		if (mmu_op30_invea(opcode))
			break;
		if (!mmu_op30_helper_get_fc(extra, fc))
			break;
		if (!level && a)
			break;
		_stprintf(instrname, _T("PTEST%c"), rw ? 'R' : 'W');
		disasm_lc_mnemo(instrname);
		_stprintf(instrname + _tcslen(instrname), _T(" %s,"), fc);
		pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, seaddr2, actualea, safemode);
		_stprintf(instrname + _tcslen(instrname), _T(",#%d"), level);
		if (a)
			_stprintf(instrname + _tcslen(instrname), _T(",%c%d"), disasm_areg, areg);
		break;
	}
	default:
		disasm_lc_mnemo(instrname);
		break;
	}
	return pc;
}

static uae_u16 get_disasm_word(uaecptr pc, uae_u16 *bufpc, int bufpcsizep, int offset)
{
	offset /= 2;
	if (bufpc) {
		if (bufpcsizep > offset) {
			return bufpc[offset];
		}
		return 0;
	} else {
		return get_word_debug(pc + offset * 2);
	}
}
static void add_disasm_word(uaecptr *pcp, uae_u16 **bufpcp, int *bufpcsizep, int add)
{
	if (*bufpcp) {
		*bufpcp += add;
		*bufpcsizep -= add;
	} else {
		*pcp += add;
	}
}

uae_u32 m68k_disasm_2(TCHAR *buf, int bufsize, uaecptr pc, uae_u16 *bufpc, int bufpcsize, uaecptr *nextpc, int cnt, uae_u32 *seaddr, uae_u32 *deaddr, uaecptr lastpc, int safemode)
{
	uae_u32 seaddr2;
	uae_u32 deaddr2;
	int actualea_src = 0;
	int actualea_dst = 0;
#ifdef WINUAE_FOR_HATARI
	const int orig_size = bufsize;
#endif

	if (!table68k)
		return 0;
	while (cnt-- > 0) {
		TCHAR instrname[256], *ccpt;
		TCHAR segout[256], segname[256];
		int i;
		uae_u32 opcode;
		uae_u16 extra;
		struct mnemolookup *lookup;
		struct instr *dp;
		uaecptr oldpc;
		uaecptr m68kpc_illg = 0;
		int illegal = 0;
		int segid, lastsegid;
		TCHAR *symbolpos;
		bool skip = false;

		seaddr2 = deaddr2 = 0xffffffff;
		oldpc = pc;
		opcode = get_disasm_word(pc, bufpc, bufpcsize, 0);
		extra = get_disasm_word(pc, bufpc, bufpcsize, 2);
		if (cpufunctbl[opcode] == op_illg_1 || cpufunctbl[opcode] == op_unimpl_1) {
			m68kpc_illg = pc + 2;
			illegal = 1;
		}

		dp = table68k + opcode;
		if (dp->mnemo == i_ILLG) {
			illegal = 0;
			opcode = 0x4AFC;
			dp = table68k + opcode;
		}
		for (lookup = lookuptab;lookup->mnemo != dp->mnemo; lookup++)
			;

		lastsegid = -1;
		bool exact = false;
		segid = 0;
		if (!bufpc) {
			if (lastpc != 0xffffffff) {
				lastsegid = debugmem_get_segment(lastpc, NULL, NULL, NULL, NULL);
			}
			segid = debugmem_get_segment(pc, &exact, NULL, segout, segname);
			if (segid && (lastsegid != -1 || exact) && (segid != lastsegid || pc == lastpc || exact)) {
				buf = buf_out(buf, &bufsize, _T("%s\n"), segname);
			}
		}
		symbolpos = buf;

		buf = buf_out (buf, &bufsize, disasm_lc_nhex(_T("%08X ")), pc);

		if (segid) {
			buf = buf_out(buf, &bufsize, _T("%s "), segout);
		}

		add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
		
		if (lookup->friendlyname)
			_tcscpy(instrname, lookup->friendlyname);
		else
			_tcscpy(instrname, lookup->name);
		ccpt = _tcsstr(instrname, _T("cc"));
		if (ccpt != 0) {
			if ((opcode & 0xf000) == 0xf000) {
				if (lookup->mnemo == i_FBcc) {
					_tcscpy(ccpt, fpccnames[opcode & 0x1f]);
				} else {
					_tcscpy(ccpt, fpccnames[extra & 0x1f]);
				}
			} else {
				_tcscpy(ccpt, ccnames[dp->cc]);
				if (dp->mnemo == i_Bcc && dp->cc == 0) {
					_tcscpy(ccpt, _T("RA")); // BT -> BRA
				}
			}
		}
		disasm_lc_mnemo(instrname);
		disasm_size(instrname, dp);

		if (lookup->mnemo == i_MOVEC2 || lookup->mnemo == i_MOVE2C) {
			uae_u16 imm = extra;
			uae_u16 creg = imm & 0x0fff;
			uae_u16 r = imm >> 12;
			TCHAR regs[16];
			const TCHAR *cname = _T("?");
			int j;
			for (j = 0; m2cregs[j].regname; j++) {
				if (m2cregs[j].regno == creg)
					break;
			}
			_stprintf(regs, _T("%c%d"), r >= 8 ? disasm_areg : disasm_dreg, r >= 8 ? r - 8 : r);
			if (m2cregs[j].regname)
				cname = m2cregs[j].regname;
			if (lookup->mnemo == i_MOVE2C) {
				_tcscat(instrname, regs);
				_tcscat(instrname, _T(","));
				_tcscat(instrname, cname);
			} else {
				_tcscat(instrname, cname);
				_tcscat(instrname, _T(","));
				_tcscat(instrname, regs);
			}
			int lvl = (currprefs.cpu_model - 68000) / 10;
			if (lvl == 6)
				lvl = 5;
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			if (lvl < 1 || !(m2cregs[j].flags & (1 << (lvl - 1))))
				illegal = -1;
		} else if (lookup->mnemo == i_CHK2) {
			TCHAR *p;
			if (!(extra & 0x0800)) {
				instrname[1] = 'M';
				instrname[2] = 'P';
				disasm_lc_mnemo(instrname);
			}
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			p = instrname + _tcslen(instrname);
			_stprintf(p, _T(",%c%d"), (extra & 0x8000) ? disasm_areg : disasm_dreg, (extra >> 12) & 7);
		} else if (lookup->mnemo == i_CAS) {
			TCHAR *p = instrname + _tcslen(instrname);
			_stprintf(p, _T("%c%d,%c%d,"), disasm_dreg, extra & 7, disasm_dreg, (extra >> 6) & 7);
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, &actualea_dst, safemode);
		} else if (lookup->mnemo == i_CAS2) {
			TCHAR *p = instrname + _tcslen(instrname);
			uae_u16 extra2 = get_word_debug(pc + 2);
			_stprintf(p, _T("%c%d:%c%d,%c%d,%c%d,(%c%d):(%c%d)"),
				disasm_dreg, extra & 7, disasm_dreg, extra2 & 7, disasm_dreg, (extra >> 6) & 7, disasm_dreg, (extra2 >> 6) & 7,
				(extra & 0x8000) ? disasm_areg : disasm_dreg, (extra >> 12) & 7,
				(extra2 & 0x8000) ? disasm_dreg : disasm_dreg, (extra2 >> 12) & 7);
			add_disasm_word(&pc, &bufpc, &bufpcsize, 4);
		} else if (lookup->mnemo == i_ORSR || lookup->mnemo == i_ANDSR || lookup->mnemo == i_EORSR) {
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			_tcscat(instrname, dp->size == sz_byte ? disasm_lc_reg(_T(",CCR")) : disasm_lc_reg(_T(",SR")));
		} else if (lookup->mnemo == i_MVR2USP) {
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			_tcscat(instrname, disasm_lc_reg(_T(",USP")));
		} else if (lookup->mnemo == i_MVUSP2R) {
			_tcscat(instrname, disasm_lc_reg(_T("USP,")));
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
		} else if (lookup->mnemo == i_MV2SR) {
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			_tcscat(instrname, dp->size == sz_byte ? disasm_lc_reg(_T(",CCR")) : disasm_lc_reg(_T(",SR")));
		} else if (lookup->mnemo == i_MVSR2) {		
			_tcscat(instrname, dp->size == sz_byte ? disasm_lc_reg(_T("CCR,")) : disasm_lc_reg(_T("SR,")));
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
		} else if (lookup->mnemo == i_MVMEL) {
			uae_u16 mask = extra;
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			pc = ShowEA (NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			movemout (instrname, mask, dp->dmode, 0, true);
		} else if (lookup->mnemo == i_MVMLE) {
			uae_u16 mask = extra;
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			if (movemout(instrname, mask, dp->dmode, 0, false))
				_tcscat(instrname, _T(","));
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, &actualea_dst, safemode);
		} else if (lookup->mnemo == i_DIVL || lookup->mnemo == i_MULL) {
			TCHAR *p;
			extra = get_disasm_word(pc, bufpc, bufpcsize, 0);
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			if (extra & 0x0800) // signed/unsigned
				instrname[3] = 'S';
			else
				instrname[3] = 'U';
			if (lookup->mnemo == i_DIVL && !(extra & 0x0400)) {
				// DIVSL.L/DIVUL.L
				instrname[8] = 0;
				instrname[7] = ' ';
				instrname[6] = instrname[5];
				instrname[5] = instrname[4];
				instrname[4] = 'L';
			}
			disasm_lc_mnemo(instrname);
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			p = instrname + _tcslen(instrname);
			if (extra & 0x0400)
				_stprintf(p, _T(",%c%d:%c%d"), disasm_dreg, extra & 7, disasm_dreg, (extra >> 12) & 7);
			else
				_stprintf(p, _T(",%c%d"), disasm_dreg, (extra >> 12) & 7);
		} else if (lookup->mnemo == i_MOVES) {
			TCHAR *p;
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			if (!(extra & 0x0800)) {
				pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, &actualea_dst, safemode);
				p = instrname + _tcslen(instrname);
				_stprintf(p, _T(",%c%d"), (extra & 0x8000) ? disasm_areg : disasm_dreg, (extra >> 12) & 7);
			} else {
				p = instrname + _tcslen(instrname);
				_stprintf(p, _T("%c%d,"), (extra & 0x8000) ? disasm_areg : disasm_dreg, (extra >> 12) & 7);
				pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			}
		} else if (lookup->mnemo == i_BFEXTS || lookup->mnemo == i_BFEXTU ||
				   lookup->mnemo == i_BFCHG || lookup->mnemo == i_BFCLR ||
				   lookup->mnemo == i_BFFFO || lookup->mnemo == i_BFINS ||
				   lookup->mnemo == i_BFSET || lookup->mnemo == i_BFTST) {
			TCHAR *p;
			int reg = -1;

			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			p = instrname + _tcslen(instrname);
			if (lookup->mnemo == i_BFEXTS || lookup->mnemo == i_BFEXTU || lookup->mnemo == i_BFFFO || lookup->mnemo == i_BFINS)
				reg = (extra >> 12) & 7;
			if (lookup->mnemo == i_BFINS)
				_stprintf(p, _T("%c%d,"), disasm_dreg, reg);
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			_tcscat(instrname, _T(" {"));
			p = instrname + _tcslen(instrname);
			if (extra & 0x0800)
				_stprintf(p, _T("%c%d"), disasm_dreg, (extra >> 6) & 7);
			else
				_stprintf(p, _T("%d"), (extra >> 6) & 31);
			_tcscat(instrname, _T(":"));
			p = instrname + _tcslen(instrname);
			if (extra & 0x0020)
				_stprintf(p, _T("%c%d"), disasm_dreg, extra & 7);
			else
				_stprintf(p, _T("%d"), extra  & 31);
			_tcscat(instrname, _T("}"));
			p = instrname + _tcslen(instrname);
			if (lookup->mnemo == i_BFFFO || lookup->mnemo == i_BFEXTS || lookup->mnemo == i_BFEXTU)
				_stprintf(p, _T(",%c%d"), disasm_dreg, reg);
		} else if (lookup->mnemo == i_CPUSHA || lookup->mnemo == i_CPUSHL || lookup->mnemo == i_CPUSHP ||
			lookup->mnemo == i_CINVA || lookup->mnemo == i_CINVL || lookup->mnemo == i_CINVP) {
			if ((opcode & 0xc0) == 0xc0)
				_tcscat(instrname, disasm_lc_reg(_T("BC")));
			else if (opcode & 0x80)
				_tcscat(instrname, disasm_lc_reg(_T("IC")));
			else if (opcode & 0x40)
				_tcscat(instrname, disasm_lc_reg(_T("DC")));
			else
				_tcscat(instrname, _T("?"));
			if (lookup->mnemo == i_CPUSHL || lookup->mnemo == i_CPUSHP || lookup->mnemo == i_CINVL || lookup->mnemo == i_CINVP) {
				TCHAR *p = instrname + _tcslen(instrname);
				_stprintf(p, _T(",(%c%d)"), disasm_areg, opcode & 7);
			}
		} else if (lookup->mnemo == i_MOVE16) {
			TCHAR *p = instrname + _tcslen(instrname);
			if (opcode & 0x20) {
				_stprintf(p, _T("(%c%d)+,(%c%d)+"), disasm_areg, opcode & 7, disasm_areg, (extra >> 12) & 7);
				add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			} else {
				uae_u32 addr = get_long_debug(pc);
				int ay = opcode & 7;
				pc += 4;
				switch ((opcode >> 3) & 3)
				{
				case 0:
					_stprintf(p, _T("(%c%d)+,$%08x"), disasm_areg, ay, addr);
					break;
				case 1:
					_stprintf(p, _T("$%08x,(%c%d)+"), addr, disasm_areg, ay);
					break;
				case 2:
					_stprintf(p, _T("(%c%d),$%08x"), disasm_areg, ay, addr);
					break;
				case 3:
					_stprintf(p, _T("$%08x,(%c%d)"), addr, disasm_areg, ay);
					break;
				}
			}
		} else if (lookup->mnemo == i_PACK || lookup->mnemo == i_UNPK) {
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			_tcscat(instrname, _T(","));
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, &actualea_dst, safemode);
			extra = get_word_debug(pc);
			_stprintf(instrname + _tcslen(instrname), disasm_lc_hex(_T(",#$%04X")), extra);
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
		} else if (lookup->mnemo == i_LPSTOP) {
			if (extra == 0x01c0) {
				uae_u16 extra2 = get_word_debug(pc + 2);
				_tcscpy(instrname, _T("LPSTOP"));
				disasm_lc_mnemo(instrname);
				_stprintf(instrname + _tcslen(instrname), disasm_lc_hex(_T(" #$%04X")), extra2);
				add_disasm_word(&pc, &bufpc, &bufpcsize, 4);
			} else {
				_tcscpy(instrname, _T("ILLG"));
				disasm_lc_mnemo(instrname);
				_stprintf(instrname + _tcslen(instrname), disasm_lc_hex(_T(" #$%04X")), extra);
				add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			}
		} else if (lookup->mnemo == i_CALLM) {
			TCHAR *p = instrname + _tcslen(instrname);
			_stprintf(p, _T("#%d,"), extra & 255);
			pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
		} else if (lookup->mnemo == i_FDBcc) {
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			_tcscat(instrname, _T(","));
			pc = ShowEA(NULL, pc, opcode, 0, imm1, sz_word, instrname, &deaddr2, &actualea_dst, safemode);
		} else if (lookup->mnemo == i_FTRAPcc) {
			pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &seaddr2, &actualea_src, safemode);
			int mode = opcode & 7;
			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			if (mode == 2) {
				pc = ShowEA(NULL, pc, opcode, 0, imm1, sz_word, instrname, NULL, NULL, safemode);
			} else if (mode == 3) {
				pc = ShowEA(NULL, pc, opcode, 0, imm2, sz_long, instrname, NULL, NULL, safemode);
			}
		} else if (lookup->mnemo == i_FPP) {
			TCHAR *p;
			int ins = extra & 0x7f;
			int size = (extra >> 10) & 7;

			add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			if ((extra & 0xfc00) == 0x5c00) { // FMOVECR (=i_FPP with source specifier = 7)
				fpdata fp;
				fpu_get_constant(&fp, extra & 0x7f);
				_tcscpy(instrname, _T("FMOVECR.X"));
				disasm_lc_mnemo(instrname);
				_stprintf(instrname + _tcslen(instrname), _T(" #0x%02x [%s],%s%d"), extra & 0x7f, fpp_print(&fp, 0), disasm_fpreg, (extra >> 7) & 7);
			} else if ((extra & 0x8000) == 0x8000) { // FMOVEM or FMOVE control register
				int dr = (extra >> 13) & 1;
				int mode;
				int dreg = (extra >> 4) & 7;
				int regmask, fpmode;
				
				if (extra & 0x4000) {
					mode = (extra >> 11) & 3;
					regmask = extra & 0xff;  // FMOVEM FPx
					fpmode = 1;
					_tcscpy(instrname, _T("FMOVEM.X "));
					disasm_lc_mnemo(instrname);
				} else {
					mode = 0;
					regmask = (extra >> 10) & 7;  // FMOVEM or FMOVE control
					fpmode = 2;
					_tcscpy(instrname, _T("FMOVEM.L "));
					if (regmask == 1 || regmask == 2 || regmask == 4)
						_tcscpy(instrname, _T("FMOVE.L "));
					disasm_lc_mnemo(instrname);
				}
				p = instrname + _tcslen(instrname);
				if (dr) {
					if (mode & 1)
						_stprintf(p, _T("%c%d"), disasm_dreg, dreg);
					else
						movemout(p, regmask, dp->dmode, fpmode, false);
					_tcscat(instrname, _T(","));
					pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, &actualea_dst, safemode);
				} else {
					if ((opcode & 0x3f) == 0x3c && !(extra & 0x2000)) {
						// FMOVEM #xxx,control registers (strange one, can have up to 3 long word immediates)
						bool entry = false;
						if (!(extra & (0x400 | 0x800 | 0x1000)))
							extra |= 0x400;
						for (int i = 0; i < 3; i++) {
							if (extra & (0x1000 >> i)) {
								if (entry)
									_tcscat(p, _T("/"));
								entry = true;
								p = instrname + _tcslen(instrname);
								_stprintf(p, disasm_lc_hex(_T("#$%08X")), get_ilong_debug(pc));
								add_disasm_word(&pc, &bufpc, &bufpcsize, 4);
							}
						}
					} else {
						pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, &actualea_dst, safemode);
					}
					p = instrname + _tcslen(instrname);
					if (mode & 1)
						_stprintf(p, _T(",%c%d"), disasm_dreg, dreg);
					else
						movemout(p, regmask, dp->dmode, fpmode, true);
				}
			} else {
				if (fpuopcodes[ins]) {
					_tcscpy(instrname, fpuopcodes[ins]);
				} else {
					_stprintf(instrname, _T("%s?%02X"), disasm_lc_reg(_T("F")), ins);
				}
				disasm_lc_mnemo(instrname);
				if ((extra & (0x8000 | 0x4000 | 0x2000)) == (0x4000 | 0x2000)) { // FMOVE to memory/data register
					int kfactor = extra & 0x7f;
					_tcscpy(instrname, _T("FMOVE."));
					_tcscat(instrname, fpsizes[size]);
					disasm_lc_mnemo(instrname);
					_tcscat(instrname, _T(" "));
					p = instrname + _tcslen(instrname);
					_stprintf(p, _T("%s%d,"), disasm_fpreg, (extra >> 7) & 7);
					pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, fpsizeconv[size], instrname, &deaddr2, &actualea_dst, safemode);
					p = instrname + _tcslen(instrname);
					if (size == 7) {
						_stprintf(p, _T(" {%c%d}"), disasm_dreg, (kfactor >> 4));
					} else if (kfactor) {
						if (kfactor & 0x40)
							kfactor |= ~0x3f;
						_stprintf(p, _T(" {%d}"), kfactor);
					}
				} else if ((extra & (0x8000 | 0x2000)) == 0) {
					if (extra & 0x4000) { // source is EA
						_tcscat(instrname, _T("."));
						_tcscat(instrname, disasm_lc_size(fpsizes[size]));
						_tcscat(instrname, _T(" "));
						pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, fpsizeconv[size], instrname, &seaddr2, &actualea_src, safemode);
					} else { // source is FPx
						p = instrname + _tcslen(instrname);
						_tcscat(p, disasm_lc_reg(_T(".X")));
						p = instrname + _tcslen(instrname);
						_stprintf(p, _T(" %s%d"), disasm_fpreg, (extra >> 10) & 7);
					}
					p = instrname + _tcslen(instrname);
					if ((extra & 0x4000) || (((extra >> 7) & 7) != ((extra >> 10) & 7)))
						_stprintf(p, _T(",%s%d"), disasm_fpreg, (extra >> 7) & 7);
					if (ins >= 0x30 && ins < 0x38) { // FSINCOS
						p = instrname + _tcslen(instrname);
						_stprintf(p, _T(",%s%d"), disasm_fpreg, extra & 7);
					}
				}
				if (ins >= 0x40 && currprefs.fpu_model >= 68881 && fpuopcodes[ins]) {
					_tcscat(instrname, _T(" (68040+)"));
				}
			}
		} else if (lookup->mnemo == i_MMUOP030) {
			pc = disasm_mmu030(pc, opcode, extra, dp, instrname, &seaddr2, &actualea_src, safemode);
		} else if ((opcode & 0xf000) == 0xa000) {
			_tcscpy(instrname, _T("A-LINE"));
			disasm_lc_mnemo(instrname);
		} else {
			if (lookup->mnemo == i_FBcc && (opcode & 0x1f) == 0 && extra == 0) {
				_tcscpy(instrname, _T("FNOP"));
				disasm_lc_mnemo(instrname);
				add_disasm_word(&pc, &bufpc, &bufpcsize, 2);
			} else {
				if (dp->suse) {
					pc = ShowEA(NULL, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, &seaddr2, &actualea_src, safemode);

					// JSR x(a6) / JMP x(a6)
					if (opcode == 0x4ea8 + 6 || opcode == 0x4ee8 + 6) {
						TCHAR sname[256];
						if (debugger_get_library_symbol(m68k_areg(regs, 6), 0xffff0000 | extra, sname)) {
							TCHAR *p = instrname + _tcslen(instrname);
							_stprintf(p, _T(" %s"), sname);
							resolve_if_jmp(instrname, m68k_areg(regs, 6) + (uae_s16)extra);
						}
					}
					// show target address if JSR x(pc) + JMP xxxx combination
					if (opcode == 0x4eba && seaddr2 && instrname[0]) { // JSR x(pc)
						resolve_if_jmp(instrname, seaddr2);
					}
				}
				if (dp->suse && dp->duse)
					_tcscat(instrname, _T(","));
				if (dp->duse) {
					pc = ShowEA(NULL, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, &deaddr2, &actualea_dst, safemode);
				}
				if (lookup->mnemo == i_RTS || lookup->mnemo == i_RTD || lookup->mnemo == i_RTR || lookup->mnemo == i_RTE) {
					uaecptr a = regs.regs[15];
					TCHAR eas[100];
					if (lookup->mnemo == i_RTE || lookup->mnemo == i_RTR) {
						a += 2;
					}
					if (disasm_flags & DISASM_FLAG_EA) {
						_stprintf(eas, disasm_lc_hex(_T(" == $%08X")), get_ilong_debug(a));
					}
					_tcscat(instrname, eas);
				}
			}
		}

		if (disasm_flags & DISASM_FLAG_WORDS) {
			for (i = 0; i < (pc - oldpc) / 2 && i < disasm_max_words; i++) {
				buf = buf_out(buf, &bufsize, disasm_lc_nhex(_T("%04X ")), get_word_debug(oldpc + i * 2));
			}
			while (i++ < disasm_min_words) {
				buf = buf_out(buf, &bufsize, _T("     "));
			}
		}

		if (illegal)
			buf = buf_out (buf, &bufsize, _T("[ "));
		buf = buf_out (buf, &bufsize, instrname);
		if (illegal)
			buf = buf_out (buf, &bufsize, _T(" ]"));

		if (ccpt != 0) {
			uaecptr addr2 = deaddr2 != 0xffffffff ? deaddr2 : seaddr2;
			if (deaddr)
				*deaddr = pc;
			if ((opcode & 0xf000) == 0xf000) {
				if (currprefs.fpu_model) {
					buf = buf_out(buf, &bufsize, disasm_lc_hex(_T(" == $%08X")), addr2);
					if (fpp_cond(dp->cc)) {
						buf = buf_out(buf, &bufsize, _T(" (T)"));
					} else {
						buf = buf_out(buf, &bufsize, _T(" (F)"));
					}
				}
			} else {
				if (dp->mnemo == i_Bcc || dp->mnemo == i_DBcc) {
					buf = buf_out(buf, &bufsize, disasm_lc_hex(_T(" == $%08X")), addr2);
					if (cctrue(dp->cc)) {
						buf = buf_out(buf, &bufsize, _T(" (T)"));
					} else {
						buf = buf_out(buf, &bufsize, _T(" (F)"));
					}
				} else {
					if (disasm_flags & DISASM_FLAG_CC) {
						if (cctrue(dp->cc)) {
							buf = buf_out(buf, &bufsize, _T(" (T)"));
						} else {
							buf = buf_out(buf, &bufsize, _T(" (F)"));
						}
					}
				}
			}
		} else if ((opcode & 0xff00) == 0x6100) { /* BSR */
			if (deaddr)
				*deaddr = pc;
			if (disasm_flags & DISASM_FLAG_EA) {
				buf = buf_out(buf, &bufsize, disasm_lc_hex(_T(" == $%08X")), seaddr2);
			}
		}
#ifdef WINUAE_FOR_HATARI
		/* outputting only single disassembly line, and there's profile info? */
		if (Profile_CpuAddr_HasData(oldpc)) {
# define PROFILE_OUTPUT_COLUMN 68
# define SPACE_FOR_NEWLINE 3
			int count = bufsize - (orig_size - PROFILE_OUTPUT_COLUMN);
			if (count > 0) {
				snprintf(buf, bufsize, "%*c", count, ' ');
				count = _tcslen(buf);
				bufsize -= count;
				buf += count;
			}
			Profile_CpuAddr_DataStr(buf, bufsize - SPACE_FOR_NEWLINE, oldpc);
			count = _tcslen(buf);
			bufsize -= count;
			buf += count;
		}
#endif
		buf = buf_out (buf, &bufsize, _T("\n"));

		for (uaecptr segpc = oldpc; segpc < pc; segpc++) {
			TCHAR segout[256];
			if (debugmem_get_symbol(segpc, segout, sizeof(segout) / sizeof(TCHAR))) {
				_tcscat(segout, _T(":\n"));
				if (bufsize > _tcslen(segout)) {
					memmove(symbolpos + _tcslen(segout), symbolpos, (_tcslen(symbolpos) + 1) * sizeof(TCHAR));
					memcpy(symbolpos, segout, _tcslen(segout) * sizeof(TCHAR));
					bufsize -= _tcslen(segout);
					buf += _tcslen(segout);
					symbolpos += _tcslen(segout);
				}
			}
		}

		int srcline = -1;
		for (uaecptr segpc = oldpc; segpc < pc; segpc++) {
			TCHAR sourceout[256];
			int line = debugmem_get_sourceline(segpc, sourceout, sizeof(sourceout) / sizeof(TCHAR));
			if (line < 0)
				break;
			if (srcline != line) {
				if (srcline < 0)
					buf = buf_out(buf, &bufsize, _T("\n"));
				buf = buf_out(buf, &bufsize, sourceout);
				srcline = line;
			}
		}
		if (srcline >= 0) {
			buf = buf_out(buf, &bufsize, _T("\n"));
		}

		if (illegal > 0)
			pc = m68kpc_illg;
	}
	if (nextpc)
		*nextpc = pc;
	if (seaddr)
		*seaddr = seaddr2;
	if (deaddr)
		*deaddr = deaddr2;
	return (actualea_src ? 1 : 0) | (actualea_dst ? 2 : 0);
}


/*************************************************************
Disasm the m68kcode at the given address into instrname
and instrcode
*************************************************************/
void sm68k_disasm (TCHAR *instrname, TCHAR *instrcode, uaecptr addr, uaecptr *nextpc, uaecptr lastpc)
{
	TCHAR *ccpt;
	uae_u32 opcode;
	struct mnemolookup *lookup;
	struct instr *dp;
	uaecptr pc, oldpc;

	pc = oldpc = addr;
	opcode = get_word_debug (pc);
	if (cpufunctbl[opcode] == op_illg_1) {
		opcode = 0x4AFC;
	}
	dp = table68k + opcode;
	for (lookup = lookuptab;lookup->mnemo != dp->mnemo; lookup++);

	pc += 2;

	_tcscpy (instrname, lookup->name);
	ccpt = _tcsstr (instrname, _T("cc"));
	if (ccpt != 0) {
		_tcsncpy (ccpt, ccnames[dp->cc], 2);
	}
	switch (dp->size){
	case sz_byte: _tcscat (instrname, _T(".B ")); break;
	case sz_word: _tcscat (instrname, _T(".W ")); break;
	case sz_long: _tcscat (instrname, _T(".L ")); break;
	default: _tcscat (instrname, _T("   ")); break;
	}

	if (dp->suse) {
		pc = ShowEA (0, pc, opcode, dp->sreg, dp->smode, dp->size, instrname, NULL, NULL, 0);
	}
	if (dp->suse && dp->duse)
		_tcscat (instrname, _T(","));
	if (dp->duse) {
		pc = ShowEA (0, pc, opcode, dp->dreg, dp->dmode, dp->size, instrname, NULL, NULL, 0);
	}
	if (instrcode)
	{
		int i;
		for (i = 0; i < (pc - oldpc) / 2; i++)
		{
			_stprintf (instrcode, _T("%04x "), get_iword_debug (oldpc + i * 2));
			instrcode += _tcslen (instrcode);
		}
	}
	if (nextpc)
		*nextpc = pc;
}
