多模块程序设计与混合编程汇总课件

PPT
  • 阅读 93 次
  • 下载 0 次
  • 页数 218 页
  • 大小 748.001 KB
  • 2022-11-24 上传
  • 收藏
  • 违规举报
  • © 版权认领
下载文档40.00 元 加入VIP免费下载
此文档由【小橙橙】提供上传,收益归文档提供者,本网站只提供存储服务。若此文档侵犯了您的版权,欢迎进行违规举报版权认领
多模块程序设计与混合编程汇总课件
可在后台配置第一页与第二页中间广告代码
多模块程序设计与混合编程汇总课件
可在后台配置第二页与第三页中间广告代码
多模块程序设计与混合编程汇总课件
可在后台配置第三页与第四页中间广告代码
多模块程序设计与混合编程汇总课件
多模块程序设计与混合编程汇总课件
还剩10页未读,继续阅读
【这是免费文档,您可以免费阅读】
/ 218
  • 收藏
  • 违规举报
  • © 版权认领
下载文档40.00 元 加入VIP免费下载
文本内容

【文档说明】多模块程序设计与混合编程汇总课件.ppt,共(218)页,748.001 KB,由小橙橙上传

转载请保留链接:https://www.ichengzhen.cn/view-45520.html

以下为本文档部分文字说明:

第9章多模块程序设计与混合编程第9章多模块程序设计与混合编程9.1多模块程序设计的基本概念9.2汇编语言程序的多模块连接9.3汇编语言与高级程序的连接习题9第9章多模块程序设计与混合编程9.1多模块程序设计的基本概念在汇编语言程序

设计中,源程序所涉及的标识符(变量、标号、段名和过程名等)都在本程序中定义,它与本程序之外的标识符不发生任何关系,这种程序设计称为单模块程序设计。本章将介绍多模块程序设计方法,也称为模块化程序设计方法。所谓模块,从功能上讲它可

以是整个大程序中较为独立的一个部分,从源程序结构形式上讲它是由END结束的一个完整程序。第9章多模块程序设计与混合编程一个源模块可以单独汇编,形成自己的目标模块。最后,由连接程序将各个目标模块连接成一个可执行程序。采用模块化程序设计有下面一些优点:

(1)一个复杂程序可以分成若干个模块,可由不同人员分头完成;(2)每个模块的任务明确,便于理解;(3)单个模块易于编写和调试;(4)便于程序的维护和修改;(5)可以直接利用已有的模块。第9章多模块程序设计与

混合编程采用模块化程序设计时,必须合理分割模块,严格定义各模块的输入、输出参数和各模块间的通信方式。在单模块程序设计时,模块内所用到的段、变量及标号等各种标识符都必须在本模块内给予定义,否则汇编时将会

给出错误信息。多模块程序设计时,由于各个模块都是整个程序的一个部分,因此,各模块之间不仅会有数据上的传递,而且会出现各模块间的变量、标号等标识符的交叉引用。第9章多模块程序设计与混合编程如何实现这种交叉引用,如何实现各模块间各段的连接是汇编语言多模块程序设计的重要问题,也是本章要叙述的主要内容。

此外,多程序模块的连接不仅适用于汇编语言的程序模块,也适用于汇编语言程序模块与高级语言的程序模块的连接。第9章多模块程序设计与混合编程9.2汇编语言程序的多模块连接9.2.1多模块之间段的连接1.SEGMENT语句提供的连接信息段定义的完整语句为段名SEGMENT[定位类型][组合类型]['类

别']段名ENDS第9章多模块程序设计与混合编程在第4章中已指出,段定义后,段名有段地址、偏移地址、定位类型、组合类型和'类别'5个属性,前3个属性在第4章中已作过介绍,本节要介绍后两个属性,它们用来为连接过程提供多模块间段连接的有关信息。第

9章多模块程序设计与混合编程1)组合类型组合类型告诉汇编程序应为连接程序提供本段与其他段连接的有关信息,如本段与其他段是否组合为同一段,一旦组合后,本段信息与其他段信息的关系如何等等。组合类型有6种不同的类型:NONE、PUBLIC、COMMON、AT表

达式、STACK和MEMORY。段定义时,组合类型若被省略,则隐含为NONE类型。第9章多模块程序设计与混合编程NONE类型表示本段与不同模块中的其他段在逻辑上不发生关系,连接后各模块中的各段都有自己

的段地址(也称基地址)。PUBLIC类型表明连接时,应把不同模块中属于该类型的同名同类别的段相继地连成一个段,其中所有的变量或标号都有相同的段地址,连接的顺序与LINK时用户所提供的各模块的顺序一致(本节的最后将给出连接的基本方法)。各模块中属于PUBLIC类型的同名同类别的

各段的总长度不能超过64KB。第9章多模块程序设计与混合编程STACK类型与PUBLIC类型的处理方法一样,只是组合后的这个段用作堆栈。当段定义中指明了STACK类型后,说明堆栈段已经确定,所以,在可执行

文件装入内存后段寄存器SS中已是该段的段地址,堆栈指针SP已指向堆栈栈底。故在一般示例中的这两个寄存器传送初值的指令可以省去。第9章多模块程序设计与混合编程COMMON类型表明连接时,应将不同模块中属于该类型的同名同类别的各段连接成一段,它们共用一个基地址,且互相覆盖。连

接后,段的长度取决于最长的COMMON段的长度。AT表达式类型表明连接时,应将本段装在根据表达式求值得到的16位段地址上,表达式也可以是一个有效的常数。该类型可以将我们要定义的段设定在固定的地址范围内。必须注意,定义AT类型的段内不应

包括任何指令语句或有初值的变量定义语句,但该段内允许设定标号,与标号有相同属性的过程定义语句或无初值的变量定义语句,这种类型仅仅用来将该段指向内存区中的某个段,第9章多模块程序设计与混合编程使该段的段名及

段内的变量与指向的内存区的段地址有关。例如,若要用一个过程名SUB1代表BIOS中的某段子程序,我们可以这样定义一个段和过程:CODEBSEGMENTAT表达式1ORGNSUB1PROCFARSUB1END

PCODEBENDS第9章多模块程序设计与混合编程其中,表达式1的值即为某子程序所在段的段地址,N即为该子程序在段内的偏移地址。这样定义后,程序中调用过程名SUB1时,即调用BIOS中对应的子程序。其中也可以

定义一个标号,并用ORG指定该标号的偏移地址,这样,该标号就与该段内的这个偏移地址相关。第9章多模块程序设计与混合编程MEMORY类型表明连接时应把本段装在被连接的其他所有段之上(地址高端),当有多个

段为此类型时,只有汇编程序遇到的第1个段才认为是MEMORY段,而其他段则当作COMMON类型。图9.1给出了不同模块中组合类型为PUBLIC和COMMON时的连接结果。图中两个模块的数据段都为COMMON类型,连接后,这两个段组合成一个段,并且互相覆盖,其长度取两个模块中段长

度长的段,即为第2模块的长度。由于COMMON类型的段组合后,相互覆盖,所以,只有不同模块采用公用缓冲区时才使用这种类型。第9章多模块程序设计与混合编程图9.1中两个模块的代码段为PUBLIC类型,因此,连接后,两

个模块的代码段也组合成一个段,但它们并不覆盖,而且两个代码段相邻地连接在一起,其顺序也与LINK时提供的目标模块的顺序一致。组合后段的长度应是两个代码段长度的和。图9.1中两个源模块中的数据段和代码段都没有给出类别,这也是允许的,但若某个模块中给出了类别而另一模块中不给出类别,那么,这两个模块

中的同名段将不能组合成一个段,它们被认为不是同类别的段。模块1和模块2如下:第9章多模块程序设计与混合编程图9.1PUBLIC和COMMON的连接结果连接后的数据段连接后的代码段模块1用的数据段模块2用的数据段模块1的代码段模块2的代码段第9章多模块程序设计与混合编程

模块1:DATASEGMENTCOMMOMDW20HDUP(?)DATAENDSCODESEGMENTPUBLICCODEENDS第9章多模块程序设计与混合编程模块2:DATASEGMENTCOMMOMDW30HDUP(?)DATAENDSCODESEGMENTPUBLICCODEEND

S第9章多模块程序设计与混合编程2)'类别'类别可以是任何一个合法的名称,但必须用单引号括起来。连接时,将把不同模块中相同'类别'的各段在物理上相邻地连接在一起,其顺序亦与LINK时提供的各模块顺序一致。当'类别'相同的各段的段

名不同时,它们连接后虽在同一物理段内,但实际上它们仍不属于同一个段,也就是说它们的段基址不相同。这样做的一个好处仅是便于程序的固化。在编程时,它们都是独立的代码段,各段有各自的段基地址,但连接后,它们却在同一物理段,从而可以把各段程序固化在一起。第9章多

模块程序设计与混合编程2.GROUP伪指令GROUP伪指令格式为段组名GROUP段名[,段名,…]格式中的段组名是用户自己定义的一个标识符,其定义规则和语句名的定义规则相同,格式中的段名是本模块中已定义的段的名称。第9章多模块程序设计与混合编程GROUP

伪指令提供了段的另一种组合形式。它把多个由SEGMENT语句定义的段组合在同一段组中,用一个段组名表示。同一段组内的各段将装在同一物理段内,它们有相同的段基址。同一段组内,段的数目不受限制,但总字节数不能超过64KB,同一段组内的各段的组合类型和类别可以不同。第9章多模块程序设计与混合编程【

例9-1】GROUP伪指令应用举例。模块1:AGROUPGROUPCODE1,CODE2;定义了一个段组AGROUPCODE1SEGMENTASSUMECS:AGROUPSTART:CODE1ENDS

CODE2SEGMENTASSUMECS:AGROUP第9章多模块程序设计与混合编程CODE2ENDSENDSTART模块2:AGROUPGROUPCODE3CODE3SEGMENTASSUMECS:AGROUPCODE3ENDSEN

D第9章多模块程序设计与混合编程例中有2个模块3个段,连接后这3个代码段都将组合在同一物理段内,各模块中的CS都指向该段组名(AGROUP所代表的段地址),3个代码段中的所有标号或变量都有相同的段基地址。从上面的介绍我们看到,段定义中的组合类型、类别和

段组定义都为多模块之间段的连接提供了必要的信息,但它们又不完全相同。组合类型中的PUBLIC、STACK和COMMON类型都指出将把各模块中的相应段组合成一个段,且有相同的段基址,但前提是这些段的段名和类别必须相同。第9章多模

块程序设计与混合编程段组定义同样也指出将把各模块中的相应段组合成一个段,且有相同的段基址,但它对各段的段名和段的类型没有限制,并且各段还可以有自己的段基址,这对于将分头编写的多个模块组合成一个段提供了方便,也就是说只要在各模块中加一条段组名相同的段组定义语句即可。

第9章多模块程序设计与混合编程类别则指出了各模块中凡是类别相同的各段不论其段名和组合类型如何,都将它们组合在同一物理段内,但它们的段地址却不一定是相同的,只有当段名相同且组合类型为PUBLIC、STACK和COMMON之一时,段地址才相同。类别的这一功能对程序的固化带来了方便,我们可以把

编程时并不属于同一段的程序或数据组合在一起(同一物理段内)。第9章多模块程序设计与混合编程此外,组合类型还为连接程序提供了当这些段组合成一段后,段内信息关系的有关信息。如PUBLIC和STACK类型是将各模块中的所有信息都顺序地连接在一起,互不覆盖,而COMMON类型组合

后各模块中的信息互相覆盖,这对当各模块需要一个公共缓冲区时是十分有用的。第9章多模块程序设计与混合编程9.2.2模块间的交叉访问模块间的交叉访问是指一个模块中要引用另一个模块中定义的变量、标号等标识符。例如,主模块中要调用的过程名是在另一个子模块中定义

的,子模块中要用到的变量可能是在主模块中定义的。这样,一个源模块中定义的标识符就可能有两类:一类是供本模块使用的,我们称它们为局部标识符;另一类是同时可供本模块和其他模块使用的,我们称这类标识符为外部标识符

或全局标识符。第9章多模块程序设计与混合编程那么,如何告诉汇编程序本模块中哪些标识符是全局的(也就是可以被其他模块引用的),哪些标识符是要引用外部的(也就是本模块中没有定义的),以及出现了交叉访问后,汇编语言程序编

程又应注意些什么问题,这些都是本节所要讨论的主要内容。第9章多模块程序设计与混合编程1.伪指令PUBLIC和EXTRN1)伪指令PUBLIC伪指令PUBLIC的功能是指明本模块中所定义的标识符有哪些是可以提供其他

模块调用的外部标识符。格式如下:PUBLIC标识符,标识符,…其中,PUBLIC是伪指令操作码助记符,其后的标识符就是本模块内定义的可供其他模块调用的标识符,它可以是变量名,也可以是标号或过程名。第9章多模块程序设计与混合编程2)伪指令EXTRN伪指令EX

TRN的功能是指出本模块中用到的哪些标识符是由其他模块定义的。格式如下:EXTRN标识符:类型,标识符:类型,…其中,EXTRN是伪指令操作码助记符,其后的标识符就是本模块中要引用的外部标识符。这里,标识符必须指出其类型,这是在汇编时为

了生成正确的机器码所必需的。变量的类型可以是BYTE、WORD或DWORD,标号的类型可以是NEAR或FAR。第9章多模块程序设计与混合编程各模块中语句由PUBLIC和EXTRN提供的标识符必须互相呼应。连接时,连接程序将检查各模块中EXTRN语句中的每个标识符是否与PUBLIC语句中

的标识符相配合。若EXTRN语句中出现的标识符在PUBLIC语句中找不到,说明该标识符未定义,连接程序将给出错误信息,如例9-2所示。例9-2各模块中要引用的外部变量有VAR1、VAR2、VAR3和VAR4,但在PUBLIC语句中说明可以提供给其他模块引用的外部变量为

VAR1、VAR2、VAR3和VAR5,说明变量VAR4没有定义,从而出错;而变量VAR5是由模块3定义的外部变量,第9章多模块程序设计与混合编程例中没有被其他模块引用,这是允许的。此外,模块1与模块2的代码段连接后是一个段,所以,在模块1中要用到的

标号EX1虽不在同一模块中,但却是NEAR类型。标号EX2在模块3中定义,连接后它与模块1的代码段不在同一段,因而EX2为FAR类型。第9章多模块程序设计与混合编程【例9-2】模块间交叉定义示例一。语句PUBLIC和EXTRN提供的标识符必须互相呼应。模块1:E

XTRNVAR3:WORD,EX1:NEAREXTRNEX2:FARPUBLICVAR1,VAR2DATASEGMENTVAR1DB?VAR2DW?第9章多模块程序设计与混合编程DATAENDSCOD

ESEGMENTPUBLIC'CODE'START:CODEENDSENDSTART模块2:EXTRNVAR1:BYTE,VAR2:WORD,VAR5:WORDPUBLICEX1,VAR3DATASEGMENTVAR3DW?第9章多模

块程序设计与混合编程DATAENDSCODESEGMENTPUBLIC'CODE'EX1:CODEENDSEND模块3:EXTRNVAR4:BYTEPUBLICVAR5,EX2DATASEGMENTVAR5DW?第9章多模块程序设计与混合编

程DATAENDSCODE3SEGMENTEX2L:CODE3ENDSEND第9章多模块程序设计与混合编程2.模块间交叉访问时的编程考虑伪指令PUBLIC和EXTRN为不同模块之间的有关变量和标号建立了联系,

从而使模块间的交叉访问成为可能。但是不同模块中引用同一个变量(或标号)时,它们的基地址必须一致,这是多模块程序设计时应考虑的主要问题,否则程序运行时就会出错。基地址的一致有两层含义:一是每个变量(或标号)的偏移地址所对应的段的

基地址应一致;二是在使用这些变量(或标号)时,段寄存器的内容必须是该变量(或标号)所对应的段地址。第9章多模块程序设计与混合编程我们首先说明第1个问题,即不同模块中交叉引用变量(或标号)时,变量的偏移地址对应的段

基址是什么。一般地说,不同模块中的各段(代码段或数据段)在连接后不论是在同一段还是不在同一段,在不同模块中取某变量的偏移地址时,其段基地址都是相对于变量所在段的段地址的。如例9-3给出3个模块,汇编连接后有2个代码段(CODE和CODE1)和3个数

据段(DATA1、DATA2和DATA3),各段中的变量(或标号)的偏移地址都是相对于它们所在段的段地址的,例如VAR1和VAR2的基地址为DATA1段的段地址,不论是在模块1还是在模块2中访问它们都是一样的。第9章多模块程序设计与混合编程但是有时

也有不同的情况,例如当不同模块中的有些段连接后不在一个段内,而把有关伪指令EXTRN却放在了某个段内,此时就可能出现在不同模块中访问同一个变量,这个变量的偏移地址却是不同的。如例9-3中,若在模块1中将伪指令EXTRNVA

R3:WORD放在DATA1段内,那么在模块1和模块3中访问VAR3时,其偏移地址就不同了,因为在模块1中VAR3的段基址是DATA1的段地址,而在模块3中VAR3的段基址是DATA2的段地址。也就是说,在这种情况下同一个变量(VAR3)在两个不同的模块中其段基址是不同的,从而它的偏移地址也不

同,在访问这样的变量时就应特别注意。第9章多模块程序设计与混合编程【例9-3】模块间交叉定义示例二。不同模块中引用相同标识符时其基地址必须一致。模块1:EXTRNVAR2:WORD,SROUT:FAREXTRNVAR3:WORD,VAR4:WORDDATA1SEGMENTPUB

LIC'DATA'VAR1DW?DATA1ENDSCODESEGMENTASSUMECS:CODE,DS:DATA1第9章多模块程序设计与混合编程START:MOVAX,DATA1MOVDS,AXMOVAX,VAR2;VAR2的段地址在DS中MOVAX,SEGVAR3;ES中为VAR3的段地址

MOVES,AXMOVBX,ES:VAR3MOVAX,SEGVAR4MOVES,AX第9章多模块程序设计与混合编程MOVCX,ES:VAR4CALLFARPRTSROUTCODEENDSENDSTART第9章多模块程序设计与混合编程模块2:PUBLICVAR2DATA1SEGMENTPUBLIC'

DAT'VAR2DW?DATA1ENDS第9章多模块程序设计与混合编程模块3:PUBLICVAR3,VAR4DATA2SEGMENTVAR3DW?DATA2ENDSDATA3SEGMENTVAR4DW?第9章多模块程序设计与混

合编程DATA3ENDSPUBLICSROUTCODE1SEGMENTASSUMECS:CODE1SROUTPROCFARRETSROUTENDPCODE1ENDSEND第9章多模块程序设计与混合编程上面的第2个问题主要是

段寄存器的管理问题。当不同模块中的有关段连接后为同一段时,问题就十分简单,不论在哪个模块中访问该段中的变量,段寄存器的内容都可以不变,如例9-3中要访问DATA1段中的变量VAR2,不论在模块1还是在模块2中它们的段

地址是一样的,因此在这两个模块中DS的内容不须改变。但当不同模块中的某些段连接后不在同一段内时,必须注意段寄存器内容的正确设置,如例9-3模块1中要访问变量VAR3或VAR4时,应首先取出它们的段地址送入相应的段寄存器,由于变量VAR3和VAR4不在模块1中,取相应变量的段地址可有两种方法,如例

9-3和例9-4中所示。第9章多模块程序设计与混合编程【例9-4】模块间交叉定义示例三。不同模块连接后不在同一段时段寄存器地址的正确设置方法。模块1:CODATASEGMENTPUBLICEXTRNVAR1:WORD,VAR2:B

YTECODATAENDSDATASEGMENTDB?第9章多模块程序设计与混合编程DATAENDSCODESEGMENTASSUMECS:CODE,DS:DATA,ES:CODATASTART:MOVAX,DATAMOVDS,AXMOVAX,CODATAMOVES,AXMOVBX,VAR

1MOVVAR2,DL第9章多模块程序设计与混合编程CODEENDSENDSTART模块2:CODATASEGMENTPUBLICPUBLICVAR1,VAR2VAR1DW?VAR2DB?CODATAENDS

END第9章多模块程序设计与混合编程在例9-3的模块1中,我们是采用属性操作符(SEG)来获取VAR3和VAR4的段地址的。在例9-4中给出了另一种方法,因为模块1中要访问的变量VAR1和VAR2不在本模块的DATA段中,为

了取得这两个变量的段地址,我们在模块1中定义了一个与模块2中同名的段CODATA。在这个段中只定义了两个外部变量VAR1和VAR2,由于两个模块中的CODATA段的组合类型均为PUBLIC,所以连接以后它们是同一段,这样在模块1中就

可以直接取CODATA段的段地址了。第9章多模块程序设计与混合编程并且可用ASSUME语句指出哪个段寄存器将指向该段(如例中为ES),于是源程序中有关语句的段前缀也可以省略,如例中的指令MOVBX,VAR1中就省略了段前缀ES:(注意,该指令的机器码中仍有段前缀字节)。第9章多模块程序设计与混合

编程3.建立完整的可执行文件上面我们说明了多模块的源程序的设计方法和应注意的问题。由于每个模块还只是整个完整程序的一部分,因此在多个模块的源程序写好后我们还必须建立一个完整的可执行文件。建立一个完整的可执行文件的方法如下:第9章多模块

程序设计与混合编程(1)将各源程序模块分别汇编,并建立各自的目标模块(即建立各自的.OBJ文件)。(2)用连接程序LINK将这些目标模块连接成一个可执行文件。此时,在回答连接程序询问目标文件的文件名时,可用如下形式回答:第9

章多模块程序设计与混合编程模块1文件名+模块2文件名+模块3文件名+…这里,一方面给出了要连接的各模块目标文件的文件名,同时也给出了各模块间的连接顺序。可执行文件的文件名有两种提供方式:一种方式是在连接程序询问可执行文件的文件名时直接以回车回答,此时,提供的第1个目标文件的文件名就是可执行

文件的文件名;另一种方式是在连接程序询问可执行文件的文件名时另外提供一个指定的可执行文件的文件名。第9章多模块程序设计与混合编程【例9-5】C:\>LINKOBJECTMODULES[.OBJ]:MFILE+SFILE1+SFILE2↙RUNFILE[MFILE.EXE]:MFILE

1↙LISTFILE[NUL.MAP]:↙LIBRARIES[.LIB]:↙第9章多模块程序设计与混合编程上述各行冒号以后的内容是LINK时回答的信息。第1行的回答表示有3个目标模块要连接成一个可执行文件;第2行的回答表示连接后的可执行文件名为MF

ILE1.EXE;第3行的回答表示不需要建立列表文件;第4行回答表示没有专用的库文件。若第2行回答的是一个回车(↙),即没有另指定文件名,那么,连接后的可执行文件名将是MFILE.EXE。第9章多模块程序设计与混合编

程最后,还需指出,多个模块连接成一个可执行文件后,该文件只有一个启动地址,所以,连接成一个完整程序的各个源模块中只能有一个源模块中的结束伪指令END可以带表达式,以指出整个程序的启动地址。下面我们给出两个多模块程序设计的例子。例9-6给出了多模块程序的一个例子。程序的功能是

利用多个程序模块实现指定字符串的显示。程序中共有两个模块,模块1为主程序模块(称主模块),模块2为子程序模块(也称过程模块)。第9章多模块程序设计与混合编程【例9-6】多模块程序设计例一。利用多个程序模块实现指定字符串的显示。模块1:STACKSEGMENTPAR

ASTACK'STACK'DW256DUP(?)STACKENDSCODESEGMENTPUBLIC'CODE'EXTRNOUT_ROUTINE:NEAREXTRNOUT_CHAR:BYTEMESSEDB

'THISISAROUTINE.',13,10第9章多模块程序设计与混合编程ASSUMEDS:CODE,CS:CODEMAIN:CLDMOVAX,CSMOVDS,AXMOVSI,OFFSETMESSELOOP1:LODSBMOVOUT_CHAR,AL第9章多模块程序设计与混合编程CAL

LOUT_ROUTINECMPOUT_CHAR,10JNELOOP1MOVAH,4CHINT21HCODEENDSENDMAIN第9章多模块程序设计与混合编程模块2:CODESEGMENTPUBLIC'CODE'ASSUMECS:CODE,DS:CODEPUBLICOUT_

CHARPUBLICOUT_ROUTINEOUT_CHARDB?OUT_ROUTINEPROCNEARMOVDL,OUT_CHAR第9章多模块程序设计与混合编程MOVAH,02INT21HRETOUT_ROUTINEENDPCODEENDSEND第9章多模块程序设计与混合编

程例9-6中主模块中要用到子模块中的过程OUT_ROUTINE和变量OUT_CHAR,因此,它们在主模块中为外部变量,在子模块中为全局变量(如例中所示)。两个模块中的代码段为同名同类别且组合类型均为PUBLIC,连接后为同一段,因此过程调用为近调用。例中的变量都在代码段中,因此段

寄存器DS的内容在两个模块中也不需改变。请考虑:若要求在连接后这两代码段不是同一段,那么程序该做哪些修改?应注意哪些问题?第9章多模块程序设计与混合编程例9-7给出了多模块程序的第二个例子。程序完成的功能是实现两个32位数相乘

。多位数相乘利用了一个子程序MULT,它是一个独立的模块。参数的传递采用指定内存单元方式,两个操作数分别为OPRN1和OPRN2,相乘的结果存入PRDCT中,操作数中包含的字数在NSIZE中。本程序有两个模块,模块1为主模块

,变量OPRN1、OPRN2、PRDCT和NSIZE在主模块中定义。模块2是子模块,完成多位数相乘的过程。由于两个模块的代码段的组合类型为NONE类型,所以,该程序中的过程MULT应为FAR类型。此外,过程中要访问的变量是主模块中所定义的,因而主模块在DS中所设定的段地址在过程中

可以使用。第9章多模块程序设计与混合编程【例9-7】多模块程序设计例二,利用多模块实现两个32位数相乘。模块1:STACKSEGMENTSTACKDW256DUP(?)TOPLABELWORDSTACKENDSPUBLICOPRN1,OPRN2,PRDCT,NSIZEEXTRNM

ULT:FARDATASEGMENT第9章多模块程序设计与混合编程OPRN1DW8DUP(?)OPRN2DW8DUP(?)PRDCTDW16DUP(?)NSIZEDW?DATAENDSCODESEGMEN

TASSUMECS:CODE,DS:DATA,SS:STACKSTART:MOVAX,DATA第9章多模块程序设计与混合编程MOVDS,AXMOVAX,STACKMOVSS,AXMOVSP,OFFSETTOPMOVNSIZE,02CALLMULTMOVA

H,4CHINT21HCODEENDSENDSTART第9章多模块程序设计与混合编程模块2:EXTRNOPRN1:WORD,OPRN2:WORDEXTRNPRDCT:WORD,NSIZE:WORDPUBLICMULTCOD

ESEGMENT第9章多模块程序设计与混合编程ASSUMECS:CODEMULTPROCFARPUSHSIPUSHDIPUSHBXPUSHCXPUSHAXMOVAX,SEGOPRN1MOVDS,AXLEADI,OPRN1LEASI,OPRN2第9章多模块程序设计与混合编程LEABX,

PRDCTMOVCX,NSIZEPUSHBXMOVAX,0SHLCX,01CLD第9章多模块程序设计与混合编程MBMUL1:MOV[BX],AXINCBXINCBXLOOPMBMUL1POPBXMOVCX,NSIZE第9章多模块程序设计与混合编程MBMUL2:PUSHCXMOVDX,[S

I]INCSIINCSIPUSHBXPUSHDIMOVCX,NSIZEMBMUL3:PUSHCX第9章多模块程序设计与混合编程PUSHDXMOVAX,[DI]INCDIINCDIMULDXADD[BX],AXINCBXINCBXADC[BX],DXPOPDXPOPCXLOOPMB

MUL3第9章多模块程序设计与混合编程POPDIPOPBXINCBXINCBXPOPCXLOOPMBMUL2POPAXPOPCXPOPBXPOPDIPOPSIRET第9章多模块程序设计与混合编程MULTENDPCODEENDSEND第9章多模块程序设计与混合编程9.3汇编语言与高级语言程

序的连接在实际的软件开发工作中,应用程序往往与应用系统的硬件有着密切的关系。有时会遇到这样的情况:主干程序使用某种高级语言编写,而一些与硬件相关的功能,如运行时间占90%而代码只占10%的关键部分,运行次数很多的重复部分,对运行速度要求很高的

部分,要求直接访问硬件的部分,以及高级语言不支持的其他功能等,则采用汇编语言编写成相应的子程序,这样做会得到事半功倍的效果。把采用这种编程的技术或方法称为汇编语言与高级语言的混合编程。第9章多模块程序设计与混合编程汇编语

言和高级语言混合编程,需要解决两个主要的技术问题:一是不同语言程序模块之间的连接;二是调用过程中参数的传递方法。对此不同的高级语言或同一种高级语言的不同版本所采取的具体方法不尽相同。本节主要介绍汇编语言与C/C++语言接口的基本方法。第9章多模块程序设计与混合编程9.3.1调用协议调

用协议是指在进行子程序调用时,主程序向子程序传递参数以及从子程序获得返回值的约定方式。通常参数传递的方法是:主程序使用系统堆栈向子程序传递入口参数,子程序使用CPU内部寄存器来保存向主程序的返回值。此外调用协议还将确定哪些寄存器的内容需要保护,哪些寄存器可以自由使用。汇编语言和不同的高级语

言混合编程采用不同的调用协议:C/C++语言使用C语言调用协议。采用何种调用协议则使用完全段定义来指定,还可以用段的简化定义方式在MODEL语句中或者与PROC语句相联系的OPTION指示符中指定。第9章多模块程序设计

与混合编程1.入口参数传递规则1)C语言调用协议采用C语言调用协议调用一个子程序时,是按照调用参数表自右向左的顺序将子程序入口参数压入堆栈的。例如,在C语言源程序中有一条子程序调用语句为:callsub

(a,b,c);则参数的入栈顺序是,c先入栈,然后是b入栈,最后是a入栈。图9.2说明了采用C语言调用协议下,汇编语言近调用和远调用是如何通过堆栈传递入口参数的。第9章多模块程序设计与混合编程图9.2C语言调用协议通过堆栈传递入口参数情况IP(返回地址)参数1参数2参

数nSP低地址端高地址端堆栈生长方向IP(返回地址)参数1参数2参数nSP低地址端高地址端堆栈生长方向CS(返回地址)(a)NEAR型近调用(b)FAR型远调用……第9章多模块程序设计与混合编程C语言调用协议允许使用

可变个数的参数,此时,第一个压入堆栈的参数通常是用来说明后续有多少个参数的数值。需要注意的是,在C语言中有些类型的变量是先转换成另外一种类型后才压入堆栈的。如字符型变量(char)转换成整型变量(int);

无符号字符型变量(unsignedchar)转换成无符号整型变量(unsignedint);浮点型变量(float)转换成双精度型变量(double);第9章多模块程序设计与混合编程结构型变量(structure)则是按照域变量的相反顺序整体压入堆栈。对于

数组变量,压入堆栈的内容是指向数组起始地址的指针。近指针占16位,只包括段内偏移地址;远指针占32位,先压入段寄存器的段地址,然后再压入段内偏移地址。第9章多模块程序设计与混合编程2)PASCAL语言调用协议与C语言调用协议恰好相反,当采用PASCAL语言调用协议

调用一个子程序时,参数压栈的顺序是按照调用参数表自左向右将子程序入口参数压入堆栈的。例如,在PASCAL语言源程序中有一条调用语句为callsub(a,b,c);则参数入栈的顺序是:a先入栈,然后是b入栈

,最后是c入栈。图9.3说明了PASCAL语言调用协议是如何通过堆栈传递入口参数的过程。第9章多模块程序设计与混合编程图9.3PASCAL语言调用协议通过堆栈传递入口参数的情况IP(返回地址)参数n参数

2参数1SP低地址端高地址端堆栈生长方向IP(返回地址)参数1参数2参数nSP低地址端高地址端堆栈生长方向CS(返回地址)(a)NEAR型近调用(b)FAR型远调用……第9章多模块程序设计与混合编程2.返回值传递规则不论是C语

言调用协议还是PASCAL语言调用协议,汇编语言子程序均是按照如下原则保存子程序返回值的。(1)果返回值为单字节数,则放入AL;(2)如果返回值为单字,则放入AX;(3)如果返回值为双字,则放入DX:A

X,其中DX中存放高字,AX中存放低字;(4)如果返回值大小超过双字则存放在系统静态变量存储区中,指向这个静态变量存储区的指针值在AX(近指针)或者DX:AX(远指针)内。第9章多模块程序设计与混合编程在子程序完成预先定义的功能返回主程序时

,C语言调用协议和PASCAL语言调用协议从堆栈弹出入口参数的方式是不一样的。C语言调用协议是由主程序来完成参数出栈,而PASCAL语言调用协议却是由子程序来完成参数出栈工作的。第9章多模块程序设计与混合编程3.寄存器保护规则调用协议的寄存器保护规则是用来说明从主程序进入子程

序,或者从子程序返回主程序时CPU内部相应寄存器所发生的变化,以及这些变化对程序正常运行带来的影响。同时还规定了哪些寄存器可以任意使用,哪些寄存器应该先保护内容再使用等问题的规则。寄存器保护规则把CPU内部寄存器分为4类。第9章多模块程

序设计与混合编程(1)CS,IP,SS,SP和FLAGS。只要能够正确连接混合编程的各个模块,正常地进行转子程序和返回主程序,就说明对CS、IP、SS、SP的处理是正确的。一般来说,这四个寄存器不需要在程序中进行改动。FLAGS寄存器反映程序的当前状态,一般也不需

要修改,除非需要使用某个标志位在主程序和子程序间传递信息。第9章多模块程序设计与混合编程(2)DS,ES,FS和GS。因为在TINY、SMALL和MEDIUM的存储模式下只有一个数据段,如果混合编程的各个

模块连接正确,则在进入子程序时DS已经指向需要访问的数据段了,这时不需要对DS进行修改;如果互相连接的高级语言模块和汇编语言模块的数据不在同一个数据段中,则在汇编语言子程序中要先保存DS,然后修改DS的值使其指向子程序的数据段,在返回主程序时再将DS恢复到转子前的内容

。ES、FS和GS寄存器则可以随意使用,没有任何约定。第9章多模块程序设计与混合编程(3)BP,SI和DI。通常在汇编语言子程序中使用BP寄存器访问系统堆栈以获得入口参数,所以使用前要保存,返主前要恢复。高级语言中一般使用SI和DI来存放寄存器变

量。在汇编语言子程序中使用SI和DI寄存器也应该先保存后使用,返主前恢复;如果高级语言中没有定义和使用寄存器变量,则SI和DI可以任意使用。一个比较稳妥的做法就是在子程序中像保护其他寄存器一样保护SI

和DI。第9章多模块程序设计与混合编程(4)AX,BX,CX和DX。这四个寄存器在汇编语言子程序中可以任意使用,尤其是BX和CX。子程序的返回值是通过AX或者DX:AX来保存和传递的。如果子程序有返回值,应按照返回值传递规则在AX或DX:AX中正确放置返回值;如果没有返

回值,则AX和DX也可以随意使用。第9章多模块程序设计与混合编程9.3.2汇编语言与C语言的接口C/C++语言是一种通用的程序设计语言,具有数据类型丰富,运算灵活多样,语句精炼高效,表达能力强,可移植性好,有硬件控制能力等优点,因而成为当前比较流行的高级语言,其应用范围也相当广泛。虽然C/C+

+可以实现绝大部分机器语言能够实现的功能,但汇编语言还是经常被用来改进软件和硬件控制系统的效率。C语言和汇编语言的接口方法主要有模块连接法、伪变量法和行内汇编法三种。第9章多模块程序设计与混合编程本节以

TurboC2.0(简称TC)与汇编语言的混合编程为例,介绍其编程接口方法。这些例程也可以不加修改地运行于TurboC++、BorlandC++和MicrosoftC/C++中。1.模块连接法所谓模块连接法,是指分别编译/汇编C语言源程序和汇编语言源程序,然

后再将目标文件进行连接,最终形成可执行文件的混合编程方法。第9章多模块程序设计与混合编程1)在TC中调用汇编子程序和变量要使汇编语言模块和TC模块正确连接到一起,必须处理好两点:一是汇编模块必须采用和TC模块一致的存储模式;二是汇编模块还必须遵守与TC兼容的命名约定,命名约定包括段组命名约定和

函数/变量命名约定。欲使TC的存储模式和汇编语言相同,共有六种不同的存储模式可选择,即微模式(Tiny)、小模式(Small)、中模式(Medium)、紧凑模式(Compact)、大模式(Large)和巨模式(Huge)。在混合编程时应该保持汇编语言和TC

的存储模式选择一致。第9章多模块程序设计与混合编程在小模式下所有的指针都是NEAR型的近指针,而数据/堆栈和代码可以各占一个段空间,这样运算速度快而且可用空间也大,程序运行效率最高。所以,应该尽量采用小模式进行混合

编程。在TC中允许用户创建如下几种程序和数据信息:数据定义语句和可执行语句。其中数据定义语句可以用来定义局部变量、全局变量和静态变量。编译程序在编译源程序时将这些信息放在不同的段中。表9-1说明了这些段的命名约定。第9章多模块程序设计与混合编程表9-1TC存储段命名约定段名内容STACK局部

变量(包括函数入口参数和函数内定义的自动变量)_BSS未初始化的全局变量和静态变量(源文件中显式说明为FAR型或Huge型的除外)(FILENAME)_DATA已初始化的全局变量和静态变量(源文件中显式说明为FAR型或Huge型的除外)FAR_DATA源文件中显式说明为F

AR型或Huge型的已初始化的全局变量和静态变量FAR_BSS源文件中显式说明为FAR型或Huge型的未初始化的全局变量和静态变量(FILENAME)_TEXT代码段第9章多模块程序设计与混合编程如果程序中只有一个代码段,则生成的代码段

的段名为“_TEXT”;若有多个代码段,则每个代码段的段名为“文件名_TEXT”。代码段的对齐类型为“BYTE”,组合类型为“PUBLIC”,类别为“CODE”。如果程序中只有一个数据段,则生成的数据段的段名为“_

DATA”;如果有多个数据段,则每个数据段的段名为“文件名_DATA”。数据段的对齐类型为“WORD”,组合类型为“PUBLIC”,类别为“DATA”。第9章多模块程序设计与混合编程小模式和中模式下STACK、_BSS和_DATA这三个

段被组合为一个DGROUP组。当程序执行时,DS和SS均指向这个DGROUP组的开始。紧凑模式和大模式下则只有_BSS和_DATA两个段组合成DGROUP组,STACK段独占一个段空间,这时DS不再等于SS。

巨模式下没有_BSS段,也没有DGROUP组,每个DATA都分配独立的段空间,但共用一个STACK段。第9章多模块程序设计与混合编程在DGROUP组中还包括一个特殊段NULL。NULL段是DGROUP组的第一个段。该段中含有编译程序版权说明,还用于非法使用指针的检测。程序中的空指针就指向了NULL

段的开始,如果在程序中向一个空指针写入数据就会引发一个“空指针赋值”的错误。NULL段不需要定义,由C语言的编译器自动生成。如果采用完全段定义方式,则汇编语言源程序中段组命名必须按照上述约定进行;如果采用简化段定义方式,则上述工作由系统自动完成,这样可以大大减少

编程的工作量。第9章多模块程序设计与混合编程C语言在编译源程序生成目标文件时自动在函数/变量名前加一个下划线“_”,因此汇编语言要引用C语言的函数或者变量时应该在其名字前加上一个下划线。同样,为使汇编语言程序中的

函数和变量能够被C语言程序所引用,这些函数/变量名前也应该加上下划线。此外,C语言对大小写字母是敏感的,这一点和汇编语言不同,在定义和声明函数/变量名时一定要保证二者完全一致。可在TC的集成开发环境(IDE)中选择Option菜单的Linker子菜单,将其中的C

ase-sensitivelink开关项设置为Off而使连接器忽略大小写。第9章多模块程序设计与混合编程为了在C语言程序中调用汇编语言模块的子程序和变量,在C语言程序中应该使extern语句声明,同时在汇编语言程序中应该以PUBLIC伪指令声明相应的子

程序和变量。PUBLIC伪指令格式如下:PUBLIC函数名PUBLIC变量名在PUBLIC声明中没有规定函数和变量的类型,其类型取决于在汇编语言程序中的定义。注意保证C语言和汇编语言对类型声明的一致性。第9章多模块程序

设计与混合编程下面给出TC调用汇编语言子程序的主要步骤。(1)在汇编语言编程方面TC调用汇编语言子程序的主要步骤。①使用和C语言相同的存储模式定义各个段空间,没有用到的段可以不定义;②在汇编语言源程序中用PUBLIC伪指令

声明C语言需要引用的子函数和变量;③按照C语言调用协议从堆栈中取得入口参数;④对参数进行处理,实现相应的功能;⑤将返回值送入AX或者DX:AX中返回;第9章多模块程序设计与混合编程⑥使用汇编程序MASM汇编源程序形成目标文件。(2)在TC编程方面TC调用汇编语言子程

序的主要步骤。①在C语言源程序中用extern语句声明汇编语言子函数和变量;②在程序中像引用本地函数和变量一样,引用这些汇编语言子函数和变量;③编译源程序形成目标文件;④使用TLINK连接C语言和汇编语言的目标文件,形成可执行文件;⑤执行程序进行验证和调试。第9章多模块程序设计与混合编

程【例9-8】设TC源程序在小模式下编译连接,试编写一个供TC调用的汇编语言子函数max。其在TC源程序中的声明和引用如下:/*程序名:callmax.c*//*功能:演示在小模式下TC程序调用汇编语言子函数

的过程*/externintmax(int,int);/*声明max为外部函数*/第9章多模块程序设计与混合编程main(){printf(''\nCallmax(3,5),theresultis:%d'',max(3,5));}第9章多模块程序

设计与混合编程实现max子函数功能的汇编语言程序如下:;程序名:asm9_6.asm;功能:实现供TC调用的max函数完成对两个整数的比较,返回较大者;在小模式下工作_TEXTSEGMENTBYTEPUBLIC'CODE'ASSUMECS:_TEXTPUBLIC_ma

x_maxPROCNEAR第9章多模块程序设计与混合编程PUSHBPMOVBP,SPMOVAX,[BP+4];AX=aCMPAX,[BP+6];a与b比较JGEOKMOVAX,[BP+6];如果b>a,则将b送AXOK:POPBP;恢复BP寄存器的内容RE

T_maxENDP_TEXTENDSEND第9章多模块程序设计与混合编程因为汇编语言程序只实现一个函数,不需要数据段,所以可省略数据段定义。TC和汇编语言源程序编辑完毕存盘后,在DOS提示符下输入如下命令:masmasm9_6;

(汇编asm9_6.asm,生成目标文件asm9_6.obj)tcc-ms-ccallmax;(按小模式编译callmax.c,只生成目标文件callmax.obj)tlinklib\c0scallmaxasm9_6,callmax,lib\cs;(连接

目标文件生成callmax.exe)callmax;(运行callmax.exe获得结果)第9章多模块程序设计与混合编程其中,c0s和cs是TC在小模式下的启动代码目标模块文件和函数库文件,通常存放在T

C安装目录下的lib子目录中。注意,tlink命令行中这两个文件的路径一定要正确,当存储模式发生变化时这两个文件也要发生相应的变化,如大模式下为c0l和cl。例9-8说明了TC和汇编语言混合编程时是如何传递

入口参数和取得返回值的。图9.4给出在C程序callmax中调用max函数时和汇编语言的max函数执行MOVBP,SP后堆栈内容的变化情况。第9章多模块程序设计与混合编程图9.4小模式下callmax.c程序中调用max函数时堆栈入口参数变化情

况IP(返回地址)35SP低地址端高地址端堆栈生长方向BP的原始内容35SP,BP低地址端高地址端堆栈生长方向IP(返回地址)(a)callmax调用max时(b)_max执行MOVBP,SP后系统堆栈变化系统堆栈变化BP+2BP+4BP+6第9章多模块程

序设计与混合编程【例9-9】对例9-8中的C语言源程序callmax改用大模式编译连接,试编写汇编语言源程序实现max函数。/*程序名:callmax.c*//*功能:演示在小模式下TC程序调用汇编语言子函数的过程*/externintmax(int,int);

/*声明max为外部函数*/main(){第9章多模块程序设计与混合编程printf(''\nCallmax(3,5),theresultis:%d'',max(3,5));}实现max子函数功能的汇编语言

程序如下:;程序名:asm9_7.asm;功能:实现供TC调用的max函数完成对两个整数的比较,返回较大者;在大模式下工作第9章多模块程序设计与混合编程asm9_7_TEXTSEGMENTBYTEPUBLIC'CODE'AS

SUMECS:asm9_7_TEXTPUBLIC_max_maxPROCFARPUSHBPMOVBP,SPMOVAX,[BP+6];AX=aCMPAX,[BP+8];a与b比较JGEOK第9章多模块程序设计与混合编程MOVAX,[BP+8];如

果b>a,则将b送AXOK:POPBP;恢复BP寄存器的内容RET_maxENDPASM9_7_TEXTENDSEND第9章多模块程序设计与混合编程相应的callmax.c源程序不需要改变,只是在编译时改用大模式,在DOS提示符下输入如下命令:masmasm9_7;;(汇编as

m9_7.asm,生成目标文件asm9_7.obj)tcc-ml-ccallmax;(按大模式编译callmax.c,只生成目标文件callmax.obj)tlinklib\c0lcallmaxasm9_7,callmax,lib\cl第9章多模块程序设计与混合编

程;(连接目标文件生成callmax.exe)callmax;(运行callmax.exe获得结果)即可。注意tcc和tlink命令行中参数的变化。对比asm9_6.asm和asm9_7.asm两例,可以发现如下三点变化。(1)段名的变化。因为在大模式下每个代码段占用独

立的空间,有独立的段名,段名为“文件名_TEXT”。在本例中即asm9_7_TEXT。第9章多模块程序设计与混合编程(2)函数类型的变化。在大模式下C语言程序的代码段和汇编语言的代码段不属于同一个物理段,对子函数的调用属于段间调用,因此_max函数被定义为FAR型。(3)获取入口参数的偏移量的变化

。因为对max函数的调用是段间的远调用,所以在callmax程序中保存断点地址时不但要保存IP,还要保存CS,这样就使得入口参数的偏移量发生了变化。图9.5给出在大模式下C程序callmax调用max函数时和汇编语言的max函数执行M

OVBP,SP后堆栈内容的变化情况。第9章多模块程序设计与混合编程图9.5大模式下callmax.c程序中调用max函数时堆栈入口参数变化情况IP(返回地址)35SP低地址端高地址端堆栈生长方向BP的原始内容35SP,BP低地址端高地址端堆栈生长方向

IP(返回地址)(a)callmax调用max时(b)_max执行MOVBP,SP后系统堆栈变化系统堆栈变化BP+4BP+6BP+8CS(返回地址)CS(返回地址)BP+2第9章多模块程序设计与混合编程【例9-10】在汇编语言程序中定义整型变量num和实现对num进行增

值的numadder子程序,在TC程序中调用numadder实现对num的增值并显示num的结果。TC源程序如下:/*程序名:calladd.c*//*功能:调用numadder子函数完成对num的访问

与增值*/externintnum;/*声明num为外部变量*/externvoidnumadder(int);/*声明numadder为外部函数*/第9章多模块程序设计与混合编程main(){inti;for(i=1;i<10;i+=2){numadde

r(i);printf(''\ni=%d,num=%d'',i,num);}}存储模式约定为使用小模式,则定义num并实现numadder的汇编语言程序如下:;程序名:asm9_8.asm;功能:定义

和实现供TC程序引用的外部变量num及外部函数numadder第9章多模块程序设计与混合编程_DATASEGMENTWORDPUBLIC'DATA'PUBLIC_numnumDW0_DATAENDS_TE

XTSEGMENTBYTEPUBLIC'CODE'DGROUPGROUP_DATAASSUMECS:_TEXT,DS:DGROUP,SS:DGROUPPUBLIC_numadder_numadderPROCNEAR第9章多模块程

序设计与混合编程PUSHBPMOVBP,SPMOVAX,[BP+4];获得要给num增值的参数值ADDAX_numMOV_num,AX;实现对num的增值POPBPRET_numadderENDP_TEXTENDSEND第9章多

模块程序设计与混合编程源程序编辑保存完毕,在DOS提示符下输入如下命令:masmasm9_8;(汇编asm9_8.asm,生成目标文件asm9_8.obj)tcc-ms-ccalladd;(按小模式编译calladd.c,只生成目标文件c

alladd.obj)tlinklib\c0scalladdasm9_8,calladd,lib\cs;(连接目标文件生成calladd.exe)calladd;(运行calladd.exe获得结果)第9章多模块程序设计与混合编程例9-10演示了C

语言程序如何引用汇编语言程序所定义的外部变量和子函数,因为numadder子函数没有返回值,所以被声明为void型。使用完全段定义方式,汇编语言必须和引用它的C语言程序采用相同的存储模式,并遵守C语言的命名规则。一旦C语言程序的存储模式发生改变,

那么汇编语言程序就必须进行相应的修改。第9章多模块程序设计与混合编程采用简化的段定义方式,汇编程序自动实现一致性的存储模式和兼容的段命名。在使用.MODEL伪指令设定存储模式后,过程类型自动根据存储模式的变化而改变,在中、大和巨模式下过程的缺省类型是FAR,而不再一直是NEAR,这样在使用PROC

伪指令声明过程时就不需要再声明其类型了。段的简化定义可以减轻编程的工作量,今后的例程将主要采用简化的段定义方式。第9章多模块程序设计与混合编程虽然使用.MODEL伪指令可以方便地更改存储模式,但是由于更改存储模式而引起入口参数在堆

栈中的位置变化仍需要手工修改程序,所以这样编程工作仍然很繁琐。通过预定义符号@CODESIZE和@DATASIZE可以解决这个问题。预定义符号@CODESIZE表示是否只有一个代码段。当利用.MODEL伪指令设置存储模式为Tiny、Smal

l或者Compact时@CODESIZE的值为0,表示只有一个代码段,此时调用是NEAR型的;当设置存储模式为Medium、Large或者Huge时@CODESIZE的值为1,表示代码段多于一个,此时

调用则应是FAR型的。第9章多模块程序设计与混合编程预定义符号@DATASIZE表示数据段和堆栈段的个数。当利用.MODEL伪指令设置存储模式为Tiny、Small或者Medium时@DATASIZE的值为0,表示只有一个数据/堆栈段,此时DS=SS,数据指针都

是NEAR型的;当设置存储模式为Compact或者Large时@DATASIZE的值为1,表示有一个数据段和一个堆栈段;当设置存储模式为Huge时@DATASIZE的值为2,表示有多个数据段和一个堆栈段,此时DS≠SS,数据指针都是FAR型的。第9章多模块程序设计与混合编程在汇编语言程

序中通过预定义符号@CODESIZE和@DATASIZE可以判定程序当前所处的存储模式,进而决定使用什么类型的数据指针和函数指针。因此,把这两个预定义符号和条件汇编语句相结合,就可编写出适应不同存储模式的汇编语言程序。第9章多模块程序设计与混合编

程【例9-11】试用汇编语言编写供TC调用的子函数sort实现升序排序功能的程序,要求sort函数实现综合考虑各种存储模式的功能。sort函数在TC源程序中声明如下:/*程序名:callsort.c*//*功能:调用sort子函数实现对

数组的排序*/externint*sort(int*,int);/*参数是数组指针及元素个数*/main(){inti,arr[10];int*p;第9章多模块程序设计与混合编程printf(''\nBeforesorting:\n'')

;for(i=0;i<10;i++){arr[i]=10?i;printf(''%d''rr[i]);}p=sort(arr,10);printf(''\nAftersorting:\n'');for(i=0;i<10;i++)printf(''%d'

',*(p+i));}第9章多模块程序设计与混合编程根据C语言调用协议入口参数的传递规则,callsort.c程序在不同的存储模式下调用sort子函数时,入口参数的长度及其在堆栈中的位置会发生相应的变化,如图9.6所示。第9章多模块程序设计

与混合编程图9.6不同存储模式下callsort.c程序调用sort函数时堆栈参数的变化情况BP系统堆栈变化BP+4BP+6BP+2BP的原始内容数组元素个数IP(返回地址)数组指针偏移量BP+4BP+6BP+8BP+2BP的原始内容数组元素个数IP(返回地址)数组指

针偏移量BPCS(返回地址)BP+4BP+6BP+8BP+2BP的原始内容数组元素个数IP(返回地址)数组指针偏移量BP数组指针段值BP+4BP+6BP+10BP+2BP的原始内容数组指针个数IP(返

回地址)数组指针偏移量BPCS(返回地址)数组元素段值低地址端高地址端堆栈生长方向(a)小模式时(b)中模式时(c)紧凑模式时(d)大模式时BP+8第9章多模块程序设计与混合编程在汇编语言程序中实现sort函数的关键是正

确获得入口参数,即数组指针和元素个数。在完成排序后还要正确设置返回值,即数组指针。根据图9.6可以得出如下结论:在小、紧凑模式下是NEAR型段内调用,因此返回地址是单字的IP;在中、大模式下是FAR型段间调用

,因此返回地址是双字的CS:IP。这两种情况可以根据预定义符号@CODESIZE来区分。第9章多模块程序设计与混合编程在小、中模式下是NEAR型数据指针,因此堆栈中只有指针的段内偏移量,在函数返回时只需将指针的段内偏移量放在AX即可;在紧凑

、大模式下是FAR型指针,因此堆栈中包括指针的段值和段内偏移量,函数返回时也应将FAR型指针放入DX:AX中。这两种情况可以根据预定义符号@DATASIZE来区分。第9章多模块程序设计与混合编程sort函数汇编语言程序如下:;程序名:asm9_9.asm;功能:使用冒泡法对

数组进行排序,按升序排列;特点:修改.MODEL语句即可适应不同的存储模式,不需改动程序实现部分.MODELXXXXXX;根据需要确定存储模式CODESTKSTRUC;定义一个反映堆栈内容的结构数据类型SAVEBP

DW?;保存BP寄存器的原始内容第9章多模块程序设计与混合编程IF@CODESIZEEQ0RETADDRDW?;小、紧凑模式下采用段内调用ELSERETADDRDD?;中、大模式下采用段间调用ENDIFIF@DATAS

IZEEQ0ARRADDRDW?;小、中模式下NEAR型数据指针,单字ELSEARRADDRDD?;紧凑、大模式下FAR型数据指针,双字第9章多模块程序设计与混合编程ENDIFNUMDW?;数组元素个数STKENDSPUBLIC_sort_sor

tPROCPUSHBPMOVBP,SPPUSHSIIF@DATASIZEEQ0第9章多模块程序设计与混合编程MOVSI,[BP].ARRADDR;NEAR型数据指针,数组起始地址送SIELSEPUSHDS;FAR型数据指针需要改变DS,先保存DS

LDSSI,[BP].ARRADDR;数组起始地址段值送DS,段内偏移量送SIENDIFMOVCX,[BP].NUM;获得数组元素个数DECCX第9章多模块程序设计与混合编程BEGIN:PUSHCXPUSHSIXORBL,BL;交换标志清

零COMP:MOVAX,[SI]CMPAX,[SI+2]JLENEXTXCHGAX,[SI+2];不满足升序要求则交换MOV[SI],AX第9章多模块程序设计与混合编程MOVBL,0FFH;有交换发生,置交换标志N

EXT:ADDSI,2LOOPCOMP;执行一遍扫描POPSI;恢复SI指向数组起始单元POPCX;恢复CX为一次扫描循环比较的次数CMPBL,0JNEBEGIN;交换标志发生变化,说明本次扫描发生了交换第9章多模块程序设计与

混合编程OK:IF@DATASIZEEQU1;如果是多个数据/堆栈段MOVDX,DS;返回值是FAR型数据指针,段值送DXPOPDS;函数返回前要恢复DS的原始内容ENDIFMOVAX,SI;数据指针的段内偏移量送AX返回第9章多模块程序设计与混合编程POPSIPOPBPRET_sortE

NDPEND第9章多模块程序设计与混合编程当C语言程序callsort.c存储模式改变时,只需修改汇编程序asm9_9.asm中的.MODEL语句并重新编译即可。汇编程序asm9_9.asm中使用了一个结构STK,通过STK结构可以反映出执行MOVBP,SP后堆栈中的内容。声明STK结构只是为了

方便地访问堆栈中的各个参数,并没有真正给STK分配存储空间。STK结构根据预定义符号@CODESIZE和@DATASIZE的取值,综合考虑了不同存储模式下堆栈的内容变化情况,使程序在不同的存储模式下都可正确地获取入口参数。程

序实现部分也根据@DATASIZE的不同取值,对入口参数和返回值做出不同的处理。第9章多模块程序设计与混合编程此外,还可以通过TC的库函数管理工具tlib.exe把一些通用性较强的汇编语言子程序添加到TC的库函数

中,扩展TC库函数的功能。下面以sort函数为例说明添加过程。在TC的安装目录下有一个lib子目录是用来存放TC库文件的,其中的cx.lib(x可以替换为s、m、c、l和h之一,分别对应小、中、紧凑、大和巨存储模式)就是C语言源程序在编译连接形成可执行文

件时必须连接的库文件,每个库文件对应一种存储模式。想在哪种存储模式下使用sort函数就需要把sort函数添加到对应的库文件中,添加的具体步骤如下:第9章多模块程序设计与混合编程(1)修改asm9_9.asm中的.MODEL语句,使其

存储模式与要添加的库文件相对应;(2)重新汇编asm9_9.asm,形成目标文件asm9_9.obj;(3)利用tlib程序将asm9_9.obj添加到cx.lib库文件中,命令为tlibcx.lib+asm9_9.obj其中,x等于s、m、c、l和h之一,以对

应asm9_9.asm中.MODEL语句的存储模式;第9章多模块程序设计与混合编程(4)重复步骤(1)、(2)、(3)可将sort函数的实现分别添加到不同存储模式的库文件中;(5)在TC安装目录下的include子目录中创建一个sort.h文件,声明sort函数:externint*sort

(int*,int);以后在C语言源程序中只需包含sort.h头文件即可实现对sort函数的调用,省去了分别编译连接的繁琐步骤。这样一来,就可以直接在TC的IDE环境中运行callmax.c了。第9章

多模块程序设计与混合编程2)汇编语言引用TC子函数和变量汇编语言中引用TC子函数和变量,相对来说要简单些。因为所有的C函数和全局变量都被自动声明为PUBLIC,可以方便地供外部使用。只是汇编语言程序中需要使用EXTRN伪指令对用到的C函

数和变量名加以声明。其声明格式为EXTRN函数名:函数类型EXTRN变量名:变量类型第9章多模块程序设计与混合编程其中,“函数类型”可以是NEAR型或者FAR型,“变量类型”按照其占用空间大小可以是BYTE,WORD,DWORD,QWORD或者TBYTE。注意,声明的类型应该

一致。对于数组,变量类型由其数组元素的大小决定。对于结构体和联合体,变量类型由其最大域的变量的大小决定。例如,为引用C语言中如下定义的全局变量和子函数,第9章多模块程序设计与混合编程charc;inti,arr[100];longl;intfunc(inta,intb);在汇编语言中,

应有如下声明:EXTRN_c:BYTE,_i:WORD,_arr:WORD,_l:DWORDEXTRN_func:NEAR;小模式下第9章多模块程序设计与混合编程下面给出汇编语言调用TC子程序的主要步骤。(1)在TC编程方面汇编语言调用TC子

程序的主要步骤。①定义供汇编语言程序引用的全局变量;②声明和实现供汇编语言程序引用的子函数;③编译生成目标文件;第9章多模块程序设计与混合编程(2)在汇编语言编程方面汇编语言调用TC子程序的主要步骤。①用EXT

RN伪指令声明TC子函数和变量;②按照C语言调用协议将调用参数压入堆栈;③使用CALL指令调用TC的子函数;④从AX或者DX:AX中取得返回值;⑤修改SP寄存器的值,将调用参数清除出栈;⑥使用汇编程序MASM汇编源程序形成目标文件;⑦使用连接程序LINK连接汇编语言和C语言的目标文

件形成可执行文件;⑧执行程序,进行验证和调试。第9章多模块程序设计与混合编程【例9-12】汇编语言程序调用TC子程序。TC子函数max返回三个整型数中的最大数,其入口参数为三个整型指针变量:/*程序名:max.c*//*功能:返回三个整数中的最大数,在小模式下编译*/intmax(i

nt*a,int*b,int*c){intr;第9章多模块程序设计与混合编程if(*a>*b)if(*a>*c)r=*a;elser=*c;elseif(*b>*c)r=*b;elser=*c;return(

r);}第9章多模块程序设计与混合编程汇编语言程序asm9_10.asm调用max子函数完成比较功能:;程序名:asm9_10.asm;功能:演示汇编语言调用TC子程序的过程.MODELSMALLEXTRN_max:NEAR.DATAADW

0031HBDW0032HCDW0033HRESULTDW?第9章多模块程序设计与混合编程.CODESTART:LEAAX,C;压入参数3的近指针PUSHAXLEAAX,B;压入参数2的近指针PUSHAXLEAAX,A;压入参数1的近指针PUSHAXCAL

Lmax;调用TC子函数MOVRESULT,AX;取得返回值ADDSP,6;将入口参数弹出堆栈第9章多模块程序设计与混合编程MOVDX,RESULTMOVAH,2;调用显示输出功能,显示比较结果INT21HMOVAH,4CH;返回DOSINT21HENDSTART第9章多模块程序

设计与混合编程C和汇编语言源程序编辑完毕存盘后,在DOS提示符下输入如下命令:Tcc-ms-cmax;(按小模式编译max.c,只生成目标文件max.obj)masmasm9_10;(汇编asm9_10.asm,生成目标文件asm9_10.obj)linkasm9_10+max;(连接两

个目标文件,生成asm9_10.exe)asm9_10;(运行asm9_10.exe获得结果)第9章多模块程序设计与混合编程汇编语言程序调用TC子程序时要注意如下四点。(1)入口参数应该按照C语言调用协议的规定自右

向左地压入堆栈。(2)TC编译程序在函数和变量名前自动加下划线“_”,所以在调用时应使用“CALL_max”指令而不是“CALLmax”指令。(3)TC子函数在返主时不会从堆栈中弹出入口参数,所以在调用结束取得返回值后,由汇编语言主程序完成入口

参数的出栈。(4)汇编语言主程序和TC子程序应采用一致的存储模式,注意调用时参数入栈和出栈的变化。第9章多模块程序设计与混合编程例如,要求例9-12中的max函数在大模式下编译,则需要修改汇编语言主程序中以下四点。(1)存储模式修改为Large模式。

(2)_max声明为FAR型。(3)压入堆栈的不再是NEAR型指针,而是FAR型指针。(4)修改SP的值由ADDSP,6相应地变为ADDSP,12。第9章多模块程序设计与混合编程程序修改后应重新编译和连接,注意此时应使用大模式编译max.c:tcc-ml-cmax其他命令及

操作步骤和小模式下相同。第9章多模块程序设计与混合编程2.伪变量法伪变量是在TC中预定义的一些标识符。伪变量看似变量,实际上并不是真正的变量,因为TC并没有给它们分配内存空间,而是按照伪变量的名字直接访问CPU内部

寄存器。可以通过伪变量名访问的寄存器包括AL、AH、BL、BH、CL、CH、DL、DH、AX、BX、CX、DX、SI、DI、BP、SP、CS、DS、ES和SS。TC定义的伪全局变量就是在这些寄存器名字前加一个下划线“_”,如_AX。根据寄存器的长度是8位还是16位,相应的伪变量类型为

无符号字符型或者无符号整型。注意:不能使用32位寄存器,如EAX。第9章多模块程序设计与混合编程【例9-13】可以使用伪变量对例9-12中的max程序做如下改进。intmax(int*a,int*bint*c){_BX=*a;_CX=*b;_DX=*c;if(_BX>_CX)if(_BX>

_DX)_AX=_BX;else_AX=_DX;else第9章多模块程序设计与混合编程if(_CX>_DX)_AX=_CX;else_AX=_DX;return(_AX);/*此语句可以省略。因为返回值本来就在AX中*/}需要注意的是,伪变量是在寄存器中存放的,而寄存器

是频繁使用的,所以在程序中给伪变量赋值后能保留多久是不确定的。如果通过伪变量强行修改某个寄存器,比如说CS的值,可能引起意想不到的结果。建议在程序中只使用通用寄存器伪变量。第9章多模块程序设计与混合编程3.行内汇编法行内汇编是指在C语言源程序

中直接嵌入汇编语言程序行,实现相应的功能。行内汇编语句格式如下:asm<操作码><操作数><;或者换行符>其中,“操作码”可以是任何一条有效的8086指令或者汇编语言伪指令,“操作数”可以是CPU内部寄存器

或者C语言源程序中定义的变量、常量和标号。注意:只能使用8086指令。第9章多模块程序设计与混合编程行内汇编语句结束的方法有两种:一个是像普通的C语句一样以分号“;”结束;也可以像汇编语言指令一样直接以换行符结束。在

同一文本行内可有多条行内汇编语句,这时语句间必须以分号“;”分隔,但是一条行内汇编语句不能被分割为多行文本。还要注意不能再使用分号“;”来表示汇编注释的开始,注释应采用C语言的标准/*……*/来表示。第9章多模

块程序设计与混合编程【例9-14】将TC子函数max用行内汇编改写。已知其C语言定义如下:intmax(inta,intb){if(a>b)return(a);elsereturn(b);}第9章多模块程序

设计与混合编程max子函数可用行内汇编改写为:intmax(inta,intb){asmmovax,aasmcmpax,basmjgeokasmmovax,bok:return(_AX);/*本语句可以省略,因为返回值已经在AX中*/}第9章多模块程序设计与混合编程行内

汇编是宏汇编的子集,有很多的宏汇编命令这里并不支持,例如,所有的宏指令、所有的段指令和很多的伪指令。这需要编程人员在开发应用程序时加以注意。行内汇编最大的优点就是不需要程序员去考虑汇编与TC间的编程接口,如调用协议和存储模式等,只是在C语言的源程序

中嵌入汇编程序行,TC的编译程序自动处理这些汇编程序行。如果使用模块连接法以单独的汇编语言模块编写max函数,那么就需要根据C语言主调函数的调用协议和存储模式来做不同的修改,工作就变得繁琐了。第9章多模块程序设计与混合编程嵌入汇编程序行

的C语言源程序不能直接在TC的集成开发环境(IDE)下编译、连接和运行,而只能在命令行方式下使用TCC编译和TLINK连接。在使用TCC对嵌入汇编行的C语言源程序进行编译时需要加-B选项,否则TCC编译器在遇到行内汇编时报错并自动加-B选项重新启动运行。在源程序中加入一行#pragmai

nline语句也可告诉TCC编译器需要行内汇编,这时不需要-B选项。第9章多模块程序设计与混合编程TC的行内汇编需要汇编程序TASM的支持,当编译行内汇编语句的C语言源程序时TCC编译器不再直接生成目标文件,而是首先生成一个汇编语言源文件,然后调用T

ASM对该文件进行汇编,进而生成目标文件。第9章多模块程序设计与混合编程在嵌入汇编行中出现的C语言标识符,C语言编译器在编译时自动在标识符前加下划线并转变为相应的汇编语言操作数。所有的C语言标识符都可以合法使

用,包括自动变量、寄存器变量和函数参数。其中使用寄存器变量时要注意TC总是使用SI和DI实现寄存器变量,如果在C的源程序中已经定义了寄存器变量,那么就不要在嵌入的汇编语句中再修改SI和DI,否则会破坏寄存器变量的值。第9章多模块程序设计与混合编程行内汇编语句的操作数不

仅限于简单变量,还可以是构造型变量,如数组元素、结构体变量等。对构造型变量的引用方法有两种:一种方法是像在C语言中一样进行直接引用;另外一种方法就是先将构造型变量的起始地址送到某个寄存器中,再通过该寄存器间接

访问。例9-15说明了如何在行内汇编语句中引用构造型变量。第9章多模块程序设计与混合编程【例9-15】在行内汇编语句中引用构造型变量。main(){intarr[10];structS{charch;inti;}s;asmmovax,1

0asmmovarr[0],ax/*等价于arr[0]=10;*/asmleabx,a[0]asmmovwordptr[bx][3*2],20/*等价于arr[3]=20;*/第9章多模块程序设计与混合编程asmmovdl,'a'asmmovs.ch,dl/*

等价于s.ch='a';*/asmleasi,s.chasmmov[si].i,ax/*等价于s.i=10;*/printf(''\narr[0]=%d,arr[3]=%d'',arr[0],arr[3]);printf(''\ns.c

h=%c,s.i=%d'',s.ch,s.i);}第9章多模块程序设计与混合编程因为TC允许在不同的结构体变量中定义相同名字的域变量,所以在出现类似[si].i这样的引用时就无法判断是按照哪一种结构体变量的定义来访问内存了。为避免这种情况的发生,TC规定,

如果多个结构体中存在相同名字的域变量,那么在引用域变量时要在圆点运算符“.”和域变量名之间加上强制的结构类型说明。如例9-9中对si的引用就应该写成asmmov[si].(structsi),ax第9章多模块程序设计与混合编程行内汇编语句可以使用跳转指令,

跳转分直接和间接跳转。直接跳转只能跳转到函数内某个标号处,且只能近跳转。注意:行内汇编语句不能使用标号,只能使用C语言语句定义标号。如在例9-14中的“ok:”一行改为“asmok:”在编译时就会出错。间接跳转通过寄存器或者变量实现,可以近跳转也可

以远跳转,必要时使用跳转类型说明。例如:asmjmpwordptr[bx]/*近跳转*/asmjmpdwordptr[bx]/*远跳转*/第9章多模块程序设计与混合编程9.3.3汇编语言与PASCAL语言的接口PASCAL语言简单易学,源程序可读性好,便于编程和调试。最为重要的是P

ASCAL语言系统地体现了结构化程序设计思想,能够清晰地以分层结构表达数据和算法。因此PASCAL语言经常被用做结构化程序设计的教学语言,是世界上广泛使用的高级语言之一。PASCAL语言最大的缺点是控制硬件工作的能力不足,因此编写供PA

SCAL语言调用的汇编语言程序来扩展PASCAL语言的功能显得尤为重要。第9章多模块程序设计与混合编程本节以TurboPASCAL5.0(简称TP)为例介绍汇编语言与PASCAL语言的接口,但是所有例程都可以供更高版本的TurboPasca

l使用。一般来讲,汇编语言程序是作为PASCAL语言程序的一个外部子程序来供其调用的,所以在汇编语言程序中应该以PUBLIC伪指令声明相应的子程序和变量。相应地,在PASCAL语言源程序中应该使用external保留关键字声明汇编语言子程序为外部子程序,同时使用$L语句说明该外部子程序所在的

目标文件。这样,在PASCAL语言源程序中就可以像调用PASCAL语言标准子程序一样地调用汇编语言程序了。第9章多模块程序设计与混合编程TP不像TC那样可在多种存储模式下工作,TP只有一种存储模式就是紧凑模式(C

ompact)。因此,TP中的子程序调用为NEAR型调用,但是数据指针为FAR型指针。在编写汇编语言子程序时一定要注意TP的存储模式,以便正确定义子程序类型并且以正确的方式获得入口参数。可供TP调用的汇编语言子程序不能采用简化的段定义方式,必须以完全段定义方式书写,否则T

P连接程序无法正确连接PASCAL语言目标文件和汇编语言目标文件。第9章多模块程序设计与混合编程PASCAL语言和汇编语言混合编程的步骤如下:(1)在汇编语言编程方面PASCAL语言和汇编语言混合编程的步骤。①在汇编语言源程序中,用PUBLIC伪指令声明TP需要引用的子程序;②按照

PASCAL语言调用协议从堆栈中取得各类入口参数;③对参数进行处理,实现相应的功能;④将返回值送入AX或者DX:AX中;⑤使用带立即数的RETn指令返回,其中n的值为子程序入口参数的总字节数;第9章多

模块程序设计与混合编程⑥使用汇编程序MASM汇编源程序形成目标文件。(2)在TP编程方面PASCAL语言和汇编语言混合编程的步骤。①在TP源程序中用external语句声明汇编语言子程序;②用$L语句声明外部子程序所在的目标文件;③像引用标准子

程序一样在程序中引用汇编语言子程序;④编译源程序形成目标文件;第9章多模块程序设计与混合编程⑤连接PASCAL语言和汇编语言的目标文件,形成可执行文件;⑥执行程序,进行验证和调试。其中,步骤③、④和⑤可以在TP的集成开发环境(IDE)中通过菜单

命令Run一次完成,不需要在DOS提示符下以命令行方式进行。第9章多模块程序设计与混合编程【例9-16】编写供PASCAL语言程序调用的子函数max。已知其在PASCAL语言程序中有如下声明和引用:/*程序名:cmax.pas*//*

功能:声明并引用以汇编语言编写的外部子函数max*/Programcmax(output);functionmax(z,y:integer):integer;external;{$Lasm9_9.obj}第9章多模块程序设计与混合编程vara,b:integer;begina

:=123;b:=456;writeln('a=',a,'b=',b);writeln('Callmax(a,b),theresultis',max(a,b));end.第9章多模块程序设计与混合编程则实现max子函数的汇编语言程序如下:;

程序名:asm9_13.asm;功能:实现供PASCAL语言程序调用的子函数maxCODESEGMENTBYTEPUBLICASSUMECS:CODEPUBLICMAXMAXPROCNEARPUSHBPMOVBP,SP;保护BP寄存器并使之指向当前栈顶第9章多模块程序设计与混合编程

MOVAX,[BP+4];获得第二个入口参数yCMPAX,[BP+6];与第一个入口参数x比较JGEOKMOVAX,[BP+6];x>y,则返回值等于xOK:POPBP;恢复BPRET4;返回主程序并修正栈顶指

针,丢弃入口参数第9章多模块程序设计与混合编程MAXENDPCODEENDSEND汇编语言源程序asm9_9.asm编辑完毕存盘后,在DOS提示符下输入如下命令:masmasm9_13;(汇编asm9_13.asm,生成目标文件asm9_13.obj)第9章多模块程序设计与混合编程然后就可以在TP

的IDE环境中编辑cmax.pas源程序,并且使用Run菜单命令运行,则运行结果为a=123b=456Callmax(a,b),theresultis456从运行结果可以看出,用汇编语言实现的max函数可以正确完成所要求的

比较功能。第9章多模块程序设计与混合编程对例9-16,还有以下六点说明:(1)在TP源程序中对外部子程序的声明可以采用cmax.pas中的那种形式,也可以采用如下形式:functionmax(x,y:word):word;external;也就是说入口参数和返回值类型还可以采

用汇编语言的类型说明。(2)TP程序的子程序调用采用PASCAL语言调用协议,即子程序入口参数按照自左向右的顺序压入堆栈。cmax.pas调用max子函数时的堆栈示意图如图9.7所示。第9章多模块程序设计与混合编

程图9.7cmax.pas程序中调用max函数时堆栈入口参数变化情况IP(返回地址)456(b的值)123(a的值)SP堆栈生长方向BP的原始内容456(b的值)123(a的值)SP,BP低地址端高地址端堆栈生长方向IP(返回

地址)(a)cmax调用max时(b)max执行MOVBP,SP后系统堆栈变化系统堆栈变化BP+2BP+4BP+6第9章多模块程序设计与混合编程(3)TP程序中的{$Lasm9_13.obj}语句表示要和asm9_13.obj目标文件连接。asm9_13.obj就是实

现max子函数的目标文件。(4)TP的存储模式采用紧凑型模式,因此对max子函数的调用为NEAR型近调用,所以调用时堆栈只保存断点的IP,而不需要保存CS。(5)按照TP语法,max子函数的入口参数x和y是采用值传递的值形参,也就是说,在调用max子函

数时压入堆栈的是实际参数a和b的数值而不是其地址。第9章多模块程序设计与混合编程(6)PASCAL语言调用协议规定由子程序完成入口参数的出栈。在汇编语言程序返回时,必须使用带有立即数的返回指令RETn,其中n的值为所有入口参数的字节总数。在例9

-16中,n=4。如果PASCAL中声明子程序为无参,就不需要使用RETn指令了,直接用RET返回主程序即可。在PASCAL语言的子程序调用中,入口参数除了采用值传递的值形参外,还可采用地址传递的变量形参,此时在子程序

调用时压入堆栈的不再是实际参数的数值而是其地址了。例9-17演示如何在汇编语言程序中处理TP的变量形参的方式。第9章多模块程序设计与混合编程【例9-17】编写供PASCAL语言程序调用的过程maxch,要

求实现对参数x和y的比较并保证返回时x≥y。已知其在PASCAL语言程序中有如下声明和引用:/*程序名:cmaxch.pas*//*功能:声明并引用以汇编语言编写的外部过程maxch*/programcmax(output);pr

oceduremaxch(varx:Integer;vary:Integer);external;{$Lasm9-10.obj}第9章多模块程序设计与混合编程a,b:integer;begina:=123;b:=456;writeln('a=',a,'

b=',b);maxch(a,b);writeln('Aftercallingmaxch(a,b):');writeln('a=',a,'b=',b);end第9章多模块程序设计与混合编程maxch子程序不带返回值,因此被声明为过程。实现maxch

过程的汇编语言程序如下:;程序名:asm9_14.asm;功能:实现供PASCAL语言程序调用的过程maxch,完成对变量形参的控制CODESEGMENTBYTEPUBLICASSUMECS:CODEPUBL

ICMAXCHMAXCHPROCNEAR第9章多模块程序设计与混合编程PUSHBPMOVBP,SPLDSBX,[BP+4];AX=yMOVAX,[BX]MOVBX,[BP+8];[BX]=xCMPAX,[BX];比较x,y的大小JLEOK;x≥y,不需要交换XCHGAX,[BX];x<y,则交换

其内容MOVBX,[BP+4]第9章多模块程序设计与混合编程MOV[BX],AXOK:POPBPRET8;返回,弹出两个入口参数的远指针MAXCHENDPCODEENDSEND第9章多模块程序设计与混合编程汇编语言源程序asm9_14.asm编辑完毕存盘后,在DOS

提示符下输入如下命令:masmasm9_14;(汇编asm9_14.asm,生成目标文件asm9_14.obj)然后就可以在TP的IDE环境中编辑cmaxch.pas源程序,并且使用Run菜单命令运行了。运行结果为:a=123b=456A

ftercallingmaxch(a,b);A=456b=123第9章多模块程序设计与混合编程从运行结果可以看出,用汇编语言实现的maxch过程可以正确完成所要求的比较和交换功能。例9-17中的PASCAL程序使用了变量形参,因此在参数入栈时传递的是实际参

数的地址而不再是数值了。由于TP采用紧凑模式,因而所有的数据指针都是FAR型远指针。图9.8说明了cmaxch.pas调用maxch过程中系统堆栈内容的变化。第9章多模块程序设计与混合编程图9.8cmaxch.pas程序中调用maxch函数时堆栈入口参数变化情况IP(返回地址)a的段内偏移

量a的段地址SP堆栈生长方向BP的原始内容a的段内偏移量a的段地址SP,BP低地址端高地址端堆栈生长方向IP(返回地址)(a)cmaxch调用maxch时(b)maxch执行MOVBP,SP后系统堆栈变化系统堆栈变化BP+6BP+8BP+10b的段地址b的段地址BP+4b的段内偏移量

b的段内偏移量BP+2第9章多模块程序设计与混合编程PASCAL语法规定数组不能作为子程序的入口参数,这样在处理一些有关数组的问题时就无法直接使用PASCAL语言来解决了。那么能否用汇编语言解决这个问题呢?我们用例9-18来解决这个问题。第9章多模块程序设

计与混合编程【例9-18】编写供PASCAL语言程序调用的过程sort,要求实现对给定元素个数的数组进行降序排列。已知其在PASCAL语言程序中有如下声明和引用:/*程序名:csort.pas*//*功能:声明并引用以汇编语言编写的外部过程so

rt实现数组排序*/programcsort(output);vararr:array[1..10]ofinteger;i:integer;第9章多模块程序设计与混合编程proceduresort(vara:integer;c:

integer);external;{$Lasm9_11.obj}beginwriteln('Beforesorting,arrayARRis:');fori:=1to10dobeginarr[i]:=i;writ

eln(arr[i],'');end;writeln;第9章多模块程序设计与混合编程sort(arr[1],10);writeln('Aftersorting,arrayARRis:');fori:=1to10dowrite(arr[i],'');e

nd第9章多模块程序设计与混合编程因为TP中没有取数组起始地址的操作,所以在声明外部过程soft时对数组参数使用一个变量形参,在调用时将数组第一个元素的地址作为参数传递,间接地实现了取得数组起始地址的

目的。实现SORT过程的汇编语言程序如下:;程序名:asm9_18.asm;功能:实现SORT过程的要求完成对数组元素的降序排列第9章多模块程序设计与混合编程CODESEGMENTBYTEPUBLICASSUMECS:CODEPUBLICSORTSORTPRO

CNEARPUSHBPMOVBP,SPPUSHDSPUSHSIMOVCX,[BP+4];取得数组元素个数第9章多模块程序设计与混合编程LDSSI,[BP+6];将数组第一个元素的地址远指针送入DS:SIDECCX;一次扫描的比较次数BEG

IN:PUSHCXPUSHSIXORBL,BL;置交换标志COMP:MOVAX,[SI]CMPAX,[SI+2]第9章多模块程序设计与混合编程JGENEXTXCHGAX,[SI+2];不满足降序要求就交换MOV

[SI],AXMOVBL,0FFHNEXT:ADDSI,2LOOPCOMP;LOOP循环,完成一遍扫描POPSIPOPCX第9章多模块程序设计与混合编程CMPBL,0JNEBEGIN;若本次扫描有交换发生就要再进行一次扫描OK:POPS

I;恢复寄存器的原始内容POPDSPOPBPRET6;清除入栈参数并返回主程序SORTENDPCODEENDSEND第9章多模块程序设计与混合编程汇编语言源程序asm9_18.asm编辑完毕存盘后,在DOS提示符下输入如下命令:masmasm9_18;(汇编

asm9_18.asm,生成目标文件asm9_18.obj)然后就可以在TP的IDE环境中编辑csort.pas源程序,并且使用Run菜单命令运行了。运行结果为Beforesorting,arrayARRis:12345678910Aftersorting,arrayARRis:109876543

21第9章多模块程序设计与混合编程从运行结果可以看出,用汇编语言实现的SORT过程可以正确完成所要求的排序功能。图9.9说明了csort.pas调用asm9_18.asm过程中系统堆栈的变化情况。例9-18中汇编语

言程序asm9_18.asm成功扩展了PASCAL的功能,实现了对数组的排序操作。为了解决PASCAL语言中数组名不能作为数组起始地址进行子程序调用的问题,通过变量形参把数组第一个元素的远指针压入堆栈,获得

了数组的起始地址。因为不需要改变数组元素个数,所以数组元素个数c采用值传送方式并被定义为值形参形式。第9章多模块程序设计与混合编程图9.9csort.pas程序中调用SORT函数时堆栈参数变化情况IP(返回地址)arr[1]段地址SP堆栈生长方向BP的原始内容arr[1]段地

址SP,BP低地址端高地址端堆栈生长方向IP(返回地址)(a)csort调用SORT时(b)SORT执行MOVBP,SP后系统堆栈变化系统堆栈变化BP+6BP+8arr[1]段内偏移量arr[1]段内偏移量BP+410(c的值)10(c的值)BP+2

第9章多模块程序设计与混合编程习题99.1编程实现两个字符串str1('Howareyou?')和str2('Iamfine,Thankyou!')的连接输出。要求分为两个程序模块编写,在模块1中实现str1串输

出,在模块2中实现str2串换行输出。9.2采用多模块编程实现数组排序。要求在主模块arr中设置数组ARR1(1,2,3,4,5)的参数传递给子模块,在子模块SORT中采用冒泡排序法实现ARR1的逆序排列并输出。第9章多模块程序设计与混合编程9.3有如下C语言声明的外部子程序

compare()完成字符串的比较功能,请编写汇编程序实现compare()的功能,在小模式(Small)下连接。externintcompare(char*str1,char*str2);其中,str1和str2是两个字符串,串结束标志为“/0”,从串首开始比较。如果str

1>stf2,则返回值为1;如果str1和str2相同,则返回值为0;如果str1<str2,则返回值为?1。第9章多模块程序设计与混合编程9.4回文是一种特殊的字符串,其特点是从头到尾读和从尾向头读的顺序完全相同,如“12321”,或“abcdcba”。编写汇编程序实现供C语言程序调用的外

部子程序ishuiwen(),其功能是判断给定字符串是否是回文,如果是回文,则返回值为1,否则为0。在中模式下连接,其C语言声明如下:externintishuiwen(char*str);其中,*str是字符串的起始地址。第9章多模块程序设计与混合编程9.5在大模式(Large)下

用汇编语言实现供C语言程序调用的画线子程序drawline(),已知其C语言声明如下:externvoiddrawline(intx1,inty1,intx2,inty2,intcolor);其中,x1,y1是线条起点坐标,x2,y2是终点坐标,c

olor是线条颜色。第9章多模块程序设计与混合编程9.6有如下声明的PASCAL子程序:functiontotal(varfirstone:integer,num:integer,vararrsum:integer):integer;externall;其中,firsto

ne是以变参形式传递的数组的第一个元素;num为数组元素个数;arrsum也是变参,用来保存total子程序计算数组所有元素的和。total同时带有一个返回值,若求和过程没有发生溢出则返回值为0,否则返回值为?1。请用汇编语言实现供TP调用的total子程序。

小橙橙
小橙橙
文档分享,欢迎浏览!
  • 文档 25747
  • 被下载 7
  • 被收藏 0
相关资源
广告代码123
若发现您的权益受到侵害,请立即联系客服,我们会尽快为您处理。侵权客服QQ:395972555 (支持时间:9:00-21:00) 公众号
Powered by 太赞文库
×
确认删除?