第7章匯編語言與高級語言接口_第1頁
第7章匯編語言與高級語言接口_第2頁
第7章匯編語言與高級語言接口_第3頁
第7章匯編語言與高級語言接口_第4頁
第7章匯編語言與高級語言接口_第5頁
已閱讀5頁,還剩77頁未讀 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認(rèn)領(lǐng)

文檔簡介

任向民王克朝王喜德馮阿芳編著高級匯編語言程序設(shè)計實用教程(第2版)清華大學(xué)出版社高等院校信息技術(shù)規(guī)劃教材第7章

匯編語言與高級語言接口7.1混合編程7.2C/C++的嵌入式匯編

7.2.1在C/C++程序中嵌入?yún)R編語句

7.2.2在嵌入式匯編中訪問C/C++的數(shù)據(jù)

7.2.3匯編語言程序段編寫C函數(shù)

7.2.4匯編程序調(diào)用C/C++函數(shù)7.3用C/C++調(diào)用匯編

7.3.1接口

7.3.2調(diào)用匯編模塊 7.1

混合編程

一般說來高級語言具有豐富的數(shù)據(jù)結(jié)構(gòu)、種類繁多的運算符、豐富的函數(shù)、易讀易寫、可移植性好等特點,但用高級語言編寫的程序,代碼較長,占有存儲空間大,運行速度慢。

而用匯編語言編寫的程序所占內(nèi)存空間小,執(zhí)行速度快,有直接控制硬件的能力,但程序繁瑣,難讀也難編寫,且必須熟悉計算機的內(nèi)部結(jié)構(gòu)及其有關(guān)硬件知識。7.1

混合編程混合編程即由高級語言來調(diào)用或嵌入?yún)R編語言子程序,或用匯編語言調(diào)用或嵌入高級語言子程序。匯編程序常以過程的形式同高級語言(如C/C++、Basic、Pascal、Delphi等)一起使用。在與高級語言接口時,匯編器使用兩種調(diào)用協(xié)議用于C/C++語言的C/C++調(diào)用協(xié)議和用于Basic、Pascal和Fortran語言的Pascal語言調(diào)用協(xié)議。調(diào)用協(xié)議語言在MODEL語句中或與PROC語句相聯(lián)系的OPTION指示符中指定。除了用這些語句以外還可以用完全段定義指定。7.1

混合編程高級語言和匯編語言連接很容易,因為在高級語言編譯后生成的編譯程序是一個.OBJ的文件,這與匯編程序輸出的目標(biāo)文件一樣都是機器語言程序。那么我們就可以利用link將高級語言程序產(chǎn)生的.OBJ程序與匯編程序產(chǎn)生的.OBJ程序連接起來,形成一個.EXE的可執(zhí)行文件。7.1

混合編程高級語言與匯編語言的連接應(yīng)注意下面幾個問題:1.兩種語言之間的控制傳輸問題一般來說匯編語言程序作為高級語言的外部子程序,由高級語言通過函數(shù)或者過程進行調(diào)用匯編語言程序。2.參數(shù)的傳遞通常高級語言程序使用系統(tǒng)堆棧向匯編語言傳遞入口參數(shù),匯編語言程序返回時使用CPU內(nèi)部寄存器帶回計算結(jié)果。此外還需要確定哪些寄存器是需要保留下來的,哪些是可以使用的。7.1

混合編程3.存儲分配問題高級語言不需要考慮存儲分配問題,編譯程序和連接程序會自動的進行存儲分配。當(dāng)匯編語言與高級語言程序連接時,就需要考慮這個問題了。這個問題處理起來不是很復(fù)雜,一般是將匯編語言作為一個程序模塊,由連接程序決定其在存儲器中的位置。不同的高級語言與匯編語言的混合編程所采用的方法是不相同的。本章當(dāng)中主要介紹C/C++與匯編的混合編程問題,在下面的幾節(jié)當(dāng)中,我們將分別介紹C/C++的嵌入式匯編、C/C++調(diào)用匯編的具體方法。7.2

C/C++的嵌入式匯編

7.2.1在C/C++程序中嵌入?yún)R編語句

7.2.2在嵌入式匯編中訪問C/C++的數(shù)據(jù)

7.2.3匯編語言程序段編寫C函數(shù)

7.2.4匯編程序調(diào)用C/C++函數(shù)7.2

C/C++的嵌入式匯編利用匯編語言程序設(shè)計的一種非常常見的方式是在高級語言(例如C/C++)程序內(nèi)編寫匯編函數(shù)。完成這一工作有幾種不同的方式把匯編語言函數(shù)直接放到C/C++語言程序內(nèi)。這種技術(shù)稱為嵌入式匯編或內(nèi)聯(lián)匯編(InlineAssembly)。7.2

C/C++的嵌入式匯編在C/C++與匯編語言的混合編程過程中,C/C++調(diào)用匯編代碼常有兩種方法:(一)直接在C/C++程序中嵌入?yún)R編語句 (二)C/C++調(diào)用匯編語言子程序把匯編語言程序加入到C/C++程序中,必須使匯編程序和C/C++程序一樣具有明確的邊界、參數(shù)、返回值和局部變量,必須為匯編語言編寫的程序段指定段名并進行定義,如果要在它們之間傳遞參數(shù),則必須保證匯編程序用來傳遞參數(shù)的存儲區(qū)和C/C++函數(shù)使用的存儲區(qū)是一樣的。7.2

C/C++的嵌入式匯編在C/C++程序中采用“_ASM”關(guān)鍵字輸入?yún)R編語言指令語句或語句段。在C或者C++中進行嵌入式匯編需要注意以下要點:(1)嵌入式匯編語言代碼支持INTEL80x86CPU的全部32位指令系統(tǒng),但是不能使用偽指令與宏指令語句,也不能使用結(jié)構(gòu)(STRUCT)和記錄(RECORD)(2)嵌入式匯編語言可以使用C++程序中標(biāo)識符,包括標(biāo)號、變量、函數(shù)名、常量、宏、類型名、結(jié)構(gòu)和聯(lián)合的成員以及類對象的公有(PUBLIC)成員變量等;7.2

C/C++的嵌入式匯編(3)嵌入式匯編語言代碼中可以使用匯編語言格式的常數(shù)(131AH),也可以使用C++格式的常數(shù)(0X131A)(4)嵌入式匯編語言不能使用C++語言的運算符(5)嵌入式匯編語言代碼中的轉(zhuǎn)移指令和C++中的GOTO語句都能跳轉(zhuǎn)到匯編語言或者C++定義的標(biāo)號(6)嵌入式匯編語言定義的函數(shù)返回值的傳遞方法與預(yù)模塊調(diào)用匯編中匯編語言程序返回值的傳遞方法相同,在C++程序編譯時會產(chǎn)生“NORETURNVALUE”警告,可以使用#PRAGMAWARNING(DISABLE:4035)預(yù)編譯語句禁止該警告7.2.1

在C/C++程序中嵌入?yún)R編語句嵌入?yún)R編語言指令采用_ASM關(guān)鍵字,嵌入?yún)R編格式,具體應(yīng)用通常采用兩種方式。第一種方式,嵌入?yún)R編語言指令是在匯編語句前加一個_ASM關(guān)鍵字,格式如下:_ASM操作碼操作數(shù)<;或換行>其中,操作碼是處理器指令或若干偽指令;操作數(shù)是操作碼可接受的數(shù)據(jù)。內(nèi)嵌的匯編語句可以用分號“;”結(jié)束,也可以用換行符結(jié)束;一行中可以有多個匯編語句,相互間用分號分隔,但不能跨行書寫。嵌入?yún)R編語句的分號不是注釋的開始;要對語句注釋,應(yīng)使用C的注釋,如/*…*/。例如:_ASMMOVAX,DS;/*AX←DS*/_ASMPOPAX;ASMPOPDS;ASMRET;/*合法語句*/_ASMPUSHDS/*ASM語句是C/C++程序中惟一可以用換行結(jié)尾的語句*/7.2.1

在C/C++程序中嵌入?yún)R編語句在C/C++程序的函數(shù)內(nèi)部,每條匯編語言語句都是一條可執(zhí)行語句,它被編譯進程序的代碼段。在函數(shù)外部,一條匯編語句是一個外部說明,它在編譯時被放在程序的數(shù)據(jù)段中;這些外部數(shù)據(jù)可被其他程序引用。例如:_ASM ERRMSGDB’SYSTEMERROR’_ASM NUMDW0FFFFH_ASM PIDD3.1415926_ASM段可以放在C/C++語言程序段中的任何位置上。_ASM匯編語句:_ASM MOV AX,15H_ASM MOV CX,9H_ASM ADD AX,CX7.2.1

在C/C++程序中嵌入?yún)R編語句下面這個小程序就是一個實際的調(diào)用匯編的例子:#INCLUDE<STDIO.H>INTMAIN(){INT A=10;INT B=20;INT RESULT;RESULT=A*B;_ASM NOP;PRINTF("THERESULTIS",RESULT);RETURN 0;}7.2.1

在C/C++程序中嵌入?yún)R編語句在這個程序當(dāng)中,_ASMNOP不執(zhí)行任何任務(wù),是一個空操作?;镜那度?yún)R編代碼可以利用應(yīng)用程序中定義的全局C變量。這里要注意的是只有全局定義的變量才能在基本的內(nèi)聯(lián)匯編代碼內(nèi)使用。通過C/C++程序中使用的相同名稱引用這種變量。第二種方式,_ASM{匯編程序段}采用花括號的匯編語言程序段形式。_ASM{匯編程序段}如下所示:_ASM{MOV AX,15HMOV CX,9HADD AX,CX}7.2.1

在C/C++程序中嵌入?yún)R編語句包含在括號中的匯編代碼必須按照特定的格式:(1)指令必須括在引號里。(2)如果包含的指令超過一條,那么必須使用新行字符分隔匯編語言代碼的每一行。通常,還包含制表符幫助縮進匯編語言代碼,使代碼行更容易閱讀。(規(guī)則2)是因為編譯器逐字地取得ASM段中的匯編代碼,并且把它們放在為程序生成的匯編代碼中。7.2.1

在C/C++程序中嵌入?yún)R編語句下面通過幾個例子來具體的了解下嵌入式匯編的過程?!纠?-1】顯示1到1000中任一個數(shù)的二進制到十六進制數(shù)。#include<iostream.h>CHAR*BUFFER="EnterANumberBetween0To1000:";CHAR*BUFFER1="BASE";INTB=0;CHARA;/*顯示字符的匯編程序段*/7.2.1

在C/C++程序中嵌入?yún)R編語句VOIDDISPS(INTBASE,INTDATA){INTTEMP;_ASM{MOV AX,DATAMOV BX,BASEPUSH BXTOP1:MOVE DEX,0DIV BXPUSH DXCMP AX,0JNZ TOP1TOP2:POP DXCMP DX,BXJE TOP4ADD DX,30HCMP DX,39HJBE TOP3ADD DX,7TOP3:MOV TEMP,EX}COUT<<(CHAR)TEMP;_ASM{JMP TOP27.2.1

在C/C++程序中嵌入?yún)R編語句}}/*C++的主函數(shù)段*/TOP4:VOID MAIN(VOID){INT I;COUT<<BUFFER;CIN GET(A);WHILE(A>=’0’&&A<=’9’){_ASM SUBA,30HB=B*10+A;CIN GET(A);}FOR(I=2;I<17;I++){COUT<<BUFFER1;DISPS(10,I); /*調(diào)用匯編函數(shù),進行顯示*/COUT<<(CHAR)(0X20);DISPS(I,B); /*調(diào)用匯編函數(shù),進行顯示*/COUT<<(CHAR)(10);COUT<<(CHAR)(13);}}7.2.1

在C/C++程序中嵌入?yún)R編語句在MicrosoftVC++6.0環(huán)境下編寫匯編與C/C++混合程序時,只能編寫32位應(yīng)用程序,而不能編寫16位應(yīng)用程序,在32位應(yīng)用程序的混合編程中應(yīng)注意不能使用DOS功能調(diào)用INT21H,用它只能編寫16位應(yīng)用程序。由于內(nèi)嵌匯編不能使用匯編的宏和條件控制偽指令,這時就需要進行單獨編寫匯編模塊,然后和C/C++程序連接,這種編程關(guān)鍵要解決的問題是二者的接口和參數(shù)傳遞,參數(shù)傳遞包括值傳遞、指針傳遞等。7.2.1

在C/C++程序中嵌入?yún)R編語句在使用嵌入式匯編中要注意的幾個問題:(1)操作碼支持8086/8087指令或若干偽指令:db/dw/dd和extern(2)操作數(shù)是操作碼可接受的數(shù)據(jù):立即數(shù)、寄存器名,還可以是C/C++程序中的常量、變量和標(biāo)號等(3)內(nèi)嵌的匯編語句可以用分號“;”結(jié)束,也可以用換行符結(jié)束(4)使用C的注釋,如/*…*/(5)正確運用通用寄存器、標(biāo)號等7.2.2

在嵌入式匯編中訪問C/C++的數(shù)據(jù)上面講了如何在C++中使用匯編語言。反之也可以在匯編代碼段中使用設(shè)置C++的變量及其他元素。內(nèi)嵌的匯編語句除可以使用指令允許的立即數(shù)、寄存器外,還可以使用C/C++程序中的任何符號(標(biāo)識符),包括變量、常量、標(biāo)號、函數(shù)名、寄存器變量、函數(shù)參數(shù)等;C編譯程序自動將它們轉(zhuǎn)換成相應(yīng)匯編語言指令的操作數(shù),并在標(biāo)識符名前加下劃線。一般來說,只要匯編語句能夠使用存儲器操作數(shù)(地址操作數(shù)),就可以采用一個C/C++程序中的符號;同樣,只要匯編語句可以用寄存器作為合法的操作數(shù),就可以使用一個寄存器變量。7.2.2

在嵌入式匯編中訪問C/C++的數(shù)據(jù)對于具有內(nèi)嵌匯編語句的C/C++程序,C編譯器要調(diào)用匯編程序進行匯編。匯編程序在分析一條嵌入式匯編指令的操作數(shù)時,若遇到了一個標(biāo)識符,它將在C/C++程序的符號表中搜索該標(biāo)識符;但8086寄存器名不在搜索范圍之內(nèi),而且大小寫形式的寄存器名都可以使用。7.2.2

在嵌入式匯編中訪問C/C++的數(shù)據(jù)【例7-2】用嵌入?yún)R編方式實現(xiàn)取兩數(shù)較小值的函數(shù)MININTMIN(INTVAR1,INTVAR2) /*用嵌入?yún)R編語句實現(xiàn)的求較小值*/{ASMMOV AX,VAR1ASMCMP AX,VAR2ASMJLE MINEXITASMMOV AX,VAR2MINEXIT: RETURN(_AX) /*將寄存器AX的內(nèi)容作為函數(shù)的返回值*/}MAIN() /*C/C++主程序*/{MIN(100,200);}7.2.2

在嵌入式匯編中訪問C/C++的數(shù)據(jù)在_ASM模塊中,可以使C++或ASM的基數(shù)計數(shù)法,例如0x100和100H是相等的。_ASM塊中不能使用“<<”之類的C++操作符。C++和MASM通用的操作符,例如“*”和“[]”則被認(rèn)為是匯編語言的操作符,方括號[]在C中表示數(shù)組下標(biāo),而在內(nèi)嵌匯編中認(rèn)為是索引操作符,表示字節(jié)偏移量。如:_ASMMOVARRAY[6],BX;STOREBXATARRAY+6(NOTSCALED)當(dāng)然也可以使用“TYPE”來使其與C++風(fēng)格一致。例如,下面兩條語句的作用是一樣的:ASMMOV ARRAY[6*TYPEINT],0;ARRAY[6]=0;7.2.2

在嵌入式匯編中訪問C/C++的數(shù)據(jù)嵌入?yún)R編也能通過變量名直接引用C++的變量。如果C++中的類、結(jié)構(gòu)或者枚舉成員具有惟一的名稱,如果在“.”操作符之前不指定變量或者TYPEDEF名稱,則_ASM塊中只能引用成員名稱。如果成員不是惟一的,則必須在“.”之前加上變量名或TYPEDEF名稱。STRUCT NAM1{CHAR*FTH;INT NAME12;};STRUCT NAM2{INT WIN;LONG NAME12;};7.2.2

在嵌入式匯編中訪問C/C++的數(shù)據(jù)如果按下面聲明變量:STRUCTNAM1HAL;STRUCTNAM2OAT;那么,所有引用NAME12成員的地方都必須使用變量名,因為NAME12不是惟一的。而FTH變量卻具有惟一的名稱,可以簡單地用它的成員名稱來引用。如:_ASM{MOV BX,OFFSETHALMOV CX,[BX]HAL.NAME12MOV SI,[BX].FTH ;省略“HAL”}7.2.3

匯編語言程序段編寫C函數(shù)在調(diào)用函數(shù)之前應(yīng)編程將參數(shù)以逆序?qū)懭氲疆?dāng)前運行任務(wù)所使用的任務(wù)堆棧中,壓棧之前堆棧指針可不作調(diào)整。被調(diào)用的C函數(shù)即可正常訪問調(diào)用者傳遞的參數(shù),函數(shù)調(diào)用完畢后需要調(diào)整堆棧指針,清除函數(shù)調(diào)用中參數(shù)所占用的堆??臻g。C函數(shù)的返回值可以通過訪問累加器獲得。為了能夠編寫出可供C/C++調(diào)用的函數(shù)。應(yīng)了解C/C++模塊與匯編模塊的接口機制,而從以匯編形式給出的編譯結(jié)果中可方便地了解這種機制。7.2.3

匯編語言程序段編寫C函數(shù)C/C++程序中含有嵌入式匯編語言語句時,C編譯器首先將C代碼的源程序(.c)編譯成匯編語言源文件(.asm),然后激活匯編程序?qū)a(chǎn)生的匯編語言源文件編譯成目標(biāo)文件(.obj),最后激活link將目標(biāo)文件鏈接成可執(zhí)行文件(.exe)。【例7-3】將字符串中的小寫字母轉(zhuǎn)變?yōu)榇髮懽帜革@示。7.2.3

匯編語言程序段編寫C函數(shù)/*程序名:SJW7-03.C*/#INCLUDEVOID UPPER(CHAR*DEST,CHAR*SRC){ASMMOV SI,SRC /*DEST和SRC是地址指針*/ASMMOV DI,DESTASMCLDLOOP:ASMLODSB /*C/C++定義的標(biāo)號*/ASMCMP AL,'A'ASMJB COPY /*轉(zhuǎn)移到C的標(biāo)號*/ASMCMPAL,'Z'ASMJA COPY /*不是’A’到’Z’之間的字符原樣復(fù)制*/ASMSUB AL,20H /*是小寫字母轉(zhuǎn)換成大寫字母*/COPY:ASMSTOSBASMAND AL,AL /*C/C++中,字符串用NULL(0)結(jié)尾*/ASMJNZ LOOP}MAIN()/*主程序*/{CHARSTR[]="THISSTARTEDOUTASLOWERCASE!";CHARCHR[100];UPPER(CHR,STR); /*調(diào)用匯編的UPPER函數(shù)*/PRINTF("ORIGINSTRING:\N%S\N",STR);PRINTF("UPPERCASESTRING:\N%S\N",CHR);}7.2.3

匯編語言程序段編寫C函數(shù)編輯完成后,在命令行輸入如下編譯命令,選項-I和-L分別指定頭文件和庫函數(shù)的所在目錄。TCC-B–Iinclude–Llibsjw7-03.c嵌入?yún)R編方式把插入的匯編語言語句作為C/C++的組成部分,不使用完全獨立的匯編模塊,所以比調(diào)用匯編子程序更方便、快捷,并且在大存儲模式、小存儲模式下都能正常編譯通過。如下C/C++程序:7.2.3

匯編語言程序段編寫C函數(shù) /*程序名:SJW7-04.C*/INT SUM(INT,INT,INT)} /*說明函數(shù)SUM的調(diào)用格式*/INT SJW01=5; /*已初始化的變量*/INT SJW02; /*未初始化的變量*/MAIN() /*主函數(shù)*/{SJW02=SUM(1,SJW01,3);PRINTF("%D\N",SJW02);}INT SUM(INTI,INTJ,INTM)RETURN(I+J+M);7.2.3

匯編語言程序段編寫C函數(shù) 用下面的命令要求C/C++按SMALL,模式編譯SJW7-0.C。并以匯編格式輸出編譯結(jié)果:TCC-MS-SSJW7-0.C以匯編格式輸出的編譯結(jié)果保存在文件SJW7-04.ASM中,盡管該文件比較冗長。但它對理解編寫供C調(diào)用的匯編函數(shù)是有幫助的。而且對學(xué)習(xí)C/C++也是有益的。SJW7-04.ASM的主要內(nèi)容如下所示:7.2.3

匯編語言程序段編寫C函數(shù) _TEXTSEGMENTBYTEPUBLIC'CODE' ;代碼段DGROUPGROUPDATA,BSSASSUMECS:_TEXT,DS:DGROUP,SS:DGROUP_TEXTENDS_DATASEGMENTGWORDPUBLIC’DATA’;已初始化的數(shù)據(jù)段_SJW01 LABELWORD_DATAENDSPUSH AX ;為調(diào)用SUM壓入第三個參數(shù)PUSH WORDPTRDGROUP:_SJW01MOV AX,1PUSH AX ;壓入第一個參數(shù)CALL NEARPTR_SUM ;調(diào)用SUM7.2.3

匯編語言程序段編寫C函數(shù)ADD SP,6 ;廢除壓入堆棧的三個參數(shù)MOV WORDPTRDGROUP:SJW02,AXPUSH WORDPTRDGROUP:_SJW02MOV AX,OFFSETDGROUP:SJ03PUSH AX ;壓入第一個叁數(shù)CALL NEARPTR_PRINTF;POP CX ;廢除壓入堆棧的兩十參數(shù)POP CXSJ01:RET_MAIN ENDP_SUM PROCNEARPUSH BP7.2.3

匯編語言程序段編寫C函數(shù)MOV BP,SPMOV AX,WORDPTR[BP+4]ADD AX,WORDPTR[BP+6]ADD AX,WORDPTR[BP+8]JMP SHORTSJ02SJ02:POP BPRET_SUM ENDP_TEXT ENDS_BSS SEGMENTWORDPUBLIC‘BSS’_SJW02 LABELWORDDB2 DUP(?)_BSS ENDS7.2.3

匯編語言程序段編寫C函數(shù)DATASEGMENTWORDPUBLIC‘DATA’SJ03:LABELBYTEDB 47DB 100DB 12DB 0.DATA ENDSTEXT SEGMENTBYTEPUBLIC‘CODE’EXTRN_PRINTF: NEARTEXT ENDSPUBLIC _SJW02PUBLIC _SJW01PUBLIC _MAINPUBLIC _SUMEND7.2.3

匯編語言程序段編寫C函數(shù)從上面以匯編格式給出的編譯結(jié)果中,可以看到函數(shù)SUM除包含一條多余的跳轉(zhuǎn)指令外,已足夠精練。但為了方便地說明如何縮寫供C/C++調(diào)用的函數(shù),在這里仍然把上述C函數(shù)SUM改寫成匯編函數(shù)。相應(yīng)地,C/C++程序SJW7-04.C改寫如下:EXTERN SUM(INT,INT,INT)INT SJW01=5;INT SJW02;MAIN(){SJW02=SUM(1,SJW01,3);PRINTF(“%D\N”,SJW02);}7.2.4

匯編程序調(diào)用C/C++函數(shù)匯編程序中調(diào)用C函數(shù)相對比較簡單,編譯器已經(jīng)提供了相當(dāng)完善的支持。匯編語句中的CALL語句,可以用于調(diào)用其他過程,既可以是其他匯編程序段也可以是C/C++程序中的標(biāo)準(zhǔn)過程?!纠?-4】輸出“HELLOWORLD”。7.2.4

匯編程序調(diào)用C/C++函數(shù)EXTERNTEST();VOID MAIN(){TEST();}VOID SHOW(CHAR3STR){PRINTF(“%S”,STR);};匯編子程序:HELLO.ASM.MODEL SMALL,CEXTERN SHOW:NEARDATAHELLOSTRING DB‘HELLO,ASSEMBLY!’,0DH,0AH,’$’ZYSTRING DB‘HELLO,CPROGRAM!’,0.CODEPUBLIC _TEST_TESTPROCLEA DX,HELLOSTRINGMOV AH,09HINT 21HLEA AX,ZYSTRINGPUSH AXCALL _SHOWADD SP,2RET_TEST ENDPEND7.2.4

匯編程序調(diào)用C/C++函數(shù)在嵌入?yún)R編中可以無限制地訪問C++成員變量,但是卻不能隨意調(diào)用C++的成員函數(shù)。嵌入?yún)R編調(diào)用C++函數(shù)必須由自己清除堆棧?!纠?-5】調(diào)用PRINTF函數(shù)。CHAR SETGS[]="%S%"CHAR SETSJ[]="THISISAGOODDAY!";VOID MAIN(){_ASM{MOV AX,OFFSETSETSJPUSH AXMOV AX,OFFSETSETWHPUSH AXMOV AX,OFFSETSETGSPUSH AXCALL PRINTF ;調(diào)用PRINTF函數(shù)POP BX ;清除堆棧POP BXPOP BX}}7.3

用C/C++調(diào)用匯編7.3.1

接口 7.3.2

調(diào)用匯編模塊 7.3.1

接口采用模塊調(diào)用方式進行混合編程一般執(zhí)行的步驟,首先建立C++源程序(.CPP)和匯編源程序并把匯編程序編譯成.OBJ文件;然后將編譯后的匯編文件和C++源程序放入建立好的C++工程項目(.PRJ)中;最后對工程文件進行編譯、連接,生成可執(zhí)行文件。TC調(diào)用匯編語言函數(shù)時,對匯編語言的編寫要求十分嚴(yán)格,并且對大小寫字母也有嚴(yán)格的區(qū)分。如果在編寫供TC調(diào)用的匯編函數(shù)時不按照規(guī)定好了的格式書寫,調(diào)用是不會成功的。7.3.1

接口為了能正確地實現(xiàn)C/C++程序?qū)R編語言程序的調(diào)用,C/C++程序必須嚴(yán)格按照編譯系統(tǒng)要求約定的段順序和規(guī)定的段組合,據(jù)此,形成了匯編程序的一般格式。如下格式:_TEXT SEGMENTBYTEPUBLIC’CODE’DGROUP GROUP_DATA,_NEWS,CONST_DATA SEGMENTWORDPUBLIC’CODE’;初始化數(shù)據(jù)_DATA ENDS_NEWS SEBMENTWORDPUBLIC’NEWS’_NEWS ENDSASSUMECS: _TEXT,DS:DGROUP,SS:DGROUP;PUBLIC_函數(shù)名7.3.1

接口_函數(shù)名 PROCNEAR/FARPUSH BPMOV BP,SPPUSH SIPUSH DI……程序主體語句POP DIPOP SIPOP BPRET_函數(shù)名 ENDP_TEXT ENDSEND7.3.1

接口而在匯編作為主程序來調(diào)用C/C++子程序的格式上基本與上面的格式一致,只需改動如下:1)程序開始處加入語句:EXTERN_函數(shù)名:NEAR/FAR,說明這個函數(shù)是外部的,即將被調(diào)用的C/C++子程序。2)省去主過程語句中關(guān)于BP,SI,DI的堆棧操作語言。3)在主過程語句中通過CALL語句實現(xiàn)對外部函數(shù)的調(diào)用。格式如下:CALLNEARPTR_函數(shù)名7.3.1

接口雖然C/C++程序可以實現(xiàn)許多匯編語言的功能,但還是有不少特性只有匯編語言才能做: (1)執(zhí)行PUSH和POP操作。 (2)訪問BP與SP寄存器。 (3)初始化某些段寄存器。 (4)執(zhí)行時間要求嚴(yán)格的例行程序,例如顯示視頻圖形和實現(xiàn)通過端口的I/O。以上代碼模板可以用于C/C++函數(shù)使用的所有匯編語言函數(shù)。當(dāng)然,如果特定的函數(shù)不改變BX、SI或者DI寄存器,可以省略相關(guān)的PUSH和POP指令。7.3.2

調(diào)用匯編模塊C/C++程序提供了與匯編語言的接口和在C/C++程序中直接插入?yún)R編指令代碼的功能,支持以“遠調(diào)用”和“近調(diào)用”方式來調(diào)用使用匯編語言編寫的函數(shù)。實現(xiàn)二者的鏈接,要解決好以下幾個問題:1.采用一致的調(diào)用協(xié)議C/C++程序具有三種調(diào)用協(xié)議:_CDECL、_STDCALL和_FASTCALL。匯編語言利用“語言類型”確定調(diào)用協(xié)議和命名約定,支持的語言類型有:C、SYSCALL、STDCALL、PASCAL、BASIC和FORTRAN。C/C++語言調(diào)用協(xié)議從右到左壓入?yún)?shù),像它們在參數(shù)表里的位置那樣。高級語言的參數(shù)通過堆棧傳遞給匯編語言過程,匯編語言過程將返回結(jié)果放在AX或DX:AX中。在BP、DI、SI、DS、SS和方向標(biāo)志位被改動之前應(yīng)使用匯編語言過程保存起來。這些寄存器是高級語言可能用到的。圖7-1給出了C/C++調(diào)用協(xié)議下近調(diào)用和遠調(diào)用的堆棧幀。7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊對堆棧幀內(nèi)參數(shù)的訪問由C/C++語言調(diào)用協(xié)議提供,如下所示:.MODEL SMALL,CCLASS PROTOC,A:SWORD,B:SWORD

;采用C/C++調(diào)用協(xié)議

;并指出外部變量及其類型.CODECLASS PROCC,A:SWORD,B:SWORD

;SWORD對應(yīng)C/C++的SHORTIN,,保存BMOV AX,A

;使用參數(shù)AADD AX,B

;使用參數(shù)進行計算C/C++程序與匯編語言混合編程通常利用堆棧進行參數(shù)傳遞,調(diào)用協(xié)議決定利用堆棧的方法和命名約定,兩者要一致。C/C++有兩種常用的函數(shù)調(diào)用約定:_STDCALL和_CDECL,詳見表7-1。7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊可以看出,_STDCALL和_CDECL的一個重要區(qū)別在于堆棧參數(shù)的維護,是調(diào)用函數(shù)還是被調(diào)用函數(shù)負(fù)責(zé)清理堆棧_STDCALL約定被調(diào)用函數(shù)清除堆棧參數(shù),這樣做的好處是可以減少源代碼的大小,因為每次函數(shù)調(diào)用完成后,調(diào)用函數(shù)不再需要清理堆棧的指令;但是有些函數(shù)必須使用_CDECL調(diào)用約定,這種情況是參數(shù)個數(shù)未知的函數(shù),這時只能由調(diào)用函數(shù)清除堆棧參數(shù),例如庫函數(shù)PRINTF。7.3.2

調(diào)用匯編模塊_STDCALL調(diào)用約定代碼片段;調(diào)用函數(shù)…PUSH PARAM3PUSH PARAM2PUSH PARAM1CALL SUBPROC…;被調(diào)用函數(shù)PUSH BPMOV BP,SP…MOV SP,BPPOP BPRET 12(清除堆棧)_CDECL調(diào)用約定代碼片段;調(diào)用函數(shù)…PUSH PARAM3PUSH PARAM2PUSH PARAM1CALL SUBPROCADD SP,12(清除堆棧)…;被調(diào)用函數(shù)PUSH BPMOV BP,SP…MOV SP,BPPOP BPRET7.3.2

調(diào)用匯編模塊2.命名約定C/C++程序可以調(diào)用匯編語言的子程序、過程、函數(shù)和匯編語言中定義的變量,匯編語言也可以調(diào)用C/C++編的函數(shù)和定義的變量,但要注意的是,由于C編譯后的目標(biāo)文件自動地在函數(shù)名和變量名前一個下劃線,這是因為編譯系統(tǒng)為了防止和它自己使用的內(nèi)部函數(shù)和變量名發(fā)生混淆而造成錯誤,所以C匯編模塊必須使用和C/C++兼容的有關(guān)段與變量的命名約定。在C/C++程序中的所有外部名字都包含個前導(dǎo)的下劃線字符,如COLUMN。匯編程序引用在C/C++模塊中的函數(shù)與變量也必須由下劃線“_”開始。而且,由于C/C++是對大小寫字母敏感的,所以匯編模塊對于任何公共的變量名應(yīng)當(dāng)使用和C/C++模塊同樣的字母(大寫或小寫)。7.3.2

調(diào)用匯編模塊3.聲明公用函數(shù)名和變量名對C/C++程序和匯編語言使用的公用函數(shù)和變量應(yīng)該進行聲明,并且標(biāo)識符應(yīng)該一致,C++語言對標(biāo)識符區(qū)分字母的大小寫,而匯編不區(qū)分大小寫。在匯編語言程序的開始部分,應(yīng)對調(diào)用的函數(shù)和變量用EXTERN加以說明,其格式為:{EXTERN_函數(shù)名:函數(shù)類型}或{EXTERN_變量名:變量類型}其中函數(shù)類型指明函數(shù)是一個近程函數(shù)或是一個遠程函數(shù)(即處在另一個段中),分別表示為NEAR型或FAR型,而變量類型指該變量的數(shù)據(jù)類型,其對應(yīng)的關(guān)系如表7-2。7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊如調(diào)用C/C++程序中MFPRO()的函數(shù)和變量SIGN,它們在C/C++程序開始說明部分為:INT MFPRO(VOID);INT SIGN,JARRAY[10];CHAR CH;LONG RESULT;在調(diào)用它的匯編程序中則在程序開始說明為:EXTERN_MFPRO:NEAREXTEMSIGN:WORD,_JARRAY:WORD,CH:BYTE,RESULT:DWORD若C/C++程序調(diào)用匯編語言中的過程(函數(shù))和變量,則匯編語言中應(yīng)用PUBLIC進行說明,且函數(shù)名和變量名前應(yīng)帶有下劃線,即函數(shù)名和變量名的第一個符號應(yīng)是下劃線。7.3.2

調(diào)用匯編模塊4.參數(shù)的傳遞原則C/C++程序調(diào)用匯編程序時,參數(shù)是通過堆棧傳給匯編程序的,如在C/C++程序中說明了一個函數(shù)(用匯編程序?qū)懗桑篤OIDABC(CHAR*P1,INTP2);假設(shè)是在小內(nèi)存模式下進行編譯的,當(dāng)在C/C++程序中調(diào)用它時,如寫成ABC(&CH,NUM);則首先將NUM壓入堆棧,接著將&CH壓入堆棧,當(dāng)匯編語言子程序要取得C/C++程序中傳遞來的參數(shù)時,便用BP寄存器作為基地址寄存器,用它加上不同的偏移量來對棧中所存數(shù)據(jù)進行存取操作,由于一般C/C++程序和調(diào)用的子程序共用一個堆棧,因此在匯編語言子程序中開始必須執(zhí)行兩條指令,即:

PUSH

BP

POP

BP,SP7.3.2

調(diào)用匯編模塊傳送參數(shù)有3種方法:有很多把輸入值傳遞給匯編函數(shù)的方法,以及很多獲得輸出結(jié)果的方法。C/C++程序使用特定格式把輸入值傳遞到程序堆棧中,并且從AX寄存器獲得結(jié)果。如果希望匯編語言函數(shù)和C/C++程序一起工作,就必須顯式地遵守C樣式的函數(shù)格式。這就是說所有輸入變量都必須從堆棧中讀取,并且大多數(shù)輸出值都返回到AX寄存器中。1)用值:C/C++調(diào)用程序傳送變量的一個副本在堆棧中。被調(diào)用的匯編模塊可以修改傳送的值,但不能訪問調(diào)用程序原來的值。如果有一個以上的參數(shù),C/C++從最右邊的參數(shù)開始使它們進棧。7.3.2

調(diào)用匯編模塊2)用近引用:調(diào)用程序傳送數(shù)據(jù)項值的偏移地址。被調(diào)用的匯編模塊假設(shè)和調(diào)用程序共享同一個數(shù)據(jù)段。3)用遠引用:調(diào)用程序傳送段與偏移地址(段在前,然后是偏移地址)。被調(diào)用的匯編模塊假設(shè)使用與調(diào)用程序不同的數(shù)據(jù)段。被調(diào)用的程序可以使用LDS或LES指令來初始化段地址。C/C++程序傳送參數(shù)到堆棧中是以和其他語言相反的順序進行的。例如:ADDS(NUM1,NUM2);該語句先使NUM2,后使NUM1進棧,就按次序并調(diào)用ADDS。在從被調(diào)用模塊返回時,C/C++模塊(不是匯編模塊)使SP增量去丟棄傳送的參數(shù)。在被調(diào)用的匯編模塊中,用于訪問2個傳送參數(shù)的典型過程如下:7.3.2

調(diào)用匯編模塊PUSH BP ;保存BPMOV BP,SP ;用SP地址作為基址指針MOV DH,[BP+4] ;從堆棧中取值MOV DL,[BP+6]…POP BP ;恢復(fù)BPRET在PUSHBP指令之后,堆棧幀表現(xiàn)為:7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊在從被調(diào)用的模塊返回時,由于由C/C++用程序承擔(dān)清除堆棧的責(zé)任,所以發(fā)出的RET不帶立即操作數(shù)。5.C語言子程序中寄存器的使用由于匯編語言中要用到80x86CPU中的許多寄存器,而調(diào)用它的C/C++程序中也要用到一些寄存器,因而這些寄存器的作用和保護則應(yīng)引起注意,否則將導(dǎo)致程序失敗。匯編語言子程序中要用到BP、SI和DI寄存器,在C/C++程序中BP是作為參數(shù)和自動變量的基地址,而在匯編語言子程序中,它也是作為取參數(shù)的基地址,SI和DI寄存器在C/C++中用作存放寄存器變量的,故在進入?yún)R編子程序時必須通過壓棧操作將其值進行保護,匯編子程序結(jié)束時必通過彈出操作,恢復(fù)其原來在C/C++程序中的值。BX和CX寄存器可以在匯編語言子程序中任意使用,AX和DX用作存放匯編子程序的返回值,可在匯編語言子程序中使用,但若有返回值時,則要保證在子程序返回時,它們存有要返回的值。7.3.2

調(diào)用匯編模塊對于CS,SS,DS,SP,IP由于用作段寄存器和堆棧指針,在程序中不能隨意使用,根據(jù)編譯的內(nèi)存模式或FAR,NEAR調(diào)用,它們可能和C/C++程序在同一個段內(nèi),用同一個段寄存器或不同的段內(nèi)。因而可根據(jù)不同情況,有時要改變它們中的值,以得到正確段地址,但它們不能用于其他用處。如果輸入值和輸出變量被賦值給寄存器,那么在嵌入?yún)R編代碼中幾乎可以像平常一樣使用寄存器。匯編語言的一些優(yōu)勢是C/C++所無法比擬的,例如匯編程序在輸入時可以采取各種進制數(shù)據(jù)以及直接讀取數(shù)據(jù)等,可以用匯編程序作為主程序調(diào)用C/C++子程序。7.3.2

調(diào)用匯編模塊在C/C++程序中定義的變量,在匯編程序里訪問很簡單,首先在C/C++程序中定義為全局變量,然后在匯編程序中用.GLOBAL定義(變量名前同樣需加_),再利用間接尋址的方式即可實現(xiàn),在C/C++程序中定義的數(shù)組,在匯編程序里要方便地訪問,可把數(shù)組的首地址賦給輔助寄存器,利用輔助寄存器即可實現(xiàn)數(shù)組的有效訪問。圖7-2顯示把輸入值存放到堆棧中的方式,以及匯編語言函數(shù)如何訪問它們。BP寄存器用作訪問堆棧中的值的基址指針。調(diào)用匯編語言函數(shù)的程序必須知道輸入值按照什么順序存放在堆棧中,以及每個輸入值的長度和數(shù)據(jù)類型。7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊在匯編函數(shù)代碼中,C樣式函數(shù)對于可以修改哪些寄存器和函數(shù)必須保留哪些寄存器有著特定的規(guī)則。如果必須保留的寄存器在函數(shù)中被修改了,那么必須恢復(fù)寄存器的原始值,否則在執(zhí)行返回發(fā)出調(diào)用的C/C++程序時也許會出現(xiàn)不可預(yù)料的結(jié)果。如表7-3所示,被調(diào)用的函數(shù)必須保留BX,DI、SI、BP和SP寄存器,這就要求在執(zhí)行函數(shù)代碼之前把寄存器的值壓入堆棧,并且在函數(shù)準(zhǔn)備返回調(diào)用程序時把它們彈出堆棧。這通常是按照標(biāo)準(zhǔn)的開頭和結(jié)尾格式完成的。7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊6.入口參數(shù)和返回參數(shù)的約定當(dāng)被調(diào)用匯編語言子程序有值返回調(diào)用它的C/C++程序時,這個值是通過AX和DX寄存器進行傳遞的,對于小于等于32位的數(shù)據(jù)擴展為32位,存放在AX寄存器中返回;返回值是32位,則高16位存放在DX寄存器中,低16位存放在AX寄存器中。更大字節(jié)數(shù)據(jù)則將它們的地址指針存放在AX中返回。被調(diào)用的匯編模塊對于任何返回值使用以下寄存器,如表7-4所示:7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊由于任何類型進行參數(shù)傳遞時都擴展成32位,程序中沒有遠、近調(diào)用之分,所有調(diào)用都是32位的偏移地址,所有的地址參數(shù)也都是32位偏移地址,在堆棧中占4個字節(jié)。圖7-3給出了采用C++語言調(diào)用協(xié)議的堆棧示意圖。7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊調(diào)用匯編語言函數(shù)的C或者C++程序必須知道用于把輸入值傳遞給匯編函數(shù)的正確格式,以及如何處理任何返回值。(1)使用整數(shù)返回值最基本的匯編語言函數(shù)調(diào)用把32位整數(shù)值返回到AX寄存器中。調(diào)用函數(shù)獲得這個值,它必須把返回值作為整數(shù)賦值給C變量;C/C++程序生成的匯編語言代碼提取存放在AX寄存器中的值,并且把它傳送到分配給C變量名稱的內(nèi)存位置(通常是堆棧中的局部變量)。這個C變量包含來自匯編語言函數(shù)的返回值,可以在整個C/C++程序中正常的使用。下面是匯編語言函數(shù)和使用它的C/C++程序的例子。首先,匯編語言程序SQUARE.S定義一個函數(shù),它需要一個整數(shù)輸入值,求它的平方,然后把結(jié)果返回到AX寄存器中。7.3.2

調(diào)用匯編模塊(2)使用字符串返回值處理返回字符串值的函數(shù)要困難一些。和返回整數(shù)值到AX寄存器中的函數(shù)不同,這種函數(shù)不能把整個數(shù)據(jù)字符串返回到AX寄存器中(當(dāng)然,除非字符串的長度是4個字符)。對于字符串返回值的作法是,返回字符串的函數(shù)返回指向字符串存儲位置的指針。調(diào)用這個函數(shù)的C或者C++程序必須使用指針變量保存返回值。然后可以通過指針值訪問字符串。如圖7-4所示。字符串值被包含在函數(shù)的內(nèi)存空間之內(nèi),但是主程序可以訪問它,因為函數(shù)內(nèi)存空間包含在主程序的內(nèi)存空間之內(nèi)。函數(shù)返回的32位指針值是字符串開始位置所在的內(nèi)存地址。在C/C++程序中處理字符串值時,記住字符串必須使用空字符結(jié)尾。C和C++語言在變量名稱前面使用“*”表明這個變量包含一個指針,可以創(chuàng)建指向任何數(shù)據(jù)類型的指針。但是對于字符串,想要創(chuàng)建指向數(shù)據(jù)類型CHAR的指針。7.3.2

調(diào)用匯編模塊7.3.2

調(diào)用匯編模塊但是,默認(rèn)情況下,C/C++程序假設(shè)函數(shù)的返回值是整數(shù)值。必須通知編譯器這個函數(shù)將返回字符串的指針。創(chuàng)建函數(shù)調(diào)用的原型將完成這個任務(wù)。原型在使用函數(shù)

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論