由於ARM在各種執行模式下都需要設定各自的棧指標,所以"ldr sp,=xxxx"操作較多。根據ARM的ATPCS規則,對棧的操作屬於FD(滿遞減),即棧指標一直指向棧頂元素,是按地址減小的方向增長的,所以一般將SP設定在地址的最高處。
在on_sdram中:
ldr sp, =4096 @ 設定中斷模式棧指標
ldr sp, =0x34000000 @ 設定系統模式棧指標,
這是分別設定中斷模式和系統模式下的堆棧指標到4096(SRAM的有效地址的最高地址)和0x34000000(從0x30000000開始的64M的SDRAM的最高地址處,此時SDRAM已經初始化,可以使用了)。
C語言還是要轉換成彙編指令的,處理比較複雜時,就要利用到棧來儲存中間結果之類的,這時候,在你進入C語言程式之前,就要設定好棧了。函數調用是通過棧來實現的。。。調用完函數返回時,需要通過棧裡的地址來返回。
同時,函數的參數也是通過棧來傳遞。
總的來說,棧的作用就是:儲存現場/上下文,傳遞參數,儲存臨時變數
1.儲存現場/上下文
現場/上下文,意思就相當於案發現場,總有一些現場的情況,要記錄下來的,否則被別人破壞掉之後,你就無法恢複現場了。而此處說的現場,就是指CPU啟動並執行時候,用到了一些寄存器,比如r0,r1等等,對於這些寄存器的值,如果你不儲存而直接跳轉到子函數中去執行,那麼很可能就被其破壞了,因為其函數執行也要用到這些寄存器。因此,在函數調用之前,應該將這些寄存器等現場,暫時保持起來(入棧push),等調用函數執行完畢返回後(出棧pop),再恢複現場。這樣CPU就可以正確的繼續執行了。
儲存寄存器的值,一般用的是push指令,將對應的某些寄存器的值,一個個放到棧中,把對應的值壓入到棧裡面,即所謂的壓棧。然後待被調用的子函數執行完畢的時候,再調用pop,把棧中的一個個的值,賦值給對應的那些你剛開始壓棧時用到的寄存器,把對應的值從棧中彈出去,即所謂的出棧。
其中儲存的寄存器中,也包括lr的值(因為用bl指令進行跳轉的話,那麼之前的pc的值是存在lr中的),然後在子程式執行完畢的時候,再把棧中的lr的值pop出來,賦值給pc,這樣就實現了子函數的正確的返回。
2.傳遞參數
C語言進行函數調用的時候,常常會傳遞給被調用的函數一些參數,對於這些C語言層級的參數,被編譯器翻譯成組合語言的時候,就要找個地方存放一下,並且讓被調用的函數能夠訪問,否則就沒發實現傳遞參數了。對於找個地方放一下,分兩種情況。一種情況是,本身傳遞的參數不多於4個,就可以通過寄存器傳送參數。因為在前面的儲存現場的動作中,已經儲存好了對應的寄存器的值,那麼此時,這些寄存器就是閒置,可以供我們使用的了,那就可以放參數。另一種情況是,參數多於4個時,寄存器不夠用,就得用棧了。
3.臨時變數儲存在棧中
包括函數的非靜態局部變數以及編譯器自動產生的其他臨時變數。