/* This file is included through vue.c and cannot be built directly. */ #ifdef VUEAPI /***************************************************************************** * Constants * *****************************************************************************/ /* Stages */ #define CPU_FETCH 0 #define CPU_EXECUTE 1 #define CPU_HALT 2 #define CPU_EXCEPTION 3 #define CPU_FATAL 4 #define CPU_CLEAR 5 #define CPU_DUMP 6 #define CPU_RESTORE 7 /* 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 }; /***************************************************************************** * Module Functions * *****************************************************************************/ /* Decode an instruction from its binary encoding */ static void cpuDecode(VUE_INST *inst, int32_t bits) { int8_t extend; /* Sign-extend the immediate operand */ int32_t x; /* Working variable */ /* Configure instance fields */ inst->bits = bits; inst->opcode = bits >> 26 & 63; x = inst->opcode << 1 | 1; extend = LOOKUP_OPCODE[x]; inst->format = LOOKUP_OPCODE[x + 1]; inst->id = extend < 0 ? -extend : extend; inst->size = inst->format < 4 ? 2 : 4; /* Decode by format */ switch (inst->format) { case 1: inst->reg2 = bits >> 21 & 31; inst->reg1 = bits >> 16 & 31; break; case 2: x = bits >> 16 & 31; inst->reg2 = 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]; break; case 3: x = bits >> 16 & 0x1FF; inst->opcode = 0x20; inst->cond = bits >> 25 & 15; inst->disp = SIGN_EXTEND(9, x); break; case 4: x = bits & 0x3FFFFFF; inst->disp = SIGN_EXTEND(26, x); break; case 5: x = bits & 0xFFFF; inst->reg2 = bits >> 21 & 31; inst->reg1 = bits >> 16 & 31; inst->imm = extend < 0 ? SIGN_EXTEND(16, x) : x; break; case 6: x = bits & 0xFFFF; inst->reg2 = bits >> 21 & 31; inst->reg1 = bits >> 16 & 31; inst->disp = SIGN_EXTEND(16, x); break; case 7: inst->reg2 = bits >> 21 & 31; inst->reg1 = bits >> 16 & 31; inst->subopcode = bits >> 10 & 63; inst->id = inst->subopcode >= 16 ? VUE_ILLEGAL : LOOKUP_FLOATENDO[inst->subopcode]; break; } } /* Read a system register */ static int32_t cpuGetSystemRegister(VUE *vue, int 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 */ } /* Write a system register */ static int32_t cpuSetSystemRegister(VUE *vue, int 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 */ } /* Perform a system reset */ static void cpuReset(VUE *vue) { int x; /* Configure instance fields */ vue->cpu.cycles = 0; /* Duration of first fetch */ vue->cpu.fetch = 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; } /* Test a condition */ static int8_t cpuTest(VUE *vue, int 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 cpuTest(vue, condition & 7) ^ 1; } #endif