ARM匯編語言和C語言混合編程
ATPCS規則體現了一種模塊化設計的思想,其基本內容是C模塊(函數)和匯編模塊(函數)相互調用的一套規則(C51中也有類似的一套規則)。我感覺比在線匯編功能強大(不用有很多忌諱),條理更清楚(很簡單的幾條規則)。
ATPCS規則內容:
1)寄存器的使用規則
1、子程序之間通過寄存器r0~r3來傳遞參數,當參數個數多於4個時,使用堆棧來傳遞參數。此時r0~r3可記作A1~A4。
2、在子程序中,使用寄存器r4~r11保存局部變量。因此當進行子程序調用時要注意對這些寄存器的保存和恢復。此時r4~r11可記作V1~V8。
3、寄存器r12用於保存堆棧指針SP,當子程序返回時使用該寄存器出棧,記作IP。
4、寄存器r13用作堆棧指針,記作SP。寄存器r14稱為鏈接寄存器,記作LR。該寄存器用於保存子程序的返回地址。
5、寄存器r15稱為程序計數器,記作PC。
2)堆棧的使用規則
ATPCS規定堆棧采用滿遞減類型(FD,Full Descending),即堆棧通過減小存儲器地址而向下增長,堆棧指針指向內含有效數據項的最低地址。
3)參數的傳遞規則
1、整數參數的前4個使用r0~r3傳遞,其他參數使用堆棧傳遞;浮點參數使用編號最小且能夠滿足需要的一組連續的FP寄存器傳遞參數。
2、子程序的返回結果為一個32位整數時,通過r0返回;返回結果為一個64位整數時,通過r0和r1返回;依此類推。結果為浮點數時,通過浮點運算部件的寄存器F0、D0或者S0返回。
比較有條理,很清楚,我舉兩個例子:
1.匯編主程序調用C子程序
匯編程序的書寫要遵循ATPCS規則,以保證程序調用時參數正確傳遞。在匯編程序中調用C程序的方法為:首先在匯編程序中使用IMPORT偽指令或者extern事先聲明將要調用的C語言函數;然后通過BL指令來調用C函數。
例如在一個C源文件中定義了如下求和函數:
int add(int x,int y){
return(x+y);
}
調用add()函數的匯編程序結構如下:
area main,code,readonly ;代碼段
entry ;聲明程序入口
code32 ;32位ARM指令
IMPORT add 或者extern add;聲明要調用的C函數
start
……
MOV r0,1
MOV r1,2
BL add ;調用C函數add
……
end
當進行函數調用時,使用r0和r1實現參數傳遞,返回結果由r0帶回。函數調用結束后,r0的值變成3。
2.C主程序調用匯編子程序
C程序調用匯編程序時,匯編程序的書寫也要遵循ATPCS規則,以保證程序調用時參數正確傳遞。在C程序中調用匯編子程序的方法為:首先在匯編程序中使用EXPORT偽指令聲明被調用的子程序,表示該子程序將在其他文件中被調用;然后在C程序中使用extern關鍵字聲明要調用的匯編子程序為外部函數。
例如在一個匯編源文件中定義了如下求和函數:
EXPORT add ;聲明add子程序將被外部函數調用
……
add ;求和子程序add
global 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。
3、C程序中內嵌匯編語句
在C語言中內嵌匯編語句可以實現一些高級語言不能實現或者不容易實現的功能。對於時間緊迫的功能也可以通過在C語言中內嵌匯編語句來實現。內嵌的匯編器支持大部分ARM指令和Thumb指令,但是不支持諸如直接修改PC實現跳轉的底層功能,也不能直接引用C語言中的變量。
內嵌匯編:在C和C++語言中嵌入匯編語言可以實現一些高級語言中沒有的功能。
語法
__asm__( ;注意:前面是兩個“_”
“instruction
...
instruction”
);//Linux gcc中支持
__asm{
instruction
...
instruction
}; //ADS中支持
asm(“instruction[; instruction]”); //ARM C++中使用
ARM內嵌匯編語法
asm(
匯編語句模板:
輸出部分:
輸入部分:
修改部分
);
比如: asm("mov %0, %1, ror #1" :"=r" (result) : "r" (value));
共四個部分:匯編語句模板,輸出部分,輸入部分,破壞描述部分,各部分使用“:”格開,匯編語句模板必不可少,其他三部分可選,如果使用了后面的部分,而前面部分為空,也需要用“:”格開,相應部分內容為空。例如:
__asm__ __volatile__(
"CLI":
:"memory"
);
示例:/* main.c */
void __main(void)
{
int var=0xAA;
__asm //內嵌匯編標識
{
MOV R1,var
CMP R1,#0xAA
}
while(1);
}
擴展知識:匯編語句模板:
匯編語句模板由匯編語句序列組成,語句之間使用“;”、“\n”或“\n\t”分開。指令中的操作數可以使用占位符引用C語言變量,操作數占位符最多10個,名稱如下:%0,%1…,%9。
The assembler instructions, defined as asingle string constant: "mov %0, %1, ror #1"
輸出部分:輸出部分描述輸出操作數,不同的操作數描述符之間用逗號格開,每個操作數描述符由限定字符串和C語言變量組成。每個輸出操作數的限定字符串必須包含“=”表示他是一個輸出操作數。
"=r" (result)
輸入部分:輸入部分描述輸入操作數,不同的操作數描述符之間使用逗號格開,每個操作數描述符由限定字符串和C語言表達式或者C語言變量組成。
"r" (value)
修改部分(modify):這部分常常以“memory”為約束條件,以表示操作完成后內存中的內容已有改變,如果原來某個寄存器的內容來自內存,那么現在內存中這個單元的內容已經改變。
asm(code: outputoperandlist: inputoperandlist: clobberlist);
asm("mov %0, %1, ror #1" :"=r" (result) : "r" (value));
%0 refers to "=r" (result) and %1 refers to "r" (value)
產生的匯編語句:The compiler selected registerr3 for bit rotation.
It could have selected any other register,though.
ldr r3, [sp, #0]
mov r3, r3, ror #1
str r3, [sp, #4]
You can add the volatile attribute to theasm statement to instruct the compiler not to optimize your assembler code.
asm volatile("mov %0, %1, ror#1" : "=r" (result) : "r" (value));
限制字符:作用是指示編譯器如何處理其后的C語言變量與指令操作數之間的關系,例如是將變量放在寄存器中還是放在內存中等,
字母 含義
m,v,o 表示內存單元
R 表示任何通用寄存器
Q 表示寄存器eax, ebx, ecx,edx之一
I, h 表示直接操作數
E, F 表示浮點數
G 表示“任意”
a,b,c,d 表示要求使用寄存器eax/ax/al, ebx/bx/bl, ecx/cx/cl或edx/dx/dl
S, D 表示要求使用寄存器esi或edi
& 該輸出操作數不能使用過河輸入操作數相同的寄存器
% 該操作數可以和下一個數交換位置,如add
I 表示常數(0~31)
以上代碼均在ARM ADS 1.2或者在linux環境中調試通過。
總結一下,在線匯編比較簡潔,但功能有限,而且規則有點零碎;
ATPCS功能強大而且條理清楚,但需要單獨在開一個模塊;寫代碼時可以根據需要自由選擇。
代碼下載鏈接:http://download.csdn.net/detail/klcf0220/5498399
參考鏈接:http://guona081.blog.163.com/blog/static/847604692008102010366320/
ATPCS規范:http://www.cnblogs.com/luvi/archive/2008/04/30/1177961.html
喜歡開源,樂意分享的大神們,歡迎加入QQ群:176507146,你值的擁有哦!