xref: /qemu/linux-user/arm/nwfpe/fpa11.c (revision 70ce076fa6dff60585c229a4b641b13e64bf03cf)
1 /*
2     NetWinder Floating Point Emulator
3     (c) Rebel.COM, 1998,1999
4 
5     Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
6 
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11 
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15     GNU General Public License for more details.
16 
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, see <http://www.gnu.org/licenses/>.
19 */
20 
21 #include "qemu/osdep.h"
22 #include "fpa11.h"
23 
24 #include "fpopcode.h"
25 
26 //#include "fpmodule.h"
27 //#include "fpmodule.inl"
28 
29 //#include <asm/system.h>
30 
31 
32 FPA11* qemufpa = NULL;
33 CPUARMState* user_registers;
34 
35 /* Reset the FPA11 chip.  Called to initialize and reset the emulator. */
36 void resetFPA11(void)
37 {
38   int i;
39   FPA11 *fpa11 = GET_FPA11();
40 
41   /* initialize the register type array */
42   for (i=0;i<=7;i++)
43   {
44     fpa11->fType[i] = typeNone;
45   }
46 
47   /* FPSR: set system id to FP_EMULATOR, set AC, clear all other bits */
48   fpa11->fpsr = FP_EMULATOR | BIT_AC;
49 
50   /* FPCR: set SB, AB and DA bits, clear all others */
51 #ifdef MAINTAIN_FPCR
52   fpa11->fpcr = MASK_RESET;
53 #endif
54 
55   /*
56    * Real FPA11 hardware does not handle NaNs, but always takes an
57    * exception for them to be software-emulated (ARM7500FE datasheet
58    * section 10.4). There is no documented architectural requirement
59    * for NaN propagation rules and it will depend on how the OS
60    * level software emulation opted to do it. We here use prop_s_ab
61    * which matches the later VFP hardware choice and how QEMU's
62    * fpa11 emulation has worked in the past. The real Linux kernel
63    * does something slightly different: arch/arm/nwfpe/softfloat-specialize
64    * propagateFloat64NaN() has the curious behaviour that it prefers
65    * the QNaN over the SNaN, but if both are QNaN it picks A and
66    * if both are SNaN it picks B. In theory we could add this as
67    * a NaN propagation rule, but in practice FPA11 emulation is so
68    * close to totally dead that it's not worth trying to match it at
69    * this late date.
70    */
71   set_float_2nan_prop_rule(float_2nan_prop_s_ab, &fpa11->fp_status);
72   /*
73    * Use the same default NaN value as Arm VFP. This doesn't match
74    * the Linux kernel's nwfpe emulation, which uses an all-1s value.
75    */
76   set_float_default_nan_pattern(0b01000000, &fpa11->fp_status);
77 }
78 
79 void SetRoundingMode(const unsigned int opcode)
80 {
81     int rounding_mode;
82    FPA11 *fpa11 = GET_FPA11();
83 
84 #ifdef MAINTAIN_FPCR
85    fpa11->fpcr &= ~MASK_ROUNDING_MODE;
86 #endif
87    switch (opcode & MASK_ROUNDING_MODE)
88    {
89       default:
90       case ROUND_TO_NEAREST:
91          rounding_mode = float_round_nearest_even;
92 #ifdef MAINTAIN_FPCR
93          fpa11->fpcr |= ROUND_TO_NEAREST;
94 #endif
95       break;
96 
97       case ROUND_TO_PLUS_INFINITY:
98          rounding_mode = float_round_up;
99 #ifdef MAINTAIN_FPCR
100          fpa11->fpcr |= ROUND_TO_PLUS_INFINITY;
101 #endif
102       break;
103 
104       case ROUND_TO_MINUS_INFINITY:
105          rounding_mode = float_round_down;
106 #ifdef MAINTAIN_FPCR
107          fpa11->fpcr |= ROUND_TO_MINUS_INFINITY;
108 #endif
109       break;
110 
111       case ROUND_TO_ZERO:
112          rounding_mode = float_round_to_zero;
113 #ifdef MAINTAIN_FPCR
114          fpa11->fpcr |= ROUND_TO_ZERO;
115 #endif
116       break;
117   }
118    set_float_rounding_mode(rounding_mode, &fpa11->fp_status);
119 }
120 
121 void SetRoundingPrecision(const unsigned int opcode)
122 {
123     FloatX80RoundPrec rounding_precision;
124     FPA11 *fpa11 = GET_FPA11();
125 #ifdef MAINTAIN_FPCR
126     fpa11->fpcr &= ~MASK_ROUNDING_PRECISION;
127 #endif
128     switch (opcode & MASK_ROUNDING_PRECISION) {
129     case ROUND_SINGLE:
130         rounding_precision = floatx80_precision_s;
131 #ifdef MAINTAIN_FPCR
132         fpa11->fpcr |= ROUND_SINGLE;
133 #endif
134         break;
135 
136     case ROUND_DOUBLE:
137         rounding_precision = floatx80_precision_d;
138 #ifdef MAINTAIN_FPCR
139         fpa11->fpcr |= ROUND_DOUBLE;
140 #endif
141         break;
142 
143     case ROUND_EXTENDED:
144         rounding_precision = floatx80_precision_x;
145 #ifdef MAINTAIN_FPCR
146         fpa11->fpcr |= ROUND_EXTENDED;
147 #endif
148         break;
149 
150     default:
151         rounding_precision = floatx80_precision_x;
152         break;
153     }
154     set_floatx80_rounding_precision(rounding_precision, &fpa11->fp_status);
155 }
156 
157 /* Emulate the instruction in the opcode. */
158 /* ??? This is not thread safe.  */
159 unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs)
160 {
161   unsigned int nRc = 0;
162 //  unsigned long flags;
163   FPA11 *fpa11;
164   unsigned int cp;
165 //  save_flags(flags); sti();
166 
167   /* Check that this is really an FPA11 instruction: the coprocessor
168    * field in bits [11:8] must be 1 or 2.
169    */
170   cp = (opcode >> 8) & 0xf;
171   if (cp != 1 && cp != 2) {
172     return 0;
173   }
174 
175   qemufpa=qfpa;
176   user_registers=qregs;
177 
178 #if 0
179   fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n",
180           opcode, qregs[ARM_REG_PC]);
181 #endif
182   fpa11 = GET_FPA11();
183 
184   if (fpa11->initflag == 0)		/* good place for __builtin_expect */
185   {
186     resetFPA11();
187     SetRoundingMode(ROUND_TO_NEAREST);
188     SetRoundingPrecision(ROUND_EXTENDED);
189     fpa11->initflag = 1;
190   }
191 
192   set_float_exception_flags(0, &fpa11->fp_status);
193 
194   if (TEST_OPCODE(opcode,MASK_CPRT))
195   {
196     //fprintf(stderr,"emulating CPRT\n");
197     /* Emulate conversion opcodes. */
198     /* Emulate register transfer opcodes. */
199     /* Emulate comparison opcodes. */
200     nRc = EmulateCPRT(opcode);
201   }
202   else if (TEST_OPCODE(opcode,MASK_CPDO))
203   {
204     //fprintf(stderr,"emulating CPDO\n");
205     /* Emulate monadic arithmetic opcodes. */
206     /* Emulate dyadic arithmetic opcodes. */
207     nRc = EmulateCPDO(opcode);
208   }
209   else if (TEST_OPCODE(opcode,MASK_CPDT))
210   {
211     //fprintf(stderr,"emulating CPDT\n");
212     /* Emulate load/store opcodes. */
213     /* Emulate load/store multiple opcodes. */
214     nRc = EmulateCPDT(opcode);
215   }
216   else
217   {
218     /* Invalid instruction detected.  Return FALSE. */
219     nRc = 0;
220   }
221 
222 //  restore_flags(flags);
223   if(nRc == 1 && get_float_exception_flags(&fpa11->fp_status))
224   {
225     //printf("fef 0x%x\n",float_exception_flags);
226     nRc = -get_float_exception_flags(&fpa11->fp_status);
227   }
228 
229   //printf("returning %d\n",nRc);
230   return(nRc);
231 }
232 
233 #if 0
234 unsigned int EmulateAll1(unsigned int opcode)
235 {
236   switch ((opcode >> 24) & 0xf)
237   {
238      case 0xc:
239      case 0xd:
240        if ((opcode >> 20) & 0x1)
241        {
242           switch ((opcode >> 8) & 0xf)
243           {
244              case 0x1: return PerformLDF(opcode); break;
245              case 0x2: return PerformLFM(opcode); break;
246              default: return 0;
247           }
248        }
249        else
250        {
251           switch ((opcode >> 8) & 0xf)
252           {
253              case 0x1: return PerformSTF(opcode); break;
254              case 0x2: return PerformSFM(opcode); break;
255              default: return 0;
256           }
257       }
258      break;
259 
260      case 0xe:
261        if (opcode & 0x10)
262          return EmulateCPDO(opcode);
263        else
264          return EmulateCPRT(opcode);
265      break;
266 
267      default: return 0;
268   }
269 }
270 #endif
271