一、參考資料
1. 《ARM GCC內聯彙編手冊》:http://www.ethernut.de/en/documents/arm-inline-asm.html
2. 《__asm__ __volatile__內嵌彙編用法簡述》:http://www.embedu.org/Column/Column28.htm
3. 《ARM內嵌匯編示例》:http://wenku.baidu.com/view/72c12e4133687e21af45a990.html
二、C與彙編混合編程概述
1. 應遵守的規則
在使用C語言時,要用到和彙編語言的混合編程。若彙編代碼較爲簡潔,則可使用直接內嵌彙編的方法;否則要將彙編程序以文件的形式加入到項目中,按照ATPCS(ARM/Thumb過程調用標準,ARM/Thumb
Procedure Call Standard)的規定與C程序相互調用與訪問。
在C程序和ARM彙編程序之間相互調用時,必須遵守ATPCS規則。ATPCS規定了一些子程序間調用的基本規則,哪寄存器的使用規則,堆棧的使用規則和參數的傳遞規則等。
2. ATPCS規則
2.1 寄存器的使用規則
寄存器r0~r3:子程序之間通過r0~r3來傳遞參數,當參數個數多於4個時,使用堆棧來傳遞參數。此時r0~r3可記作A1~A4。
寄存器r4~r11:在子程序中使用r4~r11保存局部變量。因此當進行子程序調用時要注意對這些寄存器的保存和恢復。此時r4~r11可記作V1~V8。
寄存器r12:用於保存堆棧指針SP,當子程序返回時使用該寄存器出棧,記作IP。
寄存器r13:堆棧指針,記作SP。
寄存器r14:鏈接寄存器,記作LR。用於保存子程序的返回地址。
寄存器r15:程序計數器,記作PC。
2.2 堆棧的使用規則
ATPCS規定堆棧採用滿遞減類型(FD, Full Descending),即堆棧通過減小存儲器地址而向下增長,堆棧指針指向內含有效數據項的最低地址。
2.3 參數的傳遞規則
整數參數:前4個使用r0~r3傳遞,其他參數使用堆棧傳遞;浮點參數:使用編號最小且能夠滿足需要的一組連續的FP寄存器傳遞參數。
子程序的返回結果爲一個32位整數時,通過r0返回;返回結果爲一個64位整數時,通過r0和r1返回;依此類推。結果爲浮點數時,通過浮點運算部件的寄存器F0、D0或者S0返回。
3. 彙編程序調用C程序
彙編程序的書寫要遵循ATPCS規則,以保證程序調用時參數正確傳遞。
在彙編程序中調用C程序的方法爲:首先,在彙編程序中使用IMPORT僞指令聲明將要調用的C語言函數;然後,通過BL指令來調用C函數。
例如在一個C源文件中定義瞭如下求和函數:
int add(int x,int y)
{
return(x+y);
}
調用add()函數的彙編程序結構如下:IMPORT add ;聲明要調用的C函數
……
MOV r0,1
MOV r1,2
BL add ;調用C函數add
……
當進行函數調用時,使用r0和r1實現參數傳遞,返回結果由r0帶回。函數調用結束後,r0的值變成3。4. C程序調用匯編程序
C程序調用匯編程序時,彙編程序的書寫也要遵循ATPCS規則,以保證程序調用時參數正確傳遞。
在C程序中調用匯編子程序的方法爲:首先,在彙編程序中使用EXPORT僞指令聲明被調用的子程序,表示該子程序將在其他文件中被調用;然後在C程序中使用extern關鍵字聲明要調用的彙編子程序爲外部函數。
例如在一個彙編源文件中定義瞭如下求和函數:
EXPORT add ;聲明add子程序將被外部函數調用
……
add ;求和子程序add
ADD r0,r0,r1
MOV pc,lr
……
在一個C程序的main()函數中對add彙編子程序進行了調用:
extern int add (int x,int y); //聲明add爲外部函數
void main(){
int a=1,b=2,c;
c=add(a,b); //調用add子程序
...
}
當main()函數調用add彙編子程序時,變量a、b的值會給了r0和r1,返回結果由r0帶回,並賦值給變量c。函數調用結束後,變量c的值變成3。
5. C程序中內嵌彙編語句
在C語言中內嵌彙編語句可以實現一些高級語言不能實現或者不容易實現的功能。對於時間緊迫的功能也可以通過在C語言中內嵌彙編語句來實現。內嵌的彙編器支持大部分ARM指令和Thumb指令,但是不支持諸如直接修改PC實現跳轉的底層功能,也不能直接引用C語言中的變量。
三、C程序中內嵌彙編語句
內聯彙編格式如下:
__asm__ __volatile__(
"Instruction List"
:Output Operand List
:Input Operand List
:Clobber/Modify List
);
下面是個簡單的內嵌彙編樣例。
#include <stdio.h>
int main(int argc, char **argv)
{
int in = 100;
int out;
__asm__ __volatile__(
"mov r0, %[input]\n"
"mov %[output], r0\n"
:[output]"=r"(out)
:[input]"r"(in)
:"r0"
);
printf("out = %d\n", out);
return 0;
}
(1). __asm__是GCC 關鍵字asm 的宏定義。__asm__或asm用來聲明一個內聯彙編表達式,所以任何一個內聯彙編表達式都是以它開頭的,是必不可少的。
#define __asm__ asm
(2). __volatile__是GCC 關鍵字volatile 的宏定義。__volatile__或volatile 是可選的。如果用了它,則是向GCC 聲明不允許對該內聯彙編優化,否則當使用了優化選項(-O)進行編譯時,GCC 將會根據自己的判斷決定是否將這個內聯彙編表達式中的指令優化掉。
#define __volatile__ volatile
(3). "Instruction List"是指令列表。每條指令都必須被雙引號括起來;兩條指令必須用換行"\n"或分號";"分開。
(4). Output Operand List用來指定當前內聯彙編語句的輸出操作符列表。每一個輸出操作符都由3個部分組成:方括號[]中的符號名,限制字符串"=r",圓括號()中的C表達式構成。輸出操作符之間用逗號","分割。即:[out1]"=r"(value1), [out2]"=r"(value2), ... [outn]"=r"(valuen)
(5). Input Operand List用來指定當前內聯彙編語句的輸入操作符列表。表示方法同上。即:[in1]"=r"(value1), [in2]"=r"(value2), ... [inn]"=r"(valuen)。
(6). Clobber/Modify List通知GCC當前內聯彙編語句可能會對某些寄存器或內存進行修改,希望GCC在編譯時能夠將這一點考慮進去。這種情況一般發生在一個寄存器出現在"Instruction List",但卻不是由Input/Output操作表達式所指定的,也不是在一些Input/Output操作表達式使用"r"約束時由GCC爲其選擇的,同時此寄存器被"Instruction List"中的指令修改,而這個寄存器只是供當前內聯彙編臨時使用的情況。
如果一個內聯彙編語句的Clobber/Modify域存在"memory",那麼GCC會保證在此內聯彙編之前,如果某個內存的內容被裝入了寄存器,那麼在這個內聯彙編之後,如果需要使用這個內存處的內容,就會直接到這個內存處重新讀取,而不是使用被存放在寄存器中的拷貝。因爲這個時候寄存器中的拷貝已經很可能和內存處的內容不一致了。
綜合上述講述,普通的內嵌彙編可以如下表示:
__asm__ __volatile__(
"Instruction 1\n"
"Instruction 2\n"
...
"Instruction n\n"
:[out1]"=r"(value1), [out2]"=r"(value2), ... [outn]"=r"(valuen)
:[in1]"=r"(value1), [in2]"=r"(value2), ... [inn]"=r"(valuen)
:"r0", "r1", ... "rn"
);
輸入/輸出操作符列表中的組成部分之一是限制性字符串。對於ARM處理器,GCC4提供了以下限制:
Constraint |
Usage in ARM state |
Usage in Thumb state |
f |
Floating point registers f0 .. f7 |
Not available |
h |
Not available |
Registers r8..r15 |
G |
Immediate floating point constant |
Not available |
H |
Same a G, but negated |
Not available |
I |
Immediate value in data processing instructions |
Constant in the range 0 .. 255 |
J |
Indexing constants -4095 .. 4095 |
Constant in the range -255 .. -1 |
K |
Same as I, but inverted |
Same as I, but shifted |
L |
Same as I, but negated |
Constant in the range -7 .. 7 |
l |
Same as r |
Registers r0..r7 |
M |
Constant in the range of 0 .. 32 or a power of 2 |
Constant that is a multiple of 4 in the range of 0 .. 1020 |
m |
Any valid memory address |
|
N |
Not available |
Constant in the range of 0 .. 31 |
O |
Not available |
Constant that is a multiple of 4 in the range of -508 .. 508 |
r |
General register r0 .. r15 |
Not available |
w |
Vector floating point registers s0 .. s31 |
Not available |
X |
Any operand |
操作符 |
說明 |
= |
只寫操作,通常用於所有的輸出操作符 |
+ |
讀寫操作符,必須用於輸出操作符 |
& |
只能用於輸出的寄存器,不能使用與輸入操作數相同的寄存器 |