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