版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第9章匯編語言與C語言的連接9.1在C語言中嵌入?yún)R編語言語句9.2C語言調(diào)用匯編語言程序模塊9.3匯編語言調(diào)用C語言程序模塊本章小結(jié)
本章學(xué)習(xí)目標(biāo)
●掌握在C語言中嵌入?yún)R編語言語句的格式、數(shù)據(jù)訪問規(guī)則和編譯方法。
●熟悉匯編語言與C語言程序模塊連接的約定規(guī)則。
●了解匯編語言與C語言程序模塊連接的參數(shù)傳遞方法。
●了解匯編語言與C語言程序模塊連接的編譯及可執(zhí)行文件生成方法。匯編語言與C語言的連接即混合編程,可采用的具體方法有如下三種:
(1)直接在C程序中嵌入?yún)R編語言的語句。
(2)采用模塊連接法。分別獨(dú)立編寫匯編程序和C程序,然后采用各自的編譯工具分別生成目標(biāo)文件,再將多個(gè)目標(biāo)文件進(jìn)行連接,最后生成可執(zhí)行文件。在模塊連接法中也可以將匯編語言源程序和C語言源程序一次性編譯和連接,直接生成可執(zhí)行文件,這種方法更加簡(jiǎn)便。
(3)對(duì)C程序進(jìn)行編譯,生成相應(yīng)的匯編程序,然后對(duì)其進(jìn)行手工優(yōu)化和修改。
本章介紹前兩種方法,第3種方法請(qǐng)讀者查閱相關(guān)資料。
C語言與匯編語言分別有各自的特點(diǎn)、優(yōu)勢(shì)和應(yīng)用領(lǐng)域。為了提高程序的開發(fā)效率,通常采用C語言這樣的高級(jí)語言進(jìn)行開發(fā),在某些關(guān)鍵功能的實(shí)現(xiàn)上需要程序有較高的執(zhí)行效率或需要對(duì)計(jì)算機(jī)硬件進(jìn)行操作時(shí),在程序中可以按照一定的規(guī)則嵌入?yún)R編語言的語句,充分發(fā)揮匯編語言執(zhí)行效率高和對(duì)硬件操作方便的優(yōu)勢(shì)。9.1在C語言中嵌入?yún)R編語言語句在C程序中嵌入?yún)R編語句是一種直接的C語言與匯編語言接口方法。采用這種方法一方面可以在C程序中實(shí)現(xiàn)用C語言無法實(shí)現(xiàn)的一些硬件控制功能,如修改中斷控制寄存器、中斷使能或屏蔽、讀取狀態(tài)寄存器和中斷標(biāo)志寄存器等;另一方面,也可以用這種方法將C程序中的關(guān)鍵部分用匯編語句代替,以優(yōu)化程序。同時(shí),嵌入式匯編語言可以直接訪問C語言中定義的變量、常量、函數(shù),不必考慮具體接口細(xì)節(jié),因此有很高的開發(fā)效率。采用這種方法的缺點(diǎn)是C編譯器在編譯嵌入了匯編語句的C程序時(shí)并不檢查或分析所嵌入的匯編語句,這對(duì)查找程序中的錯(cuò)誤帶來一定困難。本書以在TurboC2.0中嵌入8086/8088匯編語言為例進(jìn)行介紹。對(duì)于在其他版本的C語言嵌入?yún)R編語言的方法基本與此一致,只是在格式上稍有不同。9.1.1在C語言中嵌入?yún)R編語言的兩種格式
嵌入式匯編語言也稱為行內(nèi)匯編語言。在C語言中嵌入?yún)R編語言有兩種格式,一種是逐行嵌入,另一種是模塊嵌入。下面通過實(shí)例進(jìn)行介紹。
1.逐行嵌入格式
格式如下:
asm匯編語言語句或偽指令<;或換行>
在逐行嵌入格式中,嵌入的各行匯編語句前面加上asm關(guān)鍵字。每個(gè)asm關(guān)鍵字后面可以寫一條匯編語句,每行可以寫多條語句,之間用分號(hào)間隔,每行以分號(hào)或換行符結(jié)束。語句不能跨行書寫。
例9.1
在主函數(shù)中嵌入?yún)R編語句,調(diào)用BIOS功能設(shè)置光標(biāo)位置,然后調(diào)用DOS功能在該位置顯示字符“@”。程序如下:
main()
{
asmmov ah, 2;asmmovbh,0;/*可以在一行 中寫多條匯編語句*/
asmmov dh, 10;/*將光標(biāo)位置設(shè)置為第10行, 第20列*/
asmmov dl, 20;
asmint
10h
/*asm語句是C程序中唯一可以用 換行結(jié)尾的語句*/
asmmov dl, ‘@’
/*調(diào)用DOS功能在該位置顯 示字符“@”*/
asmint
21h;
}
2.模塊嵌入格式
對(duì)于一些C/C++編譯器,例如BorlandC++,若連續(xù)使用若干條asm語句,則可在asm關(guān)鍵字之后用“{”和“}”將嵌入的一組匯編語句包括起來,每行以分號(hào)或換行符結(jié)束。格式如下:
asm{
一組匯編語言語句或偽指令
}
例9.2
將例9.1用模塊嵌入格式改寫,程序如下:
main()
{
asm{
mov ah, 2;
mov bh, 0;
mov dh, 10; /*將光標(biāo)位置設(shè)置為第10行,第20 列*/
mov dl, 20;
int 10h /*可以用換行符結(jié)尾*/
mov dl, ‘@’; /*調(diào)用DOS功能在該位置顯示字符 “@”*/
int 21h;
}
}
嵌入式匯編中的分號(hào)不再是一般匯編語言中注釋的開始。如果需要在嵌入式匯編語句中加入注釋,應(yīng)采用C語言的“/*…*/”注釋格式。
例9.3
求變量a+b的和,并在屏幕上顯示結(jié)果。程序如下:
main()
{
charconst*MESSAGE="\nThesumis$";
inta=1,b;
asmmov word ptrb,5
asmxor bx, bx
asmadd bx, a
asmadd bx, b
asmadd bx, 0030h /*轉(zhuǎn)換為ASCII碼*/
asmmov ah, 9
asmmov dx, MESSAGE
asmint 21h /*調(diào)用DOS功能在屏幕上顯示提示 信息*/
asmmov ah, 2
asmmov dl, bl
asmint 21h /*調(diào)用DOS功能在屏幕上顯示求和 結(jié)果*/
}在C程序的函數(shù)內(nèi)部,每條匯編語言語句都是一條可執(zhí)行語句,它被編譯進(jìn)程序的代碼段。在函數(shù)外部,一條匯編語句是一個(gè)外部說明,它在編譯時(shí)被放在程序的數(shù)據(jù)段中;這些外部數(shù)據(jù)可被其他程序引用。并不是所有的匯編語言語句和偽指令都可以作為嵌入式匯編語句。
在TurboC中允許書寫的嵌入式匯編語言語句和偽指令如下:
(1)
8086/8088指令,包括數(shù)據(jù)傳送、各種運(yùn)算、串操作、轉(zhuǎn)移等指令。
(2)
8087浮點(diǎn)運(yùn)算指令。如果使用浮點(diǎn)仿真庫,則需要在TCC編譯時(shí)加“-f”選項(xiàng),并且不能使用ffree、fdecstp和fincstp指令。
(3)偽指令僅包括變量定義偽指令DB、DW、DD和外部數(shù)據(jù)聲明偽指令EXTRN。
例如:
asmmesgdb‘Pleaseinputanumber’
asmNUMdw0E712H
asmPIdd3.1415926
在嵌入式匯編語言中一般由C語言定義變量,很少使用變量定義偽指令。
(4)如果嵌入80186或80286指令,則需要在TCC編譯時(shí)加“-l”選項(xiàng),否則系統(tǒng)不能識(shí)別這些指令。9.1.2數(shù)據(jù)訪問規(guī)則
嵌入式匯編語句除可以使用指令允許的立即數(shù)和寄存器名外,還可以使用C語言程序中定義的變量。
對(duì)于具有嵌入式匯編語句的C程序,C編譯器要調(diào)用匯編程序(TASM或MASM)進(jìn)行編譯。匯編程序在分析一條嵌入式匯編指令的操作數(shù)時(shí),若遇到了一個(gè)標(biāo)識(shí)符,它將在C程序的符號(hào)表中搜索該標(biāo)識(shí)符,如果搜索不到該標(biāo)識(shí)符則提示編譯錯(cuò)誤信息。如果嵌入式匯編指令的操作數(shù)為8086/8088寄存器名,則匯編程序不進(jìn)行搜索,直接使用該寄存器,而且大小寫形式的寄存器名都可以使用。
例9.4
用嵌入?yún)R編方式實(shí)現(xiàn)求兩個(gè)數(shù)較小值的函數(shù)min,程序如下:
#include“stdio.h”
#include“conio.h”
intmin(intv1,intv2) /*用嵌入?yún)R編語句實(shí)現(xiàn)的求較小值 */
{
asmmov ax,v1; /*嵌入?yún)R編語句可直接使用參數(shù)的 值*/
asmcmp ax,v2;
asmjle toexit; /*轉(zhuǎn)移到C語言程序的標(biāo)號(hào) toexit*/
asmmov ax,v2; /*嵌入式匯編指令中寄存器名大小 寫形式都可以*/
toexit:return(_AX);/*將AX的內(nèi)容作為函數(shù)的返回值,此 處寄存器名必須大寫*/
}
main() /*C語言主程序*/
{
printf("min(5,2)is%d\nmin(117,321)is%d",min(5,2),min(117,321));
getch();
}本例中,嵌入?yún)R編語句直接使用了C程序中的整型參數(shù)v1和v2,將v1的值存入寄存器AX。C語言的每個(gè)整型變量在存儲(chǔ)器中占兩個(gè)字節(jié),相當(dāng)于嵌入式匯編語言中的一個(gè)字,即word類型。C語言與匯編語言的變量對(duì)應(yīng)關(guān)系如表9.1所示。
表9.1C語言與匯編語言的變量對(duì)應(yīng)關(guān)系在TurboC中使用嵌入式匯編語句時(shí),還應(yīng)注意以下規(guī)則。
1.通用寄存器的訪問規(guī)則
C語言中可以直接使用通用寄存器和段寄存器,只要在寄存器名前加一個(gè)下劃線就可以,如_AX、_DL、_DS,特別要強(qiáng)調(diào)的是,寄存器名必須用大寫形式。
另外,C語言中使用SI和DI指針寄存器作為寄存器變量,利用AX和DX傳遞返回參數(shù)。
如果C語言函數(shù)中沒有聲明寄存器變量,則嵌入式匯編語句可以自由地把SI、DI用作暫存寄存器;如果C函數(shù)聲明了寄存器變量,則嵌入式匯編語句仍可以使用SI、DI,但最好采用C語言寄存器變量名,因?yàn)榧拇嫫髯兞恐械臄?shù)據(jù)是保存在SI或DI寄存器中的。
嵌入式匯編語句可以任意使用AX、BX、CX、DX寄存器以及它們的8位形式,如AH、BL等。
2.轉(zhuǎn)移指令的標(biāo)號(hào)使用規(guī)則
嵌入式匯編語句中,可使用無條件、條件轉(zhuǎn)移指令和循環(huán)指令,但它們只在一個(gè)函數(shù)內(nèi)部轉(zhuǎn)移。asm語句不能定義標(biāo)號(hào),轉(zhuǎn)移指令的目標(biāo)位置必須是C語言程序中的標(biāo)號(hào)。特別需要注意的是,既使該標(biāo)號(hào)位置與當(dāng)前指令距離超過可轉(zhuǎn)移的范圍,轉(zhuǎn)移指令也不會(huì)自動(dòng)地轉(zhuǎn)換成一個(gè)遠(yuǎn)程轉(zhuǎn)移指令,因此編程時(shí)要注意標(biāo)號(hào)的位置。
例9.5
用嵌入?yún)R編方式實(shí)現(xiàn)將字符串OriginStr中的大寫字母轉(zhuǎn)變?yōu)樾懽帜覆@示,程序如下:
main()/*主程序*/
{
charOriginStr[]="Hello,CandASM!";
charLowStr[100];
lower(OriginStr,LowStr);
printf("Originstring:%s\n",OriginStr);
printf("LowercaseString:%s\n",LowStr);
getch();
}
lower(char*src,char*dest) /*子程序,src和dest是指向 兩個(gè)字符串的地址指針*/
{asm mov si, src
asm mov di, dest
asm cld
next:
asm
lodsb /*C語言定義的標(biāo)號(hào)*/
asm cmp al, 'A'
asm jb copy /*轉(zhuǎn)移到C語言定義的標(biāo)號(hào)*/
asm cmp al, 'Z'
asm ja copy /*不是‘A’到‘Z’之間的字符原樣復(fù) 制*/
asm add al, 20h /*如果是大寫字母轉(zhuǎn)換成小 寫字母*/
copy:asm stosb
asm and al,al /*C語言中,字符串用ASCII 碼為0的字符
(NULL)結(jié)尾*/
asmjnznext
}
3.C語言結(jié)構(gòu)的引用規(guī)則
嵌入式匯編語句的操作數(shù)也可以是C語言結(jié)構(gòu)變量中的某個(gè)成員(字段),引用方法仍然采用“結(jié)構(gòu)變量名.結(jié)構(gòu)成員名”格式。
另一種引用方法是把結(jié)構(gòu)變量的首地址存入某一地址寄存器,然后采用“[地址寄存器名].結(jié)構(gòu)成員名”格式,其中地址寄存器名要用方括號(hào)括起來。
例如:
structscore{
intmath;
intbiology;
intcomputer;
}score1;
sum(){
…
asmmovbx,score1.math/*取結(jié)構(gòu)變量score1的成員math*/
asmmovsi,offsetscore1/*取結(jié)構(gòu)變量score1的主存地址*/
asmmovax,[si].computer/*取結(jié)構(gòu)變量score1的成員computer*/
…
}9.1.3嵌入?yún)R編語言的編譯方法
C語言程序中含有嵌入式匯編語言語句時(shí),C編譯器首先將C語言的源程序(.C文件)編譯成匯編語言源文件(.ASM文件);
然后調(diào)用匯編程序TurboAssembler(TASM)將產(chǎn)生的匯編語言源文件編譯成目標(biāo)文件(.OBJ文件);最后調(diào)用TLINK將目標(biāo)文件連接成可執(zhí)行文件(.EXE文件)。
例9.6
對(duì)以下程序進(jìn)行編譯并觀察運(yùn)行結(jié)果。該程序用嵌入?yún)R編方式實(shí)現(xiàn)求字符串STRN的長度(含空格)。程序如下:
main()/*主程序*/
{
charSTRN[]=“Hello,CandASM!”;
printf(“ThelengthofSTRNis%d\n”,len(STRN));
getch();
}
intlen(char*src) /*子程序。其中src是指向字符串的 地址指針*/
{asmmov si,src
asmxor bx,bx
/*C語言中,字符串用ASCII碼為0 的字符(NULL)結(jié)尾*/
next:
asmcmpbyteptr[si],0 /*比較是否到字符串結(jié)尾*/
asmjz toexit /*如果已經(jīng)到字符串結(jié)尾則 轉(zhuǎn)移到標(biāo)號(hào)toexit*/
asminc bx /*如果未到字符串結(jié)尾則字符串長 度計(jì)數(shù)值加1*/
asminc si /*si指向字符串的下一個(gè)字符*/
asmjmp next
toexit:return(_BX);/*將寄存器BX的內(nèi)容作為函數(shù)的返回 值*/
}假設(shè)TurboC程序在D:\TurboC文件夾中。編譯時(shí)還需要調(diào)用TASM.EXE程序,將該程序復(fù)制到TurboC所在文件夾中。下面介紹具體編譯步驟。
(1)將編輯完成的源程序以.C文件擴(kuò)展名保存,本例程序命名為9_6.C,最好與TurboC的TCC.EXE以及TASM.EXE文件放在同一文件夾下。
(2)單擊Windows的“開始”→“運(yùn)行”,在運(yùn)行窗口中輸入“CMD”,點(diǎn)擊“確定”按鈕,打開命令提示符窗口。
(3)在命令提示符窗口中輸入命令“D:”,然后按下回車鍵,進(jìn)入D盤。
(4)輸入命令“CDTURBOC”,然后按下回車鍵,進(jìn)入TurboC文件夾。
(5)輸入編譯命令“TCC-B9_6.C”,然后按下回車鍵。其中的文件擴(kuò)展名“.C”可以省略。例9.6的編譯結(jié)果如圖9.1所示。
如果程序沒有錯(cuò)誤,則生成目標(biāo)文件“9_6.OBJ”和可執(zhí)行文件“9_6.EXE”。如果有錯(cuò)誤,則不會(huì)生成這兩個(gè)文件,需要修改后重新編譯。
圖9.1例9.6的編譯結(jié)果
(6)輸入生成的可執(zhí)行文件名“9_6.EXE”然后按下回車鍵,就可以執(zhí)行該文件。其中的文件擴(kuò)展名“.EXE”可以省略。例9.6的執(zhí)行結(jié)果如圖9.2所示。
在使用TurboC中提供的TCC程序進(jìn)行編譯時(shí),還應(yīng)注意以下問題。
(1)
TurboC本身的開發(fā)程序TC.EXE不能直接編譯帶有嵌入式匯編語句的C語言程序,需要用TCC.EXE和TASM.EXE程序進(jìn)行編譯。
圖9.2例9.6的執(zhí)行結(jié)果
(2)
TCC命令的基本格式如下:
TCC[選項(xiàng)]要被編譯的一個(gè)或多個(gè)文件名
TCC命令可帶有多種編譯選項(xiàng),選項(xiàng)以短橫線“-”開頭,例如前面使用的“-B”選項(xiàng),選項(xiàng)區(qū)分大小寫。命令中可以同時(shí)使用多個(gè)選項(xiàng)。下面介紹常用的選項(xiàng)-B、-I、-L和-E。
選項(xiàng)-B說明通過匯編程序進(jìn)行編譯,如果不寫出該選項(xiàng),則在編譯時(shí)發(fā)現(xiàn)程序中使用了嵌入式匯編語句后,將給出警告,并自動(dòng)重新編譯。選項(xiàng)-I和-L分別指定頭文件和庫函數(shù)的所在目錄,例如頭文件和庫函數(shù)在當(dāng)前目錄下的include和lib子目錄下,可以使用如下編譯命令:
TCC-B-Iinclude-Llib9_6.C
如果頭文件和庫函數(shù)在其他位置,則需要寫明其所在路徑,例如:
TCC-B-ID:\TurboC\include-LD:\TurboC\lib9_6.C
如果不寫這兩個(gè)選項(xiàng),在編譯時(shí)可能會(huì)顯示找不到C0S.obj或emu.lib文件。如果想省略-I和-L選項(xiàng),則需要修改TurboC文件夾下的TURBOC.CFG文件,在其中寫出頭文件和庫函數(shù)的所在目錄,如:
-ID:\TurboC\include
-LD:\TurboC\lib
保存TURBOC.CFG文件,這樣以后用TCC編譯時(shí)可以省略-I和-L選項(xiàng)。
選項(xiàng)
-E用于指定通過哪個(gè)匯編程序進(jìn)行編譯。常用的匯編程序有TASM.EXE和MASM.EXE。如果不寫出該選項(xiàng),則編譯時(shí)會(huì)自動(dòng)使用TASM.EXE進(jìn)行編譯;如果想使用MASM.EXE進(jìn)行編譯則可以將MASM.EXE復(fù)制到TurboC文件夾中,在TCC命令中使用-E選項(xiàng)進(jìn)行聲明,例如:
TCC-B-EMASM.EXE-Iinclude-Llib9_6.C
如果MASM.EXE在其他位置,則需要寫明其所在路徑,例如:
TCC-B-ED:\MASM\MASM.EXE-Iinclude-Llib9_6.C
注意:選項(xiàng)-I、-L和-E與其后的目錄名或文件名之間不能有空格。
(3)如果找不到C0S.obj文件,則可以用MASM.EXE對(duì)C0.asm文件進(jìn)行編譯,將生成的目標(biāo)文件命名為C0S.obj,并將該文件存放到TurboC文件夾的lib文件夾中。此時(shí)不要用TASM編譯,否則可能無法生成目標(biāo)文件。
由上例可以看出,嵌入?yún)R編方式把插入的匯編語言語句作為C語言的組成部分,不使用完全獨(dú)立的匯編模塊,所以比調(diào)用匯編子程序更方便、快捷,并且在大存儲(chǔ)模式和小存儲(chǔ)模式下都能正常編譯通過。
除了嵌入?yún)R編語言以外,匯編語言與C語言連接的另一種方法就是模塊連接法。模塊連接法是指分別獨(dú)立編寫匯編程序和C程序,然后采用各自的編譯工具分別生成目標(biāo)文件,再將多個(gè)目標(biāo)文件進(jìn)行連接,最后生成可執(zhí)行文件。
本節(jié)介紹在C語言中調(diào)用匯編語言程序的子程序和變量的方法,匯編語言程序通常采用簡(jiǎn)化段定義形式。9.2C語言調(diào)用匯編語言程序模塊9.2.1C語言調(diào)用匯編語言子程序的主要步驟
C語言調(diào)用匯編語言子程序的主要步驟分為匯編語言和C語言兩個(gè)方面。
1.在匯編語言編程方面
①使用和C語言相同的存儲(chǔ)模式定義各個(gè)段空間,沒有用到的段可以不定義。
②在匯編語言源程序中用PUBLIC偽指令聲明C語言需要引用的子程序和變量。
③按照C語言調(diào)用協(xié)議從堆棧中取得入口參數(shù)。
④對(duì)參數(shù)進(jìn)行處理,實(shí)現(xiàn)相應(yīng)的功能。
⑤將返回值送入AX或者DX:AX中返回。
⑥使用匯編程序TASM、MASM或ML匯編源程序,形成目標(biāo)文件。
2.在C語言編程方面
①在C語言源程序中用extern語句聲明匯編語言子程序和變量。
②在C語言程序中像引用本地函數(shù)和變量一樣,引用這些匯編語言子程序和變量。
③將C語言源程序編譯形成目標(biāo)文件。
④使用TLINK或TCC連接C語言和匯編語言的目標(biāo)文件,形成可執(zhí)行文件。
⑤執(zhí)行程序進(jìn)行驗(yàn)證和調(diào)試。
注意:可執(zhí)行文件的生成方法除了上述步驟介紹的傳統(tǒng)方法以外,還可以省略生成目標(biāo)文件的步驟,采用TCC程序直接生成可執(zhí)行文件,具體方法請(qǐng)見9.2.3節(jié)。9.2.2C語言調(diào)用匯編語言子程序的約定規(guī)則
要使C語言模塊和匯編語言模塊正確地連接在一起,必須處理好三點(diǎn):
①匯編語言模塊必須遵守與C語言兼容的命名約定和聲明約定;
②注意寄存器的保護(hù)和恢復(fù)以及其他使用約定;
③匯編語言模塊必須采用和C語言模塊一致的存儲(chǔ)模式。
1.命名約定
命名約定包括變量、函數(shù)(子程序)等標(biāo)識(shí)符的命名約定。C語言和匯編語言程序中標(biāo)識(shí)符的名稱建議不要超過8個(gè)字符,通常用小寫字母。
根據(jù)使用的匯編程序不同,命名約定分為兩種情況。
(1)如果使用MASM5.0對(duì)匯編語言源程序進(jìn)行編譯,使用TCC對(duì)C語言源程序進(jìn)行編譯,分別生成目標(biāo)文件,最后經(jīng)過連接生成可執(zhí)行文件,那么C語言的源程序在被編譯后其中的變量名、函數(shù)(子程序)名等標(biāo)識(shí)符前面會(huì)加上下劃線,例如函數(shù)mysub經(jīng)過編譯后名稱變?yōu)開mysub。而匯編語言源程序在被MASM5.0編譯后其中的變量名、子程序名等標(biāo)識(shí)符名稱不變。在這種情況下,如果C語言程序要調(diào)用匯編語言程序中定義的某子程序,就必須在匯編語言程序中將該子程序命名為下劃線開頭的名稱。例如C語言程序調(diào)用了匯編語言程序中定義子程序sum,C語言的源程序在被編譯后,C語言的目標(biāo)文件中該子程序名稱變?yōu)開sum,因此在匯編語言程序中應(yīng)將該子程序命名為_sum,這樣才能保證名稱一致。對(duì)于變量的使用也是同樣道理。
(2)如果使用TASM4.1、MASM6.11或ML6.15對(duì)匯編語言源程序進(jìn)行編譯,使用TCC對(duì)C語言源程序進(jìn)行編譯,分別生成目標(biāo)文件,最后經(jīng)過連接生成可執(zhí)行文件,或者用TCC將匯編語言源程序和C語言源程序一次性編譯和連接,直接生成可執(zhí)行文件,那么匯編語言和C語言中的函數(shù)(子程序)保持同名即可被相互調(diào)用,不要求匯編語言源程序中的標(biāo)識(shí)符以下劃線開頭,這種方法更加簡(jiǎn)便。
注意:MASM5.0僅支持上面第一種情況,TASM4.1、MASM6.11和ML6.15僅支持上面第二種情況。由于匯編程序版本眾多,其他版本的支持情況請(qǐng)讀者自行驗(yàn)證,本書以下的編譯連接過程均以MASM5.0和TASM4.1兩種版本為例。建議采用TASM,因?yàn)樗cTurboC的TCC程序兼容性較好。
2.聲明約定
(1)在C語言程序中,對(duì)所要調(diào)用的外部變量、過程、函數(shù)均采用extern予以聲明,并且一般放在主調(diào)程序之前的函數(shù)體外部,具體形式如下:
extern變量類型變量名;
extern返回值類型函數(shù)或子程序名稱(參數(shù)類型表);
其中,變量類型和返回值類型是C語言中函數(shù)、變量中所允許的任意類型。當(dāng)變量或返回值類型空缺時(shí),默認(rèn)為int型。下面是對(duì)外部變量、過程、函數(shù)進(jìn)行說明的例子:
externcount; /*變量類型空缺時(shí),默認(rèn)為int型*/
externdoublegetlength(float,int); /*返回值類型為 double型*/
externsum(int,int); /*返回值類型空缺時(shí),默認(rèn) 為int型*/
externvoiddisp(char,int*); /*無返回值,第二個(gè)參數(shù)為 指針型*/
經(jīng)過聲明后,這些外部變量、過程、函數(shù)就可在C程序中直接使用。函數(shù)的參數(shù)在傳遞過程中要求參數(shù)個(gè)數(shù)、類型、順序一一對(duì)應(yīng)。C語言與匯編語言的變量對(duì)應(yīng)關(guān)系如表9.1所示。
(2)匯編語言程序的標(biāo)識(shí)符(變量名和子程序名)為了能在其他模塊可見,被C語言程序調(diào)用,必須用PUBLIC偽指令聲明。例如,一個(gè)匯編語言程序包含有供外部調(diào)用的shift和getyear子程序,以及year、month和day整型變量,則應(yīng)該加入語句:
PUBLICshift,getyear
PUBLICyear,month,day
(3)在匯編語言程序中,對(duì)所要調(diào)用的外部變量、過程、函數(shù)均采用EXTRN或EXTERN予以聲明,并且一般放在程序開始處,而且是代碼段的外部,具體形式如下:
EXTRN(或EXTERN)變量名:變量類型,變量名:變量類型,…
EXTRN(或EXTERN)子程序名稱
注意:MASM5.0和TASM4.1僅支持EXTRN聲明;MASM6.11和ML6.15對(duì)這兩種關(guān)鍵詞都支持。
3.寄存器的保護(hù)、恢復(fù)及使用約定
為不破壞C語言的運(yùn)行環(huán)境,在匯編語言子程序中要注意寄存器的保護(hù)和恢復(fù)。對(duì)于BP、SP、DS、CS和SS,如果匯編語言子程序要使用它們,并且有可能改變它們的值,則需要對(duì)其進(jìn)行保護(hù)。這些寄存器的值經(jīng)保護(hù)后,可以使用并修改,但在子程序結(jié)束前必須恢復(fù)。
寄存器AX、BX、CX、DX和ES在匯編語言子程序中通??梢匀我馐褂?。其中的AX和DX寄存器承擔(dān)了傳遞返回值的任務(wù)。標(biāo)志寄存器的值也可以任意改變。指針寄存器SI和DI比較特殊,因?yàn)門urboC將它們用作了寄存器變量。如果C語言程序中使用了寄存器變量,則匯編語言子程序使用SI和DI前必須保護(hù),在退出前恢復(fù)。如果C語言程序沒有使用寄存器變量,則匯編語言子程序不必保護(hù)SI和DI。TurboC編譯程序提供了一個(gè)編譯選擇項(xiàng)(-r),它可以禁止C編譯程序使用寄存器變量。建議總是進(jìn)行保護(hù)SI和DI。
4.存儲(chǔ)模式約定
存儲(chǔ)模式?jīng)Q定了程序、數(shù)據(jù)、堆棧在主存中的分配和存取,決定了代碼和數(shù)據(jù)的默認(rèn)指針類型,例如段寄存器CS、DS、SS、ES的設(shè)置就與所采用的存儲(chǔ)模式有關(guān)。存儲(chǔ)模式在C語言中也稱為編譯模式或主存模式。
TurboC利用TCC的選項(xiàng)-m指定程序的存儲(chǔ)模式。TurboC提供了六種存儲(chǔ)模式,分別是微型模式(tiny)、小型模式(small)、緊湊模式(compact)、中型模式(medium)、大型模式(large)和巨型模式(huge)。上述存儲(chǔ)模式與匯編語言簡(jiǎn)化段定義格式中采用.MODEL偽指令設(shè)置的存儲(chǔ)模式一致。其中的微型模式(tiny)在MASM6.0及以上版本中才支持。為了使匯編語言程序與TurboC語言程序連接到一起,
對(duì)于匯編語言簡(jiǎn)化段定義格式來說,兩者必須具有相同的存儲(chǔ)模式。相同的存儲(chǔ)模式將自動(dòng)產(chǎn)生相互兼容的調(diào)用和返回類型;同時(shí),匯編程序的段定義偽指令
.CODE、.DATA等也將產(chǎn)生與TurboC相兼容的段名稱和屬性。
連接前,C語言與匯編語言程序都有各自的代碼段、數(shù)據(jù)段;而連接后,它們的代碼段、數(shù)據(jù)段就合二為一或者彼此相關(guān)聯(lián)。應(yīng)當(dāng)說明的是,被連接的多個(gè)目標(biāo)模塊中,應(yīng)當(dāng)有一個(gè)并且只有一個(gè)具有起始模塊。也就是說,若某個(gè)C語言程序中有main()函數(shù),則匯編語言程序不用定義起始執(zhí)行點(diǎn)。由于共用一個(gè)堆棧段,因此混合編程時(shí)通常匯編語言程序無須設(shè)置堆棧段。9.2.3編譯和連接方法
按照上節(jié)所介紹的各種約定,下面通過一個(gè)C語言程序調(diào)用匯編語言子程序的簡(jiǎn)單實(shí)例,說明編譯和連接的三種不同方法。該實(shí)例中沒有參數(shù)傳遞的情況,因?yàn)閰?shù)傳遞涉及到很多細(xì)節(jié),將在下一節(jié)中詳細(xì)介紹。
例9.7C語言程序調(diào)用匯編語言子程序,顯示一段信息。程序如下:
/*C語言程序:9_7C.C*/
externvoidshow();/*聲明show是外部函數(shù)*/
main()
{show();/*C語言程序編譯后函數(shù)的名稱將轉(zhuǎn)換為_show()*/
}
;匯編語言子程序:9_7.ASM
.MODELsmall,c ;聲明采用小型存儲(chǔ)模式和C語言 類型
.DATA ;匯編語言程序通常采用簡(jiǎn)化段定義形式
MESGDB‘HelloCandAssembly!’,‘$’
.CODE
PUBLIC_show ;聲明該子程序可供外部模塊調(diào)用
_showPROC ;子程序名稱前面加下劃線以保持 與C程序編譯后的名稱一致
MOVAH,9 ;小型存儲(chǔ)模式只有一個(gè)數(shù)據(jù)段,所以不 必設(shè)置DS
MOVDX,OFFSETMESG ;寄存器AX和DX無須保護(hù)
INT21h
RET
_showENDP
END上述兩個(gè)源程序文件編輯完成后分別保存為9_7C.C和9_7.ASM,然后按照下面方法進(jìn)行編譯和連接,具體方法有三種。
方法一:對(duì)匯編語言源程序和C語言源程序采用各自的編譯工具分別生成目標(biāo)文件,再將多個(gè)目標(biāo)文件進(jìn)行連接,最后生成可執(zhí)行文件。編譯和連接過程如圖9.3所示。
(1)將匯編語言程序9_7.ASM編譯生成9_7.OBJ目標(biāo)文件。
①使用MASM5.0命令如下:
MASM/Ml9_7.ASM或MASM/Mx9_7.ASM
圖9.3方法一的編譯和連接過程其中的選項(xiàng)/Ml表示保持文件中標(biāo)識(shí)符的大小寫不變,選項(xiàng)/Mx表示保持文件中用PUBLIC或EXTERN聲明的標(biāo)識(shí)符的大小寫不變,使用上述選項(xiàng)才能保證在連接時(shí)不出錯(cuò)。命令中的擴(kuò)展名.ASM可省略。
如果使用TASM4.1、MASM6.11或ML6.15編譯,則需要按照9.2.2節(jié)命名規(guī)則的第二種情況對(duì)本例中匯編語言子程序9_7.ASM進(jìn)行修改,將被調(diào)用子程序_show名稱前的下劃線去掉,改為show。修改后的程序如下:
.MODELsmall,c;聲明采用小型存儲(chǔ)模式和C語言類型
.DATA
MESGDB'HelloCandAssembly!','$'
.CODE
PUBLICshow;聲明該子程序可供外部模塊調(diào)用,與C程序共用標(biāo)識(shí)符
showPROC;子程序名稱前面不需要加下劃線,保持與C程序中名稱一致即可
MOVAH,9;小型存儲(chǔ)模式只有一個(gè)數(shù)據(jù)段,所以不必設(shè)置DS
MOVDX,OFFSETMESG;寄存器AX和DX無須保護(hù)
INT21h
RET
showENDP
END②使用TASM4.1命令如下:
TASM9_7.ASM或TASM/ml9_7.ASM或TASM/mx9_7.ASM
其中的選項(xiàng)/ml和/mx的功能分別與MASM5.0中選項(xiàng)/Ml和/Mx的功能相同。
③使用MASM6.11命令如下:
MASM9_7.ASM或MASM/Ml9_7.ASM或TASM/Mx9_7.ASM④使用ML命令如下:
ML/c9_7.ASM或ML/Cp/c9_7.ASM或ML/Cx/c9_7.ASM
其中的選項(xiàng)/Cp表示保持文件中標(biāo)識(shí)符的大小寫不變,選項(xiàng)/Cx表示保持文件中用PUBLIC或EXTERN聲明的標(biāo)識(shí)符的大小寫不變。命令中的擴(kuò)展名.ASM不能省略。
(2)將C語言程序9_7C.C編譯生成9_7C.OBJ目標(biāo)文件。使用TC環(huán)境或TCC編譯均可。
使用TCC命令格式如下:
TCC-c-ms-I包含文件路徑-L庫文件路徑要編譯的源程序文件名…
其中,選項(xiàng)-c表示只編譯、不連接;選項(xiàng)-ms表示采用小型存儲(chǔ)模式,TCC缺省采用小型存儲(chǔ)模式,若采用其他模式,可使用-mt、-mc、-mm、-ml、-mh選項(xiàng),分別對(duì)應(yīng)微型模式(tiny)、緊湊模式(compact)、中型模式(medium)、大型模式(large)和巨型模式(huge)。命令中的擴(kuò)展名.C可省略。選項(xiàng)-I指定連接所需的頭文件等包含文件所在的路徑,若C程序中有#include包含文件行,則需加選項(xiàng)-I,指明被包含文件的路徑;選項(xiàng)-L指定所需的初始化模塊和庫函數(shù)的所在路徑。如果TurboC文件夾下的TURBOC.CFG文件已經(jīng)按照9.1.3節(jié)介紹的方法配置完成,則可省略選項(xiàng)-I和-L。本例使用的TCC命令如下:
TCC-c9_7C.C
(3)利用連接程序TLINK將9_7.OBJ和9_7C.OBJ這兩個(gè)目標(biāo)文件連接在一起,得到可執(zhí)行程序文件9_7.EXE。TLINK命令格式如下:
TLINK目標(biāo)文件名,可執(zhí)行文件名,映像文件名,庫文件名
其中可寫入將要連接的多個(gè)目標(biāo)文件名,它們之間用空格間隔。如果省略可執(zhí)行文件名,則將生成與第一個(gè)目標(biāo)文件同名的.EXE可執(zhí)行文件;如果省略映像文件名,則將生成與可執(zhí)行文件同名的.MAP映像文件。注意:即使文件名被省略,其中的逗號(hào)也不能省略。
完成本例中兩個(gè)程序的連接可使用如下命令:
TLINKLIB\C0S.OBJ9_7.OBJ9_7C.OBJ,9_7.EXE,9_7.MAP,LIB\CS.LIB
直接使用TurboC的連接程序TLINK進(jìn)行連接時(shí),用戶必須指定與存儲(chǔ)模式一致的初始化模塊和函數(shù)庫文件,并且初始化模塊必須是第一個(gè)被連接的目標(biāo)文件。命令中,LIB\C0S.OBJ和LIB\CS.LIB就是在LIB目錄下小型存儲(chǔ)模式的初始化模塊C0S.OBJ和函數(shù)庫CS.LIB。命令中的文件擴(kuò)展名均可省略。如果將文件擴(kuò)展名和映像文件名均省略,則上面的命令可簡(jiǎn)寫成如下形式:
TLINKLIB\C0S9_79_7C,9_7,,LIB\CS
以MASM5.0為例,按方法一編譯、連接過程及生成的可執(zhí)行文件9_7.EXE運(yùn)行結(jié)果如圖9.4所示。
圖9.4方法一的編譯、連接過程及可執(zhí)行文件運(yùn)行結(jié)果有興趣的讀者可按照上面介紹的方法修改程序,改用TASM4.1、MASM6.11或ML6.15等其他版本的匯編程序重新進(jìn)行編譯。
方法二:對(duì)匯編語言源程序進(jìn)行編譯生成目標(biāo)文件,而C語言源程序不必編譯為目標(biāo)文件,經(jīng)TCC直接進(jìn)行編譯和連接,最后生成可執(zhí)行文件。編譯和連接過程如圖9.5所示。
圖9.5方法二的編譯和連接過程方法二的編譯和連接過程中省略了對(duì)C語言源程序的編譯,而采用TCC程序?qū)語言源程序的編譯和連接一次完成,這樣更加方便。此方法的一般格式如下:
TCC-ms-I包含文件路徑-L庫文件路徑要連接的源程序和目標(biāo)文件名…
其中選項(xiàng)-ms、-I、-L的含義與方法一中相同。要連接的源程序和目標(biāo)文件名可以是C語言源程序名或匯編語言目標(biāo)文件名,命令中的C程序擴(kuò)展名.C可省略,但目標(biāo)文件的擴(kuò)展名.OBJ不能省略。匯編語言目標(biāo)文件事先用MASM、TASM或ML生成。C語言源程序名和匯編語言目標(biāo)文件名的次序可以顛倒,最后生成的可執(zhí)行文件名以放在前面的文件名稱命名。本例使用的TCC命令如下:
TCC-ms9_7.OBJ9_7C.C
以MASM5.0為例,按方法二編譯、連接過程及生成的可執(zhí)行文件9_7.EXE運(yùn)行結(jié)果如圖9.6所示。
圖9.6方法二的編譯、連接過程及可執(zhí)行文件運(yùn)行結(jié)果有興趣的讀者可按照方法二中介紹的方法修改程序,改用TASM4.1、MASM6.11或ML6.15等其他版本的匯編程序重新進(jìn)行編譯。
方法三:由TCC程序自動(dòng)調(diào)用TASM和TLINK將編譯和連接過程一次性完成,不需要生成目標(biāo)文件,直接生成可執(zhí)行文件。編譯和連接過程如圖9.7所示。
可見,方法三是上述三種方法中最簡(jiǎn)便的。
此方法中匯編語言與C語言共用了標(biāo)識(shí)符,根據(jù)9.2.2節(jié)中命名規(guī)則的第二種情況,匯編語言源程序中的標(biāo)識(shí)符不要求加下劃線,只要保證匯編語言與C語言程序中標(biāo)識(shí)符同名即可。
圖9.7方法三的編譯和連接過程因此可以對(duì)例9.7中匯編語言子程序9_7.ASM進(jìn)行修改,將被調(diào)用子程序_show名稱前的下劃線去掉,改為show。修改后的程序在方法一中已經(jīng)給出。
方法三所使用的TCC命令一般格式如下:
TCC-ms-I包含文件路徑-L庫文件路徑要連接的源程序文件名…
其中選項(xiàng)-ms、-I、-L的含義與方法一中相同。要連接的源程序文件名是C語言和匯編語言源程序文件名,命令中的C程序擴(kuò)展名.C可省略,但匯編語言源程序文件的擴(kuò)展名.ASM不能省略。C語言和匯編語言源程序文件名的次序可以顛倒,最后生成的可執(zhí)行文件名以放在前面的文件名稱命名。本例使用的TCC命令如下:
TCC9_7.ASM9_7C.C
編譯和連接成功后將生成可執(zhí)行文件9_7.EXE,同時(shí)自動(dòng)生成目標(biāo)文件9_7.OBJ和9_7C.OBJ。
C程序的編譯和模塊連接也可以在TurboC集成環(huán)境下進(jìn)行。
此時(shí)需要選用一致的存儲(chǔ)模式,并建立一個(gè)工程文件,包括需編譯連接的C源文件名以及匯編語言目標(biāo)文件名。具體方法請(qǐng)讀者查閱相關(guān)資料。9.2.4參數(shù)的傳遞方法
1.通過外部變量傳遞參數(shù)
C語言調(diào)用匯編語言的子程序時(shí),參數(shù)的傳遞可以通過共用外部變量實(shí)現(xiàn)。
例9.8
將例9.3改寫為用C語言調(diào)用匯編語言的子程序getsum實(shí)現(xiàn)求a+b的和,并在屏幕上顯示結(jié)果。程序如下:
/*C語言程序:9_8C.C*/
#include"stdio.h"
inta=1,b=5,sum; /*聲明為外部變量,即全局變量*/
externvoidgetsum(void); /*聲明為外部函數(shù)*/
main() /*C語言主程序*/
{
getsum();
printf(“Thesumis%d”,sum);
getch();
}
;匯編語言程序:9_8.ASM
.MODELsmall,c
EXTRN_a:word,_b:word,_sum:word ;聲明_a、_b、_sum 為外部變量
PUBLIC_getsum ;聲明_getsum可被 外部程序調(diào)用
.CODE
_getsum
PROC
XORBX, BX
ADDBX, _a
ADDBX, _b
MOV_SUM,bx
RET
_getsumENDP
END在本例中,匯編語言程序使用了C程序定義的外部變量a、b、sum。根據(jù)前面介紹的命名規(guī)則的第一種情況,匯編語言程序中對(duì)這三個(gè)變量要使用前面加下劃線的名稱_a、_b、_sum;同理,C程序調(diào)用的getsum()在匯編語言源程序中被命名為_getsum。在匯編語言程序中必須將_a、_b、_sum聲明為外部變量才能使用,并且在兩個(gè)程序中,同一個(gè)變量的類型必須一致。C語言與匯編語言的數(shù)據(jù)類型對(duì)應(yīng)關(guān)系如表9.1所示。本例中,C程序的整型變量a、b、sum在匯編語言程序中對(duì)應(yīng)為字(word)類型。采用上節(jié)介紹的第二種編譯連接方法,以MASM5.0為例,對(duì)例9.8進(jìn)行編譯、連接過程及生成的可執(zhí)行文件9_8.EXE運(yùn)行結(jié)果如圖9.8所示。
圖9.8例9.8的編譯、連接過程及可執(zhí)行文件運(yùn)行結(jié)果
如果改用TASM4.1、MASM6.11或ML6.15匯編程序進(jìn)行編譯,則根據(jù)9.2.2節(jié)中命名規(guī)則的第二種情況,匯編語言源程序中的標(biāo)識(shí)符不要求加下劃線,只要保證匯編語言與C語言程序中標(biāo)識(shí)符同名即可。因此可以對(duì)例9.8中匯編語言子程序9_8.ASM進(jìn)行修改(C語言程序不變),將變量和子程序名稱前的下劃線去掉,修改后的程序如下:
.MODELsmall,c
EXTRNa:word,b:WORD,sum:WORD;聲明a、b、sum為外部變量,名稱前不加下劃線
PUBLICgetsum
;聲明getsum可被外部程序調(diào)用,名稱前不加下劃線
.CODE
getsumPROC
XORBX, BX
ADDBX, a
ADDBX, b
MOV
SUM, BX
RET
getsumENDP
END
有興趣的讀者可采用修改后的程序,用9.2.3節(jié)介紹的第三種編譯連接方法,直接生成可執(zhí)行文件,命令如下:
TCC9_8.ASM9_8C.C
C語言程序中也可使用匯編語言程序中的變量。此時(shí),要求在匯編語言程序中將該變量用PUBLIC進(jìn)行聲明,C程序中也必須用extern聲明,并且數(shù)據(jù)類型對(duì)應(yīng)一致。下面舉例說明。
例9.9
將例9.8中的a、b、sum變量改寫為在匯編語言源程序中定義,用C語言調(diào)用匯編語言的子程序getsum實(shí)現(xiàn)求a+b的和,并在屏幕上顯示結(jié)果。程序如下:
/*C語言程序:9_9C.C*/
#include"stdio.h"
externintsum; /*聲明為外部變量*/
externvoidgetsum(void); /*聲明為外部函數(shù)*/
main() /*C語言主程序*/
{
getsum();
printf("Thesumis%d",sum);
getch();
}
;匯編語言程序:9_9.ASM
.MODELsmall,c
PUBLIC_sum;聲明_sum為公用變量
PUBLIC_getsum;聲明_getsum可被外部程序調(diào)用
.data
aDW1
bDW5
_sumDW0
.CODE
_getsumPROC
XORBX, BX
ADDBX, a
ADDBX, b
MOV_SUM, BX
RET
_getsumENDP
END在本例中,C程序使用了匯編語言程序定義的外部變量_sum。根據(jù)前面介紹的命名規(guī)則的第一種情況,匯編語言程序中使用前面加下劃線的名稱_sum,而在C程序中使用不加下劃線的名稱sum。在匯編語言程序中必須將_sum聲明為公用變量,同時(shí)C程序中將其聲明為外部變量。_sum在匯編語言程序?yàn)樽?word)類型,對(duì)應(yīng)C語言中的數(shù)據(jù)類型為整型。
2.利用堆棧傳遞參數(shù)
C語言程序也可以通過堆棧將參數(shù)傳遞給被調(diào)用的匯編語言子程序。
C程序調(diào)用函數(shù)(子程序)之前,先從該函數(shù)中的最右邊的參數(shù)開始依次將參數(shù)壓入堆棧,最后壓入最左邊的參數(shù)。也就是說,參數(shù)壓入堆棧的順序與實(shí)參表中參數(shù)的排列順序相反。當(dāng)被調(diào)用函數(shù)運(yùn)行結(jié)束后,壓入堆棧中的參數(shù)已無保留的價(jià)值,C程序會(huì)立即調(diào)整堆棧指針SP,使之恢復(fù)壓入?yún)?shù)以前的值,這樣就釋放了堆棧中為參數(shù)保留的空間。也就是說,堆棧的平衡是在主函數(shù)程序?qū)崿F(xiàn)的,子程序不必在返回時(shí)調(diào)整堆棧指針SP。這就是C語言參數(shù)傳遞的規(guī)則,也稱為C語言調(diào)用規(guī)范。不同的語言采用的參數(shù)傳遞規(guī)則并不相同,例如Pascal語言就采用了不同于C語言的參數(shù)傳遞規(guī)則。匯編語言默認(rèn)采用C語言的參數(shù)傳遞規(guī)則,也可以選用Pascal語言的參數(shù)傳遞規(guī)則,但要保證調(diào)用程序和被調(diào)用程序采用的規(guī)則一致。
C語言參數(shù)在壓棧時(shí),有些參數(shù)要先進(jìn)行數(shù)據(jù)類型轉(zhuǎn)換再壓棧,各類型的參數(shù)在堆棧中所占的字節(jié)數(shù)不相同,如int型占2個(gè)字節(jié)、float和double型均占8個(gè)字節(jié)等,參數(shù)的類型與其在堆棧中所占的字節(jié)數(shù)如表9.2所示。
表9.2參數(shù)的類型與其在堆棧中所占用字節(jié)數(shù)根據(jù)存儲(chǔ)模式約定,C語言程序以tiny、small、compact模式編譯時(shí),它以近過程(NEAR)屬性調(diào)用外部函數(shù),此時(shí)堆棧中除了保存調(diào)用參數(shù)外,還需要保存偏移地址IP作為返回地址,IP在堆棧中占2個(gè)字節(jié),如圖9.9(a)所示。而如果C語言程序以medium、large、huge模式編譯,則外部函數(shù)具有遠(yuǎn)過程(FAR)屬性,堆棧中除了保存調(diào)用參數(shù)外,還保存段地址CS和偏移地址IP作為返回地址,CS和IP在堆棧中共占4個(gè)字節(jié),此時(shí)堆棧指向參數(shù)的指針SP需要多加2個(gè)字節(jié),如圖9.9(b)所示。
圖9.9調(diào)用外部函數(shù)時(shí)堆棧的情況
例9.10
利用堆棧傳遞參數(shù),實(shí)現(xiàn)求兩個(gè)數(shù)較小值的函數(shù)min。程序如下:
/*C語言程序:9_10C.C*/
externintmin(int,int); /*聲明引用外部函數(shù)*/
main() /*C語言主程序*/
{printf("Theminnumberis%d",min(131,765)); /*調(diào)用min并傳遞兩個(gè)實(shí)
際參數(shù)*/
getch();
}
;匯編語言程序:9_10.ASM
.MODELsmall,c
PUBLIC_min
.CODE
_min
PROC ;在小型模式下,這 是一個(gè)NEAR近過程
PUSH BP
MOV BP, SP ;使BP指向棧頂
MOV AX, [BP+4] ;從堆棧中取第1個(gè) 參數(shù)
CMP AX, [BP+6] ;將第1個(gè)參數(shù)與第2 個(gè)參數(shù)比較
JLE toexit
MOV AX, [BP+6] ;將最小數(shù)保存到ax 中,作為返回值
toexit:POPBP
RET
_minENDP
END
本例中,當(dāng)發(fā)生外部函數(shù)調(diào)用時(shí)堆棧的情況如圖9.10所示。
圖9.10例9.10調(diào)用外部函數(shù)時(shí)堆棧的情況匯編語言子程序向C程序返回處理結(jié)果時(shí),是通過AX和DX保存返回值并傳遞給調(diào)用者的,但對(duì)于不同長度的返回值,使用寄存器的情況也不同。具體規(guī)則如下:
(1)如果返回值小于或等于16位,如char、short、int類型時(shí),則將其存放在AX寄存器中。
(2)如果返回值是32位,如long類型時(shí),則返回值存放在DX和AX寄存器中,其中DX存儲(chǔ)高16位,AX存儲(chǔ)低16位。
(3)如果返回值大于32位,如float、double類型時(shí),則存放在靜態(tài)變量存儲(chǔ)區(qū),AX寄存器存放指向這個(gè)存儲(chǔ)區(qū)的偏移地址。對(duì)于32位FAR指針,則存儲(chǔ)區(qū)的偏移地址在AX中,段地址在DX中。
例9.11
將例9.10中匯編語言程序的存儲(chǔ)模式改為大型模式,C語言程序不變。程序如下:
C語言程序9_10C.C與例9.10相同,此處略。
;匯編語言程序:L9_11.ASM,大型模式下程序文件名必須以字母開頭
.MODELlarge,c
PUBLIC_min
.CODE
_minPROC ;在大型模式下,這是一個(gè)FAR遠(yuǎn)過程
PUSH BP
MOV BP, SP ;使BP指向棧頂
MOV AX, [BP+6] ;從堆棧中取第1個(gè) 參數(shù)
CMP AX, [BP+8] ;將第1個(gè)參數(shù)與第2 個(gè)參數(shù)比較
JLE toexit
MOV AX, [BP+8] ;將最小數(shù)保存到 AX中,作為返回值
toexit:
POP BP
RET
_minENDP
END
本例中,當(dāng)發(fā)生外部函數(shù)調(diào)用時(shí)堆棧的情況如圖9.11所示。
圖9.11例9.11調(diào)用外部函數(shù)時(shí)堆棧的情況本例中匯編語言程序的存儲(chǔ)模式為大型模式,要求程序文件名必須以字母開頭,否則無法成功編譯。另外使用TLINK連接時(shí)需使用如下命令:
TLINKLIB\C0LL9_119_10C,L9_11,,LIB\CL
命令中,LIB\C0L和LIB\CL是在LIB目錄下大型存儲(chǔ)模式的初始化模塊C0L.OBJ和函數(shù)庫CL.LIB。
使用TCC連接時(shí)需使用如下命令:
TCC-mlL9_11.OBJ9_10C.C
命令中,選項(xiàng)
-ml表示采用大型存儲(chǔ)模式。有興趣的讀者可分別修改例9.9~例9.11中的匯編語言源程序,C語言程序不變,用9.2.3節(jié)介紹的第三種編譯、連接方法重新生成可執(zhí)行文件。
3.利用堆棧傳遞參數(shù)的改進(jìn)方法
通過例9.10和例9.11可以發(fā)現(xiàn),采用堆棧方式傳遞參數(shù)時(shí),需要知道參數(shù)和返回地址在堆棧中的位置以及變化情況,編程時(shí)稍有不慎就會(huì)產(chǎn)生錯(cuò)誤。下面介紹一種改進(jìn)的參數(shù)傳遞方法。下面舉例說明。
例9.12
對(duì)例9.10求兩個(gè)數(shù)較小值的函數(shù)min進(jìn)行改進(jìn),C語言程序不變,主要修改了匯編語言程序。程序如下:
C語言程序9_10C.C與例9.10相同,此處略。
;匯編語言程序:9_12.ASM
.MODELsmall,c
PUBLICmin ;匯編語言程序和C語言程序共用標(biāo)識(shí)符,不必加下劃線
.CODE
minPROC,v1:WORD, v2:WORD ;為子程序(函數(shù))min增加形式參數(shù)v1、v2
MOV AX, v1 ;從堆棧中取第1個(gè)參數(shù)
CMP AX, v2 ;將第1個(gè)參數(shù)與第2個(gè)參數(shù) 比較
JLE toexit
MOV AX, v2 ;將最小數(shù)保存到AX中, 作為返回值
toexit:RET
minENDP
END
本例中子程序min作為匯編語言程序和C語言程序共用標(biāo)識(shí)符,不必加下劃線,匯編語言源程序在定義min同時(shí)聲明了形式參數(shù)v1、v2,并指明其類型。在調(diào)用子程序時(shí)實(shí)際參數(shù)分別對(duì)應(yīng)形式參數(shù),避免了直接對(duì)堆棧進(jìn)行操作。本例使用9.2.3節(jié)介紹的第三種方法進(jìn)行編譯和連接,使用TCC一次性完成編譯和連接,命令如下:
TCC9_12.ASM9_10C.C
命令中,匯編語言程序擴(kuò)展名.OBJ不能省略,C語言文件擴(kuò)展名
.C可以省略。該命令執(zhí)行時(shí)TCC會(huì)自動(dòng)調(diào)用TASM和TLINK完成編譯和連接。編譯和連接成功后將生成可執(zhí)行文件9_12.EXE,同時(shí)自動(dòng)生成目標(biāo)文件9_12.OBJ和9_10C.OBJ。
本例編譯、連接過程及生成的可執(zhí)行文件9_12.EXE運(yùn)行結(jié)果如圖9.12所示。
圖9.12例9.12的編譯、連接過程及可執(zhí)行文件運(yùn)行結(jié)果
4.地址參數(shù)的傳遞
C語言程序與匯編語言程序的參數(shù)傳遞,可采用傳數(shù)值和傳地址兩種方式。如果參數(shù)是傳數(shù)值的,則可直接寫出實(shí)際參數(shù),如例9.10;如果參數(shù)是傳地址的,則在聲明中將參數(shù)類型說明為指針類型,調(diào)用時(shí),用“&”取得變量的地址作為實(shí)參傳遞。例如:
externadd(int*,int*,int);/*函數(shù)的聲明*/
add(&m,&n,c);/*函數(shù)的調(diào)用,前兩個(gè)參數(shù)傳地址,最 后一個(gè)參數(shù)傳數(shù)值*/
在匯編語言子程序中,利用基址指針BP,先取得地址,再間接取內(nèi)容,修改后送回原處,同時(shí)以RET返回。下面舉例說明。
例9.13
采用傳地址方式進(jìn)行參數(shù)傳遞,將變量中的小寫字母變?yōu)榇髮懽帜?。程序如下?/p>
/*C語言程序:9_13C.C*/
externvoidupper(char*); /*聲明外部函數(shù),其參數(shù)類 型為指針型*/
main()
{charchr='a';
printf("Beforcallchris%c\n",chr);
upper(&chr); /*將地址作為參數(shù)傳遞*/
printf("Aftercallchris%c\n",chr);
getch();
}
;匯編語言程序:9_13.ASM
.MODELsmall,c
.CODE
PUBLICupper
upperPROC
PUSH BP
MOV BP, SP
MOV BX, [BP+4] ;從堆棧中得到變量chr的地址,送給BX
MOV AX, [bx] ;得到變量chr的值,送給AX
SUB AX, 20h ;將AX中的字符ASCII碼減20h,由小寫字母變?yōu)榇?/p>
;寫字母
MOV [BX],AX ;轉(zhuǎn)換后的大寫字母ASCII碼,存入變量chr所在地址單元
POP BP
RET
upperENDP
END本例以傳地址方式傳送參數(shù),實(shí)際被壓入堆棧的是變量chr所在存儲(chǔ)單元的邏輯地址,得到的返回值也存入這個(gè)地址所指向的存儲(chǔ)單元,即變量chr中。
作為參數(shù)傳送的地址也要分成近指針(僅含偏移地址)和遠(yuǎn)指針(含段地址和偏移地址)。C語言程序以tiny、small、medium模式編譯時(shí),它以近指針傳遞地址,在堆棧占2個(gè)字節(jié);而如果C語言程序以compact、large、huge模式編譯,則地址是32位的遠(yuǎn)指針,在堆棧中要占4個(gè)字節(jié)。C程序調(diào)用子程序時(shí)堆棧的情況可以參考例9.10和例9.11來理解。
本例編譯、連接過程及生成的可執(zhí)行文件9_13.EXE運(yùn)行結(jié)果如圖9.13所示。
圖9.13例9.13的編譯、連接過程及可執(zhí)行文件運(yùn)行結(jié)果
本節(jié)介紹在匯編語言程序中調(diào)用C語言程序模塊的方法。雖然匯編語言程序調(diào)用C語言函數(shù)的情況不經(jīng)常使用,但這是可以實(shí)現(xiàn)的。其中的調(diào)用約定規(guī)則、編譯和連接方法以及參數(shù)傳遞方法與9.2節(jié)介紹的方法類似。讀者在學(xué)習(xí)時(shí)應(yīng)注意對(duì)比其中的相似點(diǎn)與不同點(diǎn)。9.3匯編語言調(diào)用C語言程序模塊9.3.1匯編語言調(diào)用C語言程序的主要步驟
匯編語言調(diào)用C語言程序模塊的主要步驟分為C語言和匯編語言兩個(gè)方面。
1.在C語言編程方面
①如果使用外部變量傳遞參數(shù),則需定義供匯編語言程序引用的外部變量(全局變量);
②聲明和實(shí)現(xiàn)供匯編語言程序引用的子函數(shù),如需返回值,則用return返回;
③將C語言源程序編譯生成目標(biāo)文件。
2.在匯編語言編程方面
①用EXTRN或EXTERN偽指令聲明C語言定義的子函數(shù)和變量為外部變量;
②如果使用堆棧傳遞參數(shù),則按照C語言調(diào)用協(xié)議將調(diào)用參數(shù)壓入堆棧;
③使用CALL指令調(diào)用C語言定義的子函數(shù);
④從AX或者DX:AX中取得返回值;
⑤修改SP寄存器的值,將調(diào)用參數(shù)清除出棧,保持堆棧棧頂回到調(diào)用前位置;
⑥使用匯編程序TASM、MASM或ML匯編源程序,形成目標(biāo)文件;
⑦使用TLINK或TCC連接C語言和匯編語言的目標(biāo)文件,形成可執(zhí)行文件;
⑧執(zhí)行程序進(jìn)行驗(yàn)證和調(diào)試。
注意:可執(zhí)行文件的生成方法除了上述步驟介紹的傳統(tǒng)方法以外,還可以省略生成目標(biāo)文件的步驟,采用TCC程序直接生成可執(zhí)行文件,具體方法請(qǐng)見9.2.3節(jié)。9.3.2匯編語言調(diào)用C語言程序的約定規(guī)則
9.2節(jié)介紹的C語言程序調(diào)用匯編語言程序的各種約定仍然適用,而且必須遵循。另外,還要注意以下幾點(diǎn)。
1.命名約定
如果使用MASM5.0對(duì)匯編語言源程序進(jìn)行編譯,由于C語言程序經(jīng)編譯后自動(dòng)在函數(shù)和變量名前加下劃線“_”,所以在匯編語言調(diào)用C語言函數(shù)時(shí)應(yīng)使用“CALL_mysub”指令而不是“CALLmysub”指令,其中mysub為C語言中定義的函數(shù)。對(duì)于變量的使用也是同樣道理。
如果使用TASM4.1、MASM6.11或ML6.15對(duì)匯編語言源程序進(jìn)行編譯,那么匯編語言和C語言中的函數(shù)(子程序)保持同名即可被相互調(diào)用,不要求匯編語言源程序中的標(biāo)識(shí)符以下劃線開頭,這種方法更加簡(jiǎn)便。
2.聲明約定
為了使C函數(shù)對(duì)匯編語言程序可見,匯編語
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年第四季度山東臨沂市公安機(jī)關(guān)招錄警務(wù)輔助人員400人考試備考題庫附答案
- 2025年貴州電子科技職業(yè)學(xué)院輔導(dǎo)員招聘考試真題匯編附答案
- 2025福建省福州市潤樓教育科技集團(tuán)有限公司招聘2人備考題庫附答案
- 2026上海交通大學(xué)醫(yī)學(xué)院繼續(xù)教育學(xué)院繼續(xù)教育管理辦公室招聘1人參考題庫附答案
- 2025貴州遵義市教育體育局直屬事業(yè)單位遵義市體育運(yùn)動(dòng)學(xué)校招聘事業(yè)單位人員1人備考題庫及一套完整答案詳解
- 2026廣東廣州生物醫(yī)藥與健康研究院潘光錦組招聘生物信息學(xué)分析師科研助理1人備考題庫帶答案詳解
- 2026中國科學(xué)院化學(xué)研究所極端環(huán)境高分子材料實(shí)驗(yàn)室項(xiàng)目聘用人員招聘?jìng)淇碱}庫完整參考答案詳解
- 2025廣西百色市科學(xué)技術(shù)館面向全市公開選調(diào)館長1人備考題庫附答案詳解
- 2025至2030中國直播電商平臺(tái)GMV增長分析及未來競(jìng)爭(zhēng)格局研究報(bào)告
- 2026年春季云南曲靖市關(guān)工委麒麟希望學(xué)校學(xué)期教師招聘4人備考題庫及答案詳解一套
- T-CSUS 69-2024 智慧水務(wù)技術(shù)標(biāo)準(zhǔn)
- 國家開放大學(xué)法學(xué)本科《商法》歷年期末考試試題及答案題庫
- UL583標(biāo)準(zhǔn)中文版-2018電動(dòng)工業(yè)車輛UL中文版標(biāo)準(zhǔn)
- 鋼結(jié)構(gòu)加工制造工藝
- 2024年新華東師大版七年級(jí)上冊(cè)數(shù)學(xué)全冊(cè)教案(新版教材)
- 新版高中物理必做實(shí)驗(yàn)?zāi)夸浖捌鞑?(電子版)
- 冀人版五年級(jí)科學(xué)上冊(cè)期末測(cè)試卷4份(含答案)
- 菜肴造型與盛裝工藝
- 甲狀腺癌醫(yī)學(xué)知識(shí)講座
- ABAQUS在隧道及地下工程中的應(yīng)用
- 工作匯報(bào)PPT(山與海之歌動(dòng)態(tài))大氣震撼模板
評(píng)論
0/150
提交評(píng)論