xref: /plan9/sys/src/9/rb/fpimips.c (revision f43f8ee646e2cb29aea7fd7bb5fc7318a3f4921f)
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 &REG(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)&REG(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)&REG(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