1 /*
2 * this doesn't attempt to implement MIPS 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 * we emulate the original MIPS FP register model: 32-bits each,
8 * F(2n) and F(2n+1) are a double, with lower-order word first;
9 * note that this is little-endian order, unlike the rest of the
10 * machine, so double-word operations will need to swap the words
11 * when transferring between FP registers and memory.
12 *
13 * on some machines, we can convert to an FP internal representation when
14 * moving to FPU registers and back (to integer, for example) when moving
15 * from them. the MIPS is different: its conversion instructions operate
16 * on FP registers only, and there's no way to tell if data being moved
17 * into an FP register is integer or FP, so it must be possible to store
18 * integers in FP registers without conversion. Furthermore, pairs of FP
19 * registers can be combined into a double. So we keep the raw bits
20 * around as the canonical representation and convert only to and from
21 * Internal FP format when we must (i.e., before calling the common fpi
22 * code).
23 */
24 #include "u.h"
25 #include "../port/lib.h"
26 #include "mem.h"
27 #include "dat.h"
28 #include "fns.h"
29 #include "ureg.h"
30 #include "../port/fpi.h"
31 #include <tos.h>
32
33 #ifdef FPEMUDEBUG
34 #define DBG(bits) (fpemudebug & (bits))
35 #define intpr _intpr
36 #define internsane _internsane
37 #define dbgstuck _dbgstuck
38 #else
39 #define DBG(bits) (0)
40 #define internsane(i, ur) do { USED(ur); } while(0)
41 #define intpr(i, reg, fmt, ufp) do {} while(0)
42 #define dbgstuck(pc, ur, ufp) do {} while(0)
43 #endif
44
45 #define OFR(memb) (uintptr)&((Ureg*)0)->memb /* offset into Ureg of memb */
46 #define REG(ur, r) *acpureg(ur, r) /* cpu reg in Ureg */
47 #define FREG(ufp, fr) (ufp)->reg[(fr) & REGMASK] /* fp reg raw bits */
48
49 /*
50 * instruction decoding for COP1 instructions; integer instructions
51 * are laid out differently.
52 */
53 #define OP(ul) ((ul) >> 26)
54 #define REGMASK MASK(5) /* mask for a register number */
55 #define FMT(ul) (((ul) >> 21) & REGMASK) /* data type */
56 #define REGT(ul) (((ul) >> 16) & REGMASK) /* source2 register */
57 #define REGS(ul) (((ul) >> 11) & REGMASK) /* source1 register */
58 #define REGD(ul) (((ul) >> 6) & REGMASK) /* destination register */
59 #define FUNC(ul) ((ul) & MASK(6))
60
61 enum {
62 Dbgbasic = 1<<0, /* base debugging: ops, except'ns */
63 Dbgmoves = 1<<1, /* not very exciting usually */
64 Dbgregs = 1<<2, /* print register contents around ops */
65 Dbgdelay = 1<<3, /* branch-delay-slot-related machinery */
66
67 /* fpimips status codes */
68 Failed = -1,
69 Advpc, /* advance pc normally */
70 Leavepc, /* don't change the pc */
71 Leavepcret, /* ... and return to user mode now */
72 Nomatch,
73
74 /* no-ops */
75 NOP = 0x27, /* NOR R0, R0, R0 */
76 MIPSNOP = 0, /* SLL R0, R0, R0 */
77
78 /* fp op-codes */
79 COP1 = 0x11, /* fpu op */
80 LWC1 = 0x31, /* load float/long */
81 LDC1 = 0x35, /* load double/vlong */
82 SWC1 = 0x39, /* store float/long */
83 SDC1 = 0x3d, /* store double/vlong */
84
85 N = 1<<31, /* condition codes */
86 Z = 1<<30,
87 C = 1<<29,
88 V = 1<<28,
89
90 /* data types (format field values) */
91 MFC1 = 0, /* and func == 0 ... */
92 DMFC1, /* vlong move */
93 CFC1, /* ctl word move */
94 MTC1 = 4,
95 DMTC1,
96 CTC1, /* ... end `and func == 0' */
97 BRANCH = 8,
98 Ffloat = 16,
99 Fdouble,
100 Flong = 20,
101 Fvlong,
102
103 /* fp control registers */
104 Fpimp = 0,
105 Fpcsr = 31,
106 };
107
108 typedef struct FP1 FP1;
109 typedef struct FP2 FP2;
110 typedef struct FPcvt FPcvt;
111 typedef struct Instr Instr;
112
113 struct Instr { /* a COP1 instruction, broken out and registers converted */
114 int iw; /* whole word */
115 uintptr pc;
116 int o; /* opcode or cop1 func code */
117 int fmt; /* operand format */
118 int rm; /* first operand register */
119 int rn; /* second operand register */
120 int rd; /* destination register */
121
122 Internal *fm; /* converted from FREG(ufp, rm) */
123 Internal *fn;
124 char *dfmt;
125 FPsave *ufp; /* fp state, including fp registers */
126 Ureg *ur; /* user registers */
127 };
128
129 struct FP2 {
130 char* name;
131 void (*f)(Internal*, Internal*, Internal*);
132 };
133
134 struct FP1 {
135 char* name;
136 void (*f)(Internal*, Internal*);
137 };
138
139 struct FPcvt {
140 char* name;
141 void (*f)(int, int, int, Ureg *, FPsave *);
142 };
143
144 static int roff[32] = {
145 0, OFR(r1), OFR(r2), OFR(r3),
146 OFR(r4), OFR(r5), OFR(r6), OFR(r7),
147 OFR(r8), OFR(r9), OFR(r10), OFR(r11),
148 OFR(r12), OFR(r13), OFR(r14), OFR(r15),
149 OFR(r16), OFR(r17), OFR(r18), OFR(r19),
150 OFR(r20), OFR(r21), OFR(r22), OFR(r23),
151 OFR(r24), OFR(r25), OFR(r26), OFR(r27),
152 OFR(r28), OFR(sp), OFR(r30), OFR(r31),
153 };
154
155 /*
156 * plan 9 assumes F24 initialized to 0.0, F26 to 0.5, F28 to 1.0, F30 to 2.0.
157 */
158 enum {
159 FZERO = 24,
160 FHALF = 26,
161 };
162 static Internal fpconst[Nfpregs] = { /* indexed by register no. */
163 /* s, e, l, h */
164 [FZERO] {0, 0x1, 0x00000000, 0x00000000}, /* 0.0 */
165 [FHALF] {0, 0x3FE, 0x00000000, 0x08000000}, /* 0.5 */
166 [28] {0, 0x3FF, 0x00000000, 0x08000000}, /* 1.0 */
167 [30] {0, 0x400, 0x00000000, 0x08000000}, /* 2.0 */
168 };
169
170 static char *fmtnames[] = {
171 [MFC1] "MF",
172 [DMFC1] "DMF",
173 [CFC1] "CF",
174 [MTC1] "MT",
175 [DMTC1] "DMT",
176 [CTC1] "CT",
177 [BRANCH]"BR",
178
179 [Ffloat]"F",
180 [Fdouble]"D",
181 [Flong] "W",
182 [Fvlong]"L",
183 };
184
185 static char *prednames[] = {
186 [0] "F",
187 [1] "UN",
188 [2] "EQ",
189 [3] "UEQ",
190 [4] "OLT",
191 [5] "ULT",
192 [6] "OLE",
193 [7] "ULE",
194 [8] "SF",
195 [9] "NGLE",
196 [10] "SEQ",
197 [11] "NGL",
198 [12] "LT",
199 [13] "NGE",
200 [14] "LE",
201 [15] "NGT",
202 };
203
204 int fpemudebug = 0; /* settable via /dev/archctl */
205
206 static ulong dummyr0;
207 static QLock watchlock; /* lock for watch-points */
208
209 ulong branch(Ureg*, ulong);
210 int isbranch(ulong *);
211
212 static int fpimips(ulong, ulong, Ureg *, FPsave *);
213
214 char *
fpemuprint(char * p,char * ep)215 fpemuprint(char *p, char *ep)
216 {
217 #ifdef FPEMUDEBUG
218 return seprint(p, ep, "fpemudebug %d\n", fpemudebug);
219 #else
220 USED(ep);
221 return p;
222 #endif
223 }
224
225 static ulong *
acpureg(Ureg * ur,int r)226 acpureg(Ureg *ur, int r)
227 {
228 r &= REGMASK;
229 if (r == 0 || roff[r] == 0) {
230 dummyr0 = 0;
231 return &dummyr0;
232 }
233 return (ulong *)((char*)ur + roff[r]);
234 }
235
236 ulong *
reg(Ureg * ur,int r)237 reg(Ureg *ur, int r) /* for faultmips */
238 {
239 return ®(ur, r);
240 }
241
242 static void
_internsane(Internal * i,Ureg * ur)243 _internsane(Internal *i, Ureg *ur)
244 {
245 static char buf[ERRMAX];
246
247 USED(i);
248 if (!(DBG(Dbgbasic)))
249 return;
250 if ((unsigned)i->s > 1) {
251 snprint(buf, sizeof buf,
252 "fpuemu: bogus Internal sign at pc=%#p", ur->pc);
253 error(buf);
254 }
255 if ((unsigned)i->e > DoubleExpMax) {
256 snprint(buf, sizeof buf,
257 "fpuemu: bogus Internal exponent at pc=%#p", ur->pc);
258 error(buf);
259 }
260 }
261
262 /*
263 * mips binary operations (d = n operator m)
264 */
265
266 static void
fadd(Internal * m,Internal * n,Internal * d)267 fadd(Internal *m, Internal *n, Internal *d)
268 {
269 (m->s == n->s? fpiadd: fpisub)(m, n, d);
270 }
271
272 static void
fsub(Internal * m,Internal * n,Internal * d)273 fsub(Internal *m, Internal *n, Internal *d)
274 {
275 m->s ^= 1;
276 (m->s == n->s? fpiadd: fpisub)(m, n, d);
277 }
278
279 /*
280 * mips unary operations
281 */
282
283 static void
frnd(Internal * m,Internal * d)284 frnd(Internal *m, Internal *d)
285 {
286 short e;
287 Internal tmp;
288
289 tmp = fpconst[FHALF];
290 (m->s? fsub: fadd)(&tmp, m, d);
291 if(IsWeird(d))
292 return;
293 fpiround(d);
294 e = (d->e - ExpBias) + 1;
295 if(e <= 0)
296 SetZero(d);
297 else if(e > FractBits){
298 if(e < 2*FractBits)
299 d->l &= ~((1<<(2*FractBits - e))-1);
300 }else{
301 d->l = 0;
302 if(e < FractBits)
303 d->h &= ~((1<<(FractBits-e))-1);
304 }
305 }
306
307 /* debugging: print internal representation of an fp reg */
308 static void
_intpr(Internal * i,int reg,int fmt,FPsave * ufp)309 _intpr(Internal *i, int reg, int fmt, FPsave *ufp)
310 {
311 USED(i);
312 if (!(DBG(Dbgregs)))
313 return;
314 if (fmt == Fdouble && reg < 31)
315 iprint("\tD%02d: l %08lux h %08lux =\ts %d e %04d h %08lux l %08lux\n",
316 reg, FREG(ufp, reg), FREG(ufp, reg+1),
317 i->s, i->e, i->h, i->l);
318 else
319 iprint("\tF%02d: %08lux =\ts %d e %04d h %08lux l %08lux\n",
320 reg, FREG(ufp, reg),
321 i->s, i->e, i->h, i->l);
322 delay(75);
323 }
324
325 static void
dreg2dbl(Double * dp,int reg,FPsave * ufp)326 dreg2dbl(Double *dp, int reg, FPsave *ufp)
327 {
328 reg &= ~1;
329 dp->l = FREG(ufp, reg);
330 dp->h = FREG(ufp, reg+1);
331 }
332
333 static void
dbl2dreg(int reg,Double * dp,FPsave * ufp)334 dbl2dreg(int reg, Double *dp, FPsave *ufp)
335 {
336 reg &= ~1;
337 FREG(ufp, reg) = dp->l;
338 FREG(ufp, reg+1) = dp->h;
339 }
340
341 static void
vreg2dbl(Double * dp,int reg,FPsave * ufp)342 vreg2dbl(Double *dp, int reg, FPsave *ufp)
343 {
344 reg &= ~1;
345 dp->l = FREG(ufp, reg+1);
346 dp->h = FREG(ufp, reg);
347 }
348
349 static void
dbl2vreg(int reg,Double * dp,FPsave * ufp)350 dbl2vreg(int reg, Double *dp, FPsave *ufp)
351 {
352 reg &= ~1;
353 FREG(ufp, reg+1) = dp->l;
354 FREG(ufp, reg) = dp->h;
355 }
356
357 /* convert fmt (rm) to double (rd) */
358 static void
fcvtd(int fmt,int rm,int rd,Ureg * ur,FPsave * ufp)359 fcvtd(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp)
360 {
361 Double d;
362 Internal intrn;
363
364 switch (fmt) {
365 case Ffloat:
366 fpis2i(&intrn, &FREG(ufp, rm));
367 internsane(&intrn, ur);
368 fpii2d(&d, &intrn);
369 break;
370 case Fdouble:
371 dreg2dbl(&d, rm, ufp);
372 break;
373 case Flong:
374 fpiw2i(&intrn, &FREG(ufp, rm));
375 internsane(&intrn, ur);
376 fpii2d(&d, &intrn);
377 break;
378 case Fvlong:
379 vreg2dbl(&d, rm, ufp);
380 fpiv2i(&intrn, &d);
381 internsane(&intrn, ur);
382 fpii2d(&d, &intrn);
383 break;
384 }
385 dbl2dreg(rd, &d, ufp);
386 if (fmt != Fdouble && DBG(Dbgregs))
387 intpr(&intrn, rm, Fdouble, ufp);
388 }
389
390 /* convert fmt (rm) to single (rd) */
391 static void
fcvts(int fmt,int rm,int rd,Ureg * ur,FPsave * ufp)392 fcvts(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp)
393 {
394 Double d;
395 Internal intrn;
396
397 switch (fmt) {
398 case Ffloat:
399 FREG(ufp, rd) = FREG(ufp, rm);
400 break;
401 case Fdouble:
402 dreg2dbl(&d, rm, ufp);
403 fpid2i(&intrn, &d);
404 break;
405 case Flong:
406 fpiw2i(&intrn, &FREG(ufp, rm));
407 break;
408 case Fvlong:
409 vreg2dbl(&d, rm, ufp);
410 fpiv2i(&intrn, &d);
411 break;
412 }
413 if (fmt != Ffloat) {
414 if(DBG(Dbgregs))
415 intpr(&intrn, rm, Ffloat, ufp);
416 internsane(&intrn, ur);
417 fpii2s(&FREG(ufp, rd), &intrn);
418 }
419 }
420
421 /* convert fmt (rm) to long (rd) */
422 static void
fcvtw(int fmt,int rm,int rd,Ureg * ur,FPsave * ufp)423 fcvtw(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp)
424 {
425 Double d;
426 Internal intrn;
427
428 switch (fmt) {
429 case Ffloat:
430 fpis2i(&intrn, &FREG(ufp, rm));
431 break;
432 case Fdouble:
433 dreg2dbl(&d, rm, ufp);
434 fpid2i(&intrn, &d);
435 break;
436 case Flong:
437 FREG(ufp, rd) = FREG(ufp, rm);
438 break;
439 case Fvlong:
440 vreg2dbl(&d, rm, ufp);
441 fpiv2i(&intrn, &d);
442 break;
443 }
444 if (fmt != Flong) {
445 if(DBG(Dbgregs))
446 intpr(&intrn, rm, Flong, ufp);
447 internsane(&intrn, ur);
448 fpii2w((long *)&FREG(ufp, rd), &intrn);
449 }
450 }
451
452 /* convert fmt (rm) to vlong (rd) */
453 static void
fcvtv(int fmt,int rm,int rd,Ureg * ur,FPsave * ufp)454 fcvtv(int fmt, int rm, int rd, Ureg *ur, FPsave *ufp)
455 {
456 Double d;
457 Internal intrn;
458
459 switch (fmt) {
460 case Ffloat:
461 fpis2i(&intrn, &FREG(ufp, rm));
462 break;
463 case Fdouble:
464 dreg2dbl(&d, rm, ufp);
465 fpid2i(&intrn, &d);
466 break;
467 case Flong:
468 fpiw2i(&intrn, &FREG(ufp, rm));
469 break;
470 case Fvlong:
471 vreg2dbl(&d, rm, ufp);
472 dbl2vreg(rd, &d, ufp);
473 break;
474 }
475 if (fmt != Fvlong) {
476 if(DBG(Dbgregs))
477 intpr(&intrn, rm, Fvlong, ufp);
478 internsane(&intrn, ur);
479 fpii2v((vlong *)&FREG(ufp, rd), &intrn);
480 }
481 }
482
483 /*
484 * MIPS function codes
485 */
486
487 static FP2 optab2[] = { /* Fd := Fn OP Fm (binary) */
488 [0] {"ADDF", fadd}, /* can ignore fmt, just use doubles */
489 [1] {"SUBF", fsub},
490 [2] {"MULF", fpimul},
491 [3] {"DIVF", fpidiv},
492 };
493
494 static FP1 optab1[32] = { /* Fd := OP Fm (unary) */
495 [4] {"SQTF", /*fsqt*/0},
496 [5] {"ABSF", /*fabsf*/0}, /* inline in unaryemu... */
497 [6] {"MOVF", /*fmov*/0},
498 [7] {"NEGF", /*fmovn*/0},
499 [8] {"ROUND.L", /*froundl*/0}, /* 64-bit integer results ... */
500 [9] {"TRUNC.L", /*ftruncl*/0},
501 [10] {"CEIL.L", /*fceill*/0},
502 [11] {"FLOOR.L", /*ffloorl*/0},
503 [12] {"ROUND.W", frnd}, /* 32-bit integer results ... */
504 [13] {"TRUNC.W", /*ftrunc*/0},
505 [14] {"CEIL.W", /*fceil*/0},
506 [15] {"FLOOR.W", /*ffloor*/0},
507 /* 17—19 are newish MIPS32/64 conditional moves */
508 /* 21, 22, 28—31 are newish reciprocal or sqrt */
509 };
510
511 static FPcvt optabcvt[] = { /* Fd := OP(fmt, Fm) (unary) */
512 [32] {"CVT.S", fcvts}, /* must honour fmt as src format */
513 [33] {"CVT.D", fcvtd},
514 [36] {"CVT.W", fcvtw},
515 [37] {"CVT.L", fcvtv},
516 };
517
518 /*
519 * No type conversion is implied and the type of the cpu register is
520 * unknown, so copy the bits into reg.
521 * Later instructions will have to know the correct type and use the
522 * right format specifier to convert to or from Internal FP.
523 */
524 static void
fld(int d,ulong ea,int n,FPsave * ufp)525 fld(int d, ulong ea, int n, FPsave *ufp)
526 {
527 if(DBG(Dbgmoves))
528 iprint("MOV%c #%lux, F%d\n", n==8? 'D': 'F', ea, d);
529 if (n == 4)
530 memmove(&FREG(ufp, d), (void *)ea, 4);
531 else if (n == 8){
532 d &= ~1;
533 /* NB: we swap order of the words */
534 memmove(&FREG(ufp, d), (void *)(ea+4), 4);
535 memmove(&FREG(ufp, d+1), (void *)ea, 4);
536 } else
537 panic("fld: n (%d) not 4 nor 8", n);
538 }
539
540 static void
fst(ulong ea,int s,int n,FPsave * ufp)541 fst(ulong ea, int s, int n, FPsave *ufp)
542 {
543 if(DBG(Dbgmoves))
544 iprint("MOV%c F%d,#%lux\n", n==8? 'D': 'F', s, ea);
545 if (n == 4)
546 memmove((void *)ea, &FREG(ufp, s), 4);
547 else if (n == 8){
548 s &= ~1;
549 /* NB: we swap order of the words */
550 memmove((void *)(ea+4), &FREG(ufp, s), 4);
551 memmove((void *)ea, &FREG(ufp, s+1), 4);
552 } else
553 panic("fst: n (%d) not 4 nor 8", n);
554 }
555
556 void
unimp(ulong pc,ulong op,char * msg)557 unimp(ulong pc, ulong op, char *msg)
558 {
559 char buf[120];
560
561 snprint(buf, sizeof(buf), "sys: fp: pc=%#lux unimp fp %#.8lux: %s",
562 pc, op, msg);
563 if(DBG(Dbgbasic))
564 iprint("FPE: %s\n", buf);
565 error(buf);
566 /* no return */
567 }
568
569 static int
isfpop(ulong iw)570 isfpop(ulong iw)
571 {
572 switch (OP(iw)) {
573 case COP1:
574 case LWC1:
575 case LDC1:
576 case SWC1:
577 case SDC1:
578 return 1;
579 default:
580 return 0;
581 }
582 }
583
584 static int
ldst(ulong op,Ureg * ur,FPsave * ufp)585 ldst(ulong op, Ureg *ur, FPsave *ufp)
586 {
587 int rn, rd, o, size, wr;
588 short off;
589 ulong ea;
590
591 /* we're using the COP1 macros, but the fields have diff'nt meanings */
592 o = OP(op);
593 rn = FMT(op);
594 off = op;
595 ea = REG(ur, rn) + off;
596 rd = REGT(op);
597 //iprint("fpemu: ld/st (F%d)=%#lux + %d => ea %#lux\n", rn, REG(ur, rn), off, ea);
598
599 size = 4;
600 if (o == LDC1 || o == SDC1)
601 size = 8;
602 wr = (o == SWC1 || o == SDC1);
603 validaddr(ea, size, wr);
604
605 switch (o) {
606 case LWC1: /* load an fp register, rd, from memory */
607 case LDC1: /* load an fp register pair, (rd, rd+1), from memory */
608 fld(rd, ea, size, ufp);
609 break;
610 case SWC1: /* store an fp register, rd, into memory */
611 case SDC1: /* store an fp register pair, (rd, rd+1), into memory */
612 fst(ea, rd, size, ufp);
613 break;
614 default:
615 unimp(ur->pc, op, "unknown non-COP1 load or store");
616 return Failed;
617 }
618 return Advpc;
619 }
620
621 static int
cop1mov(Instr * ip)622 cop1mov(Instr *ip)
623 {
624 int fs, rt;
625 uvlong vl;
626 FPsave *ufp;
627 Ureg *ur;
628
629 fs = ip->rm; /* F(s) aka rm */
630 rt = ip->rn; /* R(t) aka rn */
631 ur = ip->ur;
632 ufp = ip->ufp;
633 //iprint("fpemu: cop1 prob ld/st (R%d)=%#lux FREG%d\n", rn, REG(ip->ur, rn), rm);
634
635 /* MIPS fp register pairs are in little-endian order: low word first */
636 switch (ip->fmt) {
637 case MTC1:
638 /* load an fp register, F(s), from cpu register R(t) */
639 fld(fs, (uintptr)®(ur, rt), 4, ufp);
640 return Advpc;
641 case DMTC1:
642 /*
643 * load an fp register pair, (F(s), F(s+1)),
644 * from cpu registers (rt, rt+1)
645 */
646 iprint("fpemu: 64-bit DMTC1 may have words backward\n");
647 rt &= ~1;
648 vl = (uvlong)REG(ur, rt+1) << 32 | REG(ur, rt);
649 fld(fs & ~1, (uintptr)&vl, 8, ufp);
650 return Advpc;
651 case MFC1:
652 /* store an fp register, fs, into a cpu register rt */
653 fst((uintptr)®(ur, rt), fs, 4, ufp);
654 return Advpc;
655 case DMFC1:
656 /*
657 * store an fp register pair, (F(s), F(s+1)),
658 * into cpu registers (rt, rt+1)
659 */
660 iprint("fpemu: 64-bit DMFC1 may have words backward\n");
661 fst((uintptr)&vl, fs & ~1, 8, ufp);
662 rt &= ~1;
663 REG(ur, rt) = (ulong)vl;
664 REG(ur, rt+1) = vl>>32;
665 return Advpc;
666 case CFC1:
667 switch (fs) {
668 case Fpimp: /* MOVW FCR0,Rn */
669 REG(ur, rt) = 0x500; /* claim to be r4k */
670 break;
671 case Fpcsr:
672 REG(ur, rt) = ufp->fpcontrol;
673 break;
674 }
675 if(DBG(Dbgbasic))
676 iprint("MOVW FCR%d, R%d\n", fs, rt);
677 return Advpc;
678 case CTC1:
679 switch (fs) {
680 case Fpcsr:
681 ufp->fpcontrol = REG(ur, rt);
682 break;
683 }
684 if(DBG(Dbgbasic))
685 iprint("MOVW R%d, FCR%d\n", rt, fs);
686 return Advpc;
687 }
688 return Nomatch; /* not a load or store; keep looking */
689 }
690
691 static char *
decodefmt(int fmt)692 decodefmt(int fmt)
693 {
694 if (fmtnames[fmt])
695 return fmtnames[fmt];
696 else
697 return "GOK";
698 }
699
700 static char *
predname(int pred)701 predname(int pred) /* predicate name */
702 {
703 if (prednames[pred])
704 return prednames[pred];
705 else
706 return "GOK";
707 }
708
709 static int
fcmpf(Internal m,Internal n,int,int cond)710 fcmpf(Internal m, Internal n, int, int cond)
711 {
712 int i;
713
714 if(IsWeird(&m) || IsWeird(&n)){
715 /* BUG: should trap if not masked */
716 return 0;
717 }
718 fpiround(&n);
719 fpiround(&m);
720 i = fpicmp(&m, &n); /* returns -1, 0, or 1 */
721 switch (cond) {
722 case 0: /* F - false */
723 case 1: /* UN - unordered */
724 return 0;
725 case 2: /* EQ */
726 case 3: /* UEQ */
727 return i == 0;
728 case 4: /* OLT */
729 case 5: /* ULT */
730 return i < 0;
731 case 6: /* OLE */
732 case 7: /* ULE */
733 return i <= 0;
734 case 8: /* SF */
735 case 9: /* NGLE - not >, < or = */
736 return 0;
737 case 10: /* SEQ */
738 return i == 0;
739 case 11: /* NGL */
740 return i != 0;
741 case 12: /* LT */
742 case 13: /* NGE */
743 return i < 0;
744 case 14: /* LE */
745 case 15: /* NGT */
746 return i <= 0;
747 }
748 return 0;
749 }
750
751 /*
752 * assuming that ur->pc points to a branch instruction,
753 * change it to point to the branch's target and return it.
754 */
755 static uintptr
followbr(Ureg * ur)756 followbr(Ureg *ur)
757 {
758 uintptr npc;
759
760 npc = branch(ur, up->fpsave.fpstatus);
761 if(npc == 0)
762 panic("fpemu: branch expected but not seen at %#p", ur->pc);
763 ur->pc = npc;
764 return npc;
765 }
766
767 /* emulate COP1 instruction in branch delay slot */
768 static void
dsemu(Instr * ip,ulong dsinsn,Ureg * ur,FPsave * ufp)769 dsemu(Instr *ip, ulong dsinsn, Ureg *ur, FPsave *ufp)
770 {
771 uintptr npc;
772
773 npc = ur->pc; /* save ur->pc since fpemu will change it */
774 if(DBG(Dbgdelay))
775 iprint(">>> emulating br delay slot\n");
776
777 fpimips(ip->pc + 4, dsinsn, ur, ufp);
778
779 if(DBG(Dbgdelay))
780 iprint("<<< done emulating br delay slot\n");
781 ur->pc = npc;
782 }
783
784 /*
785 * execute non-COP1 instruction in branch delay slot, in user mode with
786 * user registers, then trap so we can finish up and take the branch.
787 */
788 static void
dsexec(Instr * ip,Ureg * ur,FPsave * ufp)789 dsexec(Instr *ip, Ureg *ur, FPsave *ufp)
790 {
791 ulong dsaddr, wpaddr;
792 Tos *tos;
793
794 /*
795 * copy delay slot, EHB, EHB, EHB to tos->kscr, flush caches,
796 * point pc there, set watch point on tos->kscr[2], return.
797 * this is safe since we've already checked for branches (and FP
798 * instructions) in the delay slot, so the instruction can be
799 * executed at any address.
800 */
801 dsaddr = ip->pc + 4;
802 tos = (Tos*)(USTKTOP-sizeof(Tos));
803 tos->kscr[0] = *(ulong *)dsaddr;
804 tos->kscr[1] = 0xc0; /* EHB; we could use some trap instead */
805 tos->kscr[2] = 0xc0; /* EHB */
806 tos->kscr[3] = 0xc0; /* EHB */
807 dcflush(tos->kscr, sizeof tos->kscr);
808 icflush(tos->kscr, sizeof tos->kscr);
809
810 wpaddr = (ulong)&tos->kscr[2] & ~7; /* clear I/R/W bits */
811 ufp->fpdelayexec = 1;
812 ufp->fpdelaypc = ip->pc; /* remember branch ip->pc */
813 ufp->fpdelaysts = ufp->fpstatus; /* remember state of FPCOND */
814 ur->pc = (ulong)tos->kscr; /* restart in tos */
815 qlock(&watchlock); /* wait for first watchpoint */
816 setwatchlo0(wpaddr | 1<<2); /* doubleword addr(!); i-fetches only */
817 setwatchhi0(TLBPID(tlbvirt())<<16); /* asid; see mmu.c */
818 if (DBG(Dbgdelay))
819 iprint("fpemu: set %s watch point at %#lux, after br ds %#lux...",
820 up->text, wpaddr, *(ulong *)dsaddr);
821 /* return to user mode, await fpwatch() trap */
822 }
823
824 void
fpwatch(Ureg * ur)825 fpwatch(Ureg *ur) /* called on watch-point trap */
826 {
827 FPsave *ufp;
828
829 ufp = &up->fpsave;
830 if(ufp->fpdelayexec == 0)
831 panic("fpwatch: unexpected watch trap");
832
833 /* assume we got here after branch-delay-slot execution */
834 ufp->fpdelayexec = 0;
835 setwatchlo0(0);
836 setwatchhi0(0);
837 qunlock(&watchlock);
838
839 ur->pc = ufp->fpdelaypc; /* pc of fp branch */
840 ur->cause &= BD; /* take no chances */
841 ufp->fpstatus = ufp->fpdelaysts;
842 followbr(ur); /* sets ur->pc to fp branch target */
843 if (DBG(Dbgdelay))
844 iprint("delay slot executed; resuming at %#lux\n", ur->pc);
845 }
846
847 static ulong
validiw(uintptr pc)848 validiw(uintptr pc)
849 {
850 validaddr(pc, 4, 0);
851 return *(ulong*)pc;
852 }
853
854 /*
855 * COP1 (6) | BRANCH (5) | cc (3) | likely | true | offset(16)
856 * cc = ip->rn >> 2; // assume cc == 0
857 */
858 static int
bremu(Instr * ip)859 bremu(Instr *ip)
860 {
861 int off, taken;
862 ulong dsinsn;
863 FPsave *ufp;
864 Ureg *ur;
865
866 if (ip->iw & (1<<17))
867 error("fpuemu: `likely' fp branch (obs)");
868 ufp = ip->ufp;
869 if (ufp->fpstatus & FPCOND)
870 taken = ip->iw & (1<<16); /* taken iff BCT */
871 else
872 taken = !(ip->iw & (1<<16)); /* taken iff BCF */
873 dsinsn = validiw(ip->pc + 4); /* delay slot addressible? */
874 if(DBG(Dbgdelay)){
875 off = (short)(ip->iw & MASK(16));
876 iprint("BFP%c\t%d(PC): %staken\n", (ip->iw & (1<<16)? 'T': 'F'),
877 off, taken? "": "not ");
878 iprint("\tdelay slot: %08lux\n", dsinsn);
879 delay(75);
880 }
881 ur = ip->ur;
882 assert(ur->pc == ip->pc);
883 if(!taken)
884 return Advpc; /* didn't branch, so return to delay slot */
885
886 /*
887 * fp branch taken; emulate or execute the delay slot, then jump.
888 */
889 if(dsinsn == NOP || dsinsn == MIPSNOP){
890 ; /* delay slot does nothing */
891 }else if(isbranch((ulong *)(ip->pc + 4)))
892 error("fpuemu: branch in fp branch delay slot");
893 else if (isfpop(dsinsn))
894 dsemu(ip, dsinsn, ur, ufp); /* emulate delay slot */
895 else{
896 /*
897 * The hard case: we need to execute the delay slot
898 * in user mode with user registers. Set a watch point,
899 * return to user mode, await fpwatch() trap.
900 */
901 dsexec(ip, ur, ufp);
902 return Leavepcret;
903 }
904 followbr(ur);
905 return Leavepc;
906 }
907
908 /* interpret fp reg as fmt (float or double) and convert to Internal */
909 static void
reg2intern(Internal * i,int reg,int fmt,Ureg * ur)910 reg2intern(Internal *i, int reg, int fmt, Ureg *ur)
911 {
912 Double d;
913 FPsave *ufp;
914
915 /* we may see other fmt types on conversion or unary ops; ignore */
916 ufp = &up->fpsave;
917 switch (fmt) {
918 case Ffloat:
919 fpis2i(i, &FREG(ufp, reg));
920 internsane(i, ur);
921 break;
922 case Fdouble:
923 dreg2dbl(&d, reg, ufp);
924 fpid2i(i, &d);
925 internsane(i, ur);
926 break;
927 default:
928 SetQNaN(i); /* cause trouble if we try to use i */
929 break;
930 }
931 }
932
933 /* convert Internal to fp reg as fmt (float or double) */
934 static void
intern2reg(int reg,Internal * i,int fmt,Ureg * ur)935 intern2reg(int reg, Internal *i, int fmt, Ureg *ur)
936 {
937 Double d;
938 FPsave *ufp;
939 Internal tmp;
940
941 ufp = &up->fpsave;
942 tmp = *i; /* make a disposable copy */
943 internsane(&tmp, ur);
944 switch (fmt) {
945 case Ffloat:
946 fpii2s(&FREG(ufp, reg), &tmp);
947 break;
948 case Fdouble:
949 fpii2d(&d, &tmp);
950 dbl2dreg(reg, &d, ufp);
951 break;
952 default:
953 panic("intern2reg: bad fmt %d", fmt);
954 }
955 }
956
957 /*
958 * comparisons - encoded slightly differently than arithmetic:
959 * COP1 (6) | fmt(5) | ft (5) | fs (5) | # same
960 * cc (3) | 0 | A=0 | # diff, was REGD
961 * FC=11 | cond (4) # FUNC
962 */
963 static int
cmpemu(Instr * ip)964 cmpemu(Instr *ip)
965 {
966 int cc, cond;
967
968 cc = ip->rd >> 2;
969 cond = ip->o & MASK(4);
970 reg2intern(ip->fn, ip->rn, ip->fmt, ip->ur);
971 /* fpicmp args are swapped, so this is `n compare m' */
972 if (fcmpf(*ip->fm, *ip->fn, cc, cond))
973 ip->ufp->fpstatus |= FPCOND;
974 else
975 ip->ufp->fpstatus &= ~FPCOND;
976 if(DBG(Dbgbasic))
977 iprint("CMP%s.%s F%d,F%d =%d\n", predname(cond), ip->dfmt,
978 ip->rm, ip->rn, (ip->ufp->fpstatus & FPCOND? 1: 0));
979 if(DBG(Dbgregs)) {
980 intpr(ip->fm, ip->rm, ip->fmt, ip->ufp);
981 intpr(ip->fn, ip->rn, ip->fmt, ip->ufp);
982 delay(75);
983 }
984 return Advpc;
985 }
986
987 static int
binemu(Instr * ip)988 binemu(Instr *ip)
989 {
990 FP2 *fp;
991 Internal fd, prfd;
992 Internal *fn;
993
994 fp = &optab2[ip->o];
995 if(fp->f == nil)
996 unimp(ip->pc, ip->iw, "missing binary op");
997
998 /* convert the second operand */
999 fn = ip->fn;
1000 reg2intern(fn, ip->rn, ip->fmt, ip->ur);
1001 if(DBG(Dbgregs))
1002 intpr(fn, ip->rn, ip->fmt, ip->ufp);
1003
1004 if(DBG(Dbgbasic)){
1005 iprint("%s.%s\tF%d,F%d,F%d\n", fp->name, ip->dfmt,
1006 ip->rm, ip->rn, ip->rd);
1007 delay(75);
1008 }
1009 /*
1010 * fn and fm are scratch Internals just for this instruction,
1011 * so it's okay to let the fpi routines trash them in the course
1012 * of operation.
1013 */
1014 /* NB: fpi routines take m and n (s and t) in reverse order */
1015 (*fp->f)(fn, ip->fm, &fd);
1016
1017 /* convert the result */
1018 if(DBG(Dbgregs))
1019 prfd = fd; /* intern2reg modifies fd */
1020 intern2reg(ip->rd, &fd, ip->fmt, ip->ur);
1021 if(DBG(Dbgregs))
1022 intpr(&prfd, ip->rd, ip->fmt, ip->ufp);
1023 return Advpc;
1024 }
1025
1026 static int
unaryemu(Instr * ip)1027 unaryemu(Instr *ip)
1028 {
1029 int o;
1030 FP1 *fp;
1031 FPsave *ufp;
1032
1033 o = ip->o;
1034 fp = &optab1[o];
1035 if(DBG(Dbgbasic)){
1036 iprint("%s.%s\tF%d,F%d\n", fp->name, ip->dfmt, ip->rm, ip->rd);
1037 delay(75);
1038 }
1039 if(o == 6){ /* MOV */
1040 int rm, rd;
1041
1042 ufp = ip->ufp;
1043 rd = ip->rd;
1044 rm = ip->rm;
1045 if(ip->fmt == Fdouble){
1046 rd &= ~1;
1047 rm &= ~1;
1048 FREG(ufp, rd+1) = FREG(ufp, rm+1);
1049 }
1050 FREG(ufp, rd) = FREG(ufp, rm);
1051 }else{
1052 Internal fdint, prfd;
1053 Internal *fd;
1054
1055 switch(o){
1056 case 5: /* ABS */
1057 fd = ip->fm; /* use src Internal as dest */
1058 fd->s = 0;
1059 break;
1060 case 7: /* NEG */
1061 fd = ip->fm; /* use src Internal as dest */
1062 fd->s ^= 1;
1063 break;
1064 default:
1065 if(fp->f == nil)
1066 unimp(ip->pc, ip->iw, "missing unary op");
1067 fd = &fdint;
1068 (*fp->f)(ip->fm, fd);
1069 break;
1070 }
1071 if(DBG(Dbgregs))
1072 prfd = *fd; /* intern2reg modifies fd */
1073 intern2reg(ip->rd, fd, ip->fmt, ip->ur);
1074 if(DBG(Dbgregs))
1075 intpr(&prfd, ip->rd, ip->fmt, ip->ufp);
1076 }
1077 return Advpc;
1078 }
1079
1080 static int
cvtemu(Instr * ip)1081 cvtemu(Instr *ip)
1082 {
1083 FPcvt *fp;
1084
1085 fp = &optabcvt[ip->o];
1086 if(fp->f == nil)
1087 unimp(ip->pc, ip->iw, "missing conversion op");
1088 if(DBG(Dbgbasic)){
1089 iprint("%s.%s\tF%d,F%d\n", fp->name, ip->dfmt, ip->rm, ip->rd);
1090 delay(75);
1091 }
1092 (*fp->f)(ip->fmt, ip->rm, ip->rd, ip->ur, ip->ufp);
1093 return Advpc;
1094 }
1095
1096 static void
cop1decode(Instr * ip,ulong iw,ulong pc,Ureg * ur,FPsave * ufp,Internal * imp,Internal * inp)1097 cop1decode(Instr *ip, ulong iw, ulong pc, Ureg *ur, FPsave *ufp,
1098 Internal *imp, Internal *inp)
1099 {
1100 ip->iw = iw;
1101 ip->pc = pc;
1102 ip->ur = ur;
1103 ip->ufp = ufp;
1104 ip->fmt = FMT(iw);
1105 ip->rm = REGS(iw); /* 1st operand */
1106 ip->rn = REGT(iw); /* 2nd operand (ignored by unary ops) */
1107 ip->rd = REGD(iw); /* destination */
1108 ip->o = FUNC(iw);
1109 ip->fm = imp;
1110 ip->fn = inp;
1111 if (DBG(Dbgbasic))
1112 ip->dfmt = decodefmt(ip->fmt);
1113 }
1114
1115 void
fpstuck(uintptr pc,FPsave * fp)1116 fpstuck(uintptr pc, FPsave *fp)
1117 {
1118 USED(pc);
1119 if(!(DBG(Dbgbasic)))
1120 return;
1121 if (fp->fppc == pc) {
1122 fp->fpcnt++;
1123 if (fp->fpcnt > 4)
1124 panic("fpuemu: cpu%d stuck at pid %ld %s pc %#p "
1125 "instr %#8.8lux", m->machno, up->pid, up->text,
1126 pc, *(ulong *)pc);
1127 } else {
1128 fp->fppc = pc;
1129 fp->fpcnt = 0;
1130 }
1131 }
1132
1133 static void
_dbgstuck(ulong pc,Ureg * ur,FPsave * ufp)1134 _dbgstuck(ulong pc, Ureg *ur, FPsave *ufp)
1135 {
1136 fpstuck(pc, ufp);
1137 if (DBG(Dbgdelay) && ur->cause & BD)
1138 iprint("fpuemu: FP in a branch delay slot\n");
1139 }
1140
1141 /* decode the opcode and call common emulation code */
1142 static int
fpimips(ulong pc,ulong op,Ureg * ur,FPsave * ufp)1143 fpimips(ulong pc, ulong op, Ureg *ur, FPsave *ufp)
1144 {
1145 int r, o;
1146 Instr insn;
1147 Instr *ip;
1148 Internal im, in;
1149
1150 /* note: would update fault status here if we noted numeric exceptions */
1151 dummyr0 = 0;
1152 switch (OP(op)) {
1153 case LWC1:
1154 case LDC1:
1155 case SWC1:
1156 case SDC1:
1157 dbgstuck(pc, ur, ufp);
1158 return ldst(op, ur, ufp);
1159 default:
1160 unimp(pc, op, "non-FP instruction");
1161 return Failed;
1162 case COP1:
1163 dbgstuck(pc, ur, ufp);
1164 break;
1165 }
1166
1167 ip = &insn;
1168 cop1decode(ip, op, pc, ur, ufp, &im, &in);
1169 if (ip->fmt == BRANCH) { /* FP conditional branch? */
1170 r = bremu(ip);
1171 if(DBG(Dbgdelay)){
1172 iprint("resuming after br, at %#lux", ur->pc);
1173 if (r == Leavepcret)
1174 iprint("..."); /* we'll be right back */
1175 else
1176 iprint("\n");
1177 }
1178 return r;
1179 }
1180 o = ip->o;
1181 if (o == 0 && ip->rd == 0) { /* *[TF]C1 load or store? */
1182 r = cop1mov(ip);
1183 if (r != Nomatch)
1184 return r;
1185 /* else wasn't a [tf]c1 move */
1186 }
1187 /* don't decode & print rm yet; it might be an integer */
1188 if(o >= 32 && o < 40) /* conversion? */
1189 return cvtemu(ip);
1190
1191 /* decode the mandatory operand, rm */
1192 reg2intern(ip->fm, ip->rm, ip->fmt, ip->ur);
1193 if(DBG(Dbgregs))
1194 intpr(&im, ip->rm, ip->fmt, ip->ufp);
1195
1196 /*
1197 * arithmetic
1198 * all operands must be of the same format
1199 */
1200 if(o >= 4 && o < 32) /* monadic */
1201 return unaryemu(ip);
1202 if(o < 4) /* the few binary ops */
1203 return binemu(ip);
1204
1205 if(o >= 48 && (ip->rd & MASK(2)) == 0) /* comparison? */
1206 return cmpemu(ip);
1207
1208 /* don't recognise the opcode */
1209 if(DBG(Dbgbasic))
1210 iprint("fp at %#lux: %#8.8lux BOGON\n", pc, op);
1211 unimp(pc, op, "unknown opcode");
1212 return Failed;
1213 }
1214
1215 static FPsave *
fpinit(Ureg * ur)1216 fpinit(Ureg *ur)
1217 {
1218 int i, n;
1219 Double d;
1220 FPsave *ufp;
1221 Internal tmp;
1222
1223 /*
1224 * because all the emulated fp state is in the proc structure,
1225 * it need not be saved/restored
1226 */
1227 ufp = &up->fpsave;
1228 switch(up->fpstate){
1229 case FPactive:
1230 case FPinactive:
1231 error("fpu (in)active but fp is emulated");
1232 case FPinit:
1233 up->fpstate = FPemu;
1234 ufp->fpcontrol = 0;
1235 ufp->fpstatus = 0;
1236 ufp->fpcnt = 0;
1237 ufp->fppc = 0;
1238 for(n = 0; n < Nfpregs-1; n += 2) {
1239 if (fpconst[n].h == 0) /* uninitialised consts */
1240 i = FZERO; /* treated as 0.0 */
1241 else
1242 i = n;
1243 tmp = fpconst[i];
1244 internsane(&tmp, ur);
1245 fpii2d(&d, &tmp);
1246 dbl2dreg(n, &d, ufp);
1247 }
1248 break;
1249 }
1250 return ufp;
1251 }
1252
1253 /*
1254 * called from trap.c's CCPU case, only to deal with user-mode
1255 * instruction faults.
1256 *
1257 * libc/mips/lock.c reads FCR0 to determine what kind of system
1258 * this is (and thus if it can use LL/SC or must use some
1259 * system-dependent method). So we simulate the move from FCR0.
1260 * All modern mips have LL/SC, so just claim to be an r4k.
1261 */
1262 int
fpuemu(Ureg * ureg)1263 fpuemu(Ureg *ureg)
1264 {
1265 int s;
1266 uintptr pc;
1267 ulong iw, r;
1268
1269 if(waserror()){
1270 postnote(up, 1, up->errstr, NDebug);
1271 return -1;
1272 }
1273
1274 if(up->fpstate & FPillegal)
1275 error("floating point in note handler");
1276 if(up->fpsave.fpdelayexec)
1277 panic("fpuemu: entered with outstanding watch trap");
1278
1279 pc = ureg->pc;
1280 validaddr(pc, 4, 0);
1281 /* only the first instruction can be in a branch delay slot */
1282 if(ureg->cause & BD) {
1283 pc += 4;
1284 validaddr(pc, 4, 0); /* check branch delay slot */
1285 }
1286 iw = *(ulong*)pc;
1287 do {
1288 /* recognise & optimise a common case */
1289 if (iw == 0x44410000){ /* MOVW FCR0,R1 (CFC1) */
1290 ureg->r1 = 0x500; /* claim an r4k */
1291 r = Advpc;
1292 if (DBG(Dbgbasic))
1293 iprint("faked MOVW FCR0,R1\n");
1294 }else{
1295 s = spllo();
1296 if(waserror()){
1297 splx(s);
1298 nexterror();
1299 }
1300 r = fpimips(pc, iw, ureg, fpinit(ureg));
1301 splx(s);
1302 poperror();
1303 if (r == Failed || r == Leavepcret)
1304 break;
1305 }
1306 if (r == Advpc) /* simulation succeeded, advance the pc? */
1307 if(ureg->cause & BD)
1308 followbr(ureg);
1309 else
1310 ureg->pc += 4;
1311 ureg->cause &= ~BD;
1312
1313 pc = ureg->pc;
1314 iw = validiw(pc);
1315 while (iw == NOP || iw == MIPSNOP) { /* skip NOPs */
1316 pc += 4;
1317 ureg->pc = pc;
1318 iw = validiw(pc);
1319 }
1320 /* is next ins'n also FP? */
1321 } while (isfpop(iw));
1322 if (r == Failed){
1323 iprint("fpuemu: fp emulation failed for %#lux"
1324 " at pc %#p in %lud %s\n",
1325 iw, ureg->pc, up->pid, up->text);
1326 unimp(ureg->pc, iw, "no fp instruction");
1327 /* no return */
1328 }
1329 ureg->cause &= ~BD;
1330 poperror();
1331 return 0;
1332 }
1333
1334 int
isbranch(ulong * pc)1335 isbranch(ulong *pc)
1336 {
1337 ulong iw;
1338
1339 iw = *(ulong*)pc;
1340 /*
1341 * Integer unit jumps first
1342 */
1343 switch(iw>>26){
1344 case 0: /* SPECIAL: JR or JALR */
1345 switch(iw&0x3F){
1346 case 0x09: /* JALR */
1347 case 0x08: /* JR */
1348 return 1;
1349 default:
1350 return 0;
1351 }
1352 case 1: /* BCOND */
1353 switch((iw>>16) & 0x1F){
1354 case 0x10: /* BLTZAL */
1355 case 0x00: /* BLTZ */
1356 case 0x11: /* BGEZAL */
1357 case 0x01: /* BGEZ */
1358 return 1;
1359 default:
1360 return 0;
1361 }
1362 case 3: /* JAL */
1363 case 2: /* JMP */
1364 case 4: /* BEQ */
1365 case 5: /* BNE */
1366 case 6: /* BLEZ */
1367 case 7: /* BGTZ */
1368 return 1;
1369 }
1370 /*
1371 * Floating point unit jumps
1372 */
1373 if((iw>>26) == COP1)
1374 switch((iw>>16) & 0x3C1){
1375 case 0x101: /* BCT */
1376 case 0x181: /* BCT */
1377 case 0x100: /* BCF */
1378 case 0x180: /* BCF */
1379 return 1;
1380 }
1381 return 0;
1382 }
1383
1384 /*
1385 * if current instruction is a (taken) branch, return new pc and,
1386 * for jump-and-links, set r31.
1387 */
1388 ulong
branch(Ureg * ur,ulong fcr31)1389 branch(Ureg *ur, ulong fcr31)
1390 {
1391 ulong iw, npc, rs, rt, rd, offset, targ, next;
1392
1393 iw = ur->pc;
1394 iw = *(ulong*)iw;
1395 rs = (iw>>21) & 0x1F;
1396 if(rs)
1397 rs = REG(ur, rs);
1398 rt = (iw>>16) & 0x1F;
1399 if(rt)
1400 rt = REG(ur, rt);
1401 offset = iw & ((1<<16)-1);
1402 if(offset & (1<<15)) /* sign extend */
1403 offset |= ~((1<<16)-1);
1404 offset <<= 2;
1405 targ = ur->pc + 4 + offset; /* branch target */
1406 /* ins'n after delay slot (assumes delay slot has already been exec'd) */
1407 next = ur->pc + 8;
1408 /*
1409 * Integer unit jumps first
1410 */
1411 switch(iw>>26){
1412 case 0: /* SPECIAL: JR or JALR */
1413 switch(iw&0x3F){
1414 case 0x09: /* JALR */
1415 rd = (iw>>11) & 0x1F;
1416 if(rd)
1417 REG(ur, rd) = next;
1418 /* fall through */
1419 case 0x08: /* JR */
1420 return rs;
1421 default:
1422 return 0;
1423 }
1424 case 1: /* BCOND */
1425 switch((iw>>16) & 0x1F){
1426 case 0x10: /* BLTZAL */
1427 ur->r31 = next;
1428 /* fall through */
1429 case 0x00: /* BLTZ */
1430 if((long)rs < 0)
1431 return targ;
1432 return next;
1433 case 0x11: /* BGEZAL */
1434 ur->r31 = next;
1435 /* fall through */
1436 case 0x01: /* BGEZ */
1437 if((long)rs >= 0)
1438 return targ;
1439 return next;
1440 default:
1441 return 0;
1442 }
1443 case 3: /* JAL */
1444 ur->r31 = next;
1445 /* fall through */
1446 case 2: /* JMP */
1447 npc = iw & ((1<<26)-1);
1448 npc <<= 2;
1449 return npc | (ur->pc&0xF0000000);
1450 case 4: /* BEQ */
1451 if(rs == rt)
1452 return targ;
1453 return next;
1454 case 5: /* BNE */
1455 if(rs != rt)
1456 return targ;
1457 return next;
1458 case 6: /* BLEZ */
1459 if((long)rs <= 0)
1460 return targ;
1461 return next;
1462 case 7: /* BGTZ */
1463 if((long)rs > 0)
1464 return targ;
1465 return next;
1466 }
1467 /*
1468 * Floating point unit jumps
1469 */
1470 if((iw>>26) == COP1)
1471 switch((iw>>16) & 0x3C1){
1472 case 0x101: /* BCT */
1473 case 0x181: /* BCT */
1474 if(fcr31 & FPCOND)
1475 return targ;
1476 return next;
1477 case 0x100: /* BCF */
1478 case 0x180: /* BCF */
1479 if(!(fcr31 & FPCOND))
1480 return targ;
1481 return next;
1482 }
1483 /* shouldn't get here */
1484 return 0;
1485 }
1486