ARM的匯編編程,本質上就是針對CPU寄存器的編程,所以我們首先要弄清楚ARM有哪些寄存器?這些寄存器都是如何使用的?
ARM寄存器分為2類,普通寄存器和狀態寄存器
寄存器類別 |
寄存器在匯編中的名稱 |
各模式下實際訪問的寄存器 |
||||||
用戶 |
系統 |
管理 |
中止 |
未定義 |
中斷 |
快中斷 |
||
通用寄存器和程序計數器 |
R0(a1) |
R0 |
||||||
R1(a2) |
R1 |
|||||||
R2(a3) |
R2 |
|||||||
R3(a4) |
R3 |
|||||||
R4(v1) |
R4 |
|||||||
R5(v2) |
R5 |
|||||||
R6(v3) |
R6 |
|||||||
R7(v4) |
R7 |
|||||||
R8(v5) |
R8 |
R8_fiq |
||||||
R9(SB,v6) |
R9 |
R9_fiq |
||||||
R10(SL,v7) |
R10 |
R10_fiq |
||||||
R11(FP,v8) |
R11 |
R11_fiq |
||||||
R12(IP) |
R12 |
R12_fiq |
||||||
R13(SP) |
R13 |
R13_svc |
R13_abt |
R13_und |
R13_irq |
R13_fiq |
||
R14(LR) |
R14 |
R14_svc |
R14_abt |
R14_und |
R14_irq |
R14_fiq |
||
R15(PC) |
R15 |
|||||||
狀態寄存器 |
CPSR |
CPSR |
||||||
SPSR |
無 |
SPSR_abt |
SPSR_abt |
SPSR_und |
SPSR_irq |
SPSR_fiq |
請看上表的第2列,普通寄存器總共16個,分別為R0-R15;狀態寄存器共2個,分別為CPSR和SPSR
普通寄存器中特別要提出來的是R13、R14、R15。
R15別名PC(program counter),中文稱為程序計數器,它的值是當前正在執行的指令在內存中的位置(不考慮流水線的影響,參見流水線對PC值的影響一文),而當指令執行結束后,CPU硬件會自動將PC的值加上一個單位,從而使得PC的值為下一條即將執行的指令在內存中的位置,這樣CPU硬件就可以根據PC的值自動完成取指的操作。正是由於有PC的存在,以及CPU硬件會自動增加PC的值,並根據PC的值完成取指操作,才使得CPU一旦上電就永不停歇地運轉,由此可見PC寄存器對於計算機的重要性。對於我們進行匯編程序編寫而言,PC寄存器亦是十分重要,因為當程序員通過匯編指令完成了對PC寄存器的賦值操作的時候,其實就是完成了一次無條件跳轉,這一點非常重要,請務必要牢記。
R14別名LR(linked register),中文稱為鏈接寄存器,它與子程序調用密切相關,用於存放子程序的返回地址,它是ARM程序實現子程序調用的關鍵所在。下面我們用C語言中對子程序調用的實現細節來說明LR是如何被使用的。
1 int main(void)
2 {
3 int k, i = 1, j = 2;
4 addsub(i, j);
5 k = 3;
6 }
7 int addsub(int a, int b)
8 {
9 int c;
10 c = a + b;
11 return c;
12 }
對於上面的程序,編譯器會將第4行編譯為指令:BL addsub,將第11行編譯為指令:MOV pc, lr。(關於BL和MOV指令詳見“基本尋址模式與基本指令”)
在這里,關鍵指令BL addsub會完成2件事情:1、將子程序的返回地址(也就是第5行代碼在內存中的位置)保存到寄存器LR中;2、跳轉到子程序addsub的第1條指令處。這樣就完成了子程序的調用。而指令MOV pc, lr則將保存在lr中的返回地址賦給pc,這樣就完成了從子程序的返回。由此可見,lr是用於存放子程序的返回地址的。
另外一個要引起注意的問題是,如果子程序又調用了孫子程序,那么根據前面的分析,在調用孫子程序時,lr寄存器中的值將從子程序的返回地址變為孫子程序的返回地址,這將導致從孫子程序返回子程序沒有問題,但從子程序返回父程序則會出錯。那么這個問題如何解決呢?其實,如果我們編寫的是C程序,那么我們一點也不用擔心,因為編譯器會為我們考慮一切,針對這個問題,編譯器會在孫子程序的入口處增加入棧操作將lr的值入棧,然后在孫子程序的返回處增加出棧操作,將lr的值恢復,從而解決這個難題。不過我們一定要保持頭腦的清醒,因為你要知道,我們現在是在編寫匯編子程序,此時編譯器已經不能在這方面給我們提供保障,所以當你在編寫匯編子程序的時候,發現該子程序還要再調用孫子程序,那么請你務必記住,一定要在子程序的入口處保存lr寄存器的值。
好了,現在輪到寄存器R13了,R13又名SP(stack pointer),中文名稱棧指針寄存器。顧名思義,它是用於存放堆棧的棧頂地址的。也就是說,每次當我們進行出棧和入棧的時候,都將根據該寄存器的值來決定訪問內存的位置(即:出入棧的內存位置),同時在出棧和入棧操作完成后,SP寄存器的值也應該相應增加或減少。這里要特別說明的是,其實在32位的ARM指令集中沒有專門的入棧指令和出棧指令,所以並不是一定要用SP來作為棧指針寄存器,除了PC外,任何普通寄存器均可作為棧指針寄存器,只不過,約定俗成都使用SP罷了。我們將在“其它尋址模式與其它指令”一文中見到ARM中使用SP作為棧指針寄存器的出入棧指令。
寄存器R0-R12是普通的數據寄存器,可用於任何地方。在不涉及ATPCS規則(在“ATPCS與混合編程”一文中詳細介紹)的情況下,他們並沒有什么特別的用法。
狀態寄存器CPSR(current program status register),中文名稱:當前程序狀態寄存器,顧名思義它是用於保存程序的當前狀態的。那么,程序的哪些狀態是需要保存的呢?
上圖是CPSR寄存器的內容,主要由以下部分組成:
1、條件代碼標志位。它們是ARM指令條件執行的依據。
N:運算結果的最高位反映在該標志位。對於有符號二進制補碼,結果為負數時N=1,結果為正數或零時N=0;
Z:指令結果為0時Z=1(通常表示比較結果“相等”),否則Z=0;
C:當進行加法運算(包括CMN指令),並且最高位產生進位時C=1,否則C=0。當進行減法運算(包括CMP 指令),並且最高位產生借位時C=0,否則C=1。對於結合移位操作的非加法/減法指令,C為從最高位最后移出的值,其它指令C通常不變
V:當進行加法/減法運算,並且發生有符號溢出時V=1,否則V=0,其它指令V通常不變
2、控制位。它們將控制CPU是否響應中斷。
I:中斷禁止位,當I位置位時,IRQ中斷被禁止
F:快中斷禁止位,當F位置位時,FIQ中斷被禁止
T:反映了CPU當前的狀態。當T位置位時,處理器正在Thumb狀態下運行;當T位清零時,處理器正在ARM狀態下運行
3、模式位
包括M4、M3、M2、M1和M0,這些位決定了處理器的模式(關於處理器模式詳見“ARM處理器模式與異常初步”一文)。
總共有7種模式:用戶、快中斷、中斷、管理、中止、未定義、系統,分別會用於不同的情況和異常。由此可見,不是所有模式位的組合都定義了有效的處理器模式,如果使用了錯誤的設置,將引起一個無法恢復的錯誤。
SPSR(saved program status register),中文名稱:保存的程序狀態寄存器
該寄存器的結構與CPSR完全一樣,在異常發生時(關於異常,請參見“ARM處理器模式與異常初步”一文),由硬件自動將異常發生前的CPSR的值存放到SPSR中,以便將來在異常處理結束后,程序能恢復原來CPSR的值。