在C語言中怎樣使用匯編語言呢?這個問題在不同的編譯器中,具體實現方法是不同的。但是在實現大方上也但是就是有兩種,而且各種編譯器的實現方法也是大同小異。一種是在C語言中嵌入匯編語言代碼,另一種是讓C語言從外部調用匯編。下面我們就以Borland格式為例來說一說具體用法。但是,GCC和Microsoft的實現方法的和Borland只在格式上有點區分。當然,GCC的嵌入匯編是AT&T格式的。還好,不管什麽格式,只是表達形式的不同而已,其內在含義是一模相同的。還是那句話各種編譯器的實現方法是大同小異的,並沒有本質的區分。另外在最後還附帶了一篇介紹在GCC中使用內嵌匯編的文章。
一、兩種實現方式
首先,我們看一看在C語言中怎樣嵌入匯編語言代碼。在C語言中嵌入匯編語言代碼,也有兩種格式,一種是單句的,一種是模塊的。
我們來看看一些簡單的例子。
例子1:
單句格式的:
main()
{
asm mov ah,2;
asm mov bh,0;
asm mov dl, 20;
asm mov dh,10;
asm int 10h; /*調用BIOS中斷配置光標位置*/
}
模塊格式的:
main()
{
asm{
mov ah,2
mov bh,0
mov dl, 20
mov dh,10
int 10h
}
}
在這個小程式裏面並沒有突出“嵌入”二字。但是從這個程式中能夠看出其基本格式。嵌入的各行代碼前面加上asm關鍵字或把匯編語句放入asm代碼塊中,每行以分號或換行符結束,而註釋必須是C語言格式的。
下面我們來看一個讓C語言和匯編協作的例子:
例子2:
main()
{
char const *MESSAGE=”OutPut from asm..\n$”;
asm{
mov ah, 9
mov dx, MESSAGE
int 21h
}
}
上面這個例子十分的簡單,他的純C語言版本是:
#i nclude
main()
{
printf(“OutPut from asm..\n$”);
}
接下來我們看一看怎樣讓C語言調用匯編例程。我們還是看一個簡單的小程式:
C語言部分如下:
extern cursor (int,int),
main()
{
cursor(15,12);
}
匯編語言部分如下:
.MODEL SMALL
.CODE
PUBLIC
_CURSOR PROC
PUSH BP
MOV BP,SP
MOV DH,[BP+4]
MOV DL,[BP+6]
MOV AH,02
MOV BH,00
INT 10H
POP BP
RET
_CURSOR ENDP
通過上面這個程式,您會看到調用匯編語言的關鍵就是怎樣傳遞參數。事實上,是通過堆棧來傳遞的但是具體規則是什麽呢?下面我就來看看。
二、調用規則
實際上,在C語言中使用匯編語言最困難的就是怎樣安全有效的傳遞參數。否則在調用匯編子程式時就會從堆棧中取出錯誤的參數。更可惡的是這種錯誤在編譯的時候是不會發現錯誤提示的。
下面是C和MASM匯編語言混合是用的時候采用的規則:
1、 參數傳遞的次序和他們出現的次序是相反的。例如上例中的cursor (x,y)中,首先傳遞的是y,然後才是x。這和我們的一般想法是不相同的,所以在這兒容易出現錯誤。
2、 傳遞完參數後,C程式還將保存(CS,IP)。假如C程式是SMALL或COMPACT存儲模式下編譯的(或過程是NEAR型的),那麽只保存IP,而在MEDIUM、LARGE或HUGE模式下編譯的(或過程是FAR型的),那麽CS和IP都會被壓入堆棧,其順序是CS在前,IP在後。但是這個過程是C語言自動進行的而無需我們幹預。這也就是我們在例子2中為什麽用MOV DH,[BP+4]而不是MOV DH,[BP]。因為前面是CS和IP而不是參數,真正的參數從[BP+4]開始。
3、 更有BP也必須保存在堆棧中,然後我們才能夠通過BP和偏移地址來訪問參數。
4、 最後一條指令應當是後面不帶數字的RET,因為把堆棧到原始位置的工作將由C程式重新獲得控制權以後才會執行。
5、 任何於C程式共享的名稱都必須在前面加下劃線,而且C語言只識別前8個字符。
6、 對於普通的參數C語言傳遞的是參數值,而對於數組,傳遞的是指針(也就是數據的地址)。
7、 假如C程式是在MEDIUM、LARGE或HUGE模式下編譯的,那麽匯編語言過程應該設為FAR型,C程式是SMALL或COMPACT存儲模式下編譯的,那麽匯編語言過程應該設為NEAR型。
但是在MASM5.1或TASM1.0連同更高的版本的時候就不必擔心偏移地址、在共享名稱前加下劃線連同保存BP這些瑣事了,因為他們能夠由編譯器自動完成了。很顯然例子2是舊格式的。
三、把參數返回C程式
當C程式需要從匯編過程獲得某個參數時,這個參數應該通過寄存器來傳遞。具體使用哪些寄存器取決於參數的大小,請看下表:
寄存器
大小(字節)
C數據類型
AL
1
Char,short
AX
2
Int
DX:AX
4
Long
四、把匯編語言程式和C語言程式鏈接到一起
1、 確保匯編語言中的過程被定義為PUBLIC,過程名以下劃線開始。例如,在C語言中叫做“sum”到匯編語言中就應該是“_sum”.
2、 在C語言程式中過程定義為外部類型,例如在例子2中的extern cursor (int,int)。
3、 用匯編器對匯編語言程式匯編,得到XXX.obj文檔。
4、 用C語言編譯器編譯C語言程式,得到YYY.obj文檔。
5、 用鏈接器將他們鏈接到一起生成可執行文檔:
link XXX.obj + YYY.obj
以上就是混合使用C語言和匯編語言應該註意的幾點問題。關於在GCC中使用匯編語言大體上是和上面相同的,只是實現細節上有一點區分而已。下面的這篇文章對於在GCC中使用內嵌匯編進行周詳的解釋。
GCC使用的內嵌匯編語法格式小教程
本文對內嵌匯編語法,從基本語法、內嵌匯編的格式介紹、和擴展的內嵌匯編格式進行了周詳說明,需要說明的是GCC采用的是AT&T的匯編格式.
一、 基本語法
語法上主要有以下幾個不同.
★ 寄存器命名原則
AT&T: %eax Intel: eax
★源/目的操作數順序
AT&T: movl %eax,%ebx Intel: mov ebx,eax
★常數/立即數的格式
AT&T: movl $_value,%ebx Intel: mov eax,_value
把_value的地址放入eax寄存器
AT&T: movl $0xd00d,%ebx Intel: mov ebx,0xd00d
★ 操作數長度標識
AT&T: movw %ax,%bx Intel: mov bx,ax
★尋址方式
AT&T: immed32(basepointer,indexpointer,indexscale)
Intel: [basepointer indexpointer*indexscale imm32]
Linux工作於保護模式下,用的是32位線性地址,所以在計算地址時不用考慮egment:offset的問題.上式中的地址應為:
imm32 basepointer indexpointer*indexscale
下面是一些例子:
★直接尋址
AT&T: _booga ;
_booga是個全局的c變量註意加上$是表示地址引用,不加是表示值引用.
註:對於局部變量,能夠通過堆棧指針引用.
Intel: [_booga]
★寄存器間接尋址
AT&T: (%eax)
Intel: [eax]
★變址尋址
AT&T: _variable(%eax)
Intel: [eax _variable]
AT&T: _array(,%eax,4)
Intel: [eax*4 _array]
AT&T: _array(%ebx,%eax,8)
Intel: [ebx eax*8 _array]
二、 基本的內嵌匯編
基本的內嵌匯編很簡單,一般是按照下面的格式
asm(statements);
例如:asm(nop); asm(cli);
asm 和 __asm__是完全相同的.
文章標籤
全站熱搜
