123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644 |
- /*---------------------------------------------------------------------------+
- | fpu_trig.c |
- | |
- | Implementation of the FPU "transcendental" functions. |
- | |
- | Copyright (C) 1992,1993,1994,1997,1999 |
- | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
- | Australia. E-mail billm@melbpc.org.au |
- | |
- | |
- +---------------------------------------------------------------------------*/
- #include "fpu_system.h"
- #include "exception.h"
- #include "fpu_emu.h"
- #include "status_w.h"
- #include "control_w.h"
- #include "reg_constant.h"
- static void rem_kernel(unsigned long long st0, unsigned long long *y,
- unsigned long long st1, unsigned long long q, int n);
- #define BETTER_THAN_486
- #define FCOS 4
- /* Used only by fptan, fsin, fcos, and fsincos. */
- /* This routine produces very accurate results, similar to
- using a value of pi with more than 128 bits precision. */
- /* Limited measurements show no results worse than 64 bit precision
- except for the results for arguments close to 2^63, where the
- precision of the result sometimes degrades to about 63.9 bits */
- static int trig_arg(FPU_REG *st0_ptr, int even)
- {
- FPU_REG tmp;
- u_char tmptag;
- unsigned long long q;
- int old_cw = control_word, saved_status = partial_status;
- int tag, st0_tag = TAG_Valid;
- if (exponent(st0_ptr) >= 63) {
- partial_status |= SW_C2; /* Reduction incomplete. */
- return -1;
- }
- control_word &= ~CW_RC;
- control_word |= RC_CHOP;
- setpositive(st0_ptr);
- tag = FPU_u_div(st0_ptr, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f,
- SIGN_POS);
- FPU_round_to_int(&tmp, tag); /* Fortunately, this can't overflow
- to 2^64 */
- q = significand(&tmp);
- if (q) {
- rem_kernel(significand(st0_ptr),
- &significand(&tmp),
- significand(&CONST_PI2),
- q, exponent(st0_ptr) - exponent(&CONST_PI2));
- setexponent16(&tmp, exponent(&CONST_PI2));
- st0_tag = FPU_normalize(&tmp);
- FPU_copy_to_reg0(&tmp, st0_tag);
- }
- if ((even && !(q & 1)) || (!even && (q & 1))) {
- st0_tag =
- FPU_sub(REV | LOADED | TAG_Valid, (int)&CONST_PI2,
- FULL_PRECISION);
- #ifdef BETTER_THAN_486
- /* So far, the results are exact but based upon a 64 bit
- precision approximation to pi/2. The technique used
- now is equivalent to using an approximation to pi/2 which
- is accurate to about 128 bits. */
- if ((exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64)
- || (q > 1)) {
- /* This code gives the effect of having pi/2 to better than
- 128 bits precision. */
- significand(&tmp) = q + 1;
- setexponent16(&tmp, 63);
- FPU_normalize(&tmp);
- tmptag =
- FPU_u_mul(&CONST_PI2extra, &tmp, &tmp,
- FULL_PRECISION, SIGN_POS,
- exponent(&CONST_PI2extra) +
- exponent(&tmp));
- setsign(&tmp, getsign(&CONST_PI2extra));
- st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION);
- if (signnegative(st0_ptr)) {
- /* CONST_PI2extra is negative, so the result of the addition
- can be negative. This means that the argument is actually
- in a different quadrant. The correction is always < pi/2,
- so it can't overflow into yet another quadrant. */
- setpositive(st0_ptr);
- q++;
- }
- }
- #endif /* BETTER_THAN_486 */
- }
- #ifdef BETTER_THAN_486
- else {
- /* So far, the results are exact but based upon a 64 bit
- precision approximation to pi/2. The technique used
- now is equivalent to using an approximation to pi/2 which
- is accurate to about 128 bits. */
- if (((q > 0)
- && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64))
- || (q > 1)) {
- /* This code gives the effect of having p/2 to better than
- 128 bits precision. */
- significand(&tmp) = q;
- setexponent16(&tmp, 63);
- FPU_normalize(&tmp); /* This must return TAG_Valid */
- tmptag =
- FPU_u_mul(&CONST_PI2extra, &tmp, &tmp,
- FULL_PRECISION, SIGN_POS,
- exponent(&CONST_PI2extra) +
- exponent(&tmp));
- setsign(&tmp, getsign(&CONST_PI2extra));
- st0_tag = FPU_sub(LOADED | (tmptag & 0x0f), (int)&tmp,
- FULL_PRECISION);
- if ((exponent(st0_ptr) == exponent(&CONST_PI2)) &&
- ((st0_ptr->sigh > CONST_PI2.sigh)
- || ((st0_ptr->sigh == CONST_PI2.sigh)
- && (st0_ptr->sigl > CONST_PI2.sigl)))) {
- /* CONST_PI2extra is negative, so the result of the
- subtraction can be larger than pi/2. This means
- that the argument is actually in a different quadrant.
- The correction is always < pi/2, so it can't overflow
- into yet another quadrant. */
- st0_tag =
- FPU_sub(REV | LOADED | TAG_Valid,
- (int)&CONST_PI2, FULL_PRECISION);
- q++;
- }
- }
- }
- #endif /* BETTER_THAN_486 */
- FPU_settag0(st0_tag);
- control_word = old_cw;
- partial_status = saved_status & ~SW_C2; /* Reduction complete. */
- return (q & 3) | even;
- }
- /* Convert a long to register */
- static void convert_l2reg(long const *arg, int deststnr)
- {
- int tag;
- long num = *arg;
- u_char sign;
- FPU_REG *dest = &st(deststnr);
- if (num == 0) {
- FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
- return;
- }
- if (num > 0) {
- sign = SIGN_POS;
- } else {
- num = -num;
- sign = SIGN_NEG;
- }
- dest->sigh = num;
- dest->sigl = 0;
- setexponent16(dest, 31);
- tag = FPU_normalize(dest);
- FPU_settagi(deststnr, tag);
- setsign(dest, sign);
- return;
- }
- static void single_arg_error(FPU_REG *st0_ptr, u_char st0_tag)
- {
- if (st0_tag == TAG_Empty)
- FPU_stack_underflow(); /* Puts a QNaN in st(0) */
- else if (st0_tag == TW_NaN)
- real_1op_NaN(st0_ptr); /* return with a NaN in st(0) */
- #ifdef PARANOID
- else
- EXCEPTION(EX_INTERNAL | 0x0112);
- #endif /* PARANOID */
- }
- static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag)
- {
- int isNaN;
- switch (st0_tag) {
- case TW_NaN:
- isNaN = (exponent(st0_ptr) == EXP_OVER)
- && (st0_ptr->sigh & 0x80000000);
- if (isNaN && !(st0_ptr->sigh & 0x40000000)) { /* Signaling ? */
- EXCEPTION(EX_Invalid);
- if (control_word & CW_Invalid) {
- /* The masked response */
- /* Convert to a QNaN */
- st0_ptr->sigh |= 0x40000000;
- push();
- FPU_copy_to_reg0(st0_ptr, TAG_Special);
- }
- } else if (isNaN) {
- /* A QNaN */
- push();
- FPU_copy_to_reg0(st0_ptr, TAG_Special);
- } else {
- /* pseudoNaN or other unsupported */
- EXCEPTION(EX_Invalid);
- if (control_word & CW_Invalid) {
- /* The masked response */
- FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
- push();
- FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
- }
- }
- break; /* return with a NaN in st(0) */
- #ifdef PARANOID
- default:
- EXCEPTION(EX_INTERNAL | 0x0112);
- #endif /* PARANOID */
- }
- }
- /*---------------------------------------------------------------------------*/
- static void f2xm1(FPU_REG *st0_ptr, u_char tag)
- {
- FPU_REG a;
- clear_C1();
- if (tag == TAG_Valid) {
- /* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */
- if (exponent(st0_ptr) < 0) {
- denormal_arg:
- FPU_to_exp16(st0_ptr, &a);
- /* poly_2xm1(x) requires 0 < st(0) < 1. */
- poly_2xm1(getsign(st0_ptr), &a, st0_ptr);
- }
- set_precision_flag_up(); /* 80486 appears to always do this */
- return;
- }
- if (tag == TAG_Zero)
- return;
- if (tag == TAG_Special)
- tag = FPU_Special(st0_ptr);
- switch (tag) {
- case TW_Denormal:
- if (denormal_operand() < 0)
- return;
- goto denormal_arg;
- case TW_Infinity:
- if (signnegative(st0_ptr)) {
- /* -infinity gives -1 (p16-10) */
- FPU_copy_to_reg0(&CONST_1, TAG_Valid);
- setnegative(st0_ptr);
- }
- return;
- default:
- single_arg_error(st0_ptr, tag);
- }
- }
- static void fptan(FPU_REG *st0_ptr, u_char st0_tag)
- {
- FPU_REG *st_new_ptr;
- int q;
- u_char arg_sign = getsign(st0_ptr);
- /* Stack underflow has higher priority */
- if (st0_tag == TAG_Empty) {
- FPU_stack_underflow(); /* Puts a QNaN in st(0) */
- if (control_word & CW_Invalid) {
- st_new_ptr = &st(-1);
- push();
- FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */
- }
- return;
- }
- if (STACK_OVERFLOW) {
- FPU_stack_overflow();
- return;
- }
- if (st0_tag == TAG_Valid) {
- if (exponent(st0_ptr) > -40) {
- if ((q = trig_arg(st0_ptr, 0)) == -1) {
- /* Operand is out of range */
- return;
- }
- poly_tan(st0_ptr);
- setsign(st0_ptr, (q & 1) ^ (arg_sign != 0));
- set_precision_flag_up(); /* We do not really know if up or down */
- } else {
- /* For a small arg, the result == the argument */
- /* Underflow may happen */
- denormal_arg:
- FPU_to_exp16(st0_ptr, st0_ptr);
- st0_tag =
- FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
- FPU_settag0(st0_tag);
- }
- push();
- FPU_copy_to_reg0(&CONST_1, TAG_Valid);
- return;
- }
- if (st0_tag == TAG_Zero) {
- push();
- FPU_copy_to_reg0(&CONST_1, TAG_Valid);
- setcc(0);
- return;
- }
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st0_tag == TW_Denormal) {
- if (denormal_operand() < 0)
- return;
- goto denormal_arg;
- }
- if (st0_tag == TW_Infinity) {
- /* The 80486 treats infinity as an invalid operand */
- if (arith_invalid(0) >= 0) {
- st_new_ptr = &st(-1);
- push();
- arith_invalid(0);
- }
- return;
- }
- single_arg_2_error(st0_ptr, st0_tag);
- }
- static void fxtract(FPU_REG *st0_ptr, u_char st0_tag)
- {
- FPU_REG *st_new_ptr;
- u_char sign;
- register FPU_REG *st1_ptr = st0_ptr; /* anticipate */
- if (STACK_OVERFLOW) {
- FPU_stack_overflow();
- return;
- }
- clear_C1();
- if (st0_tag == TAG_Valid) {
- long e;
- push();
- sign = getsign(st1_ptr);
- reg_copy(st1_ptr, st_new_ptr);
- setexponent16(st_new_ptr, exponent(st_new_ptr));
- denormal_arg:
- e = exponent16(st_new_ptr);
- convert_l2reg(&e, 1);
- setexponentpos(st_new_ptr, 0);
- setsign(st_new_ptr, sign);
- FPU_settag0(TAG_Valid); /* Needed if arg was a denormal */
- return;
- } else if (st0_tag == TAG_Zero) {
- sign = getsign(st0_ptr);
- if (FPU_divide_by_zero(0, SIGN_NEG) < 0)
- return;
- push();
- FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
- setsign(st_new_ptr, sign);
- return;
- }
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st0_tag == TW_Denormal) {
- if (denormal_operand() < 0)
- return;
- push();
- sign = getsign(st1_ptr);
- FPU_to_exp16(st1_ptr, st_new_ptr);
- goto denormal_arg;
- } else if (st0_tag == TW_Infinity) {
- sign = getsign(st0_ptr);
- setpositive(st0_ptr);
- push();
- FPU_copy_to_reg0(&CONST_INF, TAG_Special);
- setsign(st_new_ptr, sign);
- return;
- } else if (st0_tag == TW_NaN) {
- if (real_1op_NaN(st0_ptr) < 0)
- return;
- push();
- FPU_copy_to_reg0(st0_ptr, TAG_Special);
- return;
- } else if (st0_tag == TAG_Empty) {
- /* Is this the correct behaviour? */
- if (control_word & EX_Invalid) {
- FPU_stack_underflow();
- push();
- FPU_stack_underflow();
- } else
- EXCEPTION(EX_StackUnder);
- }
- #ifdef PARANOID
- else
- EXCEPTION(EX_INTERNAL | 0x119);
- #endif /* PARANOID */
- }
- static void fdecstp(void)
- {
- clear_C1();
- top--;
- }
- static void fincstp(void)
- {
- clear_C1();
- top++;
- }
- static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag)
- {
- int expon;
- clear_C1();
- if (st0_tag == TAG_Valid) {
- u_char tag;
- if (signnegative(st0_ptr)) {
- arith_invalid(0); /* sqrt(negative) is invalid */
- return;
- }
- /* make st(0) in [1.0 .. 4.0) */
- expon = exponent(st0_ptr);
- denormal_arg:
- setexponent16(st0_ptr, (expon & 1));
- /* Do the computation, the sign of the result will be positive. */
- tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS);
- addexponent(st0_ptr, expon >> 1);
- FPU_settag0(tag);
- return;
- }
- if (st0_tag == TAG_Zero)
- return;
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st0_tag == TW_Infinity) {
- if (signnegative(st0_ptr))
- arith_invalid(0); /* sqrt(-Infinity) is invalid */
- return;
- } else if (st0_tag == TW_Denormal) {
- if (signnegative(st0_ptr)) {
- arith_invalid(0); /* sqrt(negative) is invalid */
- return;
- }
- if (denormal_operand() < 0)
- return;
- FPU_to_exp16(st0_ptr, st0_ptr);
- expon = exponent16(st0_ptr);
- goto denormal_arg;
- }
- single_arg_error(st0_ptr, st0_tag);
- }
- static void frndint_(FPU_REG *st0_ptr, u_char st0_tag)
- {
- int flags, tag;
- if (st0_tag == TAG_Valid) {
- u_char sign;
- denormal_arg:
- sign = getsign(st0_ptr);
- if (exponent(st0_ptr) > 63)
- return;
- if (st0_tag == TW_Denormal) {
- if (denormal_operand() < 0)
- return;
- }
- /* Fortunately, this can't overflow to 2^64 */
- if ((flags = FPU_round_to_int(st0_ptr, st0_tag)))
- set_precision_flag(flags);
- setexponent16(st0_ptr, 63);
- tag = FPU_normalize(st0_ptr);
- setsign(st0_ptr, sign);
- FPU_settag0(tag);
- return;
- }
- if (st0_tag == TAG_Zero)
- return;
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st0_tag == TW_Denormal)
- goto denormal_arg;
- else if (st0_tag == TW_Infinity)
- return;
- else
- single_arg_error(st0_ptr, st0_tag);
- }
- static int fsin(FPU_REG *st0_ptr, u_char tag)
- {
- u_char arg_sign = getsign(st0_ptr);
- if (tag == TAG_Valid) {
- int q;
- if (exponent(st0_ptr) > -40) {
- if ((q = trig_arg(st0_ptr, 0)) == -1) {
- /* Operand is out of range */
- return 1;
- }
- poly_sine(st0_ptr);
- if (q & 2)
- changesign(st0_ptr);
- setsign(st0_ptr, getsign(st0_ptr) ^ arg_sign);
- /* We do not really know if up or down */
- set_precision_flag_up();
- return 0;
- } else {
- /* For a small arg, the result == the argument */
- set_precision_flag_up(); /* Must be up. */
- return 0;
- }
- }
- if (tag == TAG_Zero) {
- setcc(0);
- return 0;
- }
- if (tag == TAG_Special)
- tag = FPU_Special(st0_ptr);
- if (tag == TW_Denormal) {
- if (denormal_operand() < 0)
- return 1;
- /* For a small arg, the result == the argument */
- /* Underflow may happen */
- FPU_to_exp16(st0_ptr, st0_ptr);
- tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
- FPU_settag0(tag);
- return 0;
- } else if (tag == TW_Infinity) {
- /* The 80486 treats infinity as an invalid operand */
- arith_invalid(0);
- return 1;
- } else {
- single_arg_error(st0_ptr, tag);
- return 1;
- }
- }
- static int f_cos(FPU_REG *st0_ptr, u_char tag)
- {
- u_char st0_sign;
- st0_sign = getsign(st0_ptr);
- if (tag == TAG_Valid) {
- int q;
- if (exponent(st0_ptr) > -40) {
- if ((exponent(st0_ptr) < 0)
- || ((exponent(st0_ptr) == 0)
- && (significand(st0_ptr) <=
- 0xc90fdaa22168c234LL))) {
- poly_cos(st0_ptr);
- /* We do not really know if up or down */
- set_precision_flag_down();
- return 0;
- } else if ((q = trig_arg(st0_ptr, FCOS)) != -1) {
- poly_sine(st0_ptr);
- if ((q + 1) & 2)
- changesign(st0_ptr);
- /* We do not really know if up or down */
- set_precision_flag_down();
- return 0;
- } else {
- /* Operand is out of range */
- return 1;
- }
- } else {
- denormal_arg:
- setcc(0);
- FPU_copy_to_reg0(&CONST_1, TAG_Valid);
- #ifdef PECULIAR_486
- set_precision_flag_down(); /* 80486 appears to do this. */
- #else
- set_precision_flag_up(); /* Must be up. */
- #endif /* PECULIAR_486 */
- return 0;
- }
- } else if (tag == TAG_Zero) {
- FPU_copy_to_reg0(&CONST_1, TAG_Valid);
- setcc(0);
- return 0;
- }
- if (tag == TAG_Special)
- tag = FPU_Special(st0_ptr);
- if (tag == TW_Denormal) {
- if (denormal_operand() < 0)
- return 1;
- goto denormal_arg;
- } else if (tag == TW_Infinity) {
- /* The 80486 treats infinity as an invalid operand */
- arith_invalid(0);
- return 1;
- } else {
- single_arg_error(st0_ptr, tag); /* requires st0_ptr == &st(0) */
- return 1;
- }
- }
- static void fcos(FPU_REG *st0_ptr, u_char st0_tag)
- {
- f_cos(st0_ptr, st0_tag);
- }
- static void fsincos(FPU_REG *st0_ptr, u_char st0_tag)
- {
- FPU_REG *st_new_ptr;
- FPU_REG arg;
- u_char tag;
- /* Stack underflow has higher priority */
- if (st0_tag == TAG_Empty) {
- FPU_stack_underflow(); /* Puts a QNaN in st(0) */
- if (control_word & CW_Invalid) {
- st_new_ptr = &st(-1);
- push();
- FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */
- }
- return;
- }
- if (STACK_OVERFLOW) {
- FPU_stack_overflow();
- return;
- }
- if (st0_tag == TAG_Special)
- tag = FPU_Special(st0_ptr);
- else
- tag = st0_tag;
- if (tag == TW_NaN) {
- single_arg_2_error(st0_ptr, TW_NaN);
- return;
- } else if (tag == TW_Infinity) {
- /* The 80486 treats infinity as an invalid operand */
- if (arith_invalid(0) >= 0) {
- /* Masked response */
- push();
- arith_invalid(0);
- }
- return;
- }
- reg_copy(st0_ptr, &arg);
- if (!fsin(st0_ptr, st0_tag)) {
- push();
- FPU_copy_to_reg0(&arg, st0_tag);
- f_cos(&st(0), st0_tag);
- } else {
- /* An error, so restore st(0) */
- FPU_copy_to_reg0(&arg, st0_tag);
- }
- }
- /*---------------------------------------------------------------------------*/
- /* The following all require two arguments: st(0) and st(1) */
- /* A lean, mean kernel for the fprem instructions. This relies upon
- the division and rounding to an integer in do_fprem giving an
- exact result. Because of this, rem_kernel() needs to deal only with
- the least significant 64 bits, the more significant bits of the
- result must be zero.
- */
- static void rem_kernel(unsigned long long st0, unsigned long long *y,
- unsigned long long st1, unsigned long long q, int n)
- {
- int dummy;
- unsigned long long x;
- x = st0 << n;
- /* Do the required multiplication and subtraction in the one operation */
- /* lsw x -= lsw st1 * lsw q */
- asm volatile ("mull %4; subl %%eax,%0; sbbl %%edx,%1":"=m"
- (((unsigned *)&x)[0]), "=m"(((unsigned *)&x)[1]),
- "=a"(dummy)
- :"2"(((unsigned *)&st1)[0]), "m"(((unsigned *)&q)[0])
- :"%dx");
- /* msw x -= msw st1 * lsw q */
- asm volatile ("mull %3; subl %%eax,%0":"=m" (((unsigned *)&x)[1]),
- "=a"(dummy)
- :"1"(((unsigned *)&st1)[1]), "m"(((unsigned *)&q)[0])
- :"%dx");
- /* msw x -= lsw st1 * msw q */
- asm volatile ("mull %3; subl %%eax,%0":"=m" (((unsigned *)&x)[1]),
- "=a"(dummy)
- :"1"(((unsigned *)&st1)[0]), "m"(((unsigned *)&q)[1])
- :"%dx");
- *y = x;
- }
- /* Remainder of st(0) / st(1) */
- /* This routine produces exact results, i.e. there is never any
- rounding or truncation, etc of the result. */
- static void do_fprem(FPU_REG *st0_ptr, u_char st0_tag, int round)
- {
- FPU_REG *st1_ptr = &st(1);
- u_char st1_tag = FPU_gettagi(1);
- if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
- FPU_REG tmp, st0, st1;
- u_char st0_sign, st1_sign;
- u_char tmptag;
- int tag;
- int old_cw;
- int expdif;
- long long q;
- unsigned short saved_status;
- int cc;
- fprem_valid:
- /* Convert registers for internal use. */
- st0_sign = FPU_to_exp16(st0_ptr, &st0);
- st1_sign = FPU_to_exp16(st1_ptr, &st1);
- expdif = exponent16(&st0) - exponent16(&st1);
- old_cw = control_word;
- cc = 0;
- /* We want the status following the denorm tests, but don't want
- the status changed by the arithmetic operations. */
- saved_status = partial_status;
- control_word &= ~CW_RC;
- control_word |= RC_CHOP;
- if (expdif < 64) {
- /* This should be the most common case */
- if (expdif > -2) {
- u_char sign = st0_sign ^ st1_sign;
- tag = FPU_u_div(&st0, &st1, &tmp,
- PR_64_BITS | RC_CHOP | 0x3f,
- sign);
- setsign(&tmp, sign);
- if (exponent(&tmp) >= 0) {
- FPU_round_to_int(&tmp, tag); /* Fortunately, this can't
- overflow to 2^64 */
- q = significand(&tmp);
- rem_kernel(significand(&st0),
- &significand(&tmp),
- significand(&st1),
- q, expdif);
- setexponent16(&tmp, exponent16(&st1));
- } else {
- reg_copy(&st0, &tmp);
- q = 0;
- }
- if ((round == RC_RND)
- && (tmp.sigh & 0xc0000000)) {
- /* We may need to subtract st(1) once more,
- to get a result <= 1/2 of st(1). */
- unsigned long long x;
- expdif =
- exponent16(&st1) - exponent16(&tmp);
- if (expdif <= 1) {
- if (expdif == 0)
- x = significand(&st1) -
- significand(&tmp);
- else /* expdif is 1 */
- x = (significand(&st1)
- << 1) -
- significand(&tmp);
- if ((x < significand(&tmp)) ||
- /* or equi-distant (from 0 & st(1)) and q is odd */
- ((x == significand(&tmp))
- && (q & 1))) {
- st0_sign = !st0_sign;
- significand(&tmp) = x;
- q++;
- }
- }
- }
- if (q & 4)
- cc |= SW_C0;
- if (q & 2)
- cc |= SW_C3;
- if (q & 1)
- cc |= SW_C1;
- } else {
- control_word = old_cw;
- setcc(0);
- return;
- }
- } else {
- /* There is a large exponent difference ( >= 64 ) */
- /* To make much sense, the code in this section should
- be done at high precision. */
- int exp_1, N;
- u_char sign;
- /* prevent overflow here */
- /* N is 'a number between 32 and 63' (p26-113) */
- reg_copy(&st0, &tmp);
- tmptag = st0_tag;
- N = (expdif & 0x0000001f) + 32; /* This choice gives results
- identical to an AMD 486 */
- setexponent16(&tmp, N);
- exp_1 = exponent16(&st1);
- setexponent16(&st1, 0);
- expdif -= N;
- sign = getsign(&tmp) ^ st1_sign;
- tag =
- FPU_u_div(&tmp, &st1, &tmp,
- PR_64_BITS | RC_CHOP | 0x3f, sign);
- setsign(&tmp, sign);
- FPU_round_to_int(&tmp, tag); /* Fortunately, this can't
- overflow to 2^64 */
- rem_kernel(significand(&st0),
- &significand(&tmp),
- significand(&st1),
- significand(&tmp), exponent(&tmp)
- );
- setexponent16(&tmp, exp_1 + expdif);
- /* It is possible for the operation to be complete here.
- What does the IEEE standard say? The Intel 80486 manual
- implies that the operation will never be completed at this
- point, and the behaviour of a real 80486 confirms this.
- */
- if (!(tmp.sigh | tmp.sigl)) {
- /* The result is zero */
- control_word = old_cw;
- partial_status = saved_status;
- FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
- setsign(&st0, st0_sign);
- #ifdef PECULIAR_486
- setcc(SW_C2);
- #else
- setcc(0);
- #endif /* PECULIAR_486 */
- return;
- }
- cc = SW_C2;
- }
- control_word = old_cw;
- partial_status = saved_status;
- tag = FPU_normalize_nuo(&tmp);
- reg_copy(&tmp, st0_ptr);
- /* The only condition to be looked for is underflow,
- and it can occur here only if underflow is unmasked. */
- if ((exponent16(&tmp) <= EXP_UNDER) && (tag != TAG_Zero)
- && !(control_word & CW_Underflow)) {
- setcc(cc);
- tag = arith_underflow(st0_ptr);
- setsign(st0_ptr, st0_sign);
- FPU_settag0(tag);
- return;
- } else if ((exponent16(&tmp) > EXP_UNDER) || (tag == TAG_Zero)) {
- stdexp(st0_ptr);
- setsign(st0_ptr, st0_sign);
- } else {
- tag =
- FPU_round(st0_ptr, 0, 0, FULL_PRECISION, st0_sign);
- }
- FPU_settag0(tag);
- setcc(cc);
- return;
- }
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st1_tag == TAG_Special)
- st1_tag = FPU_Special(st1_ptr);
- if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
- || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
- || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
- if (denormal_operand() < 0)
- return;
- goto fprem_valid;
- } else if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
- FPU_stack_underflow();
- return;
- } else if (st0_tag == TAG_Zero) {
- if (st1_tag == TAG_Valid) {
- setcc(0);
- return;
- } else if (st1_tag == TW_Denormal) {
- if (denormal_operand() < 0)
- return;
- setcc(0);
- return;
- } else if (st1_tag == TAG_Zero) {
- arith_invalid(0);
- return;
- } /* fprem(?,0) always invalid */
- else if (st1_tag == TW_Infinity) {
- setcc(0);
- return;
- }
- } else if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
- if (st1_tag == TAG_Zero) {
- arith_invalid(0); /* fprem(Valid,Zero) is invalid */
- return;
- } else if (st1_tag != TW_NaN) {
- if (((st0_tag == TW_Denormal)
- || (st1_tag == TW_Denormal))
- && (denormal_operand() < 0))
- return;
- if (st1_tag == TW_Infinity) {
- /* fprem(Valid,Infinity) is o.k. */
- setcc(0);
- return;
- }
- }
- } else if (st0_tag == TW_Infinity) {
- if (st1_tag != TW_NaN) {
- arith_invalid(0); /* fprem(Infinity,?) is invalid */
- return;
- }
- }
- /* One of the registers must contain a NaN if we got here. */
- #ifdef PARANOID
- if ((st0_tag != TW_NaN) && (st1_tag != TW_NaN))
- EXCEPTION(EX_INTERNAL | 0x118);
- #endif /* PARANOID */
- real_2op_NaN(st1_ptr, st1_tag, 0, st1_ptr);
- }
- /* ST(1) <- ST(1) * log ST; pop ST */
- static void fyl2x(FPU_REG *st0_ptr, u_char st0_tag)
- {
- FPU_REG *st1_ptr = &st(1), exponent;
- u_char st1_tag = FPU_gettagi(1);
- u_char sign;
- int e, tag;
- clear_C1();
- if ((st0_tag == TAG_Valid) && (st1_tag == TAG_Valid)) {
- both_valid:
- /* Both regs are Valid or Denormal */
- if (signpositive(st0_ptr)) {
- if (st0_tag == TW_Denormal)
- FPU_to_exp16(st0_ptr, st0_ptr);
- else
- /* Convert st(0) for internal use. */
- setexponent16(st0_ptr, exponent(st0_ptr));
- if ((st0_ptr->sigh == 0x80000000)
- && (st0_ptr->sigl == 0)) {
- /* Special case. The result can be precise. */
- u_char esign;
- e = exponent16(st0_ptr);
- if (e >= 0) {
- exponent.sigh = e;
- esign = SIGN_POS;
- } else {
- exponent.sigh = -e;
- esign = SIGN_NEG;
- }
- exponent.sigl = 0;
- setexponent16(&exponent, 31);
- tag = FPU_normalize_nuo(&exponent);
- stdexp(&exponent);
- setsign(&exponent, esign);
- tag =
- FPU_mul(&exponent, tag, 1, FULL_PRECISION);
- if (tag >= 0)
- FPU_settagi(1, tag);
- } else {
- /* The usual case */
- sign = getsign(st1_ptr);
- if (st1_tag == TW_Denormal)
- FPU_to_exp16(st1_ptr, st1_ptr);
- else
- /* Convert st(1) for internal use. */
- setexponent16(st1_ptr,
- exponent(st1_ptr));
- poly_l2(st0_ptr, st1_ptr, sign);
- }
- } else {
- /* negative */
- if (arith_invalid(1) < 0)
- return;
- }
- FPU_pop();
- return;
- }
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st1_tag == TAG_Special)
- st1_tag = FPU_Special(st1_ptr);
- if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
- FPU_stack_underflow_pop(1);
- return;
- } else if ((st0_tag <= TW_Denormal) && (st1_tag <= TW_Denormal)) {
- if (st0_tag == TAG_Zero) {
- if (st1_tag == TAG_Zero) {
- /* Both args zero is invalid */
- if (arith_invalid(1) < 0)
- return;
- } else {
- u_char sign;
- sign = getsign(st1_ptr) ^ SIGN_NEG;
- if (FPU_divide_by_zero(1, sign) < 0)
- return;
- setsign(st1_ptr, sign);
- }
- } else if (st1_tag == TAG_Zero) {
- /* st(1) contains zero, st(0) valid <> 0 */
- /* Zero is the valid answer */
- sign = getsign(st1_ptr);
- if (signnegative(st0_ptr)) {
- /* log(negative) */
- if (arith_invalid(1) < 0)
- return;
- } else if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- else {
- if (exponent(st0_ptr) < 0)
- sign ^= SIGN_NEG;
- FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
- setsign(st1_ptr, sign);
- }
- } else {
- /* One or both operands are denormals. */
- if (denormal_operand() < 0)
- return;
- goto both_valid;
- }
- } else if ((st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
- if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
- return;
- }
- /* One or both arg must be an infinity */
- else if (st0_tag == TW_Infinity) {
- if ((signnegative(st0_ptr)) || (st1_tag == TAG_Zero)) {
- /* log(-infinity) or 0*log(infinity) */
- if (arith_invalid(1) < 0)
- return;
- } else {
- u_char sign = getsign(st1_ptr);
- if ((st1_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- FPU_copy_to_reg1(&CONST_INF, TAG_Special);
- setsign(st1_ptr, sign);
- }
- }
- /* st(1) must be infinity here */
- else if (((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal))
- && (signpositive(st0_ptr))) {
- if (exponent(st0_ptr) >= 0) {
- if ((exponent(st0_ptr) == 0) &&
- (st0_ptr->sigh == 0x80000000) &&
- (st0_ptr->sigl == 0)) {
- /* st(0) holds 1.0 */
- /* infinity*log(1) */
- if (arith_invalid(1) < 0)
- return;
- }
- /* else st(0) is positive and > 1.0 */
- } else {
- /* st(0) is positive and < 1.0 */
- if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- changesign(st1_ptr);
- }
- } else {
- /* st(0) must be zero or negative */
- if (st0_tag == TAG_Zero) {
- /* This should be invalid, but a real 80486 is happy with it. */
- #ifndef PECULIAR_486
- sign = getsign(st1_ptr);
- if (FPU_divide_by_zero(1, sign) < 0)
- return;
- #endif /* PECULIAR_486 */
- changesign(st1_ptr);
- } else if (arith_invalid(1) < 0) /* log(negative) */
- return;
- }
- FPU_pop();
- }
- static void fpatan(FPU_REG *st0_ptr, u_char st0_tag)
- {
- FPU_REG *st1_ptr = &st(1);
- u_char st1_tag = FPU_gettagi(1);
- int tag;
- clear_C1();
- if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
- valid_atan:
- poly_atan(st0_ptr, st0_tag, st1_ptr, st1_tag);
- FPU_pop();
- return;
- }
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st1_tag == TAG_Special)
- st1_tag = FPU_Special(st1_ptr);
- if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
- || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
- || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
- if (denormal_operand() < 0)
- return;
- goto valid_atan;
- } else if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
- FPU_stack_underflow_pop(1);
- return;
- } else if ((st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
- if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) >= 0)
- FPU_pop();
- return;
- } else if ((st0_tag == TW_Infinity) || (st1_tag == TW_Infinity)) {
- u_char sign = getsign(st1_ptr);
- if (st0_tag == TW_Infinity) {
- if (st1_tag == TW_Infinity) {
- if (signpositive(st0_ptr)) {
- FPU_copy_to_reg1(&CONST_PI4, TAG_Valid);
- } else {
- setpositive(st1_ptr);
- tag =
- FPU_u_add(&CONST_PI4, &CONST_PI2,
- st1_ptr, FULL_PRECISION,
- SIGN_POS,
- exponent(&CONST_PI4),
- exponent(&CONST_PI2));
- if (tag >= 0)
- FPU_settagi(1, tag);
- }
- } else {
- if ((st1_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- if (signpositive(st0_ptr)) {
- FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
- setsign(st1_ptr, sign); /* An 80486 preserves the sign */
- FPU_pop();
- return;
- } else {
- FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
- }
- }
- } else {
- /* st(1) is infinity, st(0) not infinity */
- if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
- }
- setsign(st1_ptr, sign);
- } else if (st1_tag == TAG_Zero) {
- /* st(0) must be valid or zero */
- u_char sign = getsign(st1_ptr);
- if ((st0_tag == TW_Denormal) && (denormal_operand() < 0))
- return;
- if (signpositive(st0_ptr)) {
- /* An 80486 preserves the sign */
- FPU_pop();
- return;
- }
- FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
- setsign(st1_ptr, sign);
- } else if (st0_tag == TAG_Zero) {
- /* st(1) must be TAG_Valid here */
- u_char sign = getsign(st1_ptr);
- if ((st1_tag == TW_Denormal) && (denormal_operand() < 0))
- return;
- FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
- setsign(st1_ptr, sign);
- }
- #ifdef PARANOID
- else
- EXCEPTION(EX_INTERNAL | 0x125);
- #endif /* PARANOID */
- FPU_pop();
- set_precision_flag_up(); /* We do not really know if up or down */
- }
- static void fprem(FPU_REG *st0_ptr, u_char st0_tag)
- {
- do_fprem(st0_ptr, st0_tag, RC_CHOP);
- }
- static void fprem1(FPU_REG *st0_ptr, u_char st0_tag)
- {
- do_fprem(st0_ptr, st0_tag, RC_RND);
- }
- static void fyl2xp1(FPU_REG *st0_ptr, u_char st0_tag)
- {
- u_char sign, sign1;
- FPU_REG *st1_ptr = &st(1), a, b;
- u_char st1_tag = FPU_gettagi(1);
- clear_C1();
- if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
- valid_yl2xp1:
- sign = getsign(st0_ptr);
- sign1 = getsign(st1_ptr);
- FPU_to_exp16(st0_ptr, &a);
- FPU_to_exp16(st1_ptr, &b);
- if (poly_l2p1(sign, sign1, &a, &b, st1_ptr))
- return;
- FPU_pop();
- return;
- }
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st1_tag == TAG_Special)
- st1_tag = FPU_Special(st1_ptr);
- if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
- || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
- || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
- if (denormal_operand() < 0)
- return;
- goto valid_yl2xp1;
- } else if ((st0_tag == TAG_Empty) | (st1_tag == TAG_Empty)) {
- FPU_stack_underflow_pop(1);
- return;
- } else if (st0_tag == TAG_Zero) {
- switch (st1_tag) {
- case TW_Denormal:
- if (denormal_operand() < 0)
- return;
- case TAG_Zero:
- case TAG_Valid:
- setsign(st0_ptr, getsign(st0_ptr) ^ getsign(st1_ptr));
- FPU_copy_to_reg1(st0_ptr, st0_tag);
- break;
- case TW_Infinity:
- /* Infinity*log(1) */
- if (arith_invalid(1) < 0)
- return;
- break;
- case TW_NaN:
- if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
- return;
- break;
- default:
- #ifdef PARANOID
- EXCEPTION(EX_INTERNAL | 0x116);
- return;
- #endif /* PARANOID */
- break;
- }
- } else if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
- switch (st1_tag) {
- case TAG_Zero:
- if (signnegative(st0_ptr)) {
- if (exponent(st0_ptr) >= 0) {
- /* st(0) holds <= -1.0 */
- #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
- changesign(st1_ptr);
- #else
- if (arith_invalid(1) < 0)
- return;
- #endif /* PECULIAR_486 */
- } else if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- else
- changesign(st1_ptr);
- } else if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- break;
- case TW_Infinity:
- if (signnegative(st0_ptr)) {
- if ((exponent(st0_ptr) >= 0) &&
- !((st0_ptr->sigh == 0x80000000) &&
- (st0_ptr->sigl == 0))) {
- /* st(0) holds < -1.0 */
- #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
- changesign(st1_ptr);
- #else
- if (arith_invalid(1) < 0)
- return;
- #endif /* PECULIAR_486 */
- } else if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- else
- changesign(st1_ptr);
- } else if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- break;
- case TW_NaN:
- if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
- return;
- }
- } else if (st0_tag == TW_NaN) {
- if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
- return;
- } else if (st0_tag == TW_Infinity) {
- if (st1_tag == TW_NaN) {
- if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
- return;
- } else if (signnegative(st0_ptr)) {
- #ifndef PECULIAR_486
- /* This should have higher priority than denormals, but... */
- if (arith_invalid(1) < 0) /* log(-infinity) */
- return;
- #endif /* PECULIAR_486 */
- if ((st1_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- #ifdef PECULIAR_486
- /* Denormal operands actually get higher priority */
- if (arith_invalid(1) < 0) /* log(-infinity) */
- return;
- #endif /* PECULIAR_486 */
- } else if (st1_tag == TAG_Zero) {
- /* log(infinity) */
- if (arith_invalid(1) < 0)
- return;
- }
- /* st(1) must be valid here. */
- else if ((st1_tag == TW_Denormal) && (denormal_operand() < 0))
- return;
- /* The Manual says that log(Infinity) is invalid, but a real
- 80486 sensibly says that it is o.k. */
- else {
- u_char sign = getsign(st1_ptr);
- FPU_copy_to_reg1(&CONST_INF, TAG_Special);
- setsign(st1_ptr, sign);
- }
- }
- #ifdef PARANOID
- else {
- EXCEPTION(EX_INTERNAL | 0x117);
- return;
- }
- #endif /* PARANOID */
- FPU_pop();
- return;
- }
- static void fscale(FPU_REG *st0_ptr, u_char st0_tag)
- {
- FPU_REG *st1_ptr = &st(1);
- u_char st1_tag = FPU_gettagi(1);
- int old_cw = control_word;
- u_char sign = getsign(st0_ptr);
- clear_C1();
- if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
- long scale;
- FPU_REG tmp;
- /* Convert register for internal use. */
- setexponent16(st0_ptr, exponent(st0_ptr));
- valid_scale:
- if (exponent(st1_ptr) > 30) {
- /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
- if (signpositive(st1_ptr)) {
- EXCEPTION(EX_Overflow);
- FPU_copy_to_reg0(&CONST_INF, TAG_Special);
- } else {
- EXCEPTION(EX_Underflow);
- FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
- }
- setsign(st0_ptr, sign);
- return;
- }
- control_word &= ~CW_RC;
- control_word |= RC_CHOP;
- reg_copy(st1_ptr, &tmp);
- FPU_round_to_int(&tmp, st1_tag); /* This can never overflow here */
- control_word = old_cw;
- scale = signnegative(st1_ptr) ? -tmp.sigl : tmp.sigl;
- scale += exponent16(st0_ptr);
- setexponent16(st0_ptr, scale);
- /* Use FPU_round() to properly detect under/overflow etc */
- FPU_round(st0_ptr, 0, 0, control_word, sign);
- return;
- }
- if (st0_tag == TAG_Special)
- st0_tag = FPU_Special(st0_ptr);
- if (st1_tag == TAG_Special)
- st1_tag = FPU_Special(st1_ptr);
- if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
- switch (st1_tag) {
- case TAG_Valid:
- /* st(0) must be a denormal */
- if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- FPU_to_exp16(st0_ptr, st0_ptr); /* Will not be left on stack */
- goto valid_scale;
- case TAG_Zero:
- if (st0_tag == TW_Denormal)
- denormal_operand();
- return;
- case TW_Denormal:
- denormal_operand();
- return;
- case TW_Infinity:
- if ((st0_tag == TW_Denormal)
- && (denormal_operand() < 0))
- return;
- if (signpositive(st1_ptr))
- FPU_copy_to_reg0(&CONST_INF, TAG_Special);
- else
- FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
- setsign(st0_ptr, sign);
- return;
- case TW_NaN:
- real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
- return;
- }
- } else if (st0_tag == TAG_Zero) {
- switch (st1_tag) {
- case TAG_Valid:
- case TAG_Zero:
- return;
- case TW_Denormal:
- denormal_operand();
- return;
- case TW_Infinity:
- if (signpositive(st1_ptr))
- arith_invalid(0); /* Zero scaled by +Infinity */
- return;
- case TW_NaN:
- real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
- return;
- }
- } else if (st0_tag == TW_Infinity) {
- switch (st1_tag) {
- case TAG_Valid:
- case TAG_Zero:
- return;
- case TW_Denormal:
- denormal_operand();
- return;
- case TW_Infinity:
- if (signnegative(st1_ptr))
- arith_invalid(0); /* Infinity scaled by -Infinity */
- return;
- case TW_NaN:
- real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
- return;
- }
- } else if (st0_tag == TW_NaN) {
- if (st1_tag != TAG_Empty) {
- real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
- return;
- }
- }
- #ifdef PARANOID
- if (!((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty))) {
- EXCEPTION(EX_INTERNAL | 0x115);
- return;
- }
- #endif
- /* At least one of st(0), st(1) must be empty */
- FPU_stack_underflow();
- }
- /*---------------------------------------------------------------------------*/
- static FUNC_ST0 const trig_table_a[] = {
- f2xm1, fyl2x, fptan, fpatan,
- fxtract, fprem1, (FUNC_ST0) fdecstp, (FUNC_ST0) fincstp
- };
- void FPU_triga(void)
- {
- (trig_table_a[FPU_rm]) (&st(0), FPU_gettag0());
- }
- static FUNC_ST0 const trig_table_b[] = {
- fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, (FUNC_ST0) fsin, fcos
- };
- void FPU_trigb(void)
- {
- (trig_table_b[FPU_rm]) (&st(0), FPU_gettag0());
- }
|