Linux ARM 下C嵌套匯編

在C中嵌套匯編的作用:
1) 彙編執行的代碼效率更高;
2) 某些操作使用匯編編寫代碼更方便,如對協處理器的操作;

那麼如何在C中嵌套匯編呢?
基本語法:
asm volatile (“code” : output : input : modify describtion);
如:
asm volatile(
“asm instruction \n”
“asm instruction \n”
“asm instruction \n”
………
“asm instruction \n”
:輸出(如“=r”(val))
:輸入(如“0”(val))
:描述(如”memory”,”cc”)
);
指令序列:在嵌套匯編中%0 %1 %2 分別表示參數0,參數1,參數2,最多可以到%10(別的博客裏看到的,沒有試驗過)。每條指令後面都有一個\n作爲分割符。
輸出部分:可以用於指定%n輸出到哪個變量,如第一個輸出爲”=r”(val),表示要將指令序列中的%0的值保存到val中,並且val的值需要用寄存器保存。多個輸出參數用逗號分開。
輸入部分:可以指定參數列表,如”0”(參數1) ,”1”(參數2)…..多個輸入參數用逗號分開。

下面舉例子說明一下:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 main()
  4 {
  5     int a=6,b=13,c;
  6 //  int a=6,int b=13,int c;
  7 //  int a=6;
  8 //  int b=14;
  9 //  int c;
 10     __asm__ volatile(
 11         "mov r5,%0\n"   //r5=6
 12         "mov r6,%1\n"   //r6=13
 13         "add %2,%0,#2\n"    //%2=8,a=8
 14         :"=r"(b),"=r"(c),"=r"(a)    //6,13,8
 15         :"0"(a),"1"(b),"2"(c)//6 13
 16         :"memory","cc"
 17     );
 18     printf("a b c %d %d %d \n",a,b,c);
 19 }
~                    

運行結果是:a b c 8 6 13
爲什麼呢,首先在輸入部分指定了%0=a=6,%1=b=13,%2=c.
在指令序列中運算之後,得到的是%2=8。由於輸出部分指定了a爲%2,因此此時a=8。
b的值是%0,%0是誰呢?看輸入部分%0是a,那麼b就是6。輸出部分指定了c的值要用%1表示,而輸入部分將%1設置爲13,因此c爲13。
所以最終結果就是 a=8,b=6,c=13.
在分析嵌套匯編的時候,要先看輸入,再看指令序列,最後看輸出。
可以看出,用C嵌套匯編,要交換2個變量的值是很容易的。如下面的例子就是交換了ab的值:main()
4 {
5 int a=6,b=13,c;
6 // int a=6,int b=13,int c;
7 // int a=6;
8 // int b=14;
9 // int c;
10 __asm__ volatile(
11 "nop\n"
12 :"=r"(b),"=r"(a) //6,13,8
13 :"0"(a),"1"(b)//6 13
14 :"memory","cc"
15 );
16 printf("a b c %d %d %d \n",a,b,c);

運行結果:a b c 13 6 0 ,可見,ab的值已經交換了。
常見的交換AB值的操作:
1.嵌套匯編,指定輸入輸出部分即可
2.異或運算:
{a = a ^ b; b = a ^ b; a = a ^ b;}
不過這種方法只能交換整數,因爲一元運算符 ^ 要求操作數是整數。
3.加減法:
a=a+b-a;b=a+b-a;但是容易發生溢出.
4.新建變量c。c=a;a=b;b=c;

需要注意的問題是,在默認情況下,指令序列中的%0是由R0來存儲的,%1是R1存儲的
如下代碼:

這裏寫代碼片
```__asm__ __volatile__ (
 95     "adrl   r0, main\n"
 96     "ldr    r1,=0xb0003000\n"
 97     "mov    %0,r0\n"
 98     "mov    %1,r1\n"
 99     "sub    %2,%1,%0\n"
100     :"=r"(phy),"=r"(vaddr),"=r"(delat)
101     :"0"(phy),"1"(vaddr),"2"(delat)
102     );
@               
這是個裸板程序,main函數是源文件中第一個被編譯的文件,首先被執行,我們把他的鏈接地址指定爲0xb0003000。我們把編譯好的文件,用TFTP下載到板子的0xa8003000處,執行如下命令:
mw.b a8003000 ff 2000;tftp a8003000 test.bin;go 0xa8003000
看打印效果:
runaddr is 0xa8003000 linkaddr is 0xa8003000,offset is 0
很明顯,這不是我們想要的,我們已經指定了linkaddr爲0xb0003000。爲什麼呢?怎麼改正呢?
反彙編看一下





<div class="se-preview-section-delimiter"></div>

b0003048: e24f0050 sub r0, pc, #80 ; 0x50
27 b000304c: e1a00000 nop ; (mov r0, r0)
28 b0003050: e59f17e4 ldr r1, [pc, #2020] ; b000383c

__asm__ __volatile__ (
 95     "adrl   r6, main\n"
 96     "ldr    r7,=0xb0003000\n"
 97     "mov    %0,r6\n"
 98     "mov    %1,r7\n"
 99     "sub    %2,%1,%0\n"
100     :"=r"(phy),"=r"(vaddr),"=r"(delat)
101     :"0"(phy),"1"(vaddr),"2"(delat)
102     //:"r0","r1","r2"
103     );

用r6/r7代替原來的r0/r1,但是這樣依然是不安全的,最好是再明確地告訴編譯器保護部分。
常見的描述信息
“memory”內存有改動,不使用寄存器做緩存。
“cc”
“registerr” //告訴編譯器某些寄存器要在彙編指令中使用
詳細的可以看看:
http://www.ethernut.de/en/documents/arm-inline-asm.html
“r12”, “cc”
informs the compiler that the assembly code modifies register r12 and updates the condition code flags
.

在彙編中調用C函數,如果要傳遞參數怎麼辦呢?
通常情況下,對於參數傳遞,分兩種情況。一種情況是,本身傳遞的參數不多
於 4 個,就可以通過寄存器 r0~r3 傳送參數。因爲在前面的保存現場的動作中,已經保存好了對應的寄存器的值,那麼此時,這些寄存器就是空閒的,可以供我們使用的了,那就可以放參數。
另一種情況是,參數多於 4 個時,寄存器不夠用,就得用棧了。
因此棧的作用:
1.保護現場。//保存被調用時的相關寄存器
2.參數傳遞 //
3.保存臨時變量。//…
下面舉個例子:
看看下面的arm的 異常向量表的建立方式。
所謂異常向量,是arm工作異常模式的時候才進入的。
除了系統、用戶模式外剩下的5種都屬於異常(現在多了一個監視模式)
即管理模式、未定義指令模式、數據訪問終止模式、中斷、快速中斷模式。
但是異常向量有:復位、未定義、swi、指令預取、數據異常、保留、中斷、快速中斷
共32個字節。

 __asm__ (
347 "vectors:\n"
348     "b reset\n" 
349     "b und\n"
350     "b swi\n"
351     "b pre_abt\n"
352     "b dat_abt\n"
353     ".word 0\n"
354     "b irq\n"
355     "b fiq\n"
356 "reset:\n"
357 "und:\n"
358     "mov sp, #0x94000000\n" //未定義指令、復位的時候的棧頂。
359     "stmfd sp!, {r0-r12, lr}\n"//保護現場
360
361     "mov r0, sp\n"//參數傳遞
362     "mov r3, #0x94000000\n"
363     "ldr r3, [r3]\n"//
364     "blx r3\n"//執行異常處理函數
365 
366     "mov sp, #0x94000000\n" //
367     "ldmea sp, {r0-r12, pc}^\n"//恢復現場。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章