Linux ARM下Cネストアセンブリ

11149 ワード

Cにネストされたアセンブリの役割:1)アセンブリ実行のコード効率がより高い;2)コプロセッサの操作など、アセンブリを使用してコードを記述する操作が便利である.
では、どのようにしてCにアセンブリをネストしますか?基本構文:asm volatile(「code」:output:input:modify describtion);例えば、asm volatile(「asm instruction」「asm instruction」「asm instruction」……「asm instruction」:出力(例えば「=r」(val)):入力(例えば「0」(val):記述(例えば「memory」、「cc」);命令シーケンス:ネストされたアセンブリで%0%1%2はそれぞれパラメータ0、パラメータ1、パラメータ2を表し、最大%10(他のブログで見たもので、試験したことがありません)まで使用できます.各命令の後ろには分割子として1つあります.出力部:命令シーケンスの%0の値をvalに保存し、valの値をレジスタで保存する必要があることを示す%nがどの変数に出力されるかを指定できます.複数の出力パラメータをカンマで区切ります.入力部:「0」(パラメータ1)、「1」(パラメータ2)などのパラメータリストを指定できます.複数の入力パラメータをカンマで区切ります.
次に例を挙げて説明します.
  1 #include
  2 #include
  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
"
//r5=6 12 "mov r6,%1
"
//r6=13 13 "add %2,%0,#2
"
//%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
"
,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
"
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
",a,b,c);
実行結果:a b c 13 6 0、abの値が交換されたことがわかります.よくあるAB値の交換操作:1.ネストアセンブリは、入出力部を指定すればよい.排他演算:{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はR 0によって格納され、%1はR 1によって格納される次のコードである.
      
```__asm__ __volatile__ (
 95     "adrl   r0, main
"
96 "ldr r1,=0xb0003000
"
97 "mov %0,r0
"
98 "mov %1,r1
"
99 "sub %2,%1,%0
"
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
"
96 "ldr r7,=0xb0003000
"
97 "mov %0,r6
"
98 "mov %1,r7
"
99 "sub %2,%1,%0
"
100 :"=r"(phy),"=r"(vaddr),"=r"(delat) 101 :"0"(phy),"1"(vaddr),"2"(delat) 102 //:"r0","r1","r2" 103 );

元のr 0/r 1の代わりにr 6/r 7を用いるが、それでも安全ではないので、コンパイラ保護部分をさらに明確に伝えることが望ましい.一般的な説明情報「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関数を呼び出し、パラメータを渡すにはどうすればいいですか?通常、パラメータ伝達には2つのケースがあります.1つは、自身が伝達するパラメータが4個以下であれば、レジスタr 0~r 3を介してパラメータを伝達することができる.前のセーブフィールドの動作では、対応するレジスタの値が保存されているので、この場合、これらのレジスタは空きであり、私たちが使用できるようになり、パラメータを置くことができます.もう1つの場合、パラメータが4つ以上ある場合、レジスタが足りないのでスタックを使わなければなりません.したがってスタックの役割:1.現場を守る.//呼び出すときの相関レジスタ2を保存する.パラメータ伝達//3.一時変数を保存します.//...次に例を挙げます.次のarmの異常ベクトルテーブルの作成方法を見てみましょう.異常ベクトルとはarm動作異常モードのときに入るものです.システム、ユーザモードを除いた残りの5つは異常(現在は1つの監視モードが増えている)である管理モード、未定義命令モード、データアクセス終了モード、割り込み、高速割り込みモードである.ただし、異常ベクトルは、リセット、未定義、swi、命令プリフェッチ、データ異常、保持、割り込み、高速割り込みの32バイトである.
 __asm__ (
347 "vectors:
"
348 "b reset
"
349 "b und
"
350 "b swi
"
351 "b pre_abt
"
352 "b dat_abt
"
353 ".word 0
"
354 "b irq
"
355 "b fiq
"
356 "reset:
"
357 "und:
"
358 "mov sp, #0x94000000
"
// 、 。 359 "stmfd sp!, {r0-r12, lr}
"
// 360 361 "mov r0, sp
"
// 362 "mov r3, #0x94000000
"
363 "ldr r3, [r3]
"
// 364 "blx r3
"
// 365 366 "mov sp, #0x94000000
"
// 367 "ldmea sp, {r0-r12, pc}^
"
// 。