xref: /plan9/sys/src/9/teg2/fpiarm.c (revision d11907f1310fb3aff37a1c3d604436d4028fd86f)
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), &REG(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(&REG(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