OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gdb-7.1/] [sim/] [mips/] [cp1.c] - Diff between revs 834 and 842

Only display areas with differences | Details | Blame | View Log

Rev 834 Rev 842
/*> cp1.c <*/
/*> cp1.c <*/
/* MIPS Simulator FPU (CoProcessor 1) support.
/* MIPS Simulator FPU (CoProcessor 1) support.
   Copyright (C) 2002, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
   Copyright (C) 2002, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
   Originally created by Cygnus Solutions.  Extensive modifications,
   Originally created by Cygnus Solutions.  Extensive modifications,
   including paired-single operation support and MIPS-3D support
   including paired-single operation support and MIPS-3D support
   contributed by Ed Satterthwaite and Chris Demetriou, of Broadcom
   contributed by Ed Satterthwaite and Chris Demetriou, of Broadcom
   Corporation (SiByte).
   Corporation (SiByte).
 
 
This file is part of GDB, the GNU debugger.
This file is part of GDB, the GNU debugger.
 
 
This program is free software; you can redistribute it and/or modify
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
(at your option) any later version.
 
 
This program is distributed in the hope that it will be useful,
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
GNU General Public License for more details.
 
 
You should have received a copy of the GNU General Public License
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 
/* XXX: The following notice should be removed as soon as is practical:  */
/* XXX: The following notice should be removed as soon as is practical:  */
/* Floating Point Support for gdb MIPS simulators
/* Floating Point Support for gdb MIPS simulators
 
 
   This file is part of the MIPS sim
   This file is part of the MIPS sim
 
 
                THIS SOFTWARE IS NOT COPYRIGHTED
                THIS SOFTWARE IS NOT COPYRIGHTED
   (by Cygnus.)
   (by Cygnus.)
 
 
   Cygnus offers the following for use in the public domain.  Cygnus
   Cygnus offers the following for use in the public domain.  Cygnus
   makes no warranty with regard to the software or it's performance
   makes no warranty with regard to the software or it's performance
   and the user accepts the software "AS IS" with all faults.
   and the user accepts the software "AS IS" with all faults.
 
 
   CYGNUS DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO
   CYGNUS DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD TO
   THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
   MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
 
   (Originally, this code was in interp.c)
   (Originally, this code was in interp.c)
*/
*/
 
 
#include "sim-main.h"
#include "sim-main.h"
 
 
/* Within cp1.c we refer to sim_cpu directly.  */
/* Within cp1.c we refer to sim_cpu directly.  */
#define CPU cpu
#define CPU cpu
#define SD CPU_STATE(cpu)
#define SD CPU_STATE(cpu)
 
 
/*-- FPU support routines ---------------------------------------------------*/
/*-- FPU support routines ---------------------------------------------------*/
 
 
/* Numbers are held in normalized form. The SINGLE and DOUBLE binary
/* Numbers are held in normalized form. The SINGLE and DOUBLE binary
   formats conform to ANSI/IEEE Std 754-1985.
   formats conform to ANSI/IEEE Std 754-1985.
 
 
   SINGLE precision floating:
   SINGLE precision floating:
      seeeeeeeefffffffffffffffffffffff
      seeeeeeeefffffffffffffffffffffff
        s =  1bit  = sign
        s =  1bit  = sign
        e =  8bits = exponent
        e =  8bits = exponent
        f = 23bits = fraction
        f = 23bits = fraction
 
 
   SINGLE precision fixed:
   SINGLE precision fixed:
      siiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
      siiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
        s =  1bit  = sign
        s =  1bit  = sign
        i = 31bits = integer
        i = 31bits = integer
 
 
   DOUBLE precision floating:
   DOUBLE precision floating:
      seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff
      seeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffff
        s =  1bit  = sign
        s =  1bit  = sign
        e = 11bits = exponent
        e = 11bits = exponent
        f = 52bits = fraction
        f = 52bits = fraction
 
 
   DOUBLE precision fixed:
   DOUBLE precision fixed:
      siiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
      siiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
        s =  1bit  = sign
        s =  1bit  = sign
        i = 63bits = integer
        i = 63bits = integer
 
 
   PAIRED SINGLE precision floating:
   PAIRED SINGLE precision floating:
      seeeeeeeefffffffffffffffffffffffseeeeeeeefffffffffffffffffffffff
      seeeeeeeefffffffffffffffffffffffseeeeeeeefffffffffffffffffffffff
      |         upper                ||         lower                |
      |         upper                ||         lower                |
        s =  1bit  = sign
        s =  1bit  = sign
        e =  8bits = exponent
        e =  8bits = exponent
        f = 23bits = fraction
        f = 23bits = fraction
    Note: upper = [63..32], lower = [31..0]
    Note: upper = [63..32], lower = [31..0]
 */
 */
 
 
/* Extract packed single values:  */
/* Extract packed single values:  */
#define FP_PS_upper(v) (((v) >> 32) & (unsigned)0xFFFFFFFF)
#define FP_PS_upper(v) (((v) >> 32) & (unsigned)0xFFFFFFFF)
#define FP_PS_lower(v) ((v) & (unsigned)0xFFFFFFFF)
#define FP_PS_lower(v) ((v) & (unsigned)0xFFFFFFFF)
#define FP_PS_cat(u,l) (((unsigned64)((u) & (unsigned)0xFFFFFFFF) << 32) \
#define FP_PS_cat(u,l) (((unsigned64)((u) & (unsigned)0xFFFFFFFF) << 32) \
                        | (unsigned64)((l) & 0xFFFFFFFF))
                        | (unsigned64)((l) & 0xFFFFFFFF))
 
 
/* Explicit QNaN values.  */
/* Explicit QNaN values.  */
#define FPQNaN_SINGLE   (0x7FBFFFFF)
#define FPQNaN_SINGLE   (0x7FBFFFFF)
#define FPQNaN_WORD     (0x7FFFFFFF)
#define FPQNaN_WORD     (0x7FFFFFFF)
#define FPQNaN_DOUBLE   (UNSIGNED64 (0x7FF7FFFFFFFFFFFF))
#define FPQNaN_DOUBLE   (UNSIGNED64 (0x7FF7FFFFFFFFFFFF))
#define FPQNaN_LONG     (UNSIGNED64 (0x7FFFFFFFFFFFFFFF))
#define FPQNaN_LONG     (UNSIGNED64 (0x7FFFFFFFFFFFFFFF))
#define FPQNaN_PS       (FP_PS_cat (FPQNaN_SINGLE, FPQNaN_SINGLE))
#define FPQNaN_PS       (FP_PS_cat (FPQNaN_SINGLE, FPQNaN_SINGLE))
 
 
static const char *fpu_format_name (FP_formats fmt);
static const char *fpu_format_name (FP_formats fmt);
#ifdef DEBUG
#ifdef DEBUG
static const char *fpu_rounding_mode_name (int rm);
static const char *fpu_rounding_mode_name (int rm);
#endif
#endif
 
 
uword64
uword64
value_fpr (sim_cpu *cpu,
value_fpr (sim_cpu *cpu,
           address_word cia,
           address_word cia,
           int fpr,
           int fpr,
           FP_formats fmt)
           FP_formats fmt)
{
{
  uword64 value = 0;
  uword64 value = 0;
  int err = 0;
  int err = 0;
 
 
  /* Treat unused register values, as fixed-point 64bit values.  */
  /* Treat unused register values, as fixed-point 64bit values.  */
  if (fmt == fmt_unknown)
  if (fmt == fmt_unknown)
    {
    {
#if 1
#if 1
      /* If request to read data as "unknown", then use the current
      /* If request to read data as "unknown", then use the current
         encoding:  */
         encoding:  */
      fmt = FPR_STATE[fpr];
      fmt = FPR_STATE[fpr];
#else
#else
      fmt = fmt_long;
      fmt = fmt_long;
#endif
#endif
    }
    }
 
 
  /* For values not yet accessed, set to the desired format.  */
  /* For values not yet accessed, set to the desired format.  */
  if (fmt < fmt_uninterpreted)
  if (fmt < fmt_uninterpreted)
    {
    {
      if (FPR_STATE[fpr] == fmt_uninterpreted)
      if (FPR_STATE[fpr] == fmt_uninterpreted)
        {
        {
          FPR_STATE[fpr] = fmt;
          FPR_STATE[fpr] = fmt;
#ifdef DEBUG
#ifdef DEBUG
          printf ("DBG: Register %d was fmt_uninterpreted. Now %s\n", fpr,
          printf ("DBG: Register %d was fmt_uninterpreted. Now %s\n", fpr,
                  fpu_format_name (fmt));
                  fpu_format_name (fmt));
#endif /* DEBUG */
#endif /* DEBUG */
        }
        }
      else if (fmt != FPR_STATE[fpr])
      else if (fmt != FPR_STATE[fpr])
        {
        {
          sim_io_eprintf (SD, "FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%s)\n",
          sim_io_eprintf (SD, "FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%s)\n",
                          fpr, fpu_format_name (FPR_STATE[fpr]),
                          fpr, fpu_format_name (FPR_STATE[fpr]),
                          fpu_format_name (fmt), pr_addr (cia));
                          fpu_format_name (fmt), pr_addr (cia));
          FPR_STATE[fpr] = fmt_unknown;
          FPR_STATE[fpr] = fmt_unknown;
        }
        }
    }
    }
 
 
  if (FPR_STATE[fpr] == fmt_unknown)
  if (FPR_STATE[fpr] == fmt_unknown)
    {
    {
      /* Set QNaN value:  */
      /* Set QNaN value:  */
      switch (fmt)
      switch (fmt)
        {
        {
        case fmt_single:  value = FPQNaN_SINGLE;  break;
        case fmt_single:  value = FPQNaN_SINGLE;  break;
        case fmt_double:  value = FPQNaN_DOUBLE;  break;
        case fmt_double:  value = FPQNaN_DOUBLE;  break;
        case fmt_word:    value = FPQNaN_WORD;    break;
        case fmt_word:    value = FPQNaN_WORD;    break;
        case fmt_long:    value = FPQNaN_LONG;    break;
        case fmt_long:    value = FPQNaN_LONG;    break;
        case fmt_ps:      value = FPQNaN_PS;      break;
        case fmt_ps:      value = FPQNaN_PS;      break;
        default:          err = -1;               break;
        default:          err = -1;               break;
        }
        }
    }
    }
  else if (SizeFGR () == 64)
  else if (SizeFGR () == 64)
    {
    {
      switch (fmt)
      switch (fmt)
        {
        {
        case fmt_uninterpreted_32:
        case fmt_uninterpreted_32:
        case fmt_single:
        case fmt_single:
        case fmt_word:
        case fmt_word:
          value = (FGR[fpr] & 0xFFFFFFFF);
          value = (FGR[fpr] & 0xFFFFFFFF);
          break;
          break;
 
 
        case fmt_uninterpreted_64:
        case fmt_uninterpreted_64:
        case fmt_uninterpreted:
        case fmt_uninterpreted:
        case fmt_double:
        case fmt_double:
        case fmt_long:
        case fmt_long:
        case fmt_ps:
        case fmt_ps:
          value = FGR[fpr];
          value = FGR[fpr];
          break;
          break;
 
 
        default:
        default:
          err = -1;
          err = -1;
          break;
          break;
        }
        }
    }
    }
  else
  else
    {
    {
      switch (fmt)
      switch (fmt)
        {
        {
        case fmt_uninterpreted_32:
        case fmt_uninterpreted_32:
        case fmt_single:
        case fmt_single:
        case fmt_word:
        case fmt_word:
          value = (FGR[fpr] & 0xFFFFFFFF);
          value = (FGR[fpr] & 0xFFFFFFFF);
          break;
          break;
 
 
        case fmt_uninterpreted_64:
        case fmt_uninterpreted_64:
        case fmt_uninterpreted:
        case fmt_uninterpreted:
        case fmt_double:
        case fmt_double:
        case fmt_long:
        case fmt_long:
          if ((fpr & 1) == 0)
          if ((fpr & 1) == 0)
            {
            {
              /* Even register numbers only.  */
              /* Even register numbers only.  */
#ifdef DEBUG
#ifdef DEBUG
              printf ("DBG: ValueFPR: FGR[%d] = %s, FGR[%d] = %s\n",
              printf ("DBG: ValueFPR: FGR[%d] = %s, FGR[%d] = %s\n",
                      fpr + 1, pr_uword64 ((uword64) FGR[fpr+1]),
                      fpr + 1, pr_uword64 ((uword64) FGR[fpr+1]),
                      fpr, pr_uword64 ((uword64) FGR[fpr]));
                      fpr, pr_uword64 ((uword64) FGR[fpr]));
#endif
#endif
              value = ((((uword64) FGR[fpr+1]) << 32)
              value = ((((uword64) FGR[fpr+1]) << 32)
                       | (FGR[fpr] & 0xFFFFFFFF));
                       | (FGR[fpr] & 0xFFFFFFFF));
            }
            }
          else
          else
            {
            {
              SignalException (ReservedInstruction, 0);
              SignalException (ReservedInstruction, 0);
            }
            }
          break;
          break;
 
 
        case fmt_ps:
        case fmt_ps:
          SignalException (ReservedInstruction, 0);
          SignalException (ReservedInstruction, 0);
          break;
          break;
 
 
        default:
        default:
          err = -1;
          err = -1;
          break;
          break;
        }
        }
    }
    }
 
 
  if (err)
  if (err)
    SignalExceptionSimulatorFault ("Unrecognised FP format in ValueFPR ()");
    SignalExceptionSimulatorFault ("Unrecognised FP format in ValueFPR ()");
 
 
#ifdef DEBUG
#ifdef DEBUG
  printf ("DBG: ValueFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d\n",
  printf ("DBG: ValueFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d\n",
          fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia),
          fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia),
          SizeFGR ());
          SizeFGR ());
#endif /* DEBUG */
#endif /* DEBUG */
 
 
  return (value);
  return (value);
}
}
 
 
void
void
store_fpr (sim_cpu *cpu,
store_fpr (sim_cpu *cpu,
           address_word cia,
           address_word cia,
           int fpr,
           int fpr,
           FP_formats fmt,
           FP_formats fmt,
           uword64 value)
           uword64 value)
{
{
  int err = 0;
  int err = 0;
 
 
#ifdef DEBUG
#ifdef DEBUG
  printf ("DBG: StoreFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d, \n",
  printf ("DBG: StoreFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d, \n",
          fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia),
          fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia),
          SizeFGR ());
          SizeFGR ());
#endif /* DEBUG */
#endif /* DEBUG */
 
 
  if (SizeFGR () == 64)
  if (SizeFGR () == 64)
    {
    {
      switch (fmt)
      switch (fmt)
        {
        {
        case fmt_uninterpreted_32:
        case fmt_uninterpreted_32:
          fmt = fmt_uninterpreted;
          fmt = fmt_uninterpreted;
        case fmt_single:
        case fmt_single:
        case fmt_word:
        case fmt_word:
          if (STATE_VERBOSE_P (SD))
          if (STATE_VERBOSE_P (SD))
            sim_io_eprintf (SD,
            sim_io_eprintf (SD,
                            "Warning: PC 0x%s: interp.c store_fpr DEADCODE\n",
                            "Warning: PC 0x%s: interp.c store_fpr DEADCODE\n",
                            pr_addr (cia));
                            pr_addr (cia));
          FGR[fpr] = (((uword64) 0xDEADC0DE << 32) | (value & 0xFFFFFFFF));
          FGR[fpr] = (((uword64) 0xDEADC0DE << 32) | (value & 0xFFFFFFFF));
          FPR_STATE[fpr] = fmt;
          FPR_STATE[fpr] = fmt;
          break;
          break;
 
 
        case fmt_uninterpreted_64:
        case fmt_uninterpreted_64:
          fmt = fmt_uninterpreted;
          fmt = fmt_uninterpreted;
        case fmt_uninterpreted:
        case fmt_uninterpreted:
        case fmt_double:
        case fmt_double:
        case fmt_long:
        case fmt_long:
        case fmt_ps:
        case fmt_ps:
          FGR[fpr] = value;
          FGR[fpr] = value;
          FPR_STATE[fpr] = fmt;
          FPR_STATE[fpr] = fmt;
          break;
          break;
 
 
        default:
        default:
          FPR_STATE[fpr] = fmt_unknown;
          FPR_STATE[fpr] = fmt_unknown;
          err = -1;
          err = -1;
          break;
          break;
        }
        }
    }
    }
  else
  else
    {
    {
      switch (fmt)
      switch (fmt)
        {
        {
        case fmt_uninterpreted_32:
        case fmt_uninterpreted_32:
          fmt = fmt_uninterpreted;
          fmt = fmt_uninterpreted;
        case fmt_single:
        case fmt_single:
        case fmt_word:
        case fmt_word:
          FGR[fpr] = (value & 0xFFFFFFFF);
          FGR[fpr] = (value & 0xFFFFFFFF);
          FPR_STATE[fpr] = fmt;
          FPR_STATE[fpr] = fmt;
          break;
          break;
 
 
        case fmt_uninterpreted_64:
        case fmt_uninterpreted_64:
          fmt = fmt_uninterpreted;
          fmt = fmt_uninterpreted;
        case fmt_uninterpreted:
        case fmt_uninterpreted:
        case fmt_double:
        case fmt_double:
        case fmt_long:
        case fmt_long:
          if ((fpr & 1) == 0)
          if ((fpr & 1) == 0)
            {
            {
              /* Even register numbers only.  */
              /* Even register numbers only.  */
              FGR[fpr+1] = (value >> 32);
              FGR[fpr+1] = (value >> 32);
              FGR[fpr] = (value & 0xFFFFFFFF);
              FGR[fpr] = (value & 0xFFFFFFFF);
              FPR_STATE[fpr + 1] = fmt;
              FPR_STATE[fpr + 1] = fmt;
              FPR_STATE[fpr] = fmt;
              FPR_STATE[fpr] = fmt;
            }
            }
          else
          else
            {
            {
              FPR_STATE[fpr] = fmt_unknown;
              FPR_STATE[fpr] = fmt_unknown;
              FPR_STATE[fpr ^ 1] = fmt_unknown;
              FPR_STATE[fpr ^ 1] = fmt_unknown;
              SignalException (ReservedInstruction, 0);
              SignalException (ReservedInstruction, 0);
            }
            }
          break;
          break;
 
 
        case fmt_ps:
        case fmt_ps:
          FPR_STATE[fpr] = fmt_unknown;
          FPR_STATE[fpr] = fmt_unknown;
          SignalException (ReservedInstruction, 0);
          SignalException (ReservedInstruction, 0);
          break;
          break;
 
 
        default:
        default:
          FPR_STATE[fpr] = fmt_unknown;
          FPR_STATE[fpr] = fmt_unknown;
          err = -1;
          err = -1;
          break;
          break;
        }
        }
    }
    }
 
 
  if (err)
  if (err)
    SignalExceptionSimulatorFault ("Unrecognised FP format in StoreFPR ()");
    SignalExceptionSimulatorFault ("Unrecognised FP format in StoreFPR ()");
 
 
#ifdef DEBUG
#ifdef DEBUG
  printf ("DBG: StoreFPR: fpr[%d] = 0x%s (format %s)\n",
  printf ("DBG: StoreFPR: fpr[%d] = 0x%s (format %s)\n",
          fpr, pr_uword64 (FGR[fpr]), fpu_format_name (fmt));
          fpr, pr_uword64 (FGR[fpr]), fpu_format_name (fmt));
#endif /* DEBUG */
#endif /* DEBUG */
 
 
  return;
  return;
}
}
 
 
 
 
/* CP1 control/status register access functions.  */
/* CP1 control/status register access functions.  */
 
 
void
void
test_fcsr (sim_cpu *cpu,
test_fcsr (sim_cpu *cpu,
           address_word cia)
           address_word cia)
{
{
  unsigned int cause;
  unsigned int cause;
 
 
  cause = (FCSR & fcsr_CAUSE_mask) >> fcsr_CAUSE_shift;
  cause = (FCSR & fcsr_CAUSE_mask) >> fcsr_CAUSE_shift;
  if ((cause & ((FCSR & fcsr_ENABLES_mask) >> fcsr_ENABLES_shift)) != 0
  if ((cause & ((FCSR & fcsr_ENABLES_mask) >> fcsr_ENABLES_shift)) != 0
      || (cause & (1 << UO)))
      || (cause & (1 << UO)))
    {
    {
      SignalExceptionFPE();
      SignalExceptionFPE();
    }
    }
}
}
 
 
unsigned_word
unsigned_word
value_fcr(sim_cpu *cpu,
value_fcr(sim_cpu *cpu,
          address_word cia,
          address_word cia,
          int fcr)
          int fcr)
{
{
  unsigned32 value = 0;
  unsigned32 value = 0;
 
 
  switch (fcr)
  switch (fcr)
    {
    {
    case 0:  /* FP Implementation and Revision Register.  */
    case 0:  /* FP Implementation and Revision Register.  */
      value = FCR0;
      value = FCR0;
      break;
      break;
    case 25:  /* FP Condition Codes Register (derived from FCSR).  */
    case 25:  /* FP Condition Codes Register (derived from FCSR).  */
      value = (FCR31 & fcsr_FCC_mask) >> fcsr_FCC_shift;
      value = (FCR31 & fcsr_FCC_mask) >> fcsr_FCC_shift;
      value = (value & 0x1) | (value >> 1);   /* Close FCC gap.  */
      value = (value & 0x1) | (value >> 1);   /* Close FCC gap.  */
      break;
      break;
    case 26:  /* FP Exceptions Register (derived from FCSR).  */
    case 26:  /* FP Exceptions Register (derived from FCSR).  */
      value = FCR31 & (fcsr_CAUSE_mask | fcsr_FLAGS_mask);
      value = FCR31 & (fcsr_CAUSE_mask | fcsr_FLAGS_mask);
      break;
      break;
    case 28:  /* FP Enables Register (derived from FCSR).  */
    case 28:  /* FP Enables Register (derived from FCSR).  */
      value = FCR31 & (fcsr_ENABLES_mask | fcsr_RM_mask);
      value = FCR31 & (fcsr_ENABLES_mask | fcsr_RM_mask);
      if ((FCR31 & fcsr_FS) != 0)
      if ((FCR31 & fcsr_FS) != 0)
        value |= fenr_FS;
        value |= fenr_FS;
      break;
      break;
    case 31:  /* FP Control/Status Register (FCSR).  */
    case 31:  /* FP Control/Status Register (FCSR).  */
      value = FCR31 & ~fcsr_ZERO_mask;
      value = FCR31 & ~fcsr_ZERO_mask;
      break;
      break;
    }
    }
 
 
  return (EXTEND32 (value));
  return (EXTEND32 (value));
}
}
 
 
void
void
store_fcr(sim_cpu *cpu,
store_fcr(sim_cpu *cpu,
          address_word cia,
          address_word cia,
          int fcr,
          int fcr,
          unsigned_word value)
          unsigned_word value)
{
{
  unsigned32 v;
  unsigned32 v;
 
 
  v = VL4_8(value);
  v = VL4_8(value);
  switch (fcr)
  switch (fcr)
    {
    {
    case 25:  /* FP Condition Codes Register (stored into FCSR).  */
    case 25:  /* FP Condition Codes Register (stored into FCSR).  */
      v = (v << 1) | (v & 0x1);             /* Adjust for FCC gap.  */
      v = (v << 1) | (v & 0x1);             /* Adjust for FCC gap.  */
      FCR31 &= ~fcsr_FCC_mask;
      FCR31 &= ~fcsr_FCC_mask;
      FCR31 |= ((v << fcsr_FCC_shift) & fcsr_FCC_mask);
      FCR31 |= ((v << fcsr_FCC_shift) & fcsr_FCC_mask);
      break;
      break;
    case 26:  /* FP Exceptions Register (stored into FCSR).  */
    case 26:  /* FP Exceptions Register (stored into FCSR).  */
      FCR31 &= ~(fcsr_CAUSE_mask | fcsr_FLAGS_mask);
      FCR31 &= ~(fcsr_CAUSE_mask | fcsr_FLAGS_mask);
      FCR31 |= (v & (fcsr_CAUSE_mask | fcsr_FLAGS_mask));
      FCR31 |= (v & (fcsr_CAUSE_mask | fcsr_FLAGS_mask));
      test_fcsr(cpu, cia);
      test_fcsr(cpu, cia);
      break;
      break;
    case 28:  /* FP Enables Register (stored into FCSR).  */
    case 28:  /* FP Enables Register (stored into FCSR).  */
      if ((v & fenr_FS) != 0)
      if ((v & fenr_FS) != 0)
        v |= fcsr_FS;
        v |= fcsr_FS;
      else
      else
        v &= ~fcsr_FS;
        v &= ~fcsr_FS;
      FCR31 &= (fcsr_FCC_mask | fcsr_CAUSE_mask | fcsr_FLAGS_mask);
      FCR31 &= (fcsr_FCC_mask | fcsr_CAUSE_mask | fcsr_FLAGS_mask);
      FCR31 |= (v & (fcsr_FS | fcsr_ENABLES_mask | fcsr_RM_mask));
      FCR31 |= (v & (fcsr_FS | fcsr_ENABLES_mask | fcsr_RM_mask));
      test_fcsr(cpu, cia);
      test_fcsr(cpu, cia);
      break;
      break;
    case 31:  /* FP Control/Status Register (FCSR).  */
    case 31:  /* FP Control/Status Register (FCSR).  */
      FCR31 = v & ~fcsr_ZERO_mask;
      FCR31 = v & ~fcsr_ZERO_mask;
      test_fcsr(cpu, cia);
      test_fcsr(cpu, cia);
      break;
      break;
    }
    }
}
}
 
 
void
void
update_fcsr (sim_cpu *cpu,
update_fcsr (sim_cpu *cpu,
             address_word cia,
             address_word cia,
             sim_fpu_status status)
             sim_fpu_status status)
{
{
  FCSR &= ~fcsr_CAUSE_mask;
  FCSR &= ~fcsr_CAUSE_mask;
 
 
  if (status != 0)
  if (status != 0)
    {
    {
      unsigned int cause = 0;
      unsigned int cause = 0;
 
 
      /* map between sim_fpu codes and MIPS FCSR */
      /* map between sim_fpu codes and MIPS FCSR */
      if (status & (sim_fpu_status_invalid_snan
      if (status & (sim_fpu_status_invalid_snan
                    | sim_fpu_status_invalid_isi
                    | sim_fpu_status_invalid_isi
                    | sim_fpu_status_invalid_idi
                    | sim_fpu_status_invalid_idi
                    | sim_fpu_status_invalid_zdz
                    | sim_fpu_status_invalid_zdz
                    | sim_fpu_status_invalid_imz
                    | sim_fpu_status_invalid_imz
                    | sim_fpu_status_invalid_cmp
                    | sim_fpu_status_invalid_cmp
                    | sim_fpu_status_invalid_sqrt
                    | sim_fpu_status_invalid_sqrt
                    | sim_fpu_status_invalid_cvi))
                    | sim_fpu_status_invalid_cvi))
        cause |= (1 << IO);
        cause |= (1 << IO);
      if (status & sim_fpu_status_invalid_div0)
      if (status & sim_fpu_status_invalid_div0)
        cause |= (1 << DZ);
        cause |= (1 << DZ);
      if (status & sim_fpu_status_overflow)
      if (status & sim_fpu_status_overflow)
        cause |= (1 << OF);
        cause |= (1 << OF);
      if (status & sim_fpu_status_underflow)
      if (status & sim_fpu_status_underflow)
        cause |= (1 << UF);
        cause |= (1 << UF);
      if (status & sim_fpu_status_inexact)
      if (status & sim_fpu_status_inexact)
        cause |= (1 << IR);
        cause |= (1 << IR);
#if 0 /* Not yet.  */
#if 0 /* Not yet.  */
      /* Implicit clearing of other bits by unimplemented done by callers.  */
      /* Implicit clearing of other bits by unimplemented done by callers.  */
      if (status & sim_fpu_status_unimplemented)
      if (status & sim_fpu_status_unimplemented)
        cause |= (1 << UO);
        cause |= (1 << UO);
#endif
#endif
 
 
      FCSR |= (cause << fcsr_CAUSE_shift);
      FCSR |= (cause << fcsr_CAUSE_shift);
      test_fcsr (cpu, cia);
      test_fcsr (cpu, cia);
      FCSR |= ((cause & ~(1 << UO)) << fcsr_FLAGS_shift);
      FCSR |= ((cause & ~(1 << UO)) << fcsr_FLAGS_shift);
    }
    }
  return;
  return;
}
}
 
 
static sim_fpu_round
static sim_fpu_round
rounding_mode(int rm)
rounding_mode(int rm)
{
{
  sim_fpu_round round;
  sim_fpu_round round;
 
 
  switch (rm)
  switch (rm)
    {
    {
    case FP_RM_NEAREST:
    case FP_RM_NEAREST:
      /* Round result to nearest representable value. When two
      /* Round result to nearest representable value. When two
         representable values are equally near, round to the value
         representable values are equally near, round to the value
         that has a least significant bit of zero (i.e. is even).  */
         that has a least significant bit of zero (i.e. is even).  */
      round = sim_fpu_round_near;
      round = sim_fpu_round_near;
      break;
      break;
    case FP_RM_TOZERO:
    case FP_RM_TOZERO:
      /* Round result to the value closest to, and not greater in
      /* Round result to the value closest to, and not greater in
         magnitude than, the result.  */
         magnitude than, the result.  */
      round = sim_fpu_round_zero;
      round = sim_fpu_round_zero;
      break;
      break;
    case FP_RM_TOPINF:
    case FP_RM_TOPINF:
      /* Round result to the value closest to, and not less than,
      /* Round result to the value closest to, and not less than,
         the result.  */
         the result.  */
      round = sim_fpu_round_up;
      round = sim_fpu_round_up;
      break;
      break;
    case FP_RM_TOMINF:
    case FP_RM_TOMINF:
      /* Round result to the value closest to, and not greater than,
      /* Round result to the value closest to, and not greater than,
         the result.  */
         the result.  */
      round = sim_fpu_round_down;
      round = sim_fpu_round_down;
      break;
      break;
    default:
    default:
      round = 0;
      round = 0;
      fprintf (stderr, "Bad switch\n");
      fprintf (stderr, "Bad switch\n");
      abort ();
      abort ();
    }
    }
  return round;
  return round;
}
}
 
 
/* When the FS bit is set, MIPS processors return zero for
/* When the FS bit is set, MIPS processors return zero for
   denormalized results and optionally replace denormalized inputs
   denormalized results and optionally replace denormalized inputs
   with zero.  When FS is clear, some implementation trap on input
   with zero.  When FS is clear, some implementation trap on input
   and/or output, while other perform the operation in hardware.  */
   and/or output, while other perform the operation in hardware.  */
static sim_fpu_denorm
static sim_fpu_denorm
denorm_mode(sim_cpu *cpu)
denorm_mode(sim_cpu *cpu)
{
{
  sim_fpu_denorm denorm;
  sim_fpu_denorm denorm;
 
 
  /* XXX: FIXME: Eventually should be CPU model dependent.  */
  /* XXX: FIXME: Eventually should be CPU model dependent.  */
  if (GETFS())
  if (GETFS())
    denorm = sim_fpu_denorm_zero;
    denorm = sim_fpu_denorm_zero;
  else
  else
    denorm = 0;
    denorm = 0;
  return denorm;
  return denorm;
}
}
 
 
 
 
/* Comparison operations.  */
/* Comparison operations.  */
 
 
static sim_fpu_status
static sim_fpu_status
fp_test(unsigned64 op1,
fp_test(unsigned64 op1,
        unsigned64 op2,
        unsigned64 op2,
        FP_formats fmt,
        FP_formats fmt,
        int abs,
        int abs,
        int cond,
        int cond,
        int *condition)
        int *condition)
{
{
  sim_fpu wop1;
  sim_fpu wop1;
  sim_fpu wop2;
  sim_fpu wop2;
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
  int  less, equal, unordered;
  int  less, equal, unordered;
 
 
  /* The format type has already been checked:  */
  /* The format type has already been checked:  */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
      {
      {
        sim_fpu_32to (&wop1, op1);
        sim_fpu_32to (&wop1, op1);
        sim_fpu_32to (&wop2, op2);
        sim_fpu_32to (&wop2, op2);
        break;
        break;
      }
      }
    case fmt_double:
    case fmt_double:
      {
      {
        sim_fpu_64to (&wop1, op1);
        sim_fpu_64to (&wop1, op1);
        sim_fpu_64to (&wop2, op2);
        sim_fpu_64to (&wop2, op2);
        break;
        break;
      }
      }
    default:
    default:
      fprintf (stderr, "Bad switch\n");
      fprintf (stderr, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  if (sim_fpu_is_nan (&wop1) || sim_fpu_is_nan (&wop2))
  if (sim_fpu_is_nan (&wop1) || sim_fpu_is_nan (&wop2))
    {
    {
      if ((cond & (1 << 3)) ||
      if ((cond & (1 << 3)) ||
          sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2))
          sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2))
        status = sim_fpu_status_invalid_snan;
        status = sim_fpu_status_invalid_snan;
      less = 0;
      less = 0;
      equal = 0;
      equal = 0;
      unordered = 1;
      unordered = 1;
    }
    }
  else
  else
    {
    {
      if (abs)
      if (abs)
        {
        {
          status |= sim_fpu_abs (&wop1, &wop1);
          status |= sim_fpu_abs (&wop1, &wop1);
          status |= sim_fpu_abs (&wop2, &wop2);
          status |= sim_fpu_abs (&wop2, &wop2);
        }
        }
      equal = sim_fpu_is_eq (&wop1, &wop2);
      equal = sim_fpu_is_eq (&wop1, &wop2);
      less = !equal && sim_fpu_is_lt (&wop1, &wop2);
      less = !equal && sim_fpu_is_lt (&wop1, &wop2);
      unordered = 0;
      unordered = 0;
    }
    }
  *condition = (((cond & (1 << 2)) && less)
  *condition = (((cond & (1 << 2)) && less)
                || ((cond & (1 << 1)) && equal)
                || ((cond & (1 << 1)) && equal)
                || ((cond & (1 << 0)) && unordered));
                || ((cond & (1 << 0)) && unordered));
  return status;
  return status;
}
}
 
 
void
void
fp_cmp(sim_cpu *cpu,
fp_cmp(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       unsigned64 op1,
       unsigned64 op1,
       unsigned64 op2,
       unsigned64 op2,
       FP_formats fmt,
       FP_formats fmt,
       int abs,
       int abs,
       int cond,
       int cond,
       int cc)
       int cc)
{
{
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
 
 
  /* The format type should already have been checked.  The FCSR is
  /* The format type should already have been checked.  The FCSR is
     updated before the condition codes so that any exceptions will
     updated before the condition codes so that any exceptions will
     be signalled before the condition codes are changed.  */
     be signalled before the condition codes are changed.  */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
    case fmt_double:
    case fmt_double:
      {
      {
        int result;
        int result;
        status = fp_test(op1, op2, fmt, abs, cond, &result);
        status = fp_test(op1, op2, fmt, abs, cond, &result);
        update_fcsr (cpu, cia, status);
        update_fcsr (cpu, cia, status);
        SETFCC (cc, result);
        SETFCC (cc, result);
        break;
        break;
      }
      }
    case fmt_ps:
    case fmt_ps:
      {
      {
        int result0, result1;
        int result0, result1;
        status  = fp_test(FP_PS_lower (op1), FP_PS_lower (op2), fmt_single,
        status  = fp_test(FP_PS_lower (op1), FP_PS_lower (op2), fmt_single,
                          abs, cond, &result0);
                          abs, cond, &result0);
        status |= fp_test(FP_PS_upper (op1), FP_PS_upper (op2), fmt_single,
        status |= fp_test(FP_PS_upper (op1), FP_PS_upper (op2), fmt_single,
                          abs, cond, &result1);
                          abs, cond, &result1);
        update_fcsr (cpu, cia, status);
        update_fcsr (cpu, cia, status);
        SETFCC (cc, result0);
        SETFCC (cc, result0);
        SETFCC (cc+1, result1);
        SETFCC (cc+1, result1);
        break;
        break;
      }
      }
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
}
}
 
 
 
 
/* Basic arithmetic operations.  */
/* Basic arithmetic operations.  */
 
 
static unsigned64
static unsigned64
fp_unary(sim_cpu *cpu,
fp_unary(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         int (*sim_fpu_op)(sim_fpu *, const sim_fpu *),
         int (*sim_fpu_op)(sim_fpu *, const sim_fpu *),
         unsigned64 op,
         unsigned64 op,
         FP_formats fmt)
         FP_formats fmt)
{
{
  sim_fpu wop;
  sim_fpu wop;
  sim_fpu ans;
  sim_fpu ans;
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
  unsigned64 result = 0;
  unsigned64 result = 0;
 
 
  /* The format type has already been checked: */
  /* The format type has already been checked: */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
      {
      {
        unsigned32 res;
        unsigned32 res;
        sim_fpu_32to (&wop, op);
        sim_fpu_32to (&wop, op);
        status |= (*sim_fpu_op) (&ans, &wop);
        status |= (*sim_fpu_op) (&ans, &wop);
        status |= sim_fpu_round_32 (&ans, round, denorm);
        status |= sim_fpu_round_32 (&ans, round, denorm);
        sim_fpu_to32 (&res, &ans);
        sim_fpu_to32 (&res, &ans);
        result = res;
        result = res;
        break;
        break;
      }
      }
    case fmt_double:
    case fmt_double:
      {
      {
        unsigned64 res;
        unsigned64 res;
        sim_fpu_64to (&wop, op);
        sim_fpu_64to (&wop, op);
        status |= (*sim_fpu_op) (&ans, &wop);
        status |= (*sim_fpu_op) (&ans, &wop);
        status |= sim_fpu_round_64 (&ans, round, denorm);
        status |= sim_fpu_round_64 (&ans, round, denorm);
        sim_fpu_to64 (&res, &ans);
        sim_fpu_to64 (&res, &ans);
        result = res;
        result = res;
        break;
        break;
      }
      }
    case fmt_ps:
    case fmt_ps:
      {
      {
        int status_u = 0, status_l = 0;
        int status_u = 0, status_l = 0;
        unsigned32 res_u, res_l;
        unsigned32 res_u, res_l;
        sim_fpu_32to (&wop, FP_PS_upper(op));
        sim_fpu_32to (&wop, FP_PS_upper(op));
        status_u |= (*sim_fpu_op) (&ans, &wop);
        status_u |= (*sim_fpu_op) (&ans, &wop);
        sim_fpu_to32 (&res_u, &ans);
        sim_fpu_to32 (&res_u, &ans);
        sim_fpu_32to (&wop, FP_PS_lower(op));
        sim_fpu_32to (&wop, FP_PS_lower(op));
        status_l |= (*sim_fpu_op) (&ans, &wop);
        status_l |= (*sim_fpu_op) (&ans, &wop);
        sim_fpu_to32 (&res_l, &ans);
        sim_fpu_to32 (&res_l, &ans);
        result = FP_PS_cat(res_u, res_l);
        result = FP_PS_cat(res_u, res_l);
        status = status_u | status_l;
        status = status_u | status_l;
        break;
        break;
      }
      }
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  update_fcsr (cpu, cia, status);
  update_fcsr (cpu, cia, status);
  return result;
  return result;
}
}
 
 
static unsigned64
static unsigned64
fp_binary(sim_cpu *cpu,
fp_binary(sim_cpu *cpu,
          address_word cia,
          address_word cia,
          int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
          int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
          unsigned64 op1,
          unsigned64 op1,
          unsigned64 op2,
          unsigned64 op2,
          FP_formats fmt)
          FP_formats fmt)
{
{
  sim_fpu wop1;
  sim_fpu wop1;
  sim_fpu wop2;
  sim_fpu wop2;
  sim_fpu ans;
  sim_fpu ans;
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
  unsigned64 result = 0;
  unsigned64 result = 0;
 
 
  /* The format type has already been checked: */
  /* The format type has already been checked: */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
      {
      {
        unsigned32 res;
        unsigned32 res;
        sim_fpu_32to (&wop1, op1);
        sim_fpu_32to (&wop1, op1);
        sim_fpu_32to (&wop2, op2);
        sim_fpu_32to (&wop2, op2);
        status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        status |= sim_fpu_round_32 (&ans, round, denorm);
        status |= sim_fpu_round_32 (&ans, round, denorm);
        sim_fpu_to32 (&res, &ans);
        sim_fpu_to32 (&res, &ans);
        result = res;
        result = res;
        break;
        break;
      }
      }
    case fmt_double:
    case fmt_double:
      {
      {
        unsigned64 res;
        unsigned64 res;
        sim_fpu_64to (&wop1, op1);
        sim_fpu_64to (&wop1, op1);
        sim_fpu_64to (&wop2, op2);
        sim_fpu_64to (&wop2, op2);
        status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        status |= sim_fpu_round_64 (&ans, round, denorm);
        status |= sim_fpu_round_64 (&ans, round, denorm);
        sim_fpu_to64 (&res, &ans);
        sim_fpu_to64 (&res, &ans);
        result = res;
        result = res;
        break;
        break;
      }
      }
    case fmt_ps:
    case fmt_ps:
      {
      {
        int status_u = 0, status_l = 0;
        int status_u = 0, status_l = 0;
        unsigned32 res_u, res_l;
        unsigned32 res_u, res_l;
        sim_fpu_32to (&wop1, FP_PS_upper(op1));
        sim_fpu_32to (&wop1, FP_PS_upper(op1));
        sim_fpu_32to (&wop2, FP_PS_upper(op2));
        sim_fpu_32to (&wop2, FP_PS_upper(op2));
        status_u |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        status_u |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        sim_fpu_to32 (&res_u, &ans);
        sim_fpu_to32 (&res_u, &ans);
        sim_fpu_32to (&wop1, FP_PS_lower(op1));
        sim_fpu_32to (&wop1, FP_PS_lower(op1));
        sim_fpu_32to (&wop2, FP_PS_lower(op2));
        sim_fpu_32to (&wop2, FP_PS_lower(op2));
        status_l |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        status_l |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        sim_fpu_to32 (&res_l, &ans);
        sim_fpu_to32 (&res_l, &ans);
        result = FP_PS_cat(res_u, res_l);
        result = FP_PS_cat(res_u, res_l);
        status = status_u | status_l;
        status = status_u | status_l;
        break;
        break;
      }
      }
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  update_fcsr (cpu, cia, status);
  update_fcsr (cpu, cia, status);
  return result;
  return result;
}
}
 
 
/* Common MAC code for single operands (.s or .d), defers setting FCSR.  */
/* Common MAC code for single operands (.s or .d), defers setting FCSR.  */
static sim_fpu_status
static sim_fpu_status
inner_mac(int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
inner_mac(int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
          unsigned64 op1,
          unsigned64 op1,
          unsigned64 op2,
          unsigned64 op2,
          unsigned64 op3,
          unsigned64 op3,
          int scale,
          int scale,
          int negate,
          int negate,
          FP_formats fmt,
          FP_formats fmt,
          sim_fpu_round round,
          sim_fpu_round round,
          sim_fpu_denorm denorm,
          sim_fpu_denorm denorm,
          unsigned64 *result)
          unsigned64 *result)
{
{
  sim_fpu wop1;
  sim_fpu wop1;
  sim_fpu wop2;
  sim_fpu wop2;
  sim_fpu ans;
  sim_fpu ans;
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
  sim_fpu_status op_status;
  sim_fpu_status op_status;
  unsigned64 temp = 0;
  unsigned64 temp = 0;
 
 
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
      {
      {
        unsigned32 res;
        unsigned32 res;
        sim_fpu_32to (&wop1, op1);
        sim_fpu_32to (&wop1, op1);
        sim_fpu_32to (&wop2, op2);
        sim_fpu_32to (&wop2, op2);
        status |= sim_fpu_mul (&ans, &wop1, &wop2);
        status |= sim_fpu_mul (&ans, &wop1, &wop2);
        if (scale != 0 && sim_fpu_is_number (&ans))  /* number or denorm */
        if (scale != 0 && sim_fpu_is_number (&ans))  /* number or denorm */
          ans.normal_exp += scale;
          ans.normal_exp += scale;
        status |= sim_fpu_round_32 (&ans, round, denorm);
        status |= sim_fpu_round_32 (&ans, round, denorm);
        wop1 = ans;
        wop1 = ans;
        op_status = 0;
        op_status = 0;
        sim_fpu_32to (&wop2, op3);
        sim_fpu_32to (&wop2, op3);
        op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        op_status |= sim_fpu_round_32 (&ans, round, denorm);
        op_status |= sim_fpu_round_32 (&ans, round, denorm);
        status |= op_status;
        status |= op_status;
        if (negate)
        if (negate)
          {
          {
            wop1 = ans;
            wop1 = ans;
            op_status = sim_fpu_neg (&ans, &wop1);
            op_status = sim_fpu_neg (&ans, &wop1);
            op_status |= sim_fpu_round_32 (&ans, round, denorm);
            op_status |= sim_fpu_round_32 (&ans, round, denorm);
            status |= op_status;
            status |= op_status;
          }
          }
        sim_fpu_to32 (&res, &ans);
        sim_fpu_to32 (&res, &ans);
        temp = res;
        temp = res;
        break;
        break;
      }
      }
    case fmt_double:
    case fmt_double:
      {
      {
        unsigned64 res;
        unsigned64 res;
        sim_fpu_64to (&wop1, op1);
        sim_fpu_64to (&wop1, op1);
        sim_fpu_64to (&wop2, op2);
        sim_fpu_64to (&wop2, op2);
        status |= sim_fpu_mul (&ans, &wop1, &wop2);
        status |= sim_fpu_mul (&ans, &wop1, &wop2);
        if (scale != 0 && sim_fpu_is_number (&ans))  /* number or denorm */
        if (scale != 0 && sim_fpu_is_number (&ans))  /* number or denorm */
          ans.normal_exp += scale;
          ans.normal_exp += scale;
        status |= sim_fpu_round_64 (&ans, round, denorm);
        status |= sim_fpu_round_64 (&ans, round, denorm);
        wop1 = ans;
        wop1 = ans;
        op_status = 0;
        op_status = 0;
        sim_fpu_64to (&wop2, op3);
        sim_fpu_64to (&wop2, op3);
        op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
        op_status |= sim_fpu_round_64 (&ans, round, denorm);
        op_status |= sim_fpu_round_64 (&ans, round, denorm);
        status |= op_status;
        status |= op_status;
        if (negate)
        if (negate)
          {
          {
            wop1 = ans;
            wop1 = ans;
            op_status = sim_fpu_neg (&ans, &wop1);
            op_status = sim_fpu_neg (&ans, &wop1);
            op_status |= sim_fpu_round_64 (&ans, round, denorm);
            op_status |= sim_fpu_round_64 (&ans, round, denorm);
            status |= op_status;
            status |= op_status;
          }
          }
        sim_fpu_to64 (&res, &ans);
        sim_fpu_to64 (&res, &ans);
        temp = res;
        temp = res;
        break;
        break;
      }
      }
    default:
    default:
      fprintf (stderr, "Bad switch\n");
      fprintf (stderr, "Bad switch\n");
      abort ();
      abort ();
    }
    }
  *result = temp;
  *result = temp;
  return status;
  return status;
}
}
 
 
/* Common implementation of madd, nmadd, msub, nmsub that does
/* Common implementation of madd, nmadd, msub, nmsub that does
   intermediate rounding per spec.  Also used for recip2 and rsqrt2,
   intermediate rounding per spec.  Also used for recip2 and rsqrt2,
   which are transformed into equivalent nmsub operations.  The scale
   which are transformed into equivalent nmsub operations.  The scale
   argument is an adjustment to the exponent of the intermediate
   argument is an adjustment to the exponent of the intermediate
   product op1*op2.  It is currently non-zero for rsqrt2 (-1), which
   product op1*op2.  It is currently non-zero for rsqrt2 (-1), which
   requires an effective division by 2. */
   requires an effective division by 2. */
static unsigned64
static unsigned64
fp_mac(sim_cpu *cpu,
fp_mac(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
       int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
       unsigned64 op1,
       unsigned64 op1,
       unsigned64 op2,
       unsigned64 op2,
       unsigned64 op3,
       unsigned64 op3,
       int scale,
       int scale,
       int negate,
       int negate,
       FP_formats fmt)
       FP_formats fmt)
{
{
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
  unsigned64 result = 0;
  unsigned64 result = 0;
 
 
  /* The format type has already been checked: */
  /* The format type has already been checked: */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
    case fmt_double:
    case fmt_double:
      status = inner_mac(sim_fpu_op, op1, op2, op3, scale,
      status = inner_mac(sim_fpu_op, op1, op2, op3, scale,
                         negate, fmt, round, denorm, &result);
                         negate, fmt, round, denorm, &result);
      break;
      break;
    case fmt_ps:
    case fmt_ps:
      {
      {
        int status_u, status_l;
        int status_u, status_l;
        unsigned64 result_u, result_l;
        unsigned64 result_u, result_l;
        status_u = inner_mac(sim_fpu_op, FP_PS_upper(op1), FP_PS_upper(op2),
        status_u = inner_mac(sim_fpu_op, FP_PS_upper(op1), FP_PS_upper(op2),
                             FP_PS_upper(op3), scale, negate, fmt_single,
                             FP_PS_upper(op3), scale, negate, fmt_single,
                             round, denorm, &result_u);
                             round, denorm, &result_u);
        status_l = inner_mac(sim_fpu_op, FP_PS_lower(op1), FP_PS_lower(op2),
        status_l = inner_mac(sim_fpu_op, FP_PS_lower(op1), FP_PS_lower(op2),
                             FP_PS_lower(op3), scale, negate, fmt_single,
                             FP_PS_lower(op3), scale, negate, fmt_single,
                             round, denorm, &result_l);
                             round, denorm, &result_l);
        result = FP_PS_cat(result_u, result_l);
        result = FP_PS_cat(result_u, result_l);
        status = status_u | status_l;
        status = status_u | status_l;
        break;
        break;
      }
      }
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  update_fcsr (cpu, cia, status);
  update_fcsr (cpu, cia, status);
  return result;
  return result;
}
}
 
 
/* Common rsqrt code for single operands (.s or .d), intermediate rounding.  */
/* Common rsqrt code for single operands (.s or .d), intermediate rounding.  */
static sim_fpu_status
static sim_fpu_status
inner_rsqrt(unsigned64 op1,
inner_rsqrt(unsigned64 op1,
            FP_formats fmt,
            FP_formats fmt,
            sim_fpu_round round,
            sim_fpu_round round,
            sim_fpu_denorm denorm,
            sim_fpu_denorm denorm,
            unsigned64 *result)
            unsigned64 *result)
{
{
  sim_fpu wop1;
  sim_fpu wop1;
  sim_fpu ans;
  sim_fpu ans;
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
  sim_fpu_status op_status;
  sim_fpu_status op_status;
  unsigned64 temp = 0;
  unsigned64 temp = 0;
 
 
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
      {
      {
        unsigned32 res;
        unsigned32 res;
        sim_fpu_32to (&wop1, op1);
        sim_fpu_32to (&wop1, op1);
        status |= sim_fpu_sqrt (&ans, &wop1);
        status |= sim_fpu_sqrt (&ans, &wop1);
        status |= sim_fpu_round_32 (&ans, status, round);
        status |= sim_fpu_round_32 (&ans, status, round);
        wop1 = ans;
        wop1 = ans;
        op_status = sim_fpu_inv (&ans, &wop1);
        op_status = sim_fpu_inv (&ans, &wop1);
        op_status |= sim_fpu_round_32 (&ans, round, denorm);
        op_status |= sim_fpu_round_32 (&ans, round, denorm);
        sim_fpu_to32 (&res, &ans);
        sim_fpu_to32 (&res, &ans);
        temp = res;
        temp = res;
        status |= op_status;
        status |= op_status;
        break;
        break;
      }
      }
    case fmt_double:
    case fmt_double:
      {
      {
        unsigned64 res;
        unsigned64 res;
        sim_fpu_64to (&wop1, op1);
        sim_fpu_64to (&wop1, op1);
        status |= sim_fpu_sqrt (&ans, &wop1);
        status |= sim_fpu_sqrt (&ans, &wop1);
        status |= sim_fpu_round_64 (&ans, round, denorm);
        status |= sim_fpu_round_64 (&ans, round, denorm);
        wop1 = ans;
        wop1 = ans;
        op_status = sim_fpu_inv (&ans, &wop1);
        op_status = sim_fpu_inv (&ans, &wop1);
        op_status |= sim_fpu_round_64 (&ans, round, denorm);
        op_status |= sim_fpu_round_64 (&ans, round, denorm);
        sim_fpu_to64 (&res, &ans);
        sim_fpu_to64 (&res, &ans);
        temp = res;
        temp = res;
        status |= op_status;
        status |= op_status;
        break;
        break;
      }
      }
    default:
    default:
      fprintf (stderr, "Bad switch\n");
      fprintf (stderr, "Bad switch\n");
      abort ();
      abort ();
    }
    }
  *result = temp;
  *result = temp;
  return status;
  return status;
}
}
 
 
static unsigned64
static unsigned64
fp_inv_sqrt(sim_cpu *cpu,
fp_inv_sqrt(sim_cpu *cpu,
            address_word cia,
            address_word cia,
            unsigned64 op1,
            unsigned64 op1,
            FP_formats fmt)
            FP_formats fmt)
{
{
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_round round = rounding_mode (GETRM());
  sim_fpu_round denorm = denorm_mode (cpu);
  sim_fpu_round denorm = denorm_mode (cpu);
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
  unsigned64 result = 0;
  unsigned64 result = 0;
 
 
  /* The format type has already been checked: */
  /* The format type has already been checked: */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
    case fmt_double:
    case fmt_double:
      status = inner_rsqrt (op1, fmt, round, denorm, &result);
      status = inner_rsqrt (op1, fmt, round, denorm, &result);
      break;
      break;
    case fmt_ps:
    case fmt_ps:
      {
      {
        int status_u, status_l;
        int status_u, status_l;
        unsigned64 result_u, result_l;
        unsigned64 result_u, result_l;
        status_u = inner_rsqrt (FP_PS_upper(op1), fmt_single, round, denorm,
        status_u = inner_rsqrt (FP_PS_upper(op1), fmt_single, round, denorm,
                                &result_u);
                                &result_u);
        status_l = inner_rsqrt (FP_PS_lower(op1), fmt_single, round, denorm,
        status_l = inner_rsqrt (FP_PS_lower(op1), fmt_single, round, denorm,
                                &result_l);
                                &result_l);
        result = FP_PS_cat(result_u, result_l);
        result = FP_PS_cat(result_u, result_l);
        status = status_u | status_l;
        status = status_u | status_l;
        break;
        break;
      }
      }
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  update_fcsr (cpu, cia, status);
  update_fcsr (cpu, cia, status);
  return result;
  return result;
}
}
 
 
 
 
unsigned64
unsigned64
fp_abs(sim_cpu *cpu,
fp_abs(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       unsigned64 op,
       unsigned64 op,
       FP_formats fmt)
       FP_formats fmt)
{
{
  return fp_unary(cpu, cia, &sim_fpu_abs, op, fmt);
  return fp_unary(cpu, cia, &sim_fpu_abs, op, fmt);
}
}
 
 
unsigned64
unsigned64
fp_neg(sim_cpu *cpu,
fp_neg(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       unsigned64 op,
       unsigned64 op,
       FP_formats fmt)
       FP_formats fmt)
{
{
  return fp_unary(cpu, cia, &sim_fpu_neg, op, fmt);
  return fp_unary(cpu, cia, &sim_fpu_neg, op, fmt);
}
}
 
 
unsigned64
unsigned64
fp_add(sim_cpu *cpu,
fp_add(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       unsigned64 op1,
       unsigned64 op1,
       unsigned64 op2,
       unsigned64 op2,
       FP_formats fmt)
       FP_formats fmt)
{
{
  return fp_binary(cpu, cia, &sim_fpu_add, op1, op2, fmt);
  return fp_binary(cpu, cia, &sim_fpu_add, op1, op2, fmt);
}
}
 
 
unsigned64
unsigned64
fp_sub(sim_cpu *cpu,
fp_sub(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       unsigned64 op1,
       unsigned64 op1,
       unsigned64 op2,
       unsigned64 op2,
       FP_formats fmt)
       FP_formats fmt)
{
{
  return fp_binary(cpu, cia, &sim_fpu_sub, op1, op2, fmt);
  return fp_binary(cpu, cia, &sim_fpu_sub, op1, op2, fmt);
}
}
 
 
unsigned64
unsigned64
fp_mul(sim_cpu *cpu,
fp_mul(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       unsigned64 op1,
       unsigned64 op1,
       unsigned64 op2,
       unsigned64 op2,
       FP_formats fmt)
       FP_formats fmt)
{
{
  return fp_binary(cpu, cia, &sim_fpu_mul, op1, op2, fmt);
  return fp_binary(cpu, cia, &sim_fpu_mul, op1, op2, fmt);
}
}
 
 
unsigned64
unsigned64
fp_div(sim_cpu *cpu,
fp_div(sim_cpu *cpu,
       address_word cia,
       address_word cia,
       unsigned64 op1,
       unsigned64 op1,
       unsigned64 op2,
       unsigned64 op2,
       FP_formats fmt)
       FP_formats fmt)
{
{
  return fp_binary(cpu, cia, &sim_fpu_div, op1, op2, fmt);
  return fp_binary(cpu, cia, &sim_fpu_div, op1, op2, fmt);
}
}
 
 
unsigned64
unsigned64
fp_recip(sim_cpu *cpu,
fp_recip(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op,
         unsigned64 op,
         FP_formats fmt)
         FP_formats fmt)
{
{
  return fp_unary(cpu, cia, &sim_fpu_inv, op, fmt);
  return fp_unary(cpu, cia, &sim_fpu_inv, op, fmt);
}
}
 
 
unsigned64
unsigned64
fp_sqrt(sim_cpu *cpu,
fp_sqrt(sim_cpu *cpu,
        address_word cia,
        address_word cia,
        unsigned64 op,
        unsigned64 op,
        FP_formats fmt)
        FP_formats fmt)
{
{
  return fp_unary(cpu, cia, &sim_fpu_sqrt, op, fmt);
  return fp_unary(cpu, cia, &sim_fpu_sqrt, op, fmt);
}
}
 
 
unsigned64
unsigned64
fp_rsqrt(sim_cpu *cpu,
fp_rsqrt(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op,
         unsigned64 op,
         FP_formats fmt)
         FP_formats fmt)
{
{
  return fp_inv_sqrt(cpu, cia, op, fmt);
  return fp_inv_sqrt(cpu, cia, op, fmt);
}
}
 
 
unsigned64
unsigned64
fp_madd(sim_cpu *cpu,
fp_madd(sim_cpu *cpu,
        address_word cia,
        address_word cia,
        unsigned64 op1,
        unsigned64 op1,
        unsigned64 op2,
        unsigned64 op2,
        unsigned64 op3,
        unsigned64 op3,
        FP_formats fmt)
        FP_formats fmt)
{
{
  return fp_mac(cpu, cia, &sim_fpu_add, op1, op2, op3, 0, 0, fmt);
  return fp_mac(cpu, cia, &sim_fpu_add, op1, op2, op3, 0, 0, fmt);
}
}
 
 
unsigned64
unsigned64
fp_msub(sim_cpu *cpu,
fp_msub(sim_cpu *cpu,
        address_word cia,
        address_word cia,
        unsigned64 op1,
        unsigned64 op1,
        unsigned64 op2,
        unsigned64 op2,
        unsigned64 op3,
        unsigned64 op3,
        FP_formats fmt)
        FP_formats fmt)
{
{
  return fp_mac(cpu, cia, &sim_fpu_sub, op1, op2, op3, 0, 0, fmt);
  return fp_mac(cpu, cia, &sim_fpu_sub, op1, op2, op3, 0, 0, fmt);
}
}
 
 
unsigned64
unsigned64
fp_nmadd(sim_cpu *cpu,
fp_nmadd(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op1,
         unsigned64 op1,
         unsigned64 op2,
         unsigned64 op2,
         unsigned64 op3,
         unsigned64 op3,
         FP_formats fmt)
         FP_formats fmt)
{
{
  return fp_mac(cpu, cia, &sim_fpu_add, op1, op2, op3, 0, 1, fmt);
  return fp_mac(cpu, cia, &sim_fpu_add, op1, op2, op3, 0, 1, fmt);
}
}
 
 
unsigned64
unsigned64
fp_nmsub(sim_cpu *cpu,
fp_nmsub(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op1,
         unsigned64 op1,
         unsigned64 op2,
         unsigned64 op2,
         unsigned64 op3,
         unsigned64 op3,
         FP_formats fmt)
         FP_formats fmt)
{
{
  return fp_mac(cpu, cia, &sim_fpu_sub, op1, op2, op3, 0, 1, fmt);
  return fp_mac(cpu, cia, &sim_fpu_sub, op1, op2, op3, 0, 1, fmt);
}
}
 
 
 
 
/* MIPS-3D ASE operations.  */
/* MIPS-3D ASE operations.  */
 
 
/* Variant of fp_binary for *r.ps MIPS-3D operations. */
/* Variant of fp_binary for *r.ps MIPS-3D operations. */
static unsigned64
static unsigned64
fp_binary_r(sim_cpu *cpu,
fp_binary_r(sim_cpu *cpu,
            address_word cia,
            address_word cia,
            int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
            int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
            unsigned64 op1,
            unsigned64 op1,
            unsigned64 op2)
            unsigned64 op2)
{
{
  sim_fpu wop1;
  sim_fpu wop1;
  sim_fpu wop2;
  sim_fpu wop2;
  sim_fpu ans;
  sim_fpu ans;
  sim_fpu_round round = rounding_mode (GETRM ());
  sim_fpu_round round = rounding_mode (GETRM ());
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_status status_u, status_l;
  sim_fpu_status status_u, status_l;
  unsigned64 result;
  unsigned64 result;
  unsigned32 res_u, res_l;
  unsigned32 res_u, res_l;
 
 
  /* The format must be fmt_ps.  */
  /* The format must be fmt_ps.  */
  status_u = 0;
  status_u = 0;
  sim_fpu_32to (&wop1, FP_PS_upper (op1));
  sim_fpu_32to (&wop1, FP_PS_upper (op1));
  sim_fpu_32to (&wop2, FP_PS_lower (op1));
  sim_fpu_32to (&wop2, FP_PS_lower (op1));
  status_u |= (*sim_fpu_op) (&ans, &wop1, &wop2);
  status_u |= (*sim_fpu_op) (&ans, &wop1, &wop2);
  status_u |= sim_fpu_round_32 (&ans, round, denorm);
  status_u |= sim_fpu_round_32 (&ans, round, denorm);
  sim_fpu_to32 (&res_u, &ans);
  sim_fpu_to32 (&res_u, &ans);
  status_l = 0;
  status_l = 0;
  sim_fpu_32to (&wop1, FP_PS_upper (op2));
  sim_fpu_32to (&wop1, FP_PS_upper (op2));
  sim_fpu_32to (&wop2, FP_PS_lower (op2));
  sim_fpu_32to (&wop2, FP_PS_lower (op2));
  status_l |= (*sim_fpu_op) (&ans, &wop1, &wop2);
  status_l |= (*sim_fpu_op) (&ans, &wop1, &wop2);
  status_l |= sim_fpu_round_32 (&ans, round, denorm);
  status_l |= sim_fpu_round_32 (&ans, round, denorm);
  sim_fpu_to32 (&res_l, &ans);
  sim_fpu_to32 (&res_l, &ans);
  result = FP_PS_cat (res_u, res_l);
  result = FP_PS_cat (res_u, res_l);
 
 
  update_fcsr (cpu, cia, status_u | status_l);
  update_fcsr (cpu, cia, status_u | status_l);
  return result;
  return result;
}
}
 
 
unsigned64
unsigned64
fp_add_r(sim_cpu *cpu,
fp_add_r(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op1,
         unsigned64 op1,
         unsigned64 op2,
         unsigned64 op2,
         FP_formats fmt)
         FP_formats fmt)
{
{
  return fp_binary_r (cpu, cia, &sim_fpu_add, op1, op2);
  return fp_binary_r (cpu, cia, &sim_fpu_add, op1, op2);
}
}
 
 
unsigned64
unsigned64
fp_mul_r(sim_cpu *cpu,
fp_mul_r(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op1,
         unsigned64 op1,
         unsigned64 op2,
         unsigned64 op2,
         FP_formats fmt)
         FP_formats fmt)
{
{
  return fp_binary_r (cpu, cia, &sim_fpu_mul, op1, op2);
  return fp_binary_r (cpu, cia, &sim_fpu_mul, op1, op2);
}
}
 
 
#define NR_FRAC_GUARD   (60)
#define NR_FRAC_GUARD   (60)
#define IMPLICIT_1 LSBIT64 (NR_FRAC_GUARD)
#define IMPLICIT_1 LSBIT64 (NR_FRAC_GUARD)
 
 
static int
static int
fpu_inv1(sim_fpu *f, const sim_fpu *l)
fpu_inv1(sim_fpu *f, const sim_fpu *l)
{
{
  static const sim_fpu sim_fpu_one = {
  static const sim_fpu sim_fpu_one = {
    sim_fpu_class_number, 0, IMPLICIT_1, 0
    sim_fpu_class_number, 0, IMPLICIT_1, 0
  };
  };
  int  status = 0;
  int  status = 0;
  sim_fpu t;
  sim_fpu t;
 
 
  if (sim_fpu_is_zero (l))
  if (sim_fpu_is_zero (l))
    {
    {
      *f = sim_fpu_maxfp;
      *f = sim_fpu_maxfp;
      f->sign = l->sign;
      f->sign = l->sign;
      return sim_fpu_status_invalid_div0;
      return sim_fpu_status_invalid_div0;
    }
    }
  if (sim_fpu_is_infinity (l))
  if (sim_fpu_is_infinity (l))
    {
    {
      *f = sim_fpu_zero;
      *f = sim_fpu_zero;
      f->sign = l->sign;
      f->sign = l->sign;
      return status;
      return status;
    }
    }
  status |= sim_fpu_div (f, &sim_fpu_one, l);
  status |= sim_fpu_div (f, &sim_fpu_one, l);
  return status;
  return status;
}
}
 
 
static int
static int
fpu_inv1_32(sim_fpu *f, const sim_fpu *l)
fpu_inv1_32(sim_fpu *f, const sim_fpu *l)
{
{
  if (sim_fpu_is_zero (l))
  if (sim_fpu_is_zero (l))
    {
    {
      *f = sim_fpu_max32;
      *f = sim_fpu_max32;
      f->sign = l->sign;
      f->sign = l->sign;
      return sim_fpu_status_invalid_div0;
      return sim_fpu_status_invalid_div0;
    }
    }
  return fpu_inv1 (f, l);
  return fpu_inv1 (f, l);
}
}
 
 
static int
static int
fpu_inv1_64(sim_fpu *f, const sim_fpu *l)
fpu_inv1_64(sim_fpu *f, const sim_fpu *l)
{
{
  if (sim_fpu_is_zero (l))
  if (sim_fpu_is_zero (l))
    {
    {
      *f = sim_fpu_max64;
      *f = sim_fpu_max64;
      f->sign = l->sign;
      f->sign = l->sign;
      return sim_fpu_status_invalid_div0;
      return sim_fpu_status_invalid_div0;
    }
    }
  return fpu_inv1 (f, l);
  return fpu_inv1 (f, l);
}
}
 
 
unsigned64
unsigned64
fp_recip1(sim_cpu *cpu,
fp_recip1(sim_cpu *cpu,
          address_word cia,
          address_word cia,
          unsigned64 op,
          unsigned64 op,
          FP_formats fmt)
          FP_formats fmt)
{
{
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
    case fmt_ps:
    case fmt_ps:
      return fp_unary (cpu, cia, &fpu_inv1_32, op, fmt);
      return fp_unary (cpu, cia, &fpu_inv1_32, op, fmt);
    case fmt_double:
    case fmt_double:
      return fp_unary (cpu, cia, &fpu_inv1_64, op, fmt);
      return fp_unary (cpu, cia, &fpu_inv1_64, op, fmt);
    }
    }
  return 0;
  return 0;
}
}
 
 
unsigned64
unsigned64
fp_recip2(sim_cpu *cpu,
fp_recip2(sim_cpu *cpu,
          address_word cia,
          address_word cia,
          unsigned64 op1,
          unsigned64 op1,
          unsigned64 op2,
          unsigned64 op2,
          FP_formats fmt)
          FP_formats fmt)
{
{
  static const unsigned64 one_single = UNSIGNED64 (0x3F800000);
  static const unsigned64 one_single = UNSIGNED64 (0x3F800000);
  static const unsigned64 one_double = UNSIGNED64 (0x3FF0000000000000);
  static const unsigned64 one_double = UNSIGNED64 (0x3FF0000000000000);
  static const unsigned64 one_ps = (UNSIGNED64 (0x3F800000) << 32 | UNSIGNED64 (0x3F800000));
  static const unsigned64 one_ps = (UNSIGNED64 (0x3F800000) << 32 | UNSIGNED64 (0x3F800000));
  unsigned64 one;
  unsigned64 one;
 
 
  /* Implemented as nmsub fd, 1, fs, ft.  */
  /* Implemented as nmsub fd, 1, fs, ft.  */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:  one = one_single;  break;
    case fmt_single:  one = one_single;  break;
    case fmt_double:  one = one_double;  break;
    case fmt_double:  one = one_double;  break;
    case fmt_ps:      one = one_ps;      break;
    case fmt_ps:      one = one_ps;      break;
    default:          one = 0;           abort ();
    default:          one = 0;           abort ();
    }
    }
  return fp_mac (cpu, cia, &sim_fpu_sub, op1, op2, one, 0, 1, fmt);
  return fp_mac (cpu, cia, &sim_fpu_sub, op1, op2, one, 0, 1, fmt);
}
}
 
 
static int
static int
fpu_inv_sqrt1(sim_fpu *f, const sim_fpu *l)
fpu_inv_sqrt1(sim_fpu *f, const sim_fpu *l)
{
{
  static const sim_fpu sim_fpu_one = {
  static const sim_fpu sim_fpu_one = {
    sim_fpu_class_number, 0, IMPLICIT_1, 0
    sim_fpu_class_number, 0, IMPLICIT_1, 0
  };
  };
  int  status = 0;
  int  status = 0;
  sim_fpu t;
  sim_fpu t;
 
 
  if (sim_fpu_is_zero (l))
  if (sim_fpu_is_zero (l))
    {
    {
      *f = sim_fpu_maxfp;
      *f = sim_fpu_maxfp;
      f->sign = l->sign;
      f->sign = l->sign;
      return sim_fpu_status_invalid_div0;
      return sim_fpu_status_invalid_div0;
    }
    }
  if (sim_fpu_is_infinity (l))
  if (sim_fpu_is_infinity (l))
    {
    {
      if (!l->sign)
      if (!l->sign)
        {
        {
          f->class = sim_fpu_class_zero;
          f->class = sim_fpu_class_zero;
          f->sign = 0;
          f->sign = 0;
        }
        }
      else
      else
        {
        {
          *f = sim_fpu_qnan;
          *f = sim_fpu_qnan;
          status = sim_fpu_status_invalid_sqrt;
          status = sim_fpu_status_invalid_sqrt;
        }
        }
      return status;
      return status;
    }
    }
  status |= sim_fpu_sqrt (&t, l);
  status |= sim_fpu_sqrt (&t, l);
  status |= sim_fpu_div (f, &sim_fpu_one, &t);
  status |= sim_fpu_div (f, &sim_fpu_one, &t);
  return status;
  return status;
}
}
 
 
static int
static int
fpu_inv_sqrt1_32(sim_fpu *f, const sim_fpu *l)
fpu_inv_sqrt1_32(sim_fpu *f, const sim_fpu *l)
{
{
  if (sim_fpu_is_zero (l))
  if (sim_fpu_is_zero (l))
    {
    {
      *f = sim_fpu_max32;
      *f = sim_fpu_max32;
      f->sign = l->sign;
      f->sign = l->sign;
      return sim_fpu_status_invalid_div0;
      return sim_fpu_status_invalid_div0;
    }
    }
  return fpu_inv_sqrt1 (f, l);
  return fpu_inv_sqrt1 (f, l);
}
}
 
 
static int
static int
fpu_inv_sqrt1_64(sim_fpu *f, const sim_fpu *l)
fpu_inv_sqrt1_64(sim_fpu *f, const sim_fpu *l)
{
{
  if (sim_fpu_is_zero (l))
  if (sim_fpu_is_zero (l))
    {
    {
      *f = sim_fpu_max64;
      *f = sim_fpu_max64;
      f->sign = l->sign;
      f->sign = l->sign;
      return sim_fpu_status_invalid_div0;
      return sim_fpu_status_invalid_div0;
    }
    }
  return fpu_inv_sqrt1 (f, l);
  return fpu_inv_sqrt1 (f, l);
}
}
 
 
unsigned64
unsigned64
fp_rsqrt1(sim_cpu *cpu,
fp_rsqrt1(sim_cpu *cpu,
          address_word cia,
          address_word cia,
          unsigned64 op,
          unsigned64 op,
          FP_formats fmt)
          FP_formats fmt)
{
{
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
    case fmt_ps:
    case fmt_ps:
      return fp_unary (cpu, cia, &fpu_inv_sqrt1_32, op, fmt);
      return fp_unary (cpu, cia, &fpu_inv_sqrt1_32, op, fmt);
    case fmt_double:
    case fmt_double:
      return fp_unary (cpu, cia, &fpu_inv_sqrt1_64, op, fmt);
      return fp_unary (cpu, cia, &fpu_inv_sqrt1_64, op, fmt);
    }
    }
  return 0;
  return 0;
}
}
 
 
unsigned64
unsigned64
fp_rsqrt2(sim_cpu *cpu,
fp_rsqrt2(sim_cpu *cpu,
          address_word cia,
          address_word cia,
          unsigned64 op1,
          unsigned64 op1,
          unsigned64 op2,
          unsigned64 op2,
          FP_formats fmt)
          FP_formats fmt)
{
{
  static const unsigned64 half_single = UNSIGNED64 (0x3F000000);
  static const unsigned64 half_single = UNSIGNED64 (0x3F000000);
  static const unsigned64 half_double = UNSIGNED64 (0x3FE0000000000000);
  static const unsigned64 half_double = UNSIGNED64 (0x3FE0000000000000);
  static const unsigned64 half_ps = (UNSIGNED64 (0x3F000000) << 32 | UNSIGNED64 (0x3F000000));
  static const unsigned64 half_ps = (UNSIGNED64 (0x3F000000) << 32 | UNSIGNED64 (0x3F000000));
  unsigned64 half;
  unsigned64 half;
 
 
  /* Implemented as (nmsub fd, 0.5, fs, ft)/2, where the divide is
  /* Implemented as (nmsub fd, 0.5, fs, ft)/2, where the divide is
     done by scaling the exponent during multiply.  */
     done by scaling the exponent during multiply.  */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:  half = half_single;  break;
    case fmt_single:  half = half_single;  break;
    case fmt_double:  half = half_double;  break;
    case fmt_double:  half = half_double;  break;
    case fmt_ps:      half = half_ps;      break;
    case fmt_ps:      half = half_ps;      break;
    default:          half = 0;            abort ();
    default:          half = 0;            abort ();
    }
    }
  return fp_mac (cpu, cia, &sim_fpu_sub, op1, op2, half, -1, 1, fmt);
  return fp_mac (cpu, cia, &sim_fpu_sub, op1, op2, half, -1, 1, fmt);
}
}
 
 
 
 
/* Conversion operations.  */
/* Conversion operations.  */
 
 
uword64
uword64
convert (sim_cpu *cpu,
convert (sim_cpu *cpu,
         address_word cia,
         address_word cia,
         int rm,
         int rm,
         uword64 op,
         uword64 op,
         FP_formats from,
         FP_formats from,
         FP_formats to)
         FP_formats to)
{
{
  sim_fpu wop;
  sim_fpu wop;
  sim_fpu_round round = rounding_mode (rm);
  sim_fpu_round round = rounding_mode (rm);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  unsigned32 result32;
  unsigned32 result32;
  unsigned64 result64;
  unsigned64 result64;
  sim_fpu_status status = 0;
  sim_fpu_status status = 0;
 
 
  /* Convert the input to sim_fpu internal format */
  /* Convert the input to sim_fpu internal format */
  switch (from)
  switch (from)
    {
    {
    case fmt_double:
    case fmt_double:
      sim_fpu_64to (&wop, op);
      sim_fpu_64to (&wop, op);
      break;
      break;
    case fmt_single:
    case fmt_single:
      sim_fpu_32to (&wop, op);
      sim_fpu_32to (&wop, op);
      break;
      break;
    case fmt_word:
    case fmt_word:
      status = sim_fpu_i32to (&wop, op, round);
      status = sim_fpu_i32to (&wop, op, round);
      break;
      break;
    case fmt_long:
    case fmt_long:
      status = sim_fpu_i64to (&wop, op, round);
      status = sim_fpu_i64to (&wop, op, round);
      break;
      break;
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  /* Convert sim_fpu format into the output */
  /* Convert sim_fpu format into the output */
  /* The value WOP is converted to the destination format, rounding
  /* The value WOP is converted to the destination format, rounding
     using mode RM. When the destination is a fixed-point format, then
     using mode RM. When the destination is a fixed-point format, then
     a source value of Infinity, NaN or one which would round to an
     a source value of Infinity, NaN or one which would round to an
     integer outside the fixed point range then an IEEE Invalid Operation
     integer outside the fixed point range then an IEEE Invalid Operation
     condition is raised.  Not used if destination format is PS.  */
     condition is raised.  Not used if destination format is PS.  */
  switch (to)
  switch (to)
    {
    {
    case fmt_single:
    case fmt_single:
      status |= sim_fpu_round_32 (&wop, round, denorm);
      status |= sim_fpu_round_32 (&wop, round, denorm);
      /* For a NaN, normalize mantissa bits (cvt.s.d can't preserve them) */
      /* For a NaN, normalize mantissa bits (cvt.s.d can't preserve them) */
      if (sim_fpu_is_qnan (&wop))
      if (sim_fpu_is_qnan (&wop))
        wop = sim_fpu_qnan;
        wop = sim_fpu_qnan;
      sim_fpu_to32 (&result32, &wop);
      sim_fpu_to32 (&result32, &wop);
      result64 = result32;
      result64 = result32;
      break;
      break;
    case fmt_double:
    case fmt_double:
      status |= sim_fpu_round_64 (&wop, round, denorm);
      status |= sim_fpu_round_64 (&wop, round, denorm);
      /* For a NaN, normalize mantissa bits (make cvt.d.s consistent) */
      /* For a NaN, normalize mantissa bits (make cvt.d.s consistent) */
      if (sim_fpu_is_qnan (&wop))
      if (sim_fpu_is_qnan (&wop))
        wop = sim_fpu_qnan;
        wop = sim_fpu_qnan;
      sim_fpu_to64 (&result64, &wop);
      sim_fpu_to64 (&result64, &wop);
      break;
      break;
    case fmt_word:
    case fmt_word:
      status |= sim_fpu_to32i (&result32, &wop, round);
      status |= sim_fpu_to32i (&result32, &wop, round);
      result64 = result32;
      result64 = result32;
      break;
      break;
    case fmt_long:
    case fmt_long:
      status |= sim_fpu_to64i (&result64, &wop, round);
      status |= sim_fpu_to64i (&result64, &wop, round);
      break;
      break;
    default:
    default:
      result64 = 0;
      result64 = 0;
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  update_fcsr (cpu, cia, status);
  update_fcsr (cpu, cia, status);
  return result64;
  return result64;
}
}
 
 
unsigned64
unsigned64
ps_lower(sim_cpu *cpu,
ps_lower(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op)
         unsigned64 op)
{
{
  return FP_PS_lower (op);
  return FP_PS_lower (op);
}
}
 
 
unsigned64
unsigned64
ps_upper(sim_cpu *cpu,
ps_upper(sim_cpu *cpu,
         address_word cia,
         address_word cia,
         unsigned64 op)
         unsigned64 op)
{
{
  return FP_PS_upper(op);
  return FP_PS_upper(op);
}
}
 
 
unsigned64
unsigned64
pack_ps(sim_cpu *cpu,
pack_ps(sim_cpu *cpu,
        address_word cia,
        address_word cia,
        unsigned64 op1,
        unsigned64 op1,
        unsigned64 op2,
        unsigned64 op2,
        FP_formats fmt)
        FP_formats fmt)
{
{
  unsigned64 result = 0;
  unsigned64 result = 0;
 
 
  /* The registers must specify FPRs valid for operands of type
  /* The registers must specify FPRs valid for operands of type
     "fmt". If they are not valid, the result is undefined. */
     "fmt". If they are not valid, the result is undefined. */
 
 
  /* The format type should already have been checked: */
  /* The format type should already have been checked: */
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
      {
      {
        sim_fpu wop;
        sim_fpu wop;
        unsigned32 res_u, res_l;
        unsigned32 res_u, res_l;
        sim_fpu_32to (&wop, op1);
        sim_fpu_32to (&wop, op1);
        sim_fpu_to32 (&res_u, &wop);
        sim_fpu_to32 (&res_u, &wop);
        sim_fpu_32to (&wop, op2);
        sim_fpu_32to (&wop, op2);
        sim_fpu_to32 (&res_l, &wop);
        sim_fpu_to32 (&res_l, &wop);
        result = FP_PS_cat(res_u, res_l);
        result = FP_PS_cat(res_u, res_l);
        break;
        break;
      }
      }
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  return result;
  return result;
}
}
 
 
unsigned64
unsigned64
convert_ps (sim_cpu *cpu,
convert_ps (sim_cpu *cpu,
            address_word cia,
            address_word cia,
            int rm,
            int rm,
            unsigned64 op,
            unsigned64 op,
            FP_formats from,
            FP_formats from,
            FP_formats to)
            FP_formats to)
{
{
  sim_fpu wop_u, wop_l;
  sim_fpu wop_u, wop_l;
  sim_fpu_round round = rounding_mode (rm);
  sim_fpu_round round = rounding_mode (rm);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  sim_fpu_denorm denorm = denorm_mode (cpu);
  unsigned32 res_u, res_l;
  unsigned32 res_u, res_l;
  unsigned64 result;
  unsigned64 result;
  sim_fpu_status status_u = 0, status_l = 0;
  sim_fpu_status status_u = 0, status_l = 0;
 
 
  /* As convert, but used only for paired values (formats PS, PW) */
  /* As convert, but used only for paired values (formats PS, PW) */
 
 
  /* Convert the input to sim_fpu internal format */
  /* Convert the input to sim_fpu internal format */
  switch (from)
  switch (from)
    {
    {
    case fmt_word:   /* fmt_pw */
    case fmt_word:   /* fmt_pw */
      sim_fpu_i32to (&wop_u, (op >> 32) & (unsigned)0xFFFFFFFF, round);
      sim_fpu_i32to (&wop_u, (op >> 32) & (unsigned)0xFFFFFFFF, round);
      sim_fpu_i32to (&wop_l, op & (unsigned)0xFFFFFFFF, round);
      sim_fpu_i32to (&wop_l, op & (unsigned)0xFFFFFFFF, round);
      break;
      break;
    case fmt_ps:
    case fmt_ps:
      sim_fpu_32to (&wop_u, FP_PS_upper(op));
      sim_fpu_32to (&wop_u, FP_PS_upper(op));
      sim_fpu_32to (&wop_l, FP_PS_lower(op));
      sim_fpu_32to (&wop_l, FP_PS_lower(op));
      break;
      break;
    default:
    default:
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  /* Convert sim_fpu format into the output */
  /* Convert sim_fpu format into the output */
  switch (to)
  switch (to)
    {
    {
    case fmt_word:   /* fmt_pw */
    case fmt_word:   /* fmt_pw */
      status_u |= sim_fpu_to32i (&res_u, &wop_u, round);
      status_u |= sim_fpu_to32i (&res_u, &wop_u, round);
      status_l |= sim_fpu_to32i (&res_l, &wop_l, round);
      status_l |= sim_fpu_to32i (&res_l, &wop_l, round);
      result = (((unsigned64)res_u) << 32) | (unsigned64)res_l;
      result = (((unsigned64)res_u) << 32) | (unsigned64)res_l;
      break;
      break;
    case fmt_ps:
    case fmt_ps:
      status_u |= sim_fpu_round_32 (&wop_u, 0, round);
      status_u |= sim_fpu_round_32 (&wop_u, 0, round);
      status_l |= sim_fpu_round_32 (&wop_l, 0, round);
      status_l |= sim_fpu_round_32 (&wop_l, 0, round);
      sim_fpu_to32 (&res_u, &wop_u);
      sim_fpu_to32 (&res_u, &wop_u);
      sim_fpu_to32 (&res_l, &wop_l);
      sim_fpu_to32 (&res_l, &wop_l);
      result = FP_PS_cat(res_u, res_l);
      result = FP_PS_cat(res_u, res_l);
      break;
      break;
    default:
    default:
      result = 0;
      result = 0;
      sim_io_eprintf (SD, "Bad switch\n");
      sim_io_eprintf (SD, "Bad switch\n");
      abort ();
      abort ();
    }
    }
 
 
  update_fcsr (cpu, cia, status_u | status_l);
  update_fcsr (cpu, cia, status_u | status_l);
  return result;
  return result;
}
}
 
 
static const char *
static const char *
fpu_format_name (FP_formats fmt)
fpu_format_name (FP_formats fmt)
{
{
  switch (fmt)
  switch (fmt)
    {
    {
    case fmt_single:
    case fmt_single:
      return "single";
      return "single";
    case fmt_double:
    case fmt_double:
      return "double";
      return "double";
    case fmt_word:
    case fmt_word:
      return "word";
      return "word";
    case fmt_long:
    case fmt_long:
      return "long";
      return "long";
    case fmt_ps:
    case fmt_ps:
      return "ps";
      return "ps";
    case fmt_unknown:
    case fmt_unknown:
      return "<unknown>";
      return "<unknown>";
    case fmt_uninterpreted:
    case fmt_uninterpreted:
      return "<uninterpreted>";
      return "<uninterpreted>";
    case fmt_uninterpreted_32:
    case fmt_uninterpreted_32:
      return "<uninterpreted_32>";
      return "<uninterpreted_32>";
    case fmt_uninterpreted_64:
    case fmt_uninterpreted_64:
      return "<uninterpreted_64>";
      return "<uninterpreted_64>";
    default:
    default:
      return "<format error>";
      return "<format error>";
    }
    }
}
}
 
 
#ifdef DEBUG
#ifdef DEBUG
static const char *
static const char *
fpu_rounding_mode_name (int rm)
fpu_rounding_mode_name (int rm)
{
{
  switch (rm)
  switch (rm)
    {
    {
    case FP_RM_NEAREST:
    case FP_RM_NEAREST:
      return "Round";
      return "Round";
    case FP_RM_TOZERO:
    case FP_RM_TOZERO:
      return "Trunc";
      return "Trunc";
    case FP_RM_TOPINF:
    case FP_RM_TOPINF:
      return "Ceil";
      return "Ceil";
    case FP_RM_TOMINF:
    case FP_RM_TOMINF:
      return "Floor";
      return "Floor";
    default:
    default:
      return "<rounding mode error>";
      return "<rounding mode error>";
    }
    }
}
}
#endif /* DEBUG */
#endif /* DEBUG */
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.