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.

386 lines
9.8 KiB

  1. page ,132
  2. subttl emfprem.asm - fprem
  3. ;***
  4. ;emfprem.asm - FPREM emulation
  5. ;
  6. ; Copyright (c) 1987, Microsoft Corporation
  7. ;
  8. ;Purpose:
  9. ; To emulate the 8087/80287 FPREM instruction.
  10. ;
  11. ;Revision History:
  12. ; 08-12-86 JMB Initial version
  13. ;
  14. ; 09-23-86 JMB Changed from unsigned (jnc) to signed branch
  15. ; (jge) on lc < 0 comparison
  16. ;
  17. ; 10-24-86 BCM Fixed a problem with large quotients that
  18. ; caused a signed comparison (cmp bp,2) to
  19. ; behave in an undesired manner when bp (quotient)
  20. ; becomes larger than 32767; we now use bigquot
  21. ; to indicate a quotients overflowing an unsigned word
  22. ; Also, the quotient is now enregistered in bp
  23. ; through most of the FPREM algorithm.
  24. ;
  25. ; 11-14-86 BCM Fixed a problem with the 4-word comparison of
  26. ; numerator and denominator mantissas by substituting
  27. ; unsigned-compare jumps (JB and JA) for signed-compare
  28. ; jumps (JL and JG).
  29. ;
  30. ; Also see emulator.hst
  31. ;
  32. ;*******************************************************************************
  33. ; The following is a working C program which was used to simulate
  34. ; floating point numbers and to test the algorithm used in the
  35. ; fprem emulation.
  36. ;#include <math.h>
  37. ;#include <stdio.h>
  38. ;
  39. ;typedef struct floating {
  40. ; unsigned long man;
  41. ; unsigned int expo;
  42. ; } FLOAT_NUM;
  43. ;
  44. ;double fprem(double,double,unsigned int *);
  45. ;
  46. ;#define normalize(n) while (n&0x8000==0) { \
  47. ; if (lc == 0) \
  48. ; return(ldexp((double)(num.man)/65536,den.expo)); \
  49. ; n <<= 1; \
  50. ; lc--; \
  51. ; *pq <<=1; \
  52. ; }
  53. ;
  54. ;
  55. ;main() {
  56. ; unsigned int qv,qt;
  57. ; double n,d,rv,rt;
  58. ; FILE *fpinp;
  59. ;
  60. ; fpinp = fopen("fprem.dat","r");
  61. ; if (fpinp) {
  62. ; while (fscanf(fpinp,"%E %E",&n,&d) != EOF) {
  63. ; qv=(unsigned int)(n/d);
  64. ; rv=n-(d*qv);
  65. ; printf(" \nnumerator is %f\n denominator is %f",n,d);
  66. ; printf(" \nquotient is %x\n remainder is %f",qv,rv);
  67. ; rt = fprem(n,d,&qt);
  68. ; printf(" \nquotient is %x\n remainder is %f\n\n",qt,rt);
  69. ; }
  70. ; fclose(fpinp);
  71. ; }
  72. ; else
  73. ; printf(" \nerror opening fprem.dat");
  74. ; }
  75. ;
  76. ;double fprem(n,d,pq)
  77. ; double n,d;
  78. ; unsigned int *pq; {
  79. ; int lc;
  80. ; FLOAT_NUM num;
  81. ; FLOAT_NUM den;
  82. ;
  83. ; num.man = (unsigned long)(65536*frexp(n,&num.expo));
  84. ; den.man = (unsigned long)(65536*frexp(d,&den.expo));
  85. ;
  86. ; printf(" \nnumerator mantissa: %lx",num.man);
  87. ; printf(" \nnumerator exponent: %x",num.expo);
  88. ; printf(" \ndenominator mantissa: %lx",den.man);
  89. ; printf(" \ndenominator exponent: %x",den.expo);
  90. ;
  91. ; *pq=0;
  92. ; lc = num.expo - den.expo;
  93. ; if (lc < 0) { /* then the numerator is the remainder */
  94. ; return(ldexp((double)(num.man)/65536,num.expo));
  95. ; }
  96. ; while(1) {
  97. ; if (den.man <= num.man) { /* do subtraction */
  98. ; num.man -= den.man;
  99. ; (*pq)++;
  100. ; if (lc == 0) {
  101. ; /* normalize(num.man) */
  102. ; return(ldexp((double)(num.man)/65536,den.expo));
  103. ; }
  104. ; normalize(num.man)
  105. ; }
  106. ; else { /* don't do the subtraction */
  107. ; if (lc == 0) {
  108. ; /* normalize(num.man) */
  109. ; return(ldexp((double)(num.man)/65536,den.expo));
  110. ; }
  111. ;
  112. ; num.man <<= 1;
  113. ; lc--;
  114. ; (*pq) <<= 1;
  115. ;
  116. ; num.man -= den.man;
  117. ; (*pq)++;
  118. ;
  119. ; normalize(num.man)
  120. ; }
  121. ; }
  122. ; }
  123. ;***
  124. ;eFPREM - entry point for FPREM emulation
  125. ;Purpose:
  126. ;
  127. ;Entry:
  128. ;
  129. ;Exit:
  130. ;
  131. ;Uses:
  132. ;
  133. ;Exceptions:
  134. ;*******************************************************************************
  135. ProfBegin FPREM
  136. pub eFPREM
  137. ; NOTE: The C program excerpts interspersed below are from the C program
  138. ; shown in its entirety above.
  139. ;
  140. ; The correspondence between the C variables and the assembly
  141. ; language version is as follows:
  142. ;
  143. ; C version masm version
  144. ; *pq bp (quotient)
  145. ; lc loopct
  146. ; num.expo Expon[di]
  147. ; den.expo Expon[si]
  148. ; num.man MB0[di],MB2[di],MB4[di],MB6[di]
  149. ; den.man MB0[si],MB2[si],MB4[si],MB6[si]
  150. ; *pq=0;
  151. ; lc = num.expo - den.expo;
  152. push ebp ;save bp; use bp as quotient
  153. mov edi,[CURSTK] ;point to ST(0), the numerator
  154. mov [RESULT],edi ;ST(0) is result (remainder)
  155. xor bp,bp ;begin with quotient = 0
  156. mov bigquot,0 ;quotient not > 65535 yet
  157. mov esi,edi ;si points to ST(0)
  158. sub esi,Reg87Len ;si points to ST(1), the denominator
  159. mov ax,word ptr Expon[edi] ;ax <== numerator exponent
  160. sub ax,word ptr Expon[esi] ;loopct = (num exponent - den exponent)
  161. mov loopct,ax
  162. mov dx,MB0[edi] ;move the mantissa of the
  163. mov cx,MB2[edi] ;numerator into
  164. mov bx,MB4[edi] ;ax:bx:cx:dx
  165. mov ax,MB6[edi]
  166. ; if (lc < 0) { /* then the numerator is the remainder */
  167. ; return(ldexp((double)(num.man)/65536,num.expo));
  168. ; }
  169. jge short fpremLoop
  170. mov si,Expon[edi]
  171. jmp DoneEarly
  172. ; while(1) {
  173. fpremLoop:
  174. ; if (den.man <= num.man) { /* do subtraction */
  175. cmp ax,MB6[esi] ;compare msw of num to msw of den
  176. jb short NumLess ;numerator is less
  177. ja short NumMore ;numerator is more
  178. cmp bx,MB4[esi] ;compare word 2 of num to word 2 of den
  179. jb short NumLess ;numerator is less
  180. ja short NumMore ;numerator is more
  181. cmp cx,MB2[esi] ;compare word 4 of num to word 4 of den
  182. jb short NumLess ;numerator is less
  183. ja short NumMore ;numerator is more
  184. cmp dx,MB0[esi] ;compare lsw of num to lsw of den
  185. jb short NumLess ;numerator is less
  186. ; num.man -= den.man;
  187. ; (*pq)++;
  188. ; if (lc == 0) {
  189. ; /* normalize(num.man) */
  190. ; return(ldexp((double)(num.man)/65536,den.expo));
  191. ; }
  192. NumMore:
  193. call SubInc ;do subtraction, increment quotient
  194. cmp loopct,0 ;is expon diff zero?
  195. je short Done ;yes, then we're done
  196. ; normalize(num.man)
  197. ; }
  198. call fpremNorm ;normalize the numerator
  199. jnz fpremLoop ;do the next iteration
  200. jmp short Done ;loop counter is zero; we're done
  201. ; else { /* don't do the subtraction */
  202. ; if (lc == 0) {
  203. ; /* normalize(num.man) */
  204. ; return(ldexp((double)(num.man)/65536,den.expo));
  205. ; }
  206. NumLess:
  207. cmp loopct,0 ;is expon diff zero?
  208. je short Done ;yes, then all done
  209. ;
  210. ; num.man <<= 1;
  211. ; lc--;
  212. ; (*pq) <<= 1;
  213. call ShiftDec ;shift quotient, numerator
  214. ;
  215. ; num.man -= den.man;
  216. ; (*pq)++;
  217. call SubInc ;do subtraction, increment quotient
  218. ;
  219. ; normalize(num.man)
  220. ; }
  221. call fpremNorm ;normalize for next iteration
  222. jnz fpremLoop ;do next iteration
  223. jmp short Done ;loop counter is zero; we're done
  224. ; remainder: ax:bx:cx:dx is mantissa; Expon[si] is the exponent
  225. Done:
  226. ;NOTE: the rounding routine wants the mantissa in di:bx:cx:dx:bp
  227. ; the exponent in SI the sign and the old BP on the stack
  228. mov si,Expon[esi] ; mov exponent to si
  229. DoneEarly:
  230. mov di,Flag[edi] ; move sign of remainder to di
  231. xchg di,ax ; di becomes high mantissa word
  232. mov ah,al ; move sign to ah
  233. push ax ; put sign on stack
  234. ; Except for bp which gets zeroed out later,
  235. ; everything is now set up the way it needs to be for the normalization
  236. ; routine, NODRQQ. Before we go there we need to set up the status
  237. ; word as it should be set by fprem. For simplicity we did a complete
  238. ; reduction of the dividend in one pass, so we will always clear C2
  239. ; to indicate that the reduction is complete.
  240. mov ax,bp ; move quotient into ax
  241. and bp,0FFFCh ; check if quotient mod 64K < 4
  242. or bp,bigquot ; and quotient < 64K
  243. ; bp is zero if and only if quotient < 4
  244. ; (bp no longer holds the quotient itself)
  245. ; al has low byte of quotient
  246. ; ah will be for C0-C3 flags
  247. mov ah,SWcc ; move status word to ah
  248. and ah,not C2 ; clear C2 to indicate complete
  249. test al,01h ; is low bit of quotient set?
  250. jnz short SetC1 ; yes, go set C1
  251. and ah,not C1 ; low bit off, turn off C1
  252. jmp short DoC3 ; do the C3 bit
  253. SetC1:
  254. or ah,C1 ; low bit on, turn on C1
  255. DoC3:
  256. test al,02h ; is bit 1 of quotient set?
  257. jnz short SetC3 ; yes, go set C3
  258. or bp,bp ; is quotient less than 4?
  259. jz short QuotL2 ; then quotient < 2 (bit 1 off)
  260. ; so don't set c0 or c3 from quotient
  261. ; else if quotient >= 4
  262. and ah,not C3 ; bit 1 is off, so turn off C3
  263. jmp short DoC0 ; do the C0 bit
  264. SetC3:
  265. or ah,C3 ; bit 1 on, turn on C3
  266. DoC0:
  267. test al,04h ; is bit 2 of quotient set?
  268. jnz short SetC0 ; yes, go set C0
  269. or bp,bp ; is quotient less than 4?
  270. jz short QuotL4 ; yes, don't set c0 from quotient
  271. ; else if quotient >= 4
  272. and ah,not C0 ; bit 2 off, turn off C0
  273. jmp short GoNormal ; we're done, go normalize
  274. SetC0:
  275. or ah,C0 ; bit 1 on, turn on C0
  276. GoNormal:
  277. mov SWcc,ah ; set new status word
  278. xor bp,bp ; clear low mantissa word
  279. jmp NODRQQ ; go normalize
  280. ; (does pop ax, pop bp)
  281. ; special case code if quotient is less than 2
  282. QuotL2:
  283. mov al,SWcc ; get old status word
  284. test al,C1 ; was C1 set
  285. jnz short SetC3toC1 ; yes, set C3
  286. and ah,not C3 ; clear C3
  287. jmp short QuotL4
  288. SetC3toC1:
  289. or ah,C3 ; set C3
  290. ; special case code if quotient is less than 4
  291. QuotL4:
  292. mov al,SWcc ; get old status word
  293. test al,C3 ; was C3 set
  294. jnz short SetC0toC3 ; yes, set C0
  295. and ah,not C0 ; clear C0
  296. jmp short GoNormal ; go normalize the result
  297. SetC0toC3:
  298. or ah,C0 ; set C0
  299. jmp short GoNormal ; go normalize the result
  300. ;#define normalize(n) while (n&0x8000==0) { \
  301. ; if (lc == 0) \
  302. ; return(ldexp((double)(num.man)/65536,den.expo)); \
  303. ; n <<= 1; \
  304. ; lc--; \
  305. ; *pq <<=1; \
  306. ; }
  307. ;Inputs: ah contains high byte of numerator mantissa
  308. ;Outputs: zero flag set indicates the loop counter is zero so we're finished
  309. ; zero flag clear indicates the number was already normalized
  310. fpremNorm:
  311. test ah,80h ;is the numerator normalized?
  312. jnz short fpremIsNorm ;yes
  313. ;no, normalize it
  314. cmp loopct,0 ;is expon diff zero?
  315. je short fpremIsNorm ;yes, then we're done
  316. call ShiftDec ;shift num, quotient
  317. ;decrement loop ctr
  318. jmp short fpremNorm
  319. fpremIsNorm:
  320. ret
  321. ShiftDec:
  322. shl dx,1 ;numerator*2
  323. rcl cx,1
  324. rcl bx,1
  325. rcl ax,1
  326. dec loopct ;reduce exponent diff by one
  327. shl bp,1 ;quotient*2
  328. jc short QuotLarge ;carry out on quotient shift
  329. ret
  330. QuotLarge:
  331. mov bigquot,1 ;indicate large quotient > 65535
  332. ret
  333. SubInc:
  334. sub dx,MB0[esi] ;subtract lsw of den from lsw of num
  335. sbb cx,MB2[esi] ;subtract next word of den from num
  336. sbb bx,MB4[esi] ;subtract next word of den from num
  337. sbb ax,MB6[esi] ;subtract msw of den from msw of num
  338. inc bp ;add one to quotient
  339. ret
  340. ProfEnd FPREM