1251 lines
40 KiB
C
1251 lines
40 KiB
C
/* 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 = 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) cpuADD_IMM(vue)
|
|
|
|
/* 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.reg2] & \
|
|
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.reg2] | \
|
|
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.reg2] ^ \
|
|
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 & 0x3F) == 4) {
|
|
vue->cpu.fetch = 1;
|
|
return VUE_FALSE;
|
|
}
|
|
}
|
|
|
|
/* Second unit */
|
|
else {
|
|
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF;
|
|
vue->cpu.fetch = -1;
|
|
}
|
|
|
|
/* Decode the instruction and advance to execute stage */
|
|
cpuDecode(&vue->cpu.inst);
|
|
vue->cpu.stage = CPU_EXECUTE;
|
|
return VUE_FALSE;
|
|
}
|
|
|
|
/* 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
|