1 /* $OpenBSD: fpu.c,v 1.26 2024/03/29 21:14:31 miod Exp $ */
2 /* $NetBSD: fpu.c,v 1.11 2000/12/06 01:47:50 mrg Exp $ */
3
4 /*
5 * Copyright (c) 2001 Jason L. Wright (jason@thought.net)
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 /*
31 * Copyright (c) 1992, 1993
32 * The Regents of the University of California. All rights reserved.
33 *
34 * This software was developed by the Computer Systems Engineering group
35 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
36 * contributed to Berkeley.
37 *
38 * All advertising materials mentioning features or use of this software
39 * must display the following acknowledgement:
40 * This product includes software developed by the University of
41 * California, Lawrence Berkeley Laboratory.
42 *
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
45 * are met:
46 * 1. Redistributions of source code must retain the above copyright
47 * notice, this list of conditions and the following disclaimer.
48 * 2. Redistributions in binary form must reproduce the above copyright
49 * notice, this list of conditions and the following disclaimer in the
50 * documentation and/or other materials provided with the distribution.
51 * 3. Neither the name of the University nor the names of its contributors
52 * may be used to endorse or promote products derived from this software
53 * without specific prior written permission.
54 *
55 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
56 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
57 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
58 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
59 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
60 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
61 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
63 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
64 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
65 * SUCH DAMAGE.
66 *
67 * @(#)fpu.c 8.1 (Berkeley) 6/11/93
68 */
69
70 #include <sys/param.h>
71 #include <sys/proc.h>
72 #include <sys/signal.h>
73 #include <sys/systm.h>
74 #include <sys/syslog.h>
75 #include <sys/signalvar.h>
76
77 #include <machine/instr.h>
78 #include <machine/fsr.h>
79 #include <machine/reg.h>
80
81 #include <sparc64/fpu/fpu_emu.h>
82 #include <sparc64/fpu/fpu_extern.h>
83
84 int fpu_regoffset(int, int);
85 int fpu_insn_fmov(struct fpstate *, struct fpemu *, union instr);
86 int fpu_insn_fabs(struct fpstate *, struct fpemu *, union instr);
87 int fpu_insn_fneg(struct fpstate *, struct fpemu *, union instr);
88 int fpu_insn_itof(struct fpemu *, union instr, int, int *,
89 int *, u_int *);
90 int fpu_insn_ftoi(struct fpemu *, union instr, int *, int, u_int *);
91 int fpu_insn_ftof(struct fpemu *, union instr, int *, int *, u_int *);
92 int fpu_insn_fsqrt(struct fpemu *, union instr, int *, int *, u_int *);
93 int fpu_insn_fcmp(struct fpstate *, struct fpemu *, union instr, int);
94 int fpu_insn_fmul(struct fpemu *, union instr, int *, int *, u_int *);
95 int fpu_insn_fmulx(struct fpemu *, union instr, int *, int *, u_int *);
96 int fpu_insn_fdiv(struct fpemu *, union instr, int *, int *, u_int *);
97 int fpu_insn_fadd(struct fpemu *, union instr, int *, int *, u_int *);
98 int fpu_insn_fsub(struct fpemu *, union instr, int *, int *, u_int *);
99 int fpu_insn_fmovcc(struct proc *, struct fpstate *, union instr);
100 int fpu_insn_fmovr(struct proc *, struct fpstate *, union instr);
101 void fpu_fcopy(u_int *, u_int *, int);
102
103 #ifdef DEBUG
104 int fpe_debug = 0;
105
106 /*
107 * Dump a `fpn' structure.
108 */
109 void
fpu_dumpfpn(struct fpn * fp)110 fpu_dumpfpn(struct fpn *fp)
111 {
112 static char *class[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" };
113
114 printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2],
115 fp->fp_sign ? '-' : ' ', fp->fp_mant[0], fp->fp_mant[1],
116 fp->fp_mant[2], fp->fp_mant[3], fp->fp_exp);
117 }
118 void
fpu_dumpstate(struct fpstate * fs)119 fpu_dumpstate(struct fpstate *fs)
120 {
121 int i;
122
123 for (i = 0; i < 64; i++)
124 printf("%%f%02d: %08x%s",
125 i, fs->fs_regs[i], ((i & 3) == 3) ? "\n" : " ");
126 }
127 #endif
128
129 /*
130 * fpu_execute returns the following error numbers (0 = no error):
131 */
132 #define FPE 1 /* take a floating point exception */
133 #define NOTFPU 2 /* not an FPU instruction */
134
135 /*
136 * Translate current exceptions into `first' exception. The
137 * bits go the wrong way for ffs() (0x10 is most important, etc).
138 * There are only 5, so do it the obvious way.
139 */
140 #define X1(x) x
141 #define X2(x) x,x
142 #define X4(x) x,x,x,x
143 #define X8(x) X4(x),X4(x)
144 #define X16(x) X8(x),X8(x)
145
146 static const char cx_to_trapx[] = {
147 X1(FSR_NX),
148 X2(FSR_DZ),
149 X4(FSR_UF),
150 X8(FSR_OF),
151 X16(FSR_NV)
152 };
153 static const u_char fpu_codes[] = {
154 X1(FPE_FLTINEX_TRAP),
155 X2(FPE_FLTDIV_TRAP),
156 X4(FPE_FLTUND_TRAP),
157 X8(FPE_FLTOVF_TRAP),
158 X16(FPE_FLTOPERR_TRAP)
159 };
160
161 static const int fpu_types[] = {
162 X1(FPE_FLTRES),
163 X2(FPE_FLTDIV),
164 X4(FPE_FLTUND),
165 X8(FPE_FLTOVF),
166 X16(FPE_FLTINV)
167 };
168
169 void
fpu_fcopy(u_int * src,u_int * dst,int type)170 fpu_fcopy(u_int *src, u_int *dst, int type)
171 {
172 *dst++ = *src++;
173 if (type == FTYPE_SNG || type == FTYPE_INT)
174 return;
175 *dst++ = *src++;
176 if (type != FTYPE_EXT)
177 return;
178 *dst++ = *src++;
179 *dst = *src;
180 }
181
182 /*
183 * The FPU gave us an exception. Clean up the mess.
184 */
185 void
fpu_cleanup(struct proc * p,struct fpstate * fs,union instr instr,union sigval sv)186 fpu_cleanup(struct proc *p, struct fpstate *fs, union instr instr,
187 union sigval sv)
188 {
189 int i, fsr = fs->fs_fsr, error;
190 struct fpemu fe;
191
192 switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) {
193 case FSR_TT_NONE:
194 #ifdef DEBUG
195 printf("fpu_cleanup: invoked although no exception\n");
196 #endif
197 return;
198 case FSR_TT_IEEE:
199 if ((i = fsr & FSR_CX) == 0)
200 panic("fpu ieee trap, but no exception");
201 trapsignal(p, SIGFPE, fpu_codes[i - 1], fpu_types[i - 1], sv);
202 return;
203 case FSR_TT_UNFIN:
204 if (instr.i_int == 0) {
205 #ifdef DEBUG
206 printf("fpu_cleanup: unfinished fpop\n");
207 #endif
208 return;
209 }
210 break;
211 case FSR_TT_UNIMP:
212 if (instr.i_int == 0)
213 panic("fpu_cleanup: unimplemented fpop without insn");
214 break;
215 case FSR_TT_SEQ:
216 panic("fpu sequence error");
217 /* NOTREACHED */
218 case FSR_TT_HWERR:
219 log(LOG_ERR, "fpu hardware error (%s[%d])\n",
220 p->p_p->ps_comm, p->p_p->ps_pid);
221 uprintf("%s[%d]: fpu hardware error\n",
222 p->p_p->ps_comm, p->p_p->ps_pid);
223 trapsignal(p, SIGFPE, -1, FPE_FLTINV, sv); /* ??? */
224 return;
225 default:
226 printf("fsr=0x%x\n", fsr);
227 panic("fpu error");
228 }
229
230 /* emulate the instructions left in the queue */
231 fe.fe_fpstate = fs;
232 if (instr.i_any.i_op != IOP_reg ||
233 (instr.i_op3.i_op3 != IOP3_FPop1 &&
234 instr.i_op3.i_op3 != IOP3_FPop2))
235 panic("bogus fpu instruction to emulate");
236 error = fpu_execute(p, &fe, instr);
237 switch (error) {
238 case 0:
239 break;
240 case FPE:
241 trapsignal(p, SIGFPE,
242 fpu_codes[(fs->fs_fsr & FSR_CX) - 1],
243 fpu_types[(fs->fs_fsr & FSR_CX) - 1], sv);
244 break;
245 case NOTFPU:
246 default:
247 trapsignal(p, SIGILL, 0, ILL_COPROC, sv);
248 break;
249 }
250 }
251
252 /*
253 * Compute offset given a register and type. For 32 bit sparc, bits 1 and 0
254 * must be zero for ext types, and bit 0 must be 0 for double and long types.
255 * For 64bit sparc, bit 1 must be zero for quad types, and bit 0 becomes bit
256 * 5 in the register offset for long, double, and quad types.
257 */
258 int
fpu_regoffset(int rx,int type)259 fpu_regoffset(int rx, int type)
260 {
261 if (type == FTYPE_LNG || type == FTYPE_DBL || type == FTYPE_EXT) {
262 rx |= (rx & 1) << 5;
263 rx &= 0x3e;
264 if ((type == FTYPE_EXT) && (rx & 2))
265 return (-1);
266 }
267 return (rx);
268 }
269
270 /*
271 * Execute an FPU instruction (one that runs entirely in the FPU; not
272 * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be
273 * modified to reflect the setting the hardware would have left.
274 */
275 int
fpu_execute(struct proc * p,struct fpemu * fe,union instr instr)276 fpu_execute(struct proc *p, struct fpemu *fe, union instr instr)
277 {
278 struct fpstate *fs;
279 int opf, rdtype, rd, err, mask, cx, fsr;
280 u_int space[4];
281
282 DPRINTF(FPE_INSN, ("op3: %x, opf %x\n", instr.i_opf.i_op3,
283 instr.i_opf.i_opf));
284 DPRINTF(FPE_STATE, ("BEFORE:\n"));
285 DUMPSTATE(FPE_STATE, fe->fe_fpstate);
286 opf = instr.i_opf.i_opf;
287 fs = fe->fe_fpstate;
288 fe->fe_fsr = fs->fs_fsr & ~FSR_CX;
289 fe->fe_cx = 0;
290
291 if ((instr.i_int & 0xc0000000) != 0x80000000)
292 return (NOTFPU);
293
294 if (instr.i_opf.i_op3 == IOP3_FPop2) {
295 switch (opf) {
296 case FCMPS: case FCMPD: case FCMPQ:
297 return (fpu_insn_fcmp(fs, fe, instr, 0));
298
299 case FCMPES: case FCMPED: case FCMPEQ:
300 return (fpu_insn_fcmp(fs, fe, instr, 1));
301
302 case FMVFC0S: case FMVFC0D: case FMVFC0Q:
303 case FMVFC1S: case FMVFC1D: case FMVFC1Q:
304 case FMVFC2S: case FMVFC2D: case FMVFC2Q:
305 case FMVFC3S: case FMVFC3D: case FMVFC3Q:
306 case FMVICS: case FMVICD: case FMVICQ:
307 case FMVXCS: case FMVXCD: case FMVXCQ:
308 return (fpu_insn_fmovcc(p, fs, instr));
309
310 case FMOVZS: case FMOVZD: case FMOVZQ:
311 case FMOVLEZS: case FMOVLEZD: case FMOVLEZQ:
312 case FMOVLZS: case FMOVLZD: case FMOVLZQ:
313 case FMOVNZS: case FMOVNZD: case FMOVNZQ:
314 case FMOVGZS: case FMOVGZD: case FMOVGZQ:
315 case FMOVGEZS: case FMOVGEZD: case FMOVGEZQ:
316 return (fpu_insn_fmovr(p, fs, instr));
317 }
318 return (NOTFPU);
319 }
320
321 if (instr.i_opf.i_op3 != IOP3_FPop1)
322 return (NOTFPU);
323
324 switch (instr.i_opf.i_opf) {
325 case FSTOX: case FDTOX: case FQTOX:
326 rdtype = FTYPE_LNG;
327 if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0)
328 return (err);
329 break;
330
331 case FSTOI: case FDTOI: case FQTOI:
332 rdtype = FTYPE_INT;
333 if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0)
334 return (err);
335 break;
336
337 case FITOS: case FITOD: case FITOQ:
338 if ((err = fpu_insn_itof(fe, instr, FTYPE_INT, &rd,
339 &rdtype, space)) != 0)
340 return (err);
341 break;
342
343 case FXTOS: case FXTOD: case FXTOQ:
344 if ((err = fpu_insn_itof(fe, instr, FTYPE_LNG, &rd,
345 &rdtype, space)) != 0)
346 return (err);
347 break;
348
349 case FSTOD: case FSTOQ:
350 case FDTOS: case FDTOQ:
351 case FQTOS: case FQTOD:
352 if ((err = fpu_insn_ftof(fe, instr, &rd, &rdtype, space)) != 0)
353 return (err);
354 break;
355
356 case FMOVS: case FMOVD: case FMOVQ:
357 return (fpu_insn_fmov(fs, fe, instr));
358
359 case FNEGS: case FNEGD: case FNEGQ:
360 return (fpu_insn_fneg(fs, fe, instr));
361
362 case FABSS: case FABSD: case FABSQ:
363 return (fpu_insn_fabs(fs, fe, instr));
364
365 case FSQRTS: case FSQRTD: case FSQRTQ:
366 if ((err = fpu_insn_fsqrt(fe, instr, &rd, &rdtype, space)) != 0)
367 return (err);
368 break;
369
370 case FMULS: case FMULD: case FMULQ:
371 if ((err = fpu_insn_fmul(fe, instr, &rd, &rdtype, space)) != 0)
372 return (err);
373 break;
374
375 case FDIVS: case FDIVD: case FDIVQ:
376 if ((err = fpu_insn_fdiv(fe, instr, &rd, &rdtype, space)) != 0)
377 return (err);
378 break;
379
380 case FSMULD: case FDMULQ:
381 if ((err = fpu_insn_fmulx(fe, instr, &rd, &rdtype, space)) != 0)
382 return (err);
383 break;
384
385 case FADDS: case FADDD: case FADDQ:
386 if ((err = fpu_insn_fadd(fe, instr, &rd, &rdtype, space)) != 0)
387 return (err);
388 break;
389
390 case FSUBS: case FSUBD: case FSUBQ:
391 if ((err = fpu_insn_fsub(fe, instr, &rd, &rdtype, space)) != 0)
392 return (err);
393 break;
394 default:
395 return (NOTFPU);
396 }
397
398 cx = fe->fe_cx;
399 fsr = fe->fe_fsr;
400 if (cx != 0) {
401 mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
402 if (cx & mask) {
403 /* not accrued??? */
404 fs->fs_fsr = (fsr & ~FSR_FTT) |
405 (FSR_TT_IEEE << FSR_FTT_SHIFT) |
406 (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT);
407 return (FPE);
408 }
409 fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT);
410 }
411 fs->fs_fsr = fsr;
412 fpu_fcopy(space, fs->fs_regs + rd, rdtype);
413 DPRINTF(FPE_STATE, ("AFTER:\n"));
414 DUMPSTATE(FPE_STATE, fs);
415 return (0);
416 }
417
418 /*
419 * Handler for FMOV[SDQ] emulation.
420 */
421 int
fpu_insn_fmov(struct fpstate * fs,struct fpemu * fe,union instr instr)422 fpu_insn_fmov(struct fpstate *fs, struct fpemu *fe, union instr instr)
423 {
424 int opf = instr.i_opf.i_opf, rs, rd, rtype;
425
426 rtype = opf & 3;
427 if (rtype == 0)
428 return (NOTFPU);
429 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
430 return (NOTFPU);
431 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
432 return (NOTFPU);
433 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
434 fs->fs_fsr = fe->fe_fsr;
435 return (0);
436 }
437
438 /*
439 * Handler for FABS[SDQ] emulation.
440 */
441 int
fpu_insn_fabs(struct fpstate * fs,struct fpemu * fe,union instr instr)442 fpu_insn_fabs(struct fpstate *fs, struct fpemu *fe, union instr instr)
443 {
444 int opf = instr.i_opf.i_opf, rs, rd, rtype;
445
446 rtype = opf & 3;
447 if (rtype == 0)
448 return (NOTFPU);
449 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
450 return (NOTFPU);
451 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
452 return (NOTFPU);
453 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
454 fs->fs_regs[rd] = fs->fs_regs[rd] & ~(1U << 31);
455 fs->fs_fsr = fe->fe_fsr;
456 return (0);
457 }
458
459 /*
460 * Handler for FNEG[SDQ] emulation.
461 */
462 int
fpu_insn_fneg(struct fpstate * fs,struct fpemu * fe,union instr instr)463 fpu_insn_fneg(struct fpstate *fs, struct fpemu *fe, union instr instr)
464 {
465 int opf = instr.i_opf.i_opf, rs, rd, rtype;
466
467 rtype = opf & 3;
468 if (rtype == 0)
469 return (NOTFPU);
470 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
471 return (NOTFPU);
472 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
473 return (NOTFPU);
474 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
475 fs->fs_regs[rd] = fs->fs_regs[rd] ^ (1U << 31);
476 fs->fs_fsr = fe->fe_fsr;
477 return (0);
478 }
479
480 /*
481 * Handler for F[XI]TO[SDQ] emulation.
482 */
483 int
fpu_insn_itof(struct fpemu * fe,union instr instr,int rstype,int * rdp,int * rdtypep,u_int * space)484 fpu_insn_itof(struct fpemu *fe, union instr instr, int rstype, int *rdp,
485 int *rdtypep, u_int *space)
486 {
487 int opf = instr.i_opf.i_opf, rs, rd, rdtype;
488
489 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
490 return (NOTFPU);
491
492 rdtype = (opf >> 2) & 3;
493 if (rdtype == 0)
494 return (NOTFPU);
495 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
496 return (NOTFPU);
497
498 DPRINTF(FPE_INSN, ("itof %%f%d(%d, %d) -> %%f%d(%d, %d)\n",
499 rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd));
500 fpu_explode(fe, &fe->fe_f1, rstype, rs);
501 fpu_implode(fe, &fe->fe_f1, rdtype, space);
502 *rdp = rd;
503 *rdtypep = rdtype;
504 return (0);
505 }
506
507 /*
508 * Handler for F[SDQ]TO[XI] emulation.
509 */
510 int
fpu_insn_ftoi(struct fpemu * fe,union instr instr,int * rdp,int rdtype,u_int * space)511 fpu_insn_ftoi(struct fpemu *fe, union instr instr, int *rdp, int rdtype,
512 u_int *space)
513 {
514 int opf = instr.i_opf.i_opf, rd, rstype, rs;
515
516 rstype = opf & 3;
517 if (rstype == 0)
518 return (NOTFPU);
519 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
520 return (NOTFPU);
521 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
522 return (NOTFPU);
523
524 fpu_explode(fe, &fe->fe_f1, rstype, rs);
525 fpu_implode(fe, &fe->fe_f1, rdtype, space);
526 *rdp = rd;
527 return (0);
528 }
529
530 /*
531 * Handler for F[SDQ]TO[SDQ] emulation.
532 */
533 int
fpu_insn_ftof(struct fpemu * fe,union instr instr,int * rdp,int * rdtypep,u_int * space)534 fpu_insn_ftof(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep,
535 u_int *space)
536 {
537 int opf = instr.i_opf.i_opf, rd, rs, rdtype, rstype;
538
539 rstype = opf & 3;
540 rdtype = (opf >> 2) & 3;
541
542 if ((rstype == rdtype) || (rstype == 0) || (rdtype == 0))
543 return (NOTFPU);
544
545 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
546 return (NOTFPU);
547 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
548 return (NOTFPU);
549
550 DPRINTF(FPE_INSN, ("ftof %%f%d(%d, %d) -> %%f%d(%d, %d)\n",
551 rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd));
552
553 fpu_explode(fe, &fe->fe_f1, rstype, rs);
554 fpu_implode(fe, &fe->fe_f1, rdtype, space);
555 *rdp = rd;
556 *rdtypep = rdtype;
557 return (0);
558 }
559
560 /*
561 * Handler for FQSRT[SDQ] emulation.
562 */
563 int
fpu_insn_fsqrt(struct fpemu * fe,union instr instr,int * rdp,int * rdtypep,u_int * space)564 fpu_insn_fsqrt(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep,
565 u_int *space)
566 {
567 int opf = instr.i_opf.i_opf, rd, rs, rtype;
568 struct fpn *fp;
569
570 rtype = opf & 3;
571 if (rtype == 0)
572 return (NOTFPU);
573 if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
574 return (NOTFPU);
575 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
576 return (NOTFPU);
577
578 fpu_explode(fe, &fe->fe_f1, rtype, rs);
579 fp = fpu_sqrt(fe);
580 fpu_implode(fe, fp, rtype, space);
581 *rdp = rd;
582 *rdtypep = rtype;
583 return (0);
584 }
585
586 /*
587 * Handler for FCMP{E}[SDQ] emulation.
588 */
589 int
fpu_insn_fcmp(struct fpstate * fs,struct fpemu * fe,union instr instr,int cmpe)590 fpu_insn_fcmp(struct fpstate *fs, struct fpemu *fe, union instr instr,
591 int cmpe)
592 {
593 int opf = instr.i_opf.i_opf, rs1, rs2, rtype, cx, fsr;
594
595 rtype = opf & 3;
596 if (rtype == 0)
597 return (NOTFPU);
598 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
599 return (NOTFPU);
600 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
601 return (NOTFPU);
602
603 fpu_explode(fe, &fe->fe_f1, rtype, rs1);
604 fpu_explode(fe, &fe->fe_f2, rtype, rs2);
605 fpu_compare(fe, cmpe);
606
607 /*
608 * The only possible exception here is NV; catch it early
609 * and get out, as there is no result register.
610 */
611 cx = fe->fe_cx;
612 fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT);
613 if (cx != 0) {
614 if (fsr & (FSR_NV << FSR_TEM_SHIFT)) {
615 fs->fs_fsr = (fsr & ~FSR_FTT) |
616 (FSR_TT_IEEE << FSR_FTT_SHIFT);
617 return (FPE);
618 }
619 fsr |= FSR_NV << FSR_AX_SHIFT;
620 }
621 fs->fs_fsr = fsr;
622 return (0);
623 }
624
625 /*
626 * Handler for FMUL[SDQ] emulation.
627 */
628 int
fpu_insn_fmul(struct fpemu * fe,union instr instr,int * rdp,int * rdtypep,u_int * space)629 fpu_insn_fmul(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep,
630 u_int *space)
631 {
632 struct fpn *fp;
633 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
634
635 rtype = opf & 3;
636 if (rtype == 0)
637 return (NOTFPU);
638 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
639 return (NOTFPU);
640 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
641 return (NOTFPU);
642 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
643 return (NOTFPU);
644
645 fpu_explode(fe, &fe->fe_f1, rtype, rs1);
646 fpu_explode(fe, &fe->fe_f2, rtype, rs2);
647 fp = fpu_mul(fe);
648 fpu_implode(fe, fp, rtype, space);
649 *rdp = rd;
650 *rdtypep = rtype;
651 return (0);
652 }
653
654 /*
655 * Handler for FSMULD, FDMULQ emulation.
656 */
657 int
fpu_insn_fmulx(struct fpemu * fe,union instr instr,int * rdp,int * rdtypep,u_int * space)658 fpu_insn_fmulx(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep,
659 u_int *space)
660 {
661 struct fpn *fp;
662 int opf = instr.i_opf.i_opf, rd, rdtype, rstype, rs1, rs2;
663
664 rstype = opf & 3;
665 rdtype = (opf >> 2) & 3;
666 if ((rstype != rdtype + 1) || (rstype == 0) || (rdtype == 0))
667 return (NOTFPU);
668 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rstype)) < 0)
669 return (NOTFPU);
670 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
671 return (NOTFPU);
672 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
673 return (NOTFPU);
674
675 fpu_explode(fe, &fe->fe_f1, rstype, rs1);
676 fpu_explode(fe, &fe->fe_f2, rstype, rs2);
677 fp = fpu_mul(fe);
678 fpu_implode(fe, fp, rdtype, space);
679 *rdp = rd;
680 *rdtypep = rdtype;
681 return (0);
682 }
683
684 /*
685 * Handler for FDIV[SDQ] emulation.
686 */
687 int
fpu_insn_fdiv(struct fpemu * fe,union instr instr,int * rdp,int * rdtypep,u_int * space)688 fpu_insn_fdiv(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep,
689 u_int *space)
690 {
691 struct fpn *fp;
692 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
693
694 rtype = opf & 3;
695 if (rtype == 0)
696 return (NOTFPU);
697 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
698 return (NOTFPU);
699 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
700 return (NOTFPU);
701 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
702 return (NOTFPU);
703
704 fpu_explode(fe, &fe->fe_f1, rtype, rs1);
705 fpu_explode(fe, &fe->fe_f2, rtype, rs2);
706 fp = fpu_div(fe);
707 fpu_implode(fe, fp, rtype, space);
708 *rdp = rd;
709 *rdtypep = rtype;
710 return (0);
711 }
712
713 /*
714 * Handler for FADD[SDQ] emulation.
715 */
716 int
fpu_insn_fadd(struct fpemu * fe,union instr instr,int * rdp,int * rdtypep,u_int * space)717 fpu_insn_fadd(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep,
718 u_int *space)
719 {
720 struct fpn *fp;
721 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
722
723 rtype = opf & 3;
724 if (rtype == 0)
725 return (NOTFPU);
726 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
727 return (NOTFPU);
728 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
729 return (NOTFPU);
730 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
731 return (NOTFPU);
732
733 fpu_explode(fe, &fe->fe_f1, rtype, rs1);
734 fpu_explode(fe, &fe->fe_f2, rtype, rs2);
735 fp = fpu_add(fe);
736 fpu_implode(fe, fp, rtype, space);
737 *rdp = rd;
738 *rdtypep = rtype;
739 return (0);
740 }
741
742 /*
743 * Handler for FSUB[SDQ] emulation.
744 */
745 int
fpu_insn_fsub(struct fpemu * fe,union instr instr,int * rdp,int * rdtypep,u_int * space)746 fpu_insn_fsub(struct fpemu *fe, union instr instr, int *rdp, int *rdtypep,
747 u_int *space)
748 {
749 struct fpn *fp;
750 int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
751
752 rtype = opf & 3;
753 if (rtype == 0)
754 return (NOTFPU);
755 if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
756 return (NOTFPU);
757 if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
758 return (NOTFPU);
759 if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
760 return (NOTFPU);
761
762 fpu_explode(fe, &fe->fe_f1, rtype, rs1);
763 fpu_explode(fe, &fe->fe_f2, rtype, rs2);
764 fp = fpu_sub(fe);
765 fpu_implode(fe, fp, rtype, space);
766 *rdp = rd;
767 *rdtypep = rtype;
768 return (0);
769 }
770
771 /*
772 * Handler for FMOV[SDQ][cond] emulation.
773 */
774 int
fpu_insn_fmovcc(struct proc * p,struct fpstate * fs,union instr instr)775 fpu_insn_fmovcc(struct proc *p, struct fpstate *fs, union instr instr)
776 {
777 int rtype, rd, rs, cond;
778
779 rtype = instr.i_fmovcc.i_opf_low & 3;
780 if ((rtype == 0) || (instr.i_int & 0x00040000))
781 return (NOTFPU);
782
783 if ((rd = fpu_regoffset(instr.i_fmovcc.i_rd, rtype)) < 0)
784 return (NOTFPU);
785 if ((rs = fpu_regoffset(instr.i_fmovcc.i_rs2, rtype)) < 0)
786 return (NOTFPU);
787
788 switch (instr.i_fmovcc.i_opf_cc) {
789 case 0:
790 cond = (fs->fs_fsr >> FSR_FCC_SHIFT) & FSR_FCC_MASK;
791 break;
792 case 1:
793 cond = (fs->fs_fsr >> FSR_FCC1_SHIFT) & FSR_FCC_MASK;
794 break;
795 case 2:
796 cond = (fs->fs_fsr >> FSR_FCC2_SHIFT) & FSR_FCC_MASK;
797 break;
798 case 3:
799 cond = (fs->fs_fsr >> FSR_FCC3_SHIFT) & FSR_FCC_MASK;
800 break;
801 case 4:
802 cond = (p->p_md.md_tf->tf_tstate >> TSTATE_CCR_SHIFT) &
803 PSR_ICC;
804 break;
805 case 6:
806 cond = (p->p_md.md_tf->tf_tstate >>
807 (TSTATE_CCR_SHIFT + XCC_SHIFT)) & PSR_ICC;
808 break;
809 default:
810 return (NOTFPU);
811 }
812
813 if (instr.i_fmovcc.i_cond != cond)
814 return (0);
815
816 fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
817 return (0);
818 }
819
820 /*
821 * Handler for FMOVR[icond][SDQ] emulation.
822 */
823 int
fpu_insn_fmovr(struct proc * p,struct fpstate * fs,union instr instr)824 fpu_insn_fmovr(struct proc *p, struct fpstate *fs, union instr instr)
825 {
826 int rtype, rd, rs2, rs1;
827
828 rtype = instr.i_fmovcc.i_opf_low & 3;
829 if ((rtype == 0) || (instr.i_int & 0x00002000))
830 return (NOTFPU);
831
832 if ((rd = fpu_regoffset(instr.i_fmovr.i_rd, rtype)) < 0)
833 return (NOTFPU);
834 if ((rs2 = fpu_regoffset(instr.i_fmovr.i_rs2, rtype)) < 0)
835 return (NOTFPU);
836 rs1 = instr.i_fmovr.i_rs1;
837
838 switch (instr.i_fmovr.i_rcond) {
839 case 1: /* Z */
840 if (rs1 != 0 &&
841 (int64_t)p->p_md.md_tf->tf_global[rs1] != 0)
842 return (0);
843 break;
844 case 2: /* LEZ */
845 if (rs1 != 0 &&
846 (int64_t)p->p_md.md_tf->tf_global[rs1] > 0)
847 return (0);
848 break;
849 case 3: /* LZ */
850 if (rs1 == 0 ||
851 (int64_t)p->p_md.md_tf->tf_global[rs1] >= 0)
852 return (0);
853 break;
854 case 5: /* NZ */
855 if (rs1 == 0 ||
856 (int64_t)p->p_md.md_tf->tf_global[rs1] == 0)
857 return (0);
858 break;
859 case 6: /* NGZ */
860 if (rs1 == 0 ||
861 (int64_t)p->p_md.md_tf->tf_global[rs1] <= 0)
862 return (0);
863 break;
864 case 7: /* NGEZ */
865 if (rs1 != 0 &&
866 (int64_t)p->p_md.md_tf->tf_global[rs1] < 0)
867 return (0);
868 break;
869 default:
870 return (NOTFPU);
871 }
872
873 fpu_fcopy(fs->fs_regs + rs2, fs->fs_regs + rd, rtype);
874 return (0);
875 }
876