xref: /plan9-contrib/sys/src/9/vt4/fpipower.c (revision d6dfd9ef91cf0fa8514a249d5f2a550978c19369)
1 /*
2  * this doesn't attempt to implement Power architecture 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	"fpi.h"
16 
17 #define	REG(x) (*(long*)(((char*)em->ur)+roff[(x)]))
18 #define	FR(x) (*(Internal*)em->fr[(x)&0x1F])
19 #define	REGSP	0	/* in Plan 9, the user and kernel stacks are different */
20 
21 #define	getulong(a) (*(ulong*)(a))
22 
23 enum {
24 	CRLT = 1<<31,
25 	CRGT = 1<<30,
26 	CREQ = 1<<29,
27 	CRSO = 1<<28,
28 	CRFU = CRSO,
29 
30 	CRFX = 1<<27,
31 	CRFEX = 1<<26,
32 	CRVX = 1<<25,
33 	CROX = 1<<24,
34 };
35 
36 #define getCR(x,w) (((w)>>(28-(x*4)))&0xF)
37 #define mkCR(x,v) (((v)&0xF)<<(28-(x*4)))
38 
39 #define simm(xx, ii)	xx = (short)(ii&0xFFFF);
40 #define getairr(i)	rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; simm(imm,i)
41 #define getarrr(i)	rd = (i>>21)&0x1f; ra = (i>>16)&0x1f; rb = (i>>11)&0x1f;
42 #define getop(i) ((i>>26)&0x3F)
43 #define getxo(i) ((i>>1)&0x3FF)
44 
45 #define	FPS_FX	(1<<31)	/* exception summary (sticky) */
46 #define	FPS_EX	(1<<30)	/* enabled exception summary */
47 #define	FPS_VX	(1<<29)	/* invalid operation exception summary */
48 #define	FPS_OX	(1<<28)	/* overflow exception OX (sticky) */
49 #define	FPS_UX	(1<<27)	/* underflow exception UX (sticky) */
50 #define	FPS_ZX	(1<<26)	/* zero divide exception ZX (sticky) */
51 #define	FPS_XX	(1<<25)	/* inexact exception XX (sticky) */
52 #define	FPS_VXSNAN (1<<24)	/* invalid operation exception for SNaN (sticky) */
53 #define	FPS_VXISI	(1<<23)	/* invalid operation exception for ∞-∞ (sticky) */
54 #define	FPS_VXIDI	(1<<22)	/* invalid operation exception for ∞/∞ (sticky) */
55 #define	FPS_VXZDZ (1<<21)	/* invalid operation exception for 0/0 (sticky) */
56 #define	FPS_VXIMZ	(1<<20)	/* invalid operation exception for ∞*0 (sticky) */
57 #define	FPS_VXVC	(1<<19)	/* invalid operation exception for invalid compare (sticky) */
58 #define	FPS_FR	(1<<18)	/* fraction rounded */
59 #define	FPS_FI	(1<<17)	/* fraction inexact */
60 #define	FPS_FPRF	(1<<16)	/* floating point result class */
61 #define	FPS_FPCC	(0xF<<12)	/* <, >, =, unordered */
62 #define	FPS_VXCVI	(1<<8)	/* enable exception for invalid integer convert (sticky) */
63 #define	FPS_VE	(1<<7)	/* invalid operation exception enable */
64 #define	FPS_OE	(1<<6)	/* enable overflow exceptions */
65 #define	FPS_UE	(1<<5)	/* enable underflow */
66 #define	FPS_ZE	(1<<4)	/* enable zero divide */
67 #define	FPS_XE	(1<<3)	/* enable inexact exceptions */
68 #define	FPS_RN	(3<<0)	/* rounding mode */
69 
70 typedef struct Emreg Emreg;
71 
72 struct Emreg {
73 	Ureg*	ur;
74 	ulong	(*fr)[3];
75 	FPsave*	ufp;
76 	ulong	ir;
77 	char*	name;
78 };
79 
80 int	fpemudebug = 0;
81 
82 #undef OFR
83 #define	OFR(X)	((ulong)&((Ureg*)0)->X)
84 
85 static	int	roff[] = {
86 	OFR(r0), OFR(r1), OFR(r2), OFR(r3),
87 	OFR(r4), OFR(r5), OFR(r6), OFR(r7),
88 	OFR(r8), OFR(r9), OFR(r10), OFR(r11),
89 	OFR(r12), OFR(r13), OFR(r14), OFR(r15),
90 	OFR(r16), OFR(r17), OFR(r18), OFR(r19),
91 	OFR(r20), OFR(r21), OFR(r22), OFR(r23),
92 	OFR(r24), OFR(r25), OFR(r26), OFR(r27),
93 	OFR(r28), OFR(r29), OFR(r30), OFR(r31),
94 };
95 
96 /*
97  * initial FP register values assumed by qc's code
98  */
99 static Internal fpreginit[] = {
100 	/* s, e, l, h */
101 	{0, 0x400, 0x00000000, 0x08000000},	/* F31=2.0 */
102 	{0, 0x3FF, 0x00000000, 0x08000000},	/* F30=1.0 */
103 	{0, 0x3FE, 0x00000000, 0x08000000},	/* F29=0.5 */
104 	{0, 0x1, 0x00000000, 0x00000000}, /* F28=0.0 */
105 	{0, 0x433, 0x00000000, 0x08000040},	/* F27=FREGCVI */
106 };
107 
108 static void
fadd(Emreg * em,Internal * d,int ra,int rb)109 fadd(Emreg *em, Internal *d, int ra, int rb)
110 {
111 	Internal a, b;
112 
113 	a = FR(ra);
114 	b = FR(rb);
115 	(a.s == b.s? fpiadd: fpisub)(&b, &a, d);
116 }
117 
118 static void
fsub(Emreg * em,Internal * d,int ra,int rb)119 fsub(Emreg *em, Internal *d, int ra, int rb)
120 {
121 	Internal a, b;
122 
123 	a = FR(ra);
124 	b = FR(rb);
125 	b.s ^= 1;
126 	(b.s == a.s? fpiadd: fpisub)(&b, &a, d);
127 }
128 
129 static void
fmul(Emreg * em,Internal * d,int ra,int rb)130 fmul(Emreg *em, Internal *d, int ra, int rb)
131 {
132 	Internal a, b;
133 
134 	a = FR(ra);
135 	b = FR(rb);
136 	fpimul(&b, &a, d);
137 }
138 
139 static void
fdiv(Emreg * em,Internal * d,int ra,int rb)140 fdiv(Emreg *em, Internal *d, int ra, int rb)
141 {
142 	Internal a, b;
143 
144 	a = FR(ra);
145 	b = FR(rb);
146 	fpidiv(&b, &a, d);
147 }
148 
149 static void
fmsub(Emreg * em,Internal * d,int ra,int rc,int rb)150 fmsub(Emreg *em, Internal *d, int ra, int rc, int rb)
151 {
152 	Internal a, c, b, t;
153 
154 	a = FR(ra);
155 	c = FR(rc);
156 	b = FR(rb);
157 	fpimul(&a, &c, &t);
158 	b.s ^= 1;
159 	(b.s == t.s? fpiadd: fpisub)(&b, &t, d);
160 }
161 
162 static void
fmadd(Emreg * em,Internal * d,int ra,int rc,int rb)163 fmadd(Emreg *em, Internal *d, int ra, int rc, int rb)
164 {
165 	Internal a, c, b, t;
166 
167 	a = FR(ra);
168 	c = FR(rc);
169 	b = FR(rb);
170 	fpimul(&a, &c, &t);
171 	(t.s == b.s? fpiadd: fpisub)(&b, &t, d);
172 }
173 
174 static ulong	setfpscr(Emreg*);
175 static void	setfpcc(Emreg*, int);
176 
177 static void
unimp(Emreg * em,ulong op)178 unimp(Emreg *em, ulong op)
179 {
180 	char buf[60];
181 
182 	snprint(buf, sizeof(buf), "sys: fp: pc=%lux unimp fp 0x%.8lux", em->ur->pc, op);
183 	if(fpemudebug)
184 		print("FPE: %s\n", buf);
185 	error(buf);
186 	/* no return */
187 }
188 
189 /*
190  * floating load/store
191  */
192 
193 static void
fpeairr(Emreg * em,ulong ir,void ** eap,int * rdp)194 fpeairr(Emreg *em, ulong ir, void **eap, int *rdp)
195 {
196 	ulong ea;
197 	long imm;
198 	int ra, rd, upd;
199 
200 	getairr(ir);
201 	ea = imm;
202 	upd = (ir&(1L<<26))!=0;
203 	if(ra) {
204 		ea += REG(ra);
205 		if(upd){
206 			if(REGSP && ra == REGSP)
207 				panic("fpemu: r1 update");	/* can't do it because we're running on the same stack */
208 			REG(ra) = ea;
209 		}
210 	} else {
211 		if(upd)
212 			unimp(em, ir);
213 	}
214 	*rdp = rd;
215 	*eap = (void*)ea;
216 	if(fpemudebug)
217 		print("%8.8lux %s\tf%d,%ld(r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, imm, ra, ea, upd);
218 }
219 
220 static void
fpearrr(Emreg * em,ulong ir,int upd,void ** eap,int * rdp)221 fpearrr(Emreg *em, ulong ir, int upd, void **eap, int *rdp)
222 {
223 	ulong ea;
224 	int ra, rb, rd;
225 
226 	getarrr(ir);
227 	ea = REG(rb);
228 	if(ra){
229 		ea += REG(ra);
230 		if(upd){
231 			if(REGSP && ra == REGSP)
232 				panic("fpemu: r1 update");
233 			REG(ra) = ea;
234 		}
235 		if(fpemudebug)
236 			print("%8.8lux %s\tf%d,(r%d+r%d) ea=%lux upd=%d\n", em->ur->pc, em->name, rd, ra, rb, ea, upd);
237 	} else {
238 		if(upd)
239 			unimp(em, ir);
240 		if(fpemudebug)
241 			print("%8.8lux %s\tf%d,(r%d) ea=%lux\n", em->ur->pc, em->name, rd, rb, ea);
242 	}
243 	*eap = (void*)ea;
244 	*rdp = rd;
245 }
246 
247 static void
lfs(Emreg * em,ulong ir)248 lfs(Emreg *em, ulong ir)
249 {
250 	void *ea;
251 	int rd;
252 
253 	em->name = "lfs";
254 	fpeairr(em, ir, &ea, &rd);
255 	fpis2i(&FR(rd), (void*)ea);
256 }
257 
258 static void
lfsx(Emreg * em,ulong ir)259 lfsx(Emreg *em, ulong ir)
260 {
261 	void *ea;
262 	int rd;
263 
264 	em->name = "lfsx";
265 	fpearrr(em, ir, ((ir>>1)&0x3FF)==567, &ea, &rd);
266 	fpis2i(&FR(rd), (void*)ea);
267 }
268 
269 static void
lfd(Emreg * em,ulong ir)270 lfd(Emreg *em, ulong ir)
271 {
272 	void *ea;
273 	int rd;
274 
275 	em->name = "lfd";
276 	fpeairr(em, ir, &ea, &rd);
277 	fpid2i(&FR(rd), (void*)ea);
278 }
279 
280 static void
lfdx(Emreg * em,ulong ir)281 lfdx(Emreg *em, ulong ir)
282 {
283 	void *ea;
284 	int rd;
285 
286 	em->name = "lfdx";
287 	fpearrr(em, ir, ((ir>>1)&0x3FF)==631, &ea, &rd);
288 	fpid2i(&FR(rd), (void*)ea);
289 }
290 
291 static void
stfs(Emreg * em,ulong ir)292 stfs(Emreg *em, ulong ir)
293 {
294 	void *ea;
295 	int rd;
296 	Internal tmp;
297 
298 	em->name = "stfs";
299 	fpeairr(em, ir, &ea, &rd);
300 	tmp = FR(rd);
301 	fpii2s(ea, &tmp);
302 }
303 
304 static void
stfsx(Emreg * em,ulong ir)305 stfsx(Emreg *em, ulong ir)
306 {
307 	void *ea;
308 	int rd;
309 	Internal tmp;
310 
311 	em->name = "stfsx";
312 	fpearrr(em, ir, getxo(ir)==695, &ea, &rd);
313 	tmp = FR(rd);
314 	fpii2s(ea, &tmp);
315 }
316 
317 static void
stfd(Emreg * em,ulong ir)318 stfd(Emreg *em, ulong ir)
319 {
320 	void *ea;
321 	int rd;
322 	Internal tmp;
323 
324 	em->name = "stfd";
325 	fpeairr(em, ir, &ea, &rd);
326 	tmp = FR(rd);
327 	fpii2d(ea, &tmp);
328 }
329 
330 static void
stfdx(Emreg * em,ulong ir)331 stfdx(Emreg *em, ulong ir)
332 {
333 	void *ea;
334 	int rd;
335 	Internal tmp;
336 
337 	em->name = "stfdx";
338 	fpearrr(em, ir, ((ir>>1)&0x3FF)==759, &ea, &rd);
339 	tmp = FR(rd);
340 	fpii2d(ea, &tmp);
341 }
342 
343 static void
mcrfs(Emreg * em,ulong ir)344 mcrfs(Emreg *em, ulong ir)
345 {
346 	int rd, ra, rb;
347 	static ulong fpscr0[] ={
348 		FPS_FX|FPS_OX,
349 		FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN,
350 		FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ,
351 		FPS_VXVC,
352 		0,
353 		FPS_VXCVI,
354 	};
355 
356 	getarrr(ir);
357 	if(rb || ra&3 || rd&3)
358 		unimp(em, ir);
359 	ra >>= 2;
360 	rd >>= 2;
361 	em->ur->cr = (em->ur->cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, em->ufp->fpscr));
362 	em->ufp->fpscr &= ~fpscr0[ra];
363 	if(fpemudebug)
364 		print("%8.8lux mcrfs\tcrf%d,crf%d\n", em->ur->pc, rd, ra);
365 }
366 
367 static void
mffs(Emreg * em,ulong ir)368 mffs(Emreg *em, ulong ir)
369 {
370 	int rd, ra, rb;
371 	Double dw;
372 
373 	getarrr(ir);
374 	if(ra || rb)
375 		unimp(em, ir);
376 	dw.h = 0;
377 	dw.l = ((uvlong)0xFFF8000L<<16)|em->ufp->fpscr;
378 	fpid2i(&FR(rd), &dw);
379 	/* it's anyone's guess how CR1 should be set when ir&1 */
380 	em->ur->cr &= ~mkCR(1, 0xE);	/* leave SO, reset others */
381 	if(fpemudebug)
382 		print("%8.8lux mffs%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
383 }
384 
385 static void
mtfsb1(Emreg * em,ulong ir)386 mtfsb1(Emreg *em, ulong ir)
387 {
388 	int rd, ra, rb;
389 
390 	getarrr(ir);
391 	if(ra || rb)
392 		unimp(em, ir);
393 	em->ufp->fpscr |= (1L << (31-rd));
394 	/* BUG: should set summary bits */
395 	if(ir & 1)
396 		em->ur->cr &= ~mkCR(1, 0xE);	/* BUG: manual unclear: leave SO, reset others? */
397 	if(fpemudebug)
398 		print("%8.8lux mtfsb1%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
399 }
400 
401 static void
mtfsb0(Emreg * em,ulong ir)402 mtfsb0(Emreg *em, ulong ir)
403 {
404 	int rd, ra, rb;
405 
406 	getarrr(ir);
407 	if(ra || rb)
408 		unimp(em, ir);
409 	em->ufp->fpscr &= ~(1L << (31-rd));
410 	if(ir & 1)
411 		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
412 	if(fpemudebug)
413 		print("%8.8lux mtfsb0%s\tfr%d\n", em->ur->pc, ir&1?".":"", rd);
414 }
415 
416 static void
mtfsf(Emreg * em,ulong ir)417 mtfsf(Emreg *em, ulong ir)
418 {
419 	int fm, rb, i;
420 	ulong v;
421 	Internal b;
422 	Double db;
423 
424 	if(ir & ((1L << 25)|(1L << 16)))
425 		unimp(em, ir);
426 	rb = (ir >> 11) & 0x1F;
427 	fm = (ir >> 17) & 0xFF;
428 	b = FR(rb);
429 	fpii2d(&db, &b);	/* reconstruct hi/lo format to recover low word */
430 	v = db.l;
431 	for(i=0; i<8; i++)
432 		if(fm & (1 << (7-i)))
433 			em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v));
434 	/* BUG: should set FEX and VX `according to the usual rule' */
435 	if(ir & 1)
436 		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
437 	if(fpemudebug)
438 		print("%8.8lux mtfsf%s\t#%.2x,fr%d\n", em->ur->pc, ir&1?".":"", fm, rb);
439 }
440 
441 static void
mtfsfi(Emreg * em,ulong ir)442 mtfsfi(Emreg *em, ulong ir)
443 {
444 	int imm, rd;
445 
446 	if(ir & ((0x7F << 16)|(1L << 11)))
447 		unimp(em, ir);
448 	rd = (ir >> 23) & 0xF;
449 	imm = (ir >> 12) & 0xF;
450 	em->ufp->fpscr = (em->ufp->fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm);
451 	/* BUG: should set FEX and VX `according to the usual rule' */
452 	if(ir & 1)
453 		em->ur->cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
454 	if(fpemudebug)
455 		print("%8.8lux mtfsfi%s\tcrf%d,#%x\n", em->ur->pc, ir&1?".":"", rd, imm);
456 }
457 
458 static void
fcmp(Emreg * em,ulong ir)459 fcmp(Emreg *em, ulong ir)
460 {
461 	int fc, rd, ra, rb, sig, i;
462 	Internal a, b;
463 
464 	getarrr(ir);
465 	if(rd & 3)
466 		unimp(em, ir);
467 	rd >>= 2;
468 	sig = 0;
469 	switch(getxo(ir)) {
470 	default:
471 		unimp(em, ir);
472 	case 32:
473 		if(fpemudebug)
474 			print("%8.8lux fcmpo\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
475 		sig = 1;
476 		break;
477 	case 0:
478 		if(fpemudebug)
479 			print("%8.8lux fcmpu\tcr%d,f%d,f%d\n", em->ur->pc, rd, ra, rb);
480 		break;
481 	}
482 	if(IsWeird(&FR(ra)) || IsWeird(&FR(rb))) {
483 		if(sig){
484 			;	/* BUG: should trap if not masked ... */
485 		}
486 		fc = CRFU;
487 	} else {
488 		a = FR(ra);
489 		b = FR(rb);
490 		fpiround(&a);
491 		fpiround(&b);
492 		i = fpicmp(&a, &b);
493 		if(i > 0)
494 			fc = CRGT;
495 		else if(i == 0)
496 			fc = CREQ;
497 		else
498 			fc = CRLT;
499 	}
500 	fc >>= 28;
501 	em->ur->cr = (em->ur->cr & ~mkCR(rd,~0)) | mkCR(rd, fc);
502 	em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (fc<<11);
503 	/* BUG: update FX, VXSNAN, VXVC */
504 }
505 
506 static void
fariths(Emreg * em,ulong ir)507 fariths(Emreg *em, ulong ir)
508 {
509 	int rd, ra, rb, rc, fmt;
510 	char *cc, *n;
511 	ulong fpscr;
512 	Internal *d;
513 
514 	fmt = 0;
515 	rc = (ir>>6)&0x1F;
516 	getarrr(ir);
517 	d = &FR(rd);
518 	switch(getxo(ir)&0x1F) {	/* partial XO decode */
519 	case 22:	/* fsqrts */
520 	case 24:	/* fres */
521 	default:
522 		unimp(em, ir);
523 		return;
524 	case 18:
525 		if(IsZero(&FR(rb))) {
526 			em->ufp->fpscr |= FPS_ZX | FPS_FX;
527 			error("sys: fp: zero divide");
528 		}
529 		fdiv(em, d, ra, rb);
530 		n = "fdivs";
531 		break;
532 	case 20:
533 		fsub(em, d, ra, rb);
534 		n = "fsubs";
535 		break;
536 	case 21:
537 		fadd(em, d, ra, rb);
538 		n = "fadds";
539 		break;
540 	case 25:
541 		fmul(em, d, ra, rc);
542 		rb = rc;
543 		n = "fmuls";
544 		break;
545 	case 28:
546 		fmsub(em, d, ra, rc, rb);
547 		fmt = 2;
548 		n = "fmsubs";
549 		break;
550 	case 29:
551 		fmadd(em, d, ra, rc, rb);
552 		fmt = 2;
553 		n = "fmadds";
554 		break;
555 	case 30:
556 		fmsub(em, d, ra, rc, rb);
557 		d->s ^= 1;
558 		fmt = 2;
559 		n = "fnmsubs";
560 		break;
561 	case 31:
562 		fmadd(em, d, ra, rc, rb);
563 		d->s ^= 1;
564 		fmt = 2;
565 		n = "fnmadds";
566 		break;
567 	}
568 	if(fmt==1 && ra)
569 		unimp(em, ir);
570 	fpscr = setfpscr(em);
571 	setfpcc(em, rd);
572 	cc = "";
573 	if(ir & 1) {
574 		cc = ".";
575 		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
576 	}
577 	if(fpemudebug) {
578 		switch(fmt) {
579 		case 0:
580 			print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
581 			break;
582 		case 1:
583 			print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
584 			break;
585 		case 2:
586 			print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
587 			break;
588 		}
589 	}
590 }
591 
592 static void
farith(Emreg * em,ulong ir)593 farith(Emreg *em, ulong ir)
594 {
595 	Word w;
596 	Double dv;
597 	int rd, ra, rb, rc, fmt;
598 	char *cc, *n;
599 	ulong fpscr;
600 	int nocc;
601 	Internal *d;
602 
603 	fmt = 0;
604 	nocc = 0;
605 	rc = (ir>>6)&0x1F;
606 	getarrr(ir);
607 	d = &FR(rd);
608 	switch(getxo(ir)&0x1F) { /* partial XO decode */
609 	case 22:	/* frsqrt */
610 	case 23:	/* fsel */
611 	case 26:	/* fsqrte */
612 	default:
613 		unimp(em, ir);
614 		return;
615 	case 12:	/* frsp */
616 		*d = FR(rb);	/* BUG: doesn't round to single precision */
617 		fmt = 1;
618 		n = "frsp";
619 		break;
620 	case 14:	/* fctiw */	/* BUG: ignores rounding mode */
621 	case 15:	/* fctiwz */
622 		fpii2w(&w, &FR(rb));
623 		dv.h = 0;
624 		dv.l = w;
625 		fpid2i(d, &dv);
626 		fmt = 1;
627 		nocc = 1;
628 		n = "fctiw";
629 		break;
630 	case 18:
631 		if(IsZero(&FR(rb))) {
632 			em->ufp->fpscr |= FPS_ZX | FPS_FX;
633 			error("sys: fp: zero divide");
634 		}
635 		fdiv(em, d, ra, rb);
636 		n = "fdiv";
637 		break;
638 	case 20:
639 		fsub(em, d, ra, rb);
640 		n = "fsub";
641 		break;
642 	case 21:
643 		fadd(em, d, ra, rb);
644 		n = "fadd";
645 		break;
646 	case 25:
647 		fmul(em, d, ra, rc);
648 		rb = rc;
649 		n = "fmul";
650 		break;
651 	case 28:
652 		fmsub(em, d, ra, rc, rb);
653 		fmt = 2;
654 		n = "fmsub";
655 		break;
656 	case 29:
657 		fmadd(em, d, ra, rc, rb);
658 		fmt = 2;
659 		n = "fmadd";
660 		break;
661 	case 30:
662 		fmsub(em, d, ra, rc, rb);
663 		d->s ^= 1;
664 		fmt = 2;
665 		n = "fnmsub";
666 		break;
667 	case 31:
668 		fmadd(em, d, ra, rc, rb);
669 		d->s ^= 1;
670 		fmt = 2;
671 		n = "fnmadd";
672 		break;
673 	}
674 	if(fmt==1 && ra)
675 		unimp(em, ir);
676 	fpscr = setfpscr(em);
677 	if(nocc == 0)
678 		setfpcc(em, rd);
679 	cc = "";
680 	if(ir & 1) {
681 		cc = ".";
682 		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
683 	}
684 	if(fpemudebug) {
685 		switch(fmt) {
686 		case 0:
687 			print("%8.8lux %s%s\tfr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rb);
688 			break;
689 		case 1:
690 			print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
691 			break;
692 		case 2:
693 			print("%8.8lux %s%s\tfr%d,fr%d,fr%d,fr%d\n", em->ur->pc, n, cc, rd, ra, rc, rb);
694 			break;
695 		}
696 	}
697 }
698 
699 static void
farith2(Emreg * em,ulong ir)700 farith2(Emreg *em, ulong ir)
701 {
702 	int rd, ra, rb;
703 	char *cc, *n;
704 	ulong fpscr;
705 	Internal *d, *b;
706 
707 	getarrr(ir);
708 	if(ra)
709 		unimp(em, ir);
710 	d = &FR(rd);
711 	b = &FR(rb);
712 	switch(getxo(ir)) { /* full XO decode */
713 	default:
714 		unimp(em, ir);
715 	case 40:
716 		*d = *b;
717 		d->s ^= 1;
718 		n = "fneg";
719 		break;
720 	case 72:
721 		*d = *b;
722 		n = "fmr";
723 		break;
724 	case 136:
725 		*d = *b;
726 		d->s = 1;
727 		n = "fnabs";
728 		break;
729 	case 264:
730 		*d = *b;
731 		d->s = 0;
732 		n = "fabs";
733 		break;
734 	}
735 	fpscr = setfpscr(em);
736 	setfpcc(em, rd);
737 	cc = "";
738 	if(ir & 1) {
739 		cc = ".";
740 		em->ur->cr = (em->ur->cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
741 	}
742 	if(fpemudebug)
743 		print("%8.8lux %s%s\tfr%d,fr%d\n", em->ur->pc, n, cc, rd, rb);
744 }
745 
746 static ulong
setfpscr(Emreg * em)747 setfpscr(Emreg *em)
748 {
749 	ulong fps, fpscr;
750 
751 	fps = 0;	/* BUG: getfsr() */
752 	fpscr = em->ufp->fpscr;
753 	if(fps & FPAOVFL)
754 		fpscr |= FPS_OX;
755 	if(fps & FPAINEX)
756 		fpscr |= FPS_XX;
757 	if(fps & FPAUNFL)
758 		fpscr |= FPS_UX;
759 	if(fps & FPAZDIV)
760 		fpscr |= FPS_ZX;
761 	if(fpscr != em->ufp->fpscr) {
762 		fpscr |= FPS_FX;
763 		em->ufp->fpscr = fpscr;
764 	}
765 	return fpscr;
766 }
767 
768 static void
setfpcc(Emreg * em,int r)769 setfpcc(Emreg *em, int r)
770 {
771 	int c;
772 	Internal *d;
773 
774 	d = &FR(r);
775 	c = 0;
776 	if(IsZero(d))
777 		c |= 2;
778 	else if(d->s == 1)
779 		c |= 4;
780 	else
781 		c |= 8;
782 	if(IsNaN(d))
783 		c |= 1;
784 	em->ufp->fpscr = (em->ufp->fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */
785 }
786 
787 static	uchar	op63flag[32] = {
788 [12] 1, [14] 1, [15] 1, [18] 1, [20] 1, [21] 1, [22] 1,
789 [23] 1, [25] 1, [26] 1, [28] 1, [29] 1, [30] 1, [31] 1,
790 };
791 
792 /*
793  * returns the number of FP instructions emulated
794  */
795 int
fpipower(Ureg * ur)796 fpipower(Ureg *ur)
797 {
798 	ulong op;
799 	int xo;
800 	Emreg emreg, *em;
801 	FPsave *ufp;
802 	int n;
803 
804 	ufp = &up->fpsave; /* because all the state is in FPsave, it need not be saved/restored */
805 	em = &emreg;
806 	em->ur = ur;
807 	em->fr = ufp->emreg;
808 	em->ufp = ufp;
809 	em->name = nil;
810 	if(em->ufp->fpistate != FPactive) {
811 		em->ufp->fpistate = FPactive;
812 		em->ufp->fpscr = 0;	/* TO DO */
813 		for(n = 0; n < nelem(fpreginit); n++)
814 			FR(31-n) = fpreginit[n];
815 	}
816 	for(n=0;;n++){
817 		validaddr(ur->pc, 4, 0);
818 		op = getulong(ur->pc);
819 		em->ir = op;
820 		if(fpemudebug > 1)
821 			print("%8.8lux %8.8lux: ", ur->pc, op);
822 		switch(op>>26){
823 		default:
824 			return n;
825 		case 48:	/* lfs */
826 		case 49:	/* lfsu */
827 			lfs(em, op);
828 			break;
829 		case 50:	/* lfd */
830 		case 51:	/* lfdu */
831 			lfd(em, op);
832 			break;
833 		case 52:	/* stfs */
834 		case 53:	/* stfsu */
835 			stfs(em, op);
836 			break;
837 		case 54:	/* stfd */
838 		case 55:	/* stfdu */
839 			stfd(em, op);
840 			break;
841 		case 31:	/* indexed load/store */
842 			xo = getxo(op);
843 			if((xo & 0x300) != 0x200)
844 				return n;
845 			switch(xo){
846 			default:
847 				return n;
848 			case 535:	/* lfsx */
849 			case 567:	/* lfsux */
850 				lfsx(em, op);
851 				break;
852 			case 599:	/* lfdx */
853 			case 631:	/* lfdux */
854 				lfdx(em, op);
855 				break;
856 			case 663:	/* stfsx */
857 			case 695:	/* stfsux */
858 				stfsx(em, op);
859 				break;
860 			case 727:	/* stfdx */
861 			case 759:	/* stfdux */
862 				stfdx(em, op);
863 				break;
864 			}
865 			break;
866 		case 63:	/* double precision */
867 			xo = getxo(op);
868 			if(op63flag[xo & 0x1F]){
869 				farith(em, op);
870 				break;
871 			}
872 			switch(xo){
873 			default:
874 				return n;
875 			case 0:	/* fcmpu */
876 			case 32:	/* fcmpo */
877 				fcmp(em, op);
878 				break;
879 			case 40:	/* fneg */
880 			case 72:	/* fmr */
881 			case 136:	/* fnabs */
882 			case 264:	/* fabs */
883 				farith2(em, op);
884 				break;
885 			case 38:
886 				mtfsb1(em, op);
887 				break;
888 			case 64:
889 				mcrfs(em, op);
890 				break;
891 			case 70:
892 				mtfsb0(em, op);
893 				break;
894 			case 134:
895 				mtfsfi(em, op);
896 				break;
897 			case 583:
898 				mffs(em, op);
899 				break;
900 			case 711:
901 				mtfsf(em, op);
902 				break;
903 			}
904 			break;
905 		case 59:	/* single precision */
906 			fariths(em, op);
907 			break;
908 		}
909 		ur->pc += 4;
910 		if(anyhigher())
911 			sched();
912 	}
913 }
914 
915 /*
916 50:	lfd	frD,d(rA)
917 51:	lfdu	frD,d(rA)
918 31,631:	lfdux	frD,rA,rB
919 31,599:	lfdx	frD,rA,rB
920 48:	lfs	frD,d(rA)
921 49:	lfsu	frD,d(rA)
922 31,567:	lfsux	frD,rA,rB
923 31,535:	lfsx	frD,rA,rB
924 
925 54:	stfd	frS,d(rA)
926 55:	stfdu	frS,d(rA)
927 31,759:	stfdux	frS,rA,rB
928 31,727:	stfdx	frS,rA,rB
929 52:	stfs	frS,d(rA)
930 53:	stfsu	frS,d(rA)
931 31,695:	stfsux	frS,rA,rB
932 31,663:	stfsx	frS,rA,rB
933 
934 63,64:	mcrfs	crfD,crfS
935 63,583:	mffs[.]	frD
936 63,70:	mtfsb0[.]	crbD
937 63,38:	mtfsb1[.]	crbD
938 63,711:	mtfsf[.]	FM,frB
939 63,134:	mtfsfi[.]	crfD,IMM
940 */
941 
942 /*
943 float to int:
944 	FMOVD	g+0(SB),F1
945 	FCTIWZ	F1,F4
946 	FMOVD	F4,.rathole+0(SB)
947 	MOVW	.rathole+4(SB),R7
948 	MOVW	R7,l+0(SB)
949 */
950 
951 /*
952 int to float:
953 	MOVW	$1127219200,R9
954 	MOVW	l+0(SB),R7
955 	MOVW	R9,.rathole+0(SB)
956 	XOR	$-2147483648,R7,R6
957 	MOVW	R6,.rathole+4(SB)
958 	FMOVD	.rathole+0(SB),F0
959 	FSUB	F27,F0
960 
961 unsigned to float:
962 	MOVW	ul+0(SB),R5
963 	MOVW	R9,.rathole+0(SB)
964 	XOR	$-2147483648,R5,R4
965 	MOVW	R4,.rathole+4(SB)
966 	FMOVD	.rathole+0(SB),F3
967 	FSUB	F27,F3
968 	FCMPU	F3,F28
969 	BGE	,3(PC)
970 	FMOVD	$4.29496729600000000e+09,F2
971 	FADD	F2,F3
972 	FMOVD	F3,g+0(SB)
973 */
974