;*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.;
;*****************************************************************************/
;/*****************************************************************************
;*
;* SOURCE FILE NAME = FONTXFRM.ASM
;*
;* DESCRIPTIVE NAME =
;*
;*
;* The following IBM OS/2 2.1 source code is provided to you
;* solely for the purpose of assisting you in your development of
;* OS/2 2.x 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.
;*
;* VERSION
;*
;* DATE
;*
;* DESCRIPTION
;*
;* FUNCTIONS
;*
;* NOTES
;*
;* STRUCTURES
;*
;* EXTERNAL REFERENCES
;*
;* EXTERNAL FUNCTIONS
;*
;* CHANGE ACTIVITY =
;*   DATE      FLAG          APAR     CHANGE DESCRIPTION
;*   --------  ----------    -----    --------------------------------------
;*   08-24-88  Greg Hitchcock  -      Copied it from the engine.
;*                                    next line added MJB
;*                                    and removed AK (PD00074)
;*                                    MODEL MEDIUM
;*
;*   05/09/91  A.Kilpatrick    - PD00081 : added far_get_sqr_determinant
;*                               function.
;*
;*   09/04/91 L. Cheatham     -  PD00146 : Added code to support device
;*                               outline fonts for heritage (4029) printer
;*
;*   04/28/92 L. Cheatham     -  PD00700 : Don't hard code high 2 bytes of
;*                               hdc to 100x in xform_metrics for call to
;*                               GREConvertWithMatrix call.
;*
;*   05/27/92 D. Howell       -  PD00712 : Remove xform_point routine
;*
;*   12/15/92 L. Cheatham     - PD00775 : Save and restore additional used
;*                              registers.
;*
;******************************************************************************/

.386p

INCL_NOCOMMON    EQU   1
        INCL_WINP              equ      1
        INCL_WINP_SELECTIVE    equ      1
        INCL_WINP_SEI          equ      1
        INCL_WINP_FSRS         equ      1
.xlist
INCLUDE 32cmacro.inc
?PLM = CC_SYSCALL
INCL_FONTFILEFORMAT     equ     1
INCL_GPITRANSFORMS      equ     1
GreConvertWithMatrix    equ     00000409Bh

INCLUDE os2.inc
INCLUDE pmddi.inc

INCLUDE firewall.inc
INCLUDE engine.inc
.list

if FIREWALLS
INCL_WINP               equ     1
INCL_WINP_SEI           equ     1
;;INCLUDE os2p.inc  removed for DDK requirements not to ship PMWINP.INC
INCLUDE pmwinx.inc

externP    <WinSetErrorInfo>,C
endif

externP    Gre32Entry6

TEMP STRUC
 LO  DW ?
 HI  DW ?
TEMP ENDS

CODE32      SEGMENT Dword USE32 PUBLIC 'CODE'
            ASSUME CS:FLAT,SS:FLAT,DS:FLAT,ES:FLAT

if FIREWALLS

;---------------------------Public-Routine------------------------------;
; perform_rip
;
; The given error message is passed to WinSetErrorInfo with the
; abort flag set
;
; Entry:
;       None
; Returns:
;       None
; Error Returns:
;       None
; Registers Destroyed:
;       AX,BX,CX,DX,ES
; History:
;       Sun 17-Jan-1988 17:19:07 -by-  Walt Moore [waltm]
;       Wrote it.
;-----------------------------------------------------------------------;

ErrorMsg        msg_Overflow,<Overflow flag should not be set>

SEIOptions = SEI_REGISTERS+SEI_STACKTRACE

cProc   perform_rip,<PUBLIC,NODATA,NONWIN>
        parmw   npError
        localV  errorid,%(size TEMP)
cBegin
        mov     word ptr errorid.lo, 1
        mov     word ptr errorid.hi, SEVERITY_SEVERE
        cCall   WinSetErrorInfo,<errorid,SEIOptions,npError>,<C>
cEnd


endif   ; FIREWALLS

;---------------------------Internal-Routine----------------------------;
;   WORD FAR PASCAL   xform_metrics (hdc, lpMetrics, xfm)
;
;   Transforms the font metrics
;
;   Entry:
;       hdc
;       lpMetrics  target text metrics to be scaled
;       xfm        Conversion matrix
;
;   Note:
;       All sizes are returned in world coordinates
;
;   Returns:
;         CF = 0,
;
;   Error Returns:
;         CF = 1
;-----------------------------------------------------------------------;


POINT_COUNT1        equ (foca_sCharSlope-foca_yEmHeight)/2
POINT_COUNT2        equ (foca_usKerningPairs-foca_ySubscriptXSize)/2
POINTS_IN_METRICS   equ (POINT_COUNT1+POINT_COUNT2)


;--------------------------------------------------------------
; PD00775 : Save bx, cx, ,es ,ds registers
;--------------------------------------------------------------
cProc   xform_metrics,<PUBLIC,NONWIN>,<esi,edi,ebx,ecx,es,ds>
        parmD   hdc
        parmD   lpMetrics
        parmD   xfm
        localD  hddc
        localD  myCount
        localD  lppts
        localV  ptMetrics,%(size POINTL * (POINTS_IN_METRICS+1))
cBegin
        cld

; zero out the array of metrics, the first point will be 0,0

        lea     edi,ptMetrics

        mov   ecx,(size POINTL * (POINTS_IN_METRICS+1)) / 2
        xor     eax,eax
        rep     stosw

; move back to the second point in the array

        lea     edi,ptMetrics.size POINTL

; move the heights and widths into the point array so that we can xform
; them. The information in the metrics consists of
;   eight   ys
;   three   xs
;   one     y
;   four    points
;   two     ys

        mov     esi,lpMetrics
        add     esi,fm_lEmHeight

; move the first eight ys

        .errnz  ((foca_yExternalLeading-foca_yEmHeight)/2)-7
        mov   ecx,((foca_yExternalLeading-foca_yEmHeight) / 2) + 1
move_eight_ys:
        lodsw
        mov     [edi].ptl_y.lo,ax
        inc     esi
        inc     esi
        add     edi,size POINTL
        loop    move_eight_ys

; now move the three xs

        mov     ecx,((foca_xEmInc-foca_xAveCharWidth) / 2) + 1
move_three_xs:
        lodsw
        mov     [edi].ptl_x.lo,ax
        inc     esi
        inc     esi
        add     edi,size POINTL
        loop    move_three_xs

; move one more y

        lodsw
        mov     [edi].ptl_y.lo,ax
        add     edi,size POINTL

        add     esi,2+foca_ySubscriptXSize-foca_sCharSlope

        .errnz  ((foca_xEmInc-foca_xAveCharWidth)/2)-2

; now we have to move four points

        mov     ecx,((foca_ySuperscriptXOffset-foca_ySubscriptXSize)/(size POINTS))+1
move_four_points:
        lodsw
        inc     esi
        inc     esi
        mov     [edi].ptl_x.lo,ax
        add     edi,size POINTL
        lodsw
        inc     esi
        inc     esi
        mov     [edi].ptl_y.lo,ax
        add     edi,size POINTL
        loop    move_four_points     ; zeros cx

        mov     ecx,((foca_yStrikeoutPosition-foca_yUnderscoreSize)/2)+1

move_four_more_ys:

        lodsw
        inc     esi
        inc     esi
        mov     [edi].ptl_y.lo,ax
        add     edi,size POINTL
        loop    move_four_more_ys

; back to the begining

        lea     edi,ptMetrics
        assert  cx,E,0
        mov     hddc,ecx
        mov     myCount.hi,cx
        mov     myCount.lo,POINTS_IN_METRICS+1
        mov     lppts,edi
        cCall   Gre32Entry6,<hdc,lppts,myCount,xfm,hddc,GreConvertWithMatrix>

        or      eax,eax
        jnz     not_error_xform_metrics

        stc
        jmp     exit_xform_metrics              ; MJB change
                                                ; used to be jmp short

; zero base the points before moving them back

not_error_xform_metrics:

        mov     ecx,POINTS_IN_METRICS
        mov     eax,dword ptr [edi].ptl_x        ; pick up the original x,y
   ;;   mov     dx,word ptr ss:[di].ptl_x.hi
        mov     ebx,dword ptr [edi].ptl_y
   ;;   mov     si,word ptr ss:[di].ptl_y.hi

zero_base_points:
        add     edi,SIZE POINTL
        sub     [edi].ptl_x,eax
   ;;   sbb     ss:[di].ptl_x.hi,dx
        sub     [edi].ptl_y,ebx
   ;;   sbb     ss:[di].ptl_y.hi,si
        loop    zero_base_points

; ok they are converted so now move them back to the metrics structure


        mov     edi,lpMetrics
        add     edi,fm_lEmHeight

        lea     esi,ptMetrics.size POINTL

        .errnz  fm_lXHeight-fm_lEmHeight-4
        .errnz  fm_lMaxAscender-fm_lXHeight-4
        .errnz  fm_lMaxDescender-fm_lMaxAscender-4
        .errnz  fm_lLowerCaseAscent-fm_lMaxDescender-4
        .errnz  fm_lLowerCaseDescent-fm_lLowerCaseAscent-4
        .errnz  fm_lInternalLeading-fm_lLowerCaseDescent-4
        .errnz  fm_lExternalLeading-fm_lInternalLeading-4
        .errnz  fm_lAveCharWidth-fm_lExternalLeading-4
        .errnz  fm_lMaxCharInc-fm_lAveCharWidth-4
        .errnz  fm_lEmInc-fm_lMaxCharInc-4
        .errnz  fm_lMaxBaselineExt-fm_lEmInc-4

        mov     ecx,((fm_lMaxBaselineExt-fm_lEmHeight)/4)+1
        assert  cx,E,12

store_up_to_fm_lMaxBaselineExt:
        cCall   get_vector_length
        stosd
        add     esi,size POINTL
        loop    store_up_to_fm_lMaxBaselineExt

        .errnz  fm_sCharSlope-fm_lMaxBaselineExt-4

        add     edi,fm_lSubscriptXSize-fm_sCharSlope

        .errnz  fm_lSubscriptYSize-fm_lSubscriptXSize-4
        .errnz  fm_lSubscriptXOffset-fm_lSubscriptYSize-4
        .errnz  fm_lSubscriptYOffset-fm_lSubscriptXOffset-4
        .errnz  fm_lSuperscriptXSize-fm_lSubscriptYOffset-4
        .errnz  fm_lSuperscriptYSize-fm_lSuperscriptXSize-4
        .errnz  fm_lSuperscriptXOffset-fm_lSuperscriptYSize-4
        .errnz  fm_lSuperscriptYOffset-fm_lSuperscriptXOffset-4
        .errnz  fm_lUnderscoreSize-fm_lSuperscriptYOffset-4
        .errnz  fm_lUnderscorePosition-fm_lUnderscoreSize-4
        .errnz  fm_lStrikeoutSize-fm_lUnderscorePosition-4
        .errnz  fm_lStrikeoutPosition-fm_lStrikeoutSize-4

        mov     ecx,((fm_lStrikeoutPosition-fm_lSubscriptXSize)/4)+1
        assert  cx,E,12

store_up_to_fm_lStrikeoutPosition:
        cCall   get_vector_length
        stosd
        add     esi,size POINTL
        loop    store_up_to_fm_lStrikeoutPosition
        clc

exit_xform_metrics:

cEnd

;---------------------------Public-Routine------------------------------;
; DWORD get_vector_length()
;
; finds the length of a vector in an array of vectors
;
; Entry:
;   esi --> point
;
; Returns:
;   eax --> the length of the point
; Error:
;   OF set
; History:
;  Mon 28-Mar-1988 17:17:13  -by-  Paul Klingler [paulk]
;-----------------------------------------------------------------------;

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


;--------------------------------------------------------------
; PD00775 : Save cx register
;--------------------------------------------------------------
cProc   get_vector_length,<PUBLIC,NONWIN>,<ebx,ecx,es>
        LocalQ  qSum                    ; UNSIGNED since squares are +
cBegin

        mov     eax,[esi].ptl_x
        mov     ebx,eax
        imul    ebx                              ;; edx:eax = x^2
        mov     dword ptr qSum.uq0,eax
        mov     dword ptr qSum.uq2,edx
        mov     eax,[esi].ptl_y
        mov     ebx,eax
        imul    ebx                              ;; edx:eax = y^2
        add     eax,dword ptr qSum.uq0
        adc     edx,dword ptr qSum.uq2
        call    big_fix_square_root     ; DX:AX = sqrt(DX:CX:BX:AX)
                                        ;;new eax = sqrt(edx:eax)
cEnd

;---------------------------Public-Routine------------------------------;
; DWORD far_get_vector_length()
;
; finds the length of a vector in an array of vectors
;
; MJB modified to a FAR proc.
;
; Entry:
;   lpPoint - vector to get the length of
;
; Returns:
;   dx:ax --> the length of the point
; Error:
;   OF set
; History:
;  Mon 28-Mar-1988 17:17:13  -by-  Paul Klingler [paulk]
;-----------------------------------------------------------------------;

;--------------------------------------------------------------
; PD00775 : Save bx,si,di,ds registers
;--------------------------------------------------------------
cProc   far_get_vector_length,<PUBLIC,NONWIN>,<ebx,ecx,es,ds,esi,edi>      ;;;
        parmD   lpPoint
        LocalQ  qSum                    ; UNSIGNED since squares are +
cBegin

    ;;  les     bx,lpPoint              ; MJB
    ;;  push    bx                      ; get lpPoint into esi
    ;;  pop     si                      ; as it is in get_vector_length
        mov     esi,lpPoint

        mov     eax,[esi].ptl_x
        mov     ebx,eax
        imul    ebx                     ; edx:eax = x^2
        mov     dword ptr qSum.uq0,eax
        mov     dword ptr qSum.uq2,edx
        mov     eax,[esi].ptl_y
        mov     ebx,eax
        imul    ebx                      ; edx:eax = y^2
        add     eax,dword ptr qSum.uq0
        adc     edx,dword ptr qSum.uq2   ; we will have no overflow (+)
        call    big_fix_square_root      ; EAX = sqrt(EDX:EAX)

cEnd


;---------------------------Public-Routine------------------------------;
; DWORD far_get_sqr_determinant()
;
; PD00081 : added new function
; finds the square root of the determinant of a m11,m12,m21,m22
; assumes +ve determinant
;
; Entry:
;   m11, m12, m21, m22 (all FIXED)
;
; Returns:
;   dx:ax --> sqr( m11 * m22 - m12 * m21 )
; Error:
;   OF set
; History:
;  Thur 9-May-1991 -by-  Andrew Kilpatrick
;-----------------------------------------------------------------------;

;--------------------------------------------------------------
; PD00775 : Save bx, si, di, ds registers
;--------------------------------------------------------------
cProc   far_get_sqr_determinant,<PUBLIC,NONWIN>,<ebx,ecx,es,ds,esi,edi>      ;;;
        parmD   m11
        parmD   m12
        parmD   m21
        parmD   m22
        LocalQ  qSum                    ; SIGNED ?
cBegin
        mov     eax, m12
        mov     ebx, m21
        imul    ebx                       ; edx:eax = m12*m21
        mov     dword ptr qSum.uq0,eax
        mov     dword ptr qSum.uq2,edx
        mov     eax,m11
        mov     ebx,m22
        imul    ebx                       ; dx:cx:bx:ax = m11*m22

        sub     eax,dword ptr qSum.uq0
        sbb     edx,dword ptr qSum.uq2    ; we will have no overflow (-)

        ;-------------------------------------------------------------
        ;PD00146, PD00148, PD00216
        ;Added new code to support negative determinants
        ;-------------------------------------------------------------
        jge     not_negative
        ; The determinant is negative, make it positive.
        not     edx
        not     eax
        inc     eax
        adc     edx, 0
not_negative:
        ;-------------------------------------------------------------
        ;PD00146, PD00148, PD00216
        ;End of new code to support negative determinants
        ;-------------------------------------------------------------

        call    big_fix_square_root     ; eAX = sqrt(eDX:eAX)

cEnd


;-------------------------------Public-Routine---------------------------;
; big_fix_square_root
;
;   Approximates the square root of an unsigned BIGFIXED
;
; Entry:
;     EDX:EAX = unsigned BIGFIXED
; Returns:
;     EAX = square root
; Error Returns:
;     none
; Registers Destroyed:
;     BX,CX
; Calls:
;     none
; History:
;  Fri 29-Jul-1988 09:18:55 -by-  Lee A. Newberg [leen]
; Rounds answer to the nearest 0.0001h.  Fractions of 0.00008h or greater
; are rounded up.
;
;  Sun 06-Mar-1988            -by-   Martin Picha        [martinpi]
; Wrote it.  (pulled out of solve_quadratic)
;------------------------------------------------------------------------;

cProc big_fix_square_root,<PUBLIC,NONWIN>,<ebx>
        localQ  tempq
cBegin
           mov     dword ptr tempq.uq0, eax
           mov     dword ptr tempq.uq2, edx
           xor     ebx,ebx
        ;; 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, dword ptr tempq.uq1
        shr     eax,2
        jmp     SHORT call_sqrt
convert_1616:
        ;; use 16.16 representation of number
        cmp     eax, 0h
        jle     SHORT convert_3232neg
        cmp     eax, 7fffffffh
        jle     SHORT convert_3232
convert_3232neg:
        mov     eax, dword ptr tempq.uq1
        jmp     SHORT call_sqrt
convert_3232:
        cmp    edx,0h
        jnz    convert_3232neg
        mov    eax, dword ptr tempq.uq0
        mov    ebx, 1h
call_sqrt:
        push    edx
        cCall   square_root
        pop     edx

        cmp     ebx, 1
        jnz     SHORT no_shift_3232
        shr     eax, 8
        jmp     fin
        ;; 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

no_shift_3232:
        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------------------------------;
; uqNormalize
;
; Normalizes a UQUAD (unsigned quadword) so that the highest order bit
; is 1.  Returns only the top 32 bits of the normalized UQUAD.  Returns
; the number of shifts done.  Retuns ZF=1 if the UQUAD was zero.
;
; Entry:
;       eDX:eAX = UQUAD
; Returns:
;       eAX = normalized top 32 bits
;       CX    = shift count
;       ZF    = 1 if the UQUAD is zero, 0 otherwise
; Registers Destroyed:
;       BX
; History:
;  Mon 25-Jan-1988 22:07:03  -by-  Charles Whitmer [chuckwh]
; Wrote it.
;-----------------------------------------------------------------------;


cProc   uqNormalize,<PUBLIC,NONWIN>,<esi,edi>
cBegin
        sub     ecx,ecx
        or      edx,edx                 ; Test if hi-dword is zero
        jnz     SHORT uqN_need_normalize      ; non-zero, test only hi-dword
        mov     edx,eax                 ; zero, test if lo-dword is zero
        or      edx,edx
        jnz     SHORT @F
        jmp     SHORT uqNormalize_exit        ; the zero exit
@@:
        mov     ECX,32                  ; pretend that it was shifted by 32

uqN_need_normalize:

        bsr     ebx,edx                 ; scan for the first 1 bit

        neg     EBX
        add     EBX,31
        xchg    ECX,EBX
        shld    edx,eax,cl              ; double shift cl times
        add     ECX,EBX
        mov     EAX,EDX
        or      EAX,EAX                 ; zero flag is set if the
                                        ;   top 32 bits are now zero
                                        ;   (only occurs when the input
                                        ;    UQUAD == 1)
        jnz     SHORT uqNormalize_exit

uqNormalize_exit:
                                        ; return value is in EAX
cEnd


;---------------------------Public-Routine------------------------------;
; square_root
;
; Computes the Fixed square root of a Fixed using Newton's method.
;
; This routine will occasionally give a different result from
; the old_square_root routine.  This is caused by a very small lack of
; precission.  For instance, the square root of 1.0001h is approximately
; 1.00007FFh.  This routine calculates it to be 1.0000800.  Hence when
; we round it off and put it in a fixed point we get 1.0001 instead
; of 1.0000.
;
; When firewalls are set this routine is called by another routine
; which borrows this routine's name.
;
; Entry:
;       eAX = number to square root
; Returns:
;       eAX = square root of input
;       Zero flag set if the output (and the input) are zero.
; Error Returns:
;       OF = 1, eAX = 0       if input is negative
; Registers Destroyed:
;       BX,CX
; Registers Preserved:
;       DS,ES,SI,DI
; History:
;  Tue 02-Aug-1988 12:50:30 -by-  Lee A. Newberg [leen]
; Wrote it.
;-----------------------------------------------------------------------;


;--------------------------------------------------------------
; PD00775 : Save bx and cx registers
;--------------------------------------------------------------
cProc   square_root,<PUBLIC,NEAR,NONWIN>,<esi,edi,ebx,ecx>
         localD dwnumber
cBegin
        mov    dwnumber,eax
;;; Computes the square root of a fixed point integer using a
;;; variation of newtons method.
;;;     Totally accurate for
;;;     16.16 fixed integers.
        ;;
        ;; verify that dwNumber exists in Z+
        ;;
        or      eax, eax
        jns     @f
        xor     eax, eax                ; if dwNumber < 0, return 0
        jmp     square_root_exit
@@:
        cmp     eax, 0
        jne     @f
        jmp     square_root_exit        ; if dwNumber = 0, return 0
@@:
        cmp     eax, 1
        jne     @f
        mov     eax, 0100h              ; if dwNumber = 1, return 0100h
        jmp     square_root_exit
@@:
        ;;
        ;; at this point the value in EAX exists in Z+
        ;;
        ;; The following variation on newtons algorithm
        ;; will be used:
        ;;
        ;;      F (n+1) := ((square/F(n) + F(n)) / 2
        ;;
        ;; This calculation will be carried out iteratively
        ;; 8 times.  This should ensure a fairly high level
        ;; of accuracy.
        ;;
        shr     eax, 1          ;initial guess = dwNumber / 2
        mov     ebx, eax        ;EBX = F(n)

        mov     ecx, 8                  ;; set loop counter

next_guess:
        xor     edx, edx
        mov     eax, dwNumber   ;convert dwNumber to 32:32 in EDX:EAX
        shld    edx, eax, 16
        shl     eax, 16

        div     ebx             ; EAX = dwNumber(32:32) / Fn(16:16)
        add     eax, ebx        ; EAX = Sq/Fn + Fn
        shr     eax, 1          ; EAX = (Sq/Fn + Fn) / 2
        cmp     ebx, eax        ; are they the same
        je      square_root_exit
        mov     ebx, eax        ; EBX = F(n+1)

        dec     ecx             ; iterate
        jnz     next_guess

square_root_exit:
                                ;*; result in EAX
cEnd    ;;; end of 32math-square root

;---------------------------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:
;       eAX = ULONG
; Returns:
;       eAX = 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.
;-----------------------------------------------------------------------;


cProc   ulNormalize,<PUBLIC,NONWIN>
cBegin
     xor       ecx,ecx
     or        eax,eax
     jnz       No_Zero
     jmp       ulNormalize_exit
No_Zero:
     bsr       ecx,eax       ; scan right for the first set bit
     neg       ecx
     add       ecx,31
     shl       eax,cl
ulNormalize_exit:
cEnd

CODE32  ENDS
        END

