/* V810 Emulator * * Copyright (C) 2006 David Tucker * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // Macro test taken from http://gcc.gnu.org/viewcvs/trunk/gcc/testsuite/gcc.dg/20020919-1.c?view=markup&pathrev=142696 //#if defined (__powerpc__) || defined (__PPC__) || defined (__ppc__) || defined (__POWERPC__) || defined (PPC) || defined (_IBMR2) // register v810_timestamp_t timestamp_rl asm("15") = v810_timestamp; //#elif defined(__x86_64__) // register v810_timestamp_t timestamp_rl asm("r11") = v810_timestamp; //#else register v810_timestamp_t timestamp_rl = v810_timestamp; //#endif uint32 opcode; uint32 tmp2; int val = 0; #define ADDCLOCK(__n) { timestamp += __n; } #define CHECK_HALTED(); { if(Halted && timestamp < next_event_ts) { timestamp = next_event_ts; } } while(Running) { #ifdef RB_DEBUGMODE uint32 old_PC = RB_GETPC(); #endif uint32 tmpop; assert(timestamp_rl <= next_event_ts); if(!IPendingCache) { if(Halted) { timestamp_rl = next_event_ts; } else if(in_bstr) { tmpop = in_bstr_to; opcode = tmpop >> 9; goto op_BSTR; } } while(timestamp_rl < next_event_ts) { #ifdef RB_DEBUGMODE old_PC = RB_GETPC(); #endif P_REG[0] = 0; //Zero the Zero Reg!!! RB_CPUHOOK(RB_GETPC()); { //printf("%08x\n", RB_GETPC()); { v810_timestamp_t timestamp = timestamp_rl; tmpop = RB_RDOP(0, 0); timestamp_rl = timestamp; } opcode = (tmpop >> 9) | IPendingCache; //printf("%02x\n", opcode >> 1); #if HAVE_COMPUTED_GOTO #define CGBEGIN static const void *const op_goto_table[256] = { #define CGE(l) &&l, #define CGEND }; goto *op_goto_table[opcode]; #else /* (uint8) cast for cheaper alternative to generated branch+compare bounds check instructions, but still more expensive than computed goto which needs no masking nor bounds checking. */ #define CGBEGIN { enum { CGESB = 1 + __COUNTER__ }; switch((uint8)opcode) { #define CGE(l) case __COUNTER__ - CGESB: goto l; #define CGEND } } #endif CGBEGIN CGE(op_MOV) CGE(op_MOV) CGE(op_ADD) CGE(op_ADD) CGE(op_SUB) CGE(op_SUB) CGE(op_CMP) CGE(op_CMP) CGE(op_SHL) CGE(op_SHL) CGE(op_SHR) CGE(op_SHR) CGE(op_JMP) CGE(op_JMP) CGE(op_SAR) CGE(op_SAR) CGE(op_MUL) CGE(op_MUL) CGE(op_DIV) CGE(op_DIV) CGE(op_MULU) CGE(op_MULU) CGE(op_DIVU) CGE(op_DIVU) CGE(op_OR) CGE(op_OR) CGE(op_AND) CGE(op_AND) CGE(op_XOR) CGE(op_XOR) CGE(op_NOT) CGE(op_NOT) CGE(op_MOV_I) CGE(op_MOV_I) CGE(op_ADD_I) CGE(op_ADD_I) CGE(op_SETF) CGE(op_SETF) CGE(op_CMP_I) CGE(op_CMP_I) CGE(op_SHL_I) CGE(op_SHL_I) CGE(op_SHR_I) CGE(op_SHR_I) CGE(op_EI) CGE(op_EI) CGE(op_SAR_I) CGE(op_SAR_I) CGE(op_TRAP) CGE(op_TRAP) CGE(op_RETI) CGE(op_RETI) CGE(op_HALT) CGE(op_HALT) CGE(op_INVALID) CGE(op_INVALID) CGE(op_LDSR) CGE(op_LDSR) CGE(op_STSR) CGE(op_STSR) CGE(op_DI) CGE(op_DI) CGE(op_BSTR) CGE(op_BSTR) CGE(op_BV) CGE(op_BL) CGE(op_BE) CGE(op_BNH) CGE(op_BN) CGE(op_BR) CGE(op_BLT) CGE(op_BLE) CGE(op_BNV) CGE(op_BNL) CGE(op_BNE) CGE(op_BH) CGE(op_BP) CGE(op_NOP) CGE(op_BGE) CGE(op_BGT) CGE(op_MOVEA) CGE(op_MOVEA) CGE(op_ADDI) CGE(op_ADDI) CGE(op_JR) CGE(op_JR) CGE(op_JAL) CGE(op_JAL) CGE(op_ORI) CGE(op_ORI) CGE(op_ANDI) CGE(op_ANDI) CGE(op_XORI) CGE(op_XORI) CGE(op_MOVHI) CGE(op_MOVHI) CGE(op_LD_B) CGE(op_LD_B) CGE(op_LD_H) CGE(op_LD_H) CGE(op_INVALID) CGE(op_INVALID) CGE(op_LD_W) CGE(op_LD_W) CGE(op_ST_B) CGE(op_ST_B) CGE(op_ST_H) CGE(op_ST_H) CGE(op_INVALID) CGE(op_INVALID) CGE(op_ST_W) CGE(op_ST_W) CGE(op_IN_B) CGE(op_IN_B) CGE(op_IN_H) CGE(op_IN_H) CGE(op_CAXI) CGE(op_CAXI) CGE(op_IN_W) CGE(op_IN_W) CGE(op_OUT_B) CGE(op_OUT_B) CGE(op_OUT_H) CGE(op_OUT_H) CGE(op_FPP) CGE(op_FPP) CGE(op_OUT_W) CGE(op_OUT_W) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INVALID) CGE(op_INT_HANDLER) CGEND // Bit string subopcodes #define DO_AM_BSTR() \ const uint32 arg1 = (tmpop >> 5) & 0x1F; \ const uint32 arg2 = (tmpop & 0x1F); \ RB_INCPCBY2(); #define DO_AM_FPP() \ const uint32 arg1 = (tmpop >> 5) & 0x1F; \ const uint32 arg2 = (tmpop & 0x1F); \ const uint32 arg3 = ((RB_RDOP(2) >> 10)&0x3F); \ RB_INCPCBY4(); #define DO_AM_UDEF() \ RB_INCPCBY2(); #define DO_AM_I() \ const uint32 arg1 = tmpop & 0x1F; \ const uint32 arg2 = (tmpop >> 5) & 0x1F; \ RB_INCPCBY2(); #define DO_AM_II() DO_AM_I(); #define DO_AM_IV() \ const uint32 arg1 = ((tmpop & 0x000003FF) << 16) | RB_RDOP(2); \ #define DO_AM_V() \ const uint32 arg3 = (tmpop >> 5) & 0x1F; \ const uint32 arg2 = tmpop & 0x1F; \ const uint32 arg1 = RB_RDOP(2); \ RB_INCPCBY4(); #define DO_AM_VIa() \ const uint32 arg1 = RB_RDOP(2); \ const uint32 arg2 = tmpop & 0x1F; \ const uint32 arg3 = (tmpop >> 5) & 0x1F; \ RB_INCPCBY4(); \ #define DO_AM_VIb() \ const uint32 arg1 = (tmpop >> 5) & 0x1F; \ const uint32 arg2 = RB_RDOP(2); \ const uint32 arg3 = (tmpop & 0x1F); \ RB_INCPCBY4(); \ #define DO_AM_IX() \ const uint32 arg1 = (tmpop & 0x1); \ RB_INCPCBY2(); \ #define DO_AM_III() \ const uint32 arg1 = tmpop & 0x1FE; #include "v810_do_am.h" #define BEGIN_OP(meowtmpop) { op_##meowtmpop: v810_timestamp_t timestamp = timestamp_rl; DO_##meowtmpop ##_AM(); #define END_OP() timestamp_rl = timestamp; goto OpFinished; } #define END_OP_SKIPLO() timestamp_rl = timestamp; goto OpFinishedSkipLO; } BEGIN_OP(MOV); ADDCLOCK(1); SetPREG(arg2, P_REG[arg1]); END_OP(); BEGIN_OP(ADD); ADDCLOCK(1); uint32 temp = P_REG[arg2] + P_REG[arg1]; SetFlag(PSW_OV, ((P_REG[arg2]^(~P_REG[arg1]))&(P_REG[arg2]^temp))&0x80000000); SetFlag(PSW_CY, temp < P_REG[arg2]); SetPREG(arg2, temp); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(SUB); ADDCLOCK(1); uint32 temp = P_REG[arg2] - P_REG[arg1]; SetFlag(PSW_OV, ((P_REG[arg2]^P_REG[arg1])&(P_REG[arg2]^temp))&0x80000000); SetFlag(PSW_CY, temp > P_REG[arg2]); SetPREG(arg2, temp); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(CMP); ADDCLOCK(1); uint32 temp = P_REG[arg2] - P_REG[arg1]; SetSZ(temp); SetFlag(PSW_OV, ((P_REG[arg2]^P_REG[arg1])&(P_REG[arg2]^temp))&0x80000000); SetFlag(PSW_CY, temp > P_REG[arg2]); END_OP(); BEGIN_OP(SHL); ADDCLOCK(1); val = P_REG[arg1] & 0x1F; // set CY before we destroy the regisrer info.... SetFlag(PSW_CY, (val != 0) && ((P_REG[arg2] >> (32 - val))&0x01) ); SetFlag(PSW_OV, FALSE); SetPREG(arg2, P_REG[arg2] << val); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(SHR); ADDCLOCK(1); val = P_REG[arg1] & 0x1F; // set CY before we destroy the regisrer info.... SetFlag(PSW_CY, (val) && ((P_REG[arg2] >> (val-1))&0x01)); SetFlag(PSW_OV, FALSE); SetPREG(arg2, P_REG[arg2] >> val); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(JMP); (void)arg2; // arg2 is unused. ADDCLOCK(3); RB_SETPC((P_REG[arg1] & 0xFFFFFFFE)); if(RB_AccurateMode) { BRANCH_ALIGN_CHECK(PC); } RB_ADDBT(old_PC, RB_GETPC(), 0); END_OP(); BEGIN_OP(SAR); ADDCLOCK(1); val = P_REG[arg1] & 0x1F; SetFlag(PSW_CY, (val) && ((P_REG[arg2]>>(val-1))&0x01) ); SetFlag(PSW_OV, FALSE); SetPREG(arg2, (uint32) ((int32)P_REG[arg2] >> val)); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(OR); ADDCLOCK(1); SetPREG(arg2, P_REG[arg1] | P_REG[arg2]); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(AND); ADDCLOCK(1); SetPREG(arg2, P_REG[arg1] & P_REG[arg2]); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(XOR); ADDCLOCK(1); SetPREG(arg2, P_REG[arg1] ^ P_REG[arg2]); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(NOT); ADDCLOCK(1); SetPREG(arg2, ~P_REG[arg1]); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(MOV_I); ADDCLOCK(1); SetPREG(arg2,sign_5(arg1)); END_OP(); BEGIN_OP(ADD_I); ADDCLOCK(1); uint32 temp = P_REG[arg2] + sign_5(arg1); SetFlag(PSW_OV, ((P_REG[arg2]^(~sign_5(arg1)))&(P_REG[arg2]^temp))&0x80000000); SetFlag(PSW_CY, (uint32)temp < P_REG[arg2]); SetPREG(arg2, (uint32)temp); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(SETF); ADDCLOCK(1); P_REG[arg2] = 0; switch (arg1 & 0x0F) { case COND_V: if (TESTCOND_V) P_REG[arg2] = 1; break; case COND_C: if (TESTCOND_C) P_REG[arg2] = 1; break; case COND_Z: if (TESTCOND_Z) P_REG[arg2] = 1; break; case COND_NH: if (TESTCOND_NH) P_REG[arg2] = 1; break; case COND_S: if (TESTCOND_S) P_REG[arg2] = 1; break; case COND_T: P_REG[arg2] = 1; break; case COND_LT: if (TESTCOND_LT) P_REG[arg2] = 1; break; case COND_LE: if (TESTCOND_LE) P_REG[arg2] = 1; break; case COND_NV: if (TESTCOND_NV) P_REG[arg2] = 1; break; case COND_NC: if (TESTCOND_NC) P_REG[arg2] = 1; break; case COND_NZ: if (TESTCOND_NZ) P_REG[arg2] = 1; break; case COND_H: if (TESTCOND_H) P_REG[arg2] = 1; break; case COND_NS: if (TESTCOND_NS) P_REG[arg2] = 1; break; case COND_F: //always false! do nothing more break; case COND_GE: if (TESTCOND_GE) P_REG[arg2] = 1; break; case COND_GT: if (TESTCOND_GT) P_REG[arg2] = 1; break; } END_OP(); BEGIN_OP(CMP_I); ADDCLOCK(1); uint32 temp = P_REG[arg2] - sign_5(arg1); SetSZ(temp); SetFlag(PSW_OV, ((P_REG[arg2]^(sign_5(arg1)))&(P_REG[arg2]^temp))&0x80000000); SetFlag(PSW_CY, temp > P_REG[arg2]); END_OP(); BEGIN_OP(SHR_I); ADDCLOCK(1); SetFlag(PSW_CY, arg1 && ((P_REG[arg2] >> (arg1-1))&0x01) ); // set CY before we destroy the regisrer info.... SetPREG(arg2, P_REG[arg2] >> arg1); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(SHL_I); ADDCLOCK(1); SetFlag(PSW_CY, arg1 && ((P_REG[arg2] >> (32 - arg1))&0x01) ); // set CY before we destroy the regisrer info.... SetPREG(arg2, P_REG[arg2] << arg1); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(SAR_I); ADDCLOCK(1); SetFlag(PSW_CY, arg1 && ((P_REG[arg2]>>(arg1-1))&0x01) ); SetPREG(arg2, (uint32) ((int32)P_REG[arg2] >> arg1)); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg2]); END_OP(); BEGIN_OP(LDSR); // Loads a Sys Reg with the value in specified PR ADDCLOCK(1); // ? SetSREG(timestamp, arg1 & 0x1F, P_REG[arg2 & 0x1F]); END_OP(); BEGIN_OP(STSR); // Loads a PR with the value in specified Sys Reg ADDCLOCK(1); // ? P_REG[arg2 & 0x1F] = GetSREG(arg1 & 0x1F); END_OP(); BEGIN_OP(EI); (void)arg1; // arg1 is unused. (void)arg2; // arg2 is unused. if(VBMode) { ADDCLOCK(1); S_REG[PSW] = S_REG[PSW] &~ PSW_ID; RecalcIPendingCache(); } else { ADDCLOCK(1); RB_DECPCBY2(); Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); CHECK_HALTED(); } END_OP(); BEGIN_OP(DI); (void)arg1; // arg1 is unused. (void)arg2; // arg2 is unused. if(VBMode) { ADDCLOCK(1); S_REG[PSW] |= PSW_ID; IPendingCache = 0; } else { ADDCLOCK(1); RB_DECPCBY2(); Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); CHECK_HALTED(); } END_OP(); #define COND_BRANCH(cond) \ if(cond) \ { \ ADDCLOCK(3); \ RB_PCRELCHANGE(sign_9(arg1) & 0xFFFFFFFE); \ if(RB_AccurateMode) \ { \ BRANCH_ALIGN_CHECK(PC); \ } \ RB_ADDBT(old_PC, RB_GETPC(), 0); \ } \ else \ { \ ADDCLOCK(1); \ RB_INCPCBY2(); \ } BEGIN_OP(BV); COND_BRANCH(TESTCOND_V); END_OP(); BEGIN_OP(BL); COND_BRANCH(TESTCOND_L); END_OP(); BEGIN_OP(BE); COND_BRANCH(TESTCOND_E); END_OP(); BEGIN_OP(BNH); COND_BRANCH(TESTCOND_NH); END_OP(); BEGIN_OP(BN); COND_BRANCH(TESTCOND_N); END_OP(); BEGIN_OP(BR); COND_BRANCH(TRUE); END_OP(); BEGIN_OP(BLT); COND_BRANCH(TESTCOND_LT); END_OP(); BEGIN_OP(BLE); COND_BRANCH(TESTCOND_LE); END_OP(); BEGIN_OP(BNV); COND_BRANCH(TESTCOND_NV); END_OP(); BEGIN_OP(BNL); COND_BRANCH(TESTCOND_NL); END_OP(); BEGIN_OP(BNE); COND_BRANCH(TESTCOND_NE); END_OP(); BEGIN_OP(BH); COND_BRANCH(TESTCOND_H); END_OP(); BEGIN_OP(BP); COND_BRANCH(TESTCOND_P); END_OP(); BEGIN_OP(BGE); COND_BRANCH(TESTCOND_GE); END_OP(); BEGIN_OP(BGT); COND_BRANCH(TESTCOND_GT); END_OP(); BEGIN_OP(JR); ADDCLOCK(3); RB_PCRELCHANGE(sign_26(arg1) & 0xFFFFFFFE); if(RB_AccurateMode) { BRANCH_ALIGN_CHECK(PC); } RB_ADDBT(old_PC, RB_GETPC(), 0); END_OP(); BEGIN_OP(JAL); ADDCLOCK(3); P_REG[31] = RB_GETPC() + 4; RB_PCRELCHANGE(sign_26(arg1) & 0xFFFFFFFE); if(RB_AccurateMode) { BRANCH_ALIGN_CHECK(PC); } RB_ADDBT(old_PC, RB_GETPC(), 0); END_OP(); BEGIN_OP(MOVEA); ADDCLOCK(1); SetPREG(arg3, P_REG[arg2] + sign_16(arg1)); END_OP(); BEGIN_OP(ADDI); ADDCLOCK(1); uint32 temp = P_REG[arg2] + sign_16(arg1); SetFlag(PSW_OV, ((P_REG[arg2]^(~sign_16(arg1)))&(P_REG[arg2]^temp))&0x80000000); SetFlag(PSW_CY, (uint32)temp < P_REG[arg2]); SetPREG(arg3, (uint32)temp); SetSZ(P_REG[arg3]); END_OP(); BEGIN_OP(ORI); ADDCLOCK(1); SetPREG(arg3, arg1 | P_REG[arg2]); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg3]); END_OP(); BEGIN_OP(ANDI); ADDCLOCK(1); SetPREG(arg3, (arg1 & P_REG[arg2])); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg3]); END_OP(); BEGIN_OP(XORI); ADDCLOCK(1); SetPREG(arg3, arg1 ^ P_REG[arg2]); SetFlag(PSW_OV, FALSE); SetSZ(P_REG[arg3]); END_OP(); BEGIN_OP(MOVHI); ADDCLOCK(1); SetPREG(arg3, (arg1 << 16) + P_REG[arg2]); END_OP(); // LD.B BEGIN_OP(LD_B); ADDCLOCK(1); tmp2 = (sign_16(arg1)+P_REG[arg2])&0xFFFFFFFF; SetPREG(arg3, sign_8(MemRead8(timestamp, tmp2))); //should be 3 clocks when executed alone, 2 when precedes another LD, or 1 //when precedes an instruction with many clocks (I'm guessing FP, MUL, DIV, etc) if(lastop >= 0) { if(lastop == LASTOP_LD) { ADDCLOCK(1); } else { ADDCLOCK(2); } } lastop = LASTOP_LD; END_OP_SKIPLO(); // LD.H BEGIN_OP(LD_H); ADDCLOCK(1); tmp2 = (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFE; SetPREG(arg3, sign_16(MemRead16(timestamp, tmp2))); if(lastop >= 0) { if(lastop == LASTOP_LD) { ADDCLOCK(1); } else { ADDCLOCK(2); } } lastop = LASTOP_LD; END_OP_SKIPLO(); // LD.W BEGIN_OP(LD_W); ADDCLOCK(1); tmp2 = (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFC; if(MemReadBus32[tmp2 >> 24]) { SetPREG(arg3, MemRead32(timestamp, tmp2)); if(lastop >= 0) { if(lastop == LASTOP_LD) { ADDCLOCK(1); } else { ADDCLOCK(2); } } } else { uint32 rv; rv = MemRead16(timestamp, tmp2); rv |= MemRead16(timestamp, tmp2 | 2) << 16; SetPREG(arg3, rv); if(lastop >= 0) { if(lastop == LASTOP_LD) { ADDCLOCK(3); } else { ADDCLOCK(4); } } } lastop = LASTOP_LD; END_OP_SKIPLO(); // ST.B BEGIN_OP(ST_B); ADDCLOCK(1); MemWrite8(timestamp, sign_16(arg2)+P_REG[arg3], P_REG[arg1] & 0xFF); if(lastop == LASTOP_ST) { ADDCLOCK(1); } lastop = LASTOP_ST; END_OP_SKIPLO(); // ST.H BEGIN_OP(ST_H); ADDCLOCK(1); MemWrite16(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFE, P_REG[arg1] & 0xFFFF); if(lastop == LASTOP_ST) { ADDCLOCK(1); } lastop = LASTOP_ST; END_OP_SKIPLO(); // ST.W BEGIN_OP(ST_W); ADDCLOCK(1); tmp2 = (sign_16(arg2)+P_REG[arg3]) & 0xFFFFFFFC; if(MemWriteBus32[tmp2 >> 24]) { MemWrite32(timestamp, tmp2, P_REG[arg1]); if(lastop == LASTOP_ST) { ADDCLOCK(1); } } else { MemWrite16(timestamp, tmp2, P_REG[arg1] & 0xFFFF); MemWrite16(timestamp, tmp2 | 2, P_REG[arg1] >> 16); if(lastop == LASTOP_ST) { ADDCLOCK(3); } } lastop = LASTOP_ST; END_OP_SKIPLO(); // IN.B BEGIN_OP(IN_B); { ADDCLOCK(3); SetPREG(arg3, IORead8(timestamp, sign_16(arg1)+P_REG[arg2])); } lastop = LASTOP_IN; END_OP_SKIPLO(); // IN.H BEGIN_OP(IN_H); { ADDCLOCK(3); SetPREG(arg3, IORead16(timestamp, (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFE)); } lastop = LASTOP_IN; END_OP_SKIPLO(); // IN.W BEGIN_OP(IN_W); if(IORead32) { ADDCLOCK(3); SetPREG(arg3, IORead32(timestamp, (sign_16(arg1)+P_REG[arg2]) & 0xFFFFFFFC)); } else { uint32 eff_addr = (sign_16(arg1) + P_REG[arg2]) & 0xFFFFFFFC; uint32 rv; ADDCLOCK(5); rv = IORead16(timestamp, eff_addr); rv |= IORead16(timestamp, eff_addr | 2) << 16; SetPREG(arg3, rv); } lastop = LASTOP_IN; END_OP_SKIPLO(); // OUT.B BEGIN_OP(OUT_B); ADDCLOCK(1); IOWrite8(timestamp, sign_16(arg2)+P_REG[arg3],P_REG[arg1]&0xFF); if(lastop == LASTOP_OUT) { ADDCLOCK(1); } lastop = LASTOP_OUT; END_OP_SKIPLO(); // OUT.H BEGIN_OP(OUT_H); ADDCLOCK(1); IOWrite16(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFE,P_REG[arg1]&0xFFFF); if(lastop == LASTOP_OUT) { ADDCLOCK(1); } lastop = LASTOP_OUT; END_OP_SKIPLO(); // OUT.W BEGIN_OP(OUT_W); ADDCLOCK(1); if(IOWrite32) IOWrite32(timestamp, (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFC,P_REG[arg1]); else { uint32 eff_addr = (sign_16(arg2)+P_REG[arg3])&0xFFFFFFFC; IOWrite16(timestamp, eff_addr, P_REG[arg1] & 0xFFFF); IOWrite16(timestamp, eff_addr | 2, P_REG[arg1] >> 16); } if(lastop == LASTOP_OUT) { if(IOWrite32) { ADDCLOCK(1); } else { ADDCLOCK(3); } } lastop = LASTOP_OUT; END_OP_SKIPLO(); BEGIN_OP(NOP); (void)arg1; // arg1 is unused. ADDCLOCK(1); RB_INCPCBY2(); END_OP(); BEGIN_OP(RETI); (void)arg1; // arg1 is unused. ADDCLOCK(10); //Return from Trap/Interupt if(S_REG[PSW] & PSW_NP) { // Read the FE Reg RB_SETPC(S_REG[FEPC] & 0xFFFFFFFE); S_REG[PSW] = S_REG[FEPSW]; } else { //Read the EI Reg Interupt RB_SETPC(S_REG[EIPC] & 0xFFFFFFFE); S_REG[PSW] = S_REG[EIPSW]; } RecalcIPendingCache(); RB_ADDBT(old_PC, RB_GETPC(), 0); END_OP(); BEGIN_OP(MUL); ADDCLOCK(13); uint64 temp = (int64)(int32)P_REG[arg1] * (int32)P_REG[arg2]; SetPREG(30, (uint32)(temp >> 32)); SetPREG(arg2, temp); SetSZ(P_REG[arg2]); SetFlag(PSW_OV, temp != (uint64)(int64)(int32)(uint32)temp); lastop = -1; END_OP_SKIPLO(); BEGIN_OP(MULU); ADDCLOCK(13); uint64 temp = (uint64)P_REG[arg1] * (uint64)P_REG[arg2]; SetPREG(30, (uint32)(temp >> 32)); SetPREG(arg2, (uint32)temp); SetSZ(P_REG[arg2]); SetFlag(PSW_OV, temp != (uint32)temp); lastop = -1; END_OP_SKIPLO(); BEGIN_OP(DIVU); ADDCLOCK(36); if(P_REG[arg1] == 0) // Divide by zero! { RB_DECPCBY2(); Exception(ZERO_DIV_HANDLER_ADDR, ECODE_ZERO_DIV); CHECK_HALTED(); } else { // Careful here, since arg2 can be == 30 uint32 quotient = (uint32)P_REG[arg2] / (uint32)P_REG[arg1]; uint32 remainder = (uint32)P_REG[arg2] % (uint32)P_REG[arg1]; SetPREG(30, remainder); SetPREG(arg2, quotient); SetFlag(PSW_OV, FALSE); SetSZ(quotient); } lastop = -1; END_OP_SKIPLO(); BEGIN_OP(DIV); //if(P_REG[arg1] & P_REG[arg2] & 0x80000000) //{ // printf("Div: %08x %08x\n", P_REG[arg1], P_REG[arg2]); //} ADDCLOCK(38); if((uint32)P_REG[arg1] == 0) // Divide by zero! { RB_DECPCBY2(); Exception(ZERO_DIV_HANDLER_ADDR, ECODE_ZERO_DIV); CHECK_HALTED(); } else { if((P_REG[arg2]==0x80000000)&&(P_REG[arg1]==0xFFFFFFFF)) { SetFlag(PSW_OV, TRUE); P_REG[30]=0; SetPREG(arg2, 0x80000000); SetSZ(P_REG[arg2]); } else { // Careful here, since arg2 can be == 30 uint32 quotient = (int32)P_REG[arg2] / (int32)P_REG[arg1]; uint32 remainder = (int32)P_REG[arg2] % (int32)P_REG[arg1]; SetPREG(30, remainder); SetPREG(arg2, quotient); SetFlag(PSW_OV, FALSE); SetSZ(quotient); } } lastop = -1; END_OP_SKIPLO(); BEGIN_OP(FPP); ADDCLOCK(1); fpu_subop(timestamp, arg3, arg1, arg2); lastop = -1; CHECK_HALTED(); END_OP_SKIPLO(); BEGIN_OP(BSTR); if(!in_bstr) { ADDCLOCK(1); } if(bstr_subop(timestamp, arg2, arg1)) { RB_DECPCBY2(); in_bstr = TRUE; in_bstr_to = tmpop; } else { in_bstr = FALSE; have_src_cache = have_dst_cache = FALSE; } END_OP(); BEGIN_OP(HALT); (void)arg1; // arg1 is unused. ADDCLOCK(1); Halted = HALT_HALT; //printf("Untested opcode: HALT\n"); END_OP(); BEGIN_OP(TRAP); (void)arg2; // arg2 is unused. ADDCLOCK(15); Exception(TRAP_HANDLER_BASE + (arg1 & 0x10), ECODE_TRAP_BASE + (arg1 & 0x1F)); CHECK_HALTED(); END_OP(); BEGIN_OP(CAXI); //printf("Untested opcode: caxi\n"); // Lock bus(N/A) ADDCLOCK(26); { uint32 addr, tmp, compare_temp; uint32 to_write; addr = sign_16(arg1) + P_REG[arg2]; addr &= ~3; if(MemReadBus32[addr >> 24]) tmp = MemRead32(timestamp, addr); else { tmp = MemRead16(timestamp, addr); tmp |= MemRead16(timestamp, addr | 2) << 16; } compare_temp = P_REG[arg3] - tmp; SetSZ(compare_temp); SetFlag(PSW_OV, ((P_REG[arg3]^tmp)&(P_REG[arg3]^compare_temp))&0x80000000); SetFlag(PSW_CY, compare_temp > P_REG[arg3]); if(!compare_temp) // If they're equal... to_write = P_REG[30]; else to_write = tmp; if(MemWriteBus32[addr >> 24]) MemWrite32(timestamp, addr, to_write); else { MemWrite16(timestamp, addr, to_write & 0xFFFF); MemWrite16(timestamp, addr | 2, to_write >> 16); } P_REG[arg3] = tmp; } // Unlock bus(N/A) END_OP(); op_INT_HANDLER: { int iNum = ilevel; S_REG[EIPC] = GetPC(); S_REG[EIPSW] = S_REG[PSW]; SetPC(0xFFFFFE00 | (iNum << 4)); RB_ADDBT(old_PC, RB_GETPC(), 0xFE00 | (iNum << 4)); S_REG[ECR] = 0xFE00 | (iNum << 4); S_REG[PSW] |= PSW_EP; S_REG[PSW] |= PSW_ID; S_REG[PSW] &= ~PSW_AE; // Now, set need to set the interrupt enable level to he level that is being processed + 1, // saturating at 15. iNum++; if(iNum > 0x0F) iNum = 0x0F; S_REG[PSW] &= ~PSW_IA; S_REG[PSW] |= iNum << 16; // Accepting an interrupt takes us out of normal HALT status, of course! Halted = HALT_NONE; // Invalidate our bitstring state(forces the instruction to be re-read, and the r/w buffers reloaded). in_bstr = FALSE; have_src_cache = FALSE; have_dst_cache = FALSE; IPendingCache = 0; goto OpFinished; } BEGIN_OP(INVALID); RB_DECPCBY2(); if(!RB_AccurateMode) { RB_SETPC(RB_GETPC()); if((uint32)(RB_RDOP(0, 0) >> 9) != opcode) { //printf("Trampoline: %08x %02x\n", RB_GETPC(), opcode >> 1); } else { ADDCLOCK(1); Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); CHECK_HALTED(); } } else { ADDCLOCK(1); Exception(INVALID_OP_HANDLER_ADDR, ECODE_INVALID_OP); CHECK_HALTED(); } END_OP(); } OpFinished: ; lastop = opcode; OpFinishedSkipLO: ; } // end while(timestamp_rl < next_event_ts) next_event_ts = event_handler(timestamp_rl); //printf("Next: %d, Cur: %d\n", next_event_ts, timestamp); } v810_timestamp = timestamp_rl;