diff options
Diffstat (limited to 'private/mvdm/wow16/timer/math.asm')
-rw-r--r-- | private/mvdm/wow16/timer/math.asm | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/private/mvdm/wow16/timer/math.asm b/private/mvdm/wow16/timer/math.asm new file mode 100644 index 000000000..0a08b3c75 --- /dev/null +++ b/private/mvdm/wow16/timer/math.asm @@ -0,0 +1,349 @@ + page ,132 +;---------------------------Module-Header-------------------------------; +; Module Name: MATH.ASM +; +; Contains FIXED point math routines. +; +; Created: Sun 30-Aug-1987 19:28:30 +; Author: Charles Whitmer [chuckwh] +; +; Copyright (c) 1987 Microsoft Corporation +;-----------------------------------------------------------------------; + +?WIN = 0 +?PLM = 1 +?NODATA = 0 +?DF=1 +PMODE=1 + + .xlist + include cmacros.inc +; include windows.inc + include timer.inc + .list + +UQUAD struc +uq0 dw ? +uq1 dw ? +uq2 dw ? +uq3 dw ? +UQUAD ends + +; The following structure should be used to access high and low +; words of a DWORD. This means that "word ptr foo[2]" -> "foo.hi". + +LONG struc +lo dw ? +hi dw ? +LONG ends + +sBegin Code286 + assumes cs,Code286 + assumes ds,nothing + assumes es,nothing + +;---------------------------Public-Routine------------------------------; +; qdiv +; +; This is an extended precision divide routine which is intended to +; emulate the 80386 64 bit/32 bit DIV instruction. We don't have the +; 32 bit registers to work with, but we pack the arguments and results +; into what registers we do have. We will divide two unsigned numbers +; and return the quotient and remainder. We will do INT 0 for overflow, +; just like the 80386 microcode. This should ease conversion later. +; +; Entry: +; DX:CX:BX:AX = UQUAD Numerator +; SI:DI = ULONG Denominator +; Returns: +; DX:AX = quotient +; CX:BX = remainder +; Registers Destroyed: +; none +; History: +; Tue 26-Jan-1988 00:02:09 -by- Charles Whitmer [chuckwh] +; Wrote it. +;-----------------------------------------------------------------------; + assumes ds,nothing + assumes es,nothing + +cProc qdiv,<PUBLIC,NEAR>,<si,di> + localQ uqNumerator + localD ulDenominator + localD ulQuotient + localW cShift +cBegin + +; stuff the quad word into local memory + + mov uqNumerator.uq0,ax + mov uqNumerator.uq1,bx + mov uqNumerator.uq2,cx + mov uqNumerator.uq3,dx + + +; check for a zero Numerator + + or ax,bx + or ax,cx + or ax,dx + jz qdiv_exit_relay ; quotient = remainder = 0 + +; handle the special case when the denominator lives in the low word + + or si,si + jnz not_that_special + +; calculate (DX=0):CX:BX:uqNumerator.uq0 / (SI=0):DI + + cmp di,1 ; separate out the trivial case + jz div_by_one + xchg dx,cx ; CX = remainder.hi = 0 + mov ax,bx + div di + mov bx,ax ; BX = quotient.hi + mov ax,uqNumerator.uq0 + div di ; AX = quotient.lo + xchg bx,dx ; DX = quotient.hi, BX = remainder.lo +ifdef WIMP + or ax,ax ; clear OF +endif +qdiv_exit_relay: + jmp qdiv_exit + +; calculate (DX=0):(CX=0):BX:uqNumerator.uq0 / (SI=0):(DI=1) + +div_by_one: + xchg dx,bx ; DX = quotient.hi, BX = remainder.lo = 0 + mov ax,uqNumerator.uq0 ; AX = quotient.lo + jmp qdiv_exit +not_that_special: + +; handle the special case when the denominator lives in the high word + + or di,di + jnz not_this_special_either + +; calculate DX:CX:BX:uqNumerator.uq0 / SI:(DI=0) + + cmp si,1 ; separate out the trivial case + jz div_by_10000h + mov ax,cx + div si + mov cx,ax ; CX = quotient.hi + mov ax,bx + div si ; AX = quotient.lo + xchg cx,dx ; DX = quotient.hi, CX = remainder.hi + mov bx,uqNumerator.uq0 ; BX = remainder.lo +ifdef WIMP + or ax,ax ; clear OF +endif + jmp qdiv_exit + +; calculate (DX=0):CX:BX:uqNumerator.uq0 / (SI=1):(DI=0) + +div_by_10000h: + xchg cx,dx ; DX = quotient.hi, CX = remainder.hi = 0 + mov ax,bx ; AX = quotient.lo + mov bx,uqNumerator.uq0 ; BX = remainder.lo + jmp qdiv_exit +not_this_special_either: + +; normalize the denominator + + mov dx,si + mov ax,di + call ulNormalize ; DX:AX = normalized denominator + mov cShift,cx ; CX < 16 + mov ulDenominator.lo,ax + mov ulDenominator.hi,dx + + +; shift the Numerator by the same amount + + jcxz numerator_is_shifted + mov si,-1 + shl si,cl + not si ; SI = mask + mov bx,uqNumerator.uq3 + shl bx,cl + mov ax,uqNumerator.uq2 + rol ax,cl + mov di,si + and di,ax + or bx,di + mov uqNumerator.uq3,bx + xor ax,di + mov bx,uqNumerator.uq1 + rol bx,cl + mov di,si + and di,bx + or ax,di + mov uqNumerator.uq2,ax + xor bx,di + mov ax,uqNumerator.uq0 + rol ax,cl + mov di,si + and di,ax + or bx,di + mov uqNumerator.uq1,bx + xor ax,di + mov uqNumerator.uq0,ax +numerator_is_shifted: + +; set up registers for division + + mov dx,uqNumerator.uq3 + mov ax,uqNumerator.uq2 + mov di,uqNumerator.uq1 + mov cx,ulDenominator.hi + mov bx,ulDenominator.lo + +; check for case when Denominator has only 16 bits + + or bx,bx + jnz must_do_long_division + div cx + mov si,ax + mov ax,uqNumerator.uq1 + div cx + xchg si,dx ; DX:AX = quotient + mov di,uqNumerator.uq0 ; SI:DI = remainder (shifted) + jmp short unshift_remainder +must_do_long_division: + +; do the long division, part IZ@NL@% + + cmp dx,cx ; we only know that DX:AX < CX:BX! + jb first_division_is_safe + mov ulQuotient.hi,0 ; i.e. 10000h, our guess is too big + mov si,ax + sub si,bx ; ... remainder is negative + jmp short first_adjuster +first_division_is_safe: + div cx + mov ulQuotient.hi,ax + mov si,dx + mul bx ; fix remainder for low order term + sub di,ax + sbb si,dx + jnc first_adjuster_done ; The remainder is UNSIGNED! We have +first_adjuster: ; to use the carry flag to keep track + dec ulQuotient.hi ; of the sign. The adjuster loop + add di,bx ; watches for a change to the carry + adc si,cx ; flag which would indicate a sign + jnc first_adjuster ; change IF we had more bits to keep +first_adjuster_done: ; a sign in. + +; do the long division, part II + + mov dx,si + mov ax,di + mov di,uqNumerator.uq0 + cmp dx,cx ; we only know that DX:AX < CX:BX! + jb second_division_is_safe + mov ulQuotient.lo,0 ; i.e. 10000h, our guess is too big + mov si,ax + sub si,bx ; ... remainder is negative + jmp short second_adjuster +second_division_is_safe: + div cx + mov ulQuotient.lo,ax + mov si,dx + mul bx ; fix remainder for low order term + sub di,ax + sbb si,dx + jnc second_adjuster_done +second_adjuster: + dec ulQuotient.lo + add di,bx + adc si,cx + jnc second_adjuster +second_adjuster_done: + mov ax,ulQuotient.lo + mov dx,ulQuotient.hi + +; unshift the remainder in SI:DI + +unshift_remainder: + mov cx,cShift + jcxz remainder_unshifted + mov bx,-1 + shr bx,cl + not bx + shr di,cl + ror si,cl + and bx,si + or di,bx + xor si,bx +remainder_unshifted: + mov cx,si + mov bx,di +ifdef WIMP + or ax,ax ; clear OF +endif +qdiv_exit: +cEnd + +;---------------------------Public-Routine------------------------------; +; ulNormalize +; +; Normalizes a ULONG so that the highest order bit is 1. Returns the +; number of shifts done. Also returns ZF=1 if the ULONG was zero. +; +; Entry: +; DX:AX = ULONG +; Returns: +; DX:AX = normalized ULONG +; CX = shift count +; ZF = 1 if the ULONG is zero, 0 otherwise +; Registers Destroyed: +; none +; History: +; Mon 25-Jan-1988 22:07:03 -by- Charles Whitmer [chuckwh] +; Wrote it. +;-----------------------------------------------------------------------; + assumes ds,nothing + assumes es,nothing + +cProc ulNormalize,<PUBLIC,NEAR> +cBegin + +; shift by words + + xor cx,cx + or dx,dx + js ulNormalize_exit + jnz top_word_ok + xchg ax,dx + or dx,dx + jz ulNormalize_exit ; the zero exit + mov cl,16 + js ulNormalize_exit +top_word_ok: + +; shift by bytes + + or dh,dh + jnz top_byte_ok + xchg dh,dl + xchg dl,ah + xchg ah,al + add cl,8 + or dh,dh + js ulNormalize_exit +top_byte_ok: + +; do the rest by bits + +next_byte: + inc cx + add ax,ax + adc dx,dx + jns next_byte +ulNormalize_exit: +cEnd + +sEnd + + end |