'===========================================================================
' Subject: Used Records                       Date: 06-16-02 (  :  )       
'  Author: Don Schullian, Jr.                 Code: PB                     
'  Origin: d83@DASoftVSS.com                Packet: BINARY.ABC
'===========================================================================
#IF 0
    ----------------------------                           PowerBASIC
 ---|          DASoft          |------------------------------------------
    ----------------------------         Code           DATE: 2002-04-12
    | FILE NAME   Bit_Scan.bas |          by
    ----------------------------  Don Schullian, Jr.

              This code is released into the Public Domain
       ----------------------------------------------------------
        No guarantee as to the viability, accuracy, or safety of
         use of this code is implied, warranted, or guaranteed
       ----------------------------------------------------------
                         Use at your own risk!
       ----------------------------------------------------------
                  CONTACT AUTHOR AT d83@DASoftVSS.com
 -------------------------------------------------------------------------
       NOTE: As fBitScan compliments the BIT functions in PowerBASIC it
             would be advisable if you were to follow the same rules.

 PARAMETERS: Aptr     = pointer/address to the array to be scanned
             Bytes    = number of bytes to be scanned
             StartBit = bit to start scan
             TestVal  = value to be tested for (1 or 0)

    RETURNS: -1 if no match was found
             The bit number of the next requested value

      USAGE: I used this function, among other things, to keep track of
             empty records in a file. If the record is used/active then
             the bit for that record number is ON (1) else it is off.
             When I add a record I scann the array to see which record
             number is empty.
#ENDIF
'
'
'
FUNCTION fBITscan ALIAS "fBITscan" ( BYVAL Aptr     AS LONG, _
                                     BYVAL Bytes    AS LONG, _
                                     BYVAL StartBit AS LONG, _
                                     BYVAL TestVal  AS LONG  ) EXPORT AS LONG

  #REGISTER NONE

        ! push    esi             ; popped in Exet
        ! mov     esi, Aptr       ; array into ESI
        ! mov     ecx, Bytes      ; # of bytes into ECX
        ! mov     eax, StartBit   ; starting bit number
        ! mov     ebx, TestVal    ; load test value into ebx
        !                         ; BL = SCAN MASK FOR BIT SEARCH
        ! cmp     ebx, 0          ; if testing for OFF bit.....
        ! je      L1              ;  goto
        ! mov      bl, 0          ; BL = 0 if testing for ON bit
        ! jmp     L2              ;
  L1:                             '
        ! mov     bl, &hFF        ; BL = 255 if testing for OFF bit
  L2:                             ' COMPUTE START BIT OFFSET & MASK
        ! cmp     eax, 0          ; if start bit = 0 .....
        ! je      L6              ;  goto
        !                         ; COMPUTE FULL BYTES TO BE SKIPPED
        ! mov      dl, al         ;  DL = will test odd bits
        ! shr     eax, 3          ; EAX = full bytes from offset
        ! cmp     eax, ecx        ; if past end of array.....
        ! jge     L8              ;   goto
        ! sub     ecx, eax        ; reduce Bytes counter
        ! add     esi, eax        ; move pointer
        ! and      dl, &b00000111 ; EDX = odd bits
        ! cmp      dl, 0          ; if no odd bits.....
        ! je      L6              ;   goto
        ! lodsb                   ; load next byte
        ! cmp     al, bl          ; if byte has a winner....
        ! jne     L3              ;  goto
        ! dec     ecx             ; lope off one more byte counter
        ! jecxz   L8              ; if past end of array goto
        ! jmp     L6              ; scan remaining bytes
  L3:                             '
        ! push    ecx             ; >-------------------------------|
        ! mov      cl, dl         ; CL holds bit count to mask      |
        ! mov      dl, &b11111111 ; DL will become mask             |
        ! cmp      bl, 0          ; if search for ON bit            |
        ! je      L4              ;  goto                           |
        ! mov      ch, 8          ;                                 |
        ! sub      ch, cl         ;                                 |
        ! mov      cl, ch         ; CL = ( 8 - CL )                 |
        ! shl      dl, cl         ; shift off left most bits        |
        ! shr      dl, cl         ; move mask back into position    |
        ! or       al, dl         ; set odd bits to ON for OFF test |
        ! jmp     L5              ; send to pop & test              |
  L4:                             '                                 |
        ! shr      dl, cl         ; shift off right most bits       |
        ! shl      dl, cl         ; move mask back into position    |
        ! and      al, dl         ; set odd bits to OFF for ON test |
  L5:                             '                                 |
        ! pop     ecx             ; <-------------------------------|
        ! jmp     L7              ;  goto
        ! ;;;;;;;;;;;;;;;;;;;;;;;;;
  L6:                             ' WHILE ECX > 0
        ! lodsb                   ;  load byte into AL
  L7:                             '
        ! cmp     al, bl          ;  if byte <> test mask ....
        ! jne     L9              ;   goto
        ! loop    L6              ; WEND
        ! ;;;;;;;;;;;;;;;;;;;;;;;;;
  L8:                             ' BIT NOT FOUND .....
        ! mov     edx, -1         ; load negative return value
        ! jmp     Exet            ; goto
        ! ;;;;;;;;;;;;;;;;;;;;;;;;;
  L9:                             ' FINAL TEST
        ! mov     edx, Bytes      ; reload total bytes to be tested
        ! sub     edx, ecx        ; compute number of bytes tested
        ! shl     edx, 3          ; multiply by 8 (bits)
        ! cmp      bl, 0          ; if testing for ON bits
        ! je      LB              ;  goto
        ! ;;;;;;;;;;;;;;;;;;;;;;;;;
  LA:                             ' TEST FOR OFF BIT WITHIN THE BYTE
        ! shr      al, 1          ; shift off right most bit
        ! jnc     Exet            ; if the bit was ZERO goto
        ! inc     edx             ; bump bit position by one
        ! jmp     LA              ; loop
        ! ;;;;;;;;;;;;;;;;;;;;;;;;;
  LB:                             ' TEST FOR ON BIT WITHIN THE BYTE
        ! shr      al, 1          ; shift off right most bit
        ! jc      Exet            ; if bit was ONE goto
        ! inc     edx             ; bump bit position by one
        ! jmp     LB              ; loop
  Exet:                           '
        ! pop     esi             ; popped from 1sl line of code
        ! mov     FUNCTION, edx   ; RETURN value

END FUNCTION
'
'--------------------------------------------------------------------------
'This function is thrown in to demonstrate how one can write more friendly
'functions that use fBitScan.
'--------------------------------------------------------------------------
'
FUNCTION fBitScan_Long (       Arr()    AS LONG, _
                         BYVAL StartBit AS LONG, _
                         BYVAL TestVal  AS LONG ) AS LONG

  DIM L     AS LOCAL LONG
  DIM Bytes AS LOCAL LONG

  L     = LBOUND(Arr(1))
  Bytes = 4 * (1 + UBOUND(Arr(1)) - L)

  FUNCTION = fBitScan(BYREF Arr(L), Bytes, StartBit, TestVal)

END FUNCTION
'
'--------------------------------------------------------------------------
'Test Code for PBcc
'--------------------------------------------------------------------------
'
FUNCTION WINMAIN( BYVAL hInstance   AS LONG,       _
                  BYVAL hPrevInst   AS LONG,       _
                  BYVAL lpszCmdLine AS ASCIIZ PTR, _
                  BYVAL nCmdShow    AS LONG        ) EXPORT AS LONG

  DIM bArr(19) AS LOCAL BYTE
  DIM iArr(19) AS LOCAL INTEGER
  DIM lArr(19) AS LOCAL LONG
  DIM xArr(19) AS LOCAL WORD
  DIM X        AS LOCAL LONG

  RANDOMIZE TIMER                          '
                                           '
  BIT SET bArr(0), 30                      '
  X = fBitScan(BYREF bArr(0), 20, 0, 1)    ' the answer will be 30
  PRINT X                                  '
                                           '
  BIT SET iArr(0), 50                      '
  X = fBitScan(BYREF iArr(0), 40, 10, 1)   ' returns 50 but started
  PRINT X                                  ' search at 10
                                           '
  BIT SET lArr(0), 100                     '
  X = fBitScan_Long(lArr(), 0, 1)          ' returns 100
  PRINT X                                  '
                                           '
  X = fBitScan(BYREF xArr(0), 40, 0, 1)    ' no bits set: returns -1
  PRINT X                                  '
  PRINT                                    '
                                           '
  FOR X = 1 TO 10                          ' here we'll set 10 bits
    BIT SET xArr(0), RND(0,319)            ' at random in the array
  NEXT                                     '
                                           '
  X = -1                                   ' start with bit 0
  DO                                       ' find ON bits
    INCR X                                 '  skip last found bit
    X = fBitScan(BYREF xArr(0), 40, X, 1)  '  get next ON bit
    IF X = -1 THEN EXIT LOOP               '  all done!
    PRINT FORMAT$(X, "000 ");              '  print found bit number
  LOOP                                     '
  PRINT                                    '

  WAITKEY$

END FUNCTION
