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