Search code examples
assemblybit-manipulationbitavr

AVR Assembler check is number odd


So we gotta make a simple program that gonna check out is number odd or even.

That is my first time with assembler. Many examples were about ARM and WASM, but its AVR is really pain.

start:
ldi r16, $0
ldi r17, $1 ; Load decimal 1 into r16
ldi r18, $2 ; Load decimal 2 into r17
ldi r19, $3 ; Load decimal 3 into r18
ldi r20, $4 ; Load decimal 4 into r19
ldi r21, $5 ; Load decimal 5 into r20

CLC                 ; clear carry flag
tst r21             ; check is r20 odd
BRHS    isodd       ; goto is odd
    MOV r10, r18    ; not odd
    JMP exit
isodd:
    MOV r10, r17    ; is odd
exit:
nop

Solution

  • AVR allows efficiently testing any single bit by copying it into the T flag with bst where you can branch on it. (Or copy it to another bit of another register with bld, as shown in the manual's example for bst.)

    The key instructions here:

       bst   r21, 0            ; T = (r21 >> 0) & 1
       brtc  is_even           ; jump if (r21 & (1<<0) == 0)
    

    Or even better, if your branch only needs to skip one instruction, sbrs - Skip if Bit in Register is Set. (The skipped instruction could itself be a branch, if you wanted, but bst / brt[sc] may be faster in that case.)

       sbrs r21, 0
         nop              ; runs only for even R21
    
       ... code that runs for both cases
    

    e.g. GCC uses it when compiling if(x&1) bar(); (Godbolt):

    foo:
            sbrs r24,0
            ret             ; return (skipped for odd)
            rjmp bar        ; else tailcall
    

    The manual says tst reg is just an alias for and reg,reg to set flags according to the whole value. (html extract).

    Unlike some ISAs with more opcodes (like ARM where you would tst r0, #1), AVR doesn't have a non-destructive AND-but-only-set-flags instruction, only a cmp (which is like sub but doesn't modify the destination reg). If you wanted to do it that way, you could load a 1 into another register and AND into that, setting or clearing the Z flag for BRNE or BREQ.

    Or andi r21, 1 if you don't mind destroying r21. Or right-shift it to shift the low bit into the carry flag. This does still let you branch on odd/even in 2 instructions instead of 3, in a way that's more like other ISAs.