#include #include #include #include #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 */ }