微型计算机原理与应用(电子信息类)第5章_第1页
微型计算机原理与应用(电子信息类)第5章_第2页
微型计算机原理与应用(电子信息类)第5章_第3页
微型计算机原理与应用(电子信息类)第5章_第4页
微型计算机原理与应用(电子信息类)第5章_第5页
已阅读5页,还剩146页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第5章 多模块程序设计5.1 汇编语言程序的多模块连接5.2 结构与记录5.3 汇编语言程序与高级语言程序的连接返回主目录第5章 多模块程序设计在第4章所介绍的程序设计中,源程序所涉及的标识符 (变量、标号、段名、过程名等)都在本程序中定义,它与本程 序之外的标识符不发生任何关系,这种程序设计称为单模块 程序设计。本章将介绍多模块程序设计方法,也称为模块化 程序设计方法。所谓模块,从功能上讲它可以是整个大程序 的较为独立的一个部分,从源程序结构形式上讲它是由END 结束的一个完整程序。因而一个源模块可以单独汇编,形成 自己的目标模块。最后,由连接程序将各个目标模块连接成 一个可执行程序。采用模

2、块化程序设计有下面一些优点(1) 一个复杂程序可以分成若干个模块,可由不同人员分 头完成;(2)每个模块的任务明确,便于理解;(3)单个模块易于编写和调试;(4)便于程序的维护和修改;(5)可以直接利用已有的模块。采用模块化程序设计时,必须合理分割模块,严格定义各 模块的输入、输出参数及各模块间的通讯方式。在单模块程序设计时,模块内所用到的段、变量及标号 等各种标识符都必须在本模块内给予定义,否则汇编时将会 给出错误信息。多模块程序设计时,由于各个模块都是整个 程序的一个部分,因此,各模块之间不仅会有数据上的传 递,而且会出现各模块间的变量、标号等标识符的交叉引用。如何实现这种交叉引用,如何实

3、现各模块间各段的连接是汇 编语言多模块程序设计的重要问题,也是本章要叙述的主要 内容。此外,多程序模块的连接不仅适用于汇编语言的程序 模块,也适用于汇编语言程序模块与高级语言的程序模块的连接。因此,本章还将介绍汇编语言与两种高级葡#ASIC 和TURBO - C)之间连接的基本方法。K15.1 汇编语言程序的多模块连接5.1.1 多模块之间段的连接1. SEGMENT语句提供的连接信息段定义的完整语句为:段名SEGMENT 定位类型组合类型'类别 段名ENDS在第4章中已指出,段定义后,段名有段地址、偏移地 址、定位类型、组合类型和'类别,5个属性。前3个属性在 第4章中已作介

4、绍,本节要介绍后两个属性,它们用来为连接 过程提供多模块间段连接的有关信息。1)组合类型组合类型告诉汇编程序应为连接程序提供本段与其它段连 接的有关信息,如本段与其它段是否组合为同一段;组合后, 本段信息与其它段信息的关系如何,等等。为了提供这样一些 信息,组合类型有如下6种不同的类型:NONE、PUBLIC> COMMON> AT表达式、STACK> MEMORYo段定义时,组 合类型若被省略,隐含为NONE类型。(1) NONE类型:表示本段与不同模块中的其它段在逻辑上 不发生关系。连接后各模块中的各段都有自己的段地址(也称基 地址)。(2) PUBLIC类型:表明连接时

5、,应把不同模块中属于该类 型的同名同类别的段相继地连成一个段,其中所有的变量或标 号都有相同的段地址。连接的顺序与LINK时用户所提供的各 模块的顺序一致(本节的最后将给出连接的基本方法)。各模块 中属于PUBLIC类型的同名同类别的各段的总长度不能超过64 KBo(3) STACK类型:与PUBLIC类型同样处理,只是组合后 的这个段用作堆栈。当段定义中指明了STACK类型后,说明 堆栈段已经确定,所以,在可执行文件装入内存后段寄存器SS 中已是该段的段地址,堆栈指针SP已指向堆栈底。这样,第4 章例中为这两个寄存器传送初值的指令可以省去。(4) COMMON类型:表明连接时,应将不同模块中

6、属于该 类型的同名同类别的各段连接成一段,它们共用一个基地址, 且互相覆盖。连接后,段的长度取决于最长的COMMON段的 长度。(5) AT表达式类型:表明连接时,应将本段装在根据表达 式求值得到的16位段地址上,表达式也可以是一个有效的常 数。该类型可以将我们要定义的段设定在固定的地址范围内。 必须注意,定义AT类型的段内不应包括任何指令语句或有初 值的变量定义语句。但该段内允许设定标号或与标号有相同属 性的过程定义语句或无初值的变量定义语句。它仅仅用来将该 段指向内存区中的某个段,使该段的段名及段内的变量与指向 的内存区的段地址有关。例如,若要用一个过程名SUB1代表 BIOS中的某段子程

7、序,我们可以这样定义一个段和过程:CODEBSEGMENTAT 表达式 1ORGNSUB1PROCFARSUB1ENDPCODEBENDS其中,表达式1的值即为某子程序所在段的段地址,N即 为该子程序在段内的偏移地址。这样定义后,程序中调用过程名SUB1时,即调用BIOS中 对应的子程序。其中也可以定义一个标号,并用ORG指定该标 号的偏移地址,这样,该标号就与该段内的这个偏移地址相关。(6) MEMORY类型:表明连接时应把本段装在被连接的 其它所有段之上(地址高端)。当有多个段为此类型时,只有 汇编程序遇到的第1个段才认为是MEMORY段,而其它段 则当作COMMON类型。图5.1给出了不

8、同模块中组合类型为PUBLIC和 COMMON时的连接结果。图中两个模块的数据段都为 COMMON类型,连接后,这两个段组合成一个段,并且互相 覆盖,其长度取两个模块中段长度长的,即为第2模块的长 度。由于COMMON类型的段组合后,相互覆盖,所以,只有 不同模块采用公用缓冲区时才使用这种类型。图5.1中两个模块的代码段为PUBLIC类型,因此,连 接后,两个模块的代码段也组合成一个段,但它们并不覆 盖,而是两个代码段相邻地连接在一起,其顺序也与LINK 时提供的目标模块的顺序一致。组合后段的长度应是两个 代码段长度的和。图5.1中两个源模块中的数据段和代码段 都没有给出类别,这也是允许的,但

9、若某个模块中给出了 类别而另一模块中不给出类别,那么,这两个模块中的同 名段将不能组合成一个段,它们被认为不是同类别的段。 模块1和模块2如下:德接后的蝴段再块2的代码盘>5.】PUBLIC和COMMON的述揆箝果模块1DATASEGMENT COMMONDW 20H DUP(?)DATAENDSCODESEGMENT PUBLICCODEENDS模块2:DATESEGMENT COMMONDW 30H DUP(?)DATEENDSCODESEGMENT PUBLICCODEENDS2)、类别,类别可以是任何一个合法的名称,但必须用单引号括起来。 连接时,将把不同模块中相同、类别的各段在

10、物理上相邻地 连接在一起,其顺序亦与LINK时提供的各模块顺序一致。当 、类别,相同的各段的段名不同时,它们连接后虽在同一物理 段内,但它们仍不属于同一段,也就是它们的段基址不相同。 这样做的一个好处是便于程序的固化。在编程时,它们都是独 立的代码段,各段有各自的段基地址,但连接后,它们却在同 一物理段,从而可以固化在一起。2. GROUP伪指令伪指令格式:段组名GROUP段名,段名,格式中的段组名是用户自己定义的一个标识符,其定义 规则与语句名的定义规则相同,格式中的段名是本模块中已 定义的段的段名称。GROUP伪指令提供了段的另一种组合形式。它把多个由 SEGMENT语句定义的段组合在同一

11、段组中,用一个段组名表 示。同一段组内的各段将装在同一物理段内,它们有相同的 段基址。同一段组内的段的数目不受限制,但总的字节数不 能超过64 Ko同一段组内的各段的组合类型和类别可以不同。模块1:定义了一个段组AGROUPGROUPCODE1 , CODE2 ;AGROUPCODE 1 SEGMENTASSUME CS: AGROUPSTART: CODEI ENDSCODE2SEGMENTASSUME CS: AGROUPCODE2ENDSEND STARTVAR5:模块2:EXTRNVAR1 : BYTE , VAR2 : WORD , WORDPUBLICEXl, VAR3DATASE

12、GMENTVAR3DW? DATAENDSCODESEGMENT PUBLIC' CODE' EXI: . CODEENDSEND模块3:EXTRNVAR4: BYTE PUBLIC VAR5, EX2 DATASEGMENT VAR5DW? DATAENDSCODE3SEGMENT EX2: .KDW .CODE3ENDSEND2.模块间交叉访问时的编程考虑伪指令PUB LIC和EXTRN为不同模块之间的有关变量和 标号建立了联系,从而使模块间的交叉访问成为可能。但是 不同模块中引用同一个变量(或标号)时,它们的基地址必须 一致,这是多模块程序设计时应考虑的主要问题,否则程序

13、 的运行就会出错。这里,基地址的一致有两层含义,一是每 个变量(或标号)的偏移地址所对应的段的基地址应一致;二 是在使用这些变量(或标号)时,段寄存器的内容必须是该变 量(或标号)所对应的段地址。我们首先说明第1个问题,即不同模块中交叉引用变量 (或标号)时,变量的偏移地址对应的段基址是什么。一般地 说,不同模块中的各段(代码段或数据段)在连接后不论是在 同一段还是不在同一段,在不同模块中取某变量的偏移地址 时,其段基地址都相对于变量所在段的段地址。如例5.1.2给出3个模块,汇编连接后有2个代码段(CODE和CODE1)和 3 个数据段(DATA1、DATA2和DATA3), 各段中的变量(

14、或标号)的偏移地址都是相对于它们所在段的段 地址的,例如VAR1和VAR2的基地址为DATA1段的段地 址,不论是在模块1还是在模块2中访问它们都是一样的。但是有时也有不同的情况,例如当不同模块中的有些段连 接后不在一个段内,而把有关伪指令EXTRN却放在了某个段 内,此时就可能出现在不同模块中访问同一个变量,这个变量 的偏移地址却是不同的。如例5.1.2中,若在模块1中将伪指 令EXTRN VAR3: WORD放在DATA1段内,那么在模块1和 模块3中访问VAR3时,其偏移地址就不同了,因为在模块1中VAR3的段基址 是DATA1的段地址,而在模块3中VAR3的段基址是DATA2 的段地址

15、。也就是说,在这种情况下同一个变量(VAR3)在两 个不同的模块中其段基址是不同的,从而它的偏移地址也不 同,在访问这样的变量时就应特别注意。例 5.1.2#;模块1:EXTRNVAR2: WORD, SROUT: FAREXTRNV AR3 : WORD, VAR4: WORDDATA 1 SEGMENT PUBLICzVAR1DW?DATA 1 ENDSCODESEGMENTASSUME CS: CODE, DS:START: MO VAX, DATA1DAT'DATA1MOV DS, AXKDW22 .MO VAX, VAR2; VAR2的段地址在DS中MOV AX, SEG V

16、AR3; ES中为VAR3的段地址MOVES, AXMOVBX, ES: VAR3MO VAX, SEG VAR4MOV ES, AXMO VCX, ES: VAR4CALLFAR PRT SROUTCODEENDS STARTEND START KZK)3 KHT5SS模块2:PUBLICVAR2DATA 1 SEGMENT PUBLIC 7 DATVAR2DW?DATA 1 ENDS模块3:PUBLICVAR3, VAR4DATA2SEGMENT VAR3DW? DATA2ENDSDATA3SEGMENTVAR4DW?DATA3ENDSPUBLICSROUTCODEI SEGMENTASS

17、UME CS: CODEISROUTPROC FAR RETSROUTENDPCODEI ENDSEND上面的第2个问题主要是段寄存器的管理问题。当不同 模块中的有关段连接后为同一段时,问题就十分简单,不论 在哪个模块中访问该段中的变量,段寄存器的内容都可以不 变,如例5.1.2中要访问DATA1段中的变量VAR2,不论在 模块1还是在模块2中它们的段地址是一样的,因此在这两 模块中DS中的内容不需改变。但当不同模块中的某些段连接 后不在同一段内时,必须注意段寄存器内容的正确设置,如 例5.1.2模块1中要访问变量VAR3或VAR4时,应首先取 出它们的段地址送入相应的段寄存器,由于变量VAR

18、3和 VAR4不在模块1中,取相应变量的段地址可有两种方法, 如例5.1.2和例5.1.3中所示。例 5.1.3#;模块1:CODATASEGMENT PUBLICBYTEEXTRNVAR1: WORD, VAR2:CODATAENDSDATASEGMENTDB? DATAENDSCODESEGMENTASSUME CS: CODE, DS: DATA, ES: CODATA START: MOV AX, DATA MOV DS, AX MOV AX, CODATAMOV ES, AXMOV BX, VARIMOV VAR2, DL CODEENDSEND START模块2:CODATASEG

19、MENT PUBLICPUBLIC VARI, VAR2VAR1DW?VAR2DB? CODATAENDSEND在例5.1.2的模块1中,我们是采用属性操作符(SEG)来 获取VAR3和VAR4的段地址的。在例5.1.3中给出了另一种 方法,因为模块1中要访问的变量VAR1和VAR2不在本模块 的DATA段中,为了取得这两个变量的段地址,我们在模块1 中定义了一个与模块2中同名的段CODATA。在这个段中只 定义了两个外部变量VAR1和VAR2,由于两个模块中的 COD AT A段的组合类型均为PUBLIC,所以连接以后它们是同 一段,这样在模块1中就可以直接取CODATA段的段地址了。 并且

20、可用ASSUME语句指出哪个段寄存器将指向该段(如例中 为ES),于是源程序中有关语句的段前缀也可以省略,如例中 的指令MOV BX, VARI中就省略了段前缀ES:(注意,该指 令的机器码中仍有段前缀字节)。3.完整的可执行文件的建立上面我们说明了多模块的源程序的设计方法和应注意的 问题。由于每个模块还只是整个完整程序的一部分,因此在 多个模块的源程序写好后我们还必须建立一个完整的可执行 文件。建立一个完整的可执行文件方法如下:(1)首先,将各源程序模块分别汇编,并建立各自的目 标模块(即建立各自的.OBJ文件)。(2)然后,用连接程序LINK将这些目标模块连接成一个 可执行文件。此时,在回

21、答连接程序询问目标文件的文件名 时,可以如下形式回答:模块1文件名+模块2文件名+模块3文件名+这里,一方面给出了要连接的各模块的目标文件的文件 名,同时也给出了各模块间的连接顺序。3)可执行文件的文件名有两种提供方式,一种方式是在 连接程序询问可执行文件的文件名时直接以回车回答,此 时,提供的第1个目标文件的文件名就是可执行文件的文件 名,另一种方式是在连接程序询问可执行文件的文件名时提 供一个指定的可执行文件的文件名。例如:C>LINKObject Modules .OBJ MFILE+SFILE1+SFILE2DWRun File MFILE.EXE : MFILE1Run Fil

22、e MFILE.EXE : MFILE1List File NUL.MAP:Libraries .LIB : ZK)HT5SS上述各行冒号以后的内容是LINK时回答的信息。第1行 的回答表示有3个目标模块要连接成一个可执行文件;第2行 的回答表示连接后的可执行文件名为MFILE1.EXE;第3行的 回答表示不需要建立列表文件;第4行回答表示没有专用的库 文件。若第2行回答的是一个回车(),即没有另指定文件 名,那么,连接后的可执行文件名将是MFILE .EXEo最后,我们还需指出,多个模块连接成一个可执行文件 后,该文件只有一个启动地址,所以,连接成一个完整程序 的各个源模块中只能有一个源模块

23、中的结束伪指令END可以 带表达式,以指出整个程序的启动地址。下面我们给出两个多模块程序设计的例子:例5.1.4给出了多模块程序的一个例子。程序的功能是 利用多个程序模块实现指定字符串的显示。程序中共有两个 模块,模块1为主程序模块(称主模块),模块2为子程序模 块(也称过程模块)。例 5.1.4模块1:STACKSEGMENT PARA STACK'DW 256 DUP(?)STACKENDSCODESEGMENT PUBLIC' CODE,EXTRNOUT -ROUTINE: NEAREXTRNOUT -CHAR: BYTEASSUMECS: CODESTACK'1

24、3, 10MESSEDB' THIS IS A ROUTINE.MAIN: CLDMOVAX, CSMOVDS, AXASSUMEDS: CODEMOVSL OFFSET MESSELOOP1: LODSBMOVOUT -CHAR, ALCALLOUT -ROUTINECMPOUT -CHAR, 10JNELOOP1MO V AH, 4CHINT21HCODEENDSEND MAIN模块2:CODESEGMENT PUBLIC ' CODE'ASSUMECS: CODE, DS: CODEPUBLICOUT -CHARPUBLICOUT -ROUTINEOUT -CHA

25、RDB?OUT -ROUTINEPROCNEARMOVDL, OUT -CHARMO V AH, 02INT21HRETOUT -ROUTINEENDPCODEENDSEND例5.1.4中主模块中要用到子模块中的过程OUT - ROUTINE和变量OUT-CHAR,因此,它们在主模块中为外 部变量,在子模块中为全局变量(如例中所示)。两个模块中 的代码段为同名同类别且组合类型均为PUBLIC,连接后为 同一段,因此过程调用为近调用。例中的变量都在代码段 中,因此段寄存器DS的内容在两个模块中也不需改变。请 考虑,若要求在连接后这两代码段不是同一段,那么程序 该作哪些修改,应注意哪些问题。例5.

26、1.5给出了多模块程序的第2个例子。程序完成的 功能是实现两个32位数的相乘。多位数相乘利用了一个子 程序MULT,它是一个独立的模块。参数的传递采用指定内存单元方式,两个操作数分别为 OPRN1和OPRN2,相乘的结果存入PRDCT中,操作数中包含 的字数在NSIZE中。乘法运算的过程与第4章中的例4.3.11相 同。本程序有两个模块,模块1为主模块,变量OPRN1、 OPRN2> PRDCT及NSIZE在主模块中定义。模块2是完成多 位数相乘的过程模块。由于两个模块的代码段的组合类型为 NONE类型,所以,该程序中的过程MULT应为FAR类型。此 外,过程中要访问的变量是主模块中所定

27、义的,因而主模块 在DS中所设定的段地址在过程中可以使用。例 5.1.5模块1:STACKSEGMENTSTACKDW 256 DUP(?)TOPLABELWORDSTACKENDSPUBLICOPRN1, OPRN2, PRDCT, NSIZEEXTRNMULT: FARDATASEGMENTOPRN1DW 8 DUP(?)OPRN2DW 8 DUP(?)PRDCTDW 16 DUP(?)NSIZEDW?DATAENDSCODESEGMENTASSUMECS: CODE, DS: DATA, SS: STACKSTART: MO VAX, DATAMOVDS, AXMOVAX, STACKM

28、OVSS, AXMOVSP, OFFSET TOPMOVNSIZE, 02CALLMULTMO V AH, 4CHINT21HCODEENDSEND START模块2:EXTRNOPRN1 : WORD, OPRN2: WORDEXTRNPRDCT: WORD, NSIZE: WORDPUBLICMULTCODESEGMENTASSUMECS: CODEMULTPROCFARPUSHSIPUSHDIPUSHBXPUSHCXPUSHAXMO VAX, SEG OPRN1MOVDS, AXLEADI, OPRN1LEASL OPRN2LEABX, PRDCTMO VCX, NSIZEPUSHBXM

29、OVAX, 0SHLCX, 01CLDMBMUL1: MOV BX , AXINCBXINCBXLOOPMBMUL1POPBXMO VCX, NSIZEMBMUL2: PUSHCXMOVDX, SIINCSIINCSIPUSHBXPUSHDIMO VCX, NSIZEMBMUL3 : PUSHCXPUSHDXMO VAX, DIINCDIINCDIMULDXADD BX , AXINCBXINCBXADC BX , DXPOPDXPOPCXLOOPMBMUL3POPDIPOPBXINCBXINCBXPOPCXLOOPMBMUL2POPAXPOPCXPOPBXPOPDIPOPSIRETMUL嚅?

30、里多模块程序设计CODEENDSEND5.2结构与记录5.2.1 结构1.结构定义格式结构定义格式为:结构名STRUC 结构名ENDS变量定义语句结构名是用户自己为所定义的结构所起的名称。 STRUC、ENDS是用来定义结构的伪指令。同一个结构定义中的STRUC、ENDS前的结构名必须相 同,其间为变量定义语句。例如:STRASTRUCSAVEBPDW?SAVEIPDW?SAVECSDW?NDW?RESULTDW?STRAENDSSTRBSTRUCNUMBDB?NAMEDB 10 DUP(?)AGEDB?SCOREDB 5 DUP(?)STRBENDS例中STRA和STRB为结构名,它是所定义

31、的结构类型的 名称,相当于用来定义字类型变量或双字类型变量的DW或 DDo伪指令STRUC和ENDS中间是一组变量定义语句,这组 语句中定义的变量类型可以不同。例中STRA定义的结构有5 个字变量。变量定义语句的变量标识符称为结构的字段标识 符,它表示一个字段的开始。结构STRA有5个字段,它是第4章中求N!的递归子程序 中在堆栈中存放的参数及返回地址的一种结构形式。这个结 构反映了这些字段之间的位置关系,即结构中的第1个字是 存放寄存器BP的内容,第2、第3个字存放的是返回地址, 第4个字是参数N,第5个字是存放结果的地址。2 .结构类型变量的定义上面我们已经指出,结构定义只定义了一种变量类

32、型, 结构名只是类型名,它不是变量,从而也不占据内存,只有 用该结构名定义了属于这种类型的变量后,汇编时才给该变 量分配内存单元。变量定义格式:变量名结构名初值表格式中的变量名同其它变量一样,是为变量起的名称。 结构名是已经按上面的格式定义的一种结构的名称。初值表 是为该结构中各字段赋的初值,其中尖括号是语法符号,不 能省略。初值表中各项的排列顺序及类型必须与结构定义中 各字段的顺序及类型一致,各字段的初值之间用逗号分隔。 若不改变结构定义时给出的预赋值,可以直接用逗号,若所 有的值都不改变,那么,初值表可以用一个空的角括号 表示。初值表中的每一项可以是一个常数表达式,每个字段 对应一个表达式

33、,这就是说,一个字段中只有一个赋值时才 可在变量定义时给该字段重新赋值。若某字段定义时其预赋 值为字符串,那么,变量定义时,可以用一个新的字符串代 替。当新字符串长度小于原来字符串的长度时,后面用空格 填充;当新字符串长度大于原来字符串长度时,多余部分被 截去。DSTACKISTRA,5, 00)STUDENT 1STRBKZK)3 KHT5SS3例中DSTACK1为STRA类型的变量,其初值表表明结构 中的前两字段保留预赋值,第3、第4字段给予了新的初值。 变量STUDENT1为STRB类型,它不改变结构定义时的所有预 赋值。3 .结构变量中字段的引用定义结构类型的变量以后,要访问结构变量中

34、某个字 段,可采用下面的方式:结构变量名.字段名变址寄存器或基址寄存器.字段名其中结构变量名代表了该结构变量所占内存的首地址的 偏移地址,字段名则代表了该字段的开始位置在结构中的偏 移量。例如结构STRA中的N的偏移量为6, RESULT的偏移 量为8;结构STRB的NUM的偏移量为0, SCORE的偏移量为 12o若已有上述的结构定义及其变量定义,我们可以有下面 的指令:MOVSTUDENT1 .NUMB, 01MOVSTUDENT1. SCORE, 80MOVBX, BP .NMOVBX, BP .RESULT上面第1条指令是将01送到结构类型变量STUDENT1 的NUMB字段。从寻址方

35、式说,该指令的目的操作数为直接 寻址方式,其有效地址EA=OFFSET STUDENT 1+NUMBo 第 2条指令则将80送至IJSTUDENT1的SCORE字段的第1字节中。 第3、第4条指令使用的是基址寄存器BP,若BP中存入的是 STRA结构变量的首地址,那么,这两条指令将取出对应结构变量中的N字段和 RESULT字段的内容,并且它们与下面两条指令等效:MOVBX, BP+6MOVBX, BP+8我们看到,在第4章中例4.3.14中正是采用这样两条指 令从堆栈中获取参数的。在定义结构类型以后,为取得堆栈 中相应的信息,我们不必计算该参数在堆栈中的相对位置, 而可以直接使用字段名获取该字

36、段的内容。这样,不仅使程 序更加清晰,而且不易发生错误。采用结构类型后,例 4.3.14中的递归过程可以有下面的形式:CODESEGMENTASSUMECS: CODESTACKSTRSTRUCSAVEBPDW?SAVEIPDW?SAVECSDW?NDW?RESULTDW?STACKSTRENDSFACTPROCFAR PUSHBP MOVBP, SP PUSHBX PUSHAX.RESULTNMOVBX, BP MOVAX, BP. CMPAX, 0JEDONEPUSHBXDECAXPUSHAXCALLFAR PTR FACTMOVBX, BP .RESULTMOV AX, BXMUL BP

37、 .NJMPSHORT RETURNDONE:MOVAX, 1RETURN:MOV BX , AXPOPAXPOPBXPOPBPRET4FACTENDPCODEENDS5.2.2记录1 .记录定义格式记录的定义格式为:记录名RECORD字段名:宽度=表达式,字段名: 宽度=表达式,记录名和字段名是用户自己定义的标识符,不可省略。 宽度表示该字段所占的位数,它可以是116的整数。若各字 段宽度之和超过8位,那么,该记录按两个字节考虑,否则 该记录按一个字节考虑。格式中的表达式是给对应字段预赋 的初值,若不预赋初值,则表达式可以省略。一个记录可以有若干个字段,每个字段必须有字段名及 其宽度,而表达

38、式不是必要的。格式中的字段名及其宽度将 按定义中给出的顺序由一个字节(或字)的高端向低端排列, 若各字段的总位数少于8位(或16位)时,则所有字段向字 节(或字)的低位靠齐。例如:RECI RECORD SB1: 3=101B, SB2: 5=11011B, SB3: 6=100000BREC2RECORD OPCODE: 5, MODE: 3, OPRI: 4=8, OPR2:例中定义了两个记录类型REC1和REC2。REC1把一个 字分成了 3个字段,其字段名分别为SB1、SB2、SB3,它 们的宽度分别为3、5、6,且给每个字段均预赋了初值。由 于各字段的宽度之和小于16,所以一个字的最

39、高两位没有用。 REC2将一个字分成了 4个字段,每个字段的字段名分别为 OPCODE> MODE、OPRk OPR2,其对应的宽度分别为5、 3、4、4,其中只有第3个字段预赋了初值。2 .记录类型变量的定义同结构一样,上面的定义只是定义了变量的一种类型。 只有定义了这种类型的变量后,汇编时才为该变量分配内存 单元。变量定义格式:变量名记录名初值表格式中的变量名是所定义的属于该记录类型的变量的名称。记录名是已经按上述格式定义了的一种记录的名称。初值表是给各字段赋的初值,尖括号是语法符号,不能省略。各初值的排列顺序应与定义时一致,各初值间用逗号隔开。若初值与类型定义时预赋值一致,可以直接

40、用逗号,若全部 初值都与预赋值一致,则初值表可以用空尖括号。例如REVARREC1 ,001110 #;INSTRUCARRAYREC220H DUP()#;例中定义了两个变量,变量REVAR为REC1类型,它的 第1、第2字段的初值与预赋值相同,第3个字段给予了新的 初值,所以变量REVAR的格式为:一 一I 01110 110 0 111 115SB1SB2SB30变量INSTRUCARRAY保留了 20H个字,其类型为 REC2,每个字的第3个字段初值为8,其它字段没有确定 值。变量INSTRUCARRAY中的一个字的形式为:15 COPOCDE MODE OPRIOPR2 010 0

41、03 .记录有关的操作1) WIDTH操作符格式:WIDTH记录字段名 该操作返回的是记录字段所占的位数。例如:KH*2WIDTHSB3;返回的值为6WIDTHOPCODE;返回的值为52) MASK操作符格式:MASK记录字段名该操作符返回的是一个8位或16位的二进制数,在这 个二进制数中,对应于该字段的各位为1,其余各位为Oo 例如:最后我们指出,结构和记录两种变量类型是可以由其它变量类型代替的,也就是它们不是编程时所必须的但有了这两种类型后可以给程序设计带来方便,特别是当程序中所用 的数据比较复杂时。例如学生情况登记表,表中可以有若干 个学生,每个学生又有若干项,此时,我们可以采用DB、

42、 DW来定义所有学生的每一项。若采用结构类型,我们可以 为每个学生的情况表定义成一种结构(如STRB),每个学生定 义一个结构变量,那么,在访问某个学生的某一项时,可以 直接采用变量名和字段名实现。显然,这样的程序不仅易写 易读,而且不易出错。同样,程序中有时有些变量只需一位或几位二进制位表示,自然,我们可以采用每个变量分别用一个字节的方法实 现,或者将儿个变量合用同一个字节,但访问这些变量时必须对它进行分离操作,而记录类型则为采用这样一类变量的编程提供了方便,它既可以用同一字节表示几个不同变量(各个字段),又可以利用字段名对它们进行访问。5.3汇编语言程序与高级语言程序的连接前面已经指出,模

43、块化程序设计时,各程序模块可以是 汇编语言的程序模块,也可以是不同语言的程序模块。本节 将介绍汇编语言程序分别与BASIC语言和C语言程序连接的有 关问题。5.3.1 汇编语言程序与BASIC语言程序的连接众所周知,BASIC语言是一种容易掌握、易于编程、使用非 常方便的语言。但其主要的缺点是运行时间长,特别是解释 BASIC就更显得突出。对于要求运行速度快,甚至在实时应用等场合,希望既能保留BASIC的主要优点,又能提高运算 速度,通常采用BASIC语言与汇编语言混合编程技术,即主 程序采用BASIC语言,子程序采用汇编语言。显然,这就存 在BASIC语言主程序如何调用汇编语言子程序的问题。

44、它主 要包括主程序与子程序的连接与它们之间的参数传递。BASIC语言程序与汇编语言子程序的连接有两种不同的情况,即解释BASIC程序与汇编语言程序的连接和编译BASIC 程序与汇编语言程序的连接。下面我们分别叙述。1.解释BASIC程序与汇编语言子程序的连接对于解释BASIC语言,在建立了BASIC的源程序文件之 后,运行该文件时不产生目标文件,因而不能直接利用 LINK程序将它与汇编语言子程序的目标文件连接起来。这 种连接工作要由用户自己来完成。完成这一工作要解决的主 要问题是两种语言的程序在内存如何存放和它们之间的信息 如何沟通。1)内存分配为了保证两种语言的程序在内存的存放不发生冲突,

45、我们首先要了解BASIC解释程序调入内存之后,内存空间的 分配。图5.2给出了这种分配,图中指出,BASIC解释程序 调入内存以后,BASIC语言的解释程序、用户程序及其工作 区和堆栈区占据内存的低地址端。uniHhi#作累疑常职部分BASIC解褥程 序及I .柞区BASECKHASSCfflP 程序区BASIC用栈区内存空闲区累跣便用风FOOOs FFFFH图配2内存空间的分配那么,汇编语言子程序应放置在何处并该如何设置呢?这可以有多种方法,当内存容量较小时,可以将汇编语言子 程序放置于图5.2中的BASIC区域,但此区域BASIC程序不能 再使用,也就是应在BASIC工作区中保留一段内存,

46、以备存 放汇编语言的可执行程序。这可以在BASIC程序中使用 CLEAR命令实现。这样做的结果减少了BASIC程序的使用空间,因此,当内存容量较大时,通常不采用这种方法,而是 将汇编语言子程序的可执行程序放在BASIC区之外。下面介 绍两种设置汇编语言子程序在内存存放位置的方法:(1)将汇编语言子程序的可执行文件存放在内存的地址高端。此方法是,首先建立汇编语言子程序的源文件,如文件 件,即AA.OBJ文件。名为 AA.ASM。然后,对该源文件进行宏汇编,建立目标文最后,利用LINK程序对该目标文件进行连接,并采用 /D/H参数,使连接后的可执行文件装在内存的尽可能高端(由 连接程序保证它在高端

47、空闲区),这样便建立了可执行文件 AA.EXEo(2)将汇编语言子程序的可执行文件存放在指定内存空间。 此方法首先可采用上述过程建立一个汇编语言子程序的可执 行文件,但连接时不用/D/H参数,也就是说,此时汇编语言 子程序的存放位置不加指定。然后,在DEBUG状态下调入 该可执行文件后,用M命令将该程序传送到某指定位置。为 了避免汇编语言子程序与BASIC程序在内存的冲突,该指定 区必须为空闲区。至此,我们已将BASIC程序和汇编程序分别存入内存的 不同区域,但是,它们还没有连接到一起构成同一个可执行 文件,因而它们还不能互相调用。2)两种程序的连接大家知道,解释BASIC程序是在BASIC状

48、态下运行的。而由LINK程序建立的汇编语言子程序的可执行文件是不能在 该状态下调入的。为使这样两种程序连接到一起,并能配合 运行,通常要使汇编语言子程序的可执行文件成为BSAIC语 言程序的一部分,然后采用BASIC语言的BLOAD语句将其调 入内存的指定区。但是,我们必须指出,LINK建立的二进制代码文件是不 能用BLOAD语句调入的,而BLOAD语句调入的文件只能是由 BSAVE语句建立在盘上的二进制代码文件。因此,要将两种 语言程序连接起来,首先要将LINK建立的可执行文件转换成 BSAVE存入的文件(也称为汇编语言子程序的二进制内存映像 文件)。(1) BASIC程序与建立在地址高端的

49、汇编语言子程序的可 执行文件的连接方法有两种。第一,利用DEBUG程序将已建立在盘上的AA.EXE文件调 入内存,并用R命令显示各寄存器的内容。例如:AX=OOOO BX=OOOO CX=0280 DX=OOOOSP=0080 BP=OOOO SI=OOOO DI=0000DS=0913ES=0913 SS=9F91 CS=9F74 IP=OOOO这样我们就获得了汇编语言子程序的开始地址和汇编语言子程序的长度,它们分别是CS : IP的内容,即9F74 : 0000和BX : CX的内容即 0000 : 0280Ho第二,在DEBUG状态下,用N和L命令调入BASIC解释程 序,然后用G命令运

50、行调入的BASICA程序,使系统处于 BASIC状态,即:NGWBASIC.EXE这时,可运行下面的BASIC程序,使盘上建立汇编语言子程序的二进制映像文件,并设其文件名为AA.COM:10DEF SEG=&H9F7420N=030K=0280H40BSAVE " AA.COM ,N, K50END程序中的SEG、N和K就是在DEBUG状态下用R命令显 示的CS、IP和CX的内容,即汇编语言子程序的入口地址的段、 偏移地址及其长度。注意,它们都是十六进制数。按这种方法建立的AA.COM文件便可在BASIC程序中用 BLOAD语句调入了。(2) BASIC程序与建立在指定内存区

51、的汇编语言子程序的可执行文件的连接方法有3种。第一,利用DEBUG程序将已建立在盘上的AA.EXE文件 调入内存,并用R命令显示各寄存器内容,以获得它们的起 始地址和长度。第二,利用M命令,将该段程序搬到内存的指定区,即:MADDR1 长度 ADDR2这里ADDR1的段地址和偏移地址是R命令显示的CS和IP 的内容,长度为BX:CX的内容;ADDR2是指定内存区的段 地址和偏移量。例如:M9F74 : 002804000 : 00这样就将建立在9F74H段的一段程序搬到了 4000H段。 第三,可用上面的同样方法将BASIC语言的解释程序调入内 存,并使系统处于BASIC状态。然后在BASIC

52、状态下,同样 运行一段程序将上面的已存入指定区的汇编语言子程序用 BSAVE语句在盘上建立AA.COM文件。(3) BASIC程序的基本格式。为实现BASIC程序调用汇编语言子程序,BASIC程序的基本格式如下:5DEFSEG=&H40000 10BLOADnAA.COMn20DEFSEG=&H400030MN=040CALLMN(A%, B%, C%)50END#;程序中首先将上面已建立的汇编语言子程序的代码文件调 入内存,语句20和30指定了汇编语言子程序入口地址的段与 偏移地址,语句40即调用该子程序,其中MN后的内容为调 用子程序时所需的参数,例中均为整型数。我们称这种

53、调用为 带参调用,调用时也可以不带参数,称为无参调用,此时MN 后可不带任何信息。3)参数传递及汇编语言子程序的基本格式汇编语言子程序与BASIC语言程序之间的参数传递也可采 用多种方式,例如可采用指定内存区传递方式和堆栈传递方式。 在前一种方式下,BASIC程序用POKE和PEEK语句可以将需传 递的信息存入指定区,也可以从指定区获取信息,汇编语言子 程序便可从指定区取得入口参数和返回结果参数。本节将主要 介绍后一种方式。(1)堆栈传递方式的BASIC调用语句。在这种方式下, BASIC程序中的调用语句采用带参调用语句,其格式为:DEFSEG=&H段地址CALLMN(参数1,参数2,

54、参数3,)该语句执行时,将完成下面操作: 将参数1,参数2,参数3的地址依次压入堆栈; 将返回地址压入堆栈; 转向程序由SEG和MN指出的子程序的入口。执行该语 句后,堆栈的内容如图5.3所示。皿*BP )+2(BP ) 44<Bf)+6UP)N(Bf ) + 10(BP )(IF>(CJ )酷3的itth鼻霞2的地址朔出的地址困5.3堆栈的内容(2)堆栈传递方式时汇编语言子程序的基本格式如下:CODESEGMENTMNPROCFARSTART:PUSHBP;保存BPMOVBP, SP; BP指向当前栈顶PUSHSIPUSHDXPUSHCXPUSHBXPUSHAXMOVSLMOVB

55、X,MOVSLMO VCX,MOVBX,BP+10;取参数1的偏移地址SI;将参数1送BXBP+8;取参数2的偏移地址SI;将参数2送CXBP+6;取参数3的偏移地址MOV BX , DX ;将结果返回MOV BX , DX;将结果返回POP AX;恢复各寄存器内容POPBXPOPCXPOPDXPOPSIPOPBPRET6MNENDPCODEENDEND STARTHJ汇编语言子程序必须为FAR类型,这是因为BASIC的CALL语言为远调用。程序中的开始部分为从堆栈中获得存放各参数的地址,其方法与汇编语言子程序的堆栈传递参数方 式时获得参数的方法基本一样,需注意的是,BASIC语言调 用时,压入堆栈的都是存放参数的地址,而不是参数本身。 为从该偏移地址中获取参数,其段地址为BASIC程序中设定 的DS中的内容,为使程序中不破坏DS的内容,特别是当有结 果参数需返回给BASIC程序时,更应注意这一点。例如上面 格式中的参数3是结果

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论