reg_u_div.S 12 KB

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