fpa11.c 5.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
    NetWinder Floating Point Emulator
    (c) Rebel.COM, 1998,1999

    Direct questions, comments to Scott Bambrough <scottb@netwinder.org>

    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
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include "fpa11.h"

#include "fpopcode.h"

//#include "fpmodule.h"
//#include "fpmodule.inl"

//#include <asm/system.h>

#include <stdio.h>

33
FPA11* qemufpa = NULL;
P
pbrook 已提交
34
CPUARMState* user_registers;
35 36 37 38 39 40

/* Reset the FPA11 chip.  Called to initialize and reset the emulator. */
void resetFPA11(void)
{
  int i;
  FPA11 *fpa11 = GET_FPA11();
41

42 43 44 45 46
  /* initialize the register type array */
  for (i=0;i<=7;i++)
  {
    fpa11->fType[i] = typeNone;
  }
47

48 49
  /* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */
  fpa11->fpsr = FP_EMULATOR | BIT_AC;
50

51
  /* FPCR: set SB, AB and DA bits, clear all others */
52
#ifdef MAINTAIN_FPCR
53 54 55 56 57 58
  fpa11->fpcr = MASK_RESET;
#endif
}

void SetRoundingMode(const unsigned int opcode)
{
B
bellard 已提交
59
    int rounding_mode;
60
   FPA11 *fpa11 = GET_FPA11();
B
bellard 已提交
61

62
#ifdef MAINTAIN_FPCR
63
   fpa11->fpcr &= ~MASK_ROUNDING_MODE;
64
#endif
65 66 67 68
   switch (opcode & MASK_ROUNDING_MODE)
   {
      default:
      case ROUND_TO_NEAREST:
B
bellard 已提交
69
         rounding_mode = float_round_nearest_even;
70
#ifdef MAINTAIN_FPCR
71
         fpa11->fpcr |= ROUND_TO_NEAREST;
72
#endif
73
      break;
74

75
      case ROUND_TO_PLUS_INFINITY:
B
bellard 已提交
76
         rounding_mode = float_round_up;
77
#ifdef MAINTAIN_FPCR
78
         fpa11->fpcr |= ROUND_TO_PLUS_INFINITY;
79
#endif
80
      break;
81

82
      case ROUND_TO_MINUS_INFINITY:
B
bellard 已提交
83
         rounding_mode = float_round_down;
84
#ifdef MAINTAIN_FPCR
85
         fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
86
#endif
87
      break;
88

89
      case ROUND_TO_ZERO:
B
bellard 已提交
90
         rounding_mode = float_round_to_zero;
91
#ifdef MAINTAIN_FPCR
92
         fpa11->fpcr |= ROUND_TO_ZERO;
93
#endif
94 95
      break;
  }
B
bellard 已提交
96
   set_float_rounding_mode(rounding_mode, &fpa11->fp_status);
97 98 99 100
}

void SetRoundingPrecision(const unsigned int opcode)
{
B
bellard 已提交
101
    int rounding_precision;
102
   FPA11 *fpa11 = GET_FPA11();
103
#ifdef MAINTAIN_FPCR
104
   fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
105
#endif
106 107 108
   switch (opcode & MASK_ROUNDING_PRECISION)
   {
      case ROUND_SINGLE:
B
bellard 已提交
109
         rounding_precision = 32;
110
#ifdef MAINTAIN_FPCR
111
         fpa11->fpcr |= ROUND_SINGLE;
112
#endif
113
      break;
114

115
      case ROUND_DOUBLE:
B
bellard 已提交
116
         rounding_precision = 64;
117
#ifdef MAINTAIN_FPCR
118
         fpa11->fpcr |= ROUND_DOUBLE;
119
#endif
120
      break;
121

122
      case ROUND_EXTENDED:
B
bellard 已提交
123
         rounding_precision = 80;
124
#ifdef MAINTAIN_FPCR
125
         fpa11->fpcr |= ROUND_EXTENDED;
126
#endif
127
      break;
128

B
bellard 已提交
129
      default: rounding_precision = 80;
130
  }
B
bellard 已提交
131
   set_floatx80_rounding_precision(rounding_precision, &fpa11->fp_status);
132 133 134
}

/* Emulate the instruction in the opcode. */
P
pbrook 已提交
135 136
/* ??? This is not thread safe.  */
unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs)
137 138 139
{
  unsigned int nRc = 0;
//  unsigned long flags;
140
  FPA11 *fpa11;
141 142 143 144
//  save_flags(flags); sti();

  qemufpa=qfpa;
  user_registers=qregs;
145

146 147 148 149 150 151 152 153 154 155 156 157 158 159
#if 0
  fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n",
          opcode, qregs[REG_PC]);
#endif
  fpa11 = GET_FPA11();

  if (fpa11->initflag == 0)		/* good place for __builtin_expect */
  {
    resetFPA11();
    SetRoundingMode(ROUND_TO_NEAREST);
    SetRoundingPrecision(ROUND_EXTENDED);
    fpa11->initflag = 1;
  }

160 161
  set_float_exception_flags(0, &fpa11->fp_status);

162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
  if (TEST_OPCODE(opcode,MASK_CPRT))
  {
    //fprintf(stderr,"emulating CPRT\n");
    /* Emulate conversion opcodes. */
    /* Emulate register transfer opcodes. */
    /* Emulate comparison opcodes. */
    nRc = EmulateCPRT(opcode);
  }
  else if (TEST_OPCODE(opcode,MASK_CPDO))
  {
    //fprintf(stderr,"emulating CPDO\n");
    /* Emulate monadic arithmetic opcodes. */
    /* Emulate dyadic arithmetic opcodes. */
    nRc = EmulateCPDO(opcode);
  }
  else if (TEST_OPCODE(opcode,MASK_CPDT))
  {
    //fprintf(stderr,"emulating CPDT\n");
    /* Emulate load/store opcodes. */
    /* Emulate load/store multiple opcodes. */
    nRc = EmulateCPDT(opcode);
  }
  else
  {
    /* Invalid instruction detected.  Return FALSE. */
    nRc = 0;
  }

//  restore_flags(flags);
191 192 193
  if(nRc == 1 && get_float_exception_flags(&fpa11->fp_status))
  {
    //printf("fef 0x%x\n",float_exception_flags);
194
    nRc = -get_float_exception_flags(&fpa11->fp_status);
195
  }
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226

  //printf("returning %d\n",nRc);
  return(nRc);
}

#if 0
unsigned int EmulateAll1(unsigned int opcode)
{
  switch ((opcode >> 24) & 0xf)
  {
     case 0xc:
     case 0xd:
       if ((opcode >> 20) & 0x1)
       {
          switch ((opcode >> 8) & 0xf)
          {
             case 0x1: return PerformLDF(opcode); break;
             case 0x2: return PerformLFM(opcode); break;
             default: return 0;
          }
       }
       else
       {
          switch ((opcode >> 8) & 0xf)
          {
             case 0x1: return PerformSTF(opcode); break;
             case 0x2: return PerformSFM(opcode); break;
             default: return 0;
          }
      }
     break;
227

228
     case 0xe:
229 230 231 232 233
       if (opcode & 0x10)
         return EmulateCPDO(opcode);
       else
         return EmulateCPRT(opcode);
     break;
234

235 236 237 238
     default: return 0;
  }
}
#endif