1*6a9ecf9fSrin /* $NetBSD: fpu_emu.c,v 1.60 2022/09/20 12:25:01 rin Exp $ */
218b2f7e6Ssimonb
318b2f7e6Ssimonb /*
418b2f7e6Ssimonb * Copyright 2001 Wasabi Systems, Inc.
518b2f7e6Ssimonb * All rights reserved.
618b2f7e6Ssimonb *
718b2f7e6Ssimonb * Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc.
818b2f7e6Ssimonb *
918b2f7e6Ssimonb * Redistribution and use in source and binary forms, with or without
1018b2f7e6Ssimonb * modification, are permitted provided that the following conditions
1118b2f7e6Ssimonb * are met:
1218b2f7e6Ssimonb * 1. Redistributions of source code must retain the above copyright
1318b2f7e6Ssimonb * notice, this list of conditions and the following disclaimer.
1418b2f7e6Ssimonb * 2. Redistributions in binary form must reproduce the above copyright
1518b2f7e6Ssimonb * notice, this list of conditions and the following disclaimer in the
1618b2f7e6Ssimonb * documentation and/or other materials provided with the distribution.
1718b2f7e6Ssimonb * 3. All advertising materials mentioning features or use of this software
1818b2f7e6Ssimonb * must display the following acknowledgement:
1918b2f7e6Ssimonb * This product includes software developed for the NetBSD Project by
2018b2f7e6Ssimonb * Wasabi Systems, Inc.
2118b2f7e6Ssimonb * 4. The name of Wasabi Systems, Inc. may not be used to endorse
2218b2f7e6Ssimonb * or promote products derived from this software without specific prior
2318b2f7e6Ssimonb * written permission.
2418b2f7e6Ssimonb *
2518b2f7e6Ssimonb * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
2618b2f7e6Ssimonb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2718b2f7e6Ssimonb * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2818b2f7e6Ssimonb * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
2918b2f7e6Ssimonb * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
3018b2f7e6Ssimonb * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
3118b2f7e6Ssimonb * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
3218b2f7e6Ssimonb * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3318b2f7e6Ssimonb * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3418b2f7e6Ssimonb * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3518b2f7e6Ssimonb * POSSIBILITY OF SUCH DAMAGE.
3618b2f7e6Ssimonb */
3718b2f7e6Ssimonb
3818b2f7e6Ssimonb /*
3918b2f7e6Ssimonb * Copyright (c) 1992, 1993
4018b2f7e6Ssimonb * The Regents of the University of California. All rights reserved.
4118b2f7e6Ssimonb *
4218b2f7e6Ssimonb * This software was developed by the Computer Systems Engineering group
4318b2f7e6Ssimonb * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
4418b2f7e6Ssimonb * contributed to Berkeley.
4518b2f7e6Ssimonb *
4618b2f7e6Ssimonb * All advertising materials mentioning features or use of this software
4718b2f7e6Ssimonb * must display the following acknowledgement:
4818b2f7e6Ssimonb * This product includes software developed by the University of
4918b2f7e6Ssimonb * California, Lawrence Berkeley Laboratory.
5018b2f7e6Ssimonb *
5118b2f7e6Ssimonb * Redistribution and use in source and binary forms, with or without
5218b2f7e6Ssimonb * modification, are permitted provided that the following conditions
5318b2f7e6Ssimonb * are met:
5418b2f7e6Ssimonb * 1. Redistributions of source code must retain the above copyright
5518b2f7e6Ssimonb * notice, this list of conditions and the following disclaimer.
5618b2f7e6Ssimonb * 2. Redistributions in binary form must reproduce the above copyright
5718b2f7e6Ssimonb * notice, this list of conditions and the following disclaimer in the
5818b2f7e6Ssimonb * documentation and/or other materials provided with the distribution.
59aad01611Sagc * 3. Neither the name of the University nor the names of its contributors
6018b2f7e6Ssimonb * may be used to endorse or promote products derived from this software
6118b2f7e6Ssimonb * without specific prior written permission.
6218b2f7e6Ssimonb *
6318b2f7e6Ssimonb * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6418b2f7e6Ssimonb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6518b2f7e6Ssimonb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6618b2f7e6Ssimonb * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6718b2f7e6Ssimonb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6818b2f7e6Ssimonb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6918b2f7e6Ssimonb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
7018b2f7e6Ssimonb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
7118b2f7e6Ssimonb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
7218b2f7e6Ssimonb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
7318b2f7e6Ssimonb * SUCH DAMAGE.
7418b2f7e6Ssimonb *
7518b2f7e6Ssimonb * @(#)fpu.c 8.1 (Berkeley) 6/11/93
7618b2f7e6Ssimonb */
7718b2f7e6Ssimonb
78ed517291Slukem #include <sys/cdefs.h>
79*6a9ecf9fSrin __KERNEL_RCSID(0, "$NetBSD: fpu_emu.c,v 1.60 2022/09/20 12:25:01 rin Exp $");
80ed517291Slukem
8116031f7dSrin #ifdef _KERNEL_OPT
8218b2f7e6Ssimonb #include "opt_ddb.h"
8316031f7dSrin #endif
8418b2f7e6Ssimonb
8518b2f7e6Ssimonb #include <sys/param.h>
8606f65540Srin #include <sys/systm.h>
8706f65540Srin #include <sys/evcnt.h>
8818b2f7e6Ssimonb #include <sys/proc.h>
8906f65540Srin #include <sys/siginfo.h>
9018b2f7e6Ssimonb #include <sys/signal.h>
914923f4a4Smatt #include <sys/signalvar.h>
9218b2f7e6Ssimonb #include <sys/syslog.h>
9318b2f7e6Ssimonb
9418b2f7e6Ssimonb #include <powerpc/instr.h>
95afdc9a38Srin #include <powerpc/psl.h>
96afdc9a38Srin
9718b2f7e6Ssimonb #include <machine/fpu.h>
9806f65540Srin #include <machine/reg.h>
994923f4a4Smatt #include <machine/trap.h>
10018b2f7e6Ssimonb
10118b2f7e6Ssimonb #include <powerpc/fpu/fpu_emu.h>
10218b2f7e6Ssimonb #include <powerpc/fpu/fpu_extern.h>
10318b2f7e6Ssimonb
1040eb35ef8Sthorpej #define FPU_EMU_EVCNT_DECL(name) \
1050eb35ef8Sthorpej static struct evcnt fpu_emu_ev_##name = \
1060eb35ef8Sthorpej EVCNT_INITIALIZER(EVCNT_TYPE_TRAP, NULL, "fpemu", #name); \
1070eb35ef8Sthorpej EVCNT_ATTACH_STATIC(fpu_emu_ev_##name)
1080eb35ef8Sthorpej
1090eb35ef8Sthorpej #define FPU_EMU_EVCNT_INCR(name) \
110113c35cbSthorpej fpu_emu_ev_##name.ev_count++
1110eb35ef8Sthorpej
1120eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(stfiwx);
1130eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fpstore);
1140eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fpload);
1150eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fcmpu);
1160eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(frsp);
1170eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fctiw);
1180eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fcmpo);
1190eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(mtfsb1);
1200eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fnegabs);
1210eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(mcrfs);
1220eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(mtfsb0);
1230eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fmr);
1240eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(mtfsfi);
1250eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fnabs);
1260eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fabs);
1270eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(mffs);
1280eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(mtfsf);
1290eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fctid);
1300eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fcfid);
1310eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fdiv);
1320eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fsub);
1330eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fadd);
1340eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fsqrt);
1350eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fsel);
1360eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fpres);
1370eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fmul);
1380eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(frsqrte);
1394d3fc228Srin FPU_EMU_EVCNT_DECL(fmsub);
1404d3fc228Srin FPU_EMU_EVCNT_DECL(fmadd);
1410eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fnmsub);
1420eb35ef8Sthorpej FPU_EMU_EVCNT_DECL(fnmadd);
14318b2f7e6Ssimonb
14418b2f7e6Ssimonb /* FPSR exception masks */
14518b2f7e6Ssimonb #define FPSR_EX_MSK (FPSCR_VX|FPSCR_OX|FPSCR_UX|FPSCR_ZX| \
14618b2f7e6Ssimonb FPSCR_XX|FPSCR_VXSNAN|FPSCR_VXISI|FPSCR_VXIDI| \
14718b2f7e6Ssimonb FPSCR_VXZDZ|FPSCR_VXIMZ|FPSCR_VXVC|FPSCR_VXSOFT|\
14818b2f7e6Ssimonb FPSCR_VXSQRT|FPSCR_VXCVI)
14918b2f7e6Ssimonb #define FPSR_EX (FPSCR_VE|FPSCR_OE|FPSCR_UE|FPSCR_ZE|FPSCR_XE)
150f86c9cf0Srin #define FPSR_INV (FPSCR_VXSNAN|FPSCR_VXISI|FPSCR_VXIDI| \
151f86c9cf0Srin FPSCR_VXZDZ|FPSCR_VXIMZ|FPSCR_VXVC|FPSCR_VXSOFT|\
152f86c9cf0Srin FPSCR_VXSQRT|FPSCR_VXCVI)
153bcec7308Srin #define MCRFS_MASK \
154bcec7308Srin ( \
155bcec7308Srin FPSCR_FX | FPSCR_OX | \
156bcec7308Srin FPSCR_UX | FPSCR_ZX | FPSCR_XX | FPSCR_VXSNAN | \
157bcec7308Srin FPSCR_VXISI | FPSCR_VXIDI | FPSCR_VXZDZ | FPSCR_VXIMZ | \
158bcec7308Srin FPSCR_VXVC | \
159bcec7308Srin FPSCR_VXSOFT | FPSCR_VXSQRT | FPSCR_VXCVI \
160bcec7308Srin )
16118b2f7e6Ssimonb
162e41cdfd4Srin #define FR(reg) (fs->fpreg[reg])
16318b2f7e6Ssimonb
16418b2f7e6Ssimonb int fpe_debug = 0;
16518b2f7e6Ssimonb
16618b2f7e6Ssimonb #ifdef DDB
16718b2f7e6Ssimonb extern vaddr_t opc_disasm(vaddr_t loc, int opcode);
16818b2f7e6Ssimonb #endif
16918b2f7e6Ssimonb
170d95d2a1bSrin static int fpu_execute(struct trapframe *, struct fpemu *, union instr *);
171d95d2a1bSrin
17218b2f7e6Ssimonb #ifdef DEBUG
17318b2f7e6Ssimonb /*
17418b2f7e6Ssimonb * Dump a `fpn' structure.
17518b2f7e6Ssimonb */
17618b2f7e6Ssimonb void
fpu_dumpfpn(struct fpn * fp)17718b2f7e6Ssimonb fpu_dumpfpn(struct fpn *fp)
17818b2f7e6Ssimonb {
1797052d7a8Sscw static const char *class[] = {
18018b2f7e6Ssimonb "SNAN", "QNAN", "ZERO", "NUM", "INF"
18118b2f7e6Ssimonb };
18218b2f7e6Ssimonb
1831f138860Srin KASSERT(fp != NULL);
1841f138860Srin
18500281cd7Srin printf("%s %c.%x %x %x %xE%d\n", class[fp->fp_class + 2],
18618b2f7e6Ssimonb fp->fp_sign ? '-' : ' ',
18718b2f7e6Ssimonb fp->fp_mant[0], fp->fp_mant[1],
18818b2f7e6Ssimonb fp->fp_mant[2], fp->fp_mant[3],
18918b2f7e6Ssimonb fp->fp_exp);
19018b2f7e6Ssimonb }
19118b2f7e6Ssimonb #endif
19218b2f7e6Ssimonb
19318b2f7e6Ssimonb /*
19418b2f7e6Ssimonb * fpu_execute returns the following error numbers (0 = no error):
19518b2f7e6Ssimonb */
19618b2f7e6Ssimonb #define FPE 1 /* take a floating point exception */
19718b2f7e6Ssimonb #define NOTFPU 2 /* not an FPU instruction */
19818b2f7e6Ssimonb #define FAULT 3
19918b2f7e6Ssimonb
20018b2f7e6Ssimonb
20118b2f7e6Ssimonb /*
20218b2f7e6Ssimonb * Emulate a floating-point instruction.
20389f91290Srin * Return true if insn is consumed anyway.
20489f91290Srin * Otherwise, the caller must take care of it.
20518b2f7e6Ssimonb */
2064923f4a4Smatt bool
fpu_emulate(struct trapframe * tf,struct fpreg * fpf,ksiginfo_t * ksi)2074923f4a4Smatt fpu_emulate(struct trapframe *tf, struct fpreg *fpf, ksiginfo_t *ksi)
20818b2f7e6Ssimonb {
209afdc9a38Srin struct pcb *pcb;
2104923f4a4Smatt union instr insn;
2114923f4a4Smatt struct fpemu fe;
2124923f4a4Smatt
2134923f4a4Smatt KSI_INIT_TRAP(ksi);
2144923f4a4Smatt ksi->ksi_signo = 0;
2154923f4a4Smatt ksi->ksi_addr = (void *)tf->tf_srr0;
21618b2f7e6Ssimonb
21718b2f7e6Ssimonb /* initialize insn.is_datasize to tell it is *not* initialized */
21818b2f7e6Ssimonb fe.fe_fpstate = fpf;
21918b2f7e6Ssimonb fe.fe_cx = 0;
22018b2f7e6Ssimonb
22118b2f7e6Ssimonb /* always set this (to avoid a warning) */
22218b2f7e6Ssimonb
223b8ea2c8cSmatt if (copyin((void *) (tf->tf_srr0), &insn.i_int, sizeof (insn.i_int))) {
22418b2f7e6Ssimonb #ifdef DEBUG
22518b2f7e6Ssimonb printf("fpu_emulate: fault reading opcode\n");
22618b2f7e6Ssimonb #endif
2274923f4a4Smatt ksi->ksi_signo = SIGSEGV;
2284923f4a4Smatt ksi->ksi_trap = EXC_ISI;
2294923f4a4Smatt ksi->ksi_code = SEGV_MAPERR;
2304923f4a4Smatt return true;
23118b2f7e6Ssimonb }
23218b2f7e6Ssimonb
23318b2f7e6Ssimonb DPRINTF(FPE_EX, ("fpu_emulate: emulating insn %x at %p\n",
234b8ea2c8cSmatt insn.i_int, (void *)tf->tf_srr0));
23518b2f7e6Ssimonb
23618b2f7e6Ssimonb if ((insn.i_any.i_opcd == OPC_TWI) ||
23718b2f7e6Ssimonb ((insn.i_any.i_opcd == OPC_integer_31) &&
23818b2f7e6Ssimonb (insn.i_x.i_xo == OPC31_TW))) {
23918b2f7e6Ssimonb /* Check for the two trap insns. */
24018b2f7e6Ssimonb DPRINTF(FPE_EX, ("fpu_emulate: SIGTRAP\n"));
2414923f4a4Smatt ksi->ksi_signo = SIGTRAP;
2424923f4a4Smatt ksi->ksi_trap = EXC_PGM;
243e8cd8b2cSrin ksi->ksi_code = TRAP_BRKPT;
2444923f4a4Smatt return true;
24518b2f7e6Ssimonb }
246b8ea2c8cSmatt switch (fpu_execute(tf, &fe, &insn)) {
24718b2f7e6Ssimonb case 0:
248afdc9a38Srin success:
24918b2f7e6Ssimonb DPRINTF(FPE_EX, ("fpu_emulate: success\n"));
250b8ea2c8cSmatt tf->tf_srr0 += 4;
2514923f4a4Smatt return true;
25218b2f7e6Ssimonb
25318b2f7e6Ssimonb case FPE:
254afdc9a38Srin pcb = lwp_getpcb(curlwp);
255afdc9a38Srin if ((pcb->pcb_flags & PSL_FE_PREC) == 0)
256afdc9a38Srin goto success;
25718b2f7e6Ssimonb DPRINTF(FPE_EX, ("fpu_emulate: SIGFPE\n"));
2584923f4a4Smatt ksi->ksi_signo = SIGFPE;
2594923f4a4Smatt ksi->ksi_trap = EXC_PGM;
260d51192caSrin ksi->ksi_code = fpu_get_fault_code();
2614923f4a4Smatt return true;
26218b2f7e6Ssimonb
26318b2f7e6Ssimonb case FAULT:
26418b2f7e6Ssimonb DPRINTF(FPE_EX, ("fpu_emulate: SIGSEGV\n"));
2654923f4a4Smatt ksi->ksi_signo = SIGSEGV;
2664923f4a4Smatt ksi->ksi_trap = EXC_DSI;
2674923f4a4Smatt ksi->ksi_code = SEGV_MAPERR;
2684923f4a4Smatt ksi->ksi_addr = (void *)fe.fe_addr;
2694923f4a4Smatt return true;
27018b2f7e6Ssimonb
27118b2f7e6Ssimonb case NOTFPU:
27218b2f7e6Ssimonb default:
27318b2f7e6Ssimonb DPRINTF(FPE_EX, ("fpu_emulate: SIGILL\n"));
2740e2afb8cSrin #if defined(DDB) && defined(DEBUG)
27518b2f7e6Ssimonb if (fpe_debug & FPE_EX) {
27618b2f7e6Ssimonb printf("fpu_emulate: illegal insn %x at %p:",
277b8ea2c8cSmatt insn.i_int, (void *) (tf->tf_srr0));
278b8ea2c8cSmatt opc_disasm((vaddr_t)(tf->tf_srr0), insn.i_int);
27918b2f7e6Ssimonb }
2807fe4c3baSsimonb #endif
2814923f4a4Smatt return false;
28218b2f7e6Ssimonb }
28318b2f7e6Ssimonb }
28418b2f7e6Ssimonb
28518b2f7e6Ssimonb /*
286ce81a022Srin * fpu_to_single(): Helper function for stfs{,u}{,x}.
287ce81a022Srin *
288ce81a022Srin * Single-precision (float) data is internally represented in
289ce81a022Srin * double-precision (double) format in floating-point registers (FRs).
290ce81a022Srin * Even though double value cannot be translated into float format in
291ce81a022Srin * general, Power ISA (2.0.3--3.1) specify conversion algorithm when
292ce81a022Srin * stored to memory (see Sec. 4.6.3):
293ce81a022Srin *
294ce81a022Srin * - Extra fraction bits are truncated regardless of rounding mode.
295ce81a022Srin * - When magnitude is larger than the maximum number in float format,
296ce81a022Srin * bits 63--62 and 58--29 are mechanically copied into bits 31--0.
297ce81a022Srin * - When magnitude is representable as denormalized number in float
298ce81a022Srin * format, it is stored as normalized double value in FRs;
299ce81a022Srin * denormalization is required in this case.
300ce81a022Srin * - When magnitude is smaller than the minimum denormalized number in
301*6a9ecf9fSrin * float format, the result is undefined. For G5 (970MP Rev 1.1),
302ce81a022Srin * (sign | 0) seems to be stored. For G4 and prior, some ``random''
303ce81a022Srin * garbage is stored in exponent. We mimic G5 for now.
304ce81a022Srin */
305ce81a022Srin static uint32_t
fpu_to_single(uint64_t reg)306ce81a022Srin fpu_to_single(uint64_t reg)
307ce81a022Srin {
308ce81a022Srin uint32_t sign, frac, word;
309ce81a022Srin int exp, shift;
310ce81a022Srin
311ce81a022Srin sign = (reg & __BIT(63)) >> 32;
312ce81a022Srin exp = __SHIFTOUT(reg, __BITS(62, 52)) - 1023;
313ce81a022Srin if (exp > -127 || (reg & ~__BIT(63)) == 0) {
314ce81a022Srin /*
315ce81a022Srin * No denormalization required: normalized, zero, inf, NaN,
316ce81a022Srin * or numbers larger than MAXFLOAT (see comment above).
317ce81a022Srin *
318ce81a022Srin * Note that MSB and 7-LSBs in exponent are same for double
319ce81a022Srin * and float formats in this case.
320ce81a022Srin */
321ce81a022Srin word = ((reg & __BIT(62)) >> 32) |
322ce81a022Srin __SHIFTOUT(reg, __BITS(58, 52) | __BITS(51, 29));
323ce81a022Srin } else if (exp <= -127 && exp >= -149) {
324ce81a022Srin /* Denormalized. */
325ce81a022Srin shift = - 126 - exp; /* 1 ... 23 */
326ce81a022Srin frac = __SHIFTOUT(__BIT(52) | reg, __BITS(52, 29 + shift));
327ce81a022Srin word = /* __SHIFTIN(0, __BITS(30, 23)) | */ frac;
328ce81a022Srin } else {
329ce81a022Srin /* Undefined. Mimic G5 for now. */
330ce81a022Srin word = 0;
331ce81a022Srin }
332ce81a022Srin return sign | word;
333ce81a022Srin }
334ce81a022Srin
335ce81a022Srin /*
33618b2f7e6Ssimonb * Execute an FPU instruction (one that runs entirely in the FPU; not
33718b2f7e6Ssimonb * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be
33818b2f7e6Ssimonb * modified to reflect the setting the hardware would have left.
33918b2f7e6Ssimonb *
34018b2f7e6Ssimonb * Note that we do not catch all illegal opcodes, so you can, for instance,
34118b2f7e6Ssimonb * multiply two integers this way.
34218b2f7e6Ssimonb */
343d95d2a1bSrin static int
fpu_execute(struct trapframe * tf,struct fpemu * fe,union instr * insn)34418b2f7e6Ssimonb fpu_execute(struct trapframe *tf, struct fpemu *fe, union instr *insn)
34518b2f7e6Ssimonb {
34618b2f7e6Ssimonb struct fpn *fp;
34718b2f7e6Ssimonb union instr instr = *insn;
34818b2f7e6Ssimonb int *a;
349a289f324Srin int ra, rb, rc, rt, type, mask, fsr, cx, bf, setcr, cond;
350a289f324Srin u_int bits;
35118b2f7e6Ssimonb struct fpreg *fs;
35241cf117bSrin int i;
35318b2f7e6Ssimonb
35418b2f7e6Ssimonb /* Setup work. */
35518b2f7e6Ssimonb fp = NULL;
35618b2f7e6Ssimonb fs = fe->fe_fpstate;
35718b2f7e6Ssimonb fe->fe_fpscr = ((int *)&fs->fpscr)[1];
35818b2f7e6Ssimonb
35918b2f7e6Ssimonb /*
36018b2f7e6Ssimonb * On PowerPC all floating point values are stored in registers
36118b2f7e6Ssimonb * as doubles, even when used for single precision operations.
36218b2f7e6Ssimonb */
36318b2f7e6Ssimonb type = FTYPE_DBL;
36418b2f7e6Ssimonb cond = instr.i_any.i_rc;
36518b2f7e6Ssimonb setcr = 0;
36635407593Ssimonb bf = 0; /* XXX gcc */
36718b2f7e6Ssimonb
36818b2f7e6Ssimonb #if defined(DDB) && defined(DEBUG)
36918b2f7e6Ssimonb if (fpe_debug & FPE_EX) {
370b8ea2c8cSmatt vaddr_t loc = tf->tf_srr0;
37118b2f7e6Ssimonb
37218b2f7e6Ssimonb printf("Trying to emulate: %p ", (void *)loc);
37318b2f7e6Ssimonb opc_disasm(loc, instr.i_int);
37418b2f7e6Ssimonb }
37518b2f7e6Ssimonb #endif
37618b2f7e6Ssimonb
37718b2f7e6Ssimonb /*
37818b2f7e6Ssimonb * `Decode' and execute instruction.
37918b2f7e6Ssimonb */
38018b2f7e6Ssimonb
38118b2f7e6Ssimonb if ((instr.i_any.i_opcd >= OPC_LFS && instr.i_any.i_opcd <= OPC_STFDU) ||
38218b2f7e6Ssimonb instr.i_any.i_opcd == OPC_integer_31) {
38318b2f7e6Ssimonb /*
38418b2f7e6Ssimonb * Handle load/store insns:
38518b2f7e6Ssimonb *
38618b2f7e6Ssimonb * Convert to/from single if needed, calculate addr,
38718b2f7e6Ssimonb * and update index reg if needed.
38818b2f7e6Ssimonb */
389177d4805Srin vaddr_t addr;
390468acab6Srin size_t size = sizeof(double);
39118b2f7e6Ssimonb int store, update;
39218b2f7e6Ssimonb
39318b2f7e6Ssimonb cond = 0; /* ld/st never set condition codes */
39418b2f7e6Ssimonb
39518b2f7e6Ssimonb
39618b2f7e6Ssimonb if (instr.i_any.i_opcd == OPC_integer_31) {
39718b2f7e6Ssimonb if (instr.i_x.i_xo == OPC31_STFIWX) {
3980eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(stfiwx);
3990eb35ef8Sthorpej
40018b2f7e6Ssimonb /* Store as integer */
40118b2f7e6Ssimonb ra = instr.i_x.i_ra;
40218b2f7e6Ssimonb rb = instr.i_x.i_rb;
4036068b273Sthorpej DPRINTF(FPE_INSN, ("reg %d has %lx reg %d has %lx\n",
404b8ea2c8cSmatt ra, tf->tf_fixreg[ra], rb, tf->tf_fixreg[rb]));
40518b2f7e6Ssimonb
406b8ea2c8cSmatt addr = tf->tf_fixreg[rb];
40718b2f7e6Ssimonb if (ra != 0)
408b8ea2c8cSmatt addr += tf->tf_fixreg[ra];
40918b2f7e6Ssimonb rt = instr.i_x.i_rt;
41018b2f7e6Ssimonb a = (int *)&fs->fpreg[rt];
41118b2f7e6Ssimonb DPRINTF(FPE_INSN,
41218b2f7e6Ssimonb ("fpu_execute: Store INT %x at %p\n",
41318b2f7e6Ssimonb a[1], (void *)addr));
4144923f4a4Smatt if (copyout(&a[1], (void *)addr, sizeof(int))) {
4154923f4a4Smatt fe->fe_addr = addr;
41618b2f7e6Ssimonb return (FAULT);
4174923f4a4Smatt }
41818b2f7e6Ssimonb return (0);
41918b2f7e6Ssimonb }
42018b2f7e6Ssimonb
42118b2f7e6Ssimonb if ((instr.i_x.i_xo & OPC31_FPMASK) != OPC31_FPOP)
42218b2f7e6Ssimonb /* Not an indexed FP load/store op */
42318b2f7e6Ssimonb return (NOTFPU);
42418b2f7e6Ssimonb
42518b2f7e6Ssimonb store = (instr.i_x.i_xo & 0x80);
426468acab6Srin if ((instr.i_x.i_xo & 0x40) == 0) {
42718b2f7e6Ssimonb type = FTYPE_SNG;
428468acab6Srin size = sizeof(float);
429468acab6Srin }
43018b2f7e6Ssimonb update = (instr.i_x.i_xo & 0x20);
43118b2f7e6Ssimonb
43218b2f7e6Ssimonb /* calculate EA of load/store */
43318b2f7e6Ssimonb ra = instr.i_x.i_ra;
43418b2f7e6Ssimonb rb = instr.i_x.i_rb;
4356068b273Sthorpej DPRINTF(FPE_INSN, ("reg %d has %lx reg %d has %lx\n",
436b8ea2c8cSmatt ra, tf->tf_fixreg[ra], rb, tf->tf_fixreg[rb]));
437b8ea2c8cSmatt addr = tf->tf_fixreg[rb];
43818b2f7e6Ssimonb if (ra != 0)
439b8ea2c8cSmatt addr += tf->tf_fixreg[ra];
44018b2f7e6Ssimonb rt = instr.i_x.i_rt;
44118b2f7e6Ssimonb } else {
44218b2f7e6Ssimonb store = instr.i_d.i_opcd & 0x4;
443468acab6Srin if ((instr.i_d.i_opcd & 0x2) == 0) {
44418b2f7e6Ssimonb type = FTYPE_SNG;
445468acab6Srin size = sizeof(float);
446468acab6Srin }
44718b2f7e6Ssimonb update = instr.i_d.i_opcd & 0x1;
44818b2f7e6Ssimonb
44918b2f7e6Ssimonb /* calculate EA of load/store */
45018b2f7e6Ssimonb ra = instr.i_d.i_ra;
45118b2f7e6Ssimonb addr = instr.i_d.i_d;
4526068b273Sthorpej DPRINTF(FPE_INSN, ("reg %d has %lx displ %lx\n",
453b8ea2c8cSmatt ra, tf->tf_fixreg[ra], addr));
45418b2f7e6Ssimonb if (ra != 0)
455b8ea2c8cSmatt addr += tf->tf_fixreg[ra];
45618b2f7e6Ssimonb rt = instr.i_d.i_rt;
45718b2f7e6Ssimonb }
45818b2f7e6Ssimonb
45918b2f7e6Ssimonb if (update && ra == 0)
46018b2f7e6Ssimonb return (NOTFPU);
46118b2f7e6Ssimonb
46218b2f7e6Ssimonb if (store) {
46318b2f7e6Ssimonb /* Store */
464ce81a022Srin uint32_t word;
465ce81a022Srin const void *kaddr;
466ce81a022Srin
4670eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fpstore);
46818b2f7e6Ssimonb if (type != FTYPE_DBL) {
469ce81a022Srin /*
470ce81a022Srin * As Power ISA specifies conversion algorithm
471ce81a022Srin * for store floating-point single insns, we
472ce81a022Srin * cannot use fpu_explode() and _implode() here.
473ce81a022Srin * See fpu_to_single() and comment therein for
474ce81a022Srin * more details.
475ce81a022Srin */
47618b2f7e6Ssimonb DPRINTF(FPE_INSN,
47718b2f7e6Ssimonb ("fpu_execute: Store SNG at %p\n",
47818b2f7e6Ssimonb (void *)addr));
479ce81a022Srin word = fpu_to_single(FR(rt));
480ce81a022Srin kaddr = &word;
48118b2f7e6Ssimonb } else {
48218b2f7e6Ssimonb DPRINTF(FPE_INSN,
48318b2f7e6Ssimonb ("fpu_execute: Store DBL at %p\n",
48418b2f7e6Ssimonb (void *)addr));
485ce81a022Srin kaddr = &FR(rt);
486ce81a022Srin }
487ce81a022Srin if (copyout(kaddr, (void *)addr, size)) {
4884923f4a4Smatt fe->fe_addr = addr;
48918b2f7e6Ssimonb return (FAULT);
49018b2f7e6Ssimonb }
49118b2f7e6Ssimonb } else {
49218b2f7e6Ssimonb /* Load */
4930eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fpload);
49418b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: Load from %p\n",
49518b2f7e6Ssimonb (void *)addr));
496f608923fSrin if (copyin((const void *)addr, &FR(rt), size)) {
4974923f4a4Smatt fe->fe_addr = addr;
49818b2f7e6Ssimonb return (FAULT);
4994923f4a4Smatt }
50018b2f7e6Ssimonb if (type != FTYPE_DBL) {
501e41cdfd4Srin fpu_explode(fe, fp = &fe->fe_f1, type, FR(rt));
502e41cdfd4Srin fpu_implode(fe, fp, FTYPE_DBL, &FR(rt));
50318b2f7e6Ssimonb }
50418b2f7e6Ssimonb }
50518b2f7e6Ssimonb if (update)
506b8ea2c8cSmatt tf->tf_fixreg[ra] = addr;
50718b2f7e6Ssimonb /* Complete. */
50818b2f7e6Ssimonb return (0);
50918b2f7e6Ssimonb } else if (instr.i_any.i_opcd == OPC_sp_fp_59 ||
51018b2f7e6Ssimonb instr.i_any.i_opcd == OPC_dp_fp_63) {
51118b2f7e6Ssimonb
51218b2f7e6Ssimonb
51318b2f7e6Ssimonb if (instr.i_any.i_opcd == OPC_dp_fp_63 &&
51418b2f7e6Ssimonb !(instr.i_a.i_xo & OPC63M_MASK)) {
51518b2f7e6Ssimonb /* Format X */
51618b2f7e6Ssimonb rt = instr.i_x.i_rt;
51718b2f7e6Ssimonb ra = instr.i_x.i_ra;
51818b2f7e6Ssimonb rb = instr.i_x.i_rb;
51918b2f7e6Ssimonb
52018b2f7e6Ssimonb
52118b2f7e6Ssimonb /* One of the special opcodes.... */
52218b2f7e6Ssimonb switch (instr.i_x.i_xo) {
52318b2f7e6Ssimonb case OPC63_FCMPU:
5240eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fcmpu);
52518b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FCMPU\n"));
52618b2f7e6Ssimonb rt >>= 2;
527e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
528e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
52918b2f7e6Ssimonb fpu_compare(fe, 0);
53018b2f7e6Ssimonb /* Make sure we do the condition regs. */
53118b2f7e6Ssimonb cond = 0;
53218b2f7e6Ssimonb /* N.B.: i_rs is already left shifted by two. */
53318b2f7e6Ssimonb bf = instr.i_x.i_rs & 0xfc;
53418b2f7e6Ssimonb setcr = 1;
53518b2f7e6Ssimonb break;
53618b2f7e6Ssimonb
53718b2f7e6Ssimonb case OPC63_FRSP:
53818b2f7e6Ssimonb /*
53918b2f7e6Ssimonb * Convert to single:
54018b2f7e6Ssimonb *
54118b2f7e6Ssimonb * PowerPC uses this to round a double
54218b2f7e6Ssimonb * precision value to single precision,
54318b2f7e6Ssimonb * but values in registers are always
54418b2f7e6Ssimonb * stored in double precision format.
54518b2f7e6Ssimonb */
5460eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(frsp);
54718b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FRSP\n"));
548e41cdfd4Srin fpu_explode(fe, fp = &fe->fe_f1, FTYPE_DBL,
549e41cdfd4Srin FR(rb));
550e41cdfd4Srin fpu_implode(fe, fp, FTYPE_SNG, &FR(rt));
551e41cdfd4Srin fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG,
552e41cdfd4Srin FR(rt));
55341cf117bSrin type = FTYPE_DBL | FTYPE_FPSCR;
55418b2f7e6Ssimonb break;
55518b2f7e6Ssimonb case OPC63_FCTIW:
55618b2f7e6Ssimonb case OPC63_FCTIWZ:
5570eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fctiw);
55818b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FCTIW\n"));
559e41cdfd4Srin fpu_explode(fe, fp = &fe->fe_f1, type, FR(rb));
56053d5668dSrin type = FTYPE_INT | FTYPE_FPSCR;
5612a0258e4Srin if (instr.i_x.i_xo == OPC63_FCTIWZ)
5622a0258e4Srin type |= FTYPE_RD_RZ;
56318b2f7e6Ssimonb break;
56418b2f7e6Ssimonb case OPC63_FCMPO:
5650eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fcmpo);
56618b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FCMPO\n"));
56718b2f7e6Ssimonb rt >>= 2;
568e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
569e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
57018b2f7e6Ssimonb fpu_compare(fe, 1);
57118b2f7e6Ssimonb /* Make sure we do the condition regs. */
57218b2f7e6Ssimonb cond = 0;
57318b2f7e6Ssimonb /* N.B.: i_rs is already left shifted by two. */
57418b2f7e6Ssimonb bf = instr.i_x.i_rs & 0xfc;
57518b2f7e6Ssimonb setcr = 1;
57618b2f7e6Ssimonb break;
57718b2f7e6Ssimonb case OPC63_MTFSB1:
5780eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(mtfsb1);
57918b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: MTFSB1\n"));
5800e21243cSrin fe->fe_cx = (1 << (31 - rt)) &
5810e21243cSrin ~(FPSCR_FEX | FPSCR_VX);
58218b2f7e6Ssimonb break;
58318b2f7e6Ssimonb case OPC63_FNEG:
5840eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fnegabs);
58518b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FNEGABS\n"));
586c5a6be17Swiz memcpy(&fs->fpreg[rt], &fs->fpreg[rb],
58718b2f7e6Ssimonb sizeof(double));
58818b2f7e6Ssimonb a = (int *)&fs->fpreg[rt];
58918b2f7e6Ssimonb *a ^= (1 << 31);
59018b2f7e6Ssimonb break;
59118b2f7e6Ssimonb case OPC63_MCRFS:
5920eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(mcrfs);
59318b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: MCRFS\n"));
59418b2f7e6Ssimonb cond = 0;
59518b2f7e6Ssimonb rt &= 0x1c;
59618b2f7e6Ssimonb ra &= 0x1c;
59718b2f7e6Ssimonb /* Extract the bits we want */
598bcec7308Srin bits = (fe->fe_fpscr >> (28 - ra)) & 0xf;
59918b2f7e6Ssimonb /* Clear the bits we copied. */
600bcec7308Srin mask = (0xf << (28 - ra)) & MCRFS_MASK;
601bcec7308Srin fe->fe_fpscr &= ~mask;
60218b2f7e6Ssimonb /* Now shove them in the right part of cr */
603b8ea2c8cSmatt tf->tf_cr &= ~(0xf << (28 - rt));
604bcec7308Srin tf->tf_cr |= bits << (28 - rt);
60518b2f7e6Ssimonb break;
60618b2f7e6Ssimonb case OPC63_MTFSB0:
6070eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(mtfsb0);
60818b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: MTFSB0\n"));
6090e21243cSrin fe->fe_fpscr &= ~(1 << (31 - rt)) |
6100e21243cSrin (FPSCR_FEX | FPSCR_VX);
61118b2f7e6Ssimonb break;
61218b2f7e6Ssimonb case OPC63_FMR:
6130eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fmr);
61418b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FMR\n"));
615c5a6be17Swiz memcpy(&fs->fpreg[rt], &fs->fpreg[rb],
61618b2f7e6Ssimonb sizeof(double));
61718b2f7e6Ssimonb break;
61818b2f7e6Ssimonb case OPC63_MTFSFI:
6190eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(mtfsfi);
62018b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: MTFSFI\n"));
62118b2f7e6Ssimonb rb >>= 1;
62218b2f7e6Ssimonb rt &= 0x1c; /* Already left-shifted 4 */
623a4da158fSrin bits = rb << (28 - rt);
62418b2f7e6Ssimonb mask = 0xf << (28 - rt);
625a4da158fSrin fe->fe_fpscr = (fe->fe_fpscr & ~mask) | bits;
62618b2f7e6Ssimonb break;
62718b2f7e6Ssimonb case OPC63_FNABS:
6280eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fnabs);
62918b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FABS\n"));
630c5a6be17Swiz memcpy(&fs->fpreg[rt], &fs->fpreg[rb],
63118b2f7e6Ssimonb sizeof(double));
63218b2f7e6Ssimonb a = (int *)&fs->fpreg[rt];
63318b2f7e6Ssimonb *a |= (1 << 31);
63418b2f7e6Ssimonb break;
63518b2f7e6Ssimonb case OPC63_FABS:
6360eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fabs);
63718b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FABS\n"));
638c5a6be17Swiz memcpy(&fs->fpreg[rt], &fs->fpreg[rb],
63918b2f7e6Ssimonb sizeof(double));
64018b2f7e6Ssimonb a = (int *)&fs->fpreg[rt];
64118b2f7e6Ssimonb *a &= ~(1 << 31);
64218b2f7e6Ssimonb break;
64318b2f7e6Ssimonb case OPC63_MFFS:
6440eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(mffs);
64518b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: MFFS\n"));
646c5a6be17Swiz memcpy(&fs->fpreg[rt], &fs->fpscr,
64718b2f7e6Ssimonb sizeof(fs->fpscr));
64818b2f7e6Ssimonb break;
64918b2f7e6Ssimonb case OPC63_MTFSF:
6500eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(mtfsf);
65118b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: MTFSF\n"));
652a4da158fSrin if ((rt = instr.i_xfl.i_flm) == -1) {
65318b2f7e6Ssimonb mask = -1;
654a4da158fSrin } else {
65518b2f7e6Ssimonb mask = 0;
65618b2f7e6Ssimonb /* Convert 1 bit -> 4 bits */
657a4da158fSrin for (i = 0; i < 8; i++)
658a4da158fSrin if (rt & (1 << i))
659a4da158fSrin mask |=
660a4da158fSrin (0xf << (4 * i));
66118b2f7e6Ssimonb }
662c7cd6dc4Srin a = (int *)&fs->fpreg[rb];
663a4da158fSrin bits = a[1] & mask;
664a4da158fSrin fe->fe_fpscr = (fe->fe_fpscr & ~mask) | bits;
66518b2f7e6Ssimonb break;
66618b2f7e6Ssimonb case OPC63_FCTID:
66718b2f7e6Ssimonb case OPC63_FCTIDZ:
6680eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fctid);
66918b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FCTID\n"));
670e41cdfd4Srin fpu_explode(fe, fp = &fe->fe_f1, type, FR(rb));
67153d5668dSrin type = FTYPE_LNG | FTYPE_FPSCR;
6722a0258e4Srin if (instr.i_x.i_xo == OPC63_FCTIDZ)
6732a0258e4Srin type |= FTYPE_RD_RZ;
67418b2f7e6Ssimonb break;
67518b2f7e6Ssimonb case OPC63_FCFID:
6760eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fcfid);
67718b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FCFID\n"));
678995c865bSrin fpu_explode(fe, fp = &fe->fe_f1, FTYPE_LNG,
679995c865bSrin FR(rb));
68041cf117bSrin type = FTYPE_DBL | FTYPE_FPSCR;
68118b2f7e6Ssimonb break;
68218b2f7e6Ssimonb default:
68318b2f7e6Ssimonb return (NOTFPU);
68418b2f7e6Ssimonb break;
68518b2f7e6Ssimonb }
68618b2f7e6Ssimonb } else {
68718b2f7e6Ssimonb /* Format A */
68818b2f7e6Ssimonb rt = instr.i_a.i_frt;
68918b2f7e6Ssimonb ra = instr.i_a.i_fra;
69018b2f7e6Ssimonb rb = instr.i_a.i_frb;
69118b2f7e6Ssimonb rc = instr.i_a.i_frc;
69218b2f7e6Ssimonb
693011775c9Srin /*
694011775c9Srin * All arithmetic operations work on registers, which
695011775c9Srin * are stored as doubles.
696011775c9Srin */
69718b2f7e6Ssimonb type = FTYPE_DBL;
69818b2f7e6Ssimonb switch ((unsigned int)instr.i_a.i_xo) {
69918b2f7e6Ssimonb case OPC59_FDIVS:
7000eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fdiv);
70118b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FDIV\n"));
702e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
703e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
70418b2f7e6Ssimonb fp = fpu_div(fe);
70518b2f7e6Ssimonb break;
70618b2f7e6Ssimonb case OPC59_FSUBS:
7070eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fsub);
70818b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FSUB\n"));
709e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
710e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
71118b2f7e6Ssimonb fp = fpu_sub(fe);
71218b2f7e6Ssimonb break;
71318b2f7e6Ssimonb case OPC59_FADDS:
7140eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fadd);
71518b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FADD\n"));
716e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
717e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
71818b2f7e6Ssimonb fp = fpu_add(fe);
71918b2f7e6Ssimonb break;
72018b2f7e6Ssimonb case OPC59_FSQRTS:
7210eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fsqrt);
72218b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FSQRT\n"));
723e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(rb));
72418b2f7e6Ssimonb fp = fpu_sqrt(fe);
72518b2f7e6Ssimonb break;
72618b2f7e6Ssimonb case OPC63M_FSEL:
7270eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fsel);
72818b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FSEL\n"));
72918b2f7e6Ssimonb a = (int *)&fe->fe_fpstate->fpreg[ra];
730bb2c4f43Srin if ((( a[0] & 0x80000000) &&
731bb2c4f43Srin ((a[0] & 0x7fffffff) | a[1])) ||
732bb2c4f43Srin (( a[0] & 0x7ff00000) &&
733bb2c4f43Srin ((a[0] & 0x000fffff) | a[1]))) {
734bb2c4f43Srin /* negative/NaN or NaN */
73518b2f7e6Ssimonb rc = rb;
736bb2c4f43Srin }
73718b2f7e6Ssimonb DPRINTF(FPE_INSN, ("f%d => f%d\n", rc, rt));
738c5a6be17Swiz memcpy(&fs->fpreg[rt], &fs->fpreg[rc],
73918b2f7e6Ssimonb sizeof(double));
74018b2f7e6Ssimonb break;
74118b2f7e6Ssimonb case OPC59_FRES:
7420eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fpres);
74318b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FPRES\n"));
744e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, FTYPE_INT, 1);
745bd50f540Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
746bd50f540Srin fp = fpu_div(fe);
74718b2f7e6Ssimonb break;
74818b2f7e6Ssimonb case OPC59_FMULS:
7490eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fmul);
75018b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FMUL\n"));
751e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
752e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rc));
75318b2f7e6Ssimonb fp = fpu_mul(fe);
75418b2f7e6Ssimonb break;
75518b2f7e6Ssimonb case OPC63M_FRSQRTE:
75618b2f7e6Ssimonb /* Reciprocal sqrt() estimate */
7570eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(frsqrte);
75818b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FRSQRTE\n"));
759e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(rb));
76017d7b875Sscw fp = fpu_sqrt(fe);
76118b2f7e6Ssimonb fe->fe_f2 = *fp;
762e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, FTYPE_INT, 1);
763bd50f540Srin fp = fpu_div(fe);
76418b2f7e6Ssimonb break;
76518b2f7e6Ssimonb case OPC59_FMSUBS:
7664d3fc228Srin FPU_EMU_EVCNT_INCR(fmsub);
7674d3fc228Srin DPRINTF(FPE_INSN, ("fpu_execute: FMSUB\n"));
768e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
769e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rc));
77018b2f7e6Ssimonb fp = fpu_mul(fe);
77118b2f7e6Ssimonb fe->fe_f1 = *fp;
772e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
77318b2f7e6Ssimonb fp = fpu_sub(fe);
77418b2f7e6Ssimonb break;
77518b2f7e6Ssimonb case OPC59_FMADDS:
7764d3fc228Srin FPU_EMU_EVCNT_INCR(fmadd);
7774d3fc228Srin DPRINTF(FPE_INSN, ("fpu_execute: FMADD\n"));
778e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
779e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rc));
78018b2f7e6Ssimonb fp = fpu_mul(fe);
78118b2f7e6Ssimonb fe->fe_f1 = *fp;
782e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
78318b2f7e6Ssimonb fp = fpu_add(fe);
78418b2f7e6Ssimonb break;
78518b2f7e6Ssimonb case OPC59_FNMSUBS:
7860eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fnmsub);
78718b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FNMSUB\n"));
788e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
789e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rc));
79018b2f7e6Ssimonb fp = fpu_mul(fe);
79118b2f7e6Ssimonb fe->fe_f1 = *fp;
792e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
79318b2f7e6Ssimonb fp = fpu_sub(fe);
79418b2f7e6Ssimonb /* Negate */
79576a49f3aSrin if (!ISNAN(fp))
79618b2f7e6Ssimonb fp->fp_sign ^= 1;
79718b2f7e6Ssimonb break;
79818b2f7e6Ssimonb case OPC59_FNMADDS:
7990eb35ef8Sthorpej FPU_EMU_EVCNT_INCR(fnmadd);
80018b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: FNMADD\n"));
801e41cdfd4Srin fpu_explode(fe, &fe->fe_f1, type, FR(ra));
802e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rc));
80318b2f7e6Ssimonb fp = fpu_mul(fe);
80418b2f7e6Ssimonb fe->fe_f1 = *fp;
805e41cdfd4Srin fpu_explode(fe, &fe->fe_f2, type, FR(rb));
80618b2f7e6Ssimonb fp = fpu_add(fe);
80718b2f7e6Ssimonb /* Negate */
80876a49f3aSrin if (!ISNAN(fp))
80918b2f7e6Ssimonb fp->fp_sign ^= 1;
81018b2f7e6Ssimonb break;
81118b2f7e6Ssimonb default:
81218b2f7e6Ssimonb return (NOTFPU);
81318b2f7e6Ssimonb break;
81418b2f7e6Ssimonb }
815011775c9Srin
816011775c9Srin /* If the instruction was single precision, round */
817011775c9Srin if (!(instr.i_any.i_opcd & 0x4)) {
81841cf117bSrin fpu_implode(fe, fp, FTYPE_SNG | FTYPE_FPSCR,
819e41cdfd4Srin &FR(rt));
820e41cdfd4Srin fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG,
821e41cdfd4Srin FR(rt));
8225ef7ca99Srin } else
82341cf117bSrin type |= FTYPE_FPSCR;
82418b2f7e6Ssimonb }
82518b2f7e6Ssimonb } else {
82618b2f7e6Ssimonb return (NOTFPU);
82718b2f7e6Ssimonb }
82818b2f7e6Ssimonb
82918b2f7e6Ssimonb /*
83018b2f7e6Ssimonb * ALU operation is complete. Collapse the result and then check
83118b2f7e6Ssimonb * for exceptions. If we got any, and they are enabled, do not
83218b2f7e6Ssimonb * alter the destination register, just stop with an exception.
83318b2f7e6Ssimonb * Otherwise set new current exceptions and accrue.
83418b2f7e6Ssimonb */
83518b2f7e6Ssimonb if (fp)
836e41cdfd4Srin fpu_implode(fe, fp, type, &FR(rt));
83718b2f7e6Ssimonb cx = fe->fe_cx;
838f86c9cf0Srin fsr = fe->fe_fpscr & ~(FPSCR_FEX|FPSCR_VX);
83918b2f7e6Ssimonb if (cx != 0) {
84018b2f7e6Ssimonb fsr |= cx;
84118b2f7e6Ssimonb DPRINTF(FPE_INSN, ("fpu_execute: cx %x, fsr %x\n", cx, fsr));
84218b2f7e6Ssimonb }
843f86c9cf0Srin if (fsr & FPSR_INV)
844f86c9cf0Srin fsr |= FPSCR_VX;
8451b2567feSrin mask = (fsr & FPSR_EX) << (25 - 3);
8461b2567feSrin if (fsr & mask)
8471b2567feSrin fsr |= FPSCR_FEX;
848a4da158fSrin if ((fsr ^ fe->fe_fpscr) & FPSR_EX_MSK)
849f86c9cf0Srin fsr |= FPSCR_FX;
85018b2f7e6Ssimonb
85118b2f7e6Ssimonb if (cond) {
852a289f324Srin bits = fsr & 0xf0000000;
85318b2f7e6Ssimonb /* Isolate condition codes */
854a289f324Srin bits >>= 28;
85518b2f7e6Ssimonb /* Move fpu condition codes to cr[1] */
85611704a44Srin tf->tf_cr &= ~(0x0f000000);
857a289f324Srin tf->tf_cr |= (bits << 24);
858a289f324Srin DPRINTF(FPE_INSN, ("fpu_execute: cr[1] <= %x\n", bits));
85918b2f7e6Ssimonb }
86018b2f7e6Ssimonb
86118b2f7e6Ssimonb if (setcr) {
862a289f324Srin bits = fsr & FPSCR_FPCC;
86318b2f7e6Ssimonb /* Isolate condition codes */
864a289f324Srin bits <<= 16;
865bf24477dSrin /* Move fpu condition codes to cr[bf/4] */
866b8ea2c8cSmatt tf->tf_cr &= ~(0xf0000000>>bf);
867a289f324Srin tf->tf_cr |= (bits >> bf);
868a289f324Srin DPRINTF(FPE_INSN, ("fpu_execute: cr[%d] (cr=%x) <= %x\n", bf/4, tf->tf_cr, bits));
86918b2f7e6Ssimonb }
87018b2f7e6Ssimonb
87118b2f7e6Ssimonb ((int *)&fs->fpscr)[1] = fsr;
87218b2f7e6Ssimonb if (fsr & FPSCR_FEX)
87318b2f7e6Ssimonb return(FPE);
87418b2f7e6Ssimonb return (0); /* success */
87518b2f7e6Ssimonb }
876