I'm trying to write some assembly language for Arduino Duemilanove (AVR ATmega328P). Learning assembly language jointly in parallel with compiling and disassembling C code, I have got this:
(Compiled with AVR_GCC)
int main() {
volatile int a = 0;
while (1) {
++a;
}
return 0;
}
Which turns into
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
...
64: 0c 94 51 00 jmp 0xa2 ; 0xa2 <__bad_interrupt>
00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d8 e0 ldi r29, 0x08 ; 8
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61
00000074 <__do_copy_data>:
74: 11 e0 ldi r17, 0x01 ; 1
76: a0 e0 ldi r26, 0x00 ; 0
78: b1 e0 ldi r27, 0x01 ; 1
7a: e4 ec ldi r30, 0xC4 ; 196
7c: f0 e0 ldi r31, 0x00 ; 0
7e: 02 c0 rjmp .+4 ; 0x84 <__do_copy_data+0x10>
80: 05 90 lpm r0, Z+
82: 0d 92 st X+, r0
84: a0 30 cpi r26, 0x00 ; 0
86: b1 07 cpc r27, r17
88: d9 f7 brne .-10 ; 0x80 <__do_copy_data+0xc>
0000008a <__do_clear_bss>:
8a: 11 e0 ldi r17, 0x01 ; 1
8c: a0 e0 ldi r26, 0x00 ; 0
8e: b1 e0 ldi r27, 0x01 ; 1
90: 01 c0 rjmp .+2 ; 0x94 <.do_clear_bss_start>
00000092 <.do_clear_bss_loop>:
92: 1d 92 st X+, r1
00000094 <.do_clear_bss_start>:
94: a0 30 cpi r26, 0x00 ; 0
96: b1 07 cpc r27, r17
98: e1 f7 brne .-8 ; 0x92 <.do_clear_bss_loop>
9a: 0e 94 53 00 call 0xa6 ; 0xa6 <main>
9e: 0c 94 60 00 jmp 0xc0 ; 0xc0 <_exit>
000000a2 <__bad_interrupt>:
a2: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
000000a6 <main>:
a6: cf 93 push r28
a8: df 93 push r29
aa: 00 d0 rcall .+0 ; 0xac <main+0x6>
ac: cd b7 in r28, 0x3d ; 61
ae: de b7 in r29, 0x3e ; 62
b0: 1a 82 std Y+2, r1 ; 0x02
b2: 19 82 std Y+1, r1 ; 0x01
b4: 89 81 ldd r24, Y+1 ; 0x01
b6: 9a 81 ldd r25, Y+2 ; 0x02
b8: 01 96 adiw r24, 0x01 ; 1
ba: 9a 83 std Y+2, r25 ; 0x02
bc: 89 83 std Y+1, r24 ; 0x01
be: fa cf rjmp .-12 ; 0xb4 <main+0xe>
000000c0 <_exit>:
c0: f8 94 cli
000000c2 <__stop_program>:
c2: ff cf rjmp .-2 ; 0xc2 <__stop_program>
I tried to understand a few things:
main
. Any general explanations?ldi r17, 1
? We did that before (just a stupid remark). Or can something else alter r17?The dot/period is used as a shortcut to indicate this instruction's address or location or something relative to that. .+8 means from here plus 8. You have to account for the nuances of the instruction set and/or assembler relative to the instruction set. As the additional information from the assembler indicates, the .-8 is going to do_clear_bss_loop
which is eight bytes back including the two bytes for the instruction itself. The original code probably just had the label in there, brne do_clear_bss_loop
.
It is likely copying the data segment; .text
is basically read-only. It is your code and it wants to live in flash on this platform. .data
, though, is read/write and usually initialized to non-zero values. So with the power off, your initial values need to be preserved somewhere, in flash for example, but before you start your real program the bootstrap will need to copy the initial .data segment values from flash to their actual home in RAM. Then as the program runs, it can read and/or modify those values as desired.
For example:
int x = 5;
main ()
{
x = x + 1;
}
That value 5 has to be in flash in order to start from power up only using flash to hold non-volatile information. But before you can read/write the memory location for x you need it in RAM, so some startup code copies all of the .data
sgement stuff from flash to RAM.
Sorry for that long explanation for something that is only a guess looking at your question.
.bss
are variables in your program that are initialized to zero. With the .data
segment, if we had 100 items we would need 100 things in flash. But with .bss
if we have 100 items we only need to tell someone that there are 100 items. We don't need 100 zeros in flash, just compile/assemble it into the code.
So
int x = 5;
int y;
int main ()
{
while(1)
{
y = y + x + 1;
}
}
x
is in .data
and the 5 needs to be in non-volatile storage. The y is in .bss
and only needs to be zeroed before main is called to comply with the C standard.
Granted, you may not be using global variables yourself, but there may be other data that is in some way using the .data
and/or .bss
segments and as a result the bootstrap code prepares the .data
and .bss
segments before calling main()
so that your C programming experience is as expected.
I realize this is a late answer. However, I still think it may be interesting to have a detailed point-by-point answer to all the questions.
- What is the .-8 or alike syntax? (address 0x98 or 0xAA for instance.)
It means: "jump back 8 bytes from here". Beware that the program counter has already been incremented by the length of the instruction (2 bytes), thus brne .-8
will move you 6 bytes (not 8) prior to the brne instruction itself. In the same vein, rcall .+0
will push the program counter to the stack without altering the program flow. This is a trick only intended to reserve two bytes of stack space in a single instruction.
- Around lines with address 80 to 88 (end of __do_copy_data) there are some funny things. It seems to me that this loads all the program code into RAM, from address 0xC4. Why?
No, nothing is copied, this is an empty loop. On lines 84 to 88 there is a test that exits the loop when the pointer X (r27:r26) equals 0x0100. Since X is initialized to 0x0100, this will not loop at all.
This loop is intended to copy the data section from flash to RAM. It does basically something like this:
X = DATA_START; // RAM address
Z = 0x00C4; // Flash address
while (X != DATA_START + DATA_SIZE)
ram[X++] = flash[Z++];
but your program happens to have an empty data section (DATA_SIZE == 0
in the above pseudo-code).
Also, you should note that your program ends at address 0x00c3, thus the Z pointer is initialized to point right after the program code. This is where the initial values of the initialized variables are supposed to be.
- In __do_clear_bss_start/loop, we clear all the work we have just done by setting bytes in the RAM to 0 (value of r1). Why? All this to finally call
main
. Any general explanations?
No, nothing will be overwritten. This loop clears the BSS, which normally comes right after the data section, with no overlap. Pseudocode:
X = BSS_START;
while (X != BSS_START + BSS_SIZE)
ram[X++] = 0;
where BSS_START == DATA_START + DATA_SIZE
. This is also an empty loop in your program because you have an empty bss.
- Why doesn't disasembling show .bss, .rodata or other sections?
Because objdump -d
only disassembles the sections expected to hold code.
- Line 6a, why is SREG cleared? Isn't it set to what it should be after every instruction?
Most instructions only alter some bits of SREG. Also, this clears the global interrupt enable bit.
- Lines 6c and 6e: what do 0xFF and 0x08 correspond to? r28 and r29 are the stack pointer low and high.
The stack pointer is loaded with 0x08ff, which is the last RAM location in the ATmega328P. The stack will grow downwards from there.
- I played a bit and added a static global variable. Why do we store in RAM starting from 0x0100 and not 0x0000?
RAM is at 0x0100–0x08ff on the 328P. Below this address you have some memory-mapped registers (the CPU registers and the I/O registers). Check the datasheet for details, section "8.3 SRAM Data Memory".
- At line 8a, why
ldi r17, 1
? We did that before (just a stupid remark). Or can something else alter r17?
Line 8a is useless. It is here because of the way the linker builds the program by gluing together different pieces: __do_copy_data
and __do_clear_bss
are independent routines, they do not rely on whatever the other left in the registers.
- We start copying the program in flash to the RAM, starting at 0xC4 (.bss and other sections I guess), but the cpi/cpc of X with regard to 1 will make ALL the flash copied into all the RAM. Is it just by laziness of the compiler to not stop copying when .bss sections are done copying?
You misunderstood this part of the code. The cpi, cpc and brne instructions will loop only as long as X is different from r17:0x00 (i.e. 0x0100, since r17 = 1). C.f. the pseudo-codes above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With