xref: /plan9/sys/src/9/bcm/fpiarm.c (revision d11907f1310fb3aff37a1c3d604436d4028fd86f)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * this doesn't attempt to implement ARM floating-point properties
35d9de2d3SDavid du Colombier  * that aren't visible in the Inferno environment.
45d9de2d3SDavid du Colombier  * all arithmetic is done in double precision.
55d9de2d3SDavid du Colombier  * the FP trap status isn't updated.
65d9de2d3SDavid du Colombier  */
75d9de2d3SDavid du Colombier #include	"u.h"
85d9de2d3SDavid du Colombier #include	"../port/lib.h"
95d9de2d3SDavid du Colombier #include	"mem.h"
105d9de2d3SDavid du Colombier #include	"dat.h"
115d9de2d3SDavid du Colombier #include	"fns.h"
125d9de2d3SDavid du Colombier 
135d9de2d3SDavid du Colombier #include	"ureg.h"
145d9de2d3SDavid du Colombier 
155d9de2d3SDavid du Colombier #include	"arm.h"
16*d11907f1SDavid du Colombier #include	"../port/fpi.h"
175d9de2d3SDavid du Colombier 
185d9de2d3SDavid du Colombier #define ARM7500			/* emulate old pre-VFP opcodes */
195d9de2d3SDavid du Colombier 
205d9de2d3SDavid du Colombier /* undef this if correct kernel r13 isn't in Ureg;
215d9de2d3SDavid du Colombier  * check calculation in fpiarm below
225d9de2d3SDavid du Colombier  */
235d9de2d3SDavid du Colombier 
245d9de2d3SDavid du Colombier #define	REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
255d9de2d3SDavid du Colombier #ifdef ARM7500
265d9de2d3SDavid du Colombier #define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
275d9de2d3SDavid du Colombier #else
285d9de2d3SDavid du Colombier #define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)])
295d9de2d3SDavid du Colombier #endif
305d9de2d3SDavid du Colombier 
315d9de2d3SDavid du Colombier typedef struct FP2 FP2;
325d9de2d3SDavid du Colombier typedef struct FP1 FP1;
335d9de2d3SDavid du Colombier 
345d9de2d3SDavid du Colombier struct FP2 {
355d9de2d3SDavid du Colombier 	char*	name;
365d9de2d3SDavid du Colombier 	void	(*f)(Internal, Internal, Internal*);
375d9de2d3SDavid du Colombier };
385d9de2d3SDavid du Colombier 
395d9de2d3SDavid du Colombier struct FP1 {
405d9de2d3SDavid du Colombier 	char*	name;
415d9de2d3SDavid du Colombier 	void	(*f)(Internal*, Internal*);
425d9de2d3SDavid du Colombier };
435d9de2d3SDavid du Colombier 
445d9de2d3SDavid du Colombier enum {
455d9de2d3SDavid du Colombier 	N = 1<<31,
465d9de2d3SDavid du Colombier 	Z = 1<<30,
475d9de2d3SDavid du Colombier 	C = 1<<29,
485d9de2d3SDavid du Colombier 	V = 1<<28,
495d9de2d3SDavid du Colombier 	REGPC = 15,
505d9de2d3SDavid du Colombier };
515d9de2d3SDavid du Colombier 
525d9de2d3SDavid du Colombier enum {
535d9de2d3SDavid du Colombier 	fpemudebug = 0,
545d9de2d3SDavid du Colombier };
555d9de2d3SDavid du Colombier 
565d9de2d3SDavid du Colombier #undef OFR
575d9de2d3SDavid du Colombier #define	OFR(X)	((ulong)&((Ureg*)0)->X)
585d9de2d3SDavid du Colombier 
595d9de2d3SDavid du Colombier static	int	roff[] = {
605d9de2d3SDavid du Colombier 	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
615d9de2d3SDavid du Colombier 	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
625d9de2d3SDavid du Colombier 	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
635d9de2d3SDavid du Colombier 	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
645d9de2d3SDavid du Colombier };
655d9de2d3SDavid du Colombier 
665d9de2d3SDavid du Colombier static Internal fpconst[8] = {		/* indexed by op&7 (ARM 7500 FPA) */
675d9de2d3SDavid du Colombier 	/* s, e, l, h */
685d9de2d3SDavid du Colombier 	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
695d9de2d3SDavid du Colombier 	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
705d9de2d3SDavid du Colombier 	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
715d9de2d3SDavid du Colombier 	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
725d9de2d3SDavid du Colombier 	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
735d9de2d3SDavid du Colombier 	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
745d9de2d3SDavid du Colombier 	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
755d9de2d3SDavid du Colombier 	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
765d9de2d3SDavid du Colombier };
775d9de2d3SDavid du Colombier 
785d9de2d3SDavid du Colombier /*
795d9de2d3SDavid du Colombier  * arm binary operations
805d9de2d3SDavid du Colombier  */
815d9de2d3SDavid du Colombier 
825d9de2d3SDavid du Colombier static void
fadd(Internal m,Internal n,Internal * d)835d9de2d3SDavid du Colombier fadd(Internal m, Internal n, Internal *d)
845d9de2d3SDavid du Colombier {
855d9de2d3SDavid du Colombier 	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
865d9de2d3SDavid du Colombier }
875d9de2d3SDavid du Colombier 
885d9de2d3SDavid du Colombier static void
fsub(Internal m,Internal n,Internal * d)895d9de2d3SDavid du Colombier fsub(Internal m, Internal n, Internal *d)
905d9de2d3SDavid du Colombier {
915d9de2d3SDavid du Colombier 	m.s ^= 1;
925d9de2d3SDavid du Colombier 	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
935d9de2d3SDavid du Colombier }
945d9de2d3SDavid du Colombier 
955d9de2d3SDavid du Colombier static void
fsubr(Internal m,Internal n,Internal * d)965d9de2d3SDavid du Colombier fsubr(Internal m, Internal n, Internal *d)
975d9de2d3SDavid du Colombier {
985d9de2d3SDavid du Colombier 	n.s ^= 1;
995d9de2d3SDavid du Colombier 	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
1005d9de2d3SDavid du Colombier }
1015d9de2d3SDavid du Colombier 
1025d9de2d3SDavid du Colombier static void
fmul(Internal m,Internal n,Internal * d)1035d9de2d3SDavid du Colombier fmul(Internal m, Internal n, Internal *d)
1045d9de2d3SDavid du Colombier {
1055d9de2d3SDavid du Colombier 	fpimul(&m, &n, d);
1065d9de2d3SDavid du Colombier }
1075d9de2d3SDavid du Colombier 
1085d9de2d3SDavid du Colombier static void
fdiv(Internal m,Internal n,Internal * d)1095d9de2d3SDavid du Colombier fdiv(Internal m, Internal n, Internal *d)
1105d9de2d3SDavid du Colombier {
1115d9de2d3SDavid du Colombier 	fpidiv(&m, &n, d);
1125d9de2d3SDavid du Colombier }
1135d9de2d3SDavid du Colombier 
1145d9de2d3SDavid du Colombier static void
fdivr(Internal m,Internal n,Internal * d)1155d9de2d3SDavid du Colombier fdivr(Internal m, Internal n, Internal *d)
1165d9de2d3SDavid du Colombier {
1175d9de2d3SDavid du Colombier 	fpidiv(&n, &m, d);
1185d9de2d3SDavid du Colombier }
1195d9de2d3SDavid du Colombier 
1205d9de2d3SDavid du Colombier /*
1215d9de2d3SDavid du Colombier  * arm unary operations
1225d9de2d3SDavid du Colombier  */
1235d9de2d3SDavid du Colombier 
1245d9de2d3SDavid du Colombier static void
fmov(Internal * m,Internal * d)1255d9de2d3SDavid du Colombier fmov(Internal *m, Internal *d)
1265d9de2d3SDavid du Colombier {
1275d9de2d3SDavid du Colombier 	*d = *m;
1285d9de2d3SDavid du Colombier }
1295d9de2d3SDavid du Colombier 
1305d9de2d3SDavid du Colombier static void
fmovn(Internal * m,Internal * d)1315d9de2d3SDavid du Colombier fmovn(Internal *m, Internal *d)
1325d9de2d3SDavid du Colombier {
1335d9de2d3SDavid du Colombier 	*d = *m;
1345d9de2d3SDavid du Colombier 	d->s ^= 1;
1355d9de2d3SDavid du Colombier }
1365d9de2d3SDavid du Colombier 
1375d9de2d3SDavid du Colombier static void
fabsf(Internal * m,Internal * d)1385d9de2d3SDavid du Colombier fabsf(Internal *m, Internal *d)
1395d9de2d3SDavid du Colombier {
1405d9de2d3SDavid du Colombier 	*d = *m;
1415d9de2d3SDavid du Colombier 	d->s = 0;
1425d9de2d3SDavid du Colombier }
1435d9de2d3SDavid du Colombier 
1445d9de2d3SDavid du Colombier static void
frnd(Internal * m,Internal * d)1455d9de2d3SDavid du Colombier frnd(Internal *m, Internal *d)
1465d9de2d3SDavid du Colombier {
1475d9de2d3SDavid du Colombier 	short e;
1485d9de2d3SDavid du Colombier 
1495d9de2d3SDavid du Colombier 	(m->s? fsub: fadd)(fpconst[6], *m, d);
1505d9de2d3SDavid du Colombier 	if(IsWeird(d))
1515d9de2d3SDavid du Colombier 		return;
1525d9de2d3SDavid du Colombier 	fpiround(d);
1535d9de2d3SDavid du Colombier 	e = (d->e - ExpBias) + 1;
1545d9de2d3SDavid du Colombier 	if(e <= 0)
1555d9de2d3SDavid du Colombier 		SetZero(d);
1565d9de2d3SDavid du Colombier 	else if(e > FractBits){
1575d9de2d3SDavid du Colombier 		if(e < 2*FractBits)
1585d9de2d3SDavid du Colombier 			d->l &= ~((1<<(2*FractBits - e))-1);
1595d9de2d3SDavid du Colombier 	}else{
1605d9de2d3SDavid du Colombier 		d->l = 0;
1615d9de2d3SDavid du Colombier 		if(e < FractBits)
1625d9de2d3SDavid du Colombier 			d->h &= ~((1<<(FractBits-e))-1);
1635d9de2d3SDavid du Colombier 	}
1645d9de2d3SDavid du Colombier }
1655d9de2d3SDavid du Colombier 
1665d9de2d3SDavid du Colombier /*
1675d9de2d3SDavid du Colombier  * ARM 7500 FPA opcodes
1685d9de2d3SDavid du Colombier  */
1695d9de2d3SDavid du Colombier 
1705d9de2d3SDavid du Colombier static	FP1	optab1[16] = {	/* Fd := OP Fm */
1715d9de2d3SDavid du Colombier [0]	{"MOVF",	fmov},
1725d9de2d3SDavid du Colombier [1]	{"NEGF",	fmovn},
1735d9de2d3SDavid du Colombier [2]	{"ABSF",	fabsf},
1745d9de2d3SDavid du Colombier [3]	{"RNDF",	frnd},
1755d9de2d3SDavid du Colombier [4]	{"SQTF",	/*fsqt*/0},
1765d9de2d3SDavid du Colombier /* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
1775d9de2d3SDavid du Colombier /* URD and NRM aren't implemented */
1785d9de2d3SDavid du Colombier };
1795d9de2d3SDavid du Colombier 
1805d9de2d3SDavid du Colombier static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
1815d9de2d3SDavid du Colombier [0]	{"ADDF",	fadd},
1825d9de2d3SDavid du Colombier [1]	{"MULF",	fmul},
1835d9de2d3SDavid du Colombier [2]	{"SUBF",	fsub},
1845d9de2d3SDavid du Colombier [3]	{"RSUBF",	fsubr},
1855d9de2d3SDavid du Colombier [4]	{"DIVF",	fdiv},
1865d9de2d3SDavid du Colombier [5]	{"RDIVF",	fdivr},
1875d9de2d3SDavid du Colombier /* POW, RPW deprecated */
1885d9de2d3SDavid du Colombier [8]	{"REMF",	/*frem*/0},
1895d9de2d3SDavid du Colombier [9]	{"FMF",	fmul},	/* fast multiply */
1905d9de2d3SDavid du Colombier [10]	{"FDV",	fdiv},	/* fast divide */
1915d9de2d3SDavid du Colombier [11]	{"FRD",	fdivr},	/* fast reverse divide */
1925d9de2d3SDavid du Colombier /* POL deprecated */
1935d9de2d3SDavid du Colombier };
1945d9de2d3SDavid du Colombier 
1955d9de2d3SDavid du Colombier static ulong
fcmp(Internal * n,Internal * m)1965d9de2d3SDavid du Colombier fcmp(Internal *n, Internal *m)
1975d9de2d3SDavid du Colombier {
1985d9de2d3SDavid du Colombier 	int i;
1995d9de2d3SDavid du Colombier 	Internal rm, rn;
2005d9de2d3SDavid du Colombier 
2015d9de2d3SDavid du Colombier 	if(IsWeird(m) || IsWeird(n)){
2025d9de2d3SDavid du Colombier 		/* BUG: should trap if not masked */
2035d9de2d3SDavid du Colombier 		return V|C;
2045d9de2d3SDavid du Colombier 	}
2055d9de2d3SDavid du Colombier 	rn = *n;
2065d9de2d3SDavid du Colombier 	rm = *m;
2075d9de2d3SDavid du Colombier 	fpiround(&rn);
2085d9de2d3SDavid du Colombier 	fpiround(&rm);
2095d9de2d3SDavid du Colombier 	i = fpicmp(&rn, &rm);
2105d9de2d3SDavid du Colombier 	if(i > 0)
2115d9de2d3SDavid du Colombier 		return C;
2125d9de2d3SDavid du Colombier 	else if(i == 0)
2135d9de2d3SDavid du Colombier 		return C|Z;
2145d9de2d3SDavid du Colombier 	else
2155d9de2d3SDavid du Colombier 		return N;
2165d9de2d3SDavid du Colombier }
2175d9de2d3SDavid du Colombier 
2185d9de2d3SDavid du Colombier static void
fld(void (* f)(Internal *,void *),int d,ulong ea,int n,FPsave * ufp)2195d9de2d3SDavid du Colombier fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
2205d9de2d3SDavid du Colombier {
2215d9de2d3SDavid du Colombier 	void *mem;
2225d9de2d3SDavid du Colombier 
2235d9de2d3SDavid du Colombier 	mem = (void*)ea;
2245d9de2d3SDavid du Colombier 	(*f)(&FR(ufp, d), mem);
2255d9de2d3SDavid du Colombier 	if(fpemudebug)
2265d9de2d3SDavid du Colombier 		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
2275d9de2d3SDavid du Colombier }
2285d9de2d3SDavid du Colombier 
2295d9de2d3SDavid du Colombier static void
fst(void (* f)(void *,Internal *),ulong ea,int s,int n,FPsave * ufp)2305d9de2d3SDavid du Colombier fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
2315d9de2d3SDavid du Colombier {
2325d9de2d3SDavid du Colombier 	Internal tmp;
2335d9de2d3SDavid du Colombier 	void *mem;
2345d9de2d3SDavid du Colombier 
2355d9de2d3SDavid du Colombier 	mem = (void*)ea;
2365d9de2d3SDavid du Colombier 	tmp = FR(ufp, s);
2375d9de2d3SDavid du Colombier 	if(fpemudebug)
2385d9de2d3SDavid du Colombier 		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
2395d9de2d3SDavid du Colombier 	(*f)(mem, &tmp);
2405d9de2d3SDavid du Colombier }
2415d9de2d3SDavid du Colombier 
2425d9de2d3SDavid du Colombier static int
condok(int cc,int c)2435d9de2d3SDavid du Colombier condok(int cc, int c)
2445d9de2d3SDavid du Colombier {
2455d9de2d3SDavid du Colombier 	switch(c){
2465d9de2d3SDavid du Colombier 	case 0:	/* Z set */
2475d9de2d3SDavid du Colombier 		return cc&Z;
2485d9de2d3SDavid du Colombier 	case 1:	/* Z clear */
2495d9de2d3SDavid du Colombier 		return (cc&Z) == 0;
2505d9de2d3SDavid du Colombier 	case 2:	/* C set */
2515d9de2d3SDavid du Colombier 		return cc&C;
2525d9de2d3SDavid du Colombier 	case 3:	/* C clear */
2535d9de2d3SDavid du Colombier 		return (cc&C) == 0;
2545d9de2d3SDavid du Colombier 	case 4:	/* N set */
2555d9de2d3SDavid du Colombier 		return cc&N;
2565d9de2d3SDavid du Colombier 	case 5:	/* N clear */
2575d9de2d3SDavid du Colombier 		return (cc&N) == 0;
2585d9de2d3SDavid du Colombier 	case 6:	/* V set */
2595d9de2d3SDavid du Colombier 		return cc&V;
2605d9de2d3SDavid du Colombier 	case 7:	/* V clear */
2615d9de2d3SDavid du Colombier 		return (cc&V) == 0;
2625d9de2d3SDavid du Colombier 	case 8:	/* C set and Z clear */
2635d9de2d3SDavid du Colombier 		return cc&C && (cc&Z) == 0;
2645d9de2d3SDavid du Colombier 	case 9:	/* C clear or Z set */
2655d9de2d3SDavid du Colombier 		return (cc&C) == 0 || cc&Z;
2665d9de2d3SDavid du Colombier 	case 10:	/* N set and V set, or N clear and V clear */
2675d9de2d3SDavid du Colombier 		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
2685d9de2d3SDavid du Colombier 	case 11:	/* N set and V clear, or N clear and V set */
2695d9de2d3SDavid du Colombier 		return (cc&(N|V))==N || (cc&(N|V))==V;
2705d9de2d3SDavid du Colombier 	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
2715d9de2d3SDavid du Colombier 		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
2725d9de2d3SDavid du Colombier 	case 13:	/* Z set, or N set and V clear or N clear and V set */
2735d9de2d3SDavid du Colombier 		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
2745d9de2d3SDavid du Colombier 	case 14:	/* always */
2755d9de2d3SDavid du Colombier 		return 1;
2765d9de2d3SDavid du Colombier 	case 15:	/* never (reserved) */
2775d9de2d3SDavid du Colombier 		return 0;
2785d9de2d3SDavid du Colombier 	}
2795d9de2d3SDavid du Colombier 	return 0;	/* not reached */
2805d9de2d3SDavid du Colombier }
2815d9de2d3SDavid du Colombier 
2825d9de2d3SDavid du Colombier static void
unimp(ulong pc,ulong op)2835d9de2d3SDavid du Colombier unimp(ulong pc, ulong op)
2845d9de2d3SDavid du Colombier {
2855d9de2d3SDavid du Colombier 	char buf[60];
2865d9de2d3SDavid du Colombier 
2875d9de2d3SDavid du Colombier 	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
2885d9de2d3SDavid du Colombier 	if(fpemudebug)
2895d9de2d3SDavid du Colombier 		print("FPE: %s\n", buf);
2905d9de2d3SDavid du Colombier 	error(buf);
2915d9de2d3SDavid du Colombier 	/* no return */
2925d9de2d3SDavid du Colombier }
2935d9de2d3SDavid du Colombier 
2945d9de2d3SDavid du Colombier static void
fpemu(ulong pc,ulong op,Ureg * ur,FPsave * ufp)2955d9de2d3SDavid du Colombier fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
2965d9de2d3SDavid du Colombier {
2975d9de2d3SDavid du Colombier 	int rn, rd, tag, o;
2985d9de2d3SDavid du Colombier 	long off;
2995d9de2d3SDavid du Colombier 	ulong ea;
3005d9de2d3SDavid du Colombier 	Internal tmp, *fm, *fn;
3015d9de2d3SDavid du Colombier 
3025d9de2d3SDavid du Colombier 	/* note: would update fault status here if we noted numeric exceptions */
3035d9de2d3SDavid du Colombier 
3045d9de2d3SDavid du Colombier 	/*
3055d9de2d3SDavid du Colombier 	 * LDF, STF; 10.1.1
3065d9de2d3SDavid du Colombier 	 */
3075d9de2d3SDavid du Colombier 	if(((op>>25)&7) == 6){
3085d9de2d3SDavid du Colombier 		if(op & (1<<22))
3095d9de2d3SDavid du Colombier 			unimp(pc, op);	/* packed or extended */
3105d9de2d3SDavid du Colombier 		rn = (op>>16)&0xF;
3115d9de2d3SDavid du Colombier 		off = (op&0xFF)<<2;
3125d9de2d3SDavid du Colombier 		if((op & (1<<23)) == 0)
3135d9de2d3SDavid du Colombier 			off = -off;
3145d9de2d3SDavid du Colombier 		ea = REG(ur, rn);
3155d9de2d3SDavid du Colombier 		if(rn == REGPC)
3165d9de2d3SDavid du Colombier 			ea += 8;
3175d9de2d3SDavid du Colombier 		if(op & (1<<24))
3185d9de2d3SDavid du Colombier 			ea += off;
3195d9de2d3SDavid du Colombier 		rd = (op>>12)&7;
3205d9de2d3SDavid du Colombier 		if(op & (1<<20)){
3215d9de2d3SDavid du Colombier 			if(op & (1<<15))
3225d9de2d3SDavid du Colombier 				fld(fpid2i, rd, ea, 8, ufp);
3235d9de2d3SDavid du Colombier 			else
3245d9de2d3SDavid du Colombier 				fld(fpis2i, rd, ea, 4, ufp);
3255d9de2d3SDavid du Colombier 		}else{
3265d9de2d3SDavid du Colombier 			if(op & (1<<15))
3275d9de2d3SDavid du Colombier 				fst(fpii2d, ea, rd, 8, ufp);
3285d9de2d3SDavid du Colombier 			else
3295d9de2d3SDavid du Colombier 				fst(fpii2s, ea, rd, 4, ufp);
3305d9de2d3SDavid du Colombier 		}
3315d9de2d3SDavid du Colombier 		if((op & (1<<24)) == 0)
3325d9de2d3SDavid du Colombier 			ea += off;
3335d9de2d3SDavid du Colombier 		if(op & (1<<21))
3345d9de2d3SDavid du Colombier 			REG(ur, rn) = ea;
3355d9de2d3SDavid du Colombier 		return;
3365d9de2d3SDavid du Colombier 	}
3375d9de2d3SDavid du Colombier 
3385d9de2d3SDavid du Colombier 	/*
3395d9de2d3SDavid du Colombier 	 * CPRT/transfer, 10.3
3405d9de2d3SDavid du Colombier 	 */
3415d9de2d3SDavid du Colombier 	if(op & (1<<4)){
3425d9de2d3SDavid du Colombier 		rd = (op>>12) & 0xF;
3435d9de2d3SDavid du Colombier 
3445d9de2d3SDavid du Colombier 		/*
3455d9de2d3SDavid du Colombier 		 * compare, 10.3.1
3465d9de2d3SDavid du Colombier 		 */
3475d9de2d3SDavid du Colombier 		if(rd == 15 && op & (1<<20)){
3485d9de2d3SDavid du Colombier 			rn = (op>>16)&7;
3495d9de2d3SDavid du Colombier 			fn = &FR(ufp, rn);
3505d9de2d3SDavid du Colombier 			if(op & (1<<3)){
3515d9de2d3SDavid du Colombier 				fm = &fpconst[op&7];
3525d9de2d3SDavid du Colombier 				if(fpemudebug)
3535d9de2d3SDavid du Colombier 					tag = 'C';
3545d9de2d3SDavid du Colombier 			}else{
3555d9de2d3SDavid du Colombier 				fm = &FR(ufp, op&7);
3565d9de2d3SDavid du Colombier 				if(fpemudebug)
3575d9de2d3SDavid du Colombier 					tag = 'F';
3585d9de2d3SDavid du Colombier 			}
3595d9de2d3SDavid du Colombier 			switch((op>>21)&7){
3605d9de2d3SDavid du Colombier 			default:
3615d9de2d3SDavid du Colombier 				unimp(pc, op);
3625d9de2d3SDavid du Colombier 			case 4:	/* CMF: Fn :: Fm */
3635d9de2d3SDavid du Colombier 			case 6:	/* CMFE: Fn :: Fm (with exception) */
3645d9de2d3SDavid du Colombier 				ur->psr &= ~(N|C|Z|V);
3655d9de2d3SDavid du Colombier 				ur->psr |= fcmp(fn, fm);
3665d9de2d3SDavid du Colombier 				break;
3675d9de2d3SDavid du Colombier 			case 5:	/* CNF: Fn :: -Fm */
3685d9de2d3SDavid du Colombier 			case 7:	/* CNFE: Fn :: -Fm (with exception) */
3695d9de2d3SDavid du Colombier 				tmp = *fm;
3705d9de2d3SDavid du Colombier 				tmp.s ^= 1;
3715d9de2d3SDavid du Colombier 				ur->psr &= ~(N|C|Z|V);
3725d9de2d3SDavid du Colombier 				ur->psr |= fcmp(fn, &tmp);
3735d9de2d3SDavid du Colombier 				break;
3745d9de2d3SDavid du Colombier 			}
3755d9de2d3SDavid du Colombier 			if(fpemudebug)
3765d9de2d3SDavid du Colombier 				print("CMPF	%c%d,F%ld =%#lux\n",
3775d9de2d3SDavid du Colombier 					tag, rn, op&7, ur->psr>>28);
3785d9de2d3SDavid du Colombier 			return;
3795d9de2d3SDavid du Colombier 		}
3805d9de2d3SDavid du Colombier 
3815d9de2d3SDavid du Colombier 		/*
3825d9de2d3SDavid du Colombier 		 * other transfer, 10.3
3835d9de2d3SDavid du Colombier 		 */
3845d9de2d3SDavid du Colombier 		switch((op>>20)&0xF){
3855d9de2d3SDavid du Colombier 		default:
3865d9de2d3SDavid du Colombier 			unimp(pc, op);
3875d9de2d3SDavid du Colombier 		case 0:	/* FLT */
3885d9de2d3SDavid du Colombier 			rn = (op>>16) & 7;
3895d9de2d3SDavid du Colombier 			fpiw2i(&FR(ufp, rn), &REG(ur, rd));
3905d9de2d3SDavid du Colombier 			if(fpemudebug)
3915d9de2d3SDavid du Colombier 				print("MOVW[FD]	R%d, F%d\n", rd, rn);
3925d9de2d3SDavid du Colombier 			break;
3935d9de2d3SDavid du Colombier 		case 1:	/* FIX */
3945d9de2d3SDavid du Colombier 			if(op & (1<<3))
3955d9de2d3SDavid du Colombier 				unimp(pc, op);
3965d9de2d3SDavid du Colombier 			rn = op & 7;
3975d9de2d3SDavid du Colombier 			tmp = FR(ufp, rn);
3985d9de2d3SDavid du Colombier 			fpii2w(&REG(ur, rd), &tmp);
3995d9de2d3SDavid du Colombier 			if(fpemudebug)
4005d9de2d3SDavid du Colombier 				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
4015d9de2d3SDavid du Colombier 			break;
4025d9de2d3SDavid du Colombier 		case 2:	/* FPSR := Rd */
4035d9de2d3SDavid du Colombier 			ufp->status = REG(ur, rd);
4045d9de2d3SDavid du Colombier 			if(fpemudebug)
4055d9de2d3SDavid du Colombier 				print("MOVW	R%d, FPSR\n", rd);
4065d9de2d3SDavid du Colombier 			break;
4075d9de2d3SDavid du Colombier 		case 3:	/* Rd := FPSR */
4085d9de2d3SDavid du Colombier 			REG(ur, rd) = ufp->status;
4095d9de2d3SDavid du Colombier 			if(fpemudebug)
4105d9de2d3SDavid du Colombier 				print("MOVW	FPSR, R%d\n", rd);
4115d9de2d3SDavid du Colombier 			break;
4125d9de2d3SDavid du Colombier 		case 4:	/* FPCR := Rd */
4135d9de2d3SDavid du Colombier 			ufp->control = REG(ur, rd);
4145d9de2d3SDavid du Colombier 			if(fpemudebug)
4155d9de2d3SDavid du Colombier 				print("MOVW	R%d, FPCR\n", rd);
4165d9de2d3SDavid du Colombier 			break;
4175d9de2d3SDavid du Colombier 		case 5:	/* Rd := FPCR */
4185d9de2d3SDavid du Colombier 			REG(ur, rd) = ufp->control;
4195d9de2d3SDavid du Colombier 			if(fpemudebug)
4205d9de2d3SDavid du Colombier 				print("MOVW	FPCR, R%d\n", rd);
4215d9de2d3SDavid du Colombier 			break;
4225d9de2d3SDavid du Colombier 		}
4235d9de2d3SDavid du Colombier 		return;
4245d9de2d3SDavid du Colombier 	}
4255d9de2d3SDavid du Colombier 
4265d9de2d3SDavid du Colombier 	/*
4275d9de2d3SDavid du Colombier 	 * arithmetic
4285d9de2d3SDavid du Colombier 	 */
4295d9de2d3SDavid du Colombier 
4305d9de2d3SDavid du Colombier 	if(op & (1<<3)){	/* constant */
4315d9de2d3SDavid du Colombier 		fm = &fpconst[op&7];
4325d9de2d3SDavid du Colombier 		if(fpemudebug)
4335d9de2d3SDavid du Colombier 			tag = 'C';
4345d9de2d3SDavid du Colombier 	}else{
4355d9de2d3SDavid du Colombier 		fm = &FR(ufp, op&7);
4365d9de2d3SDavid du Colombier 		if(fpemudebug)
4375d9de2d3SDavid du Colombier 			tag = 'F';
4385d9de2d3SDavid du Colombier 	}
4395d9de2d3SDavid du Colombier 	rd = (op>>12)&7;
4405d9de2d3SDavid du Colombier 	o = (op>>20)&0xF;
4415d9de2d3SDavid du Colombier 	if(op & (1<<15)){	/* monadic */
4425d9de2d3SDavid du Colombier 		FP1 *fp;
4435d9de2d3SDavid du Colombier 		fp = &optab1[o];
4445d9de2d3SDavid du Colombier 		if(fp->f == nil)
4455d9de2d3SDavid du Colombier 			unimp(pc, op);
4465d9de2d3SDavid du Colombier 		if(fpemudebug)
4475d9de2d3SDavid du Colombier 			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
4485d9de2d3SDavid du Colombier 		(*fp->f)(fm, &FR(ufp, rd));
4495d9de2d3SDavid du Colombier 	} else {
4505d9de2d3SDavid du Colombier 		FP2 *fp;
4515d9de2d3SDavid du Colombier 		fp = &optab2[o];
4525d9de2d3SDavid du Colombier 		if(fp->f == nil)
4535d9de2d3SDavid du Colombier 			unimp(pc, op);
4545d9de2d3SDavid du Colombier 		rn = (op>>16)&7;
4555d9de2d3SDavid du Colombier 		if(fpemudebug)
4565d9de2d3SDavid du Colombier 			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
4575d9de2d3SDavid du Colombier 		(*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
4585d9de2d3SDavid du Colombier 	}
4595d9de2d3SDavid du Colombier }
4605d9de2d3SDavid du Colombier 
4615d9de2d3SDavid du Colombier /*
4625d9de2d3SDavid du Colombier  * returns the number of FP instructions emulated
4635d9de2d3SDavid du Colombier  */
4645d9de2d3SDavid du Colombier int
fpiarm(Ureg * ur)4655d9de2d3SDavid du Colombier fpiarm(Ureg *ur)
4665d9de2d3SDavid du Colombier {
4675d9de2d3SDavid du Colombier 	ulong op, o, cp;
4685d9de2d3SDavid du Colombier 	FPsave *ufp;
4695d9de2d3SDavid du Colombier 	int n;
4705d9de2d3SDavid du Colombier 
4715d9de2d3SDavid du Colombier 	if(up == nil)
4725d9de2d3SDavid du Colombier 		panic("fpiarm not in a process");
4735d9de2d3SDavid du Colombier 	ufp = &up->fpsave;
4745d9de2d3SDavid du Colombier 	/*
4755d9de2d3SDavid du Colombier 	 * because all the emulated fp state is in the proc structure,
4765d9de2d3SDavid du Colombier 	 * it need not be saved/restored
4775d9de2d3SDavid du Colombier 	 */
4785d9de2d3SDavid du Colombier 	switch(up->fpstate){
4795d9de2d3SDavid du Colombier 	case FPactive:
4805d9de2d3SDavid du Colombier 	case FPinactive:
4815d9de2d3SDavid du Colombier 		error("illegal instruction: emulated fpu opcode in VFP mode");
4825d9de2d3SDavid du Colombier 	case FPinit:
4835d9de2d3SDavid du Colombier 		assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
4845d9de2d3SDavid du Colombier 		up->fpstate = FPemu;
4855d9de2d3SDavid du Colombier 		ufp->control = 0;
4865d9de2d3SDavid du Colombier 		ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
4875d9de2d3SDavid du Colombier 		for(n = 0; n < 8; n++)
4885d9de2d3SDavid du Colombier 			FR(ufp, n) = fpconst[0];
4895d9de2d3SDavid du Colombier 	}
4905d9de2d3SDavid du Colombier 	for(n=0; ;n++){
4915d9de2d3SDavid du Colombier 		validaddr(ur->pc, 4, 0);
4925d9de2d3SDavid du Colombier 		op = *(ulong*)(ur->pc);
4935d9de2d3SDavid du Colombier 		if(fpemudebug)
4945d9de2d3SDavid du Colombier 			print("%#lux: %#8.8lux ", ur->pc, op);
4955d9de2d3SDavid du Colombier 		o = (op>>24) & 0xF;
4965d9de2d3SDavid du Colombier 		cp = (op>>8) & 0xF;
4975d9de2d3SDavid du Colombier 		if(!ISFPAOP(cp, o))
4985d9de2d3SDavid du Colombier 			break;
4995d9de2d3SDavid du Colombier 		if(condok(ur->psr, op>>28))
5005d9de2d3SDavid du Colombier 			fpemu(ur->pc, op, ur, ufp);
5015d9de2d3SDavid du Colombier 		ur->pc += 4;		/* pretend cpu executed the instr */
5025d9de2d3SDavid du Colombier 	}
5035d9de2d3SDavid du Colombier 	if(fpemudebug)
5045d9de2d3SDavid du Colombier 		print("\n");
5055d9de2d3SDavid du Colombier 	return n;
5065d9de2d3SDavid du Colombier }
507