GNU 嵌入式彙編

在/linux0.11/kernel/trap.c文件中,第一次接觸到C語言中的嵌入式彙編代碼。詳細的使用說明可以參考GNUgcc手冊中第4章的內容或者參考文獻《using assembly with gcc》。

    具有輸入和輸出參數的嵌入式彙編的基本格式爲:


       在/linux0.11/kernel/trap.c文件中,第一次接觸到C語言中的嵌入式彙編代碼。詳細的使用說明可以參考GNUgcc手冊中第4章的內容或者參考文獻《using assembly with gcc》。

    具有輸入和輸出參數的嵌入式彙編的基本格式爲:


        asm("彙編語句“

       asm("彙編語句“

            : 輸出寄存器

            : 輸入寄存器

            : 會被修改的寄存器);

    其中,”彙編語句“是你寫彙編指令的地方;”輸出寄存器“表示當這段嵌入彙編執行完之後,哪些寄存器用於存放輸出數據。”輸入寄存器“表示在執行彙編代碼時,這裏指定的一些寄存器中應該存放的輸入值,他們分別對應着一C變量或者常數值。下面將舉例說明嵌入式彙編的具體因使用方法。

    例如1:

    #define get_seg_byte(seg,addr)\

    ({\

    register char _res;\

    __asm__("push %% fs; \

             mov %%ax,%%fs;\

             movb %%fs:%2,%%al;\

             pop %%fs\

            :"=a"(_res) \

            :"0"(seg),"m"(*(addr)));\

    _res;})

    這段代碼定義了1個嵌入式彙編函數。因爲是宏語句,需要在一行上定義,因此這裏使用反斜行‘\’將這些語句連成1行。第1行定義了宏的名稱,即宏函數名稱爲get_seg_byte(seg,addr)。第3行定義了一個寄存器變量_reg 。 第4行上的_asm_表示嵌入彙編語句開始。從第4行到第7行的4條AT&T 格式的彙編語句。

    第8行是輸出寄存器,這句話的含義是在這段代碼運行結束後將eax所代表的寄存器中的值放入_res 變量中,作爲本函數的輸出值。爲了在上面彙編語句中使用該地址值,嵌入彙編程序規定把輸入和輸出寄存器統一按順序編號,順序是從輸出寄存器序列從左到右從上到下以%0 開始,分別記爲%0,%1,%2....%9。因此,輸出寄存器編號爲%0 輸入寄存器前一部分""(seg)的編號爲%1,而後一部分的編號爲%2. 上面第6行上的%2代表(*(addr))這個內存偏移量。

    現在分析4-7行上代碼的具體作用。第1句將fs段寄存器的內容入棧;第2句將eax中的段值賦給fs段寄存器;第3句是把fs:(*addr))所制定的字節放入al寄存器。當執行完彙編語句後,輸出寄存器eax的值將被放入_res。

    經過上面的分析,我們知道宏名稱中的seg代表一指定的內存段值,而addr表示一內存偏移地址量。到現在爲止,我們應該很清楚這段程序的功能。該宏函數是從指定的段和偏移量的內存地址處取一個字節。

    例如2:

        asm("cld\n\t"

            "rep\n\t"

            "stol"

            : 

            :"c"(count-1),"a"(fill_value),"D"(des)

            :"%ecx","%edi");

    1-3行這三句是常用的彙編語句,用以清方向標識位,城府保存值。第四行說明沒有用到輸出寄存器。第5行的含義是將count-1的值將在到ecx寄存器中(加載碼是“c“)fill_value 加載到eax中,dest放到edi中。 爲什們要讓gcc編譯程序去做這樣的加載,而不讓我們自己做呢?因爲gcc在它進行寄存器分配時可以進行某些優化工作。例如fill_value值可能已經加載到eax中。如果在一個循環語句中的話,gcc可能在整個循環操作中保存eax,這樣就可以在每次循環中少用1個movl語句。

    最後1行是告訴gcc這些寄存器中的值已經改變了。很奇怪吧?不過,gcc知道你拿這些寄存器做了什麼後,這確實能夠對gcc的優化操作有所幫助。

    下面是可能會用的寄存器加載碼及其具體含義:

代碼    說明

a        使用寄存器eax

b        使用寄存器ebx

c        使用寄存器ecx

d        使用寄存器edx

S        使用寄存器esi

D        使用寄存器edi

q        使用動態分配字節可尋址寄存器 (eax、ebx、ecx或edx)

r        使用任意動態分配的寄存器

g        使用通用有效的地址即可(eax、ebx、edx、ecx 或者內存變量)

A        使用eax和edx聯合(64bit)

m        使用內存地址

o        使用內存地址,並可以加載偏移值

I        使用常數0-31

J        使用常數0-63

K        使用常數0-255

L        使用常數0-65536

M        使用常數0-3

N        使用1字節常數(0-255)

O        使用常數0-31

    下面的例子不是讓自己制定那個變量使用那個寄存器,而是讓gcc爲你選擇

    asm("leal(%1,%1,4),%0"

        :"r"(y)

        :"0"(x));

    第1句leal(r1,r2,4),r3 語句表示r3=r1+r2×4。這個例子可以非常快的將x成5.其中"%0","%1" 是指gcc自動分配的寄存器。這裏%1 代表輸入值x要放入的寄存器,”%0“表示輸出寄存器。所以如果gcc將r指定爲eax的話,那麼上面彙編語句的含義爲”leal(eax,eax,4),eax"    注意 如果不希望彙編語句被gcc優化而挪動地方,就需要在asm符號後面添加volatile關鍵字

    asm volatile();或者

    __asm__ __volatile__();



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