在針對ARM體系結構的編程中,通常很難直接使用C語言產生操做協處理器的相關代碼,所以使用匯編語言來實現就成爲了惟一的選擇。但若是徹底經過彙編代碼實現,又會過於複雜、難以調試。所以,C語言內嵌彙編的方式卻是一個不錯的選擇。然而,使用內聯彙編的一個主要問題是,內聯彙編的語法格式與使用的編譯器直接相關,也就是說,使用不一樣的C編譯器內聯彙編代碼時,它們的寫法是各不相同的。下面介紹在ARM體系結構下GCC的內聯彙編。GCC內聯彙編的通常格式:linux
asm(
代碼列表
: 輸出運算符列表
: 輸入運算符列表
: 被更改資源列表
);編程
在C代碼中嵌入彙編須要使用asm關鍵字,在asm的修飾下,代碼列表、輸出運算符列表、輸入運算符列表和被更改的資源列表這4個部分被3個「:」分隔。下面,咱們看一個例子:函數
void test(void)
{
……
asm(
"mov r1,#1\n"
:
:
:"r1"
);
……
} 網站
注:換行符和製表符的使用可使得指令列表看起來變得美觀。你第一次看起來可能有點怪異,可是當C編譯器編譯C語句的是候,它就是按照上面(換行和製表)生成彙編的。調試
函數test中內嵌了一條彙編指令實現將當即數1賦值給寄存器R1的操做。因爲沒有任何形式的輸出和輸入,所以輸出和輸入列表的位置上什麼都沒有填寫。可是,在彙編代碼執行過程當中R1寄存器會被修改,所以爲了通知編譯器,在被更改資源列表中,須要寫上寄存器R1。htm
寄存器被修改這種現象發生的頻率仍是比較高的。例如,在調用某段彙編程序以前,寄存器R1可能已經保存了某個重要數據,當彙編指令被調用以後,R1寄存器被賦予了新的值,原來的值就會被修改,因此,須要將會被修改的寄存器放入到被更改資源列表中,這樣編譯器會自動幫助咱們解決這個問題。也能夠說,出如今被更改資源列表中的資源會在調用匯編代碼一開始就首先保存起來,而後在彙編代碼結束時釋放出去。因此,上面的代碼與以下代碼從語義上來講是等價的。資源
void test(void)
{
……
asm(
"stmfd sp!,{r1}\n"
"mov r1,#1\n"
"ldmfd sp!,{r1}\n"
);
……
} 開發
這段代碼中的內聯彙編既無輸出又無輸入,也沒有資源被更改,只留下了彙編代碼的部分。因爲程序在修改R1以前已經將寄存器R1的值壓入了堆棧,在使用完以後,又將R1的值從堆棧中彈出,因此,經過被更改資源列表來臨時保存R1的值就沒什麼必要了。get
在以上兩段代碼中,彙編指令都是獨立運行的。但更多的時候,C和內聯彙編之間會存在一種交互。C程序須要把某些值傳遞給內聯彙編運算,內聯彙編也會把運算結果輸出給C代碼。此時就能夠經過將適當的值列在輸入運算符列表和輸出運算符列表中來實現這一要求。請看下面的例子:編譯器
void test(void)
{
int tmp=5;
asm(
"mov r4,%0\n"
:
:"r"(tmp)
:"r4"
);
}
上面的代碼中有一條mov指令,該指令將%0賦值給R4。這裏,符號%0表明出如今輸入運算符列表和輸出運算符列表中的第一個值。若是%1存在的話,那麼它就表明出如今列表中的第二個值,依此類推。因此,在該段代碼中,%0表明的就是「r」(tmp)這個表達式的值了。
那麼這個新的表達式又該怎樣解釋呢?原來,在「r」(tmp)這個表達式中,tmp表明的正是C語言向內聯彙編輸入的變量,操做符「r」則表明tmp的值會經過某一個寄存器來傳遞。在GCC4中與之相相似的操做符還包括「m」、「I」,等等,其含義見下表:
ARM嵌入式開發中的GCC內聯彙編簡介
與輸入運算符列表的應用方法一致,當C語言須要利用內聯彙編輸出結果時,可使用輸出運算符列表來實現,其格式應該是下面這樣的。
void test(void)
{
int tmp;
asm(
"mov %0,#1\n"
:"=r"(tmp)
:
);
}
在上面的代碼中,本來應出如今輸入運算符列表中的運算符,如今出如今了輸出運算符列表中,同時變量tmp將會存儲內聯彙編的輸出結果。這裏有一點可能已經引發你們的注意了,上面的代碼中操做符r的前面多了一個「=」。這個等號被稱爲約束脩飾符,其做用是對內聯彙編的操做符進行修飾。幾種修飾符的含義以下表所示:
ARM嵌入式開發中的GCC內聯彙編簡介
當一個操做符沒有修飾符對其進行修飾時,表明這個操做符是隻讀的。當咱們須要將內聯彙編的結果輸出出來,那麼至少要保證該操做符是可寫的。所以,「=」或者「+」也就必不可少了。
本篇文章來源於 Linux公社網站(www.linuxidc.com) 原文連接:http://www.linuxidc.com/Linux/2012-11/74645.htm