/* This file is included through vue.c and cannot be built directly. */ #ifdef VUEAPI /***************************************************************************** * Constants * *****************************************************************************/ /* Stages */ #define CPU_FATAL -1 #define CPU_FETCH 0 #define CPU_EXECUTE 1 #define CPU_EXCEPTION 2 #define CPU_HALT 3 #define CPU_CLEAR 4 #define CPU_DUMP 5 #define CPU_RESTORE 6 /* Intermediate instruction IDs */ #define BITSTRING -2 #define FLOATENDO -3 /* Opcode lookup table */ static const int8_t LOOKUP_OPCODE[] = { VUE_MOV_REG, 1, VUE_ADD_REG, 1, VUE_SUB , 1, VUE_CMP_REG, 1, VUE_SHL_REG, 1, VUE_SHR_REG, 1, VUE_JMP , 1, VUE_SAR_REG, 1, VUE_MUL , 1, VUE_DIV , 1, VUE_MULU , 1, VUE_DIVU , 1, VUE_OR , 1, VUE_AND , 1, VUE_XOR , 1, VUE_NOT , 1, VUE_MOV_IMM,-2, VUE_ADD_IMM,-2, VUE_SETF , 2, VUE_CMP_IMM,-2, VUE_SHL_IMM, 2, VUE_SHR_IMM, 2, VUE_CLI , 2, VUE_SAR_IMM, 2, VUE_TRAP , 2, VUE_RETI , 2, VUE_HALT , 2, VUE_ILLEGAL, 0, VUE_LDSR , 2, VUE_STSR , 2, VUE_SEI , 2, BITSTRING , 2, VUE_BCOND , 3, VUE_BCOND , 3, VUE_BCOND , 3, VUE_BCOND , 3, VUE_BCOND , 3, VUE_BCOND , 3, VUE_BCOND , 3, VUE_BCOND , 3, VUE_MOVEA ,-5, VUE_ADDI ,-5, VUE_JR , 4, VUE_JAL , 4, VUE_ORI , 5, VUE_ANDI , 5, VUE_XORI , 5, VUE_MOVHI , 5, VUE_LD_B , 6, VUE_LD_H , 6, VUE_ILLEGAL, 0, VUE_LD_W , 6, VUE_ST_B , 6, VUE_ST_H , 6, VUE_ILLEGAL, 0, VUE_ST_W , 6, VUE_IN_B , 6, VUE_IN_H , 6, VUE_CAXI , 6, VUE_IN_W , 6, VUE_OUT_B , 6, VUE_OUT_H , 6, FLOATENDO , 7, VUE_OUT_W , 6 }; /* Bit string lookup table */ static const int8_t LOOKUP_BITSTRING[] = { VUE_SCH0BSU, VUE_SCH0BSD, VUE_SCH1BSU, VUE_SCH1BSD, VUE_ILLEGAL, VUE_ILLEGAL, VUE_ILLEGAL, VUE_ILLEGAL, VUE_ORBSU , VUE_ANDBSU , VUE_XORBSU , VUE_MOVBSU , VUE_ORNBSU , VUE_ANDNBSU, VUE_XORNBSU, VUE_XORNBSU }; /* Floating-point and Nintendo lookup table */ static const int8_t LOOKUP_FLOATENDO[] = { VUE_CMPF_S , VUE_ILLEGAL, VUE_CVT_WS , VUE_CVT_SW , VUE_ADDF_S , VUE_SUBF_S , VUE_MULF_S , VUE_DIVF_S , VUE_XB , VUE_XH , VUE_REV , VUE_TRNC_SW, VUE_MPYHW , VUE_ILLEGAL, VUE_ILLEGAL, VUE_ILLEGAL }; /* Instruction cycle counts */ static const int8_t CYCLES[] = { 1, 1, 28, 1, 1, 1, 1, 1, 1, 26, 12, 1, 1, 10, 14, 16, 38, 44, 36, 1, 5, 5, 5, 3, 3, 3, 5, 5, 5, 8, 1, 1, 1, 1, 1, 9, 13, 30, 13, 1, 1, 1, 1, 1, 1, 4, 4, 4, 10, 22, 1, 1, 1, 1, 1, 1, 12, 1, 1, 1, 1, 1, 4, 4, 4, 8, 1, 28, 15, 14, 6, 1, 1, 1, 1, 1 }; /***************************************************************************** * Instruction Helpers * *****************************************************************************/ /* Integer addition */ static int32_t cpuAdd(Vue *vue, int left, int right) { int32_t result = left + right; vue->cpu.psw_cy = (uint32_t) result < (uint32_t) left ? 1 : 0; vue->cpu.psw_s = result >> 31 & 1; vue->cpu.psw_ov = (~(left ^ right) & (left ^ result)) >> 31 & 1; vue->cpu.psw_z = result ? 0 : 1; return result; } /* Bitwise operation */ static void cpuBitwise(Vue *vue, int32_t result) { vue->cpu.program[vue->cpu.inst.reg2] = result; vue->cpu.psw_ov = 0; vue->cpu.psw_s = result >> 31 & 1; vue->cpu.psw_z = result ? 0 : 1; } /* Reinterpret integer bits as a float promoted to double */ #define cpuFloat(x) ((double) *(float *)&(x)) /* Determine whether the floating-point operands are reserved values */ static vbool cpuFloatReserved(Vue *vue, vbool left) { int32_t exponent; /* Operand exponent */ int32_t operand; /* Current operand */ int x; /* Iterator */ /* Operands */ int32_t operands[2]; operands[0] = vue->cpu.program[vue->cpu.inst.reg2]; operands[1] = vue->cpu.program[vue->cpu.inst.reg1]; /* Process both operands */ for (x = left ^ 1; x < 2; x++) { operand = operands[x]; exponent = operand & 0x7F800000; /* Check for a reserved operand */ if (!( exponent == 0x7F800000 || /* Indefinite */ (exponent == 0 && (operand & 0x007FFFFF) != 0) /* Denormal */ )) continue; /* The value is a reserved operand */ vue->cpu.exception = 0xFF60; vue->cpu.psw_fro = 1; return VUE_TRUE; } /* Neither operand is a reserved value */ return VUE_FALSE; } /* Convert a floating short to word */ static void cpuFloatConvert(Vue *vue, vbool round) { int32_t bits; /* Number of bits to shift */ int32_t digits; /* Significant digits */ int32_t result; /* Output value */ int32_t sign; /* Sign of the result */ /* Reserved operand */ if (cpuFloatReserved(vue, VUE_FALSE)) return; /* Parse the floating-point value */ result = vue->cpu.program[vue->cpu.inst.reg1]; bits = (result >> 23 & 0xFF) - 150; digits = (result & 0x007FFFFF) | 0x00800000; sign = result & 0x80000000; /* Maximum negative word value */ if (result == (int32_t) 0xCF000000) { digits = (int32_t) 0x80000000; sign = 0; } /* Invalid operation (word overflow) */ else if (bits >= 8) { vue->cpu.exception = 0xFF70; return; } /* Zero */ else if ((result & 0x7FFFFFFF) == 0) digits = 0; /* No significant digits are shifted out */ else if (bits >= 0) digits <<= bits; /* All significant digits are shifted out */ else if (bits < -24) { vue->cpu.psw_fpr = 1; /* The implicit leading 1 was lost */ digits = 0; } /* Some significant digits are shifted out */ else { result = 1 << (-bits - 1); /* Position of the "one half" bit */ if (digits & ((result << 1) - 1)) /* The lost bits are not all zero */ vue->cpu.psw_fpr = 1; if (round && (digits & result)) /* The "one half" bit is set */ digits += result; digits = digits >> -bits; } /* Common processing */ result = sign ? -digits : digits; vue->cpu.program[vue->cpu.inst.reg2] = result; vue->cpu.psw_ov = 0; vue->cpu.psw_s = result >> 31 & 1; vue->cpu.psw_z = result ? 0 : 1; } /* Floating-point result */ static void cpuFloatResult(Vue *vue, vbool compare, double full) { int32_t bits = 0x7F7FFFFF; /* Binary representation of result */ float result = *(float *)&bits; /* Operation output */ /* Overflow */ if (full > result || full < -result) { vue->cpu.exception = 0xFF64; vue->cpu.psw_fov = 1; return; } /* Process the result */ result = (float) full; bits = *(int32_t *)&result; /* Underflow */ if ((bits & 0x7F800000) == 0 && (bits & 0x007FFFFF) != 0) { bits = 0; result = 0; vue->cpu.psw_fud = 1; } /* Operations other than compare */ if (!compare) { vue->cpu.program[vue->cpu.inst.reg2] = bits; if (result != full) /* Precision degradation */ vue->cpu.psw_fpr = 1; } /* Common processing */ vue->cpu.psw_cy = vue->cpu.psw_s = bits >> 31 & 1; vue->cpu.psw_ov = 0; vue->cpu.psw_z = bits ? 0 : 1; } /* Read a system register */ static int32_t cpuGetSystemRegister(Vue *vue, int32_t index) { switch (index) { case VUE_ADTRE: return vue->cpu.adtre; case VUE_EIPC : return vue->cpu.eipc; case VUE_EIPSW: return vue->cpu.eipsw; case VUE_FEPC : return vue->cpu.fepc; case VUE_FEPSW: return vue->cpu.fepsw; case VUE_ECR : return vue->cpu.ecr_fecc << 16 | vue->cpu.ecr_eicc; case VUE_PIR : return 0x00005346; case VUE_TKCW : return 0x000000E0; case VUE_CHCW : return vue->cpu.chcw_ice << 1; case 29 : return vue->cpu.sr29; case 30 : return 0x00000004; case 31 : return vue->cpu.sr31; case VUE_PSW : return vue->cpu.psw_i << 16 | vue->cpu.psw_fov << 6 | vue->cpu.psw_np << 15 | vue->cpu.psw_fud << 5 | vue->cpu.psw_ep << 14 | vue->cpu.psw_fpr << 4 | vue->cpu.psw_ae << 13 | vue->cpu.psw_cy << 3 | vue->cpu.psw_id << 12 | vue->cpu.psw_ov << 2 | vue->cpu.psw_fro << 9 | vue->cpu.psw_s << 1 | vue->cpu.psw_fiv << 8 | vue->cpu.psw_z | vue->cpu.psw_fzd << 7 ; /* Remaining cases to encourage tableswitch */ case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 26: case 27: case 28: return 0; } return 1; /* Unreachable */ } /* Proxy for IN and LD */ #define cpuIN_LD(vue, type) \ if (!cpuRead(vue, vue->cpu.program[vue->cpu.inst.reg1] + \ vue->cpu.inst.disp, type, -1)) \ vue->cpu.program[vue->cpu.inst.reg2] = vue->cpu.access.value /* Transfer program to another address */ static void cpuJump(Vue *vue, int32_t address) { int level; if (address != vue->cpu.pc) { level = vue->cpu.psw_np ? 2 : vue->cpu.psw_ep; vue->cpu.jumpFrom[level] = vue->cpu.pc; vue->cpu.jumpTo [level] = address; } vue->cpu.pc = address - vue->cpu.inst.size; } /* Proxy for OUT and ST */ #define cpuOUT_ST(vue, type) \ cpuWrite(vue, vue->cpu.program[vue->cpu.inst.reg1] + \ vue->cpu.inst.disp, type, vue->cpu.program[vue->cpu.inst.reg2]) /* Perform a bus read */ static vbool cpuRead(Vue *vue, int32_t address, int8_t type, int8_t fetch) { /* Perform the operation */ vue->cpu.access.address = address; vue->cpu.access.fetch = fetch; vue->cpu.access.type = type; vue->cpu.access.value = vueRead(vue, address, type); /* There is no application callback */ if (vue->onRead == NULL) return VUE_FALSE; /* Call the application callback */ vue->breakCode = vue->onRead(vue, &vue->cpu.access); return vue->breakCode != 0; } /* Write a system register */ static int32_t cpuSetSystemRegister(Vue *vue, int32_t index, int32_t value, vbool debug) { switch (index) { case VUE_ADTRE: return vue->cpu.adtre = value & 0xFFFFFFFE; case VUE_EIPC : return vue->cpu.eipc = value & 0xFFFFFFFE; case VUE_EIPSW: return vue->cpu.eipsw = value & 0x000FF3FF; case VUE_FEPC : return vue->cpu.fepc = value & 0xFFFFFFFE; case VUE_FEPSW: return vue->cpu.fepsw = value & 0x000FF3FF; case 29 : return vue->cpu.sr29 = value; case 31 : return vue->cpu.sr31 = debug || value >= 0 ? value : -value; case VUE_CHCW : vue->cpu.chcw_cen = value >> 20 & 0x00000FFF; vue->cpu.chcw_cec = value >> 8 & 0x00000FFF; vue->cpu.chcw_sa = value >> 8 & 0x00FFFFFF; vue->cpu.chcw_icr = value >> 5 & 1; vue->cpu.chcw_icd = value >> 4 & 1; vue->cpu.chcw_ice = value >> 1 & 1; vue->cpu.chcw_icc = value & 1; /* Only one of ICC, ICD or ICR is set */ value &= 0x00000031; if ((value & (value - 1)) == 0) { /* Clear */ /* Dump */ /* Restore */ } return vue->cpu.chcw_ice << 1; case VUE_ECR: if (debug) { vue->cpu.ecr_fecc = value >> 16 & 0xFFFF; vue->cpu.ecr_eicc = value & 0xFFFF; } return vue->cpu.ecr_fecc << 16 | vue->cpu.ecr_eicc; case VUE_PSW : vue->cpu.psw_i = value >> 16 & 15; vue->cpu.psw_np = value >> 15 & 1; vue->cpu.psw_ep = value >> 14 & 1; vue->cpu.psw_ae = value >> 13 & 1; vue->cpu.psw_id = value >> 12 & 1; vue->cpu.psw_fro = value >> 9 & 1; vue->cpu.psw_fiv = value >> 8 & 1; vue->cpu.psw_fzd = value >> 7 & 1; vue->cpu.psw_fov = value >> 6 & 1; vue->cpu.psw_fud = value >> 5 & 1; vue->cpu.psw_fpr = value >> 4 & 1; vue->cpu.psw_cy = value >> 3 & 1; vue->cpu.psw_ov = value >> 2 & 1; vue->cpu.psw_s = value >> 1 & 1; vue->cpu.psw_z = value & 1; return value & 0x000FF3FF; /* Remaining cases to encourage tableswitch */ case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23: case 26: case 27: case 28: case 30: return 0; } return 1; /* Unreachable */ } /* Arithmetic shift right */ static void cpuShiftArithmetic(Vue *vue, int32_t value, int32_t bits) { bits &= 31; /* Nothing to do */ if (bits == 0) vue->cpu.psw_cy = 0; /* Perform the operation */ else { vue->cpu.psw_cy = value >> (bits - 1) & 1; value >>= bits; bits = 32 - bits; value = SIGN_EXTEND(bits, value); } /* Common processing */ cpuBitwise(vue, value); } /* Logical shift left */ static void cpuShiftLeft(Vue *vue, int32_t value, int32_t bits) { bits &= 31; /* Nothing to do */ if (bits == 0) vue->cpu.psw_cy = 0; /* Perform the operation */ else { vue->cpu.psw_cy = value >> (32 - bits) & 1; value <<= bits; } /* Commmon processing */ cpuBitwise(vue, value); } /* Logical shift right */ static void cpuShiftRight(Vue *vue, int32_t value, int32_t bits) { bits &= 31; /* Nothing to do */ if (bits == 0) vue->cpu.psw_cy = 0; /* Perform the operation */ else { vue->cpu.psw_cy = value >> (bits - 1) & 1; value = value >> bits & ~((uint32_t) 0xFFFFFFFF << (32 - bits)); } /* Commmon processing */ cpuBitwise(vue, value); } /* Integer subtraction */ static int32_t cpuSubtract(Vue *vue, int left, int right) { int32_t result = left - right; vue->cpu.psw_cy = (uint32_t) result > (uint32_t) left ? 1 : 0; vue->cpu.psw_s = result >> 31 & 1; vue->cpu.psw_ov = ((left ^ right) & (left ^ result)) >> 31 & 1; vue->cpu.psw_z = result ? 0 : 1; return result; } /* Test a condition */ static int8_t cpuTestCondition(Vue *vue, int32_t condition) { switch (condition) { case 0: return vue->cpu.psw_ov; case 1: return vue->cpu.psw_cy; case 2: return vue->cpu.psw_z; case 3: return vue->cpu.psw_cy | vue->cpu.psw_z; case 4: return vue->cpu.psw_s; case 5: return 1; case 6: return vue->cpu.psw_ov ^ vue->cpu.psw_s; case 7: return (vue->cpu.psw_ov ^ vue->cpu.psw_s) | vue->cpu.psw_z; } return cpuTestCondition(vue, condition & 7) ^ 1; } /* Perform a bus write */ static vbool cpuWrite(Vue *vue, int32_t address, int8_t type, int32_t value) { /* Prepare the operation */ vue->cpu.access.address = address; vue->cpu.access.fetch = -1; vue->cpu.access.type = type; vue->cpu.access.value = value; /* Application callback */ if (vue->onWrite != NULL) { vue->breakCode = vue->onWrite(vue, &vue->cpu.access); if (vue->breakCode != 0) return VUE_TRUE; if (vue->cpu.access.type == VUE_CANCEL) return VUE_FALSE; } /* Perform the operation */ vueWrite(vue, vue->cpu.access.address, vue->cpu.access.type, vue->cpu.access.value); return VUE_FALSE; } /***************************************************************************** * Instruction Functions * *****************************************************************************/ /* Add Immediate */ #define cpuADD_IMM(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = cpuAdd(vue, \ vue->cpu.program[vue->cpu.inst.reg2], vue->cpu.inst.imm) /* Add Register */ #define cpuADD_REG(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = cpuAdd(vue, \ vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.program[vue->cpu.inst.reg1]) /* Add Floating Short */ #define cpuADDF_S(vue) \ if (!cpuFloatReserved(vue, VUE_TRUE)) cpuFloatResult(vue, VUE_FALSE, \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg2]) + \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg1])) /* Add Immediate */ #define cpuADDI(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = cpuAdd(vue, \ vue->cpu.program[vue->cpu.inst.reg1], vue->cpu.inst.imm) /* And */ #define cpuAND(vue) \ cpuBitwise(vue, vue->cpu.program[vue->cpu.inst.reg2] & \ vue->cpu.program[vue->cpu.inst.reg1]) /* And Immediate */ #define cpuANDI(vue) \ cpuBitwise(vue, vue->cpu.program[vue->cpu.inst.reg1] & \ vue->cpu.inst.imm) /* Branch on Condition */ static void cpuBCOND(Vue *vue) { if (!cpuTestCondition(vue, vue->cpu.inst.cond & 15)) return; vue->cpu.cycles = 2; cpuJump(vue, vue->cpu.pc + vue->cpu.inst.disp); } /* Compare and Exchange Interlocked */ static void cpuCAXI(Vue *vue) { int32_t address = vue->cpu.program[vue->cpu.inst.reg1] + vue->cpu.inst.disp; int32_t left = vue->cpu.program[vue->cpu.inst.reg2]; int32_t lock; /* Value of lock word */ /* Retrieve the lock word */ if (cpuRead(vue, address, VUE_S32, -1)) return; lock = vue->cpu.access.value; /* Compare and exchange */ if (lock == left) vue->cpu.access.value = vue->cpu.program[30]; if (cpuWrite(vue, address, VUE_S32, vue->cpu.access.value)) return; /* Update CPU state */ cpuSubtract(vue, left, lock); vue->cpu.program[vue->cpu.inst.reg2] = lock; } /* Clear Interrupt Disable Flag */ #define cpuCLI(vue) \ vue->cpu.psw_id = 0 /* Compare Immediate */ #define cpuCMP_IMM(vue) \ cpuSubtract(vue, \ vue->cpu.program[vue->cpu.inst.reg2], vue->cpu.inst.imm) /* Compare Register */ #define cpuCMP_REG(vue) \ cpuSubtract(vue, \ vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.program[vue->cpu.inst.reg1]) /* Compare Floating Short */ #define cpuCMPF_S(vue) \ if (!cpuFloatReserved(vue, VUE_TRUE)) cpuFloatResult(vue, VUE_TRUE, \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg2]) - \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg1])) /* Convert Short Floating to Word */ #define cpuCVT_SW(vue) \ cpuFloatConvert(vue, VUE_TRUE) /* Convert Word Integer to Short */ static void cpuCVT_WS(Vue *vue) { int32_t bits = vue->cpu.program[vue->cpu.inst.reg1]; float result = (float) bits; vue->cpu.program[vue->cpu.inst.reg2] = *(int32_t *)&result; if (result != bits) /* Precision degradation */ vue->cpu.psw_fpr = 1; vue->cpu.psw_cy = vue->cpu.psw_s = bits >> 31 & 1; vue->cpu.psw_ov = 0; vue->cpu.psw_z = bits ? 0 : 1; } /* Divide */ static void cpuDIV(Vue *vue) { int32_t left = vue->cpu.program[vue->cpu.inst.reg2]; int32_t right = vue->cpu.program[vue->cpu.inst.reg1]; int32_t result; /* Zero division */ if (right == 0) { vue->cpu.exception = 0xFF80; return; } /* Special case */ if ((left == (int32_t) 0x80000000) && (right == (int32_t) 0xFFFFFFFF)) { vue->cpu.program[30] = 0; vue->cpu.program[vue->cpu.inst.reg2] = 0x80000000; vue->cpu.psw_ov = 1; vue->cpu.psw_s = 1; vue->cpu.psw_z = 0; return; } /* Regular case */ result = left / right; vue->cpu.program[30] = left % right; vue->cpu.program[vue->cpu.inst.reg2] = result; vue->cpu.psw_ov = 0; vue->cpu.psw_s = result >> 31 & 1; vue->cpu.psw_z = result ? 0 : 1; } /* Divide Floating Short */ static void cpuDIVF_S(Vue *vue) { int32_t left = vue->cpu.program[vue->cpu.inst.reg2]; int32_t right = vue->cpu.program[vue->cpu.inst.reg1]; /* Reserved operand */ if (cpuFloatReserved(vue, VUE_TRUE)) return; /* An exception has occurred */ if (!right) { vue->cpu.exception = left ? 0xFF68 : 0xFF70; return; } /* Perform the operation */ cpuFloatResult(vue, VUE_FALSE, cpuFloat(left) / cpuFloat(right)); } /* Divide Unsigned */ static void cpuDIVU(Vue *vue) { uint32_t left = vue->cpu.program[vue->cpu.inst.reg2]; uint32_t right = vue->cpu.program[vue->cpu.inst.reg1]; uint32_t result; /* Zero division */ if (right == 0) { vue->cpu.exception = 0xFF80; return; } /* Regular case */ result = left / right; vue->cpu.program[30] = left % right; vue->cpu.program[vue->cpu.inst.reg2] = result; vue->cpu.psw_ov = 0; vue->cpu.psw_s = result >> 31 & 1; vue->cpu.psw_z = result ? 0 : 1; } /* Halt */ #define cpuHALT(vue) \ vue->cpu.stage = CPU_HALT; vue->cpu.inst.size = 0 /* Input Byte from Port */ #define cpuIN_B(vue) cpuIN_LD(vue, VUE_U8) /* Input Halfword from Port */ #define cpuIN_H(vue) cpuIN_LD(vue, VUE_U16) /* Input Word from Port */ #define cpuIN_W(vue) cpuIN_LD(vue, VUE_S32) /* Jump and Link */ #define cpuJAL(vue) \ vue->cpu.program[31] = vue->cpu.pc + 4; \ cpuJump(vue, vue->cpu.pc + vue->cpu.inst.disp) /* Jump Register */ #define cpuJMP(vue) \ cpuJump(vue, vue->cpu.program[vue->cpu.inst.reg1]) /* Jump Relative */ #define cpuJR(vue) \ cpuJump(vue, vue->cpu.pc + vue->cpu.inst.disp) /* Load Byte */ #define cpuLD_B(vue) cpuIN_LD(vue, VUE_S8) /* Load Halfword */ #define cpuLD_H(vue) cpuIN_LD(vue, VUE_S16) /* Load Word */ #define cpuLD_W(vue) cpuIN_LD(vue, VUE_S32) /* Load to System Register */ #define cpuLDSR(vue) \ cpuSetSystemRegister(vue, vue->cpu.inst.imm & 31, \ vue->cpu.program[vue->cpu.inst.reg2], VUE_FALSE) /* Move Immediate */ #define cpuMOV_IMM(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = vue->cpu.inst.imm /* Move Register */ #define cpuMOV_REG(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = \ vue->cpu.program[vue->cpu.inst.reg1] /* Multiply Halfword */ static void cpuMPYHW(Vue *vue) { int32_t right = vue->cpu.program[vue->cpu.inst.reg1]; vue->cpu.program[vue->cpu.inst.reg2] *= SIGN_EXTEND(17, right); } /* Multiply */ static void cpuMUL(Vue *vue) { int64_t full = (int64_t) vue->cpu.program[vue->cpu.inst.reg2] * (int64_t) vue->cpu.program[vue->cpu.inst.reg1]; int32_t lower = (int32_t) full; vue->cpu.program[30] = (int32_t) (full >> 32); vue->cpu.program[vue->cpu.inst.reg2] = lower; vue->cpu.psw_ov = (int64_t) lower != full ? 1 : 0; vue->cpu.psw_s = lower >> 31 & 1; vue->cpu.psw_z = lower ? 0 : 1; } /* Multiply Floating Short */ #define cpuMULF_S(vue) \ if (!cpuFloatReserved(vue, VUE_TRUE)) cpuFloatResult(vue, VUE_FALSE, \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg2]) * \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg1])) /* Multiply Unsigned */ static void cpuMULU(Vue *vue) { uint64_t full = (uint64_t)(uint32_t) vue->cpu.program[vue->cpu.inst.reg2] * (uint64_t)(uint32_t) vue->cpu.program[vue->cpu.inst.reg1]; int32_t lower = (int32_t) full; vue->cpu.program[30] = (int32_t) (full >> 32); vue->cpu.program[vue->cpu.inst.reg2] = lower; vue->cpu.psw_ov = full > (uint32_t) 0xFFFFFFFF ? 1 : 0; vue->cpu.psw_s = lower >> 31 & 1; vue->cpu.psw_z = lower ? 0 : 1; } /* Add */ #define cpuMOVEA(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = \ vue->cpu.program[vue->cpu.inst.reg1] + vue->cpu.inst.imm /* Add */ #define cpuMOVHI(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = \ vue->cpu.program[vue->cpu.inst.reg1] + (vue->cpu.inst.imm << 16) /* Not */ #define cpuNOT(vue) \ cpuBitwise(vue, ~vue->cpu.program[vue->cpu.inst.reg1]) /* Output Byte to Port */ #define cpuOUT_B(vue) cpuOUT_ST(vue, VUE_U8) /* Output Halfword to Port */ #define cpuOUT_H(vue) cpuOUT_ST(vue, VUE_U16) /* Output Word to Port */ #define cpuOUT_W(vue) cpuOUT_ST(vue, VUE_S32) /* Or */ #define cpuOR(vue) \ cpuBitwise(vue, vue->cpu.program[vue->cpu.inst.reg2] | \ vue->cpu.program[vue->cpu.inst.reg1]) /* Or Immediate */ #define cpuORI(vue) \ cpuBitwise(vue, vue->cpu.program[vue->cpu.inst.reg1] | \ vue->cpu.inst.imm) /* Return from Trap or Interrupt */ static void cpuRETI(Vue *vue) { if (vue->cpu.psw_np) { vue->cpu.pc = vue->cpu.fepc; cpuSetSystemRegister(vue, VUE_PSW, vue->cpu.fepsw, VUE_FALSE); } else { vue->cpu.pc = vue->cpu.eipc; cpuSetSystemRegister(vue, VUE_PSW, vue->cpu.eipsw, VUE_FALSE); } } /* Shift Arithmetic Right by Immediate */ #define cpuSAR_IMM(vue) \ cpuShiftArithmetic(vue, vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.inst.imm) /* Shift Arithmetic Right by Register */ #define cpuSAR_REG(vue) \ cpuShiftArithmetic(vue, vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.program[vue->cpu.inst.reg1]) /* Set Interrupt Disable Flag */ #define cpuSEI(vue) \ vue->cpu.psw_id = 1 /* Set Flag Condition */ #define cpuSETF(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = \ cpuTestCondition(vue, vue->cpu.inst.imm & 15) /* Shift Logical Left by Immediate */ #define cpuSHL_IMM(vue) \ cpuShiftLeft(vue, vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.inst.imm) /* Shift Logical Left by Register */ #define cpuSHL_REG(vue) \ cpuShiftLeft(vue, vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.program[vue->cpu.inst.reg1]) /* Shift Logical Right by Immediate */ #define cpuSHR_IMM(vue) \ cpuShiftRight(vue, vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.inst.imm) /* Shift Logical Right by Register */ #define cpuSHR_REG(vue) \ cpuShiftRight(vue, vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.program[vue->cpu.inst.reg1]) /* Reverse Bits in Word */ static void cpuREV(Vue *vue) { int32_t value = vue->cpu.program[vue->cpu.inst.reg1]; value = (value >> 16 & 0x0000FFFF) | value << 16; value = (value >> 8 & 0x00FF00FF) | (value << 8 & 0xFF00FF00); value = (value >> 4 & 0x0F0F0F0F) | (value << 4 & 0xF0F0F0F0); value = (value >> 2 & 0x33333333) | (value << 2 & 0xCCCCCCCC); value = (value >> 1 & 0x55555555) | (value << 1 & 0xAAAAAAAA); vue->cpu.program[vue->cpu.inst.reg2] = value; } /* Store Byte */ #define cpuST_B(vue) cpuOUT_ST(vue, VUE_S8) /* Store Halfword */ #define cpuST_H(vue) cpuOUT_ST(vue, VUE_S16) /* Store Word */ #define cpuST_W(vue) cpuOUT_ST(vue, VUE_S32) /* Store Contents of System Register */ #define cpuSTSR(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = \ cpuGetSystemRegister(vue, vue->cpu.inst.imm & 31) /* Subtract */ #define cpuSUB(vue) \ vue->cpu.program[vue->cpu.inst.reg2] = cpuSubtract(vue, \ vue->cpu.program[vue->cpu.inst.reg2], \ vue->cpu.program[vue->cpu.inst.reg1]) /* Subtract Floating Short */ #define cpuSUBF_S(vue) \ if (!cpuFloatReserved(vue, VUE_TRUE)) cpuFloatResult(vue, VUE_FALSE, \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg2]) - \ cpuFloat(vue->cpu.program[vue->cpu.inst.reg1])) /* Trap */ #define cpuTRAP(vue) \ vue->cpu.exception = 0xFFA0 | (vue->cpu.inst.imm & 15); \ vue->cpu.pc += 2 /* Truncate Short Floating to Word */ #define cpuTRNC_SW(vue) \ cpuFloatConvert(vue, VUE_FALSE) /* Exchange Byte */ static void cpuXB(Vue *vue) { int32_t value = vue->cpu.program[vue->cpu.inst.reg2]; vue->cpu.program[vue->cpu.inst.reg2] = (value & 0xFFFF0000) | (value >> 8 & 0xFF) | (value & 0xFF) << 8; } /* Exchange Halfword */ static void cpuXH(Vue *vue) { int32_t value = vue->cpu.program[vue->cpu.inst.reg2]; vue->cpu.program[vue->cpu.inst.reg2] = (value >> 16 & 0xFFFF) | value << 16; } /* Exclusive Or */ #define cpuXOR(vue) \ cpuBitwise(vue, vue->cpu.program[vue->cpu.inst.reg2] ^ \ vue->cpu.program[vue->cpu.inst.reg1]) /* Exclusive Or Immediate */ #define cpuXORI(vue) \ cpuBitwise(vue, vue->cpu.program[vue->cpu.inst.reg1] ^ \ vue->cpu.inst.imm) /***************************************************************************** * Stage Functions * *****************************************************************************/ /* Decode an instruction from its binary encoding */ static void cpuDecode(VueInstruction *inst) { int8_t extend; /* Sign-extend the immediate operand */ int32_t x; /* Working variable */ /* Configure instance fields */ inst->opcode = inst->bits >> 26 & 63; x = inst->opcode << 1; inst->id = LOOKUP_OPCODE[x ]; extend = LOOKUP_OPCODE[x + 1]; inst->format = extend < 0 ? -extend : extend; inst->size = inst->format < 4 ? 2 : 4; /* Decode by format */ switch (inst->format) { case 1: inst->reg2 = inst->bits >> 21 & 31; inst->reg1 = inst->bits >> 16 & 31; break; case 2: x = inst->bits >> 16 & 31; inst->reg2 = inst->bits >> 21 & 31; inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x; if (inst->id == BITSTRING) { inst->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x]; inst->subopcode = x; } if (inst->id == VUE_SETF) inst->cond = x & 15; break; case 3: x = inst->bits >> 16 & 0x1FF; inst->opcode = 0x20; inst->cond = inst->bits >> 25 & 15; inst->disp = SIGN_EXTEND(9, x); break; case 4: x = inst->bits & 0x3FFFFFF; inst->disp = SIGN_EXTEND(26, x); break; case 5: x = inst->bits & 0xFFFF; inst->reg2 = inst->bits >> 21 & 31; inst->reg1 = inst->bits >> 16 & 31; inst->imm = extend < 0 ? SIGN_EXTEND(16, x) : x; break; case 6: x = inst->bits & 0xFFFF; inst->reg2 = inst->bits >> 21 & 31; inst->reg1 = inst->bits >> 16 & 31; inst->disp = SIGN_EXTEND(16, x); break; case 7: inst->reg2 = inst->bits >> 21 & 31; inst->reg1 = inst->bits >> 16 & 31; inst->subopcode = inst->bits >> 10 & 63; inst->id = inst->subopcode >= 16 ? VUE_ILLEGAL : LOOKUP_FLOATENDO[inst->subopcode]; break; } } /* Operations for exception stage */ static vbool cpuException(Vue *vue) { vbool isIRQ; /* The exception is an interrupt */ int32_t psw; /* Current value of PSW */ /* Application callback */ if (vue->onException != NULL) { vue->breakCode = vue->onException(vue, vue->cpu.exception); if (vue->breakCode != 0) return VUE_TRUE; } /* Configure working variables */ vue->cpu.exception &= 0xFFFF; isIRQ = (vue->cpu.exception & 0xFF00) == 0xFE00; psw = cpuGetSystemRegister(vue, VUE_PSW); /* Fatal exception */ if (vue->cpu.psw_np != 0) { vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000 | vue->cpu.exception); vueWrite(vue, 0x00000004, VUE_S32, psw); vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc); vue->cpu.stage = CPU_FATAL; return VUE_TRUE; } /* Duplexed exception */ if (vue->cpu.psw_ep != 0) { vue->cpu.ecr_fecc = vue->cpu.exception; vue->cpu.fepc = vue->cpu.pc; vue->cpu.fepsw = psw; vue->cpu.psw_np = 1; vue->cpu.pc = 0xFFFFFFD0; } /* Regular exception */ else { vue->cpu.ecr_eicc = vue->cpu.exception; vue->cpu.eipc = vue->cpu.pc; vue->cpu.eipsw = psw; vue->cpu.psw_ep = 1; vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception & 0xFFF0); if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */ vue->cpu.pc = 0xFFFFFF60; } /* Interrupt */ if (isIRQ) { psw = vue->cpu.exception >> 4 & 15; vue->cpu.psw_i = psw < 15 ? psw : 15; if (vue->cpu.stage == CPU_HALT) vue->cpu.pc += 2; } /* Common processing */ vue->cpu.cycles = 0; /* TODO: Determine the actual number */ vue->cpu.exception = 0; vue->cpu.psw_ae = 0; vue->cpu.psw_id = 1; vue->cpu.stage = CPU_FETCH; return VUE_FALSE; } /* Check for an exception or interrupt */ static vbool cpuTestException(Vue *vue) { int32_t level; /* Interrupt level */ /* Check for an interrupt */ if (vue->cpu.irq != 0 && (vue->cpu.exception | vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) { for (level = 4; level >= 0; level--) if ((vue->cpu.irq >> level & 1) != 0) break; vue->cpu.exception = 0xFE00 | level << 4; } /* There is no exception */ if (vue->cpu.exception == 0) /* No further processing */ return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0; /* An exception has occurred */ vue->cpu.cycles = 0; vue->cpu.stage = CPU_EXCEPTION; return VUE_FALSE; } /* Operations for execute stage */ static vbool cpuExecute(Vue *vue) { /* Application callback */ if (vue->onExecute != NULL) { vue->breakCode = vue->onExecute(vue, &vue->cpu.inst); if (vue->breakCode != 0) return VUE_TRUE; } /* Processing by instruction ID */ switch (vue->cpu.inst.id) { case VUE_ADD_IMM: cpuADD_IMM(vue); break; case VUE_ADD_REG: cpuADD_REG(vue); break; case VUE_ADDF_S : cpuADDF_S (vue); break; case VUE_ADDI : cpuADDI (vue); break; case VUE_AND : cpuAND (vue); break; /*case VUE_ANDBSU : cpuANDBSU (vue); break;*/ case VUE_ANDI : cpuANDI (vue); break; /*case VUE_ANDNBSU: cpuANDNBSU(vue); break;*/ case VUE_BCOND : cpuBCOND (vue); break; case VUE_CAXI : cpuCAXI (vue); break; case VUE_CLI : cpuCLI (vue); break; case VUE_CMP_IMM: cpuCMP_IMM(vue); break; case VUE_CMP_REG: cpuCMP_REG(vue); break; case VUE_CMPF_S : cpuCMPF_S (vue); break; case VUE_CVT_SW : cpuCVT_SW (vue); break; case VUE_CVT_WS : cpuCVT_WS (vue); break; case VUE_DIV : cpuDIV (vue); break; case VUE_DIVF_S : cpuDIVF_S (vue); break; case VUE_DIVU : cpuDIVU (vue); break; case VUE_HALT : cpuHALT (vue); break; case VUE_IN_B : cpuIN_B (vue); break; case VUE_IN_H : cpuIN_H (vue); break; case VUE_IN_W : cpuIN_W (vue); break; case VUE_JAL : cpuJAL (vue); break; case VUE_JMP : cpuJMP (vue); break; case VUE_JR : cpuJR (vue); break; case VUE_LD_B : cpuLD_B (vue); break; case VUE_LD_H : cpuLD_H (vue); break; case VUE_LD_W : cpuLD_W (vue); break; case VUE_LDSR : cpuLDSR (vue); break; case VUE_MOV_IMM: cpuMOV_IMM(vue); break; case VUE_MOV_REG: cpuMOV_REG(vue); break; /*case VUE_MOVBSU : cpuMOVBSU (vue); break;*/ case VUE_MOVEA : cpuMOVEA (vue); break; case VUE_MOVHI : cpuMOVHI (vue); break; case VUE_MPYHW : cpuMPYHW (vue); break; case VUE_MUL : cpuMUL (vue); break; case VUE_MULF_S : cpuMULF_S (vue); break; case VUE_MULU : cpuMULU (vue); break; case VUE_NOT : cpuNOT (vue); break; /*case VUE_NOTBSU : cpuNOTBSU (vue); break;*/ case VUE_OR : cpuOR (vue); break; /*case VUE_ORBSU : cpuORBSU (vue); break;*/ case VUE_ORI : cpuORI (vue); break; /*case VUE_ORNBSU : cpuORNBSU (vue); break;*/ case VUE_OUT_B : cpuOUT_B (vue); break; case VUE_OUT_H : cpuOUT_H (vue); break; case VUE_OUT_W : cpuOUT_W (vue); break; case VUE_RETI : cpuRETI (vue); break; case VUE_REV : cpuREV (vue); break; case VUE_SAR_IMM: cpuSAR_IMM(vue); break; case VUE_SAR_REG: cpuSAR_REG(vue); break; /*case VUE_SCH0BSD: cpuSCH0BSD(vue); break;*/ /*case VUE_SCH0BSU: cpuSCH0BSU(vue); break;*/ /*case VUE_SCH1BSD: cpuSCH1BSD(vue); break;*/ /*case VUE_SCH1BSU: cpuSCH1BSU(vue); break;*/ case VUE_SEI : cpuSEI (vue); break; case VUE_SETF : cpuSETF (vue); break; case VUE_SHL_IMM: cpuSHL_IMM(vue); break; case VUE_SHL_REG: cpuSHL_REG(vue); break; case VUE_SHR_IMM: cpuSHR_IMM(vue); break; case VUE_SHR_REG: cpuSHR_REG(vue); break; case VUE_ST_B : cpuST_B (vue); break; case VUE_ST_H : cpuST_H (vue); break; case VUE_ST_W : cpuST_W (vue); break; case VUE_STSR : cpuSTSR (vue); break; case VUE_SUB : cpuSUB (vue); break; case VUE_SUBF_S : cpuSUBF_S (vue); break; case VUE_TRAP : cpuTRAP (vue); break; case VUE_TRNC_SW: cpuTRNC_SW(vue); break; case VUE_XB : cpuXB (vue); break; case VUE_XH : cpuXH (vue); break; case VUE_XOR : cpuXOR (vue); break; /*case VUE_XORBSU : cpuXORBSU (vue); break;*/ case VUE_XORI : cpuXORI (vue); break; /*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/ default: /* Invalid instruction */ vue->cpu.exception = 0xFF90; } /* An application break was requested */ if (vue->breakCode != 0) return VUE_TRUE; /* Common processing */ if (vue->cpu.exception == 0) { vue->cpu.cycles += CYCLES[vue->cpu.inst.id]; vue->cpu.pc += vue->cpu.inst.size; } vue->cpu.program[0] = 0; cpuTestException(vue); if (vue->cpu.stage == CPU_EXECUTE) vue->cpu.stage = CPU_FETCH; return VUE_FALSE; } /* Determine the size of an instruction given its opcode */ static int32_t cpuSize(int32_t opcode) { int32_t ret = LOOKUP_OPCODE[opcode << 1 | 1]; return (ret < 0 ? -ret : ret) < 4 ? 2 : 4; } /* Operations for fetch stage */ static vbool cpuFetch(Vue *vue) { /* Entering the fetch stage */ if (vue->cpu.fetch == -1) vue->cpu.fetch = 0; /* Read the bits from the bus */ if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1), VUE_U16, vue->cpu.fetch)) return VUE_TRUE; /* TODO: Determine how many cycles this takes */ /* First unit */ if (vue->cpu.fetch == 0) { vue->cpu.inst.bits = vue->cpu.access.value << 16; if (cpuSize(vue->cpu.access.value >> 10) == 4) { vue->cpu.fetch = 1; return VUE_FALSE; } } /* Second unit */ else { vue->cpu.inst.bits |= vue->cpu.access.value; vue->cpu.fetch = -1; } /* Decode the instruction and advance to execute stage */ cpuDecode(&vue->cpu.inst); vue->cpu.stage = CPU_EXECUTE; return VUE_FALSE; } /***************************************************************************** * Internal Functions * ******************************************************************************/ /* Process the simulation */ static void cpuEmulate(Vue *vue, uint32_t cycles) { /* The CPU is in fatal halt status */ if (vue->cpu.stage == CPU_FATAL) return; /* Process for the given number of cycles */ for (;;) { /* The next stage occurs after the given number of cycles */ if (vue->cpu.cycles > cycles) { vue->cpu.cycles -= cycles; return; } /* Processing by stage */ cycles -= vue->cpu.cycles; vue->cpu.cycles = 0; switch (vue->cpu.stage) { case CPU_EXCEPTION: if (cpuException(vue)) return; break; case CPU_EXECUTE : if (cpuExecute (vue)) return; break; case CPU_FETCH : if (cpuFetch (vue)) return; break; case CPU_HALT : cpuTestException(vue); return; } } } /* System reset */ static void cpuReset(Vue *vue) { int32_t x; /* Configure instance fields */ vue->cpu.cycles = 0; vue->cpu.exception = 0; vue->cpu.fetch = -1; vue->cpu.irq = 0; vue->cpu.stage = CPU_FETCH; /* Clear all registers (hardware only sets ECR, PC and PSW) */ for (x = 0; x < 32; x++) { vue->cpu.program[x] = 0; cpuSetSystemRegister(vue, x, 0, VUE_TRUE); } /* Configure jump history */ for (x = 0; x < 3; x++) vue->cpu.jumpFrom[x] = vue->cpu.jumpTo[x] = 0xFFFFFFF0; /* Configure registers */ vue->cpu.ecr_eicc = 0xFFF0; vue->cpu.pc = 0xFFFFFFF0; vue->cpu.psw_np = 1; } /* Determine the number of CPU cycles until a breakpoint could trigger */ static uint32_t cpuUntil(Vue *vue, uint32_t cycles) { return vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT ? cycles : cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles; } #endif