xref: /plan9-contrib/sys/src/9/bcm/fpiarm.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
1 /*
2  * this doesn't attempt to implement ARM floating-point properties
3  * that aren't visible in the Inferno environment.
4  * all arithmetic is done in double precision.
5  * the FP trap status isn't updated.
6  */
7 #include	"u.h"
8 #include	"../port/lib.h"
9 #include	"mem.h"
10 #include	"dat.h"
11 #include	"fns.h"
12 
13 #include	"ureg.h"
14 
15 #include	"arm.h"
16 #include	"../port/fpi.h"
17 
18 #define ARM7500			/* emulate old pre-VFP opcodes */
19 
20 /* undef this if correct kernel r13 isn't in Ureg;
21  * check calculation in fpiarm below
22  */
23 
24 #define	REG(ur, x) (*(long*)(((char*)(ur))+roff[(x)]))
25 #ifdef ARM7500
26 #define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&7])
27 #else
28 #define	FR(ufp, x) (*(Internal*)(ufp)->regs[(x)&(Nfpregs - 1)])
29 #endif
30 
31 typedef struct FP2 FP2;
32 typedef struct FP1 FP1;
33 
34 struct FP2 {
35 	char*	name;
36 	void	(*f)(Internal, Internal, Internal*);
37 };
38 
39 struct FP1 {
40 	char*	name;
41 	void	(*f)(Internal*, Internal*);
42 };
43 
44 enum {
45 	N = 1<<31,
46 	Z = 1<<30,
47 	C = 1<<29,
48 	V = 1<<28,
49 	REGPC = 15,
50 };
51 
52 enum {
53 	fpemudebug = 0,
54 };
55 
56 #undef OFR
57 #define	OFR(X)	((ulong)&((Ureg*)0)->X)
58 
59 static	int	roff[] = {
60 	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
61 	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
62 	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
63 	OFR(r12), OFR(r13), OFR(r14), OFR(pc),
64 };
65 
66 static Internal fpconst[8] = {		/* indexed by op&7 (ARM 7500 FPA) */
67 	/* s, e, l, h */
68 	{0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
69 	{0, 0x3FF, 0x00000000, 0x08000000},	/* 1.0 */
70 	{0, 0x400, 0x00000000, 0x08000000},	/* 2.0 */
71 	{0, 0x400, 0x00000000, 0x0C000000},	/* 3.0 */
72 	{0, 0x401, 0x00000000, 0x08000000},	/* 4.0 */
73 	{0, 0x401, 0x00000000, 0x0A000000},	/* 5.0 */
74 	{0, 0x3FE, 0x00000000, 0x08000000},	/* 0.5 */
75 	{0, 0x402, 0x00000000, 0x0A000000},	/* 10.0 */
76 };
77 
78 /*
79  * arm binary operations
80  */
81 
82 static void
fadd(Internal m,Internal n,Internal * d)83 fadd(Internal m, Internal n, Internal *d)
84 {
85 	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
86 }
87 
88 static void
fsub(Internal m,Internal n,Internal * d)89 fsub(Internal m, Internal n, Internal *d)
90 {
91 	m.s ^= 1;
92 	(m.s == n.s? fpiadd: fpisub)(&m, &n, d);
93 }
94 
95 static void
fsubr(Internal m,Internal n,Internal * d)96 fsubr(Internal m, Internal n, Internal *d)
97 {
98 	n.s ^= 1;
99 	(n.s == m.s? fpiadd: fpisub)(&n, &m, d);
100 }
101 
102 static void
fmul(Internal m,Internal n,Internal * d)103 fmul(Internal m, Internal n, Internal *d)
104 {
105 	fpimul(&m, &n, d);
106 }
107 
108 static void
fdiv(Internal m,Internal n,Internal * d)109 fdiv(Internal m, Internal n, Internal *d)
110 {
111 	fpidiv(&m, &n, d);
112 }
113 
114 static void
fdivr(Internal m,Internal n,Internal * d)115 fdivr(Internal m, Internal n, Internal *d)
116 {
117 	fpidiv(&n, &m, d);
118 }
119 
120 /*
121  * arm unary operations
122  */
123 
124 static void
fmov(Internal * m,Internal * d)125 fmov(Internal *m, Internal *d)
126 {
127 	*d = *m;
128 }
129 
130 static void
fmovn(Internal * m,Internal * d)131 fmovn(Internal *m, Internal *d)
132 {
133 	*d = *m;
134 	d->s ^= 1;
135 }
136 
137 static void
fabsf(Internal * m,Internal * d)138 fabsf(Internal *m, Internal *d)
139 {
140 	*d = *m;
141 	d->s = 0;
142 }
143 
144 static void
frnd(Internal * m,Internal * d)145 frnd(Internal *m, Internal *d)
146 {
147 	short e;
148 
149 	(m->s? fsub: fadd)(fpconst[6], *m, d);
150 	if(IsWeird(d))
151 		return;
152 	fpiround(d);
153 	e = (d->e - ExpBias) + 1;
154 	if(e <= 0)
155 		SetZero(d);
156 	else if(e > FractBits){
157 		if(e < 2*FractBits)
158 			d->l &= ~((1<<(2*FractBits - e))-1);
159 	}else{
160 		d->l = 0;
161 		if(e < FractBits)
162 			d->h &= ~((1<<(FractBits-e))-1);
163 	}
164 }
165 
166 /*
167  * ARM 7500 FPA opcodes
168  */
169 
170 static	FP1	optab1[16] = {	/* Fd := OP Fm */
171 [0]	{"MOVF",	fmov},
172 [1]	{"NEGF",	fmovn},
173 [2]	{"ABSF",	fabsf},
174 [3]	{"RNDF",	frnd},
175 [4]	{"SQTF",	/*fsqt*/0},
176 /* LOG, LGN, EXP, SIN, COS, TAN, ASN, ACS, ATN all `deprecated' */
177 /* URD and NRM aren't implemented */
178 };
179 
180 static	FP2	optab2[16] = {	/* Fd := Fn OP Fm */
181 [0]	{"ADDF",	fadd},
182 [1]	{"MULF",	fmul},
183 [2]	{"SUBF",	fsub},
184 [3]	{"RSUBF",	fsubr},
185 [4]	{"DIVF",	fdiv},
186 [5]	{"RDIVF",	fdivr},
187 /* POW, RPW deprecated */
188 [8]	{"REMF",	/*frem*/0},
189 [9]	{"FMF",	fmul},	/* fast multiply */
190 [10]	{"FDV",	fdiv},	/* fast divide */
191 [11]	{"FRD",	fdivr},	/* fast reverse divide */
192 /* POL deprecated */
193 };
194 
195 static ulong
fcmp(Internal * n,Internal * m)196 fcmp(Internal *n, Internal *m)
197 {
198 	int i;
199 	Internal rm, rn;
200 
201 	if(IsWeird(m) || IsWeird(n)){
202 		/* BUG: should trap if not masked */
203 		return V|C;
204 	}
205 	rn = *n;
206 	rm = *m;
207 	fpiround(&rn);
208 	fpiround(&rm);
209 	i = fpicmp(&rn, &rm);
210 	if(i > 0)
211 		return C;
212 	else if(i == 0)
213 		return C|Z;
214 	else
215 		return N;
216 }
217 
218 static void
fld(void (* f)(Internal *,void *),int d,ulong ea,int n,FPsave * ufp)219 fld(void (*f)(Internal*, void*), int d, ulong ea, int n, FPsave *ufp)
220 {
221 	void *mem;
222 
223 	validaddr(ea, n, 0);
224 	mem = (void*)ea;
225 	(*f)(&FR(ufp, d), mem);
226 	if(fpemudebug)
227 		print("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
228 }
229 
230 static void
fst(void (* f)(void *,Internal *),ulong ea,int s,int n,FPsave * ufp)231 fst(void (*f)(void*, Internal*), ulong ea, int s, int n, FPsave *ufp)
232 {
233 	Internal tmp;
234 	void *mem;
235 
236 	validaddr(ea, n, 1);
237 	mem = (void*)ea;
238 	tmp = FR(ufp, s);
239 	if(fpemudebug)
240 		print("MOV%c	F%d,#%lux\n", n==8? 'D': 'F', s, ea);
241 	(*f)(mem, &tmp);
242 }
243 
244 static int
condok(int cc,int c)245 condok(int cc, int c)
246 {
247 	switch(c){
248 	case 0:	/* Z set */
249 		return cc&Z;
250 	case 1:	/* Z clear */
251 		return (cc&Z) == 0;
252 	case 2:	/* C set */
253 		return cc&C;
254 	case 3:	/* C clear */
255 		return (cc&C) == 0;
256 	case 4:	/* N set */
257 		return cc&N;
258 	case 5:	/* N clear */
259 		return (cc&N) == 0;
260 	case 6:	/* V set */
261 		return cc&V;
262 	case 7:	/* V clear */
263 		return (cc&V) == 0;
264 	case 8:	/* C set and Z clear */
265 		return cc&C && (cc&Z) == 0;
266 	case 9:	/* C clear or Z set */
267 		return (cc&C) == 0 || cc&Z;
268 	case 10:	/* N set and V set, or N clear and V clear */
269 		return (~cc&(N|V))==0 || (cc&(N|V)) == 0;
270 	case 11:	/* N set and V clear, or N clear and V set */
271 		return (cc&(N|V))==N || (cc&(N|V))==V;
272 	case 12:	/* Z clear, and either N set and V set or N clear and V clear */
273 		return (cc&Z) == 0 && ((~cc&(N|V))==0 || (cc&(N|V))==0);
274 	case 13:	/* Z set, or N set and V clear or N clear and V set */
275 		return (cc&Z) || (cc&(N|V))==N || (cc&(N|V))==V;
276 	case 14:	/* always */
277 		return 1;
278 	case 15:	/* never (reserved) */
279 		return 0;
280 	}
281 	return 0;	/* not reached */
282 }
283 
284 static void
unimp(ulong pc,ulong op)285 unimp(ulong pc, ulong op)
286 {
287 	char buf[60];
288 
289 	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", pc, op);
290 	if(fpemudebug)
291 		print("FPE: %s\n", buf);
292 	error(buf);
293 	/* no return */
294 }
295 
296 static void
fpemu(ulong pc,ulong op,Ureg * ur,FPsave * ufp)297 fpemu(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
298 {
299 	int rn, rd, tag, o;
300 	long off;
301 	ulong ea;
302 	Internal tmp, *fm, *fn;
303 
304 	/* note: would update fault status here if we noted numeric exceptions */
305 
306 	/*
307 	 * LDF, STF; 10.1.1
308 	 */
309 	if(((op>>25)&7) == 6){
310 		if(op & (1<<22))
311 			unimp(pc, op);	/* packed or extended */
312 		rn = (op>>16)&0xF;
313 		off = (op&0xFF)<<2;
314 		if((op & (1<<23)) == 0)
315 			off = -off;
316 		ea = REG(ur, rn);
317 		if(rn == REGPC)
318 			ea += 8;
319 		if(op & (1<<24))
320 			ea += off;
321 		rd = (op>>12)&7;
322 		if(op & (1<<20)){
323 			if(op & (1<<15))
324 				fld(fpid2i, rd, ea, 8, ufp);
325 			else
326 				fld(fpis2i, rd, ea, 4, ufp);
327 		}else{
328 			if(op & (1<<15))
329 				fst(fpii2d, ea, rd, 8, ufp);
330 			else
331 				fst(fpii2s, ea, rd, 4, ufp);
332 		}
333 		if((op & (1<<24)) == 0)
334 			ea += off;
335 		if(op & (1<<21))
336 			REG(ur, rn) = ea;
337 		return;
338 	}
339 
340 	/*
341 	 * CPRT/transfer, 10.3
342 	 */
343 	if(op & (1<<4)){
344 		rd = (op>>12) & 0xF;
345 
346 		/*
347 		 * compare, 10.3.1
348 		 */
349 		if(rd == 15 && op & (1<<20)){
350 			rn = (op>>16)&7;
351 			fn = &FR(ufp, rn);
352 			if(op & (1<<3)){
353 				fm = &fpconst[op&7];
354 				if(fpemudebug)
355 					tag = 'C';
356 			}else{
357 				fm = &FR(ufp, op&7);
358 				if(fpemudebug)
359 					tag = 'F';
360 			}
361 			switch((op>>21)&7){
362 			default:
363 				unimp(pc, op);
364 			case 4:	/* CMF: Fn :: Fm */
365 			case 6:	/* CMFE: Fn :: Fm (with exception) */
366 				ur->psr &= ~(N|C|Z|V);
367 				ur->psr |= fcmp(fn, fm);
368 				break;
369 			case 5:	/* CNF: Fn :: -Fm */
370 			case 7:	/* CNFE: Fn :: -Fm (with exception) */
371 				tmp = *fm;
372 				tmp.s ^= 1;
373 				ur->psr &= ~(N|C|Z|V);
374 				ur->psr |= fcmp(fn, &tmp);
375 				break;
376 			}
377 			if(fpemudebug)
378 				print("CMPF	%c%d,F%ld =%#lux\n",
379 					tag, rn, op&7, ur->psr>>28);
380 			return;
381 		}
382 
383 		/*
384 		 * other transfer, 10.3
385 		 */
386 		switch((op>>20)&0xF){
387 		default:
388 			unimp(pc, op);
389 		case 0:	/* FLT */
390 			rn = (op>>16) & 7;
391 			fpiw2i(&FR(ufp, rn), &REG(ur, rd));
392 			if(fpemudebug)
393 				print("MOVW[FD]	R%d, F%d\n", rd, rn);
394 			break;
395 		case 1:	/* FIX */
396 			if(op & (1<<3))
397 				unimp(pc, op);
398 			rn = op & 7;
399 			tmp = FR(ufp, rn);
400 			fpii2w(&REG(ur, rd), &tmp);
401 			if(fpemudebug)
402 				print("MOV[FD]W	F%d, R%d =%ld\n", rn, rd, REG(ur, rd));
403 			break;
404 		case 2:	/* FPSR := Rd */
405 			ufp->status = REG(ur, rd);
406 			if(fpemudebug)
407 				print("MOVW	R%d, FPSR\n", rd);
408 			break;
409 		case 3:	/* Rd := FPSR */
410 			REG(ur, rd) = ufp->status;
411 			if(fpemudebug)
412 				print("MOVW	FPSR, R%d\n", rd);
413 			break;
414 		case 4:	/* FPCR := Rd */
415 			ufp->control = REG(ur, rd);
416 			if(fpemudebug)
417 				print("MOVW	R%d, FPCR\n", rd);
418 			break;
419 		case 5:	/* Rd := FPCR */
420 			REG(ur, rd) = ufp->control;
421 			if(fpemudebug)
422 				print("MOVW	FPCR, R%d\n", rd);
423 			break;
424 		}
425 		return;
426 	}
427 
428 	/*
429 	 * arithmetic
430 	 */
431 
432 	if(op & (1<<3)){	/* constant */
433 		fm = &fpconst[op&7];
434 		if(fpemudebug)
435 			tag = 'C';
436 	}else{
437 		fm = &FR(ufp, op&7);
438 		if(fpemudebug)
439 			tag = 'F';
440 	}
441 	rd = (op>>12)&7;
442 	o = (op>>20)&0xF;
443 	if(op & (1<<15)){	/* monadic */
444 		FP1 *fp;
445 		fp = &optab1[o];
446 		if(fp->f == nil)
447 			unimp(pc, op);
448 		if(fpemudebug)
449 			print("%s	%c%ld,F%d\n", fp->name, tag, op&7, rd);
450 		(*fp->f)(fm, &FR(ufp, rd));
451 	} else {
452 		FP2 *fp;
453 		fp = &optab2[o];
454 		if(fp->f == nil)
455 			unimp(pc, op);
456 		rn = (op>>16)&7;
457 		if(fpemudebug)
458 			print("%s	%c%ld,F%d,F%d\n", fp->name, tag, op&7, rn, rd);
459 		(*fp->f)(*fm, FR(ufp, rn), &FR(ufp, rd));
460 	}
461 }
462 
463 /*
464  * returns the number of FP instructions emulated
465  */
466 int
fpiarm(Ureg * ur)467 fpiarm(Ureg *ur)
468 {
469 	ulong op, o, cp;
470 	FPsave *ufp;
471 	int n;
472 
473 	if(up == nil)
474 		panic("fpiarm not in a process");
475 	ufp = &up->fpsave;
476 	/*
477 	 * because all the emulated fp state is in the proc structure,
478 	 * it need not be saved/restored
479 	 */
480 	switch(up->fpstate){
481 	case FPactive:
482 	case FPinactive:
483 		error("illegal instruction: emulated fpu opcode in VFP mode");
484 	case FPinit:
485 		assert(sizeof(Internal) <= sizeof(ufp->regs[0]));
486 		up->fpstate = FPemu;
487 		ufp->control = 0;
488 		ufp->status = (0x01<<28)|(1<<12); /* sw emulation, alt. C flag */
489 		for(n = 0; n < 8; n++)
490 			FR(ufp, n) = fpconst[0];
491 	}
492 	for(n=0; ;n++){
493 		validaddr(ur->pc, 4, 0);
494 		op = *(ulong*)(ur->pc);
495 		if(fpemudebug)
496 			print("%#lux: %#8.8lux ", ur->pc, op);
497 		o = (op>>24) & 0xF;
498 		cp = (op>>8) & 0xF;
499 		if(!ISFPAOP(cp, o))
500 			break;
501 		if(condok(ur->psr, op>>28))
502 			fpemu(ur->pc, op, ur, ufp);
503 		ur->pc += 4;		/* pretend cpu executed the instr */
504 	}
505 	if(fpemudebug)
506 		print("\n");
507 	return n;
508 }
509