diff options
Diffstat (limited to 'arch/i386/math-emu/reg_compare.c')
-rw-r--r-- | arch/i386/math-emu/reg_compare.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/arch/i386/math-emu/reg_compare.c b/arch/i386/math-emu/reg_compare.c new file mode 100644 index 000000000000..f37c5b5a35ad --- /dev/null +++ b/arch/i386/math-emu/reg_compare.c | |||
@@ -0,0 +1,381 @@ | |||
1 | /*---------------------------------------------------------------------------+ | ||
2 | | reg_compare.c | | ||
3 | | | | ||
4 | | Compare two floating point registers | | ||
5 | | | | ||
6 | | Copyright (C) 1992,1993,1994,1997 | | ||
7 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | | ||
8 | | E-mail billm@suburbia.net | | ||
9 | | | | ||
10 | | | | ||
11 | +---------------------------------------------------------------------------*/ | ||
12 | |||
13 | /*---------------------------------------------------------------------------+ | ||
14 | | compare() is the core FPU_REG comparison function | | ||
15 | +---------------------------------------------------------------------------*/ | ||
16 | |||
17 | #include "fpu_system.h" | ||
18 | #include "exception.h" | ||
19 | #include "fpu_emu.h" | ||
20 | #include "control_w.h" | ||
21 | #include "status_w.h" | ||
22 | |||
23 | |||
24 | static int compare(FPU_REG const *b, int tagb) | ||
25 | { | ||
26 | int diff, exp0, expb; | ||
27 | u_char st0_tag; | ||
28 | FPU_REG *st0_ptr; | ||
29 | FPU_REG x, y; | ||
30 | u_char st0_sign, signb = getsign(b); | ||
31 | |||
32 | st0_ptr = &st(0); | ||
33 | st0_tag = FPU_gettag0(); | ||
34 | st0_sign = getsign(st0_ptr); | ||
35 | |||
36 | if ( tagb == TAG_Special ) | ||
37 | tagb = FPU_Special(b); | ||
38 | if ( st0_tag == TAG_Special ) | ||
39 | st0_tag = FPU_Special(st0_ptr); | ||
40 | |||
41 | if ( ((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal)) | ||
42 | || ((tagb != TAG_Valid) && (tagb != TW_Denormal)) ) | ||
43 | { | ||
44 | if ( st0_tag == TAG_Zero ) | ||
45 | { | ||
46 | if ( tagb == TAG_Zero ) return COMP_A_eq_B; | ||
47 | if ( tagb == TAG_Valid ) | ||
48 | return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); | ||
49 | if ( tagb == TW_Denormal ) | ||
50 | return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | ||
51 | | COMP_Denormal; | ||
52 | } | ||
53 | else if ( tagb == TAG_Zero ) | ||
54 | { | ||
55 | if ( st0_tag == TAG_Valid ) | ||
56 | return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); | ||
57 | if ( st0_tag == TW_Denormal ) | ||
58 | return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | ||
59 | | COMP_Denormal; | ||
60 | } | ||
61 | |||
62 | if ( st0_tag == TW_Infinity ) | ||
63 | { | ||
64 | if ( (tagb == TAG_Valid) || (tagb == TAG_Zero) ) | ||
65 | return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); | ||
66 | else if ( tagb == TW_Denormal ) | ||
67 | return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | ||
68 | | COMP_Denormal; | ||
69 | else if ( tagb == TW_Infinity ) | ||
70 | { | ||
71 | /* The 80486 book says that infinities can be equal! */ | ||
72 | return (st0_sign == signb) ? COMP_A_eq_B : | ||
73 | ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); | ||
74 | } | ||
75 | /* Fall through to the NaN code */ | ||
76 | } | ||
77 | else if ( tagb == TW_Infinity ) | ||
78 | { | ||
79 | if ( (st0_tag == TAG_Valid) || (st0_tag == TAG_Zero) ) | ||
80 | return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); | ||
81 | if ( st0_tag == TW_Denormal ) | ||
82 | return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | ||
83 | | COMP_Denormal; | ||
84 | /* Fall through to the NaN code */ | ||
85 | } | ||
86 | |||
87 | /* The only possibility now should be that one of the arguments | ||
88 | is a NaN */ | ||
89 | if ( (st0_tag == TW_NaN) || (tagb == TW_NaN) ) | ||
90 | { | ||
91 | int signalling = 0, unsupported = 0; | ||
92 | if ( st0_tag == TW_NaN ) | ||
93 | { | ||
94 | signalling = (st0_ptr->sigh & 0xc0000000) == 0x80000000; | ||
95 | unsupported = !((exponent(st0_ptr) == EXP_OVER) | ||
96 | && (st0_ptr->sigh & 0x80000000)); | ||
97 | } | ||
98 | if ( tagb == TW_NaN ) | ||
99 | { | ||
100 | signalling |= (b->sigh & 0xc0000000) == 0x80000000; | ||
101 | unsupported |= !((exponent(b) == EXP_OVER) | ||
102 | && (b->sigh & 0x80000000)); | ||
103 | } | ||
104 | if ( signalling || unsupported ) | ||
105 | return COMP_No_Comp | COMP_SNaN | COMP_NaN; | ||
106 | else | ||
107 | /* Neither is a signaling NaN */ | ||
108 | return COMP_No_Comp | COMP_NaN; | ||
109 | } | ||
110 | |||
111 | EXCEPTION(EX_Invalid); | ||
112 | } | ||
113 | |||
114 | if (st0_sign != signb) | ||
115 | { | ||
116 | return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | ||
117 | | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | ||
118 | COMP_Denormal : 0); | ||
119 | } | ||
120 | |||
121 | if ( (st0_tag == TW_Denormal) || (tagb == TW_Denormal) ) | ||
122 | { | ||
123 | FPU_to_exp16(st0_ptr, &x); | ||
124 | FPU_to_exp16(b, &y); | ||
125 | st0_ptr = &x; | ||
126 | b = &y; | ||
127 | exp0 = exponent16(st0_ptr); | ||
128 | expb = exponent16(b); | ||
129 | } | ||
130 | else | ||
131 | { | ||
132 | exp0 = exponent(st0_ptr); | ||
133 | expb = exponent(b); | ||
134 | } | ||
135 | |||
136 | #ifdef PARANOID | ||
137 | if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); | ||
138 | if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); | ||
139 | #endif /* PARANOID */ | ||
140 | |||
141 | diff = exp0 - expb; | ||
142 | if ( diff == 0 ) | ||
143 | { | ||
144 | diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are | ||
145 | identical */ | ||
146 | if ( diff == 0 ) | ||
147 | { | ||
148 | diff = st0_ptr->sigl > b->sigl; | ||
149 | if ( diff == 0 ) | ||
150 | diff = -(st0_ptr->sigl < b->sigl); | ||
151 | } | ||
152 | } | ||
153 | |||
154 | if ( diff > 0 ) | ||
155 | { | ||
156 | return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | ||
157 | | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | ||
158 | COMP_Denormal : 0); | ||
159 | } | ||
160 | if ( diff < 0 ) | ||
161 | { | ||
162 | return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | ||
163 | | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | ||
164 | COMP_Denormal : 0); | ||
165 | } | ||
166 | |||
167 | return COMP_A_eq_B | ||
168 | | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | ||
169 | COMP_Denormal : 0); | ||
170 | |||
171 | } | ||
172 | |||
173 | |||
174 | /* This function requires that st(0) is not empty */ | ||
175 | int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag) | ||
176 | { | ||
177 | int f = 0, c; | ||
178 | |||
179 | c = compare(loaded_data, loaded_tag); | ||
180 | |||
181 | if (c & COMP_NaN) | ||
182 | { | ||
183 | EXCEPTION(EX_Invalid); | ||
184 | f = SW_C3 | SW_C2 | SW_C0; | ||
185 | } | ||
186 | else | ||
187 | switch (c & 7) | ||
188 | { | ||
189 | case COMP_A_lt_B: | ||
190 | f = SW_C0; | ||
191 | break; | ||
192 | case COMP_A_eq_B: | ||
193 | f = SW_C3; | ||
194 | break; | ||
195 | case COMP_A_gt_B: | ||
196 | f = 0; | ||
197 | break; | ||
198 | case COMP_No_Comp: | ||
199 | f = SW_C3 | SW_C2 | SW_C0; | ||
200 | break; | ||
201 | #ifdef PARANOID | ||
202 | default: | ||
203 | EXCEPTION(EX_INTERNAL|0x121); | ||
204 | f = SW_C3 | SW_C2 | SW_C0; | ||
205 | break; | ||
206 | #endif /* PARANOID */ | ||
207 | } | ||
208 | setcc(f); | ||
209 | if (c & COMP_Denormal) | ||
210 | { | ||
211 | return denormal_operand() < 0; | ||
212 | } | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | |||
217 | static int compare_st_st(int nr) | ||
218 | { | ||
219 | int f = 0, c; | ||
220 | FPU_REG *st_ptr; | ||
221 | |||
222 | if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) | ||
223 | { | ||
224 | setcc(SW_C3 | SW_C2 | SW_C0); | ||
225 | /* Stack fault */ | ||
226 | EXCEPTION(EX_StackUnder); | ||
227 | return !(control_word & CW_Invalid); | ||
228 | } | ||
229 | |||
230 | st_ptr = &st(nr); | ||
231 | c = compare(st_ptr, FPU_gettagi(nr)); | ||
232 | if (c & COMP_NaN) | ||
233 | { | ||
234 | setcc(SW_C3 | SW_C2 | SW_C0); | ||
235 | EXCEPTION(EX_Invalid); | ||
236 | return !(control_word & CW_Invalid); | ||
237 | } | ||
238 | else | ||
239 | switch (c & 7) | ||
240 | { | ||
241 | case COMP_A_lt_B: | ||
242 | f = SW_C0; | ||
243 | break; | ||
244 | case COMP_A_eq_B: | ||
245 | f = SW_C3; | ||
246 | break; | ||
247 | case COMP_A_gt_B: | ||
248 | f = 0; | ||
249 | break; | ||
250 | case COMP_No_Comp: | ||
251 | f = SW_C3 | SW_C2 | SW_C0; | ||
252 | break; | ||
253 | #ifdef PARANOID | ||
254 | default: | ||
255 | EXCEPTION(EX_INTERNAL|0x122); | ||
256 | f = SW_C3 | SW_C2 | SW_C0; | ||
257 | break; | ||
258 | #endif /* PARANOID */ | ||
259 | } | ||
260 | setcc(f); | ||
261 | if (c & COMP_Denormal) | ||
262 | { | ||
263 | return denormal_operand() < 0; | ||
264 | } | ||
265 | return 0; | ||
266 | } | ||
267 | |||
268 | |||
269 | static int compare_u_st_st(int nr) | ||
270 | { | ||
271 | int f = 0, c; | ||
272 | FPU_REG *st_ptr; | ||
273 | |||
274 | if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) | ||
275 | { | ||
276 | setcc(SW_C3 | SW_C2 | SW_C0); | ||
277 | /* Stack fault */ | ||
278 | EXCEPTION(EX_StackUnder); | ||
279 | return !(control_word & CW_Invalid); | ||
280 | } | ||
281 | |||
282 | st_ptr = &st(nr); | ||
283 | c = compare(st_ptr, FPU_gettagi(nr)); | ||
284 | if (c & COMP_NaN) | ||
285 | { | ||
286 | setcc(SW_C3 | SW_C2 | SW_C0); | ||
287 | if (c & COMP_SNaN) /* This is the only difference between | ||
288 | un-ordered and ordinary comparisons */ | ||
289 | { | ||
290 | EXCEPTION(EX_Invalid); | ||
291 | return !(control_word & CW_Invalid); | ||
292 | } | ||
293 | return 0; | ||
294 | } | ||
295 | else | ||
296 | switch (c & 7) | ||
297 | { | ||
298 | case COMP_A_lt_B: | ||
299 | f = SW_C0; | ||
300 | break; | ||
301 | case COMP_A_eq_B: | ||
302 | f = SW_C3; | ||
303 | break; | ||
304 | case COMP_A_gt_B: | ||
305 | f = 0; | ||
306 | break; | ||
307 | case COMP_No_Comp: | ||
308 | f = SW_C3 | SW_C2 | SW_C0; | ||
309 | break; | ||
310 | #ifdef PARANOID | ||
311 | default: | ||
312 | EXCEPTION(EX_INTERNAL|0x123); | ||
313 | f = SW_C3 | SW_C2 | SW_C0; | ||
314 | break; | ||
315 | #endif /* PARANOID */ | ||
316 | } | ||
317 | setcc(f); | ||
318 | if (c & COMP_Denormal) | ||
319 | { | ||
320 | return denormal_operand() < 0; | ||
321 | } | ||
322 | return 0; | ||
323 | } | ||
324 | |||
325 | /*---------------------------------------------------------------------------*/ | ||
326 | |||
327 | void fcom_st(void) | ||
328 | { | ||
329 | /* fcom st(i) */ | ||
330 | compare_st_st(FPU_rm); | ||
331 | } | ||
332 | |||
333 | |||
334 | void fcompst(void) | ||
335 | { | ||
336 | /* fcomp st(i) */ | ||
337 | if ( !compare_st_st(FPU_rm) ) | ||
338 | FPU_pop(); | ||
339 | } | ||
340 | |||
341 | |||
342 | void fcompp(void) | ||
343 | { | ||
344 | /* fcompp */ | ||
345 | if (FPU_rm != 1) | ||
346 | { | ||
347 | FPU_illegal(); | ||
348 | return; | ||
349 | } | ||
350 | if ( !compare_st_st(1) ) | ||
351 | poppop(); | ||
352 | } | ||
353 | |||
354 | |||
355 | void fucom_(void) | ||
356 | { | ||
357 | /* fucom st(i) */ | ||
358 | compare_u_st_st(FPU_rm); | ||
359 | |||
360 | } | ||
361 | |||
362 | |||
363 | void fucomp(void) | ||
364 | { | ||
365 | /* fucomp st(i) */ | ||
366 | if ( !compare_u_st_st(FPU_rm) ) | ||
367 | FPU_pop(); | ||
368 | } | ||
369 | |||
370 | |||
371 | void fucompp(void) | ||
372 | { | ||
373 | /* fucompp */ | ||
374 | if (FPU_rm == 1) | ||
375 | { | ||
376 | if ( !compare_u_st_st(1) ) | ||
377 | poppop(); | ||
378 | } | ||
379 | else | ||
380 | FPU_illegal(); | ||
381 | } | ||