;*DDK*************************************************************************/
;
; 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
;---------------------------Module-Header-------------------------------;
; 32MATH.ASM
;
; Contains FIXED point math routines. All the routines are from
; MATH.ASM or CFRONTS.ASM, but modified with Intel's 32-bit instructions.
;
;
;-----------------------------------------------------------------------;

;-----------------------------------------------------------------------;
; WARNING!!!!!
; Many of these routines have been converted to C Set/2 _Optlink
;   calling conventions for speed. Care must be taken in modifying
;   any of these routines, and they must be changed if the _Optlink
;   conventions ever change!
;-----------------------------------------------------------------------;

        .xlist

; Condition/Error flags returned by certain functions in math.asm:
PLOT_NUM_IS_ZERO          equ           1
PLOT_NUM_IS_NEGATIVE      equ           2
PLOT_OVERFLOW             equ           4

        include 32cmacro.inc

        .list


LONG    struc
lo      dw      ?
hi      dw      ?
LONG    ends


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


QUAD      struc
q0               dw      ?       ;*; lowest-order word
q1               dw      ?       ;*; low DWord
q2               dw      ?
q3               dw      ?
QUAD      ends

QUAD_0_12_3      struc
q12q0            dw      ?       ;*; low DWord
q12              dd      ?
q12q3            dw      ?
QUAD_0_12_3      ends

QUAD_01_23      struc
q01              dd      ?       ;*; low DWord
q23              dd      ?
QUAD_01_23      ends


?PLM = CC_SYSCALL       ; // K&I (_stdcall -> _syscall because called from C)

FLATSEGDEF

.DATA


staticD SRootConst,< 00000135h > ;  0
                  DD 000001b5h   ;  1
                  DD 0000026ah   ;  2
                  DD 0000036ah   ;  3
                  DD 000004d4h   ;  4
                  DD 000006d4h   ;  5
                  DD 00000f50h   ;  6
                  DD 00000da8h   ;  7
                  DD 00001350h   ;  8
                  DD 00001b50h   ;  9
                  DD 000026a0h   ; 10
                  DD 000036a0h   ; 11
                  DD 00004d41h   ; 12
                  DD 00006d41h   ; 13
                  DD 00009a82h   ; 14
                  DD 0000da82h   ; 15
                  DD 00000135h   ; 16
                  DD 000001b5h   ; 17
                  DD 0000026ah   ; 18
                  DD 0000036ah   ; 19
                  DD 000004d4h   ; 20
                  DD 000006d4h   ; 21
                  DD 000009a8h   ; 22
                  DD 00000da8h   ; 23
                  DD 00001350h   ; 24
                  DD 00001b60h   ; 25
                  DD 000026a0h   ; 26
                  DD 000036a0h   ; 27
                  DD 00004d41h   ; 28
                  DD 00006d41h   ; 29
                  DD 00009a82h   ; 30
                  DD 0000da82h   ; 31

;.CODE **K&I** These 2 lines fix linker fixupp bugs. KRL 6-Mar-92
;      **K&I** The     bites when caller and callee are cross segment.
;      **K&I** and the caller is ASM and the callee is C.

; PAGE_TUNE
; Change 'CODE32' to 'SEG35' for new tuned code.
SEG35  SEGMENT DWORD USE32 PUBLIC 'CODE'
      ASSUME  CS:FLAT









;---------------------------Public-Routine------------------------------;
; FIXED FX_FXMulFX(FIXED, FIXED)
;
; Stub entry to assembly function fxmultiply().
;
; Multiplies two 32 bit fixed point numbers.  FIXED is a signed 32 bit
; number with 16 bits of integer and 16 bits of fraction.
;
; Entry:
;       fxX = FIXED x
;       fxY = FIXED y
; Returns:
;       EAX = FIXED x*y
; Error Returns:
;       EAX = 7FFF:FFFF
; History:
;-----------------------------------------------------------------------;

if 0
cProc FX_FXMulFX,<PUBLIC>
        parmD   fxFixed1
        parmD   fxFixed2

        localQ  qTemp
        localD  ulOFlag
cBegin
        ADR     qTemp
        ADR     ulOFlag

        cCall   fxmultiply,<@qTemp,fxFixed1,fxFixed2,@ulOFlag>
endif
        PUBLIC  FX_FXMulFX
FX_FXMulFX proc near
        sub     esp,12
        push    esp
        push    edx
        push    eax
        lea     eax,[esp+16]            ; 3 stack entries plus next
        push    eax
        call    fxmultiply
        add     esp,16
if 0
        cCall   fxmultiply,<eax,fxFixed1,fxFixed2,ecx>

        mov     ECX,ulOFlag
endif
        mov     ECX,[esp]
        jecxz   SHORT cmul_returnval
        mov     EAX,07FFFFFFFh          ; overflow, return 7FFF:FFFF
        jmp     SHORT cmul_exit
cmul_returnval:
if 0
        mov     EAX,qTemp.q12           ;*; return middle DWord: 16.16
endif
        mov     EAX,[esp+6]
cmul_exit:

if 0
cEnd
endif
        add     esp,12
        ret
FX_FXMulFX      endp


;---------------------------Public-Routine------------------------------;
; VOID FXToFXQuad(FXQUAD *pUQ, FIXED fxVal);
;
; Convert a fixedlong value to a fixedquad.
;
; History:
;   Mon 08-Apr-1991 19:27:56  -by-
; Wrote it.
;-----------------------------------------------------------------------;

if 0
cProc FXToFXQuad,<PUBLIC>
        parmDP  pfxQ
        parmD   fxVal
cBegin
        mov     EDX,pfxQ
        mov     EAX,fxVal
endif
        PUBLIC  FXToFXQuad
FXToFXQuad proc near
        xchg    eax,edx
        sub     ecx,ecx

        shld    ECX,EAX,16
        shl     EAX,16
        mov     [EDX].q23,ECX
        mov     [EDX].q01,EAX
if 0
cEnd
endif
        ret
FXToFXQuad      endp


;-------------------------------Public-Routine---------------------------;
; quad_square_root        FIXED quad_square_root(UQUAD *puQuad);
; formerly:
;  big_fix_square_root_386
;
;   Approximates the square root of an unsigned BIGFIXED
;
; Entry:
;     EDX:EAX = unsigned QUAD
; Returns:
;     EAX = square root
; Error Returns:
;     none
; Calls:
;     none
; History:
;------------------------------------------------------------------------;

cProc quad_square_root,<PUBLIC>,<EBX,ESI,EDI>
        parmDP  puQuad

        localD  dwNumber
        localD  dwOFlag

cBegin

        ;; get quad into EDX
        mov     esi, puQuad
        mov     edx, [esi].q23

        ;; if the quad has no significant bits in the high word
        ;; of EDX then convert to a 16.16 fixed

        cmp     edx, 00007FFFh  ; handle numbers upto 7FFF which will
        jle     SHORT convert_1616    ;  remain +ve after converting to 16.16
        cmp     edx, 0000FFFFh  ; handle numbers 8000->FFFF which could
        jle     SHORT convert_1616_ng ;  be -ve after converting to 16.16

        ;; the integer portion of the quad is greater than 65536.
        ;; this makes the fractional portion inconsequential, so
        ;; only use the integer

        mov     eax, edx
        jmp     SHORT call_sqrt

convert_1616_ng:
        ;; use 16.16 representation of number, but since it will
        ;; appear to be -ve, divide by 4 to make it +ve. Then multiply
        ;; sqrt by 2 later. The data lost will be inconsequential

        mov     eax, [esi].q12
        shr     eax,2
        jmp     SHORT call_sqrt

convert_1616:
        ;; use 16.16 representation of number

        mov     eax, [esi].q12

call_sqrt:
        mov     dwNumber, eax
        ;;;ADR     <dwOFlag>

        ;;;cCall   square_root,<dwNumber, @dwOFlag>
        push    edx
        cCall   SRoot,<dwNumber>
        pop     edx

        ;; Check to see if we are using High DWord in the Quad
        ;; if we are then we need to adjust to a 16:16 fixed
        ;; return value - MLC/MRC

        cmp     edx, 0000FFFFh
        jle     SHORT skip_shift
        shl     eax, 8
        jmp     SHORT fin

skip_shift:
        ;; Multiply the sqrt by 2 if we divided by 4 originally
        cmp     edx, 00007FFFh
        jle     SHORT fin
        shl     eax,1

fin:
        ;; at this point EAX contains our result
        ;;
        ;; end of routine
cEnd


;---------------------------Public-Routine------------------------------;
; LONG L_LDivFX(LONG, FIXED)
;
; Computes the SIGNED LONG quotient of a SIGNED LONG and a SIGNED FIXED.
;
; Entry:
;       lNumerator    = SIGNED LONG numerator
;       fxDenominator = SIGNED FIXED denominator
; Returns:
;       EAX = SIGNED LONG quotient
; Error Returns:
;       EAX = 7FFF:FFFF
; History:
;-----------------------------------------------------------------------;


cProc   L_LDivFX,<PUBLIC>
        parmD   lNumerator
        parmD   fxDenominator

        localQ  q
        localD  ulRemainder
        localD  lOFlag
cBegin
        ADR     ulRemainder
        ADR     lOFlag
        ADR     q

        mov     EAX,lNumerator          ; EDX:EAX = QUAD Numerator
        cdq
        shld    EDX,EAX,16
        shl     EAX,16
        mov     q.q01,EAX
        mov     q.q23,EDX
        cCall   iq_div,<@q,fxDenominator,@ulRemainder,@lOFlag>
        jno     SHORT ciqdiv_exit
        mov     EAX,07FFFFFFFh
ciqdiv_exit:
        ;*; answer is in EAX after iqdiv call.
cEnd

if 0
        PUBLIC  L_LDivFX
L_LDivFX proc   near
        sub     esp,16
        lea     ecx,[esp+4]
        push    esp
        push    ecx
        push    edx
        cdq
        shld    edx,eax,16
        shl     eax,16
        add     ecx,4
        mov     [ecx].q01,eax
        mov     [ecx].q23,edx
        push    ecx
        call    iq_div
        jno     SHORT ciqdiv_exit
        mov     eax,07fffffffh
ciqdiv_exit:
        add     esp,32
        ret
L_LDivFX        endp
endif


;---------------------------Public-Routine------------------------------;
; LFLAG _stdcall AddFXQuad(FXQUAD *pFXQ1, FXQUAD *pFXQ1, FXQUAD *pFXQ3);
; returns PLOT_OVERFLOW if set, else 0.
;
; History:
;-----------------------------------------------------------------------;
if 0
cProc AddFXQuad,<PUBLIC>,<EBX>
        parmDP  pFXQ1
        parmDP  pFXQ2
        parmDP  pFXQ3
cBegin
        mov     ECX,pFXQ1
        mov     EDX,pFXQ2

        mov     EBX,[ECX].q01
        mov     ECX,[ECX].q23

        mov     EAX,PLOT_OVERFLOW

        add     EBX,[EDX].q01
        adc     ECX,[EDX].q23
        jo      SHORT @F

        sub     EAX,PLOT_OVERFLOW
@@:
        mov     EDX,pFXQ3
        mov     [EDX].q01,EBX
        mov     [EDX].q23,ECX
cEnd
endif
        PUBLIC  AddFXQuad
AddFXQuad proc  near
        push    ebx
        mov     ebx,[eax].q23
        mov     eax,[eax].q01
        add     eax,[edx].q01
        adc     ebx,[edx].q23
        mov     [ecx].q01,eax
        mov     [ecx].q23,ebx
        mov     eax,PLOT_OVERFLOW
        jo      short   @F
        sub     eax,eax
@@:
        pop     ebx
        ret
AddFXQuad       endp



;;;
;;; FIXED fxmultiply(QUAD *pQuad, LONG x, LONG y, LONG *plOFlag);
;;;
;;; Multiplies two 32 bit fixed point numbers.  FIXED is a signed 32 bit
;;; number with 16 bits of integer and 16 bits of fraction.
;;;
;;;     RETURNS:  fixed format integer in EAX, or NULL if
;;;               PLOT_OVERFLOW occurred.  The QUAD value
;;;               will be unaffected
;;;
;;; Set *pulOFlag to PLOT_OK if no overflow, else to PLOT_OVERFLOW.
;;; Returns middle DWORD of QUAD result.
;;;
;;; history
;;; ------------------------------------------------------------------------
;;; ------------------------------------------------------------------------
;;;
EAX_SIGNED      equ     1h
EDX_SIGNED      equ     2h

cProc fxmultiply,<PUBLIC>,<EBX,ESI>
        parmDP  pQuad
        parmD   x
        parmD   y
        parmDP  pOFlag
cBegin
        mov     EAX,x
        imul    y

fxmul_done:

        ;;
        ;; The top 17 bits of EDX:EAX must be the same
        ;; for no overflow.
        ;; Move the result to the proper registers
        ;;
        mov     ebx,edx                 ; Result from EDX:EAX
                                        ;          to EBX.EAX
        mov     esi, pOFlag
        mov     dword ptr [esi], 0

        and     ebx, 0FFFF8000h
        jz      SHORT no_oflow
        cmp     ebx, 0FFFF8000h
        jz      SHORT no_oflow

        mov     dword ptr [esi], PLOT_OVERFLOW

no_oflow:
        ;mov     edi,pQuad
        ;mov     [edi].q01,eax
        ;mov     [edi].q23,edx
        ;mov     eax,[edi].q12

        mov     EBX,pQuad
        mov     [EBX].q01,eax
        mov     [EBX].q23,edx
        mov     eax,[EBX].q12
cEnd


;---------------------------Public-Routine-----------------------------------;
;
;  ULONG PLOT_ASMCALL SRoot(ULONG ulVal)
;
;  Return the square root of the given positive 32-bit value.
;
;  History:
;
;----------------------------------------------------------------------------;

;cProc SRoot,<PUBLIC>,<EBX,ECX,EDX,ESI,EDI>
cProc SRoot,<PUBLIC>,<EBX,ESI,EDI>
        parmD   ulVal
cBegin
        mov     EAX,ulVal
        mov     ESI,ulVal
        mov     EDI,ulVal

        or      EAX,EAX
        jnz     SHORT @F

        jmp     SHORT SRoot_finis   ; return 0

@@:
        bsr     EBX,EAX                ; search position starts at
                                       ;  index (operandsize - 1)
        mov     EBX,SRootConst[EBX*4]

        cmp     EAX,10000h
        mov     ECX,EAX
        jge     SHORT SR_GE_1_compute

        ; sqx         = ulVal = passed-in parameter
        ; sqx       <<= 16;
        ; sqrtx_small = SRootConst[]
        ; 
        ; sqrtx_small = ((sqx / sqrtx_small) + sqrtx_small) >> 1;
        ; sqrtx_small = ((sqx / sqrtx_small) + sqrtx_small) >> 1;
        ; sqrtx_small = ((sqx / sqrtx_small) + sqrtx_small) >> 1;
        ; return((LONG)sqrtx_small);

        mov     ECX,EAX   ;  sqx
        shl     EAX,10h
        mov     ECX,EAX

        ;       sqrtx_small = ((sqx / sqrtx_small) + sqrtx_small) >> 1;
        sub     EDX,EDX
        div     EBX       ;  sqrtx_small
        add     EAX,EBX   ;  sqrtx_small
        shr     EAX,01H
        mov     EBX,EAX   ;  sqrtx_small

        ;       sqrtx_small = ((sqx / sqrtx_small) + sqrtx_small) >> 1;
        mov     EAX,ECX   ;  sqx
        sub     EDX,EDX
        div     EBX       ;  sqrtx_small
        add     EAX,EBX   ;  sqrtx_small
        shr     EAX,01H
        mov     EBX,EAX   ;  sqrtx_small

        ;       sqrtx_small = ((sqx / sqrtx_small) + sqrtx_small) >> 1;
        mov     EAX,ECX   ;  sqx
        sub     EDX,EDX
        div     EBX       ;  sqrtx_small
        add     EAX,EBX   ;  sqrtx_small
        shr     EAX,01H

        jmp     SHORT SRoot_finis


SR_GE_1_compute:

        ; sqx   = ulVal = passed-in parameter
        ; sqrtx = SRootConst[]
        ; 
        ; sqrtx = ((sqx / sqrtx) + sqrtx) >> 1;
        ; sqrtx = ((sqx / sqrtx) + sqrtx) >> 1;
        ; y = ulVal - (sqrtx * sqrtx);
        ; sqrtx = (sqrtx << 8) + (((y << 12) / sqrtx) >> 5);
        ; return(sqrtx);

        ;       sqrtx = ((sqx / sqrtx) + sqrtx) >> 1;

        cdq
        idiv    EBX     ;  sqrtx
        add     EAX,EBX ;  sqrtx
        sar     EAX,1
        mov     EBX,EAX ;  sqrtx

        ;       sqrtx = ((sqx / sqrtx) + sqrtx) >> 1;
        mov     EAX,ECX ;  sqx
        cdq
        idiv    EBX     ;  sqrtx
        add     EAX,EBX ;  sqrtx
        sar     EAX,1
        mov     EBX,EAX ;  sqrtx

        ;       y = ulVal - (sqrtx * sqrtx);
        imul    EAX,EBX ;  sqrtx
        sub     ECX,EAX
        mov     EAX,ECX ;  y

        ;       sqrtx = (sqrtx << 8) + (((y << 12) / sqrtx) >> 5);
        shl     EAX,12
        cdq
        idiv    EBX     ;    sqrtx

        sar     EAX,5
        shl     EBX,8
        add     EAX,EBX

SRoot_finis:
cEnd


;;;
;;; iq_div
;;;
;;; This routine just keeps track of the signs and calls idiv to do the
;;; real work.
;;;
;;; Entry:
;;;       EDX:EAX = QUAD Numerator
;;;       EBX     = LONG Denominator
;;; Returns:
;;;       EAX = quotient
;;;       EDX = remainder
;;;       OF flag
;;; History:
;;;
IQ_DIV_RESULT_SIGN       equ     1
IQ_DIV_REM_SIGN          equ     2

cProc iq_div,<PUBLIC>,<EBX,ESI>
        parmDP  pQuad
        parmD   ulDenominator
        parmDP  pRemainder
        parmDP  pOFlag
cBegin
        mov     EAX,pQuad
        mov     EDX,dword ptr [EAX].q2
        mov     EAX,dword ptr [EAX].q0
        mov     EBX,ulDenominator
        mov     ESI,pOFlag

        ; CX will store bits indicating if the quotient and remainder need to be
        ; negated.

        sub     ecx,ecx
        mov     DWORD PTR [ESI],ecx

        ; take the absolute value of the denominator

        or      ebx,ebx
        jns     SHORT denominator_is_cool
        xor     cl,IQ_DIV_RESULT_SIGN
        neg     ebx

        ; EBX is now unsigned so possible overflow is unimportant.

denominator_is_cool:

        ; take the absolute value of the numerator

        or      edx,edx
        jns     SHORT numerator_is_cool
        xor     cl,IQ_DIV_RESULT_SIGN + IQ_DIV_REM_SIGN
        not     edx
        neg     eax
        sbb     edx,-1

        ; EDX:EAX is now unsigned so possible overflow is unimportant.

numerator_is_cool:

        ; do the unsigned division

        cmp     ebx,edx
        jbe     SHORT iqdiv_overflow
        div     ebx

        ; check for overflow

        or      edx,edx
        jns     SHORT have_a_bit_to_spare

iqdiv_overflow:
        mov     DWORD PTR [ESI],1
        mov     ah,80h
        dec     ah
        jmp     SHORT iqdiv_exit

have_a_bit_to_spare:
        ; negate the result, if required

        test    cl,IQ_DIV_RESULT_SIGN
        jz      SHORT result_is_done
        neg     eax

result_is_done:
        ; negate the remainder, if required

        test    cl,IQ_DIV_REM_SIGN
        jz      SHORT remainder_is_done
        neg     edx

        ; Can't overflow because this number is closer to zero than the
        ; denominator.

remainder_is_done:
        mov     ESI,pRemainder
        mov     [ESI],EDX
        ;*; EAX already holds quotient
iqdiv_exit:
                               ;*; return answer (quotient) in EAX
cEnd

; PAGE_TUNE
; End segment.
SEG35   ENDS
        END
