reg_u_div.S 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. .file "reg_u_div.S"
  2. /*---------------------------------------------------------------------------+
  3. | reg_u_div.S |
  4. | |
  5. | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
  6. | |
  7. | Copyright (C) 1992,1993,1995,1997 |
  8. | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
  9. | E-mail billm@suburbia.net |
  10. | |
  11. | |
  12. +---------------------------------------------------------------------------*/
  13. /*---------------------------------------------------------------------------+
  14. | Call from C as: |
  15. | int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest, |
  16. | unsigned int control_word, char *sign) |
  17. | |
  18. | Does not compute the destination exponent, but does adjust it. |
  19. | |
  20. | Return value is the tag of the answer, or-ed with FPU_Exception if |
  21. | one was raised, or -1 on internal error. |
  22. +---------------------------------------------------------------------------*/
  23. #include "exception.h"
  24. #include "fpu_emu.h"
  25. #include "control_w.h"
  26. /* #define dSIGL(x) (x) */
  27. /* #define dSIGH(x) 4(x) */
  28. #ifndef NON_REENTRANT_FPU
  29. /*
  30. Local storage on the stack:
  31. Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
  32. Overflow flag: ovfl_flag
  33. */
  34. #define FPU_accum_3 -4(%ebp)
  35. #define FPU_accum_2 -8(%ebp)
  36. #define FPU_accum_1 -12(%ebp)
  37. #define FPU_accum_0 -16(%ebp)
  38. #define FPU_result_1 -20(%ebp)
  39. #define FPU_result_2 -24(%ebp)
  40. #define FPU_ovfl_flag -28(%ebp)
  41. #else
  42. .data
  43. /*
  44. Local storage in a static area:
  45. Result: FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
  46. Overflow flag: ovfl_flag
  47. */
  48. .align 4,0
  49. FPU_accum_3:
  50. .long 0
  51. FPU_accum_2:
  52. .long 0
  53. FPU_accum_1:
  54. .long 0
  55. FPU_accum_0:
  56. .long 0
  57. FPU_result_1:
  58. .long 0
  59. FPU_result_2:
  60. .long 0
  61. FPU_ovfl_flag:
  62. .byte 0
  63. #endif /* NON_REENTRANT_FPU */
  64. #define REGA PARAM1
  65. #define REGB PARAM2
  66. #define DEST PARAM3
  67. .text
  68. ENTRY(FPU_u_div)
  69. pushl %ebp
  70. movl %esp,%ebp
  71. #ifndef NON_REENTRANT_FPU
  72. subl $28,%esp
  73. #endif /* NON_REENTRANT_FPU */
  74. pushl %esi
  75. pushl %edi
  76. pushl %ebx
  77. movl REGA,%esi
  78. movl REGB,%ebx
  79. movl DEST,%edi
  80. movswl EXP(%esi),%edx
  81. movswl EXP(%ebx),%eax
  82. subl %eax,%edx
  83. addl EXP_BIAS,%edx
  84. /* A denormal and a large number can cause an exponent underflow */
  85. cmpl EXP_WAY_UNDER,%edx
  86. jg xExp_not_underflow
  87. /* Set to a really low value allow correct handling */
  88. movl EXP_WAY_UNDER,%edx
  89. xExp_not_underflow:
  90. movw %dx,EXP(%edi)
  91. #ifdef PARANOID
  92. /* testl $0x80000000, SIGH(%esi) // Dividend */
  93. /* je L_bugged */
  94. testl $0x80000000, SIGH(%ebx) /* Divisor */
  95. je L_bugged
  96. #endif /* PARANOID */
  97. /* Check if the divisor can be treated as having just 32 bits */
  98. cmpl $0,SIGL(%ebx)
  99. jnz L_Full_Division /* Can't do a quick divide */
  100. /* We should be able to zip through the division here */
  101. movl SIGH(%ebx),%ecx /* The divisor */
  102. movl SIGH(%esi),%edx /* Dividend */
  103. movl SIGL(%esi),%eax /* Dividend */
  104. cmpl %ecx,%edx
  105. setaeb FPU_ovfl_flag /* Keep a record */
  106. jb L_no_adjust
  107. subl %ecx,%edx /* Prevent the overflow */
  108. L_no_adjust:
  109. /* Divide the 64 bit number by the 32 bit denominator */
  110. divl %ecx
  111. movl %eax,FPU_result_2
  112. /* Work on the remainder of the first division */
  113. xorl %eax,%eax
  114. divl %ecx
  115. movl %eax,FPU_result_1
  116. /* Work on the remainder of the 64 bit division */
  117. xorl %eax,%eax
  118. divl %ecx
  119. testb $255,FPU_ovfl_flag /* was the num > denom ? */
  120. je L_no_overflow
  121. /* Do the shifting here */
  122. /* increase the exponent */
  123. incw EXP(%edi)
  124. /* shift the mantissa right one bit */
  125. stc /* To set the ms bit */
  126. rcrl FPU_result_2
  127. rcrl FPU_result_1
  128. rcrl %eax
  129. L_no_overflow:
  130. jmp LRound_precision /* Do the rounding as required */
  131. /*---------------------------------------------------------------------------+
  132. | Divide: Return arg1/arg2 to arg3. |
  133. | |
  134. | This routine does not use the exponents of arg1 and arg2, but does |
  135. | adjust the exponent of arg3. |
  136. | |
  137. | The maximum returned value is (ignoring exponents) |
  138. | .ffffffff ffffffff |
  139. | ------------------ = 1.ffffffff fffffffe |
  140. | .80000000 00000000 |
  141. | and the minimum is |
  142. | .80000000 00000000 |
  143. | ------------------ = .80000000 00000001 (rounded) |
  144. | .ffffffff ffffffff |
  145. | |
  146. +---------------------------------------------------------------------------*/
  147. L_Full_Division:
  148. /* Save extended dividend in local register */
  149. movl SIGL(%esi),%eax
  150. movl %eax,FPU_accum_2
  151. movl SIGH(%esi),%eax
  152. movl %eax,FPU_accum_3
  153. xorl %eax,%eax
  154. movl %eax,FPU_accum_1 /* zero the extension */
  155. movl %eax,FPU_accum_0 /* zero the extension */
  156. movl SIGL(%esi),%eax /* Get the current num */
  157. movl SIGH(%esi),%edx
  158. /*----------------------------------------------------------------------*/
  159. /* Initialization done.
  160. Do the first 32 bits. */
  161. movb $0,FPU_ovfl_flag
  162. cmpl SIGH(%ebx),%edx /* Test for imminent overflow */
  163. jb LLess_than_1
  164. ja LGreater_than_1
  165. cmpl SIGL(%ebx),%eax
  166. jb LLess_than_1
  167. LGreater_than_1:
  168. /* The dividend is greater or equal, would cause overflow */
  169. setaeb FPU_ovfl_flag /* Keep a record */
  170. subl SIGL(%ebx),%eax
  171. sbbl SIGH(%ebx),%edx /* Prevent the overflow */
  172. movl %eax,FPU_accum_2
  173. movl %edx,FPU_accum_3
  174. LLess_than_1:
  175. /* At this point, we have a dividend < divisor, with a record of
  176. adjustment in FPU_ovfl_flag */
  177. /* We will divide by a number which is too large */
  178. movl SIGH(%ebx),%ecx
  179. addl $1,%ecx
  180. jnc LFirst_div_not_1
  181. /* here we need to divide by 100000000h,
  182. i.e., no division at all.. */
  183. mov %edx,%eax
  184. jmp LFirst_div_done
  185. LFirst_div_not_1:
  186. divl %ecx /* Divide the numerator by the augmented
  187. denom ms dw */
  188. LFirst_div_done:
  189. movl %eax,FPU_result_2 /* Put the result in the answer */
  190. mull SIGH(%ebx) /* mul by the ms dw of the denom */
  191. subl %eax,FPU_accum_2 /* Subtract from the num local reg */
  192. sbbl %edx,FPU_accum_3
  193. movl FPU_result_2,%eax /* Get the result back */
  194. mull SIGL(%ebx) /* now mul the ls dw of the denom */
  195. subl %eax,FPU_accum_1 /* Subtract from the num local reg */
  196. sbbl %edx,FPU_accum_2
  197. sbbl $0,FPU_accum_3
  198. je LDo_2nd_32_bits /* Must check for non-zero result here */
  199. #ifdef PARANOID
  200. jb L_bugged_1
  201. #endif /* PARANOID */
  202. /* need to subtract another once of the denom */
  203. incl FPU_result_2 /* Correct the answer */
  204. movl SIGL(%ebx),%eax
  205. movl SIGH(%ebx),%edx
  206. subl %eax,FPU_accum_1 /* Subtract from the num local reg */
  207. sbbl %edx,FPU_accum_2
  208. #ifdef PARANOID
  209. sbbl $0,FPU_accum_3
  210. jne L_bugged_1 /* Must check for non-zero result here */
  211. #endif /* PARANOID */
  212. /*----------------------------------------------------------------------*/
  213. /* Half of the main problem is done, there is just a reduced numerator
  214. to handle now.
  215. Work with the second 32 bits, FPU_accum_0 not used from now on */
  216. LDo_2nd_32_bits:
  217. movl FPU_accum_2,%edx /* get the reduced num */
  218. movl FPU_accum_1,%eax
  219. /* need to check for possible subsequent overflow */
  220. cmpl SIGH(%ebx),%edx
  221. jb LDo_2nd_div
  222. ja LPrevent_2nd_overflow
  223. cmpl SIGL(%ebx),%eax
  224. jb LDo_2nd_div
  225. LPrevent_2nd_overflow:
  226. /* The numerator is greater or equal, would cause overflow */
  227. /* prevent overflow */
  228. subl SIGL(%ebx),%eax
  229. sbbl SIGH(%ebx),%edx
  230. movl %edx,FPU_accum_2
  231. movl %eax,FPU_accum_1
  232. incl FPU_result_2 /* Reflect the subtraction in the answer */
  233. #ifdef PARANOID
  234. je L_bugged_2 /* Can't bump the result to 1.0 */
  235. #endif /* PARANOID */
  236. LDo_2nd_div:
  237. cmpl $0,%ecx /* augmented denom msw */
  238. jnz LSecond_div_not_1
  239. /* %ecx == 0, we are dividing by 1.0 */
  240. mov %edx,%eax
  241. jmp LSecond_div_done
  242. LSecond_div_not_1:
  243. divl %ecx /* Divide the numerator by the denom ms dw */
  244. LSecond_div_done:
  245. movl %eax,FPU_result_1 /* Put the result in the answer */
  246. mull SIGH(%ebx) /* mul by the ms dw of the denom */
  247. subl %eax,FPU_accum_1 /* Subtract from the num local reg */
  248. sbbl %edx,FPU_accum_2
  249. #ifdef PARANOID
  250. jc L_bugged_2
  251. #endif /* PARANOID */
  252. movl FPU_result_1,%eax /* Get the result back */
  253. mull SIGL(%ebx) /* now mul the ls dw of the denom */
  254. subl %eax,FPU_accum_0 /* Subtract from the num local reg */
  255. sbbl %edx,FPU_accum_1 /* Subtract from the num local reg */
  256. sbbl $0,FPU_accum_2
  257. #ifdef PARANOID
  258. jc L_bugged_2
  259. #endif /* PARANOID */
  260. jz LDo_3rd_32_bits
  261. #ifdef PARANOID
  262. cmpl $1,FPU_accum_2
  263. jne L_bugged_2
  264. #endif /* PARANOID */
  265. /* need to subtract another once of the denom */
  266. movl SIGL(%ebx),%eax
  267. movl SIGH(%ebx),%edx
  268. subl %eax,FPU_accum_0 /* Subtract from the num local reg */
  269. sbbl %edx,FPU_accum_1
  270. sbbl $0,FPU_accum_2
  271. #ifdef PARANOID
  272. jc L_bugged_2
  273. jne L_bugged_2
  274. #endif /* PARANOID */
  275. addl $1,FPU_result_1 /* Correct the answer */
  276. adcl $0,FPU_result_2
  277. #ifdef PARANOID
  278. jc L_bugged_2 /* Must check for non-zero result here */
  279. #endif /* PARANOID */
  280. /*----------------------------------------------------------------------*/
  281. /* The division is essentially finished here, we just need to perform
  282. tidying operations.
  283. Deal with the 3rd 32 bits */
  284. LDo_3rd_32_bits:
  285. movl FPU_accum_1,%edx /* get the reduced num */
  286. movl FPU_accum_0,%eax
  287. /* need to check for possible subsequent overflow */
  288. cmpl SIGH(%ebx),%edx /* denom */
  289. jb LRound_prep
  290. ja LPrevent_3rd_overflow
  291. cmpl SIGL(%ebx),%eax /* denom */
  292. jb LRound_prep
  293. LPrevent_3rd_overflow:
  294. /* prevent overflow */
  295. subl SIGL(%ebx),%eax
  296. sbbl SIGH(%ebx),%edx
  297. movl %edx,FPU_accum_1
  298. movl %eax,FPU_accum_0
  299. addl $1,FPU_result_1 /* Reflect the subtraction in the answer */
  300. adcl $0,FPU_result_2
  301. jne LRound_prep
  302. jnc LRound_prep
  303. /* This is a tricky spot, there is an overflow of the answer */
  304. movb $255,FPU_ovfl_flag /* Overflow -> 1.000 */
  305. LRound_prep:
  306. /*
  307. * Prepare for rounding.
  308. * To test for rounding, we just need to compare 2*accum with the
  309. * denom.
  310. */
  311. movl FPU_accum_0,%ecx
  312. movl FPU_accum_1,%edx
  313. movl %ecx,%eax
  314. orl %edx,%eax
  315. jz LRound_ovfl /* The accumulator contains zero. */
  316. /* Multiply by 2 */
  317. clc
  318. rcll $1,%ecx
  319. rcll $1,%edx
  320. jc LRound_large /* No need to compare, denom smaller */
  321. subl SIGL(%ebx),%ecx
  322. sbbl SIGH(%ebx),%edx
  323. jnc LRound_not_small
  324. movl $0x70000000,%eax /* Denom was larger */
  325. jmp LRound_ovfl
  326. LRound_not_small:
  327. jnz LRound_large
  328. movl $0x80000000,%eax /* Remainder was exactly 1/2 denom */
  329. jmp LRound_ovfl
  330. LRound_large:
  331. movl $0xff000000,%eax /* Denom was smaller */
  332. LRound_ovfl:
  333. /* We are now ready to deal with rounding, but first we must get
  334. the bits properly aligned */
  335. testb $255,FPU_ovfl_flag /* was the num > denom ? */
  336. je LRound_precision
  337. incw EXP(%edi)
  338. /* shift the mantissa right one bit */
  339. stc /* Will set the ms bit */
  340. rcrl FPU_result_2
  341. rcrl FPU_result_1
  342. rcrl %eax
  343. /* Round the result as required */
  344. LRound_precision:
  345. decw EXP(%edi) /* binary point between 1st & 2nd bits */
  346. movl %eax,%edx
  347. movl FPU_result_1,%ebx
  348. movl FPU_result_2,%eax
  349. jmp fpu_reg_round
  350. #ifdef PARANOID
  351. /* The logic is wrong if we got here */
  352. L_bugged:
  353. pushl EX_INTERNAL|0x202
  354. call EXCEPTION
  355. pop %ebx
  356. jmp L_exit
  357. L_bugged_1:
  358. pushl EX_INTERNAL|0x203
  359. call EXCEPTION
  360. pop %ebx
  361. jmp L_exit
  362. L_bugged_2:
  363. pushl EX_INTERNAL|0x204
  364. call EXCEPTION
  365. pop %ebx
  366. jmp L_exit
  367. L_exit:
  368. movl $-1,%eax
  369. popl %ebx
  370. popl %edi
  371. popl %esi
  372. leave
  373. ret
  374. #endif /* PARANOID */