1
\$\begingroup\$

I wrote the following program to turn an LED on and off at 1-second intervals:

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB = 0b00100000;
    for (;;) {
        PORTB = 0b00100000;
        _delay_ms(1000);
        PORTB = 0b00000000;
        _delay_ms(1000);
    }
    return 0;
}

I tried disassembling the binary file of this code using the avr-objdump of which the output is:

00000080 <main>:
  80:   80 e2           ldi r24, 0x20   ; 32
  82:   84 b9           out 0x04, r24   ; 4

00000084 <.L2>:
  84:   85 b9           out 0x05, r24   ; 5
  86:   2f ef           ldi r18, 0xFF   ; 255
  88:   33 ed           ldi r19, 0xD3   ; 211
  8a:   90 e3           ldi r25, 0x30   ; 48

0000008c <.L1^B1>:
  8c:   21 50           subi    r18, 0x01   ; 1
  8e:   30 40           sbci    r19, 0x00   ; 0
  90:   90 40           sbci    r25, 0x00   ; 0
  92:   e1 f7           brne    .-8         ; 0x8c <.L1^B1>
  94:   00 c0           rjmp    .+0         ; 0x96 <L0^A>

00000096 <L0^A>:
  96:   00 00           nop
  98:   15 b8           out 0x05, r1    ; 5
  9a:   2f ef           ldi r18, 0xFF   ; 255
  9c:   33 ed           ldi r19, 0xD3   ; 211
  9e:   90 e3           ldi r25, 0x30   ; 48

000000a0 <.L1^B2>:
  a0:   21 50           subi    r18, 0x01   ; 1
  a2:   30 40           sbci    r19, 0x00   ; 0
  a4:   90 40           sbci    r25, 0x00   ; 0
  a6:   e1 f7           brne    .-8         ; 0xa0 <.L1^B2>
  a8:   00 c0           rjmp    .+0         ; 0xaa <L0^A>

000000aa <L0^A>:
  aa:   00 00           nop
  ac:   eb cf           rjmp    .-42        ; 0x84 <.L2>

000000ae <_exit>:
  ae:   f8 94           cli

000000b0 <__stop_program>:
  b0:   ff cf           rjmp    .-2         ; 0xb0 <__stop_program>

My understanding of the above assembly code is:

  • the two lines under 00000080 <main>: sets the data direction of PB5 to output.
  • 00000084 <.L2>: section turns the LED on.
  • 0000008c <.L1^B1>: waits for 1 second (first 4 lines) which then jumps to 00000096 <L0^A>:
  • 00000096 <L0^A>: turns the LED off by writing the contents of r1 (which is currently 0s) to PB5
  • then 000000a0 <.L1^B2>: waits for 1 second and jumps to 000000aa <L0^A>:
  • 000000aa <L0^A>: spends one clock cycle and jumps to 00000084 <.L2>: which turns the LED on. So on and so forth.

Questions:

  1. Is my understanding of the assembly code correct?
  2. What is the purpose of a nop sometimes as the first instruction?
\$\endgroup\$
5
  • \$\begingroup\$ Have you checked the data sheets to see if R24 is a data direction register? \$\endgroup\$
    – Andy aka
    Feb 22, 2023 at 13:13
  • \$\begingroup\$ What is your goal? If you want to learn assembly, I don't recommend this method. Compilers often produce code that is harder to understand than assembly code generated by humans. \$\endgroup\$
    – Mattman944
    Feb 22, 2023 at 13:26
  • 1
    \$\begingroup\$ @Andyaka I checked the datasheet and thought that 0x04 is the data direction register. I thought that I'm writing the contents of r24 to 0x04, I may be wrong... \$\endgroup\$
    – kovac
    Feb 22, 2023 at 13:38
  • 1
    \$\begingroup\$ @kovac you are right: r24 is often used as a temporary register. Data direction register B is at IO address 4, ie, when using OUT. (And also at memory address 0x24, for when using ST.) It's a pecularity of this CPU that some registers appear in both IO and memory address spaces, but with different addresses. \$\endgroup\$
    – jonathanjo
    Feb 22, 2023 at 13:55
  • \$\begingroup\$ Take a look here godbolt.org/z/9jTd7sdnj \$\endgroup\$
    – G36
    Feb 22, 2023 at 16:26

1 Answer 1

2
\$\begingroup\$
  1. In general, yes, but disassembling the raw binary may not be very fruitful, as you could just look at the compiler output listing which includes the C source code and generated assembly opcodes.

  2. You ask for 1000 millisecond delay, which is done by looping 3199999 times. As the loop will be a few clock cycles less than 1000 milliseconds, it adds extra useless RJMP and NOP opcodes to waste a few clock cycles to end up closer to 1000 milliseconds.

\$\endgroup\$
2
  • \$\begingroup\$ For 1., how do I get this? \$\endgroup\$
    – kovac
    Feb 22, 2023 at 13:40
  • 1
    \$\begingroup\$ Most C compilers support -S for making the assmbler file filename.s from filename.c. If you're using the avr toolset, avr-gcc -S -Os -mmcu=atmega328 -c filename.c is likely to give you useful output. Also look at assembler listing output: avr-as -mmcu=atmega328 -o filename.o -a=filename.lst filename.s \$\endgroup\$
    – jonathanjo
    Feb 22, 2023 at 13:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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