1 /*---------------------------------------------------------------------------+
4 | Functions to add or subtract two registers and put the result in a third. |
6 | Copyright (C) 1992,1993,1997 |
7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8 | E-mail billm@suburbia.net |
11 +---------------------------------------------------------------------------*/
13 /*---------------------------------------------------------------------------+
14 | For each function, the destination may be any FPU_REG, including one of |
15 | the source FPU_REGs. |
16 | Each function returns 0 if the answer is o.k., otherwise a non-zero |
17 | value is returned, indicating either an exception condition or an |
19 +---------------------------------------------------------------------------*/
21 #include "exception.h"
22 #include "reg_constant.h"
24 #include "control_w.h"
25 #include "fpu_system.h"
28 int add_sub_specials(FPU_REG
const *a
, u_char taga
, u_char signa
,
29 FPU_REG
const *b
, u_char tagb
, u_char signb
,
30 FPU_REG
*dest
, int deststnr
, int control_w
);
33 Operates on st(0) and st(n), or on st(0) and temporary data.
34 The destination must be one of the source st(x).
36 int FPU_add(FPU_REG
const *b
, u_char tagb
, int deststnr
, int control_w
)
39 FPU_REG
*dest
= &st(deststnr
);
40 u_char signb
= getsign(b
);
41 u_char taga
= FPU_gettag0();
42 u_char signa
= getsign(a
);
43 u_char saved_sign
= getsign(dest
);
44 int diff
, tag
, expa
, expb
;
52 /* Both registers are valid */
55 /* signs are the same */
56 tag
= FPU_u_add(a
, b
, dest
, control_w
, signa
, expa
, expb
);
60 /* The signs are different, so do a subtraction */
64 diff
= a
->sigh
- b
->sigh
; /* This works only if the ms bits
68 diff
= a
->sigl
> b
->sigl
;
70 diff
= -(a
->sigl
< b
->sigl
);
76 tag
= FPU_u_sub(a
, b
, dest
, control_w
, signa
, expa
, expb
);
80 tag
= FPU_u_sub(b
, a
, dest
, control_w
, signb
, expb
, expa
);
84 FPU_copy_to_regi(&CONST_Z
, TAG_Zero
, deststnr
);
85 /* sign depends upon rounding mode */
86 setsign(dest
, ((control_w
& CW_RC
) != RC_DOWN
)
87 ? SIGN_POS
: SIGN_NEG
);
94 setsign(dest
, saved_sign
);
97 FPU_settagi(deststnr
, tag
);
101 if ( taga
== TAG_Special
)
102 taga
= FPU_Special(a
);
103 if ( tagb
== TAG_Special
)
104 tagb
= FPU_Special(b
);
106 if ( ((taga
== TAG_Valid
) && (tagb
== TW_Denormal
))
107 || ((taga
== TW_Denormal
) && (tagb
== TAG_Valid
))
108 || ((taga
== TW_Denormal
) && (tagb
== TW_Denormal
)) )
112 if ( denormal_operand() < 0 )
113 return FPU_Exception
;
119 expa
= exponent16(a
);
120 expb
= exponent16(b
);
124 if ( (taga
== TW_NaN
) || (tagb
== TW_NaN
) )
127 return real_2op_NaN(b
, tagb
, deststnr
, a
);
129 return real_2op_NaN(a
, taga
, deststnr
, a
);
132 return add_sub_specials(a
, taga
, signa
, b
, tagb
, signb
,
133 dest
, deststnr
, control_w
);
137 /* Subtract b from a. (a-b) -> dest */
138 int FPU_sub(int flags
, int rm
, int control_w
)
140 FPU_REG
const *a
, *b
;
142 u_char taga
, tagb
, signa
, signb
, saved_sign
, sign
;
143 int diff
, tag
, expa
, expb
, deststnr
;
146 taga
= FPU_gettag0();
149 if ( flags
& LOADED
)
157 tagb
= FPU_gettagi(rm
);
159 if ( flags
& DEST_RM
)
172 dest
= &st(deststnr
);
173 saved_sign
= getsign(dest
);
175 if ( !(taga
| tagb
) )
181 /* Both registers are valid */
187 diff
= a
->sigh
- b
->sigh
; /* Works only if ms bits are identical */
190 diff
= a
->sigl
> b
->sigl
;
192 diff
= -(a
->sigl
< b
->sigl
);
196 switch ( (((int)signa
)*2 + signb
) / SIGN_NEG
)
203 tag
= FPU_u_sub(a
, b
, dest
, control_w
, signa
, expa
, expb
);
205 else if ( diff
== 0 )
207 FPU_copy_to_regi(&CONST_Z
, TAG_Zero
, deststnr
);
209 /* sign depends upon rounding mode */
210 setsign(dest
, ((control_w
& CW_RC
) != RC_DOWN
)
211 ? SIGN_POS
: SIGN_NEG
);
216 sign
= signa
^ SIGN_NEG
;
217 tag
= FPU_u_sub(b
, a
, dest
, control_w
, sign
, expb
, expa
);
221 tag
= FPU_u_add(a
, b
, dest
, control_w
, SIGN_POS
, expa
, expb
);
224 tag
= FPU_u_add(a
, b
, dest
, control_w
, SIGN_NEG
, expa
, expb
);
228 EXCEPTION(EX_INTERNAL
|0x111);
234 setsign(dest
, saved_sign
);
237 FPU_settagi(deststnr
, tag
);
241 if ( taga
== TAG_Special
)
242 taga
= FPU_Special(a
);
243 if ( tagb
== TAG_Special
)
244 tagb
= FPU_Special(b
);
246 if ( ((taga
== TAG_Valid
) && (tagb
== TW_Denormal
))
247 || ((taga
== TW_Denormal
) && (tagb
== TAG_Valid
))
248 || ((taga
== TW_Denormal
) && (tagb
== TW_Denormal
)) )
252 if ( denormal_operand() < 0 )
253 return FPU_Exception
;
259 expa
= exponent16(a
);
260 expb
= exponent16(b
);
265 if ( (taga
== TW_NaN
) || (tagb
== TW_NaN
) )
267 FPU_REG
const *d1
, *d2
;
278 if ( flags
& LOADED
)
279 return real_2op_NaN(b
, tagb
, deststnr
, d1
);
280 if ( flags
& DEST_RM
)
281 return real_2op_NaN(a
, taga
, deststnr
, d2
);
283 return real_2op_NaN(b
, tagb
, deststnr
, d2
);
286 return add_sub_specials(a
, taga
, signa
, b
, tagb
, signb
^ SIGN_NEG
,
287 dest
, deststnr
, control_w
);
292 int add_sub_specials(FPU_REG
const *a
, u_char taga
, u_char signa
,
293 FPU_REG
const *b
, u_char tagb
, u_char signb
,
294 FPU_REG
*dest
, int deststnr
, int control_w
)
296 if ( ((taga
== TW_Denormal
) || (tagb
== TW_Denormal
))
297 && (denormal_operand() < 0) )
298 return FPU_Exception
;
300 if (taga
== TAG_Zero
)
302 if (tagb
== TAG_Zero
)
304 /* Both are zero, result will be zero. */
305 u_char different_signs
= signa
^ signb
;
307 FPU_copy_to_regi(a
, TAG_Zero
, deststnr
);
308 if ( different_signs
)
310 /* Signs are different. */
311 /* Sign of answer depends upon rounding mode. */
312 setsign(dest
, ((control_w
& CW_RC
) != RC_DOWN
)
313 ? SIGN_POS
: SIGN_NEG
);
316 setsign(dest
, signa
); /* signa may differ from the sign of a. */
322 if ( (tagb
== TW_Denormal
) && (b
->sigh
& 0x80000000) )
324 /* A pseudoDenormal, convert it. */
325 addexponent(dest
, 1);
328 else if ( tagb
> TAG_Empty
)
330 setsign(dest
, signb
); /* signb may differ from the sign of b. */
331 FPU_settagi(deststnr
, tagb
);
335 else if (tagb
== TAG_Zero
)
338 if ( (taga
== TW_Denormal
) && (a
->sigh
& 0x80000000) )
340 /* A pseudoDenormal */
341 addexponent(dest
, 1);
344 else if ( taga
> TAG_Empty
)
346 setsign(dest
, signa
); /* signa may differ from the sign of a. */
347 FPU_settagi(deststnr
, taga
);
350 else if (taga
== TW_Infinity
)
352 if ( (tagb
!= TW_Infinity
) || (signa
== signb
) )
354 FPU_copy_to_regi(a
, TAG_Special
, deststnr
);
355 setsign(dest
, signa
); /* signa may differ from the sign of a. */
358 /* Infinity-Infinity is undefined. */
359 return arith_invalid(deststnr
);
361 else if (tagb
== TW_Infinity
)
363 FPU_copy_to_regi(b
, TAG_Special
, deststnr
);
364 setsign(dest
, signb
); /* signb may differ from the sign of b. */
369 EXCEPTION(EX_INTERNAL
|0x101);
372 return FPU_Exception
;