]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
2 | /*---------------------------------------------------------------------------+ |
3 | | reg_add_sub.c | | |
4 | | | | |
5 | | Functions to add or subtract two registers and put the result in a third. | | |
6 | | | | |
7 | | Copyright (C) 1992,1993,1997 | | |
8 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | | |
9 | | E-mail billm@suburbia.net | | |
10 | | | | |
11 | | | | |
12 | +---------------------------------------------------------------------------*/ | |
13 | ||
14 | /*---------------------------------------------------------------------------+ | |
15 | | For each function, the destination may be any FPU_REG, including one of | | |
16 | | the source FPU_REGs. | | |
17 | | Each function returns 0 if the answer is o.k., otherwise a non-zero | | |
18 | | value is returned, indicating either an exception condition or an | | |
19 | | internal error. | | |
20 | +---------------------------------------------------------------------------*/ | |
21 | ||
22 | #include "exception.h" | |
23 | #include "reg_constant.h" | |
24 | #include "fpu_emu.h" | |
25 | #include "control_w.h" | |
26 | #include "fpu_system.h" | |
27 | ||
28 | static | |
29 | int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, | |
30 | FPU_REG const *b, u_char tagb, u_char signb, | |
3d0d14f9 | 31 | FPU_REG * dest, int deststnr, int control_w); |
1da177e4 LT |
32 | |
33 | /* | |
34 | Operates on st(0) and st(n), or on st(0) and temporary data. | |
35 | The destination must be one of the source st(x). | |
36 | */ | |
37 | int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w) | |
38 | { | |
3d0d14f9 IM |
39 | FPU_REG *a = &st(0); |
40 | FPU_REG *dest = &st(deststnr); | |
41 | u_char signb = getsign(b); | |
42 | u_char taga = FPU_gettag0(); | |
43 | u_char signa = getsign(a); | |
44 | u_char saved_sign = getsign(dest); | |
45 | int diff, tag, expa, expb; | |
46 | ||
47 | if (!(taga | tagb)) { | |
48 | expa = exponent(a); | |
49 | expb = exponent(b); | |
50 | ||
51 | valid_add: | |
52 | /* Both registers are valid */ | |
53 | if (!(signa ^ signb)) { | |
54 | /* signs are the same */ | |
55 | tag = | |
56 | FPU_u_add(a, b, dest, control_w, signa, expa, expb); | |
57 | } else { | |
58 | /* The signs are different, so do a subtraction */ | |
59 | diff = expa - expb; | |
60 | if (!diff) { | |
61 | diff = a->sigh - b->sigh; /* This works only if the ms bits | |
62 | are identical. */ | |
63 | if (!diff) { | |
64 | diff = a->sigl > b->sigl; | |
65 | if (!diff) | |
66 | diff = -(a->sigl < b->sigl); | |
67 | } | |
68 | } | |
69 | ||
70 | if (diff > 0) { | |
71 | tag = | |
72 | FPU_u_sub(a, b, dest, control_w, signa, | |
73 | expa, expb); | |
74 | } else if (diff < 0) { | |
75 | tag = | |
76 | FPU_u_sub(b, a, dest, control_w, signb, | |
77 | expb, expa); | |
78 | } else { | |
79 | FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); | |
80 | /* sign depends upon rounding mode */ | |
81 | setsign(dest, ((control_w & CW_RC) != RC_DOWN) | |
82 | ? SIGN_POS : SIGN_NEG); | |
83 | return TAG_Zero; | |
84 | } | |
1da177e4 | 85 | } |
1da177e4 | 86 | |
3d0d14f9 IM |
87 | if (tag < 0) { |
88 | setsign(dest, saved_sign); | |
89 | return tag; | |
90 | } | |
91 | FPU_settagi(deststnr, tag); | |
92 | return tag; | |
1da177e4 | 93 | } |
1da177e4 | 94 | |
3d0d14f9 IM |
95 | if (taga == TAG_Special) |
96 | taga = FPU_Special(a); | |
97 | if (tagb == TAG_Special) | |
98 | tagb = FPU_Special(b); | |
1da177e4 | 99 | |
3d0d14f9 | 100 | if (((taga == TAG_Valid) && (tagb == TW_Denormal)) |
1da177e4 | 101 | || ((taga == TW_Denormal) && (tagb == TAG_Valid)) |
3d0d14f9 IM |
102 | || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { |
103 | FPU_REG x, y; | |
104 | ||
105 | if (denormal_operand() < 0) | |
106 | return FPU_Exception; | |
107 | ||
108 | FPU_to_exp16(a, &x); | |
109 | FPU_to_exp16(b, &y); | |
110 | a = &x; | |
111 | b = &y; | |
112 | expa = exponent16(a); | |
113 | expb = exponent16(b); | |
114 | goto valid_add; | |
115 | } | |
1da177e4 | 116 | |
3d0d14f9 IM |
117 | if ((taga == TW_NaN) || (tagb == TW_NaN)) { |
118 | if (deststnr == 0) | |
119 | return real_2op_NaN(b, tagb, deststnr, a); | |
120 | else | |
121 | return real_2op_NaN(a, taga, deststnr, a); | |
122 | } | |
1da177e4 | 123 | |
3d0d14f9 IM |
124 | return add_sub_specials(a, taga, signa, b, tagb, signb, |
125 | dest, deststnr, control_w); | |
1da177e4 LT |
126 | } |
127 | ||
1da177e4 LT |
128 | /* Subtract b from a. (a-b) -> dest */ |
129 | int FPU_sub(int flags, int rm, int control_w) | |
130 | { | |
3d0d14f9 IM |
131 | FPU_REG const *a, *b; |
132 | FPU_REG *dest; | |
133 | u_char taga, tagb, signa, signb, saved_sign, sign; | |
134 | int diff, tag = 0, expa, expb, deststnr; | |
135 | ||
136 | a = &st(0); | |
137 | taga = FPU_gettag0(); | |
138 | ||
139 | deststnr = 0; | |
140 | if (flags & LOADED) { | |
141 | b = (FPU_REG *) rm; | |
142 | tagb = flags & 0x0f; | |
143 | } else { | |
144 | b = &st(rm); | |
145 | tagb = FPU_gettagi(rm); | |
146 | ||
147 | if (flags & DEST_RM) | |
148 | deststnr = rm; | |
1da177e4 LT |
149 | } |
150 | ||
3d0d14f9 IM |
151 | signa = getsign(a); |
152 | signb = getsign(b); | |
153 | ||
154 | if (flags & REV) { | |
155 | signa ^= SIGN_NEG; | |
156 | signb ^= SIGN_NEG; | |
157 | } | |
158 | ||
159 | dest = &st(deststnr); | |
160 | saved_sign = getsign(dest); | |
161 | ||
162 | if (!(taga | tagb)) { | |
163 | expa = exponent(a); | |
164 | expb = exponent(b); | |
165 | ||
166 | valid_subtract: | |
167 | /* Both registers are valid */ | |
168 | ||
169 | diff = expa - expb; | |
170 | ||
171 | if (!diff) { | |
172 | diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ | |
173 | if (!diff) { | |
174 | diff = a->sigl > b->sigl; | |
175 | if (!diff) | |
176 | diff = -(a->sigl < b->sigl); | |
177 | } | |
178 | } | |
179 | ||
180 | switch ((((int)signa) * 2 + signb) / SIGN_NEG) { | |
181 | case 0: /* P - P */ | |
182 | case 3: /* N - N */ | |
183 | if (diff > 0) { | |
184 | /* |a| > |b| */ | |
185 | tag = | |
186 | FPU_u_sub(a, b, dest, control_w, signa, | |
187 | expa, expb); | |
188 | } else if (diff == 0) { | |
189 | FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); | |
190 | ||
191 | /* sign depends upon rounding mode */ | |
192 | setsign(dest, ((control_w & CW_RC) != RC_DOWN) | |
193 | ? SIGN_POS : SIGN_NEG); | |
194 | return TAG_Zero; | |
195 | } else { | |
196 | sign = signa ^ SIGN_NEG; | |
197 | tag = | |
198 | FPU_u_sub(b, a, dest, control_w, sign, expb, | |
199 | expa); | |
200 | } | |
201 | break; | |
202 | case 1: /* P - N */ | |
203 | tag = | |
204 | FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, | |
205 | expb); | |
206 | break; | |
207 | case 2: /* N - P */ | |
208 | tag = | |
209 | FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, | |
210 | expb); | |
211 | break; | |
1da177e4 | 212 | #ifdef PARANOID |
3d0d14f9 IM |
213 | default: |
214 | EXCEPTION(EX_INTERNAL | 0x111); | |
215 | return -1; | |
1da177e4 | 216 | #endif |
3d0d14f9 IM |
217 | } |
218 | if (tag < 0) { | |
219 | setsign(dest, saved_sign); | |
220 | return tag; | |
221 | } | |
222 | FPU_settagi(deststnr, tag); | |
223 | return tag; | |
1da177e4 | 224 | } |
1da177e4 | 225 | |
3d0d14f9 IM |
226 | if (taga == TAG_Special) |
227 | taga = FPU_Special(a); | |
228 | if (tagb == TAG_Special) | |
229 | tagb = FPU_Special(b); | |
1da177e4 | 230 | |
3d0d14f9 | 231 | if (((taga == TAG_Valid) && (tagb == TW_Denormal)) |
1da177e4 | 232 | || ((taga == TW_Denormal) && (tagb == TAG_Valid)) |
3d0d14f9 IM |
233 | || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { |
234 | FPU_REG x, y; | |
1da177e4 | 235 | |
3d0d14f9 IM |
236 | if (denormal_operand() < 0) |
237 | return FPU_Exception; | |
238 | ||
239 | FPU_to_exp16(a, &x); | |
240 | FPU_to_exp16(b, &y); | |
241 | a = &x; | |
242 | b = &y; | |
243 | expa = exponent16(a); | |
244 | expb = exponent16(b); | |
1da177e4 | 245 | |
3d0d14f9 | 246 | goto valid_subtract; |
1da177e4 | 247 | } |
3d0d14f9 IM |
248 | |
249 | if ((taga == TW_NaN) || (tagb == TW_NaN)) { | |
250 | FPU_REG const *d1, *d2; | |
251 | if (flags & REV) { | |
252 | d1 = b; | |
253 | d2 = a; | |
254 | } else { | |
255 | d1 = a; | |
256 | d2 = b; | |
257 | } | |
258 | if (flags & LOADED) | |
259 | return real_2op_NaN(b, tagb, deststnr, d1); | |
260 | if (flags & DEST_RM) | |
261 | return real_2op_NaN(a, taga, deststnr, d2); | |
262 | else | |
263 | return real_2op_NaN(b, tagb, deststnr, d2); | |
1da177e4 | 264 | } |
1da177e4 | 265 | |
3d0d14f9 IM |
266 | return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG, |
267 | dest, deststnr, control_w); | |
268 | } | |
1da177e4 LT |
269 | |
270 | static | |
271 | int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, | |
272 | FPU_REG const *b, u_char tagb, u_char signb, | |
3d0d14f9 | 273 | FPU_REG * dest, int deststnr, int control_w) |
1da177e4 | 274 | { |
3d0d14f9 IM |
275 | if (((taga == TW_Denormal) || (tagb == TW_Denormal)) |
276 | && (denormal_operand() < 0)) | |
277 | return FPU_Exception; | |
278 | ||
279 | if (taga == TAG_Zero) { | |
280 | if (tagb == TAG_Zero) { | |
281 | /* Both are zero, result will be zero. */ | |
282 | u_char different_signs = signa ^ signb; | |
283 | ||
284 | FPU_copy_to_regi(a, TAG_Zero, deststnr); | |
285 | if (different_signs) { | |
286 | /* Signs are different. */ | |
287 | /* Sign of answer depends upon rounding mode. */ | |
288 | setsign(dest, ((control_w & CW_RC) != RC_DOWN) | |
289 | ? SIGN_POS : SIGN_NEG); | |
290 | } else | |
291 | setsign(dest, signa); /* signa may differ from the sign of a. */ | |
292 | return TAG_Zero; | |
293 | } else { | |
294 | reg_copy(b, dest); | |
295 | if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) { | |
296 | /* A pseudoDenormal, convert it. */ | |
297 | addexponent(dest, 1); | |
298 | tagb = TAG_Valid; | |
299 | } else if (tagb > TAG_Empty) | |
300 | tagb = TAG_Special; | |
301 | setsign(dest, signb); /* signb may differ from the sign of b. */ | |
302 | FPU_settagi(deststnr, tagb); | |
303 | return tagb; | |
304 | } | |
305 | } else if (tagb == TAG_Zero) { | |
306 | reg_copy(a, dest); | |
307 | if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) { | |
308 | /* A pseudoDenormal */ | |
309 | addexponent(dest, 1); | |
310 | taga = TAG_Valid; | |
311 | } else if (taga > TAG_Empty) | |
312 | taga = TAG_Special; | |
313 | setsign(dest, signa); /* signa may differ from the sign of a. */ | |
314 | FPU_settagi(deststnr, taga); | |
315 | return taga; | |
316 | } else if (taga == TW_Infinity) { | |
317 | if ((tagb != TW_Infinity) || (signa == signb)) { | |
318 | FPU_copy_to_regi(a, TAG_Special, deststnr); | |
319 | setsign(dest, signa); /* signa may differ from the sign of a. */ | |
320 | return taga; | |
321 | } | |
322 | /* Infinity-Infinity is undefined. */ | |
323 | return arith_invalid(deststnr); | |
324 | } else if (tagb == TW_Infinity) { | |
325 | FPU_copy_to_regi(b, TAG_Special, deststnr); | |
326 | setsign(dest, signb); /* signb may differ from the sign of b. */ | |
327 | return tagb; | |
1da177e4 | 328 | } |
1da177e4 | 329 | #ifdef PARANOID |
3d0d14f9 | 330 | EXCEPTION(EX_INTERNAL | 0x101); |
1da177e4 LT |
331 | #endif |
332 | ||
3d0d14f9 | 333 | return FPU_Exception; |
1da177e4 | 334 | } |