summaryrefslogtreecommitdiffstats
path: root/private/mvdm/wow16/win87em/emfprem.asm
diff options
context:
space:
mode:
Diffstat (limited to 'private/mvdm/wow16/win87em/emfprem.asm')
-rw-r--r--private/mvdm/wow16/win87em/emfprem.asm386
1 files changed, 386 insertions, 0 deletions
diff --git a/private/mvdm/wow16/win87em/emfprem.asm b/private/mvdm/wow16/win87em/emfprem.asm
new file mode 100644
index 000000000..69f484045
--- /dev/null
+++ b/private/mvdm/wow16/win87em/emfprem.asm
@@ -0,0 +1,386 @@
+ 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