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