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