ARM中ADS環境下C語言和彙編語言混合編程及示例

 在嵌入式系統開發中,目前使用的主要編程語言是C 和彙編,雖然C++已經有相應的編譯器,但是現在使用還是比較少的。
    在稍大規模的嵌入式程序設計中,大部分的代碼都是用C來編寫的,主要是因爲C語言具有較強的結構性,便於人的理解,並且具有大量的庫支持。但對於一寫硬件上的操作,很多地方還是要用到彙編語言,例如硬件系統的初始化中的CPU 狀態的設定,中斷的使能,主頻的設定,RAM控制參數等。另外在一些對性能非常敏感的代碼塊,基於彙編與機器碼一一對應的關係,這時不能依靠C編譯器的生成代碼,而要手工編寫彙編,從而達到優化的目的。彙編語言是和CPU的指令集緊密相連的,作爲涉及底層的嵌入式系統開發,熟練對應彙編語言的使用也是必須的。
    單純的C或者彙編編程請參考相關的書籍或者手冊,這裏主要討論C和彙編的混合編程,包括相互之間的函數調用。下面分四種情況來進行討論,不涉及C++語言。

一、在C語言中內嵌彙編
    在C中內嵌的彙編指令包含大部分的ARM和Thumb指令,不過使用與單純的彙編程序使用的指令略有不同,存在一些限制,主要有下面幾個方
面:
    a 不能直接向PC 寄存器賦值,程序跳轉要使用B或者BL指令;
    b 在使用物理寄存器時,不要使用過於複雜的C表達式,避免物理寄存器衝突;
    c R12和R13可能被編譯器用來存放中間編譯結果,計算表達式值時可能把R0-R3、R12及R14用於子程序調用,因此避免直接使用這些物理寄
存器;
    d 一般不要直接指定物理寄存器;
    e 讓編譯器進行分配內嵌彙編使用的標記是__asm或asm關鍵字,用法如下:__asm{instruction [; instruction]}或 asm("instruction

[; instruction]")。
    下面是一個例子來說明如何在C中內嵌彙編語言:
//C語言文件*.c
http://hi.baidu.com/procatlaw/
#include <stdio.h>
void my_strcpy(const char *src, char *dest){
char ch;
       __asm{
              loop:
                     ldrb ch, [src], #1
                     strb ch, [dest], #1
                     cmp ch, #0
                     bne loop
       }
}
int main(){
       char *a="forget it and move on!";
       char b[64];
       my_strcpy(a, b);
       printf("original: %s", a);
       printf("copyed: %s", b);
       return 0;
}
    在此例子中C語言和彙編之間的值傳遞是用C語言的指針來實現的,因爲指針對應的是地址,所以彙編中也可以訪問。

 


二、在彙編中使用C定義的全局變量
    內嵌彙編不用單獨編輯彙編語言文件,比較簡潔,但是有很多的限制。當彙編的代碼較多時一般放在單獨的彙編文件中,這時就需要在匯

編文件和C文件之間進行一些數據的傳遞,最簡便的辦法就是使用全局變量。
    下面是一個C語言和彙編語言共享全局變量的例子:
//C語言文件*.c
http://hi.baidu.com/procatlaw/
#include <stdio.h>
int gVar=12;
extern asmDouble(void);
int main(){
       printf("original value of gVar is: %d", gVar_1);
       asmDouble();
       printf(" modified value of gVar is: %d", gVar_1);
       return 0;
}

;彙編語言文件*.S http://hi.baidu.com/procatlaw/
       AREA asmfile, CODE, READONLY EXPORT asmDouble
       IMPORT gVar
asmDouble
       ldr r0, =gVar
       ldr r1, [r0]
       mov r2, #2
       mul r3, r1, r2
       str r3, [r0]
       mov pc, lr
       END
    在此例中,彙編文件與C文件之間相互傳遞了全局變量gVar和函數asmDouble,留意聲明的關鍵字extern和IMPORT

 


三、在C中調用匯編的函數
    有一些對機器要求高的敏感函數,通過C語言編寫再通過C編譯器翻譯有時會出現誤差,因此這樣的函數一般採用彙編語言來編寫,然後供C

語言調用。在C文件中調用匯編文件中的函數,要注意的有兩點,一是要在C文件中聲明所調用的彙編函數原型,並加入extern關鍵字作爲引入

函數的聲明;二是在彙編文件中對對應的彙編代碼段標識用EXPORT關鍵字作爲導出函數的聲明,函數通過mov pc, lr指令返回。這樣,就可以

在C文件中使用該函數了。從C語言的角度的角度,並不知道調用的函數的實現是用C語言還是彙編彙編語言,原因C語言的函數名起到表明函數

代碼起始地址的作用,而這個作用和彙編語言的代碼段標識符是一致的。
    下面是一個C語言調用匯編函數例子:
//C語言文件*.c
http://hi.baidu.com/procatlaw/
#include <stdio.h>
extern void asm_strcpy(const char *src, char *dest);
int main(){
       const char *s="seasons in the sun"; char d[32];
       asm_strcpy(s, d);
       printf("source: %s", s);
       printf(" destination: %s",d);
       return 0;
}

;彙編語言文件*.S http://hi.baidu.com/procatlaw/
       AREA asmfile, CODE, READONLY
       EXPORT asm_strcpy
asm_strcpy
       loop
       ldrb r4, [r0], #1
       cmp r4, #0
       beq over
       strb r4, [r1], #1
       b loop
       over
       mov pc, lr
       END
    在此例中,C語言和彙編語言之間的參數傳遞是通過對應的用R0-R3來進行傳遞,即R0傳遞第一個參數,R1傳遞第二個參數,多於4個時藉助
棧完成,函數的返回值通過R0來傳遞。這個規定叫作ATPCS(ARM Thumb Procedure Call Standard),具體見ATPCS規範。

 


四、在彙編中調用C的函數
    在彙編語言中調用C語言的函數,需要在彙編中IMPORT對應的C函數名,然後將C的代碼放在一個獨立的C文件中進行編譯,剩下的工作由連
接器來處理。
    下面是一個彙編語言調用C語言函數例子:
//C語言文件*.c
http://hi.baidu.com/procatlaw/

int cFun(int a, int b, int c){
       return a+b+c;
}

;彙編語言文件*.S http://hi.baidu.com/procatlaw/
       AREA asmfile, CODE, READONLY
       EXPORT cFun
start
       mov r0, #0x1
       mov r1, #0x2
       mov r2, #0x3
       bl cFun
       nop
       nop
       b start
       END

    在彙編語言中調用C語言的函數,參數的傳遞也是按照ATPCS規範來實現的。

    在這裏簡單介紹一下部分ATPCS規範:

    子程序間通過寄存器R0~R3來傳遞參數。
    A.在子程序中,使用寄存器R4~R11來保存局部變量。
    B.寄存器R12用於子程序間scratch寄存器(用於保存SP,在函數返回時使用該寄存器出桟),記作IP。
    C.寄存器R13用於數據棧指針,記作SP。寄存器SP在進入子程序時的值和退出子程序時的值必須相等。  
    D.寄存器R14稱爲鏈接寄存器,記作LR。它用於保存子程序的返回地址。
    E.寄存器R15是程序計數器,記作PC
    F.參數不超過4個時,可以使用寄存器R0~R3來傳遞參數,當參數超過4個時,還可以使用數據棧來傳遞參數。
    G.結果爲一個32位整數時,可以通過寄存器R0返回
    H.結果爲一個64位整數時,可以通過寄存器R0和R1返回,依次類推。

    以上通過幾個簡單的例子演示了嵌入式開發中常用的C 和彙編混合編程的一些方法和基本的思路,其實最核心的問題就是如何在C 和彙編之間傳值,剩下的問題就是各自用自己的方式來進行處理。以上只是拋磚引玉,更詳細和複雜的使用方法要結合實際應用並參考相關的資料。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章