#include "emu.h"
#include "debugger.h"
#include "mb86233.h"

static char * COND(unsigned int cond)
{
	static char bufs[4][256];
	static int bufindex = 0;
	char *buf = &bufs[bufindex++][0];

	bufindex &= 3;

    switch(cond)
    {
		case 0x16:
			sprintf(buf,"always");
			return buf;

		case 0x00:
			sprintf(buf,"eq");
			return buf;

		case 0x01:
			sprintf(buf,"ge");
			return buf;

		case 0x02:
			sprintf(buf,"le");
			return buf;

		case 0x06:
			sprintf(buf,"never");
			return buf;

		case 0x10:
			sprintf(buf,"(--r12)!=0");
			return buf;

		case 0x11:
			sprintf(buf,"(--r13)!=0");
			return buf;
    }

    sprintf(buf,"unk (%x)",cond);
	return buf;
}

static char * REGS( UINT32 reg, int IsSource )
{
	static char bufs[4][256];
	static int bufindex = 0;
	char *buf = &bufs[bufindex++][0];
	int			mode = (reg >> 6 ) & 0x07;

	bufindex &= 3;

	reg &= 0x3f;

	if ( mode == 0 || mode == 1 || mode == 3 )
    {
        if ( reg < 0x10 )
		{
			sprintf(buf,"r%d",reg);
    	    return buf;
		}

		switch(reg)
		{
			case 0x10:
				sprintf(buf,"a");
			break;

			case 0x11:
				sprintf(buf,"a.e");
			break;

			case 0x12:
				sprintf(buf,"a.m");
			break;

			case 0x13:
				sprintf(buf,"b");
			break;

			case 0x14:
				sprintf(buf,"b.e");
			break;

			case 0x15:
				sprintf(buf,"b.m");
			break;

			case 0x19:
				sprintf(buf,"d");
			break;

			case 0x1a:
				sprintf(buf,"d.e");
			break;

			case 0x1b:
				sprintf(buf,"d.m");
			break;

			case 0x1c:
				sprintf(buf,"p");
			break;

			case 0x1d:
				sprintf(buf,"p.e");
			break;

			case 0x1e:
				sprintf(buf,"p.m");
			break;

			case 0x1f:
				sprintf(buf,"shift");
			break;

			case 0x20:
				sprintf(buf,"parport");
			break;

			case 0x21:
				sprintf(buf,"FIn");
			break;

			case 0x22:
				sprintf(buf,"FOut");
			break;

			case 0x23:
				sprintf(buf,"EB");
			break;

			default:
				sprintf(buf,"Unkreg (%x)",reg);
			break;
		}
    }
	else if ( mode == 2 )
	{
		char *p = buf;

		p += sprintf(p,"0x%x+",reg & 0x1f);

		if ( IsSource )
		{
			if ( !( reg & 0x20 ) )
				p += sprintf(p,"r0+");

			p += sprintf(p,"r2");
		}
		else
		{
			if ( !( reg & 0x20 ) )
				p += sprintf(p,"r1+");

			p += sprintf(p,"r3");
		}
	}
    else if ( mode == 6 )
	{
		char *p = buf;

		if ( IsSource )
		{
			if ( !( reg & 0x20 ) )
				p += sprintf(p,"r0+");

			p += sprintf(p,"r2");
		}
		else
		{
			if ( !( reg & 0x20 ) )
				p += sprintf(p,"r1+");

			p += sprintf(p,"r3");
		}

		if ( reg & 0x10 )
			p += sprintf(p,"--%d", 0x20 - ( reg & 0x1f ) );
		else
			p += sprintf(p,"++%d", reg & 0x1f );
    }
    else
    {
		sprintf(buf,"UNKMOD %x (0x%x)", mode, reg);
    }

	return buf;
}

static char * INDIRECT( UINT32 reg, int IsSource )
{
    static char bufs[4][256];
	static int bufindex = 0;
	char *buf = &bufs[bufindex++][0];
	int			mode = ( reg >> 6 ) & 0x07;

	bufindex &= 3;

    if ( mode == 0 || mode == 3 || mode == 1)
    {
		sprintf(buf,"0x%x",reg);
	}
	else if ( mode == 2 )
	{
		char *p = buf;

		p += sprintf(p,"0x%x+",reg&0x1f);

		if ( IsSource )
		{
			if ( !(reg & 0x20) )
				p += sprintf(p,"r0+");

			p += sprintf(p,"r2");
		}
		else
		{
			if ( !(reg & 0x20) )
				p += sprintf(p,"r1+");

			p += sprintf(p,"r3");
		}
	}
    else if ( mode == 6 || mode == 7 )
    {
		char *p = buf;

		if ( IsSource )
		{
			if ( !( reg & 0x20 ) )
				p += sprintf(p,"r0+");

			p += sprintf(p,"r2");
		}
		else
		{
			if( !( reg & 0x20 ) )
				p += sprintf(p,"r1+");

			p += sprintf(p,"r3");
		}

		if ( reg & 0x10 )
			p += sprintf(p,"--%d",0x20 - ( reg & 0x1f ));
		else
			p += sprintf(p,"++%d",reg & 0x1f);
    }
    else
    {
		sprintf(buf,"UNKMOD %x (0x%x)", mode, reg);
    }

	return buf;
}


static char * ALU( UINT32 alu)
{
	static char bufs[4][256];
	static int bufindex = 0;
	char *buf = &bufs[bufindex++][0];

	bufindex &= 3;

    switch( alu )
    {
		case 0x0:
			buf[0] = 0;
	    break;

		case 0x1:
			sprintf(buf,"d=d&a");
	    break;

		case 0x2:
			sprintf(buf,"d=d|a");
	    break;

	    case 0x3:
			sprintf(buf,"d=d^a");
	    break;

		case 0x5:
			sprintf(buf,"cmp d,a");
	    break;

		case 0x6:
			sprintf(buf,"d=d+a");
	    break;

		case 0x7:
			sprintf(buf,"d=d-a");
	    break;

		case 0x8:
			sprintf(buf,"p=a*b");
	    break;

		case 0x9:
			sprintf(buf,"d=d+p, p=a*b");
	    break;

		case 0xa:
			sprintf(buf,"d=d-p, p=a*b");
	    break;

		case 0xb:
			sprintf(buf,"d=fabs d");
	    break;

		case 0xc:
			sprintf(buf,"d=d+p");
	    break;

		case 0xd:
			sprintf(buf,"d=p, p=a*b");
	    break;

		case 0xe:
			sprintf(buf,"d=float(d)");
	    break;

		case 0xf:
			sprintf(buf,"d=int(d)");
	    break;

		case 0x10:
			sprintf(buf,"d=d/a");
	    break;

		case 0x11:
			sprintf(buf,"d=-d");
	    break;

	    case 0x13:
			sprintf(buf,"d=a+b");
	    break;

		case 0x14:
			sprintf(buf,"d=b-a");
	    break;

		case 0x16:
			sprintf(buf,"d=(lsr d,shift)");
	    break;

		case 0x17:
			sprintf(buf,"d=(lsl d,shift)");
	    break;

		case 0x18:
			sprintf(buf,"d=(asr d,shift)");
	    break;

		case 0x1a:
			sprintf(buf,"d=d+a (int)");
	    break;

		case 0x1b:
			sprintf(buf,"d=d-a (int)");
	    break;

		default:
			sprintf(buf,"ALU UNK(%x)",alu);
	    break;
    }

	return buf;
}

static unsigned dasm_mb86233(char *buffer, UINT32 opcode )
{
	char *p = buffer;
	UINT32	grp = ( opcode >> 26 ) & 0x3f;

	switch( grp )
	{
		case 0x0:	/* Dual move */
		{
			UINT32 r1=opcode & 0x1ff;
			UINT32 r2=(opcode>>9) & 0x7f;
			UINT32 alu=(opcode>>21) & 0x1f;
			UINT32 op=(opcode>>16) & 0x1f;

			if ( alu != 0 )
				p += sprintf(p, "%s, ", ALU(alu) );

			switch( op )
			{
				case 0x0c:	/* a = RAM[addr], b = BRAM[addr] */
					p += sprintf(p,"LAB RAM(0x%x)->a,BRAM(0x%x)->b",r1,r2);
				break;

				case 0x0d:	/* a = RAM[addr], b = BRAM[addr] */
					p += sprintf(p,"LAB RAM(0x%x)->a,BRAM(%s)->b",r1,INDIRECT(r2|(2<<6),0));
				break;

				case 0x0f:	/* a = RAM[addr], b = BRAM[reg] */
					p += sprintf(p,"LAB RAM(0x%x)->a,BRAM(%s)->b",r1,INDIRECT(r2|(6<<6),0));
				break;

				case 0x10:	/* a = BRAM[reg], b = RAM[addr] */
					p += sprintf(p,"LAB BRAM(%s)->a,RAM(0x%x)->b",INDIRECT(r1,1),r2);
				break;

				default:
					p += sprintf(p,"UNKDUAL (%x)",op);
				break;
			}
		}
		break;

	    case 0x7:	/* LD/MOV */
		{
		    UINT32 r1=opcode & 0x1ff;
			UINT32 r2=(opcode>>9) & 0x7f;
			UINT32 alu=(opcode>>21) & 0x1f;
			UINT32 op=(opcode>>16) & 0x1f;

			if ( alu != 0 )
			{
				p += sprintf(p, "%s", ALU(alu) );

				if ( !(op == 0x1f && r1 == 0x10 && r2 == 0x0f) )
					p += sprintf(p, ", ");
			}

		    switch(op)
		    {
				case 0x03:	/* RAM->External Indirect */
				{
					p += sprintf(p,"MOV RAM(0x%x)->E(EB+%s)",r1,INDIRECT(r2|(6<<6),0));
				}
				break;

				case 0x04:	/* MOV RAM->External */
				{
					p += sprintf(p,"MOV RAM(0x%x)->E(EB+0x%x)",r1,r2);
				}
				break;

				case 0x07:	/* RAMInd->External */
				{
					p += sprintf(p,"MOV RAM(%s)->E(EB+%s)",INDIRECT(r1,1),INDIRECT(r2|(6<<6),0));
				}
			    break;

				case 0x08:	/* External->RAM */
				{
					p += sprintf(p,"MOV EXT(EB+");
					p += sprintf(p,"%s",INDIRECT(r1,1));
				    p += sprintf(p,")->RAM(0x%x)",r2);
				}
			    break;

				case 0x0b:	/* External->RAMInd */
				{
					int mode = ( r1 >> 6 ) & 0x07;

					p += sprintf(p,"MOV EXT(EB+");

					if ( mode == 0 || mode == 3 || mode == 1 )
						p += sprintf(p,"RAM(");

					p += sprintf(p,"%s",INDIRECT(r1,1));

					if ( mode == 0 || mode == 3 || mode == 1)
						p += sprintf(p,")");

				    p += sprintf(p,")->RAM(%s)",INDIRECT(r2|(6<<6),0));
				}
			    break;

				case 0x0c:	/* MOV RAM->BRAM */
					p += sprintf(p,"MOV RAM(0x%x)->BRAM(0x%x)",r1,r2);
				break;

				case 0x0f:	/* MOV RAMInd->BRAMInd */
					p += sprintf(p,"MOV RAM(%s)->BRAM(%s)",INDIRECT(r1,1),INDIRECT(r2|(6<<6),0));
				break;

				case 0x10:	/* MOV BRAMInd->RAM */
					p += sprintf(p,"MOV BRAM(%s)->RAM(0x%x)",INDIRECT(r1,1),r2);
				break;

				case 0x13:	/* MOV BRAMInd->RAMInd */
					p += sprintf(p,"MOV BRAM(%s)->RAM(%s)",INDIRECT(r1,1),INDIRECT(r2|(6<<6),0));
				break;

				case 0x1c:  /* MOV Reg->RAMInd */
					if ( ( r2 >> 6 ) & 0x01)
					{
						p += sprintf(p,"MOV %s->EXT(EB+%s)",REGS(r2,1),INDIRECT(r1,0));
					}
					else
					{
						p += sprintf(p,"MOV %s->RAM(%s)",REGS(r2,1),INDIRECT(r1,0));
					}
			    break;

				case 0x1d:	/* MOV RAM->Reg */
				{
					if ( r1 & 0x180 )
					{
						p += sprintf(p,"MOV RAM(%s)->%s",REGS(r1,0),REGS(r2,0));
					}
					else
					{
						p += sprintf(p,"MOV RAM(0x%x)->%s",r1,REGS(r2,0));
					}
				}
			    break;

				case 0x1e:	/* External->Reg */
				{
					int		mode2 = (r2 >> 6) & 1;
					p += sprintf(p,"MOV EXT(EB+%s)->%s",INDIRECT(r1,mode2),REGS(r2,0));
				}
			    break;

				case 0x1f:	/* MOV Reg->Reg */
					if ( !(r1 == 0x10 && r2 == 0x0f) )
					{
						p += sprintf(p,"MOV %s->%s",REGS(r1,1),REGS(r2,0));
					}
				break;

				default:
					p += sprintf(p,"UNKMV (0x%x)",op);
			    break;
		    }
		}
		break;

	    case 0x0e:	/* Load 24 bit val */
		{
			UINT32 sub=(opcode>>24)&0x3;
			static const char regs[4] = { 'p', 'a', 'b', 'd' };

			p += sprintf(p,"LDIMM24 0x%X->%c",opcode&0xffffff, regs[sub]);
		}
		break;

		case 0x0f:	/* repeat */
		{
			UINT32	alu = ( opcode >> 20 ) & 0x1f;
			UINT32	sub2 = ( opcode >> 16 ) & 0x0f;

			if ( alu != 0 )
				p += sprintf(p, "%s, ", ALU(alu) );

			if ( sub2 == 0x00 )
			{
				p += sprintf(p,"CLEAR ");

				switch( opcode & 0x3f )
				{
					case 0x04: p += sprintf(p, "a" ); break;
					case 0x08: p += sprintf(p, "b" ); break;
					case 0x10: p += sprintf(p, "d" ); break;
					default: p += sprintf(p, "UNKNOWN REG(%x)",opcode&0x3F); break;
				}
			}
			else if ( sub2 == 0x02 )
				p += sprintf(p,"CLRFLAG 0x%x",opcode&0xffff);
			else if ( sub2==0x4 )
			{
				if ( (opcode & 0xfff) == 0 )
					p += sprintf(p,"REP 0x100");
				else
					p += sprintf(p,"REP 0x%x",opcode&0xff);
			}
			else if ( sub2 == 0x06 )
				p += sprintf(p,"SETFLAG 0x%x",opcode&0xffff);
		}
		break;

	    case 0x10:
		{
		    UINT32	dst=(opcode>>24)&0xf;
		    UINT32	imm=(opcode)&0xFFFF;

		    if ( dst <= 3 )
				p += sprintf(p,"LDIMM 0x%x->r%d",imm,dst);
		    else
				p += sprintf(p,"LDIMM 0x%x->UNKDST(0x%x)",imm,dst);
		}
		break;

		case 0x13:
		{
			UINT32 sub = ( opcode >> 24 ) & 0x03;

		    p += sprintf(p,"LDIMM 0x%X->",opcode&0xffffff);

			if ( sub == 0 ) p += sprintf(p,"r12");
			else if ( sub == 1 ) p += sprintf(p,"r13");
			else p += sprintf(p,"UNKREG(%x)", sub);
		}
		break;

		case 0x14:
		{
			UINT32 sub = ( opcode >> 24 ) & 0x03;

			p += sprintf(p,"LDIMM 0x%X->",opcode&0xffffff);

			if ( sub == 0 ) p += sprintf(p,"a.exp");
			else if ( sub == 1 ) p += sprintf(p,"a.e");
			else if ( sub == 2 ) p += sprintf(p,"a.m");
			else p += sprintf(p,"UNKREG(%x)", sub);
		}
		break;

		case 0x15:
		{
			UINT32 sub = ( opcode >> 24 ) & 0x03;

		    p += sprintf(p,"LDIMM 0x%X->",opcode&0xffffff);

			if ( sub == 0 ) p += sprintf(p,"b.exp");
			else if ( sub == 1 ) p += sprintf(p,"b.e");
			else if ( sub == 2 ) p += sprintf(p,"b.m");
			else p += sprintf(p,"UNKREG(%x)", sub);
		}
		break;

		case 0x16:
		{
		    UINT32 sub = ( opcode >> 24 ) & 0x03;

			p += sprintf(p,"LDIMM 0x%X->",opcode&0xffffff);

			if ( sub == 2 ) p += sprintf(p,"d.e");
			else if ( sub == 3 ) p += sprintf(p,"d.m");
			else p += sprintf(p,"UNKREG(%x)", sub);
		}
		break;

		case 0x17:
		{
			UINT32 sub = ( opcode >> 24 ) & 0x03;

		    p += sprintf(p,"LDIMM 0x%X->",opcode&0xffffff);

			if ( sub == 0x03 ) p += sprintf(p,"shift");
			else p += sprintf(p,"UNKREG(%x)", sub);
		}
		break;

		case 0x18:
		{
			UINT32 sub = ( opcode >> 24 ) & 0x03;

			p += sprintf(p,"LDIMM24 0x%X->",opcode&0xffffff);

			if ( sub == 0x03 ) p += sprintf(p,"EB");
			else p += sprintf(p,"UNKREG(%x)", sub);
		}
		break;

	    case 0x2f:
		{
		    UINT32 cond = ( opcode >> 20 ) & 0x1f;
		    UINT32 subtype = ( opcode >> 16 ) & 0x0f;
		    UINT32 data = opcode & 0xffff;

		    switch( subtype )
		    {
				case 0:
					p += sprintf(p,"BRIF %s 0x%X", COND(cond), data);
			    break;

				case 2:
					p += sprintf(p,"BRIF %s ", COND(cond));
					if ( data & 0x4000 )
						p += sprintf(p,"%s",REGS(data&0x3f,0));
					else
						p += sprintf(p,"RAM(0x%x)",data);
			    break;

				case 4:
					p += sprintf(p,"BSIF %s 0x%X", COND(cond), data);
			    break;

				case 0x6:
					p += sprintf(p,"BSIF %s ", COND(cond));
					if ( data & 0x4000 )
						p += sprintf(p,"%s",REGS(data&0x3f,0));
					else
						p += sprintf(p,"RAM(0x%x)",data);
				break;

				case 0xa:
					p += sprintf(p,"RTIF %s", COND(cond));
			    break;

				case 0xc:
					p += sprintf(p,"LDIF %s RAM(0x%x)->%s", COND(cond),data&0x1ff,REGS((data>>9)&0x3f,0));
				break;

				case 0xe:
					p += sprintf(p,"RIIF %s", COND(cond));
			    break;

				default:
					p += sprintf(p,"UNKG5 (%x cond %x)",subtype,cond);
			    break;
		    }
		}
		break;

		case 0x3f:
		{
		    UINT32 cond = ( opcode >> 20 ) & 0x1f;
		    UINT32 subtype = ( opcode >> 16 ) & 0x0f;
		    UINT32 data = opcode & 0xffff;

		    switch( subtype )
		    {
				case 0:
					p += sprintf(p,"BRUL %s 0x%X", COND(cond), data);
			    break;

				case 2:
					p += sprintf(p,"BRUL %s ", COND(cond));
					if ( data & 0x4000 )
						p += sprintf(p,"%s",REGS(data&0x3f,0));
					else
						p += sprintf(p,"RAM(0x%x)",data);
			    break;

				case 4:
					p += sprintf(p,"BSUL %s 0x%X", COND(cond), data);
			    break;

				case 0x6:
					p += sprintf(p,"BSUL %s ", COND(cond));
					if ( data & 0x4000 )
						p += sprintf(p,"%s",REGS(data&0x3f,0));
					else
						p += sprintf(p,"RAM(0x%x)",data);
				break;

				case 0xa:
					p += sprintf(p,"RTUL %s", COND(cond));
			    break;

				case 0xc:
					p += sprintf(p,"LDUL %s RAM(0x%x)->%s", COND(cond),data&0x1ff,REGS((data>>9)&0x3f,0));
				break;

				case 0xe:
					p += sprintf(p,"RIUL %s", COND(cond));
			    break;

				default:
					p += sprintf(p,"UNKG5 (%x cond %x)",subtype,cond);
			    break;
		    }
		}
		break;

		default:
			p += sprintf(p,"UNKOP");
		break;
	}

	return (1 | DASMFLAG_SUPPORTED);
}

CPU_DISASSEMBLE( mb86233 )
{
	UINT32 op = *(UINT32 *)oprom;
	op = LITTLE_ENDIANIZE_INT32(op);
	return dasm_mb86233(buffer, op);
}
