/* This file is included into vb.c and cannot be compiled on its own. */ #ifdef VBAPI /********************************* Constants *********************************/ /* Operation IDs */ #define CPU_EXCEPTION 0 #define CPU_HALTING 1 #define CPU_FATAL 2 #define CPU_FETCH 3 #define CPU_ILLEGAL 4 #define CPU_BITSTRING 5 #define CPU_FLOATENDO 6 #define CPU_ADD_IMM 7 #define CPU_ADD_REG 8 #define CPU_ADDF_S 9 #define CPU_ADDI 10 #define CPU_AND 11 #define CPU_ANDBSU 12 #define CPU_ANDI 13 #define CPU_ANDNBSU 14 #define CPU_BCOND 15 #define CPU_CAXI 16 #define CPU_CLI 17 #define CPU_CMP_IMM 18 #define CPU_CMP_REG 19 #define CPU_CMPF_S 20 #define CPU_CVT_SW 21 #define CPU_CVT_WS 22 #define CPU_DIV 23 #define CPU_DIVF_S 24 #define CPU_DIVU 25 #define CPU_HALT 26 #define CPU_IN_B 27 #define CPU_IN_H 28 #define CPU_IN_W 29 #define CPU_JAL 30 #define CPU_JMP 31 #define CPU_JR 32 #define CPU_LD_B 33 #define CPU_LD_H 34 #define CPU_LD_W 35 #define CPU_LDSR 36 #define CPU_MOV_IMM 37 #define CPU_MOV_REG 38 #define CPU_MOVBSU 39 #define CPU_MOVEA 40 #define CPU_MOVHI 41 #define CPU_MPYHW 42 #define CPU_MUL 43 #define CPU_MULF_S 44 #define CPU_MULU 45 #define CPU_NOT 46 #define CPU_NOTBSU 47 #define CPU_OR 48 #define CPU_ORBSU 49 #define CPU_ORI 50 #define CPU_ORNBSU 51 #define CPU_OUT_B 52 #define CPU_OUT_H 53 #define CPU_OUT_W 54 #define CPU_RETI 55 #define CPU_REV 56 #define CPU_SAR_IMM 57 #define CPU_SAR_REG 58 #define CPU_SCH0BSD 59 #define CPU_SCH0BSU 60 #define CPU_SCH1BSD 61 #define CPU_SCH1BSU 62 #define CPU_SEI 63 #define CPU_SETF 64 #define CPU_SHL_IMM 65 #define CPU_SHL_REG 66 #define CPU_SHR_IMM 67 #define CPU_SHR_REG 68 #define CPU_ST_B 69 #define CPU_ST_H 70 #define CPU_ST_W 71 #define CPU_STSR 72 #define CPU_SUB 73 #define CPU_SUBF_S 74 #define CPU_TRAP 75 #define CPU_TRNC_SW 76 #define CPU_XB 77 #define CPU_XH 78 #define CPU_XOR 79 #define CPU_XORBSU 80 #define CPU_XORI 81 #define CPU_XORNBSU 82 /* Abstract operand types */ #define CPU_IMP(x) -x-1 #define CPU_DISP9 1 #define CPU_DISP26 2 #define CPU_IMM16S 3 #define CPU_IMM16U 4 #define CPU_IMM5S 5 #define CPU_IMM5U 6 #define CPU_MEM 7 #define CPU_REG1 8 #define CPU_REG2 9 /* Functional operand types */ #define CPU_LITERAL 0 #define CPU_MEMORY 1 #define CPU_REGISTER 2 /* Bit string operations */ #define CPU_AND_BS 0 #define CPU_ANDN_BS 1 #define CPU_MOV_BS 2 #define CPU_NOT_BS 3 #define CPU_OR_BS 4 #define CPU_ORN_BS 5 #define CPU_XOR_BS 6 #define CPU_XORN_BS 7 /* Instruction code lengths by opcode */ static const uint8_t INST_LENGTHS[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; /********************************** Macros ***********************************/ /* Master clocks per CPU cycles */ #define cpuClocks(x) x /* Shorthand */ #define auxData sim->cpu.aux.data /******************************** Lookup Data ********************************/ /* Opdefs by opcode */ static const uint8_t OPDEFS[] = { CPU_MOV_REG, CPU_ADD_REG, CPU_SUB , CPU_CMP_REG , /* 000000 */ 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 , /* 010000 */ 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 , /* 100000 */ 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 , /* 110000 */ 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 }; /* Opdefs by bit string sub-opcode */ static const uint8_t OPDEFS_BITSTRING[] = { CPU_SCH0BSU, CPU_SCH0BSD, CPU_SCH1BSU, CPU_SCH1BSD, /* 00000 */ CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ORBSU , CPU_ANDBSU , CPU_XORBSU , CPU_MOVBSU , /* 01000 */ CPU_ORNBSU , CPU_ANDNBSU, CPU_XORNBSU, CPU_NOTBSU , CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 10000 */ CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, /* 11000 */ CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL }; /* Opdefs by floating-point/Nintendo sub-opcodes */ static const uint8_t OPDEFS_FLOATENDO[] = { CPU_CMPF_S , CPU_ILLEGAL, CPU_CVT_WS , CPU_CVT_SW , /* 000000 */ 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, /* 010000 */ 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, /* 100000 */ 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, /* 110000 */ CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL }; /***************************** Callback Handlers *****************************/ /* Prepare to execute an instruction */ #ifndef VB_DIRECT_EXECUTE #define VB_ON_EXECUTE sim->onExecute #else extern int vbxOnExecute(VB *, uint32_t, const uint16_t *, int); #define VB_ON_EXECUTE vbxOnExecute #endif static int cpuExecute(VB *sim) { return sim->onExecute != NULL && VB_ON_EXECUTE(sim, sim->cpu.pc, sim->cpu.code, sim->cpu.length) ; } #undef VB_ON_EXECUTE /* Read a value from the memory bus */ #ifndef VB_DIRECT_READ #define VB_ON_READ sim->onRead #else extern int vbxOnRead(VB *, uint32_t, int, int32_t *, uint32_t *); #define VB_ON_READ vbxOnRead #endif static int cpuRead(VB *sim, uint32_t address, int type, int32_t *value) { uint32_t cycles = 4; /* TODO: Research this */ /* Retrieve the value from the simulation state directly */ busRead(sim, address, type, value); /* Invoke the callback if available */ if ( sim->onRead != NULL && VB_ON_READ(sim, address, type, value, &cycles) ) return 1; /* Update state */ sim->cpu.clocks += cpuClocks(cycles); return 0; } #undef VB_ON_READ /* Fetch a code unit from the memory bus */ #ifndef VB_DIRECT_FETCH #define VB_ON_FETCH sim->onFetch #else extern int vbxOnFetch(VB *, int, uint32_t, int32_t *, uint32_t *); #define VB_ON_FETCH vbxOnFetch #endif static int cpuReadFetch(VB *sim, int fetch, uint32_t address, int32_t *value) { uint32_t cycles = 0; /* Retrieve the value from the simulation state directly */ busRead(sim, address, VB_U16, value); /* Invoke the callback if available */ if ( sim->onFetch != NULL && VB_ON_FETCH(sim, fetch, address, value, &cycles) ) return 1; /* Update state */ sim->cpu.clocks += cpuClocks(cycles); return 0; } #undef VB_ON_FETCH /* Write a value to the memory bus */ #ifndef VB_DIRECT_WRITE #define VB_ON_WRITE sim->onWrite #else extern int vbxOnWrite(VB *, uint32_t, int, int32_t *, uint32_t *, int *); #define VB_ON_WRITE vbxOnWrite #endif static int cpuWrite(VB *sim, uint32_t address, int type, int32_t value) { int cancel = 0; uint32_t cycles = 3; /* TODO: Research this */ /* Invoke the callback if available */ if ( sim->onWrite != NULL && VB_ON_WRITE(sim, address, type, &value, &cycles, &cancel) ) return 1; /* Write the value to the simulation state directly */ if (!cancel) busWrite(sim, address, type, value, 0); /* Update state */ sim->cpu.clocks += cpuClocks(cycles); return 0; } #undef VB_ON_WRITE /****************************** Pipeline Stages ******************************/ /* Fetch the code bits for the next instruction */ static int cpuFetch(VB *sim) { int32_t value; switch (sim->cpu.step) { case 0: sim->cpu.pc = sim->cpu.nextPC; /* Fallthrough */ case 1: /* Retrieve the first code unit */ if (cpuReadFetch(sim, 0, sim->cpu.pc, &value)) { sim->cpu.step = 1; return 1; } /* Update state */ sim->cpu.code[0] = value; sim->cpu.length = INST_LENGTHS[value >> 10 & 63]; sim->cpu.step = 3 - sim->cpu.length; /* Wait any clocks taken */ if (sim->cpu.clocks != 0) return 0; /* Skip fetching a second code unit */ if (sim->cpu.length == 1) goto Step3; /* Fallthrough */ case 2: /* Retrieve the second code unit */ if (cpuReadFetch(sim, 1, sim->cpu.pc + 2, &value)) return 1; /* Update state */ sim->cpu.code[1] = value; sim->cpu.step = 3; /* Wait any clocks taken */ if (sim->cpu.clocks != 0) return 0; /* Fallthrough */ case 3: Step3: /* Prepare to execute the instruction */ if (cpuExecute(sim)) return 1; /* Select operation definition */ sim->cpu.operation = OPDEFS[sim->cpu.code[0] >> 10]; switch (sim->cpu.operation) { case CPU_BITSTRING: sim->cpu.operation = OPDEFS_BITSTRING[sim->cpu.code[0] & 31]; break; case CPU_FLOATENDO: sim->cpu.operation = OPDEFS_FLOATENDO[sim->cpu.code[1] >> 10]; } /* Update state */ sim->cpu.step = 0; } return 0; } /**************************** Instruction Helpers ****************************/ /* Parse the immediate 4-bit condition value */ static int32_t cpuGetCond(VB *sim) { return sim->cpu.code[0] >> 9 & 15; } /* Parse the 9-bit displacement value */ static int32_t cpuGetDisp9(VB *sim) { return SignExtend(sim->cpu.code[0], 9); } /* Parse the 26-bit displacement value */ static int32_t cpuGetDisp26(VB *sim) { return SignExtend((int32_t) sim->cpu.code[0] << 16 | sim->cpu.code[1], 26); } /* Parse the immediate 5-bit sign-extended value */ static int32_t cpuGetImm5S(VB *sim) { return SignExtend(sim->cpu.code[0], 5); } /* Parse the immediate 5-bit zero-filled value */ static int32_t cpuGetImm5U(VB *sim) { return sim->cpu.code[0] & 31; } /* Parse the immediate 16-bit sign-extended value */ static int32_t cpuGetImm16S(VB *sim) { return SignExtend(sim->cpu.code[1], 16); } /* Parse the immediate 16-bit zero-filled value */ static int32_t cpuGetImm16U(VB *sim) { return sim->cpu.code[1]; } /* Resolve the operand value for reg1 */ static int32_t cpuGetReg1(VB *sim) { return sim->cpu.program[sim->cpu.code[0] & 31]; } /* Resolve the operand value for reg2 */ static int32_t cpuGetReg2(VB *sim) { return sim->cpu.program[sim->cpu.code[0] >> 5 & 31]; } /* Supply an operand value for reg2 */ static void cpuSetReg2(VB *sim, int32_t value) { int reg2 = sim->cpu.code[0] >> 5 & 31; if (reg2 != 0) sim->cpu.program[reg2] = value; } /* Advance to the next instruction */ static void cpuAdvance(VB *sim, uint32_t clocks) { sim->cpu.clocks += clocks; sim->cpu.operation = CPU_FETCH; sim->cpu.nextPC = sim->cpu.pc + (sim->cpu.length << 1); sim->cpu.step = 0; } /* Interpret floating short bits as word integer bits */ static int32_t cpuFloatToWord(float x) { return *(int32_t *) &x; } /* Raise an exception */ static int cpuThrow(VB *sim, uint16_t cause) { sim->cpu.exception = cause; sim->cpu.operation = CPU_EXCEPTION; return 0; } /* Interpret word integer bits as floating short bits */ static float cpuWordToFloat(int32_t x) { return *(float *) &x; } /* Retrieve the value of a system register */ static uint32_t cpuGetSystemRegister(VB *sim, int index) { switch (index) { case VB_ADTRE: return sim->cpu.adtre; case VB_CHCW : return sim->cpu.chcw.ice << 1; case VB_ECR : return (uint32_t) sim->cpu.ecr.fecc << 16 | (uint32_t) sim->cpu.ecr.eicc; 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_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; case VB_TKCW : return 0x000000E0; case 29 : return sim->cpu.sr29; case 30 : return 0x00000004; case 31 : return sim->cpu.sr31; } return 0x00000000; /* All others */ } /* Specify a new value for a system register */ static uint32_t cpuSetSystemRegister(VB*sim, int index, uint32_t value, int debug) { switch (index) { case VB_ADTRE: return sim->cpu.adtre = value & 0xFFFFFFFE; case VB_CHCW : /* TODO: Configure instruction cache */ sim->cpu.chcw.ice = value >> 1 & 1; return value & 0x00000002; case VB_ECR : if (debug) { sim->cpu.ecr.fecc = value >> 16; sim->cpu.ecr.eicc = value; } return (uint32_t) sim->cpu.ecr.fecc << 16 | (uint32_t) sim->cpu.ecr.eicc; 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_PSW : sim->cpu.psw.i = value >> 16 & 0xF; 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; case VB_TKCW : return 0x000000E0; case 29 : return sim->cpu.sr29 = value; case 30 : return 0x00000004; case 31 : return sim->cpu.sr31 = debug ? value : *(int32_t *) &value < 0 ? (uint32_t) -*(int32_t *) &value : value; } return 0x00000000; /* All others */ } /* Addition common processing */ static int32_t cpuAdd(VB *sim, int32_t b) { int32_t a = cpuGetReg2(sim); int32_t c = a + b; sim->cpu.psw.cy = (uint32_t) c < (uint32_t) a; sim->cpu.psw.ov = (~(a ^ b) & (a ^ c)) < 0; sim->cpu.psw.s = c < 0; sim->cpu.psw.z = c == 0; return c; } /* Bitwise common processing */ static int32_t cpuBitwise(VB *sim, int32_t c) { sim->cpu.psw.ov = 0; sim->cpu.psw.s = c < 0; sim->cpu.psw.z = c == 0; return c; } /* Test a condition */ static int cpuCondition(VB *sim, int id) { switch (id) { case 0: return sim->cpu.psw.ov; case 1: return sim->cpu.psw.cy; case 2: return sim->cpu.psw.z; case 3: return sim->cpu.psw.cy | sim->cpu.psw.z; case 4: return sim->cpu.psw.s; case 5: return 1; case 6: return sim->cpu.psw.ov ^ sim->cpu.psw.s; case 7: return (sim->cpu.psw.ov ^ sim->cpu.psw.s) | sim->cpu.psw.z; } return !cpuCondition(sim, id & 7); } /* Floating-point common processing */ static int cpuFloatCommon(VB *sim, double value, int32_t *bits) { float floatVal; /* Overflow */ if (value < -FLT_MAX || value > FLT_MAX) { /* TODO: CPU does give reg2 some value */ sim->cpu.psw.fov = 1; cpuThrow(sim, 0xFF64); return 1; } /* Underflow */ if (value < 0 ? value > -FLT_MIN : value < FLT_MIN) { sim->cpu.psw.fud = 1; floatVal = 0; /* TODO: Can this produce negative zero? */ } /* Regular conversion */ else floatVal = value; /* Precision degradation */ if (floatVal != value) sim->cpu.psw.fpr = 1; /* Update state */ *bits = cpuFloatToWord(floatVal); sim->cpu.psw.cy = floatVal < 0; sim->cpu.psw.ov = 0; sim->cpu.psw.s = floatVal < 0; sim->cpu.psw.z = floatVal == 0; return 0; } /* Test whether a floating-point register value is a reserved operand */ static int cpuFloatReservedOne(int32_t bits) { int32_t e = bits >> 23 & 0xFF; return e ? (bits & 0x7FFFFFFF) != 0 : e == 0xFF; } /* Test whether floating-point register values are reserved operands */ static int cpuFloatReserved(VB *sim, int32_t a, int32_t b) { if (!(cpuFloatReservedOne(a) || cpuFloatReservedOne(b))) return 0; sim->cpu.psw.fro = 1; cpuThrow(sim, 0xFF60); return 1; } /* Jump and branch common processing */ static void cpuJump(VB *sim, uint32_t address) { sim->cpu.clocks += cpuClocks(3); sim->cpu.operation = CPU_FETCH; sim->cpu.nextPC = address & 0xFFFFFFFE; sim->cpu.step = 0; /* TODO: Clear prefetch buffer */ } /* Arithmetic right shift common processing */ static int32_t cpuShiftArithmetic(VB *sim, int32_t b) { int32_t a = cpuGetReg2(sim); #ifndef VB_SIGNED_PROPAGATE int32_t c = b == 0 ? a : SignExtend(a >> b, 32 - b); #else int32_t c = a >> b; #endif sim->cpu.psw.cy = b == 0 ? 0 : a >> (b - 1) & 1; sim->cpu.psw.ov = 0; sim->cpu.psw.s = c < 0; sim->cpu.psw.z = c == 0; return c; } /* Left shift common processing */ static int32_t cpuShiftLeft(VB *sim, int32_t b) { int32_t a = cpuGetReg2(sim); int32_t c = (uint32_t) a << b; sim->cpu.psw.cy = b == 0 ? 0 : a >> (32 - b) & 1; sim->cpu.psw.ov = 0; sim->cpu.psw.s = c < 0; sim->cpu.psw.z = c == 0; return c; } /* Logical right shift common processing */ static int32_t cpuShiftRight(VB *sim, int32_t b) { int32_t a = cpuGetReg2(sim); int32_t c = a >> b & (uint32_t) 0xFFFFFFFF << (32 - b); sim->cpu.psw.cy = b == 0 ? 0 : a >> (b - 1) & 1; sim->cpu.psw.ov = 0; sim->cpu.psw.s = c < 0; sim->cpu.psw.z = c == 0; return c; } /* Subtraction common processing */ static int32_t cpuSubtract(VB *sim, int32_t b) { int32_t a = cpuGetReg2(sim); int32_t c = a - b; sim->cpu.psw.cy = (uint32_t) a < (uint32_t) b; sim->cpu.psw.ov = ((a ^ b) & (a ^ c)) < 0; sim->cpu.psw.s = c < 0; sim->cpu.psw.z = c == 0; return c; } /* Memory load/input instruction */ static int cpuLD_IN(VB *sim, int type) { switch (sim->cpu.step) { case 0: auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim); /* Fallthrough */ case 1: /* Read the value from memory */ if (cpuRead(sim, auxData.address, type, &auxData.value)) { sim->cpu.step = 1; return 1; } /* Update state */ sim->cpu.clocks += cpuClocks(1); /* Wait for clocks taken */ sim->cpu.step = 2; return 0; case 2: cpuSetReg2(sim, auxData.value); cpuAdvance(sim, cpuClocks(4)); /* TODO: Research */ } return 0; } /* Memory store/output instruction */ static int cpuST_OUT(VB *sim, int type) { switch (sim->cpu.step) { case 0: auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim); /* Fallthrough */ case 1: /* Write the value to memory */ if (cpuWrite(sim, auxData.address, type, cpuGetReg2(sim))) { sim->cpu.step = 1; return 1; } /* Update state */ sim->cpu.clocks += cpuClocks(1); /* Wait for clocks taken */ sim->cpu.step = 2; return 0; case 2: cpuSetReg2(sim, auxData.value); cpuAdvance(sim, cpuClocks(3)); /* TODO: Research */ } return 0; } /************************** Instruction Operations ***************************/ /* ADD immediate */ static void cpuADDImm(VB *sim) { cpuSetReg2(sim, cpuAdd(sim, cpuGetImm5S(sim))); cpuAdvance(sim, cpuClocks(1)); } /* ADD register */ static void cpuADDReg(VB *sim) { cpuSetReg2(sim, cpuAdd(sim, cpuGetReg1(sim))); cpuAdvance(sim, cpuClocks(1)); } /* ADDF.S */ static void cpuADDF_S(VB *sim) { int32_t a = cpuGetReg2(sim); int32_t b = cpuGetReg1(sim); double c; /* Result */ int32_t d; /* Float bits for result */ if (cpuFloatReserved(sim, a, b)) return; c = (double) cpuWordToFloat(a) + cpuWordToFloat(b); if (cpuFloatCommon(sim, c, &d)) return; cpuSetReg2(sim, d); cpuAdvance(sim, cpuClocks(28)); /* TODO: Research */ } /* ADDI */ static void cpuADDI(VB *sim) { cpuSetReg2(sim, cpuAdd(sim, cpuGetImm16S(sim))); cpuAdvance(sim, cpuClocks(1)); } /* AND */ static void cpuAND(VB *sim) { cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) & cpuGetReg1(sim))); cpuAdvance(sim, cpuClocks(1)); } /* ANDI */ static void cpuANDI(VB *sim) { cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) & cpuGetImm16U(sim))); cpuAdvance(sim, cpuClocks(1)); } /* BCOND */ static void cpuBCOND(VB *sim) { if (cpuCondition(sim, cpuGetCond(sim))) cpuJump(sim, sim->cpu.pc + cpuGetDisp9(sim)); else cpuAdvance(sim, cpuClocks(1)); } /* CAXI */ static int cpuCAXI(VB *sim) { switch (sim->cpu.step) { case 0: auxData.address = cpuGetReg1(sim) + cpuGetImm16S(sim); /* Fallthrough */ case 1: /* Read the compare value */ if (cpuRead(sim, auxData.address, VB_S32, &auxData.value)) { sim->cpu.step = 1; return 1; } /* Update state */ sim->cpu.clocks += cpuClocks(1); /* TODO: Research */ cpuSubtract(sim, auxData.value); if (sim->cpu.psw.z) auxData.value = sim->cpu.program[30]; /* Wait for clocks taken */ sim->cpu.step = 2; return 0; case 2: /* Write the exchange value */ if (cpuWrite(sim, auxData.address, VB_S32, auxData.value)) return 1; /* Update state */ cpuSetReg2(sim, auxData.value); cpuAdvance(sim, cpuClocks(25)); /* TODO: Research */ } return 0; } /* CLI */ static void cpuCLI(VB *sim) { sim->cpu.psw.id = 0; cpuAdvance(sim, cpuClocks(12)); } /* CMP immediate */ static void cpuCMPImm(VB *sim) { cpuSubtract(sim, cpuGetImm5S(sim)); cpuAdvance(sim, cpuClocks(1)); } /* CMP register */ static void cpuCMPReg(VB *sim) { cpuSubtract(sim, cpuGetReg1(sim)); cpuAdvance(sim, cpuClocks(1)); } /* CMPF.S */ static void cpuCMPF_S(VB *sim) { int32_t a = cpuGetReg2(sim); int32_t b = cpuGetReg1(sim); double c; /* Result */ int32_t d; /* Float bits for result */ if (cpuFloatReserved(sim, a, b)) return; c = (double) cpuWordToFloat(a) - cpuWordToFloat(b); if (cpuFloatCommon(sim, c, &d)) return; cpuAdvance(sim, cpuClocks(10)); /* TODO: Research */ } /* CVT.SW */ static void cpuCVT_SW(VB *sim) { double f; int32_t w = cpuGetReg1(sim); if (cpuFloatReservedOne(w)) { cpuThrow(sim, 0xFF60); return; } f = cpuWordToFloat(w); if (f <= -2147483648.5 || f >= 2147483647.5) { sim->cpu.psw.fiv = 1; cpuThrow(sim, 0xFF70); return; } w = f < 0 ? f - 0.5 : f + 0.5; cpuSetReg2(sim, w); if (w != f) sim->cpu.psw.fpr = 1; sim->cpu.psw.ov = 0; sim->cpu.psw.s = w < 0; sim->cpu.psw.z = w == 0; cpuAdvance(sim, cpuClocks(14)); /* TODO: Research */ } /* CVT.WS */ static void cpuCVT_WS(VB *sim) { int32_t w = cpuGetReg1(sim); float f = w; if (f != w) sim->cpu.psw.fpr = 1; w = cpuFloatToWord(f); cpuSetReg2(sim, w); sim->cpu.psw.cy = w < 0; sim->cpu.psw.ov = 0; sim->cpu.psw.s = w < 0; sim->cpu.psw.z = w == 0; cpuAdvance(sim, cpuClocks(16)); /* TODO: Research */ } /* DIV */ static void cpuDIV(VB *sim) { int32_t a; /* Dividend */ int32_t b; /* Divisor */ int32_t c; /* Remainder */ int32_t d; /* Quotient */ #ifdef VB_DIV_GENERIC int sa; /* Sign of a */ int sb; /* Sign of b */ #endif /* Zero division */ b = cpuGetReg1(sim); if (b == 0) { cpuThrow(sim, 0xFF80); return; } /* Compute results */ a = cpuGetReg2(sim); sim->cpu.psw.ov = (uint32_t) a == (uint32_t) 0x80000000 && b == -1; #ifndef VB_DIV_GENERIC c = a % b; d = a / b; #else /* Do not divide */ if (sim->cpu.psw.ov) { c = 0; d = a; } /* Perform division */ else { /* Take signs and absolute values of operands */ sa = a < 0; if (sa) a = -a; sb = b < 0; if (sb) b = -b; /* Compute results */ c = (uint32_t) a % (uint32_t) b; if (sa) c = -c; d = (uint32_t) a / (uint32_t) b; if (sa ^ sb) d = -d; } #endif sim->cpu.program[30] = c; cpuSetReg2(sim, d); /* Flags */ sim->cpu.psw.s = d < 0; sim->cpu.psw.z = d == 0; cpuAdvance(sim, cpuClocks(38)); } /* DIVF.S */ static void cpuDIVF_S(VB *sim) { int32_t a = cpuGetReg2(sim); int32_t b = cpuGetReg1(sim); double c; /* Result */ int32_t d; /* Float bits for result */ if (cpuFloatReserved(sim, a, b)) return; if ((b & 0x7FFFFFFF) == 0) { cpuThrow(sim, (a & 0x7FFFFFFF) == 0 ? 0xFF70 : 0xFF68); return; } c = (double) cpuWordToFloat(a) / cpuWordToFloat(b); if (cpuFloatCommon(sim, c, &d)) return; cpuSetReg2(sim, d); cpuAdvance(sim, cpuClocks(44)); } /* DIVU */ static void cpuDIVU(VB *sim) { uint32_t a; /* Dividend */ uint32_t b; /* Divisor */ int32_t c; /* Remainder */ int32_t d; /* Quotient */ /* Zero division */ b = cpuGetReg1(sim); if (b == 0) { cpuThrow(sim, 0xFF80); return; } /* Compute results */ a = cpuGetReg2(sim); c = a % b; d = a / b; sim->cpu.program[30] = c; cpuSetReg2(sim, d); /* Flags */ sim->cpu.psw.ov = 0; sim->cpu.psw.s = d < 0; sim->cpu.psw.z = d == 0; cpuAdvance(sim, cpuClocks(36)); } /* HALT */ static void cpuHALT(VB *sim) { sim->cpu.operation = CPU_HALTING; /* TODO: Research clocks */ } /* IN.B */ static int cpuIN_B(VB *sim) { return cpuLD_IN(sim, VB_U8); } /* IN.H */ static int cpuIN_H(VB *sim) { return cpuLD_IN(sim, VB_U16); } /* IN.W */ static int cpuIN_W(VB *sim) { return cpuLD_IN(sim, VB_S32); } /* JAL */ static void cpuJAL(VB *sim) { sim->cpu.program[31] = sim->cpu.pc + 4; cpuJump(sim, sim->cpu.pc + cpuGetDisp26(sim)); } /* JMP */ static void cpuJMP(VB *sim) { cpuJump(sim, cpuGetReg1(sim)); } /* JR */ static void cpuJR(VB *sim) { cpuJump(sim, sim->cpu.pc + cpuGetDisp26(sim)); } /* LD.B */ static int cpuLD_B(VB *sim) { return cpuLD_IN(sim, VB_S8); } /* LD.H */ static int cpuLD_H(VB *sim) { return cpuLD_IN(sim, VB_S16); } /* LD.W */ static int cpuLD_W(VB *sim) { return cpuLD_IN(sim, VB_S32); } /* LDSR */ static void cpuLDSR(VB *sim) { cpuSetSystemRegister(sim, cpuGetImm5U(sim), cpuGetReg2(sim), 0); cpuAdvance(sim, cpuClocks(8)); } /* MOV immediate */ static void cpuMOVImm(VB *sim) { cpuSetReg2(sim, cpuGetImm5S(sim)); cpuAdvance(sim, cpuClocks(1)); } /* MOV register */ static void cpuMOVReg(VB *sim) { cpuSetReg2(sim, cpuGetReg1(sim)); cpuAdvance(sim, cpuClocks(1)); } /* MOVEA */ static void cpuMOVEA(VB *sim) { cpuSetReg2(sim, cpuGetReg1(sim) + cpuGetImm16S(sim)); cpuAdvance(sim, cpuClocks(1)); } /* MOVHI */ static void cpuMOVHI(VB *sim) { cpuSetReg2(sim, cpuGetReg1(sim) + (cpuGetImm16U(sim) << 16)); cpuAdvance(sim, cpuClocks(1)); } /* MPYHW */ static void cpuMPYHW(VB *sim) { cpuSetReg2(sim, cpuGetReg2(sim) * SignExtend(cpuGetReg1(sim), 17)); } /* MUL */ static void cpuMUL(VB *sim) { int64_t a = cpuGetReg2(sim); int64_t b = cpuGetReg1(sim); int64_t c = a * b; int32_t d = c; sim->cpu.program[30] = c >> 32; cpuSetReg2(sim, d); sim->cpu.psw.ov = d != c; sim->cpu.psw.s = d < 0; sim->cpu.psw.z = d == 0; cpuAdvance(sim, 13); } /* MULF.S */ static void cpuMULF_S(VB *sim) { int32_t a = cpuGetReg2(sim); int32_t b = cpuGetReg1(sim); double c; /* Result */ int32_t d; /* Float bits for result */ if (cpuFloatReserved(sim, a, b)) return; c = (double) cpuWordToFloat(a) * cpuWordToFloat(b); if (cpuFloatCommon(sim, c, &d)) return; cpuSetReg2(sim, d); cpuAdvance(sim, cpuClocks(30)); /* TODO: Research */ } /* MULU */ static void cpuMULU(VB *sim) { uint64_t a = cpuGetReg2(sim); uint64_t b = cpuGetReg1(sim); int64_t c = a * b; int32_t d = c; sim->cpu.program[30] = c >> 32; cpuSetReg2(sim, d); sim->cpu.psw.ov = d != c; sim->cpu.psw.s = d < 0; sim->cpu.psw.z = d == 0; cpuAdvance(sim, 13); } /* NOT */ static void cpuNOT(VB *sim) { cpuSetReg2(sim, cpuBitwise(sim, ~cpuGetReg1(sim))); cpuAdvance(sim, cpuClocks(1)); } /* OR */ static void cpuOR(VB *sim) { cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) | cpuGetReg1(sim))); cpuAdvance(sim, cpuClocks(1)); } /* ORI */ static void cpuORI(VB *sim) { cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) | cpuGetImm16U(sim))); cpuAdvance(sim, cpuClocks(1)); } /* OUT.B */ static int cpuOUT_B(VB *sim) { return cpuST_OUT(sim, VB_U8); } /* OUT.H */ static int cpuOUT_H(VB *sim) { return cpuST_OUT(sim, VB_U16); } /* OUT.W */ static int cpuOUT_W(VB *sim) { return cpuST_OUT(sim, VB_S32); } /* RETI */ static void cpuRETI(VB *sim) { if (sim->cpu.psw.np) { sim->cpu.nextPC = sim->cpu.fepc; cpuSetSystemRegister(sim, VB_PSW, sim->cpu.fepsw, 0); } else { sim->cpu.nextPC = sim->cpu.eipc; cpuSetSystemRegister(sim, VB_PSW, sim->cpu.eipsw, 0); } sim->cpu.clocks += cpuClocks(10); sim->cpu.operation = CPU_FETCH; } /* REV */ static void cpuREV(VB *sim) { uint32_t x = cpuGetReg1(sim); x = (x << 16 & 0xFFFF0000) | (x >> 16 & 0x0000FFFF); x = (x << 8 & 0xFF00FF00) | (x >> 8 & 0x00FF00FF); x = (x << 4 & 0xF0F0F0F0) | (x >> 4 & 0x0F0F0F0F); x = (x << 2 & 0xCCCCCCCC) | (x >> 2 & 0x33333333); x = (x << 1 & 0xAAAAAAAA) | (x >> 1 & 0x55555555); cpuSetReg2(sim, x); } /* SAR immediate */ static void cpuSARImm(VB *sim) { cpuSetReg2(sim, cpuShiftArithmetic(sim, cpuGetImm5U(sim))); cpuAdvance(sim, cpuClocks(1)); } /* SEI */ static void cpuSEI(VB *sim) { sim->cpu.psw.id = 1; cpuAdvance(sim, cpuClocks(12)); } /* SETF */ static void cpuSETF(VB *sim) { cpuSetReg2(sim, cpuCondition(sim, cpuGetImm5U(sim) & 15)); cpuAdvance(sim, cpuClocks(1)); } /* SHR register */ static void cpuSARReg(VB *sim) { cpuSetReg2(sim, cpuShiftArithmetic(sim, cpuGetReg1(sim) & 31)); cpuAdvance(sim, cpuClocks(1)); } /* SHL immediate */ static void cpuSHLImm(VB *sim) { cpuSetReg2(sim, cpuShiftLeft(sim, cpuGetImm5U(sim))); cpuAdvance(sim, cpuClocks(1)); } /* SHR register */ static void cpuSHLReg(VB *sim) { cpuSetReg2(sim, cpuShiftLeft(sim, cpuGetReg1(sim) & 31)); cpuAdvance(sim, cpuClocks(1)); } /* SHR immediate */ static void cpuSHRImm(VB *sim) { cpuSetReg2(sim, cpuShiftRight(sim, cpuGetImm5U(sim))); cpuAdvance(sim, cpuClocks(1)); } /* SHR register */ static void cpuSHRReg(VB *sim) { cpuSetReg2(sim, cpuShiftRight(sim, cpuGetReg1(sim) & 31)); cpuAdvance(sim, cpuClocks(1)); } /* ST.B */ static int cpuST_B(VB *sim) { return cpuST_OUT(sim, VB_S8); } /* ST.H */ static int cpuST_H(VB *sim) { return cpuST_OUT(sim, VB_S16); } /* ST.W */ static int cpuST_W(VB *sim) { return cpuST_OUT(sim, VB_S32); } /* STSR */ static void cpuSTSR(VB *sim) { cpuSetReg2(sim, cpuGetSystemRegister(sim, cpuGetImm5U(sim))); cpuAdvance(sim, cpuClocks(8)); } /* SUB */ static void cpuSUB(VB *sim) { cpuSetReg2(sim, cpuSubtract(sim, cpuGetReg1(sim))); cpuAdvance(sim, cpuClocks(1)); } /* SUBF.S */ static void cpuSUBF_S(VB *sim) { int32_t a = cpuGetReg2(sim); int32_t b = cpuGetReg1(sim); double c; /* Result */ int32_t d; /* Float bits for result */ if (cpuFloatReserved(sim, a, b)) return; c = (double) cpuWordToFloat(a) - cpuWordToFloat(b); if (cpuFloatCommon(sim, c, &d)) return; cpuSetReg2(sim, d); cpuAdvance(sim, cpuClocks(28)); /* TODO: Research */ } /* TRAP */ static void cpuTRAP(VB *sim) { sim->cpu.clocks += cpuClocks(15); sim->cpu.exception = 0xFFA0 + cpuGetImm5U(sim); sim->cpu.operation = CPU_EXCEPTION; } /* TRNC.SW */ static void cpuTRNC_SW(VB *sim) { double f; int32_t w = cpuGetReg1(sim); if (cpuFloatReservedOne(w)) { cpuThrow(sim, 0xFF60); return; } f = cpuWordToFloat(w); if (f <= -2147483649.0 || f >= 2147483648.0) { sim->cpu.psw.fiv = 1; cpuThrow(sim, 0xFF70); return; } w = f; cpuSetReg2(sim, w); if (w != f) sim->cpu.psw.fpr = 1; sim->cpu.psw.ov = 0; sim->cpu.psw.s = w < 0; sim->cpu.psw.z = w == 0; cpuAdvance(sim, cpuClocks(14)); /* TODO: Research */ } /* XB */ static void cpuXB(VB *sim) { uint32_t x = cpuGetReg2(sim); x = (x & 0xFFFF0000) | (x << 8 & 0x0000FF00) | (x >> 8 & 0x000000FF); cpuSetReg2(sim, x); } /* XH */ static void cpuXH(VB *sim) { uint32_t x = cpuGetReg2(sim); x = (x << 16 & 0xFFFF0000) | (x >> 16 & 0x0000FFFF); cpuSetReg2(sim, x); } /* XOR */ static void cpuXOR(VB *sim) { cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) ^ cpuGetReg1(sim))); cpuAdvance(sim, cpuClocks(1)); } /* XORI */ static void cpuXORI(VB *sim) { cpuSetReg2(sim, cpuBitwise(sim, cpuGetReg2(sim) ^ cpuGetImm16U(sim))); cpuAdvance(sim, cpuClocks(1)); } /***************************** Library Functions *****************************/ /* Process component */ static int cpuEmulate(VB *sim, uint32_t clocks) { int brk = 0; /* Process until there are clocks to wait */ for (;;) { /* The next event is after the time remaining */ if (sim->cpu.clocks > clocks) { sim->cpu.clocks -= clocks; return 0; } /* Advance forward the CPU's number of clocks */ if (sim->cpu.clocks != 0) { clocks -= sim->cpu.clocks; sim->cpu.clocks = 0; } /* Processing by operation ID */ switch (sim->cpu.operation) { case CPU_FETCH: brk = cpuFetch(sim); break; case CPU_ADD_IMM: cpuADDImm (sim); break; case CPU_ADD_REG: cpuADDReg (sim); break; case CPU_ADDF_S : cpuADDF_S (sim); break; case CPU_ADDI : cpuADDI (sim); break; case CPU_AND : cpuAND (sim); break; case CPU_ANDI : cpuANDI (sim); break; case CPU_BCOND : cpuBCOND (sim); break; case CPU_CAXI : brk = cpuCAXI (sim); break; case CPU_CLI : cpuCLI (sim); break; case CPU_CMP_IMM: cpuCMPImm (sim); break; case CPU_CMP_REG: cpuCMPReg (sim); break; case CPU_CMPF_S : cpuCMPF_S (sim); break; case CPU_CVT_SW : cpuCVT_SW (sim); break; case CPU_CVT_WS : cpuCVT_WS (sim); break; case CPU_DIV : cpuDIV (sim); break; case CPU_DIVF_S : cpuDIVF_S (sim); break; case CPU_DIVU : cpuDIVU (sim); break; case CPU_HALT : cpuHALT (sim); break; case CPU_IN_B : brk = cpuIN_B (sim); break; case CPU_IN_H : brk = cpuIN_H (sim); break; case CPU_IN_W : brk = cpuIN_W (sim); break; case CPU_JAL : cpuJAL (sim); break; case CPU_JMP : cpuJMP (sim); break; case CPU_JR : cpuJR (sim); break; case CPU_LD_B : brk = cpuLD_B (sim); break; case CPU_LD_H : brk = cpuLD_H (sim); break; case CPU_LD_W : brk = cpuLD_W (sim); break; case CPU_LDSR : cpuLDSR (sim); break; case CPU_MOV_IMM: cpuMOVImm (sim); break; case CPU_MOV_REG: cpuMOVReg (sim); break; case CPU_MOVEA : cpuMOVEA (sim); break; case CPU_MOVHI : cpuMOVHI (sim); break; case CPU_MPYHW : cpuMPYHW (sim); break; case CPU_MUL : cpuMUL (sim); break; case CPU_MULF_S : cpuMULF_S (sim); break; case CPU_MULU : cpuMULU (sim); break; case CPU_NOT : cpuNOT (sim); break; case CPU_OR : cpuOR (sim); break; case CPU_ORI : cpuORI (sim); break; case CPU_OUT_B : brk = cpuOUT_B (sim); break; case CPU_OUT_H : brk = cpuOUT_H (sim); break; case CPU_OUT_W : brk = cpuOUT_W (sim); break; case CPU_RETI : cpuRETI (sim); break; case CPU_REV : cpuREV (sim); break; case CPU_SAR_IMM: cpuSARImm (sim); break; case CPU_SAR_REG: cpuSARReg (sim); break; case CPU_SEI : cpuSEI (sim); break; case CPU_SETF : cpuSETF (sim); break; case CPU_SHL_IMM: cpuSHLImm (sim); break; case CPU_SHL_REG: cpuSHLReg (sim); break; case CPU_SHR_IMM: cpuSHRImm (sim); break; case CPU_SHR_REG: cpuSHRReg (sim); break; case CPU_ST_B : brk = cpuST_B (sim); break; case CPU_ST_H : brk = cpuST_H (sim); break; case CPU_ST_W : brk = cpuST_W (sim); break; case CPU_STSR : cpuSTSR (sim); break; case CPU_SUB : cpuSUB (sim); break; case CPU_SUBF_S : cpuSUBF_S (sim); break; case CPU_TRAP : cpuTRAP (sim); break; case CPU_TRNC_SW: cpuTRNC_SW(sim); break; case CPU_XB : cpuXB (sim); break; case CPU_XH : cpuXH (sim); break; case CPU_XOR : cpuXOR (sim); break; case CPU_XORI : cpuXORI (sim); break; default: return -1; /* TODO: Temporary for debugging */ } /* A callback requested a break */ if (brk) return 1; } return 0; } /* Determine how many clocks are guaranteed to process */ static uint32_t cpuUntil(VB *sim, uint32_t clocks) { return sim->cpu.clocks < clocks ? sim->cpu.clocks : clocks; } #endif /* VBAPI */