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