一、C嵌套匯編linux
首先說一下關於GCC編譯嵌有彙編語言的c語言吧,GCC編譯的彙編語言不是咱們上課時學的Intel x86彙編,而是AT&T彙編,二者的區別能夠查看《Gcc使用的內嵌彙編語法格式小教程》。編程
下面是內嵌彙編的格式:語法:__asm__(「instruction函數
…… instruction"); //Linux gcc中支持(注意asm的下劃線均爲兩個不然GCC將會沒法編譯。)oop
__asm{指針
instruction調試
instruction教程
}; //ADS中支持(注意asm的下劃線均爲兩個不然GCC將會沒法編譯。)內存
asm(「instruction [; instruction]」); //ARM C++中使用 開發
例1是我在linux環境下,編的嵌有彙編程序的c語言,並經過了GCC的編譯:字符串
例1:
#include<stdio.h>
int plus(int a,int b)
{
__asm__
(
「add %1,%0\n\t」:」+r」(a):」r」(b)
);
return (c);
}
int main()
{int a,b,c;
a=2;
b=1;
c=plus(a,b);
printf(「c=%d\n」,c);
}
這個程序應該是很簡單的,但關鍵是子函數中嵌入的那段彙編程序,具體的寫法能夠參看其餘文章。
例2一樣是c語言中嵌入了彙編,與例1不一樣的是,這個程序的編譯環境爲ADS。
例2
#include <stdio.h>
void my_strcpy(char* src, const char* dst){
int ch;
__asm{
loop:
LDRB ch, [src], #1
STRB ch, [dst], #1
CMP ch, #0
BNE loop
};
}
int main(void){
const char* a = "Hello World!";
char b[20];
__asm{
MOV R0, a
MOV R1, b
BL my_strcpy, {R0, R1}
};
printf("Original String: %s\n",a);
printf("Copied String: %s\n",b);
return 0;
}
必定要注意例1與例2中彙編語言的語法格式。
二、C語言調用匯編
再說一下如何將一個c語言文件與一個彙編文件經過ADS環境編譯,並經過ATX進行DEBUG調試的。先看一下下面的例3。
例3
Cfile.c
#include <stdio.h>
extern void strcopy(char *d, const char *s);
int main()
{ const char *srcstr = "abcde";
char dststr[32];
/* dststr is an array since we're going to change it */
printf("Before copying:\n");
printf(" '%s'\n '%s'\n",srcstr,dststr);
strcopy(dststr,srcstr);
printf("After copying:\n");
printf(" '%s'\n '%s'\n",srcstr,dststr);
return 0;
}
Asmfile.s
AREA SCopy, CODE, READONLY
EXPORT strcopy
strcopy
; r0 points to destination string
; r1 points to source string
LDRB r2, [r1],#1 ; load byte and update address
STRB r2, [r0],#1 ; store byte and update address;
CMP r2, #0 ; check for zero terminator
BNE strcopy ; keep going if not
MOV pc,lr ; Return
END ;注意!!彙編代碼編寫時必定要縮進,不然編譯將會出錯
這是一個c語言調用匯編的例子,功能是爲了實現字符串的拷貝,其中彙編文件爲字符串拷貝的功能子函數。在這裏須要說明的是c語言調用匯編語言的一些基本規則,首先是參數傳遞的規則,c語言的函數前4個參數經過R0-R3來傳遞,其它參數經過堆棧(FD)傳遞,且這種傳遞是單項的,即彙編語言中的R0-R3的值不會再回傳給c語言。拿例3舉例來講,當在語言中調用strcopy(dststr,srcstr);時,字符串dststr的首地址將會傳給r0,srcstr的首地址將會傳給r1,當彙編語言拿到這兩個寄存器時,就會經過地址依次加1的形式進行地址內容的複製也就是字符串的複製,當複製到最後一個字母e時,經過比較r2寄存器中的值是否爲0來判斷是否調出彙編程序(由於在c語言中聲明字符串時末尾被自動的添加了一個\0),這裏須要注意的是,此時寄存器r0的值爲指向源字符串末尾的’\0’的地址值,而寄存器r1的值爲指向已經拷貝過的目的字符串中的」e」的地址值,當調出彙編程序時,r0,r1這兩個值將不會回傳給strcopy(dststr,srcstr);中的兩個參數dststr和srcstr,這兩個參數的值仍然是c語言在初始化這兩個字符串時指向字符串的首地址,這一點能夠經過ATX調試時觀察寄存器的變化狀況來證實。可是爲何地址值沒有變化,但卻實現了字符串的拷貝了呢?這主要時由於經過彙編程序,雖然沒有改變兩個指針的位置,但卻改變了兩個字符串所在內存地址中的內容,這種方式就是c語言中常說的引用方式,即dststr和r0起初指向的是同一內存空間,可是字符串複製時只是利用r0來複制的,而dststr的位置卻沒有發生變化。所以在c語言中輸出字符串時並不須要將dststr減去字符串的個數來實現指向字符串的首地址。
這個程序中第二個須要注意的地方是,彙編程序段中的起到臨時存放字符串的r2寄存器,很奇怪的是這個地方的寄存器不能換成r4,若是換成r4的話,輸出的結果就會有問題,這一點我如今尚未找到答案,但願未來某一天能碰見高人給我指點一下。
最後須要注意的地方是在彙編程序末尾必定要加上MOV pc, lr
用ADS編譯後,兩個文件會被自動的連接,並在工程文件夾下生成一個.o文件,這個文件就是未來要下到開發板上的二進制文件,其中還有一個.axf的鏡像文件,這個文件是用來進行ATX調試的,默認的單步調試是在反彙編中進行的,這就會給調試程序帶來極大的不便,經過本身的摸索,發現能夠經過設置strong source實如今c語言中進行單步調試,兩外在單步調試中經過watch來觀察c語言中的形參的值和地址的變化狀況,便於程序的調試,須要強調的一點時,彙編程序與c程序的文件名不能相同,不然將沒法用ATX進行調試。
另外,在彙編程序中訪問c程序全局變量的例子。程序中變量globvl是在c程序中聲明的全局變量。在彙編程序中首先用IMPORT僞操做聲明該變量;再將其內存地址讀入到寄存器R1中;再將其值讀入到寄存器R0中;修改後再將寄存器R0的值賦於變量globvl。請參看例4
例4
#include <stdio.h>
int globvl;
int main(){
globvl = 0;
asmsub();
printf(「globvl = %d」, globvl);
return 0;
}
AREA globals, CODE, READONLY
EXPORT asmsub
IMPORT globvl
asmsub
LDR r1, =globvl
LDR r0, [r1]
ADD r0, r0, #2
STR r0, [r1]
MOV pc, lr
END ;注意!!彙編代碼編寫時必定要縮進,不然編譯將會出錯
三、彙編調用c
最後我再談一下如何在彙編中調用c,看一下例5
例5
int g(int a, int b, int c, int d, int e)
{
return a + b + c + d + e;
}
;彙編程序調用c程序g()計算5個整數i, 2*i, 3*i, 4*i, 5*i的和
EXPORT f
AREA f, CODE, READONLY
IMPORT g ;使用僞操做數IMPORT聲明c程序g()
STR lr, [sp,#-4]! ;保存返回地址
ADD r1, r0, r0 ;假設進入程序f時,r0中的值爲i,r1值設爲2*i
ADD r2, r1, r0 ;r2的值設爲3*i
ADD r3, r1, r2 ;r3的值設爲5*i
STR r3, [sp, # -4]! ;第五個參數5*i經過數據棧傳遞
ADD r3, r1, r1 ;r4值設爲4*i
BL g ;調用c程序g()
ADD sp, sp, #4 ;調整數據棧指針,準備返回
LDR pc, [sp], #4 ;返回
END ;注意!!彙編代碼編寫時必定要縮進,不然編譯將會出錯
注意,c語言最終返回的五個數之和放到了r0寄存器中。