summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/v810/v810.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/v810/v810.cpp')
-rw-r--r--src/devices/cpu/v810/v810.cpp468
1 files changed, 277 insertions, 191 deletions
diff --git a/src/devices/cpu/v810/v810.cpp b/src/devices/cpu/v810/v810.cpp
index ddf14a2b7fe..058a9f976cb 100644
--- a/src/devices/cpu/v810/v810.cpp
+++ b/src/devices/cpu/v810/v810.cpp
@@ -1,11 +1,12 @@
// license:BSD-3-Clause
-// copyright-holders:Angelo Salese, Tomasz Slanina
+// copyright-holders: Tomasz Slanina, Angelo Salese
/******************************************************************
- NEC V810 (upd70732) core
+ NEC V810 (μpd70732) core
Tomasz Slanina
Angelo Salese
Change Log
+ - 07/04/2023 - Update opcode timings (Angelo Salese)
- 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)
@@ -18,12 +19,23 @@
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
+ - Verify floating point opcodes (single precision IEEE-754 standard)
+ - CY flag in floating point opcodes that supports them;
+ - Subclass NVC (vboy CPU), few extra opcodes plus onchip peripherals ($0200'0000 area);
+ - implement trap opcode;
+ - implement halt opcode;
+ - implement double exception behaviour;
+ - implement NP fatal exception;
+ - implement NMI;
+ - implement floating point exceptions;
+ - verify and improve bitstring opcodes;
+ - cache handling;
+ - external bus timing (on load/store opcodes):
+ \- 3 cycles for ROM
+ \- 2 cycles for RAM
+ \- 1 cycle for cache
+ - pipeline;
+ - implement caxi opcode;
******************************************************************/
@@ -33,7 +45,7 @@
#define clkIF 3
#define clkMEM 3
-
+#define clkIRQ 14
DEFINE_DEVICE_TYPE(V810, v810_device, "v810", "NEC V810")
@@ -47,10 +59,19 @@ v810_device::v810_device(const machine_config &mconfig, const char *tag, device_
device_memory_interface::space_config_vector v810_device::memory_space_config() const
{
- return space_config_vector {
- std::make_pair(AS_PROGRAM, &m_program_config),
- std::make_pair(AS_IO, &m_io_config)
- };
+ if(has_configured_map(AS_IO))
+ {
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config),
+ std::make_pair(AS_IO, &m_io_config)
+ };
+ }
+ else
+ {
+ return space_config_vector {
+ std::make_pair(AS_PROGRAM, &m_program_config)
+ };
+ }
}
@@ -169,6 +190,7 @@ std::unique_ptr<util::disasm_interface> v810_device::create_disassembler()
+// r0 is literally a "register zero", reading returns 0, writing is ignored.
void v810_device::SETREG(uint32_t reg,uint32_t val)
{
if(reg)
@@ -186,13 +208,14 @@ uint32_t v810_device::GETREG(uint32_t reg)
uint32_t v810_device::opUNDEF(uint32_t op)
{
logerror("V810: Unknown opcode %x @ %x",op,PC-2);
+ machine().debug_break();
return clkIF;
}
uint32_t v810_device::opMOVr(uint32_t op) // mov reg1, reg2
{
SETREG(GET2,GETREG(GET1));
- return clkIF;
+ return 1;
}
uint32_t v810_device::opMOVEA(uint32_t op) // movea imm16, reg1, reg2
@@ -202,7 +225,7 @@ uint32_t v810_device::opMOVEA(uint32_t op) // movea imm16, reg1, reg2
PC+=2;
op2=I16(op2);
SETREG(GET2,op1+op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opMOVHI(uint32_t op) // movhi imm16, reg1 ,reg2
@@ -212,13 +235,13 @@ uint32_t v810_device::opMOVHI(uint32_t op) // movhi imm16, reg1 ,reg2
op2=UI16(op2);
op2<<=16;
SETREG(GET2,GETREG(GET1)+op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opMOVi(uint32_t op) // mov imm5,r2
{
SETREG(GET2,I5(op));
- return clkIF;
+ return 1;
}
uint32_t v810_device::opADDr(uint32_t op) // add r1,r2
@@ -230,7 +253,7 @@ uint32_t v810_device::opADDr(uint32_t op) // add r1,r2
CHECK_OVADD(op1,op2,res);
CHECK_ZS(res);
SETREG(GET2,res);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opADDi(uint32_t op) // add imm5,r2
@@ -242,7 +265,7 @@ uint32_t v810_device::opADDi(uint32_t op) // add imm5,r2
CHECK_OVADD(op1,op2,res);
CHECK_ZS(res);
SETREG(GET2,res);
- return clkIF;
+ return 1;
}
@@ -258,7 +281,7 @@ uint32_t v810_device::opADDI(uint32_t op) // addi imm16, reg1, reg2
CHECK_OVADD(op1,op2,res);
CHECK_ZS(res);
SETREG(GET2,res);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSUBr(uint32_t op) // sub r1,r2
@@ -270,7 +293,7 @@ uint32_t v810_device::opSUBr(uint32_t op) // sub r1,r2
CHECK_OVSUB(op1,op2,res);
CHECK_ZS(res);
SETREG(GET2,res);
- return clkIF;
+ return 1;
}
@@ -282,7 +305,7 @@ uint32_t v810_device::opCMPr(uint32_t op) // cmp r1,r2
CHECK_CY(res);
CHECK_OVSUB(op1,op2,res);
CHECK_ZS(res);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opCMPi(uint32_t op) // cmpi imm5,r2
@@ -293,7 +316,7 @@ uint32_t v810_device::opCMPi(uint32_t op) // cmpi imm5,r2
CHECK_CY(res);
CHECK_OVSUB(op1,op2,res);
CHECK_ZS(res);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSETFi(uint32_t op) // setf imm5,r2
@@ -333,42 +356,41 @@ uint32_t v810_device::opSETFi(uint32_t op) // setf imm5,r2
case 7: //ble
res=GET_Z||(GET_S^GET_OV);
- break;
+ break;
case 8: //bnv
res=!GET_OV;
- break;
+ break;
case 9: //bnl
res=!GET_CY;
- break;
+ break;
case 10: //bne
res=!GET_Z;
- break;
+ break;
case 11: //bh
res=!(GET_Z||GET_CY);
- break;
+ break;
case 12: //bp
res=!GET_S;
- break;
+ break;
case 13: //nop
-
break;
case 14: //bge
res=!(GET_OV^GET_S);
- break;
+ break;
case 15: //bgt
res=!(GET_Z||(GET_OV^GET_S));
break;
}
SETREG(GET2,res);
- return clkIF;
+ return 1;
}
@@ -380,7 +402,7 @@ uint32_t v810_device::opANDr(uint32_t op) // and r1,r2
CHECK_ZS(op2);
SET_OV(0);
SETREG(GET2,op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opANDI(uint32_t op) // andi imm16,r1,r2
@@ -394,7 +416,7 @@ uint32_t v810_device::opANDI(uint32_t op) // andi imm16,r1,r2
SET_OV(0);
SET_S(0);
SETREG(GET2,op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opORr(uint32_t op) // or r1,r2
@@ -405,7 +427,7 @@ uint32_t v810_device::opORr(uint32_t op) // or r1,r2
CHECK_ZS(op2);
SET_OV(0);
SETREG(GET2,op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opORI(uint32_t op) // ori imm16,r1,r2
@@ -419,7 +441,7 @@ uint32_t v810_device::opORI(uint32_t op) // ori imm16,r1,r2
SET_OV(0);
SET_S(0);
SETREG(GET2,op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opXORr(uint32_t op) // xor r1,r2
@@ -430,7 +452,7 @@ uint32_t v810_device::opXORr(uint32_t op) // xor r1,r2
CHECK_ZS(op2);
SET_OV(0);
SETREG(GET2,op2);
- return clkIF;
+ return 1;
}
@@ -438,14 +460,14 @@ uint32_t v810_device::opLDSR(uint32_t op) // ldsr reg2,regID
{
uint32_t op1=UI5(op);
SETREG(32+op1,GETREG(GET2));
- return clkIF;
+ return 2;
}
uint32_t v810_device::opSTSR(uint32_t op) // ldsr regID,reg2
{
uint32_t op1=UI5(op);
SETREG(GET2,GETREG(32+op1));
- return clkIF;
+ return 2;
}
@@ -460,7 +482,7 @@ uint32_t v810_device::opXORI(uint32_t op) // xori imm16,r1,r2
SET_OV(0);
SET_S(0);
SETREG(GET2,op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opNOTr(uint32_t op) // not r1,r2
@@ -470,7 +492,7 @@ uint32_t v810_device::opNOTr(uint32_t op) // not r1,r2
CHECK_ZS(op2);
SET_OV(0);
SETREG(GET2,op2);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSHLr(uint32_t op) // shl r1,r2
@@ -490,7 +512,7 @@ uint32_t v810_device::opSHLr(uint32_t op) // shl r1,r2
SETREG(GET2,tmp&0xffffffff);
CHECK_ZS(GETREG(GET2));
}
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSHLi(uint32_t op) // shl imm5,r2
@@ -509,7 +531,7 @@ uint32_t v810_device::opSHLi(uint32_t op) // shl imm5,r2
SETREG(GET2,tmp&0xffffffff);
}
CHECK_ZS(GETREG(GET2));
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSHRr(uint32_t op) // shr r1,r2
@@ -527,7 +549,7 @@ uint32_t v810_device::opSHRr(uint32_t op) // shr r1,r2
SETREG(GET2,(tmp>>1)&0xffffffff);
}
CHECK_ZS(GETREG(GET2));
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSHRi(uint32_t op) // shr imm5,r2
@@ -545,7 +567,7 @@ uint32_t v810_device::opSHRi(uint32_t op) // shr imm5,r2
SETREG(GET2,tmp&0xffffffff);
}
CHECK_ZS(GETREG(GET2));
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSARr(uint32_t op) // sar r1,r2
@@ -564,7 +586,7 @@ uint32_t v810_device::opSARr(uint32_t op) // sar r1,r2
SETREG(GET2,tmp);
}
CHECK_ZS(GETREG(GET2));
- return clkIF;
+ return 1;
}
uint32_t v810_device::opSARi(uint32_t op) // sar imm5,r2
@@ -582,13 +604,13 @@ uint32_t v810_device::opSARi(uint32_t op) // sar imm5,r2
SETREG(GET2,tmp);
}
CHECK_ZS(GETREG(GET2));
- return clkIF;
+ return 1;
}
uint32_t v810_device::opJMPr(uint32_t op)
{
PC=GETREG(GET1)&~1;
- return clkIF+2;
+ return 3;
}
@@ -596,7 +618,7 @@ uint32_t v810_device::opJR(uint32_t op)
{
uint32_t tmp=R_OP(PC);
PC=PC-2+(D26(op,tmp)&~1);
- return clkIF+2;
+ return 3;
}
uint32_t v810_device::opJAL(uint32_t op)
@@ -607,26 +629,29 @@ uint32_t v810_device::opJAL(uint32_t op)
PC+=D26(op,tmp);
PC-=4;
PC&=~1;
- return clkIF+2;
+ return 3;
}
-
+// TODO: specific to NVC
uint32_t v810_device::opEI(uint32_t op)
{
SET_ID(0);
- return clkIF;
+ return 1;
}
+// TODO: specific to NVC
uint32_t v810_device::opDI(uint32_t op)
{
SET_ID(1);
- return clkIF;
+ return 1;
}
uint32_t v810_device::opTRAP(uint32_t op)
{
- printf("V810: TRAP @ %X\n",PC-2);
- return clkIF;
+ logerror("V810: TRAP @ %X\n", PC - 2);
+ machine().debug_break();
+ // TODO: assume same as irq time
+ return clkIRQ;
}
uint32_t v810_device::opRETI(uint32_t op)
@@ -638,13 +663,14 @@ uint32_t v810_device::opRETI(uint32_t op)
PC = EIPC;
PSW = EIPSW;
}
- return clkIF;
+ return 1;
}
uint32_t v810_device::opHALT(uint32_t op)
{
- printf("V810: HALT @ %X\n",PC-2);
- return clkIF;
+ logerror("V810: HALT @ %X\n",PC-2);
+ machine().debug_break();
+ return 1;
}
uint32_t v810_device::opB(uint32_t op)
@@ -654,73 +680,74 @@ uint32_t v810_device::opB(uint32_t op)
{
case 0: //bv
doBranch=GET_OV;
- break;
+ break;
case 1: //bl
doBranch=GET_CY;
- break;
+ break;
case 2: //be
doBranch=GET_Z;
- break;
+ break;
case 3: //bnh
doBranch=GET_Z||GET_CY;
- break;
+ break;
case 4: //bn
doBranch=GET_S;
- break;
+ break;
case 5: //br
doBranch=1;
- break;
+ break;
case 6: //blt
doBranch=GET_S^GET_OV;
- break;
+ break;
case 7: //ble
doBranch=GET_Z||(GET_S^GET_OV);
- break;
+ break;
case 8: //bnv
doBranch=!GET_OV;
- break;
+ break;
case 9: //bnl
doBranch=!GET_CY;
- break;
+ break;
case 10: //bne
doBranch=!GET_Z;
- break;
+ break;
case 11: //bh
doBranch=!(GET_Z||GET_CY);
- break;
+ break;
case 12: //bp
doBranch=!GET_S;
- break;
+ break;
case 13: //nop
- break;
+ break;
case 14: //bge
doBranch=!(GET_OV^GET_S);
- break;
+ break;
case 15: //bgt
doBranch=!(GET_Z||(GET_OV^GET_S));
- break;
+ break;
}
if(doBranch)
{
- PC=PC-2+(D9(op)&~1);
+ PC = PC - 2 + (D9(op) &~1);
+ return 3;
}
- return clkIF;
+ return 1;
}
uint32_t v810_device::opLDB(uint32_t op) // ld.b disp16[reg1],reg2
@@ -732,7 +759,7 @@ uint32_t v810_device::opLDB(uint32_t op) // ld.b disp16[reg1],reg2
tmp=R_B(tmp);
tmp|=(tmp&0x80)?0xffffff00:0;
SETREG(GET2,tmp);
- return clkIF+clkMEM;
+ return 2+clkMEM;
}
uint32_t v810_device::opLDH(uint32_t op) // ld.h disp16[reg1],reg2
@@ -744,7 +771,7 @@ uint32_t v810_device::opLDH(uint32_t op) // ld.h disp16[reg1],reg2
tmp=R_H(tmp&~1);
tmp|=(tmp&0x8000)?0xffff0000:0;
SETREG(GET2,tmp);
- return clkIF+clkMEM;
+ return 2+clkMEM;
}
uint32_t v810_device::opLDW(uint32_t op) // ld.w disp16[reg1],reg2
@@ -755,7 +782,7 @@ uint32_t v810_device::opLDW(uint32_t op) // ld.w disp16[reg1],reg2
tmp+=GETREG(GET1);
tmp=R_W(tmp&~3);
SETREG(GET2,tmp);
- return clkIF+clkMEM;
+ return 2+clkMEM;
}
uint32_t v810_device::opINB(uint32_t op) // in.b disp16[reg1],reg2
@@ -766,14 +793,15 @@ uint32_t v810_device::opINB(uint32_t op) // in.b disp16[reg1],reg2
tmp+=GETREG(GET1);
tmp=RIO_B(tmp);
SETREG(GET2,tmp);
- return clkIF+clkMEM;
+ return 2+clkMEM;
}
uint32_t v810_device::opCAXI(uint32_t op) // caxi disp16[reg1],reg2
{
printf("V810 CAXI execute\n");
PC+=2;
- return clkIF;
+ // TODO: check me
+ return 1;
}
uint32_t v810_device::opINH(uint32_t op) // in.h disp16[reg1],reg2
@@ -784,7 +812,7 @@ uint32_t v810_device::opINH(uint32_t op) // in.h disp16[reg1],reg2
tmp+=GETREG(GET1);
tmp=RIO_H(tmp&~1);
SETREG(GET2,tmp);
- return clkIF+clkMEM;
+ return 2+clkMEM;
}
uint32_t v810_device::opINW(uint32_t op) // in.w disp16[reg1],reg2
@@ -795,7 +823,7 @@ uint32_t v810_device::opINW(uint32_t op) // in.w disp16[reg1],reg2
tmp+=GETREG(GET1);
tmp=RIO_W(tmp&~3);
SETREG(GET2,tmp);
- return clkIF+clkMEM;
+ return 2+clkMEM;
}
uint32_t v810_device::opSTB(uint32_t op) // st.b reg2, disp16[reg1]
@@ -805,7 +833,7 @@ uint32_t v810_device::opSTB(uint32_t op) // st.b reg2, disp16[reg1]
tmp=D16(tmp);
tmp+=GETREG(GET1);
W_B(tmp,GETREG(GET2)&0xff);
- return clkIF+clkMEM;
+ return 1+clkMEM;
}
uint32_t v810_device::opSTH(uint32_t op) // st.h reg2, disp16[reg1]
@@ -815,7 +843,7 @@ uint32_t v810_device::opSTH(uint32_t op) // st.h reg2, disp16[reg1]
tmp=D16(tmp);
tmp+=GETREG(GET1);
W_H(tmp&~1,GETREG(GET2)&0xffff);
- return clkIF+clkMEM;
+ return 1+clkMEM;
}
uint32_t v810_device::opSTW(uint32_t op) // st.w reg2, disp16[reg1]
@@ -825,7 +853,7 @@ uint32_t v810_device::opSTW(uint32_t op) // st.w reg2, disp16[reg1]
tmp=D16(tmp);
tmp+=GETREG(GET1);
W_W(tmp&~3,GETREG(GET2));
- return clkIF+clkMEM;
+ return 1+clkMEM;
}
uint32_t v810_device::opOUTB(uint32_t op) // out.b reg2, disp16[reg1]
@@ -835,7 +863,7 @@ uint32_t v810_device::opOUTB(uint32_t op) // out.b reg2, disp16[reg1]
tmp=D16(tmp);
tmp+=GETREG(GET1);
WIO_B(tmp,GETREG(GET2)&0xff);
- return clkIF+clkMEM;
+ return 1+clkMEM;
}
uint32_t v810_device::opOUTH(uint32_t op) // out.h reg2, disp16[reg1]
@@ -845,7 +873,7 @@ uint32_t v810_device::opOUTH(uint32_t op) // out.h reg2, disp16[reg1]
tmp=D16(tmp);
tmp+=GETREG(GET1);
WIO_H(tmp&~1,GETREG(GET2)&0xffff);
- return clkIF+clkMEM;
+ return 1+clkMEM;
}
uint32_t v810_device::opOUTW(uint32_t op) // out.w reg2, disp16[reg1]
@@ -855,7 +883,7 @@ uint32_t v810_device::opOUTW(uint32_t op) // out.w reg2, disp16[reg1]
tmp=D16(tmp);
tmp+=GETREG(GET1);
WIO_W(tmp&~3,GETREG(GET2));
- return clkIF+clkMEM;
+ return 1+clkMEM;
}
uint32_t v810_device::opMULr(uint32_t op) // mul r1,r2
@@ -872,7 +900,7 @@ uint32_t v810_device::opMULr(uint32_t op) // mul r1,r2
SET_CY((tmp!=0));
SETREG(GET2,op2);
SETREG(30,tmp);
- return clkIF;
+ return 13;
}
uint32_t v810_device::opMULUr(uint32_t op) // mulu r1,r2
@@ -889,7 +917,7 @@ uint32_t v810_device::opMULUr(uint32_t op) // mulu r1,r2
SET_CY((tmp!=0));
SETREG(GET2,op2);
SETREG(30,tmp);
- return clkIF;
+ return 13;
}
uint32_t v810_device::opDIVr(uint32_t op) // div r1,r2
@@ -904,8 +932,11 @@ uint32_t v810_device::opDIVr(uint32_t op) // div r1,r2
CHECK_ZS(GETREG(GET2));
}
else
- printf("DIVr divide by zero?\n");
- return clkIF;
+ {
+ //machine().debug_break();
+ throw emu_fatalerror("DIVr divide by zero\n");
+ }
+ return 38;
}
uint32_t v810_device::opDIVUr(uint32_t op) // divu r1,r2
@@ -920,11 +951,14 @@ uint32_t v810_device::opDIVUr(uint32_t op) // divu r1,r2
CHECK_ZS(GETREG(GET2));
}
else
- printf("DIVUr divide by zero?\n");
- return clkIF;
+ {
+ //machine().debug_break();
+ throw emu_fatalerror("DIVUr divide by zero\n");
+ }
+ return 38;
}
-void v810_device::opADDF(uint32_t op)
+uint32_t v810_device::opADDF(uint32_t op)
{
//TODO: CY
float val1=u2f(GETREG(GET1));
@@ -934,9 +968,10 @@ void v810_device::opADDF(uint32_t op)
SET_Z((val2==0.0f)?1:0);
SET_S((val2<0.0f)?1:0);
SETREG(GET2,f2u(val2));
+ return 24;
}
-void v810_device::opSUBF(uint32_t op)
+uint32_t v810_device::opSUBF(uint32_t op)
{
float val1=u2f(GETREG(GET1));
float val2=u2f(GETREG(GET2));
@@ -946,9 +981,10 @@ void v810_device::opSUBF(uint32_t op)
SET_Z((val2==0.0f)?1:0);
SET_S((val2<0.0f)?1:0);
SETREG(GET2,f2u(val2));
+ return 26;
}
-void v810_device::opMULF(uint32_t op)
+uint32_t v810_device::opMULF(uint32_t op)
{
//TODO: CY
float val1=u2f(GETREG(GET1));
@@ -958,9 +994,10 @@ void v810_device::opMULF(uint32_t op)
SET_Z((val2==0.0f)?1:0);
SET_S((val2<0.0f)?1:0);
SETREG(GET2,f2u(val2));
+ return 27;
}
-void v810_device::opDIVF(uint32_t op)
+uint32_t v810_device::opDIVF(uint32_t op)
{
//TODO: CY
float val1=u2f(GETREG(GET1));
@@ -969,22 +1006,25 @@ void v810_device::opDIVF(uint32_t op)
if(val1!=0)
val2/=val1;
else
- printf("DIVF divide by zero?\n");
+ throw emu_fatalerror("DIVF divide by zero\n");
SET_Z((val2==0.0f)?1:0);
SET_S((val2<0.0f)?1:0);
SETREG(GET2,f2u(val2));
+ return 44;
}
-void v810_device::opTRNC(uint32_t op)
+uint32_t v810_device::opTRNC(uint32_t op)
{
float val1=u2f(GETREG(GET1));
SET_OV(0);
SET_Z((val1==0.0f)?1:0);
SET_S((val1<0.0f)?1:0);
SETREG(GET2,(int32_t)val1);
+ // TODO: unknown
+ return 18;
}
-void v810_device::opCMPF(uint32_t op)
+uint32_t v810_device::opCMPF(uint32_t op)
{
float val1=u2f(GETREG(GET1));
float val2=u2f(GETREG(GET2));
@@ -993,18 +1033,22 @@ void v810_device::opCMPF(uint32_t op)
val2-=val1;
SET_Z((val2==0.0f)?1:0);
SET_S((val2<0.0f)?1:0);
+ // TODO: unknown
+ return 18;
}
-void v810_device::opCVTS(uint32_t op)
+uint32_t v810_device::opCVTS(uint32_t op)
{
float val1=u2f(GETREG(GET1));
SET_OV(0);
SET_Z((val1==0.0f)?1:0);
SET_S((val1<0.0f)?1:0);
SETREG(GET2,(int32_t)val1);
+ // TODO: unknown
+ return 18;
}
-void v810_device::opCVTW(uint32_t op)
+uint32_t v810_device::opCVTW(uint32_t op)
{
//TODO: CY
float val1=(int32_t)GETREG(GET1);
@@ -1012,81 +1056,98 @@ void v810_device::opCVTW(uint32_t op)
SET_Z((val1==0.0f)?1:0);
SET_S((val1<0.0f)?1:0);
SETREG(GET2,f2u(val1));
+ // TODO: unknown
+ return 18;
}
-void v810_device::opMPYHW(uint32_t op)
-{
- int val1=(GETREG(GET1) & 0xffff);
- int val2=(GETREG(GET2) & 0xffff);
- SET_OV(0);
- val2*=val1;
- SET_Z((val2==0.0f)?1:0);
- SET_S((val2<0.0f)?1:0);
- SETREG(GET2,val2);
-}
-
-void v810_device::opXB(uint32_t op)
+uint32_t v810_device::opXB(uint32_t op)
{
int val=GETREG(GET2);
- SET_OV(0);
- val = (val & 0xffff0000) | ((val & 0xff) << 8) | ((val & 0xff00) >> 8);
- SET_Z((val==0.0f)?1:0);
- SET_S((val<0.0f)?1:0);
+ val = (val & 0xffff0000) | swapendian_int16(val & 0xffff);
+ // TODO: verify flags really being unaffected
+ //SET_OV(0);
+ //SET_Z((val==0.0f)?1:0);
+ //SET_S((val<0.0f)?1:0);
SETREG(GET2,val);
+ // TODO: unknown
+ return 1;
}
-void v810_device::opXH(uint32_t op)
+uint32_t v810_device::opXH(uint32_t op)
{
int val=GETREG(GET2);
- SET_OV(0);
val = ((val & 0xffff0000)>>16) | ((val & 0xffff)<<16);
- SET_Z((val==0.0f)?1:0);
- SET_S((val<0.0f)?1:0);
+ // TODO: verify flags really being unaffected
+ //SET_OV(0);
+ //SET_Z((val==0.0f)?1:0);
+ //SET_S((val<0.0f)?1:0);
SETREG(GET2,val);
+ // TODO: unknown
+ return 1;
+}
+
+uint32_t v810_device::opMPYHW(uint32_t op)
+{
+ s16 val1 = (s16)GETREG(GET1);
+ s16 val2 = (s16)GETREG(GET2);
+ s32 result = (s32)(val1 * val2);
+ // TODO: verify flags really being unaffected
+ //SET_OV(0);
+ //SET_Z((result == 0) ? 1 : 0);
+ //SET_S((result < 0) ? 1 : 0);
+ SETREG(GET2,result);
+ return 9;
}
uint32_t v810_device::opFpoint(uint32_t op)
{
uint32_t tmp=R_OP(PC);
- PC+=2;
- switch((tmp&0xfc00)>>10)
+ uint32_t op_cycles = 0;
+ const u8 op_type = (tmp&0xfc00)>>10;
+ PC += 2;
+ switch(op_type)
{
- 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;
+ // TODO: (*) denotes Virtual Boy specific opcodes
+ // likely needs co-processor override
+ case 0x0: op_cycles = opCMPF(op);break;
+ case 0x2: op_cycles = opCVTW(op);break;
+ case 0x3: op_cycles = opCVTS(op);break;
+ case 0x4: op_cycles = opADDF(op);break;
+ case 0x5: op_cycles = opSUBF(op);break;
+ case 0x6: op_cycles = opMULF(op);break;
+ case 0x7: op_cycles = opDIVF(op);break;
+ case 0x8: op_cycles = opXB(op); break; // (*)
+ case 0x9: op_cycles = opXH(op); break; // (*)
+ //case 0xa: REV (*)
+ case 0xb: op_cycles = opTRNC(op);break;
+ case 0xc: op_cycles = opMPYHW(op); break; // (*)
+ default:
+ throw emu_fatalerror("Floating point unknown type %02x\n", op_type);
+ break;
}
- return clkIF+1;
+ return op_cycles;
}
-/* TODO: clocks */
+// TODO: makes host performance to dip
+// TODO: verify atomicity wrt interrupts & DRQ
+// Use scenario: pcfx reading from CD data port with no increment of source address.
uint32_t v810_device::opBSU(uint32_t op)
{
if(!(op & 8))
- fatalerror("V810: unknown BSU opcode %04x\n",op);
+ throw emu_fatalerror("V810: unknown BSU opcode %04x\n",op);
{
- uint32_t srcbit,dstbit,src,dst,size;
- uint32_t dsttmp,tmp;
+ uint32_t dsttmp, tmp;
uint8_t 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;
+ uint32_t dstbit = R26 & 0x1f;
+ uint32_t srcbit = R27 & 0x1f;
+ uint32_t size = R28;
+ uint32_t dst = R29 & ~3;
+ uint32_t src = R30 & ~3;
switch(op & 0xf)
{
@@ -1154,7 +1215,8 @@ uint32_t v810_device::opBSU(uint32_t op)
W_W(dst,tmp);
break;
- default: fatalerror("V810: unemulated BSU opcode %04x\n",op);
+ default:
+ throw emu_fatalerror("V810: unemulated BSU opcode %04x\n",op);
}
srcbit++;
@@ -1178,10 +1240,12 @@ uint32_t v810_device::opBSU(uint32_t op)
R30 = src;
if(size != 0)
- PC-=2;
+ PC -= 2;
}
- return clkIF+1; //TODO: correct?
+ // TODO: all bitstring timings implied for vboy 16-bit ext bus
+ // TODO: search type BSU takes 4 cycles
+ return 12;
}
const v810_device::opcode_func v810_device::s_OpCodeTable[64] =
@@ -1258,13 +1322,11 @@ void v810_device::device_start()
space(AS_PROGRAM).specific(m_program);
space(has_space(AS_IO) ? AS_IO : AS_PROGRAM).specific(m_io);
- m_irq_line = 0;
- m_irq_state = CLEAR_LINE;
+ m_irq_state = 0;
m_nmi_line = CLEAR_LINE;
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));
@@ -1341,58 +1403,82 @@ void v810_device::state_string_export(const device_state_entry &entry, std::stri
void v810_device::device_reset()
{
- int i;
- for(i=0;i<64;i++) m_reg[i]=0;
+ // everything else is "Undefind" (sic)
+ m_reg[0] = 0;
PC = 0xfffffff0;
- PSW = 0x1000;
- ECR = 0x0000fff0;
+ PSW = 0x00008000;
+ ECR = 0x0000fff0;
}
-void v810_device::take_interrupt()
+// TODO: remaining exception types
+void v810_device::check_interrupts()
{
- EIPC = PC;
- EIPSW = PSW;
+ if (m_irq_state == 0)
+ return;
- PC = 0xfffffe00 | (m_irq_line << 4);
- ECR = 0xfe00 | (m_irq_line << 4);
+ if (GET_NP || GET_EP || GET_ID)
+ return;
- uint8_t num = m_irq_line + 1;
- if (num==0x10) num=0x0f;
+ for (u16 irq_line = 15; irq_line >= (PSW & 0xF0000) >> 16; irq_line --)
+ {
+ if (!((1 << irq_line) & m_irq_state))
+ continue;
- PSW &= 0xfff0ffff; // clear interrupt level
- SET_EP(1);
- SET_ID(1);
- PSW |= num << 16;
+ standard_irq_callback(irq_line, PC);
+
+ EIPC = PC;
+ EIPSW = PSW;
- m_icount-= clkIF;
+ PC = 0xfffffe00 | (irq_line << 4);
+ ECR = 0xfe00 | (irq_line << 4);
+
+ uint8_t num = irq_line + 1;
+ if (num == 0x10) num = 0x0f;
+
+ // clear interrupt level
+ PSW &= 0xfff0ffff;
+ SET_EP(1);
+ SET_ID(1);
+ SET_AE(0);
+ PSW |= num << 16;
+
+ m_icount -= clkIRQ;
+ return;
+ }
}
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_t op;
+ // TODO: move in execution body
+ // (breaks pcfx boot)
+ check_interrupts();
- m_PPC=PC;
+ do
+ {
+ m_PPC = PC;
debugger_instruction_hook(PC);
- op=R_OP(PC);
- PC+=2;
- int cnt;
- cnt = (this->*s_OpCodeTable[op>>10])(op);
- m_icount-= cnt;
- }
+ uint32_t op = R_OP(PC);
+ PC += 2;
+
+ int cnt = (this->*s_OpCodeTable[op>>10])(op);
+ m_icount -= cnt;
+
+ } while(m_icount > 0);
}
-void v810_device::execute_set_input( int irqline, int state)
+// TODO: logically connects to /INTV0-/INTV3 pins
+// v810 just exposes an INT and 4 V(ector?) lines.
+// - on pcfx there's an unknown chip irq priority/mask dispatcher;
+// - on vboy it's implied in the NVC specs with a laconic "Interrupt Encoder".
+// It's also 5 possible lines, which wouldn't work bitwise;
+void v810_device::execute_set_input( int irqline, int state )
{
- m_irq_state = state;
- m_irq_line = irqline;
+ if (state == HOLD_LINE)
+ throw emu_fatalerror("V810: using HOLD_LINE is unsupported by the core");
+ u16 mask = 1 << irqline;
+ if (state == ASSERT_LINE)
+ m_irq_state |= mask;
+ else
+ m_irq_state &= ~mask;
}