blob: ecd06680581c51cbb7631cd0240708e92d9ef919 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*---------------------------------------------------------------------------+
2 | fpu_trig.c |
3 | |
4 | Implementation of the FPU "transcendental" functions. |
5 | |
6 | Copyright (C) 1992,1993,1994,1997,1999 |
7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
8 | Australia. E-mail billm@melbpc.org.au |
9 | |
10 | |
11 +---------------------------------------------------------------------------*/
12
13#include "fpu_system.h"
14#include "exception.h"
15#include "fpu_emu.h"
16#include "status_w.h"
17#include "control_w.h"
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010018#include "reg_constant.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070019
20static void rem_kernel(unsigned long long st0, unsigned long long *y,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010021 unsigned long long st1, unsigned long long q, int n);
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
23#define BETTER_THAN_486
24
25#define FCOS 4
26
27/* Used only by fptan, fsin, fcos, and fsincos. */
28/* This routine produces very accurate results, similar to
29 using a value of pi with more than 128 bits precision. */
30/* Limited measurements show no results worse than 64 bit precision
31 except for the results for arguments close to 2^63, where the
32 precision of the result sometimes degrades to about 63.9 bits */
Ingo Molnare8d591d2008-01-30 13:30:12 +010033static int trig_arg(FPU_REG *st0_ptr, int even)
Linus Torvalds1da177e2005-04-16 15:20:36 -070034{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010035 FPU_REG tmp;
36 u_char tmptag;
37 unsigned long long q;
38 int old_cw = control_word, saved_status = partial_status;
39 int tag, st0_tag = TAG_Valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010041 if (exponent(st0_ptr) >= 63) {
42 partial_status |= SW_C2; /* Reduction incomplete. */
43 return -1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070045
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010046 control_word &= ~CW_RC;
47 control_word |= RC_CHOP;
48
49 setpositive(st0_ptr);
50 tag = FPU_u_div(st0_ptr, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f,
51 SIGN_POS);
52
53 FPU_round_to_int(&tmp, tag); /* Fortunately, this can't overflow
54 to 2^64 */
55 q = significand(&tmp);
56 if (q) {
57 rem_kernel(significand(st0_ptr),
58 &significand(&tmp),
59 significand(&CONST_PI2),
60 q, exponent(st0_ptr) - exponent(&CONST_PI2));
61 setexponent16(&tmp, exponent(&CONST_PI2));
62 st0_tag = FPU_normalize(&tmp);
63 FPU_copy_to_reg0(&tmp, st0_tag);
64 }
65
66 if ((even && !(q & 1)) || (!even && (q & 1))) {
67 st0_tag =
68 FPU_sub(REV | LOADED | TAG_Valid, (int)&CONST_PI2,
Linus Torvalds1da177e2005-04-16 15:20:36 -070069 FULL_PRECISION);
Ingo Molnar3d0d14f2008-01-30 13:30:11 +010070
71#ifdef BETTER_THAN_486
72 /* So far, the results are exact but based upon a 64 bit
73 precision approximation to pi/2. The technique used
74 now is equivalent to using an approximation to pi/2 which
75 is accurate to about 128 bits. */
76 if ((exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64)
77 || (q > 1)) {
78 /* This code gives the effect of having pi/2 to better than
79 128 bits precision. */
80
81 significand(&tmp) = q + 1;
82 setexponent16(&tmp, 63);
83 FPU_normalize(&tmp);
84 tmptag =
85 FPU_u_mul(&CONST_PI2extra, &tmp, &tmp,
86 FULL_PRECISION, SIGN_POS,
87 exponent(&CONST_PI2extra) +
88 exponent(&tmp));
89 setsign(&tmp, getsign(&CONST_PI2extra));
90 st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION);
91 if (signnegative(st0_ptr)) {
92 /* CONST_PI2extra is negative, so the result of the addition
93 can be negative. This means that the argument is actually
94 in a different quadrant. The correction is always < pi/2,
95 so it can't overflow into yet another quadrant. */
96 setpositive(st0_ptr);
97 q++;
98 }
99 }
100#endif /* BETTER_THAN_486 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100102#ifdef BETTER_THAN_486
103 else {
104 /* So far, the results are exact but based upon a 64 bit
105 precision approximation to pi/2. The technique used
106 now is equivalent to using an approximation to pi/2 which
107 is accurate to about 128 bits. */
108 if (((q > 0)
109 && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64))
110 || (q > 1)) {
111 /* This code gives the effect of having p/2 to better than
112 128 bits precision. */
113
114 significand(&tmp) = q;
115 setexponent16(&tmp, 63);
116 FPU_normalize(&tmp); /* This must return TAG_Valid */
117 tmptag =
118 FPU_u_mul(&CONST_PI2extra, &tmp, &tmp,
119 FULL_PRECISION, SIGN_POS,
120 exponent(&CONST_PI2extra) +
121 exponent(&tmp));
122 setsign(&tmp, getsign(&CONST_PI2extra));
123 st0_tag = FPU_sub(LOADED | (tmptag & 0x0f), (int)&tmp,
124 FULL_PRECISION);
125 if ((exponent(st0_ptr) == exponent(&CONST_PI2)) &&
126 ((st0_ptr->sigh > CONST_PI2.sigh)
127 || ((st0_ptr->sigh == CONST_PI2.sigh)
128 && (st0_ptr->sigl > CONST_PI2.sigl)))) {
129 /* CONST_PI2extra is negative, so the result of the
130 subtraction can be larger than pi/2. This means
131 that the argument is actually in a different quadrant.
132 The correction is always < pi/2, so it can't overflow
133 into yet another quadrant. */
134 st0_tag =
135 FPU_sub(REV | LOADED | TAG_Valid,
136 (int)&CONST_PI2, FULL_PRECISION);
137 q++;
138 }
139 }
140 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141#endif /* BETTER_THAN_486 */
142
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100143 FPU_settag0(st0_tag);
144 control_word = old_cw;
145 partial_status = saved_status & ~SW_C2; /* Reduction complete. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100147 return (q & 3) | even;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148}
149
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150/* Convert a long to register */
151static void convert_l2reg(long const *arg, int deststnr)
152{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100153 int tag;
154 long num = *arg;
155 u_char sign;
156 FPU_REG *dest = &st(deststnr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100158 if (num == 0) {
159 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
160 return;
161 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100163 if (num > 0) {
164 sign = SIGN_POS;
165 } else {
166 num = -num;
167 sign = SIGN_NEG;
168 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100170 dest->sigh = num;
171 dest->sigl = 0;
172 setexponent16(dest, 31);
173 tag = FPU_normalize(dest);
174 FPU_settagi(deststnr, tag);
175 setsign(dest, sign);
176 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177}
178
Ingo Molnare8d591d2008-01-30 13:30:12 +0100179static void single_arg_error(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700180{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100181 if (st0_tag == TAG_Empty)
182 FPU_stack_underflow(); /* Puts a QNaN in st(0) */
183 else if (st0_tag == TW_NaN)
184 real_1op_NaN(st0_ptr); /* return with a NaN in st(0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100186 else
187 EXCEPTION(EX_INTERNAL | 0x0112);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188#endif /* PARANOID */
189}
190
Ingo Molnare8d591d2008-01-30 13:30:12 +0100191static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100193 int isNaN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100195 switch (st0_tag) {
196 case TW_NaN:
197 isNaN = (exponent(st0_ptr) == EXP_OVER)
198 && (st0_ptr->sigh & 0x80000000);
199 if (isNaN && !(st0_ptr->sigh & 0x40000000)) { /* Signaling ? */
200 EXCEPTION(EX_Invalid);
201 if (control_word & CW_Invalid) {
202 /* The masked response */
203 /* Convert to a QNaN */
204 st0_ptr->sigh |= 0x40000000;
205 push();
206 FPU_copy_to_reg0(st0_ptr, TAG_Special);
207 }
208 } else if (isNaN) {
209 /* A QNaN */
210 push();
211 FPU_copy_to_reg0(st0_ptr, TAG_Special);
212 } else {
213 /* pseudoNaN or other unsupported */
214 EXCEPTION(EX_Invalid);
215 if (control_word & CW_Invalid) {
216 /* The masked response */
217 FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
218 push();
219 FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
220 }
221 }
222 break; /* return with a NaN in st(0) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100224 default:
225 EXCEPTION(EX_INTERNAL | 0x0112);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226#endif /* PARANOID */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100227 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228}
229
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230/*---------------------------------------------------------------------------*/
231
Ingo Molnare8d591d2008-01-30 13:30:12 +0100232static void f2xm1(FPU_REG *st0_ptr, u_char tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100234 FPU_REG a;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100236 clear_C1();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100238 if (tag == TAG_Valid) {
239 /* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */
240 if (exponent(st0_ptr) < 0) {
241 denormal_arg:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100243 FPU_to_exp16(st0_ptr, &a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700244
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100245 /* poly_2xm1(x) requires 0 < st(0) < 1. */
246 poly_2xm1(getsign(st0_ptr), &a, st0_ptr);
247 }
248 set_precision_flag_up(); /* 80486 appears to always do this */
249 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100252 if (tag == TAG_Zero)
253 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100255 if (tag == TAG_Special)
256 tag = FPU_Special(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100258 switch (tag) {
259 case TW_Denormal:
260 if (denormal_operand() < 0)
261 return;
262 goto denormal_arg;
263 case TW_Infinity:
264 if (signnegative(st0_ptr)) {
265 /* -infinity gives -1 (p16-10) */
266 FPU_copy_to_reg0(&CONST_1, TAG_Valid);
267 setnegative(st0_ptr);
268 }
269 return;
270 default:
271 single_arg_error(st0_ptr, tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273}
274
Ingo Molnare8d591d2008-01-30 13:30:12 +0100275static void fptan(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100277 FPU_REG *st_new_ptr;
278 int q;
279 u_char arg_sign = getsign(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100281 /* Stack underflow has higher priority */
282 if (st0_tag == TAG_Empty) {
283 FPU_stack_underflow(); /* Puts a QNaN in st(0) */
284 if (control_word & CW_Invalid) {
285 st_new_ptr = &st(-1);
286 push();
287 FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */
288 }
289 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100292 if (STACK_OVERFLOW) {
293 FPU_stack_overflow();
294 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100297 if (st0_tag == TAG_Valid) {
298 if (exponent(st0_ptr) > -40) {
299 if ((q = trig_arg(st0_ptr, 0)) == -1) {
300 /* Operand is out of range */
301 return;
302 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100304 poly_tan(st0_ptr);
305 setsign(st0_ptr, (q & 1) ^ (arg_sign != 0));
306 set_precision_flag_up(); /* We do not really know if up or down */
307 } else {
308 /* For a small arg, the result == the argument */
309 /* Underflow may happen */
310
311 denormal_arg:
312
313 FPU_to_exp16(st0_ptr, st0_ptr);
314
315 st0_tag =
316 FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
317 FPU_settag0(st0_tag);
318 }
319 push();
320 FPU_copy_to_reg0(&CONST_1, TAG_Valid);
321 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100324 if (st0_tag == TAG_Zero) {
325 push();
326 FPU_copy_to_reg0(&CONST_1, TAG_Valid);
327 setcc(0);
328 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100331 if (st0_tag == TAG_Special)
332 st0_tag = FPU_Special(st0_ptr);
333
334 if (st0_tag == TW_Denormal) {
335 if (denormal_operand() < 0)
336 return;
337
338 goto denormal_arg;
339 }
340
341 if (st0_tag == TW_Infinity) {
342 /* The 80486 treats infinity as an invalid operand */
343 if (arith_invalid(0) >= 0) {
344 st_new_ptr = &st(-1);
345 push();
346 arith_invalid(0);
347 }
348 return;
349 }
350
351 single_arg_2_error(st0_ptr, st0_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352}
353
Ingo Molnare8d591d2008-01-30 13:30:12 +0100354static void fxtract(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100356 FPU_REG *st_new_ptr;
357 u_char sign;
358 register FPU_REG *st1_ptr = st0_ptr; /* anticipate */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100360 if (STACK_OVERFLOW) {
361 FPU_stack_overflow();
362 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100364
365 clear_C1();
366
367 if (st0_tag == TAG_Valid) {
368 long e;
369
370 push();
371 sign = getsign(st1_ptr);
372 reg_copy(st1_ptr, st_new_ptr);
373 setexponent16(st_new_ptr, exponent(st_new_ptr));
374
375 denormal_arg:
376
377 e = exponent16(st_new_ptr);
378 convert_l2reg(&e, 1);
379 setexponentpos(st_new_ptr, 0);
380 setsign(st_new_ptr, sign);
381 FPU_settag0(TAG_Valid); /* Needed if arg was a denormal */
382 return;
383 } else if (st0_tag == TAG_Zero) {
384 sign = getsign(st0_ptr);
385
386 if (FPU_divide_by_zero(0, SIGN_NEG) < 0)
387 return;
388
389 push();
390 FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
391 setsign(st_new_ptr, sign);
392 return;
393 }
394
395 if (st0_tag == TAG_Special)
396 st0_tag = FPU_Special(st0_ptr);
397
398 if (st0_tag == TW_Denormal) {
399 if (denormal_operand() < 0)
400 return;
401
402 push();
403 sign = getsign(st1_ptr);
404 FPU_to_exp16(st1_ptr, st_new_ptr);
405 goto denormal_arg;
406 } else if (st0_tag == TW_Infinity) {
407 sign = getsign(st0_ptr);
408 setpositive(st0_ptr);
409 push();
410 FPU_copy_to_reg0(&CONST_INF, TAG_Special);
411 setsign(st_new_ptr, sign);
412 return;
413 } else if (st0_tag == TW_NaN) {
414 if (real_1op_NaN(st0_ptr) < 0)
415 return;
416
417 push();
418 FPU_copy_to_reg0(st0_ptr, TAG_Special);
419 return;
420 } else if (st0_tag == TAG_Empty) {
421 /* Is this the correct behaviour? */
422 if (control_word & EX_Invalid) {
423 FPU_stack_underflow();
424 push();
425 FPU_stack_underflow();
426 } else
427 EXCEPTION(EX_StackUnder);
428 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100430 else
431 EXCEPTION(EX_INTERNAL | 0x119);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432#endif /* PARANOID */
433}
434
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435static void fdecstp(void)
436{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100437 clear_C1();
438 top--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700439}
440
441static void fincstp(void)
442{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100443 clear_C1();
444 top++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445}
446
Ingo Molnare8d591d2008-01-30 13:30:12 +0100447static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100449 int expon;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100451 clear_C1();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100453 if (st0_tag == TAG_Valid) {
454 u_char tag;
455
456 if (signnegative(st0_ptr)) {
457 arith_invalid(0); /* sqrt(negative) is invalid */
458 return;
459 }
460
461 /* make st(0) in [1.0 .. 4.0) */
462 expon = exponent(st0_ptr);
463
464 denormal_arg:
465
466 setexponent16(st0_ptr, (expon & 1));
467
468 /* Do the computation, the sign of the result will be positive. */
469 tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS);
470 addexponent(st0_ptr, expon >> 1);
471 FPU_settag0(tag);
472 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 }
474
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100475 if (st0_tag == TAG_Zero)
476 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700477
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100478 if (st0_tag == TAG_Special)
479 st0_tag = FPU_Special(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100481 if (st0_tag == TW_Infinity) {
482 if (signnegative(st0_ptr))
483 arith_invalid(0); /* sqrt(-Infinity) is invalid */
484 return;
485 } else if (st0_tag == TW_Denormal) {
486 if (signnegative(st0_ptr)) {
487 arith_invalid(0); /* sqrt(negative) is invalid */
488 return;
489 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100491 if (denormal_operand() < 0)
492 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700493
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100494 FPU_to_exp16(st0_ptr, st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100496 expon = exponent16(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100498 goto denormal_arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 }
500
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100501 single_arg_error(st0_ptr, st0_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503}
504
Ingo Molnare8d591d2008-01-30 13:30:12 +0100505static void frndint_(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100507 int flags, tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100509 if (st0_tag == TAG_Valid) {
510 u_char sign;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100512 denormal_arg:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100514 sign = getsign(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100516 if (exponent(st0_ptr) > 63)
517 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100519 if (st0_tag == TW_Denormal) {
520 if (denormal_operand() < 0)
521 return;
522 }
523
524 /* Fortunately, this can't overflow to 2^64 */
525 if ((flags = FPU_round_to_int(st0_ptr, st0_tag)))
526 set_precision_flag(flags);
527
528 setexponent16(st0_ptr, 63);
529 tag = FPU_normalize(st0_ptr);
530 setsign(st0_ptr, sign);
531 FPU_settag0(tag);
532 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 }
534
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100535 if (st0_tag == TAG_Zero)
536 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100538 if (st0_tag == TAG_Special)
539 st0_tag = FPU_Special(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100541 if (st0_tag == TW_Denormal)
542 goto denormal_arg;
543 else if (st0_tag == TW_Infinity)
544 return;
545 else
546 single_arg_error(st0_ptr, st0_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547}
548
Ingo Molnare8d591d2008-01-30 13:30:12 +0100549static int fsin(FPU_REG *st0_ptr, u_char tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100551 u_char arg_sign = getsign(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100553 if (tag == TAG_Valid) {
554 int q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100556 if (exponent(st0_ptr) > -40) {
557 if ((q = trig_arg(st0_ptr, 0)) == -1) {
558 /* Operand is out of range */
559 return 1;
560 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100562 poly_sine(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100564 if (q & 2)
565 changesign(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100567 setsign(st0_ptr, getsign(st0_ptr) ^ arg_sign);
568
569 /* We do not really know if up or down */
570 set_precision_flag_up();
571 return 0;
572 } else {
573 /* For a small arg, the result == the argument */
574 set_precision_flag_up(); /* Must be up. */
575 return 0;
576 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100578
579 if (tag == TAG_Zero) {
580 setcc(0);
581 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100584 if (tag == TAG_Special)
585 tag = FPU_Special(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100587 if (tag == TW_Denormal) {
588 if (denormal_operand() < 0)
589 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100591 /* For a small arg, the result == the argument */
592 /* Underflow may happen */
593 FPU_to_exp16(st0_ptr, st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100595 tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100597 FPU_settag0(tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100599 return 0;
600 } else if (tag == TW_Infinity) {
601 /* The 80486 treats infinity as an invalid operand */
602 arith_invalid(0);
603 return 1;
604 } else {
605 single_arg_error(st0_ptr, tag);
606 return 1;
607 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608}
609
Ingo Molnare8d591d2008-01-30 13:30:12 +0100610static int f_cos(FPU_REG *st0_ptr, u_char tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100612 u_char st0_sign;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100614 st0_sign = getsign(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100616 if (tag == TAG_Valid) {
617 int q;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100619 if (exponent(st0_ptr) > -40) {
620 if ((exponent(st0_ptr) < 0)
621 || ((exponent(st0_ptr) == 0)
622 && (significand(st0_ptr) <=
623 0xc90fdaa22168c234LL))) {
624 poly_cos(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100626 /* We do not really know if up or down */
627 set_precision_flag_down();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100629 return 0;
630 } else if ((q = trig_arg(st0_ptr, FCOS)) != -1) {
631 poly_sine(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100633 if ((q + 1) & 2)
634 changesign(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100636 /* We do not really know if up or down */
637 set_precision_flag_down();
638
639 return 0;
640 } else {
641 /* Operand is out of range */
642 return 1;
643 }
644 } else {
645 denormal_arg:
646
647 setcc(0);
648 FPU_copy_to_reg0(&CONST_1, TAG_Valid);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100650 set_precision_flag_down(); /* 80486 appears to do this. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651#else
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100652 set_precision_flag_up(); /* Must be up. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653#endif /* PECULIAR_486 */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100654 return 0;
655 }
656 } else if (tag == TAG_Zero) {
657 FPU_copy_to_reg0(&CONST_1, TAG_Valid);
658 setcc(0);
659 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100662 if (tag == TAG_Special)
663 tag = FPU_Special(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700664
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100665 if (tag == TW_Denormal) {
666 if (denormal_operand() < 0)
667 return 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100669 goto denormal_arg;
670 } else if (tag == TW_Infinity) {
671 /* The 80486 treats infinity as an invalid operand */
672 arith_invalid(0);
673 return 1;
674 } else {
675 single_arg_error(st0_ptr, tag); /* requires st0_ptr == &st(0) */
676 return 1;
677 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678}
679
Ingo Molnare8d591d2008-01-30 13:30:12 +0100680static void fcos(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100682 f_cos(st0_ptr, st0_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683}
684
Ingo Molnare8d591d2008-01-30 13:30:12 +0100685static void fsincos(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100687 FPU_REG *st_new_ptr;
688 FPU_REG arg;
689 u_char tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100691 /* Stack underflow has higher priority */
692 if (st0_tag == TAG_Empty) {
693 FPU_stack_underflow(); /* Puts a QNaN in st(0) */
694 if (control_word & CW_Invalid) {
695 st_new_ptr = &st(-1);
696 push();
697 FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */
698 }
699 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100702 if (STACK_OVERFLOW) {
703 FPU_stack_overflow();
704 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100707 if (st0_tag == TAG_Special)
708 tag = FPU_Special(st0_ptr);
709 else
710 tag = st0_tag;
711
712 if (tag == TW_NaN) {
713 single_arg_2_error(st0_ptr, TW_NaN);
714 return;
715 } else if (tag == TW_Infinity) {
716 /* The 80486 treats infinity as an invalid operand */
717 if (arith_invalid(0) >= 0) {
718 /* Masked response */
719 push();
720 arith_invalid(0);
721 }
722 return;
723 }
724
725 reg_copy(st0_ptr, &arg);
726 if (!fsin(st0_ptr, st0_tag)) {
727 push();
728 FPU_copy_to_reg0(&arg, st0_tag);
729 f_cos(&st(0), st0_tag);
730 } else {
731 /* An error, so restore st(0) */
732 FPU_copy_to_reg0(&arg, st0_tag);
733 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734}
735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736/*---------------------------------------------------------------------------*/
737/* The following all require two arguments: st(0) and st(1) */
738
739/* A lean, mean kernel for the fprem instructions. This relies upon
740 the division and rounding to an integer in do_fprem giving an
741 exact result. Because of this, rem_kernel() needs to deal only with
742 the least significant 64 bits, the more significant bits of the
743 result must be zero.
744 */
745static void rem_kernel(unsigned long long st0, unsigned long long *y,
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100746 unsigned long long st1, unsigned long long q, int n)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100748 int dummy;
749 unsigned long long x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100751 x = st0 << n;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100753 /* Do the required multiplication and subtraction in the one operation */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100755 /* lsw x -= lsw st1 * lsw q */
756 asm volatile ("mull %4; subl %%eax,%0; sbbl %%edx,%1":"=m"
757 (((unsigned *)&x)[0]), "=m"(((unsigned *)&x)[1]),
758 "=a"(dummy)
759 :"2"(((unsigned *)&st1)[0]), "m"(((unsigned *)&q)[0])
760 :"%dx");
761 /* msw x -= msw st1 * lsw q */
762 asm volatile ("mull %3; subl %%eax,%0":"=m" (((unsigned *)&x)[1]),
763 "=a"(dummy)
764 :"1"(((unsigned *)&st1)[1]), "m"(((unsigned *)&q)[0])
765 :"%dx");
766 /* msw x -= lsw st1 * msw q */
767 asm volatile ("mull %3; subl %%eax,%0":"=m" (((unsigned *)&x)[1]),
768 "=a"(dummy)
769 :"1"(((unsigned *)&st1)[0]), "m"(((unsigned *)&q)[1])
770 :"%dx");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100772 *y = x;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773}
774
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775/* Remainder of st(0) / st(1) */
776/* This routine produces exact results, i.e. there is never any
777 rounding or truncation, etc of the result. */
Ingo Molnare8d591d2008-01-30 13:30:12 +0100778static void do_fprem(FPU_REG *st0_ptr, u_char st0_tag, int round)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100780 FPU_REG *st1_ptr = &st(1);
781 u_char st1_tag = FPU_gettagi(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100783 if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
784 FPU_REG tmp, st0, st1;
785 u_char st0_sign, st1_sign;
786 u_char tmptag;
787 int tag;
788 int old_cw;
789 int expdif;
790 long long q;
791 unsigned short saved_status;
792 int cc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100794 fprem_valid:
795 /* Convert registers for internal use. */
796 st0_sign = FPU_to_exp16(st0_ptr, &st0);
797 st1_sign = FPU_to_exp16(st1_ptr, &st1);
798 expdif = exponent16(&st0) - exponent16(&st1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100800 old_cw = control_word;
801 cc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100803 /* We want the status following the denorm tests, but don't want
804 the status changed by the arithmetic operations. */
805 saved_status = partial_status;
806 control_word &= ~CW_RC;
807 control_word |= RC_CHOP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100809 if (expdif < 64) {
810 /* This should be the most common case */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100812 if (expdif > -2) {
813 u_char sign = st0_sign ^ st1_sign;
814 tag = FPU_u_div(&st0, &st1, &tmp,
815 PR_64_BITS | RC_CHOP | 0x3f,
816 sign);
817 setsign(&tmp, sign);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100819 if (exponent(&tmp) >= 0) {
820 FPU_round_to_int(&tmp, tag); /* Fortunately, this can't
821 overflow to 2^64 */
822 q = significand(&tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100824 rem_kernel(significand(&st0),
825 &significand(&tmp),
826 significand(&st1),
827 q, expdif);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100829 setexponent16(&tmp, exponent16(&st1));
830 } else {
831 reg_copy(&st0, &tmp);
832 q = 0;
833 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100835 if ((round == RC_RND)
836 && (tmp.sigh & 0xc0000000)) {
837 /* We may need to subtract st(1) once more,
838 to get a result <= 1/2 of st(1). */
839 unsigned long long x;
840 expdif =
841 exponent16(&st1) - exponent16(&tmp);
842 if (expdif <= 1) {
843 if (expdif == 0)
844 x = significand(&st1) -
845 significand(&tmp);
846 else /* expdif is 1 */
847 x = (significand(&st1)
848 << 1) -
849 significand(&tmp);
850 if ((x < significand(&tmp)) ||
851 /* or equi-distant (from 0 & st(1)) and q is odd */
852 ((x == significand(&tmp))
853 && (q & 1))) {
854 st0_sign = !st0_sign;
855 significand(&tmp) = x;
856 q++;
857 }
858 }
859 }
860
861 if (q & 4)
862 cc |= SW_C0;
863 if (q & 2)
864 cc |= SW_C3;
865 if (q & 1)
866 cc |= SW_C1;
867 } else {
868 control_word = old_cw;
869 setcc(0);
870 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100872 } else {
873 /* There is a large exponent difference ( >= 64 ) */
874 /* To make much sense, the code in this section should
875 be done at high precision. */
876 int exp_1, N;
877 u_char sign;
878
879 /* prevent overflow here */
880 /* N is 'a number between 32 and 63' (p26-113) */
881 reg_copy(&st0, &tmp);
882 tmptag = st0_tag;
883 N = (expdif & 0x0000001f) + 32; /* This choice gives results
884 identical to an AMD 486 */
885 setexponent16(&tmp, N);
886 exp_1 = exponent16(&st1);
887 setexponent16(&st1, 0);
888 expdif -= N;
889
890 sign = getsign(&tmp) ^ st1_sign;
891 tag =
892 FPU_u_div(&tmp, &st1, &tmp,
893 PR_64_BITS | RC_CHOP | 0x3f, sign);
894 setsign(&tmp, sign);
895
896 FPU_round_to_int(&tmp, tag); /* Fortunately, this can't
897 overflow to 2^64 */
898
899 rem_kernel(significand(&st0),
900 &significand(&tmp),
901 significand(&st1),
902 significand(&tmp), exponent(&tmp)
903 );
904 setexponent16(&tmp, exp_1 + expdif);
905
906 /* It is possible for the operation to be complete here.
907 What does the IEEE standard say? The Intel 80486 manual
908 implies that the operation will never be completed at this
909 point, and the behaviour of a real 80486 confirms this.
910 */
911 if (!(tmp.sigh | tmp.sigl)) {
912 /* The result is zero */
913 control_word = old_cw;
914 partial_status = saved_status;
915 FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
916 setsign(&st0, st0_sign);
917#ifdef PECULIAR_486
918 setcc(SW_C2);
919#else
920 setcc(0);
921#endif /* PECULIAR_486 */
922 return;
923 }
924 cc = SW_C2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 }
926
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100927 control_word = old_cw;
928 partial_status = saved_status;
929 tag = FPU_normalize_nuo(&tmp);
930 reg_copy(&tmp, st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100932 /* The only condition to be looked for is underflow,
933 and it can occur here only if underflow is unmasked. */
934 if ((exponent16(&tmp) <= EXP_UNDER) && (tag != TAG_Zero)
935 && !(control_word & CW_Underflow)) {
936 setcc(cc);
937 tag = arith_underflow(st0_ptr);
938 setsign(st0_ptr, st0_sign);
939 FPU_settag0(tag);
940 return;
941 } else if ((exponent16(&tmp) > EXP_UNDER) || (tag == TAG_Zero)) {
942 stdexp(st0_ptr);
943 setsign(st0_ptr, st0_sign);
944 } else {
945 tag =
946 FPU_round(st0_ptr, 0, 0, FULL_PRECISION, st0_sign);
947 }
948 FPU_settag0(tag);
949 setcc(cc);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100951 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 }
953
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100954 if (st0_tag == TAG_Special)
955 st0_tag = FPU_Special(st0_ptr);
956 if (st1_tag == TAG_Special)
957 st1_tag = FPU_Special(st1_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100959 if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960 || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100961 || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
962 if (denormal_operand() < 0)
963 return;
964 goto fprem_valid;
965 } else if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
966 FPU_stack_underflow();
967 return;
968 } else if (st0_tag == TAG_Zero) {
969 if (st1_tag == TAG_Valid) {
970 setcc(0);
971 return;
972 } else if (st1_tag == TW_Denormal) {
973 if (denormal_operand() < 0)
974 return;
975 setcc(0);
976 return;
977 } else if (st1_tag == TAG_Zero) {
978 arith_invalid(0);
979 return;
980 } /* fprem(?,0) always invalid */
981 else if (st1_tag == TW_Infinity) {
982 setcc(0);
983 return;
984 }
985 } else if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
986 if (st1_tag == TAG_Zero) {
987 arith_invalid(0); /* fprem(Valid,Zero) is invalid */
988 return;
989 } else if (st1_tag != TW_NaN) {
990 if (((st0_tag == TW_Denormal)
991 || (st1_tag == TW_Denormal))
992 && (denormal_operand() < 0))
993 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700994
Ingo Molnar3d0d14f2008-01-30 13:30:11 +0100995 if (st1_tag == TW_Infinity) {
996 /* fprem(Valid,Infinity) is o.k. */
997 setcc(0);
998 return;
999 }
1000 }
1001 } else if (st0_tag == TW_Infinity) {
1002 if (st1_tag != TW_NaN) {
1003 arith_invalid(0); /* fprem(Infinity,?) is invalid */
1004 return;
1005 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001008 /* One of the registers must contain a NaN if we got here. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009
1010#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001011 if ((st0_tag != TW_NaN) && (st1_tag != TW_NaN))
1012 EXCEPTION(EX_INTERNAL | 0x118);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013#endif /* PARANOID */
1014
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001015 real_2op_NaN(st1_ptr, st1_tag, 0, st1_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016
1017}
1018
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019/* ST(1) <- ST(1) * log ST; pop ST */
Ingo Molnare8d591d2008-01-30 13:30:12 +01001020static void fyl2x(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001022 FPU_REG *st1_ptr = &st(1), exponent;
1023 u_char st1_tag = FPU_gettagi(1);
1024 u_char sign;
1025 int e, tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001026
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001027 clear_C1();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001029 if ((st0_tag == TAG_Valid) && (st1_tag == TAG_Valid)) {
1030 both_valid:
1031 /* Both regs are Valid or Denormal */
1032 if (signpositive(st0_ptr)) {
1033 if (st0_tag == TW_Denormal)
1034 FPU_to_exp16(st0_ptr, st0_ptr);
1035 else
1036 /* Convert st(0) for internal use. */
1037 setexponent16(st0_ptr, exponent(st0_ptr));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001039 if ((st0_ptr->sigh == 0x80000000)
1040 && (st0_ptr->sigl == 0)) {
1041 /* Special case. The result can be precise. */
1042 u_char esign;
1043 e = exponent16(st0_ptr);
1044 if (e >= 0) {
1045 exponent.sigh = e;
1046 esign = SIGN_POS;
1047 } else {
1048 exponent.sigh = -e;
1049 esign = SIGN_NEG;
1050 }
1051 exponent.sigl = 0;
1052 setexponent16(&exponent, 31);
1053 tag = FPU_normalize_nuo(&exponent);
1054 stdexp(&exponent);
1055 setsign(&exponent, esign);
1056 tag =
1057 FPU_mul(&exponent, tag, 1, FULL_PRECISION);
1058 if (tag >= 0)
1059 FPU_settagi(1, tag);
1060 } else {
1061 /* The usual case */
1062 sign = getsign(st1_ptr);
1063 if (st1_tag == TW_Denormal)
1064 FPU_to_exp16(st1_ptr, st1_ptr);
1065 else
1066 /* Convert st(1) for internal use. */
1067 setexponent16(st1_ptr,
1068 exponent(st1_ptr));
1069 poly_l2(st0_ptr, st1_ptr, sign);
1070 }
1071 } else {
1072 /* negative */
1073 if (arith_invalid(1) < 0)
1074 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001076
1077 FPU_pop();
1078
1079 return;
1080 }
1081
1082 if (st0_tag == TAG_Special)
1083 st0_tag = FPU_Special(st0_ptr);
1084 if (st1_tag == TAG_Special)
1085 st1_tag = FPU_Special(st1_ptr);
1086
1087 if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
1088 FPU_stack_underflow_pop(1);
1089 return;
1090 } else if ((st0_tag <= TW_Denormal) && (st1_tag <= TW_Denormal)) {
1091 if (st0_tag == TAG_Zero) {
1092 if (st1_tag == TAG_Zero) {
1093 /* Both args zero is invalid */
1094 if (arith_invalid(1) < 0)
1095 return;
1096 } else {
1097 u_char sign;
1098 sign = getsign(st1_ptr) ^ SIGN_NEG;
1099 if (FPU_divide_by_zero(1, sign) < 0)
1100 return;
1101
1102 setsign(st1_ptr, sign);
1103 }
1104 } else if (st1_tag == TAG_Zero) {
1105 /* st(1) contains zero, st(0) valid <> 0 */
1106 /* Zero is the valid answer */
1107 sign = getsign(st1_ptr);
1108
1109 if (signnegative(st0_ptr)) {
1110 /* log(negative) */
1111 if (arith_invalid(1) < 0)
1112 return;
1113 } else if ((st0_tag == TW_Denormal)
1114 && (denormal_operand() < 0))
1115 return;
1116 else {
1117 if (exponent(st0_ptr) < 0)
1118 sign ^= SIGN_NEG;
1119
1120 FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
1121 setsign(st1_ptr, sign);
1122 }
1123 } else {
1124 /* One or both operands are denormals. */
1125 if (denormal_operand() < 0)
1126 return;
1127 goto both_valid;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001129 } else if ((st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
1130 if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
1131 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001133 /* One or both arg must be an infinity */
1134 else if (st0_tag == TW_Infinity) {
1135 if ((signnegative(st0_ptr)) || (st1_tag == TAG_Zero)) {
1136 /* log(-infinity) or 0*log(infinity) */
1137 if (arith_invalid(1) < 0)
1138 return;
1139 } else {
1140 u_char sign = getsign(st1_ptr);
1141
1142 if ((st1_tag == TW_Denormal)
1143 && (denormal_operand() < 0))
1144 return;
1145
1146 FPU_copy_to_reg1(&CONST_INF, TAG_Special);
1147 setsign(st1_ptr, sign);
1148 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001150 /* st(1) must be infinity here */
1151 else if (((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal))
1152 && (signpositive(st0_ptr))) {
1153 if (exponent(st0_ptr) >= 0) {
1154 if ((exponent(st0_ptr) == 0) &&
1155 (st0_ptr->sigh == 0x80000000) &&
1156 (st0_ptr->sigl == 0)) {
1157 /* st(0) holds 1.0 */
1158 /* infinity*log(1) */
1159 if (arith_invalid(1) < 0)
1160 return;
1161 }
1162 /* else st(0) is positive and > 1.0 */
1163 } else {
1164 /* st(0) is positive and < 1.0 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001166 if ((st0_tag == TW_Denormal)
1167 && (denormal_operand() < 0))
1168 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001170 changesign(st1_ptr);
1171 }
1172 } else {
1173 /* st(0) must be zero or negative */
1174 if (st0_tag == TAG_Zero) {
1175 /* This should be invalid, but a real 80486 is happy with it. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176
1177#ifndef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001178 sign = getsign(st1_ptr);
1179 if (FPU_divide_by_zero(1, sign) < 0)
1180 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181#endif /* PECULIAR_486 */
1182
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001183 changesign(st1_ptr);
1184 } else if (arith_invalid(1) < 0) /* log(negative) */
1185 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001188 FPU_pop();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189}
1190
Ingo Molnare8d591d2008-01-30 13:30:12 +01001191static void fpatan(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001192{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001193 FPU_REG *st1_ptr = &st(1);
1194 u_char st1_tag = FPU_gettagi(1);
1195 int tag;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001197 clear_C1();
1198 if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
1199 valid_atan:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001201 poly_atan(st0_ptr, st0_tag, st1_ptr, st1_tag);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001203 FPU_pop();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 return;
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001206 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001207
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001208 if (st0_tag == TAG_Special)
1209 st0_tag = FPU_Special(st0_ptr);
1210 if (st1_tag == TAG_Special)
1211 st1_tag = FPU_Special(st1_ptr);
1212
1213 if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
1214 || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
1215 || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
1216 if (denormal_operand() < 0)
1217 return;
1218
1219 goto valid_atan;
1220 } else if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
1221 FPU_stack_underflow_pop(1);
1222 return;
1223 } else if ((st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
1224 if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) >= 0)
1225 FPU_pop();
1226 return;
1227 } else if ((st0_tag == TW_Infinity) || (st1_tag == TW_Infinity)) {
1228 u_char sign = getsign(st1_ptr);
1229 if (st0_tag == TW_Infinity) {
1230 if (st1_tag == TW_Infinity) {
1231 if (signpositive(st0_ptr)) {
1232 FPU_copy_to_reg1(&CONST_PI4, TAG_Valid);
1233 } else {
1234 setpositive(st1_ptr);
1235 tag =
1236 FPU_u_add(&CONST_PI4, &CONST_PI2,
1237 st1_ptr, FULL_PRECISION,
1238 SIGN_POS,
1239 exponent(&CONST_PI4),
1240 exponent(&CONST_PI2));
1241 if (tag >= 0)
1242 FPU_settagi(1, tag);
1243 }
1244 } else {
1245 if ((st1_tag == TW_Denormal)
1246 && (denormal_operand() < 0))
1247 return;
1248
1249 if (signpositive(st0_ptr)) {
1250 FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
1251 setsign(st1_ptr, sign); /* An 80486 preserves the sign */
1252 FPU_pop();
1253 return;
1254 } else {
1255 FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
1256 }
1257 }
1258 } else {
1259 /* st(1) is infinity, st(0) not infinity */
1260 if ((st0_tag == TW_Denormal)
1261 && (denormal_operand() < 0))
1262 return;
1263
1264 FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001265 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001266 setsign(st1_ptr, sign);
1267 } else if (st1_tag == TAG_Zero) {
1268 /* st(0) must be valid or zero */
1269 u_char sign = getsign(st1_ptr);
1270
1271 if ((st0_tag == TW_Denormal) && (denormal_operand() < 0))
1272 return;
1273
1274 if (signpositive(st0_ptr)) {
1275 /* An 80486 preserves the sign */
1276 FPU_pop();
1277 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001278 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001279
1280 FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
1281 setsign(st1_ptr, sign);
1282 } else if (st0_tag == TAG_Zero) {
1283 /* st(1) must be TAG_Valid here */
1284 u_char sign = getsign(st1_ptr);
1285
1286 if ((st1_tag == TW_Denormal) && (denormal_operand() < 0))
1287 return;
1288
1289 FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
1290 setsign(st1_ptr, sign);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001293 else
1294 EXCEPTION(EX_INTERNAL | 0x125);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295#endif /* PARANOID */
1296
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001297 FPU_pop();
1298 set_precision_flag_up(); /* We do not really know if up or down */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299}
1300
Ingo Molnare8d591d2008-01-30 13:30:12 +01001301static void fprem(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001303 do_fprem(st0_ptr, st0_tag, RC_CHOP);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001304}
1305
Ingo Molnare8d591d2008-01-30 13:30:12 +01001306static void fprem1(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001307{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001308 do_fprem(st0_ptr, st0_tag, RC_RND);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001309}
1310
Ingo Molnare8d591d2008-01-30 13:30:12 +01001311static void fyl2xp1(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001313 u_char sign, sign1;
1314 FPU_REG *st1_ptr = &st(1), a, b;
1315 u_char st1_tag = FPU_gettagi(1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001316
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001317 clear_C1();
1318 if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
1319 valid_yl2xp1:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001321 sign = getsign(st0_ptr);
1322 sign1 = getsign(st1_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001324 FPU_to_exp16(st0_ptr, &a);
1325 FPU_to_exp16(st1_ptr, &b);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001326
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001327 if (poly_l2p1(sign, sign1, &a, &b, st1_ptr))
1328 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001330 FPU_pop();
1331 return;
1332 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001334 if (st0_tag == TAG_Special)
1335 st0_tag = FPU_Special(st0_ptr);
1336 if (st1_tag == TAG_Special)
1337 st1_tag = FPU_Special(st1_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001339 if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340 || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001341 || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
1342 if (denormal_operand() < 0)
1343 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001345 goto valid_yl2xp1;
1346 } else if ((st0_tag == TAG_Empty) | (st1_tag == TAG_Empty)) {
1347 FPU_stack_underflow_pop(1);
1348 return;
1349 } else if (st0_tag == TAG_Zero) {
1350 switch (st1_tag) {
1351 case TW_Denormal:
1352 if (denormal_operand() < 0)
1353 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001354
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001355 case TAG_Zero:
1356 case TAG_Valid:
1357 setsign(st0_ptr, getsign(st0_ptr) ^ getsign(st1_ptr));
1358 FPU_copy_to_reg1(st0_ptr, st0_tag);
1359 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001361 case TW_Infinity:
1362 /* Infinity*log(1) */
1363 if (arith_invalid(1) < 0)
1364 return;
1365 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001367 case TW_NaN:
1368 if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
1369 return;
1370 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001371
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001372 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001374 EXCEPTION(EX_INTERNAL | 0x116);
1375 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376#endif /* PARANOID */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001377 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378 }
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001379 } else if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
1380 switch (st1_tag) {
1381 case TAG_Zero:
1382 if (signnegative(st0_ptr)) {
1383 if (exponent(st0_ptr) >= 0) {
1384 /* st(0) holds <= -1.0 */
1385#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
1386 changesign(st1_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387#else
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001388 if (arith_invalid(1) < 0)
1389 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390#endif /* PECULIAR_486 */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001391 } else if ((st0_tag == TW_Denormal)
1392 && (denormal_operand() < 0))
1393 return;
1394 else
1395 changesign(st1_ptr);
1396 } else if ((st0_tag == TW_Denormal)
1397 && (denormal_operand() < 0))
1398 return;
1399 break;
1400
1401 case TW_Infinity:
1402 if (signnegative(st0_ptr)) {
1403 if ((exponent(st0_ptr) >= 0) &&
1404 !((st0_ptr->sigh == 0x80000000) &&
1405 (st0_ptr->sigl == 0))) {
1406 /* st(0) holds < -1.0 */
1407#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
1408 changesign(st1_ptr);
1409#else
1410 if (arith_invalid(1) < 0)
1411 return;
1412#endif /* PECULIAR_486 */
1413 } else if ((st0_tag == TW_Denormal)
1414 && (denormal_operand() < 0))
1415 return;
1416 else
1417 changesign(st1_ptr);
1418 } else if ((st0_tag == TW_Denormal)
1419 && (denormal_operand() < 0))
1420 return;
1421 break;
1422
1423 case TW_NaN:
1424 if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
1425 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001426 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001428 } else if (st0_tag == TW_NaN) {
1429 if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
1430 return;
1431 } else if (st0_tag == TW_Infinity) {
1432 if (st1_tag == TW_NaN) {
1433 if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
1434 return;
1435 } else if (signnegative(st0_ptr)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436#ifndef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001437 /* This should have higher priority than denormals, but... */
1438 if (arith_invalid(1) < 0) /* log(-infinity) */
1439 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440#endif /* PECULIAR_486 */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001441 if ((st1_tag == TW_Denormal)
1442 && (denormal_operand() < 0))
1443 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444#ifdef PECULIAR_486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001445 /* Denormal operands actually get higher priority */
1446 if (arith_invalid(1) < 0) /* log(-infinity) */
1447 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448#endif /* PECULIAR_486 */
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001449 } else if (st1_tag == TAG_Zero) {
1450 /* log(infinity) */
1451 if (arith_invalid(1) < 0)
1452 return;
1453 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001455 /* st(1) must be valid here. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001457 else if ((st1_tag == TW_Denormal) && (denormal_operand() < 0))
1458 return;
1459
1460 /* The Manual says that log(Infinity) is invalid, but a real
1461 80486 sensibly says that it is o.k. */
1462 else {
1463 u_char sign = getsign(st1_ptr);
1464 FPU_copy_to_reg1(&CONST_INF, TAG_Special);
1465 setsign(st1_ptr, sign);
1466 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001469 else {
1470 EXCEPTION(EX_INTERNAL | 0x117);
1471 return;
1472 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473#endif /* PARANOID */
1474
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001475 FPU_pop();
1476 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477
1478}
1479
Ingo Molnare8d591d2008-01-30 13:30:12 +01001480static void fscale(FPU_REG *st0_ptr, u_char st0_tag)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001482 FPU_REG *st1_ptr = &st(1);
1483 u_char st1_tag = FPU_gettagi(1);
1484 int old_cw = control_word;
1485 u_char sign = getsign(st0_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001487 clear_C1();
1488 if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
1489 long scale;
1490 FPU_REG tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001492 /* Convert register for internal use. */
1493 setexponent16(st0_ptr, exponent(st0_ptr));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001495 valid_scale:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001497 if (exponent(st1_ptr) > 30) {
1498 /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001500 if (signpositive(st1_ptr)) {
1501 EXCEPTION(EX_Overflow);
1502 FPU_copy_to_reg0(&CONST_INF, TAG_Special);
1503 } else {
1504 EXCEPTION(EX_Underflow);
1505 FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
1506 }
1507 setsign(st0_ptr, sign);
1508 return;
1509 }
1510
1511 control_word &= ~CW_RC;
1512 control_word |= RC_CHOP;
1513 reg_copy(st1_ptr, &tmp);
1514 FPU_round_to_int(&tmp, st1_tag); /* This can never overflow here */
1515 control_word = old_cw;
1516 scale = signnegative(st1_ptr) ? -tmp.sigl : tmp.sigl;
1517 scale += exponent16(st0_ptr);
1518
1519 setexponent16(st0_ptr, scale);
1520
1521 /* Use FPU_round() to properly detect under/overflow etc */
1522 FPU_round(st0_ptr, 0, 0, control_word, sign);
1523
1524 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 }
1526
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001527 if (st0_tag == TAG_Special)
1528 st0_tag = FPU_Special(st0_ptr);
1529 if (st1_tag == TAG_Special)
1530 st1_tag = FPU_Special(st1_ptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001532 if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
1533 switch (st1_tag) {
1534 case TAG_Valid:
1535 /* st(0) must be a denormal */
1536 if ((st0_tag == TW_Denormal)
1537 && (denormal_operand() < 0))
1538 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001540 FPU_to_exp16(st0_ptr, st0_ptr); /* Will not be left on stack */
1541 goto valid_scale;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001543 case TAG_Zero:
1544 if (st0_tag == TW_Denormal)
1545 denormal_operand();
1546 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001548 case TW_Denormal:
1549 denormal_operand();
1550 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001552 case TW_Infinity:
1553 if ((st0_tag == TW_Denormal)
1554 && (denormal_operand() < 0))
1555 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001557 if (signpositive(st1_ptr))
1558 FPU_copy_to_reg0(&CONST_INF, TAG_Special);
1559 else
1560 FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
1561 setsign(st0_ptr, sign);
1562 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001564 case TW_NaN:
1565 real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
1566 return;
1567 }
1568 } else if (st0_tag == TAG_Zero) {
1569 switch (st1_tag) {
1570 case TAG_Valid:
1571 case TAG_Zero:
1572 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001573
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001574 case TW_Denormal:
1575 denormal_operand();
1576 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001578 case TW_Infinity:
1579 if (signpositive(st1_ptr))
1580 arith_invalid(0); /* Zero scaled by +Infinity */
1581 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001583 case TW_NaN:
1584 real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
1585 return;
1586 }
1587 } else if (st0_tag == TW_Infinity) {
1588 switch (st1_tag) {
1589 case TAG_Valid:
1590 case TAG_Zero:
1591 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001593 case TW_Denormal:
1594 denormal_operand();
1595 return;
1596
1597 case TW_Infinity:
1598 if (signnegative(st1_ptr))
1599 arith_invalid(0); /* Infinity scaled by -Infinity */
1600 return;
1601
1602 case TW_NaN:
1603 real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
1604 return;
1605 }
1606 } else if (st0_tag == TW_NaN) {
1607 if (st1_tag != TAG_Empty) {
1608 real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
1609 return;
1610 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001611 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001612#ifdef PARANOID
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001613 if (!((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty))) {
1614 EXCEPTION(EX_INTERNAL | 0x115);
1615 return;
1616 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617#endif
1618
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001619 /* At least one of st(0), st(1) must be empty */
1620 FPU_stack_underflow();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
1622}
1623
Linus Torvalds1da177e2005-04-16 15:20:36 -07001624/*---------------------------------------------------------------------------*/
1625
1626static FUNC_ST0 const trig_table_a[] = {
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001627 f2xm1, fyl2x, fptan, fpatan,
1628 fxtract, fprem1, (FUNC_ST0) fdecstp, (FUNC_ST0) fincstp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629};
1630
1631void FPU_triga(void)
1632{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001633 (trig_table_a[FPU_rm]) (&st(0), FPU_gettag0());
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634}
1635
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001636static FUNC_ST0 const trig_table_b[] = {
1637 fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, (FUNC_ST0) fsin, fcos
1638};
Linus Torvalds1da177e2005-04-16 15:20:36 -07001639
1640void FPU_trigb(void)
1641{
Ingo Molnar3d0d14f2008-01-30 13:30:11 +01001642 (trig_table_b[FPU_rm]) (&st(0), FPU_gettag0());
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643}