《微型計(jì)算機(jī)原理及應(yīng)用》課件第9章_第1頁
《微型計(jì)算機(jī)原理及應(yīng)用》課件第9章_第2頁
《微型計(jì)算機(jī)原理及應(yīng)用》課件第9章_第3頁
《微型計(jì)算機(jī)原理及應(yīng)用》課件第9章_第4頁
《微型計(jì)算機(jī)原理及應(yīng)用》課件第9章_第5頁
已閱讀5頁,還剩156頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(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ì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論