Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*---------------------------------------------------------------------------+
0003  |  reg_add_sub.c                                                            |
0004  |                                                                           |
0005  | Functions to add or subtract two registers and put the result in a third. |
0006  |                                                                           |
0007  | Copyright (C) 1992,1993,1997                                              |
0008  |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
0009  |                  E-mail   billm@suburbia.net                              |
0010  |                                                                           |
0011  |                                                                           |
0012  +---------------------------------------------------------------------------*/
0013 
0014 /*---------------------------------------------------------------------------+
0015  |  For each function, the destination may be any FPU_REG, including one of  |
0016  | the source FPU_REGs.                                                      |
0017  |  Each function returns 0 if the answer is o.k., otherwise a non-zero      |
0018  | value is returned, indicating either an exception condition or an         |
0019  | internal error.                                                           |
0020  +---------------------------------------------------------------------------*/
0021 
0022 #include "exception.h"
0023 #include "reg_constant.h"
0024 #include "fpu_emu.h"
0025 #include "control_w.h"
0026 #include "fpu_system.h"
0027 
0028 static
0029 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
0030              FPU_REG const *b, u_char tagb, u_char signb,
0031              FPU_REG * dest, int deststnr, int control_w);
0032 
0033 /*
0034   Operates on st(0) and st(n), or on st(0) and temporary data.
0035   The destination must be one of the source st(x).
0036   */
0037 int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
0038 {
0039     FPU_REG *a = &st(0);
0040     FPU_REG *dest = &st(deststnr);
0041     u_char signb = getsign(b);
0042     u_char taga = FPU_gettag0();
0043     u_char signa = getsign(a);
0044     u_char saved_sign = getsign(dest);
0045     int diff, tag, expa, expb;
0046 
0047     if (!(taga | tagb)) {
0048         expa = exponent(a);
0049         expb = exponent(b);
0050 
0051           valid_add:
0052         /* Both registers are valid */
0053         if (!(signa ^ signb)) {
0054             /* signs are the same */
0055             tag =
0056                 FPU_u_add(a, b, dest, control_w, signa, expa, expb);
0057         } else {
0058             /* The signs are different, so do a subtraction */
0059             diff = expa - expb;
0060             if (!diff) {
0061                 diff = a->sigh - b->sigh;   /* This works only if the ms bits
0062                                    are identical. */
0063                 if (!diff) {
0064                     diff = a->sigl > b->sigl;
0065                     if (!diff)
0066                         diff = -(a->sigl < b->sigl);
0067                 }
0068             }
0069 
0070             if (diff > 0) {
0071                 tag =
0072                     FPU_u_sub(a, b, dest, control_w, signa,
0073                           expa, expb);
0074             } else if (diff < 0) {
0075                 tag =
0076                     FPU_u_sub(b, a, dest, control_w, signb,
0077                           expb, expa);
0078             } else {
0079                 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
0080                 /* sign depends upon rounding mode */
0081                 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
0082                     ? SIGN_POS : SIGN_NEG);
0083                 return TAG_Zero;
0084             }
0085         }
0086 
0087         if (tag < 0) {
0088             setsign(dest, saved_sign);
0089             return tag;
0090         }
0091         FPU_settagi(deststnr, tag);
0092         return tag;
0093     }
0094 
0095     if (taga == TAG_Special)
0096         taga = FPU_Special(a);
0097     if (tagb == TAG_Special)
0098         tagb = FPU_Special(b);
0099 
0100     if (((taga == TAG_Valid) && (tagb == TW_Denormal))
0101         || ((taga == TW_Denormal) && (tagb == TAG_Valid))
0102         || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
0103         FPU_REG x, y;
0104 
0105         if (denormal_operand() < 0)
0106             return FPU_Exception;
0107 
0108         FPU_to_exp16(a, &x);
0109         FPU_to_exp16(b, &y);
0110         a = &x;
0111         b = &y;
0112         expa = exponent16(a);
0113         expb = exponent16(b);
0114         goto valid_add;
0115     }
0116 
0117     if ((taga == TW_NaN) || (tagb == TW_NaN)) {
0118         if (deststnr == 0)
0119             return real_2op_NaN(b, tagb, deststnr, a);
0120         else
0121             return real_2op_NaN(a, taga, deststnr, a);
0122     }
0123 
0124     return add_sub_specials(a, taga, signa, b, tagb, signb,
0125                 dest, deststnr, control_w);
0126 }
0127 
0128 /* Subtract b from a.  (a-b) -> dest */
0129 int FPU_sub(int flags, int rm, int control_w)
0130 {
0131     FPU_REG const *a, *b;
0132     FPU_REG *dest;
0133     u_char taga, tagb, signa, signb, saved_sign, sign;
0134     int diff, tag = 0, expa, expb, deststnr;
0135 
0136     a = &st(0);
0137     taga = FPU_gettag0();
0138 
0139     deststnr = 0;
0140     if (flags & LOADED) {
0141         b = (FPU_REG *) rm;
0142         tagb = flags & 0x0f;
0143     } else {
0144         b = &st(rm);
0145         tagb = FPU_gettagi(rm);
0146 
0147         if (flags & DEST_RM)
0148             deststnr = rm;
0149     }
0150 
0151     signa = getsign(a);
0152     signb = getsign(b);
0153 
0154     if (flags & REV) {
0155         signa ^= SIGN_NEG;
0156         signb ^= SIGN_NEG;
0157     }
0158 
0159     dest = &st(deststnr);
0160     saved_sign = getsign(dest);
0161 
0162     if (!(taga | tagb)) {
0163         expa = exponent(a);
0164         expb = exponent(b);
0165 
0166           valid_subtract:
0167         /* Both registers are valid */
0168 
0169         diff = expa - expb;
0170 
0171         if (!diff) {
0172             diff = a->sigh - b->sigh;   /* Works only if ms bits are identical */
0173             if (!diff) {
0174                 diff = a->sigl > b->sigl;
0175                 if (!diff)
0176                     diff = -(a->sigl < b->sigl);
0177             }
0178         }
0179 
0180         switch ((((int)signa) * 2 + signb) / SIGN_NEG) {
0181         case 0: /* P - P */
0182         case 3: /* N - N */
0183             if (diff > 0) {
0184                 /* |a| > |b| */
0185                 tag =
0186                     FPU_u_sub(a, b, dest, control_w, signa,
0187                           expa, expb);
0188             } else if (diff == 0) {
0189                 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
0190 
0191                 /* sign depends upon rounding mode */
0192                 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
0193                     ? SIGN_POS : SIGN_NEG);
0194                 return TAG_Zero;
0195             } else {
0196                 sign = signa ^ SIGN_NEG;
0197                 tag =
0198                     FPU_u_sub(b, a, dest, control_w, sign, expb,
0199                           expa);
0200             }
0201             break;
0202         case 1: /* P - N */
0203             tag =
0204                 FPU_u_add(a, b, dest, control_w, SIGN_POS, expa,
0205                       expb);
0206             break;
0207         case 2: /* N - P */
0208             tag =
0209                 FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa,
0210                       expb);
0211             break;
0212 #ifdef PARANOID
0213         default:
0214             EXCEPTION(EX_INTERNAL | 0x111);
0215             return -1;
0216 #endif
0217         }
0218         if (tag < 0) {
0219             setsign(dest, saved_sign);
0220             return tag;
0221         }
0222         FPU_settagi(deststnr, tag);
0223         return tag;
0224     }
0225 
0226     if (taga == TAG_Special)
0227         taga = FPU_Special(a);
0228     if (tagb == TAG_Special)
0229         tagb = FPU_Special(b);
0230 
0231     if (((taga == TAG_Valid) && (tagb == TW_Denormal))
0232         || ((taga == TW_Denormal) && (tagb == TAG_Valid))
0233         || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
0234         FPU_REG x, y;
0235 
0236         if (denormal_operand() < 0)
0237             return FPU_Exception;
0238 
0239         FPU_to_exp16(a, &x);
0240         FPU_to_exp16(b, &y);
0241         a = &x;
0242         b = &y;
0243         expa = exponent16(a);
0244         expb = exponent16(b);
0245 
0246         goto valid_subtract;
0247     }
0248 
0249     if ((taga == TW_NaN) || (tagb == TW_NaN)) {
0250         FPU_REG const *d1, *d2;
0251         if (flags & REV) {
0252             d1 = b;
0253             d2 = a;
0254         } else {
0255             d1 = a;
0256             d2 = b;
0257         }
0258         if (flags & LOADED)
0259             return real_2op_NaN(b, tagb, deststnr, d1);
0260         if (flags & DEST_RM)
0261             return real_2op_NaN(a, taga, deststnr, d2);
0262         else
0263             return real_2op_NaN(b, tagb, deststnr, d2);
0264     }
0265 
0266     return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
0267                 dest, deststnr, control_w);
0268 }
0269 
0270 static
0271 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
0272              FPU_REG const *b, u_char tagb, u_char signb,
0273              FPU_REG * dest, int deststnr, int control_w)
0274 {
0275     if (((taga == TW_Denormal) || (tagb == TW_Denormal))
0276         && (denormal_operand() < 0))
0277         return FPU_Exception;
0278 
0279     if (taga == TAG_Zero) {
0280         if (tagb == TAG_Zero) {
0281             /* Both are zero, result will be zero. */
0282             u_char different_signs = signa ^ signb;
0283 
0284             FPU_copy_to_regi(a, TAG_Zero, deststnr);
0285             if (different_signs) {
0286                 /* Signs are different. */
0287                 /* Sign of answer depends upon rounding mode. */
0288                 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
0289                     ? SIGN_POS : SIGN_NEG);
0290             } else
0291                 setsign(dest, signa);   /* signa may differ from the sign of a. */
0292             return TAG_Zero;
0293         } else {
0294             reg_copy(b, dest);
0295             if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
0296                 /* A pseudoDenormal, convert it. */
0297                 addexponent(dest, 1);
0298                 tagb = TAG_Valid;
0299             } else if (tagb > TAG_Empty)
0300                 tagb = TAG_Special;
0301             setsign(dest, signb);   /* signb may differ from the sign of b. */
0302             FPU_settagi(deststnr, tagb);
0303             return tagb;
0304         }
0305     } else if (tagb == TAG_Zero) {
0306         reg_copy(a, dest);
0307         if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) {
0308             /* A pseudoDenormal */
0309             addexponent(dest, 1);
0310             taga = TAG_Valid;
0311         } else if (taga > TAG_Empty)
0312             taga = TAG_Special;
0313         setsign(dest, signa);   /* signa may differ from the sign of a. */
0314         FPU_settagi(deststnr, taga);
0315         return taga;
0316     } else if (taga == TW_Infinity) {
0317         if ((tagb != TW_Infinity) || (signa == signb)) {
0318             FPU_copy_to_regi(a, TAG_Special, deststnr);
0319             setsign(dest, signa);   /* signa may differ from the sign of a. */
0320             return taga;
0321         }
0322         /* Infinity-Infinity is undefined. */
0323         return arith_invalid(deststnr);
0324     } else if (tagb == TW_Infinity) {
0325         FPU_copy_to_regi(b, TAG_Special, deststnr);
0326         setsign(dest, signb);   /* signb may differ from the sign of b. */
0327         return tagb;
0328     }
0329 #ifdef PARANOID
0330     EXCEPTION(EX_INTERNAL | 0x101);
0331 #endif
0332 
0333     return FPU_Exception;
0334 }