1 /* 2 * TriCore emulation for qemu: fpu helper. 3 * 4 * Copyright (c) 2016 Bastian Koppelmann University of Paderborn 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include "qemu/osdep.h" 21 #include "cpu.h" 22 #include "exec/helper-proto.h" 23 24 #define ADD_NAN 0x7cf00001 25 #define DIV_NAN 0x7fc00008 26 #define MUL_NAN 0x7fc00002 27 #define FPU_FS PSW_USB_C 28 #define FPU_FI PSW_USB_V 29 #define FPU_FV PSW_USB_SV 30 #define FPU_FZ PSW_USB_AV 31 #define FPU_FU PSW_USB_SAV 32 33 /* we don't care about input_denormal */ 34 static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) 35 { 36 return get_float_exception_flags(&env->fp_status) 37 & (float_flag_invalid 38 | float_flag_overflow 39 | float_flag_underflow 40 | float_flag_output_denormal 41 | float_flag_divbyzero 42 | float_flag_inexact); 43 } 44 45 static inline bool f_is_denormal(float32 arg) 46 { 47 return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); 48 } 49 50 static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) 51 { 52 uint8_t some_excp = 0; 53 set_float_exception_flags(0, &env->fp_status); 54 55 if (flags & float_flag_invalid) { 56 env->FPU_FI = 1 << 31; 57 some_excp = 1; 58 } 59 60 if (flags & float_flag_overflow) { 61 env->FPU_FV = 1 << 31; 62 some_excp = 1; 63 } 64 65 if (flags & float_flag_underflow || flags & float_flag_output_denormal) { 66 env->FPU_FU = 1 << 31; 67 some_excp = 1; 68 } 69 70 if (flags & float_flag_divbyzero) { 71 env->FPU_FZ = 1 << 31; 72 some_excp = 1; 73 } 74 75 if (flags & float_flag_inexact || flags & float_flag_output_denormal) { 76 env->PSW |= 1 << 26; 77 some_excp = 1; 78 } 79 80 env->FPU_FS = some_excp; 81 } 82 83 #define FADD_SUB(op) \ 84 uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ 85 { \ 86 float32 arg1 = make_float32(r1); \ 87 float32 arg2 = make_float32(r2); \ 88 uint32_t flags; \ 89 float32 f_result; \ 90 \ 91 f_result = float32_##op(arg2, arg1, &env->fp_status); \ 92 flags = f_get_excp_flags(env); \ 93 if (flags) { \ 94 /* If the output is a NaN, but the inputs aren't, \ 95 we return a unique value. */ \ 96 if ((flags & float_flag_invalid) \ 97 && !float32_is_any_nan(arg1) \ 98 && !float32_is_any_nan(arg2)) { \ 99 f_result = ADD_NAN; \ 100 } \ 101 f_update_psw_flags(env, flags); \ 102 } else { \ 103 env->FPU_FS = 0; \ 104 } \ 105 return (uint32_t)f_result; \ 106 } 107 FADD_SUB(add) 108 FADD_SUB(sub) 109 110 uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 111 { 112 uint32_t flags; 113 float32 arg1 = make_float32(r1); 114 float32 arg2 = make_float32(r2); 115 float32 f_result; 116 117 f_result = float32_mul(arg1, arg2, &env->fp_status); 118 119 flags = f_get_excp_flags(env); 120 if (flags) { 121 /* If the output is a NaN, but the inputs aren't, 122 we return a unique value. */ 123 if ((flags & float_flag_invalid) 124 && !float32_is_any_nan(arg1) 125 && !float32_is_any_nan(arg2)) { 126 f_result = MUL_NAN; 127 } 128 f_update_psw_flags(env, flags); 129 } else { 130 env->FPU_FS = 0; 131 } 132 return (uint32_t)f_result; 133 134 } 135 136 uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 137 { 138 uint32_t flags; 139 float32 arg1 = make_float32(r1); 140 float32 arg2 = make_float32(r2); 141 float32 f_result; 142 143 f_result = float32_div(arg1, arg2 , &env->fp_status); 144 145 flags = f_get_excp_flags(env); 146 if (flags) { 147 /* If the output is a NaN, but the inputs aren't, 148 we return a unique value. */ 149 if ((flags & float_flag_invalid) 150 && !float32_is_any_nan(arg1) 151 && !float32_is_any_nan(arg2)) { 152 f_result = DIV_NAN; 153 } 154 f_update_psw_flags(env, flags); 155 } else { 156 env->FPU_FS = 0; 157 } 158 159 return (uint32_t)f_result; 160 } 161 162 uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 163 { 164 uint32_t result, flags; 165 float32 arg1 = make_float32(r1); 166 float32 arg2 = make_float32(r2); 167 168 set_flush_inputs_to_zero(0, &env->fp_status); 169 170 result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); 171 result |= f_is_denormal(arg1) << 4; 172 result |= f_is_denormal(arg2) << 5; 173 174 flags = f_get_excp_flags(env); 175 if (flags) { 176 f_update_psw_flags(env, flags); 177 } else { 178 env->FPU_FS = 0; 179 } 180 181 set_flush_inputs_to_zero(1, &env->fp_status); 182 return result; 183 } 184 185 uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) 186 { 187 float32 f_arg = make_float32(arg); 188 int32_t result, flags; 189 190 result = float32_to_int32(f_arg, &env->fp_status); 191 192 flags = f_get_excp_flags(env); 193 if (flags) { 194 if (float32_is_any_nan(f_arg)) { 195 result = 0; 196 } 197 f_update_psw_flags(env, flags); 198 } else { 199 env->FPU_FS = 0; 200 } 201 return (uint32_t)result; 202 } 203 204 uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) 205 { 206 float32 f_result; 207 uint32_t flags; 208 f_result = int32_to_float32(arg, &env->fp_status); 209 210 flags = f_get_excp_flags(env); 211 if (flags) { 212 f_update_psw_flags(env, flags); 213 } else { 214 env->FPU_FS = 0; 215 } 216 return (uint32_t)f_result; 217 } 218 219 uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) 220 { 221 float32 f_arg = make_float32(arg); 222 uint32_t result; 223 int32_t flags; 224 225 result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status); 226 227 flags = f_get_excp_flags(env); 228 if (flags & float_flag_invalid) { 229 flags &= ~float_flag_inexact; 230 if (float32_is_any_nan(f_arg)) { 231 result = 0; 232 } 233 } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { 234 flags = float_flag_invalid; 235 result = 0; 236 } 237 238 if (flags) { 239 f_update_psw_flags(env, flags); 240 } else { 241 env->FPU_FS = 0; 242 } 243 return result; 244 } 245