ref: e655f4b8bdd9da0c51bd4bece8a2ea2088e57236
dir: /sys/src/9/sgi/fptrap.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "ureg.h" #include "io.h" #include "../port/error.h" enum /* op */ { ABS = 5, ADD = 0, CVTD = 33, CVTS = 32, CVTW = 36, DIV = 3, MOV = 6, MUL = 2, NEG = 7, SUB = 1, }; static int fpunimp(ulong); static ulong branch(Ureg*, ulong); void fptrap(Ureg *ur) { ulong iw, npc; if((up->fpsave->fpstatus&(1<<17)) == 0) return; if(ur->cause & (1<<31)) iw = *(ulong*)(ur->pc+4); else iw = *(ulong*)ur->pc; if(fpunimp(iw) == 0) return; if(ur->cause & (1<<31)){ npc = branch(ur, up->fpsave->fpstatus); if(npc == 0) return; ur->pc = npc; } else ur->pc += 4; up->fpsave->fpstatus &= ~(1<<17); } static void unpack(FPsave *f, int fmt, int reg, int *sign, int *exp) { *sign = 1; if(f->reg[reg] & 0x80000000) *sign = -1; switch(fmt){ case 0: *exp = ((f->reg[reg]>>23)&0xFF) - ((1<<7)-2); break; case 1: if(reg & 1) /* shouldn't happen */ reg &= ~1; *exp = ((f->reg[reg]>>20)&0x7FF) - ((1<<10)-2); break; } } static void zeroreg(FPsave *f, int fmt, int reg, int sign) { int size; size = 0; switch(fmt){ case 0: size = 4; break; case 1: if(reg & 1) reg &= ~1; size = 8; break; } memset(&f->reg[reg], 0, size); if(sign < 0) f->reg[reg] |= 0x80000000; } static int fpunimp(ulong iw) { int ss, st, sd; int es, et, ed; int maxe, maxm; ulong op, fmt, ft, fs, fd; if((iw>>25) != 0x23) return 0; op = iw & ((1<<6)-1); fmt = (iw>>21) & ((1<<4)-1); ft = (iw>>16) & ((1<<5)-1); fs = (iw>>11) & ((1<<5)-1); fd = (iw>>6) & ((1<<5)-1); unpack(up->fpsave, fmt, fs, &ss, &es); unpack(up->fpsave, fmt, ft, &st, &et); ed = 0; maxe = 0; maxm = 0; switch(fmt){ case 0: maxe = 1<<7; maxm = 24; break; case 1: maxe = 1<<10; maxm = 53; break; } switch(op){ case ABS: up->fpsave->reg[fd] &= ~0x80000000; return 1; case NEG: up->fpsave->reg[fd] ^= 0x80000000; return 1; case SUB: st = -st; case ADD: if(es<-(maxe-maxm) && et<-(maxe-maxm)) ed = -maxe; if(es > et) sd = es; else sd = et; break; case DIV: et = -et; case MUL: sd = 1; if(ss != st) sd = -1; ed = es + et; break; case CVTS: if(fmt != 1) return 0; fmt = 0; /* convert FROM double TO single */ maxe = 1<<7; ed = es; sd = ss; break; default: /* probably a compare */ return 0; } if(ed <= -(maxe-5)){ /* guess: underflow */ zeroreg(up->fpsave, fmt, fd, sd); /* Set underflow exception and sticky */ up->fpsave->fpstatus |= (1<<3)|(1<<13); return 1; } return 0; } static ulong branch(Ureg *ur, ulong fcr31) { ulong iw, npc, rs, rt, rd, offset; iw = *(ulong*)ur->pc; rs = (iw>>21) & 0x1F; if(rs) rs = *reg(ur, rs); rt = (iw>>16) & 0x1F; if(rt) rt = *reg(ur, rt); offset = iw & ((1<<16)-1); if(offset & (1<<15)) /* sign extend */ offset |= ~((1<<16)-1); offset <<= 2; /* * Integer unit jumps first */ switch(iw>>26){ case 0: /* SPECIAL: JR or JALR */ switch(iw&0x3F){ case 0x09: /* JALR */ rd = (iw>>11) & 0x1F; if(rd) *reg(ur, rd) = ur->pc+8; /* fall through */ case 0x08: /* JR */ return rs; default: return 0; } case 1: /* BCOND */ switch((iw>>16) & 0x1F){ case 0x10: /* BLTZAL */ ur->r31 = ur->pc + 8; /* fall through */ case 0x00: /* BLTZ */ if((long)rs < 0) return ur->pc+4 + offset; return ur->pc + 8; case 0x11: /* BGEZAL */ ur->r31 = ur->pc + 8; /* fall through */ case 0x01: /* BGEZ */ if((long)rs >= 0) return ur->pc+4 + offset; return ur->pc + 8; default: return 0; } case 3: /* JAL */ ur->r31 = ur->pc+8; /* fall through */ case 2: /* JMP */ npc = iw & ((1<<26)-1); npc <<= 2; return npc | (ur->pc&0xF0000000); case 4: /* BEQ */ if(rs == rt) return ur->pc+4 + offset; return ur->pc + 8; case 5: /* BNE */ if(rs != rt) return ur->pc+4 + offset; return ur->pc + 8; case 6: /* BLEZ */ if((long)rs <= 0) return ur->pc+4 + offset; return ur->pc + 8; case 7: /* BGTZ */ if((long)rs > 0) return ur->pc+4 + offset; return ur->pc + 8; } /* * Floating point unit jumps */ if((iw>>26) == 0x11) /* COP1 */ switch((iw>>16) & 0x3C1){ case 0x101: /* BCT */ case 0x181: /* BCT */ if(fcr31 & (1<<23)) return ur->pc+4 + offset; return ur->pc + 8; case 0x100: /* BCF */ case 0x180: /* BCF */ if(!(fcr31 & (1<<23))) return ur->pc+4 + offset; return ur->pc + 8; } /* shouldn't get here */ return 0; }