plan9fox/sys/src/cmd/qi/float.c
2011-03-30 19:35:09 +03:00

742 lines
13 KiB
C

#include <u.h>
#include <libc.h>
#include <bio.h>
#include <mach.h>
#define Extern extern
#include "power.h"
ulong setfpscr(void);
void setfpcc(double);
void farith(ulong);
void farith2(ulong);
void fariths(ulong);
void fcmp(ulong);
void mtfsb1(ulong);
void mcrfs(ulong);
void mtfsb0(ulong);
void mtfsf(ulong);
void mtfsfi(ulong);
void mffs(ulong);
void mtfsf(ulong);
Inst op59[] = {
[18] {fariths, "fdivs", Ifloat},
[20] {fariths, "fsubs", Ifloat},
[21] {fariths, "fadds", Ifloat},
[22] {unimp, "fsqrts", Ifloat},
[24] {unimp, "fres", Ifloat},
[25] {fariths, "fmuls", Ifloat},
[28] {fariths, "fmsubs", Ifloat},
[29] {fariths, "fmadds", Ifloat},
[30] {fariths, "fnmsubs", Ifloat},
[31] {fariths, "fnmadds", Ifloat},
};
Inset ops59 = {op59, nelem(op59)};
Inst op63a[] = {
[12] {farith, "frsp", Ifloat},
[14] {farith, "fctiw", Ifloat},
[15] {farith, "fctiwz", Ifloat},
[18] {farith, "fdiv", Ifloat},
[20] {farith, "fsub", Ifloat},
[21] {farith, "fadd", Ifloat},
[22] {unimp, "frsqrt", Ifloat},
[23] {unimp, "fsel", Ifloat},
[25] {farith, "fmul", Ifloat},
[26] {unimp, "frsqrte", Ifloat},
[28] {farith, "fmsub", Ifloat},
[29] {farith, "fmadd", Ifloat},
[30] {farith, "fnmsub", Ifloat},
[31] {farith, "fnmadd", Ifloat},
};
Inset ops63a= {op63a, nelem(op63a)};
Inst op63b[] = {
[0] {fcmp, "fcmpu", Ifloat},
[32] {fcmp, "fcmpo", Ifloat},
[38] {mtfsb1, "mtfsb1", Ifloat},
[40] {farith2, "fneg", Ifloat},
[64] {mcrfs, "mcrfs", Ifloat},
[70] {mtfsb0, "mtfsb0", Ifloat},
[72] {farith2, "fmr", Ifloat},
[134] {mtfsfi, "mtfsfi", Ifloat},
[136] {farith2, "fnabs", Ifloat},
[264] {farith2, "fabs", Ifloat},
[583] {mffs, "mffs", Ifloat},
[711] {mtfsf, "mtfsf", Ifloat},
};
Inset ops63b = {op63b, nelem(op63b)};
void
fpreginit(void)
{
int i;
/* Normally initialised by the kernel */
reg.fd[27] = 4503601774854144.0;
reg.fd[29] = 0.5;
reg.fd[28] = 0.0;
reg.fd[30] = 1.0;
reg.fd[31] = 2.0;
for(i = 0; i < 27; i++)
reg.fd[i] = reg.fd[28];
}
static double
v2fp(uvlong v)
{
FPdbleword f;
f.hi = v>>32;
f.lo = v;
return f.x;
}
static uvlong
fp2v(double d)
{
FPdbleword f;
f.x = d;
return ((uvlong)f.hi<<32) | f.lo;
}
void
lfs(ulong ir)
{
ulong ea;
int imm, ra, rd, upd;
union {
ulong i;
float f;
} u;
getairr(ir);
ea = imm;
upd = (ir&(1L<<26))!=0;
if(ra) {
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
} else {
if(upd)
undef(ir);
}
if(trace)
itrace("%s\tf%d,%ld(r%d) ea=%lux", ci->name, rd, imm, ra, ea);
u.i = getmem_w(ea);
reg.fd[rd] = u.f;
}
void
lfsx(ulong ir)
{
ulong ea;
int rd, ra, rb, upd;
union {
ulong i;
float f;
} u;
getarrr(ir);
ea = reg.r[rb];
upd = ((ir>>1)&0x3FF)==567;
if(ra){
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
if(trace)
itrace("%s\tf%d,(r%d+r%d) ea=%lux", ci->name, rd, ra, rb, ea);
} else {
if(upd)
undef(ir);
if(trace)
itrace("%s\tf%d,(r%d) ea=%lux", ci->name, rd, rb, ea);
}
u.i = getmem_w(ea);
reg.fd[rd] = u.f;
}
void
lfd(ulong ir)
{
ulong ea;
int imm, ra, rd, upd;
getairr(ir);
ea = imm;
upd = (ir&(1L<<26))!=0;
if(ra) {
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
} else {
if(upd)
undef(ir);
}
if(trace)
itrace("%s\tf%d,%ld(r%d) ea=%lux", ci->name, rd, imm, ra, ea);
reg.fd[rd] = v2fp(getmem_v(ea));
}
void
lfdx(ulong ir)
{
ulong ea;
int rd, ra, rb, upd;
getarrr(ir);
ea = reg.r[rb];
upd = ((ir>>1)&0x3FF)==631;
if(ra){
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
if(trace)
itrace("%s\tf%d,(r%d+r%d) ea=%lux", ci->name, rd, ra, rb, ea);
} else {
if(upd)
undef(ir);
if(trace)
itrace("%s\tf%d,(r%d) ea=%lux", ci->name, rd, rb, ea);
}
reg.fd[rd] = v2fp(getmem_v(ea));
}
void
stfs(ulong ir)
{
ulong ea;
int imm, ra, rd, upd;
union {
float f;
ulong w;
} u;
getairr(ir);
ea = imm;
upd = (ir&(1L<<26))!=0;
if(ra) {
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
} else {
if(upd)
undef(ir);
}
if(trace)
itrace("%s\tf%d,%ld(r%d) %lux=%g",
ci->name, rd, imm, ra, ea, reg.fd[rd]);
u.f = reg.fd[rd]; /* BUG: actual PPC conversion is more subtle than this */
putmem_w(ea, u.w);
}
void
stfsx(ulong ir)
{
ulong ea;
int rd, ra, rb, upd;
union {
float f;
ulong w;
} u;
getarrr(ir);
ea = reg.r[rb];
upd = getxo(ir)==695;
if(ra){
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
if(trace)
itrace("%s\tf%d,(r%d+r%d) %lux=%g", ci->name, rd, ra, rb, ea, (float)reg.fd[rd]);
} else {
if(upd)
undef(ir);
if(trace)
itrace("%s\tf%d,(r%d) %lux=%g", ci->name, rd, rb, ea, (float)reg.fd[rd]);
}
u.f = reg.fd[rd]; /* BUG: actual PPC conversion is more subtle than this */
putmem_w(ea, u.w);
}
void
stfd(ulong ir)
{
ulong ea;
int imm, ra, rd, upd;
getairr(ir);
ea = imm;
upd = (ir&(1L<<26))!=0;
if(ra) {
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
} else {
if(upd)
undef(ir);
}
if(trace)
itrace("%s\tf%d,%ld(r%d) %lux=%g",
ci->name, rd, imm, ra, ea, reg.fd[rd]);
putmem_v(ea, fp2v(reg.fd[rd]));
}
void
stfdx(ulong ir)
{
ulong ea;
int rd, ra, rb, upd;
getarrr(ir);
ea = reg.r[rb];
upd = ((ir>>1)&0x3FF)==759;
if(ra){
ea += reg.r[ra];
if(upd)
reg.r[ra] = ea;
if(trace)
itrace("%s\tf%d,(r%d+r%d) %lux=%g", ci->name, rd, ra, rb, ea, reg.fd[rd]);
} else {
if(upd)
undef(ir);
if(trace)
itrace("%s\tf%d,(r%d) %lux=%g", ci->name, rd, rb, ea, reg.fd[rd]);
}
putmem_v(ea, fp2v(reg.fd[rd]));
}
void
mcrfs(ulong ir)
{
ulong 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)
undef(ir);
ra >>= 2;
rd >>= 2;
reg.cr = (reg.cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, reg.fpscr));
reg.fpscr &= ~fpscr0[ra];
if(trace)
itrace("mcrfs\tcrf%d,crf%d\n", rd, ra);
}
void
mffs(ulong ir)
{
int rd, ra, rb;
FPdbleword d;
getarrr(ir);
if(ra || rb)
undef(ir);
d.hi = 0xFFF80000UL;
d.lo = reg.fpscr;
reg.fd[rd] = d.x;
/* it's anyone's guess how CR1 should be set when ir&1 */
reg.cr &= ~mkCR(1, 0xE); /* leave SO, reset others */
if(trace)
itrace("mffs%s\tfr%d\n", ir&1?".":"", rd);
}
void
mtfsb1(ulong ir)
{
int rd, ra, rb;
getarrr(ir);
if(ra || rb)
undef(ir);
reg.fpscr |= (1L << (31-rd));
/* BUG: should set summary bits */
if(ir & 1)
reg.cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
if(trace)
itrace("mtfsb1%s\tfr%d\n", ir&1?".":"", rd);
}
void
mtfsb0(ulong ir)
{
int rd, ra, rb;
getarrr(ir);
if(ra || rb)
undef(ir);
reg.fpscr &= ~(1L << (31-rd));
if(ir & 1)
reg.cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
if(trace)
itrace("mtfsb0%s\tfr%d\n", ir&1?".":"", rd);
}
void
mtfsf(ulong ir)
{
int fm, rb, i;
FPdbleword d;
ulong v;
if(ir & ((1L << 25)|(1L << 16)))
undef(ir);
rb = (ir >> 11) & 0x1F;
fm = (ir >> 17) & 0xFF;
d.x = reg.fd[rb];
v = d.lo;
for(i=0; i<8; i++)
if(fm & (1 << (7-i)))
reg.fpscr = (reg.fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v));
/* BUG: should set FEX and VX `according to the usual rule' */
if(ir & 1)
reg.cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
if(trace)
itrace("mtfsf%s\t#%.2x,fr%d", ir&1?".":"", fm, rb);
}
void
mtfsfi(ulong ir)
{
int imm, rd;
if(ir & ((0x7F << 16)|(1L << 11)))
undef(ir);
rd = (ir >> 23) & 0xF;
imm = (ir >> 12) & 0xF;
reg.fpscr = (reg.fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm);
/* BUG: should set FEX and VX `according to the usual rule' */
if(ir & 1)
reg.cr &= ~mkCR(1, 0xE); /* BUG: manual unclear: leave SO, reset others? */
if(trace)
itrace("mtfsfi%s\tcrf%d,#%x", ir&1?".":"", rd, imm);
}
void
fcmp(ulong ir)
{
int fc, rd, ra, rb;
getarrr(ir);
if(rd & 3)
undef(ir);
rd >>= 2;
SET(fc);
switch(getxo(ir)) {
default:
undef(ir);
case 0:
if(trace)
itrace("fcmpu\tcr%d,f%d,f%d", rd, ra, rb);
if(isNaN(reg.fd[ra]) || isNaN(reg.fd[rb])) {
fc = CRFU;
break;
}
if(reg.fd[ra] == reg.fd[rb]) {
fc = CREQ;
break;
}
if(reg.fd[ra] < reg.fd[rb]) {
fc = CRLT;
break;
}
if(reg.fd[ra] > reg.fd[rb]) {
fc = CRGT;
break;
}
print("qi: fcmp error\n");
break;
case 32:
if(trace)
itrace("fcmpo\tcr%d,f%d,f%d", rd, ra, rb);
if(isNaN(reg.fd[ra]) || isNaN(reg.fd[rb])) { /* BUG: depends whether quiet or signalling ... */
fc = CRFU;
Bprint(bioout, "invalid_fp_register\n");
longjmp(errjmp, 0);
}
if(reg.fd[ra] == reg.fd[rb]) {
fc = CREQ;
break;
}
if(reg.fd[ra] < reg.fd[rb]) {
fc = CRLT;
break;
}
if(reg.fd[ra] > reg.fd[rb]) {
fc = CRGT;
break;
}
print("qi: fcmp error\n");
break;
}
fc >>= 28;
reg.cr = (reg.cr & ~mkCR(rd,~0)) | mkCR(rd, fc);
reg.fpscr = (reg.fpscr & ~0xF800) | (fc<<11);
/* BUG: update FX, VXSNAN, VXVC */
}
/*
* the farith functions probably don't produce the right results
* in the presence of NaNs, Infs, etc., esp. wrt exception handling,
*/
void
fariths(ulong ir)
{
int rd, ra, rb, rc, fmt;
char *cc;
ulong fpscr;
fmt = 0;
rc = (ir>>6)&0x1F;
getarrr(ir);
switch(getxo(ir)&0x1F) { /* partial XO decode */
default:
undef(ir);
case 18:
if((float)reg.fd[rb] == 0.0) {
Bprint(bioout, "fp_exception ZX\n");
reg.fpscr |= FPS_ZX | FPS_FX;
longjmp(errjmp, 0);
}
reg.fd[rd] = (float)(reg.fd[ra] / reg.fd[rb]);
break;
case 20:
reg.fd[rd] = (float)(reg.fd[ra] - reg.fd[rb]);
break;
case 21:
reg.fd[rd] = (float)(reg.fd[ra] + reg.fd[rb]);
break;
case 25:
reg.fd[rd] = (float)(reg.fd[ra] * reg.fd[rc]);
rb = rc;
break;
case 28:
reg.fd[rd] = (float)((reg.fd[ra] * reg.fd[rc]) - reg.fd[rb]);
fmt = 2;
break;
case 29:
reg.fd[rd] = (float)((reg.fd[ra] * reg.fd[rc]) + reg.fd[rb]);
fmt = 2;
break;
case 30:
reg.fd[rd] = (float)-((reg.fd[ra] * reg.fd[rc]) - reg.fd[rb]);
fmt = 2;
break;
case 31:
reg.fd[rd] = (float)-((reg.fd[ra] * reg.fd[rc]) + reg.fd[rb]);
fmt = 2;
break;
}
if(fmt==1 && ra)
undef(ir);
fpscr = setfpscr();
setfpcc(reg.fd[rd]);
cc = "";
if(ir & 1) {
cc = ".";
reg.cr = (reg.cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
}
if(trace) {
switch(fmt) {
case 0:
itrace("%s%s\tfr%d,fr%d,fr%d", ci->name, cc, rd, ra, rb);
break;
case 1:
itrace("%s%s\tfr%d,fr%d", ci->name, cc, rd, rb);
break;
case 2:
itrace("%s%s\tfr%d,fr%d,fr%d,fr%d", ci->name, cc, rd, ra, rc, rb);
break;
}
}
}
void
farith(ulong ir)
{
vlong vl;
int rd, ra, rb, rc, fmt;
char *cc;
ulong fpscr;
int nocc;
double d;
fmt = 0;
nocc = 0;
rc = (ir>>6)&0x1F;
getarrr(ir);
switch(getxo(ir)&0x1F) { /* partial XO decode */
default:
undef(ir);
case 12: /* frsp */
reg.fd[rd] = (float)reg.fd[rb];
fmt = 1;
break;
case 14: /* fctiw */ /* BUG: ignores rounding mode */
case 15: /* fctiwz */
d = reg.fd[rb];
if(d >= 0x7fffffff)
vl = 0x7fffffff;
else if(d < 0x80000000)
vl = 0x80000000;
else
vl = d;
reg.fd[rd] = v2fp(vl);
fmt = 1;
nocc = 1;
break;
case 18:
if(reg.fd[rb] == 0.0) {
Bprint(bioout, "fp_exception ZX\n");
reg.fpscr |= FPS_ZX | FPS_FX;
longjmp(errjmp, 0);
}
reg.fd[rd] = reg.fd[ra] / reg.fd[rb];
break;
case 20:
reg.fd[rd] = reg.fd[ra] - reg.fd[rb];
break;
case 21:
reg.fd[rd] = reg.fd[ra] + reg.fd[rb];
break;
case 25:
reg.fd[rd] = reg.fd[ra] * reg.fd[rc];
rb = rc;
break;
case 28:
reg.fd[rd] = (reg.fd[ra] * reg.fd[rc]) - reg.fd[rb];
fmt = 2;
break;
case 29:
reg.fd[rd] = (reg.fd[ra] * reg.fd[rc]) + reg.fd[rb];
fmt = 2;
break;
case 30:
reg.fd[rd] = -((reg.fd[ra] * reg.fd[rc]) - reg.fd[rb]);
fmt = 2;
break;
case 31:
reg.fd[rd] = -((reg.fd[ra] * reg.fd[rc]) + reg.fd[rb]);
fmt = 2;
break;
}
if(fmt==1 && ra)
undef(ir);
fpscr = setfpscr();
if(nocc == 0)
setfpcc(reg.fd[rd]);
cc = "";
if(ir & 1) {
cc = ".";
reg.cr = (reg.cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
}
if(trace) {
switch(fmt) {
case 0:
itrace("%s%s\tfr%d,fr%d,fr%d", ci->name, cc, rd, ra, rb);
break;
case 1:
itrace("%s%s\tfr%d,fr%d", ci->name, cc, rd, rb);
break;
case 2:
itrace("%s%s\tfr%d,fr%d,fr%d,fr%d", ci->name, cc, rd, ra, rc, rb);
break;
}
}
}
void
farith2(ulong ir)
{
int rd, ra, rb;
char *cc;
ulong fpscr;
getarrr(ir);
switch(getxo(ir)) { /* full XO decode */
default:
undef(ir);
case 40:
reg.fd[rd] = -reg.fd[rb];
break;
case 72:
reg.fd[rd] = reg.fd[rb];
break;
case 136:
reg.fd[rd] = -fabs(reg.fd[rb]);
break;
case 264:
reg.fd[rd] = fabs(reg.fd[rb]);
break;
}
if(ra)
undef(ir);
fpscr = setfpscr();
setfpcc(reg.fd[rd]);
cc = "";
if(ir & 1) {
cc = ".";
reg.cr = (reg.cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
}
if(trace)
itrace("%s%s\tfr%d,fr%d", ci->name, cc, rd, rb);
}
ulong
setfpscr(void)
{
ulong fps, fpscr;
fps = getfsr();
fpscr = reg.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 != reg.fpscr) {
fpscr |= FPS_FX;
reg.fpscr = fpscr;
}
return fpscr;
}
void
setfpcc(double r)
{
int c;
c = 0;
if(r == 0)
c |= 2;
else if(r < 0)
c |= 4;
else
c |= 8;
if(isNaN(r))
c |= 1;
reg.fpscr = (reg.fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */
}