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

page ,132
subttl emfprem.asm - fprem
;***
;emfprem.asm - FPREM emulation
;
; Copyright (c) 1987, Microsoft Corporation
;
;Purpose:
; To emulate the 8087/80287 FPREM instruction.
;
;Revision History:
; 08-12-86 JMB Initial version
;
; 09-23-86 JMB Changed from unsigned (jnc) to signed branch
; (jge) on lc < 0 comparison
;
; 10-24-86 BCM Fixed a problem with large quotients that
; caused a signed comparison (cmp bp,2) to
; behave in an undesired manner when bp (quotient)
; becomes larger than 32767; we now use bigquot
; to indicate a quotients overflowing an unsigned word
; Also, the quotient is now enregistered in bp
; through most of the FPREM algorithm.
;
; 11-14-86 BCM Fixed a problem with the 4-word comparison of
; numerator and denominator mantissas by substituting
; unsigned-compare jumps (JB and JA) for signed-compare
; jumps (JL and JG).
;
; Also see emulator.hst
;
;*******************************************************************************
; The following is a working C program which was used to simulate
; floating point numbers and to test the algorithm used in the
; fprem emulation.
;#include <math.h>
;#include <stdio.h>
;
;typedef struct floating {
; unsigned long man;
; unsigned int expo;
; } FLOAT_NUM;
;
;double fprem(double,double,unsigned int *);
;
;#define normalize(n) while (n&0x8000==0) { \
; if (lc == 0) \
; return(ldexp((double)(num.man)/65536,den.expo)); \
; n <<= 1; \
; lc--; \
; *pq <<=1; \
; }
;
;
;main() {
; unsigned int qv,qt;
; double n,d,rv,rt;
; FILE *fpinp;
;
; fpinp = fopen("fprem.dat","r");
; if (fpinp) {
; while (fscanf(fpinp,"%E %E",&n,&d) != EOF) {
; qv=(unsigned int)(n/d);
; rv=n-(d*qv);
; printf(" \nnumerator is %f\n denominator is %f",n,d);
; printf(" \nquotient is %x\n remainder is %f",qv,rv);
; rt = fprem(n,d,&qt);
; printf(" \nquotient is %x\n remainder is %f\n\n",qt,rt);
; }
; fclose(fpinp);
; }
; else
; printf(" \nerror opening fprem.dat");
; }
;
;double fprem(n,d,pq)
; double n,d;
; unsigned int *pq; {
; int lc;
; FLOAT_NUM num;
; FLOAT_NUM den;
;
; num.man = (unsigned long)(65536*frexp(n,&num.expo));
; den.man = (unsigned long)(65536*frexp(d,&den.expo));
;
; printf(" \nnumerator mantissa: %lx",num.man);
; printf(" \nnumerator exponent: %x",num.expo);
; printf(" \ndenominator mantissa: %lx",den.man);
; printf(" \ndenominator exponent: %x",den.expo);
;
; *pq=0;
; lc = num.expo - den.expo;
; if (lc < 0) { /* then the numerator is the remainder */
; return(ldexp((double)(num.man)/65536,num.expo));
; }
; while(1) {
; if (den.man <= num.man) { /* do subtraction */
; num.man -= den.man;
; (*pq)++;
; if (lc == 0) {
; /* normalize(num.man) */
; return(ldexp((double)(num.man)/65536,den.expo));
; }
; normalize(num.man)
; }
; else { /* don't do the subtraction */
; if (lc == 0) {
; /* normalize(num.man) */
; return(ldexp((double)(num.man)/65536,den.expo));
; }
;
; num.man <<= 1;
; lc--;
; (*pq) <<= 1;
;
; num.man -= den.man;
; (*pq)++;
;
; normalize(num.man)
; }
; }
; }
;***
;eFPREM - entry point for FPREM emulation
;Purpose:
;
;Entry:
;
;Exit:
;
;Uses:
;
;Exceptions:
;*******************************************************************************
ProfBegin FPREM
pub eFPREM
; NOTE: The C program excerpts interspersed below are from the C program
; shown in its entirety above.
;
; The correspondence between the C variables and the assembly
; language version is as follows:
;
; C version masm version
; *pq bp (quotient)
; lc loopct
; num.expo Expon[di]
; den.expo Expon[si]
; num.man MB0[di],MB2[di],MB4[di],MB6[di]
; den.man MB0[si],MB2[si],MB4[si],MB6[si]
; *pq=0;
; lc = num.expo - den.expo;
push ebp ;save bp; use bp as quotient
mov edi,[CURSTK] ;point to ST(0), the numerator
mov [RESULT],edi ;ST(0) is result (remainder)
xor bp,bp ;begin with quotient = 0
mov bigquot,0 ;quotient not > 65535 yet
mov esi,edi ;si points to ST(0)
sub esi,Reg87Len ;si points to ST(1), the denominator
mov ax,word ptr Expon[edi] ;ax <== numerator exponent
sub ax,word ptr Expon[esi] ;loopct = (num exponent - den exponent)
mov loopct,ax
mov dx,MB0[edi] ;move the mantissa of the
mov cx,MB2[edi] ;numerator into
mov bx,MB4[edi] ;ax:bx:cx:dx
mov ax,MB6[edi]
; if (lc < 0) { /* then the numerator is the remainder */
; return(ldexp((double)(num.man)/65536,num.expo));
; }
jge short fpremLoop
mov si,Expon[edi]
jmp DoneEarly
; while(1) {
fpremLoop:
; if (den.man <= num.man) { /* do subtraction */
cmp ax,MB6[esi] ;compare msw of num to msw of den
jb short NumLess ;numerator is less
ja short NumMore ;numerator is more
cmp bx,MB4[esi] ;compare word 2 of num to word 2 of den
jb short NumLess ;numerator is less
ja short NumMore ;numerator is more
cmp cx,MB2[esi] ;compare word 4 of num to word 4 of den
jb short NumLess ;numerator is less
ja short NumMore ;numerator is more
cmp dx,MB0[esi] ;compare lsw of num to lsw of den
jb short NumLess ;numerator is less
; num.man -= den.man;
; (*pq)++;
; if (lc == 0) {
; /* normalize(num.man) */
; return(ldexp((double)(num.man)/65536,den.expo));
; }
NumMore:
call SubInc ;do subtraction, increment quotient
cmp loopct,0 ;is expon diff zero?
je short Done ;yes, then we're done
; normalize(num.man)
; }
call fpremNorm ;normalize the numerator
jnz fpremLoop ;do the next iteration
jmp short Done ;loop counter is zero; we're done
; else { /* don't do the subtraction */
; if (lc == 0) {
; /* normalize(num.man) */
; return(ldexp((double)(num.man)/65536,den.expo));
; }
NumLess:
cmp loopct,0 ;is expon diff zero?
je short Done ;yes, then all done
;
; num.man <<= 1;
; lc--;
; (*pq) <<= 1;
call ShiftDec ;shift quotient, numerator
;
; num.man -= den.man;
; (*pq)++;
call SubInc ;do subtraction, increment quotient
;
; normalize(num.man)
; }
call fpremNorm ;normalize for next iteration
jnz fpremLoop ;do next iteration
jmp short Done ;loop counter is zero; we're done
; remainder: ax:bx:cx:dx is mantissa; Expon[si] is the exponent
Done:
;NOTE: the rounding routine wants the mantissa in di:bx:cx:dx:bp
; the exponent in SI the sign and the old BP on the stack
mov si,Expon[esi] ; mov exponent to si
DoneEarly:
mov di,Flag[edi] ; move sign of remainder to di
xchg di,ax ; di becomes high mantissa word
mov ah,al ; move sign to ah
push ax ; put sign on stack
; Except for bp which gets zeroed out later,
; everything is now set up the way it needs to be for the normalization
; routine, NODRQQ. Before we go there we need to set up the status
; word as it should be set by fprem. For simplicity we did a complete
; reduction of the dividend in one pass, so we will always clear C2
; to indicate that the reduction is complete.
mov ax,bp ; move quotient into ax
and bp,0FFFCh ; check if quotient mod 64K < 4
or bp,bigquot ; and quotient < 64K
; bp is zero if and only if quotient < 4
; (bp no longer holds the quotient itself)
; al has low byte of quotient
; ah will be for C0-C3 flags
mov ah,SWcc ; move status word to ah
and ah,not C2 ; clear C2 to indicate complete
test al,01h ; is low bit of quotient set?
jnz short SetC1 ; yes, go set C1
and ah,not C1 ; low bit off, turn off C1
jmp short DoC3 ; do the C3 bit
SetC1:
or ah,C1 ; low bit on, turn on C1
DoC3:
test al,02h ; is bit 1 of quotient set?
jnz short SetC3 ; yes, go set C3
or bp,bp ; is quotient less than 4?
jz short QuotL2 ; then quotient < 2 (bit 1 off)
; so don't set c0 or c3 from quotient
; else if quotient >= 4
and ah,not C3 ; bit 1 is off, so turn off C3
jmp short DoC0 ; do the C0 bit
SetC3:
or ah,C3 ; bit 1 on, turn on C3
DoC0:
test al,04h ; is bit 2 of quotient set?
jnz short SetC0 ; yes, go set C0
or bp,bp ; is quotient less than 4?
jz short QuotL4 ; yes, don't set c0 from quotient
; else if quotient >= 4
and ah,not C0 ; bit 2 off, turn off C0
jmp short GoNormal ; we're done, go normalize
SetC0:
or ah,C0 ; bit 1 on, turn on C0
GoNormal:
mov SWcc,ah ; set new status word
xor bp,bp ; clear low mantissa word
jmp NODRQQ ; go normalize
; (does pop ax, pop bp)
; special case code if quotient is less than 2
QuotL2:
mov al,SWcc ; get old status word
test al,C1 ; was C1 set
jnz short SetC3toC1 ; yes, set C3
and ah,not C3 ; clear C3
jmp short QuotL4
SetC3toC1:
or ah,C3 ; set C3
; special case code if quotient is less than 4
QuotL4:
mov al,SWcc ; get old status word
test al,C3 ; was C3 set
jnz short SetC0toC3 ; yes, set C0
and ah,not C0 ; clear C0
jmp short GoNormal ; go normalize the result
SetC0toC3:
or ah,C0 ; set C0
jmp short GoNormal ; go normalize the result
;#define normalize(n) while (n&0x8000==0) { \
; if (lc == 0) \
; return(ldexp((double)(num.man)/65536,den.expo)); \
; n <<= 1; \
; lc--; \
; *pq <<=1; \
; }
;Inputs: ah contains high byte of numerator mantissa
;Outputs: zero flag set indicates the loop counter is zero so we're finished
; zero flag clear indicates the number was already normalized
fpremNorm:
test ah,80h ;is the numerator normalized?
jnz short fpremIsNorm ;yes
;no, normalize it
cmp loopct,0 ;is expon diff zero?
je short fpremIsNorm ;yes, then we're done
call ShiftDec ;shift num, quotient
;decrement loop ctr
jmp short fpremNorm
fpremIsNorm:
ret
ShiftDec:
shl dx,1 ;numerator*2
rcl cx,1
rcl bx,1
rcl ax,1
dec loopct ;reduce exponent diff by one
shl bp,1 ;quotient*2
jc short QuotLarge ;carry out on quotient shift
ret
QuotLarge:
mov bigquot,1 ;indicate large quotient > 65535
ret
SubInc:
sub dx,MB0[esi] ;subtract lsw of den from lsw of num
sbb cx,MB2[esi] ;subtract next word of den from num
sbb bx,MB4[esi] ;subtract next word of den from num
sbb ax,MB6[esi] ;subtract msw of den from msw of num
inc bp ;add one to quotient
ret
ProfEnd FPREM