2020-08-06 01:40:23 +00:00
|
|
|
/* 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
|
|
|
|
|
2020-08-08 01:04:11 +00:00
|
|
|
/* 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 Functions *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/* Decoder for Format I */
|
|
|
|
static void cpuFormatI(VUE_INST *inst) {
|
|
|
|
inst->reg2 = inst->bits >> 21 & 31;
|
|
|
|
inst->reg1 = inst->bits >> 16 & 31;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decoder for Format II */
|
|
|
|
static void cpuFormatII(VUE_INST *inst, int extend) {
|
|
|
|
int32_t imm = inst->bits >> 16 & 31;
|
|
|
|
inst->reg2 = inst->bits >> 21 & 31;
|
|
|
|
inst->imm = extend ? SIGN_EXTEND(5, imm) : imm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decoder for Format III */
|
|
|
|
static void cpuFormatIII(VUE_INST *inst) {
|
|
|
|
int32_t disp = inst->bits >> 16 & 0x1FF;
|
|
|
|
inst->opcode = 0x20;
|
|
|
|
inst->cond = inst->bits >> 25 & 15;
|
|
|
|
inst->disp = SIGN_EXTEND(9, disp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decoder for Format IV */
|
|
|
|
static void cpuFormatIV(VUE_INST *inst) {
|
|
|
|
int32_t disp = inst->bits & 0x3FFFFFF;
|
|
|
|
inst->disp = SIGN_EXTEND(26, disp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decoder for Format V */
|
|
|
|
static void cpuFormatV(VUE_INST *inst, int extend) {
|
|
|
|
int32_t imm = inst->bits & 0xFFFF;
|
|
|
|
inst->reg2 = inst->bits >> 21 & 31;
|
|
|
|
inst->reg1 = inst->bits >> 16 & 31;
|
|
|
|
inst->imm = extend ? SIGN_EXTEND(16, imm) : imm;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decoder for Format VI */
|
|
|
|
static void cpuFormatVI(VUE_INST *inst) {
|
|
|
|
int32_t disp = inst->bits & 0xFFFF;
|
|
|
|
inst->reg2 = inst->bits >> 21 & 31;
|
|
|
|
inst->reg1 = inst->bits >> 16 & 31;
|
|
|
|
inst->disp = SIGN_EXTEND(16, disp);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decoder for Format VII */
|
|
|
|
static void cpuFormatVII(VUE_INST *inst) {
|
|
|
|
inst->reg2 = inst->bits >> 21 & 31;
|
|
|
|
inst->reg1 = inst->bits >> 16 & 31;
|
|
|
|
inst->subopcode = inst->bits >> 10 & 63;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Decode an instruction from its binary encoding */
|
|
|
|
static void cpuDecode(VUE_INST *inst, int32_t bits) {
|
|
|
|
int extend;
|
|
|
|
|
|
|
|
/* Configure instance fields */
|
|
|
|
inst->bits = bits;
|
|
|
|
inst->opcode = bits >> 26 & 63;
|
|
|
|
inst->id = inst->opcode << 1 | 1;
|
|
|
|
inst->format = LOOKUP_OPCODE[inst->id + 1];
|
|
|
|
inst->id = LOOKUP_OPCODE[inst->id];
|
|
|
|
|
|
|
|
/* Determine whether to sign-extend the immediate operand */
|
|
|
|
if ((extend = inst->format < 0))
|
|
|
|
inst->format = -inst->format;
|
|
|
|
|
|
|
|
/* Determine the size in bytes of the instruction */
|
|
|
|
inst->size = inst->format < 4 ? 2 : 4;
|
|
|
|
if (inst->size == 2)
|
|
|
|
inst->bits &= 0xFFFF0000;
|
|
|
|
|
|
|
|
/* Decode by format */
|
|
|
|
switch (inst->format) {
|
|
|
|
case 0: return; /* Nothing to do */
|
|
|
|
case 1: cpuFormatI (inst ); break;
|
|
|
|
case 2: cpuFormatII (inst, extend); break;
|
|
|
|
case 3: cpuFormatIII(inst ); break;
|
|
|
|
case 4: cpuFormatIV (inst ); break;
|
|
|
|
case 5: cpuFormatV (inst, extend); break;
|
|
|
|
case 6: cpuFormatVI (inst ); break;
|
|
|
|
case 7: cpuFormatVII(inst ); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resolve final instruction ID by subopcode */
|
|
|
|
if (inst->id == FLOATENDO)
|
|
|
|
inst->id = inst->subopcode >= 16 ? VUE_ILLEGAL :
|
|
|
|
LOOKUP_FLOATENDO[inst->subopcode];
|
|
|
|
else if (inst->id == BITSTRING)
|
|
|
|
inst->id = inst->imm >= 16 ? VUE_ILLEGAL :
|
|
|
|
LOOKUP_BITSTRING[inst->imm ];
|
|
|
|
}
|
|
|
|
|
2020-08-06 01:40:23 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Module Functions *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
|
2020-08-06 21:37:05 +00:00
|
|
|
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;
|
|
|
|
|
2020-08-06 01:40:23 +00:00
|
|
|
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 */
|
2020-08-06 21:37:05 +00:00
|
|
|
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:
|
2020-08-06 01:40:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1; /* Unreachable */
|
|
|
|
}
|
|
|
|
|
2020-08-06 21:37:05 +00:00
|
|
|
/* Perform a system reset */
|
2020-08-06 01:40:23 +00:00
|
|
|
static void cpuReset(VUE *vue) {
|
|
|
|
int x;
|
|
|
|
|
|
|
|
/* Configure instance fields */
|
|
|
|
vue->cpu.cycles = 0;
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2020-08-06 21:37:05 +00:00
|
|
|
/* Configure registers */
|
|
|
|
vue->cpu.ecr_eicc = 0xFFF0;
|
2020-08-07 19:21:03 +00:00
|
|
|
vue->cpu.jumpFrom = 0xFFFFFFF0;
|
|
|
|
vue->cpu.jumpTo = 0xFFFFFFF0;
|
2020-08-06 21:37:05 +00:00
|
|
|
vue->cpu.pc = 0xFFFFFFF0;
|
|
|
|
vue->cpu.psw_np = 1;
|
2020-08-06 01:40:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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
|