]>
Commit | Line | Data |
---|---|---|
996a729f BK |
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 | ||
baf410dc | 50 | static void f_update_psw_flags(CPUTriCoreState *env, uint8_t flags) |
996a729f BK |
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 | } | |
baf410dc BK |
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) | |
daab3f7f BK |
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 | } | |
446ee5b2 BK |
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 | } | |
743cd09d BK |
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 | } | |
0d4c3b80 BK |
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 | } |