1 /*---------------------------------------------------------------------------+ 2 | reg_add_sub.c | 3 | | 4 | Functions to add or subtract two registers and put the result in a third. | 5 | | 6 | Copyright (C) 1992,1993,1997 | 7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | 8 | E-mail billm@suburbia.net | 9 | | 10 | | 11 +---------------------------------------------------------------------------*/ 12 13 /*---------------------------------------------------------------------------+ 14 | For each function, the destination may be any FPU_REG, including one of | 15 | the source FPU_REGs. | 16 | Each function returns 0 if the answer is o.k., otherwise a non-zero | 17 | value is returned, indicating either an exception condition or an | 18 | internal error. | 19 +---------------------------------------------------------------------------*/ 20 21 #include "exception.h" 22 #include "reg_constant.h" 23 #include "fpu_emu.h" 24 #include "control_w.h" 25 #include "fpu_system.h" 26 27 static 28 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, 29 FPU_REG const *b, u_char tagb, u_char signb, 30 FPU_REG *dest, int deststnr, int control_w); 31 32 /* 33 Operates on st(0) and st(n), or on st(0) and temporary data. 34 The destination must be one of the source st(x). 35 */ 36 int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w) 37 { 38 FPU_REG *a = &st(0); 39 FPU_REG *dest = &st(deststnr); 40 u_char signb = getsign(b); 41 u_char taga = FPU_gettag0(); 42 u_char signa = getsign(a); 43 u_char saved_sign = getsign(dest); 44 int diff, tag, expa, expb; 45 46 if ( !(taga | tagb) ) 47 { 48 expa = exponent(a); 49 expb = exponent(b); 50 51 valid_add: 52 /* Both registers are valid */ 53 if (!(signa ^ signb)) 54 { 55 /* signs are the same */ 56 tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb); 57 } 58 else 59 { 60 /* The signs are different, so do a subtraction */ 61 diff = expa - expb; 62 if (!diff) 63 { 64 diff = a->sigh - b->sigh; /* This works only if the ms bits 65 are identical. */ 66 if (!diff) 67 { 68 diff = a->sigl > b->sigl; 69 if (!diff) 70 diff = -(a->sigl < b->sigl); 71 } 72 } 73 74 if (diff > 0) 75 { 76 tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); 77 } 78 else if ( diff < 0 ) 79 { 80 tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa); 81 } 82 else 83 { 84 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); 85 /* sign depends upon rounding mode */ 86 setsign(dest, ((control_w & CW_RC) != RC_DOWN) 87 ? SIGN_POS : SIGN_NEG); 88 return TAG_Zero; 89 } 90 } 91 92 if ( tag < 0 ) 93 { 94 setsign(dest, saved_sign); 95 return tag; 96 } 97 FPU_settagi(deststnr, tag); 98 return tag; 99 } 100 101 if ( taga == TAG_Special ) 102 taga = FPU_Special(a); 103 if ( tagb == TAG_Special ) 104 tagb = FPU_Special(b); 105 106 if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) 107 || ((taga == TW_Denormal) && (tagb == TAG_Valid)) 108 || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) 109 { 110 FPU_REG x, y; 111 112 if ( denormal_operand() < 0 ) 113 return FPU_Exception; 114 115 FPU_to_exp16(a, &x); 116 FPU_to_exp16(b, &y); 117 a = &x; 118 b = &y; 119 expa = exponent16(a); 120 expb = exponent16(b); 121 goto valid_add; 122 } 123 124 if ( (taga == TW_NaN) || (tagb == TW_NaN) ) 125 { 126 if ( deststnr == 0 ) 127 return real_2op_NaN(b, tagb, deststnr, a); 128 else 129 return real_2op_NaN(a, taga, deststnr, a); 130 } 131 132 return add_sub_specials(a, taga, signa, b, tagb, signb, 133 dest, deststnr, control_w); 134 } 135 136 137 /* Subtract b from a. (a-b) -> dest */ 138 int FPU_sub(int flags, int rm, int control_w) 139 { 140 FPU_REG const *a, *b; 141 FPU_REG *dest; 142 u_char taga, tagb, signa, signb, saved_sign, sign; 143 int diff, tag = 0, expa, expb, deststnr; 144 145 a = &st(0); 146 taga = FPU_gettag0(); 147 148 deststnr = 0; 149 if ( flags & LOADED ) 150 { 151 b = (FPU_REG *)rm; 152 tagb = flags & 0x0f; 153 } 154 else 155 { 156 b = &st(rm); 157 tagb = FPU_gettagi(rm); 158 159 if ( flags & DEST_RM ) 160 deststnr = rm; 161 } 162 163 signa = getsign(a); 164 signb = getsign(b); 165 166 if ( flags & REV ) 167 { 168 signa ^= SIGN_NEG; 169 signb ^= SIGN_NEG; 170 } 171 172 dest = &st(deststnr); 173 saved_sign = getsign(dest); 174 175 if ( !(taga | tagb) ) 176 { 177 expa = exponent(a); 178 expb = exponent(b); 179 180 valid_subtract: 181 /* Both registers are valid */ 182 183 diff = expa - expb; 184 185 if (!diff) 186 { 187 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ 188 if (!diff) 189 { 190 diff = a->sigl > b->sigl; 191 if (!diff) 192 diff = -(a->sigl < b->sigl); 193 } 194 } 195 196 switch ( (((int)signa)*2 + signb) / SIGN_NEG ) 197 { 198 case 0: /* P - P */ 199 case 3: /* N - N */ 200 if (diff > 0) 201 { 202 /* |a| > |b| */ 203 tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); 204 } 205 else if ( diff == 0 ) 206 { 207 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); 208 209 /* sign depends upon rounding mode */ 210 setsign(dest, ((control_w & CW_RC) != RC_DOWN) 211 ? SIGN_POS : SIGN_NEG); 212 return TAG_Zero; 213 } 214 else 215 { 216 sign = signa ^ SIGN_NEG; 217 tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa); 218 } 219 break; 220 case 1: /* P - N */ 221 tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb); 222 break; 223 case 2: /* N - P */ 224 tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb); 225 break; 226 #ifdef PARANOID 227 default: 228 EXCEPTION(EX_INTERNAL|0x111); 229 return -1; 230 #endif 231 } 232 if ( tag < 0 ) 233 { 234 setsign(dest, saved_sign); 235 return tag; 236 } 237 FPU_settagi(deststnr, tag); 238 return tag; 239 } 240 241 if ( taga == TAG_Special ) 242 taga = FPU_Special(a); 243 if ( tagb == TAG_Special ) 244 tagb = FPU_Special(b); 245 246 if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) 247 || ((taga == TW_Denormal) && (tagb == TAG_Valid)) 248 || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) 249 { 250 FPU_REG x, y; 251 252 if ( denormal_operand() < 0 ) 253 return FPU_Exception; 254 255 FPU_to_exp16(a, &x); 256 FPU_to_exp16(b, &y); 257 a = &x; 258 b = &y; 259 expa = exponent16(a); 260 expb = exponent16(b); 261 262 goto valid_subtract; 263 } 264 265 if ( (taga == TW_NaN) || (tagb == TW_NaN) ) 266 { 267 FPU_REG const *d1, *d2; 268 if ( flags & REV ) 269 { 270 d1 = b; 271 d2 = a; 272 } 273 else 274 { 275 d1 = a; 276 d2 = b; 277 } 278 if ( flags & LOADED ) 279 return real_2op_NaN(b, tagb, deststnr, d1); 280 if ( flags & DEST_RM ) 281 return real_2op_NaN(a, taga, deststnr, d2); 282 else 283 return real_2op_NaN(b, tagb, deststnr, d2); 284 } 285 286 return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG, 287 dest, deststnr, control_w); 288 } 289 290 291 static 292 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, 293 FPU_REG const *b, u_char tagb, u_char signb, 294 FPU_REG *dest, int deststnr, int control_w) 295 { 296 if ( ((taga == TW_Denormal) || (tagb == TW_Denormal)) 297 && (denormal_operand() < 0) ) 298 return FPU_Exception; 299 300 if (taga == TAG_Zero) 301 { 302 if (tagb == TAG_Zero) 303 { 304 /* Both are zero, result will be zero. */ 305 u_char different_signs = signa ^ signb; 306 307 FPU_copy_to_regi(a, TAG_Zero, deststnr); 308 if ( different_signs ) 309 { 310 /* Signs are different. */ 311 /* Sign of answer depends upon rounding mode. */ 312 setsign(dest, ((control_w & CW_RC) != RC_DOWN) 313 ? SIGN_POS : SIGN_NEG); 314 } 315 else 316 setsign(dest, signa); /* signa may differ from the sign of a. */ 317 return TAG_Zero; 318 } 319 else 320 { 321 reg_copy(b, dest); 322 if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) ) 323 { 324 /* A pseudoDenormal, convert it. */ 325 addexponent(dest, 1); 326 tagb = TAG_Valid; 327 } 328 else if ( tagb > TAG_Empty ) 329 tagb = TAG_Special; 330 setsign(dest, signb); /* signb may differ from the sign of b. */ 331 FPU_settagi(deststnr, tagb); 332 return tagb; 333 } 334 } 335 else if (tagb == TAG_Zero) 336 { 337 reg_copy(a, dest); 338 if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) ) 339 { 340 /* A pseudoDenormal */ 341 addexponent(dest, 1); 342 taga = TAG_Valid; 343 } 344 else if ( taga > TAG_Empty ) 345 taga = TAG_Special; 346 setsign(dest, signa); /* signa may differ from the sign of a. */ 347 FPU_settagi(deststnr, taga); 348 return taga; 349 } 350 else if (taga == TW_Infinity) 351 { 352 if ( (tagb != TW_Infinity) || (signa == signb) ) 353 { 354 FPU_copy_to_regi(a, TAG_Special, deststnr); 355 setsign(dest, signa); /* signa may differ from the sign of a. */ 356 return taga; 357 } 358 /* Infinity-Infinity is undefined. */ 359 return arith_invalid(deststnr); 360 } 361 else if (tagb == TW_Infinity) 362 { 363 FPU_copy_to_regi(b, TAG_Special, deststnr); 364 setsign(dest, signb); /* signb may differ from the sign of b. */ 365 return tagb; 366 } 367 368 #ifdef PARANOID 369 EXCEPTION(EX_INTERNAL|0x101); 370 #endif 371 372 return FPU_Exception; 373 } 374 375