第5章多模块程序设计5.1汇编语言程序的多模块连接5.2结构与记录5.3汇编语言程序与高级语言程序的连接第5章多模块程序设计 返回主目录第5章多模块程序设计第5章多模块程序设计在第4章所介绍的程序设计中,源程序所涉及的标识符(变量、标号、段名、过程名等)都在本程序中定义,它与本程序之外的标识符不发生任何关系,这种程序设计称为单模块程序设计。本章将介绍多模块程序设计方法,也称为模块化程序设计方法。所谓模块,从功能上讲它可以是整个大程序的较为独立的一个部分,从源程序结构形式上讲它是由END结束的一个完整程序。因而一个源模块可以单独汇编,形成自己的目标模块。昀后,由连接程序将各个目标模块连接成一个可执行程序。采用模块化程序设计有下面一些优点 第5章多模块程序设计(1)一个复杂程序可以分成若干个模块,可由不同人员分头完成; (2)每个模块的任务明确,便于理解; (3)单个模块易于编写和调试; (4)便于程序的维护和修改; (5)可以直接利用已有的模块。 采用模块化程序设计时,必须合理分割模块,严格定义各模块的输入、输出参数及各模块间的通讯方式。 第5章多模块程序设计在单模块程序设计时,模块内所用到的段、变量及标号等各种标识符都必须在本模块内给予定义,否则汇编时将会给出错误信息。多模块程序设计时,由于各个模块都是整个程序的一个部分,因此,各模块之间不仅会有数据上的传递,而且会出现各模块间的变量、标号等标识符的交叉引用。如何实现这种交叉引用,如何实现各模块间各段的连接是汇编语言多模块程序设计的重要问题,也是本章要叙述的主要内容。此外,多程序模块的连接不仅适用于汇编语言的程序模块,也适用于汇编语言程序模块与高级语言的程序模块的连接。因此,本章还将介绍汇编语言与两种高级语言(BASIC和TURBO-C)之间连接的基本方法。 第5章多模块程序设计5.1汇编语言程序的多模块连接 5.1.1多模块之间段的连接 1.SEGMENT语句提供的连接信息 段定义的完整语句为: 段名SEGMENT[定位类型][组合类型][‘类别’] … 段名ENDS 在第4章中已指出,段定义后,段名有段地址、偏移地址、定位类型、组合类型和‘类别’5个属性。前3个属性在第4章中已作介绍,本节要介绍后两个属性,它们用来为连接过程提供多模块间段连接的有关信息。 第5章多模块程序设计1)组合类型 组合类型告诉汇编程序应为连接程序提供本段与其它段连接的有关信息,如本段与其它段是否组合为同一段;组合后,本段信息与其它段信息的关系如何,等等。为了提供这样一些信息,组合类型有如下6种不同的类型:NONE、PUBLIC、COMMON、AT表达式、STACK、MEMORY。段定义时,组合类型若被省略,隐含为NONE类型。 (1)NONE类型:表示本段与不同模块中的其它段在逻辑上不发生关系。连接后各模块中的各段都有自己的段地址(也称基地址)。 第5章多模块程序设计(2)PUBLIC类型:表明连接时,应把不同模块中属于该类型的同名同类别的段相继地连成一个段,其中所有的变量或标号都有相同的段地址。连接的顺序与LINK时用户所提供的各模块的顺序一致(本节的昀后将给出连接的基本方法)。各模块中属于PUBLIC类型的同名同类别的各段的总长度不能超过64KB。 (3)STACK类型:与PUBLIC类型同样处理,只是组合后的这个段用作堆栈。当段定义中指明了STACK类型后,说明堆栈段已经确定,所以,在可执行文件装入内存后段寄存器SS中已是该段的段地址,堆栈指针SP已指向堆栈底。这样,第4章例中为这两个寄存器传送初值的指令可以省去。第5章多模块程序设计(4)COMMON类型:表明连接时,应将不同模块中属于该类型的同名同类别的各段连接成一段,它们共用一个基地址,且互相覆盖。连接后,段的长度取决于昀长的COMMON段的长度。 (5)AT表达式类型:表明连接时,应将本段装在根据表达式求值得到的16位段地址上,表达式也可以是一个有效的常数。该类型可以将我们要定义的段设定在固定的地址范围内。必须注意,定义AT类型的段内不应包括任何指令语句或有初值的变量定义语句。但该段内允许设定标号或与标号有相同属性的过程定义语句或无初值的变量定义语句。它仅仅用来将该段指向内存区中的某个段,使该段的段名及段内的变量与指向的内存区的段地址有关。例如,若要用一个过程名SUB1代表BIOS中的某段子程序,我们可以这样定义一个段和过程:第5章多模块程序设计CODEBSEGMENTAT表达式1 ORGN SUB1PROCFAR SUB1ENDP CODEBENDS其中,表达式1的值即为某子程序所在段的段地址,N即为该子程序在段内的偏移地址。这样定义后,程序中调用过程名SUB1时,即调用BIOS中对应的子程序。其中也可以定义一个标号,并用ORG指定该标号的偏移地址,这样,该标号就与该段内的这个偏移地址相关。第5章多模块程序设计(6)MEMORY类型:表明连接时应把本段装在被连接的其它所有段之上(地址高端)。当有多个段为此类型时,只有汇编程序遇到的第1个段才认为是MEMORY段,而其它段则当作COMMON类型。 图5.1给出了不同模块中组合类型为PUBLIC和COMMON时的连接结果。图中两个模块的数据段都为COMMON类型,连接后,这两个段组合成一个段,并且互相覆盖,其长度取两个模块中段长度长的,即为第2模块的长度。由于COMMON类型的段组合后,相互覆盖,所以,只有不同模块采用公用缓冲区时才使用这种类型。第5章多模块程序设计图5.1中两个模块的代码段为PUBLIC类型,因此,连接后,两个模块的代码段也组合成一个段,但它们并不覆盖,而是两个代码段相邻地连接在一起,其顺序也与LINK时提供的目标模块的顺序一致。组合后段的长度应是两个代码段长度的和。图5.1中两个源模块中的数据段和代码段都没有给出类别,这也是允许的,但若某个模块中给出了类别而另一模块中不给出类别,那么,这两个模块中的同名段将不能组合成一个段,它们被认为不是同类别的段。模块1和模块2如下: 第5章多模块程序设计第5章多模块程序设计模块1: DATASEGMENTCOMMON DW20HDUP(?) DATAENDS CODESEGMENTPUBLIC … CODEENDS 第5章多模块程序设计模块2: DATESEGMENTCOMMON DW30HDUP(?) DATEENDS CODESEGMENTPUBLIC … CODEENDS第5章多模块程序设计2)‘类别’ 类别可以是任何一个合法的名称,但必须用单引号括起来。连接时,将把不同模块中相同‘类别”的各段在物理上相邻地连接在一起,其顺序亦与LINK时提供的各模块顺序一致。当‘类别’相同的各段的段名不同时,它们连接后虽在同一物理段内,但它们仍不属于同一段,也就是它们的段基址不相同。这样做的一个好处是便于程序的固化。在编程时,它们都是独立的代码段,各段有各自的段基地址,但连接后,它们却在同一物理段,从而可以固化在一起。第5章多模块程序设计2.GROUP伪指令 伪指令格式: 段组名GROUP段名[,段名,…] 格式中的段组名是用户自己定义的一个标识符,其定义规则与语句名的定义规则相同,格式中的段名是本模块中已定义的段的段名称。 GROUP伪指令提供了段的另一种组合形式。它把多个由SEGMENT语句定义的段组合在同一段组中,用一个段组名表示。同一段组内的各段将装在同一物理段内,它们有相同的段基址。同一段组内的段的数目不受限制,但总的字节数不能超过64K。同一段组内的各段的组合类型和类别可以不同。第5章多模块程序设计模块1: AGROUPGROUPCODE1,CODE2;定义了一个段组AGROUP CODE1SEGMENT ASSUMECS:AGROUP START: … CODE1ENDS CODE2SEGMENT ASSUMECS:AGROUP … CODE2ENDS ENDSTART 第5章多模块程序设计模块2: EXTRNVAR1:BYTE,VAR2:WORD,VAR5:WORD PUBLICEX1,VAR3 DATASEGMENT VAR3DW? … DATAENDS CODESEGMENTPUBLIC′CODE′ … EX1: … … CODEENDS END第5章多模块程序设计模块3: EXTRNVAR4:BYTE PUBLICVAR5,EX2 DATASEGMENT VAR5DW? … DATAENDS CODE3SEGMENT … EX2: … 〖DW] … CODE3ENDS END第5章多模块程序设计2.模块间交叉访问时的编程考虑 伪指令PUBLIC和EXTRN为不同模块之间的有关变量和标号建立了联系,从而使模块间的交叉访问成为可能。但是不同模块中引用同一个变量(或标号)时,它们的基地址必须一致,这是多模块程序设计时应考虑的主要问题,否则程序的运行就会出错。这里,基地址的一致有两层含义,一是每个变量(或标号)的偏移地址所对应的段的基地址应一致;二是在使用这些变量(或标号)时,段寄存器的内容必须是该变量(或标号)所对应的段地址。 我们首先说明第1个问题,即不同模块中交叉引用变量(或标号)时,变量的偏移地址对应的段基址是什么。一般地说,不同模块中的各段(代码段或数据段)在连接后不论是在同一段还是不在同一段,在不同模块中取某变量的偏移地址时,其段基地址都相对于变量所在段的段地址。第5章多模块程序设计如例5.1.2给出3个模块,汇编连接后有2个代码段(CODE和CODE1)和3个数据段(DATA1、DATA2和DATA3),各段中的变量(或标号)的偏移地址都是相对于它们所在段的段地址的,例如VAR1和VAR2的基地址为DATA1段的段地址,不论是在模块1还是在模块2中访问它们都是一样的。 但是有时也有不同的情况,例如当不同模块中的有些段连接后不在一个段内,而把有关伪指令EXTRN却放在了某个段内,此时就可能出现在不同模块中访问同一个变量,这个变量的偏移地址却是不同的。如例5.1.2中,若在模块1中将伪指令EXTRNVAR3:WORD放在DATA1段内,那么在模块1和模块3中访问VAR3时,第5章多模块程序设计其偏移地址就不同了,因为在模块1中VAR3的段基址是DATA1的段地址,而在模块3中VAR3的段基址是DATA2的段地址。也就是说,在这种情况下同一个变量(VAR3)在两个不同的模块中其段基址是不同的,从而它的偏移地址也不同,在访问这样的变量时就应特别注意。 例5.1.2#;模块1: EXTRNVAR2:WORD,SROUT:FAR EXTRNVAR3:WORD,VAR4:WORD 第5章多模块程序设计DATA1SEGMENTPUBLIC′DAT′ VAR1DW? DATA1ENDS CODESEGMENT ASSUMECS:CODE,DS:DATA1 START:MOVAX,DATA1 MOVDS,AX 〖DW2〗 … MOVAX,VAR2;VAR2的段地址在DS中 第5章多模块程序设计… MOVAX,SEGVAR3;ES中为VAR3的段地址 MOVES,AX MOVBX,ES:VAR3 … MOVAX,SEGVAR4 MOVES,AX MOVCX,ES:VAR4 … CALLFARPRTSROUT 第5章多模块程序设计 … CODEENDSSTART ENDSTART〖ZK)〗〖HT5SS〗 模块2: PUBLICVAR2 DATA1SEGMENTPUBLIC′DAT′ VAR2DW? … DATA1ENDS第5章多模块程序设计…模块3: PUBLICVAR3,VAR4 DATA2SEGMENT … VAR3DW? … DATA2ENDS DATA3SEGMENT … VAR4DW? 第5章多模块程序设计… DATA3ENDS PUBLICSROUT CODE1SEGMENT ASSUMECS:CODE1 SROUTPROCFAR … RET SROUTENDP COD