1176491Smarcel/* $NetBSD: fpu_emu.c,v 1.14 2005/12/11 12:18:42 christos Exp $ */ 2176491Smarcel 3176491Smarcel/* 4176491Smarcel * Copyright 2001 Wasabi Systems, Inc. 5176491Smarcel * All rights reserved. 6176491Smarcel * 7176491Smarcel * Written by Eduardo Horvath and Simon Burge for Wasabi Systems, Inc. 8176491Smarcel * 9176491Smarcel * Redistribution and use in source and binary forms, with or without 10176491Smarcel * modification, are permitted provided that the following conditions 11176491Smarcel * are met: 12176491Smarcel * 1. Redistributions of source code must retain the above copyright 13176491Smarcel * notice, this list of conditions and the following disclaimer. 14176491Smarcel * 2. Redistributions in binary form must reproduce the above copyright 15176491Smarcel * notice, this list of conditions and the following disclaimer in the 16176491Smarcel * documentation and/or other materials provided with the distribution. 17176491Smarcel * 3. All advertising materials mentioning features or use of this software 18176491Smarcel * must display the following acknowledgement: 19176491Smarcel * This product includes software developed for the NetBSD Project by 20176491Smarcel * Wasabi Systems, Inc. 21176491Smarcel * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22176491Smarcel * or promote products derived from this software without specific prior 23176491Smarcel * written permission. 24176491Smarcel * 25176491Smarcel * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26176491Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27176491Smarcel * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28176491Smarcel * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29176491Smarcel * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30176491Smarcel * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31176491Smarcel * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32176491Smarcel * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33176491Smarcel * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34176491Smarcel * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35176491Smarcel * POSSIBILITY OF SUCH DAMAGE. 36176491Smarcel */ 37176491Smarcel 38176491Smarcel/* 39176491Smarcel * Copyright (c) 1992, 1993 40176491Smarcel * The Regents of the University of California. All rights reserved. 41176491Smarcel * 42176491Smarcel * This software was developed by the Computer Systems Engineering group 43176491Smarcel * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 44176491Smarcel * contributed to Berkeley. 45176491Smarcel * 46176491Smarcel * All advertising materials mentioning features or use of this software 47176491Smarcel * must display the following acknowledgement: 48176491Smarcel * This product includes software developed by the University of 49176491Smarcel * California, Lawrence Berkeley Laboratory. 50176491Smarcel * 51176491Smarcel * Redistribution and use in source and binary forms, with or without 52176491Smarcel * modification, are permitted provided that the following conditions 53176491Smarcel * are met: 54176491Smarcel * 1. Redistributions of source code must retain the above copyright 55176491Smarcel * notice, this list of conditions and the following disclaimer. 56176491Smarcel * 2. Redistributions in binary form must reproduce the above copyright 57176491Smarcel * notice, this list of conditions and the following disclaimer in the 58176491Smarcel * documentation and/or other materials provided with the distribution. 59176491Smarcel * 3. Neither the name of the University nor the names of its contributors 60176491Smarcel * may be used to endorse or promote products derived from this software 61176491Smarcel * without specific prior written permission. 62176491Smarcel * 63176491Smarcel * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 64176491Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 65176491Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 66176491Smarcel * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 67176491Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 68176491Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 69176491Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 70176491Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 71176491Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 72176491Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 73176491Smarcel * SUCH DAMAGE. 74176491Smarcel * 75176491Smarcel * @(#)fpu.c 8.1 (Berkeley) 6/11/93 76176491Smarcel */ 77176491Smarcel 78176491Smarcel#include <sys/cdefs.h> 79176491Smarcel__FBSDID("$FreeBSD: releng/10.3/sys/powerpc/fpu/fpu_emu.c 266005 2014-05-14 04:57:55Z ian $"); 80176491Smarcel 81176491Smarcel#include "opt_ddb.h" 82176491Smarcel 83176491Smarcel#include <sys/param.h> 84176491Smarcel#include <sys/systm.h> 85176491Smarcel#include <sys/kdb.h> 86176491Smarcel#include <sys/kernel.h> 87176491Smarcel#include <sys/proc.h> 88176491Smarcel#include <sys/sysctl.h> 89176491Smarcel#include <sys/signal.h> 90176491Smarcel#include <sys/syslog.h> 91176491Smarcel#include <sys/signalvar.h> 92176491Smarcel 93176491Smarcel#include <machine/fpu.h> 94176491Smarcel#include <machine/reg.h> 95176491Smarcel 96176491Smarcel#include <powerpc/fpu/fpu_emu.h> 97176491Smarcel#include <powerpc/fpu/fpu_extern.h> 98176491Smarcel#include <powerpc/fpu/fpu_instr.h> 99176491Smarcel 100227309Sedstatic SYSCTL_NODE(_hw, OID_AUTO, fpu_emu, CTLFLAG_RW, 0, "FPU emulator"); 101176491Smarcel 102176491Smarcel#define FPU_EMU_EVCNT_DECL(name) \ 103176491Smarcelstatic u_int fpu_emu_evcnt_##name; \ 104176491SmarcelSYSCTL_INT(_hw_fpu_emu, OID_AUTO, evcnt_##name, CTLFLAG_RD, \ 105176491Smarcel &fpu_emu_evcnt_##name, 0, "") 106176491Smarcel 107176491Smarcel#define FPU_EMU_EVCNT_INCR(name) fpu_emu_evcnt_##name++ 108176491Smarcel 109176491SmarcelFPU_EMU_EVCNT_DECL(stfiwx); 110176491SmarcelFPU_EMU_EVCNT_DECL(fpstore); 111176491SmarcelFPU_EMU_EVCNT_DECL(fpload); 112176491SmarcelFPU_EMU_EVCNT_DECL(fcmpu); 113176491SmarcelFPU_EMU_EVCNT_DECL(frsp); 114176491SmarcelFPU_EMU_EVCNT_DECL(fctiw); 115176491SmarcelFPU_EMU_EVCNT_DECL(fcmpo); 116176491SmarcelFPU_EMU_EVCNT_DECL(mtfsb1); 117176491SmarcelFPU_EMU_EVCNT_DECL(fnegabs); 118176491SmarcelFPU_EMU_EVCNT_DECL(mcrfs); 119176491SmarcelFPU_EMU_EVCNT_DECL(mtfsb0); 120176491SmarcelFPU_EMU_EVCNT_DECL(fmr); 121176491SmarcelFPU_EMU_EVCNT_DECL(mtfsfi); 122176491SmarcelFPU_EMU_EVCNT_DECL(fnabs); 123176491SmarcelFPU_EMU_EVCNT_DECL(fabs); 124176491SmarcelFPU_EMU_EVCNT_DECL(mffs); 125176491SmarcelFPU_EMU_EVCNT_DECL(mtfsf); 126176491SmarcelFPU_EMU_EVCNT_DECL(fctid); 127176491SmarcelFPU_EMU_EVCNT_DECL(fcfid); 128176491SmarcelFPU_EMU_EVCNT_DECL(fdiv); 129176491SmarcelFPU_EMU_EVCNT_DECL(fsub); 130176491SmarcelFPU_EMU_EVCNT_DECL(fadd); 131176491SmarcelFPU_EMU_EVCNT_DECL(fsqrt); 132176491SmarcelFPU_EMU_EVCNT_DECL(fsel); 133176491SmarcelFPU_EMU_EVCNT_DECL(fpres); 134176491SmarcelFPU_EMU_EVCNT_DECL(fmul); 135176491SmarcelFPU_EMU_EVCNT_DECL(frsqrte); 136176491SmarcelFPU_EMU_EVCNT_DECL(fmulsub); 137176491SmarcelFPU_EMU_EVCNT_DECL(fmuladd); 138176491SmarcelFPU_EMU_EVCNT_DECL(fnmsub); 139176491SmarcelFPU_EMU_EVCNT_DECL(fnmadd); 140176491Smarcel 141176491Smarcel/* FPSR exception masks */ 142176491Smarcel#define FPSR_EX_MSK (FPSCR_VX|FPSCR_OX|FPSCR_UX|FPSCR_ZX| \ 143176491Smarcel FPSCR_XX|FPSCR_VXSNAN|FPSCR_VXISI|FPSCR_VXIDI| \ 144176491Smarcel FPSCR_VXZDZ|FPSCR_VXIMZ|FPSCR_VXVC|FPSCR_VXSOFT|\ 145176491Smarcel FPSCR_VXSQRT|FPSCR_VXCVI) 146176491Smarcel#define FPSR_EX (FPSCR_VE|FPSCR_OE|FPSCR_UE|FPSCR_ZE|FPSCR_XE) 147176491Smarcel#define FPSR_EXOP (FPSR_EX_MSK&(~FPSR_EX)) 148176491Smarcel 149176491Smarcelint fpe_debug = 0; 150176491Smarcel 151176491Smarcel#ifdef DEBUG 152176491Smarcelvm_offset_t opc_disasm(vm_offset_t, int); 153176491Smarcel 154176491Smarcel/* 155176491Smarcel * Dump a `fpn' structure. 156176491Smarcel */ 157176491Smarcelvoid 158176491Smarcelfpu_dumpfpn(struct fpn *fp) 159176491Smarcel{ 160176491Smarcel static const char *class[] = { 161176491Smarcel "SNAN", "QNAN", "ZERO", "NUM", "INF" 162176491Smarcel }; 163176491Smarcel 164176491Smarcel printf("%s %c.%x %x %x %xE%d", class[fp->fp_class + 2], 165176491Smarcel fp->fp_sign ? '-' : ' ', 166176491Smarcel fp->fp_mant[0], fp->fp_mant[1], 167176491Smarcel fp->fp_mant[2], fp->fp_mant[3], 168176491Smarcel fp->fp_exp); 169176491Smarcel} 170176491Smarcel#endif 171176491Smarcel 172176491Smarcel/* 173176491Smarcel * fpu_execute returns the following error numbers (0 = no error): 174176491Smarcel */ 175176491Smarcel#define FPE 1 /* take a floating point exception */ 176176491Smarcel#define NOTFPU 2 /* not an FPU instruction */ 177176491Smarcel#define FAULT 3 178176491Smarcel 179176491Smarcel 180176491Smarcel/* 181176491Smarcel * Emulate a floating-point instruction. 182176491Smarcel * Return zero for success, else signal number. 183176491Smarcel * (Typically: zero, SIGFPE, SIGILL, SIGSEGV) 184176491Smarcel */ 185176491Smarcelint 186176491Smarcelfpu_emulate(struct trapframe *frame, struct fpreg *fpf) 187176491Smarcel{ 188176491Smarcel static union instr insn; 189176491Smarcel static struct fpemu fe; 190176491Smarcel static int lastill = 0; 191176491Smarcel int sig; 192176491Smarcel 193176491Smarcel /* initialize insn.is_datasize to tell it is *not* initialized */ 194176491Smarcel fe.fe_fpstate = fpf; 195176491Smarcel fe.fe_cx = 0; 196176491Smarcel 197176491Smarcel /* always set this (to avoid a warning) */ 198176491Smarcel 199176491Smarcel if (copyin((void *) (frame->srr0), &insn.i_int, sizeof (insn.i_int))) { 200176491Smarcel#ifdef DEBUG 201176491Smarcel printf("fpu_emulate: fault reading opcode\n"); 202176491Smarcel#endif 203176491Smarcel return SIGSEGV; 204176491Smarcel } 205176491Smarcel 206176491Smarcel DPRINTF(FPE_EX, ("fpu_emulate: emulating insn %x at %p\n", 207176491Smarcel insn.i_int, (void *)frame->srr0)); 208176491Smarcel 209176491Smarcel 210176491Smarcel if ((insn.i_any.i_opcd == OPC_TWI) || 211176491Smarcel ((insn.i_any.i_opcd == OPC_integer_31) && 212176491Smarcel (insn.i_x.i_xo == OPC31_TW))) { 213176491Smarcel /* Check for the two trap insns. */ 214176491Smarcel DPRINTF(FPE_EX, ("fpu_emulate: SIGTRAP\n")); 215176491Smarcel return (SIGTRAP); 216176491Smarcel } 217176491Smarcel sig = 0; 218176491Smarcel switch (fpu_execute(frame, &fe, &insn)) { 219176491Smarcel case 0: 220176491Smarcel DPRINTF(FPE_EX, ("fpu_emulate: success\n")); 221176491Smarcel frame->srr0 += 4; 222176491Smarcel break; 223176491Smarcel 224176491Smarcel case FPE: 225176491Smarcel DPRINTF(FPE_EX, ("fpu_emulate: SIGFPE\n")); 226176491Smarcel sig = SIGFPE; 227176491Smarcel break; 228176491Smarcel 229176491Smarcel case FAULT: 230176491Smarcel DPRINTF(FPE_EX, ("fpu_emulate: SIGSEGV\n")); 231176491Smarcel sig = SIGSEGV; 232176491Smarcel break; 233176491Smarcel 234176491Smarcel case NOTFPU: 235176491Smarcel default: 236176491Smarcel DPRINTF(FPE_EX, ("fpu_emulate: SIGILL\n")); 237176491Smarcel#ifdef DEBUG 238176491Smarcel if (fpe_debug & FPE_EX) { 239176491Smarcel printf("fpu_emulate: illegal insn %x at %p:", 240176491Smarcel insn.i_int, (void *) (frame->srr0)); 241176491Smarcel opc_disasm(frame->srr0, insn.i_int); 242176491Smarcel } 243176491Smarcel#endif 244176491Smarcel /* 245176491Smarcel * XXXX retry an illegal insn once due to cache issues. 246176491Smarcel */ 247176491Smarcel if (lastill == frame->srr0) { 248176491Smarcel sig = SIGILL; 249176491Smarcel#ifdef DEBUG 250176491Smarcel if (fpe_debug & FPE_EX) 251176491Smarcel kdb_enter(KDB_WHY_UNSET, "illegal instruction"); 252176491Smarcel#endif 253176491Smarcel } 254176491Smarcel lastill = frame->srr0; 255176491Smarcel break; 256176491Smarcel } 257176491Smarcel 258176491Smarcel return (sig); 259176491Smarcel} 260176491Smarcel 261176491Smarcel/* 262176491Smarcel * Execute an FPU instruction (one that runs entirely in the FPU; not 263176491Smarcel * FBfcc or STF, for instance). On return, fe->fe_fs->fs_fsr will be 264176491Smarcel * modified to reflect the setting the hardware would have left. 265176491Smarcel * 266176491Smarcel * Note that we do not catch all illegal opcodes, so you can, for instance, 267176491Smarcel * multiply two integers this way. 268176491Smarcel */ 269176491Smarcelint 270176491Smarcelfpu_execute(struct trapframe *tf, struct fpemu *fe, union instr *insn) 271176491Smarcel{ 272176491Smarcel struct fpn *fp; 273176491Smarcel union instr instr = *insn; 274176491Smarcel int *a; 275176491Smarcel vm_offset_t addr; 276176491Smarcel int ra, rb, rc, rt, type, mask, fsr, cx, bf, setcr; 277176491Smarcel unsigned int cond; 278176491Smarcel struct fpreg *fs; 279176491Smarcel 280176491Smarcel /* Setup work. */ 281176491Smarcel fp = NULL; 282176491Smarcel fs = fe->fe_fpstate; 283176491Smarcel fe->fe_fpscr = ((int *)&fs->fpscr)[1]; 284176491Smarcel 285176491Smarcel /* 286176491Smarcel * On PowerPC all floating point values are stored in registers 287176491Smarcel * as doubles, even when used for single precision operations. 288176491Smarcel */ 289176491Smarcel type = FTYPE_DBL; 290176491Smarcel cond = instr.i_any.i_rc; 291176491Smarcel setcr = 0; 292176491Smarcel bf = 0; /* XXX gcc */ 293176491Smarcel 294176491Smarcel#if defined(DDB) && defined(DEBUG) 295176491Smarcel if (fpe_debug & FPE_EX) { 296176491Smarcel vm_offset_t loc = tf->srr0; 297176491Smarcel 298176491Smarcel printf("Trying to emulate: %p ", (void *)loc); 299176491Smarcel opc_disasm(loc, instr.i_int); 300176491Smarcel } 301176491Smarcel#endif 302176491Smarcel 303176491Smarcel /* 304176491Smarcel * `Decode' and execute instruction. 305176491Smarcel */ 306176491Smarcel 307176491Smarcel if ((instr.i_any.i_opcd >= OPC_LFS && instr.i_any.i_opcd <= OPC_STFDU) || 308176491Smarcel instr.i_any.i_opcd == OPC_integer_31) { 309176491Smarcel /* 310176491Smarcel * Handle load/store insns: 311176491Smarcel * 312176491Smarcel * Convert to/from single if needed, calculate addr, 313176491Smarcel * and update index reg if needed. 314176491Smarcel */ 315176491Smarcel double buf; 316176491Smarcel size_t size = sizeof(float); 317176491Smarcel int store, update; 318176491Smarcel 319176491Smarcel cond = 0; /* ld/st never set condition codes */ 320176491Smarcel 321176491Smarcel 322176491Smarcel if (instr.i_any.i_opcd == OPC_integer_31) { 323176491Smarcel if (instr.i_x.i_xo == OPC31_STFIWX) { 324176491Smarcel FPU_EMU_EVCNT_INCR(stfiwx); 325176491Smarcel 326176491Smarcel /* Store as integer */ 327176491Smarcel ra = instr.i_x.i_ra; 328176491Smarcel rb = instr.i_x.i_rb; 329266005Sian DPRINTF(FPE_INSN, 330266005Sian ("reg %d has %jx reg %d has %jx\n", 331266005Sian ra, (uintmax_t)tf->fixreg[ra], rb, 332266005Sian (uintmax_t)tf->fixreg[rb])); 333176491Smarcel 334176491Smarcel addr = tf->fixreg[rb]; 335176491Smarcel if (ra != 0) 336176491Smarcel addr += tf->fixreg[ra]; 337176491Smarcel rt = instr.i_x.i_rt; 338176491Smarcel a = (int *)&fs->fpreg[rt]; 339176491Smarcel DPRINTF(FPE_INSN, 340176491Smarcel ("fpu_execute: Store INT %x at %p\n", 341176491Smarcel a[1], (void *)addr)); 342176491Smarcel if (copyout(&a[1], (void *)addr, sizeof(int))) 343176491Smarcel return (FAULT); 344176491Smarcel return (0); 345176491Smarcel } 346176491Smarcel 347176491Smarcel if ((instr.i_x.i_xo & OPC31_FPMASK) != OPC31_FPOP) 348176491Smarcel /* Not an indexed FP load/store op */ 349176491Smarcel return (NOTFPU); 350176491Smarcel 351176491Smarcel store = (instr.i_x.i_xo & 0x80); 352176491Smarcel if (instr.i_x.i_xo & 0x40) 353176491Smarcel size = sizeof(double); 354176491Smarcel else 355176491Smarcel type = FTYPE_SNG; 356176491Smarcel update = (instr.i_x.i_xo & 0x20); 357176491Smarcel 358176491Smarcel /* calculate EA of load/store */ 359176491Smarcel ra = instr.i_x.i_ra; 360176491Smarcel rb = instr.i_x.i_rb; 361266005Sian DPRINTF(FPE_INSN, ("reg %d has %jx reg %d has %jx\n", 362266005Sian ra, (uintmax_t)tf->fixreg[ra], rb, 363266005Sian (uintmax_t)tf->fixreg[rb])); 364176491Smarcel addr = tf->fixreg[rb]; 365176491Smarcel if (ra != 0) 366176491Smarcel addr += tf->fixreg[ra]; 367176491Smarcel rt = instr.i_x.i_rt; 368176491Smarcel } else { 369176491Smarcel store = instr.i_d.i_opcd & 0x4; 370176491Smarcel if (instr.i_d.i_opcd & 0x2) 371176491Smarcel size = sizeof(double); 372176491Smarcel else 373176491Smarcel type = FTYPE_SNG; 374176491Smarcel update = instr.i_d.i_opcd & 0x1; 375176491Smarcel 376176491Smarcel /* calculate EA of load/store */ 377176491Smarcel ra = instr.i_d.i_ra; 378176491Smarcel addr = instr.i_d.i_d; 379266005Sian DPRINTF(FPE_INSN, ("reg %d has %jx displ %jx\n", 380266005Sian ra, (uintmax_t)tf->fixreg[ra], 381266005Sian (uintmax_t)addr)); 382176491Smarcel if (ra != 0) 383176491Smarcel addr += tf->fixreg[ra]; 384176491Smarcel rt = instr.i_d.i_rt; 385176491Smarcel } 386176491Smarcel 387176491Smarcel if (update && ra == 0) 388176491Smarcel return (NOTFPU); 389176491Smarcel 390176491Smarcel if (store) { 391176491Smarcel /* Store */ 392176491Smarcel FPU_EMU_EVCNT_INCR(fpstore); 393176491Smarcel if (type != FTYPE_DBL) { 394176491Smarcel DPRINTF(FPE_INSN, 395176491Smarcel ("fpu_execute: Store SNG at %p\n", 396176491Smarcel (void *)addr)); 397176491Smarcel fpu_explode(fe, fp = &fe->fe_f1, FTYPE_DBL, rt); 398176491Smarcel fpu_implode(fe, fp, type, (void *)&buf); 399176491Smarcel if (copyout(&buf, (void *)addr, size)) 400176491Smarcel return (FAULT); 401176491Smarcel } else { 402176491Smarcel DPRINTF(FPE_INSN, 403176491Smarcel ("fpu_execute: Store DBL at %p\n", 404176491Smarcel (void *)addr)); 405176491Smarcel if (copyout(&fs->fpreg[rt], (void *)addr, size)) 406176491Smarcel return (FAULT); 407176491Smarcel } 408176491Smarcel } else { 409176491Smarcel /* Load */ 410176491Smarcel FPU_EMU_EVCNT_INCR(fpload); 411176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: Load from %p\n", 412176491Smarcel (void *)addr)); 413176491Smarcel if (copyin((const void *)addr, &fs->fpreg[rt], size)) 414176491Smarcel return (FAULT); 415176491Smarcel if (type != FTYPE_DBL) { 416176491Smarcel fpu_explode(fe, fp = &fe->fe_f1, type, rt); 417176491Smarcel fpu_implode(fe, fp, FTYPE_DBL, 418176491Smarcel (u_int *)&fs->fpreg[rt]); 419176491Smarcel } 420176491Smarcel } 421176491Smarcel if (update) 422176491Smarcel tf->fixreg[ra] = addr; 423176491Smarcel /* Complete. */ 424176491Smarcel return (0); 425176491Smarcel#ifdef notyet 426176491Smarcel } else if (instr.i_any.i_opcd == OPC_load_st_62) { 427266005Sian /* These are 64-bit extensions */ 428176491Smarcel return (NOTFPU); 429176491Smarcel#endif 430176491Smarcel } else if (instr.i_any.i_opcd == OPC_sp_fp_59 || 431176491Smarcel instr.i_any.i_opcd == OPC_dp_fp_63) { 432176491Smarcel 433176491Smarcel 434176491Smarcel if (instr.i_any.i_opcd == OPC_dp_fp_63 && 435176491Smarcel !(instr.i_a.i_xo & OPC63M_MASK)) { 436176491Smarcel /* Format X */ 437176491Smarcel rt = instr.i_x.i_rt; 438176491Smarcel ra = instr.i_x.i_ra; 439176491Smarcel rb = instr.i_x.i_rb; 440176491Smarcel 441176491Smarcel 442176491Smarcel /* One of the special opcodes.... */ 443176491Smarcel switch (instr.i_x.i_xo) { 444176491Smarcel case OPC63_FCMPU: 445176491Smarcel FPU_EMU_EVCNT_INCR(fcmpu); 446176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FCMPU\n")); 447176491Smarcel rt >>= 2; 448176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 449176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 450176491Smarcel fpu_compare(fe, 0); 451176491Smarcel /* Make sure we do the condition regs. */ 452176491Smarcel cond = 0; 453176491Smarcel /* N.B.: i_rs is already left shifted by two. */ 454176491Smarcel bf = instr.i_x.i_rs & 0xfc; 455176491Smarcel setcr = 1; 456176491Smarcel break; 457176491Smarcel 458176491Smarcel case OPC63_FRSP: 459176491Smarcel /* 460176491Smarcel * Convert to single: 461176491Smarcel * 462176491Smarcel * PowerPC uses this to round a double 463176491Smarcel * precision value to single precision, 464176491Smarcel * but values in registers are always 465176491Smarcel * stored in double precision format. 466176491Smarcel */ 467176491Smarcel FPU_EMU_EVCNT_INCR(frsp); 468176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FRSP\n")); 469176491Smarcel fpu_explode(fe, fp = &fe->fe_f1, FTYPE_DBL, rb); 470176491Smarcel fpu_implode(fe, fp, FTYPE_SNG, 471176491Smarcel (u_int *)&fs->fpreg[rt]); 472176491Smarcel fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG, rt); 473176491Smarcel type = FTYPE_DBL; 474176491Smarcel break; 475176491Smarcel case OPC63_FCTIW: 476176491Smarcel case OPC63_FCTIWZ: 477176491Smarcel FPU_EMU_EVCNT_INCR(fctiw); 478176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FCTIW\n")); 479176491Smarcel fpu_explode(fe, fp = &fe->fe_f1, type, rb); 480176491Smarcel type = FTYPE_INT; 481176491Smarcel break; 482176491Smarcel case OPC63_FCMPO: 483176491Smarcel FPU_EMU_EVCNT_INCR(fcmpo); 484176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FCMPO\n")); 485176491Smarcel rt >>= 2; 486176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 487176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 488176491Smarcel fpu_compare(fe, 1); 489176491Smarcel /* Make sure we do the condition regs. */ 490176491Smarcel cond = 0; 491176491Smarcel /* N.B.: i_rs is already left shifted by two. */ 492176491Smarcel bf = instr.i_x.i_rs & 0xfc; 493176491Smarcel setcr = 1; 494176491Smarcel break; 495176491Smarcel case OPC63_MTFSB1: 496176491Smarcel FPU_EMU_EVCNT_INCR(mtfsb1); 497176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: MTFSB1\n")); 498176491Smarcel fe->fe_fpscr |= 499176491Smarcel (~(FPSCR_VX|FPSR_EX) & (1<<(31-rt))); 500176491Smarcel break; 501176491Smarcel case OPC63_FNEG: 502176491Smarcel FPU_EMU_EVCNT_INCR(fnegabs); 503176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FNEGABS\n")); 504176491Smarcel memcpy(&fs->fpreg[rt], &fs->fpreg[rb], 505176491Smarcel sizeof(double)); 506176491Smarcel a = (int *)&fs->fpreg[rt]; 507261455Seadler *a ^= (1U << 31); 508176491Smarcel break; 509176491Smarcel case OPC63_MCRFS: 510176491Smarcel FPU_EMU_EVCNT_INCR(mcrfs); 511176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: MCRFS\n")); 512176491Smarcel cond = 0; 513176491Smarcel rt &= 0x1c; 514176491Smarcel ra &= 0x1c; 515176491Smarcel /* Extract the bits we want */ 516176491Smarcel mask = (fe->fe_fpscr >> (28 - ra)) & 0xf; 517176491Smarcel /* Clear the bits we copied. */ 518176491Smarcel fe->fe_cx = 519176491Smarcel (FPSR_EX_MSK | (0xf << (28 - ra))); 520176491Smarcel fe->fe_fpscr &= fe->fe_cx; 521176491Smarcel /* Now shove them in the right part of cr */ 522176491Smarcel tf->cr &= ~(0xf << (28 - rt)); 523176491Smarcel tf->cr |= (mask << (28 - rt)); 524176491Smarcel break; 525176491Smarcel case OPC63_MTFSB0: 526176491Smarcel FPU_EMU_EVCNT_INCR(mtfsb0); 527176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: MTFSB0\n")); 528176491Smarcel fe->fe_fpscr &= 529176491Smarcel ((FPSCR_VX|FPSR_EX) & ~(1<<(31-rt))); 530176491Smarcel break; 531176491Smarcel case OPC63_FMR: 532176491Smarcel FPU_EMU_EVCNT_INCR(fmr); 533176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FMR\n")); 534176491Smarcel memcpy(&fs->fpreg[rt], &fs->fpreg[rb], 535176491Smarcel sizeof(double)); 536176491Smarcel break; 537176491Smarcel case OPC63_MTFSFI: 538176491Smarcel FPU_EMU_EVCNT_INCR(mtfsfi); 539176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: MTFSFI\n")); 540176491Smarcel rb >>= 1; 541176491Smarcel rt &= 0x1c; /* Already left-shifted 4 */ 542176491Smarcel fe->fe_cx = rb << (28 - rt); 543176491Smarcel mask = 0xf<<(28 - rt); 544176491Smarcel fe->fe_fpscr = (fe->fe_fpscr & ~mask) | 545176491Smarcel fe->fe_cx; 546176491Smarcel/* XXX weird stuff about OX, FX, FEX, and VX should be handled */ 547176491Smarcel break; 548176491Smarcel case OPC63_FNABS: 549176491Smarcel FPU_EMU_EVCNT_INCR(fnabs); 550176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FABS\n")); 551176491Smarcel memcpy(&fs->fpreg[rt], &fs->fpreg[rb], 552176491Smarcel sizeof(double)); 553176491Smarcel a = (int *)&fs->fpreg[rt]; 554261455Seadler *a |= (1U << 31); 555176491Smarcel break; 556176491Smarcel case OPC63_FABS: 557176491Smarcel FPU_EMU_EVCNT_INCR(fabs); 558176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FABS\n")); 559176491Smarcel memcpy(&fs->fpreg[rt], &fs->fpreg[rb], 560176491Smarcel sizeof(double)); 561176491Smarcel a = (int *)&fs->fpreg[rt]; 562261455Seadler *a &= ~(1U << 31); 563176491Smarcel break; 564176491Smarcel case OPC63_MFFS: 565176491Smarcel FPU_EMU_EVCNT_INCR(mffs); 566176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: MFFS\n")); 567176491Smarcel memcpy(&fs->fpreg[rt], &fs->fpscr, 568176491Smarcel sizeof(fs->fpscr)); 569176491Smarcel break; 570176491Smarcel case OPC63_MTFSF: 571176491Smarcel FPU_EMU_EVCNT_INCR(mtfsf); 572176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: MTFSF\n")); 573176491Smarcel if ((rt = instr.i_xfl.i_flm) == -1) 574176491Smarcel mask = -1; 575176491Smarcel else { 576176491Smarcel mask = 0; 577176491Smarcel /* Convert 1 bit -> 4 bits */ 578176491Smarcel for (ra = 0; ra < 8; ra ++) 579176491Smarcel if (rt & (1<<ra)) 580176491Smarcel mask |= (0xf<<(4*ra)); 581176491Smarcel } 582176491Smarcel a = (int *)&fs->fpreg[rt]; 583176491Smarcel fe->fe_cx = mask & a[1]; 584176491Smarcel fe->fe_fpscr = (fe->fe_fpscr&~mask) | 585176491Smarcel (fe->fe_cx); 586176491Smarcel/* XXX weird stuff about OX, FX, FEX, and VX should be handled */ 587176491Smarcel break; 588176491Smarcel case OPC63_FCTID: 589176491Smarcel case OPC63_FCTIDZ: 590176491Smarcel FPU_EMU_EVCNT_INCR(fctid); 591176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FCTID\n")); 592176491Smarcel fpu_explode(fe, fp = &fe->fe_f1, type, rb); 593176491Smarcel type = FTYPE_LNG; 594176491Smarcel break; 595176491Smarcel case OPC63_FCFID: 596176491Smarcel FPU_EMU_EVCNT_INCR(fcfid); 597176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FCFID\n")); 598176491Smarcel type = FTYPE_LNG; 599176491Smarcel fpu_explode(fe, fp = &fe->fe_f1, type, rb); 600176491Smarcel type = FTYPE_DBL; 601176491Smarcel break; 602176491Smarcel default: 603176491Smarcel return (NOTFPU); 604176491Smarcel break; 605176491Smarcel } 606176491Smarcel } else { 607176491Smarcel /* Format A */ 608176491Smarcel rt = instr.i_a.i_frt; 609176491Smarcel ra = instr.i_a.i_fra; 610176491Smarcel rb = instr.i_a.i_frb; 611176491Smarcel rc = instr.i_a.i_frc; 612176491Smarcel 613266004Sian /* 614266004Sian * All arithmetic operations work on registers, which 615266004Sian * are stored as doubles. 616266004Sian */ 617266004Sian type = FTYPE_DBL; 618176491Smarcel switch ((unsigned int)instr.i_a.i_xo) { 619176491Smarcel case OPC59_FDIVS: 620176491Smarcel FPU_EMU_EVCNT_INCR(fdiv); 621176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FDIV\n")); 622176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 623176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 624176491Smarcel fp = fpu_div(fe); 625176491Smarcel break; 626176491Smarcel case OPC59_FSUBS: 627176491Smarcel FPU_EMU_EVCNT_INCR(fsub); 628176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FSUB\n")); 629176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 630176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 631176491Smarcel fp = fpu_sub(fe); 632176491Smarcel break; 633176491Smarcel case OPC59_FADDS: 634176491Smarcel FPU_EMU_EVCNT_INCR(fadd); 635176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FADD\n")); 636176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 637176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 638176491Smarcel fp = fpu_add(fe); 639176491Smarcel break; 640176491Smarcel case OPC59_FSQRTS: 641176491Smarcel FPU_EMU_EVCNT_INCR(fsqrt); 642176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FSQRT\n")); 643176491Smarcel fpu_explode(fe, &fe->fe_f1, type, rb); 644176491Smarcel fp = fpu_sqrt(fe); 645176491Smarcel break; 646176491Smarcel case OPC63M_FSEL: 647176491Smarcel FPU_EMU_EVCNT_INCR(fsel); 648176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FSEL\n")); 649176491Smarcel a = (int *)&fe->fe_fpstate->fpreg[ra]; 650176491Smarcel if ((*a & 0x80000000) && (*a & 0x7fffffff)) 651176491Smarcel /* fra < 0 */ 652176491Smarcel rc = rb; 653176491Smarcel DPRINTF(FPE_INSN, ("f%d => f%d\n", rc, rt)); 654176491Smarcel memcpy(&fs->fpreg[rt], &fs->fpreg[rc], 655176491Smarcel sizeof(double)); 656176491Smarcel break; 657176491Smarcel case OPC59_FRES: 658176491Smarcel FPU_EMU_EVCNT_INCR(fpres); 659176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FPRES\n")); 660176491Smarcel fpu_explode(fe, &fe->fe_f1, type, rb); 661176491Smarcel fp = fpu_sqrt(fe); 662176491Smarcel /* now we've gotta overwrite the dest reg */ 663176491Smarcel *((int *)&fe->fe_fpstate->fpreg[rt]) = 1; 664176491Smarcel fpu_explode(fe, &fe->fe_f1, FTYPE_INT, rt); 665176491Smarcel fpu_div(fe); 666176491Smarcel break; 667176491Smarcel case OPC59_FMULS: 668176491Smarcel FPU_EMU_EVCNT_INCR(fmul); 669176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FMUL\n")); 670176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 671176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rc); 672176491Smarcel fp = fpu_mul(fe); 673176491Smarcel break; 674176491Smarcel case OPC63M_FRSQRTE: 675176491Smarcel /* Reciprocal sqrt() estimate */ 676176491Smarcel FPU_EMU_EVCNT_INCR(frsqrte); 677176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FRSQRTE\n")); 678176491Smarcel fpu_explode(fe, &fe->fe_f1, type, rb); 679176491Smarcel fp = fpu_sqrt(fe); 680176491Smarcel fe->fe_f2 = *fp; 681176491Smarcel /* now we've gotta overwrite the dest reg */ 682176491Smarcel *((int *)&fe->fe_fpstate->fpreg[rt]) = 1; 683176491Smarcel fpu_explode(fe, &fe->fe_f1, FTYPE_INT, rt); 684176491Smarcel fpu_div(fe); 685176491Smarcel break; 686176491Smarcel case OPC59_FMSUBS: 687176491Smarcel FPU_EMU_EVCNT_INCR(fmulsub); 688176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FMULSUB\n")); 689176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 690176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rc); 691176491Smarcel fp = fpu_mul(fe); 692176491Smarcel fe->fe_f1 = *fp; 693176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 694176491Smarcel fp = fpu_sub(fe); 695176491Smarcel break; 696176491Smarcel case OPC59_FMADDS: 697176491Smarcel FPU_EMU_EVCNT_INCR(fmuladd); 698176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FMULADD\n")); 699176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 700176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rc); 701176491Smarcel fp = fpu_mul(fe); 702176491Smarcel fe->fe_f1 = *fp; 703176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 704176491Smarcel fp = fpu_add(fe); 705176491Smarcel break; 706176491Smarcel case OPC59_FNMSUBS: 707176491Smarcel FPU_EMU_EVCNT_INCR(fnmsub); 708176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FNMSUB\n")); 709176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 710176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rc); 711176491Smarcel fp = fpu_mul(fe); 712176491Smarcel fe->fe_f1 = *fp; 713176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 714176491Smarcel fp = fpu_sub(fe); 715176491Smarcel /* Negate */ 716176491Smarcel fp->fp_sign ^= 1; 717176491Smarcel break; 718176491Smarcel case OPC59_FNMADDS: 719176491Smarcel FPU_EMU_EVCNT_INCR(fnmadd); 720176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: FNMADD\n")); 721176491Smarcel fpu_explode(fe, &fe->fe_f1, type, ra); 722176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rc); 723176491Smarcel fp = fpu_mul(fe); 724176491Smarcel fe->fe_f1 = *fp; 725176491Smarcel fpu_explode(fe, &fe->fe_f2, type, rb); 726176491Smarcel fp = fpu_add(fe); 727176491Smarcel /* Negate */ 728176491Smarcel fp->fp_sign ^= 1; 729176491Smarcel break; 730176491Smarcel default: 731176491Smarcel return (NOTFPU); 732176491Smarcel break; 733176491Smarcel } 734266004Sian 735266004Sian /* If the instruction was single precision, round */ 736266004Sian if (!(instr.i_any.i_opcd & 0x4)) { 737266004Sian fpu_implode(fe, fp, FTYPE_SNG, 738266004Sian (u_int *)&fs->fpreg[rt]); 739266004Sian fpu_explode(fe, fp = &fe->fe_f1, FTYPE_SNG, rt); 740266004Sian } 741176491Smarcel } 742176491Smarcel } else { 743176491Smarcel return (NOTFPU); 744176491Smarcel } 745176491Smarcel 746176491Smarcel /* 747176491Smarcel * ALU operation is complete. Collapse the result and then check 748176491Smarcel * for exceptions. If we got any, and they are enabled, do not 749176491Smarcel * alter the destination register, just stop with an exception. 750176491Smarcel * Otherwise set new current exceptions and accrue. 751176491Smarcel */ 752176491Smarcel if (fp) 753176491Smarcel fpu_implode(fe, fp, type, (u_int *)&fs->fpreg[rt]); 754176491Smarcel cx = fe->fe_cx; 755176491Smarcel fsr = fe->fe_fpscr; 756176491Smarcel if (cx != 0) { 757176491Smarcel fsr &= ~FPSCR_FX; 758176491Smarcel if ((cx^fsr)&FPSR_EX_MSK) 759176491Smarcel fsr |= FPSCR_FX; 760176491Smarcel mask = fsr & FPSR_EX; 761176491Smarcel mask <<= (25-3); 762176491Smarcel if (cx & mask) 763176491Smarcel fsr |= FPSCR_FEX; 764176491Smarcel if (cx & FPSCR_FPRF) { 765176491Smarcel /* Need to replace CC */ 766176491Smarcel fsr &= ~FPSCR_FPRF; 767176491Smarcel } 768176491Smarcel if (cx & (FPSR_EXOP)) 769176491Smarcel fsr |= FPSCR_VX; 770176491Smarcel fsr |= cx; 771176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: cx %x, fsr %x\n", cx, fsr)); 772176491Smarcel } 773176491Smarcel 774176491Smarcel if (cond) { 775176491Smarcel cond = fsr & 0xf0000000; 776176491Smarcel /* Isolate condition codes */ 777176491Smarcel cond >>= 28; 778176491Smarcel /* Move fpu condition codes to cr[1] */ 779176491Smarcel tf->cr &= (0x0f000000); 780176491Smarcel tf->cr |= (cond<<24); 781176491Smarcel DPRINTF(FPE_INSN, ("fpu_execute: cr[1] <= %x\n", cond)); 782176491Smarcel } 783176491Smarcel 784176491Smarcel if (setcr) { 785176491Smarcel cond = fsr & FPSCR_FPCC; 786176491Smarcel /* Isolate condition codes */ 787176491Smarcel cond <<= 16; 788176491Smarcel /* Move fpu condition codes to cr[1] */ 789176491Smarcel tf->cr &= ~(0xf0000000>>bf); 790176491Smarcel tf->cr |= (cond>>bf); 791266005Sian DPRINTF(FPE_INSN, ("fpu_execute: cr[%d] (cr=%jx) <= %x\n", 792266005Sian bf/4, (uintmax_t)tf->cr, cond)); 793176491Smarcel } 794176491Smarcel 795176491Smarcel ((int *)&fs->fpscr)[1] = fsr; 796176491Smarcel if (fsr & FPSCR_FEX) 797176491Smarcel return(FPE); 798176491Smarcel return (0); /* success */ 799176491Smarcel} 800