diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/i386/math-emu/reg_add_sub.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'arch/i386/math-emu/reg_add_sub.c')
-rw-r--r-- | arch/i386/math-emu/reg_add_sub.c | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/arch/i386/math-emu/reg_add_sub.c b/arch/i386/math-emu/reg_add_sub.c new file mode 100644 index 000000000000..7cd3b37ac084 --- /dev/null +++ b/arch/i386/math-emu/reg_add_sub.c | |||
@@ -0,0 +1,374 @@ | |||
1 | /*---------------------------------------------------------------------------+ | ||
2 | | reg_add_sub.c | | ||
3 | | | | ||
4 | | Functions to add or subtract two registers and put the result in a third. | | ||
5 | | | | ||
6 | | Copyright (C) 1992,1993,1997 | | ||
7 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | | ||
8 | | E-mail billm@suburbia.net | | ||
9 | | | | ||
10 | | | | ||
11 | +---------------------------------------------------------------------------*/ | ||
12 | |||
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 | | ||
18 | | internal error. | | ||
19 | +---------------------------------------------------------------------------*/ | ||
20 | |||
21 | #include "exception.h" | ||
22 | #include "reg_constant.h" | ||
23 | #include "fpu_emu.h" | ||
24 | #include "control_w.h" | ||
25 | #include "fpu_system.h" | ||
26 | |||
27 | static | ||
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); | ||
31 | |||
32 | /* | ||
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). | ||
35 | */ | ||
36 | int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w) | ||
37 | { | ||
38 | FPU_REG *a = &st(0); | ||
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; | ||
45 | |||
46 | if ( !(taga | tagb) ) | ||
47 | { | ||
48 | expa = exponent(a); | ||
49 | expb = exponent(b); | ||
50 | |||
51 | valid_add: | ||
52 | /* Both registers are valid */ | ||
53 | if (!(signa ^ signb)) | ||
54 | { | ||
55 | /* signs are the same */ | ||
56 | tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb); | ||
57 | } | ||
58 | else | ||
59 | { | ||
60 | /* The signs are different, so do a subtraction */ | ||
61 | diff = expa - expb; | ||
62 | if (!diff) | ||
63 | { | ||
64 | diff = a->sigh - b->sigh; /* This works only if the ms bits | ||
65 | are identical. */ | ||
66 | if (!diff) | ||
67 | { | ||
68 | diff = a->sigl > b->sigl; | ||
69 | if (!diff) | ||
70 | diff = -(a->sigl < b->sigl); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | if (diff > 0) | ||
75 | { | ||
76 | tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); | ||
77 | } | ||
78 | else if ( diff < 0 ) | ||
79 | { | ||
80 | tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa); | ||
81 | } | ||
82 | else | ||
83 | { | ||
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); | ||
88 | return TAG_Zero; | ||
89 | } | ||
90 | } | ||
91 | |||
92 | if ( tag < 0 ) | ||
93 | { | ||
94 | setsign(dest, saved_sign); | ||
95 | return tag; | ||
96 | } | ||
97 | FPU_settagi(deststnr, tag); | ||
98 | return tag; | ||
99 | } | ||
100 | |||
101 | if ( taga == TAG_Special ) | ||
102 | taga = FPU_Special(a); | ||
103 | if ( tagb == TAG_Special ) | ||
104 | tagb = FPU_Special(b); | ||
105 | |||
106 | if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) | ||
107 | || ((taga == TW_Denormal) && (tagb == TAG_Valid)) | ||
108 | || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) | ||
109 | { | ||
110 | FPU_REG x, y; | ||
111 | |||
112 | if ( denormal_operand() < 0 ) | ||
113 | return FPU_Exception; | ||
114 | |||
115 | FPU_to_exp16(a, &x); | ||
116 | FPU_to_exp16(b, &y); | ||
117 | a = &x; | ||
118 | b = &y; | ||
119 | expa = exponent16(a); | ||
120 | expb = exponent16(b); | ||
121 | goto valid_add; | ||
122 | } | ||
123 | |||
124 | if ( (taga == TW_NaN) || (tagb == TW_NaN) ) | ||
125 | { | ||
126 | if ( deststnr == 0 ) | ||
127 | return real_2op_NaN(b, tagb, deststnr, a); | ||
128 | else | ||
129 | return real_2op_NaN(a, taga, deststnr, a); | ||
130 | } | ||
131 | |||
132 | return add_sub_specials(a, taga, signa, b, tagb, signb, | ||
133 | dest, deststnr, control_w); | ||
134 | } | ||
135 | |||
136 | |||
137 | /* Subtract b from a. (a-b) -> dest */ | ||
138 | int FPU_sub(int flags, int rm, int control_w) | ||
139 | { | ||
140 | FPU_REG const *a, *b; | ||
141 | FPU_REG *dest; | ||
142 | u_char taga, tagb, signa, signb, saved_sign, sign; | ||
143 | int diff, tag = 0, expa, expb, deststnr; | ||
144 | |||
145 | a = &st(0); | ||
146 | taga = FPU_gettag0(); | ||
147 | |||
148 | deststnr = 0; | ||
149 | if ( flags & LOADED ) | ||
150 | { | ||
151 | b = (FPU_REG *)rm; | ||
152 | tagb = flags & 0x0f; | ||
153 | } | ||
154 | else | ||
155 | { | ||
156 | b = &st(rm); | ||
157 | tagb = FPU_gettagi(rm); | ||
158 | |||
159 | if ( flags & DEST_RM ) | ||
160 | deststnr = rm; | ||
161 | } | ||
162 | |||
163 | signa = getsign(a); | ||
164 | signb = getsign(b); | ||
165 | |||
166 | if ( flags & REV ) | ||
167 | { | ||
168 | signa ^= SIGN_NEG; | ||
169 | signb ^= SIGN_NEG; | ||
170 | } | ||
171 | |||
172 | dest = &st(deststnr); | ||
173 | saved_sign = getsign(dest); | ||
174 | |||
175 | if ( !(taga | tagb) ) | ||
176 | { | ||
177 | expa = exponent(a); | ||
178 | expb = exponent(b); | ||
179 | |||
180 | valid_subtract: | ||
181 | /* Both registers are valid */ | ||
182 | |||
183 | diff = expa - expb; | ||
184 | |||
185 | if (!diff) | ||
186 | { | ||
187 | diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ | ||
188 | if (!diff) | ||
189 | { | ||
190 | diff = a->sigl > b->sigl; | ||
191 | if (!diff) | ||
192 | diff = -(a->sigl < b->sigl); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | switch ( (((int)signa)*2 + signb) / SIGN_NEG ) | ||
197 | { | ||
198 | case 0: /* P - P */ | ||
199 | case 3: /* N - N */ | ||
200 | if (diff > 0) | ||
201 | { | ||
202 | /* |a| > |b| */ | ||
203 | tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); | ||
204 | } | ||
205 | else if ( diff == 0 ) | ||
206 | { | ||
207 | FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); | ||
208 | |||
209 | /* sign depends upon rounding mode */ | ||
210 | setsign(dest, ((control_w & CW_RC) != RC_DOWN) | ||
211 | ? SIGN_POS : SIGN_NEG); | ||
212 | return TAG_Zero; | ||
213 | } | ||
214 | else | ||
215 | { | ||
216 | sign = signa ^ SIGN_NEG; | ||
217 | tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa); | ||
218 | } | ||
219 | break; | ||
220 | case 1: /* P - N */ | ||
221 | tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb); | ||
222 | break; | ||
223 | case 2: /* N - P */ | ||
224 | tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb); | ||
225 | break; | ||
226 | #ifdef PARANOID | ||
227 | default: | ||
228 | EXCEPTION(EX_INTERNAL|0x111); | ||
229 | return -1; | ||
230 | #endif | ||
231 | } | ||
232 | if ( tag < 0 ) | ||
233 | { | ||
234 | setsign(dest, saved_sign); | ||
235 | return tag; | ||
236 | } | ||
237 | FPU_settagi(deststnr, tag); | ||
238 | return tag; | ||
239 | } | ||
240 | |||
241 | if ( taga == TAG_Special ) | ||
242 | taga = FPU_Special(a); | ||
243 | if ( tagb == TAG_Special ) | ||
244 | tagb = FPU_Special(b); | ||
245 | |||
246 | if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) | ||
247 | || ((taga == TW_Denormal) && (tagb == TAG_Valid)) | ||
248 | || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) | ||
249 | { | ||
250 | FPU_REG x, y; | ||
251 | |||
252 | if ( denormal_operand() < 0 ) | ||
253 | return FPU_Exception; | ||
254 | |||
255 | FPU_to_exp16(a, &x); | ||
256 | FPU_to_exp16(b, &y); | ||
257 | a = &x; | ||
258 | b = &y; | ||
259 | expa = exponent16(a); | ||
260 | expb = exponent16(b); | ||
261 | |||
262 | goto valid_subtract; | ||
263 | } | ||
264 | |||
265 | if ( (taga == TW_NaN) || (tagb == TW_NaN) ) | ||
266 | { | ||
267 | FPU_REG const *d1, *d2; | ||
268 | if ( flags & REV ) | ||
269 | { | ||
270 | d1 = b; | ||
271 | d2 = a; | ||
272 | } | ||
273 | else | ||
274 | { | ||
275 | d1 = a; | ||
276 | d2 = b; | ||
277 | } | ||
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); | ||
282 | else | ||
283 | return real_2op_NaN(b, tagb, deststnr, d2); | ||
284 | } | ||
285 | |||
286 | return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG, | ||
287 | dest, deststnr, control_w); | ||
288 | } | ||
289 | |||
290 | |||
291 | static | ||
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) | ||
295 | { | ||
296 | if ( ((taga == TW_Denormal) || (tagb == TW_Denormal)) | ||
297 | && (denormal_operand() < 0) ) | ||
298 | return FPU_Exception; | ||
299 | |||
300 | if (taga == TAG_Zero) | ||
301 | { | ||
302 | if (tagb == TAG_Zero) | ||
303 | { | ||
304 | /* Both are zero, result will be zero. */ | ||
305 | u_char different_signs = signa ^ signb; | ||
306 | |||
307 | FPU_copy_to_regi(a, TAG_Zero, deststnr); | ||
308 | if ( different_signs ) | ||
309 | { | ||
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); | ||
314 | } | ||
315 | else | ||
316 | setsign(dest, signa); /* signa may differ from the sign of a. */ | ||
317 | return TAG_Zero; | ||
318 | } | ||
319 | else | ||
320 | { | ||
321 | reg_copy(b, dest); | ||
322 | if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) ) | ||
323 | { | ||
324 | /* A pseudoDenormal, convert it. */ | ||
325 | addexponent(dest, 1); | ||
326 | tagb = TAG_Valid; | ||
327 | } | ||
328 | else if ( tagb > TAG_Empty ) | ||
329 | tagb = TAG_Special; | ||
330 | setsign(dest, signb); /* signb may differ from the sign of b. */ | ||
331 | FPU_settagi(deststnr, tagb); | ||
332 | return tagb; | ||
333 | } | ||
334 | } | ||
335 | else if (tagb == TAG_Zero) | ||
336 | { | ||
337 | reg_copy(a, dest); | ||
338 | if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) ) | ||
339 | { | ||
340 | /* A pseudoDenormal */ | ||
341 | addexponent(dest, 1); | ||
342 | taga = TAG_Valid; | ||
343 | } | ||
344 | else if ( taga > TAG_Empty ) | ||
345 | taga = TAG_Special; | ||
346 | setsign(dest, signa); /* signa may differ from the sign of a. */ | ||
347 | FPU_settagi(deststnr, taga); | ||
348 | return taga; | ||
349 | } | ||
350 | else if (taga == TW_Infinity) | ||
351 | { | ||
352 | if ( (tagb != TW_Infinity) || (signa == signb) ) | ||
353 | { | ||
354 | FPU_copy_to_regi(a, TAG_Special, deststnr); | ||
355 | setsign(dest, signa); /* signa may differ from the sign of a. */ | ||
356 | return taga; | ||
357 | } | ||
358 | /* Infinity-Infinity is undefined. */ | ||
359 | return arith_invalid(deststnr); | ||
360 | } | ||
361 | else if (tagb == TW_Infinity) | ||
362 | { | ||
363 | FPU_copy_to_regi(b, TAG_Special, deststnr); | ||
364 | setsign(dest, signb); /* signb may differ from the sign of b. */ | ||
365 | return tagb; | ||
366 | } | ||
367 | |||
368 | #ifdef PARANOID | ||
369 | EXCEPTION(EX_INTERNAL|0x101); | ||
370 | #endif | ||
371 | |||
372 | return FPU_Exception; | ||
373 | } | ||
374 | |||