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" 23996a729fSBastian Koppelmann 24*ddd7feadSBastian Koppelmann #define QUIET_NAN 0x7fc00000 25*ddd7feadSBastian Koppelmann #define ADD_NAN 0x7fc00001 26996a729fSBastian Koppelmann #define DIV_NAN 0x7fc00008 27996a729fSBastian Koppelmann #define MUL_NAN 0x7fc00002 28996a729fSBastian Koppelmann #define FPU_FS PSW_USB_C 29996a729fSBastian Koppelmann #define FPU_FI PSW_USB_V 30996a729fSBastian Koppelmann #define FPU_FV PSW_USB_SV 31996a729fSBastian Koppelmann #define FPU_FZ PSW_USB_AV 32996a729fSBastian Koppelmann #define FPU_FU PSW_USB_SAV 33996a729fSBastian Koppelmann 34996a729fSBastian Koppelmann /* we don't care about input_denormal */ 35996a729fSBastian Koppelmann static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) 36996a729fSBastian Koppelmann { 37996a729fSBastian Koppelmann return get_float_exception_flags(&env->fp_status) 38996a729fSBastian Koppelmann & (float_flag_invalid 39996a729fSBastian Koppelmann | float_flag_overflow 40996a729fSBastian Koppelmann | float_flag_underflow 41996a729fSBastian Koppelmann | float_flag_output_denormal 42996a729fSBastian Koppelmann | float_flag_divbyzero 43996a729fSBastian Koppelmann | float_flag_inexact); 44996a729fSBastian Koppelmann } 45996a729fSBastian Koppelmann 46996a729fSBastian Koppelmann static inline bool f_is_denormal(float32 arg) 47996a729fSBastian Koppelmann { 48996a729fSBastian Koppelmann return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); 49996a729fSBastian Koppelmann } 50996a729fSBastian Koppelmann 51*ddd7feadSBastian Koppelmann static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2, 52*ddd7feadSBastian Koppelmann float32 arg3, float32 result, 53*ddd7feadSBastian Koppelmann uint32_t muladd_negate_c) 54*ddd7feadSBastian Koppelmann { 55*ddd7feadSBastian Koppelmann uint32_t aSign, bSign, cSign; 56*ddd7feadSBastian Koppelmann uint32_t aExp, bExp, cExp; 57*ddd7feadSBastian Koppelmann 58*ddd7feadSBastian Koppelmann if (float32_is_any_nan(arg1) || float32_is_any_nan(arg2) || 59*ddd7feadSBastian Koppelmann float32_is_any_nan(arg3)) { 60*ddd7feadSBastian Koppelmann return QUIET_NAN; 61*ddd7feadSBastian Koppelmann } else if (float32_is_infinity(arg1) && float32_is_zero(arg2)) { 62*ddd7feadSBastian Koppelmann return MUL_NAN; 63*ddd7feadSBastian Koppelmann } else if (float32_is_zero(arg1) && float32_is_infinity(arg2)) { 64*ddd7feadSBastian Koppelmann return MUL_NAN; 65*ddd7feadSBastian Koppelmann } else { 66*ddd7feadSBastian Koppelmann aSign = arg1 >> 31; 67*ddd7feadSBastian Koppelmann bSign = arg2 >> 31; 68*ddd7feadSBastian Koppelmann cSign = arg3 >> 31; 69*ddd7feadSBastian Koppelmann 70*ddd7feadSBastian Koppelmann aExp = (arg1 >> 23) & 0xff; 71*ddd7feadSBastian Koppelmann bExp = (arg2 >> 23) & 0xff; 72*ddd7feadSBastian Koppelmann cExp = (arg3 >> 23) & 0xff; 73*ddd7feadSBastian Koppelmann 74*ddd7feadSBastian Koppelmann if (muladd_negate_c) { 75*ddd7feadSBastian Koppelmann cSign ^= 1; 76*ddd7feadSBastian Koppelmann } 77*ddd7feadSBastian Koppelmann if (((aExp == 0xff) || (bExp == 0xff)) && (cExp == 0xff)) { 78*ddd7feadSBastian Koppelmann if (aSign ^ bSign ^ cSign) { 79*ddd7feadSBastian Koppelmann return ADD_NAN; 80*ddd7feadSBastian Koppelmann } 81*ddd7feadSBastian Koppelmann } 82*ddd7feadSBastian Koppelmann } 83*ddd7feadSBastian Koppelmann 84*ddd7feadSBastian Koppelmann return result; 85*ddd7feadSBastian Koppelmann } 86*ddd7feadSBastian Koppelmann 87baf410dcSBastian Koppelmann static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) 88996a729fSBastian Koppelmann { 89996a729fSBastian Koppelmann uint8_t some_excp = 0; 90996a729fSBastian Koppelmann set_float_exception_flags(0, &env->fp_status); 91996a729fSBastian Koppelmann 92996a729fSBastian Koppelmann if (flags & float_flag_invalid) { 93996a729fSBastian Koppelmann env->FPU_FI = 1 << 31; 94996a729fSBastian Koppelmann some_excp = 1; 95996a729fSBastian Koppelmann } 96996a729fSBastian Koppelmann 97996a729fSBastian Koppelmann if (flags & float_flag_overflow) { 98996a729fSBastian Koppelmann env->FPU_FV = 1 << 31; 99996a729fSBastian Koppelmann some_excp = 1; 100996a729fSBastian Koppelmann } 101996a729fSBastian Koppelmann 102996a729fSBastian Koppelmann if (flags & float_flag_underflow || flags & float_flag_output_denormal) { 103996a729fSBastian Koppelmann env->FPU_FU = 1 << 31; 104996a729fSBastian Koppelmann some_excp = 1; 105996a729fSBastian Koppelmann } 106996a729fSBastian Koppelmann 107996a729fSBastian Koppelmann if (flags & float_flag_divbyzero) { 108996a729fSBastian Koppelmann env->FPU_FZ = 1 << 31; 109996a729fSBastian Koppelmann some_excp = 1; 110996a729fSBastian Koppelmann } 111996a729fSBastian Koppelmann 112996a729fSBastian Koppelmann if (flags & float_flag_inexact || flags & float_flag_output_denormal) { 113996a729fSBastian Koppelmann env->PSW |= 1 << 26; 114996a729fSBastian Koppelmann some_excp = 1; 115996a729fSBastian Koppelmann } 116996a729fSBastian Koppelmann 117996a729fSBastian Koppelmann env->FPU_FS = some_excp; 118996a729fSBastian Koppelmann } 119baf410dcSBastian Koppelmann 120baf410dcSBastian Koppelmann #define FADD_SUB(op) \ 121baf410dcSBastian Koppelmann uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2) \ 122baf410dcSBastian Koppelmann { \ 123baf410dcSBastian Koppelmann float32 arg1 = make_float32(r1); \ 124baf410dcSBastian Koppelmann float32 arg2 = make_float32(r2); \ 125baf410dcSBastian Koppelmann uint32_t flags; \ 126baf410dcSBastian Koppelmann float32 f_result; \ 127baf410dcSBastian Koppelmann \ 128baf410dcSBastian Koppelmann f_result = float32_##op(arg2, arg1, &env->fp_status); \ 129baf410dcSBastian Koppelmann flags = f_get_excp_flags(env); \ 130baf410dcSBastian Koppelmann if (flags) { \ 131baf410dcSBastian Koppelmann /* If the output is a NaN, but the inputs aren't, \ 132baf410dcSBastian Koppelmann we return a unique value. */ \ 133baf410dcSBastian Koppelmann if ((flags & float_flag_invalid) \ 134baf410dcSBastian Koppelmann && !float32_is_any_nan(arg1) \ 135baf410dcSBastian Koppelmann && !float32_is_any_nan(arg2)) { \ 136baf410dcSBastian Koppelmann f_result = ADD_NAN; \ 137baf410dcSBastian Koppelmann } \ 138baf410dcSBastian Koppelmann f_update_psw_flags(env, flags); \ 139baf410dcSBastian Koppelmann } else { \ 140baf410dcSBastian Koppelmann env->FPU_FS = 0; \ 141baf410dcSBastian Koppelmann } \ 142baf410dcSBastian Koppelmann return (uint32_t)f_result; \ 143baf410dcSBastian Koppelmann } 144baf410dcSBastian Koppelmann FADD_SUB(add) 145baf410dcSBastian Koppelmann FADD_SUB(sub) 146daab3f7fSBastian Koppelmann 147daab3f7fSBastian Koppelmann uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 148daab3f7fSBastian Koppelmann { 149daab3f7fSBastian Koppelmann uint32_t flags; 150daab3f7fSBastian Koppelmann float32 arg1 = make_float32(r1); 151daab3f7fSBastian Koppelmann float32 arg2 = make_float32(r2); 152daab3f7fSBastian Koppelmann float32 f_result; 153daab3f7fSBastian Koppelmann 154daab3f7fSBastian Koppelmann f_result = float32_mul(arg1, arg2, &env->fp_status); 155daab3f7fSBastian Koppelmann 156daab3f7fSBastian Koppelmann flags = f_get_excp_flags(env); 157daab3f7fSBastian Koppelmann if (flags) { 158daab3f7fSBastian Koppelmann /* If the output is a NaN, but the inputs aren't, 159daab3f7fSBastian Koppelmann we return a unique value. */ 160daab3f7fSBastian Koppelmann if ((flags & float_flag_invalid) 161daab3f7fSBastian Koppelmann && !float32_is_any_nan(arg1) 162daab3f7fSBastian Koppelmann && !float32_is_any_nan(arg2)) { 163daab3f7fSBastian Koppelmann f_result = MUL_NAN; 164daab3f7fSBastian Koppelmann } 165daab3f7fSBastian Koppelmann f_update_psw_flags(env, flags); 166daab3f7fSBastian Koppelmann } else { 167daab3f7fSBastian Koppelmann env->FPU_FS = 0; 168daab3f7fSBastian Koppelmann } 169daab3f7fSBastian Koppelmann return (uint32_t)f_result; 170daab3f7fSBastian Koppelmann 171daab3f7fSBastian Koppelmann } 172446ee5b2SBastian Koppelmann 173446ee5b2SBastian Koppelmann uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 174446ee5b2SBastian Koppelmann { 175446ee5b2SBastian Koppelmann uint32_t flags; 176446ee5b2SBastian Koppelmann float32 arg1 = make_float32(r1); 177446ee5b2SBastian Koppelmann float32 arg2 = make_float32(r2); 178446ee5b2SBastian Koppelmann float32 f_result; 179446ee5b2SBastian Koppelmann 180446ee5b2SBastian Koppelmann f_result = float32_div(arg1, arg2 , &env->fp_status); 181446ee5b2SBastian Koppelmann 182446ee5b2SBastian Koppelmann flags = f_get_excp_flags(env); 183446ee5b2SBastian Koppelmann if (flags) { 184446ee5b2SBastian Koppelmann /* If the output is a NaN, but the inputs aren't, 185446ee5b2SBastian Koppelmann we return a unique value. */ 186446ee5b2SBastian Koppelmann if ((flags & float_flag_invalid) 187446ee5b2SBastian Koppelmann && !float32_is_any_nan(arg1) 188446ee5b2SBastian Koppelmann && !float32_is_any_nan(arg2)) { 189446ee5b2SBastian Koppelmann f_result = DIV_NAN; 190446ee5b2SBastian Koppelmann } 191446ee5b2SBastian Koppelmann f_update_psw_flags(env, flags); 192446ee5b2SBastian Koppelmann } else { 193446ee5b2SBastian Koppelmann env->FPU_FS = 0; 194446ee5b2SBastian Koppelmann } 195446ee5b2SBastian Koppelmann 196446ee5b2SBastian Koppelmann return (uint32_t)f_result; 197446ee5b2SBastian Koppelmann } 198743cd09dSBastian Koppelmann 199*ddd7feadSBastian Koppelmann uint32_t helper_fmadd(CPUTriCoreState *env, uint32_t r1, 200*ddd7feadSBastian Koppelmann uint32_t r2, uint32_t r3) 201*ddd7feadSBastian Koppelmann { 202*ddd7feadSBastian Koppelmann uint32_t flags; 203*ddd7feadSBastian Koppelmann float32 arg1 = make_float32(r1); 204*ddd7feadSBastian Koppelmann float32 arg2 = make_float32(r2); 205*ddd7feadSBastian Koppelmann float32 arg3 = make_float32(r3); 206*ddd7feadSBastian Koppelmann float32 f_result; 207*ddd7feadSBastian Koppelmann 208*ddd7feadSBastian Koppelmann f_result = float32_muladd(arg1, arg2, arg3, 0, &env->fp_status); 209*ddd7feadSBastian Koppelmann 210*ddd7feadSBastian Koppelmann flags = f_get_excp_flags(env); 211*ddd7feadSBastian Koppelmann if (flags) { 212*ddd7feadSBastian Koppelmann if (flags & float_flag_invalid) { 213*ddd7feadSBastian Koppelmann arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 214*ddd7feadSBastian Koppelmann arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 215*ddd7feadSBastian Koppelmann arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 216*ddd7feadSBastian Koppelmann f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 0); 217*ddd7feadSBastian Koppelmann } 218*ddd7feadSBastian Koppelmann f_update_psw_flags(env, flags); 219*ddd7feadSBastian Koppelmann } else { 220*ddd7feadSBastian Koppelmann env->FPU_FS = 0; 221*ddd7feadSBastian Koppelmann } 222*ddd7feadSBastian Koppelmann return (uint32_t)f_result; 223*ddd7feadSBastian Koppelmann } 224*ddd7feadSBastian Koppelmann 225*ddd7feadSBastian Koppelmann uint32_t helper_fmsub(CPUTriCoreState *env, uint32_t r1, 226*ddd7feadSBastian Koppelmann uint32_t r2, uint32_t r3) 227*ddd7feadSBastian Koppelmann { 228*ddd7feadSBastian Koppelmann uint32_t flags; 229*ddd7feadSBastian Koppelmann float32 arg1 = make_float32(r1); 230*ddd7feadSBastian Koppelmann float32 arg2 = make_float32(r2); 231*ddd7feadSBastian Koppelmann float32 arg3 = make_float32(r3); 232*ddd7feadSBastian Koppelmann float32 f_result; 233*ddd7feadSBastian Koppelmann 234*ddd7feadSBastian Koppelmann f_result = float32_muladd(arg1, arg2, arg3, float_muladd_negate_product, 235*ddd7feadSBastian Koppelmann &env->fp_status); 236*ddd7feadSBastian Koppelmann 237*ddd7feadSBastian Koppelmann flags = f_get_excp_flags(env); 238*ddd7feadSBastian Koppelmann if (flags) { 239*ddd7feadSBastian Koppelmann if (flags & float_flag_invalid) { 240*ddd7feadSBastian Koppelmann arg1 = float32_squash_input_denormal(arg1, &env->fp_status); 241*ddd7feadSBastian Koppelmann arg2 = float32_squash_input_denormal(arg2, &env->fp_status); 242*ddd7feadSBastian Koppelmann arg3 = float32_squash_input_denormal(arg3, &env->fp_status); 243*ddd7feadSBastian Koppelmann 244*ddd7feadSBastian Koppelmann f_result = f_maddsub_nan_result(arg1, arg2, arg3, f_result, 1); 245*ddd7feadSBastian Koppelmann } 246*ddd7feadSBastian Koppelmann f_update_psw_flags(env, flags); 247*ddd7feadSBastian Koppelmann } else { 248*ddd7feadSBastian Koppelmann env->FPU_FS = 0; 249*ddd7feadSBastian Koppelmann } 250*ddd7feadSBastian Koppelmann return (uint32_t)f_result; 251*ddd7feadSBastian Koppelmann } 252*ddd7feadSBastian Koppelmann 253743cd09dSBastian Koppelmann uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2) 254743cd09dSBastian Koppelmann { 255743cd09dSBastian Koppelmann uint32_t result, flags; 256743cd09dSBastian Koppelmann float32 arg1 = make_float32(r1); 257743cd09dSBastian Koppelmann float32 arg2 = make_float32(r2); 258743cd09dSBastian Koppelmann 259743cd09dSBastian Koppelmann set_flush_inputs_to_zero(0, &env->fp_status); 260743cd09dSBastian Koppelmann 261743cd09dSBastian Koppelmann result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1); 262743cd09dSBastian Koppelmann result |= f_is_denormal(arg1) << 4; 263743cd09dSBastian Koppelmann result |= f_is_denormal(arg2) << 5; 264743cd09dSBastian Koppelmann 265743cd09dSBastian Koppelmann flags = f_get_excp_flags(env); 266743cd09dSBastian Koppelmann if (flags) { 267743cd09dSBastian Koppelmann f_update_psw_flags(env, flags); 268743cd09dSBastian Koppelmann } else { 269743cd09dSBastian Koppelmann env->FPU_FS = 0; 270743cd09dSBastian Koppelmann } 271743cd09dSBastian Koppelmann 272743cd09dSBastian Koppelmann set_flush_inputs_to_zero(1, &env->fp_status); 273743cd09dSBastian Koppelmann return result; 274743cd09dSBastian Koppelmann } 2750d4c3b80SBastian Koppelmann 2760d4c3b80SBastian Koppelmann uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg) 2770d4c3b80SBastian Koppelmann { 2780d4c3b80SBastian Koppelmann float32 f_arg = make_float32(arg); 2790d4c3b80SBastian Koppelmann int32_t result, flags; 2800d4c3b80SBastian Koppelmann 2810d4c3b80SBastian Koppelmann result = float32_to_int32(f_arg, &env->fp_status); 2820d4c3b80SBastian Koppelmann 2830d4c3b80SBastian Koppelmann flags = f_get_excp_flags(env); 2840d4c3b80SBastian Koppelmann if (flags) { 2850d4c3b80SBastian Koppelmann if (float32_is_any_nan(f_arg)) { 2860d4c3b80SBastian Koppelmann result = 0; 2870d4c3b80SBastian Koppelmann } 2880d4c3b80SBastian Koppelmann f_update_psw_flags(env, flags); 2890d4c3b80SBastian Koppelmann } else { 2900d4c3b80SBastian Koppelmann env->FPU_FS = 0; 2910d4c3b80SBastian Koppelmann } 2920d4c3b80SBastian Koppelmann return (uint32_t)result; 2930d4c3b80SBastian Koppelmann } 2940d4c3b80SBastian Koppelmann 2950d4c3b80SBastian Koppelmann uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg) 2960d4c3b80SBastian Koppelmann { 2970d4c3b80SBastian Koppelmann float32 f_result; 2980d4c3b80SBastian Koppelmann uint32_t flags; 2990d4c3b80SBastian Koppelmann f_result = int32_to_float32(arg, &env->fp_status); 3000d4c3b80SBastian Koppelmann 3010d4c3b80SBastian Koppelmann flags = f_get_excp_flags(env); 3020d4c3b80SBastian Koppelmann if (flags) { 3030d4c3b80SBastian Koppelmann f_update_psw_flags(env, flags); 3040d4c3b80SBastian Koppelmann } else { 3050d4c3b80SBastian Koppelmann env->FPU_FS = 0; 3060d4c3b80SBastian Koppelmann } 3070d4c3b80SBastian Koppelmann return (uint32_t)f_result; 3080d4c3b80SBastian Koppelmann } 3098f75983dSBastian Koppelmann 3108f75983dSBastian Koppelmann uint32_t helper_ftouz(CPUTriCoreState *env, uint32_t arg) 3118f75983dSBastian Koppelmann { 3128f75983dSBastian Koppelmann float32 f_arg = make_float32(arg); 3138f75983dSBastian Koppelmann uint32_t result; 3148f75983dSBastian Koppelmann int32_t flags; 3158f75983dSBastian Koppelmann 3168f75983dSBastian Koppelmann result = float32_to_uint32_round_to_zero(f_arg, &env->fp_status); 3178f75983dSBastian Koppelmann 3188f75983dSBastian Koppelmann flags = f_get_excp_flags(env); 3198f75983dSBastian Koppelmann if (flags & float_flag_invalid) { 3208f75983dSBastian Koppelmann flags &= ~float_flag_inexact; 3218f75983dSBastian Koppelmann if (float32_is_any_nan(f_arg)) { 3228f75983dSBastian Koppelmann result = 0; 3238f75983dSBastian Koppelmann } 3248f75983dSBastian Koppelmann } else if (float32_lt_quiet(f_arg, 0, &env->fp_status)) { 3258f75983dSBastian Koppelmann flags = float_flag_invalid; 3268f75983dSBastian Koppelmann result = 0; 3278f75983dSBastian Koppelmann } 3288f75983dSBastian Koppelmann 3298f75983dSBastian Koppelmann if (flags) { 3308f75983dSBastian Koppelmann f_update_psw_flags(env, flags); 3318f75983dSBastian Koppelmann } else { 3328f75983dSBastian Koppelmann env->FPU_FS = 0; 3338f75983dSBastian Koppelmann } 3348f75983dSBastian Koppelmann return result; 3358f75983dSBastian Koppelmann } 336