diff --git a/makefile b/makefile index b9dc268..3eb7dab 100644 --- a/makefile +++ b/makefile @@ -10,7 +10,7 @@ default: @echo @echo "Planet Virtual Boy Emulator" @echo " https://www.planetvb.com/" - @echo " August 5, 2020" + @echo " August 11, 2020" @echo @echo "Intended build environment: Debian i386 or amd64" @echo " gcc-multilib" diff --git a/src/core/cpu.c b/src/core/cpu.c index 0dc79e1..4ed4e35 100644 --- a/src/core/cpu.c +++ b/src/core/cpu.c @@ -68,27 +68,622 @@ static const int8_t CYCLES[] = { +/***************************************************************************** + * 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) { + 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 = 0; 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.code = 0xFF60; + vue->cpu.psw_fro = 1; + return VUE_TRUE; + } + + /* Neither operand is a reserved value */ + return VUE_FALSE; +} + +/* Floating-point result */ +static void cpuFloatResult(VUE *vue, 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.code = 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; + } + + /* Precision degradation */ + if (result != full) + vue->cpu.psw_fpr = 1; + + /* Common processing */ + vue->cpu.program[vue->cpu.inst.reg2] = bits; + 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 */ +} + +/* 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; +} + +/* 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 + +/* 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]) + + + /***************************************************************************** * Instruction Functions * *****************************************************************************/ -/* JMP */ -static void cpuJMP(VUE *vue) { - vue->cpu.pc = vue->cpu.program[vue->cpu.inst.reg1] - 2; +/* 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)) cpuFloatResult(vue, \ + 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.pc += vue->cpu.inst.disp - 2; + vue->cpu.cycles = 2; } -/* MOVEA */ -static void cpuMOVEA(VUE *vue) { - vue->cpu.program[vue->cpu.inst.reg2] = - vue->cpu.program[vue->cpu.inst.reg1] + vue->cpu.inst.imm; +/* 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]) + +/* 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.code = 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; } -/* MOVHI */ -static void cpuMOVHI(VUE *vue) { - vue->cpu.program[vue->cpu.inst.reg2] = - vue->cpu.program[vue->cpu.inst.reg1] + (vue->cpu.inst.imm << 16); +/* 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.code = 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, \ + vue->cpu.pc += vue->cpu.inst.disp - 4 + +/* Jump Register */ +#define cpuJMP(vue) \ + vue->cpu.pc = vue->cpu.program[vue->cpu.inst.reg1] - 2 + +/* Jump Relative */ +#define cpuJR(vue) \ + vue->cpu.pc += vue->cpu.inst.disp - 4 + +/* 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 */ +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 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]) + +/* 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]) + +/* 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]) + +/* Trap */ +#define cpuTRAP(vue) \ + vue->cpu.exception.code = 0xFFA0 | (vue->cpu.inst.imm & 15), \ + vue->cpu.pc += 2 + +/* 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) + /***************************************************************************** @@ -185,97 +780,97 @@ static vbool cpuExecute(VUE *vue) { vue->breakCode = vue->onExecute(vue, &vue->cpu.inst); if (vue->breakCode != 0) return VUE_TRUE; + vue->cpu.inst.reg1 &= 31; + vue->cpu.inst.reg2 &= 31; } - /* Determine the default number of cycles for the instruction */ - if (vue->cpu.inst.id >= 0 && vue->cpu.inst.id <= 75) - vue->cpu.cycles = CYCLES[vue->cpu.inst.id]; - /* 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_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_ANDI : cpuANDI (vue); break; /*case VUE_ANDNBSU: cpuANDNBSU(vue); break;*/ - /*case VUE_BCOND : cpuBCOND (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_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_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_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_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;*/ + /*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.code = 0xFF90; - vue->cpu.inst.size = 0; } /* Common processing */ - vue->cpu.pc += vue->cpu.inst.size; + if (vue->cpu.exception.code == 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) @@ -293,17 +888,9 @@ static int32_t cpuSize(int32_t opcode) { static vbool cpuFetch(VUE *vue) { /* Read the bits from the bus */ - vue->cpu.access.address = vue->cpu.pc + (vue->cpu.fetch << 1); - vue->cpu.access.fetch = vue->cpu.fetch; - vue->cpu.access.type = VUE_U16; - vue->cpu.access.value = vueRead(vue, vue->cpu.access.address, VUE_U16); - - /* Application callback */ - if (vue->onRead != NULL) { - vue->breakCode = vue->onRead(vue, &vue->cpu.access); - if (vue->breakCode != 0) - return VUE_TRUE; - } + if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1), + VUE_U16, vue->cpu.fetch)) + return VUE_TRUE; /* First unit */ if (vue->cpu.fetch == 0) { @@ -326,41 +913,6 @@ static vbool cpuFetch(VUE *vue) { return VUE_FALSE; } -/* 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 */ -} - /* Operations for exception stage */ static vbool cpuException(VUE *vue) { vbool isIRQ; /* The exception is an interrupt */ @@ -411,6 +963,8 @@ static vbool cpuException(VUE *vue) { if (isIRQ) { psw = vue->cpu.exception.code >> 4 & 15; vue->cpu.psw_i = psw < 15 ? psw : 15; + if (vue->cpu.stage == CPU_HALT) + vue->cpu.pc += 2; } /* Common processing */ @@ -452,73 +1006,6 @@ vue->cpu.cycles = 0; /* DEBUG: Stop processing after execute */ } -/* 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 */ -} - /* System reset */ static void cpuReset(VUE *vue) { int32_t x; @@ -546,21 +1033,6 @@ static void cpuReset(VUE *vue) { vue->cpu.psw_np = 1; } -/* 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; -} - /* Determine the number of CPU cycles until something can happen */ static int32_t cpuUntil(VUE *vue, int32_t cycles) { if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT) diff --git a/src/core/include/vue.h b/src/core/include/vue.h index 95b9149..31d980f 100644 --- a/src/core/include/vue.h +++ b/src/core/include/vue.h @@ -24,11 +24,12 @@ extern "C" { #define VUE_TRUE 1 /* Memory access types */ -#define VUE_S8 0 -#define VUE_U8 1 -#define VUE_S16 2 -#define VUE_U16 3 -#define VUE_S32 4 +#define VUE_S8 0 +#define VUE_U8 1 +#define VUE_S16 2 +#define VUE_U16 3 +#define VUE_S32 4 +#define VUE_CANCEL 5 /* System register indexes */ #define VUE_ADTRE 25 diff --git a/src/core/vue.c b/src/core/vue.c index ea8c85e..5d84c9a 100644 --- a/src/core/vue.c +++ b/src/core/vue.c @@ -112,32 +112,27 @@ static void writeBuffer(uint8_t *data, uint32_t datlen, uint32_t address, /* Common processing */ address &= (~size + 1) & (datlen - 1); - /* The host is little-endian */ - #ifndef VUE_BIGENDIAN - switch (type) { - case VUE_S8 : /* Fallthrough */ - case VUE_U8 : *(int8_t *)&data[address] = (int8_t ) value; break; - case VUE_S16: /* Fallthrough */ - case VUE_U16: *(int16_t *)&data[address] = (int16_t ) value; break; - case VUE_S32: *(int32_t *)&data[address] = (int32_t ) value; break; - } - /* The host is big-endian */ - #else + #ifdef VUE_BIGENDIAN switch (type) { case VUE_S32: - data[address + 3] = (uint8_t) (value >> 24); - data[address + 2] = (uint8_t) (value >> 16); + value = (value & 0xFFFF0000) >> 16 | value << 16; /* Fallthrough */ case VUE_S16: /* Fallthrough */ case VUE_U16: - data[address + 1] = (uint8_t) (value >> 8); - /* Fallthrough */ - case VUE_S8 : /* Fallthrough */ - case VUE_U8 : value |= - data[address ] = (uint8_t) value; + value = (value & 0xFF00FF00) >> 8 | (value & 0x00FF00FF) << 8; } #endif + + /* Processing by data type */ + switch (type) { + case VUE_S8 : /* Fallthrough */ + case VUE_U8 : *(int8_t *)&data[address] = (int8_t ) value; break; + case VUE_S16: /* Fallthrough */ + case VUE_U16: *(int16_t *)&data[address] = (int16_t ) value; break; + case VUE_S32: *(int32_t *)&data[address] = (int32_t ) value; break; + } + } /* Write bytes to a byte buffer */ diff --git a/src/desktop/vue/CPU.java b/src/desktop/vue/CPU.java index afc8696..e1bfa84 100644 --- a/src/desktop/vue/CPU.java +++ b/src/desktop/vue/CPU.java @@ -256,21 +256,6 @@ this.cycles = 0; // DEBUG: Stop processing after execute return 1; // Unreachable } - // Test a condition - int test(int condition) { - switch (condition) { - case 0: return psw_ov; - case 1: return psw_cy; - case 2: return psw_z; - case 3: return psw_cy | psw_z; - case 4: return psw_s; - case 5: return 1; - case 6: return psw_ov | psw_s; - case 7: return psw_ov ^ psw_s | psw_z; - } - return test(condition & 7) ^ 1; - } - // Determine the number of CPU cycles until something can happen int until(int cycles) { if (stage == FATAL || stage == HALT) @@ -294,8 +279,6 @@ this.cycles = 0; // DEBUG: Stop processing after execute return true; } -System.out.printf("Exception %04X %d %d\n", exception.code, psw_ep, psw_np); - // Configure working variables exception.code &= 0xFFFF; boolean isIRQ = (exception.code & 0xFF00) == 0xFE00; @@ -331,8 +314,11 @@ System.out.printf("Exception %04X %d %d\n", exception.code, psw_ep, psw_np); } // Interrupt - if (isIRQ) + if (isIRQ) { psw_i = Math.min(15, exception.code >> 4 & 15); + if (stage == HALT) + pc += 2; + } // Common processing cycles = 0; // TODO: Determine the actual number @@ -353,63 +339,57 @@ System.out.printf("Exception %04X %d %d\n", exception.code, psw_ep, psw_np); return true; } -System.out.printf("Execute %08X %d\n", pc, inst.id); - - // Determine the default number of cycles for the instruction - if (inst.id >= 0 && inst.id <= 75) - cycles = CYCLES[inst.id]; - // Processing by instruction ID switch (inst.id) { - //case VUE.ADD_IMM: ADD_IMM(); break; - //case VUE.ADD_REG: ADD_REG(); break; - //case VUE.ADDF_S : ADDF_S (); break; - //case VUE.ADDI : ADDI (); break; - //case VUE.AND : AND (); break; + case VUE.ADD_IMM: ADD_IMM(); break; + case VUE.ADD_REG: ADD_REG(); break; + case VUE.ADDF_S : ADDF_S (); break; + case VUE.ADDI : ADDI (); break; + case VUE.AND : AND (); break; //case VUE.ANDBSU : ANDBSU (); break; - //case VUE.ANDI : ANDI (); break; + case VUE.ANDI : ANDI (); break; //case VUE.ANDNBSU: ANDNBSU(); break; - //case VUE.BCOND : BCOND (); break; + case VUE.BCOND : BCOND (); break; //case VUE.CAXI : CAXI (); break; //case VUE.CLI : CLI (); break; - //case VUE.CMP_IMM: CMP_IMM(); break; - //case VUE.CMP_REG: CMP_REG(); break; + case VUE.CMP_IMM: CMP_IMM(); break; + case VUE.CMP_REG: CMP_REG(); break; //case VUE.CMPF_S : CMPF_S (); break; //case VUE.CVT_SW : CVT_SW (); break; //case VUE.CVT_WS : CVT_WS (); break; - //case VUE.DIV : DIV (); break; + case VUE.DIV : DIV (); break; //case VUE.DIVF_S : DIVF_S (); break; - //case VUE.DIVU : DIVU (); break; - //case VUE.HALT : HALT (); break; - //case VUE.IN_B : IN_B (); break; - //case VUE.IN_H : IN_H (); break; - //case VUE.IN_W : IN_W (); break; - //case VUE.JAL : JAL (); break; + case VUE.DIVU : DIVU (); break; + case VUE.HALT : HALT (); break; + case VUE.IN_B : IN_B (); break; + case VUE.IN_H : IN_H (); break; + case VUE.IN_W : IN_W (); break; + case VUE.JAL : JAL (); break; case VUE.JMP : JMP (); break; - //case VUE.JR : JR (); break; - //case VUE.LD_B : LD_B (); break; - //case VUE.LD_H : LD_H (); break; - //case VUE.LD_W : LD_W (); break; - //case VUE.LDSR : LDSR (); break; - //case VUE.MOV_IMM: MOV_IMM(); break; - //case VUE.MOV_REG: MOV_REG(); break; + case VUE.JR : JR (); break; + case VUE.LD_B : LD_B (); break; + case VUE.LD_H : LD_H (); break; + case VUE.LD_W : LD_W (); break; + case VUE.LDSR : LDSR (); break; + case VUE.MOV_IMM: MOV_IMM(); break; + case VUE.MOV_REG: MOV_REG(); break; //case VUE.MOVBSU : MOVBSU (); break; case VUE.MOVEA : MOVEA (); break; case VUE.MOVHI : MOVHI (); break; //case VUE.MPYHW : MPYHW (); break; - //case VUE.MUL : MUL (); break; + case VUE.MUL : MUL (); break; //case VUE.MULF_S : MULF_S (); break; - //case VUE.MULU : MULU (); break; - //case VUE.NOT : NOT (); break; + case VUE.MULU : MULU (); break; + case VUE.NOT : NOT (); break; //case VUE.NOTBSU : NOTBSU (); break; - //case VUE.OR : OR (); break; + case VUE.OR : OR (); break; //case VUE.ORBSU : ORBSU (); break; - //case VUE.ORI : ORI (); break; + case VUE.ORI : ORI (); break; //case VUE.ORNBSU : ORNBSU (); break; - //case VUE.OUT_B : OUT_B (); break; - //case VUE.OUT_H : OUT_H (); break; - //case VUE.OUT_W : OUT_W (); break; - //case VUE.RETI : RETI (); break; + case VUE.OUT_B : OUT_B (); break; + case VUE.OUT_H : OUT_H (); break; + case VUE.OUT_W : OUT_W (); break; + case VUE.RETI : RETI (); break; //case VUE.REV : REV (); break; //case VUE.SAR_IMM: SAR_IMM(); break; //case VUE.SAR_REG: SAR_REG(); break; @@ -423,27 +403,33 @@ System.out.printf("Execute %08X %d\n", pc, inst.id); //case VUE.SHL_REG: SHL_REG(); break; //case VUE.SHR_IMM: SHR_IMM(); break; //case VUE.SHR_REG: SHR_REG(); break; - //case VUE.ST_B : ST_B (); break; - //case VUE.ST_H : ST_H (); break; - //case VUE.ST_W : ST_W (); break; - //case VUE.STSR : STSR (); break; - //case VUE.SUB : SUB (); break; + case VUE.ST_B : ST_B (); break; + case VUE.ST_H : ST_H (); break; + case VUE.ST_W : ST_W (); break; + case VUE.STSR : STSR (); break; + case VUE.SUB : SUB (); break; //case VUE.SUBF_S : SUBF_S (); break; - //case VUE.TRAP : TRAP (); break; + case VUE.TRAP : TRAP (); break; //case VUE.TRNC_SW: TRNC_SW(); break; //case VUE.XB : XB (); break; //case VUE.XH : XH (); break; - //case VUE.XOR : XOR (); break; + case VUE.XOR : XOR (); break; //case VUE.XORBSU : XORBSU (); break; - //case VUE.XORI : XORI (); break; + case VUE.XORI : XORI (); break; //case VUE.XORNBSU: XORNBSU(); break; default: // Invalid instruction exception.code = 0xFF90; - inst.size = 0; } + // An application break was requested + if (vue.breakCode != 0) + return true; + // Common processing - pc += inst.size; + if (exception.code == 0) { + cycles += CYCLES[inst.id]; + pc += inst.size; + } program[0] = 0; testException(); if (stage == EXECUTE) @@ -455,19 +441,8 @@ System.out.printf("Execute %08X %d\n", pc, inst.id); private boolean fetch() { // Read the bits from the bus - access.address = pc + (fetch << 1); - access.fetch = fetch; - access.type = VUE.U16; - access.value = vue.read(access.address, VUE.U16); - -System.out.printf("Fetch %08X %04X\n", access.address, access.value); - - // Application callback - if (vue.onRead != null) { - vue.breakCode = vue.onRead.call(vue, access); - if (vue.breakCode != 0) - return true; - } + if (read(pc + (fetch << 1), VUE.U16, fetch)) + return true; // First unit if (fetch == 0) { @@ -490,6 +465,39 @@ System.out.printf("Fetch %08X %04X\n", access.address, access.value); return false; } + // Perform a bus read + private boolean read(int address, int type, int fetch) { + + // Perform the operation + access.address = address; + access.fetch = fetch; + access.type = type; + access.value = vue.read(address, type); + + // There is no application callback + if (vue.onRead == null) + return false; + + // Call the application callback + vue.breakCode = vue.onRead.call(vue, access); + return vue.breakCode != 0; + } + + // Test a condition + private int testCondition(int condition) { + switch (condition) { + case 0: return psw_ov; + case 1: return psw_cy; + case 2: return psw_z; + case 3: return psw_cy | psw_z; + case 4: return psw_s; + case 5: return 1; + case 6: return psw_ov | psw_s; + case 7: return psw_ov ^ psw_s | psw_z; + } + return testCondition(condition & 7) ^ 1; + } + // Check for an exception or interrupt private boolean testException() { @@ -512,25 +520,463 @@ System.out.printf("Fetch %08X %04X\n", access.address, access.value); return false; } + // Perform a bus write + private boolean write(int address, int type, int value) { + + // Prepare the operation + access.address = address; + access.fetch = -1; + access.type = type; + access.value = value; + + // Application callback + if (vue.onWrite != null) { + vue.breakCode = vue.onWrite.call(vue, access); + if (vue.breakCode != 0) + return true; + if (access.type == VUE.CANCEL) + return false; + } + + // Perform the operation + vue.write(access.address, access.type, access.value); + return false; + } + + + + /////////////////////////////////////////////////////////////////////////// + // Instruction Helpers // + /////////////////////////////////////////////////////////////////////////// + + // Integer addition + private int add(int left, int right) { + int result = left + right; + psw_cy = Integer.compareUnsigned(result, left) < 0 ? 1 : 0; + psw_s = result >>> 31; + psw_ov = (~(left ^ right) & (left ^ result)) >>> 31; + psw_z = result == 0 ? 1 : 0; + return result; + } + + // Bitwise operation + private void bitwise(int result) { + program[inst.reg2] = result; + psw_ov = 0; + psw_s = result >>> 31; + psw_z = result == 0 ? 1 : 0; + } + + // Determine whether the floating-point operands are reserved values + private boolean floatReserved() { + for (int x = 0; x < 2; x++) { + int operand = program[x == 0 ? inst.reg2 : inst.reg1]; + int 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 + exception.code = 0xFF60; + psw_fro = 1; + return true; + } + return false; + } + + // Floating-point result + private void floatResult(double full) { + + // Overflow + if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) { + exception.code = 0xFF64; + psw_fov = 1; + return; + } + + // Process the result + float result = (float) full; + int bits = Float.floatToRawIntBits(result); + + // Underflow + if ((bits & 0x7F800000) == 0 && (bits & 0x007FFFFF) != 0) { + bits = 0; + result = 0; + psw_fud = 1; + } + + // Precision degradation + if (result != full) + psw_fpr = 1; + + // Common processing + program[inst.reg2] = bits; + psw_cy = psw_s = bits >>> 31; + psw_ov = 0; + psw_z = bits == 0 ? 1 : 0; + } + + // Proxy for IN and LD + private void IN_LD(int type) { + if (!read(program[inst.reg1] + inst.disp, type, -1)) + program[inst.reg2] = access.value; + } + + // Proxy for OUT and ST + private void OUT_ST(int type) { + write(program[inst.reg1] + inst.disp, type, program[inst.reg2]); + } + + // Arithmetic shift right + private void shiftArithmetic(int value, int bits) { + bits &= 31; + psw_cy = bits == 0 ? 0 : value >> bits - 1 & 1; + bitwise(value >> bits); + } + + // Logical shift left + private void shiftLeft(int value, int bits) { + bits &= 31; + psw_cy = bits == 0 ? 0 : value >> 32 - bits & 1; + bitwise(value << bits); + } + + // Logical shift right + private void shiftRight(int value, int bits) { + bits &= 31; + psw_cy = bits == 0 ? 0 : value >> bits - 1 & 1; + bitwise(value >>> bits); + } + + // Integer subtraction + private int subtract(int left, int right) { + int result = left - right; + psw_cy = Integer.compareUnsigned(result, left) > 0 ? 1 : 0; + psw_s = result >>> 31; + psw_ov = ((left ^ right) & (left ^ result)) >>> 31; + psw_z = result == 0 ? 1 : 0; + return result; + } + /////////////////////////////////////////////////////////////////////////// // Instruction Methods // /////////////////////////////////////////////////////////////////////////// - // JMP + // Add Immediate + private void ADD_IMM() { + program[inst.reg2] = add(program[inst.reg2], inst.imm); + } + + // Add Register + private void ADD_REG() { + program[inst.reg2] = add(program[inst.reg2], program[inst.reg1]); + } + + // Add Floating Short + private void ADDF_S() { + if (!floatReserved()) floatResult( + (double) Float.intBitsToFloat(program[inst.reg2]) + + (double) Float.intBitsToFloat(program[inst.reg1])); + } + + // Add Immediate + private void ADDI() { + program[inst.reg2] = add(program[inst.reg2], inst.imm); + } + + // And + private void AND() { + bitwise(program[inst.reg2] & program[inst.reg1]); + } + + // And Immediate + private void ANDI() { + bitwise(program[inst.reg2] & inst.imm); + } + + // Branch on Condition + private void BCOND() { + if (testCondition(inst.cond) == 0) + return; + pc += inst.disp - 2; + cycles = 2; + } + + // Compare Immediate + private void CMP_IMM() { + subtract(program[inst.reg2], inst.imm); + } + + // Compare Register + private void CMP_REG() { + subtract(program[inst.reg2], program[inst.reg1]); + } + + // Divide + private void DIV() { + int left = program[inst.reg2]; + int right = program[inst.reg1]; + + // Zero division + if (right == 0) { + exception.code = 0xFF80; + return; + } + + // Special case + if (left == 0x80000000 && right == 0xFFFFFFFF) { + program[30] = 0; + program[inst.reg2] = 0x80000000; + psw_ov = 1; + psw_s = 1; + psw_z = 0; + return; + } + + // Regular case + int result = left / right; + program[30] = left % right; + program[inst.reg2] = result; + psw_ov = 0; + psw_s = result >>> 31; + psw_z = result == 0 ? 1 : 0; + } + + // Divide Unsigned + private void DIVU() { + long left = program[inst.reg2] & 0xFFFFFFFFL; + long right = program[inst.reg1] & 0xFFFFFFFFL; + + // Zero division + if (right == 0) { + exception.code = 0xFF80; + return; + } + + // Regular case + int result = (int) (left / right); + program[30] = (int) (left % right); + program[inst.reg2] = result; + psw_ov = 0; + psw_s = result >>> 31; + psw_z = result == 0 ? 1 : 0; + } + + // Halt + private void HALT() { + stage = HALT; + inst.size = 0; + } + + // Input Byte from Port + private void IN_B() { + IN_LD(VUE.U8); + } + + // Input Halfword from Port + private void IN_H() { + IN_LD(VUE.U16); + } + + // Input Word from Port + private void IN_W() { + IN_LD(VUE.S32); + } + + // Jump and Link + private void JAL() { + program[31] = pc + 4; + pc += inst.disp - 4; + } + + // Jump Register private void JMP() { pc = program[inst.reg1] - 2; } - // MOVEA + // Jump Relative + private void JR() { + pc += inst.disp - 4; + } + + // Load Byte + private void LD_B() { + IN_LD(VUE.S8); + } + + // Load Halfword + private void LD_H() { + IN_LD(VUE.S16); + } + + // Load Word + private void LD_W() { + IN_LD(VUE.S32); + } + + // Load to System Register + private void LDSR() { + setSystemRegister(inst.imm & 31, program[inst.reg2], false); + } + + // Move Immediate + private void MOV_IMM() { + program[inst.reg2] = inst.imm; + } + + // Move Register + private void MOV_REG() { + program[inst.reg2] = program[inst.reg1]; + } + + // Add private void MOVEA() { program[inst.reg2] = program[inst.reg1] + inst.imm; } - // MOVHI + // Add private void MOVHI() { program[inst.reg2] = program[inst.reg1] + (inst.imm << 16); } + // Multiply + private void MUL() { + long full = (long) program[inst.reg2] * (long) program[inst.reg1]; + int result = (int) full; + program[30] = (int) (full >> 32); + program[inst.reg2] = result; + psw_ov = (long) result != full ? 1 : 0; + psw_s = result >>> 31; + psw_z = result == 0 ? 1 : 0; + } + + // Multiply Unsigned + private void MULU() { + long full = (program[inst.reg2] & 0xFFFFFFFFL) * + (program[inst.reg1] & 0xFFFFFFFFL); + int result = (int) full; + program[30] = (int) (full >> 32); + program[inst.reg2] = result; + psw_ov = full > 0xFFFFFFFFL ? 1 : 0; + psw_s = result >>> 31; + psw_z = result == 0 ? 1 : 0; + } + + // Not + private void NOT() { + bitwise(~program[inst.reg1]); + } + + // Or + private void OR() { + bitwise(program[inst.reg2] | program[inst.reg1]); + } + + // Or Immediate + private void ORI() { + bitwise(program[inst.reg2] | inst.imm); + } + + // Output Byte to Port + private void OUT_B() { + OUT_ST(VUE.U8); + } + + // Output Halfword to Port + private void OUT_H() { + OUT_ST(VUE.U16); + } + + // Output Word to Port + private void OUT_W() { + OUT_ST(VUE.S32); + } + + // Return from Trap or Interrupt + private void RETI() { + if (psw_np == 1) { + pc = fepc; + setSystemRegister(VUE.PSW, fepsw, false); + } else { + pc = eipc; + setSystemRegister(VUE.PSW, eipsw, false); + } + } + + // Shift Arithmetic Right by Immediate + private void SAR_IMM() { + shiftArithmetic(program[inst.reg2], inst.imm); + } + + // Shift Arithmetic Right by Register + private void SAR_REG() { + shiftArithmetic(program[inst.reg2], program[inst.reg1]); + } + + // Shift Logical Left by Immediate + private void SHL_IMM() { + shiftLeft(program[inst.reg2], inst.imm); + } + + // Shift Logical Left by Register + private void SHL_REG() { + shiftLeft(program[inst.reg2], program[inst.reg1]); + } + + // Shift Logical Right by Immediate + private void SHR_IMM() { + shiftRight(program[inst.reg2], inst.imm); + } + + // Shift Logical Right by Register + private void SHR_REG() { + shiftRight(program[inst.reg2], program[inst.reg1]); + } + + // Store Byte + private void ST_B() { + OUT_ST(VUE.S8); + } + + // Store Halfword + private void ST_H() { + OUT_ST(VUE.S16); + } + + // Store Word + private void ST_W() { + OUT_ST(VUE.S32); + } + + // Store Contents of System Register + private void STSR() { + program[inst.reg2] = getSystemRegister(inst.imm & 31); + } + + // Subtract + private void SUB() { + program[inst.reg2] = subtract(program[inst.reg2], program[inst.reg1]); + } + + // Trap + private void TRAP() { + exception.code = 0xFFA0 | inst.imm & 15; + pc += 2; + } + + // Exclusive Or + private void XOR() { + bitwise(program[inst.reg2] ^ program[inst.reg1]); + } + + // Exclusive Or Immediate + private void XORI() { + bitwise(program[inst.reg2] ^ inst.imm); + } + }