/* This file is included into vb.c and cannot be compiled on its own. */ #ifdef VBAPI /********************************* Constants *********************************/ /* Operation IDs */ #define CPU_HALTING 0 #define CPU_FATAL 1 #define CPU_FETCH 2 #define CPU_ILLEGAL 3 #define CPU_BITSTRING 4 #define CPU_FLOATENDO 5 #define CPU_DOWN 6 #define CPU_BITWISE 7 #define CPU_UP 8 #define CPU_ADD 9 #define CPU_ADDF_S 10 #define CPU_ADDI 11 #define CPU_AND 12 #define CPU_ANDI 13 #define CPU_BCOND 14 #define CPU_CAXI 15 #define CPU_CLI 16 #define CPU_CMP 17 #define CPU_CMPF_S 18 #define CPU_CVT_SW 19 #define CPU_CVT_WS 20 #define CPU_DIV 21 #define CPU_DIVF_S 22 #define CPU_DIVU 23 #define CPU_HALT 24 #define CPU_IN_B 25 #define CPU_IN_H 26 #define CPU_IN_W 27 #define CPU_JAL 28 #define CPU_JMP 29 #define CPU_JR 30 #define CPU_LD_B 31 #define CPU_LD_H 32 #define CPU_LD_W 33 #define CPU_LDSR 34 #define CPU_MOV 35 #define CPU_MOVEA 36 #define CPU_MOVHI 37 #define CPU_MPYHW 38 #define CPU_MUL 39 #define CPU_MULF_S 40 #define CPU_MULU 41 #define CPU_NOT 42 #define CPU_OR 43 #define CPU_ORI 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 50 #define CPU_SEI 51 #define CPU_SETF 52 #define CPU_SHL 53 #define CPU_SHR 54 #define CPU_ST_B 55 #define CPU_ST_H 56 #define CPU_ST_W 57 #define CPU_STSR 58 #define CPU_SUB 59 #define CPU_SUBF_S 60 #define CPU_TRAP 61 #define CPU_TRNC_SW 62 #define CPU_XB 63 #define CPU_XH 64 #define CPU_XOR 65 #define CPU_XORI 66 /* 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 , CPU_ADD , CPU_SUB , CPU_CMP , /* 000000 */ CPU_SHL , CPU_SHR , CPU_JMP , CPU_SAR , CPU_MUL , CPU_DIV , CPU_MULU , CPU_DIVU , CPU_OR , CPU_AND , CPU_XOR , CPU_NOT , CPU_MOV , CPU_ADD , CPU_SETF , CPU_CMP , /* 010000 */ CPU_SHL , CPU_SHR , CPU_CLI , CPU_SAR , 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_UP , CPU_DOWN , CPU_UP , CPU_DOWN , /* 00000 */ CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_ILLEGAL, CPU_BITWISE, CPU_BITWISE, CPU_BITWISE, CPU_BITWISE, /* 01000 */ CPU_BITWISE, CPU_BITWISE, CPU_BITWISE, CPU_BITWISE, 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 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]; } /* 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; } /* Memory access 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); sim->cpu.operation = CPU_FETCH; sim->cpu.nextPC += sim->cpu.pc + 4; sim->cpu.step = 0; } return 0; } /************************** Instruction Operations ***************************/ /* 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); } /* JMP */ static int cpuJMP(VB *sim) { sim->cpu.nextPC = cpuGetReg1(sim) & 0xFFFFFFFE; sim->cpu.clocks += cpuClocks(3); sim->cpu.operation = CPU_FETCH; /* TODO: Clear prefetch buffer */ return 0; } /* 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); } /* MOVEA */ static int cpuMOVEA(VB *sim) { cpuSetReg2(sim, cpuGetReg1(sim) + cpuGetImm16S(sim)); sim->cpu.clocks += cpuClocks(1); sim->cpu.operation = CPU_FETCH; sim->cpu.nextPC = sim->cpu.pc + 4; return 0; } /* MOVHI */ static int cpuMOVHI(VB *sim) { cpuSetReg2(sim, cpuGetReg1(sim) + (cpuGetImm16U(sim) << 16)); sim->cpu.clocks += cpuClocks(1); sim->cpu.operation = CPU_FETCH; sim->cpu.nextPC = sim->cpu.pc + 4; return 0; } /***************************** Library Functions *****************************/ /* Process component */ static int cpuEmulate(VB *sim, uint32_t clocks) { int brk; /* 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_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_JMP : brk = cpuJMP (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_MOVEA: brk = cpuMOVEA(sim); break; case CPU_MOVHI: brk = cpuMOVHI(sim); break; default: return -1; /* TODO: Temporary for debugging */ } /* A callback requested a break */ if (brk) return 1; } return 0; } /* 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 */ } /* 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 */