13de6a9c0SDavid du Colombier /*
23de6a9c0SDavid du Colombier * this doesn't attempt to implement ARM floating-point properties
33de6a9c0SDavid du Colombier * that aren't visible in the Inferno environment.
43de6a9c0SDavid du Colombier * all arithmetic is done in double precision.
53de6a9c0SDavid du Colombier * the FP trap status isn't updated.
63de6a9c0SDavid du Colombier */
73de6a9c0SDavid du Colombier #include "u.h"
83de6a9c0SDavid du Colombier #include "../port/lib.h"
93de6a9c0SDavid du Colombier #include "mem.h"
103de6a9c0SDavid du Colombier #include "dat.h"
113de6a9c0SDavid du Colombier #include "fns.h"
123de6a9c0SDavid du Colombier
133de6a9c0SDavid du Colombier #include "ureg.h"
143de6a9c0SDavid du Colombier
153de6a9c0SDavid du Colombier #include "arm.h"
16*d11907f1SDavid du Colombier #include "../port/fpi.h"
173de6a9c0SDavid du Colombier
1853ff6c4dSDavid du Colombier #define ARM7500 /* emulate old pre-VFP opcodes */
1953ff6c4dSDavid du Colombier
203de6a9c0SDavid du Colombier /* undef this if correct kernel r13 isn't in Ureg;
213de6a9c0SDavid du Colombier * check calculation in fpiarm below
223de6a9c0SDavid du Colombier */
233de6a9c0SDavid du Colombier
243de6a9c0SDavid du Colombier #define REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
253de6a9c0SDavid du Colombier #ifdef ARM7500
263de6a9c0SDavid du Colombier #define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
273de6a9c0SDavid du Colombier #else
283de6a9c0SDavid du Colombier #define FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)])
293de6a9c0SDavid du Colombier #endif
303de6a9c0SDavid du Colombier
313de6a9c0SDavid du Colombier typedef struct FP2 FP2;
323de6a9c0SDavid du Colombier typedef struct FP1 FP1;
333de6a9c0SDavid du Colombier
343de6a9c0SDavid du Colombier struct FP2 {
353de6a9c0SDavid du Colombier char* name;
363de6a9c0SDavid du Colombier void (*f)(Internal, Internal, Internal*);
373de6a9c0SDavid du Colombier };
383de6a9c0SDavid du Colombier
393de6a9c0SDavid du Colombier struct FP1 {
403de6a9c0SDavid du Colombier char* name;
413de6a9c0SDavid du Colombier void (*f)(Internal*, Internal*);
423de6a9c0SDavid du Colombier };
433de6a9c0SDavid du Colombier
443de6a9c0SDavid du Colombier enum {
453de6a9c0SDavid du Colombier N = 1<<31,
463de6a9c0SDavid du Colombier Z = 1<<30,
473de6a9c0SDavid du Colombier C = 1<<29,
483de6a9c0SDavid du Colombier V = 1<<28,
493de6a9c0SDavid du Colombier REGPC = 15,
503de6a9c0SDavid du Colombier };
513de6a9c0SDavid du Colombier
523de6a9c0SDavid du Colombier enum {
533de6a9c0SDavid du Colombier fpemudebug = 0,
543de6a9c0SDavid du Colombier };
553de6a9c0SDavid du Colombier
563de6a9c0SDavid du Colombier #undef OFR
573de6a9c0SDavid du Colombier #define OFR(X) ((ulong)&((Ureg*)0)->X)
583de6a9c0SDavid du Colombier
593de6a9c0SDavid du Colombier static int roff[] = {
603de6a9c0SDavid du Colombier OFR(r0), OFR(r1), OFR(r2), OFR(r3),
613de6a9c0SDavid du Colombier OFR(r4), OFR(r5), OFR(r6), OFR(r7),
623de6a9c0SDavid du Colombier OFR(r8), OFR(r9), OFR(r10), OFR(r11),
633de6a9c0SDavid du Colombier OFR(r12), OFR(r13), OFR(r14), OFR(pc),
643de6a9c0SDavid du Colombier };
653de6a9c0SDavid du Colombier
663de6a9c0SDavid du Colombier static Internal fpconst[8] = { /* indexed by op&7 (ARM 7500 FPA) */
673de6a9c0SDavid du Colombier /* s, e, l, h */
683de6a9c0SDavid du Colombier {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
693de6a9c0SDavid du Colombier {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */
703de6a9c0SDavid du Colombier {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */
713de6a9c0SDavid du Colombier {0, 0x400, 0x00000000, 0x0C000000}, /* 3.0 */
723de6a9c0SDavid du Colombier {0, 0x401, 0x00000000, 0x08000000}, /* 4.0 */
733de6a9c0SDavid du Colombier {0, 0x401, 0x00000000, 0x0A000000}, /* 5.0 */
743de6a9c0SDavid du Colombier {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */
753de6a9c0SDavid du Colombier {0, 0x402, 0x00000000, 0x0A000000}, /* 10.0 */
763de6a9c0SDavid du Colombier };
773de6a9c0SDavid du Colombier
783de6a9c0SDavid du Colombier /*
793de6a9c0SDavid du Colombier * arm binary operations
803de6a9c0SDavid du Colombier */
813de6a9c0SDavid du Colombier
823de6a9c0SDavid du Colombier static void
fadd(Internal m,Internal n,Internal * d)833de6a9c0SDavid du Colombier fadd(Internal m, Internal n, Internal *d)
843de6a9c0SDavid du Colombier {
853de6a9c0SDavid du Colombier (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
863de6a9c0SDavid du Colombier }
873de6a9c0SDavid du Colombier
883de6a9c0SDavid du Colombier static void
fsub(Internal m,Internal n,Internal * d)893de6a9c0SDavid du Colombier fsub(Internal m, Internal n, Internal *d)
903de6a9c0SDavid du Colombier {
913de6a9c0SDavid du Colombier m.s ^= 1;
923de6a9c0SDavid du Colombier (m.s == n.s? fpiadd: fpisub)(&m, &n, d);
933de6a9c0SDavid du Colombier }
943de6a9c0SDavid du Colombier
953de6a9c0SDavid du Colombier static void
fsubr(Internal m,Internal n,Internal * d)963de6a9c0SDavid du Colombier fsubr(Internal m, Internal n, Internal *d)
973de6a9c0SDavid du Colombier {
983de6a9c0SDavid du Colombier n.s ^= 1;
993de6a9c0SDavid du Colombier (n.s == m.s? fpiadd: fpisub)(&n, &m, d);
1003de6a9c0SDavid du Colombier }
1013de6a9c0SDavid du Colombier
1023de6a9c0SDavid du Colombier static void
fmul(Internal m,Internal n,Internal * d)1033de6a9c0SDavid du Colombier fmul(Internal m, Internal n, Internal *d)
1043de6a9c0SDavid du Colombier {
1053de6a9c0SDavid du Colombier fpimul(&m, &n, d);
1063de6a9c0SDavid du Colombier }
1073de6a9c0SDavid du Colombier
1083de6a9c0SDavid du Colombier static void
fdiv(Internal m,Internal n,Internal * d)1093de6a9c0SDavid du Colombier fdiv(Internal m, Internal n, Internal *d)
1103de6a9c0SDavid du Colombier {
1113de6a9c0SDavid du Colombier fpidiv(&m, &n, d);
1123de6a9c0SDavid du Colombier }
1133de6a9c0SDavid du Colombier
1143de6a9c0SDavid du Colombier static void
fdivr(Internal m,Internal n,Internal * d)1153de6a9c0SDavid du Colombier fdivr(Internal m, Internal n, Internal *d)
1163de6a9c0SDavid du Colombier {
1173de6a9c0SDavid du Colombier fpidiv(&n, &m, d);
1183de6a9c0SDavid du Colombier }
1193de6a9c0SDavid du Colombier
1203de6a9c0SDavid du Colombier /*
1213de6a9c0SDavid du Colombier * arm unary operations
1223de6a9c0SDavid du Colombier */
1233de6a9c0SDavid du Colombier
1243de6a9c0SDavid du Colombier static void
fmov(Internal * m,Internal * d)1253de6a9c0SDavid du Colombier fmov(Internal *m, Internal *d)
1263de6a9c0SDavid du Colombier {
1273de6a9c0SDavid du Colombier *d = *m;
1283de6a9c0SDavid du Colombier }
1293de6a9c0SDavid du Colombier
1303de6a9c0SDavid du Colombier static void
fmovn(Internal * m,Internal * d)1313de6a9c0SDavid du Colombier fmovn(Internal *m, Internal *d)
1323de6a9c0SDavid du Colombier {
1333de6a9c0SDavid du Colombier *d = *m;
1343de6a9c0SDavid du Colombier d->s ^= 1;
1353de6a9c0SDavid du Colombier }
1363de6a9c0SDavid du Colombier
1373de6a9c0SDavid du Colombier static void
fabsf(Internal * m,Internal * d)1383de6a9c0SDavid du Colombier fabsf(Internal *m, Internal *d)
1393de6a9c0SDavid du Colombier {
1403de6a9c0SDavid du Colombier *d = *m;
1413de6a9c0SDavid du Colombier d->s = 0;
1423de6a9c0SDavid du Colombier }
1433de6a9c0SDavid du Colombier
1443de6a9c0SDavid du Colombier static void
frnd(Internal * m,Internal * d)1453de6a9c0SDavid du Colombier frnd(Internal *m, Internal *d)
1463de6a9c0SDavid du Colombier {
1473de6a9c0SDavid du Colombier short e;
1483de6a9c0SDavid du Colombier
1493de6a9c0SDavid du Colombier (m->s? fsub: fadd)(fpconst[6], *m, d);
1503de6a9c0SDavid du Colombier if(IsWeird(d))
1513de6a9c0SDavid du Colombier return;
1523de6a9c0SDavid du Colombier fpiround(d);
1533de6a9c0SDavid du Colombier e = (d->e - ExpBias) + 1;
1543de6a9c0SDavid du Colombier if(e <= 0)
1553de6a9c0SDavid du Colombier SetZero(d);
1563de6a9c0SDavid du Colombier else if(e > FractBits){
1573de6a9c0SDavid du Colombier if(e < 2*FractBits)
1583de6a9c0SDavid du Colombier d->l &= ~((1<<(2*FractBits - e))-1);
1593de6a9c0SDavid du Colombier }else{
1603de6a9c0SDavid du Colombier d->l = 0;
1613de6a9c0SDavid du Colombier if(e < FractBits)
1623de6a9c0SDavid du Colombier d->h &= ~((1<<(FractBits-e))-1);
1633de6a9c0SDavid du Colombier }
1643de6a9c0SDavid du Colombier }
1653de6a9c0SDavid du Colombier
1663de6a9c0SDavid du Colombier /*
1673de6a9c0SDavid du Colombier * ARM 7500 FPA opcodes
1683de6a9c0SDavid du Colombier */
1693de6a9c0SDavid du Colombier
1703de6a9c0SDavid du Colombier static FP1 optab1[16] = { /* Fd := OP Fm */
1713de6a9c0SDavid du Colombier [0] {"MOVF", fmov},
1723de6a9c0SDavid du Colombier [1] {"NEGF", fmovn},
1733de6a9c0SDavid du Colombier [2] {"ABSF", fabsf},
1743de6a9c0SDavid du Colombier [3] {"RNDF", frnd},
1753de6a9c0SDavid du Colombier [4] {"SQTF", /*fsqt*/0},
1763de6a9c0SDavid du Colombier /* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
1773de6a9c0SDavid du Colombier /* URD and NRM aren't implemented */
1783de6a9c0SDavid du Colombier };
1793de6a9c0SDavid du Colombier
1803de6a9c0SDavid du Colombier static FP2 optab2[16] = { /* Fd := Fn OP Fm */
1813de6a9c0SDavid du Colombier [0] {"ADDF", fadd},
1823de6a9c0SDavid du Colombier [1] {"MULF", fmul},
1833de6a9c0SDavid du Colombier [2] {"SUBF", fsub},
1843de6a9c0SDavid du Colombier [3] {"RSUBF", fsubr},
1853de6a9c0SDavid du Colombier [4] {"DIVF", fdiv},
1863de6a9c0SDavid du Colombier [5] {"RDIVF", fdivr},
1873de6a9c0SDavid du Colombier /* POW, RPW deprecated */
1883de6a9c0SDavid du Colombier [8] {"REMF", /*frem*/0},
1893de6a9c0SDavid du Colombier [9] {"FMF", fmul}, /* fast multiply */
1903de6a9c0SDavid du Colombier [10] {"FDV", fdiv}, /* fast divide */
1913de6a9c0SDavid du Colombier [11] {"FRD", fdivr}, /* fast reverse divide */
1923de6a9c0SDavid du Colombier /* POL deprecated */
1933de6a9c0SDavid du Colombier };
1943de6a9c0SDavid du Colombier
1953de6a9c0SDavid du Colombier static ulong
fcmp(Internal * n,Internal * m)1963de6a9c0SDavid du Colombier fcmp(Internal *n, Internal *m)
1973de6a9c0SDavid du Colombier {
1983de6a9c0SDavid du Colombier int i;
1993de6a9c0SDavid du Colombier Internal rm, rn;
2003de6a9c0SDavid du Colombier
2013de6a9c0SDavid du Colombier if(IsWeird(m) || IsWeird(n)){
2023de6a9c0SDavid du Colombier /* BUG: should trap if not masked */
2033de6a9c0SDavid du Colombier return V|C;
2043de6a9c0SDavid du Colombier }
2053de6a9c0SDavid du Colombier rn = *n;
2063de6a9c0SDavid du Colombier rm = *m;
2073de6a9c0SDavid du Colombier fpiround(&rn);
2083de6a9c0SDavid du Colombier fpiround(&rm);
2093de6a9c0SDavid du Colombier i = fpicmp(&rn, &rm);
2103de6a9c0SDavid du Colombier if(i > 0)
2113de6a9c0SDavid du Colombier return C;
2123de6a9c0SDavid du Colombier else if(i == 0)
2133de6a9c0SDavid du Colombier return C|Z;
2143de6a9c0SDavid du Colombier else
2153de6a9c0SDavid du Colombier return N;
2163de6a9c0SDavid du Colombier }
2173de6a9c0SDavid du Colombier
2183de6a9c0SDavid du Colombier static void
fld(void (* f)(Internal *,void *),int d,ulong ea,int n,FPsave * ufp)2193de6a9c0SDavid du Colombier fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
2203de6a9c0SDavid du Colombier {
2213de6a9c0SDavid du Colombier void *mem;
2223de6a9c0SDavid du Colombier
2233de6a9c0SDavid du Colombier mem = (void*)ea;
2243de6a9c0SDavid du Colombier (*f)(&FR(ufp, d), mem);
2253de6a9c0SDavid du Colombier if(fpemudebug)
2263de6a9c0SDavid du Colombier print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
2273de6a9c0SDavid du Colombier }
2283de6a9c0SDavid du Colombier
2293de6a9c0SDavid du Colombier static void
fst(void (* f)(void *,Internal *),ulong ea,int s,int n,FPsave * ufp)2303de6a9c0SDavid du Colombier fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
2313de6a9c0SDavid du Colombier {
2323de6a9c0SDavid du Colombier Internal tmp;
2333de6a9c0SDavid du Colombier void *mem;
2343de6a9c0SDavid du Colombier
2353de6a9c0SDavid du Colombier mem = (void*)ea;
2363de6a9c0SDavid du Colombier tmp = FR(ufp, s);
2373de6a9c0SDavid du Colombier if(fpemudebug)
2383de6a9c0SDavid du Colombier print("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea);
2393de6a9c0SDavid du Colombier (*f)(mem, &tmp);
2403de6a9c0SDavid du Colombier }
2413de6a9c0SDavid du Colombier
2423de6a9c0SDavid du Colombier static int
condok(int cc,int c)2433de6a9c0SDavid du Colombier condok(int cc, int c)
2443de6a9c0SDavid du Colombier {
2453de6a9c0SDavid du Colombier switch(c){
2463de6a9c0SDavid du Colombier case 0: /* Z set */
2473de6a9c0SDavid du Colombier return cc&Z;
2483de6a9c0SDavid du Colombier case 1: /* Z clear */
2493de6a9c0SDavid du Colombier return (cc&Z) == 0;
2503de6a9c0SDavid du Colombier case 2: /* C set */
2513de6a9c0SDavid du Colombier return cc&C;
2523de6a9c0SDavid du Colombier case 3: /* C clear */
2533de6a9c0SDavid du Colombier return (cc&C) == 0;
2543de6a9c0SDavid du Colombier case 4: /* N set */
2553de6a9c0SDavid du Colombier return cc&N;
2563de6a9c0SDavid du Colombier case 5: /* N clear */
2573de6a9c0SDavid du Colombier return (cc&N) == 0;
2583de6a9c0SDavid du Colombier case 6: /* V set */
2593de6a9c0SDavid du Colombier return cc&V;
2603de6a9c0SDavid du Colombier case 7: /* V clear */
2613de6a9c0SDavid du Colombier return (cc&V) == 0;
2623de6a9c0SDavid du Colombier case 8: /* C set and Z clear */
2633de6a9c0SDavid du Colombier return cc&C && (cc&Z) == 0;
2643de6a9c0SDavid du Colombier case 9: /* C clear or Z set */
2653de6a9c0SDavid du Colombier return (cc&C) == 0 || cc&Z;
2663de6a9c0SDavid du Colombier case 10: /* N set and V set, or N clear and V clear */
2673de6a9c0SDavid du Colombier return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
2683de6a9c0SDavid du Colombier case 11: /* N set and V clear, or N clear and V set */
2693de6a9c0SDavid du Colombier return (cc&(N|V))==N || (cc&(N|V))==V;
2703de6a9c0SDavid du Colombier case 12: /* Z clear, and either N set and V set or N clear and V clear */
2713de6a9c0SDavid du Colombier return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
2723de6a9c0SDavid du Colombier case 13: /* Z set, or N set and V clear or N clear and V set */
2733de6a9c0SDavid du Colombier return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
2743de6a9c0SDavid du Colombier case 14: /* always */
2753de6a9c0SDavid du Colombier return 1;
2763de6a9c0SDavid du Colombier case 15: /* never (reserved) */
2773de6a9c0SDavid du Colombier return 0;
2783de6a9c0SDavid du Colombier }
2793de6a9c0SDavid du Colombier return 0; /* not reached */
2803de6a9c0SDavid du Colombier }
2813de6a9c0SDavid du Colombier
2823de6a9c0SDavid du Colombier static void
unimp(ulong pc,ulong op)2833de6a9c0SDavid du Colombier unimp(ulong pc, ulong op)
2843de6a9c0SDavid du Colombier {
2853de6a9c0SDavid du Colombier char buf[60];
2863de6a9c0SDavid du Colombier
2873de6a9c0SDavid du Colombier snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
2883de6a9c0SDavid du Colombier if(fpemudebug)
2893de6a9c0SDavid du Colombier print("FPE: %s\n", buf);
2903de6a9c0SDavid du Colombier error(buf);
2913de6a9c0SDavid du Colombier /* no return */
2923de6a9c0SDavid du Colombier }
2933de6a9c0SDavid du Colombier
2943de6a9c0SDavid du Colombier static void
fpemu(ulong pc,ulong op,Ureg * ur,FPsave * ufp)2953de6a9c0SDavid du Colombier fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
2963de6a9c0SDavid du Colombier {
2973de6a9c0SDavid du Colombier int rn, rd, tag, o;
2983de6a9c0SDavid du Colombier long off;
2993de6a9c0SDavid du Colombier ulong ea;
3003de6a9c0SDavid du Colombier Internal tmp, *fm, *fn;
3013de6a9c0SDavid du Colombier
3023de6a9c0SDavid du Colombier /* note: would update fault status here if we noted numeric exceptions */
3033de6a9c0SDavid du Colombier
3043de6a9c0SDavid du Colombier /*
3053de6a9c0SDavid du Colombier * LDF, STF; 10.1.1
3063de6a9c0SDavid du Colombier */
3073de6a9c0SDavid du Colombier if(((op>>25)&7) == 6){
3083de6a9c0SDavid du Colombier if(op & (1<<22))
3093de6a9c0SDavid du Colombier unimp(pc, op); /* packed or extended */
3103de6a9c0SDavid du Colombier rn = (op>>16)&0xF;
3113de6a9c0SDavid du Colombier off = (op&0xFF)<<2;
3123de6a9c0SDavid du Colombier if((op & (1<<23)) == 0)
3133de6a9c0SDavid du Colombier off = -off;
3143de6a9c0SDavid du Colombier ea = REG(ur, rn);
3153de6a9c0SDavid du Colombier if(rn == REGPC)
3163de6a9c0SDavid du Colombier ea += 8;
3173de6a9c0SDavid du Colombier if(op & (1<<24))
3183de6a9c0SDavid du Colombier ea += off;
3193de6a9c0SDavid du Colombier rd = (op>>12)&7;
3203de6a9c0SDavid du Colombier if(op & (1<<20)){
3213de6a9c0SDavid du Colombier if(op & (1<<15))
3223de6a9c0SDavid du Colombier fld(fpid2i, rd, ea, 8, ufp);
3233de6a9c0SDavid du Colombier else
3243de6a9c0SDavid du Colombier fld(fpis2i, rd, ea, 4, ufp);
3253de6a9c0SDavid du Colombier }else{
3263de6a9c0SDavid du Colombier if(op & (1<<15))
3273de6a9c0SDavid du Colombier fst(fpii2d, ea, rd, 8, ufp);
3283de6a9c0SDavid du Colombier else
3293de6a9c0SDavid du Colombier fst(fpii2s, ea, rd, 4, ufp);
3303de6a9c0SDavid du Colombier }
3313de6a9c0SDavid du Colombier if((op & (1<<24)) == 0)
3323de6a9c0SDavid du Colombier ea += off;
3333de6a9c0SDavid du Colombier if(op & (1<<21))
3343de6a9c0SDavid du Colombier REG(ur, rn) = ea;
3353de6a9c0SDavid du Colombier return;
3363de6a9c0SDavid du Colombier }
3373de6a9c0SDavid du Colombier
3383de6a9c0SDavid du Colombier /*
3393de6a9c0SDavid du Colombier * CPRT/transfer, 10.3
3403de6a9c0SDavid du Colombier */
3413de6a9c0SDavid du Colombier if(op & (1<<4)){
3423de6a9c0SDavid du Colombier rd = (op>>12) & 0xF;
3433de6a9c0SDavid du Colombier
3443de6a9c0SDavid du Colombier /*
3453de6a9c0SDavid du Colombier * compare, 10.3.1
3463de6a9c0SDavid du Colombier */
3473de6a9c0SDavid du Colombier if(rd == 15 && op & (1<<20)){
3483de6a9c0SDavid du Colombier rn = (op>>16)&7;
3493de6a9c0SDavid du Colombier fn = &FR(ufp, rn);
3503de6a9c0SDavid du Colombier if(op & (1<<3)){
3513de6a9c0SDavid du Colombier fm = &fpconst[op&7];
3523de6a9c0SDavid du Colombier if(fpemudebug)
3533de6a9c0SDavid du Colombier tag = 'C';
3543de6a9c0SDavid du Colombier }else{
3553de6a9c0SDavid du Colombier fm = &FR(ufp, op&7);
3563de6a9c0SDavid du Colombier if(fpemudebug)
3573de6a9c0SDavid du Colombier tag = 'F';
3583de6a9c0SDavid du Colombier }
3593de6a9c0SDavid du Colombier switch((op>>21)&7){
3603de6a9c0SDavid du Colombier default:
3613de6a9c0SDavid du Colombier unimp(pc, op);
3623de6a9c0SDavid du Colombier case 4: /* CMF: Fn :: Fm */
3633de6a9c0SDavid du Colombier case 6: /* CMFE: Fn :: Fm (with exception) */
3643de6a9c0SDavid du Colombier ur->psr &= ~(N|C|Z|V);
3653de6a9c0SDavid du Colombier ur->psr |= fcmp(fn, fm);
3663de6a9c0SDavid du Colombier break;
3673de6a9c0SDavid du Colombier case 5: /* CNF: Fn :: -Fm */
3683de6a9c0SDavid du Colombier case 7: /* CNFE: Fn :: -Fm (with exception) */
3693de6a9c0SDavid du Colombier tmp = *fm;
3703de6a9c0SDavid du Colombier tmp.s ^= 1;
3713de6a9c0SDavid du Colombier ur->psr &= ~(N|C|Z|V);
3723de6a9c0SDavid du Colombier ur->psr |= fcmp(fn, &tmp);
3733de6a9c0SDavid du Colombier break;
3743de6a9c0SDavid du Colombier }
3753de6a9c0SDavid du Colombier if(fpemudebug)
3763de6a9c0SDavid du Colombier print("CMPF %c%d,F%ld =%#lux\n",
3773de6a9c0SDavid du Colombier tag, rn, op&7, ur->psr>>28);
3783de6a9c0SDavid du Colombier return;
3793de6a9c0SDavid du Colombier }
3803de6a9c0SDavid du Colombier
3813de6a9c0SDavid du Colombier /*
3823de6a9c0SDavid du Colombier * other transfer, 10.3
3833de6a9c0SDavid du Colombier */
3843de6a9c0SDavid du Colombier switch((op>>20)&0xF){
3853de6a9c0SDavid du Colombier default:
3863de6a9c0SDavid du Colombier unimp(pc, op);
3873de6a9c0SDavid du Colombier case 0: /* FLT */
3883de6a9c0SDavid du Colombier rn = (op>>16) & 7;
3893de6a9c0SDavid du Colombier fpiw2i(&FR(ufp, rn), ®(ur, rd));
3903de6a9c0SDavid du Colombier if(fpemudebug)
3913de6a9c0SDavid du Colombier print("MOVW[FD] R%d, F%d\n", rd, rn);
3923de6a9c0SDavid du Colombier break;
3933de6a9c0SDavid du Colombier case 1: /* FIX */
3943de6a9c0SDavid du Colombier if(op & (1<<3))
3953de6a9c0SDavid du Colombier unimp(pc, op);
3963de6a9c0SDavid du Colombier rn = op & 7;
3973de6a9c0SDavid du Colombier tmp = FR(ufp, rn);
3983de6a9c0SDavid du Colombier fpii2w(®(ur, rd), &tmp);
3993de6a9c0SDavid du Colombier if(fpemudebug)
4003de6a9c0SDavid du Colombier print("MOV[FD]W F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
4013de6a9c0SDavid du Colombier break;
4023de6a9c0SDavid du Colombier case 2: /* FPSR := Rd */
4033de6a9c0SDavid du Colombier ufp->status = REG(ur, rd);
4043de6a9c0SDavid du Colombier if(fpemudebug)
4053de6a9c0SDavid du Colombier print("MOVW R%d, FPSR\n", rd);
4063de6a9c0SDavid du Colombier break;
4073de6a9c0SDavid du Colombier case 3: /* Rd := FPSR */
4083de6a9c0SDavid du Colombier REG(ur, rd) = ufp->status;
4093de6a9c0SDavid du Colombier if(fpemudebug)
4103de6a9c0SDavid du Colombier print("MOVW FPSR, R%d\n", rd);
4113de6a9c0SDavid du Colombier break;
4123de6a9c0SDavid du Colombier case 4: /* FPCR := Rd */
4133de6a9c0SDavid du Colombier ufp->control = REG(ur, rd);
4143de6a9c0SDavid du Colombier if(fpemudebug)
4153de6a9c0SDavid du Colombier print("MOVW R%d, FPCR\n", rd);
4163de6a9c0SDavid du Colombier break;
4173de6a9c0SDavid du Colombier case 5: /* Rd := FPCR */
4183de6a9c0SDavid du Colombier REG(ur, rd) = ufp->control;
4193de6a9c0SDavid du Colombier if(fpemudebug)
4203de6a9c0SDavid du Colombier print("MOVW FPCR, R%d\n", rd);
4213de6a9c0SDavid du Colombier break;
4223de6a9c0SDavid du Colombier }
4233de6a9c0SDavid du Colombier return;
4243de6a9c0SDavid du Colombier }
4253de6a9c0SDavid du Colombier
4263de6a9c0SDavid du Colombier /*
4273de6a9c0SDavid du Colombier * arithmetic
4283de6a9c0SDavid du Colombier */
4293de6a9c0SDavid du Colombier
4303de6a9c0SDavid du Colombier if(op & (1<<3)){ /* constant */
4313de6a9c0SDavid du Colombier fm = &fpconst[op&7];
4323de6a9c0SDavid du Colombier if(fpemudebug)
4333de6a9c0SDavid du Colombier tag = 'C';
4343de6a9c0SDavid du Colombier }else{
4353de6a9c0SDavid du Colombier fm = &FR(ufp, op&7);
4363de6a9c0SDavid du Colombier if(fpemudebug)
4373de6a9c0SDavid du Colombier tag = 'F';
4383de6a9c0SDavid du Colombier }
4393de6a9c0SDavid du Colombier rd = (op>>12)&7;
4403de6a9c0SDavid du Colombier o = (op>>20)&0xF;
4413de6a9c0SDavid du Colombier if(op & (1<<15)){ /* monadic */
4423de6a9c0SDavid du Colombier FP1 *fp;
4433de6a9c0SDavid du Colombier fp = &optab1[o];
4443de6a9c0SDavid du Colombier if(fp->f == nil)
4453de6a9c0SDavid du Colombier unimp(pc, op);
4463de6a9c0SDavid du Colombier if(fpemudebug)
4473de6a9c0SDavid du Colombier print("%s %c%ld,F%d\n", fp->name, tag, op&7, rd);
4483de6a9c0SDavid du Colombier (*fp->f)(fm, &FR(ufp, rd));
4493de6a9c0SDavid du Colombier } else {
4503de6a9c0SDavid du Colombier FP2 *fp;
4513de6a9c0SDavid du Colombier fp = &optab2[o];
4523de6a9c0SDavid du Colombier if(fp->f == nil)
4533de6a9c0SDavid du Colombier unimp(pc, op);
4543de6a9c0SDavid du Colombier rn = (op>>16)&7;
4553de6a9c0SDavid du Colombier if(fpemudebug)
4563de6a9c0SDavid du Colombier print("%s %c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
4573de6a9c0SDavid du Colombier (*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
4583de6a9c0SDavid du Colombier }
4593de6a9c0SDavid du Colombier }
4603de6a9c0SDavid du Colombier
4613de6a9c0SDavid du Colombier /*
4623de6a9c0SDavid du Colombier * returns the number of FP instructions emulated
4633de6a9c0SDavid du Colombier */
4643de6a9c0SDavid du Colombier int
fpiarm(Ureg * ur)4653de6a9c0SDavid du Colombier fpiarm(Ureg *ur)
4663de6a9c0SDavid du Colombier {
4673de6a9c0SDavid du Colombier ulong op, o, cp;
4683de6a9c0SDavid du Colombier FPsave *ufp;
4693de6a9c0SDavid du Colombier int n;
4703de6a9c0SDavid du Colombier
4713de6a9c0SDavid du Colombier if(up == nil)
4723de6a9c0SDavid du Colombier panic("fpiarm not in a process");
4733de6a9c0SDavid du Colombier ufp = &up->fpsave;
4743de6a9c0SDavid du Colombier /*
4753de6a9c0SDavid du Colombier * because all the emulated fp state is in the proc structure,
4763de6a9c0SDavid du Colombier * it need not be saved/restored
4773de6a9c0SDavid du Colombier */
4789b7bf7dfSDavid du Colombier switch(up->fpstate){
4799b7bf7dfSDavid du Colombier case FPactive:
4809b7bf7dfSDavid du Colombier case FPinactive:
4819b7bf7dfSDavid du Colombier error("illegal instruction: emulated fpu opcode in VFP mode");
4829b7bf7dfSDavid du Colombier case FPinit:
4833de6a9c0SDavid du Colombier assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
4849b7bf7dfSDavid du Colombier up->fpstate = FPemu;
4853de6a9c0SDavid du Colombier ufp->control = 0;
4863de6a9c0SDavid du Colombier ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
4873de6a9c0SDavid du Colombier for(n = 0; n < 8; n++)
4883de6a9c0SDavid du Colombier FR(ufp, n) = fpconst[0];
4893de6a9c0SDavid du Colombier }
4903de6a9c0SDavid du Colombier for(n=0; ;n++){
4913de6a9c0SDavid du Colombier validaddr(ur->pc, 4, 0);
4923de6a9c0SDavid du Colombier op = *(ulong*)(ur->pc);
4933de6a9c0SDavid du Colombier if(fpemudebug)
4943de6a9c0SDavid du Colombier print("%#lux: %#8.8lux ", ur->pc, op);
4953de6a9c0SDavid du Colombier o = (op>>24) & 0xF;
4963de6a9c0SDavid du Colombier cp = (op>>8) & 0xF;
4973de6a9c0SDavid du Colombier if(!ISFPAOP(cp, o))
4983de6a9c0SDavid du Colombier break;
4993de6a9c0SDavid du Colombier if(condok(ur->psr, op>>28))
5003de6a9c0SDavid du Colombier fpemu(ur->pc, op, ur, ufp);
5013de6a9c0SDavid du Colombier ur->pc += 4; /* pretend cpu executed the instr */
5023de6a9c0SDavid du Colombier }
5033de6a9c0SDavid du Colombier if(fpemudebug)
5043de6a9c0SDavid du Colombier print("\n");
5053de6a9c0SDavid du Colombier return n;
5063de6a9c0SDavid du Colombier }
507