ref: e047274e8dedebb9588a82e48e6cd9d7e50cae79
dir: /os/cerf405/fpipower.c/
/* * this doesn't attempt to implement Power architecture 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. */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "ureg.h" #include "fpi.h" #define REG(x) (*(long*)(((char*)em->ur)+roff[(x)])) #define FR(x) (*(Internal*)em->fr[(x)&0x1F]) #define REGSP 1 /* stack pointer */ /* BUG: check fetch (not worthwhile in Inferno) */ #define getulong(a) (*(ulong*)(a)) enum { CRLT = 1<<31, CRGT = 1<<30, CREQ = 1<<29, CRSO = 1<<28, CRFU = CRSO, CRFX = 1<<27, CRFEX = 1<<26, CRVX = 1<<25, CROX = 1<<24, }; #define getCR(x,w) (((w)>>(28-(x*4)))&0xF) #define mkCR(x,v) (((v)&0xF)<<(28-(x*4))) #define simm(xx, ii) xx = (short)(ii&0xFFFF); #define getairr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i) #define getarrr(i) rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f; #define getop(i) ((i>>26)&0x3F) #define getxo(i) ((i>>1)&0x3FF) #define FPS_FX (1<<31) /* exception summary (sticky) */ #define FPS_EX (1<<30) /* enabled exception summary */ #define FPS_VX (1<<29) /* invalid operation exception summary */ #define FPS_OX (1<<28) /* overflow exception OX (sticky) */ #define FPS_UX (1<<27) /* underflow exception UX (sticky) */ #define FPS_ZX (1<<26) /* zero divide exception ZX (sticky) */ #define FPS_XX (1<<25) /* inexact exception XX (sticky) */ #define FPS_VXSNAN (1<<24) /* invalid operation exception for SNaN (sticky) */ #define FPS_VXISI (1<<23) /* invalid operation exception for ∞-∞ (sticky) */ #define FPS_VXIDI (1<<22) /* invalid operation exception for ∞/∞ (sticky) */ #define FPS_VXZDZ (1<<21) /* invalid operation exception for 0/0 (sticky) */ #define FPS_VXIMZ (1<<20) /* invalid operation exception for ∞*0 (sticky) */ #define FPS_VXVC (1<<19) /* invalid operation exception for invalid compare (sticky) */ #define FPS_FR (1<<18) /* fraction rounded */ #define FPS_FI (1<<17) /* fraction inexact */ #define FPS_FPRF (1<<16) /* floating point result class */ #define FPS_FPCC (0xF<<12) /* <, >, =, unordered */ #define FPS_VXCVI (1<<8) /* enable exception for invalid integer convert (sticky) */ #define FPS_VE (1<<7) /* invalid operation exception enable */ #define FPS_OE (1<<6) /* enable overflow exceptions */ #define FPS_UE (1<<5) /* enable underflow */ #define FPS_ZE (1<<4) /* enable zero divide */ #define FPS_XE (1<<3) /* enable inexact exceptions */ #define FPS_RN (3<<0) /* rounding mode */ typedef struct Emreg Emreg; struct Emreg { Ureg* ur; ulong (*fr)[3]; FPenv* ufp; ulong ir; char* name; }; int fpemudebug = 0; #undef OFR #define OFR(X) ((ulong)&((Ureg*)0)->X) static int roff[] = { OFR(r0), 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(r29), OFR(r30), OFR(r31), }; /* * initial FP register values assumed by qc's code */ static Internal fpreginit[] = { /* s, e, l, h */ {0, 0x400, 0x00000000, 0x08000000}, /* F31=2.0 */ {0, 0x3FF, 0x00000000, 0x08000000}, /* F30=1.0 */ {0, 0x3FE, 0x00000000, 0x08000000}, /* F29=0.5 */ {0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */ {0, 0x433, 0x00000000, 0x08000040}, /* F27=FREGCVI */ }; static void fadd(Emreg *em, Internal *d, int ra, int rb) { Internal a, b; a = FR(ra); b = FR(rb); (a.s == b.s? fpiadd: fpisub)(&b, &a, d); } static void fsub(Emreg *em, Internal *d, int ra, int rb) { Internal a, b; a = FR(ra); b = FR(rb); b.s ^= 1; (b.s == a.s? fpiadd: fpisub)(&b, &a, d); } static void fmul(Emreg *em, Internal *d, int ra, int rb) { Internal a, b; a = FR(ra); b = FR(rb); fpimul(&b, &a, d); } static void fdiv(Emreg *em, Internal *d, int ra, int rb) { Internal a, b; a = FR(ra); b = FR(rb); fpidiv(&b, &a, d); } static void fmsub(Emreg *em, Internal *d, int ra, int rc, int rb) { Internal a, c, b, t; a = FR(ra); c = FR(rc); b = FR(rb); fpimul(&a, &c, &t); b.s ^= 1; (b.s == t.s? fpiadd: fpisub)(&b, &t, d); } static void fmadd(Emreg *em, Internal *d, int ra, int rc, int rb) { Internal a, c, b, t; a = FR(ra); c = FR(rc); b = FR(rb); fpimul(&a, &c, &t); (t.s == b.s? fpiadd: fpisub)(&b, &t, d); } static ulong setfpscr(Emreg*); static void setfpcc(Emreg*, int); static void unimp(Emreg *em, ulong op) { char buf[60]; snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op); if(fpemudebug) print("FPE: %s\n", buf); error(buf); /* no return */ } /* * floating load/store */ static void fpeairr(Emreg *em, ulong ir, void **eap, int *rdp) { ulong ea; long imm; int ra, rd, upd; getairr(ir); ea = imm; upd = (ir&(1L<<26))!=0; if(ra) { ea += REG(ra); if(upd){ if(ra == REGSP) panic("fpemu: r1 update"); /* can't do it because we're running on the same stack */ REG(ra) = ea; } } else { if(upd) unimp(em, ir); } *rdp = rd; *eap = (void*)ea; if(fpemudebug) print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd); } static void fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp) { ulong ea; int ra, rb, rd; getarrr(ir); ea = REG(rb); if(ra){ ea += REG(ra); if(upd){ if(ra == REGSP) panic("fpemu: r1 update"); REG(ra) = ea; } if(fpemudebug) print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd); } else { if(upd) unimp(em, ir); if(fpemudebug) print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea); } *eap = (void*)ea; *rdp = rd; } static void lfs(Emreg *em, ulong ir) { void *ea; int rd; em->name = "lfs"; fpeairr(em, ir, &ea, &rd); fpis2i(&FR(rd), (void*)ea); } static void lfsx(Emreg *em, ulong ir) { void *ea; int rd; em->name = "lfsx"; fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd); fpis2i(&FR(rd), (void*)ea); } static void lfd(Emreg *em, ulong ir) { void *ea; int rd; em->name = "lfd"; fpeairr(em, ir, &ea, &rd); fpid2i(&FR(rd), (void*)ea); } static void lfdx(Emreg *em, ulong ir) { void *ea; int rd; em->name = "lfdx"; fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd); fpid2i(&FR(rd), (void*)ea); } static void stfs(Emreg *em, ulong ir) { void *ea; int rd; Internal tmp; em->name = "stfs"; fpeairr(em, ir, &ea, &rd); tmp = FR(rd); fpii2s(ea, &tmp); } static void stfsx(Emreg *em, ulong ir) { void *ea; int rd; Internal tmp; em->name = "stfsx"; fpearrr(em, ir, getxo(ir)==695, &ea, &rd); tmp = FR(rd); fpii2s(ea, &tmp); } static void stfd(Emreg *em, ulong ir) { void *ea; int rd; Internal tmp; em->name = "stfd"; fpeairr(em, ir, &ea, &rd); tmp = FR(rd); fpii2d(ea, &tmp); } static void stfdx(Emreg *em, ulong ir) { void *ea; int rd; Internal tmp; em->name = "stfdx"; fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd); tmp = FR(rd); fpii2d(ea, &tmp); } static void mcrfs(Emreg *em, ulong ir) { int rd, ra, rb; static ulong fpscr0[] ={ FPS_FX|FPS_OX, FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN, FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ, FPS_VXVC, 0, FPS_VXCVI, }; getarrr(ir); if(rb || ra&3 || rd&3) unimp(em, ir); ra >>= 2; rd >>= 2; em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr)); em->ufp->fpscr &= ~fpscr0[ra]; if(fpemudebug) print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra); } static void mffs(Emreg *em, ulong ir) { int rd, ra, rb; Double dw; getarrr(ir); if(ra || rb) unimp(em, ir); dw.h = 0; dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr; fpid2i(&FR(rd), &dw); /* it's anyone's guess how CR1 should be set when ir&1 */ em->ur->cr &= ~mkCR(1, 0xE); /* leave SO, reset others */ if(fpemudebug) print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); } static void mtfsb1(Emreg *em, ulong ir) { int rd, ra, rb; getarrr(ir); if(ra || rb) unimp(em, ir); em->ufp->fpscr |= (1L << (31-rd)); /* BUG: should set summary bits */ if(ir & 1) em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ if(fpemudebug) print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); } static void mtfsb0(Emreg *em, ulong ir) { int rd, ra, rb; getarrr(ir); if(ra || rb) unimp(em, ir); em->ufp->fpscr &= ~(1L << (31-rd)); if(ir & 1) em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ if(fpemudebug) print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd); } static void mtfsf(Emreg *em, ulong ir) { int fm, rb, i; ulong v; Internal b; Double db; if(ir & ((1L << 25)|(1L << 16))) unimp(em, ir); rb = (ir >> 11) & 0x1F; fm = (ir >> 17) & 0xFF; b = FR(rb); fpii2d(&db, &b); /* reconstruct hi/lo format to recover low word */ v = db.l; for(i=0; i<8; i++) if(fm & (1 << (7-i))) em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v)); /* BUG: should set FEX and VX `according to the usual rule' */ if(ir & 1) em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ if(fpemudebug) print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb); } static void mtfsfi(Emreg *em, ulong ir) { int imm, rd; if(ir & ((0x7F << 16)|(1L << 11))) unimp(em, ir); rd = (ir >> 23) & 0xF; imm = (ir >> 12) & 0xF; em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm); /* BUG: should set FEX and VX `according to the usual rule' */ if(ir & 1) em->ur->cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */ if(fpemudebug) print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm); } static void fcmp(Emreg *em, ulong ir) { int fc, rd, ra, rb, sig, i; getarrr(ir); if(rd & 3) unimp(em, ir); rd >>= 2; sig = 0; switch(getxo(ir)) { default: unimp(em, ir); case 32: if(fpemudebug) print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); sig = 1; break; case 0: if(fpemudebug) print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb); break; } if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) { if(sig){ ; /* BUG: should trap if not masked ... */ } fc = CRFU; } else { i = fpicmp(&FR(ra), &FR(rb)); if(i > 0) fc = CRGT; else if(i == 0) fc = CREQ; else fc = CRLT; } fc >>= 28; em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc); em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11); /* BUG: update FX, VXSNAN, VXVC */ } static void fariths(Emreg *em, ulong ir) { int rd, ra, rb, rc, fmt; char *cc, *n; ulong fpscr; Internal *d; fmt = 0; rc = (ir>>6)&0x1F; getarrr(ir); d = &FR(rd); switch(getxo(ir)&0x1F) { /* partial XO decode */ case 22: /* fsqrts */ case 24: /* fres */ default: unimp(em, ir); return; case 18: if(IsZero(&FR(rb))) { em->ufp->fpscr |= FPS_ZX | FPS_FX; error("sys: fp: zero divide"); } fdiv(em, d, ra, rb); n = "fdivs"; break; case 20: fsub(em, d, ra, rb); n = "fsubs"; break; case 21: fadd(em, d, ra, rb); n = "fadds"; break; case 25: fmul(em, d, ra, rc); rb = rc; n = "fmuls"; break; case 28: fmsub(em, d, ra, rc, rb); fmt = 2; n = "fmsubs"; break; case 29: fmadd(em, d, ra, rc, rb); fmt = 2; n = "fmadds"; break; case 30: fmsub(em, d, ra, rc, rb); d->s ^= 1; fmt = 2; n = "fnmsubs"; break; case 31: fmadd(em, d, ra, rc, rb); d->s ^= 1; fmt = 2; n = "fnmadds"; break; } if(fmt==1 && ra) unimp(em, ir); fpscr = setfpscr(em); setfpcc(em, rd); cc = ""; if(ir & 1) { cc = "."; em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); } if(fpemudebug) { switch(fmt) { case 0: print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); break; case 1: print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); break; case 2: print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); break; } } } static void farith(Emreg *em, ulong ir) { Word w; Double dv; int rd, ra, rb, rc, fmt; char *cc, *n; ulong fpscr; int nocc; Internal *d; fmt = 0; nocc = 0; rc = (ir>>6)&0x1F; getarrr(ir); d = &FR(rd); switch(getxo(ir)&0x1F) { /* partial XO decode */ case 22: /* frsqrt */ case 23: /* fsel */ case 26: /* fsqrte */ default: unimp(em, ir); return; case 12: /* frsp */ *d = FR(rb); /* BUG: doesn't round to single precision */ fmt = 1; n = "frsp"; break; case 14: /* fctiw */ /* BUG: ignores rounding mode */ case 15: /* fctiwz */ fpii2w(&w, &FR(rb)); dv.h = 0; dv.l = w; fpid2i(d, &dv); fmt = 1; nocc = 1; n = "fctiw"; break; case 18: if(IsZero(&FR(rb))) { em->ufp->fpscr |= FPS_ZX | FPS_FX; error("sys: fp: zero divide"); } fdiv(em, d, ra, rb); n = "fdiv"; break; case 20: fsub(em, d, ra, rb); n = "fsub"; break; case 21: fadd(em, d, ra, rb); n = "fadd"; break; case 25: fmul(em, d, ra, rc); rb = rc; n = "fmul"; break; case 28: fmsub(em, d, ra, rc, rb); fmt = 2; n = "fmsub"; break; case 29: fmadd(em, d, ra, rc, rb); fmt = 2; n = "fmadd"; break; case 30: fmsub(em, d, ra, rc, rb); d->s ^= 1; fmt = 2; n = "fnmsub"; break; case 31: fmadd(em, d, ra, rc, rb); d->s ^= 1; fmt = 2; n = "fnmadd"; break; } if(fmt==1 && ra) unimp(em, ir); fpscr = setfpscr(em); if(nocc == 0) setfpcc(em, rd); cc = ""; if(ir & 1) { cc = "."; em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); } if(fpemudebug) { switch(fmt) { case 0: print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb); break; case 1: print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); break; case 2: print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb); break; } } } static void farith2(Emreg *em, ulong ir) { int rd, ra, rb; char *cc, *n; ulong fpscr; Internal *d, *b; getarrr(ir); if(ra) unimp(em, ir); d = &FR(rd); b = &FR(rb); switch(getxo(ir)) { /* full XO decode */ default: unimp(em, ir); case 40: *d = *b; d->s ^= 1; n = "fneg"; break; case 72: *d = *b; n = "fmr"; break; case 136: *d = *b; d->s = 1; n = "fnabs"; break; case 264: *d = *b; d->s = 0; n = "fabs"; break; } fpscr = setfpscr(em); setfpcc(em, rd); cc = ""; if(ir & 1) { cc = "."; em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28)); } if(fpemudebug) print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb); } static ulong setfpscr(Emreg *em) { ulong fps, fpscr; fps = 0; /* BUG: getfsr() */ fpscr = em->ufp->fpscr; if(fps & FPAOVFL) fpscr |= FPS_OX; if(fps & FPAINEX) fpscr |= FPS_XX; if(fps & FPAUNFL) fpscr |= FPS_UX; if(fps & FPAZDIV) fpscr |= FPS_ZX; if(fpscr != em->ufp->fpscr) { fpscr |= FPS_FX; em->ufp->fpscr = fpscr; } return fpscr; } static void setfpcc(Emreg *em, int r) { int c; Internal *d; d = &FR(r); c = 0; if(IsZero(d)) c |= 2; else if(d->s == 1) c |= 4; else c |= 8; if(IsNaN(d)) c |= 1; em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */ } static uchar op63flag[32] = { [12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1, [23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1, }; /* * returns the number of FP instructions emulated */ int fpipower(Ureg *ur) { ulong op; int xo; Emreg emreg, *em; FPenv *ufp; int n; ufp = &up->env->fpu; /* because all the state is in Osenv, it need not be saved/restored */ em = &emreg; em->ur = ur; em->fr = ufp->emreg; em->ufp = ufp; em->name = nil; if(em->ufp->fpistate != FPACTIVE) { em->ufp->fpistate = FPACTIVE; em->ufp->fpscr = 0; /* TO DO */ for(n = 0; n < nelem(fpreginit); n++) FR(31-n) = fpreginit[n]; } for(n=0;;n++){ op = getulong(ur->pc); em->ir = op; if(fpemudebug > 1) print("%8.8lux %8.8lux: ", ur->pc, op); switch(op>>26){ default: return n; case 48: /* lfs */ case 49: /* lfsu */ lfs(em, op); break; case 50: /* lfd */ case 51: /* lfdu */ lfd(em, op); break; case 52: /* stfs */ case 53: /* stfsu */ stfs(em, op); break; case 54: /* stfd */ case 55: /* stfdu */ stfd(em, op); break; case 31: /* indexed load/store */ xo = getxo(op); if((xo & 0x300) != 0x200) return n; switch(xo){ default: return n; case 535: /* lfsx */ case 567: /* lfsux */ lfsx(em, op); break; case 599: /* lfdx */ case 631: /* lfdux */ lfdx(em, op); break; case 663: /* stfsx */ case 695: /* stfsux */ stfsx(em, op); break; case 727: /* stfdx */ case 759: /* stfdux */ stfdx(em, op); break; } break; case 63: /* double precision */ xo = getxo(op); if(op63flag[xo & 0x1F]){ farith(em, op); break; } switch(xo){ default: return n; case 0: /* fcmpu */ case 32: /* fcmpo */ fcmp(em, op); break; case 40: /* fneg */ case 72: /* fmr */ case 136: /* fnabs */ case 264: /* fabs */ farith2(em, op); break; case 38: mtfsb1(em, op); break; case 64: mcrfs(em, op); break; case 70: mtfsb0(em, op); break; case 134: mtfsfi(em, op); break; case 583: mffs(em, op); break; case 711: mtfsf(em, op); break; } break; case 59: /* single precision */ fariths(em, op); break; } ur->pc += 4; if(anyhigher()) sched(); } return n; } /* 50: lfd frD,d(rA) 51: lfdu frD,d(rA) 31,631: lfdux frD,rA,rB 31,599: lfdx frD,rA,rB 48: lfs frD,d(rA) 49: lfsu frD,d(rA) 31,567: lfsux frD,rA,rB 31,535: lfsx frD,rA,rB 54: stfd frS,d(rA) 55: stfdu frS,d(rA) 31,759: stfdux frS,rA,rB 31,727: stfdx frS,rA,rB 52: stfs frS,d(rA) 53: stfsu frS,d(rA) 31,695: stfsux frS,rA,rB 31,663: stfsx frS,rA,rB 63,64: mcrfs crfD,crfS 63,583: mffs[.] frD 63,70: mtfsb0[.] crbD 63,38: mtfsb1[.] crbD 63,711: mtfsf[.] FM,frB 63,134: mtfsfi[.] crfD,IMM */ /* float to int: FMOVD g+0(SB),F1 FCTIWZ F1,F4 FMOVD F4,.rathole+0(SB) MOVW .rathole+4(SB),R7 MOVW R7,l+0(SB) */ /* int to float: MOVW $1127219200,R9 MOVW l+0(SB),R7 MOVW R9,.rathole+0(SB) XOR $-2147483648,R7,R6 MOVW R6,.rathole+4(SB) FMOVD .rathole+0(SB),F0 FSUB F27,F0 unsigned to float: MOVW ul+0(SB),R5 MOVW R9,.rathole+0(SB) XOR $-2147483648,R5,R4 MOVW R4,.rathole+4(SB) FMOVD .rathole+0(SB),F3 FSUB F27,F3 FCMPU F3,F28 BGE ,3(PC) FMOVD $4.29496729600000000e+09,F2 FADD F2,F3 FMOVD F3,g+0(SB) */