版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
任向民王克朝王喜德冯阿芳编著高级汇编语言程序设计实用教程(第2版)清华大学出版社高等院校信息技术规划教材第7章
汇编语言与高级语言接口7.1混合编程7.2C/C++的嵌入式汇编
7.2.1在C/C++程序中嵌入汇编语句
7.2.2在嵌入式汇编中访问C/C++的数据
7.2.3汇编语言程序段编写C函数
7.2.4汇编程序调用C/C++函数7.3用C/C++调用汇编
7.3.1接口
7.3.2调用汇编模块 7.1
混合编程
一般说来高级语言具有丰富的数据结构、种类繁多的运算符、丰富的函数、易读易写、可移植性好等特点,但用高级语言编写的程序,代码较长,占有存储空间大,运行速度慢。
而用汇编语言编写的程序所占内存空间小,执行速度快,有直接控制硬件的能力,但程序繁琐,难读也难编写,且必须熟悉计算机的内部结构及其有关硬件知识。7.1
混合编程混合编程即由高级语言来调用或嵌入汇编语言子程序,或用汇编语言调用或嵌入高级语言子程序。汇编程序常以过程的形式同高级语言(如C/C++、Basic、Pascal、Delphi等)一起使用。在与高级语言接口时,汇编器使用两种调用协议用于C/C++语言的C/C++调用协议和用于Basic、Pascal和Fortran语言的Pascal语言调用协议。调用协议语言在MODEL语句中或与PROC语句相联系的OPTION指示符中指定。除了用这些语句以外还可以用完全段定义指定。7.1
混合编程高级语言和汇编语言连接很容易,因为在高级语言编译后生成的编译程序是一个.OBJ的文件,这与汇编程序输出的目标文件一样都是机器语言程序。那么我们就可以利用link将高级语言程序产生的.OBJ程序与汇编程序产生的.OBJ程序连接起来,形成一个.EXE的可执行文件。7.1
混合编程高级语言与汇编语言的连接应注意下面几个问题:1.两种语言之间的控制传输问题一般来说汇编语言程序作为高级语言的外部子程序,由高级语言通过函数或者过程进行调用汇编语言程序。2.参数的传递通常高级语言程序使用系统堆栈向汇编语言传递入口参数,汇编语言程序返回时使用CPU内部寄存器带回计算结果。此外还需要确定哪些寄存器是需要保留下来的,哪些是可以使用的。7.1
混合编程3.存储分配问题高级语言不需要考虑存储分配问题,编译程序和连接程序会自动的进行存储分配。当汇编语言与高级语言程序连接时,就需要考虑这个问题了。这个问题处理起来不是很复杂,一般是将汇编语言作为一个程序模块,由连接程序决定其在存储器中的位置。不同的高级语言与汇编语言的混合编程所采用的方法是不相同的。本章当中主要介绍C/C++与汇编的混合编程问题,在下面的几节当中,我们将分别介绍C/C++的嵌入式汇编、C/C++调用汇编的具体方法。7.2
C/C++的嵌入式汇编
7.2.1在C/C++程序中嵌入汇编语句
7.2.2在嵌入式汇编中访问C/C++的数据
7.2.3汇编语言程序段编写C函数
7.2.4汇编程序调用C/C++函数7.2
C/C++的嵌入式汇编利用汇编语言程序设计的一种非常常见的方式是在高级语言(例如C/C++)程序内编写汇编函数。完成这一工作有几种不同的方式把汇编语言函数直接放到C/C++语言程序内。这种技术称为嵌入式汇编或内联汇编(InlineAssembly)。7.2
C/C++的嵌入式汇编在C/C++与汇编语言的混合编程过程中,C/C++调用汇编代码常有两种方法:(一)直接在C/C++程序中嵌入汇编语句 (二)C/C++调用汇编语言子程序把汇编语言程序加入到C/C++程序中,必须使汇编程序和C/C++程序一样具有明确的边界、参数、返回值和局部变量,必须为汇编语言编写的程序段指定段名并进行定义,如果要在它们之间传递参数,则必须保证汇编程序用来传递参数的存储区和C/C++函数使用的存储区是一样的。7.2
C/C++的嵌入式汇编在C/C++程序中采用“_ASM”关键字输入汇编语言指令语句或语句段。在C或者C++中进行嵌入式汇编需要注意以下要点:(1)嵌入式汇编语言代码支持INTEL80x86CPU的全部32位指令系统,但是不能使用伪指令与宏指令语句,也不能使用结构(STRUCT)和记录(RECORD)(2)嵌入式汇编语言可以使用C++程序中标识符,包括标号、变量、函数名、常量、宏、类型名、结构和联合的成员以及类对象的公有(PUBLIC)成员变量等;7.2
C/C++的嵌入式汇编(3)嵌入式汇编语言代码中可以使用汇编语言格式的常数(131AH),也可以使用C++格式的常数(0X131A)(4)嵌入式汇编语言不能使用C++语言的运算符(5)嵌入式汇编语言代码中的转移指令和C++中的GOTO语句都能跳转到汇编语言或者C++定义的标号(6)嵌入式汇编语言定义的函数返回值的传递方法与预模块调用汇编中汇编语言程序返回值的传递方法相同,在C++程序编译时会产生“NORETURNVALUE”警告,可以使用#PRAGMAWARNING(DISABLE:4035)预编译语句禁止该警告7.2.1
在C/C++程序中嵌入汇编语句嵌入汇编语言指令采用_ASM关键字,嵌入汇编格式,具体应用通常采用两种方式。第一种方式,嵌入汇编语言指令是在汇编语句前加一个_ASM关键字,格式如下:_ASM操作码操作数<;或换行>其中,操作码是处理器指令或若干伪指令;操作数是操作码可接受的数据。内嵌的汇编语句可以用分号“;”结束,也可以用换行符结束;一行中可以有多个汇编语句,相互间用分号分隔,但不能跨行书写。嵌入汇编语句的分号不是注释的开始;要对语句注释,应使用C的注释,如/*…*/。例如:_ASMMOVAX,DS;/*AX←DS*/_ASMPOPAX;ASMPOPDS;ASMRET;/*合法语句*/_ASMPUSHDS/*ASM语句是C/C++程序中惟一可以用换行结尾的语句*/7.2.1
在C/C++程序中嵌入汇编语句在C/C++程序的函数内部,每条汇编语言语句都是一条可执行语句,它被编译进程序的代码段。在函数外部,一条汇编语句是一个外部说明,它在编译时被放在程序的数据段中;这些外部数据可被其他程序引用。例如:_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++程序中嵌入汇编语句下面这个小程序就是一个实际的调用汇编的例子:#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++程序中嵌入汇编语句在这个程序当中,_ASMNOP不执行任何任务,是一个空操作。基本的嵌入汇编代码可以利用应用程序中定义的全局C变量。这里要注意的是只有全局定义的变量才能在基本的内联汇编代码内使用。通过C/C++程序中使用的相同名称引用这种变量。第二种方式,_ASM{汇编程序段}采用花括号的汇编语言程序段形式。_ASM{汇编程序段}如下所示:_ASM{MOV AX,15HMOV CX,9HADD AX,CX}7.2.1
在C/C++程序中嵌入汇编语句包含在括号中的汇编代码必须按照特定的格式:(1)指令必须括在引号里。(2)如果包含的指令超过一条,那么必须使用新行字符分隔汇编语言代码的每一行。通常,还包含制表符帮助缩进汇编语言代码,使代码行更容易阅读。(规则2)是因为编译器逐字地取得ASM段中的汇编代码,并且把它们放在为程序生成的汇编代码中。7.2.1
在C/C++程序中嵌入汇编语句下面通过几个例子来具体的了解下嵌入式汇编的过程。【例7-1】显示1到1000中任一个数的二进制到十六进制数。#include<iostream.h>CHAR*BUFFER="EnterANumberBetween0To1000:";CHAR*BUFFER1="BASE";INTB=0;CHARA;/*显示字符的汇编程序段*/7.2.1
在C/C++程序中嵌入汇编语句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++程序中嵌入汇编语句}}/*C++的主函数段*/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); /*调用汇编函数,进行显示*/COUT<<(CHAR)(0X20);DISPS(I,B); /*调用汇编函数,进行显示*/COUT<<(CHAR)(10);COUT<<(CHAR)(13);}}7.2.1
在C/C++程序中嵌入汇编语句在MicrosoftVC++6.0环境下编写汇编与C/C++混合程序时,只能编写32位应用程序,而不能编写16位应用程序,在32位应用程序的混合编程中应注意不能使用DOS功能调用INT21H,用它只能编写16位应用程序。由于内嵌汇编不能使用汇编的宏和条件控制伪指令,这时就需要进行单独编写汇编模块,然后和C/C++程序连接,这种编程关键要解决的问题是二者的接口和参数传递,参数传递包括值传递、指针传递等。7.2.1
在C/C++程序中嵌入汇编语句在使用嵌入式汇编中要注意的几个问题:(1)操作码支持8086/8087指令或若干伪指令:db/dw/dd和extern(2)操作数是操作码可接受的数据:立即数、寄存器名,还可以是C/C++程序中的常量、变量和标号等(3)内嵌的汇编语句可以用分号“;”结束,也可以用换行符结束(4)使用C的注释,如/*…*/(5)正确运用通用寄存器、标号等7.2.2
在嵌入式汇编中访问C/C++的数据上面讲了如何在C++中使用汇编语言。反之也可以在汇编代码段中使用设置C++的变量及其他元素。内嵌的汇编语句除可以使用指令允许的立即数、寄存器外,还可以使用C/C++程序中的任何符号(标识符),包括变量、常量、标号、函数名、寄存器变量、函数参数等;C编译程序自动将它们转换成相应汇编语言指令的操作数,并在标识符名前加下划线。一般来说,只要汇编语句能够使用存储器操作数(地址操作数),就可以采用一个C/C++程序中的符号;同样,只要汇编语句可以用寄存器作为合法的操作数,就可以使用一个寄存器变量。7.2.2
在嵌入式汇编中访问C/C++的数据对于具有内嵌汇编语句的C/C++程序,C编译器要调用汇编程序进行汇编。汇编程序在分析一条嵌入式汇编指令的操作数时,若遇到了一个标识符,它将在C/C++程序的符号表中搜索该标识符;但8086寄存器名不在搜索范围之内,而且大小写形式的寄存器名都可以使用。7.2.2
在嵌入式汇编中访问C/C++的数据【例7-2】用嵌入汇编方式实现取两数较小值的函数MININTMIN(INTVAR1,INTVAR2) /*用嵌入汇编语句实现的求较小值*/{ASMMOV AX,VAR1ASMCMP AX,VAR2ASMJLE MINEXITASMMOV AX,VAR2MINEXIT: RETURN(_AX) /*将寄存器AX的内容作为函数的返回值*/}MAIN() /*C/C++主程序*/{MIN(100,200);}7.2.2
在嵌入式汇编中访问C/C++的数据在_ASM模块中,可以使C++或ASM的基数计数法,例如0x100和100H是相等的。_ASM块中不能使用“<<”之类的C++操作符。C++和MASM通用的操作符,例如“*”和“[]”则被认为是汇编语言的操作符,方括号[]在C中表示数组下标,而在内嵌汇编中认为是索引操作符,表示字节偏移量。如:_ASMMOVARRAY[6],BX;STOREBXATARRAY+6(NOTSCALED)当然也可以使用“TYPE”来使其与C++风格一致。例如,下面两条语句的作用是一样的:ASMMOV ARRAY[6*TYPEINT],0;ARRAY[6]=0;7.2.2
在嵌入式汇编中访问C/C++的数据嵌入汇编也能通过变量名直接引用C++的变量。如果C++中的类、结构或者枚举成员具有惟一的名称,如果在“.”操作符之前不指定变量或者TYPEDEF名称,则_ASM块中只能引用成员名称。如果成员不是惟一的,则必须在“.”之前加上变量名或TYPEDEF名称。STRUCT NAM1{CHAR*FTH;INT NAME12;};STRUCT NAM2{INT WIN;LONG NAME12;};7.2.2
在嵌入式汇编中访问C/C++的数据如果按下面声明变量:STRUCTNAM1HAL;STRUCTNAM2OAT;那么,所有引用NAME12成员的地方都必须使用变量名,因为NAME12不是惟一的。而FTH变量却具有惟一的名称,可以简单地用它的成员名称来引用。如:_ASM{MOV BX,OFFSETHALMOV CX,[BX]HAL.NAME12MOV SI,[BX].FTH ;省略“HAL”}7.2.3
汇编语言程序段编写C函数在调用函数之前应编程将参数以逆序写入到当前运行任务所使用的任务堆栈中,压栈之前堆栈指针可不作调整。被调用的C函数即可正常访问调用者传递的参数,函数调用完毕后需要调整堆栈指针,清除函数调用中参数所占用的堆栈空间。C函数的返回值可以通过访问累加器获得。为了能够编写出可供C/C++调用的函数。应了解C/C++模块与汇编模块的接口机制,而从以汇编形式给出的编译结果中可方便地了解这种机制。7.2.3
汇编语言程序段编写C函数C/C++程序中含有嵌入式汇编语言语句时,C编译器首先将C代码的源程序(.c)编译成汇编语言源文件(.asm),然后激活汇编程序将产生的汇编语言源文件编译成目标文件(.obj),最后激活link将目标文件链接成可执行文件(.exe)。【例7-3】将字符串中的小写字母转变为大写字母显示。7.2.3
汇编语言程序段编写C函数/*程序名:SJW7-03.C*/#INCLUDEVOID UPPER(CHAR*DEST,CHAR*SRC){ASMMOV SI,SRC /*DEST和SRC是地址指针*/ASMMOV DI,DESTASMCLDLOOP:ASMLODSB /*C/C++定义的标号*/ASMCMP AL,'A'ASMJB COPY /*转移到C的标号*/ASMCMPAL,'Z'ASMJA COPY /*不是’A’到’Z’之间的字符原样复制*/ASMSUB AL,20H /*是小写字母转换成大写字母*/COPY:ASMSTOSBASMAND AL,AL /*C/C++中,字符串用NULL(0)结尾*/ASMJNZ LOOP}MAIN()/*主程序*/{CHARSTR[]="THISSTARTEDOUTASLOWERCASE!";CHARCHR[100];UPPER(CHR,STR); /*调用汇编的UPPER函数*/PRINTF("ORIGINSTRING:\N%S\N",STR);PRINTF("UPPERCASESTRING:\N%S\N",CHR);}7.2.3
汇编语言程序段编写C函数编辑完成后,在命令行输入如下编译命令,选项-I和-L分别指定头文件和库函数的所在目录。TCC-B–Iinclude–Llibsjw7-03.c嵌入汇编方式把插入的汇编语言语句作为C/C++的组成部分,不使用完全独立的汇编模块,所以比调用汇编子程序更方便、快捷,并且在大存储模式、小存储模式下都能正常编译通过。如下C/C++程序:7.2.3
汇编语言程序段编写C函数 /*程序名:SJW7-04.C*/INT SUM(INT,INT,INT)} /*说明函数SUM的调用格式*/INT SJW01=5; /*已初始化的变量*/INT SJW02; /*未初始化的变量*/MAIN() /*主函数*/{SJW02=SUM(1,SJW01,3);PRINTF("%D\N",SJW02);}INT SUM(INTI,INTJ,INTM)RETURN(I+J+M);7.2.3
汇编语言程序段编写C函数 用下面的命令要求C/C++按SMALL,模式编译SJW7-0.C。并以汇编格式输出编译结果:TCC-MS-SSJW7-0.C以汇编格式输出的编译结果保存在文件SJW7-04.ASM中,尽管该文件比较冗长。但它对理解编写供C调用的汇编函数是有帮助的。而且对学习C/C++也是有益的。SJW7-04.ASM的主要内容如下所示:7.2.3
汇编语言程序段编写C函数 _TEXTSEGMENTBYTEPUBLIC'CODE' ;代码段DGROUPGROUPDATA,BSSASSUMECS:_TEXT,DS:DGROUP,SS:DGROUP_TEXTENDS_DATASEGMENTGWORDPUBLIC’DATA’;已初始化的数据段_SJW01 LABELWORD_DATAENDSPUSH AX ;为调用SUM压入第三个参数PUSH WORDPTRDGROUP:_SJW01MOV AX,1PUSH AX ;压入第一个参数CALL NEARPTR_SUM ;调用SUM7.2.3
汇编语言程序段编写C函数ADD SP,6 ;废除压入堆栈的三个参数MOV WORDPTRDGROUP:SJW02,AXPUSH WORDPTRDGROUP:_SJW02MOV AX,OFFSETDGROUP:SJ03PUSH AX ;压入第一个叁数CALL NEARPTR_PRINTF;POP CX ;废除压入堆栈的两十参数POP CXSJ01:RET_MAIN ENDP_SUM PROCNEARPUSH BP7.2.3
汇编语言程序段编写C函数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函数DATASEGMENTWORDPUBLIC‘DATA’SJ03:LABELBYTEDB 47DB 100DB 12DB 0.DATA ENDSTEXT SEGMENTBYTEPUBLIC‘CODE’EXTRN_PRINTF: NEARTEXT ENDSPUBLIC _SJW02PUBLIC _SJW01PUBLIC _MAINPUBLIC _SUMEND7.2.3
汇编语言程序段编写C函数从上面以汇编格式给出的编译结果中,可以看到函数SUM除包含一条多余的跳转指令外,已足够精练。但为了方便地说明如何缩写供C/C++调用的函数,在这里仍然把上述C函数SUM改写成汇编函数。相应地,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
汇编程序调用C/C++函数汇编程序中调用C函数相对比较简单,编译器已经提供了相当完善的支持。汇编语句中的CALL语句,可以用于调用其他过程,既可以是其他汇编程序段也可以是C/C++程序中的标准过程。【例7-4】输出“HELLOWORLD”。7.2.4
汇编程序调用C/C++函数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
汇编程序调用C/C++函数在嵌入汇编中可以无限制地访问C++成员变量,但是却不能随意调用C++的成员函数。嵌入汇编调用C++函数必须由自己清除堆栈。【例7-5】调用PRINTF函数。CHAR SETGS[]="%S%"CHAR SETSJ[]="THISISAGOODDAY!";VOID MAIN(){_ASM{MOV AX,OFFSETSETSJPUSH AXMOV AX,OFFSETSETWHPUSH AXMOV AX,OFFSETSETGSPUSH AXCALL PRINTF ;调用PRINTF函数POP BX ;清除堆栈POP BXPOP BX}}7.3
用C/C++调用汇编7.3.1
接口 7.3.2
调用汇编模块 7.3.1
接口采用模块调用方式进行混合编程一般执行的步骤,首先建立C++源程序(.CPP)和汇编源程序并把汇编程序编译成.OBJ文件;然后将编译后的汇编文件和C++源程序放入建立好的C++工程项目(.PRJ)中;最后对工程文件进行编译、连接,生成可执行文件。TC调用汇编语言函数时,对汇编语言的编写要求十分严格,并且对大小写字母也有严格的区分。如果在编写供TC调用的汇编函数时不按照规定好了的格式书写,调用是不会成功的。7.3.1
接口为了能正确地实现C/C++程序对汇编语言程序的调用,C/C++程序必须严格按照编译系统要求约定的段顺序和规定的段组合,据此,形成了汇编程序的一般格式。如下格式:_TEXT SEGMENTBYTEPUBLIC’CODE’DGROUP GROUP_DATA,_NEWS,CONST_DATA SEGMENTWORDPUBLIC’CODE’;初始化数据_DATA ENDS_NEWS SEBMENTWORDPUBLIC’NEWS’_NEWS ENDSASSUMECS: _TEXT,DS:DGROUP,SS:DGROUP;PUBLIC_函数名7.3.1
接口_函数名 PROCNEAR/FARPUSH BPMOV BP,SPPUSH SIPUSH DI……程序主体语句POP DIPOP SIPOP BPRET_函数名 ENDP_TEXT ENDSEND7.3.1
接口而在汇编作为主程序来调用C/C++子程序的格式上基本与上面的格式一致,只需改动如下:1)程序开始处加入语句:EXTERN_函数名:NEAR/FAR,说明这个函数是外部的,即将被调用的C/C++子程序。2)省去主过程语句中关于BP,SI,DI的堆栈操作语言。3)在主过程语句中通过CALL语句实现对外部函数的调用。格式如下:CALLNEARPTR_函数名7.3.1
接口虽然C/C++程序可以实现许多汇编语言的功能,但还是有不少特性只有汇编语言才能做: (1)执行PUSH和POP操作。 (2)访问BP与SP寄存器。 (3)初始化某些段寄存器。 (4)执行时间要求严格的例行程序,例如显示视频图形和实现通过端口的I/O。以上代码模板可以用于C/C++函数使用的所有汇编语言函数。当然,如果特定的函数不改变BX、SI或者DI寄存器,可以省略相关的PUSH和POP指令。7.3.2
调用汇编模块C/C++程序提供了与汇编语言的接口和在C/C++程序中直接插入汇编指令代码的功能,支持以“远调用”和“近调用”方式来调用使用汇编语言编写的函数。实现二者的链接,要解决好以下几个问题:1.采用一致的调用协议C/C++程序具有三种调用协议:_CDECL、_STDCALL和_FASTCALL。汇编语言利用“语言类型”确定调用协议和命名约定,支持的语言类型有:C、SYSCALL、STDCALL、PASCAL、BASIC和FORTRAN。C/C++语言调用协议从右到左压入参数,像它们在参数表里的位置那样。高级语言的参数通过堆栈传递给汇编语言过程,汇编语言过程将返回结果放在AX或DX:AX中。在BP、DI、SI、DS、SS和方向标志位被改动之前应使用汇编语言过程保存起来。这些寄存器是高级语言可能用到的。图7-1给出了C/C++调用协议下近调用和远调用的堆栈帧。7.3.2
调用汇编模块7.3.2
调用汇编模块对堆栈帧内参数的访问由C/C++语言调用协议提供,如下所示:.MODEL SMALL,CCLASS PROTOC,A:SWORD,B:SWORD
;采用C/C++调用协议
;并指出外部变量及其类型.CODECLASS PROCC,A:SWORD,B:SWORD
;SWORD对应C/C++的SHORTIN,,保存BMOV AX,A
;使用参数AADD AX,B
;使用参数进行计算C/C++程序与汇编语言混合编程通常利用堆栈进行参数传递,调用协议决定利用堆栈的方法和命名约定,两者要一致。C/C++有两种常用的函数调用约定:_STDCALL和_CDECL,详见表7-1。7.3.2
调用汇编模块7.3.2
调用汇编模块可以看出,_STDCALL和_CDECL的一个重要区别在于堆栈参数的维护,是调用函数还是被调用函数负责清理堆栈_STDCALL约定被调用函数清除堆栈参数,这样做的好处是可以减少源代码的大小,因为每次函数调用完成后,调用函数不再需要清理堆栈的指令;但是有些函数必须使用_CDECL调用约定,这种情况是参数个数未知的函数,这时只能由调用函数清除堆栈参数,例如库函数PRINTF。7.3.2
调用汇编模块_STDCALL调用约定代码片段;调用函数…PUSH PARAM3PUSH PARAM2PUSH PARAM1CALL SUBPROC…;被调用函数PUSH BPMOV BP,SP…MOV SP,BPPOP BPRET 12(清除堆栈)_CDECL调用约定代码片段;调用函数…PUSH PARAM3PUSH PARAM2PUSH PARAM1CALL SUBPROCADD SP,12(清除堆栈)…;被调用函数PUSH BPMOV BP,SP…MOV SP,BPPOP BPRET7.3.2
调用汇编模块2.命名约定C/C++程序可以调用汇编语言的子程序、过程、函数和汇编语言中定义的变量,汇编语言也可以调用C/C++编的函数和定义的变量,但要注意的是,由于C编译后的目标文件自动地在函数名和变量名前一个下划线,这是因为编译系统为了防止和它自己使用的内部函数和变量名发生混淆而造成错误,所以C汇编模块必须使用和C/C++兼容的有关段与变量的命名约定。在C/C++程序中的所有外部名字都包含个前导的下划线字符,如COLUMN。汇编程序引用在C/C++模块中的函数与变量也必须由下划线“_”开始。而且,由于C/C++是对大小写字母敏感的,所以汇编模块对于任何公共的变量名应当使用和C/C++模块同样的字母(大写或小写)。7.3.2
调用汇编模块3.声明公用函数名和变量名对C/C++程序和汇编语言使用的公用函数和变量应该进行声明,并且标识符应该一致,C++语言对标识符区分字母的大小写,而汇编不区分大小写。在汇编语言程序的开始部分,应对调用的函数和变量用EXTERN加以说明,其格式为:{EXTERN_函数名:函数类型}或{EXTERN_变量名:变量类型}其中函数类型指明函数是一个近程函数或是一个远程函数(即处在另一个段中),分别表示为NEAR型或FAR型,而变量类型指该变量的数据类型,其对应的关系如表7-2。7.3.2
调用汇编模块7.3.2
调用汇编模块如调用C/C++程序中MFPRO()的函数和变量SIGN,它们在C/C++程序开始说明部分为:INT MFPRO(VOID);INT SIGN,JARRAY[10];CHAR CH;LONG RESULT;在调用它的汇编程序中则在程序开始说明为:EXTERN_MFPRO:NEAREXTEMSIGN:WORD,_JARRAY:WORD,CH:BYTE,RESULT:DWORD若C/C++程序调用汇编语言中的过程(函数)和变量,则汇编语言中应用PUBLIC进行说明,且函数名和变量名前应带有下划线,即函数名和变量名的第一个符号应是下划线。7.3.2
调用汇编模块4.参数的传递原则C/C++程序调用汇编程序时,参数是通过堆栈传给汇编程序的,如在C/C++程序中说明了一个函数(用汇编程序写成):VOIDABC(CHAR*P1,INTP2);假设是在小内存模式下进行编译的,当在C/C++程序中调用它时,如写成ABC(&CH,NUM);则首先将NUM压入堆栈,接着将&CH压入堆栈,当汇编语言子程序要取得C/C++程序中传递来的参数时,便用BP寄存器作为基地址寄存器,用它加上不同的偏移量来对栈中所存数据进行存取操作,由于一般C/C++程序和调用的子程序共用一个堆栈,因此在汇编语言子程序中开始必须执行两条指令,即:
PUSH
BP
POP
BP,SP7.3.2
调用汇编模块传送参数有3种方法:有很多把输入值传递给汇编函数的方法,以及很多获得输出结果的方法。C/C++程序使用特定格式把输入值传递到程序堆栈中,并且从AX寄存器获得结果。如果希望汇编语言函数和C/C++程序一起工作,就必须显式地遵守C样式的函数格式。这就是说所有输入变量都必须从堆栈中读取,并且大多数输出值都返回到AX寄存器中。1)用值:C/C++调用程序传送变量的一个副本在堆栈中。被调用的汇编模块可以修改传送的值,但不能访问调用程序原来的值。如果有一个以上的参数,C/C++从最右边的参数开始使它们进栈。7.3.2
调用汇编模块2)用近引用:调用程序传送数据项值的偏移地址。被调用的汇编模块假设和调用程序共享同一个数据段。3)用远引用:调用程序传送段与偏移地址(段在前,然后是偏移地址)。被调用的汇编模块假设使用与调用程序不同的数据段。被调用的程序可以使用LDS或LES指令来初始化段地址。C/C++程序传送参数到堆栈中是以和其他语言相反的顺序进行的。例如:ADDS(NUM1,NUM2);该语句先使NUM2,后使NUM1进栈,就按次序并调用ADDS。在从被调用模块返回时,C/C++模块(不是汇编模块)使SP增量去丢弃传送的参数。在被调用的汇编模块中,用于访问2个传送参数的典型过程如下:7.3.2
调用汇编模块PUSH BP ;保存BPMOV BP,SP ;用SP地址作为基址指针MOV DH,[BP+4] ;从堆栈中取值MOV DL,[BP+6]…POP BP ;恢复BPRET在PUSHBP指令之后,堆栈帧表现为:7.3.2
调用汇编模块7.3.2
调用汇编模块在从被调用的模块返回时,由于由C/C++用程序承担清除堆栈的责任,所以发出的RET不带立即操作数。5.C语言子程序中寄存器的使用由于汇编语言中要用到80x86CPU中的许多寄存器,而调用它的C/C++程序中也要用到一些寄存器,因而这些寄存器的作用和保护则应引起注意,否则将导致程序失败。汇编语言子程序中要用到BP、SI和DI寄存器,在C/C++程序中BP是作为参数和自动变量的基地址,而在汇编语言子程序中,它也是作为取参数的基地址,SI和DI寄存器在C/C++中用作存放寄存器变量的,故在进入汇编子程序时必须通过压栈操作将其值进行保护,汇编子程序结束时必通过弹出操作,恢复其原来在C/C++程序中的值。BX和CX寄存器可以在汇编语言子程序中任意使用,AX和DX用作存放汇编子程序的返回值,可在汇编语言子程序中使用,但若有返回值时,则要保证在子程序返回时,它们存有要返回的值。7.3.2
调用汇编模块对于CS,SS,DS,SP,IP由于用作段寄存器和堆栈指针,在程序中不能随意使用,根据编译的内存模式或FAR,NEAR调用,它们可能和C/C++程序在同一个段内,用同一个段寄存器或不同的段内。因而可根据不同情况,有时要改变它们中的值,以得到正确段地址,但它们不能用于其他用处。如果输入值和输出变量被赋值给寄存器,那么在嵌入汇编代码中几乎可以像平常一样使用寄存器。汇编语言的一些优势是C/C++所无法比拟的,例如汇编程序在输入时可以采取各种进制数据以及直接读取数据等,可以用汇编程序作为主程序调用C/C++子程序。7.3.2
调用汇编模块在C/C++程序中定义的变量,在汇编程序里访问很简单,首先在C/C++程序中定义为全局变量,然后在汇编程序中用.GLOBAL定义(变量名前同样需加_),再利用间接寻址的方式即可实现,在C/C++程序中定义的数组,在汇编程序里要方便地访问,可把数组的首地址赋给辅助寄存器,利用辅助寄存器即可实现数组的有效访问。图7-2显示把输入值存放到堆栈中的方式,以及汇编语言函数如何访问它们。BP寄存器用作访问堆栈中的值的基址指针。调用汇编语言函数的程序必须知道输入值按照什么顺序存放在堆栈中,以及每个输入值的长度和数据类型。7.3.2
调用汇编模块7.3.2
调用汇编模块在汇编函数代码中,C样式函数对于可以修改哪些寄存器和函数必须保留哪些寄存器有着特定的规则。如果必须保留的寄存器在函数中被修改了,那么必须恢复寄存器的原始值,否则在执行返回发出调用的C/C++程序时也许会出现不可预料的结果。如表7-3所示,被调用的函数必须保留BX,DI、SI、BP和SP寄存器,这就要求在执行函数代码之前把寄存器的值压入堆栈,并且在函数准备返回调用程序时把它们弹出堆栈。这通常是按照标准的开头和结尾格式完成的。7.3.2
调用汇编模块7.3.2
调用汇编模块6.入口参数和返回参数的约定当被调用汇编语言子程序有值返回调用它的C/C++程序时,这个值是通过AX和DX寄存器进行传递的,对于小于等于32位的数据扩展为32位,存放在AX寄存器中返回;返回值是32位,则高16位存放在DX寄存器中,低16位存放在AX寄存器中。更大字节数据则将它们的地址指针存放在AX中返回。被调用的汇编模块对于任何返回值使用以下寄存器,如表7-4所示:7.3.2
调用汇编模块7.3.2
调用汇编模块由于任何类型进行参数传递时都扩展成32位,程序中没有远、近调用之分,所有调用都是32位的偏移地址,所有的地址参数也都是32位偏移地址,在堆栈中占4个字节。图7-3给出了采用C++语言调用协议的堆栈示意图。7.3.2
调用汇编模块7.3.2
调用汇编模块调用汇编语言函数的C或者C++程序必须知道用于把输入值传递给汇编函数的正确格式,以及如何处理任何返回值。(1)使用整数返回值最基本的汇编语言函数调用把32位整数值返回到AX寄存器中。调用函数获得这个值,它必须把返回值作为整数赋值给C变量;C/C++程序生成的汇编语言代码提取存放在AX寄存器中的值,并且把它传送到分配给C变量名称的内存位置(通常是堆栈中的局部变量)。这个C变量包含来自汇编语言函数的返回值,可以在整个C/C++程序中正常的使用。下面是汇编语言函数和使用它的C/C++程序的例子。首先,汇编语言程序SQUARE.S定义一个函数,它需要一个整数输入值,求它的平方,然后把结果返回到AX寄存器中。7.3.2
调用汇编模块(2)使用字符串返回值处理返回字符串值的函数要困难一些。和返回整数值到AX寄存器中的函数不同,这种函数不能把整个数据字符串返回到AX寄存器中(当然,除非字符串的长度是4个字符)。对于字符串返回值的作法是,返回字符串的函数返回指向字符串存储位置的指针。调用这个函数的C或者C++程序必须使用指针变量保存返回值。然后可以通过指针值访问字符串。如图7-4所示。字符串值被包含在函数的内存空间之内,但是主程序可以访问它,因为函数内存空间包含在主程序的内存空间之内。函数返回的32位指针值是字符串开始位置所在的内存地址。在C/C++程序中处理字符串值时,记住字符串必须使用空字符结尾。C和C++语言在变量名称前面使用“*”表明这个变量包含一个指针,可以创建指向任何数据类型的指针。但是对于字符串,想要创建指向数据类型CHAR的指针。7.3.2
调用汇编模块7.3.2
调用汇编模块但是,默认情况下,C/C++程序假设函数的返回值是整数值。必须通知编译器这个函数将返回字符串的指针。创建函数调用的原型将完成这个任务。原型在使用函数
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 陕教版道德与法治九年级上册8.1《升学就业善选择》听课评课记录
- 浙教版数学七年级上册第五章《一元一次方程》复习听评课记录
- 苏科版七年级数学上册《2.7.1理数的乘方》听评课记录
- 华东师大版七年级数学上册《第1章走进数学世界1.2人类离不开数学 》听评课记录
- 苏科版数学九年级下册8.4《抽签方法合理吗》听评课记录
- 苏科版数学九年级上册1.2《一元二次方程的解法》听评课记录4
- 生态环境监测数据共享合同(2篇)
- 环境数据共享服务合同(2篇)
- 听评课研讨记录七年级
- 沪教版数学七年级下册15.2《直角坐标平面内点的运动》听评课记录
- 电化学免疫传感器的应用
- 数据中心基础知识培训-2024鲜版
- 供电企业舆情的预防及处置
- 【高中语文】《氓》课件++统编版+高中语文选择性必修下册
- T-WAPIA 052.3-2023 无线局域网设备技术规范 第3部分:接入点和控制器
- 第4课+中古时期的亚洲(教学设计)-【中职专用】《世界历史》(高教版2023基础模块)
- 金点子活动总结汇报
- 运动技能学习与控制完整
- 原料验收标准知识培训课件
- Unit4MyfamilyStorytime(课件)人教新起点英语三年级下册
- 物流运作管理-需求预测
评论
0/150
提交评论