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