Search code examples
assemblyavrbitmaskattiny

AVR assembly - bit number to mask


In my ATtiny84a AVR Assembly program I end up with a bit number between 0 and 7, in a register, lets say r16. Now I need to create a mask with that bit number set. To make it more complicated, the timing of the operation must be the same, regardless of what bit is set.

For example if r16 = 5 the resulting mask will be 0x20 (bit 5 set).

So far I have shifted a bit into position by LSL and using r16 (the bit number) as a loop counter, then to keep exact timing regardless bit number, do a dummy loop of NOP 8-r16 times.

The assembly instruction SBR sets bit(s) in a register from a mask so it can't be used. The assembly instruction SBI sets a bit in an I/O register from bit number, but it is a constant, not a register (I could have used an I/O register as a temp register).

The mask is then used to clear a bit in a memory location, so if there is another solution to do that from a bit number in a register, then it's fine too.

I have another solution to try out (shift based with carry) but I was hoping that someone have a more elegant solution than loops and shiftings.


Solution

  • Thank you all for your creative answers, but I went with the lookup table as a macro. I find this being the most flexible solution because I can easily have different lookup tables for various purposes at a fixed 7 cycles.

    ; @0 mask table
    ; @1 bit register
    ; @2 result register
    .MACRO GetMask
        ldi     ZL,low(@0)
        ldi     ZH,high(@0)
        add     ZL,@1
        adc     ZH,ZERO
        lpm     @2,Z
    .ENDM
    
    bitmask_lookup:
        .DB 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80
    inverse_lookup:
        .DB ~0x01,~0x02,~0x04,~0x08,~0x10,~0x20,~0x40,~0x80
    lrl2_lookup:
        .DB 0x04,0x08,0x10,0x20,0x40,0x80,0x01,0x02
    
    ldi r16,2
    GetMask bitmask_lookup, r16, r1 ; gives r1 = 0b00000100
    GetMask inverse_lookup, r16, r2 ; gives r2 = 0b11111011
    GetMask lrl2_lookup,    r16, r3 ; gives r3 = 0b00010000 (left rotate by 2)
    

    Space is not so much of an issue, but speed is. However, I think this is a good compromise and I'm not forced to align data on quadwords. 7 vs 5 cycles is the price to pay.

    I already have one "ZERO" register reserved through the whole program so it costs me nothing extra to do the 16bit addition.