/* This file is included into vb.c and cannot be compiled on its own. */ #ifdef VBAPI /********************************* Constants *********************************/ /* Operations states */ #define CPU_FETCH 0 #define CPU_EXECUTE 1 #define CPU_EXCEPTION 2 #define CPU_HALTED 3 #define CPU_FATAL 4 /* Instruction IDs */ #define CPU_ILLEGAL -1 #define CPU_ADD_IMM 0 #define CPU_ADD_REG 1 #define CPU_ADDF_S 2 #define CPU_ADDI 3 #define CPU_AND 4 #define CPU_ANDBSU 5 #define CPU_ANDI 6 #define CPU_ANDNBSU 7 #define CPU_BCOND 8 #define CPU_CAXI 9 #define CPU_CLI 10 #define CPU_CMP_IMM 11 #define CPU_CMP_REG 12 #define CPU_CMPF_S 13 #define CPU_CVT_SW 14 #define CPU_CVT_WS 15 #define CPU_DIV 16 #define CPU_DIVF_S 17 #define CPU_DIVU 18 #define CPU_HALT 19 #define CPU_IN_B 20 #define CPU_IN_H 21 #define CPU_IN_W 22 #define CPU_JAL 23 #define CPU_JMP 24 #define CPU_JR 25 #define CPU_LD_B 26 #define CPU_LD_H 27 #define CPU_LD_W 28 #define CPU_LDSR 29 #define CPU_MOV_IMM 30 #define CPU_MOV_REG 31 #define CPU_MOVBSU 32 #define CPU_MOVEA 33 #define CPU_MOVHI 34 #define CPU_MPYHW 35 #define CPU_MUL 36 #define CPU_MULF_S 37 #define CPU_MULU 38 #define CPU_NOT 39 #define CPU_NOTBSU 40 #define CPU_OR 41 #define CPU_ORBSU 42 #define CPU_ORI 43 #define CPU_ORNBSU 44 #define CPU_OUT_B 45 #define CPU_OUT_H 46 #define CPU_OUT_W 47 #define CPU_RETI 48 #define CPU_REV 49 #define CPU_SAR_IMM 50 #define CPU_SAR_REG 51 #define CPU_SCH0BSD 52 #define CPU_SCH0BSU 53 #define CPU_SCH1BSD 54 #define CPU_SCH1BSU 55 #define CPU_SEI 56 #define CPU_SETF 57 #define CPU_SHL_IMM 58 #define CPU_SHL_REG 59 #define CPU_SHR_IMM 60 #define CPU_SHR_REG 61 #define CPU_ST_B 62 #define CPU_ST_H 63 #define CPU_ST_W 64 #define CPU_STSR 65 #define CPU_SUB 66 #define CPU_SUBF_S 67 #define CPU_TRAP 68 #define CPU_TRNC_SW 69 #define CPU_XB 70 #define CPU_XH 71 #define CPU_XOR 72 #define CPU_XORBSU 73 #define CPU_XORI 74 #define CPU_XORNBSU 75 #define CPU_BITSTRING 76 #define CPU_FLOATENDO 77 /* Mapping for opcodes to operation IDs */ static const int8_t CPU_OPCODES[] = { CPU_MOV_REG, CPU_ADD_REG, CPU_SUB , CPU_CMP_REG , CPU_SHL_REG, CPU_SHR_REG, CPU_JMP , CPU_SAR_REG , CPU_MUL , CPU_DIV , CPU_MULU , CPU_DIVU , CPU_OR , CPU_AND , CPU_XOR , CPU_NOT , CPU_MOV_IMM, CPU_ADD_IMM, CPU_SETF , CPU_CMP_IMM , CPU_SHL_IMM, CPU_SHR_IMM, CPU_CLI , CPU_SAR_IMM , CPU_TRAP , CPU_RETI , CPU_HALT , CPU_ILLEGAL , CPU_LDSR , CPU_STSR , CPU_SEI , CPU_BITSTRING, CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_BCOND , CPU_MOVEA , CPU_ADDI , CPU_JR , CPU_JAL , CPU_ORI , CPU_ANDI , CPU_XORI , CPU_MOVHI , CPU_LD_B , CPU_LD_H , CPU_ILLEGAL , CPU_LD_W , CPU_ST_B , CPU_ST_H , CPU_ILLEGAL , CPU_ST_W , CPU_IN_B , CPU_IN_H , CPU_CAXI , CPU_IN_W , CPU_OUT_B , CPU_OUT_H , CPU_FLOATENDO, CPU_OUT_W }; /* Mapping for bit string sub-opcodes */ static const int8_t CPU_BITSTRINGS[] = { CPU_SCH0BSU, CPU_SCH0BSD, CPU_SCH1BSU, CPU_SCH1BSD, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ORBSU , CPU_ANDBSU , CPU_XORBSU , CPU_MOVBSU , CPU_ORNBSU , CPU_ANDNBSU, CPU_XORNBSU, CPU_NOTBSU , CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL }; /* Mapping for floating-point/Nintendo sub-opcodes */ static const int8_t CPU_FLOATENDOS[] = { CPU_CMPF_S , CPU_ILLEGAL, CPU_CVT_WS , CPU_CVT_SW , CPU_ADDF_S , CPU_SUBF_S , CPU_MULF_S , CPU_DIVF_S , CPU_XB , CPU_XH , CPU_REV , CPU_TRNC_SW, CPU_MPYHW , CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL }; /* Instruction sizes by opcode */ static const uint8_t CPU_SIZES[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 4, 4, 4, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4 }; /***************************** Utility Functions *****************************/ /* Read a data unit from the bus */ static int cpuRead(VB *emu, uint32_t address, int8_t type) { /* TODO: Determine clock count here */ emu->cpu.access.clocks = 0; /* Not using a breakpoint callback */ if (emu->onRead == NULL) { emu->cpu.access.value = busRead(emu, address, type, 0); return 0; } /* Invoke the breakpoint callback */ emu->cpu.access.address = address; emu->cpu.access.type = type; return emu->onRead(emu, &emu->cpu.access); } /* Read a data unit from the bus for the purpose of an instruction fetch */ static int cpuReadFetch(VB *emu, uint32_t address) { /* TODO: Determine clock count here */ emu->cpu.access.clocks = 0; /* Not using a breakpoint callback */ if (emu->onFetch == NULL) { emu->cpu.access.value = busRead(emu, address, VB_U16, 0); return 0; } /* Invoke the breakpoint callback */ emu->cpu.access.address = address; emu->cpu.access.type = VB_U16; return emu->onFetch(emu, emu->cpu.fetch, &emu->cpu.access); } /* Write a data unit to the bus */ static int cpuWrite(VB *emu, uint32_t address, int8_t type, int32_t value) { /* TODO: Determine clock count here */ emu->cpu.access.clocks = 0; emu->cpu.access.type = type; /* Using a breakpoint callback */ if (emu->onWrite != NULL) { emu->cpu.access.value = value; if (emu->onWrite(emu, &emu->cpu.access)) return 1; } /* Write the value if the operation wasn't cancelled */ if (emu->cpu.access.type != VB_CANCEL) busWrite(emu, address, type, value, 0); return 0; } /**************************** Instruction Helpers ****************************/ /* Add two numbers and update the flags */ static int32_t cpuAdd(VB *emu, int32_t left, int32_t right) { int32_t result = left + right; emu->cpu.clocks = 1; emu->cpu.psw.cy = (uint32_t) result < (uint32_t) left; emu->cpu.psw.ov = (~(left ^ right) & (left ^ result)) >> 31 & 1; emu->cpu.psw.s = result < 0; emu->cpu.psw.z = result == 0; return result; } /* Bit string search */ static int cpuBitSearch(VB *emu, int bit, int dir) { int32_t offset; /* Bit offset in source word */ int32_t value; /* Alias of source word */ /* The bit string is of zero length */ if (emu->cpu.program[28] == 0) { emu->cpu.clocks = dir == 1 ? 13 : 15; return 0; } /* Read the source word */ if (!emu->cpu.busWait && !emu->cpu.substring) { /* Initialize state */ emu->cpu.program[30] &= 0xFFFFFFFC; emu->cpu.program[27] &= 0x0000001F; emu->cpu.psw.z = 1; /* Read the data unit from the bus */ if (cpuRead(emu, emu->cpu.program[30], VB_S32)) return 1; /* Update state */ emu->cpu.busWait = 1; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Search the bit string */ for ( offset = emu->cpu.program[27], value = emu->cpu.access.value; emu->cpu.program[28] != 0 && (offset & 31) == offset; offset += dir, emu->cpu.program[28]--, emu->cpu.program[29]++ ) { /* The current bit does not match */ if ((value >> bit & 1) != bit) continue; /* The current bit matches */ emu->cpu.substring = 0; emu->cpu.clocks = dir == 1 ? 46 : 51; emu->cpu.psw.z = 0; return 0; } /* No bit in the current word matches */ emu->cpu.substring = 1; emu->cpu.clocks = 5; emu->cpu.program[30] += dir << 2; emu->cpu.program[27] &= 0x0000001F; return 0; } /* Bit string bitwise operation */ static int cpuBitString(VB *emu, VB_INSTRUCTION *inst) { int32_t dest; /* Destination word value */ int32_t src; /* Source word value */ /* Initial invocation */ if (emu->cpu.busWait == 0 && !emu->cpu.substring) { /* Initialize state */ emu->cpu.program[30] &= 0xFFFFFFFC; emu->cpu.program[29] &= 0xFFFFFFFC; emu->cpu.program[27] &= 0x0000001F; emu->cpu.program[26] &= 0x0000001F; /* The bit string is of zero length */ if (emu->cpu.program[28] == 0) { emu->cpu.clocks = 20; return 0; } /* Read the data unit from the bus */ if (cpuRead(emu, emu->cpu.program[30], VB_S32)) return 1; /* Update state */ inst->aux[0] = emu->cpu.access.value; emu->cpu.busWait = 1; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Read the next source word */ if (emu->cpu.busWait == 1) { /* Read the data unit from the bus */ if (cpuRead(emu, emu->cpu.program[30] + 4, VB_S32)) return 1; /* Update state */ inst->aux[1] = emu->cpu.access.value; emu->cpu.busWait = 2; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Read the destination word */ if (emu->cpu.busWait == 2) { /* Read the data unit from the bus */ if (cpuRead(emu, emu->cpu.program[29], VB_S32)) return 1; /* Update state */ emu->cpu.busWait = 3; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Compute and store the destination word */ if (emu->cpu.busWait == 3) { dest = emu->cpu.access.value; src = ((int64_t) inst->aux[1] << 32 | (uint32_t) inst->aux[0]) >> (32 + emu->cpu.program[27] - emu->cpu.program[26]); /* Perform the operation */ switch (inst->bits[0] & 7) { case 0 : dest |= src; break; /* ORBSU */ case 1 : dest &= src; break; /* ANDBSU */ case 2 : dest ^= src; break; /* XORBSU */ case 3 : dest = src; break; /* MOVBSU */ case 4 : dest |= ~src; break; /* ORNBSU */ case 5 : dest &= ~src; break; /* ANDNBSU */ case 6 : dest ^= ~src; break; /* XORNBSU */ default: dest = ~src; break; /* NOTBSU */ } /* Incorporate only the bits occupied by the bit string */ src = (int32_t) 0xFFFFFFFF << emu->cpu.program[26]; if ((uint32_t)emu->cpu.program[28] < (uint32_t)32-emu->cpu.program[26]) src &= (1 << (emu->cpu.program[28] + emu->cpu.program[26])) - 1; dest = (dest & src) | (emu->cpu.access.value & ~src); /* Write the data unit to the bus */ if (cpuWrite(emu, emu->cpu.program[30], VB_S32, dest)) return 1; /* Update state */ emu->cpu.busWait = 4; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Working variables */ dest = 32 - emu->cpu.program[26]; /* Bits processed this invocation */ if ((uint32_t) emu->cpu.program[28] < (uint32_t) dest) dest = emu->cpu.program[28]; src = emu->cpu.program[30] + dest; /* New source bit offset */ /* Update state */ emu->cpu.busWait = 0; emu->cpu.substring = dest != emu->cpu.program[28]; if (src >= 32) { inst->aux[0] = inst->aux[1]; emu->cpu.program[30] += 4; } emu->cpu.program[29] += 4; emu->cpu.program[28] -= dest; emu->cpu.program[27] = src & 0x1F; emu->cpu.program[26] = 0; emu->cpu.clocks = emu->cpu.substring ? 6 : 36; return 0; } /* Common processing for most bitwise operations */ static int32_t cpuBitwise(VB *emu, int32_t result) { emu->cpu.clocks = 1; emu->cpu.psw.ov = 0; emu->cpu.psw.s = result < 0; emu->cpu.psw.z = result == 0; return result; } /* Test a condition code */ static int cpuCondition(VB *emu, int cond) { /* Falsey condition */ if (cond > 7) return !cpuCondition(emu, cond & 7); /* Truthy condition */ switch (cond) { case 0: return emu->cpu.psw.ov; /* V */ case 1: return emu->cpu.psw.cy; /* L */ case 2: return emu->cpu.psw.z; /* Z */ case 3: return emu->cpu.psw.cy | emu->cpu.psw.z; /* NH */ case 4: return emu->cpu.psw.s; /* N */ case 5: return 1; /* T */ case 6: return emu->cpu.psw.ov ^ emu->cpu.psw.s; /* LT */ default:return(emu->cpu.psw.ov^emu->cpu.psw.s)|emu->cpu.psw.z; /* LE */ } } /* Check for a floating-point reserved operand */ static int cpuFloatReserved(VB *emu, int32_t bits) { uint8_t e = bits >> 23; /* Exponent field */ /* Not reserved */ if (e != 0xFF && (e != 0x00 || (bits & 0x007FFFFF) == 0)) return 0; /* Reserved */ emu->cpu.causeCode = 0xFF60; emu->cpu.psw.fro = 1; return 1; } /* Convert a 32-bit integer to the represented floating-point value */ static double cpuFloatOperand(VB *emu, int32_t bits) { return cpuFloatReserved(emu, bits) ? 0 : (double) *(float *) &bits; } /* Convert a floating-point result to bits as a 32-bit integer */ static int32_t cpuFloatResult(VB *emu, double resultd) { float resultf; /* 32-bit conversion of result */ int32_t ret; /* Output value */ /* Overflow */ if (resultd < -FLT_MAX || resultd > FLT_MAX) { emu->cpu.causeCode = 0xFF64; emu->cpu.psw.fov = 1; return 0; } /* Underflow */ if (resultd > -FLT_MIN && resultd < FLT_MIN) { emu->cpu.psw.fud = 1; ret = 0; } /* Normalized */ else { resultf = (float) resultd; ret = *(int32_t *) &resultf; if (ret == INT32_MIN) ret = 0; /* Prevent negative zero */ /* Precision degradation */ if (resultf != resultd) emu->cpu.psw.fpr = 1; } /* Update state */ emu->cpu.psw.cy = ret < 0; emu->cpu.psw.ov = 0; emu->cpu.psw.s = ret < 0; emu->cpu.psw.z = ret == 0; return ret; } /* Convert a floating-point value to a word value */ static void cpuFloatToWord(VB *emu, VB_INSTRUCTION *inst, int truncate) { int32_t bits = emu->cpu.program[inst->bits[0] & 0x1F]; int32_t result; /* Output value */ int32_t x; /* Working variable field */ /* Zero */ if ((bits & 0x7FFFFFFF) == 0) result = 0; /* Minimum value */ else if (bits == (int32_t) 0xCF000000) result = INT32_MIN; /* Conversion */ else { x = bits >> 23 & 0xFF; /* Exponent field */ /* Reserved operand */ if (x == 0xFF || x == 0x00) { emu->cpu.causeCode = 0xFF60; emu->cpu.psw.fro = 1; return; } /* Invalid operation */ if (x >= 135) { emu->cpu.causeCode = 0xFF70; emu->cpu.psw.fiv = 1; return; } /* Parse significand bits */ result = (bits & 0x007FFFFF) | 0x00800000; /* Left shift */ if (x >= 127) result <<= x - 127; /* Right shift */ else { x = 127 - x; /* Number of bits to shift */ result >>= x; /* Update state */ x = 1 << x; /* Position of the "one halfths" bit */ /* Precision degradation */ if ((bits & (x - 1)) != 0) { emu->cpu.psw.fpr = 1; /* Apply rounding */ if (!truncate && (bits & x) != 0) result += x; } } /* Incorporate sign */ if (bits < 0) result = -result; } /* Update state */ emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = result; emu->cpu.psw.ov = 0; emu->cpu.psw.s = result < 0; emu->cpu.psw.z = result == 0; emu->cpu.clocks = 14; } /* Perform a jump */ static void cpuJump(VB *emu, VB_INSTRUCTION *inst, uint32_t address) { emu->cpu.pcFrom = emu->cpu.pc; emu->cpu.pc = address & 0xFFFFFFFE; emu->cpu.pcTo = emu->cpu.pc; emu->cpu.clocks = 3; inst->size = 0; } /* Perform an input or load */ static int cpuLoad(VB *emu,VB_INSTRUCTION *inst,uint8_t type,uint32_t clocks) { /* Initiate the read */ if (!emu->cpu.busWait) { /* Read the data unit from the bus */ if (cpuRead( emu, emu->cpu.program[inst->bits[0] & 0x1F] + SignExtend((int32_t) inst->bits[1], 16), type )) return 1; /* Update state */ emu->cpu.busWait = 1; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Complete the read */ emu->cpu.busWait = 0; emu->cpu.clocks = clocks; emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = emu->cpu.access.value; return 0; } /* Specify a new value for a system register */ static uint32_t cpuSetSystemRegister(VB *emu,int id,uint32_t value,int debug) { switch (id) { case VB_ADTRE: return emu->cpu.adtre = value & 0xFFFFFFFE; case VB_EIPC : return emu->cpu.eipc = value & 0xFFFFFFFE; case VB_EIPSW: return emu->cpu.eipsw = value & 0x000FF3FF; case VB_FEPC : return emu->cpu.fepc = value & 0xFFFFFFFE; case VB_FEPSW: return emu->cpu.fepsw = value & 0x000FF3FF; case VB_PIR : return 0x00005346; case VB_TKCW : return 0x000000E0; case 29 : return emu->cpu.sr29 = value & 0x00000001; case 31 : if (!debug && (int32_t) value < 0) value = ~value + 1; return emu->cpu.sr31 = value; case VB_CHCW : emu->cpu.chcw.ice = value >> 1 & 1; /* TODO: Perform dump/restore operations */ return value & 0x00000002; case VB_ECR : if (debug) { emu->cpu.ecr.fecc = value >> 16; emu->cpu.ecr.eicc = value; return value; } return vbGetSystemRegister(emu, id); case VB_PSW : emu->cpu.psw.i = value >> 16 & 15; emu->cpu.psw.np = value >> 15 & 1; emu->cpu.psw.ep = value >> 14 & 1; emu->cpu.psw.ae = value >> 13 & 1; emu->cpu.psw.id = value >> 12 & 1; emu->cpu.psw.fro = value >> 9 & 1; emu->cpu.psw.fiv = value >> 8 & 1; emu->cpu.psw.fzd = value >> 7 & 1; emu->cpu.psw.fov = value >> 6 & 1; emu->cpu.psw.fud = value >> 5 & 1; emu->cpu.psw.fpr = value >> 4 & 1; emu->cpu.psw.cy = value >> 3 & 1; emu->cpu.psw.ov = value >> 2 & 1; emu->cpu.psw.s = value >> 1 & 1; emu->cpu.psw.z = value & 1; return value & 0x000FF3FF; } return 0; } /* Perform a right shift */ static int cpuShiftRight(VB *emu, int32_t value, int bits, int arithmetic) { if (bits != 0) { emu->cpu.psw.cy = (value >> (bits - 1)) & 1; value = value >> bits & (int32_t) 0xFFFFFFFF << bits; if (arithmetic) value = SignExtend(value, 32 - bits); } else emu->cpu.psw.cy = 0; return cpuBitwise(emu, value); } /* Perform an output or store */ static int cpuStore(VB *emu,VB_INSTRUCTION *inst,uint8_t type,uint32_t clocks){ /* Initiate the write */ if (!emu->cpu.busWait) { /* Read the data unit from the bus */ if (cpuWrite( emu, emu->cpu.program[inst->bits[0] & 0x1F] + SignExtend((int32_t) inst->bits[1], 16), type, emu->cpu.program[inst->bits[0] >> 5 & 0x1F] )) return 1; /* Update state */ emu->cpu.busWait = 1; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Complete the write */ emu->cpu.busWait = 0; emu->cpu.clocks = clocks; return 0; } /* Subtract two numbers and update the flags */ static int32_t cpuSubtract(VB *emu, int32_t left, int32_t right) { int32_t result = left - right; emu->cpu.clocks = 1; emu->cpu.psw.cy = (uint32_t) result > (uint32_t) left; emu->cpu.psw.ov = ((left ^ right) & (left ^ result)) >> 31 & 1; emu->cpu.psw.s = result < 0; emu->cpu.psw.z = result == 0; return result; } /************************ Instruction Implementations ************************/ /* Add Immediate */ static void cpuADD_IMM(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; int32_t right = inst->bits[0] & 0x1F; *reg2 = cpuAdd(emu, *reg2, SignExtend(right, 5)); } /* Add Register */ static void cpuADD_REG(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuAdd(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F]); } /* Add Floating Short */ static void cpuADDF_S(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; double left = cpuFloatOperand(emu, *reg2); double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); int32_t result; /* Output bits */ /* Perform the operation */ if (emu->cpu.causeCode == 0) result = cpuFloatResult(emu, left + right); if (emu->cpu.causeCode != 0) return; /* Update state */ *reg2 = result; emu->cpu.clocks = 28; } /* Add Immediate */ static void cpuADDI(VB *emu, VB_INSTRUCTION *inst) { int32_t right = inst->bits[1]; emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuAdd(emu, emu->cpu.program[inst->bits[0] & 0x1F], SignExtend(right, 16)); } /* And */ static void cpuAND(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuBitwise(emu, *reg2 & emu->cpu.program[inst->bits[0] & 0x1F]); } /* And Bit String Upward */ #define cpuANDBSU(emu, inst) cpuBitString(emu, inst) /* And Not Bit String Upward */ #define cpuANDNBSU(emu, inst) cpuBitString(emu, inst) /* And Immediate */ static void cpuANDI(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu, emu->cpu.program[inst->bits[0] & 0x1F] & inst->bits[1]); } /* Conditional Branch */ static void cpuBCOND(VB *emu, VB_INSTRUCTION *inst) { int32_t disp; /* Target address displacement */ /* Branch to the target address */ if (cpuCondition(emu, inst->bits[0] >> 9 & 15)) { disp = inst->bits[0] & 0x1FF; cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 9)); } /* Do not branch */ else emu->cpu.clocks = 1; } /* Compare And Exchahge Interlocked */ static int cpuCAXI(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2; /* Program register reg2 */ /* Read the lock word */ if (emu->cpu.busWait == 0) { /* Compute the address of the lock word */ inst->aux[0] = emu->cpu.program[inst->bits[0] & 0x1F] + SignExtend((int32_t) inst->bits[1], 16); /* Read the data unit from the bus */ if (cpuRead(emu, inst->aux[0], VB_S32)) return 1; /* Update state */ emu->cpu.busWait = 1; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Compare and exchange */ if (emu->cpu.busWait == 1) { /* Process the lock word */ reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; cpuSubtract(emu, *reg2, emu->cpu.access.value); *reg2 = emu->cpu.access.value; /* Store the exchange value to the bus */ if (cpuWrite(emu, inst->aux[0], VB_S32, emu->cpu.psw.z ? emu->cpu.program[30] : emu->cpu.access.value)) return 1; /* Update state */ emu->cpu.clocks = emu->cpu.access.clocks; emu->cpu.busWait = 2; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Update state */ emu->cpu.busWait = 0; emu->cpu.clocks = 26; return 0; } /* Clear Interrupt Disable Flag */ static void cpuCLI(VB *emu) { emu->cpu.psw.id = 0; emu->cpu.clocks = 12; } /* Compare Immediate */ static void cpuCMP_IMM(VB *emu, VB_INSTRUCTION *inst) { int32_t right = inst->bits[0] & 0x1F; cpuSubtract(emu, emu->cpu.program[inst->bits[0] >> 5 & 0x1F], SignExtend(right, 5)); } /* Compare Register */ static void cpuCMP_REG(VB *emu, VB_INSTRUCTION *inst) { cpuSubtract(emu, emu->cpu.program[inst->bits[0] >> 5 & 0x1F], emu->cpu.program[inst->bits[0] & 0x1F]); } /* Compare Floating Short */ static void cpuCMPF_S(VB *emu, VB_INSTRUCTION *inst) { double left =cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]>>5&0x1F]); double right=cpuFloatOperand(emu,emu->cpu.program[inst->bits[0] &0x1F]); /* Perform the operation */ if (emu->cpu.causeCode == 0) cpuFloatResult(emu, left - right); if (emu->cpu.causeCode != 0) return; /* Update state */ emu->cpu.clocks = 10; } /* Convert Short Floating to Word Integer */ #define cpuCVT_SW(emu, inst) cpuFloatToWord(emu, inst, 0) /* Convert Word Integer to Short Floating */ static void cpuCVT_WS(VB *emu, VB_INSTRUCTION *inst) { int32_t value = emu->cpu.program[inst->bits[0] & 0x1F]; float result = (float) value; emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = *(int32_t *) &result; if (result != value) emu->cpu.psw.fpr = 1; emu->cpu.psw.cy = result < 0; emu->cpu.psw.ov = 0; emu->cpu.psw.s = result < 0; emu->cpu.psw.z = result == 0; emu->cpu.clocks = 16; } /* Divide */ static void cpuDIV(VB *emu, VB_INSTRUCTION *inst) { int32_t right = emu->cpu.program[inst->bits[0] & 0x1F]; int32_t *reg2; /* Program register reg2 */ /* Zero division */ if (right == 0) { emu->cpu.causeCode = 0xFF80; return; } /* Special case */ reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; if (*reg2 == INT32_MIN && right == -1) { emu->cpu.program[30] = 0; emu->cpu.psw.ov = 1; } /* Perform the operation */ else { emu->cpu.program[30] = *reg2 % right; *reg2 /= right; emu->cpu.psw.ov = 0; } /* Update state */ emu->cpu.psw.s = *reg2 < 0; emu->cpu.psw.z = *reg2 == 0; emu->cpu.clocks = 38; } /* Divide Floating Short */ static void cpuDIVF_S(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; double left = cpuFloatOperand(emu, *reg2); double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); int32_t result; /* Output bits */ /* Reserved operand */ if (emu->cpu.causeCode != 0) return; /* Zero division */ if (right == 0) { /* Invalid operation */ if (left == 0) { emu->cpu.causeCode = 0xFF70; emu->cpu.psw.fiv = 1; } /* Zero division */ else { emu->cpu.causeCode = 0xFF68; emu->cpu.psw.fzd = 1; } return; } /* Perform the operation */ result = cpuFloatResult(emu, left / right); if (emu->cpu.causeCode != 0) return; /* Update state */ *reg2 = result; emu->cpu.clocks = 44; } /* Divide Unsigned */ static void cpuDIVU(VB *emu, VB_INSTRUCTION *inst) { uint32_t right = emu->cpu.program[inst->bits[0] & 0x1F]; uint32_t *reg2; /* Program register reg2 */ /* Zero division */ if (right == 0) { emu->cpu.causeCode = 0xFF80; return; } /* Perform the operation */ reg2 = (uint32_t *) &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; emu->cpu.program[30] = *reg2 % right; *reg2 /= right; /* Update state */ emu->cpu.psw.ov = 0; emu->cpu.psw.s = *reg2 >> 31 & 1; emu->cpu.psw.z = *reg2 == 0; emu->cpu.clocks = 36; } /* Halt */ static void cpuHALT(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.state = CPU_HALTED; inst->size = 0; /* TODO: Find out how many clocks this takes */ } /* Input Byte */ #define cpuIN_B(emu, inst) cpuLoad(emu, inst, VB_U8, 5) /* Input Halfword */ #define cpuIN_H(emu, inst) cpuLoad(emu, inst, VB_U16, 5) /* Input Word */ #define cpuIN_W(emu, inst) cpuLoad(emu, inst, VB_S32, 5) /* Jump and Link */ static void cpuJAL(VB *emu, VB_INSTRUCTION *inst) { int32_t disp = ((int32_t) inst->bits[0]<<16 | inst->bits[1]) & 0x03FFFFFF; emu->cpu.program[31] = emu->cpu.pc + 4; cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 26)); } /* Jump Register */ #define cpuJMP(emu,inst) cpuJump(emu,inst,emu->cpu.program[inst->bits[0]&0x1F]) /* Jump Relative */ static void cpuJR(VB *emu, VB_INSTRUCTION *inst) { int32_t disp = ((int32_t) inst->bits[0]<<16 | inst->bits[1]) & 0x03FFFFFF; cpuJump(emu, inst, emu->cpu.pc + SignExtend(disp, 26)); } /* Load Byte */ #define cpuLD_B(emu, inst) cpuLoad(emu, inst, VB_S8, 5) /* Load Halfword */ #define cpuLD_H(emu, inst) cpuLoad(emu, inst, VB_S16, 5) /* Load Word */ #define cpuLD_W(emu, inst) cpuLoad(emu, inst, VB_S32, 5) /* Load to System Register */ static void cpuLDSR(VB *emu, VB_INSTRUCTION *inst) { cpuSetSystemRegister(emu, inst->bits[0] & 0x1F, emu->cpu.program[inst->bits[0] >> 5 & 0x1F], 0); emu->cpu.clocks = 8; } /* Move Immediate */ static void cpuMOV_IMM(VB *emu, VB_INSTRUCTION *inst) { int32_t value = inst->bits[0] & 0x1F; emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = SignExtend(value, 5); emu->cpu.clocks = 1; } /* Move Register */ static void cpuMOV_REG(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = emu->cpu.program[inst->bits[0] & 0x1F]; emu->cpu.clocks = 1; } /* Move Bit String Upward */ #define cpuMOVBSU(emu, inst) cpuBitString(emu, inst) /* Add */ static void cpuMOVEA(VB *emu, VB_INSTRUCTION *inst) { int32_t right = inst->bits[1]; emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = emu->cpu.program[inst->bits[0] & 0x1F] + SignExtend(right, 16); emu->cpu.clocks = 1; } /* Add */ static void cpuMOVHI(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = emu->cpu.program[inst->bits[0] & 0x1F] + ((int32_t)inst->bits[1]<<16); emu->cpu.clocks = 1; } /* Multiply Halfword */ static void cpuMPYHW(VB *emu, VB_INSTRUCTION *inst) { int32_t right = emu->cpu.program[inst->bits[0] & 0x1F] & 0x0001FFFF; emu->cpu.program[inst->bits[0] >> 5 & 0x1F] *= SignExtend(right, 17); emu->cpu.clocks = 9; } /* Multiply */ static void cpuMUL(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0xFF]; int64_t result = (int64_t) *reg2 * emu->cpu.program[inst->bits[0] & 0x1F]; emu->cpu.program[30] = result >> 32; *reg2 = result; emu->cpu.psw.ov = *reg2 != result; emu->cpu.psw.s = *reg2 < 0; emu->cpu.psw.z = *reg2 == 0; emu->cpu.clocks = 13; } /* Multiply Floating Short */ static void cpuMULF_S(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; double left = cpuFloatOperand(emu, *reg2); double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); int32_t result; /* Output bits */ /* Perform the operation */ if (emu->cpu.causeCode == 0) result = cpuFloatResult(emu, left * right); if (emu->cpu.causeCode != 0) return; /* Update state */ *reg2 = result; emu->cpu.clocks = 30; } /* Multiply Unsigned */ static void cpuMULU(VB *emu, VB_INSTRUCTION *inst) { uint32_t *reg2 = (uint32_t *) &emu->cpu.program[inst->bits[0]>>5&0xFF]; uint64_t result = (uint64_t) *reg2 * (uint32_t) emu->cpu.program[inst->bits[0] & 0x1F]; emu->cpu.program[30] = result >> 32; *reg2 = result; emu->cpu.psw.ov = *reg2 != result; emu->cpu.psw.s = *reg2 >> 31 & 1; emu->cpu.psw.z = *reg2 == 0; emu->cpu.clocks = 13; } /* Not */ static void cpuNOT(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu, ~emu->cpu.program[inst->bits[0] & 0x1F]); } /* Not Bit String Upward */ #define cpuNOTBSU(emu, inst) cpuBitString(emu, inst) /* Or */ static void cpuOR(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuBitwise(emu, *reg2 | emu->cpu.program[inst->bits[0] & 0x1F]); } /* Or Bit String Upward */ #define cpuORBSU(emu, inst) cpuBitString(emu, inst) /* Or Not Bit String Upward */ #define cpuORNBSU(emu, inst) cpuBitString(emu, inst) /* Or Immediate */ static void cpuORI(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu, emu->cpu.program[inst->bits[0] & 0x1F] | inst->bits[1]); } /* Output Byte */ #define cpuOUT_B(emu, inst) cpuStore(emu, inst, VB_U8, 4) /* Output Halfword */ #define cpuOUT_H(emu, inst) cpuStore(emu, inst, VB_U16, 4) /* Output Word */ #define cpuOUT_W(emu, inst) cpuStore(emu, inst, VB_S32, 4) /* Return from Trap or Interrupt */ static void cpuRETI(VB *emu, VB_INSTRUCTION *inst) { /* Duplexed exception */ if (emu->cpu.psw.np) { emu->cpu.pc = emu->cpu.fepc; emu->cpu.pcFrom = emu->cpu.fepcFrom; emu->cpu.pcTo = emu->cpu.fepcTo; cpuSetSystemRegister(emu, VB_PSW, emu->cpu.fepsw, 0); } /* Non-duplexed exception */ else { /* Exception */ if (emu->cpu.psw.ep) { emu->cpu.pc = emu->cpu.eipc; emu->cpu.pcFrom = emu->cpu.eipcFrom; emu->cpu.pcTo = emu->cpu.eipcTo; } /* No exception */ else { emu->cpu.pcFrom = emu->cpu.pc; emu->cpu.pc = emu->cpu.eipc; emu->cpu.pcTo = emu->cpu.pc; } /* Update state */ cpuSetSystemRegister(emu, VB_PSW, emu->cpu.eipsw, 0); } /* Update state */ emu->cpu.clocks = 10; inst->size = 0; } /* Reverse Bits in Word */ static void cpuREV(VB *emu, VB_INSTRUCTION *inst) { int32_t value = emu->cpu.program[inst->bits[0] & 0x1F]; value = (value >> 8 & 0x00FF00FF) | (value << 8 & (int32_t) 0xFF00FF00); value = (value >> 4 & 0x0F0F0F0F) | (value << 4 & (int32_t) 0xF0F0F0F0); value = (value >> 2 & 0x33333333) | (value << 2 & (int32_t) 0xCCCCCCCC); emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = (value >> 1 & 0x55555555) | (value << 1 & (int32_t) 0xAAAAAAAA); emu->cpu.clocks = 22; } /* Shift Arithmetic Right by Immediate */ static void cpuSAR_IMM(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuShiftRight(emu, *reg2, inst->bits[0] & 0x1F, 1); } /* Shift Arithmetic Right by Register */ static void cpuSAR_REG(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuShiftRight(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F, 1); } /* Search Bit 0 Downward */ #define cpuSCH0BSD(emu) cpuBitSearch(emu, 0, -1) /* Search Bit 0 Upward */ #define cpuSCH0BSU(emu) cpuBitSearch(emu, 0, 1) /* Search Bit 1 Downward */ #define cpuSCH1BSD(emu) cpuBitSearch(emu, 1, -1) /* Search Bit 1 Upward */ #define cpuSCH1BSU(emu) cpuBitSearch(emu, 1, 1) /* Set Interrupt Disable Flag */ static void cpuSEI(VB *emu) { emu->cpu.psw.id = 1; emu->cpu.clocks = 12; } /* Set Flag Condition */ static void cpuSETF(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuCondition(emu, inst->bits[0] & 15); emu->cpu.clocks = 1; } /* Shift Logical Left by Immediate */ static void cpuSHL_IMM(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; int32_t bits = inst->bits[0] & 0x1F; emu->cpu.psw.cy = bits == 0 ? 0 : *reg2 >> (32 - bits) & 1; *reg2 = cpuBitwise(emu, *reg2 << bits); } /* Shift Logical Left by Register */ static void cpuSHL_REG(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; int32_t bits = emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F; emu->cpu.psw.cy = bits == 0 ? 0 : *reg2 >> (32 - bits) & 1; *reg2 = cpuBitwise(emu, *reg2 << bits); } /* Shift Logical Right by Immediate */ static void cpuSHR_IMM(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuShiftRight(emu, *reg2, inst->bits[0] & 0x1F, 0); } /* Shift Logical Right by Register */ static void cpuSHR_REG(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuShiftRight(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F] & 0x1F, 0); } /* Store Byte */ #define cpuST_B(emu, inst) cpuStore(emu, inst, VB_S8, 4) /* Store Halfword */ #define cpuST_H(emu, inst) cpuStore(emu, inst, VB_S16, 4) /* Store Word */ #define cpuST_W(emu, inst) cpuStore(emu, inst, VB_S32, 4) /* Store Contents of System Register */ static void cpuSTSR(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = vbGetSystemRegister(emu, inst->bits[0] & 0x1F); emu->cpu.clocks = 8; } /* Subtract */ static void cpuSUB(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuSubtract(emu, *reg2, emu->cpu.program[inst->bits[0] & 0x1F]); } /* Subtract Floating Short */ static void cpuSUBF_S(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; double left = cpuFloatOperand(emu, *reg2); double right = cpuFloatOperand(emu,emu->cpu.program[inst->bits[0]&0x1F]); int32_t result; /* Output bits */ /* Perform the operation */ if (emu->cpu.causeCode == 0) result = cpuFloatResult(emu, left - right); if (emu->cpu.causeCode != 0) return; /* Update state */ *reg2 = result; emu->cpu.clocks = 28; } /* Trap */ static void cpuTRAP(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.causeCode = 0xFFA0 + (inst->bits[0] & 0x1F); emu->cpu.clocks = 15; emu->cpu.pc += 2; } /* Truncate Short Floating to Word Integer */ #define cpuTRNC_SW(emu, inst) cpuFloatToWord(emu, inst, 1) /* Exchange Byte */ static void cpuXB(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = (*reg2 >> 8 & (int32_t) 0x00FF0000) | (*reg2 << 8 & (int32_t) 0xFF000000) | (*reg2 & (int32_t) 0x0000FFFF) ; emu->cpu.clocks = 6; } /* Exchange Halfword */ static void cpuXH(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = (*reg2 >> 16 & 0x0000FFFF) | *reg2 << 16; emu->cpu.clocks = 1; } /* Exclusive Or */ static void cpuXOR(VB *emu, VB_INSTRUCTION *inst) { int32_t *reg2 = &emu->cpu.program[inst->bits[0] >> 5 & 0x1F]; *reg2 = cpuBitwise(emu, *reg2 ^ emu->cpu.program[inst->bits[0] & 0x1F]); } /* Exclusive Or Bit String Upward */ #define cpuXORBSU(emu, inst) cpuBitString(emu, inst) /* Exclusive Or Not Bit String Upward */ #define cpuXORNBSU(emu, inst) cpuBitString(emu, inst) /* Exclusive Or Immediate */ static void cpuXORI(VB *emu, VB_INSTRUCTION *inst) { emu->cpu.program[inst->bits[0] >> 5 & 0x1F] = cpuBitwise(emu, emu->cpu.program[inst->bits[0] & 0x1F] ^ inst->bits[1]); } /***************************** Module Functions ******************************/ /* Check for interrupts */ static int cpuCheckIRQs(VB *emu) { int x; /* Iterator */ /* Interrupts are masked */ if (emu->cpu.psw.np || emu->cpu.psw.ep || emu->cpu.psw.id) return 0; /* Check for interrupts */ for (x = 4; x >= emu->cpu.psw.i; x--) { /* The interrupt request line is low */ if (!emu->cpu.irq[x]) continue; /* Cause a pending HALT instruction to complete */ if (emu->cpu.state == CPU_HALTED) emu->cpu.pc += 2; /* Trigger an interrupt */ emu->cpu.causeCode = 0xFE00 | x << 4; emu->cpu.state = CPU_EXCEPTION; return 1; } /* No interrupt */ return 0; } /* Perform instruction execute operations */ static int cpuExecute(VB *emu) { int broke = 0; /* Application break occurred */ VB_INSTRUCTION *inst = &emu->cpu.inst; /* Shorthand reference */ /* Check for application break */ if (emu->onExecute != NULL && emu->onExecute(emu, inst)) return 1; /* Update state */ emu->cpu.causeCode = 0; /* Processing by ID */ switch (inst->id) { case CPU_ADD_IMM: cpuADD_IMM(emu, inst); break; case CPU_ADD_REG: cpuADD_REG(emu, inst); break; case CPU_ADDF_S : cpuADDF_S (emu, inst); break; case CPU_ADDI : cpuADDI (emu, inst); break; case CPU_AND : cpuAND (emu, inst); break; case CPU_ANDBSU : broke = cpuANDBSU (emu, inst); break; case CPU_ANDI : cpuANDI (emu, inst); break; case CPU_ANDNBSU: broke = cpuANDNBSU(emu, inst); break; case CPU_BCOND : cpuBCOND (emu, inst); break; case CPU_CAXI : broke = cpuCAXI (emu, inst); break; case CPU_CLI : cpuCLI (emu ); break; case CPU_CMP_IMM: cpuCMP_IMM(emu, inst); break; case CPU_CMP_REG: cpuCMP_REG(emu, inst); break; case CPU_CMPF_S : cpuCMPF_S (emu, inst); break; case CPU_CVT_SW : cpuCVT_SW (emu, inst); break; case CPU_CVT_WS : cpuCVT_WS (emu, inst); break; case CPU_DIV : cpuDIV (emu, inst); break; case CPU_DIVF_S : cpuDIVF_S (emu, inst); break; case CPU_DIVU : cpuDIVU (emu, inst); break; case CPU_HALT : cpuHALT (emu, inst); break; case CPU_IN_B : broke = cpuIN_B (emu, inst); break; case CPU_IN_H : broke = cpuIN_H (emu, inst); break; case CPU_IN_W : broke = cpuIN_W (emu, inst); break; case CPU_JAL : cpuJAL (emu, inst); break; case CPU_JMP : cpuJMP (emu, inst); break; case CPU_JR : cpuJR (emu, inst); break; case CPU_LD_B : broke = cpuLD_B (emu, inst); break; case CPU_LD_H : broke = cpuLD_H (emu, inst); break; case CPU_LD_W : broke = cpuLD_W (emu, inst); break; case CPU_LDSR : cpuLDSR (emu, inst); break; case CPU_MOV_IMM: cpuMOV_IMM(emu, inst); break; case CPU_MOV_REG: cpuMOV_REG(emu, inst); break; case CPU_MOVBSU : broke = cpuMOVBSU (emu, inst); break; case CPU_MOVEA : cpuMOVEA (emu, inst); break; case CPU_MOVHI : cpuMOVHI (emu, inst); break; case CPU_MPYHW : cpuMPYHW (emu, inst); break; case CPU_MUL : cpuMUL (emu, inst); break; case CPU_MULF_S : cpuMULF_S (emu, inst); break; case CPU_MULU : cpuMULU (emu, inst); break; case CPU_NOT : cpuNOT (emu, inst); break; case CPU_NOTBSU : broke = cpuNOTBSU (emu, inst); break; case CPU_OR : cpuOR (emu, inst); break; case CPU_ORBSU : broke = cpuORBSU (emu, inst); break; case CPU_ORI : cpuORI (emu, inst); break; case CPU_ORNBSU : broke = cpuORNBSU (emu, inst); break; case CPU_OUT_B : broke = cpuOUT_B (emu, inst); break; case CPU_OUT_H : broke = cpuOUT_H (emu, inst); break; case CPU_OUT_W : broke = cpuOUT_W (emu, inst); break; case CPU_RETI : cpuRETI (emu, inst); break; case CPU_REV : cpuREV (emu, inst); break; case CPU_SAR_IMM: cpuSAR_IMM(emu, inst); break; case CPU_SAR_REG: cpuSAR_REG(emu, inst); break; case CPU_SCH0BSD: cpuSCH0BSD(emu ); break; case CPU_SCH0BSU: cpuSCH0BSU(emu ); break; case CPU_SCH1BSD: cpuSCH1BSD(emu ); break; case CPU_SCH1BSU: cpuSCH1BSU(emu ); break; case CPU_SEI : cpuSEI (emu ); break; case CPU_SETF : cpuSETF (emu, inst); break; case CPU_SHL_IMM: cpuSHL_IMM(emu, inst); break; case CPU_SHL_REG: cpuSHL_REG(emu, inst); break; case CPU_SHR_IMM: cpuSHR_IMM(emu, inst); break; case CPU_SHR_REG: cpuSHR_REG(emu, inst); break; case CPU_ST_B : broke = cpuST_B (emu, inst); break; case CPU_ST_H : broke = cpuST_H (emu, inst); break; case CPU_ST_W : broke = cpuST_W (emu, inst); break; case CPU_STSR : cpuSTSR (emu, inst); break; case CPU_SUB : cpuSUB (emu, inst); break; case CPU_SUBF_S : cpuSUBF_S (emu, inst); break; case CPU_TRAP : cpuTRAP (emu, inst); break; case CPU_TRNC_SW: cpuTRNC_SW(emu, inst); break; case CPU_XB : cpuXB (emu, inst); break; case CPU_XH : cpuXH (emu, inst); break; case CPU_XOR : cpuXOR (emu, inst); break; case CPU_XORBSU : broke = cpuXORBSU (emu, inst); break; case CPU_XORI : cpuXORI (emu, inst); break; case CPU_XORNBSU: broke = cpuXORNBSU(emu, inst); break; default: /* CPU_ILLEGAL */ emu->cpu.causeCode = 0xFF90; } /* Instructions cannot modify r0 */ emu->cpu.program[0] = 0x00000000; /* An application break was requested */ if (broke) return 1; /* Post-instruction tasks */ if (emu->cpu.causeCode == 0 && !emu->cpu.busWait) { /* Advance to next instruction if not processing a bit string */ if (!emu->cpu.substring) emu->cpu.pc += inst->size; /* Check for interrupts */ cpuCheckIRQs(emu); } /* An exception or interrupt occurred */ if (emu->cpu.causeCode != 0) { emu->cpu.state = CPU_EXCEPTION; emu->cpu.substring = 0; } /* Switch to fetch mode if not halting and execution has completed */ else if (emu->cpu.state != CPU_HALTED && !emu->cpu.busWait && !emu->cpu.substring) { emu->cpu.state = CPU_FETCH; emu->cpu.fetch = 0; } return 0; } /* Enter an exception state */ static int cpuException(VB *emu) { /* Fatal exception */ if (emu->cpu.psw.np) { /* Write the cause code for debugging */ if (emu->cpu.busWait == 0) { if (cpuWrite(emu, 0x00000000, VB_S32, 0xFFFF0000 | emu->cpu.causeCode)) return 1; /* Update state */ emu->cpu.busWait = 1; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Write PSW for debugging */ if (emu->cpu.busWait == 1) { if (cpuWrite(emu, 0x00000000, VB_S32, vbGetSystemRegister(emu, VB_PSW))) return 1; /* Update state */ emu->cpu.busWait = 2; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Write PC for debugging */ if (emu->cpu.busWait == 2) { if (cpuWrite(emu, 0x00000000, VB_S32, emu->cpu.pc)) return 1; /* Update state */ emu->cpu.busWait = 3; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Update state */ emu->cpu.busWait = 0; emu->cpu.causeCode = 0; emu->cpu.state = CPU_FATAL; return 0; } /* Duplexed exception */ if (emu->cpu.psw.ep) { emu->cpu.ecr.fecc = emu->cpu.causeCode; emu->cpu.fepsw = vbGetSystemRegister(emu, VB_PSW); emu->cpu.fepc = emu->cpu.pc; emu->cpu.fepcFrom = emu->cpu.pcFrom; emu->cpu.fepcTo = emu->cpu.pcTo; emu->cpu.psw.np = 1; emu->cpu.pc = 0xFFFFFFD0; } /* Exception or interrupt */ else { emu->cpu.ecr.eicc = emu->cpu.causeCode; emu->cpu.eipsw = vbGetSystemRegister(emu, VB_PSW); emu->cpu.eipc = emu->cpu.pc; emu->cpu.psw.ep = 1; emu->cpu.pc = (emu->cpu.causeCode & 0x0040) != 0 ? 0xFFFFFF60 : ((uint32_t) 0xFFFF0000 | emu->cpu.causeCode); } /* Interrupt */ if (emu->cpu.causeCode < 0xFF00) emu->cpu.psw.i = emu->cpu.psw.i == 15 ? 15 : emu->cpu.psw.i + 1; /* Update state */ emu->cpu.causeCode = 0; emu->cpu.state = CPU_FETCH; emu->cpu.psw.id = 1; emu->cpu.psw.ae = 0; emu->cpu.pcFrom = emu->cpu.pc; emu->cpu.pcTo = emu->cpu.pc; /* emu->cpu.clocks = ? */ return 0; } /* Perform instruction fetch operations */ static int cpuFetch(VB *emu) { VB_INSTRUCTION *inst; /* Reference to emu->cpu.inst */ uint8_t opcode; /* 6-bit instruction opcode */ /* Need to read a data unit */ if (!emu->cpu.busWait) { /* Read the data unit from the bus */ if (cpuReadFetch(emu, emu->cpu.pc + (emu->cpu.fetch << 1))) return 1; /* Update state */ emu->cpu.busWait = 1; emu->cpu.clocks = emu->cpu.access.clocks; /* Wait for the bus access to complete */ if (emu->cpu.clocks > 0) return 0; } /* Update state */ inst = &emu->cpu.inst; inst->bits[emu->cpu.fetch] = emu->cpu.access.value; emu->cpu.busWait = 0; /* Working variables */ opcode = inst->bits[0] >> 10 & 0x3F; /* First fetch */ if (emu->cpu.fetch == 0) { /* Update state */ inst->size = CPU_SIZES[opcode]; /* A second fetch is needed */ if (inst->size == 4) { emu->cpu.fetch = 1; return 0; } } /* Determine the internal ID of the instruction */ inst->id = CPU_OPCODES[opcode]; switch (inst->id) { case CPU_BITSTRING: inst->id = CPU_BITSTRINGS[inst->id & 0x1F]; break; case CPU_FLOATENDO: inst->id = CPU_FLOATENDOS[inst->bits[1]>>10&0x3F]; } /* Update state */ emu->cpu.fetch = -1; emu->cpu.state = CPU_EXECUTE; return 0; } /* Process the simulation for some number of clocks */ static int cpuEmulate(VB *emu, uint32_t clocks) { /* Fatal halt: cannot break */ if (emu->cpu.state == CPU_FATAL) return 0; /* Process all clocks */ do { /* The next operation is after the remaining clocks */ if (clocks < emu->cpu.clocks) { emu->cpu.clocks -= clocks; break; } /* Update remaining clocks */ clocks -= emu->cpu.clocks; emu->cpu.clocks = 0; /* Processing by operations state */ switch (emu->cpu.state) { case CPU_FETCH : if (cpuFetch (emu)) return 1; break; case CPU_EXECUTE : if (cpuExecute (emu)) return 1; break; case CPU_EXCEPTION: if (cpuException(emu)) return 1; break; case CPU_HALTED : if (cpuCheckIRQs(emu)) return 0; break; } } while (clocks > 0); /* No break occurred */ return 0; } /* Determine the number of clocks before a break condititon could occur */ static uint32_t cpuUntil(VB *emu, uint32_t clocks) { /* Cannot break */ if ( emu->cpu.state == CPU_HALTED || emu->cpu.state == CPU_FATAL || ( emu->onException == NULL && emu->onExecute == NULL && emu->onRead == NULL && emu->onWrite == NULL )) return clocks; /* Will not break before next operation */ return emu->cpu.clocks < clocks ? emu->cpu.clocks : clocks; } #endif /* VBAPI */