xref: /plan9/sys/src/cmd/qi/float.c (revision a84536681645e23c630ce4ef2e5c3b284d4c590b)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <mach.h>
5 #define Extern extern
6 #include "power.h"
7 
8 ulong	setfpscr(void);
9 void	setfpcc(double);
10 void	farith(ulong);
11 void	farith2(ulong);
12 void	fariths(ulong);
13 void	fcmp(ulong);
14 void	mtfsb1(ulong);
15 void	mcrfs(ulong);
16 void	mtfsb0(ulong);
17 void	mtfsf(ulong);
18 void	mtfsfi(ulong);
19 void	mffs(ulong);
20 void	mtfsf(ulong);
21 
22 Inst	op59[] = {
23 [18] {fariths, "fdivs", Ifloat},
24 [20] {fariths, "fsubs", Ifloat},
25 [21] {fariths, "fadds", Ifloat},
26 [22] {unimp, "fsqrts", Ifloat},
27 [24] {unimp, "fres", Ifloat},
28 [25] {fariths, "fmuls", Ifloat},
29 [28] {fariths, "fmsubs", Ifloat},
30 [29] {fariths, "fmadds", Ifloat},
31 [30] {fariths, "fnmsubs", Ifloat},
32 [31] {fariths, "fnmadds", Ifloat},
33 };
34 
35 Inset	ops59 = {op59, nelem(op59)};
36 
37 Inst	op63a[] = {
38 [12] {farith, "frsp", Ifloat},
39 [14] {farith, "fctiw", Ifloat},
40 [15] {farith, "fctiwz", Ifloat},
41 [18] {farith, "fdiv", Ifloat},
42 [20] {farith, "fsub", Ifloat},
43 [21] {farith, "fadd", Ifloat},
44 [22] {unimp, "frsqrt", Ifloat},
45 [23] {unimp, "fsel", Ifloat},
46 [25] {farith, "fmul", Ifloat},
47 [26] {unimp, "frsqrte", Ifloat},
48 [28] {farith, "fmsub", Ifloat},
49 [29] {farith, "fmadd", Ifloat},
50 [30] {farith, "fnmsub", Ifloat},
51 [31] {farith, "fnmadd", Ifloat},
52 };
53 
54 Inset	ops63a= {op63a, nelem(op63a)};
55 
56 Inst	op63b[] = {
57 [0] {fcmp, "fcmpu", Ifloat},
58 [32] {fcmp, "fcmpo", Ifloat},
59 [38] {mtfsb1, "mtfsb1", Ifloat},
60 [40] {farith2, "fneg", Ifloat},
61 [64] {mcrfs, "mcrfs", Ifloat},
62 [70] {mtfsb0, "mtfsb0", Ifloat},
63 [72] {farith2, "fmr", Ifloat},
64 [134] {mtfsfi, "mtfsfi", Ifloat},
65 [136] {farith2, "fnabs", Ifloat},
66 [264] {farith2, "fabs", Ifloat},
67 [583] {mffs, "mffs", Ifloat},
68 [711] {mtfsf, "mtfsf", Ifloat},
69 };
70 
71 Inset	ops63b = {op63b, nelem(op63b)};
72 
73 void
74 lfs(ulong ir)
75 {
76 	ulong ea;
77 	int imm, ra, rd, upd;
78 	union {
79 		ulong	i;
80 		float	f;
81 	} u;
82 
83 	getairr(ir);
84 	ea = imm;
85 	upd = (ir&(1L<<26))!=0;
86 	if(ra) {
87 		ea += reg.r[ra];
88 		if(upd)
89 			reg.r[ra] = ea;
90 	} else {
91 		if(upd)
92 			undef(ir);
93 	}
94 	if(trace)
95 		itrace("%s\tf%d,%ld(r%d) ea=%lux", ci->name, rd, imm, ra, ea);
96 
97 	u.i = getmem_w(ea);
98 	reg.fd[rd] = u.f;
99 }
100 
101 void
102 lfsx(ulong ir)
103 {
104 	ulong ea;
105 	int rd, ra, rb, upd;
106 	union {
107 		ulong	i;
108 		float	f;
109 	} u;
110 
111 	getarrr(ir);
112 	ea = reg.r[rb];
113 	upd = ((ir>>1)&0x3FF)==567;
114 	if(ra){
115 		ea += reg.r[ra];
116 		if(upd)
117 			reg.r[ra] = ea;
118 		if(trace)
119 			itrace("%s\tf%d,(r%d+r%d) ea=%lux", ci->name, rd, ra, rb, ea);
120 	} else {
121 		if(upd)
122 			undef(ir);
123 		if(trace)
124 			itrace("%s\tf%d,(r%d) ea=%lux", ci->name, rd, rb, ea);
125 	}
126 
127 	u.i = getmem_w(ea);
128 	reg.fd[rd] = u.f;
129 }
130 
131 void
132 lfd(ulong ir)
133 {
134 	ulong ea;
135 	int imm, ra, rd, upd;
136 
137 	getairr(ir);
138 	ea = imm;
139 	upd = (ir&(1L<<26))!=0;
140 	if(ra) {
141 		ea += reg.r[ra];
142 		if(upd)
143 			reg.r[ra] = ea;
144 	} else {
145 		if(upd)
146 			undef(ir);
147 	}
148 	if(trace)
149 		itrace("%s\tf%d,%ld(r%d) ea=%lux", ci->name, rd, imm, ra, ea);
150 
151 	reg.dv[rd] = getmem_v(ea);
152 }
153 
154 void
155 lfdx(ulong ir)
156 {
157 	ulong ea;
158 	int rd, ra, rb, upd;
159 
160 	getarrr(ir);
161 	ea = reg.r[rb];
162 	upd = ((ir>>1)&0x3FF)==631;
163 	if(ra){
164 		ea += reg.r[ra];
165 		if(upd)
166 			reg.r[ra] = ea;
167 		if(trace)
168 			itrace("%s\tf%d,(r%d+r%d) ea=%lux", ci->name, rd, ra, rb, ea);
169 	} else {
170 		if(upd)
171 			undef(ir);
172 		if(trace)
173 			itrace("%s\tf%d,(r%d) ea=%lux", ci->name, rd, rb, ea);
174 	}
175 
176 	reg.dv[rd] = getmem_v(ea);
177 }
178 
179 void
180 stfs(ulong ir)
181 {
182 	ulong ea;
183 	int imm, ra, rd, upd;
184 	union {
185 		float f;
186 		ulong w;
187 	} u;
188 
189 	getairr(ir);
190 	ea = imm;
191 	upd = (ir&(1L<<26))!=0;
192 	if(ra) {
193 		ea += reg.r[ra];
194 		if(upd)
195 			reg.r[ra] = ea;
196 	} else {
197 		if(upd)
198 			undef(ir);
199 	}
200 	if(trace)
201 		itrace("%s\tf%d,%ld(r%d) %lux=%g",
202 					ci->name, rd, imm, ra, ea, reg.fd[rd]);
203 	u.f = reg.fd[rd];	/* BUG: actual PPC conversion is more subtle than this */
204 	putmem_w(ea, u.w);
205 }
206 
207 void
208 stfsx(ulong ir)
209 {
210 	ulong ea;
211 	int rd, ra, rb, upd;
212 	union {
213 		float	f;
214 		ulong	w;
215 	} u;
216 
217 	getarrr(ir);
218 	ea = reg.r[rb];
219 	upd = getxo(ir)==695;
220 	if(ra){
221 		ea += reg.r[ra];
222 		if(upd)
223 			reg.r[ra] = ea;
224 		if(trace)
225 			itrace("%s\tf%d,(r%d+r%d) %lux=%g", ci->name, rd, ra, rb, ea, (float)reg.fd[rd]);
226 	} else {
227 		if(upd)
228 			undef(ir);
229 		if(trace)
230 			itrace("%s\tf%d,(r%d) %lux=%g", ci->name, rd, rb, ea, (float)reg.fd[rd]);
231 	}
232 
233 	u.f = reg.fd[rd];	/* BUG: actual PPC conversion is more subtle than this */
234 	putmem_w(ea, u.w);
235 }
236 
237 void
238 stfd(ulong ir)
239 {
240 	ulong ea;
241 	int imm, ra, rd, upd;
242 
243 	getairr(ir);
244 	ea = imm;
245 	upd = (ir&(1L<<26))!=0;
246 	if(ra) {
247 		ea += reg.r[ra];
248 		if(upd)
249 			reg.r[ra] = ea;
250 	} else {
251 		if(upd)
252 			undef(ir);
253 	}
254 	if(trace)
255 		itrace("%s\tf%d,%ld(r%d) %lux=%g",
256 					ci->name, rd, imm, ra, ea, reg.fd[rd]);
257 
258 	putmem_v(ea, reg.dv[rd]);
259 }
260 
261 void
262 stfdx(ulong ir)
263 {
264 	ulong ea;
265 	int rd, ra, rb, upd;
266 
267 	getarrr(ir);
268 	ea = reg.r[rb];
269 	upd = ((ir>>1)&0x3FF)==759;
270 	if(ra){
271 		ea += reg.r[ra];
272 		if(upd)
273 			reg.r[ra] = ea;
274 		if(trace)
275 			itrace("%s\tf%d,(r%d+r%d) %lux=%g", ci->name, rd, ra, rb, ea, reg.fd[rd]);
276 	} else {
277 		if(upd)
278 			undef(ir);
279 		if(trace)
280 			itrace("%s\tf%d,(r%d) %lux=%g", ci->name, rd, rb, ea, reg.fd[rd]);
281 	}
282 
283 	putmem_v(ea, reg.dv[rd]);
284 }
285 
286 void
287 mcrfs(ulong ir)
288 {
289 	ulong rd, ra, rb;
290 	static ulong fpscr0[] ={
291 		FPS_FX|FPS_OX,
292 		FPS_UX|FPS_ZX|FPS_XX|FPS_VXSNAN,
293 		FPS_VXISI|FPS_VXIDI|FPS_VXZDZ|FPS_VXIMZ,
294 		FPS_VXVC,
295 		0,
296 		FPS_VXCVI,
297 	};
298 
299 	getarrr(ir);
300 	if(rb || ra&3 || rd&3)
301 		undef(ir);
302 	ra >>= 2;
303 	rd >>= 2;
304 	reg.cr = (reg.cr & ~mkCR(rd, 0xF)) | mkCR(rd, getCR(ra, reg.fpscr));
305 	reg.fpscr &= ~fpscr0[ra];
306 	if(trace)
307 		itrace("mcrfs\tcrf%d,crf%d\n", rd, ra);
308 }
309 
310 void
311 mffs(ulong ir)
312 {
313 	int rd, ra, rb;
314 
315 	getarrr(ir);
316 	if(ra || rb)
317 		undef(ir);
318 	reg.dv[rd] = ((uvlong)0xFFF8000L<<16)|reg.fpscr;
319 	/* it's anyone's guess how CR1 should be set when ir&1 */
320 	reg.cr &= ~mkCR(1, 0xE);	/* leave SO, reset others */
321 	if(trace)
322 		itrace("mffs%s\tfr%d\n", ir&1?".":"", rd);
323 }
324 
325 void
326 mtfsb1(ulong ir)
327 {
328 	int rd, ra, rb;
329 
330 	getarrr(ir);
331 	if(ra || rb)
332 		undef(ir);
333 	reg.fpscr |= (1L << (31-rd));
334 	/* BUG: should set summary bits */
335 	if(ir & 1)
336 		reg.cr &= ~mkCR(1, 0xE);	/* BUG: manual unclear: leave SO, reset others? */
337 	if(trace)
338 		itrace("mtfsb1%s\tfr%d\n", ir&1?".":"", rd);
339 }
340 
341 void
342 mtfsb0(ulong ir)
343 {
344 	int rd, ra, rb;
345 
346 	getarrr(ir);
347 	if(ra || rb)
348 		undef(ir);
349 	reg.fpscr &= ~(1L << (31-rd));
350 	if(ir & 1)
351 		reg.cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
352 	if(trace)
353 		itrace("mtfsb0%s\tfr%d\n", ir&1?".":"", rd);
354 }
355 
356 void
357 mtfsf(ulong ir)
358 {
359 	int fm, rb, i;
360 	ulong v;
361 
362 	if(ir & ((1L << 25)|(1L << 16)))
363 		undef(ir);
364 	rb = (ir >> 11) & 0x1F;
365 	fm = (ir >> 17) & 0xFF;
366 	v = reg.dv[rb];
367 	for(i=0; i<8; i++)
368 		if(fm & (1 << (7-i)))
369 			reg.fpscr = (reg.fpscr & ~mkCR(i, 0xF)) | mkCR(i, getCR(i, v));
370 	/* BUG: should set FEX and VX `according to the usual rule' */
371 	if(ir & 1)
372 		reg.cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
373 	if(trace)
374 		itrace("mtfsf%s\t#%.2x,fr%d", ir&1?".":"", fm, rb);
375 }
376 
377 void
378 mtfsfi(ulong ir)
379 {
380 	int imm, rd;
381 
382 	if(ir & ((0x7F << 16)|(1L << 11)))
383 		undef(ir);
384 	rd = (ir >> 23) & 0xF;
385 	imm = (ir >> 12) & 0xF;
386 	reg.fpscr = (reg.fpscr & ~mkCR(rd, 0xF)) | mkCR(rd, imm);
387 	/* BUG: should set FEX and VX `according to the usual rule' */
388 	if(ir & 1)
389 		reg.cr &= ~mkCR(1, 0xE);		/* BUG: manual unclear: leave SO, reset others? */
390 	if(trace)
391 		itrace("mtfsfi%s\tcrf%d,#%x", ir&1?".":"", rd, imm);
392 }
393 
394 void
395 fcmp(ulong ir)
396 {
397 	int fc, rd, ra, rb;
398 
399 	getarrr(ir);
400 	if(rd & 3)
401 		undef(ir);
402 	rd >>= 2;
403 	SET(fc);
404 	switch(getxo(ir)) {
405 	default:
406 		undef(ir);
407 	case 0:
408 		if(trace)
409 			itrace("fcmpu\tcr%d,f%d,f%d", rd, ra, rb);
410 		if(isNaN(reg.fd[ra]) || isNaN(reg.fd[rb])) {
411 			fc = CRFU;
412 			break;
413 		}
414 		if(reg.fd[ra] == reg.fd[rb]) {
415 			fc = CREQ;
416 			break;
417 		}
418 		if(reg.fd[ra] < reg.fd[rb]) {
419 			fc = CRLT;
420 			break;
421 		}
422 		if(reg.fd[ra] > reg.fd[rb]) {
423 			fc = CRGT;
424 			break;
425 		}
426 		print("qi: fcmp error\n");
427 		break;
428 	case 32:
429 		if(trace)
430 			itrace("fcmpo\tcr%d,f%d,f%d", rd, ra, rb);
431 		if(isNaN(reg.fd[ra]) || isNaN(reg.fd[rb])) {	/* BUG: depends whether quiet or signalling ... */
432 			fc = CRFU;
433 			Bprint(bioout, "invalid_fp_register\n");
434 			longjmp(errjmp, 0);
435 		}
436 		if(reg.fd[ra] == reg.fd[rb]) {
437 			fc = CREQ;
438 			break;
439 		}
440 		if(reg.fd[ra] < reg.fd[rb]) {
441 			fc = CRLT;
442 			break;
443 		}
444 		if(reg.fd[ra] > reg.fd[rb]) {
445 			fc = CRGT;
446 			break;
447 		}
448 		print("qi: fcmp error\n");
449 		break;
450 
451 	}
452 	fc >>= 28;
453 	reg.cr = (reg.cr & ~mkCR(rd,~0)) | mkCR(rd, fc);
454 	reg.fpscr = (reg.fpscr & ~0xF800) | (fc<<11);
455 	/* BUG: update FX, VXSNAN, VXVC */
456 }
457 
458 /*
459  * the farith functions probably don't produce the right results
460  * in the presence of NaNs, Infs, etc., esp. wrt exception handling,
461  */
462 void
463 fariths(ulong ir)
464 {
465 	int rd, ra, rb, rc, fmt;
466 	char *cc;
467 	ulong fpscr;
468 
469 	fmt = 0;
470 	rc = (ir>>6)&0x1F;
471 	getarrr(ir);
472 	switch(getxo(ir)&0x1F) {	/* partial XO decode */
473 	default:
474 		undef(ir);
475 	case 18:
476 		if((float)reg.fd[rb] == 0.0) {
477 			Bprint(bioout, "fp_exception ZX\n");
478 			reg.fpscr |= FPS_ZX | FPS_FX;
479 			longjmp(errjmp, 0);
480 		}
481 		reg.fd[rd] = (float)(reg.fd[ra] / reg.fd[rb]);
482 		break;
483 	case 20:
484 		reg.fd[rd] = (float)(reg.fd[ra] - reg.fd[rb]);
485 		break;
486 	case 21:
487 		reg.fd[rd] = (float)(reg.fd[ra] + reg.fd[rb]);
488 		break;
489 	case 25:
490 		reg.fd[rd] = (float)(reg.fd[ra] * reg.fd[rc]);
491 		rb = rc;
492 		break;
493 	case 28:
494 		reg.fd[rd] = (float)((reg.fd[ra] * reg.fd[rc]) - reg.fd[rb]);
495 		fmt = 2;
496 		break;
497 	case 29:
498 		reg.fd[rd] = (float)((reg.fd[ra] * reg.fd[rc]) + reg.fd[rb]);
499 		fmt = 2;
500 		break;
501 	case 30:
502 		reg.fd[rd] = (float)-((reg.fd[ra] * reg.fd[rc]) - reg.fd[rb]);
503 		fmt = 2;
504 		break;
505 	case 31:
506 		reg.fd[rd] = (float)-((reg.fd[ra] * reg.fd[rc]) + reg.fd[rb]);
507 		fmt = 2;
508 		break;
509 	}
510 	if(fmt==1 && ra)
511 		undef(ir);
512 	fpscr = setfpscr();
513 	setfpcc(reg.fd[rd]);
514 	cc = "";
515 	if(ir & 1) {
516 		cc = ".";
517 		reg.cr = (reg.cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
518 	}
519 	if(trace) {
520 		switch(fmt) {
521 		case 0:
522 			itrace("%s%s\tfr%d,fr%d,fr%d", ci->name, cc, rd, ra, rb);
523 			break;
524 		case 1:
525 			itrace("%s%s\tfr%d,fr%d", ci->name, cc, rd, rb);
526 			break;
527 		case 2:
528 			itrace("%s%s\tfr%d,fr%d,fr%d,fr%d", ci->name, cc, rd, ra, rc, rb);
529 			break;
530 		}
531 	}
532 }
533 
534 void
535 farith(ulong ir)
536 {
537 	vlong vl;
538 	int rd, ra, rb, rc, fmt;
539 	char *cc;
540 	ulong fpscr;
541 	int nocc;
542 	double d;
543 
544 	fmt = 0;
545 	nocc = 0;
546 	rc = (ir>>6)&0x1F;
547 	getarrr(ir);
548 	switch(getxo(ir)&0x1F) { /* partial XO decode */
549 	default:
550 		undef(ir);
551 	case 12:	/* frsp */
552 		reg.fd[rd] = (float)reg.fd[rb];
553 		fmt = 1;
554 		break;
555 	case 14:	/* fctiw */	/* BUG: ignores rounding mode */
556 	case 15:	/* fctiwz */
557 		d = reg.fd[rb];
558 		if(d >= 0x7fffffff)
559 			vl = 0x7fffffff;
560 		else if(d < 0x80000000)
561 			vl = 0x80000000;
562 		else
563 			vl = d;
564 		reg.dv[rd] = vl;
565 		fmt = 1;
566 		nocc = 1;
567 		break;
568 	case 18:
569 		if(reg.fd[rb] == 0.0) {
570 			Bprint(bioout, "fp_exception ZX\n");
571 			reg.fpscr |= FPS_ZX | FPS_FX;
572 			longjmp(errjmp, 0);
573 		}
574 		reg.fd[rd] = reg.fd[ra] / reg.fd[rb];
575 		break;
576 	case 20:
577 		reg.fd[rd] = reg.fd[ra] - reg.fd[rb];
578 		break;
579 	case 21:
580 		reg.fd[rd] = reg.fd[ra] + reg.fd[rb];
581 		break;
582 	case 25:
583 		reg.fd[rd] = reg.fd[ra] * reg.fd[rc];
584 		rb = rc;
585 		break;
586 	case 28:
587 		reg.fd[rd] = (reg.fd[ra] * reg.fd[rc]) - reg.fd[rb];
588 		fmt = 2;
589 		break;
590 	case 29:
591 		reg.fd[rd] = (reg.fd[ra] * reg.fd[rc]) + reg.fd[rb];
592 		fmt = 2;
593 		break;
594 	case 30:
595 		reg.fd[rd] = -((reg.fd[ra] * reg.fd[rc]) - reg.fd[rb]);
596 		fmt = 2;
597 		break;
598 	case 31:
599 		reg.fd[rd] = -((reg.fd[ra] * reg.fd[rc]) + reg.fd[rb]);
600 		fmt = 2;
601 		break;
602 	}
603 	if(fmt==1 && ra)
604 		undef(ir);
605 	fpscr = setfpscr();
606 	if(nocc == 0)
607 		setfpcc(reg.fd[rd]);
608 	cc = "";
609 	if(ir & 1) {
610 		cc = ".";
611 		reg.cr = (reg.cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
612 	}
613 	if(trace) {
614 		switch(fmt) {
615 		case 0:
616 			itrace("%s%s\tfr%d,fr%d,fr%d", ci->name, cc, rd, ra, rb);
617 			break;
618 		case 1:
619 			itrace("%s%s\tfr%d,fr%d", ci->name, cc, rd, rb);
620 			break;
621 		case 2:
622 			itrace("%s%s\tfr%d,fr%d,fr%d,fr%d", ci->name, cc, rd, ra, rc, rb);
623 			break;
624 		}
625 	}
626 }
627 
628 void
629 farith2(ulong ir)
630 {
631 	int rd, ra, rb;
632 	char *cc;
633 	ulong fpscr;
634 
635 	getarrr(ir);
636 	switch(getxo(ir)) { /* full XO decode */
637 	default:
638 		undef(ir);
639 	case 40:
640 		reg.fd[rd] = -reg.fd[rb];
641 		break;
642 	case 72:
643 		reg.fd[rd] = reg.fd[rb];
644 		break;
645 	case 136:
646 		reg.fd[rd] = -fabs(reg.fd[rb]);
647 		break;
648 	case 264:
649 		reg.fd[rd] = fabs(reg.fd[rb]);
650 		break;
651 	}
652 	if(ra)
653 		undef(ir);
654 	fpscr = setfpscr();
655 	setfpcc(reg.fd[rd]);
656 	cc = "";
657 	if(ir & 1) {
658 		cc = ".";
659 		reg.cr = (reg.cr & ~mkCR(1, ~0)) | mkCR(1, (fpscr>>28));
660 	}
661 	if(trace)
662 		itrace("%s%s\tfr%d,fr%d", ci->name, cc, rd, rb);
663 }
664 
665 ulong
666 setfpscr(void)
667 {
668 	ulong fps, fpscr;
669 
670 	fps = getfsr();
671 	fpscr = reg.fpscr;
672 	if(fps & FPAOVFL)
673 		fpscr |= FPS_OX;
674 	if(fps & FPAINEX)
675 		fpscr |= FPS_XX;
676 	if(fps & FPAUNFL)
677 		fpscr |= FPS_UX;
678 	if(fps & FPAZDIV)
679 		fpscr |= FPS_ZX;
680 	if(fpscr != reg.fpscr) {
681 		fpscr |= FPS_FX;
682 		reg.fpscr = fpscr;
683 	}
684 	return fpscr;
685 }
686 
687 void
688 setfpcc(double r)
689 {
690 	int c;
691 
692 	c = 0;
693 	if(r == 0)
694 		c |= 2;
695 	else if(r < 0)
696 		c |= 4;
697 	else
698 		c |= 8;
699 	if(isNaN(r))
700 		c |= 1;
701 	reg.fpscr = (reg.fpscr & ~0xF800) | (0<<15) | (c<<11); /* unsure about class bit */
702 }
703