'===========================================================================
' Subject: BINARY SEARCH                      Date: 02-06-97 (00:50)       
'  Author: Brian Mclaughlin                   Code: ASM                    
'  Origin: comp.lang.basic.misc             Packet: ASMCODE.ABC
'===========================================================================
; BSEARCH.ASM - assemble with MASM 5.1 or compatible assembler
;             - for QB4.x / PDS only
;             - written by Brian McLaughlin. Released to public domain.
;
; This FUNCTION accepts an integer value to search for in a sorted array,
; along with the address of the first element and the total number of
; elements in the array. It returns the number of the element where the
; value was found (assuming a 1-based array). If the value is not found,
; it returns the element (as a negative number) where the value would be 
; inserted into the array to preserve its sorted order.  
;
; For example: given a search value of 105, if element 10 contains 100 and 
; element 11 contained 110, BSearch would return -11.
;
; BSearch will give valid results ONLY for an array of integers, that is
; sorted in ascending order (lowest to highest), and has no duplicate
; values.
;
; DECLARE FUNCTION BSearch% (FindValue%, SEG Array%, TotalElements%)
;
; Example:
;         LookFor% = 666   'see if any elements of TestArray%() = 666
;         FirstElem% = LBOUND(TestArray%)
;         TotalElem% = UBOUND(TestArray%) - FirstElem% + 1
;         Result% = BSearch%(LookFor%, TestArray%(FirstElem%), TotalElem%)
;
;         IF Result% > 0 THEN
;           Result% = Result% + FirstElem% - 1
;           PRINT "Found "; LookFor%; " in element "; Result%
;         END IF

.MODEL MEDIUM, BASIC
.CODE
    Last DW 0

BSearch PROC USES SI DI, Value:WORD, Array:DWORD, Elems:WORD
    Mov BX, Value      ; point BX at Value
    Mov AX, [BX]       ; AX = value to be searched for
    Mov BX, Elems      ; point BX at Elems
    Mov DX, [BX]       ; DX = total number of elements
    Inc DX             ; adding one allows last element to be searched
    Mov DI, DX         ; DI = last element in search area
    Xor SI, SI         ; SI = first element in search area
    Mov Last, DX       ; Last = most recent element searched
    Lds CX, Array      ; point DS:CX at first element of array
    Cmp DX, 3          ; see if Elems was 0 or 1 (DX was inc'ed)
    Jb  ZeroOrOne      ; if so, deal with it as a special case
Search:
    Mov BX, DI         ; DI = top element of "search area"
    Add BX, SI         ; BX points to element halfway between top/bottom
    Add BX, CX         ; add offset of first element within DS
    And BL, 0FEh       ; this is needed whenever DI = SI+1
    Mov DX, [BX]       ; DX = the value at current "halfway" element
    Sub BX, CX         ; readjust BX "back" by amount of offset in CX
    Shr BX, 1          ; and then adjust from offset to index value
    Cmp BX, Last       ; was this element searched once before?
    Je  NotFound       ; if so, we've run out of search area
    Cmp DX, AX         ; does this element match Value?
    Je  Found          ; if it does, we've found our element!
    Mov Last, BX       ; remember we looked here already
    Cmp DX, AX         ; now compare again
    Jg  TopDown        ; if DX was > value, adjust top down
    Mov SI, BX         ; otherwise adjust bottom up
    Jmp SHORT Search   ; and keep on looking
TopDown:
    Mov DI, BX         ; adjust the top down
    Jmp SHORT Search   ; and continue looking

NotFound:
    Or  BX, BX         ; find out if BX = 0
    Jz  BXZero         ; if so, we must go decide what that means
    Neg BX             ; return a negative number
    Sub BX, 2          ; adjust 0-based to 1-based and el below to el above
    Jmp SHORT Exit
Found:
    Inc BX             ; adjust 0-based to 1-based return value
Exit:
    Mov AX, BX         ; put return value into AX
    Push SS
    Pop DS
    Ret

ZeroOrOne:
    Mov BX, -1         ; assume a return value of -1
    Dec DX             ; readjust DX to value in Elems
    Or  DX, DX         ; see if Elems = 0
    Jz  Exit           ; if so, exit with return value of -1
    Mov SI, CX         ; points DS:SI at first (and only) element
    Mov CX, [SI]       ; CX = value of only element
    Cmp AX, CX         ; how does it compare to search value?
    Jl  Exit           ; if it's below, exit with -1
    Dec BX             ; assume a return value of -2
    Cmp AX, CX         ; compare again
    Jg  Exit           ; exit with -2
    Mov BX, 1          ; or else, search value equals first element
    Jmp SHORT Exit     ; so return with 1
BXZero:
    Dec BX             ; search value is <> first element, assume it's <
    Cmp DX, AX         ; now, see which it is
    Jg  Exit           ; if it's "<" return -1
    Dec BX             ; otherwise value is ">" so,
    Jmp SHORT Exit     ; return -2
BSearch ENDP
    END
