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 74 void SetRoundingMode(const unsigned int opcode) 75 { 76 int rounding_mode; 77 FPA11 *fpa11 = GET_FPA11(); 78 79 #ifdef MAINTAIN_FPCR 80 fpa11->fpcr &= ~MASK_ROUNDING_MODE; 81 #endif 82 switch (opcode & MASK_ROUNDING_MODE) 83 { 84 default: 85 case ROUND_TO_NEAREST: 86 rounding_mode = float_round_nearest_even; 87 #ifdef MAINTAIN_FPCR 88 fpa11->fpcr |= ROUND_TO_NEAREST; 89 #endif 90 break; 91 92 case ROUND_TO_PLUS_INFINITY: 93 rounding_mode = float_round_up; 94 #ifdef MAINTAIN_FPCR 95 fpa11->fpcr |= ROUND_TO_PLUS_INFINITY; 96 #endif 97 break; 98 99 case ROUND_TO_MINUS_INFINITY: 100 rounding_mode = float_round_down; 101 #ifdef MAINTAIN_FPCR 102 fpa11->fpcr |= ROUND_TO_MINUS_INFINITY; 103 #endif 104 break; 105 106 case ROUND_TO_ZERO: 107 rounding_mode = float_round_to_zero; 108 #ifdef MAINTAIN_FPCR 109 fpa11->fpcr |= ROUND_TO_ZERO; 110 #endif 111 break; 112 } 113 set_float_rounding_mode(rounding_mode, &fpa11->fp_status); 114 } 115 116 void SetRoundingPrecision(const unsigned int opcode) 117 { 118 FloatX80RoundPrec rounding_precision; 119 FPA11 *fpa11 = GET_FPA11(); 120 #ifdef MAINTAIN_FPCR 121 fpa11->fpcr &= ~MASK_ROUNDING_PRECISION; 122 #endif 123 switch (opcode & MASK_ROUNDING_PRECISION) { 124 case ROUND_SINGLE: 125 rounding_precision = floatx80_precision_s; 126 #ifdef MAINTAIN_FPCR 127 fpa11->fpcr |= ROUND_SINGLE; 128 #endif 129 break; 130 131 case ROUND_DOUBLE: 132 rounding_precision = floatx80_precision_d; 133 #ifdef MAINTAIN_FPCR 134 fpa11->fpcr |= ROUND_DOUBLE; 135 #endif 136 break; 137 138 case ROUND_EXTENDED: 139 rounding_precision = floatx80_precision_x; 140 #ifdef MAINTAIN_FPCR 141 fpa11->fpcr |= ROUND_EXTENDED; 142 #endif 143 break; 144 145 default: 146 rounding_precision = floatx80_precision_x; 147 break; 148 } 149 set_floatx80_rounding_precision(rounding_precision, &fpa11->fp_status); 150 } 151 152 /* Emulate the instruction in the opcode. */ 153 /* ??? This is not thread safe. */ 154 unsigned int EmulateAll(unsigned int opcode, FPA11* qfpa, CPUARMState* qregs) 155 { 156 unsigned int nRc = 0; 157 // unsigned long flags; 158 FPA11 *fpa11; 159 unsigned int cp; 160 // save_flags(flags); sti(); 161 162 /* Check that this is really an FPA11 instruction: the coprocessor 163 * field in bits [11:8] must be 1 or 2. 164 */ 165 cp = (opcode >> 8) & 0xf; 166 if (cp != 1 && cp != 2) { 167 return 0; 168 } 169 170 qemufpa=qfpa; 171 user_registers=qregs; 172 173 #if 0 174 fprintf(stderr,"emulating FP insn 0x%08x, PC=0x%08x\n", 175 opcode, qregs[ARM_REG_PC]); 176 #endif 177 fpa11 = GET_FPA11(); 178 179 if (fpa11->initflag == 0) /* good place for __builtin_expect */ 180 { 181 resetFPA11(); 182 SetRoundingMode(ROUND_TO_NEAREST); 183 SetRoundingPrecision(ROUND_EXTENDED); 184 fpa11->initflag = 1; 185 } 186 187 set_float_exception_flags(0, &fpa11->fp_status); 188 189 if (TEST_OPCODE(opcode,MASK_CPRT)) 190 { 191 //fprintf(stderr,"emulating CPRT\n"); 192 /* Emulate conversion opcodes. */ 193 /* Emulate register transfer opcodes. */ 194 /* Emulate comparison opcodes. */ 195 nRc = EmulateCPRT(opcode); 196 } 197 else if (TEST_OPCODE(opcode,MASK_CPDO)) 198 { 199 //fprintf(stderr,"emulating CPDO\n"); 200 /* Emulate monadic arithmetic opcodes. */ 201 /* Emulate dyadic arithmetic opcodes. */ 202 nRc = EmulateCPDO(opcode); 203 } 204 else if (TEST_OPCODE(opcode,MASK_CPDT)) 205 { 206 //fprintf(stderr,"emulating CPDT\n"); 207 /* Emulate load/store opcodes. */ 208 /* Emulate load/store multiple opcodes. */ 209 nRc = EmulateCPDT(opcode); 210 } 211 else 212 { 213 /* Invalid instruction detected. Return FALSE. */ 214 nRc = 0; 215 } 216 217 // restore_flags(flags); 218 if(nRc == 1 && get_float_exception_flags(&fpa11->fp_status)) 219 { 220 //printf("fef 0x%x\n",float_exception_flags); 221 nRc = -get_float_exception_flags(&fpa11->fp_status); 222 } 223 224 //printf("returning %d\n",nRc); 225 return(nRc); 226 } 227 228 #if 0 229 unsigned int EmulateAll1(unsigned int opcode) 230 { 231 switch ((opcode >> 24) & 0xf) 232 { 233 case 0xc: 234 case 0xd: 235 if ((opcode >> 20) & 0x1) 236 { 237 switch ((opcode >> 8) & 0xf) 238 { 239 case 0x1: return PerformLDF(opcode); break; 240 case 0x2: return PerformLFM(opcode); break; 241 default: return 0; 242 } 243 } 244 else 245 { 246 switch ((opcode >> 8) & 0xf) 247 { 248 case 0x1: return PerformSTF(opcode); break; 249 case 0x2: return PerformSFM(opcode); break; 250 default: return 0; 251 } 252 } 253 break; 254 255 case 0xe: 256 if (opcode & 0x10) 257 return EmulateCPDO(opcode); 258 else 259 return EmulateCPRT(opcode); 260 break; 261 262 default: return 0; 263 } 264 } 265 #endif 266