Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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 *
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 *
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 *
237
reg(Ureg *ur, int r)		/* for faultmips */
238
{
239
	return &REG(ur, r);
240
}
241
 
242
static void
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
267
fadd(Internal *m, Internal *n, Internal *d)
268
{
269
	(m->s == n->s? fpiadd: fpisub)(m, n, d);
270
}
271
 
272
static void
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
692
decodefmt(int fmt)
693
{
694
	if (fmtnames[fmt])
695
		return fmtnames[fmt];
696
	else
697
		return "GOK";
698
}
699
 
700
static char *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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
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
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
}