xref: /openbsd/sys/arch/sparc64/fpu/fpu.c (revision 905646f0)
1 /*	$OpenBSD: fpu.c,v 1.21 2020/08/19 10:10:58 mpi 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/reg.h>
79 
80 #include <sparc64/fpu/fpu_emu.h>
81 #include <sparc64/fpu/fpu_extern.h>
82 
83 int fpu_regoffset(int, int);
84 int fpu_insn_fmov(struct fpstate64 *, struct fpemu *, union instr);
85 int fpu_insn_fabs(struct fpstate64 *, struct fpemu *, union instr);
86 int fpu_insn_fneg(struct fpstate64 *, struct fpemu *, union instr);
87 int fpu_insn_itof(struct fpemu *, union instr, int, int *,
88     int *, u_int *);
89 int fpu_insn_ftoi(struct fpemu *, union instr, int *, int, u_int *);
90 int fpu_insn_ftof(struct fpemu *, union instr, int *, int *, u_int *);
91 int fpu_insn_fsqrt(struct fpemu *, union instr, int *, int *, u_int *);
92 int fpu_insn_fcmp(struct fpstate64 *, struct fpemu *, union instr, int);
93 int fpu_insn_fmul(struct fpemu *, union instr, int *, int *, u_int *);
94 int fpu_insn_fmulx(struct fpemu *, union instr, int *, int *, u_int *);
95 int fpu_insn_fdiv(struct fpemu *, union instr, int *, int *, u_int *);
96 int fpu_insn_fadd(struct fpemu *, union instr, int *, int *, u_int *);
97 int fpu_insn_fsub(struct fpemu *, union instr, int *, int *, u_int *);
98 int fpu_insn_fmovcc(struct proc *, struct fpstate64 *, union instr);
99 int fpu_insn_fmovr(struct proc *, struct fpstate64 *, union instr);
100 void fpu_fcopy(u_int *, u_int *, int);
101 
102 #ifdef DEBUG
103 int fpe_debug = 0;
104 
105 /*
106  * Dump a `fpn' structure.
107  */
108 void
109 fpu_dumpfpn(struct fpn *fp)
110 {
111 	static char *class[] = { "SNAN", "QNAN", "ZERO", "NUM", "INF" };
112 
113 	printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2],
114 	    fp->fp_sign ? '-' : ' ', fp->fp_mant[0], fp->fp_mant[1],
115 	    fp->fp_mant[2], fp->fp_mant[3], fp->fp_exp);
116 }
117 void
118 fpu_dumpstate(struct fpstate64 *fs)
119 {
120 	int i;
121 
122 	for (i = 0; i < 64; i++)
123 		printf("%%f%02d: %08x%s",
124 		    i, fs->fs_regs[i], ((i & 3) == 3) ? "\n" : "   ");
125 }
126 #endif
127 
128 /*
129  * fpu_execute returns the following error numbers (0 = no error):
130  */
131 #define	FPE		1	/* take a floating point exception */
132 #define	NOTFPU		2	/* not an FPU instruction */
133 
134 /*
135  * Translate current exceptions into `first' exception.  The
136  * bits go the wrong way for ffs() (0x10 is most important, etc).
137  * There are only 5, so do it the obvious way.
138  */
139 #define	X1(x) x
140 #define	X2(x) x,x
141 #define	X4(x) x,x,x,x
142 #define	X8(x) X4(x),X4(x)
143 #define	X16(x) X8(x),X8(x)
144 
145 static char cx_to_trapx[] = {
146 	X1(FSR_NX),
147 	X2(FSR_DZ),
148 	X4(FSR_UF),
149 	X8(FSR_OF),
150 	X16(FSR_NV)
151 };
152 static u_char fpu_codes[] = {
153 	X1(FPE_FLTINEX_TRAP),
154 	X2(FPE_FLTDIV_TRAP),
155 	X4(FPE_FLTUND_TRAP),
156 	X8(FPE_FLTOVF_TRAP),
157 	X16(FPE_FLTOPERR_TRAP)
158 };
159 
160 static int fpu_types[] = {
161 	X1(FPE_FLTRES),
162 	X2(FPE_FLTDIV),
163 	X4(FPE_FLTUND),
164 	X8(FPE_FLTOVF),
165 	X16(FPE_FLTINV)
166 };
167 
168 void
169 fpu_fcopy(src, dst, type)
170 	u_int *src, *dst;
171 	int type;
172 {
173 	*dst++ = *src++;
174 	if (type == FTYPE_SNG || type == FTYPE_INT)
175 		return;
176 	*dst++ = *src++;
177 	if (type != FTYPE_EXT)
178 		return;
179 	*dst++ = *src++;
180 	*dst = *src;
181 }
182 
183 /*
184  * The FPU gave us an exception.  Clean up the mess.  Note that the
185  * fp queue can only have FPops in it, never load/store FP registers
186  * nor FBfcc instructions.  Experiments with `crashme' prove that
187  * unknown FPops do enter the queue, however.
188  */
189 void
190 fpu_cleanup(p, fs)
191 	register struct proc *p;
192 	register struct fpstate64 *fs;
193 {
194 	register int i, fsr = fs->fs_fsr, error;
195 	union instr instr;
196 	union sigval sv;
197 	struct fpemu fe;
198 
199 	sv.sival_int = p->p_md.md_tf->tf_pc;  /* XXX only approximate */
200 
201 	switch ((fsr >> FSR_FTT_SHIFT) & FSR_FTT_MASK) {
202 
203 	case FSR_TT_NONE:
204 #if 0
205 		/* XXX I'm not sure how we get here, but ignoring the trap */
206 		/* XXX seems to work in my limited tests		   */
207 		/* XXX More research to be done =)			   */
208 		panic("fpu_cleanup 1"); /* ??? */
209 #else
210 		printf("fpu_cleanup 1\n");
211 #endif
212 		break;
213 
214 	case FSR_TT_IEEE:
215 		if ((i = fsr & FSR_CX) == 0)
216 			panic("fpu ieee trap, but no exception");
217 		trapsignal(p, SIGFPE, fpu_codes[i - 1], fpu_types[i - 1], sv);
218 		break;		/* XXX should return, but queue remains */
219 
220 	case FSR_TT_UNFIN:
221 		if (fs->fs_qsize == 0) {
222 			printf("fpu_cleanup: unfinished fpop");
223 			/* The book says reexecute or emulate. */
224 			return;
225 		}
226 		break;
227 	case FSR_TT_UNIMP:
228 		if (fs->fs_qsize == 0)
229 			panic("fpu_cleanup: unimplemented fpop");
230 		break;
231 
232 	case FSR_TT_SEQ:
233 		panic("fpu sequence error");
234 		/* NOTREACHED */
235 
236 	case FSR_TT_HWERR:
237 		log(LOG_ERR, "fpu hardware error (%s[%d])\n",
238 		    p->p_p->ps_comm, p->p_p->ps_pid);
239 		uprintf("%s[%d]: fpu hardware error\n",
240 		    p->p_p->ps_comm, p->p_p->ps_pid);
241 		trapsignal(p, SIGFPE, -1, FPE_FLTINV, sv);	/* ??? */
242 		goto out;
243 
244 	default:
245 		printf("fsr=0x%x\n", fsr);
246 		panic("fpu error");
247 	}
248 
249 	/* emulate the instructions left in the queue */
250 	fe.fe_fpstate = fs;
251 	for (i = 0; i < fs->fs_qsize; i++) {
252 		instr.i_int = fs->fs_queue[i].fq_instr;
253 		if (instr.i_any.i_op != IOP_reg ||
254 		    (instr.i_op3.i_op3 != IOP3_FPop1 &&
255 		     instr.i_op3.i_op3 != IOP3_FPop2))
256 			panic("bogus fpu queue");
257 		error = fpu_execute(p, &fe, instr);
258 		switch (error) {
259 
260 		case 0:
261 			continue;
262 
263 		case FPE:
264 			trapsignal(p, SIGFPE,
265 			    fpu_codes[(fs->fs_fsr & FSR_CX) - 1],
266 			    fpu_types[(fs->fs_fsr & FSR_CX) - 1], sv);
267 			break;
268 
269 		case NOTFPU:
270 			trapsignal(p, SIGILL, 0, ILL_COPROC, sv);
271 			break;
272 
273 		default:
274 			panic("fpu_cleanup 3");
275 			/* NOTREACHED */
276 		}
277 		/* XXX should stop here, but queue remains */
278 	}
279 out:
280 	fs->fs_qsize = 0;
281 }
282 
283 /*
284  * Compute offset given a register and type.  For 32 bit sparc, bits 1 and 0
285  * must be zero for ext types, and bit 0 must be 0 for double and long types.
286  * For 64bit sparc, bit 1 must be zero for quad types, and bit 0 becomes bit
287  * 5 in the register offset for long, double, and quad types.
288  */
289 int
290 fpu_regoffset(rx, type)
291 	int rx, type;
292 {
293 	if (type == FTYPE_LNG || type == FTYPE_DBL || type == FTYPE_EXT) {
294 		rx |= (rx & 1) << 5;
295 		rx &= 0x3e;
296 		if ((type == FTYPE_EXT) && (rx & 2))
297 			return (-1);
298 	}
299 	return (rx);
300 }
301 
302 /*
303  * Execute an FPU instruction (one that runs entirely in the FPU; not
304  * FBfcc or STF, for instance).  On return, fe->fe_fs->fs_fsr will be
305  * modified to reflect the setting the hardware would have left.
306  */
307 int
308 fpu_execute(p, fe, instr)
309 	struct proc *p;
310 	struct fpemu *fe;
311 	union instr instr;
312 {
313 	struct fpstate *fs;
314 	int opf, rdtype, rd, err, mask, cx, fsr;
315 	u_int space[4];
316 
317 	DPRINTF(FPE_INSN, ("op3: %x, opf %x\n", instr.i_opf.i_op3,
318 	    instr.i_opf.i_opf));
319 	DPRINTF(FPE_STATE, ("BEFORE:\n"));
320 	DUMPSTATE(FPE_STATE, fe->fe_fpstate);
321 	opf = instr.i_opf.i_opf;
322 	fs = fe->fe_fpstate;
323 	fe->fe_fsr = fs->fs_fsr & ~FSR_CX;
324 	fe->fe_cx = 0;
325 
326 	if ((instr.i_int & 0xc0000000) != 0x80000000)
327 		return (NOTFPU);
328 
329 	if (instr.i_opf.i_op3 == IOP3_FPop2) {
330 		switch (opf) {
331 		case FCMPS: case FCMPD: case FCMPQ:
332 			return (fpu_insn_fcmp(fs, fe, instr, 0));
333 
334 		case FCMPES: case FCMPED: case FCMPEQ:
335 			return (fpu_insn_fcmp(fs, fe, instr, 1));
336 
337 		case FMVFC0S: case FMVFC0D: case FMVFC0Q:
338 		case FMVFC1S: case FMVFC1D: case FMVFC1Q:
339 		case FMVFC2S: case FMVFC2D: case FMVFC2Q:
340 		case FMVFC3S: case FMVFC3D: case FMVFC3Q:
341 		case FMVICS: case FMVICD: case FMVICQ:
342 		case FMVXCS: case FMVXCD: case FMVXCQ:
343 			return (fpu_insn_fmovcc(p, fs, instr));
344 
345 		case FMOVZS: case FMOVZD: case FMOVZQ:
346 		case FMOVLEZS: case FMOVLEZD: case FMOVLEZQ:
347 		case FMOVLZS: case FMOVLZD: case FMOVLZQ:
348 		case FMOVNZS: case FMOVNZD: case FMOVNZQ:
349 		case FMOVGZS: case FMOVGZD: case FMOVGZQ:
350 		case FMOVGEZS: case FMOVGEZD: case FMOVGEZQ:
351 			return (fpu_insn_fmovr(p, fs, instr));
352 		}
353 		return (NOTFPU);
354 	}
355 
356 	if (instr.i_opf.i_op3 != IOP3_FPop1)
357 		return (NOTFPU);
358 
359 	switch (instr.i_opf.i_opf) {
360 	case FSTOX: case FDTOX: case FQTOX:
361 		rdtype = FTYPE_LNG;
362 		if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0)
363 			return (err);
364 		break;
365 
366 	case FSTOI: case FDTOI: case FQTOI:
367 		rdtype = FTYPE_INT;
368 		if ((err = fpu_insn_ftoi(fe, instr, &rd, rdtype, space)) != 0)
369 			return (err);
370 		break;
371 
372 	case FITOS: case FITOD: case FITOQ:
373 		if ((err = fpu_insn_itof(fe, instr, FTYPE_INT, &rd,
374 		    &rdtype, space)) != 0)
375 			return (err);
376 		break;
377 
378 	case FXTOS: case FXTOD: case FXTOQ:
379 		if ((err = fpu_insn_itof(fe, instr, FTYPE_LNG, &rd,
380 		    &rdtype, space)) != 0)
381 			return (err);
382 		break;
383 
384 	case FSTOD: case FSTOQ:
385 	case FDTOS: case FDTOQ:
386 	case FQTOS: case FQTOD:
387 		if ((err = fpu_insn_ftof(fe, instr, &rd, &rdtype, space)) != 0)
388 			return (err);
389 		break;
390 
391 	case FMOVS: case FMOVD: case FMOVQ:
392 		return (fpu_insn_fmov(fs, fe, instr));
393 
394 	case FNEGS: case FNEGD: case FNEGQ:
395 		return (fpu_insn_fneg(fs, fe, instr));
396 
397 	case FABSS: case FABSD: case FABSQ:
398 		return (fpu_insn_fabs(fs, fe, instr));
399 
400 	case FSQRTS: case FSQRTD: case FSQRTQ:
401 		if ((err = fpu_insn_fsqrt(fe, instr, &rd, &rdtype, space)) != 0)
402 			return (err);
403 		break;
404 
405 	case FMULS: case FMULD: case FMULQ:
406 		if ((err = fpu_insn_fmul(fe, instr, &rd, &rdtype, space)) != 0)
407 			return (err);
408 		break;
409 
410 	case FDIVS: case FDIVD: case FDIVQ:
411 		if ((err = fpu_insn_fdiv(fe, instr, &rd, &rdtype, space)) != 0)
412 			return (err);
413 		break;
414 
415 	case FSMULD: case FDMULQ:
416 		if ((err = fpu_insn_fmulx(fe, instr, &rd, &rdtype, space)) != 0)
417 			return (err);
418 		break;
419 
420 	case FADDS: case FADDD: case FADDQ:
421 		if ((err = fpu_insn_fadd(fe, instr, &rd, &rdtype, space)) != 0)
422 			return (err);
423 		break;
424 
425 	case FSUBS: case FSUBD: case FSUBQ:
426 		if ((err = fpu_insn_fsub(fe, instr, &rd, &rdtype, space)) != 0)
427 			return (err);
428 		break;
429 	default:
430 		return (NOTFPU);
431 	}
432 
433 	cx = fe->fe_cx;
434 	fsr = fe->fe_fsr;
435 	if (cx != 0) {
436 		mask = (fsr >> FSR_TEM_SHIFT) & FSR_TEM_MASK;
437 		if (cx & mask) {
438 			/* not accrued??? */
439 			fs->fs_fsr = (fsr & ~FSR_FTT) |
440 			    (FSR_TT_IEEE << FSR_FTT_SHIFT) |
441 			    (cx_to_trapx[(cx & mask) - 1] << FSR_CX_SHIFT);
442 			return (FPE);
443 		}
444 		fsr |= (cx << FSR_CX_SHIFT) | (cx << FSR_AX_SHIFT);
445 	}
446 	fs->fs_fsr = fsr;
447 	fpu_fcopy(space, fs->fs_regs + rd, rdtype);
448 	DPRINTF(FPE_STATE, ("AFTER:\n"));
449 	DUMPSTATE(FPE_STATE, fs);
450 	return (0);
451 }
452 
453 /*
454  * Handler for FMOV[SDQ] emulation.
455  */
456 int
457 fpu_insn_fmov(fs, fe, instr)
458 	struct fpstate64 *fs;
459 	struct fpemu *fe;
460 	union instr instr;
461 {
462 	int opf = instr.i_opf.i_opf, rs, rd, rtype;
463 
464 	rtype = opf & 3;
465 	if (rtype == 0)
466 		return (NOTFPU);
467 	if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
468 		return (NOTFPU);
469 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
470 		return (NOTFPU);
471 	fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
472 	fs->fs_fsr = fe->fe_fsr;
473 	return (0);
474 }
475 
476 /*
477  * Handler for FABS[SDQ] emulation.
478  */
479 int
480 fpu_insn_fabs(fs, fe, instr)
481 	struct fpstate64 *fs;
482 	struct fpemu *fe;
483 	union instr instr;
484 {
485 	int opf = instr.i_opf.i_opf, rs, rd, rtype;
486 
487 	rtype = opf & 3;
488 	if (rtype == 0)
489 		return (NOTFPU);
490 	if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
491 		return (NOTFPU);
492 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
493 		return (NOTFPU);
494 	fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
495 	fs->fs_regs[rd] = fs->fs_regs[rd] & ~(1U << 31);
496 	fs->fs_fsr = fe->fe_fsr;
497 	return (0);
498 }
499 
500 /*
501  * Handler for FNEG[SDQ] emulation.
502  */
503 int
504 fpu_insn_fneg(fs, fe, instr)
505 	struct fpstate64 *fs;
506 	struct fpemu *fe;
507 	union instr instr;
508 {
509 	int opf = instr.i_opf.i_opf, rs, rd, rtype;
510 
511 	rtype = opf & 3;
512 	if (rtype == 0)
513 		return (NOTFPU);
514 	if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
515 		return (NOTFPU);
516 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
517 		return (NOTFPU);
518 	fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
519 	fs->fs_regs[rd] = fs->fs_regs[rd] ^ (1U << 31);
520 	fs->fs_fsr = fe->fe_fsr;
521 	return (0);
522 }
523 
524 /*
525  * Handler for F[XI]TO[SDQ] emulation.
526  */
527 int
528 fpu_insn_itof(fe, instr, rstype, rdp, rdtypep, space)
529 	struct fpemu *fe;
530 	union instr instr;
531 	u_int *space;
532 	int rstype, *rdp, *rdtypep;
533 {
534 	int opf = instr.i_opf.i_opf, rs, rd, rdtype;
535 
536 	if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
537 		return (NOTFPU);
538 
539 	rdtype = (opf >> 2) & 3;
540 	if (rdtype == 0)
541 		return (NOTFPU);
542 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
543 		return (NOTFPU);
544 
545 	DPRINTF(FPE_INSN, ("itof %%f%d(%d, %d) -> %%f%d(%d, %d)\n",
546 	    rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd));
547 	fpu_explode(fe, &fe->fe_f1, rstype, rs);
548 	fpu_implode(fe, &fe->fe_f1, rdtype, space);
549 	*rdp = rd;
550 	*rdtypep = rdtype;
551 	return (0);
552 }
553 
554 /*
555  * Handler for F[SDQ]TO[XI] emulation.
556  */
557 int
558 fpu_insn_ftoi(fe, instr, rdp, rdtype, space)
559 	struct fpemu *fe;
560 	union instr instr;
561 	u_int *space;
562 	int *rdp, rdtype;
563 {
564 	int opf = instr.i_opf.i_opf, rd, rstype, rs;
565 
566 	rstype = opf & 3;
567 	if (rstype == 0)
568 		return (NOTFPU);
569 	if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
570 		return (NOTFPU);
571 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
572 		return (NOTFPU);
573 
574 	fpu_explode(fe, &fe->fe_f1, rstype, rs);
575 	fpu_implode(fe, &fe->fe_f1, rdtype, space);
576 	*rdp = rd;
577 	return (0);
578 }
579 
580 /*
581  * Handler for F[SDQ]TO[SDQ] emulation.
582  */
583 int
584 fpu_insn_ftof(fe, instr, rdp, rdtypep, space)
585 	struct fpemu *fe;
586 	union instr instr;
587 	u_int *space;
588 	int *rdp, *rdtypep;
589 {
590 	int opf = instr.i_opf.i_opf, rd, rs, rdtype, rstype;
591 
592 	rstype = opf & 3;
593 	rdtype = (opf >> 2) & 3;
594 
595 	if ((rstype == rdtype) || (rstype == 0) || (rdtype == 0))
596 		return (NOTFPU);
597 
598 	if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
599 		return (NOTFPU);
600 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
601 		return (NOTFPU);
602 
603 	DPRINTF(FPE_INSN, ("ftof %%f%d(%d, %d) -> %%f%d(%d, %d)\n",
604 	    rs, rstype, instr.i_opf.i_rs2, rd, rdtype, instr.i_opf.i_rd));
605 
606 	fpu_explode(fe, &fe->fe_f1, rstype, rs);
607 	fpu_implode(fe, &fe->fe_f1, rdtype, space);
608 	*rdp = rd;
609 	*rdtypep = rdtype;
610 	return (0);
611 }
612 
613 /*
614  * Handler for FQSRT[SDQ] emulation.
615  */
616 int
617 fpu_insn_fsqrt(fe, instr, rdp, rdtypep, space)
618 	struct fpemu *fe;
619 	union instr instr;
620 	u_int *space;
621 	int *rdp, *rdtypep;
622 {
623 	int opf = instr.i_opf.i_opf, rd, rs, rtype;
624 	struct fpn *fp;
625 
626 	rtype = opf & 3;
627 	if (rtype == 0)
628 		return (NOTFPU);
629 	if ((rs = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
630 		return (NOTFPU);
631 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
632 		return (NOTFPU);
633 
634 	fpu_explode(fe, &fe->fe_f1, rtype, rs);
635 	fp = fpu_sqrt(fe);
636 	fpu_implode(fe, fp, rtype, space);
637 	*rdp = rd;
638 	*rdtypep = rtype;
639 	return (0);
640 }
641 
642 /*
643  * Handler for FCMP{E}[SDQ] emulation.
644  */
645 int
646 fpu_insn_fcmp(fs, fe, instr, cmpe)
647 	struct fpstate64 *fs;
648 	struct fpemu *fe;
649 	union instr instr;
650 	int cmpe;
651 {
652 	int opf = instr.i_opf.i_opf, rs1, rs2, rtype, cx, fsr;
653 
654 	rtype = opf & 3;
655 	if (rtype == 0)
656 		return (NOTFPU);
657 	if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
658 		return (NOTFPU);
659 	if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
660 		return (NOTFPU);
661 
662 	fpu_explode(fe, &fe->fe_f1, rtype, rs1);
663 	fpu_explode(fe, &fe->fe_f2, rtype, rs2);
664 	fpu_compare(fe, cmpe);
665 
666 	/*
667 	 * The only possible exception here is NV; catch it early
668 	 * and get out, as there is no result register.
669 	 */
670 	cx = fe->fe_cx;
671 	fsr = fe->fe_fsr | (cx << FSR_CX_SHIFT);
672 	if (cx != 0) {
673 		if (fsr & (FSR_NV << FSR_TEM_SHIFT)) {
674 			fs->fs_fsr = (fsr & ~FSR_FTT) |
675 			    (FSR_TT_IEEE << FSR_FTT_SHIFT);
676 			return (FPE);
677 		}
678 		fsr |= FSR_NV << FSR_AX_SHIFT;
679 	}
680 	fs->fs_fsr = fsr;
681 	return (0);
682 }
683 
684 /*
685  * Handler for FMUL[SDQ] emulation.
686  */
687 int
688 fpu_insn_fmul(fe, instr, rdp, rdtypep, space)
689 	struct fpemu *fe;
690 	union instr instr;
691 	int *rdp, *rdtypep;
692 	u_int *space;
693 {
694 	struct fpn *fp;
695 	int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
696 
697 	rtype = opf & 3;
698 	if (rtype == 0)
699 		return (NOTFPU);
700 	if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
701 		return (NOTFPU);
702 	if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
703 		return (NOTFPU);
704 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
705 		return (NOTFPU);
706 
707 	fpu_explode(fe, &fe->fe_f1, rtype, rs1);
708 	fpu_explode(fe, &fe->fe_f2, rtype, rs2);
709 	fp = fpu_mul(fe);
710 	fpu_implode(fe, fp, rtype, space);
711 	*rdp = rd;
712 	*rdtypep = rtype;
713 	return (0);
714 }
715 
716 /*
717  * Handler for FSMULD, FDMULQ emulation.
718  */
719 int
720 fpu_insn_fmulx(fe, instr, rdp, rdtypep, space)
721 	struct fpemu *fe;
722 	union instr instr;
723 	int *rdp, *rdtypep;
724 	u_int *space;
725 {
726 	struct fpn *fp;
727 	int opf = instr.i_opf.i_opf, rd, rdtype, rstype, rs1, rs2;
728 
729 	rstype = opf & 3;
730 	rdtype = (opf >> 2) & 3;
731 	if ((rstype != rdtype + 1) || (rstype == 0) || (rdtype == 0))
732 		return (NOTFPU);
733 	if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rstype)) < 0)
734 		return (NOTFPU);
735 	if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rstype)) < 0)
736 		return (NOTFPU);
737 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rdtype)) < 0)
738 		return (NOTFPU);
739 
740 	fpu_explode(fe, &fe->fe_f1, rstype, rs1);
741 	fpu_explode(fe, &fe->fe_f2, rstype, rs2);
742 	fp = fpu_mul(fe);
743 	fpu_implode(fe, fp, rdtype, space);
744 	*rdp = rd;
745 	*rdtypep = rdtype;
746 	return (0);
747 }
748 
749 /*
750  * Handler for FDIV[SDQ] emulation.
751  */
752 int
753 fpu_insn_fdiv(fe, instr, rdp, rdtypep, space)
754 	struct fpemu *fe;
755 	union instr instr;
756 	int *rdp, *rdtypep;
757 	u_int *space;
758 {
759 	struct fpn *fp;
760 	int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
761 
762 	rtype = opf & 3;
763 	if (rtype == 0)
764 		return (NOTFPU);
765 	if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
766 		return (NOTFPU);
767 	if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
768 		return (NOTFPU);
769 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
770 		return (NOTFPU);
771 
772 	fpu_explode(fe, &fe->fe_f1, rtype, rs1);
773 	fpu_explode(fe, &fe->fe_f2, rtype, rs2);
774 	fp = fpu_div(fe);
775 	fpu_implode(fe, fp, rtype, space);
776 	*rdp = rd;
777 	*rdtypep = rtype;
778 	return (0);
779 }
780 
781 /*
782  * Handler for FADD[SDQ] emulation.
783  */
784 int
785 fpu_insn_fadd(fe, instr, rdp, rdtypep, space)
786 	struct fpemu *fe;
787 	union instr instr;
788 	int *rdp, *rdtypep;
789 	u_int *space;
790 {
791 	struct fpn *fp;
792 	int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
793 
794 	rtype = opf & 3;
795 	if (rtype == 0)
796 		return (NOTFPU);
797 	if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
798 		return (NOTFPU);
799 	if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
800 		return (NOTFPU);
801 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
802 		return (NOTFPU);
803 
804 	fpu_explode(fe, &fe->fe_f1, rtype, rs1);
805 	fpu_explode(fe, &fe->fe_f2, rtype, rs2);
806 	fp = fpu_add(fe);
807 	fpu_implode(fe, fp, rtype, space);
808 	*rdp = rd;
809 	*rdtypep = rtype;
810 	return (0);
811 }
812 
813 /*
814  * Handler for FSUB[SDQ] emulation.
815  */
816 int
817 fpu_insn_fsub(fe, instr, rdp, rdtypep, space)
818 	struct fpemu *fe;
819 	union instr instr;
820 	int *rdp, *rdtypep;
821 	u_int *space;
822 {
823 	struct fpn *fp;
824 	int opf = instr.i_opf.i_opf, rd, rtype, rs1, rs2;
825 
826 	rtype = opf & 3;
827 	if (rtype == 0)
828 		return (NOTFPU);
829 	if ((rs1 = fpu_regoffset(instr.i_opf.i_rs1, rtype)) < 0)
830 		return (NOTFPU);
831 	if ((rs2 = fpu_regoffset(instr.i_opf.i_rs2, rtype)) < 0)
832 		return (NOTFPU);
833 	if ((rd = fpu_regoffset(instr.i_opf.i_rd, rtype)) < 0)
834 		return (NOTFPU);
835 
836 	fpu_explode(fe, &fe->fe_f1, rtype, rs1);
837 	fpu_explode(fe, &fe->fe_f2, rtype, rs2);
838 	fp = fpu_sub(fe);
839 	fpu_implode(fe, fp, rtype, space);
840 	*rdp = rd;
841 	*rdtypep = rtype;
842 	return (0);
843 }
844 
845 /*
846  * Handler for FMOV[SDQ][cond] emulation.
847  */
848 int
849 fpu_insn_fmovcc(p, fs, instr)
850 	struct proc *p;
851 	struct fpstate64 *fs;
852 	union instr instr;
853 {
854 	int rtype, rd, rs, cond;
855 
856 	rtype = instr.i_fmovcc.i_opf_low & 3;
857 	if ((rtype == 0) || (instr.i_int & 0x00040000))
858 		return (NOTFPU);
859 
860 	if ((rd = fpu_regoffset(instr.i_fmovcc.i_rd, rtype)) < 0)
861 		return (NOTFPU);
862 	if ((rs = fpu_regoffset(instr.i_fmovcc.i_rs2, rtype)) < 0)
863 		return (NOTFPU);
864 
865 	switch (instr.i_fmovcc.i_opf_cc) {
866 	case 0:
867 		cond = (fs->fs_fsr >> FSR_FCC_SHIFT) & FSR_FCC_MASK;
868 		break;
869 	case 1:
870 		cond = (fs->fs_fsr >> FSR_FCC1_SHIFT) & FSR_FCC_MASK;
871 		break;
872 	case 2:
873 		cond = (fs->fs_fsr >> FSR_FCC2_SHIFT) & FSR_FCC_MASK;
874 		break;
875 	case 3:
876 		cond = (fs->fs_fsr >> FSR_FCC3_SHIFT) & FSR_FCC_MASK;
877 		break;
878 	case 4:
879 		cond = (p->p_md.md_tf->tf_tstate >> TSTATE_CCR_SHIFT) &
880 		    PSR_ICC;
881 		break;
882 	case 6:
883 		cond = (p->p_md.md_tf->tf_tstate >>
884 		    (TSTATE_CCR_SHIFT + XCC_SHIFT)) & PSR_ICC;
885 		break;
886 	default:
887 		return (NOTFPU);
888 	}
889 
890 	if (instr.i_fmovcc.i_cond != cond)
891 		return (0);
892 
893 	fpu_fcopy(fs->fs_regs + rs, fs->fs_regs + rd, rtype);
894 	return (0);
895 }
896 
897 /*
898  * Handler for FMOVR[icond][SDQ] emulation.
899  */
900 int
901 fpu_insn_fmovr(p, fs, instr)
902 	struct proc *p;
903 	struct fpstate64 *fs;
904 	union instr instr;
905 {
906 	int rtype, rd, rs2, rs1;
907 
908 	rtype = instr.i_fmovcc.i_opf_low & 3;
909 	if ((rtype == 0) || (instr.i_int & 0x00002000))
910 		return (NOTFPU);
911 
912 	if ((rd = fpu_regoffset(instr.i_fmovr.i_rd, rtype)) < 0)
913 		return (NOTFPU);
914 	if ((rs2 = fpu_regoffset(instr.i_fmovr.i_rs2, rtype)) < 0)
915 		return (NOTFPU);
916 	rs1 = instr.i_fmovr.i_rs1;
917 
918 	switch (instr.i_fmovr.i_rcond) {
919 	case 1:	/* Z */
920 		if (rs1 != 0 &&
921 		    (int64_t)p->p_md.md_tf->tf_global[rs1] != 0)
922 			return (0);
923 		break;
924 	case 2: /* LEZ */
925 		if (rs1 != 0 &&
926 		    (int64_t)p->p_md.md_tf->tf_global[rs1] > 0)
927 			return (0);
928 		break;
929 	case 3: /* LZ */
930 		if (rs1 == 0 ||
931 		    (int64_t)p->p_md.md_tf->tf_global[rs1] >= 0)
932 			return (0);
933 		break;
934 	case 5:	/* NZ */
935 		if (rs1 == 0 ||
936 		    (int64_t)p->p_md.md_tf->tf_global[rs1] == 0)
937 			return (0);
938 		break;
939 	case 6: /* NGZ */
940 		if (rs1 == 0 ||
941 		    (int64_t)p->p_md.md_tf->tf_global[rs1] <= 0)
942 			return (0);
943 		break;
944 	case 7: /* NGEZ */
945 		if (rs1 != 0 &&
946 		    (int64_t)p->p_md.md_tf->tf_global[rs1] < 0)
947 			return (0);
948 		break;
949 	default:
950 		return (NOTFPU);
951 	}
952 
953 	fpu_fcopy(fs->fs_regs + rs2, fs->fs_regs + rd, rtype);
954 	return (0);
955 }
956