1 /* 2 * m68k FPU helpers 3 * 4 * Copyright (c) 2006-2007 CodeSourcery 5 * Written by Paul Brook 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2 of the License, or (at your option) any later version. 11 * 12 * This library 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 GNU 15 * General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 #include "qemu/osdep.h" 22 #include "cpu.h" 23 #include "exec/helper-proto.h" 24 #include "exec/exec-all.h" 25 26 int32_t HELPER(reds32)(CPUM68KState *env, FPReg *val) 27 { 28 return floatx80_to_int32(val->d, &env->fp_status); 29 } 30 31 float32 HELPER(redf32)(CPUM68KState *env, FPReg *val) 32 { 33 return floatx80_to_float32(val->d, &env->fp_status); 34 } 35 36 void HELPER(exts32)(CPUM68KState *env, FPReg *res, int32_t val) 37 { 38 res->d = int32_to_floatx80(val, &env->fp_status); 39 } 40 41 void HELPER(extf32)(CPUM68KState *env, FPReg *res, float32 val) 42 { 43 res->d = float32_to_floatx80(val, &env->fp_status); 44 } 45 46 void HELPER(extf64)(CPUM68KState *env, FPReg *res, float64 val) 47 { 48 res->d = float64_to_floatx80(val, &env->fp_status); 49 } 50 51 float64 HELPER(redf64)(CPUM68KState *env, FPReg *val) 52 { 53 return floatx80_to_float64(val->d, &env->fp_status); 54 } 55 56 void HELPER(firound)(CPUM68KState *env, FPReg *res, FPReg *val) 57 { 58 res->d = floatx80_round_to_int(val->d, &env->fp_status); 59 } 60 61 static void m68k_restore_precision_mode(CPUM68KState *env) 62 { 63 switch (env->fpcr & FPCR_PREC_MASK) { 64 case FPCR_PREC_X: /* extended */ 65 set_floatx80_rounding_precision(80, &env->fp_status); 66 break; 67 case FPCR_PREC_S: /* single */ 68 set_floatx80_rounding_precision(32, &env->fp_status); 69 break; 70 case FPCR_PREC_D: /* double */ 71 set_floatx80_rounding_precision(64, &env->fp_status); 72 break; 73 case FPCR_PREC_U: /* undefined */ 74 default: 75 break; 76 } 77 } 78 79 static void cf_restore_precision_mode(CPUM68KState *env) 80 { 81 if (env->fpcr & FPCR_PREC_S) { /* single */ 82 set_floatx80_rounding_precision(32, &env->fp_status); 83 } else { /* double */ 84 set_floatx80_rounding_precision(64, &env->fp_status); 85 } 86 } 87 88 static void restore_rounding_mode(CPUM68KState *env) 89 { 90 switch (env->fpcr & FPCR_RND_MASK) { 91 case FPCR_RND_N: /* round to nearest */ 92 set_float_rounding_mode(float_round_nearest_even, &env->fp_status); 93 break; 94 case FPCR_RND_Z: /* round to zero */ 95 set_float_rounding_mode(float_round_to_zero, &env->fp_status); 96 break; 97 case FPCR_RND_M: /* round toward minus infinity */ 98 set_float_rounding_mode(float_round_down, &env->fp_status); 99 break; 100 case FPCR_RND_P: /* round toward positive infinity */ 101 set_float_rounding_mode(float_round_up, &env->fp_status); 102 break; 103 } 104 } 105 106 void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val) 107 { 108 env->fpcr = val & 0xffff; 109 110 if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { 111 cf_restore_precision_mode(env); 112 } else { 113 m68k_restore_precision_mode(env); 114 } 115 restore_rounding_mode(env); 116 } 117 118 void HELPER(fitrunc)(CPUM68KState *env, FPReg *res, FPReg *val) 119 { 120 int rounding_mode = get_float_rounding_mode(&env->fp_status); 121 set_float_rounding_mode(float_round_to_zero, &env->fp_status); 122 res->d = floatx80_round_to_int(val->d, &env->fp_status); 123 set_float_rounding_mode(rounding_mode, &env->fp_status); 124 } 125 126 void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val) 127 { 128 cpu_m68k_set_fpcr(env, val); 129 } 130 131 void HELPER(fsqrt)(CPUM68KState *env, FPReg *res, FPReg *val) 132 { 133 res->d = floatx80_sqrt(val->d, &env->fp_status); 134 } 135 136 void HELPER(fabs)(CPUM68KState *env, FPReg *res, FPReg *val) 137 { 138 res->d = floatx80_abs(val->d); 139 } 140 141 void HELPER(fchs)(CPUM68KState *env, FPReg *res, FPReg *val) 142 { 143 res->d = floatx80_chs(val->d); 144 } 145 146 void HELPER(fadd)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 147 { 148 res->d = floatx80_add(val0->d, val1->d, &env->fp_status); 149 } 150 151 void HELPER(fsub)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 152 { 153 res->d = floatx80_sub(val1->d, val0->d, &env->fp_status); 154 } 155 156 void HELPER(fmul)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 157 { 158 res->d = floatx80_mul(val0->d, val1->d, &env->fp_status); 159 } 160 161 void HELPER(fdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) 162 { 163 res->d = floatx80_div(val1->d, val0->d, &env->fp_status); 164 } 165 166 static int float_comp_to_cc(int float_compare) 167 { 168 switch (float_compare) { 169 case float_relation_equal: 170 return FPSR_CC_Z; 171 case float_relation_less: 172 return FPSR_CC_N; 173 case float_relation_unordered: 174 return FPSR_CC_A; 175 case float_relation_greater: 176 return 0; 177 default: 178 g_assert_not_reached(); 179 } 180 } 181 182 void HELPER(fcmp)(CPUM68KState *env, FPReg *val0, FPReg *val1) 183 { 184 int float_compare; 185 186 float_compare = floatx80_compare(val1->d, val0->d, &env->fp_status); 187 env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare); 188 } 189 190 void HELPER(ftst)(CPUM68KState *env, FPReg *val) 191 { 192 uint32_t cc = 0; 193 194 if (floatx80_is_neg(val->d)) { 195 cc |= FPSR_CC_N; 196 } 197 198 if (floatx80_is_any_nan(val->d)) { 199 cc |= FPSR_CC_A; 200 } else if (floatx80_is_infinity(val->d)) { 201 cc |= FPSR_CC_I; 202 } else if (floatx80_is_zero(val->d)) { 203 cc |= FPSR_CC_Z; 204 } 205 env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | cc; 206 } 207