close

http://fanqiang.chinaunix.net/a4/b8/20010704/130401784_b.html


 


Inline Asm
我將首先開始inline asm, 因為似乎關這方面的疑問非常多。這是最基本的語法了, 就象在線幫助信息
中描述的:
__asm__(asm statements : outputs : inputs : registers-modified); 

這四個字段的含義是: 

asm statements - AT&T 的結構, 每新行都是分開的。 
outputs - 修飾符一定要用引號引起來, 用逗號分隔 
inputs - 修飾符一定要用引號引起來, 用逗號分隔 
registers-modified - 名字用逗號分隔
一個小小的例子: 
__asm__("
pushl %eax\n
movl $1, %eax\n 
popl %eax"
);
假如你不用到特別的輸入輸出變量或者修改任何寄存器的值,一般來說是不會使用到其他的三個字段的, 
讓我們來分析一下輸入變量。

int i = 0;

__asm__("
pushl %%eax\n
movl %0, %%eax\n
addl $1, %%eax\n
movl %%eax, %0\n
popl %%eax"
:
: "g" (i)
); // increment i
不要為上面的代碼所困擾! 我將盡力來解釋它。我們想讓輸入變量i加1,我們沒有任何輸出變量, 也沒有
改變寄存器值(我們保存了eax值)。 因此,第二個和最一個字段是空的。 因為指定了輸入字段, 我們
仍需要保留一個空的輸出字段, 但是沒有最一個字段, 因為它沒被使用。在兩個空冒號之間留下一個新
行或者至少一個空格。

下面讓我們來看看輸入字段。 附加描述符可以修正指令來讓你給定的編譯器來正確處理這些變量。他們
一般被附上雙引號。 那這個"g"是用來做什的呢?  只要是合法的匯編指令,"g"就讓編譯器決定該
在哪裡加載i的值。一般來說,你的大部分輸入變量都可以被賦予"g", 讓編譯器決定如何去加載它們 (gcc
甚至可以優化它們!)。 其他描述符使用"r" (加載到任何可用的寄存器去), "a" (ax/eax), "b" 
(bx/ebx), "c" (cx/ecx), "d" (dx/edx), "D" (di/edi), "S" (si/esi), 等等。 

我們將要提到一個在asm代碼裡面的如%0的輸入變量。如果我們有兩個輸入, 他們會一個是%0一個是%1,
 在輸入段裡按順序排列 (如下一個例子)。假如N個輸入變量且沒有輸出變量, 從%0 到%N-1將和輸入字段
裡的變量相對應, 按順序排列。 

如果任何的輸入, 輸出, 寄存器修改字段被使用, 匯編代碼裡的寄存器名必須用兩個%來代替一個%。對應
第一個沒有使用最三個字段的例子。

讓我們看看兩個輸入變量且引入了"volatile"的例子: 

int i=0, j=1;
__asm__ __volatile__("
pushl %%eax\n
movl %0, %%eax\n
addl %1, %%eax\n
movl %%eax, %0\n
popl %%eax"
:
: "g" (i), "g" (j)
); // increment i by j
Okay, 現在我們已經有了兩個輸入變量了。沒問題了, 我們只需要記住%0對應第一個輸入變量(在這個例
子中是i), %1對應在i面的列出的j。
Oh yeah, 這個volatile到底是什意思呢? 它防止你的編譯器修改你的匯編代碼,就是不進行優化(紀錄
, 刪除, 結合,等等優化手段。), 不改變代碼原樣來匯編它們。建議一般情況下使用volatile選項。

讓我們來看看輸出字段:

int i=0;
__asm__ __volatile__("
pushl %%eax\n
movl $1, %%eax\n
movl %%eax, %0\n
popl %%eax"
: "=g" (i)
); // assign 1 to i
這看起來非常象我們前面提到的輸入字段的例子; 確實也沒有很大的不同。所有的輸出修飾符前面都應該
加上=字符,他們同樣在匯編代碼裡面用%0到%N-1來表示, 在輸出字段按順序排列。你一定會問如果同時
有輸入和輸出字段會怎排序的呢? 好,下面一個例子就是讓大家知道如何同時處理輸入輸出字段的。
 
int i=0, j=1, k=0;
__asm__ __volatile__("
pushl %%eax\n
movl %1, %%eax\n
addl %2, %%eax\n
movl %%eax, %0\n
popl %%eax"
: "=g" (k)
: "g" (i), "g" (j)
); // k = i + j
Okay, 唯一個不清楚的地方就是匯編代碼中的變量的個數。我馬上來解釋一下。 
當同時使用輸入字段和輸出字段的時候: 

%0 ... %K 是輸出變量 

%K+1 ... %N 是輸入變量 

在我們的例子中, %0 對應k, %1 對應i, %2對應j。很簡單,是吧? 

到現在為止我們都沒有使用最一個字段(registers-modified)。如果我們要在我們的匯編代碼裡使用
任何寄存器, 我們要明確的用push和pop指令來保存它們, 或者列到最一個字段裡面讓gcc來處理它們。


這是前面的一個例子, 沒有明確的保留和存貯eax。 

int i=0, j=1, k=0;
__asm__ __volatile__("
pushl %%eax\n    /*譯者注:好像原文說的有點問題,明明是保存了eax的值,:(*/
movl %1, %%eax\n
addl %2, %%eax\n
movl %%eax, %0\n
popl %%eax"
: "=g" (k)
: "g" (i), "g" (j)
: "ax", "memory"
); // k = i + j
我們讓gcc來保存和存貯eax, 如果必要的話。一個16-bit寄存器名代表了32-, 16-或8-bit寄存器。 如果
我們要改寫內存 (寫入一個變量等。), 建議在register-modified字段裡面來指定"memroy"修飾符。這意
味著除了第一個例子我們都應該加上這個修飾符, 但是直到現在我才提出來, 是為了更簡單易懂。

在你的內聯匯編裡面定位標號應該使用b或f來作為終止符, 尤其是向向前的跳轉。(譯者注:b代表向
跳轉,f代表向前跳轉)

For example, 

__asm__ __volatile__("
0:\n
...
jmp 0b\n
...
jmp 1f\n
...
1:\n
...
);
這裡有個用c代碼和內聯匯編代碼混合寫的跳轉程序的例子(thanks to Srikanth B.R for this tip).

void MyFunction( int x, int y )

__asm__( "Start:" );
__asm__( ...do some comparison... ); 
__asm__( "jl Label_1" ); 

CallFunction( &x, &y ); 
__asm__("jmp Start"); 

Label_1: 
return; 
}

--------------------------------------------------------------------------------

External Asm
Blah... Okay fine. Here's a clue: Get some of your C/C++ files, 且用gcc -S file.c來編譯。 然
查看file.S文件。基本結構如下: 
.file "myasm.S"

.data
somedata: .word 0
...
 
.text
.globl __myasmfunc
__myasmfunc:
...
ret
Macros, macros! 頭文件libc/asmdefs.h便你寫asm。 在你的匯編代碼最前面包含此頭文件然就可以
使用宏了。一個例子: myasm.S: 
#include 

.file "myasm.S"

.data
.align 2
somedata: .word  0
...

.text
.align 4
FUNC(__MyExternalAsmFunc)
ENTER
movl   ARG1, %eax
...
jmp    mylabel
...
mylabel:
...
LEAVE
這是一個好的純粹的匯編代碼框架。
 

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 立你斯 的頭像
    立你斯

    立你斯學習記錄

    立你斯 發表在 痞客邦 留言(0) 人氣()