0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
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
0035
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
0053 if (!(signa ^ signb)) {
0054
0055 tag =
0056 FPU_u_add(a, b, dest, control_w, signa, expa, expb);
0057 } else {
0058
0059 diff = expa - expb;
0060 if (!diff) {
0061 diff = a->sigh - b->sigh;
0062
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
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
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
0168
0169 diff = expa - expb;
0170
0171 if (!diff) {
0172 diff = a->sigh - b->sigh;
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:
0182 case 3:
0183 if (diff > 0) {
0184
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
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:
0203 tag =
0204 FPU_u_add(a, b, dest, control_w, SIGN_POS, expa,
0205 expb);
0206 break;
0207 case 2:
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
0282 u_char different_signs = signa ^ signb;
0283
0284 FPU_copy_to_regi(a, TAG_Zero, deststnr);
0285 if (different_signs) {
0286
0287
0288 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
0289 ? SIGN_POS : SIGN_NEG);
0290 } else
0291 setsign(dest, signa);
0292 return TAG_Zero;
0293 } else {
0294 reg_copy(b, dest);
0295 if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
0296
0297 addexponent(dest, 1);
0298 tagb = TAG_Valid;
0299 } else if (tagb > TAG_Empty)
0300 tagb = TAG_Special;
0301 setsign(dest, signb);
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
0309 addexponent(dest, 1);
0310 taga = TAG_Valid;
0311 } else if (taga > TAG_Empty)
0312 taga = TAG_Special;
0313 setsign(dest, signa);
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);
0320 return taga;
0321 }
0322
0323 return arith_invalid(deststnr);
0324 } else if (tagb == TW_Infinity) {
0325 FPU_copy_to_regi(b, TAG_Special, deststnr);
0326 setsign(dest, signb);
0327 return tagb;
0328 }
0329 #ifdef PARANOID
0330 EXCEPTION(EX_INTERNAL | 0x101);
0331 #endif
0332
0333 return FPU_Exception;
0334 }