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

    mips3dsm.c
    Disassembler for the portable MIPS 3 emulator.
    Written by Aaron Giles

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

#include "cpuintrf.h"


static const char *const reg[32] =
{
	"0",	"r1",	"r2",	"r3",	"r4",	"r5",	"r6",	"r7",
	"r8",	"r9",	"r10",	"r11",	"r12",	"r13",	"r14",	"r15",
	"r16",	"r17",	"r18",	"r19",	"r20",	"r21",	"r22",	"r23",
	"r24",	"r25",	"r26",	"r27",	"r28",	"r29",	"r30",	"r31"
};


static const char *const cpreg[4][32] =
{
	{
		"Index","Random","EntryLo0","EntryLo1","Context","PageMask","Wired","Error",
		"BadVAddr","Count","EntryHi","Compare","SR","Cause","EPC","PRId",
		"Config","LLAddr","WatchLo","WatchHi","XContext","cpr21","cpr22","cpr23",
		"cpr24","cpr25","ECC","CacheError","TagLo","TagHi","ErrorEPC","cpr31"
	},
	{
		"f0",	"f1",	"f2",	"f3",	"f4",	"f5",	"f6",	"f7",
		"f8",	"f9",	"f10",	"f11",	"f12",	"f13",	"f14",	"f15",
		"f16",	"f17",	"f18",	"f19",	"f20",	"f21",	"f22",	"f23",
		"f24",	"f25",	"f26",	"f27",	"f28",	"f29",	"f30",	"f31"
	},
	{
		"cpr0",	"cpr1",	"cpr2",	"cpr3",	"cpr4",	"cpr5",	"cpr6",	"cpr7",
		"cpr8",	"cpr9",	"cpr10","cpr11","cpr12","cpr13","cpr14","cpr15",
		"cpr16","cpr17","cpr18","cpr19","cpr20","cpr21","cpr22","cpr23",
		"cpr24","cpr25","cpr26","cpr27","cpr28","cpr29","cpr30","cpr31"
	},
	{
		"cpr0",	"cpr1",	"cpr2",	"cpr3",	"cpr4",	"cpr5",	"cpr6",	"cpr7",
		"cpr8",	"cpr9",	"cpr10","cpr11","cpr12","cpr13","cpr14","cpr15",
		"cpr16","cpr17","cpr18","cpr19","cpr20","cpr21","cpr22","cpr23",
		"cpr24","cpr25","cpr26","cpr27","cpr28","cpr29","cpr30","cpr31"
	}
};


static const char *const ccreg[4][32] =
{
	{
		"ccr0",	"ccr1",	"ccr2",	"ccr3",	"ccr4",	"ccr5",	"ccr6",	"ccr7",
		"ccr8",	"ccr9",	"ccr10","ccr11","ccr12","ccr13","ccr14","ccr15",
		"ccr16","ccr17","ccr18","ccr19","ccr20","ccr21","ccr22","ccr23",
		"ccr24","ccr25","ccr26","ccr27","ccr28","ccr29","ccr30","ccr31"
	},
	{
		"ccr0",	"ccr1",	"ccr2",	"ccr3",	"ccr4",	"ccr5",	"ccr6",	"ccr7",
		"ccr8",	"ccr9",	"ccr10","ccr11","ccr12","ccr13","ccr14","ccr15",
		"ccr16","ccr17","ccr18","ccr19","ccr20","ccr21","ccr22","ccr23",
		"ccr24","ccr25","ccr26","ccr27","ccr28","ccr29","ccr30","ccr31"
	},
	{
		"ccr0",	"ccr1",	"ccr2",	"ccr3",	"ccr4",	"ccr5",	"ccr6",	"ccr7",
		"ccr8",	"ccr9",	"ccr10","ccr11","ccr12","ccr13","ccr14","ccr15",
		"ccr16","ccr17","ccr18","ccr19","ccr20","ccr21","ccr22","ccr23",
		"ccr24","ccr25","ccr26","ccr27","ccr28","ccr29","ccr30","ccr31"
	},
	{
		"ccr0",	"ccr1",	"ccr2",	"ccr3",	"ccr4",	"ccr5",	"ccr6",	"ccr7",
		"ccr8",	"ccr9",	"ccr10","ccr11","ccr12","ccr13","ccr14","ccr15",
		"ccr16","ccr17","ccr18","ccr19","ccr20","ccr21","ccr22","ccr23",
		"ccr24","ccr25","ccr26","ccr27","ccr28","ccr29","ccr30","ccr31"
	}
};


/***************************************************************************
    CODE CODE
***************************************************************************/

INLINE char *signed_16bit(INT16 val)
{
	static char temp[10];
	if (val < 0)
		sprintf(temp, "-$%x", -val);
	else
		sprintf(temp, "$%x", val);
	return temp;
}

static UINT32 dasm_cop0(UINT32 pc, UINT32 op, char *buffer)
{
	int rt = (op >> 16) & 31;
	int rd = (op >> 11) & 31;
	UINT32 flags = 0;

	switch ((op >> 21) & 31)
	{
		case 0x00:	sprintf(buffer, "mfc0   %s,%s", reg[rt], cpreg[0][rd]);					break;
		case 0x01:	sprintf(buffer, "dmfc0  %s,%s", reg[rt], cpreg[0][rd]);					break;
		case 0x02:	sprintf(buffer, "cfc0   %s,%s", reg[rt], ccreg[0][rd]);					break;
		case 0x04:	sprintf(buffer, "mtc0   %s,%s", reg[rt], cpreg[0][rd]);					break;
		case 0x05:	sprintf(buffer, "dmtc0  %s,%s", reg[rt], cpreg[0][rd]);					break;
		case 0x06:	sprintf(buffer, "ctc0   %s,%s", reg[rt], ccreg[0][rd]);					break;
		case 0x08:	/* BC */
			switch (rt)
			{
				case 0x00:	sprintf(buffer, "bc0f   $%08x", pc + 4 + ((INT16)op << 2));		break;
				case 0x01:	sprintf(buffer, "bc0t   $%08x", pc + 4 + ((INT16)op << 2));		break;
				case 0x02:	sprintf(buffer, "bc0fl [invalid]");								break;
				case 0x03:	sprintf(buffer, "bc0tl [invalid]");								break;
				default:	sprintf(buffer, "dc.l   $%08x [invalid]", op);					break;
			}
			break;
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1a:
		case 0x1b:
		case 0x1c:
		case 0x1d:
		case 0x1e:
		case 0x1f:	/* COP */
			switch (op & 0x01ffffff)
			{
				case 0x01:	sprintf(buffer, "tlbr");											break;
				case 0x02:	sprintf(buffer, "tlbwi");											break;
				case 0x06:	sprintf(buffer, "tlbwr");											break;
				case 0x08:	sprintf(buffer, "tlbp");											break;
				case 0x10:	sprintf(buffer, "rfe");	flags = DASMFLAG_STEP_OUT;					break;
				case 0x18:	sprintf(buffer, "eret [invalid]");									break;
				default:	sprintf(buffer, "cop0  $%07x", op & 0x01ffffff);					break;
			}
			break;
		default:	sprintf(buffer, "dc.l   $%08x [invalid]", op);								break;
	}
	return flags;
}

static UINT32 dasm_cop1(UINT32 pc, UINT32 op, char *buffer)
{
	static const char *const format_table[] =
	{
		"?","?","?","?","?","?","?","?","?","?","?","?","?","?","?","?",
		"s","d","?","?","w","l","?","?","?","?","?","?","?","?","?","?"
	};
	const char *fmt = format_table[(op >> 21) & 31];
	int ft = (op >> 16) & 31;
	int fs = (op >> 11) & 31;
	int fd = (op >> 6) & 31;
	int rt = (op >> 16) & 31;
	int rd = (op >> 11) & 31;
	UINT32 flags = 0;

	switch ((op >> 21) & 31)
	{
		case 0x00:	sprintf(buffer, "mfc1   %s,%s", reg[rt], cpreg[1][rd]);						break;
		case 0x01:	sprintf(buffer, "dmfc1  %s,%s", reg[rt], cpreg[1][rd]);						break;
		case 0x02:	sprintf(buffer, "cfc1   %s,%s", reg[rt], ccreg[1][rd]);						break;
		case 0x04:	sprintf(buffer, "mtc1   %s,%s", reg[rt], cpreg[1][rd]);						break;
		case 0x05:	sprintf(buffer, "dmtc1  %s,%s", reg[rt], cpreg[1][rd]);						break;
		case 0x06:	sprintf(buffer, "ctc1   %s,%s", reg[rt], ccreg[1][rd]);						break;
		case 0x08:	/* BC */
			switch (rt & 3)
			{
				case 0x00:	sprintf(buffer, "bc1f   $%08x,%d", pc + 4 + ((INT16)op << 2), (op >> 18) & 7);		break;
				case 0x01:	sprintf(buffer, "bc1t   $%08x,%d", pc + 4 + ((INT16)op << 2), (op >> 18) & 7);		break;
				case 0x02:	sprintf(buffer, "bc1fl  $%08x,%d", pc + 4 + ((INT16)op << 2), (op >> 18) & 7); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
				case 0x03:	sprintf(buffer, "bc1tl  $%08x,%d", pc + 4 + ((INT16)op << 2), (op >> 18) & 7); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
			}
			break;
		default:	/* COP */
			switch (op & 0x3f)
			{
				case 0x00:	sprintf(buffer, "add.%s  %s,%s,%s", fmt, cpreg[1][fd], cpreg[1][fs], cpreg[1][ft]);	break;
				case 0x01:	sprintf(buffer, "sub.%s  %s,%s,%s", fmt, cpreg[1][fd], cpreg[1][fs], cpreg[1][ft]);	break;
				case 0x02:	sprintf(buffer, "mul.%s  %s,%s,%s", fmt, cpreg[1][fd], cpreg[1][fs], cpreg[1][ft]);	break;
				case 0x03:	sprintf(buffer, "div.%s  %s,%s,%s", fmt, cpreg[1][fd], cpreg[1][fs], cpreg[1][ft]);	break;
				case 0x04:	sprintf(buffer, "sqrt.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x05:	sprintf(buffer, "abs.%s  %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x06:	sprintf(buffer, "mov.%s  %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x07:	sprintf(buffer, "neg.%s  %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x08:	sprintf(buffer, "round.l.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x09:	sprintf(buffer, "trunc.l.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x0a:	sprintf(buffer, "ceil.l.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x0b:	sprintf(buffer, "floor.l.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x0c:	sprintf(buffer, "round.w.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x0d:	sprintf(buffer, "trunc.w.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x0e:	sprintf(buffer, "ceil.w.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x0f:	sprintf(buffer, "floor.w.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x11:	sprintf(buffer, "mov%c.%s  %s,%s,%d", ((op >> 16) & 1) ? 't' : 'f', fmt, cpreg[1][fd], cpreg[1][fs], (op >> 18) & 7);	break;
				case 0x12:	sprintf(buffer, "movz.%s  %s,%s,%s", fmt, cpreg[1][fd], cpreg[1][fs], reg[rt]);		break;
				case 0x13:	sprintf(buffer, "movn.%s  %s,%s,%s", fmt, cpreg[1][fd], cpreg[1][fs], reg[rt]);		break;
				case 0x15:	sprintf(buffer, "recip.%s  %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x16:	sprintf(buffer, "rsqrt.%s  %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);				break;
				case 0x20:	sprintf(buffer, "cvt.s.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x21:	sprintf(buffer, "cvt.d.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x24:	sprintf(buffer, "cvt.w.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x25:	sprintf(buffer, "cvt.l.%s %s,%s", fmt, cpreg[1][fd], cpreg[1][fs]);					break;
				case 0x30:	sprintf(buffer, "c.f.%s  %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x31:	sprintf(buffer, "c.un.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x32:	sprintf(buffer, "c.eq.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x33:	sprintf(buffer, "c.ueq.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x34:	sprintf(buffer, "c.olt.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x35:	sprintf(buffer, "c.ult.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x36:	sprintf(buffer, "c.ole.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x37:	sprintf(buffer, "c.ule.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x38:	sprintf(buffer, "c.sf.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x39:	sprintf(buffer, "c.ngle.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);break;
				case 0x3a:	sprintf(buffer, "c.seq.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x3b:	sprintf(buffer, "c.ngl.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x3c:	sprintf(buffer, "c.lt.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x3d:	sprintf(buffer, "c.nge.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x3e:	sprintf(buffer, "c.le.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				case 0x3f:	sprintf(buffer, "c.ngt.%s %s,%s,%d", fmt, cpreg[1][fs], cpreg[1][ft], (op >> 8) & 7);	break;
				default:	sprintf(buffer, "cop1   $%07x", op & 0x01ffffff);									break;
			}
			break;
	}
	return flags;
}

static UINT32 dasm_cop1x(UINT32 pc, UINT32 op, char *buffer)
{
	static const char *const format3_table[] =
	{
		"s","d","?","?","w","l","?","?"
	};
	const char *fmt3 = format3_table[op & 7];
	int fr = (op >> 21) & 31;
	int ft = (op >> 16) & 31;
	int fs = (op >> 11) & 31;
	int fd = (op >> 6) & 31;
	int rs = (op >> 21) & 31;
	int rt = (op >> 16) & 31;
	int rd = (op >> 11) & 31;
	UINT32 flags = 0;

	switch (op & 0x3f)
	{
		case 0x00:	sprintf(buffer, "lwxc1   %s,%s(%s)", cpreg[1][fd], reg[rt], reg[rs]);				break;
		case 0x01:	sprintf(buffer, "ldxc1   %s,%s(%s)", cpreg[1][fd], reg[rt], reg[rs]);				break;
		case 0x08:	sprintf(buffer, "swxc1   %s,%s(%s)", cpreg[1][fd], reg[rt], reg[rs]);				break;
		case 0x09:	sprintf(buffer, "sdxc1   %s,%s(%s)", cpreg[1][fd], reg[rt], reg[rs]);				break;
		case 0x0f:	sprintf(buffer, "prefx   %d,%s(%s)", rd, reg[rt], reg[rs]);							break;
		case 0x20:
		case 0x21:
		case 0x22:
		case 0x23:
		case 0x24:
		case 0x25:
		case 0x26:
		case 0x27:	sprintf(buffer, "madd.%s  %s,%s,%s,%s", fmt3, cpreg[1][fd], cpreg[1][fr], cpreg[1][fs], cpreg[1][ft]); break;
		case 0x28:
		case 0x29:
		case 0x2a:
		case 0x2b:
		case 0x2c:
		case 0x2d:
		case 0x2e:
		case 0x2f:	sprintf(buffer, "msub.%s  %s,%s,%s,%s", fmt3, cpreg[1][fd], cpreg[1][fr], cpreg[1][fs], cpreg[1][ft]); break;
		case 0x30:
		case 0x31:
		case 0x32:
		case 0x33:
		case 0x34:
		case 0x35:
		case 0x36:
		case 0x37:	sprintf(buffer, "nmadd.%s %s,%s,%s,%s", fmt3, cpreg[1][fd], cpreg[1][fr], cpreg[1][fs], cpreg[1][ft]); break;
		case 0x38:
		case 0x39:
		case 0x3a:
		case 0x3b:
		case 0x3c:
		case 0x3d:
		case 0x3e:
		case 0x3f:	sprintf(buffer, "nmsub.%s %s,%s,%s,%s", fmt3, cpreg[1][fd], cpreg[1][fr], cpreg[1][fs], cpreg[1][ft]); break;
		default:	sprintf(buffer, "cop1   $%07x", op & 0x01ffffff);									break;
	}
	return flags;
}

static UINT32 dasm_cop2(UINT32 pc, UINT32 op, char *buffer)
{
	int rt = (op >> 16) & 31;
	int rd = (op >> 11) & 31;
	UINT32 flags = 0;

	switch ((op >> 21) & 31)
	{
		case 0x00:	sprintf(buffer, "mfc2   %s,%s", reg[rt], cpreg[2][rd]);					break;
		case 0x01:	sprintf(buffer, "dmfc2  %s,%s", reg[rt], cpreg[2][rd]);					break;
		case 0x02:	sprintf(buffer, "cfc2   %s,%s", reg[rt], ccreg[2][rd]);					break;
		case 0x04:	sprintf(buffer, "mtc2   %s,%s", reg[rt], cpreg[2][rd]);					break;
		case 0x05:	sprintf(buffer, "dmtc2  %s,%s", reg[rt], cpreg[2][rd]);					break;
		case 0x06:	sprintf(buffer, "ctc2   %s,%s", reg[rt], ccreg[2][rd]);					break;
		case 0x08:	/* BC */
			switch (rt)
			{
				case 0x00:	sprintf(buffer, "bc2f   $%08x", pc + 4 + ((INT16)op << 2));		break;
				case 0x01:	sprintf(buffer, "bc2t   $%08x", pc + 4 + ((INT16)op << 2));		break;
				case 0x02:	sprintf(buffer, "bc2fl [invalid]");								break;
				case 0x03:	sprintf(buffer, "bc2tl [invalid]");								break;
				default:	sprintf(buffer, "dc.l   $%08x [invalid]", op);					break;
			}
			break;
		case 0x10:
		case 0x11:
		case 0x12:
		case 0x13:
		case 0x14:
		case 0x15:
		case 0x16:
		case 0x17:
		case 0x18:
		case 0x19:
		case 0x1a:
		case 0x1b:
		case 0x1c:
		case 0x1d:
		case 0x1e:
		case 0x1f:	/* COP */
			sprintf(buffer, "cop2   $%07x", op & 0x01ffffff);
			break;
		default:	sprintf(buffer, "dc.l   $%08x [invalid]", op);									break;
	}
	return flags;
}

unsigned dasmmips3(char *buffer, unsigned pc, UINT32 op)
{
	int rs = (op >> 21) & 31;
	int rt = (op >> 16) & 31;
	int rd = (op >> 11) & 31;
	int shift = (op >> 6) & 31;
	UINT32 flags = 0;

	switch (op >> 26)
	{
		case 0x00:	/* SPECIAL */
			switch (op & 63)
			{
				case 0x00:	if (op == 0)
							sprintf(buffer, "nop");
							else
							sprintf(buffer, "sll    %s,%s,%d", reg[rd], reg[rt], shift);			break;
				case 0x01:	sprintf(buffer, "mov%c   %s,%s,%d", ((op >> 16) & 1) ? 't' : 'f', reg[rd], reg[rt], (op >> 18) & 7); break;
				case 0x02:	sprintf(buffer, "srl    %s,%s,%d", reg[rd], reg[rt], shift);			break;
				case 0x03:	sprintf(buffer, "sra    %s,%s,%d", reg[rd], reg[rt], shift);			break;
				case 0x04:	sprintf(buffer, "sllv   %s,%s,%s", reg[rd], reg[rt], reg[rs]);			break;
				case 0x06:	sprintf(buffer, "srlv   %s,%s,%s", reg[rd], reg[rt], reg[rs]);			break;
				case 0x07:	sprintf(buffer, "srav   %s,%s,%s", reg[rd], reg[rt], reg[rs]);			break;
				case 0x08:	sprintf(buffer, "jr     %s", reg[rs]); if (rs == 31) flags = DASMFLAG_STEP_OUT; break;
				case 0x09:	if (rd == 31)
							sprintf(buffer, "jalr   %s", reg[rs]);
							else
							sprintf(buffer, "jalr   %s,%s", reg[rs], reg[rd]); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
				case 0x0a:	sprintf(buffer, "movz   %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x0b:	sprintf(buffer, "movn   %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x0c:	sprintf(buffer, "syscall");	flags = DASMFLAG_STEP_OVER;					break;
				case 0x0d:	sprintf(buffer, "break"); flags = DASMFLAG_STEP_OVER;					break;
				case 0x0f:	sprintf(buffer, "sync"); 												break;
				case 0x10:	sprintf(buffer, "mfhi   %s", reg[rd]);									break;
				case 0x11:	sprintf(buffer, "mthi   %s", reg[rs]);									break;
				case 0x12:	sprintf(buffer, "mflo   %s", reg[rd]);									break;
				case 0x13:	sprintf(buffer, "mtlo   %s", reg[rs]);									break;
				case 0x14:	sprintf(buffer, "dsllv  %s,%s,%s", reg[rd], reg[rt], reg[rs]);			break;
				case 0x16:	sprintf(buffer, "dsrlv  %s,%s,%s", reg[rd], reg[rt], reg[rs]);			break;
				case 0x17:	sprintf(buffer, "dsrav  %s,%s,%s", reg[rd], reg[rt], reg[rs]);			break;
				case 0x18:	sprintf(buffer, "mult   %s,%s", reg[rs], reg[rt]);						break;
				case 0x19:	sprintf(buffer, "multu  %s,%s", reg[rs], reg[rt]);						break;
				case 0x1a:	sprintf(buffer, "div    %s,%s", reg[rs], reg[rt]);						break;
				case 0x1b:	sprintf(buffer, "divu   %s,%s", reg[rs], reg[rt]);						break;
				case 0x1c:	sprintf(buffer, "dmult  %s,%s", reg[rs], reg[rt]);						break;
				case 0x1d:	sprintf(buffer, "dmultu %s,%s", reg[rs], reg[rt]);						break;
				case 0x1e:	sprintf(buffer, "ddiv   %s,%s", reg[rs], reg[rt]);						break;
				case 0x1f:	sprintf(buffer, "ddivu  %s,%s", reg[rs], reg[rt]);						break;
				case 0x20:	sprintf(buffer, "add    %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x21:	sprintf(buffer, "addu   %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x22:	sprintf(buffer, "sub    %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x23:	sprintf(buffer, "subu   %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x24:	sprintf(buffer, "and    %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x25:	sprintf(buffer, "or     %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x26:	sprintf(buffer, "xor    %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x27:	sprintf(buffer, "nor    %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x2a:	sprintf(buffer, "slt    %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x2b:	sprintf(buffer, "sltu   %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x2c:	sprintf(buffer, "dadd   %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x2d:	sprintf(buffer, "daddu  %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x2e:	sprintf(buffer, "dsub   %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x2f:	sprintf(buffer, "dsubu  %s,%s,%s", reg[rd], reg[rs], reg[rt]);			break;
				case 0x30:	sprintf(buffer, "tge    %s,%s", reg[rs], reg[rt]); flags = DASMFLAG_STEP_OVER; break;
				case 0x31:	sprintf(buffer, "tgeu   %s,%s", reg[rs], reg[rt]); flags = DASMFLAG_STEP_OVER; break;
				case 0x32:	sprintf(buffer, "tlt    %s,%s", reg[rs], reg[rt]); flags = DASMFLAG_STEP_OVER; break;
				case 0x33:	sprintf(buffer, "tltu   %s,%s", reg[rs], reg[rt]); flags = DASMFLAG_STEP_OVER; break;
				case 0x34:	sprintf(buffer, "teq    %s,%s", reg[rs], reg[rt]); flags = DASMFLAG_STEP_OVER; break;
				case 0x36:	sprintf(buffer, "tne    %s,%s", reg[rs], reg[rt]) ;flags = DASMFLAG_STEP_OVER; break;
				case 0x38:	sprintf(buffer, "dsll   %s,%s,%d", reg[rd], reg[rt], shift);			break;
				case 0x3a:	sprintf(buffer, "dsrl   %s,%s,%d", reg[rd], reg[rt], shift);			break;
				case 0x3b:	sprintf(buffer, "dsra   %s,%s,%d", reg[rd], reg[rt], shift);			break;
				case 0x3c:	sprintf(buffer, "dsll   %s,%s,%d", reg[rd], reg[rt], shift+32);			break;
				case 0x3e:	sprintf(buffer, "dsrl   %s,%s,%d", reg[rd], reg[rt], shift+32);			break;
				case 0x3f:	sprintf(buffer, "dsra   %s,%s,%d", reg[rd], reg[rt], shift+32);			break;
				default:	sprintf(buffer, "dc.l   $%08x [invalid]", op);							break;
			}
			break;

		case 0x01:	/* REGIMM */
			switch ((op >> 16) & 31)
			{
				case 0x00:	sprintf(buffer, "bltz   %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2));	break;
				case 0x01:	sprintf(buffer, "bgez   %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2));	break;
				case 0x02:	sprintf(buffer, "bltzl  %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2));	break;
				case 0x03:	sprintf(buffer, "bgezl  %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2));	break;
				case 0x08:	sprintf(buffer, "tgei   %s,%s", reg[rs], signed_16bit(op)); flags = DASMFLAG_STEP_OVER; break;
				case 0x09:	sprintf(buffer, "tgeiu  %s,%s", reg[rs], signed_16bit(op)); flags = DASMFLAG_STEP_OVER; break;
				case 0x0a:	sprintf(buffer, "tlti   %s,%s", reg[rs], signed_16bit(op)); flags = DASMFLAG_STEP_OVER; break;
				case 0x0b:	sprintf(buffer, "tltiu  %s,%s", reg[rs], signed_16bit(op)); flags = DASMFLAG_STEP_OVER; break;
				case 0x0c:	sprintf(buffer, "teqi   %s,%s", reg[rs], signed_16bit(op)); flags = DASMFLAG_STEP_OVER; break;
				case 0x0e:	sprintf(buffer, "tnei   %s,%s", reg[rs], signed_16bit(op)); flags = DASMFLAG_STEP_OVER; break;
				case 0x10:	sprintf(buffer, "bltzal %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2)); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
				case 0x11:	sprintf(buffer, "bgezal %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2)); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
				case 0x12:	sprintf(buffer, "bltzall %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2)); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
				case 0x13:	sprintf(buffer, "bgezall %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2)); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
				default:	sprintf(buffer, "dc.l   $%08x [invalid]", op);							break;
			}
			break;

		case 0x02:	sprintf(buffer, "j      $%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2));	break;
		case 0x03:	sprintf(buffer, "jal    $%08x", (pc & 0xf0000000) | ((op & 0x03ffffff) << 2)); flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1); break;
		case 0x04:	if (rs == 0 && rt == 0)
					sprintf(buffer, "b      $%08x", pc + 4 + ((INT16)op << 2));
					else
					sprintf(buffer, "beq    %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((INT16)op << 2));break;
		case 0x05:	sprintf(buffer, "bne    %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((INT16)op << 2));break;
		case 0x06:	sprintf(buffer, "blez   %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2));			break;
		case 0x07:	sprintf(buffer, "bgtz   %s,$%08x", reg[rs], pc + 4 + ((INT16)op << 2));			break;
		case 0x08:	sprintf(buffer, "addi   %s,%s,%s", reg[rt], reg[rs], signed_16bit(op));			break;
		case 0x09:	sprintf(buffer, "addiu  %s,%s,%s", reg[rt], reg[rs], signed_16bit(op));			break;
		case 0x0a:	sprintf(buffer, "slti   %s,%s,%s", reg[rt], reg[rs], signed_16bit(op));			break;
		case 0x0b:	sprintf(buffer, "sltiu  %s,%s,%s", reg[rt], reg[rs], signed_16bit(op));			break;
		case 0x0c:	sprintf(buffer, "andi   %s,%s,$%04x", reg[rt], reg[rs], (UINT16)op);			break;
		case 0x0d:	sprintf(buffer, "ori    %s,%s,$%04x", reg[rt], reg[rs], (UINT16)op);			break;
		case 0x0e:	sprintf(buffer, "xori   %s,%s,$%04x", reg[rt], reg[rs], (UINT16)op);			break;
		case 0x0f:	sprintf(buffer, "lui    %s,$%04x", reg[rt], (UINT16)op);						break;
		case 0x10:	flags = dasm_cop0(pc, op, buffer);												break;
		case 0x11:	flags = dasm_cop1(pc, op, buffer);												break;
		case 0x12:	flags = dasm_cop2(pc, op, buffer);												break;
		case 0x13:	flags = dasm_cop1x(pc, op, buffer);												break;
		case 0x14:	sprintf(buffer, "beql   %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((INT16)op << 2));break;
		case 0x15:	sprintf(buffer, "bnel   %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((INT16)op << 2));break;
		case 0x16:	sprintf(buffer, "blezl  %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((INT16)op << 2));break;
		case 0x17:	sprintf(buffer, "bgtzl  %s,%s,$%08x", reg[rs], reg[rt], pc + 4 + ((INT16)op << 2));break;
		case 0x18:	sprintf(buffer, "daddi  %s,%s,%s", reg[rt], reg[rs], signed_16bit(op));			break;
		case 0x19:	sprintf(buffer, "daddiu %s,%s,%s", reg[rt], reg[rs], signed_16bit(op));			break;
		case 0x1a:	sprintf(buffer, "ldl    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x1b:	sprintf(buffer, "ldr    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x1c:	/* IDT-specific opcodes: mad/madu/mul on R4640/4650, msub on RC32364 */
			switch (op & 0x1f)
			{
				case 0: sprintf(buffer, "mad    %s,%s", reg[rs], reg[rt]); break;
				case 1: sprintf(buffer, "madu   %s,%s", reg[rs], reg[rt]); break;
				case 2: sprintf(buffer, "mul    %s,%s,%s", reg[rs], reg[rt], reg[rd]); break;
				case 4: sprintf(buffer, "msub   %s,%s", reg[rs], reg[rt]); break;
	 			default:sprintf(buffer, "dc.l   $%08x [invalid]", op);	break;
			}
			break;
		case 0x20:	sprintf(buffer, "lb     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x21:	sprintf(buffer, "lh     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x22:	sprintf(buffer, "lwl    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x23:	sprintf(buffer, "lw     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x24:	sprintf(buffer, "lbu    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x25:	sprintf(buffer, "lhu    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x26:	sprintf(buffer, "lwr    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x27:	sprintf(buffer, "lwu    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x28:	sprintf(buffer, "sb     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x29:	sprintf(buffer, "sh     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x2a:	sprintf(buffer, "swl    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x2b:	sprintf(buffer, "sw     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x2c:	sprintf(buffer, "sdl    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x2d:	sprintf(buffer, "sdr    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x2e:	sprintf(buffer, "swr    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x2f:	sprintf(buffer, "cache  %s(%s)", reg[rs], signed_16bit(op));					break;
		case 0x30:	sprintf(buffer, "ll     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x31:	sprintf(buffer, "lwc1   %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]);	break;
		case 0x32:	sprintf(buffer, "lwc2   %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]);	break;
		case 0x33:	sprintf(buffer, "pref   $%x,%s(%s)", rt, signed_16bit(op), reg[rs]);			break;
		case 0x34:	sprintf(buffer, "lld    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x35:	sprintf(buffer, "ldc1   %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]);	break;
		case 0x36:	sprintf(buffer, "ldc2   %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]);	break;
		case 0x37:	sprintf(buffer, "ld     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x38:	sprintf(buffer, "sc     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x39:	sprintf(buffer, "swc1   %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]);	break;
		case 0x3a:	sprintf(buffer, "swc2   %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]);	break;
		case 0x3c:	sprintf(buffer, "scd    %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		case 0x3d:	sprintf(buffer, "sdc1   %s,%s(%s)", cpreg[1][rt], signed_16bit(op), reg[rs]);	break;
		case 0x3e:	sprintf(buffer, "sdc2   %s,%s(%s)", cpreg[2][rt], signed_16bit(op), reg[rs]);	break;
		case 0x3f:	sprintf(buffer, "sd     %s,%s(%s)", reg[rt], signed_16bit(op), reg[rs]);		break;
		default:	sprintf(buffer, "dc.l   $%08x [invalid]", op);									break;
	}
	return 4 | flags | DASMFLAG_SUPPORTED;
}
