xref: /qemu/target/tricore/fpu_helper.c (revision 0d4c3b8010320a8ebb4061d88510e082b06ed2cc)
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 
24996a729fSBastian Koppelmann #define ADD_NAN   0x7cf00001
25996a729fSBastian Koppelmann #define DIV_NAN   0x7fc00008
26996a729fSBastian Koppelmann #define MUL_NAN   0x7fc00002
27996a729fSBastian Koppelmann #define FPU_FS PSW_USB_C
28996a729fSBastian Koppelmann #define FPU_FI PSW_USB_V
29996a729fSBastian Koppelmann #define FPU_FV PSW_USB_SV
30996a729fSBastian Koppelmann #define FPU_FZ PSW_USB_AV
31996a729fSBastian Koppelmann #define FPU_FU PSW_USB_SAV
32996a729fSBastian Koppelmann 
33996a729fSBastian Koppelmann /* we don't care about input_denormal */
34996a729fSBastian Koppelmann static inline uint8_t f_get_excp_flags(CPUTriCoreState *env)
35996a729fSBastian Koppelmann {
36996a729fSBastian Koppelmann     return get_float_exception_flags(&env->fp_status)
37996a729fSBastian Koppelmann            & (float_flag_invalid
38996a729fSBastian Koppelmann               | float_flag_overflow
39996a729fSBastian Koppelmann               | float_flag_underflow
40996a729fSBastian Koppelmann               | float_flag_output_denormal
41996a729fSBastian Koppelmann               | float_flag_divbyzero
42996a729fSBastian Koppelmann               | float_flag_inexact);
43996a729fSBastian Koppelmann }
44996a729fSBastian Koppelmann 
45996a729fSBastian Koppelmann static inline bool f_is_denormal(float32 arg)
46996a729fSBastian Koppelmann {
47996a729fSBastian Koppelmann     return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg);
48996a729fSBastian Koppelmann }
49996a729fSBastian Koppelmann 
50baf410dcSBastian Koppelmann static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags)
51996a729fSBastian Koppelmann {
52996a729fSBastian Koppelmann     uint8_t some_excp = 0;
53996a729fSBastian Koppelmann     set_float_exception_flags(0, &env->fp_status);
54996a729fSBastian Koppelmann 
55996a729fSBastian Koppelmann     if (flags & float_flag_invalid) {
56996a729fSBastian Koppelmann         env->FPU_FI = 1 << 31;
57996a729fSBastian Koppelmann         some_excp = 1;
58996a729fSBastian Koppelmann     }
59996a729fSBastian Koppelmann 
60996a729fSBastian Koppelmann     if (flags & float_flag_overflow) {
61996a729fSBastian Koppelmann         env->FPU_FV = 1 << 31;
62996a729fSBastian Koppelmann         some_excp = 1;
63996a729fSBastian Koppelmann     }
64996a729fSBastian Koppelmann 
65996a729fSBastian Koppelmann     if (flags & float_flag_underflow || flags & float_flag_output_denormal) {
66996a729fSBastian Koppelmann         env->FPU_FU = 1 << 31;
67996a729fSBastian Koppelmann         some_excp = 1;
68996a729fSBastian Koppelmann     }
69996a729fSBastian Koppelmann 
70996a729fSBastian Koppelmann     if (flags & float_flag_divbyzero) {
71996a729fSBastian Koppelmann         env->FPU_FZ = 1 << 31;
72996a729fSBastian Koppelmann         some_excp = 1;
73996a729fSBastian Koppelmann     }
74996a729fSBastian Koppelmann 
75996a729fSBastian Koppelmann     if (flags & float_flag_inexact || flags & float_flag_output_denormal) {
76996a729fSBastian Koppelmann         env->PSW |= 1 << 26;
77996a729fSBastian Koppelmann         some_excp = 1;
78996a729fSBastian Koppelmann     }
79996a729fSBastian Koppelmann 
80996a729fSBastian Koppelmann     env->FPU_FS = some_excp;
81996a729fSBastian Koppelmann }
82baf410dcSBastian Koppelmann 
83baf410dcSBastian Koppelmann #define FADD_SUB(op)                                                           \
84baf410dcSBastian Koppelmann uint32_t helper_f##op(CPUTriCoreState *env, uint32_t r1, uint32_t r2)          \
85baf410dcSBastian Koppelmann {                                                                              \
86baf410dcSBastian Koppelmann     float32 arg1 = make_float32(r1);                                           \
87baf410dcSBastian Koppelmann     float32 arg2 = make_float32(r2);                                           \
88baf410dcSBastian Koppelmann     uint32_t flags;                                                            \
89baf410dcSBastian Koppelmann     float32 f_result;                                                          \
90baf410dcSBastian Koppelmann                                                                                \
91baf410dcSBastian Koppelmann     f_result = float32_##op(arg2, arg1, &env->fp_status);                      \
92baf410dcSBastian Koppelmann     flags = f_get_excp_flags(env);                                             \
93baf410dcSBastian Koppelmann     if (flags) {                                                               \
94baf410dcSBastian Koppelmann         /* If the output is a NaN, but the inputs aren't,                      \
95baf410dcSBastian Koppelmann            we return a unique value.  */                                       \
96baf410dcSBastian Koppelmann         if ((flags & float_flag_invalid)                                       \
97baf410dcSBastian Koppelmann             && !float32_is_any_nan(arg1)                                       \
98baf410dcSBastian Koppelmann             && !float32_is_any_nan(arg2)) {                                    \
99baf410dcSBastian Koppelmann             f_result = ADD_NAN;                                                \
100baf410dcSBastian Koppelmann         }                                                                      \
101baf410dcSBastian Koppelmann         f_update_psw_flags(env, flags);                                        \
102baf410dcSBastian Koppelmann     } else {                                                                   \
103baf410dcSBastian Koppelmann         env->FPU_FS = 0;                                                       \
104baf410dcSBastian Koppelmann     }                                                                          \
105baf410dcSBastian Koppelmann     return (uint32_t)f_result;                                                 \
106baf410dcSBastian Koppelmann }
107baf410dcSBastian Koppelmann FADD_SUB(add)
108baf410dcSBastian Koppelmann FADD_SUB(sub)
109daab3f7fSBastian Koppelmann 
110daab3f7fSBastian Koppelmann uint32_t helper_fmul(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
111daab3f7fSBastian Koppelmann {
112daab3f7fSBastian Koppelmann     uint32_t flags;
113daab3f7fSBastian Koppelmann     float32 arg1 = make_float32(r1);
114daab3f7fSBastian Koppelmann     float32 arg2 = make_float32(r2);
115daab3f7fSBastian Koppelmann     float32 f_result;
116daab3f7fSBastian Koppelmann 
117daab3f7fSBastian Koppelmann     f_result = float32_mul(arg1, arg2, &env->fp_status);
118daab3f7fSBastian Koppelmann 
119daab3f7fSBastian Koppelmann     flags = f_get_excp_flags(env);
120daab3f7fSBastian Koppelmann     if (flags) {
121daab3f7fSBastian Koppelmann         /* If the output is a NaN, but the inputs aren't,
122daab3f7fSBastian Koppelmann            we return a unique value.  */
123daab3f7fSBastian Koppelmann         if ((flags & float_flag_invalid)
124daab3f7fSBastian Koppelmann             && !float32_is_any_nan(arg1)
125daab3f7fSBastian Koppelmann             && !float32_is_any_nan(arg2)) {
126daab3f7fSBastian Koppelmann                 f_result = MUL_NAN;
127daab3f7fSBastian Koppelmann         }
128daab3f7fSBastian Koppelmann         f_update_psw_flags(env, flags);
129daab3f7fSBastian Koppelmann     } else {
130daab3f7fSBastian Koppelmann         env->FPU_FS = 0;
131daab3f7fSBastian Koppelmann     }
132daab3f7fSBastian Koppelmann     return (uint32_t)f_result;
133daab3f7fSBastian Koppelmann 
134daab3f7fSBastian Koppelmann }
135446ee5b2SBastian Koppelmann 
136446ee5b2SBastian Koppelmann uint32_t helper_fdiv(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
137446ee5b2SBastian Koppelmann {
138446ee5b2SBastian Koppelmann     uint32_t flags;
139446ee5b2SBastian Koppelmann     float32 arg1 = make_float32(r1);
140446ee5b2SBastian Koppelmann     float32 arg2 = make_float32(r2);
141446ee5b2SBastian Koppelmann     float32 f_result;
142446ee5b2SBastian Koppelmann 
143446ee5b2SBastian Koppelmann     f_result = float32_div(arg1, arg2 , &env->fp_status);
144446ee5b2SBastian Koppelmann 
145446ee5b2SBastian Koppelmann     flags = f_get_excp_flags(env);
146446ee5b2SBastian Koppelmann     if (flags) {
147446ee5b2SBastian Koppelmann         /* If the output is a NaN, but the inputs aren't,
148446ee5b2SBastian Koppelmann            we return a unique value.  */
149446ee5b2SBastian Koppelmann         if ((flags & float_flag_invalid)
150446ee5b2SBastian Koppelmann             && !float32_is_any_nan(arg1)
151446ee5b2SBastian Koppelmann             && !float32_is_any_nan(arg2)) {
152446ee5b2SBastian Koppelmann                 f_result = DIV_NAN;
153446ee5b2SBastian Koppelmann         }
154446ee5b2SBastian Koppelmann         f_update_psw_flags(env, flags);
155446ee5b2SBastian Koppelmann     } else {
156446ee5b2SBastian Koppelmann         env->FPU_FS = 0;
157446ee5b2SBastian Koppelmann     }
158446ee5b2SBastian Koppelmann 
159446ee5b2SBastian Koppelmann     return (uint32_t)f_result;
160446ee5b2SBastian Koppelmann }
161743cd09dSBastian Koppelmann 
162743cd09dSBastian Koppelmann uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t r2)
163743cd09dSBastian Koppelmann {
164743cd09dSBastian Koppelmann     uint32_t result, flags;
165743cd09dSBastian Koppelmann     float32 arg1 = make_float32(r1);
166743cd09dSBastian Koppelmann     float32 arg2 = make_float32(r2);
167743cd09dSBastian Koppelmann 
168743cd09dSBastian Koppelmann     set_flush_inputs_to_zero(0, &env->fp_status);
169743cd09dSBastian Koppelmann 
170743cd09dSBastian Koppelmann     result = 1 << (float32_compare_quiet(arg1, arg2, &env->fp_status) + 1);
171743cd09dSBastian Koppelmann     result |= f_is_denormal(arg1) << 4;
172743cd09dSBastian Koppelmann     result |= f_is_denormal(arg2) << 5;
173743cd09dSBastian Koppelmann 
174743cd09dSBastian Koppelmann     flags = f_get_excp_flags(env);
175743cd09dSBastian Koppelmann     if (flags) {
176743cd09dSBastian Koppelmann         f_update_psw_flags(env, flags);
177743cd09dSBastian Koppelmann     } else {
178743cd09dSBastian Koppelmann         env->FPU_FS = 0;
179743cd09dSBastian Koppelmann     }
180743cd09dSBastian Koppelmann 
181743cd09dSBastian Koppelmann     set_flush_inputs_to_zero(1, &env->fp_status);
182743cd09dSBastian Koppelmann     return result;
183743cd09dSBastian Koppelmann }
1840d4c3b80SBastian Koppelmann 
1850d4c3b80SBastian Koppelmann uint32_t helper_ftoi(CPUTriCoreState *env, uint32_t arg)
1860d4c3b80SBastian Koppelmann {
1870d4c3b80SBastian Koppelmann     float32 f_arg = make_float32(arg);
1880d4c3b80SBastian Koppelmann     int32_t result, flags;
1890d4c3b80SBastian Koppelmann 
1900d4c3b80SBastian Koppelmann     result = float32_to_int32(f_arg, &env->fp_status);
1910d4c3b80SBastian Koppelmann 
1920d4c3b80SBastian Koppelmann     flags = f_get_excp_flags(env);
1930d4c3b80SBastian Koppelmann     if (flags) {
1940d4c3b80SBastian Koppelmann         if (float32_is_any_nan(f_arg)) {
1950d4c3b80SBastian Koppelmann             result = 0;
1960d4c3b80SBastian Koppelmann         }
1970d4c3b80SBastian Koppelmann         f_update_psw_flags(env, flags);
1980d4c3b80SBastian Koppelmann     } else {
1990d4c3b80SBastian Koppelmann         env->FPU_FS = 0;
2000d4c3b80SBastian Koppelmann     }
2010d4c3b80SBastian Koppelmann     return (uint32_t)result;
2020d4c3b80SBastian Koppelmann }
2030d4c3b80SBastian Koppelmann 
2040d4c3b80SBastian Koppelmann uint32_t helper_itof(CPUTriCoreState *env, uint32_t arg)
2050d4c3b80SBastian Koppelmann {
2060d4c3b80SBastian Koppelmann     float32 f_result;
2070d4c3b80SBastian Koppelmann     uint32_t flags;
2080d4c3b80SBastian Koppelmann     f_result = int32_to_float32(arg, &env->fp_status);
2090d4c3b80SBastian Koppelmann 
2100d4c3b80SBastian Koppelmann     flags = f_get_excp_flags(env);
2110d4c3b80SBastian Koppelmann     if (flags) {
2120d4c3b80SBastian Koppelmann         f_update_psw_flags(env, flags);
2130d4c3b80SBastian Koppelmann     } else {
2140d4c3b80SBastian Koppelmann         env->FPU_FS = 0;
2150d4c3b80SBastian Koppelmann     }
2160d4c3b80SBastian Koppelmann     return (uint32_t)f_result;
2170d4c3b80SBastian Koppelmann }
218