reg_add_sub.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  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. | For each function, the destination may be any FPU_REG, including one of |
  14. | the source FPU_REGs. |
  15. | Each function returns 0 if the answer is o.k., otherwise a non-zero |
  16. | value is returned, indicating either an exception condition or an |
  17. | internal error. |
  18. +---------------------------------------------------------------------------*/
  19. #include "exception.h"
  20. #include "reg_constant.h"
  21. #include "fpu_emu.h"
  22. #include "control_w.h"
  23. #include "fpu_system.h"
  24. static
  25. int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
  26. FPU_REG const *b, u_char tagb, u_char signb,
  27. FPU_REG * dest, int deststnr, int control_w);
  28. /*
  29. Operates on st(0) and st(n), or on st(0) and temporary data.
  30. The destination must be one of the source st(x).
  31. */
  32. int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
  33. {
  34. FPU_REG *a = &st(0);
  35. FPU_REG *dest = &st(deststnr);
  36. u_char signb = getsign(b);
  37. u_char taga = FPU_gettag0();
  38. u_char signa = getsign(a);
  39. u_char saved_sign = getsign(dest);
  40. int diff, tag, expa, expb;
  41. if (!(taga | tagb)) {
  42. expa = exponent(a);
  43. expb = exponent(b);
  44. valid_add:
  45. /* Both registers are valid */
  46. if (!(signa ^ signb)) {
  47. /* signs are the same */
  48. tag =
  49. FPU_u_add(a, b, dest, control_w, signa, expa, expb);
  50. } else {
  51. /* The signs are different, so do a subtraction */
  52. diff = expa - expb;
  53. if (!diff) {
  54. diff = a->sigh - b->sigh; /* This works only if the ms bits
  55. are identical. */
  56. if (!diff) {
  57. diff = a->sigl > b->sigl;
  58. if (!diff)
  59. diff = -(a->sigl < b->sigl);
  60. }
  61. }
  62. if (diff > 0) {
  63. tag =
  64. FPU_u_sub(a, b, dest, control_w, signa,
  65. expa, expb);
  66. } else if (diff < 0) {
  67. tag =
  68. FPU_u_sub(b, a, dest, control_w, signb,
  69. expb, expa);
  70. } else {
  71. FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
  72. /* sign depends upon rounding mode */
  73. setsign(dest, ((control_w & CW_RC) != RC_DOWN)
  74. ? SIGN_POS : SIGN_NEG);
  75. return TAG_Zero;
  76. }
  77. }
  78. if (tag < 0) {
  79. setsign(dest, saved_sign);
  80. return tag;
  81. }
  82. FPU_settagi(deststnr, tag);
  83. return tag;
  84. }
  85. if (taga == TAG_Special)
  86. taga = FPU_Special(a);
  87. if (tagb == TAG_Special)
  88. tagb = FPU_Special(b);
  89. if (((taga == TAG_Valid) && (tagb == TW_Denormal))
  90. || ((taga == TW_Denormal) && (tagb == TAG_Valid))
  91. || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
  92. FPU_REG x, y;
  93. if (denormal_operand() < 0)
  94. return FPU_Exception;
  95. FPU_to_exp16(a, &x);
  96. FPU_to_exp16(b, &y);
  97. a = &x;
  98. b = &y;
  99. expa = exponent16(a);
  100. expb = exponent16(b);
  101. goto valid_add;
  102. }
  103. if ((taga == TW_NaN) || (tagb == TW_NaN)) {
  104. if (deststnr == 0)
  105. return real_2op_NaN(b, tagb, deststnr, a);
  106. else
  107. return real_2op_NaN(a, taga, deststnr, a);
  108. }
  109. return add_sub_specials(a, taga, signa, b, tagb, signb,
  110. dest, deststnr, control_w);
  111. }
  112. /* Subtract b from a. (a-b) -> dest */
  113. int FPU_sub(int flags, int rm, int control_w)
  114. {
  115. FPU_REG const *a, *b;
  116. FPU_REG *dest;
  117. u_char taga, tagb, signa, signb, saved_sign, sign;
  118. int diff, tag = 0, expa, expb, deststnr;
  119. a = &st(0);
  120. taga = FPU_gettag0();
  121. deststnr = 0;
  122. if (flags & LOADED) {
  123. b = (FPU_REG *) rm;
  124. tagb = flags & 0x0f;
  125. } else {
  126. b = &st(rm);
  127. tagb = FPU_gettagi(rm);
  128. if (flags & DEST_RM)
  129. deststnr = rm;
  130. }
  131. signa = getsign(a);
  132. signb = getsign(b);
  133. if (flags & REV) {
  134. signa ^= SIGN_NEG;
  135. signb ^= SIGN_NEG;
  136. }
  137. dest = &st(deststnr);
  138. saved_sign = getsign(dest);
  139. if (!(taga | tagb)) {
  140. expa = exponent(a);
  141. expb = exponent(b);
  142. valid_subtract:
  143. /* Both registers are valid */
  144. diff = expa - expb;
  145. if (!diff) {
  146. diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
  147. if (!diff) {
  148. diff = a->sigl > b->sigl;
  149. if (!diff)
  150. diff = -(a->sigl < b->sigl);
  151. }
  152. }
  153. switch ((((int)signa) * 2 + signb) / SIGN_NEG) {
  154. case 0: /* P - P */
  155. case 3: /* N - N */
  156. if (diff > 0) {
  157. /* |a| > |b| */
  158. tag =
  159. FPU_u_sub(a, b, dest, control_w, signa,
  160. expa, expb);
  161. } else if (diff == 0) {
  162. FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
  163. /* sign depends upon rounding mode */
  164. setsign(dest, ((control_w & CW_RC) != RC_DOWN)
  165. ? SIGN_POS : SIGN_NEG);
  166. return TAG_Zero;
  167. } else {
  168. sign = signa ^ SIGN_NEG;
  169. tag =
  170. FPU_u_sub(b, a, dest, control_w, sign, expb,
  171. expa);
  172. }
  173. break;
  174. case 1: /* P - N */
  175. tag =
  176. FPU_u_add(a, b, dest, control_w, SIGN_POS, expa,
  177. expb);
  178. break;
  179. case 2: /* N - P */
  180. tag =
  181. FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa,
  182. expb);
  183. break;
  184. #ifdef PARANOID
  185. default:
  186. EXCEPTION(EX_INTERNAL | 0x111);
  187. return -1;
  188. #endif
  189. }
  190. if (tag < 0) {
  191. setsign(dest, saved_sign);
  192. return tag;
  193. }
  194. FPU_settagi(deststnr, tag);
  195. return tag;
  196. }
  197. if (taga == TAG_Special)
  198. taga = FPU_Special(a);
  199. if (tagb == TAG_Special)
  200. tagb = FPU_Special(b);
  201. if (((taga == TAG_Valid) && (tagb == TW_Denormal))
  202. || ((taga == TW_Denormal) && (tagb == TAG_Valid))
  203. || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
  204. FPU_REG x, y;
  205. if (denormal_operand() < 0)
  206. return FPU_Exception;
  207. FPU_to_exp16(a, &x);
  208. FPU_to_exp16(b, &y);
  209. a = &x;
  210. b = &y;
  211. expa = exponent16(a);
  212. expb = exponent16(b);
  213. goto valid_subtract;
  214. }
  215. if ((taga == TW_NaN) || (tagb == TW_NaN)) {
  216. FPU_REG const *d1, *d2;
  217. if (flags & REV) {
  218. d1 = b;
  219. d2 = a;
  220. } else {
  221. d1 = a;
  222. d2 = b;
  223. }
  224. if (flags & LOADED)
  225. return real_2op_NaN(b, tagb, deststnr, d1);
  226. if (flags & DEST_RM)
  227. return real_2op_NaN(a, taga, deststnr, d2);
  228. else
  229. return real_2op_NaN(b, tagb, deststnr, d2);
  230. }
  231. return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
  232. dest, deststnr, control_w);
  233. }
  234. static
  235. int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
  236. FPU_REG const *b, u_char tagb, u_char signb,
  237. FPU_REG * dest, int deststnr, int control_w)
  238. {
  239. if (((taga == TW_Denormal) || (tagb == TW_Denormal))
  240. && (denormal_operand() < 0))
  241. return FPU_Exception;
  242. if (taga == TAG_Zero) {
  243. if (tagb == TAG_Zero) {
  244. /* Both are zero, result will be zero. */
  245. u_char different_signs = signa ^ signb;
  246. FPU_copy_to_regi(a, TAG_Zero, deststnr);
  247. if (different_signs) {
  248. /* Signs are different. */
  249. /* Sign of answer depends upon rounding mode. */
  250. setsign(dest, ((control_w & CW_RC) != RC_DOWN)
  251. ? SIGN_POS : SIGN_NEG);
  252. } else
  253. setsign(dest, signa); /* signa may differ from the sign of a. */
  254. return TAG_Zero;
  255. } else {
  256. reg_copy(b, dest);
  257. if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
  258. /* A pseudoDenormal, convert it. */
  259. addexponent(dest, 1);
  260. tagb = TAG_Valid;
  261. } else if (tagb > TAG_Empty)
  262. tagb = TAG_Special;
  263. setsign(dest, signb); /* signb may differ from the sign of b. */
  264. FPU_settagi(deststnr, tagb);
  265. return tagb;
  266. }
  267. } else if (tagb == TAG_Zero) {
  268. reg_copy(a, dest);
  269. if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) {
  270. /* A pseudoDenormal */
  271. addexponent(dest, 1);
  272. taga = TAG_Valid;
  273. } else if (taga > TAG_Empty)
  274. taga = TAG_Special;
  275. setsign(dest, signa); /* signa may differ from the sign of a. */
  276. FPU_settagi(deststnr, taga);
  277. return taga;
  278. } else if (taga == TW_Infinity) {
  279. if ((tagb != TW_Infinity) || (signa == signb)) {
  280. FPU_copy_to_regi(a, TAG_Special, deststnr);
  281. setsign(dest, signa); /* signa may differ from the sign of a. */
  282. return taga;
  283. }
  284. /* Infinity-Infinity is undefined. */
  285. return arith_invalid(deststnr);
  286. } else if (tagb == TW_Infinity) {
  287. FPU_copy_to_regi(b, TAG_Special, deststnr);
  288. setsign(dest, signb); /* signb may differ from the sign of b. */
  289. return tagb;
  290. }
  291. #ifdef PARANOID
  292. EXCEPTION(EX_INTERNAL | 0x101);
  293. #endif
  294. return FPU_Exception;
  295. }