1996a729fSBastian Koppelmann /* 2996a729fSBastian Koppelmann * TriCore emulation for qemu: fpu helper. 3996a729fSBastian Koppelmann * 4996a729fSBastian Koppelmann * Copyright (c) 2016 Bastian Koppelmann University of Paderborn 5996a729fSBastian Koppelmann * 6996a729fSBastian Koppelmann * This library is free software; you can redistribute it and/or 7996a729fSBastian Koppelmann * modify it under the terms of the GNU Lesser General Public 8996a729fSBastian Koppelmann * License as published by the Free Software Foundation; either 9996a729fSBastian Koppelmann * version 2 of the License, or (at your option) any later version. 10996a729fSBastian Koppelmann * 11996a729fSBastian Koppelmann * This library is distributed in the hope that it will be useful, 12996a729fSBastian Koppelmann * but WITHOUT ANY WARRANTY; without even the implied warranty of 13996a729fSBastian Koppelmann * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14996a729fSBastian Koppelmann * Lesser General Public License for more details. 15996a729fSBastian Koppelmann * 16996a729fSBastian Koppelmann * You should have received a copy of the GNU Lesser General Public 17996a729fSBastian Koppelmann * License along with this library; if not, see <http://www.gnu.org/licenses/>. 18996a729fSBastian Koppelmann */ 19996a729fSBastian Koppelmann 20996a729fSBastian Koppelmann #include "qemu/osdep.h" 21996a729fSBastian Koppelmann #include "cpu.h" 22996a729fSBastian Koppelmann #include "exec/helper-proto.h" 23*24f91e81SAlex Bennée #include "fpu/softfloat.h" 24996a729fSBastian Koppelmann 25ddd7feadSBastian Koppelmann #define QUIET_NAN 0x7fc00000 26ddd7feadSBastian Koppelmann #define ADD_NAN 0x7fc00001 27996a729fSBastian Koppelmann #define DIV_NAN 0x7fc00008 28996a729fSBastian Koppelmann #define MUL_NAN 0x7fc00002 29996a729fSBastian Koppelmann #define FPU_FS PSW_USB_C 30996a729fSBastian Koppelmann #define FPU_FI PSW_USB_V 31996a729fSBastian Koppelmann #define FPU_FV PSW_USB_SV 32996a729fSBastian Koppelmann #define FPU_FZ PSW_USB_AV 33996a729fSBastian Koppelmann #define FPU_FU PSW_USB_SAV 34996a729fSBastian Koppelmann 35996a729fSBastian Koppelmann /* we don't care about input_denormal */ 36996a729fSBastian Koppelmann static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) 37996a729fSBastian Koppelmann { 38996a729fSBastian Koppelmann return get_float_exception_flags(&env->fp_status) 39996a729fSBastian Koppelmann & (float_flag_invalid 40996a729fSBastian Koppelmann | float_flag_overflow 41996a729fSBastian Koppelmann | float_flag_underflow 42996a729fSBastian Koppelmann | float_flag_output_denormal 43996a729fSBastian Koppelmann | float_flag_divbyzero 44996a729fSBastian Koppelmann | float_flag_inexact); 45996a729fSBastian Koppelmann } 46996a729fSBastian Koppelmann 47996a729fSBastian Koppelmann static inline bool f_is_denormal(float32 arg) 48996a729fSBastian Koppelmann { 49996a729fSBastian Koppelmann return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); 50996a729fSBastian Koppelmann } 51996a729fSBastian Koppelmann 52ddd7feadSBastian Koppelmann static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2, 53ddd7feadSBastian Koppelmann float32 arg3, float32 result, 54ddd7feadSBastian Koppelmann uint32_t muladd_negate_c) 55ddd7feadSBastian Koppelmann { 56ddd7feadSBastian Koppelmann uint32_t aSign, bSign, cSign; 57ddd7feadSBastian Koppelmann uint32_t aExp, bExp, cExp; 58ddd7feadSBastian Koppelmann 59ddd7feadSBastian Koppelmann if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) || 60ddd7feadSBastian Koppelmann float32_is_any_nan(arg3)) { 61ddd7feadSBastian Koppelmann return QUIET_NAN; 62ddd7feadSBastian Koppelmann } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) { 63ddd7feadSBastian Koppelmann return MUL_NAN; 64ddd7feadSBastian Koppelmann } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) { 65ddd7feadSBastian Koppelmann return MUL_NAN; 66ddd7feadSBastian Koppelmann } else { 67ddd7feadSBastian Koppelmann aSign = arg1 >> 31; 68ddd7feadSBastian Koppelmann bSign = arg2 >> 31; 69ddd7feadSBastian Koppelmann cSign = arg3 >> 31; 70ddd7feadSBastian Koppelmann 71ddd7feadSBastian Koppelmann aExp = (arg1 >> 23) & 0xff; 72ddd7feadSBastian Koppelmann bExp = (arg2 >> 23) & 0xff; 73ddd7feadSBastian Koppelmann cExp = (arg3 >> 23) & 0xff; 74ddd7feadSBastian Koppelmann 75ddd7feadSBastian Koppelmann if (muladd_negate_c) { 76ddd7feadSBastian Koppelmann cSign ^= 1; 77ddd7feadSBastian Koppelmann } 78ddd7feadSBastian Koppelmann if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) { 79ddd7feadSBastian Koppelmann if (aSign ^ bSign ^ cSign) { 80ddd7feadSBastian Koppelmann return ADD_NAN; 81ddd7feadSBastian Koppelmann } 82ddd7feadSBastian Koppelmann } 83ddd7feadSBastian Koppelmann } 84ddd7feadSBastian Koppelmann 85ddd7feadSBastian Koppelmann return result; 86ddd7feadSBastian Koppelmann } 87ddd7feadSBastian Koppelmann 88baf410dcSBastian Koppelmann static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) 89996a729fSBastian Koppelmann { 90996a729fSBastian Koppelmann uint8_t some_excp = 0; 91996a729fSBastian Koppelmann set_float_exception_flags(0, &env->fp_status); 92996a729fSBastian Koppelmann 93996a729fSBastian Koppelmann if (flags & float_flag_invalid) { 94996a729fSBastian Koppelmann env->FPU_FI = 1 << 31; 95996a729fSBastian Koppelmann some_excp = 1; 96996a729fSBastian Koppelmann } 97996a729fSBastian Koppelmann 98996a729fSBastian Koppelmann if (flags & float_flag_overflow) { 99996a729fSBastian Koppelmann env->FPU_FV = 1 << 31; 100996a729fSBastian Koppelmann some_excp = 1; 101996a729fSBastian Koppelmann } 102996a729fSBastian Koppelmann 103996a729fSBastian Koppelmann if (flags & float_flag_underflow || flags & float_flag_output_denormal) { 104996a729fSBastian Koppelmann env->FPU_FU = 1 << 31; 105996a729fSBastian Koppelmann some_excp = 1; 106996a729fSBastian Koppelmann } 107996a729fSBastian Koppelmann 108996a729fSBastian Koppelmann if (flags & float_flag_divbyzero) { 109996a729fSBastian Koppelmann env->FPU_FZ = 1 << 31; 110996a729fSBastian Koppelmann some_excp = 1; 111996a729fSBastian Koppelmann } 112996a729fSBastian Koppelmann 113996a729fSBastian Koppelmann if (flags & float_flag_inexact || flags & float_flag_output_denormal) { 114996a729fSBastian Koppelmann env->PSW |= 1 << 26; 115996a729fSBastian Koppelmann some_excp = 1; 116996a729fSBastian Koppelmann } 117996a729fSBastian Koppelmann 118996a729fSBastian Koppelmann env->FPU_FS = some_excp; 119996a729fSBastian Koppelmann } 120baf410dcSBastian Koppelmann 121baf410dcSBastian Koppelmann #define FADD_SUB(op) \ 122baf410dcSBastian Koppelmann uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ 123baf410dcSBastian Koppelmann { \ 124baf410dcSBastian Koppelmann float32 arg1 = make_float32(r1); \ 125baf410dcSBastian Koppelmann float32 arg2 = make_float32(r2); \ 126baf410dcSBastian Koppelmann uint32_t flags; \ 127baf410dcSBastian Koppelmann float32 f_result; \ 128baf410dcSBastian Koppelmann \ 129baf410dcSBastian Koppelmann f_result = float32_##op(arg2, arg1, &env->fp_status); \ 130baf410dcSBastian Koppelmann flags = f_get_excp_flags(env); \ 131baf410dcSBastian Koppelmann if (flags) { \ 132baf410dcSBastian Koppelmann /* If the output is a NaN, but the inputs aren't, \ 133baf410dcSBastian Koppelmann we return a unique value. */ \ 134baf410dcSBastian Koppelmann if ((flags & float_flag_invalid) \ 135baf410dcSBastian Koppelmann && !float32_is_any_nan(arg1) \ 136baf410dcSBastian Koppelmann && !float32_is_any_nan(arg2)) { \ 137baf410dcSBastian Koppelmann f_result = ADD_NAN; \ 138baf410dcSBastian Koppelmann } \ 139baf410dcSBastian Koppelmann f_update_psw_flags(env, flags); \ 140baf410dcSBastian Koppelmann } else { \ 141baf410dcSBastian Koppelmann env->FPU_FS = 0; \ 142baf410dcSBastian Koppelmann } \ 143baf410dcSBastian Koppelmann return (uint32_t)f_result; \ 144baf410dcSBastian Koppelmann } 145baf410dcSBastian Koppelmann FADD_SUB(add) 146baf410dcSBastian Koppelmann FADD_SUB(sub) 147daab3f7fSBastian Koppelmann 148daab3f7fSBastian Koppelmann uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 149daab3f7fSBastian Koppelmann { 150daab3f7fSBastian Koppelmann uint32_t flags; 151daab3f7fSBastian Koppelmann float32 arg1 = make_float32(r1); 152daab3f7fSBastian Koppelmann float32 arg2 = make_float32(r2); 153daab3f7fSBastian Koppelmann float32 f_result; 154daab3f7fSBastian Koppelmann 155daab3f7fSBastian Koppelmann f_result = float32_mul(arg1, arg2, &env->fp_status); 156daab3f7fSBastian Koppelmann 157daab3f7fSBastian Koppelmann flags = f_get_excp_flags(env); 158daab3f7fSBastian Koppelmann if (flags) { 159daab3f7fSBastian Koppelmann /* If the output is a NaN, but the inputs aren't, 160daab3f7fSBastian Koppelmann we return a unique value. */ 161daab3f7fSBastian Koppelmann if ((flags & float_flag_invalid) 162daab3f7fSBastian Koppelmann && !float32_is_any_nan(arg1) 163daab3f7fSBastian Koppelmann && !float32_is_any_nan(arg2)) { 164daab3f7fSBastian Koppelmann f_result = MUL_NAN; 165daab3f7fSBastian Koppelmann } 166daab3f7fSBastian Koppelmann f_update_psw_flags(env, flags); 167daab3f7fSBastian Koppelmann } else { 168daab3f7fSBastian Koppelmann env->FPU_FS = 0; 169daab3f7fSBastian Koppelmann } 170daab3f7fSBastian Koppelmann return (uint32_t)f_result; 171daab3f7fSBastian Koppelmann 172daab3f7fSBastian Koppelmann } 173446ee5b2SBastian Koppelmann 174446ee5b2SBastian Koppelmann uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 175446ee5b2SBastian Koppelmann { 176446ee5b2SBastian Koppelmann uint32_t flags; 177446ee5b2SBastian Koppelmann float32 arg1 = make_float32(r1); 178446ee5b2SBastian Koppelmann float32 arg2 = make_float32(r2); 179446ee5b2SBastian Koppelmann float32 f_result; 180446ee5b2SBastian Koppelmann 181446ee5b2SBastian Koppelmann f_result = float32_div(arg1, arg2 , &env->fp_status); 182446ee5b2SBastian Koppelmann 183446ee5b2SBastian Koppelmann flags = f_get_excp_flags(env); 184446ee5b2SBastian Koppelmann if (flags) { 185446ee5b2SBastian Koppelmann /* If the output is a NaN, but the inputs aren't, 186446ee5b2SBastian Koppelmann we return a unique value. */ 187446ee5b2SBastian Koppelmann if ((flags & float_flag_invalid) 188446ee5b2SBastian Koppelmann && !float32_is_any_nan(arg1) 189446ee5b2SBastian Koppelmann && !float32_is_any_nan(arg2)) { 190446ee5b2SBastian Koppelmann f_result = DIV_NAN; 191446ee5b2SBastian Koppelmann } 192446ee5b2SBastian Koppelmann f_update_psw_flags(env, flags); 193446ee5b2SBastian Koppelmann } else { 194446ee5b2SBastian Koppelmann env->FPU_FS = 0; 195446ee5b2SBastian Koppelmann } 196446ee5b2SBastian Koppelmann 197446ee5b2SBastian Koppelmann return (uint32_t)f_result; 198446ee5b2SBastian Koppelmann } 199743cd09dSBastian Koppelmann 200ddd7feadSBastian Koppelmann uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1, 201ddd7feadSBastian Koppelmann uint32_t r2, uint32_t r3) 202ddd7feadSBastian Koppelmann { 203ddd7feadSBastian Koppelmann uint32_t flags; 204ddd7feadSBastian Koppelmann float32 arg1 = make_float32(r1); 205ddd7feadSBastian Koppelmann float32 arg2 = make_float32(r2); 206ddd7feadSBastian Koppelmann float32 arg3 = make_float32(r3); 207ddd7feadSBastian Koppelmann float32 f_result; 208ddd7feadSBastian Koppelmann 209ddd7feadSBastian Koppelmann f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status); 210ddd7feadSBastian Koppelmann 211ddd7feadSBastian Koppelmann flags = f_get_excp_flags(env); 212ddd7feadSBastian Koppelmann if (flags) { 213ddd7feadSBastian Koppelmann if (flags & float_flag_invalid) { 214ddd7feadSBastian Koppelmann arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 215ddd7feadSBastian Koppelmann arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 216ddd7feadSBastian Koppelmann arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 217ddd7feadSBastian Koppelmann f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0); 218ddd7feadSBastian Koppelmann } 219ddd7feadSBastian Koppelmann f_update_psw_flags(env, flags); 220ddd7feadSBastian Koppelmann } else { 221ddd7feadSBastian Koppelmann env->FPU_FS = 0; 222ddd7feadSBastian Koppelmann } 223ddd7feadSBastian Koppelmann return (uint32_t)f_result; 224ddd7feadSBastian Koppelmann } 225ddd7feadSBastian Koppelmann 226ddd7feadSBastian Koppelmann uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1, 227ddd7feadSBastian Koppelmann uint32_t r2, uint32_t r3) 228ddd7feadSBastian Koppelmann { 229ddd7feadSBastian Koppelmann uint32_t flags; 230ddd7feadSBastian Koppelmann float32 arg1 = make_float32(r1); 231ddd7feadSBastian Koppelmann float32 arg2 = make_float32(r2); 232ddd7feadSBastian Koppelmann float32 arg3 = make_float32(r3); 233ddd7feadSBastian Koppelmann float32 f_result; 234ddd7feadSBastian Koppelmann 235ddd7feadSBastian Koppelmann f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product, 236ddd7feadSBastian Koppelmann &env->fp_status); 237ddd7feadSBastian Koppelmann 238ddd7feadSBastian Koppelmann flags = f_get_excp_flags(env); 239ddd7feadSBastian Koppelmann if (flags) { 240ddd7feadSBastian Koppelmann if (flags & float_flag_invalid) { 241ddd7feadSBastian Koppelmann arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 242ddd7feadSBastian Koppelmann arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 243ddd7feadSBastian Koppelmann arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 244ddd7feadSBastian Koppelmann 245ddd7feadSBastian Koppelmann f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1); 246ddd7feadSBastian Koppelmann } 247ddd7feadSBastian Koppelmann f_update_psw_flags(env, flags); 248ddd7feadSBastian Koppelmann } else { 249ddd7feadSBastian Koppelmann env->FPU_FS = 0; 250ddd7feadSBastian Koppelmann } 251ddd7feadSBastian Koppelmann return (uint32_t)f_result; 252ddd7feadSBastian Koppelmann } 253ddd7feadSBastian Koppelmann 254743cd09dSBastian Koppelmann uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 255743cd09dSBastian Koppelmann { 256743cd09dSBastian Koppelmann uint32_t result, flags; 257743cd09dSBastian Koppelmann float32 arg1 = make_float32(r1); 258743cd09dSBastian Koppelmann float32 arg2 = make_float32(r2); 259743cd09dSBastian Koppelmann 260743cd09dSBastian Koppelmann set_flush_inputs_to_zero(0, &env->fp_status); 261743cd09dSBastian Koppelmann 262743cd09dSBastian Koppelmann result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); 263743cd09dSBastian Koppelmann result |= f_is_denormal(arg1) << 4; 264743cd09dSBastian Koppelmann result |= f_is_denormal(arg2) << 5; 265743cd09dSBastian Koppelmann 266743cd09dSBastian Koppelmann flags = f_get_excp_flags(env); 267743cd09dSBastian Koppelmann if (flags) { 268743cd09dSBastian Koppelmann f_update_psw_flags(env, flags); 269743cd09dSBastian Koppelmann } else { 270743cd09dSBastian Koppelmann env->FPU_FS = 0; 271743cd09dSBastian Koppelmann } 272743cd09dSBastian Koppelmann 273743cd09dSBastian Koppelmann set_flush_inputs_to_zero(1, &env->fp_status); 274743cd09dSBastian Koppelmann return result; 275743cd09dSBastian Koppelmann } 2760d4c3b80SBastian Koppelmann 2770d4c3b80SBastian Koppelmann uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) 2780d4c3b80SBastian Koppelmann { 2790d4c3b80SBastian Koppelmann float32 f_arg = make_float32(arg); 2800d4c3b80SBastian Koppelmann int32_t result, flags; 2810d4c3b80SBastian Koppelmann 2820d4c3b80SBastian Koppelmann result = float32_to_int32(f_arg, &env->fp_status); 2830d4c3b80SBastian Koppelmann 2840d4c3b80SBastian Koppelmann flags = f_get_excp_flags(env); 2850d4c3b80SBastian Koppelmann if (flags) { 2860d4c3b80SBastian Koppelmann if (float32_is_any_nan(f_arg)) { 2870d4c3b80SBastian Koppelmann result = 0; 2880d4c3b80SBastian Koppelmann } 2890d4c3b80SBastian Koppelmann f_update_psw_flags(env, flags); 2900d4c3b80SBastian Koppelmann } else { 2910d4c3b80SBastian Koppelmann env->FPU_FS = 0; 2920d4c3b80SBastian Koppelmann } 2930d4c3b80SBastian Koppelmann return (uint32_t)result; 2940d4c3b80SBastian Koppelmann } 2950d4c3b80SBastian Koppelmann 2960d4c3b80SBastian Koppelmann uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) 2970d4c3b80SBastian Koppelmann { 2980d4c3b80SBastian Koppelmann float32 f_result; 2990d4c3b80SBastian Koppelmann uint32_t flags; 3000d4c3b80SBastian Koppelmann f_result = int32_to_float32(arg, &env->fp_status); 3010d4c3b80SBastian Koppelmann 3020d4c3b80SBastian Koppelmann flags = f_get_excp_flags(env); 3030d4c3b80SBastian Koppelmann if (flags) { 3040d4c3b80SBastian Koppelmann f_update_psw_flags(env, flags); 3050d4c3b80SBastian Koppelmann } else { 3060d4c3b80SBastian Koppelmann env->FPU_FS = 0; 3070d4c3b80SBastian Koppelmann } 3080d4c3b80SBastian Koppelmann return (uint32_t)f_result; 3090d4c3b80SBastian Koppelmann } 3108f75983dSBastian Koppelmann 3118f75983dSBastian Koppelmann uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) 3128f75983dSBastian Koppelmann { 3138f75983dSBastian Koppelmann float32 f_arg = make_float32(arg); 3148f75983dSBastian Koppelmann uint32_t result; 3158f75983dSBastian Koppelmann int32_t flags; 3168f75983dSBastian Koppelmann 3178f75983dSBastian Koppelmann result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status); 3188f75983dSBastian Koppelmann 3198f75983dSBastian Koppelmann flags = f_get_excp_flags(env); 3208f75983dSBastian Koppelmann if (flags & float_flag_invalid) { 3218f75983dSBastian Koppelmann flags &= ~float_flag_inexact; 3228f75983dSBastian Koppelmann if (float32_is_any_nan(f_arg)) { 3238f75983dSBastian Koppelmann result = 0; 3248f75983dSBastian Koppelmann } 3258f75983dSBastian Koppelmann } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { 3268f75983dSBastian Koppelmann flags = float_flag_invalid; 3278f75983dSBastian Koppelmann result = 0; 3288f75983dSBastian Koppelmann } 3298f75983dSBastian Koppelmann 3308f75983dSBastian Koppelmann if (flags) { 3318f75983dSBastian Koppelmann f_update_psw_flags(env, flags); 3328f75983dSBastian Koppelmann } else { 3338f75983dSBastian Koppelmann env->FPU_FS = 0; 3348f75983dSBastian Koppelmann } 3358f75983dSBastian Koppelmann return result; 3368f75983dSBastian Koppelmann } 33750788a3fSBastian Koppelmann 33850788a3fSBastian Koppelmann void helper_updfl(CPUTriCoreState *env, uint32_t arg) 33950788a3fSBastian Koppelmann { 34050788a3fSBastian Koppelmann env->FPU_FS = extract32(arg, 7, 1) & extract32(arg, 15, 1); 34150788a3fSBastian Koppelmann env->FPU_FI = (extract32(arg, 6, 1) & extract32(arg, 14, 1)) << 31; 34250788a3fSBastian Koppelmann env->FPU_FV = (extract32(arg, 5, 1) & extract32(arg, 13, 1)) << 31; 34350788a3fSBastian Koppelmann env->FPU_FZ = (extract32(arg, 4, 1) & extract32(arg, 12, 1)) << 31; 34450788a3fSBastian Koppelmann env->FPU_FU = (extract32(arg, 3, 1) & extract32(arg, 11, 1)) << 31; 34550788a3fSBastian Koppelmann /* clear FX and RM */ 34650788a3fSBastian Koppelmann env->PSW &= ~(extract32(arg, 10, 1) << 26); 34750788a3fSBastian Koppelmann env->PSW |= (extract32(arg, 2, 1) & extract32(arg, 10, 1)) << 26; 34850788a3fSBastian Koppelmann 34950788a3fSBastian Koppelmann fpu_set_state(env); 35050788a3fSBastian Koppelmann } 351