Maker Pro
Maker Pro

AVR-ASM Set/Clear Bit Routines

Fish4Fun

So long, and Thanks for all the Fish!
Toggling bits is pretty straight forward with an 8-bit AVR with a priori knowledge of the the particular bit to be toggled, especially if the bit to be toggled is in the address range of $20 to $3F (Registers $00 to $1F) ...

Code:
     sbi   PORTA, 0     ; 1 Set PORTA.0
     cbi   PORTA, 1     ; 1 Clear PORTA.1
;------------------------------------------
;    1 Clock Each

Still assuming a priori knowledge of the particular bit, but extending the scope to some random data space byte one might do the following:

Code:
;Assumes Y Points to Data Space Byte where bit.0 needs to be cleared .... 
;Temp = r16
;All Registers Preserved
DS_ClearBit_0:          ; 5 clks for call
     push  Temp         ; 2     
     ld    Temp, Y      ; 2
     andi  Temp, $FE    ; 1
     st    Y, Temp      ; 2
     pop   Temp         ; 2
     ret                ; 5 clks for return
;------------------------------
;                        19 including Call & Return
DS_SetBit_0:          ; 5 clks for call
     push  Temp         ; 2     
     ld    Temp, Y      ; 2
     ori   Temp, 1      ; 1
     st    Y, Temp      ; 2
     pop   Temp         ; 2
     ret                ; 5 clks for return
;------------------------------
;                        19 including Call & Return

With out a priori knowledge about the particular bit, there would need to be separate routines for bits 0-7 for both the set-bit and clear-bit functions ... one approach might be to create a jump table based on the bit provided ,,, following is the expansion of the ClearBit routines

Code:
;Assumes Bit to be cleared is in Temp = r16
;Assumes Y points to Dataspace byte
;Preserves Z
;Temp is Trashed
;66 Instructions
DS_ClearBit_X:                            ; 5 for Call
     push  ZH                             ; 2
     push  ZL                             ; 2
     andi  Temp, 7                        ; 1 limit bits to 0-7
     lsl   Temp                           ; 1 Multiply Temp by 2
     ldi   ZH, High(2*DS_ClearBit_X_JT)   ; 1
     ldi   ZL, Low(2*DS_ClearBit_X_JT)    ; 1
     add   ZL, Temp                       ; 1
     clr   Temp                           ; 1
     adc   ZH, Temp                       ; 1
     ijmp                                 ; 2
;-----------------------------------------------
;                                          16
  DS_ClearBit_X_JT:
     jmp   DS_ClearBit_0                  ; 3 + 16
     jmp   DS_ClearBit_1                  ; 3 + 16
     jmp   DS_ClearBit_2                  ; 3 + 16
     jmp   DS_ClearBit_3                  ; 3 + 16
     jmp   DS_ClearBit_4                  ; 3 + 16
     jmp   DS_ClearBit_5                  ; 3 + 16
     jmp   DS_ClearBit_6                  ; 3 + 16
     jmp   DS_ClearBit_7                  ; 3 + 16
     
DS_ClearBit_0:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $FE    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return
DS_ClearBit_1:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $FD    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return
DS_ClearBit_2:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $FB    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return
DS_ClearBit_3:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $F7    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return
DS_ClearBit_4:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $EF    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return
DS_ClearBit_5:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $DF    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return
DS_ClearBit_6:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $BF    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return
DS_ClearBit_7:          ;19 to Here
     ld    Temp, Y      ; 2
     andi  Temp, $7F    ; 1
     st    Y, Temp      ; 2
     pop   ZL           ; 2
     pop   ZH           ;
     ret                ; 5 clks for return
;------------------------------
;                        31 including Call & Return

A more compact approach replaces the separate routines and jump table with a data table, and trashes Z to reduce the clock count to 23:

Code:
    ; Zero = r15 Initialized to 0
    ; Temp = r16
    ; Temp = Bit Number 0-7
    ; Y Address Points to DataSpace Byte to Set Bit **Left Unchanged**
    ; Z Trashed
    ; Temp Trashed
    ;Instruction Count = 10 
    ; 8 Data Table Bytes
DS_ClearBit_X:                                  ; 5 for Call
          andi     Temp, 7                      ; 1 Temp = XXXX XXXX ==> 0000 0XXX
          ldi      ZH, High(2 * InsD9_DT)       ; 1 Get Data Table Address high byte
          ldi      ZL,  Low(2 * InsD9_DT)       ; 1 Get Data Table Address Low byte
          add      ZL, Temp                     ; 1
          adc      ZH, Zero                     ; 1
          lpm      Temp, Z                      ; 3
          ld       ZL, Y                        ; 2 Get Value @ Address
          and      ZL, Temp                     ; 1
          st       Y, ZL                        ; 2
          ret                                   ; 5
;***********************************************************************************
          ;                                       23 Including Call and Return
          ;
InsD9_DT:
.db $FE, $FD, $FB, $F7, $EF, $DF, $BF, $7F

Allowing Z to be trashed in the routine saves a tremendous amount of overhead in cases where numerous bits need to be set/cleared within a single routine .... the calling routine could preserve Z once and then make numerous bit manipulations before restoring Z.

Obviously the DS_SetBit_X routine is virtually identical replacing |AND| with |OR| and using a different data table....

If anyone has a more compact // lower clock count routine for general 8-Bit AVR Data Space bit manipulation please share!

Thanks in Advance!

Fish
 
Top