ARM嵌入式開發中的GCC內聯彙編簡介

轉載:http://blog.csdn.net/ce123/article/details/8209702
在針對ARM體系結構的編程中,一般很難直接使用C語言產生操作協處理器的相關代碼,因此使用彙編語言來實現就成爲了唯一的選擇。但如果完全通過彙編代碼實現,又會過於複雜、難以調試。因此,C語言內嵌彙編的方式倒是一個不錯的選擇。然而,使用內聯彙編的一個主要問題是,內聯彙編的語法格式與使用的編譯器直接相關,也就是說,使用不同的C編譯器內聯彙編代碼時,它們的寫法是各不相同的。下面介紹在ARM體系結構下GCC的內聯彙編。GCC內聯彙編的一般格式:
[plain] view plain copy print?
asm(    


     代碼列表    
     : 輸出運算符列表    
     : 輸入運算符列表    
     : 被更改資源列表    
);  
在C代碼中嵌入彙編需要使用asm關鍵字,在asm的修飾下,代碼列表、輸出運算符列表、輸入運算符列表和被更改的資源列表這4個部分被3個“:”分隔。下面,我們看一個例子:
[plain] view plain copy print?
void test(void)  
{    
    ……    
    asm(    
        "mov r1,#1\n"    
        :    
        :    
        :"r1"    
    );    
    ……    
}   

注:換行符和製表符的使用可以使得指令列表看起來變得美觀。你第一次看起來可能有點怪異,但是當C編譯器編譯C語句的是候,它就是按照上面(換行和製表)生成彙編的。
函數test中內嵌了一條彙編指令實現將立即數1賦值給寄存器R1的操作。由於沒有任何形式的輸出和輸入,因此輸出和輸入列表的位置上什麼都沒有填寫。但是,在彙編代碼執行過程中R1寄存器會被修改,因此爲了通知編譯器,在被更改資源列表中,需要寫上寄存器R1。
寄存器被修改這種現象發生的頻率還是比較高的。例如,在調用某段彙編程序之前,寄存器R1可能已經保存了某個重要數據,當彙編指令被調用之後,R1寄存器被賦予了新的值,原來的值就會被修改,所以,需要將會被修改的寄存器放入到被更改資源列表中,這樣編譯器會自動幫助我們解決這個問題。也可以說,出現在被更改資源列表中的資源會在調用匯編代碼一開始就首先保存起來,然後在彙編代碼結束時釋放出去。所以,上面的代碼與如下代碼從語義上來說是等價的。

[plain] view plain copy print?
void test(void)  
{    
    ……    
    asm(    
        "stmfd sp!,{r1}\n"  
        "mov r1,#1\n"    
        "ldmfd sp!,{r1}\n"    
    );    
    ……    
}   
這段代碼中的內聯彙編既無輸出又無輸入,也沒有資源被更改,只留下了彙編代碼的部分。由於程序在修改R1之前已經將寄存器R1的值壓入了堆棧,在使用完之後,又將R1的值從堆棧中彈出,所以,通過被更改資源列表來臨時保存R1的值就沒什麼必要了。
在以上兩段代碼中,彙編指令都是獨立運行的。但更多的時候,C和內聯彙編之間會存在一種交互。C程序需要把某些值傳遞給內聯彙編運算,內聯彙編也會把運算結果輸出給C代碼。此時就可以通過將適當的值列在輸入運算符列表和輸出運算符列表中來實現這一要求。請看下面的例子:
[plain] view plain copy print?
void test(void)  
{    
    int tmp=5;    
    asm(    
        "mov r4,%0\n"    
        :    
        :"r"(tmp)    
        :"r4"    
    );    
}   
上面的代碼中有一條mov指令,該指令將%0賦值給R4。這裏,符號%0代表出現在輸入運算符列表和輸出運算符列表中的第一個值。如果%1存在的話,那麼它就代表出現在列表中的第二個值,依此類推。所以,在該段代碼中,%0代表的就是“r”(tmp)這個表達式的值了。

這裏寫圖片描述

與輸入運算符列表的應用方法一致,當C語言需要利用內聯彙編輸出結果時,可以使用輸出運算符列表來實現,其格式應該是下面這樣的。
[plain] view plain copy print?
void test(void)  
{    
    int tmp;    
    asm(    
        "mov %0,#1\n"    
        :"=r"(tmp)    
        :    
    );    
}   
在上面的代碼中,原本應出現在輸入運算符列表中的運算符,現在出現在了輸出運算符列表中,同時變量tmp將會存儲內聯彙編的輸出結果。這裏有一點可能已經引起大家的注意了,上面的代碼中操作符r的前面多了一個“=”。這個等號被稱爲約束脩飾符,其作用是對內聯彙編的操作符進行修飾。幾種修飾符的含義如下表所示:

這裏寫圖片描述

當一個操作符沒有修飾符對其進行修飾時,代表這個操作符是隻讀的。當我們需要將內聯彙編的結果輸出出來,那麼至少要保證該操作符是可寫的。因此,“=”或者“+”也就必不可少了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章