; sqrt.asm
comment ^

        This sample program uses 486 specific features (but not the
        numeric coprocessor) to calculate the integral portion of a
        square root of a 32-bit positive integer.

        written on Sat  09-16-1995  by Ed Beroset
        and released to the public domain by the author
^
        IDEAL
        MODEL small
        p486

        STACK   400h

        DATASEG
alpha   dd      4145943340       ; a large arbitrary number
result  dw      ?                ; the answer should be about 64389 

        CODESEG
proc  main
        STARTUPCODE                     ;
        mov     edi,[alpha]             ; load 'em up
        call    sqrt                    ; call our sqrt function
        mov     [result],bx             ; demonstrate how to save result
        EXITCODE                        ; bail
endp  main

;/***************************************************************************
;
;   Name:
;       sqrt
;
;   Purpose:
;       Calculate the integral portion of the square root of a
;       positive integer.
;
;   Algorithm:
;
;       The way this is done is rather simple.  First, we use the BSR
;       instruction to scan for the bit number of the most significant
;       bit.  The result of this operation is the log (base 2) of our
;       original.  We use this information to generate a good first
;       guess for our algorithm, since 2**((log[2](X))/2) = sqrt(X).
;       That is, if we halve the log of a number and raise the result to
;       the base of our log, we get the square root of the original.
;
;       Once we have that number, we know that it must be the most
;       significant digit in our answer.  Since this is true, we can
;       successively approximate the answer by "guessing" each of the
;       bits and testing by squaring the guess.
;
;       This is not meant to be an optimal solution, but more of an
;       intellectual curiosity.  If I were actually programming a sqrt
;       function on a 486 class CPU, I'd use FSQRT and be done with it.
;       I haven't actually timed them both yet; totalling execution
;       (e.g. data sheet) timings can lead to incorrect conclusions.
;
;   Entry:
;
;       EDI = the number whose square root we're seeking
;
;   Register usage within the routine:
;
;        AX = scratch register (for multiplications)
;        BX = current guess
;        CX = current bit number
;        DX = scratch register (for multiplications)
;
;   Exit:
;
;        AX = calculated square root
;       EDX = square of number in AX
;       EDI = target value
;
;   Trashed:
;       BX, CX
;
;***************************************************************************/
proc sqrt
        bsr     ecx,edi                 ; get the log (base 2)
        shr     cx,1                    ; log2(alpha)/2
        xor     bx,bx                   ; bx=0
        inc     cx                      ;
top:
        dec     cx                      ;
        bts     ebx,ecx                 ; set bit in our answer
        mov     ax,bx                   ; save copy in ax
        mul     bx                      ; DX:AX = BX*BX
        shl     edx,16                  ; convert DX:AX to EDX
        mov     dx,ax                   ;
        cmp     edx,edi                 ; Q: is test < answer?
        jz      done                    ;   if equal, we're done
        jb      over                    ;   N: yes, so leave bit set
        btc     bx,cx                   ;   Y: too big, clear bit
over:
        inc     cx                      ; to make life a little easier
        loop    top                     ; keep going for more
done:
        ret
endp sqrt

        END
