/* * Copyright 1993,1994,1995 Emin Gun Sirer * MIPSI - Mips Instruction Set Simulator. */ #define _IEEE 1 #include #include #include "MIPS/nan.h" #include "config.h" #include "main.h" #define RUNC #include "mem.h" #include "inst.h" #include #include "mt_vm.h" /* We need some asm statements for gcc to work */ #ifndef __GNUC__ #define asm(x) #endif extern jmp_buf inrun; static void long_multiply(int v1, int v2, int sign); /* FINAL_PC - the last PC that a forked thread will get to before dying. This will allow us to catch this situation and just return gracefully. We'll pick 3 since it's an unlikely number and it's odd */ #define FINAL_PC 3 #define STACKSIZE 4096 /* 4K stack size */ #define CLOCK_INTERVAL 500000 /* clock interrupt every 500,000 insts */ void makestack() { /* creates space in the stack for this thread */ /* --- stack grows down --- */ /* Note that because curr_stack is static, each time makestack() is * invoked we allocate a different area of memory to the stack */ static unsigned curr_stack = STACK_END - 4; R[REG_SP] = curr_stack; curr_stack -= STACKSIZE; } /* * run returns: * 256 + exit code */ extern int run(run_struct *run_st) { unsigned int beginpc,arg1; unsigned int inst; int tmp; unsigned lastpc; unsigned int cop, offset, rs, rt, rd, sa, target; /* for decoding */ double fsop, ftop, fpres; /* copy the values for rs, and free */ beginpc = run_st->paddress; arg1 = (unsigned int)run_st->arg; free(run_st); if(setjmp(inrun) != 0) { beginpc = sigtramp; } /* set up initial state */ npc = beginpc; nnpc = npc + 4; R[REG_A0] = (unsigned)arg1; R[REG_RA] = FINAL_PC; /* make sure that we can identify a thread that dies, i.e. have it return to an unlikely PC value */ makestack(); /* create a stack */ while(1) { static unsigned numinst; /* Create a clock tick interrupt */ numinst++; if (numinst > CLOCK_INTERVAL) { mt_os_tick(); numinst = 0; } if (npc == FINAL_PC) { return; } lastpc = pc; pc = npc; npc = nnpc; nnpc += 4; R[0] = 0; /* this could be distributed */ inst = rnr_mem_word(pc); /* read without recording in cache record */ /* * we calculate these two here and discard if unnecessary */ rs = (inst >> 21) & 0x1f; rt = (inst >> 16) & 0x1f; offset = inst & 0xffff; switch(inst >> 26) { case SPECIAL: rd = (inst >> 11) & 0x1f; switch(inst & 0x3f) { case SLL: sa = (inst >> 6) & 0x1f; R[rd] = R[rt] << sa; continue; case SRL: sa = (inst >> 6) & 0x1f; R[rd] = R[rt] >> sa; continue; case SRA: sa = (inst >> 6) & 0x1f; R[rd] = (signed int) (((signed int)R[rt]) >> sa); continue; case SLLV: R[rd] = R[rt] << (R[rs] & 0x1f); continue; case SRLV: R[rd] = R[rt] >> (R[rs] & 0x1f); continue; case SRAV: R[rd] = (((signed int)R[rt]) >> (R[rs] & 0x1f)); continue; case JR: nnpc = R[rs]; continue; case JALR: R[rd] = npc + 4; nnpc = R[rs]; continue; case SYSCALL: if((tmp = dosyscall()) == 0) continue; else { return(tmp); } case BREAK: return 0; case MFHI: R[rd] = HI; continue; case MTHI: HI = R[rs]; continue; case MFLO: R[rd] = LO; continue; case MTLO: LO = R[rs]; continue; case MULT: long_multiply(R[rs],R[rt], 1); continue; case MULTU: long_multiply(R[rs],R[rt], 0); continue; case DIV: LO = (signed) R[rs] / (signed) R[rt]; HI = (signed) R[rs] % (signed) R[rt]; continue; case DIVU: LO = R[rs] / R[rt]; HI = R[rs] % R[rt]; continue; case ADD: /* gcc refuses to produce signed add here */ asm("!twiddlestart addu add"); R[rd] = (signed) R[rs] + (signed) R[rt]; asm("!twiddleend"); continue; case ADDU: asm("!twiddleend"); R[rd] = R[rs] + R[rt]; continue; case SUB: asm("!twiddlestart subu sub"); R[rd] = (signed) R[rs] - (signed) R[rt]; asm("!twiddleend"); continue; case SUBU: R[rd] = R[rs] - R[rt]; continue; case AND: R[rd] = R[rs] & R[rt]; continue; case OR: R[rd] = R[rs] | R[rt]; continue; case XOR: R[rd] = R[rs] ^ R[rt]; continue; case NOR: R[rd] = ~(R[rs]|R[rt]); continue; case SLT: R[rd] = ((signed) R[rs] < (signed) R[rt]) ? 1: 0; continue; case SLTU: R[rd] = (R[rs] < R[rt]) ? 1: 0; continue; } case REGIMM: switch(rt) { case BLTZ: if((signed) R[rs] < 0) nnpc = npc + (SIGNEX(offset) << 2); continue; case BLTZAL: if((signed) R[rs] < 0) { R[31] = npc + 4; nnpc = npc + (SIGNEX(offset) << 2); } continue; case BGEZ: if((signed) R[rs] >= 0) nnpc = npc + (SIGNEX(offset) << 2); continue; case BGEZAL: if((signed) R[rs] >= 0) { R[31] = npc + 4; nnpc = npc + (SIGNEX(offset) << 2); } continue; default: error(Fatal, "Unknown REGIMM instruction at 0x%x\n",pc); } case J: target = (npc & 0xf0000000) + ((inst & 0x03ffffff) << 2); nnpc = target; continue; case JAL: target = (npc & 0xf0000000) + ((inst & 0x03ffffff) << 2); R[31] = npc + 4; nnpc = target; continue; case BEQ: if(R[rs] == R[rt]) nnpc = npc + (SIGNEX(offset) << 2); continue; case BNE: if(R[rs] != R[rt]) nnpc = npc + (SIGNEX(offset) << 2); continue; case BLEZ: if((signed) R[rs] <= 0) nnpc = npc + (SIGNEX(offset) << 2); continue; case BGTZ: if((signed) R[rs] > 0) nnpc = npc + (SIGNEX(offset) << 2); continue; case ADDI: /* the compiler refuses to produce signed add here */ asm("!twiddlestart addu add"); R[rt] = (signed) R[rs]+(signed)SIGNEX(offset); asm("!twiddleend"); continue; case ADDIU: R[rt] = R[rs] + SIGNEX(offset); continue; case SLTI: R[rt] = ((signed) R[rs] < (signed) SIGNEX(offset)) ? 1 : 0; continue; case SLTIU: R[rt] = (R[rs] < SIGNEX(offset)) ? 1 : 0; continue; case ANDI: R[rt] = R[rs] & offset; continue; case ORI: R[rt] = R[rs] | offset; continue; case XORI: R[rt] = R[rs] ^ offset; continue; case LUI: R[rt] = (offset << 16); continue; case LB: R[rt] = (signed int) ((signed char) read_mem_byte(R[rs] + SIGNEX(offset))); continue; case LH: R[rt] = (signed int) ((signed short) read_mem_half(R[rs] + SIGNEX(offset))); continue; case LW: R[rt] = (signed) read_mem_word(R[rs] + SIGNEX(offset)); continue; case LBU: R[rt] = read_mem_byte(R[rs] + SIGNEX(offset)); continue; case LHU: R[rt] = read_mem_half(R[rs] + SIGNEX(offset)); continue; case SB: set_mem_byte(R[rs] + SIGNEX(offset), R[rt]); continue; case SH: set_mem_half(R[rs] + SIGNEX(offset), R[rt]); continue; case SW: set_mem_word(R[rs] + SIGNEX(offset), R[rt]); continue; case LWL: { mem_addr addr = R[rs] + SIGNEX(offset); mem_word word; word = read_mem_word(addr & 0xfffffffc); switch(addr & 0x3) { case 0: R[rt] = (word << 24) | (R[rt] & 0xffffff); continue; case 1: R[rt] = (word << 16) | (R[rt] & 0xffff); continue; case 2: R[rt] = (word << 8) | (R[rt] & 0xff); continue; case 3: R[rt] = word; continue; } } case LWR: { mem_addr addr = R[rs] + SIGNEX(offset); mem_word word; word = read_mem_word(addr & 0xfffffffc); switch(addr & 0x3) { case 0: R[rt] = R[rt]; continue; case 1: R[rt] = (R[rt] & 0xff000000) | (word >> 8); continue; case 2: R[rt] = (R[rt] & 0xffff0000) | (word >> 16); continue; case 3: R[rt] = (R[rt] & 0xffffff00) | (word >> 24); continue; } } case SWL: { mem_addr addr = R[rs] + SIGNEX(offset); mem_word data; data = read_mem_word(addr & 0xfffffffc); switch(addr & 0x3) { case 0: data = (data & 0xffffff00) | (R[rt] >> 24); break; case 1: data = (data & 0xffff0000) | (R[rt] >> 16); break; case 2: data = (data & 0xff000000) | (R[rt] >> 8); break; case 3: data = R[rt]; break; } set_mem_word(addr & 0xfffffffc, data); continue; } case SWR: { mem_addr addr = R[rs] + SIGNEX(offset); mem_word data; data = read_mem_word(addr & 0xfffffffc); switch(addr & 0x3) { case 0: data = R[rt]; break; case 1: data = (R[rt] << 8) | (data & 0xff); break; case 2: data = (R[rt] << 16) | (data & 0xffff); break; case 3: data = (R[rt] << 24) | (data & 0xffffff); break; } set_mem_word(addr & 0xfffffffc, data); continue; } case COP0: case COP1: case COP2: case COP3: rd = (inst >> 11) & 0x1f; cop = (inst >> 26) - COP0; if(cop != 1) /* Not an FPU operation */ switch(rs) { case MF: R[rt] = CPR[cop][rd]; continue; case CF: R[rt] = CCR[cop][rd]; continue; case MT: CPR[cop][rd] = R[rt]; continue; case CT: CCR[cop][rd] = R[rt]; continue; case BC: if(rt == BCF) { if(CpCond[cop] == 0) nnpc = npc + (SIGNEX(offset) << 2); continue; } else { /* BCT */ if(CpCond[cop] != 0) nnpc = npc + (SIGNEX(offset) << 2); continue; } case CO: error(Fatal,"Unknown coprocessor instruction at 0x%0x\n",pc); default: error(Fatal,"Unknown coprocessor operation at 0x%0x\n",pc); } else { int fmt = ((inst >> 21) & 0x1f) - 16; /* S=0, D=1, W=4 */ int ft = (inst >> 16) & 0x1f; int fs = (inst >> 11) & 0x1f; int fd = (inst >> 6) & 0x1f; ownedfp = 1; switch(rs) { case MF: { float val = FGR[fs]; mem_word *vp = (mem_word *) &val; R[rt] = *vp; continue; } case MT: { mem_word word = R[rt]; float *wp = (float *) &word; FGR[fs] = *wp; continue; } case CT: CCR[1][rd] = R[rt]; if(rd == 31) setfsr(R[rt]); continue; case CF: R[rt] = CCR[1][rd]; continue; case BC: if(((rt == BCF) && (FpCond == 0)) || ((rt == BCT) && (FpCond != 0))) nnpc = npc + (SIGNEX(offset) << 2); continue; case FF_S: case FF_D: case FF_W: /* FPU function */ switch(fmt) { case S: fsop = FGR[fs]; ftop = FGR[ft]; break; case D: fsop = FPR[fs >> 1]; ftop = FPR[ft >> 1]; break; case W: fsop = FWR[fs]; ftop = FWR[ft]; break; default: error(Fatal, "Unknown format at 0x%x: 0x%x", pc, rnr_mem_word(pc)); } switch(inst & 0x3f) { case FABS: fpres = fabs(fsop); break; case FADD: fpres = fsop + ftop; break; case FSUB: fpres = fsop - ftop; break; case FDIV: fpres = fsop / ftop; break; case FMUL: fpres = fsop * ftop; break; case FNEG: fpres = -fsop; break; case FMOV: fpres = fsop; break; case FCVTD: fmt = D; fpres = fsop; break; case FCVTS: fmt = S; fpres = fsop; break; case FCVTW: fmt = W; fpres = fsop; break; case C_F: case CUN: case CEQ: case CUEQ: case COLT: case CULT: case COLE: case CULE: case CSF: case CNGLE: case CSEQ: case CNGL: case CLT: case CNGE: case CLE: case CNGT: { /* BUG BUG what happened to COND_IN ?? */ int less, equal, unordered; int cond = inst & 0x1f; if(NaN(fsop) || NaN(ftop)) { less = 0; equal = 0; unordered = 1; } else { less = fsop < ftop; equal = fsop == ftop; unordered = 0; } FpCond = ((COND_LT&cond) ? less : 0) | ((COND_EQ&cond) ? equal: 0) | ((COND_UN&cond) ? unordered: 0); continue; /* don't fall out to the following switch */ } default: error(Fatal, "Unknown FPU function 0x%x at pc 0x%0x\n", read_mem_word(pc), pc); } switch(fmt) { case S: FGR[fd] = (float) fpres; break; case D: FPR[fd >> 1] = (double) fpres; break; case W: FWR[fd] = (int) fpres; break; /* no need for a default because they are caught earlier */ } continue; default: error(Fatal, "Unknown rs field of instruction 0x%x at pc 0x%0x\n", read_mem_word(pc), pc); } } case LWC1: { mem_word *wp = (mem_word *) &FGR[rt]; *wp = read_mem_word(R[rs]+SIGNEX(offset)); continue; } case SWC1: { float val = FGR [rt]; mem_word *vp = (mem_word *) &val; set_mem_word(R[rs] + SIGNEX(offset), *vp); continue; } case LWC0: case LWC2: case LWC3: CPR[(inst >> 26)-LWC0][rt] = read_mem_word(R[rs]+SIGNEX(offset)); continue; case SWC0: case SWC2: case SWC3: set_mem_word(R[rs] + SIGNEX(offset), CPR[(inst>>26) - SWC0][rt]); continue; default: error(Fatal, "Unknown opcode field of inst 0x%x at 0x%x\n", inst, pc); } } } /* Multiply two 32-bit numbers, V1 and V2, to produce a 64 bit result in the HI/LO registers. The algorithm is high-school math: A B x C D ------ AD || BD AC || CB || 0 where A and B are the high and low short words of V1, C and D are the short words of V2, AD is the product of A and D, and X || Y is (X << 16) + Y. */ #define SIGN_BIT(X) ((X) & 0x80000000) #define ARITH_OVFL(RESULT, OP1, OP2) (SIGN_BIT (OP1) == SIGN_BIT (OP2) \ && SIGN_BIT (OP1) != SIGN_BIT (RESULT)) static void long_multiply (int v1, int v2, int sign) { register long a, b, c, d; register long bd, ad, cb, ac; register long mid, mid2, carry_mid = 0; int neg_sign = 0; if(sign) { if (v1 < 0) v1 = - v1, neg_sign = 1; if (v2 < 0) v2 = - v2, neg_sign = ! neg_sign; } a = (v1 >> 16) & 0xffff; b = v1 & 0xffff; c = (v2 >> 16) & 0xffff; d = v2 & 0xffff; bd = b * d; ad = a * d; cb = c * b; ac = a * c; mid = ad + cb; if (ARITH_OVFL (mid, ad, cb)) carry_mid = 1; mid2 = mid + ((bd >> 16) & 0xffff); if (ARITH_OVFL (mid2, mid, ((bd >> 16) & 0xffff))) carry_mid += 1; LO = (bd & 0xffff) | ((mid2 & 0xffff) << 16); HI = ac + (carry_mid << 16) + ((mid2 >> 16) & 0xffff); if(sign && neg_sign) { LO = ~ LO; HI = ~ HI; LO += 1; if (LO == 0) HI += 1; } } /* * this should be at the end of everything that can cause a trap during * execution on behalf of the executing program. * we use it for range checking in signals.c */ extern void endofrun(void){}