Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

473 lines
15 KiB

  1. subttl emfdiv.asm - Division
  2. page
  3. ;*******************************************************************************
  4. ; Copyright (c) Microsoft Corporation 1991
  5. ; All Rights Reserved
  6. ;
  7. ;emfdiv.asm - long double divide
  8. ; by Tim Paterson
  9. ;
  10. ;Purpose:
  11. ; Long double division.
  12. ;Inputs:
  13. ; ebx:esi = op1 mantissa
  14. ; ecx = op1 sign in bit 15, exponent in high half
  15. ; edi = pointer to op2 and result location
  16. ; [Result] = edi
  17. ;
  18. ; Exponents are unbiased. Denormals have been normalized using
  19. ; this expanded exponent range. Neither operand is allowed to be zero.
  20. ;Outputs:
  21. ; Jumps to [RoundMode] to round and store result.
  22. ;
  23. ;Revision History:
  24. ;
  25. ; [] 09/05/91 TP Initial 32-bit version.
  26. ;
  27. ;*******************************************************************************
  28. ;Dispatch tables for division
  29. ;
  30. ;One operand has been loaded into ecx:ebx:esi ("source"), the other is
  31. ;pointed to by edi ("dest"). edi points to dividend for fdiv,
  32. ;to divisor for fdivr.
  33. ;
  34. ;Tag of source is shifted. Tag values are as follows:
  35. ;
  36. .erre TAG_SNGL eq 0 ;SINGLE: low 32 bits are zero
  37. .erre TAG_VALID eq 1
  38. .erre TAG_ZERO eq 2
  39. .erre TAG_SPCL eq 3 ;NAN, Infinity, Denormal, Empty
  40. ;dest = dest / source
  41. tFdivDisp label dword ;Source (reg) Dest (*[di])
  42. dd DivSingle ;single single
  43. dd DivSingle ;single double
  44. dd XorDestSign ;single zero
  45. dd DivSpclDest ;single special
  46. dd DivDouble ;double single
  47. dd DivDouble ;double double
  48. dd XorDestSign ;double zero
  49. dd DivSpclDest ;double special
  50. dd DivideByZero ;zero single
  51. dd DivideByZero ;zero double
  52. dd ReturnIndefinite ;zero zero
  53. dd DivSpclDest ;zero special
  54. dd DivSpclSource ;special single
  55. dd DivSpclSource ;special double
  56. dd DivSpclSource ;special zero
  57. dd TwoOpBothSpcl ;special special
  58. dd ReturnIndefinite ;Two infinities
  59. ;dest = source / dest
  60. tFdivrDisp label dword ;Source (reg) Dest (*[di])
  61. dd DivrSingle ;single single
  62. dd DivrDouble ;single double
  63. dd DivideByZero ;single zero
  64. dd DivrSpclDest ;single special
  65. dd DivrSingle ;double single
  66. dd DivrDouble ;double double
  67. dd DivideByZero ;double zero
  68. dd DivrSpclDest ;double special
  69. dd XorSourceSign ;zero single
  70. dd XorSourceSign ;zero double
  71. dd ReturnIndefinite ;zero zero
  72. dd DivrSpclDest ;zero special
  73. dd DivrSpclSource ;special single
  74. dd DivrSpclSource ;special double
  75. dd DivrSpclSource ;special zero
  76. dd TwoOpBothSpcl ;special special
  77. dd ReturnIndefinite ;Two infinities
  78. EM_ENTRY eFIDIV16
  79. eFIDIV16:
  80. push offset DivSetResult
  81. jmp Load16Int ;Returns to DivSetResult
  82. EM_ENTRY eFIDIVR16
  83. eFIDIVR16:
  84. push offset DivrSetResult
  85. jmp Load16Int
  86. EM_ENTRY eFIDIV32
  87. eFIDIV32:
  88. push offset DivSetResult
  89. jmp Load32Int
  90. EM_ENTRY eFIDIVR32
  91. eFIDIVR32:
  92. push offset DivrSetResult
  93. jmp Load32Int
  94. EM_ENTRY eFDIV32
  95. eFDIV32:
  96. push offset DivSetResult
  97. jmp Load32Real ;Returns to DivSetResult
  98. EM_ENTRY eFDIVR32
  99. eFDIVR32:
  100. push offset DivrSetResult ;Returns to DivrSetResult
  101. jmp Load32Real
  102. EM_ENTRY eFDIV64
  103. eFDIV64:
  104. push offset DivSetResult
  105. jmp Load64Real ;Returns to DivSetResult
  106. EM_ENTRY eFDIVR64
  107. eFDIVR64:
  108. push offset DivrSetResult
  109. jmp Load64Real ;Returns to DivrSetResult
  110. EM_ENTRY eFDIVRPreg
  111. eFDIVRPreg:
  112. push offset PopWhenDone
  113. EM_ENTRY eFDIVRreg
  114. eFDIVRreg:
  115. xchg esi,edi
  116. EM_ENTRY eFDIVRtop
  117. eFDIVRtop:
  118. mov ecx,EMSEG:[esi].ExpSgn
  119. mov ebx,EMSEG:[esi].lManHi
  120. mov esi,EMSEG:[esi].lManLo
  121. DivrSetResult:
  122. ;cl has tag of dividend
  123. mov ebp,offset tFdivrDisp
  124. mov EMSEG:[Result],edi ;Save result pointer
  125. mov ah,cl
  126. mov al,EMSEG:[edi].bTag
  127. and ah,not 1 ;Ignore single vs. double on dividend
  128. cmp ax,1
  129. .erre bTAG_VALID eq 1
  130. .erre bTAG_SNGL eq 0
  131. jz DivrDouble ;Divisor was double
  132. ja TwoOpResultSet
  133. ;.erre DivrSingle eq $ ;Fall into DivrSingle
  134. ;*********
  135. DivrSingle:
  136. ;*********
  137. ;Computes op1/op2
  138. ;Op1 is double, op2 is single (low 32 bits are zero)
  139. mov edx,ebx
  140. mov eax,esi ;Mantissa in edx:eax
  141. mov ebx,EMSEG:[edi].ExpSgn
  142. mov edi,EMSEG:[edi].lManHi
  143. jmp DivSingleReg
  144. SDivBigUnderflow:
  145. ;Overflow flag set could only occur with denormals (true exp < -32768)
  146. or EMSEG:[CURerr],Underflow
  147. test EMSEG:[CWmask],Underflow ;Is exception masked?
  148. jnz UnderflowZero ;Yes, return zero (in emfmul.asm)
  149. add ecx,Underbias shl 16 ;Fix up exponent
  150. jmp ContSdiv ;Continue with multiply
  151. EM_ENTRY eFDIVPreg
  152. eFDIVPreg:
  153. push offset PopWhenDone
  154. EM_ENTRY eFDIVreg
  155. eFDIVreg:
  156. xchg esi,edi
  157. EM_ENTRY eFDIVtop
  158. eFDIVtop:
  159. mov ecx,EMSEG:[esi].ExpSgn
  160. mov ebx,EMSEG:[esi].lManHi
  161. mov esi,EMSEG:[esi].lManLo
  162. DivSetResult:
  163. ;cl has tag of divisor
  164. mov ebp,offset tFdivDisp
  165. mov EMSEG:[Result],edi ;Save result pointer
  166. mov al,cl
  167. mov ah,EMSEG:[edi].bTag
  168. and ah,not 1 ;Ignore single vs. double on dividend
  169. cmp ax,1
  170. .erre bTAG_VALID eq 1
  171. .erre bTAG_SNGL eq 0
  172. jz DivDouble ;Divisor was double
  173. ja TwoOpResultSet
  174. ;.erre DivSingle eq $ ;Fall into DivSingle
  175. ;*********
  176. DivSingle:
  177. ;*********
  178. ;Computes op2/op1
  179. ;Op2 is double, op1 is single (low 32 bits are zero)
  180. xchg edi,ebx ;Mantissa in edi, op2 ptr to ebx
  181. xchg ebx,ecx ;ExpSgn to ebx, op2 ptr to ecx
  182. mov edx,EMSEG:[ecx].lManHi
  183. mov eax,EMSEG:[ecx].lManLo
  184. mov ecx,EMSEG:[ecx].ExpSgn ;Op2 loaded
  185. DivSingleReg:
  186. ;dividend mantissa in edx:eax, exponent in high ecx, sign in ch bit 7
  187. ;divisor mantissa in edi, exponent in high ebx, sign in bh bit 7
  188. xor ch,bh ;Compute result sign
  189. xor bx,bx ;Clear out sign and tag
  190. sub ecx,1 shl 16 ;Exponent adjustment needed
  191. sub ecx,ebx ;Compute result exponent
  192. .erre TexpBias eq 0 ;Exponents not biased
  193. jo SDivBigUnderflow ;Dividing denormal by large number
  194. ContSdiv:
  195. ;If dividend >= divisor, the DIV instruction will overflow. Check for
  196. ;this condition and shift the dividend right one bit if necessary.
  197. ;
  198. ;In previous versions of this algorithm for 24-bit and 53-bit mantissas,
  199. ;this shift was always performed without a test. This meant that a 1-bit
  200. ;normalization might be required at the end. This worked fine because
  201. ;32 or 64 bits were calculated, so extra precision was available for
  202. ;normalization. However, this version needs all 64 bits that are calculated,
  203. ;so we can't afford a normalization shift at the end. This test tells us
  204. ;up front how to align so we'll be normalized.
  205. xor ebx,ebx ;Extend dividend
  206. cmp edi,edx ;Will DIV overflow?
  207. ja DoSdiv ;No, we're safe
  208. shrd ebx,eax,1
  209. shrd eax,edx,1
  210. shr edx,1
  211. add ecx,1 shl 16 ;Bump exponent to account for shift
  212. DoSdiv:
  213. div edi
  214. xchg ebx,eax ;Save quotient in ebx, extend remainder
  215. div edi
  216. mov esi,eax
  217. ;We have a 64-bit quotient in ebx:esi. Now compare remainder*2 with divisor
  218. ;to compute round and sticky bits.
  219. mov eax,-1 ;Set round and sticky bits
  220. shl edx,1 ;Double remainder
  221. jc RoundJmp ;If too big, round & sticky set
  222. cmp edx,edi ;Is remainder*2 > divisor?
  223. ja RoundJmp
  224. ;Observe, oh wondering one, how you can assume the result of this last
  225. ;compare is not equality. Use the following notation: n=numerator,
  226. ;d=denominator,q=quotient,r=remainder,b=base(2^64 here). If
  227. ;initially we had n < d then there was no shift and we will find q and r
  228. ;so that q*d+r=n*b, if initially we had n >= d then there was a shift and
  229. ;we will find q and r so that q*d+r=n*b/2. If we have equality here
  230. ;then r=d/2 ==> n={possibly 2*}(2*q+1)*d/(2*b), since this can only
  231. ;be integral if d is a multiple of b, but by definition b/2 <= d < b, we
  232. ;have a contradiction. Equality is thus impossible at this point.
  233. cmp edx,1 ;Check for zero remainder
  234. sbb eax,-2 ;eax==0 if CY, ==1 if NC (was -1)
  235. RoundJmp:
  236. jmp EMSEG:[RoundMode]
  237. ;*******************************************************************************
  238. DDivBigUnderflow:
  239. ;Overflow flag set could only occur with denormals (true exp < -32768)
  240. or EMSEG:[CURerr],Underflow
  241. test EMSEG:[CWmask],Underflow ;Is exception masked?
  242. jnz UnderflowZero ;Yes, return zero (in emfmul.asm)
  243. add ecx,Underbias shl 16 ;Fix up exponent
  244. jmp ContDdiv ;Continue with multiply
  245. DivrDoubleSetFlag:
  246. ;Special entry point used by FPATAN to set bit 6 of flag dword pushed
  247. ;on stack before call.
  248. or byte ptr [esp+4],40H
  249. ;*********
  250. DivrDouble:
  251. ;*********
  252. ;Computes op1/op2
  253. mov edx,ebx
  254. mov eax,esi ;Mantissa in edx:eax
  255. mov ebx,EMSEG:[edi].ExpSgn
  256. mov esi,EMSEG:[edi].lManHi
  257. mov edi,EMSEG:[edi].lManLo
  258. jmp short DivDoubleReg
  259. HighHalfEqual:
  260. ;edx:eax:ebp = dividend
  261. ;esi:edi = divisor
  262. ;ecx = exponent and sign of result
  263. ;
  264. ;High half of dividend is equal to high half of divisor. This will cause
  265. ;the DIV instruction to overflow. If whole dividend >= whole divisor, then
  266. ;we just shift the dividend right 1 bit.
  267. cmp eax,edi ;Is dividend >= divisor?
  268. jae ShiftDividend ;Yes, divide it by two
  269. ;DIV instruction would overflow, so skip it and calculate the effective
  270. ;result. Assume a quotient of 2^32-1 and calculate the remainder. See
  271. ;detailed comments under MaxQuo below--this is a copy of that code.
  272. push ecx ;Save exp. and sign
  273. mov ebx,-1 ;Max quotient digit
  274. sub eax,edi ;Calculate correct remainder
  275. ;Currently edx == esi, but the next instruction ensures that is no longer
  276. ;true, since eax != 0. This will allow us to skip the MaxQuo check at
  277. ;DivFirstDigit.
  278. add edx,eax ;Should set CY if quotient fit
  279. mov eax,edi ;ecx:eax has new remainder
  280. jc ComputeSecond ;Remainder was positive
  281. ;Quotient doesn't fit. Note that we can no longer ensure that edx != esi
  282. ;after making a correction.
  283. mov ecx,edx ;Need remainder in ecx:eax
  284. jmp DivCorrect1
  285. ;*********
  286. DivDouble:
  287. ;*********
  288. ;Computes op2/op1
  289. mov eax,edi ;Move op2 pointer
  290. mov edi,esi
  291. mov esi,ebx ;Mantissa in esi:edi
  292. mov ebx,ecx ;ExpSgn to ebx
  293. mov ecx,EMSEG:[eax].ExpSgn ;Op2 loaded
  294. mov edx,EMSEG:[eax].lManHi
  295. mov eax,EMSEG:[eax].lManLo
  296. DivDoubleReg:
  297. ;dividend mantissa in edx:eax, exponent in high ecx, sign in ch bit 7
  298. ;divisor mantissa in esi:edi, exponent in high ebx, sign in bh bit 7
  299. xor ch,bh ;Compute result sign
  300. xor bx,bx ;Clear out sign and tag
  301. sub ecx,1 shl 16 ;Exponent adjustment needed
  302. sub ecx,ebx ;Compute result exponent
  303. .erre TexpBias eq 0 ;Exponents not biased
  304. jo DDivBigUnderflow ;Dividing denormal by large number
  305. ContDdiv:
  306. ;If dividend >= divisor, we must shift the dividend right one bit.
  307. ;This will ensure the result is normalized.
  308. ;
  309. ;In previous versions of this algorithm for 24-bit and 53-bit mantissas,
  310. ;this shift was always performed without a test. This meant that a 1-bit
  311. ;normalization might be required at the end. This worked fine because
  312. ;32 or 64 bits were calculated, so extra precision was available for
  313. ;normalization. However, this version needs all 64 bits that are calculated,
  314. ;so we can't afford a normalization shift at the end. This test tells us
  315. ;up front how to align so we'll be normalized.
  316. xor ebp,ebp ;Extend dividend
  317. cmp esi,edx ;Dividend > divisor
  318. ja DoDdiv
  319. jz HighHalfEqual ;Go compare low halves
  320. ShiftDividend:
  321. shrd ebp,eax,1
  322. shrd eax,edx,1
  323. shr edx,1
  324. add ecx,1 shl 16 ;Bump exponent to account for shift
  325. DoDdiv:
  326. push ecx ;Save exp. and sign
  327. ;edx:eax:ebp = dividend
  328. ;esi:edi = divisor
  329. ;
  330. ;Division algorithm from Knuth vol. 2, p. 237, using 32-bit "digits":
  331. ;Guess a quotient digit by dividing two MSDs of dividend by the MSD of
  332. ;divisor. If divisor is >= 1/2 the radix (radix = 2^32 in this case), then
  333. ;this guess will be no more than 2 larger than the correct value of that
  334. ;quotient digit (and never smaller). Divisor meets magnitude condition
  335. ;because it's normalized.
  336. div esi ;Guess first quotient "digit"
  337. ;Check out our guess.
  338. ;Currently, remainder in edx = dividend - (quotient * high half divisor).
  339. ;The definition of remainder is dividend - (quotient * all divisor). So
  340. ;if we subtract (quotient * low half divisor) from edx, we'll get
  341. ;the true remainder. If it's negative, our guess was too big.
  342. mov ebx,eax ;Save quotient
  343. mov ecx,edx ;Save remainder
  344. mul edi ;Quotient * low half divisor
  345. sub ebp,eax ;Subtract from dividend extension
  346. sbb ecx,edx ;Subtract from remainder
  347. mov eax,ebp ;Low remainder to eax
  348. jnc DivFirstDigit ;Was quotient OK?
  349. DivCorrect1:
  350. dec ebx ;Quotient was too big
  351. add eax,edi ;Add divisor back into remainder
  352. adc ecx,esi
  353. jnc DivCorrect1 ;Repeat if quotient is still too big
  354. DivFirstDigit:
  355. cmp ecx,esi ;Would DIV instruction overflow?
  356. jae short MaxQuo ;Yes, figure alternate quotient
  357. mov edx,ecx ;Remainder back to edx:eax
  358. ;Compute 2nd quotient "digit"
  359. ComputeSecond:
  360. div esi ;Guess 2nd quotient "digit"
  361. mov ebp,eax ;Save quotient
  362. mov ecx,edx ;Save remainder
  363. mul edi ;Quotient * low half divisor
  364. neg eax ;Subtract from dividend extended with 0
  365. sbb ecx,edx ;Subtract from remainder
  366. jnc DivSecondDigit ;Was quotient OK?
  367. DivCorrect2:
  368. dec ebp ;Quotient was too big
  369. add eax,edi ;Add divisor back into remainder
  370. adc ecx,esi
  371. jnc DivCorrect2 ;Repeat if quotient is still too big
  372. DivSecondDigit:
  373. ;ebx:ebp = quotient
  374. ;ecx:eax = remainder
  375. ;esi:edi = divisor
  376. ;Now compare remainder*2 with divisor to compute round and sticky bits.
  377. mov edx,-1 ;Set round and sticky bits
  378. shld ecx,eax,1 ;Double remainder
  379. jc DDivEnd ;If too big, round & sticky set
  380. shl eax,1
  381. sub edi,eax
  382. sbb esi,ecx ;Subtract remainder*2 from divisor
  383. jb DDivEnd ;If <0, use round & sticky bits set
  384. ;Observe, oh wondering one, how you can assume the result of this last
  385. ;compare is not equality. Use the following notation: n=numerator,
  386. ;d=denominator,q=quotient,r=remainder,b=base(2^64 here). If
  387. ;initially we had n < d then there was no shift and we will find q and r
  388. ;so that q*d+r=n*b, if initially we had n >= d then there was a shift and
  389. ;we will find q and r so that q*d+r=n*b/2. If we have equality here
  390. ;then r=d/2 ==> n={possibly 2*}(2*q+1)*d/(2*b), since this can only
  391. ;be integral if d is a multiple of b, but by definition b/2 <= d < b, we
  392. ;have a contradiction. Equality is thus impossible at this point.
  393. ;No round bit, but set sticky bit if remainder != 0.
  394. or eax,ecx ;Is remainder zero?
  395. add eax,-1 ;Set CY if non-zero
  396. adc edx,1 ;edx==0 if NC, ==1 if CY (was -1)
  397. DDivEnd:
  398. mov esi,ebp ;Result in ebx:esi
  399. mov eax,edx ;Round/sticky bits to eax
  400. pop ecx ;Recover sign/exponent
  401. jmp EMSEG:[RoundMode]
  402. MaxQuo:
  403. ;ebx = first quotient "digit"
  404. ;ecx:eax = remainder
  405. ;esi:edi = divisor
  406. ;On exit, ebp = second quotient "digit"
  407. ;
  408. ;Come here if divide instruction would overflow. This must mean that ecx == esi,
  409. ;i.e., the high halves of the dividend and divisor are equal. Assume a result
  410. ;of 2^32-1, thus remainder = dividend - ( divisor * (2^32-1) )
  411. ; = dividend - divisor * 2^32 + divisor. Since the high halves of the dividend
  412. ;and divisor are equal, dividend - divisor * 2^32 can be computed by
  413. ;subtracting only the low halves. When adding divisor (in esi) to this, note
  414. ;that ecx == esi, and we want the result in ecx anyway.
  415. ;
  416. ;Note also that since the dividend is a previous remainder, the
  417. ;dividend - divisor * 2^32 calculation must always be negative. Thus the
  418. ;addition of divisor back to it should generate a carry if it goes positive.
  419. mov ebp,-1 ;Max quotient digit
  420. sub eax,edi ;Calculate correct remainder
  421. add ecx,eax ;Should set CY if quotient fit
  422. mov eax,edi ;ecx:eax has new remainder
  423. jc DivSecondDigit ;Remainder was positive
  424. jmp DivCorrect2