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
compare(FPU_REG const * b,int tagb)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 */
FPU_compare_st_data(FPU_REG const * loaded_data,u_char loaded_tag)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
compare_st_st(int nr)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
compare_u_st_st(int nr)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
fcom_st()327 void fcom_st()
328 {
329 /* fcom st(i) */
330 compare_st_st(FPU_rm);
331 }
332
333
fcompst()334 void fcompst()
335 {
336 /* fcomp st(i) */
337 if ( !compare_st_st(FPU_rm) )
338 FPU_pop();
339 }
340
341
fcompp()342 void fcompp()
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
fucom_()355 void fucom_()
356 {
357 /* fucom st(i) */
358 compare_u_st_st(FPU_rm);
359
360 }
361
362
fucomp()363 void fucomp()
364 {
365 /* fucomp st(i) */
366 if ( !compare_u_st_st(FPU_rm) )
367 FPU_pop();
368 }
369
370
fucompp()371 void fucompp()
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 }
382