/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * this doesn't attempt to implement MIPS floating-point properties * that aren't visible in the Inferno environment. * all arithmetic is done in double precision. * the FP trap status isn't updated. * * we emulate the original MIPS FP register model: 32-bits each, * F(2n) and F(2n+1) are a double, with lower-order word first; * note that this is little-endian order, unlike the rest of the * machine, so double-word operations will need to swap the words * when transferring between FP registers and memory. * * on some machines, we can convert to an FP internal representation when * moving to FPU registers and back (to integer, for example) when moving * from them. the MIPS is different: its conversion instructions operate * on FP registers only, and there's no way to tell if data being moved * into an FP register is integer or FP, so it must be possible to store * integers in FP registers without conversion. Furthermore, pairs of FP * registers can be combined into a double. So we keep the raw bits * around as the canonical representation and convert only to and from * Internal FP format when we must (i.e., before calling the common fpi * code). */#include "u.h"#include "../port/lib.h"#include "mem.h"#include "dat.h"#include "fns.h"#include "ureg.h"#include "../port/fpi.h"#include <tos.h> #ifdef FPEMUDEBUG#define DBG(bits) (fpemudebug & (bits))#define intpr _intpr#define internsane _internsane#define dbgstuck _dbgstuck#else#define DBG(bits) (0)#define internsane(i, ur) do { USED(ur); } while(0)#define intpr(i, reg, fmt, ufp) do {} while(0)#define dbgstuck(pc, ur, ufp) do {} while(0)#endif #define OFR(memb) (uintptr)&((Ureg*)0)->memb /* offset into Ureg of memb */#define REG(ur, r) *acpureg(ur, r) /* cpu reg in Ureg */#define FREG(ufp, fr) (ufp)->reg[(fr) & REGMASK] /* fp reg raw bits */ /* * instruction decoding for COP1 instructions; integer instructions * are laid out differently. */#define OP(ul) ((ul) >> 26)#define REGMASK MASK(5) /* mask for a register number */#define FMT(ul) (((ul) >> 21) & REGMASK) /* data type */#define REGT(ul) (((ul) >> 16) & REGMASK) /* source2 register */#define REGS(ul) (((ul) >> 11) & REGMASK) /* source1 register */#define REGD(ul) (((ul) >> 6) & REGMASK) /* destination register */#define FUNC(ul) ((ul) & MASK(6)) enum { Dbgbasic = 1<<0, /* base debugging: ops, except'ns */ Dbgmoves = 1<<1, /* not very exciting usually */ Dbgregs = 1<<2, /* print register contents around ops */ Dbgdelay = 1<<3, /* branch-delay-slot-related machinery */ /* fpimips status codes */ Failed = -1, Advpc, /* advance pc normally */ Leavepc, /* don't change the pc */ Leavepcret, /* ... and return to user mode now */ Nomatch, /* no-ops */ NOP = 0x27, /* NOR R0, R0, R0 */ MIPSNOP = 0, /* SLL R0, R0, R0 */ /* fp op-codes */ COP1 = 0x11, /* fpu op */ LWC1 = 0x31, /* load float/long */ LDC1 = 0x35, /* load double/vlong */ SWC1 = 0x39, /* store float/long */ SDC1 = 0x3d, /* store double/vlong */ N = 1<<31, /* condition codes */ Z = 1<<30, C = 1<<29, V = 1<<28, /* data types (format field values) */ MFC1 = 0, /* and func == 0 ... */ DMFC1, /* vlong move */ CFC1, /* ctl word move */ MTC1 = 4, DMTC1, CTC1, /* ... end `and func == 0' */ BRANCH = 8, Ffloat = 16, Fdouble, Flong = 20, Fvlong, /* fp control registers */ Fpimp = 0, Fpcsr = 31,}; typedef struct FP1 FP1;typedef struct FP2 FP2;typedef struct FPcvt FPcvt;typedef struct Instr Instr; struct Instr { /* a COP1 instruction, broken out and registers converted */ int iw; /* whole word */ uintptr pc; int o; /* opcode or cop1 func code */ int fmt; /* operand format */ int rm; /* first operand register */ int rn; /* second operand register */ int rd; /* destination register */ Internal *fm; /* converted from FREG(ufp, rm) */ Internal *fn; char *dfmt; FPsave *ufp; /* fp state, including fp registers */ Ureg *ur; /* user registers */}; struct FP2 { char* name; void (*f)(Internal*, Internal*, Internal*);}; struct FP1 { char* name; void (*f)(Internal*, Internal*);}; struct FPcvt { char* name; void (*f)(int, int, int, Ureg *, FPsave *);}; static int roff[32] = { 0, OFR(r1), OFR(r2), OFR(r3), OFR(r4), OFR(r5), OFR(r6), OFR(r7), OFR(r8), OFR(r9), OFR(r10), OFR(r11), OFR(r12), OFR(r13), OFR(r14), OFR(r15), OFR(r16), OFR(r17), OFR(r18), OFR(r19), OFR(r20), OFR(r21), OFR(r22), OFR(r23), OFR(r24), OFR(r25), OFR(r26), OFR(r27), OFR(r28), OFR(sp), OFR(r30), OFR(r31),}; /* * plan 9 assumes F24 initialized to 0.0, F26 to 0.5, F28 to 1.0, F30 to 2.0. */enum { FZERO = 24, FHALF = 26,};static Internal fpconst[Nfpregs] = { /* indexed by register no. */ /* s, e, l, h */[FZERO] {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */[FHALF] {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */[28] {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */[30] {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */}; static char *fmtnames[] = {[MFC1] "MF",[DMFC1] "DMF",[CFC1] "CF",[MTC1] "MT",[DMTC1] "DMT",[CTC1] "CT",[BRANCH]"BR", [Ffloat]"F",[Fdouble]"D",[Flong] "W",[Fvlong]"L",}; static char *prednames[] = {[0] "F",[1] "UN",[2] "EQ",[3] "UEQ",[4] "OLT",[5] "ULT",[6] "OLE",[7] "ULE",[8] "SF",[9] "NGLE",[10] "SEQ",[11] "NGL",[12] "LT",[13] "NGE",[14] "LE",[15] "NGT",}; int fpemudebug = 0; /* settable via /dev/archctl */ static ulong dummyr0;static QLock watchlock; /* lock for watch-points */ ulong branch(Ureg*, ulong);int isbranch(ulong *); static int fpimips(ulong, ulong, Ureg *, FPsave *); char *fpemuprint(char *p, char *ep){#ifdef FPEMUDEBUG return seprint(p, ep, "fpemudebug %d\n", fpemudebug);#else USED(ep); return p;#endif} static ulong *acpureg(Ureg *ur, int r){ r &= REGMASK; if (r == 0 || roff[r] == 0) { dummyr0 = 0; return &dummyr0; } return (ulong *)((char*)ur + roff[r]);} ulong *reg(Ureg *ur, int r) /* for faultmips */{ return ®(ur, r);} static void_internsane(Internal *i, Ureg *ur){ static char buf[ERRMAX]; USED(i); if (!(DBG(Dbgbasic))) return; if ((unsigned)i->s > 1) { snprint(buf, sizeof buf, "fpuemu: bogus Internal sign at pc=%#p", ur->pc); error(buf); } if ((unsigned)i->e > DoubleExpMax) { snprint(buf, sizeof buf, "fpuemu: bogus Internal exponent at pc=%#p", ur->pc); error(buf); }} /* * mips binary operations (d = n operator m) */ static voidfadd(Internal *m, Internal *n, Internal *d){ (m->s == n->s? fpiadd: fpisub)(m, n, d);} static voidfsub(Internal *m, Internal *n, Internal *d){ m->s ^= 1; (m->s == n->s? fpiadd: fpisub)(m, n, d);} /* * mips unary operations */ static voidfrnd(Internal *m, Internal *d){ short e; Internal tmp; tmp = fpconst[FHALF]; (m->s? fsub: fadd)(&tmp, m, d); if(IsWeird(d)) return; fpiround(d); e = (d->e - ExpBias) + 1; if(e <= 0) SetZero(d); else if(e > FractBits){ if(e < 2*FractBits) d->l &= ~((1<<(2*FractBits - e))-1); }else{ d->l = 0; if(e < FractBits) d->h &= ~((1<<(FractBits-e))-1); }} /* debugging: print internal representation of an fp reg */static void_intpr(Internal *i, int reg, int fmt, FPsave *ufp){ USED(i); if (!(DBG(Dbgregs))) return; if (fmt == Fdouble && reg < 31) iprint("\tD%02d: l %08lux h %08lux =\ts %d e %04d h %08lux l %08lux\n", reg, FREG(ufp, reg), FREG(ufp, reg+1), i->s, i->e, i->h, i->l); else iprint("\tF%02d: %08lux =\ts %d e %04d h %08lux l %08lux\n", reg, FREG(ufp, reg), i->s, i->e, i->h, i->l); delay(75);} static voiddreg2dbl(Double *dp, int reg, FPsave *ufp){ reg &= ~1; dp->l = FREG(ufp, reg); dp->h = FREG(ufp, reg+1);} static voiddbl2dreg(int reg, Double *dp, FPsave *ufp){ reg &= ~1; FREG(ufp, reg) = dp->l; FREG(ufp, reg+1) = dp->h;} static voidvreg2dbl(Double *dp, int reg, FPsave *ufp){ reg &= ~1; dp->l = FREG(ufp, reg+1); dp->h = FREG(ufp, reg);} static voiddbl2vreg(int reg, Double *dp, FPsave *ufp){ reg &= ~1; FREG(ufp, reg+1) = dp->l; FREG(ufp, reg) = dp->h;} /* convert fmt (rm) to double (rd) */static voidfcvtd(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){ Double d; Internal intrn; switch (fmt) { case Ffloat: fpis2i(&intrn, &FREG(ufp, rm)); internsane(&intrn, ur); fpii2d(&d, &intrn); break; case Fdouble: dreg2dbl(&d, rm, ufp); break; case Flong: fpiw2i(&intrn, &FREG(ufp, rm)); internsane(&intrn, ur); fpii2d(&d, &intrn); break; case Fvlong: vreg2dbl(&d, rm, ufp); fpiv2i(&intrn, &d); internsane(&intrn, ur); fpii2d(&d, &intrn); break; } dbl2dreg(rd, &d, ufp); if (fmt != Fdouble && DBG(Dbgregs)) intpr(&intrn, rm, Fdouble, ufp);} /* convert fmt (rm) to single (rd) */static voidfcvts(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){ Double d; Internal intrn; switch (fmt) { case Ffloat: FREG(ufp, rd) = FREG(ufp, rm); break; case Fdouble: dreg2dbl(&d, rm, ufp); fpid2i(&intrn, &d); break; case Flong: fpiw2i(&intrn, &FREG(ufp, rm)); break; case Fvlong: vreg2dbl(&d, rm, ufp); fpiv2i(&intrn, &d); break; } if (fmt != Ffloat) { if(DBG(Dbgregs)) intpr(&intrn, rm, Ffloat, ufp); internsane(&intrn, ur); fpii2s(&FREG(ufp, rd), &intrn); }} /* convert fmt (rm) to long (rd) */static voidfcvtw(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){ Double d; Internal intrn; switch (fmt) { case Ffloat: fpis2i(&intrn, &FREG(ufp, rm)); break; case Fdouble: dreg2dbl(&d, rm, ufp); fpid2i(&intrn, &d); break; case Flong: FREG(ufp, rd) = FREG(ufp, rm); break; case Fvlong: vreg2dbl(&d, rm, ufp); fpiv2i(&intrn, &d); break; } if (fmt != Flong) { if(DBG(Dbgregs)) intpr(&intrn, rm, Flong, ufp); internsane(&intrn, ur); fpii2w((long *)&FREG(ufp, rd), &intrn); }} /* convert fmt (rm) to vlong (rd) */static voidfcvtv(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp){ Double d; Internal intrn; switch (fmt) { case Ffloat: fpis2i(&intrn, &FREG(ufp, rm)); break; case Fdouble: dreg2dbl(&d, rm, ufp); fpid2i(&intrn, &d); break; case Flong: fpiw2i(&intrn, &FREG(ufp, rm)); break; case Fvlong: vreg2dbl(&d, rm, ufp); dbl2vreg(rd, &d, ufp); break; } if (fmt != Fvlong) { if(DBG(Dbgregs)) intpr(&intrn, rm, Fvlong, ufp); internsane(&intrn, ur); fpii2v((vlong *)&FREG(ufp, rd), &intrn); }} /* * MIPS function codes */ static FP2 optab2[] = { /* Fd := Fn OP Fm (binary) */[0] {"ADDF", fadd}, /* can ignore fmt, just use doubles */[1] {"SUBF", fsub},[2] {"MULF", fpimul},[3] {"DIVF", fpidiv},Lines 1–500 of 1494. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * this doesn't attempt to implement MIPS floating-point properties * that aren't visible in the Inferno environment. * all arithmetic is done in double precision. * the FP trap status isn't updated. * * we emulate the original MIPS FP register model: 32-bits each, * F(2n) and F(2n+1) are a double, with lower-order word first; * note that this is little-endian order, unlike the rest of the * machine, so double-word operations will need to swap the words * when transferring between FP registers and memory. * * on some machines, we can convert to an FP internal representation when * moving to FPU registers and back (to integer, for example) when moving * from them. the MIPS is different: its conversion instructions operate * on FP registers only, and there's no way to tell if data being moved * into an FP register is integer or FP, so it must be possible to store * integers in FP registers without conversion. Furthermore, pairs of FP * registers can be combined into a double. So we keep the raw bits * around as the canonical representation and convert only to and from * Internal FP format when we must (i.e., before calling the common fpi * code). */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "ureg.h" #include "../port/fpi.h" #include <tos.h> #ifdef FPEMUDEBUG #define DBG(bits) (fpemudebug & (bits)) #define intpr _intpr #define internsane _internsane #define dbgstuck _dbgstuck #else #define DBG(bits) (0) #define internsane(i, ur) do { USED(ur); } while(0) #define intpr(i, reg, fmt, ufp) do {} while(0) #define dbgstuck(pc, ur, ufp) do {} while(0) #endif #define OFR(memb) (uintptr)&((Ureg*)0)->memb /* offset into Ureg of memb */ #define REG(ur, r) *acpureg(ur, r) /* cpu reg in Ureg */ #define FREG(ufp, fr) (ufp)->reg[(fr) & REGMASK] /* fp reg raw bits */ /* * instruction decoding for COP1 instructions; integer instructions * are laid out differently. */ #define OP(ul) ((ul) >> 26) #define REGMASK MASK(5) /* mask for a register number */ #define FMT(ul) (((ul) >> 21) & REGMASK) /* data type */ #define REGT(ul) (((ul) >> 16) & REGMASK) /* source2 register */ #define REGS(ul) (((ul) >> 11) & REGMASK) /* source1 register */ #define REGD(ul) (((ul) >> 6) & REGMASK) /* destination register */ #define FUNC(ul) ((ul) & MASK(6)) enum { Dbgbasic = 1<<0, /* base debugging: ops, except'ns */ Dbgmoves = 1<<1, /* not very exciting usually */ Dbgregs = 1<<2, /* print register contents around ops */ Dbgdelay = 1<<3, /* branch-delay-slot-related machinery */ /* fpimips status codes */ Failed = -1, Advpc, /* advance pc normally */ Leavepc, /* don't change the pc */ Leavepcret, /* ... and return to user mode now */ Nomatch, /* no-ops */ NOP = 0x27, /* NOR R0, R0, R0 */ MIPSNOP = 0, /* SLL R0, R0, R0 */ /* fp op-codes */ COP1 = 0x11, /* fpu op */ LWC1 = 0x31, /* load float/long */ LDC1 = 0x35, /* load double/vlong */ SWC1 = 0x39, /* store float/long */ SDC1 = 0x3d, /* store double/vlong */ N = 1<<31, /* condition codes */ Z = 1<<30, C = 1<<29, V = 1<<28, /* data types (format field values) */ MFC1 = 0, /* and func == 0 ... */ DMFC1, /* vlong move */ CFC1, /* ctl word move */ MTC1 = 4, DMTC1, CTC1, /* ... end `and func == 0' */ BRANCH = 8, Ffloat = 16, Fdouble, Flong = 20, Fvlong, /* fp control registers */ Fpimp = 0, Fpcsr = 31, }; typedef struct FP1 FP1; typedef struct FP2 FP2; typedef struct FPcvt FPcvt; typedef struct Instr Instr; struct Instr { /* a COP1 instruction, broken out and registers converted */ int iw; /* whole word */ uintptr pc; int o; /* opcode or cop1 func code */ int fmt; /* operand format */ int rm; /* first operand register */ int rn; /* second operand register */ int rd; /* destination register */ Internal *fm; /* converted from FREG(ufp, rm) */ Internal *fn; char *dfmt; FPsave *ufp; /* fp state, including fp registers */ Ureg *ur; /* user registers */ }; struct FP2 { char* name; void (*f)(Internal*, Internal*, Internal*); }; struct FP1 { char* name; void (*f)(Internal*, Internal*); }; struct FPcvt { char* name; void (*f)(int, int, int, Ureg *, FPsave *); }; static int roff[32] = { 0, OFR(r1), OFR(r2), OFR(r3), OFR(r4), OFR(r5), OFR(r6), OFR(r7), OFR(r8), OFR(r9), OFR(r10), OFR(r11), OFR(r12), OFR(r13), OFR(r14), OFR(r15), OFR(r16), OFR(r17), OFR(r18), OFR(r19), OFR(r20), OFR(r21), OFR(r22), OFR(r23), OFR(r24), OFR(r25), OFR(r26), OFR(r27), OFR(r28), OFR(sp), OFR(r30), OFR(r31), }; /* * plan 9 assumes F24 initialized to 0.0, F26 to 0.5, F28 to 1.0, F30 to 2.0. */ enum { FZERO = 24, FHALF = 26, }; static Internal fpconst[Nfpregs] = { /* indexed by register no. */ /* s, e, l, h */ [FZERO] {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */ [FHALF] {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */ [28] {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */ [30] {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */ }; static char *fmtnames[] = { [MFC1] "MF", [DMFC1] "DMF", [CFC1] "CF", [MTC1] "MT", [DMTC1] "DMT", [CTC1] "CT", [BRANCH]"BR", [Ffloat]"F", [Fdouble]"D", [Flong] "W", [Fvlong]"L", }; static char *prednames[] = { [0] "F", [1] "UN", [2] "EQ", [3] "UEQ", [4] "OLT", [5] "ULT", [6] "OLE", [7] "ULE", [8] "SF", [9] "NGLE", [10] "SEQ", [11] "NGL", [12] "LT", [13] "NGE", [14] "LE", [15] "NGT", }; int fpemudebug = 0; /* settable via /dev/archctl */ static ulong dummyr0; static QLock watchlock; /* lock for watch-points */ ulong branch(Ureg*, ulong); int isbranch(ulong *); static int fpimips(ulong, ulong, Ureg *, FPsave *); char * fpemuprint(char *p, char *ep) { #ifdef FPEMUDEBUG return seprint(p, ep, "fpemudebug %d\n", fpemudebug); #else USED(ep); return p; #endif } static ulong * acpureg(Ureg *ur, int r) { r &= REGMASK; if (r == 0 || roff[r] == 0) { dummyr0 = 0; return &dummyr0; } return (ulong *)((char*)ur + roff[r]); } ulong * reg(Ureg *ur, int r) /* for faultmips */ { return ®(ur, r); } static void _internsane(Internal *i, Ureg *ur) { static char buf[ERRMAX]; USED(i); if (!(DBG(Dbgbasic))) return; if ((unsigned)i->s > 1) { snprint(buf, sizeof buf, "fpuemu: bogus Internal sign at pc=%#p", ur->pc); error(buf); } if ((unsigned)i->e > DoubleExpMax) { snprint(buf, sizeof buf, "fpuemu: bogus Internal exponent at pc=%#p", ur->pc); error(buf); } } /* * mips binary operations (d = n operator m) */ static void fadd(Internal *m, Internal *n, Internal *d) { (m->s == n->s? fpiadd: fpisub)(m, n, d); } static void fsub(Internal *m, Internal *n, Internal *d) { m->s ^= 1; (m->s == n->s? fpiadd: fpisub)(m, n, d); } /* * mips unary operations */ static void frnd(Internal *m, Internal *d) { short e; Internal tmp; tmp = fpconst[FHALF]; (m->s? fsub: fadd)(&tmp, m, d); if(IsWeird(d)) return; fpiround(d); e = (d->e - ExpBias) + 1; if(e <= 0) SetZero(d); else if(e > FractBits){ if(e < 2*FractBits) d->l &= ~((1<<(2*FractBits - e))-1); }else{ d->l = 0; if(e < FractBits) d->h &= ~((1<<(FractBits-e))-1); } } /* debugging: print internal representation of an fp reg */ static void _intpr(Internal *i, int reg, int fmt, FPsave *ufp) { USED(i); if (!(DBG(Dbgregs))) return; if (fmt == Fdouble && reg < 31) iprint("\tD%02d: l %08lux h %08lux =\ts %d e %04d h %08lux l %08lux\n", reg, FREG(ufp, reg), FREG(ufp, reg+1), i->s, i->e, i->h, i->l); else iprint("\tF%02d: %08lux =\ts %d e %04d h %08lux l %08lux\n", reg, FREG(ufp, reg), i->s, i->e, i->h, i->l); delay(75); } static void dreg2dbl(Double *dp, int reg, FPsave *ufp) { reg &= ~1; dp->l = FREG(ufp, reg); dp->h = FREG(ufp, reg+1); } static void dbl2dreg(int reg, Double *dp, FPsave *ufp) { reg &= ~1; FREG(ufp, reg) = dp->l; FREG(ufp, reg+1) = dp->h; } static void vreg2dbl(Double *dp, int reg, FPsave *ufp) { reg &= ~1; dp->l = FREG(ufp, reg+1); dp->h = FREG(ufp, reg); } static void dbl2vreg(int reg, Double *dp, FPsave *ufp) { reg &= ~1; FREG(ufp, reg+1) = dp->l; FREG(ufp, reg) = dp->h; } /* convert fmt (rm) to double (rd) */ static void fcvtd(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) { Double d; Internal intrn; switch (fmt) { case Ffloat: fpis2i(&intrn, &FREG(ufp, rm)); internsane(&intrn, ur); fpii2d(&d, &intrn); break; case Fdouble: dreg2dbl(&d, rm, ufp); break; case Flong: fpiw2i(&intrn, &FREG(ufp, rm)); internsane(&intrn, ur); fpii2d(&d, &intrn); break; case Fvlong: vreg2dbl(&d, rm, ufp); fpiv2i(&intrn, &d); internsane(&intrn, ur); fpii2d(&d, &intrn); break; } dbl2dreg(rd, &d, ufp); if (fmt != Fdouble && DBG(Dbgregs)) intpr(&intrn, rm, Fdouble, ufp); } /* convert fmt (rm) to single (rd) */ static void fcvts(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) { Double d; Internal intrn; switch (fmt) { case Ffloat: FREG(ufp, rd) = FREG(ufp, rm); break; case Fdouble: dreg2dbl(&d, rm, ufp); fpid2i(&intrn, &d); break; case Flong: fpiw2i(&intrn, &FREG(ufp, rm)); break; case Fvlong: vreg2dbl(&d, rm, ufp); fpiv2i(&intrn, &d); break; } if (fmt != Ffloat) { if(DBG(Dbgregs)) intpr(&intrn, rm, Ffloat, ufp); internsane(&intrn, ur); fpii2s(&FREG(ufp, rd), &intrn); } } /* convert fmt (rm) to long (rd) */ static void fcvtw(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) { Double d; Internal intrn; switch (fmt) { case Ffloat: fpis2i(&intrn, &FREG(ufp, rm)); break; case Fdouble: dreg2dbl(&d, rm, ufp); fpid2i(&intrn, &d); break; case Flong: FREG(ufp, rd) = FREG(ufp, rm); break; case Fvlong: vreg2dbl(&d, rm, ufp); fpiv2i(&intrn, &d); break; } if (fmt != Flong) { if(DBG(Dbgregs)) intpr(&intrn, rm, Flong, ufp); internsane(&intrn, ur); fpii2w((long *)&FREG(ufp, rd), &intrn); } } /* convert fmt (rm) to vlong (rd) */ static void fcvtv(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp) { Double d; Internal intrn; switch (fmt) { case Ffloat: fpis2i(&intrn, &FREG(ufp, rm)); break; case Fdouble: dreg2dbl(&d, rm, ufp); fpid2i(&intrn, &d); break; case Flong: fpiw2i(&intrn, &FREG(ufp, rm)); break; case Fvlong: vreg2dbl(&d, rm, ufp); dbl2vreg(rd, &d, ufp); break; } if (fmt != Fvlong) { if(DBG(Dbgregs)) intpr(&intrn, rm, Fvlong, ufp); internsane(&intrn, ur); fpii2v((vlong *)&FREG(ufp, rd), &intrn); } } /* * MIPS function codes */ static FP2 optab2[] = { /* Fd := Fn OP Fm (binary) */ [0] {"ADDF", fadd}, /* can ignore fmt, just use doubles */ [1] {"SUBF", fsub}, [2] {"MULF", fpimul}, [3] {"DIVF", fpidiv},