5
\$\begingroup\$

What could be the fastest way to increment two combined bytes in assembler (assuming I'm working on a 8-bit CPU)? Currently I'm doing this:

OVF1_handler: ; TIMER1 overflow ISR

lds r21, timerhl ; load low byte into working register; 2 cycles
add r21, counter_inc ; add 1 to working register (value of counter_inc is 1); 1 cycle

brbs 0, OVF1_handler_carry ; branch if bit 0 (carry flag bit) of SREG is set; 1 cycle if false . 2 cycles if true
sts timerhl, r21 ; otherwise write value back to variable; 2 cycles
reti ; we're done

OVF1_handler_carry: ; in case of carry bit is set
    sts timerhl, r21 ; write value of low byte back to variable; 2 cycles

    lds r21, timerhh ; load high byte into working register; 2 cycles
    inc r21 ; increment it by 1 (no carry check needed here); 1 cycle
    sts timerhh, r21 ; write value of high byte back to variable; 2 cycles

reti ; we're done

So in sum there are

255 * (2+1+1+2) + (2+1+2+2+2+1+2) = 1542 cycles

to count from 0 to 256 (255 times (2+1+1+2) because no overflow plus 1 time (2+1+2+2+2+1+2) when overflow occurs).

Is my calculation correct and is there a faster way?

\$\endgroup\$

1 Answer 1

4
\$\begingroup\$

Have a bit more trust in your compiler. Write the code in C, compile it and look at the disassembly. Unsure which toolchain you use, but avr-gcc creates pretty well optimized code.

lds     r24 , lowbyte   ; 2 clocks
lds     r25 , highbyte  ; 2 clocks
adiw    r24 , 0x01      ; 2 clocks - Add Immediate to Word (= 16 bit)
sts     lowbyte  , r24  ; 2 clocks
sts     highbyte , r25  ; 2 clocks

You can disassemble the .elf file with the following command (provided you use the gcc toolchain):

avr-objdump -C -d $(src).elf

BTW: You probably need to push the used registers to stack beforehand and pop them afterwards (2 cycles each). Also remember that an interrupt (including reti) lasts at least 8 clock cycles apart from the instructions being executed.

; TIMER1_OVF            ;  4 clocks
push    r24             ;  2 clocks
IN      r24 , SREG      ;  1 clock  - save CPU flags
push    r24             ;  2 clocks
push    r25             ;  2 clocks
; do the addition above - 10 clocks
pop     r25             ;  2 clocks
pop     r24             ;  2 clocks
OUT     SREG , r24      ;  1 clock
pop     r24             ;  2 clocks
reti                    ;  4 clocks
; total 32 clock ticks
\$\endgroup\$
14
  • \$\begingroup\$ Or you can give avr-gcc an argument to output disasembly in the compilation process. \$\endgroup\$ Apr 7, 2013 at 20:58
  • \$\begingroup\$ Personally I think avr-gcc is generating a messy listing, it does contain lots of comments though. \$\endgroup\$
    – jippie
    Apr 7, 2013 at 21:00
  • \$\begingroup\$ So there are 10 clocks in sum. Counting from 0 to 256 would take then 256 * 10 = 2560 clocks. That's 1000 clocks more than in my code. \$\endgroup\$
    – arminb
    Apr 7, 2013 at 21:11
  • 1
    \$\begingroup\$ That is a possibility, yes, and the advantage for using assembly. Still your CPU flags will change on every interrupt, which may affect your main loop. \$\endgroup\$
    – jippie
    Apr 7, 2013 at 21:39
  • 1
    \$\begingroup\$ If that is not a problem, then there are some AVR's that have room for 2 instructions in the interrupt vector table. On top of that, if you don't use the next entry in that table you can use it yourself anyway. So with a bit of luck you can fit the entire interrupt routine in the vector table. Saves you a branch (2 clocks). \$\endgroup\$
    – jippie
    Apr 7, 2013 at 21:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.