/* This file is included into vb.c and cannot be compiled on its own. */ #ifdef VBAPI /********************************* Constants *********************************/ /* Pipeline stages */ #define CPU_FETCH 0 #define CPU_EXECUTE_A 1 #define CPU_EXECUTE_B 2 #define CPU_HALT 3 #define CPU_FATAL 4 /*********************************** Types ***********************************/ /* Handler types */ typedef int (*ExecuteAProc )(VB *); typedef void (*ExecuteBProc )(VB *); typedef void (*OperationProc)(VB *, int32_t *, int32_t); typedef int32_t (*OperandProc )(VB *); /* Opcode descriptor */ typedef struct { ExecuteAProc executeA; /* Execute handler (no state change) */ ExecuteBProc executeB; /* Execute handler (update state) */ OperationProc operation; /* Operation handler */ OperandProc operand; /* Operand handler */ uint8_t size; /* Total size in halfwords */ uint8_t aux; /* Number of clocks or access data type */ } OpDef; /* Floating-point auxiliary memory */ typedef struct { float f32; /* 32-bit result */ double f64; /* 64-bit result */ } FloatAux; /***************************** Utility Functions *****************************/ /* Check for an interrupt exception condition */ static int cpuCheckIRQs(VB *sim) { int x; /* Iterator */ /* Interrupts are masked */ if (sim->cpu.psw.id || sim->cpu.psw.ep || sim->cpu.psw.np) return 0; /* Check for interrupt requests */ for (x = 4; x >= sim->cpu.psw.i; x--) { if (!sim->cpu.irq[x]) continue; sim->cpu.exception = 0xFE00 | x << 4; return 1; } /* No interrupt */ return 0; } /* Test a condition */ static int cpuCondition(VB *sim, int index) { switch (index) { case 0: return sim->cpu.psw.ov; /*V */ case 1: return sim->cpu.psw.cy; /*C,L*/ case 2: return sim->cpu.psw.z; /*E,Z*/ case 3: return sim->cpu.psw.cy | sim->cpu.psw.z; /*NH */ case 4: return sim->cpu.psw.s; /*N */ case 5: return 1; /*T */ case 6: return sim->cpu.psw.ov ^ sim->cpu.psw.s; /*LT */ case 7: return (sim->cpu.psw.ov^sim->cpu.psw.s)|sim->cpu.psw.z;/*LE */ } return !cpuCondition(sim, index - 8); } /* Retrieve the value of a system register */ static uint32_t cpuGetSystemRegister(VB *sim, int id) { switch (id) { case VB_ADTRE: return sim->cpu.adtre; case VB_EIPC : return sim->cpu.eipc; case VB_EIPSW: return sim->cpu.eipsw; case VB_FEPC : return sim->cpu.fepc; case VB_FEPSW: return sim->cpu.fepsw; case VB_PIR : return 0x00005346; case VB_TKCW : return 0x000000E0; case 29 : return sim->cpu.sr29; case 30 : return 0x00000004; case 31 : return sim->cpu.sr31; case VB_CHCW : return (uint32_t) sim->cpu.chcw.ice << 1 ; case VB_ECR : return (uint32_t) sim->cpu.ecr.fecc << 16 | (uint32_t) sim->cpu.ecr.eicc ; case VB_PSW : return (uint32_t) sim->cpu.psw.i << 16 | (uint32_t) sim->cpu.psw.np << 15 | (uint32_t) sim->cpu.psw.ep << 14 | (uint32_t) sim->cpu.psw.ae << 13 | (uint32_t) sim->cpu.psw.id << 12 | (uint32_t) sim->cpu.psw.fro << 9 | (uint32_t) sim->cpu.psw.fiv << 8 | (uint32_t) sim->cpu.psw.fzd << 7 | (uint32_t) sim->cpu.psw.fov << 6 | (uint32_t) sim->cpu.psw.fud << 5 | (uint32_t) sim->cpu.psw.fpr << 4 | (uint32_t) sim->cpu.psw.cy << 3 | (uint32_t) sim->cpu.psw.ov << 2 | (uint32_t) sim->cpu.psw.s << 1 | (uint32_t) sim->cpu.psw.z ; } return 0; /* Invalid ID */ } /* Read a memory value from the bus */ static int cpuRead(VB *sim, uint32_t address, int type, int32_t *value) { VBAccess access; /* Bus access descriptor */ /* Retrieve the value from the simulation state */ access.clocks = 0; /* TODO: Needs research */ access.value = busRead(sim, address, type); /* Call the breakpoint handler */ if (sim->onRead != NULL) { access.address = address; access.type = type; if (sim->onRead(sim, &access)) return 1; } /* Post-processing */ sim->cpu.clocks += access.clocks; *value = access.value; return 0; } /* Fetch an instruction code unit from the bus */ static int cpuReadFetch(VB *sim) { VBAccess access; /* Bus access descriptor */ /* Retrieve the value from the simulation state */ access.address = sim->cpu.pc + (sim->cpu.step << 1); access.clocks = 0; /* TODO: Prefetch makes this tricky */ access.value = busRead(sim, access.address, VB_U16); /* Call the breakpoint handler */ if (sim->onFetch != NULL) { access.type = VB_U16; if (sim->onFetch(sim, sim->cpu.step, &access)) return 1; } /* Post-processing */ sim->cpu.clocks += access.clocks; sim->cpu.inst.code[sim->cpu.step++] = access.value; return 0; } /* Detect a floating-point reserved operand */ #define cpuFRO(x) ( \ ( \ ((x) & 0x007FFFFF) != 0x00000000 && /* Is not zero */ \ ((x) & 0x7F800000) == 0x00000000 /* Is denormal */ \ ) || ((x) & 0x7F800000) == 0x7F800000 /* Is indefinite/NaN */ \ ) /* Specify a value for a system register */ static uint32_t cpuSetSystemRegister(VB *sim,int id,uint32_t value,int debug) { switch (id) { case VB_ADTRE: return sim->cpu.adtre = value & 0xFFFFFFFE; case VB_EIPC : return sim->cpu.eipc = value & 0xFFFFFFFE; case VB_EIPSW: return sim->cpu.eipsw = value & 0x000FF3FF; case VB_FEPC : return sim->cpu.fepc = value & 0xFFFFFFFE; case VB_FEPSW: return sim->cpu.fepsw = value & 0x000FF3FF; case VB_PIR : return 0x00005346; case VB_TKCW : return 0x000000E0; case 29 : return sim->cpu.sr29 = value; case 30 : return 0x00000004; case 31 : return sim->cpu.sr31 = debug || value < (uint32_t) 0x80000000 ? value : (uint32_t) -(int32_t)value; case VB_CHCW: /* TODO: Manage cache functions */ sim->cpu.chcw.ice = value >> 1 & 1; return value & 0x00000002; case VB_ECR: if (debug) { sim->cpu.ecr.fecc = value >> 16 & 0xFFFF; sim->cpu.ecr.eicc = value & 0xFFFF; } return (uint32_t) sim->cpu.ecr.fecc << 16 | sim->cpu.ecr.eicc; case VB_PSW: sim->cpu.psw.i = value >> 16 & 15; sim->cpu.psw.np = value >> 15 & 1; sim->cpu.psw.ep = value >> 14 & 1; sim->cpu.psw.ae = value >> 13 & 1; sim->cpu.psw.id = value >> 12 & 1; sim->cpu.psw.fro = value >> 9 & 1; sim->cpu.psw.fiv = value >> 8 & 1; sim->cpu.psw.fzd = value >> 7 & 1; sim->cpu.psw.fov = value >> 6 & 1; sim->cpu.psw.fud = value >> 5 & 1; sim->cpu.psw.fpr = value >> 4 & 1; sim->cpu.psw.cy = value >> 3 & 1; sim->cpu.psw.ov = value >> 2 & 1; sim->cpu.psw.s = value >> 1 & 1; sim->cpu.psw.z = value & 1; return value & 0x000FF3FF; } return 0; /* Invalid ID */ } /* Prepare to write a memory value to the bus */ static int cpuWritePre(VB *sim,uint32_t *address,int32_t *type,int32_t *value){ VBAccess access; /* Bus access descriptor */ /* Determine how many clocks the access will take */ access.clocks = 0; /* TODO: Needs research */ /* Call the breakpoint handler */ if (sim->onWrite != NULL) { /* Query the application */ access.address = *address; access.value = *value; access.type = *type; if (sim->onWrite(sim, &access)) return 1; /* Apply changes */ *address = access.address; *value = access.value; if (access.type <= VB_S32) *type = access.type; } /* Post-processing */ sim->cpu.clocks += access.clocks; return 0; } /**************************** Execute A Handlers *****************************/ /* Standard two-operand instruction */ static int exaStdTwo(VB *sim) { OpDef *def = (OpDef *) sim->cpu.inst.def; sim->cpu.inst.aux[0] = def->operand(sim); sim->cpu.clocks += def->aux; return 0; } /* Standard three-operand instruction */ static int exaStdThree(VB *sim) { OpDef *def = (OpDef *) sim->cpu.inst.def; sim->cpu.inst.aux[0] = def->operand(sim); sim->cpu.inst.aux[1] = sim->cpu.program[sim->cpu.inst.code[0] & 31]; sim->cpu.clocks += def->aux; return 0; } /* Bit string bitwise */ static int exaBitBitwise(VB *sim) { int32_t bits; /* Working shift amount and bit mask */ uint32_t length; /* Number of bits remaining in bit string */ int32_t offDest; /* Bit offset of destination bit string */ int32_t offSrc; /* Bit offset of source bit string */ uint32_t result; /* Output wrdvalue */ uint32_t valDest; /* Destination word value */ /* Initial invocation */ if (sim->cpu.step == 0) sim->cpu.step = sim->cpu.bitstring + 1; /* Read the low-order 32 source bits */ if (sim->cpu.step == 1) { if (cpuRead(sim, sim->cpu.program[30], VB_S32, &sim->cpu.inst.aux[0])) return 1; sim->cpu.clocks += 4; /* TODO: Needs research */ sim->cpu.step = 2; } /* Read the high-order 32 source bits */ if (sim->cpu.step == 2) { if (cpuRead(sim,sim->cpu.program[30]+4,VB_S32,&sim->cpu.inst.aux[1])) return 1; sim->cpu.clocks += 4; /* TODO: Needs research */ sim->cpu.step = 3; } /* Read the destination bits */ if (sim->cpu.step == 3) { if (cpuRead(sim, sim->cpu.program[29], VB_S32, &sim->cpu.inst.aux[2])) return 1; sim->cpu.clocks += 4; /* TODO: Needs research */ sim->cpu.step = 4; } /* Compute the result */ if (sim->cpu.step == 4) { length = sim->cpu.program[28]; offDest = sim->cpu.program[26] & 31; offSrc = sim->cpu.program[27] & 31; result = sim->cpu.inst.aux[0]; valDest = sim->cpu.inst.aux[2]; bits = offDest - offSrc; /* Compose the source value */ if (bits > 0) result <<= bits; else if (bits < 0) { result >>= -bits; #ifndef VB_SIGNED_PROPAGATE result &= ((uint32_t) 1 << (32 + bits)) - 1; #endif result |= sim->cpu.inst.aux[1] << (32 + bits); } /* Compose the destination value */ ((OpDef *) sim->cpu.inst.def) ->operation(sim, (int32_t *) &result, valDest); bits = (1 << offDest) - 1; if (length < 32 && offDest + length < 32) bits |= (uint32_t) 0xFFFFFFFF << (offDest + length); sim->cpu.inst.aux[2] = (result & ~bits) | (valDest & bits); /* Prepare to write the result */ sim->cpu.inst.aux[3] = sim->cpu.program[29] & 0xFFFFFFFC; sim->cpu.inst.aux[4] = VB_S32; if (cpuWritePre(sim, (uint32_t *) &sim->cpu.inst.aux[3], &sim->cpu.inst.aux[4], &sim->cpu.inst.aux[2])) return 1; sim->cpu.clocks += 4; /* TODO: Needs research */ } return 0; } /* Bit string search */ static int exaBitSearch(VB *sim) { if (cpuRead(sim, sim->cpu.program[30], VB_S32, &sim->cpu.inst.aux[0])) return 1; sim->cpu.clocks += 4; /* TODO: Needs research */ return 0; } /* Branch on condition */ static int exaBCOND(VB *sim) { if (cpuCondition(sim, sim->cpu.inst.code[0] >> 9 & 15)) { sim->cpu.inst.aux[0] = (sim->cpu.pc + SignExtend(sim->cpu.inst.code[0], 9)) & 0xFFFFFFFE; sim->cpu.clocks += 3; } else { sim->cpu.inst.aux[0] = sim->cpu.pc + sim->cpu.inst.size; sim->cpu.clocks += 1; } return 0; } /* Compare and exchange interlocked */ static int exaCAXI(VB *sim) { /* First invocation */ if (sim->cpu.step == 0) { exaStdThree(sim); sim->cpu.inst.aux[0] += sim->cpu.inst.aux[1]; /* Address */ sim->cpu.inst.aux[1] = VB_S32; /* Write type */ sim->cpu.inst.aux[3] = sim->cpu.program[sim->cpu.inst.code[0]>>5&31]; sim->cpu.step = 1; } /* Read the lock word and determine the exchange value */ if (sim->cpu.step == 1) { if (cpuRead(sim, sim->cpu.inst.aux[0], VB_S32, &sim->cpu.inst.aux[4])) return 1; sim->cpu.inst.aux[2] = sim->cpu.inst.aux[3] == sim->cpu.inst.aux[4] ? sim->cpu.program[30] : sim->cpu.inst.aux[4]; sim->cpu.step = 2; } /* Prepare to write the exchange value */ if (sim->cpu.step == 2) { if (cpuWritePre(sim, (uint32_t *) &sim->cpu.inst.aux[0], &sim->cpu.inst.aux[1], &sim->cpu.inst.aux[2])) return 1; } return 0; } /* No special action */ static int exaDefault(VB *sim) { sim->cpu.clocks += ((OpDef *) sim->cpu.inst.def)->aux; return 0; } /* Division */ static int exaDivision(VB *sim) { exaStdTwo(sim); if (sim->cpu.inst.aux[0] == 0) { sim->cpu.clocks = 0; /* exaStdTwo adds clocks */ sim->cpu.exception = 0xFF80; /* Zero division */ } return 0; } /* Exception */ static int exaException(VB *sim) { VBException exception; /* Exception descriptor */ /* Initial invocation */ if (sim->cpu.step == 0) { /* Call the breakpoint handler */ if (sim->onException != NULL) { /* Query the application */ exception.address = sim->cpu.inst.aux[0]; exception.code = sim->cpu.exception; exception.cancel = 0; if (sim->onException(sim, &exception)) return 1; /* The application canceled the exception */ if (exception.cancel) { sim->cpu.exception = 0; sim->cpu.stage = CPU_FETCH; return 0; } /* Apply changes */ sim->cpu.inst.aux[0] = exception.address; sim->cpu.exception = exception.code; } /* Fatal exception: stage values for writing */ if (sim->cpu.psw.np) { sim->cpu.inst.aux[0] = 0x00000000; sim->cpu.inst.aux[1] = VB_S32; sim->cpu.inst.aux[2] = 0xFFFF0000 | sim->cpu.exception; sim->cpu.inst.aux[3] = 0x00000004; sim->cpu.inst.aux[4] = VB_S32; sim->cpu.inst.aux[5] = cpuGetSystemRegister(sim, VB_PSW); sim->cpu.inst.aux[6] = 0x00000008; sim->cpu.inst.aux[7] = VB_S32; sim->cpu.inst.aux[8] = sim->cpu.pc; sim->cpu.step = 1; } /* Other exception */ else sim->cpu.step = 10; } /* Prepare to dump fatal exception diagnostic values to memory */ for (; sim->cpu.step < 10; sim->cpu.step += 3) { if (cpuWritePre(sim, (uint32_t *) &sim->cpu.inst.aux[sim->cpu.step - 1], &sim->cpu.inst.aux[sim->cpu.step ], &sim->cpu.inst.aux[sim->cpu.step + 1] )) return 1; } /* Common processing */ sim->cpu.bitstring = 0; sim->cpu.clocks += 1; /* TODO: Needs research */ return 0; } /* One-operand floating-point instruction */ static int exaFloating1(VB *sim) { int bits; /* Number of bits to shift */ int32_t reg1; /* Left operand */ int32_t result; /* Operation result */ int32_t subop; /* Sub-opcode */ /* Reserved operand */ reg1 = sim->cpu.program[sim->cpu.inst.code[0] & 31]; if (cpuFRO(reg1)) { sim->cpu.fpFlags = 0x00000200; /* FRO */ sim->cpu.exception = 0xFF60; return 0; } /* Working variables */ bits = (reg1 >> 23 & 0xFF) - 150; result = (reg1 & 0x007FFFFF) | 0x00800000; subop = sim->cpu.inst.code[1] >> 10 & 63; /* Zero */ if ((reg1 & 0x7FFFFFFF) == 0x00000000) result = 0; /* Minimum negative value */ else if (bits == 8 && result == 0x00800000 && reg1 < 0) result = INT32_MIN; /* Shifting left */ else if (bits > 0) { /* Invalid operation */ if (bits > 7) { sim->cpu.fpFlags = 0x00000100; /* FIV */ sim->cpu.exception = 0xFF70; return 0; } /* Compute result */ result <<= bits; } /* Shifting right */ else if (bits < 0) { result = bits < -24 ? 0 : /* All bits shifted out */ subop == 0x0B ? result >> -bits : /* Truncate */ ((result >> (-bits - 1)) + 1) >> 1 /* Round */ ; } /* Result is negative */ if (reg1 < 0 && result != INT32_MIN) result = -result; /* Stage updates */ sim->cpu.fpFlags = result==*(float *)®1 ? 0 : 0x00000010; /* FPR */ sim->cpu.inst.aux[0] = result; sim->cpu.inst.aux[1] = subop; sim->cpu.clocks += ((OpDef *) sim->cpu.inst.def)->aux; return 0; } /* Two-operand floating-point instruction */ static int exaFloating2(VB *sim) { FloatAux *aux; /* Floating-point auxiliary memory */ int32_t bits; /* Bits of testing value */ int32_t reg1; /* Right operand */ int32_t reg2; /* Left operand */ float test; /* Floating-point testing value */ OpDef *def = (OpDef *) sim->cpu.inst.def; /* Reserved operand */ reg1 = sim->cpu.program[sim->cpu.inst.code[0] & 31]; reg2 = sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31]; if (cpuFRO(reg1) || cpuFRO(reg2)) { sim->cpu.fpFlags = 0x00000200; /* FRO */ sim->cpu.exception = 0xFF60; return 0; } /* Perform the operation */ def->operation(sim, ®2, reg1); if (sim->cpu.exception != 0) return 0; /* Handled in opDIVF_S() */ aux = (FloatAux *) &sim->cpu.inst.aux; /* Overflow */ bits = 0x7F7FFFFF; /* Maximum value */ test = *(float *)&bits; if (aux->f64 > test || aux->f64 < -test) { sim->cpu.fpFlags = 0x00000040; /* FOV */ sim->cpu.exception = 0xFF64; return 0; } /* Process result */ bits = *(int32_t *)&aux->f32; sim->cpu.fpFlags = 0; /* Zero */ if ((bits & 0x7FFFFFFF) == 0x00000000) aux->f32 = bits = 0; /* Underflow */ else if ((bits & 0x7F800000) == 0x00000000) { sim->cpu.fpFlags = 0x00000020; /* FUD */ aux->f32 = bits = 0; } /* Precision degradation */ if (aux->f32 != aux->f64) sim->cpu.fpFlags |= 0x00000010; /* FPR */ /* Other state */ sim->cpu.inst.aux[0] = bits; sim->cpu.inst.aux[1] = sim->cpu.inst.code[1] >> 10 & 63; sim->cpu.clocks += def->aux; return 0; } /* Illegal opcode */ static int exaIllegal(VB *sim) { sim->cpu.exception = 0xFF90; /* Illegal opcode */ return 0; } /* Jump relative, jump and link */ static int exaJR(VB *sim) { sim->cpu.inst.aux[0] = 0xFFFFFFFE & (sim->cpu.pc + SignExtend( (int32_t) sim->cpu.inst.code[0] << 16 | sim->cpu.inst.code[1], 26)); sim->cpu.clocks += 3; return 0; } /* Memory read */ static int exaRead(VB *sim) { /* First invocation */ if (sim->cpu.step == 0) { exaStdThree(sim); sim->cpu.inst.aux[1] += sim->cpu.inst.aux[0]; /* Address */ sim->cpu.clocks += 5; /* TODO: Needs research */ sim->cpu.step = 1; } /* Read the value */ return cpuRead(sim, sim->cpu.inst.aux[1], ((OpDef *) sim->cpu.inst.def)->aux, &sim->cpu.inst.aux[0]); } /* Trap */ static int exaTRAP(VB *sim) { /* TODO: Clocks is less 1 here because exaException adds 1 */ sim->cpu.clocks += ((OpDef *) sim->cpu.inst.def)->aux - 1; sim->cpu.exception = 0xFFA0 | (sim->cpu.inst.code[0] & 31); return 0; } /* Memory write */ static int exaWrite(VB *sim) { /* First invocation */ if (sim->cpu.step == 0) { exaStdThree(sim); sim->cpu.inst.aux[0] += sim->cpu.inst.aux[1]; sim->cpu.inst.aux[1] = ((OpDef *) sim->cpu.inst.def)->aux; /* Type */ sim->cpu.inst.aux[2] = sim->cpu.program[sim->cpu.inst.code[0]>>5&31]; sim->cpu.clocks += 4; /* TODO: Needs research */ sim->cpu.step = 1; } /* Write the value */ return cpuWritePre(sim, (uint32_t *) &sim->cpu.inst.aux[0], &sim->cpu.inst.aux[1], &sim->cpu.inst.aux[2]); } /**************************** Execute B Handlers *****************************/ /* Bit string bitwise */ static void exbBitBitwise(VB *sim) { int32_t bits; /* Write the output value */ if (sim->cpu.program[28] != 0) { busWrite(sim, sim->cpu.inst.aux[3], sim->cpu.inst.aux[4], sim->cpu.inst.aux[2], 0); } /* Prepare registers */ sim->cpu.program[26] &= 0x0000001F; sim->cpu.program[27] &= 0x0000001F; sim->cpu.program[29] &= 0xFFFFFFFC; sim->cpu.program[30] &= 0xFFFFFFFC; /* Determine how many bits of output have been processed */ bits = 32 - sim->cpu.program[26]; if ((uint32_t) sim->cpu.program[28] <= (uint32_t) bits) bits = sim->cpu.program[28]; /* Update source */ sim->cpu.program[27] += bits; if (sim->cpu.program[27] >= 32) { sim->cpu.program[27] &= 31; sim->cpu.program[30] += 4; sim->cpu.inst.aux[0] = sim->cpu.inst.aux[1]; sim->cpu.bitstring = 1; /* Read next source word */ } else sim->cpu.bitstring = 2; /* Skip source reads */ /* Update destination */ sim->cpu.program[26] += bits; if (sim->cpu.program[26] >= 32) { sim->cpu.program[26] &= 31; sim->cpu.program[29] += 4; } /* Update length */ sim->cpu.program[28] -= bits; /* Advance to the next instruction */ if (sim->cpu.program[28] == 0) { sim->cpu.bitstring = 0; sim->cpu.pc += sim->cpu.inst.size; } } /* Bit string search */ static void exbBitSearch(VB *sim) { int32_t dir = ((~sim->cpu.inst.code[0] & 1) << 1) - 1; int32_t test = sim->cpu.inst.code[0] >> 1 & 1; /* Prepare registers */ sim->cpu.program[27] &= 0x0000001F; sim->cpu.program[30] &= 0xFFFFFFFC; sim->cpu.psw.z = 1; sim->cpu.bitstring = 1; /* Process all remaining bits in the current word */ while (sim->cpu.psw.z && sim->cpu.program[28] != 0) { /* The bit does not match */ if ( (sim->cpu.inst.aux[0] & 1 << sim->cpu.program[27]) != test << sim->cpu.program[27] ) sim->cpu.program[29]++; /* A match was found */ else sim->cpu.psw.z = 0; /* Advance to the next bit */ sim->cpu.program[28]--; sim->cpu.program[27] += dir; if (sim->cpu.program[27] & 0x00000020) { sim->cpu.program[27] &= 0x0000001F; sim->cpu.program[30] += dir << 2; break; } } /* Advance to the next instruction */ if (!sim->cpu.psw.z || sim->cpu.program[28] == 0) { sim->cpu.bitstring = 0; sim->cpu.pc += sim->cpu.inst.size; } } /* Compare and exchange interlocked */ static void exbCAXI(VB *sim) { int32_t left = sim->cpu.inst.aux[3]; int32_t right = sim->cpu.inst.aux[4]; int32_t result = left - right; sim->cpu.psw.cy = (uint32_t) left < (uint32_t) right; sim->cpu.psw.ov = (int32_t) ((left ^ right) & (left ^ result)) < 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; sim->cpu.pc += sim->cpu.inst.size; sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31] = right; busWrite(sim, sim->cpu.inst.aux[0], sim->cpu.inst.aux[1], sim->cpu.inst.aux[2], 0); } /* Clear interrupt disable flag */ static void exbCLI(VB *sim) { sim->cpu.pc += sim->cpu.inst.size; sim->cpu.psw.id = 0; } /* Exception */ static void exbException(VB *sim) { int x; /* Iterator */ /* Apply staged floating-point flags */ if (sim->cpu.fpFlags != 0) { cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fpFlags | cpuGetSystemRegister(sim, VB_PSW), 0); sim->cpu.fpFlags = 0; } /* Fatal exception */ if (sim->cpu.psw.np) { for (x = 0; x < 9; x += 3) { busWrite(sim, sim->cpu.inst.aux[x], sim->cpu.inst.aux[x + 1], sim->cpu.inst.aux[x + 2], 0); } sim->cpu.stage = CPU_FATAL; return; } /* Duplexed exception */ if (sim->cpu.psw.ep) { sim->cpu.ecr.fecc = sim->cpu.exception; sim->cpu.fepc = sim->cpu.pc; sim->cpu.fepsw = cpuGetSystemRegister(sim, VB_PSW); sim->cpu.pc = 0xFFFFFFD0; sim->cpu.psw.np = 1; } /* Regular exception */ else { sim->cpu.ecr.eicc = sim->cpu.exception; sim->cpu.eipc = sim->cpu.pc; sim->cpu.eipsw = cpuGetSystemRegister(sim, VB_PSW); sim->cpu.pc = sim->cpu.inst.aux[0]; sim->cpu.psw.ep = 1; /* Interrupt */ if (sim->cpu.exception < 0xFF00) { if ((sim->cpu.inst.code[0] & 0xFC00) == 0x6800) /* HALT */ sim->cpu.eipc += sim->cpu.inst.size; sim->cpu.psw.i = Min(15, (sim->cpu.exception >> 4 & 15) + 1); } /* TRAP */ if ((sim->cpu.exception & 0xFFE0) == 0xFFA0) sim->cpu.eipc += sim->cpu.inst.size; } /* Common processing */ sim->cpu.psw.ae = 0; sim->cpu.psw.id = 1; } /* Floating-point instruction */ static void exbFloating(VB *sim) { int32_t result = sim->cpu.inst.aux[0]; /* Operation result */ int32_t subop = sim->cpu.inst.aux[1]; /* Sub-opcode */ /* Apply staged floating-point flags */ if (sim->cpu.fpFlags != 0) { cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fpFlags | cpuGetSystemRegister(sim, VB_PSW), 0); sim->cpu.fpFlags = 0; } /* Update state */ sim->cpu.pc += sim->cpu.inst.size; sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; if (subop != 0x03 && subop != 0x0B) /* CVT.SW, TRNC.SW */ sim->cpu.psw.cy = result < 0; if (subop != 0x00) /* CMPF.S */ sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31] = result; } /* Halt */ static void exbHALT(VB *sim) { sim->cpu.stage = CPU_HALT; } /* Jump and link */ static void exbJAL(VB *sim) { sim->cpu.program[31] = sim->cpu.pc + sim->cpu.inst.size; sim->cpu.pc = sim->cpu.inst.aux[0]; } /* Jump */ static void exbJMP(VB *sim) { sim->cpu.pc = sim->cpu.inst.aux[0]; } /* Return from trap or interrupt */ static void exbRETI(VB *sim) { if (sim->cpu.psw.np) { sim->cpu.pc = sim->cpu.fepc; cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fepsw, 0); } else { sim->cpu.pc = sim->cpu.eipc; cpuSetSystemRegister(sim, VB_PSW, sim->cpu.eipsw, 0); } } /* Set interrupt disable flag */ static void exbSEI(VB *sim) { sim->cpu.pc += sim->cpu.inst.size; sim->cpu.psw.id = 1; } /* Standard two-operand instruction */ static void exbStdTwo(VB *sim) { ((OpDef *) sim->cpu.inst.def)->operation(sim, &sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31], sim->cpu.inst.aux[0] ); sim->cpu.pc += sim->cpu.inst.size; } /* Standard three-operand instruction */ static void exbStdThree(VB *sim) { int32_t *reg2 = &sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31]; *reg2 = sim->cpu.inst.aux[1]; ((OpDef *) sim->cpu.inst.def)->operation(sim, reg2, sim->cpu.inst.aux[0]); sim->cpu.pc += sim->cpu.inst.size; } /**************************** Operation Handlers *****************************/ /* Add */ static void opADD(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest + src; sim->cpu.psw.cy = (uint32_t) result < (uint32_t) *dest; sim->cpu.psw.ov = (int32_t) (~(*dest ^ src) & (*dest ^ result)) < 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Add Floating Short */ static void opADDF_S(VB *sim, int32_t *dest, int32_t src) { FloatAux *aux = (FloatAux *) sim->cpu.inst.aux; double left = *(float *)dest; double right = *(float *)&src; double result = left + right; aux->f32 = result; aux->f64 = result; } /* And */ static void opAND(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest & src; sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* And Bit String Upward */ static void opANDBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest &= src; } /* And Not Bit String Upward */ static void opANDNBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest = ~*dest & src; } /* Compare */ static void opCMP(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest - src; sim->cpu.psw.cy = (uint32_t) *dest < (uint32_t) src; sim->cpu.psw.ov = (int32_t) ((*dest ^ src) & (*dest ^ result)) < 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; } /* Convert Word Integer to Short Floating */ static void opCVT_WS(VB *sim, int32_t *dest, int32_t src) { float value = (float) src; *dest = *(int32_t *)&value; if ((double) value != (double) src) sim->cpu.psw.fpr = 1; } /* Divide signed */ static void opDIV(VB *sim, int32_t *dest, int32_t src) { int32_t result; if (*dest == INT32_MIN && src == -1) { sim->cpu.psw.ov = 1; sim->cpu.psw.s = 1; sim->cpu.psw.z = 0; sim->cpu.program[30] = 0; } else { result = *dest / src; sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; sim->cpu.program[30] = *dest % src; *dest = result; } } /* Divide Floating Short */ static void opDIVF_S(VB *sim, int32_t *dest, int32_t src) { FloatAux *aux = (FloatAux *) sim->cpu.inst.aux; double left; /* Left operand */ double right; /* Right operand */ double result; /* Operation result */ /* Divisor is zero */ if (*dest == 0) { /* Invalid operation */ if (src == 0) { sim->cpu.fpFlags = 0x00000100; /* FIV */ sim->cpu.exception = 0xFF70; } /* Zero division */ else { sim->cpu.fpFlags = 0x00000080; /* FZD */ sim->cpu.exception = 0xFF68; } return; } /* Perform the operation */ left = *(float *)dest; right = *(float *)&src; result = left / right; aux->f32 = result; aux->f64 = result; } /* Divide unsigned */ static void opDIVU(VB *sim, int32_t *dest, int32_t src) { uint32_t result = (uint32_t) *dest / (uint32_t) src; sim->cpu.psw.ov = 0; sim->cpu.psw.s = (int32_t) result < 0; sim->cpu.psw.z = result == 0; sim->cpu.program[30] = (int32_t) ((uint32_t) *dest % (uint32_t) src); *dest = (int32_t) result; } /* Load to system register */ static void opLDSR(VB *sim, int32_t *dest, int32_t src) { cpuSetSystemRegister(sim, src, *dest, 0); } /* Move */ static void opMOV(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest = src; } /* Move Bit String Upward */ static void opMOVBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; (void) dest; (void) src; } /* Add Immediate */ static void opMOVEA(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest += src; } /* Multiply Halfword */ static void opMPYHW(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest *= SignExtend(src, 17); } /* Multiply signed */ static void opMUL(VB *sim, int32_t *dest, int32_t src) { int64_t result = (int64_t) *dest * (int64_t) src; int32_t resultLow = (int32_t) result; sim->cpu.psw.ov = result != resultLow; sim->cpu.psw.s = resultLow < 0; sim->cpu.psw.z = resultLow == 0; sim->cpu.program[30] = (int32_t) (result >> 32); *dest = resultLow; } /* Multiply Floating Short */ static void opMULF_S(VB *sim, int32_t *dest, int32_t src) { FloatAux *aux = (FloatAux *) sim->cpu.inst.aux; double left = *(float *)dest; double right = *(float *)&src; double result = left * right; aux->f32 = result; aux->f64 = result; } /* Multiply unsigned */ static void opMULU(VB *sim, int32_t *dest, int32_t src) { uint64_t result = (uint64_t)(uint32_t)*dest * (uint64_t)(uint32_t)src; uint32_t resultLow = (uint32_t) result; sim->cpu.psw.ov = result != resultLow; sim->cpu.psw.s = (int32_t) resultLow < 0; sim->cpu.psw.z = resultLow == 0; sim->cpu.program[30] = (int32_t) (result >> 32); *dest = (int32_t) resultLow; } /* Not */ static void opNOT(VB *sim, int32_t *dest, int32_t src) { int32_t result = ~src; sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Not Bit String Upward */ static void opNOTBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; (void) src; *dest = ~*dest; } /* Or */ static void opOR(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest | src; sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Or Bit String Upward */ static void opORBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest |= src; } /* Or Not Bit String Upward */ static void opORNBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest = ~*dest | src; } /* Reverse Bits in Word */ static void opREV(VB *sim, int32_t *dest, int32_t src) { (void) sim; src = (src << 16 & 0xFFFF0000) | (src >> 16 & 0x0000FFFF); src = (src << 8 & 0xFF00FF00) | (src >> 8 & 0x00FF00FF); src = (src << 4 & 0xF0F0F0F0) | (src >> 4 & 0x0F0F0F0F); src = (src << 2 & 0xCCCCCCCC) | (src >> 2 & 0x33333333); *dest = (src << 1 & 0xAAAAAAAA) | (src >> 1 & 0x55555555); } /* Set flag condition */ static void opSETF(VB *sim, int32_t *dest, int32_t src) { *dest = cpuCondition(sim, src & 15); } /* Shift right arithmetic */ static void opSAR(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest >> (src &= 31); #ifndef VB_SIGNED_PROPAGATE if (src != 0) result = SignExtend(result, 32 - src); #endif sim->cpu.psw.cy = src != 0 && *dest & 1 << (src - 1); sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Shift left */ static void opSHL(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest << (src &= 31); sim->cpu.psw.cy = src != 0 && *dest & 1 << (32 - src); sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Shift right logical */ static void opSHR(VB *sim, int32_t *dest, int32_t src) { int32_t result = (uint32_t) *dest >> (src &= 31); #ifndef VB_SIGNED_PROPAGATE if (src != 0) result &= ((uint32_t) 1 << (32 - src)) - 1; #endif sim->cpu.psw.cy = src != 0 && *dest & 1 << (src - 1); sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Store */ static void opST(VB *sim, int32_t *dest, int32_t src) { (void) dest, (void) src; busWrite(sim, sim->cpu.inst.aux[0], sim->cpu.inst.aux[1], sim->cpu.inst.aux[2], 0); } /* Store to system register */ static void opSTSR(VB *sim, int32_t *dest, int32_t src) { *dest = cpuGetSystemRegister(sim, src); } /* Subtract */ static void opSUB(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest - src; sim->cpu.psw.cy = (uint32_t) *dest < (uint32_t) src; sim->cpu.psw.ov = (int32_t) ((*dest ^ src) & (*dest ^ result)) < 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Subtract Floating Short */ static void opSUBF_S(VB *sim, int32_t *dest, int32_t src) { FloatAux *aux = (FloatAux *) sim->cpu.inst.aux; double left = *(float *)dest; double right = *(float *)&src; double result = left - right; aux->f32 = result; aux->f64 = result; } /* Exchange Byte */ static void opXB(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest = (src & 0xFFFF0000) | (src << 8 & 0xFF00) | (src >> 8 & 0x00FF); } /* Exchange Halfword */ static void opXH(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest = (src << 16 & 0xFFFF0000) | (src >> 16 & 0x0000FFFF); } /* Exclusive Or */ static void opXOR(VB *sim, int32_t *dest, int32_t src) { int32_t result = *dest ^ src; sim->cpu.psw.ov = 0; sim->cpu.psw.s = result < 0; sim->cpu.psw.z = result == 0; *dest = result; } /* Exclusive Or Bit String Upward */ static void opXORBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest ^= src; } /* Exclusive Or Not Bit String Upward */ static void opXORNBSU(VB *sim, int32_t *dest, int32_t src) { (void) sim; *dest = ~*dest ^ src; } /***************************** Operand Handlers ******************************/ /* imm5 (sign-extended) */ static int32_t opImm5S(VB *sim) { return SignExtend(sim->cpu.inst.code[0] & 31, 5); } /* imm5 */ static int32_t opImm5U(VB *sim) { return sim->cpu.inst.code[0] & 31; } /* imm16 (shifted left by 16) */ static int32_t opImm16H(VB *sim) { return (uint32_t) sim->cpu.inst.code[1] << 16; } /* imm16 (sign-extended) */ static int32_t opImm16S(VB *sim) { return SignExtend(sim->cpu.inst.code[1], 16); } /* imm16 */ static int32_t opImm16U(VB *sim) { return sim->cpu.inst.code[1]; } /* reg1 */ static int32_t opReg1(VB *sim) { return sim->cpu.program[sim->cpu.inst.code[0] & 31]; } /* reg2 */ static int32_t opReg2(VB *sim) { return sim->cpu.program[sim->cpu.inst.code[0] >> 5 & 31]; } /**************************** Opcode Definitions *****************************/ /* Forward references */ static int exaBitString(VB *); static int exaFloatendo(VB *); /* Exception processing */ static const OpDef OPDEF_EXCEPTION = { &exaException, &exbException, NULL, NULL, 0, 0 }; /* Top-level opcode definitions */ static const OpDef OPDEFS[] = { { &exaStdTwo , &exbStdTwo , &opMOV , &opReg1 , 1, 1 }, /* 0x00 */ { &exaStdTwo , &exbStdTwo , &opADD , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opSUB , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opCMP , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opSHL , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opSHR , &opReg1 , 1, 1 }, { &exaStdTwo , &exbJMP , NULL , &opReg1 , 1, 3 }, { &exaStdTwo , &exbStdTwo , &opSAR , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opMUL , &opReg1 , 1, 13 }, { &exaDivision , &exbStdTwo , &opDIV , &opReg1 , 1, 38 }, { &exaStdTwo , &exbStdTwo , &opMULU , &opReg1 , 1, 13 }, { &exaDivision , &exbStdTwo , &opDIVU , &opReg1 , 1, 36 }, { &exaStdTwo , &exbStdTwo , &opOR , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opAND , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opXOR , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opNOT , &opReg1 , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opMOV , &opImm5S , 1, 1 }, /* 0x10 */ { &exaStdTwo , &exbStdTwo , &opADD , &opImm5S , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opSETF , &opImm5U , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opCMP , &opImm5S , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opSHL , &opImm5U , 1, 1 }, { &exaStdTwo , &exbStdTwo , &opSHR , &opImm5U , 1, 1 }, { &exaDefault , &exbCLI , NULL , NULL , 1, 12 }, { &exaStdTwo , &exbStdTwo , &opSAR , &opImm5U , 1, 1 }, { &exaTRAP , NULL , NULL , NULL , 1, 15 }, { &exaDefault , &exbRETI , NULL , NULL , 1, 10 }, { &exaDefault , &exbHALT , NULL , NULL , 1, 0 }, { &exaIllegal , NULL , NULL , NULL , 1, 0 }, { &exaStdTwo , &exbStdTwo , &opLDSR , &opImm5U , 1, 8 }, { &exaStdTwo , &exbStdTwo , &opSTSR , &opImm5U , 1, 8 }, { &exaDefault , &exbSEI , NULL , NULL , 1, 12 }, { &exaBitString, NULL , NULL , NULL , 1, 0 }, { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, /* 0x20 */ { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, { &exaBCOND , &exbJMP , NULL , NULL , 1, 0 }, { &exaStdThree , &exbStdThree, &opMOVEA, &opImm16S, 2, 1 }, { &exaStdThree , &exbStdThree, &opADD , &opImm16S, 2, 1 }, { &exaJR , &exbJMP , NULL , NULL , 2, 0 }, { &exaJR , &exbJAL , NULL , NULL , 2, 0 }, { &exaStdThree , &exbStdThree, &opOR , &opImm16U, 2, 1 }, { &exaStdThree , &exbStdThree, &opAND , &opImm16U, 2, 1 }, { &exaStdThree , &exbStdThree, &opXOR , &opImm16U, 2, 1 }, { &exaStdThree , &exbStdThree, &opMOVEA, &opImm16H, 2, 1 }, { &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S8 }, /* 0x30 */ { &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S16 }, { &exaIllegal , NULL , NULL , NULL , 1, 0 }, { &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S32 }, { &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S8 }, { &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S16 }, { &exaIllegal , NULL , NULL , NULL , 1, 0 }, { &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S32 }, { &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_U8 }, { &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_U16 }, { &exaCAXI , &exbCAXI , NULL , &opImm16S, 2, 26 }, { &exaRead , &exbStdTwo , &opMOV , &opImm16S, 2, VB_S32 }, { &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_U8 }, { &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_U16 }, { &exaFloatendo, NULL , NULL , NULL , 2, 0 }, { &exaWrite , &exbStdTwo , &opST , &opImm16S, 2, VB_S32 } }; /* Bit string opcode definitions */ static const OpDef OPDEFS_BITSTRING[] = { { &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 }, /* 0x00 */ { &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 }, { &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 }, { &exaBitSearch , &exbBitSearch , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opORBSU , NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opANDBSU , NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opXORBSU , NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opMOVBSU , NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opORNBSU , NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opANDNBSU, NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opXORNBSU, NULL, 1, 0 }, { &exaBitBitwise, &exbBitBitwise, &opNOTBSU , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, /* 0x10 */ { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 }, { &exaIllegal , NULL , NULL , NULL, 1, 0 } }; /* Floating-point/Nintendo opcode definitions */ static const OpDef OPDEFS_FLOATENDO[] = { { &exaFloating2, &exbFloating, &opSUBF_S, NULL , 2, 10 }, /* 0x00 */ { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaStdTwo , &exbStdTwo , &opCVT_WS, &opReg1, 2, 16 }, { &exaFloating1, &exbFloating, NULL , NULL , 2, 14 }, { &exaFloating2, &exbFloating, &opADDF_S, NULL , 2, 28 }, { &exaFloating2, &exbFloating, &opSUBF_S, NULL , 2, 30 }, { &exaFloating2, &exbFloating, &opMULF_S, NULL , 2, 28 }, { &exaFloating2, &exbFloating, &opDIVF_S, NULL , 2, 44 }, { &exaStdTwo , &exbStdTwo , &opXB , &opReg2, 2, 6 }, { &exaStdTwo , &exbStdTwo , &opXH , &opReg2, 2, 1 }, { &exaStdTwo , &exbStdTwo , &opREV , &opReg1, 2, 22 }, { &exaFloating1, &exbFloating, NULL , NULL , 2, 14 }, { &exaStdTwo , &exbStdTwo , &opMPYHW , &opReg1, 2, 22 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, /* 0x10 */ { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, /* 0x20 */ { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, /* 0x30 */ { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, { &exaIllegal , NULL , NULL , NULL , 2, 0 }, }; /* Bit string instructions */ static int exaBitString(VB *sim) { OpDef *def; /* Opcode definition */ sim->cpu.inst.def = def = (OpDef *) &OPDEFS_BITSTRING[sim->cpu.inst.code[0] & 31]; return def->executeA(sim); } /* Floating-point and Nintendo instruction parser */ static int exaFloatendo(VB *sim) { OpDef *def; /* Opcode definition */ sim->cpu.inst.def = def = (OpDef *) &OPDEFS_FLOATENDO[sim->cpu.inst.code[1] >> 10 & 63]; return def->executeA(sim); } /****************************** Pipeline Stages ******************************/ /* Raise an exception */ static void cpuException(VB *sim) { sim->cpu.inst.def = (OpDef *) &OPDEF_EXCEPTION; sim->cpu.stage = CPU_EXECUTE_A; sim->cpu.inst.aux[0] = 0xFFFF0000 | (sim->cpu.exception & 0xFFF0); if (sim->cpu.inst.aux[0] == (int32_t) 0xFFFFFF70) sim->cpu.inst.aux[0] = 0xFFFFFF60; } /* Execute: Pre-processing, does not update state */ static int cpuExecuteA(VB *sim) { OpDef *def; /* Opcode descriptor */ VBInstruction inst; /* Instruction descriptor */ /* First invocation */ if (sim->cpu.step == 0 && sim->cpu.exception == 0) { /* Call the breakpoint handler */ if (sim->onExecute != NULL) { /* Query the application */ inst.address = sim->cpu.pc; inst.code[0] = sim->cpu.inst.code[0]; inst.code[1] = sim->cpu.inst.code[1]; inst.size = sim->cpu.inst.size; if (sim->onExecute(sim, &inst)) return 1; /* Apply changes */ sim->cpu.inst.code[0] = inst.code[0]; sim->cpu.inst.code[1] = inst.code[1]; sim->cpu.inst.size = inst.size; sim->cpu.inst.def = (OpDef *) &OPDEFS[sim->cpu.inst.code[0] >> 10 & 63]; } /* Detect non-bit string instruction */ if ((sim->cpu.inst.code[0] & 0xFC00) != 0x7C00) sim->cpu.bitstring = 0; } /* Processing before updating simulation state */ def = sim->cpu.inst.def; for (;;) { if (def->executeA(sim)) return 1; sim->cpu.step = 0; /* Advance to exception processing */ if (sim->cpu.exception == 0 || def == &OPDEF_EXCEPTION) break; def = (OpDef *) &OPDEF_EXCEPTION; cpuException(sim); } /* Advance to execute B */ sim->cpu.stage = CPU_EXECUTE_B; return 0; } /* Execute: Post-processing, updates state */ static void cpuExecuteB(VB *sim) { /* Perform the operation and update state */ ((OpDef *) sim->cpu.inst.def)->executeB(sim); sim->cpu.program[0] = 0; /* Advance to next pipeline stage */ if (sim->cpu.stage == CPU_EXECUTE_B) { if (cpuCheckIRQs(sim)) cpuException(sim); else if (sim->cpu.bitstring != 0) sim->cpu.stage = CPU_EXECUTE_A; else sim->cpu.stage = CPU_FETCH; } } /* Retrieve instruction data from the bus */ static int cpuFetch(VB *sim) { OpDef *def; /* Opcode definition */ /* First fetch */ if (sim->cpu.step == 0) { if (cpuReadFetch(sim)) return 1; sim->cpu.inst.def = def = (OpDef *) &OPDEFS[sim->cpu.inst.code[0] >> 10 & 0x003F]; sim->cpu.inst.size = def->size << 1; sim->cpu.step = 1; } else def = (OpDef *) sim->cpu.inst.def; /* Second fetch */ for (; sim->cpu.step < def->size; sim->cpu.step++) { if (cpuReadFetch(sim)) return 1; } /* Advance to execute A */ sim->cpu.stage = CPU_EXECUTE_A; sim->cpu.step = 0; return 0; } /***************************** Module Functions ******************************/ /* Process a simulation for a given number of clocks */ static int cpuEmulate(VB *sim, uint32_t clocks) { /* Process all clocks */ for (;;) { /* Processing by pipeline stage */ switch (sim->cpu.stage) { /* Fetch: Retrive instruction code from memory */ case CPU_FETCH: if (cpuFetch(sim)) return 1; break; /* Execute A: Check for exceptions, configure CPU clocks */ case CPU_EXECUTE_A: if (cpuExecuteA(sim)) return 1; break; /* Execute B: Wait clocks and update state */ case CPU_EXECUTE_B: /* Clocks remaining exceeds emulation clocks */ if (clocks < sim->cpu.clocks) { sim->cpu.clocks -= clocks; return 0; } /* Update simulation state */ clocks -= sim->cpu.clocks; sim->cpu.clocks = 0; cpuExecuteB(sim); break; /* Halt: Wait for an interrupt */ case CPU_HALT: if (!cpuCheckIRQs(sim)) return 0; cpuException(sim); break; /* Fatal exception: Cannot recover */ case CPU_FATAL: return 0; } }; /* Unreachable */ return 0; } /* Compute clocks without breakpoint or state change */ static int cpuUntil(VB *sim, uint32_t clocks) { return sim->cpu.stage == CPU_HALT || sim->cpu.stage == CPU_FATAL ? clocks : Min(sim->cpu.clocks, clocks); } #endif /* VBAPI */