reg_add_sub.c 8.7 KB

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