reg_add_sub.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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. {
  43. expa = exponent(a);
  44. expb = exponent(b);
  45. valid_add:
  46. /* Both registers are valid */
  47. if (!(signa ^ signb))
  48. {
  49. /* signs are the same */
  50. tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb);
  51. }
  52. else
  53. {
  54. /* The signs are different, so do a subtraction */
  55. diff = expa - expb;
  56. if (!diff)
  57. {
  58. diff = a->sigh - b->sigh; /* This works only if the ms bits
  59. are identical. */
  60. if (!diff)
  61. {
  62. diff = a->sigl > b->sigl;
  63. if (!diff)
  64. diff = -(a->sigl < b->sigl);
  65. }
  66. }
  67. if (diff > 0)
  68. {
  69. tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
  70. }
  71. else if ( diff < 0 )
  72. {
  73. tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa);
  74. }
  75. else
  76. {
  77. FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
  78. /* sign depends upon rounding mode */
  79. setsign(dest, ((control_w & CW_RC) != RC_DOWN)
  80. ? SIGN_POS : SIGN_NEG);
  81. return TAG_Zero;
  82. }
  83. }
  84. if ( tag < 0 )
  85. {
  86. setsign(dest, saved_sign);
  87. return tag;
  88. }
  89. FPU_settagi(deststnr, tag);
  90. return tag;
  91. }
  92. if ( taga == TAG_Special )
  93. taga = FPU_Special(a);
  94. if ( tagb == TAG_Special )
  95. tagb = FPU_Special(b);
  96. if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
  97. || ((taga == TW_Denormal) && (tagb == TAG_Valid))
  98. || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
  99. {
  100. FPU_REG x, y;
  101. if ( denormal_operand() < 0 )
  102. return FPU_Exception;
  103. FPU_to_exp16(a, &x);
  104. FPU_to_exp16(b, &y);
  105. a = &x;
  106. b = &y;
  107. expa = exponent16(a);
  108. expb = exponent16(b);
  109. goto valid_add;
  110. }
  111. if ( (taga == TW_NaN) || (tagb == TW_NaN) )
  112. {
  113. if ( deststnr == 0 )
  114. return real_2op_NaN(b, tagb, deststnr, a);
  115. else
  116. return real_2op_NaN(a, taga, deststnr, a);
  117. }
  118. return add_sub_specials(a, taga, signa, b, tagb, signb,
  119. dest, deststnr, control_w);
  120. }
  121. /* Subtract b from a. (a-b) -> dest */
  122. int FPU_sub(int flags, int rm, int control_w)
  123. {
  124. FPU_REG const *a, *b;
  125. FPU_REG *dest;
  126. u_char taga, tagb, signa, signb, saved_sign, sign;
  127. int diff, tag = 0, expa, expb, deststnr;
  128. a = &st(0);
  129. taga = FPU_gettag0();
  130. deststnr = 0;
  131. if ( flags & LOADED )
  132. {
  133. b = (FPU_REG *)rm;
  134. tagb = flags & 0x0f;
  135. }
  136. else
  137. {
  138. b = &st(rm);
  139. tagb = FPU_gettagi(rm);
  140. if ( flags & DEST_RM )
  141. deststnr = rm;
  142. }
  143. signa = getsign(a);
  144. signb = getsign(b);
  145. if ( flags & REV )
  146. {
  147. signa ^= SIGN_NEG;
  148. signb ^= SIGN_NEG;
  149. }
  150. dest = &st(deststnr);
  151. saved_sign = getsign(dest);
  152. if ( !(taga | tagb) )
  153. {
  154. expa = exponent(a);
  155. expb = exponent(b);
  156. valid_subtract:
  157. /* Both registers are valid */
  158. diff = expa - expb;
  159. if (!diff)
  160. {
  161. diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
  162. if (!diff)
  163. {
  164. diff = a->sigl > b->sigl;
  165. if (!diff)
  166. diff = -(a->sigl < b->sigl);
  167. }
  168. }
  169. switch ( (((int)signa)*2 + signb) / SIGN_NEG )
  170. {
  171. case 0: /* P - P */
  172. case 3: /* N - N */
  173. if (diff > 0)
  174. {
  175. /* |a| > |b| */
  176. tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
  177. }
  178. else if ( diff == 0 )
  179. {
  180. FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
  181. /* sign depends upon rounding mode */
  182. setsign(dest, ((control_w & CW_RC) != RC_DOWN)
  183. ? SIGN_POS : SIGN_NEG);
  184. return TAG_Zero;
  185. }
  186. else
  187. {
  188. sign = signa ^ SIGN_NEG;
  189. tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa);
  190. }
  191. break;
  192. case 1: /* P - N */
  193. tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb);
  194. break;
  195. case 2: /* N - P */
  196. tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb);
  197. break;
  198. #ifdef PARANOID
  199. default:
  200. EXCEPTION(EX_INTERNAL|0x111);
  201. return -1;
  202. #endif
  203. }
  204. if ( tag < 0 )
  205. {
  206. setsign(dest, saved_sign);
  207. return tag;
  208. }
  209. FPU_settagi(deststnr, tag);
  210. return tag;
  211. }
  212. if ( taga == TAG_Special )
  213. taga = FPU_Special(a);
  214. if ( tagb == TAG_Special )
  215. tagb = FPU_Special(b);
  216. if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
  217. || ((taga == TW_Denormal) && (tagb == TAG_Valid))
  218. || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
  219. {
  220. FPU_REG x, y;
  221. if ( denormal_operand() < 0 )
  222. return FPU_Exception;
  223. FPU_to_exp16(a, &x);
  224. FPU_to_exp16(b, &y);
  225. a = &x;
  226. b = &y;
  227. expa = exponent16(a);
  228. expb = exponent16(b);
  229. goto valid_subtract;
  230. }
  231. if ( (taga == TW_NaN) || (tagb == TW_NaN) )
  232. {
  233. FPU_REG const *d1, *d2;
  234. if ( flags & REV )
  235. {
  236. d1 = b;
  237. d2 = a;
  238. }
  239. else
  240. {
  241. d1 = a;
  242. d2 = b;
  243. }
  244. if ( flags & LOADED )
  245. return real_2op_NaN(b, tagb, deststnr, d1);
  246. if ( flags & DEST_RM )
  247. return real_2op_NaN(a, taga, deststnr, d2);
  248. else
  249. return real_2op_NaN(b, tagb, deststnr, d2);
  250. }
  251. return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
  252. dest, deststnr, control_w);
  253. }
  254. static
  255. int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
  256. FPU_REG const *b, u_char tagb, u_char signb,
  257. FPU_REG *dest, int deststnr, int control_w)
  258. {
  259. if ( ((taga == TW_Denormal) || (tagb == TW_Denormal))
  260. && (denormal_operand() < 0) )
  261. return FPU_Exception;
  262. if (taga == TAG_Zero)
  263. {
  264. if (tagb == TAG_Zero)
  265. {
  266. /* Both are zero, result will be zero. */
  267. u_char different_signs = signa ^ signb;
  268. FPU_copy_to_regi(a, TAG_Zero, deststnr);
  269. if ( different_signs )
  270. {
  271. /* Signs are different. */
  272. /* Sign of answer depends upon rounding mode. */
  273. setsign(dest, ((control_w & CW_RC) != RC_DOWN)
  274. ? SIGN_POS : SIGN_NEG);
  275. }
  276. else
  277. setsign(dest, signa); /* signa may differ from the sign of a. */
  278. return TAG_Zero;
  279. }
  280. else
  281. {
  282. reg_copy(b, dest);
  283. if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) )
  284. {
  285. /* A pseudoDenormal, convert it. */
  286. addexponent(dest, 1);
  287. tagb = TAG_Valid;
  288. }
  289. else if ( tagb > TAG_Empty )
  290. tagb = TAG_Special;
  291. setsign(dest, signb); /* signb may differ from the sign of b. */
  292. FPU_settagi(deststnr, tagb);
  293. return tagb;
  294. }
  295. }
  296. else if (tagb == TAG_Zero)
  297. {
  298. reg_copy(a, dest);
  299. if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) )
  300. {
  301. /* A pseudoDenormal */
  302. addexponent(dest, 1);
  303. taga = TAG_Valid;
  304. }
  305. else if ( taga > TAG_Empty )
  306. taga = TAG_Special;
  307. setsign(dest, signa); /* signa may differ from the sign of a. */
  308. FPU_settagi(deststnr, taga);
  309. return taga;
  310. }
  311. else if (taga == TW_Infinity)
  312. {
  313. if ( (tagb != TW_Infinity) || (signa == signb) )
  314. {
  315. FPU_copy_to_regi(a, TAG_Special, deststnr);
  316. setsign(dest, signa); /* signa may differ from the sign of a. */
  317. return taga;
  318. }
  319. /* Infinity-Infinity is undefined. */
  320. return arith_invalid(deststnr);
  321. }
  322. else if (tagb == TW_Infinity)
  323. {
  324. FPU_copy_to_regi(b, TAG_Special, deststnr);
  325. setsign(dest, signb); /* signb may differ from the sign of b. */
  326. return tagb;
  327. }
  328. #ifdef PARANOID
  329. EXCEPTION(EX_INTERNAL|0x101);
  330. #endif
  331. return FPU_Exception;
  332. }