;*DDK*************************************************************************/
;
; COPYRIGHT (C) Microsoft Corporation, 1989
; COPYRIGHT    Copyright (C) 1995 IBM Corporation
;
;    The following IBM OS/2 WARP source code is provided to you solely for
;    the purpose of assisting you in your development of OS/2 WARP device
;    drivers. You may use this code in accordance with the IBM License
;    Agreement provided in the IBM Device Driver Source Kit for OS/2. This
;    Copyright statement may not be removed.;
;*****************************************************************************/
        page    ,132
;/*****************************************************************************
;*
;* SOURCE FILE NAME = MATH.ASM
;*
;* DESCRIPTIVE NAME = math routines
;*
;*
;* VERSION      V2.0
;*
;* DATE         08/30/87
;*
;* DESCRIPTION  Contains math routines for 64- by 32-bit division.
;*              
;* FUNCTIONS    ulNormalize,
;*              far_qdiv,
;*              qdiv
;*              
;* NOTES        NONE
;*
;* STRUCTURES   NONE
;*
;* EXTERNAL REFERENCES
;*
;*              NONE
;*
;* EXTERNAL FUNCTIONS
;*
;*              NONE
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG        APAR   CHANGE DESCRIPTION
;*   --------  ----------  -----  --------------------------------------
;*   mm/dd/yy  @Vr.mpppxx  xxxxx  xxxxxxx
;*   08/30/87                     Author: Charles Whitmer [chuckwh]
;*   09/14/88                     Kirk Olynyk [kirko]
;*                                Added header stuff for PostScript.
;*
;*   10/04/88                     Bob Grudem [bobgru]
;*                                Stole from graphics engine.  Only the 
;*                                minimum required was taken, to solve a
;*                                problem drawing polylines on large    
;*                                devices.                              
;*
;*****************************************************************************/

        .286p
        .xlist
        include cmacros.inc
        include pmgre.inc
        include driver.inc
        include assert.mac
        include njmp.mac

        .list

UQUAD   struc
uq0     dw      ?
uq1     dw      ?
uq2     dw      ?
uq3     dw      ?
UQUAD   ends


sBegin  FarCode
        assumes cs,FarCode
        assumes ds,nothing


;/***************************************************************************
;*
;* FUNCTION NAME = ulNormalize
;*
;* DESCRIPTION   = 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.
;*
;* INPUT         = DX:AX = ULONG 
;* OUTPUT        = DX:AX = normalized ULONG
;*                 CX    = shift count
;*                 ZF    = 1 if the ULONG is zero, 0 otherwise
;*
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        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
        mov     dh,dl
        mov     dl,ah
        mov     ah,al
        xor     al,al
        add     cl,8
        or      dh,dh
        js      ulNormalize_exit
top_byte_ok:

;/*
;** do the rest by bits
;*/

        inc     cx
        add     ax,ax
        adc     dx,dx
        js      ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
        js      ulNormalize_exit
        inc     cx
        add     ax,ax
        adc     dx,dx
ulNormalize_exit:
cEnd

;/***************************************************************************
;*
;* FUNCTION NAME = qdiv
;*
;* DESCRIPTION   = 
;*
;*    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.
;*
;* INPUT         = DX:CX:BX:AX = UQUAD Numerator
;*                 SI:DI       = ULONG Denominator
;* OUTPUT        = DX:AX = quotient  
;*                 CX:BX = remainder 
;* RETURN-NORMAL = NONE
;* RETURN-ERROR  = NONE
;*
;**************************************************************************/

        assumes ds,nothing
        assumes es,nothing

cProc   far_qdiv,<PUBLIC,FAR,NODATA,NONWIN>
cBegin
        call    qdiv
cEnd

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 overflow
;*/

qdiv_restart:
        cmp     si,dx
        ja      qdiv_no_overflow
        jb      qdiv_overflow
        cmp     di,cx
        ja      qdiv_no_overflow
qdiv_overflow:
ifdef WIMP
        mov     ah,80h
        dec     ah
        jmp     qdiv_exit
else
        int     0                                 
        jmp     qdiv_restart
endif
qdiv_no_overflow:

;/*
;** 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     qdiv_not_that_special

;/*
;** calculate (DX=0):CX:BX:uqNumerator.uq0 / (SI=0):DI
;*/

        cmp     di,1                              ; separate out the trivial case
        jz      qdiv_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)
;*/

qdiv_by_one:
        xchg    dx,bx                             ; DX = quotient.hi, BX = remainder.lo = 0
        mov     ax,uqNumerator.uq0                ; AX = quotient.lo
        jmp     qdiv_exit
qdiv_not_that_special:

;/*
;** handle the special case when the denominator lives in the high word
;*/

        or      di,di
        jnz     qdiv_not_this_special_either

;/*
;** calculate DX:CX:BX:uqNumerator.uq0 / SI:(DI=0)
;*/

        cmp     si,1                              ; separate out the trivial case
        jz      qdiv_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)
;*/

qdiv_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
qdiv_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    qdiv_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
qdiv_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     qdiv_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 qdiv_unshift_remainder
qdiv_must_do_long_division:

;/*
;** do the long division, part I
;*/

        cmp     dx,cx                             ; we only know that DX:AX < CX:BX!
        jb      qdiv_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 qdiv_first_adjuster
qdiv_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     qdiv_first_adjuster_done          ; The remainder is UNSIGNED!  We have
qdiv_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     qdiv_first_adjuster               ; change IF we had more bits to keep
qdiv_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      qdiv_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 qdiv_second_adjuster
qdiv_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     qdiv_second_adjuster_done
qdiv_second_adjuster:
        dec     ulQuotient.lo
        add     di,bx
        adc     si,cx
        jnc     qdiv_second_adjuster
qdiv_second_adjuster_done:
        mov     ax,ulQuotient.lo
        mov     dx,ulQuotient.hi

;/*
;** unshift the remainder in SI:DI
;*/

qdiv_unshift_remainder:
        mov     cx,cShift
        jcxz    qdiv_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
qdiv_remainder_unshifted:
        mov     cx,si
        mov     bx,di
ifdef WIMP

        or      ax,ax                            ; clear OF
endif
qdiv_exit:
cEnd

sEnd    FarCode

        end
