Quickest way to get highest set bit on port in to a variable?


Closed Thread
Results 1 to 29 of 29

Hybrid View

  1. #1
    Join Date
    Aug 2006
    Location
    Look, behind you.
    Posts
    2,818


    Did you find this post helpful? Yes | No

    Default Re: Quickest way to get highest set bit on port in to a variable?

    Lookdown2 makes 2x code over lookup, have you tested to confirm? Thanks
    If you do not believe in MAGIC, Consider how currency has value simply by printing it, and is then traded for real assets.
    .
    Gold is the money of kings, silver is the money of gentlemen, barter is the money of peasants - but debt is the money of slaves
    .
    There simply is no "Happy Spam" If you do it you will disappear from this forum.

  2. #2
    Join Date
    Aug 2003
    Posts
    985


    Did you find this post helpful? Yes | No

    Default Re: Quickest way to get highest set bit on port in to a variable?

    Best I can think of is 3 assembler instructions, but more time than 3 cycles of course.
    Copy the port value to a byte, count how many times you shift the byte right until the byte is zero.

    If those times are for a single value,
    I think it would be better than either example to check each bit in a verbose fashion still in the compiler.
    Code:
    bytevar var byte ‘ buffer byte for port
    bitnum var byte ‘ output bit number that was set
    
    bytevar = porta
    
    
    btfsc		_bytevar		,07
    goto labelone
    btfsc		_bytevar		,06
    goto labeltwo
    btfsc		_bytevar		,05
    
    ......

  3. #3
    Join Date
    Aug 2003
    Posts
    985


    Did you find this post helpful? Yes | No

    Default Re: Quickest way to get highest set bit on port in to a variable?

    Or rotate left until carry is set:
    Code:
    bitcount var byte
    bytebuff var byte
    
    NCD8:
    bytebuff = portX
    clrf	_bitcount
    bcf	status	,C
    findbit:
    incf	_bitcount
    rrf	_bytebuff
    btfss	status	,C
    goto findbit
    return
    It’s been a while for asm, and might not work straight up, but enough to get the idea.
    findbit: is currently an endless loop if the port is zero, so you’d need to check for zero at the beginning.

  4. #4
    Join Date
    Aug 2003
    Posts
    985


    Did you find this post helpful? Yes | No

    Default Re: Quickest way to get highest set bit on port in to a variable?

    Ok,
    In the listing he’s rotating left. I would think that would result in quicker times for the lower values.
    I’m pretty sure there’s an error in my asm, as the example uses a different instruction to rotate with carry.
    This is the same thing in BASIC but rotating right so the higher values should be found faster.
    I’d be interested to enter a race with it.. just not sure some lines are converted the way I’d want them to.

    Code:
    workword var word
    workbyte var workword.byte0 ‘ 1st byte
    bitcarry var workword.8 ‘ 9th bit
    bitcount var byte
    
    
    NCD8:
    bitcount = 8 ‘ mov l to w & w to f
    workbyte = portX ‘ mov l to w & w to f
    
    if workbyte = 0 then ‘ portX was zero
    goto whatawaste
    endif
    
    inloop:
    bitcount = bitcount - 1 ‘ decf
    workword = workword << 1 ‘ 2 x rrf with carry
    
    if bitcarry = 0 then ‘ btfss
    goto inloop ‘ goto
    endif
    return
    
    whatawaste:
    bitcount = 0 ‘ bsf
    return
    Last edited by Art; - 8th March 2015 at 17:24.

  5. #5
    Join Date
    Jan 2013
    Location
    Texas USA
    Posts
    229


    Did you find this post helpful? Yes | No

    Default Re: Quickest way to get highest set bit on port in to a variable?

    Short of resorting to inline ASM and potentially having to deal with bank issues, etc., I put together the suggestions here on this thread and ran the execution tests.
    I captured the results in the table below.

    Name:  BitTestTable.png
Views: 983
Size:  14.2 KB

    Here is the code that I used to test with.

    Code:
    '****************************************************************
    '*  Name    : NCDTest.pbp                                       *
    '*  Author  : TABSoft                                           *
    '*  Notice  : Copyright (c) 2015 TABSoft                        *
    '*          : All Rights Reserved                               *
    '*  Date    : 3/3/2015                                          *
    '*  Version : 1.0                                               *
    '*  Notes   :                                                   *
    '*          :                                                   *
    '****************************************************************
    
    '*****PIC MCU Configuration Fuses (MPASM)*****
    #IF __PROCESSOR__ = "18F4620"
        #CONFIG
            CONFIG OSC = ECIO6            ; EC oscillator, port function on RA6
            ;CONFIG OSC = INTIO67	      ; Internal oscillator block, port function on RA6 and RA7
            ;CONFIG  WDT = OFF             ; WDT disabled (control is placed on the SWDTEN bit)
            CONFIG  FCMEN = OFF           ; Fail-Safe Clock Monitor disabled
            CONFIG  IESO = OFF            ; Oscillator Switchover mode disabled
            CONFIG  PWRT = OFF            ; PWRT disabled
            CONFIG  BOREN = SBORDIS       ; Brown-out Reset enabled in hardware only (SBOREN is disabled)
            CONFIG  BORV = 3              ; Minimum setting
            CONFIG  WDT = ON              ; WDT enabled
            CONFIG  WDTPS = 512           ; 1:512
            CONFIG  CCP2MX = PORTC        ; CCP2 input/output is multiplexed with RC1
            CONFIG  PBADEN = OFF          ; PORTB<4:0> pins are configured as digital I/O on Reset
            CONFIG  LPT1OSC = OFF         ; Timer1 configured for higher power operation
            CONFIG  MCLRE = ON            ; MCLR pin enabled; RE3 input pin disabled
            CONFIG  STVREN = ON           ; Stack full/underflow will cause Reset
            CONFIG  LVP = OFF             ; Single-Supply ICSP disabled
            CONFIG  XINST = OFF           ; Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
            CONFIG  DEBUG = OFF           ; Background debugger disabled, RB6 and RB7 configured as general purpose I/O pins
            CONFIG  CP0 = OFF             ; Block 0 (000800-003FFFh) not code-protected
            CONFIG  CP1 = OFF             ; Block 1 (004000-007FFFh) not code-protected
            CONFIG  CP2 = OFF             ; Block 2 (008000-00BFFFh) not code-protected
            CONFIG  CP3 = OFF             ; Block 3 (00C000-00FFFFh) not code-protected
            CONFIG  CPB = OFF             ; Boot block (000000-0007FFh) not code-protected
            CONFIG  CPD = OFF             ; Data EEPROM not code-protected
            CONFIG  WRT0 = OFF            ; Block 0 (000800-003FFFh) not write-protected
            CONFIG  WRT1 = OFF            ; Block 1 (004000-007FFFh) not write-protected
            CONFIG  WRT2 = OFF            ; Block 2 (008000-00BFFFh) not write-protected
            CONFIG  WRT3 = OFF            ; Block 3 (00C000-00FFFFh) not write-protected
            CONFIG  WRTC = OFF            ; Configuration registers (300000-3000FFh) not write-protected
            CONFIG  WRTB = OFF            ; Boot Block (000000-0007FFh) not write-protected
            CONFIG  WRTD = OFF            ; Data EEPROM not write-protected
            CONFIG  EBTR0 = OFF           ; Block 0 (000800-003FFFh) not protected from table reads executed in other blocks
            CONFIG  EBTR1 = OFF           ; Block 1 (004000-007FFFh) not protected from table reads executed in other blocks
            CONFIG  EBTR2 = OFF           ; Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks
            CONFIG  EBTR3 = OFF           ; Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks
            CONFIG  EBTRB = OFF           ; Boot Block (000000-0007FFh) not protected from table reads executed in other blocks
    
        #ENDCONFIG
    #else
        #ERROR "This program requires a PIC 18F4620 MCU"
    #endif
    
    OSCCON = $60  ' Set PIC to 4Mhz & ECIO Clock Mode
    
     DEFINE OSC 4
     ADCON0.0 = 0  ' A/D Converter module is disabled   
       ADCON1 = $0F  ' %0000 1111 AN2=VSS, AN3=VDD, AN12-0 = Digital 
       ADCON2 = $00  ' %0000 0000
        TRISB = %11111111 ' Set PORTB as input
    INTCON2.7 = 0 ' Enable PORTB pullups
        TRISC = TRISC & %11011111   ' Set PORTC pin directions (pin5 output to RTC VCC2)
    
    i var byte
    mybyte var byte
    mystatus var byte
    workword var word
    workbyte var workword.byte0 ' 1st byte
    bitcarry var workword.8 ' 9th bit
    bitcount var byte
    bytArray var byte[9]
    
    bytArray[0] = 0
    bytArray[1] = 1
    bytArray[2] = 2
    bytArray[3] = 4
    bytArray[4] = 8
    bytArray[5] = 16
    bytArray[6] = 32
    bytArray[7] = 64
    bytArray[8] = 128
    
    '*****Define LCD registers and bits*****
    define LCD_BITS      4  
    define LCD_LINES     2  ' Set to number of lines for the LCD
    Define LCD_DREG      PORTD
    Define LCD_DBIT      4  
    Define LCD_RSREG     PORTE
    Define LCD_RSBIT     0
    Define LCD_EREG      PORTE
    Define LCD_EBIT      1
    define LCD_RWREG     PORTE
    define LCD_RWBIT     2
    define LCD_COMMANDUS 1500
    define LCD_DATAUS    44
    
    
    '*****Initialize LCD*****
       Low LATE.2  		    ' LCD R/W line low (W)
       Pause 500       	  ' Wait .5 second for LCD to Initialize
    
    
    
    
    mainloop:
        do
            for i = 0 to 8
    			workword.byte1 = 0
    			workbyte = bytArray[i]		
    			gosub NCD8
    		next i
            for i = 0 to 8
    			workword.byte1 = 0
    			workbyte = bytArray[i]		
    			gosub NEWNCD8
    		next i
            for i = 0 to 8
    			mystatus = bytArray[i]		
    			gosub checkbit8
    		next i
            for i = 0 to 8
    			mystatus = bytArray[i]		
    			gosub checkbit8ztest
    		next i
    		for i = 0 to 8        
    			mystatus = bytArray[i]
    			gosub NCDSUB
            next i
    		for i = 0 to 8
    			mystatus = bytArray[i]
    			gosub LOOKSUB
            next i
    		pause 500
                
        loop
    
    
    NCD8:
    ' Input: workword (including workbyte)
    'Return: bitcount
    'Locals: bitcarry
    ' Notes: If workbyte = 0 then bitcount is set to 0
    '       otherwise bitcount = bitnumber (0-7)
    
        bitcount = 8 ' mov l to w & w to f
    
        if workbyte = 0 then ' portX was zero
            bitcount = 0
            return
        endif
    
    inloop:
        bitcount = bitcount - 1 ' decf
        workword = workword << 1 ' 2 x rrf with carry
        
        if bitcarry = 0 then 'btfss
            goto inloop ' goto
        endif
    	
    return
    
    NEWNCD8:
    ' Input: workword (including workbyte)
    'Return: bitcount
    'Locals: bitcarry
    ' Notes: If workbyte = 0 then bitcount is set to 0
    '        otherwise bitcount = bitnumber (0-7)
    
    	if workbyte = 0 then 'portX was zero
    		bitcount = 0
    		return
    	endif
    	
    	bitcount = 8	
    
    	do
    		bitcount = bitcount - 1
    		workword = workword << 1
    	loop while bitcarry = 0
    
    	return
    			
    
    checkbit8:
    ' Input: mystatus
    'Return: bitcount
    'Locals: none (direct bittest of mystatus
    ' Notes: If mystatus = 0 then bitcount is not changed
    '        otherwise bitcount = bitnumber (0-7) (can be changed to any byte value)
    	   
           if mystatus.7 = 1 then
    	       bitcount = 7
    	       return
           endif
    	   if mystatus.6 = 1 then
    	       bitcount = 6
    	       return
           endif
    	   if mystatus.5 = 1 then
    	       bitcount = 5
    	       return
           endif
    	   if mystatus.4 = 1 then
    	       bitcount = 4
    	       return
           endif
    	   if mystatus.3 = 1 then
    	       bitcount = 3
    	       return
           endif
    	   if mystatus.2 = 1 then
    	       bitcount = 2
    	       return
           endif
    	   if mystatus.1 = 1 then
    	       bitcount = 1
    	       return
           endif
    	   if mystatus.0 = 1 then
    	       bitcount = 0
    	       return
           endif
    	
    	   return
    
    checkbit8ztest:
    ' Input: mystatus
    'Return: bitcount
    'Locals: none (direct bittest of mystatus)
    ' Notes: If mystatus = 0 then bitcount is not changed (can be changed to any byte value)
    '       otherwise bitcount = bitnumber (0-7) (can be changed to any byte value)
    	   
           if mystatus = 0 then
    			return
           endif			   
           if mystatus.7 = 1 then
    	       bitcount = 7
    	       return
           endif
    	   if mystatus.6 = 1 then
    	       bitcount = 6
    	       return
           endif
    	   if mystatus.5 = 1 then
    	       bitcount = 5
    	       return
           endif
    	   if mystatus.4 = 1 then
    	       bitcount = 4
    	       return
           endif
    	   if mystatus.3 = 1 then
    	       bitcount = 3
    	       return
           endif
    	   if mystatus.2 = 1 then
    	       bitcount = 2
    	       return
           endif
    	   if mystatus.1 = 1 then
    	       bitcount = 1
    	       return
           endif
    	   if mystatus.0 = 1 then
    	       bitcount = 0
    	       return
           endif
    	
    	   return
    
    
    NCDSUB:
    ' Input: mystatus
    'Return: mybyte
    'Locals: none (direct bittest of mystatus)
    ' Notes: If mystatus = 0 then mybyte is not changed
    '        otherwise mybyte = bitnumber (1-8) 
    
        mybyte = NCD mystatus
    	
        return
        
    LOOKSUB:
    ' Input: mystatus
    'Return: mybyte
    'Locals: none (direct bittest of mystatus)
    ' Notes: If mystatus = 0 then mybyte set to 0
    '        otherwise mybyte = bitnumber (1-8) 
    
        lookdown2 mystatus, <= [%00000000, %00000001, %00000011, %00000111, %00001111, %00011111, %00111111, %01111111, %11111111], mybyte
        
        return
    
    end
    Regards,
    TABSoft

  6. #6
    Join Date
    Aug 2003
    Posts
    985


    Did you find this post helpful? Yes | No

    Default Re: Quickest way to get highest set bit on port in to a variable?

    Lol well I’m not the only one up late & can’t sleep

    Still I’m sure rsocor01’s original snippet would beat them all,
    and the NCD command could be used to generate all the lookup table,
    just that it’s going to be impractical to use a 255/256 byte table for every purpose.
    Code:
    LOOKUP MyPort, [0,1,2,2,3,3,3,3,4,........], MyVar

  7. #7
    Join Date
    Jan 2013
    Location
    Texas USA
    Posts
    229


    Did you find this post helpful? Yes | No

    Default Re: Quickest way to get highest set bit on port in to a variable?

    You're right Art, the LOOKUP method is quicker, but not by much versus the direct bit test method (checkbit8).
    The real difference is that LOOKUP is a constant 27 instruction cycles since it is just a table lookup.
    The downside as you and others state is the storage space of the table of 256 entries.
    When you compile with the 256 entry LOOKUP table it consumes 556 codewords (973 bytes) of code space which is probably not worth the minor reduction in execution time.

    The direct bit test (checkbit8 routine) does have variability in the performance based upon the input value (0-255) but if you look at the Max/Min execution time it is
    (Max: 37 Min:10 Avg: 25) instruction cycles.

    So comparing the two, worst case is that checkbit8 would be 10 inst cycles slower and best case is that checkbit8 would be 17 inst cycles faster and on average across the full (0-255) range checkbit8 is 2 inst cycles faster.
    And checkbit8 only adds 116 codewords (203 bytes).

    For these reasons my choice would be checkbit8 over the other options, except maybe pure ASM for those who are inclined.
    Regards,
    TABSoft

Similar Threads

  1. Replies: 6
    Last Post: - 18th July 2012, 02:42
  2. Whats the quickest way to set bits?
    By bearpawz in forum mel PIC BASIC Pro
    Replies: 33
    Last Post: - 26th October 2010, 04:38
  3. Bit set question
    By nverma in forum mel PIC BASIC Pro
    Replies: 4
    Last Post: - 2nd April 2007, 21:23
  4. set flags within a variable?
    By peterdeco1 in forum mel PIC BASIC Pro
    Replies: 5
    Last Post: - 24th October 2006, 11:07
  5. How do I to set one bit in a register?
    By jessey in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 11th February 2006, 08:43

Members who have read this thread : 1

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts