組合語言簡介處理器BIOS啟動程式微軟組譯器組合語言DOSWindowsVisualStudioGNUGNU 組譯器C 轉組語連結 C 語言嵌入 C 語言訊息相關網站參考文獻最新修改簡體版English |
有時,為了能增進程式的效率,或者在 C 語言當中加入一些組合語言程式碼,我們會採用內嵌的方式,將組合語言內嵌在C語言當中。針對這點,gcc提供了如圖 4.10的內嵌語法,讓我們在 C 語言中可以直接撰寫組合語言。其中,assembler template 為組合語言程式、output operands 為輸入參數、input operands 為輸入參數,而 list of clobbered registers 則指定了被更改的暫存器參數列表。
圖 4.10GNU 的內嵌組合語言語法 舉例而言,範例 4.24的C語言程式當中,就內嵌了組合語言。其中,第一欄是內嵌組合語言的C語言程式 inlineAsm1.c,第二欄則是其說明,第三欄是我們利用gcc將該程式轉換成inlineAsm.s 組合語言程式的片段,其中有底線的文字部分對應的是內嵌組合語言的展開部分。 在GNU的內嵌組合語言 (assembler template) 中,由於 % 被用作參數 %1, %2, … 的前置字元,因此當遇到暫存器(像是 %eax) 時,必須使用兩個 % 符號 (%%) 作為暫存器的起始標記,因此,在範例 4.24中,原本的 addl %ebx, %eax 指令,就成了 addl %%ebx, %%eax 這個更冗長的指令。 在範例 4.24的內嵌參數部分,:"a"(foo), "b"(bar) 是輸入參數,代表要將 foo 變數傳遞給限制條件為 "a" 的暫存器,由於在 IA32 中限制條件為"a"的暫存器就是 eax,因此,foo 變數將會傳給 eax 暫存器。同理,"b"(bar) 參數代表將 bar 傳給 ebx 暫存器。而輸出參數 :"=a"(foo) 則是將 eax 的結果傳回到 foo 變數中。 範例 4.24 內嵌組合語言的C程式
請讀者仔細觀察範例 4.24的組合語言程式碼,其中,內嵌指令 asm() 中的輸入參數部分為 :"a"(foo), "b"(bar)。這導致代表foo參數的-8(%ebp)被傳入限制條件 "a" 所對應的 eax暫存器中;而代表bar參數的-12(%ebp)則被傳入限制條件"b"所對應的ebx 暫存器中,然後,才執行內嵌指令 addl %ebx, %eax。在執行完畢之後,輸出參數 :"=a"(foo) 指定將 eax 傳回 foo:-8(%ebp) 變數中,於是最後補上了 movl %eax, -8(%ebp) 指令,完成整個內嵌呼叫的過程。 範例 4.25顯示了該程式的編譯執行過程,首先,我們利用 gcc 加上 –S 參數,將C語言編譯為組合語言檔案。接著,再利用gcc作為組譯連結器,將inlineAsm1.s組譯並連結為執行檔,然後執行之,結果,foo果然變成 25,這代表嵌入的組合語言程式發揮了效用。 範例 4.25 <範例 4.24> 的編譯與執行過程
在嵌入式組合語言 asm() 的語法中,每個參數的語法為 "限制條件"(變數),例如範例 4.24中的 "a"(foo), "b"(bar) 等,其中的 "a", "b" 都是限制條件,而 (foo), (bar) 則是C語言中所傳入傳出的變數。 限制條件可能代表某一個暫存器、一群暫存器、記憶體變數、或者數值等。例如,限制條件m代表『記憶體運算元』、p代表『合法的位址』,而 g 則代表『通用型暫存器、記憶體位址或立即定址的數值』的集合等等。 但是,限制條件的設定與CPU的架構有關,同一個符號在不同的CPU中會有不同的意義。例如,在IA32處理器中,限制條件a代表 eax、b 代表 ebx、c代表 ecx、d代表 edx、S 代表 esi、D代表 edi、r代表『eax ebx ecx edx esi edi』的集合、q代表 『eax ebx ecx edx』的集合、g代表『eax,ebx, ecx, edx 或變數』,而 1,則代表與參數 %1 相同, 2 代表與參數 %2 相同 …。 表格 4.1 GNU嵌入式組合語言的限制條件 (針對 IA32處理器)
利用表格 4.1中的限制條件,我們可以放寬暫存器的限制,改使用像 addl %2, %0 這樣的方式。這種方式可以讓編譯器自行選擇能使用的暫存器,如此,可以增進程式的效率。在範例 4.26中,我們就使用了此種參數指定的方式,利用 addl %2, %0 指令內嵌,然後,在輸出參數0 中指定 :"=r"(foo),這使得 foo 可使用的候選的暫存器放寬為 r 條件的『eax ebx ecx edx esi edi』。接著,在輸入參數 %1, %2中指定 :"0"(foo), "r"(bar),這同樣使得 bar 的候選暫存器放寬為r 條件的『eax ebx ecx edx esi edi』。但是,由於 foo 變數已經在輸出參數 :"=r"(foo)被指定了,因此,必須在第一個輸入參數的 (foo) 前加上 "0" 條件,告訴編譯器該參數所用的暫存器必須與 :"=r"(foo) 所選擇的一致,如此,就能讓編譯器有更多的選擇空間。範例 4.26顯示了我們將前述的內嵌組合語言之條件由 a, b 放寬為 r 之後的結果。 範例 4.26 將<範例 4.24> 的條件放寬為 “r” – (2)
乍看之下,範例 4.26的輸出程式碼並沒有比範例 4.24更短或更有效率,但是,仔細看範例 4.27中兩個組合語言的對照後會發現,foo 變數從 -8(%ebp) 改變為 -4(%ebp),而 bar 則從 -12(%ebp) 改變為 -8(%ebp),這是因為在範例 4.26中,由於編譯器選擇改用 addl %edx, %ecx 取代範例 4.24中的 addl %ebx, %eax,這使得範例 4.26的組合語言能少掉第4行的push %ebx 的動作,於是增進了效率,並且減少了堆疊的使用空間。 範例 4.27 <範例 4.24>與<範例 4.26>的組合語言對照
有關gcc 的內嵌組合語言的語法,您可以參考網路上的『GCC Inline Assembly - HOWTO』一文 。 在本節中,我們已經介紹了如何使用 gcc 進行編譯、組譯與連結的方法,由於gcc是一個編譯、組譯與連結的萬用工具,因此,通常我們不會直接使用 GNU的 as 組譯器,而會改用 gcc 作為組譯器,以便與 C 語言程式進行連結,甚至在C語言當中直接內嵌組合語言,這讓我門不需要完全從組合語言開始撰寫,對系統程式的開發有相當大的用處,特別是嵌入式系統。 |
組合語言 -- 在 C 語言當中內嵌組合語言
page revision: 0, last edited: 11 Oct 2010 07:31
Post preview:
Close preview