Source code of Windows XP (NT5)
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.

279 lines
7.3 KiB

  1. subttl emlsbcd.asm - FBSTP and FBLD instructions
  2. page
  3. ;*******************************************************************************
  4. ;emlsbcd.asm - FBSTP and FBLD instructions
  5. ;
  6. ; Microsoft Confidential
  7. ;
  8. ; Copyright (c) Microsoft Corporation 1991
  9. ; All Rights Reserved
  10. ;
  11. ;Purpose:
  12. ; FBSTP and FBLD instructions.
  13. ;
  14. ; These routines convert between 64-bit integer and 18-digit packed BCD
  15. ; format. They work by splitting the number being converted in half
  16. ; and converting the two halves separately. This works well because
  17. ; 9 decimal digits fit nicely within 30 binary bits, so converion of
  18. ; each half is strictly a 32-bit operation.
  19. ;
  20. ;Inputs:
  21. ; edi = [CURstk]
  22. ; dseg:esi = pointer to memory operand
  23. ;
  24. ;Revision History:
  25. ;
  26. ; [] 09/05/91 TP Initial 32-bit version.
  27. ;
  28. ;*******************************************************************************
  29. ;******
  30. eFBLD:
  31. ;******
  32. mov eax,dseg:[esi+5] ;Get high 8 digits
  33. or eax,eax ;Anything there?
  34. jz HighDigitsZero
  35. mov ecx,8
  36. call ReadDigits ;Convert first 8 digits to binary
  37. mov eax,dseg:[esi+1] ;Get next 8 digits
  38. xor edi,edi
  39. shld edi,eax,4 ;Shift ninth digit into edi
  40. imul ebx,10
  41. add edi,ebx ;Accumulate ninth digit
  42. SecondNineDigits:
  43. xor ebx,ebx ;In case eax==0
  44. shl eax,4 ;Keep digits left justified
  45. jz LastTwoDigits
  46. mov ecx,7
  47. call ReadDigits ;Convert next 7 digits to binary
  48. LastTwoDigits:
  49. mov al,dseg:[esi] ;Get last two digits
  50. shl eax,24 ;Left justify
  51. mov ecx,2
  52. call InDigitLoop ;Accumulate last two digits
  53. ;edi = binary value of high 9 digits
  54. ;ebx = binary value of low 9 digits
  55. mov eax,1000000000 ;One billion: shift nine digits left
  56. mul edi ;Left shift 9 digits. 9 cl. if edi==0
  57. add ebx,eax ;Add in low digits
  58. adc edx,0
  59. BcdReadyToNorm:
  60. ;edx:ebx = integer converted to binary
  61. mov eax,dseg:[esi+6] ;Get sign to high bit of eax
  62. mov esi,ebx
  63. mov ebx,edx
  64. mov edi,EMSEG:[CURstk]
  65. ;mantissa in ebx:esi, sign in high bit of eax
  66. ;edi = [CURstk]
  67. jmp NormQuadInt ;in emload.asm
  68. HighDigitsZero:
  69. mov eax,dseg:[esi+1] ;Get next 8 digits
  70. or eax,eax ;Anything there?
  71. jz CheckLastTwo
  72. xor edi,edi
  73. shld edi,eax,4 ;Shift ninth digit into edi
  74. jmp SecondNineDigits
  75. CheckLastTwo:
  76. mov bl,dseg:[esi] ;Get last two digits
  77. or bl,bl
  78. jz ZeroBCD
  79. mov al,bl
  80. shr al,4 ;Bring down upper digit
  81. imul eax,10
  82. and ebx,0FH ;Keep lowest digit only
  83. add ebx,eax
  84. xor edx,edx
  85. jmp BcdReadyToNorm
  86. ZeroBCD:
  87. mov ecx,bTAG_ZERO ;Exponent is zero
  88. mov ch,dseg:[esi+9] ;Get sign byte to ch
  89. xor ebx,ebx
  90. mov esi,ebx
  91. ;mantissa in ebx:esi, exp/sign in ecx
  92. ;edi = [CURstk]
  93. jmp FldCont ;in emload.asm
  94. ;*** ReadDigits
  95. ;
  96. ;Inputs:
  97. ; eax = packed BCD digits, left justified, non-zero
  98. ; ecx = no. of digits, 7 or 8
  99. ;Outputs:
  100. ; ebx = number
  101. SkipZeroDigits:
  102. sub ecx,3
  103. shl eax,12
  104. ReadDigits:
  105. ;We start by scanning off leading zeros. This costs 16 cl./nybble in
  106. ;the ScanZero loop. To reduce this cost for many leading zeros, we
  107. ;check for three leading zeros at a time. Adding this test saves
  108. ;26 cl. for 3 leading zeros, 57 cl. for 6 leading zeros, at a cost
  109. ;of only 5 cl. if less than 3 zeros. We choose 3 at a time so we
  110. ;can repeat it once (there are never more than 7 zeros).
  111. test eax,0FFF00000H ;Check first 3 nybbles for zero
  112. jz SkipZeroDigits
  113. xor ebx,ebx
  114. ScanZero:
  115. ;Note that bsr is 3 cl/bit, or 12 cl/nybble. Add in the overhead and
  116. ;this loop of 16 cl/nybble is cheaper for the 1 - 3 digits it does.
  117. dec ecx
  118. shld ebx,eax,4 ;Shift digit into ebx
  119. rol eax,4 ;Left justify **Doesn't affect ZF!**
  120. jz ScanZero ;Skip to next digit if zero
  121. jecxz ReadDigitsX
  122. InDigitLoop:
  123. ;eax = digits to convert, left justified
  124. ;ebx = result accumulation
  125. ;ecx = number of digits to convert
  126. xor edx,edx
  127. shld edx,eax,4 ;Shift digit into edx
  128. shl eax,4 ;Keep digits left justified
  129. imul ebx,10 ;Only 10 clocks on 386!
  130. add ebx,edx ;Accumulate number
  131. dec ecx
  132. jnz InDigitLoop
  133. ReadDigitsX:
  134. ret
  135. ;*******************************************************************************
  136. ChkInvalidBCD:
  137. ja SetInvalidBCD
  138. cmp edi,0A7640000H ;(1000000000*1000000000) and 0ffffffffh
  139. jb ValidBCD
  140. SetInvalidBCD:
  141. mov EMSEG:[CURerr],Invalid
  142. InvalidBCD:
  143. test EMSEG:[CWmask],Invalid ;Is it masked?
  144. jz ReadDigitsX ;No--leave memory unchanged
  145. ;Store Indefinite
  146. mov dword ptr dseg:[esi],0
  147. mov dword ptr dseg:[esi+4],0
  148. mov word ptr dseg:[esi+8],-1 ;0FF00000000H for packed BCD indefinite
  149. jmp PopStack ;in emstore.asm
  150. ;******
  151. eFBSTP:
  152. ;******
  153. call RoundToInteger ;Get integer in ebx:edi, sign in ch
  154. jc InvalidBCD
  155. cmp ebx,0DE0B6B3H ;(1000000000*1000000000) shr 32
  156. jae ChkInvalidBCD
  157. ValidBCD:
  158. and ch,bSign
  159. mov dseg:[esi+9],ch ;Fill in sign byte
  160. mov edx,ebx
  161. mov eax,edi ;Get number to edx:eax for division
  162. mov ebx,1000000000
  163. div ebx ;Break into two 9-digit halves
  164. xor ecx,ecx ;Initial digits
  165. mov edi,eax ;Save quotient
  166. mov eax,edx
  167. or eax,eax
  168. jz SaveLowBCD
  169. call WriteDigits
  170. shrd ecx,eax,4 ;Pack 8th digit
  171. xor al,al
  172. shl eax,20 ;Move digit in ah to high end
  173. SaveLowBCD:
  174. mov dseg:[esi],ecx ;Save low 8 digits
  175. mov ecx,eax ;Get ready for next 8 digits
  176. mov eax,edi
  177. or eax,eax
  178. jz ZeroHighBCD
  179. call WriteDigits
  180. shl ah,4 ;Move digit to upper nybble
  181. or al,ah ;Combine last two digits
  182. SaveHighBCD:
  183. mov dseg:[esi+4],ecx ;Save lower 8 digits
  184. mov dseg:[esi+8],al
  185. jmp PopStack
  186. ZeroHighBCD:
  187. shr ecx,28 ;Position 9th digit
  188. jmp SaveHighBCD
  189. ;*** WriteDigits
  190. ;
  191. ;Inputs:
  192. ; eax = binary number < 1,000,000,000 and > 0
  193. ; ecx = Zero or had one BCD digit left justified
  194. ;Purpose:
  195. ; Convert binary integer to BCD.
  196. ;
  197. ; The time required for the DIV instruction is dependent on operand
  198. ; size, at 6 + (no. of bits) clocks for 386. (In contrast, multiply
  199. ; by 10 as used in FBLD/ReadDigits above takes the same amount of
  200. ; time regardless of operand size--only 10 clocks.)
  201. ;
  202. ; The easy way to do this conversion would be to repeatedly do a
  203. ; 32-bit division by 10 (at 38 clocks/divide). Instead, the number
  204. ; is broken down so that mostly 8-bit division is used (only 14 clocks).
  205. ; AAM (17 clocks) is also used to save us from having to load the
  206. ; constant 10 and zero ah. AAM is faster than DIV on the 486sx.
  207. ;
  208. ;Outputs:
  209. ; ecx has seven more digits packed into it (from left)
  210. ; ah:al = most significant two digits (unpacked)
  211. ;esi,edi preserved
  212. WriteDigits:
  213. ;eax = binary number < 1,000,000,000
  214. cdq ;Zero edx
  215. mov ebx,10000
  216. div ebx ;Break into 4-digit and 5-digit pieces
  217. mov bl,100
  218. or edx,edx
  219. jz ZeroLowDigits
  220. xchg edx,eax ;Get 4-digit remainder to eax
  221. ;Compute low 4 digits
  222. ; 0 < eax < 10000
  223. div bl ;Get two 2-digit pieces. 14cl on 386
  224. mov bh,al ;Save high 2 digits
  225. mov al,ah ;Get low digits
  226. aam
  227. shl ah,4 ;Move digit to upper nybble
  228. or al,ah
  229. shrd ecx,eax,8
  230. mov al,bh ;Get high 2 digits
  231. aam
  232. shl ah,4 ;Move digit to upper nybble
  233. or al,ah
  234. shrd ecx,eax,8
  235. ;Compute high 5 digits
  236. mov eax,edx ;5-digit quotient to eax
  237. or eax,eax
  238. jz ZeroHighDigits
  239. ConvHigh5:
  240. cdq ;Zero edx
  241. shld edx,eax,16 ;Put quotient in dx:ax
  242. xor bh,bh ;bx = 100
  243. div bx ;Get 2- and 3-digit pieces. 22cl on 386
  244. xchg edx,eax ;Save high 3 digits, get log 2 digits
  245. aam
  246. shl ah,4 ;Move digit to upper nybble
  247. or al,ah
  248. shrd ecx,eax,8
  249. mov eax,edx ;Get high 3 digits
  250. mov bl,10
  251. div bl
  252. mov bl,ah ;Remainder is next digit
  253. shrd ecx,ebx,4
  254. aam ;Get last two digits
  255. ;Last two digits in ah:al
  256. ret
  257. ZeroLowDigits:
  258. shr ecx,16
  259. jmp ConvHigh5
  260. ZeroHighDigits:
  261. shr ecx,12
  262. ret