| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /*---------------------------------------------------------------------------+ | 
 | 2 |  |  reg_mul.c                                                                | | 
 | 3 |  |                                                                           | | 
 | 4 |  | Multiply one FPU_REG by another, put the result in a destination FPU_REG. | | 
 | 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 |  | Returns the tag of the result if no exceptions or errors occurred.        | | 
 | 11 |  |                                                                           | | 
 | 12 |  +---------------------------------------------------------------------------*/ | 
 | 13 |  | 
 | 14 | /*---------------------------------------------------------------------------+ | 
 | 15 |  | The destination may be any FPU_REG, including one of the source FPU_REGs. | | 
 | 16 |  +---------------------------------------------------------------------------*/ | 
 | 17 |  | 
 | 18 | #include "fpu_emu.h" | 
 | 19 | #include "exception.h" | 
 | 20 | #include "reg_constant.h" | 
 | 21 | #include "fpu_system.h" | 
 | 22 |  | 
 | 23 |  | 
 | 24 | /* | 
 | 25 |   Multiply two registers to give a register result. | 
 | 26 |   The sources are st(deststnr) and (b,tagb,signb). | 
 | 27 |   The destination is st(deststnr). | 
 | 28 |   */ | 
 | 29 | /* This routine must be called with non-empty source registers */ | 
 | 30 | int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w) | 
 | 31 | { | 
 | 32 |   FPU_REG *a = &st(deststnr); | 
 | 33 |   FPU_REG *dest = a; | 
 | 34 |   u_char taga = FPU_gettagi(deststnr); | 
 | 35 |   u_char saved_sign = getsign(dest); | 
 | 36 |   u_char sign = (getsign(a) ^ getsign(b)); | 
 | 37 |   int tag; | 
 | 38 |  | 
 | 39 |  | 
 | 40 |   if ( !(taga | tagb) ) | 
 | 41 |     { | 
 | 42 |       /* Both regs Valid, this should be the most common case. */ | 
 | 43 |  | 
 | 44 |       tag = FPU_u_mul(a, b, dest, control_w, sign, exponent(a) + exponent(b)); | 
 | 45 |       if ( tag < 0 ) | 
 | 46 | 	{ | 
 | 47 | 	  setsign(dest, saved_sign); | 
 | 48 | 	  return tag; | 
 | 49 | 	} | 
 | 50 |       FPU_settagi(deststnr, tag); | 
 | 51 |       return tag; | 
 | 52 |     } | 
 | 53 |  | 
 | 54 |   if ( taga == TAG_Special ) | 
 | 55 |     taga = FPU_Special(a); | 
 | 56 |   if ( tagb == TAG_Special ) | 
 | 57 |     tagb = FPU_Special(b); | 
 | 58 |  | 
 | 59 |   if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) | 
 | 60 | 	    || ((taga == TW_Denormal) && (tagb == TAG_Valid)) | 
 | 61 | 	    || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) | 
 | 62 |     { | 
 | 63 |       FPU_REG x, y; | 
 | 64 |       if ( denormal_operand() < 0 ) | 
 | 65 | 	return FPU_Exception; | 
 | 66 |  | 
 | 67 |       FPU_to_exp16(a, &x); | 
 | 68 |       FPU_to_exp16(b, &y); | 
 | 69 |       tag = FPU_u_mul(&x, &y, dest, control_w, sign, | 
 | 70 | 		      exponent16(&x) + exponent16(&y)); | 
 | 71 |       if ( tag < 0 ) | 
 | 72 | 	{ | 
 | 73 | 	  setsign(dest, saved_sign); | 
 | 74 | 	  return tag; | 
 | 75 | 	} | 
 | 76 |       FPU_settagi(deststnr, tag); | 
 | 77 |       return tag; | 
 | 78 |     } | 
 | 79 |   else if ( (taga <= TW_Denormal) && (tagb <= TW_Denormal) ) | 
 | 80 |     { | 
 | 81 |       if ( ((tagb == TW_Denormal) || (taga == TW_Denormal)) | 
 | 82 | 	   && (denormal_operand() < 0) ) | 
 | 83 | 	return FPU_Exception; | 
 | 84 |  | 
 | 85 |       /* Must have either both arguments == zero, or | 
 | 86 | 	 one valid and the other zero. | 
 | 87 | 	 The result is therefore zero. */ | 
 | 88 |       FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); | 
 | 89 |       /* The 80486 book says that the answer is +0, but a real | 
 | 90 | 	 80486 behaves this way. | 
 | 91 | 	 IEEE-754 apparently says it should be this way. */ | 
 | 92 |       setsign(dest, sign); | 
 | 93 |       return TAG_Zero; | 
 | 94 |     } | 
 | 95 |       /* Must have infinities, NaNs, etc */ | 
 | 96 |   else if ( (taga == TW_NaN) || (tagb == TW_NaN) ) | 
 | 97 |     { | 
 | 98 |       return real_2op_NaN(b, tagb, deststnr, &st(0)); | 
 | 99 |     } | 
 | 100 |   else if ( ((taga == TW_Infinity) && (tagb == TAG_Zero)) | 
 | 101 | 	    || ((tagb == TW_Infinity) && (taga == TAG_Zero)) ) | 
 | 102 |     { | 
 | 103 |       return arith_invalid(deststnr);  /* Zero*Infinity is invalid */ | 
 | 104 |     } | 
 | 105 |   else if ( ((taga == TW_Denormal) || (tagb == TW_Denormal)) | 
 | 106 | 	    && (denormal_operand() < 0) ) | 
 | 107 |     { | 
 | 108 |       return FPU_Exception; | 
 | 109 |     } | 
 | 110 |   else if (taga == TW_Infinity) | 
 | 111 |     { | 
 | 112 |       FPU_copy_to_regi(a, TAG_Special, deststnr); | 
 | 113 |       setsign(dest, sign); | 
 | 114 |       return TAG_Special; | 
 | 115 |     } | 
 | 116 |   else if (tagb == TW_Infinity) | 
 | 117 |     { | 
 | 118 |       FPU_copy_to_regi(b, TAG_Special, deststnr); | 
 | 119 |       setsign(dest, sign); | 
 | 120 |       return TAG_Special; | 
 | 121 |     } | 
 | 122 |  | 
 | 123 | #ifdef PARANOID | 
 | 124 |   else | 
 | 125 |     { | 
 | 126 |       EXCEPTION(EX_INTERNAL|0x102); | 
 | 127 |       return FPU_Exception; | 
 | 128 |     } | 
 | 129 | #endif /* PARANOID */  | 
 | 130 |  | 
 | 131 | 	return 0; | 
 | 132 | } |