我正在尝试为Arduino Duemilanove(AVR ATmega328P)编写一些汇编语言。在编译和反汇编C代码的同时学习汇编语言,我得到了这个:
(使用AVR_GCC编译)
int main() {
volatile int a = 0;
while (1) {
++a;
}
return 0;
}
哪个变成了
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>
我试着了解一些事情:
main
。任何一般性解释?ldi r17, 1
?我们之前做过(只是一个愚蠢的评论)。或者其他可以改变r17?答案 0 :(得分:3)
点/句点用作快捷方式,用于指示此指令的地址或位置或与之相关的内容。 。+ 8表示从这里加8.您必须考虑指令集和/或汇编程序相对于指令集的细微差别。正如汇编程序的附加信息所示,。-8将转到do_clear_bss_loop
,后面是八个字节,包括指令本身的两个字节。原始代码可能只有标签brne do_clear_bss_loop
。
可能会复制数据段; .text
基本上是只读的。这是你的代码,它希望在这个平台上使用flash。但是,.data
是读/写的,通常初始化为非零值。因此,关闭电源后,您的初始值需要保存在某处,例如闪存,但在启动实际程序之前,引导程序需要将初始.data段值从闪存复制到{{3}中的实际主页}。然后,当程序运行时,它可以根据需要读取和/或修改这些值。
例如:
int x = 5;
main ()
{
x = x + 1;
}
该值5必须处于闪存状态才能从上电开始,仅使用闪存来保存非易失性信息。但是在你可以读取/写入x的内存位置之前,你需要它在RAM中,所以一些启动代码会将所有.data
sgement内容从flash复制到RAM。
对于那些只是猜测你的问题的长篇解释感到抱歉。
.bss
是程序中初始化为零的变量。使用.data
细分,如果我们有100个项目,我们需要100个闪存内容。但是如果我们有100个项目.bss
,我们只需要告诉别人有100个项目。我们在flash中不需要100个零,只需将它编译/汇编到代码中即可。
所以
int x = 5;
int y;
int main ()
{
while(1)
{
y = y + x + 1;
}
}
x
位于.data
,而5需要位于非易失性存储中。 y在.bss
中,只有在调用main以符合C标准之前,才需要将其归零。
当然,您可能没有自己使用全局变量,但可能有其他数据以某种方式使用.data
和/或.bss
段,因此引导代码准备在调用.data
之前.bss
和main()
段,以便您的C编程体验符合预期。
答案 1 :(得分:3)
我意识到这是一个迟到的答案。但是,我仍然认为对所有问题进行详细的逐点回答可能会很有趣。
- 什么是。-8或类似的语法? (例如地址0x98或0xAA。)
醇>
这意味着:“从这里跳回8个字节”。请注意,程序计数器已经增加了指令的长度(2个字节),因此brne .-8
将在brne指令本身之前移动6个字节(而不是8个字节)。同样,rcall .+0
将程序计数器推送到堆栈而不改变程序流程。这是一个仅用于在单个指令中保留两个字节的堆栈空间的技巧。
- 在地址80到88(__do_copy_data结尾)的行周围有一些有趣的东西。在我看来,这会将所有程序代码从地址0xC4加载到RAM中。为什么?
醇>
不,没有复制,这是一个空循环。在第84到88行,当指针X(r27:r26)等于0x0100时,有一个测试退出循环。由于X初始化为0x0100,因此根本不会循环。
此循环用于将数据部分从闪存复制到RAM。它基本上是这样的:
X = DATA_START; // RAM address
Z = 0x00C4; // Flash address
while (X != DATA_START + DATA_SIZE)
ram[X++] = flash[Z++];
但您的程序碰巧有一个空数据部分(上面的伪代码中为DATA_SIZE == 0
)。
另外,您应该注意您的程序在地址0x00c3处结束,因此Z指针在程序代码之后被初始化为指向。这是初始化变量的初始值应该是的地方。
- 在__do_clear_bss_start / loop中,我们通过将RAM中的字节设置为0(r1的值)来清除我们刚刚完成的所有工作。为什么?所有这一切最终要求
醇>main
。任何一般性解释?
不,什么都不会被覆盖。该循环清除BSS,BSS通常在数据部分之后,没有重叠。伪代码:
X = BSS_START;
while (X != BSS_START + BSS_SIZE)
ram[X++] = 0;
其中BSS_START == DATA_START + DATA_SIZE
。这也是程序中的一个空循环,因为你有一个空的bss。
- 为什么不贬低显示.bss,.rodata或其他部分?
醇>
因为objdump -d
只会拆分预期保存代码的部分。
- 第6a行,为什么SREG被清除了?是不是设置了每条指令后应该是什么?
醇>
大多数指令只会改变 SREG的某些位。此外,这会清除全局中断使能位。
- 第6c和6e行:0xFF和0x08对应的是什么? r28和r29是堆栈指针低和高。
醇>
堆栈指针加载0x08ff,这是ATmega328P中的最后一个RAM位置。堆栈将从那里向下增长。
- 我玩了一下并添加了一个静态全局变量。为什么我们从0x0100开始存储在RAM中而不是0x0000?
醇>
328P上的RAM为0x0100-0x08ff。在该地址下面有一些内存映射寄存器(CPU寄存器和I / O寄存器)。有关详细信息,请查看the datasheet,“8.3 SRAM数据存储器”部分。
- 在第8a行,为什么
醇>ldi r17, 1
?我们之前做过(只是一个愚蠢的评论)。或者其他可以改变r17?
第8a行无用。这是因为链接器通过将不同的部分粘合在一起来构建程序的方式:__do_copy_data
和__do_clear_bss
是独立的例程,它们不依赖于寄存器中剩下的其他任何内容。
- 我们开始将程序在闪存中复制到RAM,从0xC4开始(.bss和其他部分我猜),但是关于1的X的cpi / cpc将使所有闪存复制到所有RAM中。只是因为编译器的懒惰才能在.bss部分复制时不停止复制吗?
醇>
你误解了这部分代码。只要X与r17不同,cpi,cpc和brne指令就会循环:0x00(即0x0100,因为r17 = 1)。 C.F.上面的伪代码。