diff options
Diffstat (limited to 'src/devices/cpu/v810/v810.cpp')
-rw-r--r-- | src/devices/cpu/v810/v810.cpp | 506 |
1 files changed, 295 insertions, 211 deletions
diff --git a/src/devices/cpu/v810/v810.cpp b/src/devices/cpu/v810/v810.cpp index 19de0e7cfca..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 - Tomasz Slanina - analog[at]op.pl - Angelo Salese - lordkale[at]libero.it + 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,23 +19,33 @@ 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; ******************************************************************/ #include "emu.h" #include "v810.h" #include "v810dasm.h" -#include "debugger.h" #define clkIF 3 #define clkMEM 3 - +#define clkIRQ 14 DEFINE_DEVICE_TYPE(V810, v810_device, "v810", "NEC V810") @@ -48,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) + }; + } } @@ -126,26 +146,26 @@ std::unique_ptr<util::disasm_interface> v810_device::create_disassembler() #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 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 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 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 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_cache->read_word(addr)) +#define R_OP(addr) (m_cache.read_word(addr)) #define GET1 (op&0x1f) #define GET2 ((op>>5)&0x1f) @@ -170,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) @@ -187,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 @@ -203,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 @@ -213,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 @@ -231,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 @@ -243,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; } @@ -259,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 @@ -271,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; } @@ -283,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 @@ -294,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 @@ -334,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; } @@ -381,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 @@ -395,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 @@ -406,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 @@ -420,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 @@ -431,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; } @@ -439,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; } @@ -461,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 @@ -471,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 @@ -491,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 @@ -510,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 @@ -528,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 @@ -546,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 @@ -565,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 @@ -583,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; } @@ -597,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) @@ -608,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) @@ -639,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) @@ -655,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 @@ -733,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 @@ -745,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 @@ -756,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 @@ -767,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 @@ -785,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 @@ -796,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] @@ -806,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] @@ -816,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] @@ -826,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] @@ -836,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] @@ -846,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] @@ -856,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 @@ -873,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 @@ -890,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 @@ -905,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 @@ -921,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)); @@ -935,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)); @@ -947,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)); @@ -959,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)); @@ -970,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)); @@ -994,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); @@ -1013,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) { @@ -1155,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++; @@ -1179,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] = @@ -1255,17 +1318,15 @@ const v810_device::opcode_func v810_device::s_OpCodeTable[64] = void v810_device::device_start() { - m_program = &space(AS_PROGRAM); - m_cache = m_program->cache<2, 0, ENDIANNESS_LITTLE>(); - m_io = &space(AS_IO); + space(AS_PROGRAM).cache(m_cache); + 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)); @@ -1316,7 +1377,6 @@ void v810_device::device_start() state_add(STATE_GENPC, "GENPC", PC).noshow(); state_add(STATE_GENPCBASE, "CURPC", m_PPC).noshow(); - state_add(STATE_GENSP, "GENSP", SP).noshow(); state_add(STATE_GENFLAGS, "GENFLAGS", PSW).formatstr("%8s").noshow(); set_icountptr(m_icount); @@ -1343,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; } |