// license:?
// copyright-holders:Angelo Salese, Tomasz Slanina
/******************************************************************
 NEC V810 (upd70732) core
  Tomasz Slanina - analog[at]op.pl
  Angelo Salese - lordkale[at]libero.it

 Change Log
 - 23/08/2012 - Implemented remaining BSU opcodes (Angelo Salese)
 - 21/08/2012 - Fixed SET.F behaviour (Angelo Salese)
 - 20/08/2012 - Fixed a sign bug with CVT.WS opcode (Angelo Salese)
 - 16/08/2012 - Added XB, XH, MPYHW, MOVBSU, ORBSU and ANDNBSU opcodes
                (Angelo Salese)
 - 19/11/2010 - Fixed interrupt handing and flag position in PSW register
                (Miodrag Milanovic)
 - 18/11/2010 - Added bare bones irq support (Miodrag Milanovic)
 - 20/07/2004 - first public release


 TODO:
    - CY flag in few floating point opcodes
        (all floating point opcodes are NOT tested!)
  - traps/interrupts/exceptions
  - bitstring opcodes currently makes the emulation to drop to 0%
  - timing
  - missing opcodes : trap, caxi

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

#include "emu.h"
#include "debugger.h"
#include "v810.h"

#define clkIF 3
#define clkMEM 3


const device_type V810 = &device_creator<v810_device>;


v810_device::v810_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
	: cpu_device(mconfig, V810, "V810", tag, owner, clock, "v810", __FILE__)
	, m_program_config("program", ENDIANNESS_LITTLE, 32, 32, 0)
	, m_io_config("io", ENDIANNESS_LITTLE, 32, 32, 0)
{
}


offs_t v810_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
{
	extern CPU_DISASSEMBLE( v810 );
	return CPU_DISASSEMBLE_NAME(v810)(this, buffer, pc, oprom, opram, options);
}


#define R0 m_reg[0]
#define R1 m_reg[1]
#define R2 m_reg[2]
#define SP m_reg[3]
#define R4 m_reg[4]
#define R5 m_reg[5]
#define R6 m_reg[6]
#define R7 m_reg[7]
#define R8 m_reg[8]
#define R9 m_reg[9]
#define R10 m_reg[10]
#define R11 m_reg[11]
#define R12 m_reg[12]
#define R13 m_reg[13]
#define R14 m_reg[14]
#define R15 m_reg[15]
#define R16 m_reg[16]
#define R17 m_reg[17]
#define R18 m_reg[18]
#define R19 m_reg[19]
#define R20 m_reg[20]
#define R21 m_reg[21]
#define R22 m_reg[22]
#define R23 m_reg[23]
#define R24 m_reg[24]
#define R25 m_reg[25]
#define R26 m_reg[26]
#define R27 m_reg[27]
#define R28 m_reg[28]
#define R29 m_reg[29]
#define R30 m_reg[30]
#define R31 m_reg[31]

#define EIPC    m_reg[32]
#define EIPSW   m_reg[33]
#define FEPC    m_reg[34]
#define FEPSW   m_reg[35]
#define ECR     m_reg[36]
#define PSW     m_reg[37]
#define PIR     m_reg[38]
#define TKCW    m_reg[39]
#define CHCW    m_reg[56]
#define ADTRE   m_reg[57]

#define PC      m_reg[64]

/* Flags */
#define GET_Z                   ( PSW & 0x00000001)
#define GET_S                   ((PSW & 0x00000002)>>1)
#define GET_OV                  ((PSW & 0x00000004)>>2)
#define GET_CY                  ((PSW & 0x00000008)>>3)
#define GET_ID                  ((PSW & 0x00001000)>>12)
#define GET_AE                  ((PSW & 0x00002000)>>13)
#define GET_EP                  ((PSW & 0x00004000)>>14)
#define GET_NP                  ((PSW & 0x00008000)>>15)

#define SET_Z(val)              (PSW = (PSW & ~0x00000001) | (val))
#define SET_S(val)              (PSW = (PSW & ~0x00000002) | ((val) << 1))
#define SET_OV(val)             (PSW = (PSW & ~0x00000004) | ((val) << 2))
#define SET_CY(val)             (PSW = (PSW & ~0x00000008) | ((val) << 3))
#define SET_ID(val)             (PSW = (PSW & ~0x00001000) | ((val) << 12))
#define SET_AE(val)             (PSW = (PSW & ~0x00002000) | ((val) << 13))
#define SET_EP(val)             (PSW = (PSW & ~0x00004000) | ((val) << 14))
#define SET_NP(val)             (PSW = (PSW & ~0x00008000) | ((val) << 15))

#define R_B(addr) (m_program->read_byte(addr))
#define R_H(addr) (m_program->read_word(addr))
#define R_W(addr) (m_program->read_dword(addr))


#define W_B(addr, val) (m_program->write_byte(addr,val))
#define W_H(addr, val) (m_program->write_word(addr,val))
#define W_W(addr, val) (m_program->write_dword(addr,val))


#define RIO_B(addr) (m_io->read_byte(addr))
#define RIO_H(addr) (m_io->read_word(addr))
#define RIO_W(addr) (m_io->read_dword(addr))


#define WIO_B(addr, val) (m_io->write_byte(addr,val))
#define WIO_H(addr, val) (m_io->write_word(addr,val))
#define WIO_W(addr, val) (m_io->write_dword(addr,val))

#define R_OP(addr)  (m_direct->read_decrypted_word(addr))

#define GET1 (op&0x1f)
#define GET2 ((op>>5)&0x1f)
#define I5(x) (((x)&0x1f)|(((x)&0x10)?0xffffffe0:0))
#define UI5(x) ((x)&0x1f)
#define I16(x) (((x)&0xffff)|(((x)&0x8000)?0xffff0000:0))
#define UI16(x) ((x)&0xffff)
#define D16(x) (((x)&0xffff)|(((x)&0x8000)?0xffff0000:0))
#define D26(x,y) ((y)|((x&0x3ff)<<16 )|((x&0x200)?0xfc000000:0))
#define D9(x) ((x&0x1ff)|((x&0x100)?0xfffffe00:0))
#define SO(opcode) (((opcode)&0xfc00)>>10)

#define CHECK_CY(x) PSW=(PSW & ~8)|(((x) & (((UINT64)1) << 32)) ? 8 : 0)
#define CHECK_OVADD(x,y,z)  PSW=(PSW & ~0x00000004) |(( ((x) ^ (z)) & ((y) ^ (z)) & 0x80000000) ? 4: 0)
#define CHECK_OVSUB(x,y,z)  PSW=(PSW & ~0x00000004) |(( ((y) ^ (z)) & ((x) ^ (y)) & 0x80000000) ? 4: 0)
#define CHECK_ZS(x) PSW=(PSW & ~3)|((UINT32)(x)==0)|(((x)&0x80000000) ? 2: 0)


#define ADD(dst, src)       { UINT64 res=(UINT64)(dst)+(UINT64)(src); SetCF(res); SetOF_Add(res,src,dst); SetSZPF(res); dst=(UINT32)res; }
#define SUB(dst, src)       { UINT64 res=(UINT64)(dst)-(INT64)(src); SetCF(res); SetOF_Sub(res,src,dst); SetSZPF(res); dst=(UINT32)res; }




void v810_device::SETREG(UINT32 reg,UINT32 val)
{
	if(reg)
		m_reg[reg]=val;
}

UINT32 v810_device::GETREG(UINT32 reg)
{
	if(reg)
		return m_reg[reg];
	else
		return 0;
}

UINT32 v810_device::opUNDEF(UINT32 op)
{
	logerror("V810: Unknown opcode %x @ %x",op,PC-2);
	return clkIF;
}

UINT32 v810_device::opMOVr(UINT32 op) // mov reg1, reg2
{
	SETREG(GET2,GETREG(GET1));
	return clkIF;
}

UINT32 v810_device::opMOVEA(UINT32 op)   // movea imm16, reg1, reg2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=R_OP(PC);
	PC+=2;
	op2=I16(op2);
	SETREG(GET2,op1+op2);
	return clkIF;
}

UINT32 v810_device::opMOVHI(UINT32 op)   // movhi imm16, reg1 ,reg2
{
	UINT32 op2=R_OP(PC);
	PC+=2;
	op2=UI16(op2);
	op2<<=16;
	SETREG(GET2,GETREG(GET1)+op2);
	return clkIF;
}

UINT32 v810_device::opMOVi(UINT32 op)    // mov imm5,r2
{
	SETREG(GET2,I5(op));
	return clkIF;
}

UINT32 v810_device::opADDr(UINT32 op)    // add r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	UINT64 res=(UINT64)op2+(UINT64)op1;
	CHECK_CY(res);
	CHECK_OVADD(op1,op2,res);
	CHECK_ZS(res);
	SETREG(GET2,res);
	return clkIF;
}

UINT32 v810_device::opADDi(UINT32 op)    // add imm5,r2
{
	UINT32 op1=I5(op);
	UINT32 op2=GETREG(GET2);
	UINT64 res=(UINT64)op2+(UINT64)op1;
	CHECK_CY(res);
	CHECK_OVADD(op1,op2,res);
	CHECK_ZS(res);
	SETREG(GET2,res);
	return clkIF;
}


UINT32 v810_device::opADDI(UINT32 op)    // addi imm16, reg1, reg2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=R_OP(PC);
	UINT64 res;
	PC+=2;
	op2=I16(op2);
	res=(UINT64)op2+(UINT64)op1;
	CHECK_CY(res);
	CHECK_OVADD(op1,op2,res);
	CHECK_ZS(res);
	SETREG(GET2,res);
	return clkIF;
}

UINT32 v810_device::opSUBr(UINT32 op)    // sub r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	UINT64 res=(UINT64)op2-(UINT64)op1;
	CHECK_CY(res);
	CHECK_OVSUB(op1,op2,res);
	CHECK_ZS(res);
	SETREG(GET2,res);
	return clkIF;
}


UINT32 v810_device::opCMPr(UINT32 op)    // cmp r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	UINT64 res=(UINT64)op2-(UINT64)op1;
	CHECK_CY(res);
	CHECK_OVSUB(op1,op2,res);
	CHECK_ZS(res);
	return clkIF;
}

UINT32 v810_device::opCMPi(UINT32 op)    // cmpi imm5,r2
{
	UINT32 op1=I5(op);
	UINT32 op2=GETREG(GET2);
	UINT64 res=(UINT64)op2-(UINT64)op1;
	CHECK_CY(res);
	CHECK_OVSUB(op1,op2,res);
	CHECK_ZS(res);
	return clkIF;
}

UINT32 v810_device::opSETFi(UINT32 op)   // setf imm5,r2
{
	UINT32 op1=I5(op);
	UINT8 res=0;
	op1&=0xf;
	switch(op1)
	{
		case 0: //bv
			res=GET_OV;
			break;

		case 1: //bl
			res=GET_CY;
			break;

		case 2: //be
			res=GET_Z;
			break;

		case 3: //bnh
			res=GET_Z||GET_CY;
			break;

		case 4: //bn
			res=GET_S;
			break;

		case 5: //br
			res=1;
			break;

		case 6: //blt
			res=GET_S^GET_OV;
			break;

		case 7: //ble
			res=GET_Z||(GET_S^GET_OV);
		break;

		case 8: //bnv
			res=!GET_OV;
		break;

		case 9: //bnl
			res=!GET_CY;
		break;

		case 10: //bne
			res=!GET_Z;
		break;

		case 11: //bh
			res=!(GET_Z||GET_CY);
		break;

		case 12: //bp
			res=!GET_S;
		break;

		case 13: //nop

			break;

		case 14: //bge
			res=!(GET_OV^GET_S);
		break;

		case 15: //bgt
			res=!(GET_Z||(GET_OV^GET_S));
			break;
	}
	SETREG(GET2,res);
	return clkIF;
}


UINT32 v810_device::opANDr(UINT32 op)    // and r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	op2&=op1;
	CHECK_ZS(op2);
	SET_OV(0);
	SETREG(GET2,op2);
	return clkIF;
}

UINT32 v810_device::opANDI(UINT32 op)    // andi imm16,r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=R_OP(PC);
	PC+=2;
	op2=UI16(op2);
	op2&=op1;
	CHECK_ZS(op2);
	SET_OV(0);
	SET_S(0);
	SETREG(GET2,op2);
	return clkIF;
}

UINT32 v810_device::opORr(UINT32 op) // or r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	op2|=op1;
	CHECK_ZS(op2);
	SET_OV(0);
	SETREG(GET2,op2);
	return clkIF;
}

UINT32 v810_device::opORI(UINT32 op) // ori imm16,r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=R_OP(PC);
	PC+=2;
	op2=UI16(op2);
	op2|=op1;
	CHECK_ZS(op2);
	SET_OV(0);
	SET_S(0);
	SETREG(GET2,op2);
	return clkIF;
}

UINT32 v810_device::opXORr(UINT32 op)    // xor r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	op2^=op1;
	CHECK_ZS(op2);
	SET_OV(0);
	SETREG(GET2,op2);
	return clkIF;
}


UINT32 v810_device::opLDSR(UINT32 op) // ldsr reg2,regID
{
	UINT32 op1=UI5(op);
	SETREG(32+op1,GETREG(GET2));
	return clkIF;
}

UINT32 v810_device::opSTSR(UINT32 op) // ldsr regID,reg2
{
	UINT32 op1=UI5(op);
	SETREG(GET2,GETREG(32+op1));
	return clkIF;
}


UINT32 v810_device::opXORI(UINT32 op)    // xori imm16,r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=R_OP(PC);
	PC+=2;
	op2=UI16(op2);
	op2^=op1;
	CHECK_ZS(op2);
	SET_OV(0);
	SET_S(0);
	SETREG(GET2,op2);
	return clkIF;
}

UINT32 v810_device::opNOTr(UINT32 op)    // not r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=~op1;
	CHECK_ZS(op2);
	SET_OV(0);
	SETREG(GET2,op2);
	return clkIF;
}

UINT32 v810_device::opSHLr(UINT32 op)    // shl r1,r2
{
	UINT64 tmp;
	UINT32 count=GETREG(GET1);
	count&=0x1f;

	SET_OV(0);
	SET_CY(0);

	if(count)
	{
		tmp=GETREG(GET2);
		tmp<<=count;
		CHECK_CY(tmp);
		SETREG(GET2,tmp&0xffffffff);
		CHECK_ZS(GETREG(GET2));
	}
	return clkIF;
}

UINT32 v810_device::opSHLi(UINT32 op)    // shl imm5,r2
{
	UINT64 tmp;
	UINT32 count=UI5(op);

	SET_OV(0);
	SET_CY(0);

	if(count)
	{
		tmp=GETREG(GET2);
		tmp<<=count;
		CHECK_CY(tmp);
		SETREG(GET2,tmp&0xffffffff);
	}
	CHECK_ZS(GETREG(GET2));
	return clkIF;
}

UINT32 v810_device::opSHRr(UINT32 op)    // shr r1,r2
{
	UINT64 tmp;
	UINT32 count=GETREG(GET1);
	count&=0x1f;
	SET_OV(0);
	SET_CY(0);
	if(count)
	{
		tmp=GETREG(GET2);
		tmp>>=count-1;
		SET_CY(tmp&1);
		SETREG(GET2,(tmp>>1)&0xffffffff);
	}
	CHECK_ZS(GETREG(GET2));
	return clkIF;
}

UINT32 v810_device::opSHRi(UINT32 op)    // shr imm5,r2
{
	UINT64 tmp;
	UINT32 count=UI5(op);
	SET_OV(0);
	SET_CY(0);
	if(count)
	{
		tmp=GETREG(GET2);
		tmp>>=count-1;
		SET_CY(tmp&1);
		tmp>>=1;
		SETREG(GET2,tmp&0xffffffff);
	}
	CHECK_ZS(GETREG(GET2));
	return clkIF;
}

UINT32 v810_device::opSARr(UINT32 op)    // sar r1,r2
{
	INT32 tmp;
	UINT32 count=GETREG(GET1);
	count&=0x1f;
	SET_OV(0);
	SET_CY(0);
	if(count)
	{
		tmp=GETREG(GET2);
		tmp>>=count-1;
		SET_CY(tmp&1);
		tmp>>=1;
		SETREG(GET2,tmp);
	}
	CHECK_ZS(GETREG(GET2));
	return clkIF;
}

UINT32 v810_device::opSARi(UINT32 op)    // sar imm5,r2
{
	INT32 tmp;
	UINT32 count=UI5(op);
	SET_OV(0);
	SET_CY(0);
	if(count)
	{
		tmp=GETREG(GET2);
		tmp>>=count-1;
		SET_CY(tmp&1);
		tmp>>=1;
		SETREG(GET2,tmp);
	}
	CHECK_ZS(GETREG(GET2));
	return clkIF;
}

UINT32 v810_device::opJMPr(UINT32 op)
{
	PC=GETREG(GET1)&~1;
	return clkIF+2;
}


UINT32 v810_device::opJR(UINT32 op)
{
	UINT32 tmp=R_OP(PC);
	PC=PC-2+(D26(op,tmp)&~1);
	return clkIF+2;
}

UINT32 v810_device::opJAL(UINT32 op)
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	R31=PC;
	PC+=D26(op,tmp);
	PC-=4;
	PC&=~1;
	return clkIF+2;
}


UINT32 v810_device::opEI(UINT32 op)
{
	SET_ID(0);
	return clkIF;
}

UINT32 v810_device::opDI(UINT32 op)
{
	SET_ID(1);
	return clkIF;
}

UINT32 v810_device::opTRAP(UINT32 op)
{
	printf("V810: TRAP @ %X\n",PC-2);
	return clkIF;
}

UINT32 v810_device::opRETI(UINT32 op)
{
	if(GET_NP) {
		PC = FEPC;
		PSW = FEPSW;
	} else {
		PC = EIPC;
		PSW = EIPSW;
	}
	return clkIF;
}

UINT32 v810_device::opHALT(UINT32 op)
{
	printf("V810: HALT @ %X",PC-2);
	return clkIF;
}

UINT32 v810_device::opB(UINT32 op)
{
	int doBranch=0;
	switch((op>>9)&0xf)
	{
		case 0: //bv
			doBranch=GET_OV;
		break;

		case 1: //bl
			doBranch=GET_CY;
		break;

		case 2: //be
			doBranch=GET_Z;
		break;

		case 3: //bnh
			doBranch=GET_Z||GET_CY;
		break;

		case 4: //bn
			doBranch=GET_S;
		break;

		case 5: //br
			doBranch=1;
		break;

		case 6: //blt
			doBranch=GET_S^GET_OV;
		break;

		case 7: //ble
			doBranch=GET_Z||(GET_S^GET_OV);
		break;

		case 8: //bnv
			doBranch=!GET_OV;
		break;

		case 9: //bnl
			doBranch=!GET_CY;
		break;

		case 10: //bne
			doBranch=!GET_Z;
		break;

		case 11: //bh
			doBranch=!(GET_Z||GET_CY);
		break;

		case 12: //bp
			doBranch=!GET_S;
		break;

		case 13: //nop

		break;

		case 14: //bge
			doBranch=!(GET_OV^GET_S);
		break;

		case 15: //bgt
			doBranch=!(GET_Z||(GET_OV^GET_S));
		break;
	}
	if(doBranch)
	{
			PC=PC-2+(D9(op)&~1);
	}
	return clkIF;
}

UINT32 v810_device::opLDB(UINT32 op) // ld.b disp16[reg1],reg2
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	tmp=R_B(tmp);
	tmp|=(tmp&0x80)?0xffffff00:0;
	SETREG(GET2,tmp);
	return clkIF+clkMEM;
}

UINT32 v810_device::opLDH(UINT32 op) // ld.h disp16[reg1],reg2
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	tmp=R_H(tmp&~1);
	tmp|=(tmp&0x8000)?0xffff0000:0;
	SETREG(GET2,tmp);
	return clkIF+clkMEM;
}

UINT32 v810_device::opLDW(UINT32 op) // ld.w disp16[reg1],reg2
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	tmp=R_W(tmp&~3);
	SETREG(GET2,tmp);
	return clkIF+clkMEM;
}

UINT32 v810_device::opINB(UINT32 op) // in.b disp16[reg1],reg2
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	tmp=RIO_B(tmp);
	SETREG(GET2,tmp);
	return clkIF+clkMEM;
}

UINT32 v810_device::opCAXI(UINT32 op)    // caxi disp16[reg1],reg2
{
	printf("V810 CAXI execute\n");
	PC+=2;
	return clkIF;
}

UINT32 v810_device::opINH(UINT32 op) // in.h disp16[reg1],reg2
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	tmp=RIO_H(tmp&~1);
	SETREG(GET2,tmp);
	return clkIF+clkMEM;
}

UINT32 v810_device::opINW(UINT32 op) // in.w disp16[reg1],reg2
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	tmp=RIO_W(tmp&~3);
	SETREG(GET2,tmp);
	return clkIF+clkMEM;
}

UINT32 v810_device::opSTB(UINT32 op) // st.b reg2, disp16[reg1]
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	W_B(tmp,GETREG(GET2)&0xff);
	return clkIF+clkMEM;
}

UINT32 v810_device::opSTH(UINT32 op) // st.h reg2, disp16[reg1]
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	W_H(tmp&~1,GETREG(GET2)&0xffff);
	return clkIF+clkMEM;
}

UINT32 v810_device::opSTW(UINT32 op) // st.w reg2, disp16[reg1]
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	W_W(tmp&~3,GETREG(GET2));
	return clkIF+clkMEM;
}

UINT32 v810_device::opOUTB(UINT32 op)    // out.b reg2, disp16[reg1]
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	WIO_B(tmp,GETREG(GET2)&0xff);
	return clkIF+clkMEM;
}

UINT32 v810_device::opOUTH(UINT32 op)    // out.h reg2, disp16[reg1]
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	WIO_H(tmp&~1,GETREG(GET2)&0xffff);
	return clkIF+clkMEM;
}

UINT32 v810_device::opOUTW(UINT32 op)    // out.w reg2, disp16[reg1]
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	tmp=D16(tmp);
	tmp+=GETREG(GET1);
	WIO_W(tmp&~3,GETREG(GET2));
	return clkIF+clkMEM;
}

UINT32 v810_device::opMULr(UINT32 op)    // mul r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	UINT64 tmp;
	tmp=(INT64)(INT32)op1*(INT64)(INT32)op2;
	op2=tmp&0xffffffff;
	tmp>>=32;
	CHECK_ZS(tmp);//z = bad!
	SET_Z( (tmp|op2)==0 );
	SET_OV((tmp!=0));
	SET_CY((tmp!=0));
	SETREG(GET2,op2);
	SETREG(30,tmp);
	return clkIF;
}

UINT32 v810_device::opMULUr(UINT32 op)   // mulu r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	UINT64 tmp;
	tmp=(UINT64)op1*(UINT64)op2;
	op2=tmp&0xffffffff;
	tmp>>=32;
	CHECK_ZS(tmp);//z = bad!
	SET_Z( (tmp|op2)==0 );
	SET_OV((tmp!=0));
	SET_CY((tmp!=0));
	SETREG(GET2,op2);
	SETREG(30,tmp);
	return clkIF;
}

UINT32 v810_device::opDIVr(UINT32 op)    // div r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	if(op1)
	{
		SETREG(30,(INT32)((INT32)op2%(INT32)op1));
		SETREG(GET2,(INT32)((INT32)op2/(INT32)op1));
		SET_OV((op1^op2^GETREG(GET2)) == 0x80000000);
		CHECK_ZS(GETREG(GET2));
	}
	else
		printf("DIVr divide by zero?\n");
	return clkIF;
}

UINT32 v810_device::opDIVUr(UINT32 op)   // divu r1,r2
{
	UINT32 op1=GETREG(GET1);
	UINT32 op2=GETREG(GET2);
	if(op1)
	{
		SETREG(30,(INT32)(op2%op1));
		SETREG(GET2,(INT32)(op2/op1));
		SET_OV((op1^op2^GETREG(GET2)) == 0x80000000);
		CHECK_ZS(GETREG(GET2));
	}
	else
		printf("DIVUr divide by zero?\n");
	return clkIF;
}

void v810_device::opADDF(UINT32 op)
{
	//TODO: CY
	float val1=u2f(GETREG(GET1));
	float val2=u2f(GETREG(GET2));
	SET_OV(0);
	val2+=val1;
	SET_Z((val2==0.0)?1:0);
	SET_S((val2<0.0)?1:0);
	SETREG(GET2,f2u(val2));
}

void v810_device::opSUBF(UINT32 op)
{
	float val1=u2f(GETREG(GET1));
	float val2=u2f(GETREG(GET2));
	SET_OV(0);
	SET_CY((val2<val1)?1:0);
	val2-=val1;
	SET_Z((val2==0.0)?1:0);
	SET_S((val2<0.0)?1:0);
	SETREG(GET2,f2u(val2));
}

void v810_device::opMULF(UINT32 op)
{
	//TODO: CY
	float val1=u2f(GETREG(GET1));
	float val2=u2f(GETREG(GET2));
	SET_OV(0);
	val2*=val1;
	SET_Z((val2==0.0)?1:0);
	SET_S((val2<0.0)?1:0);
	SETREG(GET2,f2u(val2));
}

void v810_device::opDIVF(UINT32 op)
{
	//TODO: CY
	float val1=u2f(GETREG(GET1));
	float val2=u2f(GETREG(GET2));
	SET_OV(0);
	if(val1!=0)
		val2/=val1;
	else
		printf("DIVF divide by zero?\n");
	SET_Z((val2==0.0)?1:0);
	SET_S((val2<0.0)?1:0);
	SETREG(GET2,f2u(val2));
}

void v810_device::opTRNC(UINT32 op)
{
	float val1=u2f(GETREG(GET1));
	SET_OV(0);
	SET_Z((val1==0.0)?1:0);
	SET_S((val1<0.0)?1:0);
	SETREG(GET2,(INT32)val1);
}

void v810_device::opCMPF(UINT32 op)
{
	float val1=u2f(GETREG(GET1));
	float val2=u2f(GETREG(GET2));
	SET_OV(0);
	SET_CY((val2<val1)?1:0);
	val2-=val1;
	SET_Z((val2==0.0)?1:0);
	SET_S((val2<0.0)?1:0);
}

void v810_device::opCVTS(UINT32 op)
{
	float val1=u2f(GETREG(GET1));
	SET_OV(0);
	SET_Z((val1==0.0)?1:0);
	SET_S((val1<0.0)?1:0);
	SETREG(GET2,(INT32)val1);
}

void v810_device::opCVTW(UINT32 op)
{
	//TODO: CY
	float val1=(INT32)GETREG(GET1);
	SET_OV(0);
	SET_Z((val1==0.0)?1:0);
	SET_S((val1<0.0)?1:0);
	SETREG(GET2,f2u(val1));
}

void v810_device::opMPYHW(UINT32 op)
{
	int val1=(GETREG(GET1) & 0xffff);
	int val2=(GETREG(GET2) & 0xffff);
	SET_OV(0);
	val2*=val1;
	SET_Z((val2==0.0)?1:0);
	SET_S((val2<0.0)?1:0);
	SETREG(GET2,val2);
}

void v810_device::opXB(UINT32 op)
{
	int val=GETREG(GET2);
	SET_OV(0);
	val = (val & 0xffff0000) | ((val & 0xff) << 8) | ((val & 0xff00) >> 8);
	SET_Z((val==0.0)?1:0);
	SET_S((val<0.0)?1:0);
	SETREG(GET2,val);
}


void v810_device::opXH(UINT32 op)
{
	int val=GETREG(GET2);
	SET_OV(0);
	val = ((val & 0xffff0000)>>16) | ((val & 0xffff)<<16);
	SET_Z((val==0.0)?1:0);
	SET_S((val<0.0)?1:0);
	SETREG(GET2,val);
}

UINT32 v810_device::opFpoint(UINT32 op)
{
	UINT32 tmp=R_OP(PC);
	PC+=2;
	switch((tmp&0xfc00)>>10)
	{
			case 0x0: opCMPF(op);break;
			case 0x2: opCVTW(op);break;
			case 0x3: opCVTS(op);break;
			case 0x4: opADDF(op);break;
			case 0x5: opSUBF(op);break;
			case 0x6: opMULF(op);break;
			case 0x7: opDIVF(op);break;
			case 0x8: opXB(op);  break; // *
			case 0x9: opXH(op);  break; // *
			case 0xb: opTRNC(op);break;
			case 0xc: opMPYHW(op); break; // *
			// * <- Virtual Boy specific?
			default: printf("Floating point %02x\n",(tmp&0xfc00) >> 10);break;
	}
	return clkIF+1;
}

/* TODO: clocks */
UINT32 v810_device::opBSU(UINT32 op)
{
	if(!(op & 8))
		fatalerror("V810: unknown BSU opcode %04x\n",op);

	{
		UINT32 srcbit,dstbit,src,dst,size;
		UINT32 dsttmp,tmp;
		UINT8 srctmp;

//      printf("BDST %08x BSRC %08x SIZE %08x DST %08x SRC %08x\n",R26,R27,R28,R29,R30);

		dstbit = R26 & 0x1f;
		srcbit = R27 & 0x1f;
		size =  R28;
		dst = R29 & ~3;
		src = R30 & ~3;

		switch(op & 0xf)
		{
			case 0x8: // ORBSU
				srctmp = (R_W(src) >> srcbit) & 1;
				dsttmp = R_W(dst);

				tmp = dsttmp | (srctmp << dstbit);

				W_W(dst,tmp);
				break;
			case 0x9: // ANDBSU
				srctmp = ((R_W(src) >> srcbit) & 1) ^ 1;
				dsttmp = R_W(dst);

				tmp = dsttmp & (~(srctmp << dstbit));

				W_W(dst,tmp);
				break;
			case 0xa: // XORBSU
				srctmp = (R_W(src) >> srcbit) & 1;
				dsttmp = R_W(dst);

				tmp = dsttmp ^ (srctmp << dstbit);

				W_W(dst,tmp);
				break;
			case 0xb: // MOVBSU
				srctmp = (R_W(src) >> srcbit) & 1;
				dsttmp = (R_W(dst) & ~(1 << dstbit));

				tmp = (srctmp << dstbit) | dsttmp;

				W_W(dst,tmp);
				break;
			case 0xc: // ORNBSU
				srctmp = ((R_W(src) >> srcbit) & 1) ^ 1;
				dsttmp = R_W(dst);

				tmp = dsttmp | (srctmp << dstbit);

				W_W(dst,tmp);
				break;
			case 0xd: // ANDNBSU
				srctmp = (R_W(src) >> srcbit) & 1;
				dsttmp = R_W(dst);

				tmp = dsttmp & (~(srctmp << dstbit));

				W_W(dst,tmp);
				break;
			case 0xe: // XORNBSU
				srctmp = ((R_W(src) >> srcbit) & 1) ^ 1;
				dsttmp = R_W(dst);

				tmp = dsttmp ^ (srctmp << dstbit);

				W_W(dst,tmp);
				break;
			case 0xf: // NOTBSU
				srctmp = ((R_W(src) >> srcbit) & 1) ^ 1;
				dsttmp = (R_W(dst) & ~(1 << dstbit));

				tmp = (srctmp << dstbit) | dsttmp;

				W_W(dst,tmp);
				break;
			default: fatalerror("V810: unemulated BSU opcode %04x\n",op);
		}

		srcbit++;
		dstbit++;

		srcbit&=0x1f;
		dstbit&=0x1f;

		if(srcbit == 0)
			src+=4;

		if(dstbit == 0)
			dst+=4;

		size --;

		R26 = dstbit;
		R27 = srcbit;
		R28 = size;
		R29 = dst;
		R30 = src;

		if(size != 0)
			PC-=2;
	}

	return clkIF+1; //TODO: correct?
}

const v810_device::opcode_func v810_device::s_OpCodeTable[64] =
{
	/* 0x00 */ &v810_device::opMOVr,      // mov r1,r2            1
	/* 0x01 */ &v810_device::opADDr,      // add r1,r2            1
	/* 0x02 */ &v810_device::opSUBr,      // sub r1,r2            1
	/* 0x03 */ &v810_device::opCMPr,      // cmp2 r1,r2           1
	/* 0x04 */ &v810_device::opSHLr,      // shl r1,r2            1
	/* 0x05 */ &v810_device::opSHRr,      // shr r1,r2            1
	/* 0x06 */ &v810_device::opJMPr,      // jmp [r1]             1
	/* 0x07 */ &v810_device::opSARr,      // sar r1,r2            1
	/* 0x08 */ &v810_device::opMULr,      // mul r1,r2            1
	/* 0x09 */ &v810_device::opDIVr,      // div r1,r2            1
	/* 0x0a */ &v810_device::opMULUr,     // mulu r1,r2           1
	/* 0x0b */ &v810_device::opDIVUr,     // divu r1,r2           1
	/* 0x0c */ &v810_device::opORr,       // or r1,r2             1
	/* 0x0d */ &v810_device::opANDr,      // and r1,r2            1
	/* 0x0e */ &v810_device::opXORr,      // xor r1,r2            1
	/* 0x0f */ &v810_device::opNOTr,      // not r1,r2            1
	/* 0x10 */ &v810_device::opMOVi,      // mov imm5,r2          2
	/* 0x11 */ &v810_device::opADDi,      // add imm5,r2          2
	/* 0x12 */ &v810_device::opSETFi,     // setf imm5,r2         2
	/* 0x13 */ &v810_device::opCMPi,      // cmp imm5,r2          2
	/* 0x14 */ &v810_device::opSHLi,      // shl imm5,r2          2
	/* 0x15 */ &v810_device::opSHRi,      // shr imm5,r2          2
	/* 0x16 */ &v810_device::opEI,        // ei               2
	/* 0x17 */ &v810_device::opSARi,      // sar imm5,r2          2
	/* 0x18 */ &v810_device::opTRAP,
	/* 0x19 */ &v810_device::opRETI,
	/* 0x1a */ &v810_device::opHALT,      // halt             2
	/* 0x1b */ &v810_device::opUNDEF,
	/* 0x1c */ &v810_device::opLDSR,      // ldsr reg2,regID          2
	/* 0x1d */ &v810_device::opSTSR,      // stsr regID,reg2          2
	/* 0x1e */ &v810_device::opDI,    // DI               2
	/* 0x1f */ &v810_device::opBSU,
	/* 0x20 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x21 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x22 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x23 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x24 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x25 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x26 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x27 */ &v810_device::opB,     // Branch (7 bit opcode)
	/* 0x28 */ &v810_device::opMOVEA,     // movea imm16, reg1, reg2  5
	/* 0x29 */ &v810_device::opADDI,      // addi imm16, reg1, reg2   5
	/* 0x2a */ &v810_device::opJR,    // jr disp26            4
	/* 0x2b */ &v810_device::opJAL,   // jal disp26           4
	/* 0x2c */ &v810_device::opORI,   // ori imm16, reg1, reg2    5
	/* 0x2d */ &v810_device::opANDI,      // andi imm16, reg1, reg2   5
	/* 0x2e */ &v810_device::opXORI,      // xori imm16, reg1, reg2   5
	/* 0x2f */ &v810_device::opMOVHI,     // movhi imm16, reg1 ,reg2  5
	/* 0x30 */ &v810_device::opLDB,   // ld.b disp16[reg1],reg2   6a
	/* 0x31 */ &v810_device::opLDH,   // ld.h disp16[reg1],reg2   6a
	/* 0x32 */ &v810_device::opUNDEF,
	/* 0x33 */ &v810_device::opLDW,   // ld.w disp16[reg1],reg2   6a
	/* 0x34 */ &v810_device::opSTB,   // st.b reg2, disp16[reg1]  6b
	/* 0x35 */ &v810_device::opSTH,   // st.h reg2, disp16[reg1]  6b
	/* 0x36 */ &v810_device::opUNDEF,
	/* 0x37 */ &v810_device::opSTW,   // st.w reg2, disp16[reg1]  6b
	/* 0x38 */ &v810_device::opINB,   // in.b disp16[reg1], reg2  6a
	/* 0x39 */ &v810_device::opINH,   // in.h disp16[reg1], reg2  6a
	/* 0x3a */ &v810_device::opCAXI,      // caxi disp16[reg1],reg2   6a
	/* 0x3b */ &v810_device::opINW,   // in.w disp16[reg1], reg2  6a
	/* 0x3c */ &v810_device::opOUTB,      // out.b reg2, disp16[reg1]     6b
	/* 0x3d */ &v810_device::opOUTH,      // out.h reg2, disp16[reg1]     6b
	/* 0x3e */ &v810_device::opFpoint, //floating point opcodes
	/* 0x3f */ &v810_device::opOUTW   // out.w reg2, disp16[reg1]     6b
};

void v810_device::device_start()
{
	m_irq_state = CLEAR_LINE;
	m_irq_line = 0;
	m_nmi_line = CLEAR_LINE;
	m_program = &space(AS_PROGRAM);
	m_direct = &m_program->direct();
	m_io = &space(AS_IO);

	m_irq_line = 0;
	m_irq_state = 0;
	m_nmi_line = 0;
	memset(m_reg, 0x00, sizeof(m_reg));

	save_item(NAME(m_reg));
	save_item(NAME(m_irq_line));
	save_item(NAME(m_irq_state));
	save_item(NAME(m_nmi_line));
	save_item(NAME(m_PPC));

	state_add( V810_PC,    "PC",    PC).formatstr("%08X");
	state_add( V810_R0,    "R0",    R0).formatstr("%08X");
	state_add( V810_R1,    "R1",    R1).formatstr("%08X");
	state_add( V810_R2,    "R2",    R2).formatstr("%08X");
	state_add( V810_SP,    "SP",    SP).formatstr("%08X");
	state_add( V810_R4,    "R4",    R4).formatstr("%08X");
	state_add( V810_R5,    "R5",    R5).formatstr("%08X");
	state_add( V810_R6,    "R6",    R6).formatstr("%08X");
	state_add( V810_R7,    "R7",    R7).formatstr("%08X");
	state_add( V810_R8,    "R8",    R8).formatstr("%08X");
	state_add( V810_R9,    "R9",    R9).formatstr("%08X");
	state_add( V810_R10,   "R10",   R10).formatstr("%08X");
	state_add( V810_R11,   "R11",   R11).formatstr("%08X");
	state_add( V810_R12,   "R12",   R12).formatstr("%08X");
	state_add( V810_R13,   "R13",   R13).formatstr("%08X");
	state_add( V810_R14,   "R14",   R14).formatstr("%08X");
	state_add( V810_R15,   "R15",   R15).formatstr("%08X");
	state_add( V810_R16,   "R16",   R16).formatstr("%08X");
	state_add( V810_R17,   "R17",   R17).formatstr("%08X");
	state_add( V810_R18,   "R18",   R18).formatstr("%08X");
	state_add( V810_R19,   "R19",   R19).formatstr("%08X");
	state_add( V810_R20,   "R20",   R20).formatstr("%08X");
	state_add( V810_R21,   "R21",   R21).formatstr("%08X");
	state_add( V810_R22,   "R22",   R22).formatstr("%08X");
	state_add( V810_R23,   "R23",   R23).formatstr("%08X");
	state_add( V810_R24,   "R24",   R24).formatstr("%08X");
	state_add( V810_R25,   "R25",   R25).formatstr("%08X");
	state_add( V810_R26,   "R26",   R26).formatstr("%08X");
	state_add( V810_R27,   "R27",   R27).formatstr("%08X");
	state_add( V810_R28,   "R28",   R28).formatstr("%08X");
	state_add( V810_R29,   "R29",   R29).formatstr("%08X");
	state_add( V810_R30,   "R30",   R30).formatstr("%08X");
	state_add( V810_R31,   "R31",   R31).formatstr("%08X");
	state_add( V810_EIPC,  "EIPC",  EIPC).formatstr("%08X");
	state_add( V810_PSW,   "PSW",   PSW).formatstr("%08X");
	state_add( V810_EIPSW, "EIPSW", EIPSW).formatstr("%08X");
	state_add( V810_FEPC,  "FEPC",  FEPC).formatstr("%08X");
	state_add( V810_FEPSW, "FEPSW", FEPSW).formatstr("%08X");
	state_add( V810_ECR,   "ECR",   ECR).formatstr("%08X");
	state_add( V810_PIR,   "PIR",   PIR).formatstr("%08X");
	state_add( V810_TKCW,  "TKCW",  TKCW).formatstr("%08X");
	state_add( V810_CHCW,  "CHCW",  CHCW).formatstr("%08X");
	state_add( V810_ADTRE, "ADTRE", ADTRE).formatstr("%08X");

	state_add(STATE_GENPC, "GENPC", PC).noshow();
	state_add(STATE_GENSP, "GENSP", SP).noshow();
	state_add(STATE_GENFLAGS, "GENFLAGS", PSW).formatstr("%8s").noshow();
	state_add(STATE_GENPCBASE, "GENPCBASE", m_PPC).noshow();

	m_icountptr = &m_icount;
}

void v810_device::state_string_export(const device_state_entry &entry, astring &string)
{
	switch (entry.index())
	{
		case STATE_GENFLAGS:
			string.printf( "%c%c%c%c%c%c%c%c",
				GET_AE ? 'A':'.',
				GET_NP ? 'N':'.',
				GET_EP ? 'E':'.',
				GET_ID ? 'I':'.',
				GET_CY ? 'C':'.',
				GET_OV ? 'V':'.',
				GET_S ?  'S':'.',
				GET_Z ?  'Z':'.'
			);
			break;
	}
}

void v810_device::device_reset()
{
	int i;
	for(i=0;i<64;i++)   m_reg[i]=0;
	PC = 0xfffffff0;
	PSW = 0x1000;
	ECR   = 0x0000fff0;
}

void v810_device::take_interrupt()
{
	EIPC = PC;
	EIPSW = PSW;

	PC = 0xfffffe00 | (m_irq_line << 4);
	ECR = 0xfe00 | (m_irq_line << 4);

	UINT8 num = m_irq_line + 1;
	if (num==0x10) num=0x0f;

	PSW &= 0xfff0ffff; // clear interrupt level
	SET_EP(1);
	SET_ID(1);
	PSW |= num << 16;

	m_icount-= clkIF;
}

void v810_device::execute_run()
{
	if (m_irq_state != CLEAR_LINE) {
		if (!(GET_NP | GET_EP | GET_ID)) {
			if (m_irq_line >=((PSW & 0xF0000) >> 16)) {
				take_interrupt();
			}
		}
	}
	while(m_icount>0)
	{
		UINT32 op;

		m_PPC=PC;
		debugger_instruction_hook(this, PC);
		op=R_OP(PC);
		PC+=2;
		int cnt;
		cnt = (this->*s_OpCodeTable[op>>10])(op);
		m_icount-= cnt;
	}
}


void v810_device::execute_set_input( int irqline, int state)
{
	m_irq_state = state;
	m_irq_line = irqline;
}
