我试图为Arduino Duemilanove (AVR ATmega328P)编写一些汇编语言。在学习汇编语言的同时,编写和解译C代码,我得到了以下信息:
(用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?发布于 2013-06-26 15:19:20
点/句点用作指示该指令的地址或位置或与之相关的东西的快捷方式。.+8的意思是从这里加上8,你必须考虑到指令集和/或汇编程序相对于指令集的细微差别。如汇编程序中的附加信息所示,.-8将转到do_clear_bss_loop,它返回8个字节,包括指令本身的两个字节。原来的代码中可能有标签,brne do_clear_bss_loop。
它可能是在复制数据段;.text基本上是只读的。这是你的代码,它想要生活在这个平台上的闪光灯。不过,.data是读/写的,通常初始化为非零值。因此,在断电后,您的初始值需要保存在某个地方,例如在闪存中,但是在启动真正的程序之前,引导程序需要将初始的.data段值从闪存复制到它们在随机存取存储器中的实际家中。然后,当程序运行时,它可以根据需要读取和/或修改这些值。
例如:
int x = 5;
main ()
{
x = x + 1;
}这个值5必须在闪存中,这样才能启动,只需要使用闪存来保存非易失性信息。但是,在您可以读写x的内存位置之前,您需要在RAM中使用它,所以一些启动代码会将所有的.data文件从闪存复制到内存中。
很抱歉对你的问题做了这么长的解释,只是猜测。
.bss是程序中初始化为零的变量。与.data部分,如果我们有100个项目,我们将需要100个东西在闪存。但是对于.bss,如果我们有100个条目,我们只需要告诉某人有100个条目。我们不需要闪存中的100个零,只需编译/组装到代码中即可。
所以
int x = 5;
int y;
int main ()
{
while(1)
{
y = y + x + 1;
}
}x在.data中,而这5个需要在非易失性存储中。Y在.bss中,只需要在main被调用以符合C标准之前对其进行零化。
当然,您可能不自己使用全局变量,但可能有其他数据在某种程度上使用.data和/或.bss段,因此引导代码在调用main()之前为.data和.bss段做好准备,这样您的C编程体验就像预期的那样。
发布于 2015-02-13 17:36:02
我知道这是个迟来的答复。然而,我仍然认为,有一个详细的逐点回答所有问题可能是有趣的。
意思是:“从这里跳回8个字节”。请注意,程序计数器已经被指令的长度(2字节)递增,因此brne .-8将在brne指令本身之前移动6个字节(而不是8个字节)。同样,rcall .+0将在不改变程序流的情况下将程序计数器推到堆栈中。这是一个只打算在一条指令中保留两个字节的堆栈空间的技巧。
不,没有复制,这是一个空循环。在第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指针被初始化为在程序代码之后指向。这是初始化变量的初始值应该在的位置。
main。有什么一般性解释吗?不,什么都不会被覆盖。此循环清除BSS,BSS通常是在数据部分之后出现的,没有重叠。伪码:
X = BSS_START;
while (X != BSS_START + BSS_SIZE)
ram[X++] = 0;在哪里BSS_START == DATA_START + DATA_SIZE。这也是一个空循环在你的程序,因为你有一个空的bss。
因为objdump -d只反汇编希望保存代码的部分。
大多数指令只改变SREG的一些位。此外,这将清除全局中断启用位。
堆栈指针由0x08ff加载,这是ATmega328P中的最后一个RAM位置。堆栈将从那里向下增长。
拉姆在328 p上是0x0100-0x08ff。在这个地址下面有一些内存映射寄存器( CPU寄存器和I/O寄存器)。有关详细信息,请查看数据表,“8.3SRAM数据存储器”部分。
ldi r17, 1?我们以前也这么做过(只是一句愚蠢的话)。或者其他什么东西可以改变r17?第8a行没用。之所以如此,是因为链接器通过将不同的部分粘合在一起来构建程序:__do_copy_data和__do_clear_bss是独立的例程,它们不依赖寄存器中的其他程序。
你误解了代码的这一部分。cpi、cpc和brne指令只有在X与r17:0x00不同的情况下才会循环(即0x0100,因为r17 = 1)。C.f.上面的伪码。
https://stackoverflow.com/questions/17323757
复制相似问题