C代碼(AVR-GCC編譯,優化等級-00):
#include 《avr/io.h》
int add(int a,int b)
{
int c;
c=a+b;
returnc;
}
int main(void)
{
inta=2,b=3,c=0;
c=add(a,b);
//c=sub(a,b);
}
匯編代碼:
(省略一些boot代碼)
。。。。。。。
00000054 《__ctors_end》:
54: 1124 eor r1,r1
56: 1fbe out 0x3f,r1 ;63
58: cfe5 ldi r28,0x5F ;95 //此處Y指針和SP都指到了SRAM最高端
5a: d4e0 ldi r29,0x04 ;4
5c: debf out 0x3e,r29 ;62
5e: cdbf out 0x3d,r28 ; 61
。。。
0000008e 《add》:
#include 《avr/io.h》
int add(int a,int b)
{
8e: cf93 push r28
90: df93 push r29 //保存了Y指針,此時SP已經-2,這里再減2
92: cdb7 in r28,0x3d ;61 //重新定位Y指針跟SP一樣。
94: deb7 in r29,0x3e ;62
96: 2697 sbiw r28,0x06 ;6 //減掉6,即向下開了6字節的區域,存放3變量
98: 0fb6 in r0,0x3f ;63
9a: f894 cli
9c: debf out 0x3e,r29 ;62
9e: 0fbe out 0x3f,r0 ; 63
a0: cdbf out 0x3d,r28 ;61
a2: 9a83 std Y+2,r25 ;0x02
a4: 8983 std Y+1,r24 ;0x01
a6: 7c83 std Y+4,r23 ;0x04
a8: 6b83 std Y+3,r22 ;0x03
int c;
c=a+b;
aa: 2981 ldd r18,Y+1 ;0x01
ac: 3a81 ldd r19,Y+2 ;0x02
ae: 8b81 ldd r24,Y+3 ;0x03
b0: 9c81 ldd r25,Y+4 ;0x04
b2: 820f add r24,r18
b4: 931f adc r25,r19
b6: 9e83 std Y+6,r25 ;0x06
b8: 8d83 std Y+5,r24 ;0x05
returnc;
ba: 8d81 ldd r24,Y+5 ;0x05
bc: 9e81 ldd r25,Y+6 ;0x06
be: 2696 adiw r28,0x06 ;6 //加了6個字節空間,Y指針恢復到減6之前
c0: 0fb6 in r0,0x3f ;63
c2: f894 cli
c4: debf out 0x3e,r29 ;62
c6: 0fbe out 0x3f,r0 ; 63
c8: cdbf out 0x3d,r28 ;61
ca: df91 pop r29
cc: cf91 pop r28
ce: 0895 ret //彈出堆棧中2個字節
000000d0 《main》:
}
int main(void)
{
d0: c9e5 ldi r28,0x59 ;89 //這4句給SP和Y指針重新賦值了,很明顯的在SP的
d2: d4e0 ldi r29,0x04 ;4 //上面還有6個字節(SRAM最大到045E),這6個字節
d4: debf out 0x3e,r29 ;62 //被存放了a,b,c三個變量(可以與上面理論對應)
d6: cdbf out 0x3d,r28 ;61 //通過Y指針來保存了這三個變量到這個區域
inta=2,b=3,c=0;
d8: 82e0 ldi r24,0x02 ;2
da: 90e0 ldi r25,0x00 ;0
dc: 9a83 std Y+2,r25 ;0x02
de: 8983 std Y+1,r24 ;0x01
e0: 83e0 ldi r24,0x03 ;3
e2: 90e0 ldi r25,0x00 ;0
e4: 9c83 std Y+4,r25 ;0x04
e6: 8b83 std Y+3,r24 ;0x03
e8: 1e82 std Y+6,r1 ;0x06
ea: 1d82 std Y+5,r1 ;0x05
c=add(a,b);
ec: 6b81 ldd r22,Y+3 ;0x03
ee: 7c81 ldd r23,Y+4 ;0x04
f0: 8981 ldd r24,Y+1 ;0x01
f2: 9a81 ldd r25,Y+2 ;0x02
f4: 0e 94 4700 call 0x8e《add》 //使用call時自動將PC+2的地址壓到堆棧
f8: 9e83 std Y+6,r25 ;0x06
fa: 8d83 std Y+5,r24 ;0x05
//c=sub(a,b);
}
fc: 80e0 ldi r24,0x00 ;0
fe: 90e0 ldi r25,0x00 ;0
100: 0c 94 82 00 jmp 0x104《_exit》
00000104 《_exit》:
104: ffcf rjmp 。-2 ; 0x104《_exit》
r28和r29一起組成SP指針,Y指針可以作為間接尋址,很明顯的剛開始的時候Y指針和SP都在045F這里,后來在高處開了6個字節的空間來存放臨時變量,所以Y指針成了這個軟件堆棧的棧頂,在這個過程中都是使用Y和SP的配合來實現變量和數據的改變,以及恢復,硬件堆棧和軟件堆棧在這里已經不怎么區分了
棧是可變的,要留足夠的空間才行,如果沒有操作系統用的會很少,主要取決于函數的嵌套深度參數類型。
一般情況RAM存放三種類型的數據:
1.全局變量
2.堆(典型的MALLOC函數調用),這個得看你用了還是沒有
3.棧,這個必然要用到的,有操作系統的話用的就更多了,每一個任務都會有一個棧,根據任務的函數嵌套程度可分配不同的大小。
具體要看什么編譯器了,所以首先要估計一下你的棧要用多少,然后,再計算一下你的全局變量有多少,最后定一下可能的動態分配內存(堆)有多少就可以了。
考慮到存儲器的大小的限制,我們在編寫單片機的程序時,一定要精打細算。一個程序,當系統中使用了大量的中斷資源,并且允許了中斷嵌套的存在,那么在極端的情況下,中斷處理程序就很容易發生嵌套現象。此時適當擴大硬件堆棧的大小,支持較大的函數嵌套深度,往往能解決很多莫名其妙的跑飛問題。與擁有豐富存儲器資源時的狀況不同,由于局部變量在函數發生嵌套時,都要占用軟件堆棧空間,因此大量使用使用局部變量或者使用了占用空間頗為可觀的局部數組(也包括體積巨大的結構體),在嵌套深度較大時,都有可能造成向下生長的軟件堆棧侵入其他存儲區域(詳細情形閱讀ICC的幫助文檔),導致某些變量意外修改、程序跑飛等現象。解決這一問題的方法其實很簡單,在某些局部變量占用空間較大的情況下,將其通過關鍵static聲明為靜態變量--這樣即保證了變量的局限性,又避免了將這些內容壓入軟件棧中(靜態局部變量在存儲時和全局變量沒有本質區別,采用的都是 靜態分配),只不過每次使用這些變量之前都要記得補充必要的初始化代碼。
評論