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