汇编语言之程序的基本结构_第1页
汇编语言之程序的基本结构_第2页
汇编语言之程序的基本结构_第3页
汇编语言之程序的基本结构_第4页
汇编语言之程序的基本结构_第5页
已阅读5页,还剩26页未读 继续免费阅读

下载本文档

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

文档简介

1、第6章 程序的基本结构在前面几章,我们分别介绍了用汇编语言进行程序设计所需要的几个最基本的知识:内存单元的寻址方式,变量定义和各种汇编指令格式。在掌握了这些基本内容之后,就需要学习如何把它们组成一个完整的汇编语言程序。6.1 源程序的基本组成汇编语言源程序的组成部分有:模块、段、子程序和宏等。一个模块对应一个目标文件,当开发较大型的应用程序时,该程序可能由若干个目标文件或库结合而成的。有关模块和子程序的知识和宏在第7章介绍,有关宏的知识将在第9章中叙述。6.1.1 段的定义微机系统的内存是分段管理的,为了与之相对应,汇编语言源程序也分若干个段来构成。8086CPU有四个段寄存器,在该系统环境下

2、运行的程序在某个时刻最多可访问四个段,而80386及其以后的CPU都含有六个段寄存器,于是,在这些系统环境下开发的运行程序在某个时刻最多可访问六个段。不论程序在某个时刻最多能访问多少个段,在编程序时,程序员都可以定义比该段数更多的段。在通常情况下,一个段的长度不能超过64K,在80386及其以后系统的保护方式下,段基地址是32位,段的最大长度可达4G。段的长度是指该段所占的字节数:、如果段是数据段,则其长度是其所有变量所占字节数的总和;、如果段是代码段,则其长度是其所有指令所占字节数的总和。在定义段时,每个段都有一个段名。在取段名时,要取一个具有一定含义的段名。段定义的一般格式如下:段名SEG

3、MENT对齐类型  组合类型  类别;段内的具体内容段名ENDS其中:“段名”必须是一个合法的标识符,前后二个段名要相同。可选项“对齐类型”、“组合类型”和“类别”的说明作用请见6.3节中的叙述。一个数据段的定义例子:DATA1SEGMENTword1DW1, 9078H, ?byte1DB21, 'World'DD12345678HDATA1ENDS一个代码段的例子:CODE1SEGMENTMOVAX, DATA1;把数据段DATA1的段值送AXMOVDS, AX;把AX的值送给DS,即:DS存储数据段的段值MOVAX, 4C00HINT21H;调用DOS

4、功能,结束程序的运行CODE1ENDS6.1.2 段寄存器的说明语句在汇编语言源程序中可以定义多个段,每个段都要与一个段寄存器建立一种对应关系。建立这种对应关系的说明语句格式如下:ASSUME  段寄存器名:段名, 段寄存器名:段名, 其中:段寄存器是CS、DS、ES、SS、FS和GS,段名是在段定义语句说明时的段名。在一条ASSUME语句中可建立多组段寄存器与段之间的关系,每种对应关系要用逗号分隔。例如,ASSUME  CS:CODE1, DS:DATA1上面的语句说明了:CS对应于代码段CODE1,DS对应于数据段DATA1。在ASSUME语句中,还可以用关键字NOTH

5、ING来说明某个段寄存器不与任何段相对应。下面语句说明了段寄存器ES不与某段相对应。ASSUME  ES:NOTHING在通常情况下,代码段的第一条语句就是用ASSUME语句来说明段寄存器与段之间的对应关系。在代码段的其它位置,还可以用另一个ASSUME语句来改变前面ASSUME语句所说明的对应关系,这样,代码段中的指令就用最近的ASSUME语句所建立的对应关系来确定指令中的有关信息。例6.1 汇编语言段及其段说明语句的作用。DATA1SEGMENT;定义数据段DATA1word1DW5678hbyte1DB"ABCDEFG"DATA1ENDSDATA2SEGME

6、NT;定义数据段DATA2word2DW1234hword3DW9876hDATA2ENDSDATA3SEGMENT;定义数据段DATA3byte2DB?DATA3ENDSCODE1SEGMENT;编写代码段CODE1ASSUMECS:CODE1, DS:DATA1, ES:DATA2;(1)MOVAX, DATA1;(2)MOVDS, AX;(3)MOVAX, DATA2;(4)MOVES, AX;(5)MOVAX, word1;访问段DATA1中的字变量word1MOVword2, AX;访问段DATA2中的字变量word2ASSUMEDS:DATA3, ES:NOTHING;(6)MOV

7、AX, DATA3MOVDS, AXMOVBL, byte2;访问段DATA3中的字节变量byte2MOVAX, 4C00H;(7)INT21H;(8) CODE1 ENDS语句(1)和(6)分别说明了段和段寄存器之间的对应关系,其中语句(6)重新说明语句(1)所指定的对应关系。二组语句(2)和(3)、(4)和(5)实现对段寄存器DS和ES赋初值。ASSUME说明语句只起说明作用,它不会对段寄存器赋值,所以,必须对有关段寄存器赋值。在以后的其它源程序中也都是用此方法来实现对数据段寄存器赋值的。语句(7)和(8)是调用中断21H的4CH号功能来结束本程序的执行,程序的返回代码由寄存器A

8、L来确定。结束本程序执行的指令是所有主模块必须书写的语句。注意:代码段寄存器不能由程序员在源程序中对其赋值,其值是由操作系统在装入它进入系统运行时自动赋值的。6.1.3 堆栈段的说明堆栈段是一个特殊的段,在程序中可以定义它,也可以不定义。除了要生成COM型执行文件的源程序外,一个完整的源程序一般最好定义堆栈段。如果在程序中不定义堆栈段,那么,操作系统在装入该执行程序时将自动为其指定一个64K字节的堆栈段。在程序没有定义堆栈段的情况下,在由连接程序生成执行文件时,将会产生一条如下的警告信息,但程序员可以不理会它,所生成的执行文件是可以正常运行的。warning xxxx: no stack se

9、gment  (其中:xxxx是错误号)在源程序中,可用以下方法来定义堆栈段。方法1:STACK1SEGMENTDB256 DUP(?) 256是堆栈的长度,可根据需要进行改变TOPLABEL  WORDSTACK1ENDS以上堆栈段的定义如图6.1所示。由于堆栈是按地址从大到小的存储单元顺序来存放内容的,所以,在堆栈存储单元说明语句之后,再说明一个栈顶别名,这样,对栈顶寄存器SP的赋值就很方便。在源程序的代码段中,还要添加如下程序段,才能把段STACK1当作堆栈段来使用。图6.1 堆栈段定义示意图ASSUMESS:STACK1;可在代码段的段指定语句中一起说明C

10、LI;禁止响应可屏蔽中断MOVAX, STACK1MOVSS, AXMOVSP, offset TOP;给堆栈段的栈顶寄存器SP赋初值STI;恢复响应可屏蔽中断方法2:STACK1SEGMENTSTACK;定义一个堆栈段,其段名为STACK1DB256 DUP(?)STACK1ENDS上述段定义说明了该段是堆栈段,系统会自动把段寄存器SS和栈顶寄存器SP与该堆栈段之间建立相应的关系,并设置其初值,而不用在代码段对它们进行赋值。除了以上二种方法外,还有一种更简洁的方法来定义堆栈段,有关内容请见第6.4.2节中的叙述。6.1.4 源程序的结构下面的程序是一个完整的源程序,其功能是在屏幕上显示字符串

11、“Hello, World.”。读者可参考此结构编写自己的简单程序。例6.2 在屏幕上显示字符串“HELLO,WORLD.”解:可运行下面的控件,用鼠标左键单击程序中的某一行,可阅读其含义;单击“内存”可切换内存内容的显示方式。伪指令END表示源程序到此为止,汇编程序对该语句之后的任何内容都不作处理,所以,通常情况下,伪指令END是源程序的最后一条语句。伪指令END后面可附带一个在程序中已定义的标号,由该标号指明程序的启动位置。如果源程序是一个独立的程序或主模块,那么,伪指令END后面一定要附带一个标号;如果源程序仅是一个普通模块,那么,其END后面就一定不能附带标号。6.2 程序的基本结构在

12、学习高级语言程序设计时,我们知道了程序的三大主要结构:顺序结构、分支结构和循环结构。在汇编语言的源程序也同样有此三大结构,所不同的是它们的表现形式不同。用高级语言编写程序时,由于不使用“转移语句”而使这三种结构清晰明了。但在汇编语言的源程序中,很难不使用“转移语句”(除非是一些只有简单功能的程序),有时甚至会有各种各样的“转移语句”。由于存在这些转移语句,就使得:汇编语言源程序的基本结构显得不太明确。如果源程序的编写者思维混乱,编写出来的源程序在结构上就会显得杂乱无章,反之,如果编写者条理清晰,安排的操作井然有序,那么,编写出来的程序在结构上就会一目了然。总之,不论是高级语言的源程序,还是汇编

13、语言的源程序,其程序的三大基本结构也还是万变不离其宗的。6.2.1 顺序结构顺序结构是最简单的程序结构,程序的执行顺序就是指令的编写顺序,所以,安排指令的先后次序就显得至关重要。另外,在编程序时,还要妥善保存已得到的处理结果,为后面的进一步处理直接提供前面的处理结果,从而避免不必要的重复操作。例6.3 编写程序段,完成下面公式的计算(其中:变量X和Y是32位有符号数,变量A,B和Z是16位有符号数)。A(X-Y+24)/Z的商,B(X-Y+24)/Z的余数解:DATA1SEGMENTXDD?YDD?ZDW?ADW?BDW?DATA1ENDSCODE1SEGMENTMOVAX, XMOVDX,

14、X+2;用(DX:AX)来保存32位变量X的数值SUBAX,YSBBDX, Y+2;(DX:AX)-(Y+2:Y)ADDAX, 24DADCDX, 0;(DX:AX)+24IDIVZMOVA, AXMOVB, DXCODE1ENDS在编程序时,常常需要交换二变量之值。假设需要交换值的变量名为:var1和var2,临时增加的变量名为temp。常用的算法如下:temp = var1var1 = var2var2 = temp例6.4 假设有二个字变量word1和word2,编写程序段实现交换其值的功能。解:方法1:用汇编语言指令简单“直译”上面的 交换数据方法DATA1SEGMENTword1DW

15、?word2DW?tempDW?DATA1ENDSCODE1SEGMENTMOVAX, word1MOVtemp, AX;上二语句实现语句“temp=word1”MOVAX, word2MOVword1, AX;上二语句实现语句“word1=word2”MOVAX, tempMOVword2, AX;上二语句实现语句“word2=temp”CODE1ENDS这种方法虽然也能完成功能,但显然其不能充分利用汇编语言的特点,程序效率很低。方法2:用汇编语言指令的特点来直接编译DATA1SEGMENTword1DW?word2DW?DATA1ENDSCODE1SEGMENTMOVAX, word1XC

16、HGAX, word2MOVword1, AX;能XCHG word1, word2来代替这三条指令吗?CODE1ENDS该方法充分利用了汇编语言的特点,不仅省去了中间变量temp的定义,而且程序的效率也提高了。6.2.2 分支结构分支结构是一种非常重要的程序结构,也是实现程序功能选择所必要的程序结构。由于汇编语言需要书写转移指令来实现分支结构,而转移指令肯定会破坏程序的结构,所以,编写清晰的分支结构是掌握该结构的重点,也是用汇编语言编程的基本功。在程序中,当需要进行逻辑分支时,可用每次分二支的方法来达到程序最终分多支的要求,也可是用地址表的方法来达到分多支的目的。一、显示转移指令实现的分支结

17、构在高级语句中,分支结构一般用IF语句来实现,在汇编语言中,课用无条件转移指令或条件转移指令实现的分支结构。如图6.2给出了二种常用的分支结构。在编写分支程序时,要尽可能避免编写“头重脚轻”的结构,即:当前分支条件成立时,将执行一系列指令,而条件不成立时,所执行的指令很少。这样就使后一个分支离分支点较远,有时甚至会遗忘编写后一分支程序。这种分支方式不仅不利于程序的阅读,而且也不便将来的维护。所以,在编写分支结构时,一般先处理简单的分支,再处理较复杂的分支。对多分支的情况,也可遵循“由易到难”的原则。因为简单的分支只需要较少的指令就能处理完,一旦处理完这种情况后,在后面的编程过程中就可集中考虑如

18、何处理复杂的分支。 (a) if endif结构(b) ifelseendif结构图6.2 分支结构的二种结构例6.5 已知字节变量CHAR1,编写一程序段,把它由小写字母变成大写字母。解:DATA1SEGMENTCHAR1DB?DATA1ENDSCODE1SEGMENTMOVAL, CHAR1CMPAL, aJBnextCMPAL, zJAnextSUBCHAR1, 20H;指令AND CHAR1, 0DFH也可以next:CODE1ENDS例6.6 编写一程序段,计算下列函数值。其中:变量X和Y是有符号字变量。解:DATA1SEGMENTXDW?YDW?DATA1ENDSCODE1SEGM

19、ENTMOVAX, XCMPAX, 0JGEcase23ADDAX, 10;第一种情况的计算结果JMPresultcase23:CMPAX, 10DJGcase3MOVBX, 30DIMULBX;第二种情况的计算结果JMPresultcase3:SUBAX, 9;第三种情况的计算结果result:MOVY, AX;把计算结果保存到变量Y中CODE1ENDS例6.7 把下列C语言的语句改写成等价的汇编语言程序段(不考虑运算过程中的溢出)。If (a+b > 0 && c%2 = 0) a = 62;else a = 21;其中:变量a,b和c都是有符号的整型(int)变量。

20、解:DATA1SEGMENTADW?BDW?CDW?DATA1ENDSCODE1SEGMENTMOVAX, AADDAX, BJLE_ELSE;ADD指令会改变算术标志位 TESTC, 1;C%2=0,也就是:看C的最低位是否为0JNZ_ELSEMOVA, 62DJMPNEXT_ELSE: MOVA, 21DNEXT:CODE1ENDS例6.8 用地址转移表实现下列C语言的switch语句,其中:变量A和B是有符号的整型(int)变量。switch(a%8)case 0:b = 32;break;case 1:case 2:b = a + 43;break;case 3:b = 2*

21、a;break;case 4:b-;break;case 5:case 6:case 7:printf(“Function 5_6_7”);break;解:DATA1SEGMENTADW?BDW?TableDWcase0. case12, case12, case3DWcase4, case567, case567, case567MSGDB'Function 5_6_7$'DATA1ENDSCODE1SEGMENTMOVAX, AMOVBX, AXANDBX, 7;得到BX的低三位,实现a%8的计算SHL BX, 1;由于地址表是字类型,其下标要乘2JMPTable

22、BX;利用地址表实现多路转移case0:MOVB, 32DJMPnextcase12:ADDAX, 43DMOVB, AXJMPnextcase3:SHLAX, 1MOVB, AXJMPnextcase4:DECBJMPnextcase567:LEA DX, MSGMOVAH, 9INT21HJMPnextnext:CODE1ENDS用地址表实现多路转移的关键在于:转移入口的地址表和转移情况可整数化。如果这二个要求有一个不满足,或很难构造,则无法使用该方法。为了改善汇编语言源程序的结构,减少显式转移语句所带来混乱,在宏汇编MASM 6.11系统中,增加了表达分支结构的伪指令。该伪指令

23、的书写格式与高级语言的书写方式相类似,汇编程序在汇编时会自动增加转移指令和相应的标号。理解并掌握该知识,对将来学习编译原理课程也有一定的帮助。分支伪指令的具体格式如下:格式1:.IF condition;以英文“句号”开头 指令序列 ;条件"condition"成立时所执行的指令序列.ENDIF格式2:.IF condition 指令序列1.ELSE 指令序列2;条件"condition"不成立时所执行的指令序列.ENDIF格式3:.IF condition1 指令序列1.ELSEIF condition2 指令序列2;条件"condition

24、2"成立时所执行的指令序列.ENDIF其中:条件表达式“condition”的书写方式与C语言中条件表达式的书写方式相似,也可用括号来组成复杂的条件表达式。条件表达式中可用的操作符有:=(等于)、!=(不等)、>(大于)、>=(大于等于)、<(小于)、<=(小于等于)、&(位操作与)、!(逻辑非)、&&(逻辑与)、|(逻辑或)等。若在条件表达式中检测标志位的信息,则可以使用的符号名有:CARRY?(相当于CF=1)、OVERFLOW?(OF=1)、PARITY?(PF=1)、SIGN?(SF=1)、ZERO?(ZF=1)等。例如:.IF

25、CARRY? && AX != BX;检测CF=1且AX!=BX是否成立;汇编语言指令序列.ENDIF在指令序列中,还可再含有其它的.IF伪指令,即:允许嵌套。伪指令.ELSEIF引导出另一个二叉分支,但它不能作伪指令块的第一个伪指令。汇编程序在对“条件表达式”进行代码转换时将进行代码优化处理,以便尽可能生成最好的指令代码。如:.IFax = 0汇编程序会把它转换为指令“ORax, ax”,而不是“CMPax, 0”,因为前者比后者更好,而不是简单直接地转换为后者。如果用伪指令来书写分支结构,那么,例6.5的代码段部分就可写成如下程序段:MOVAL, CHAR1.IFAL>

26、;='a' && AL<='z'语句象C语言语句吗?SUBCHAR1, 20H.ENDIF也可把例6.6的代码段部分就可写成如下程序段:MOV   AX, X.IF AX < 0ADDAX, 10;计算第一种情况的结果.ELSEIFAX <= 10MOVBX, 30DIMULBX;计算第二种情况的结果.ELSESUBAX, 9;计算第三种情况的结果.ENDIFMOVY, AX;把计算结果保存到变量Y中例6.9 根据当前计算机的时间和日期,显示上午(AM)或下午(PM),以及所在的季节。解:显示解答6

27、.2.3 循环结构循环结构是一个重要的程序结构,它具有重复执行某段程序的功能。通常,循环结构包括以下四个组成部分:1、循环初始化部分初始化循环控制变量、循环体所用到变量;2、循环体部分循环结构的主体;3、循环调整部分循环控制变量的修改、或循环终止条件的检查;4、循环控制部分程序执行的控制转移。以上四部分可以在程序中用各种不同的形式体现出来,有时也并非清析地表达出来。常用的循环结构如图6.3所示。(a)、DoWhile结构(b)、While结构图6.3 常用的循环结构示意图一、用循环指令构成循环结构在编写循环结构的程序片段时,我们可以多种方法来循环结构。如:循环次数是已知的,可用LOOP指令来构

28、造循环;当循环次数是未知或不定的,则可用条件转移或无条件转移来构成循环结构。例6.10 分类统计字数组data中正数、负数和零的个数,并分别存入内存字变量Positive、Negative和Zero中,数组元素个数保存在其第一个字中。解:显示解答例6.11 计算数组score的平均整数,并存入内存字变量Average中,数组以-1为结束标志。解:DATA1SEGMENTdataDW90, 95, 54, 65, 36, 78, 66, 0, 99, 50, -1AverageDW0DATA1ENDSCODE1SEGMENTASSUMECS:CODE1, DS:DATA1START:MOVAX,

29、 DATA1MOVDS, AXXORAX, AXXORDX, DX;用(DX,AX)来保存数组元素之和XORCX, CX;用CX来保存数组元素个数LEASI, data;用指针SI来访问整个数组again:MOVBX, word ptr SICMPBX, 0JLoverADDAX, BXADCDX, 0;把当前数组元素之值加到(DX,AX)中INCCX;数组元素个数加1ADDSI, 2JMPagainover:JCXZexit;防止零作除数,即数组是空数组DIVCXMOVAverage, AXexit:MOVAX, 4C00HINT21HCODE1ENDSENDSTART二、用伪指令实现的循环

30、结构在宏汇编MASM 6.11系统中,还增加了表达循环结构的伪指令,以便更清晰地表达WHILE循环、REPEAT-UNTIL循环。另外,还增加两个辅助循环的伪指令。这些伪指令的书写格式和含义与高级语言中相应语句的书写格式和含义相一致,所以,这些伪指令是很容易掌握的,也是非常有用的。循环伪指令的格式和含义如下:1、WHILE型循环伪指令.WHILE condition循环体的指令序列;条件"condition”成立时所执行的指令序列.ENDW其中:.ENDW与前面的.WHILE相匹配,它标志着其循环体到此结束。如果条件表达式“condition”在循环开始时,就为“假”(false),

31、那么,该循环体一次也不会被执行。2、REPEAT型循环伪指令.REPEAT 循环体的指令序列.UNTIL condition .REPEAT 循环体的指令序列.UNTILCXZ conditionREPEAT型循环在执行完循环体后,才判定逻辑表达式condition的值。若该表达式的值为真,则终止该循环,并将执行伪指令.UNTILCXZ后面的指令,否则,将向上跳转到伪指令.REPEAT之后的指令,为继续执行其循环体作准备。如果.UNTILCXZ后面没有写逻辑表达式,那么,由.REPEAT.UNTILCXZ所构成的循环与用LOOP指令所过程的循环是一致的,它们都是以“CX=0”为循环终

32、止条件。如果.UNTILCXZ后面书写了逻辑表达式,那么,该逻辑表达式的形式只能是:“EXP1=EXP2”或“EXP1!=EXP2”。所以,这时由“.REPEAT.UNTILCXZ condition”所构成的循环就与用LOOPNE/LOOPE指令所过程的循环是一致的,它们都是以“condition | CX=0”为循环终止条件。和高级语言的REPEAT型的循环一样,.REPEAT.UNTILCXZ的循环体也会至少被执行一次。.WHILE.ENDW和.REPEAT.UNTILCXZ的循环体内还可再含有循环伪指令,这样就构成了循环结构的嵌套。汇编程序在生产指令代码时会进行代码优化,以便尽可能得到

33、最优化的指令序列。3、辅助循环伪指令(1)、终止循环伪指令.BREAK.BREAK  .IF condition该伪指令用来终止包含它的最内层循环。前者是无条件终止循环,后者是仅当逻辑表达式condition为真时,才终止循环。.WHILE  1.REPEAT.BREAK .IF condition.BREAK .IF conditionENDW.UNTIL0对于以上二个循环,如果没有指令来终止循环的话,它们都将进入死循环状态,但如果在该层循环体内,存在伪指令“.BREAK .IF condition”的话,那么,当逻辑表达式condition为真时,该循环就会被终止了。(

34、2)、循环继续伪指令.CONTINUE.CONTINUE.IF condition该伪指令用于直接跳转到包含它的最内层循环的计算循环条件表达式的代码处。前者是无条件转移到计算循环条件表达式的代码处,后者是仅当条件表达式condition为真时,才进行这样的跳转。辅助循环伪指令.BREAK和.CONTINUE只能在伪指令.WHILE.ENDW和.REPEAT.UNTIL的循环体内使用。 例6.12 显示9个数字字母'1''9',26个大写字母,和显示任意输入的数字字符,并用按“回车”键来结束本程序的运行。解:DATA1SEGMENTMSG1DB13, 10,

35、60; "Iteration: "NUM1DB'1', "$"MSG2DB13, 10,  "Alphabet: $"NUM2DB'A', " $"MSG3DB13, 10, "Type digits, then press ENTER: $"DATA1ENDSCODE1SEGMENTASSUMECS:CODE1, DS:DATA1START:MOVAX, DATA1MOVDS, AXMOVCX, 9MOVAH, 09HMOVDX, OFFSET MSG

36、1.REPEATINT21HINCNUM1;显示Iteration: 1,2,9.UNTILCXZMOVDX, OFFSET MSG2INT21H;显示字符串"Alphabet:"MOVAH, 09HMOVDX, OFFSET NUM2.REPEATINT21HINCNUM2;显示当前字母;当前字母向后移.UNTILNUM2 > 'Z'显示整个大写字母表MOVAH, 09HMOVDX, OFFSET MSG3INT21H.WHILE  1;循环条件为永真的循环MOVAH, 07HINT21H;不带回显地从键盘读一个字符.BREAK .IF A

37、L = 13;如果输入“回车”键,则终止循环.CONTINUE .IF (AL<'0') | (AL>'9');如果字符不是数字字符,则继续循环MOVDL, ALMOVAH, 02HINT21H;显示所输入的数字字母.ENDWMOVAX, 4C00HINT21HCODE1ENDSENDSTART6.3 段的基本属性在通常情况下,一个复杂的应用程序会由若干个模块组成,一个模块又会含有多个段。而不同模块的段之间、同一模块的段之间往往存在某种联系,这种联系就要体现在段属性的说明上。段定义的一般格式如下:段名SEGMENT对齐类型组合类型类别段名ENDS段属

38、性“对齐类型”、“组合类型”和“类别”要按此顺序说明,但这些可选项可根据需要选择书写。如果源程序中不指定某个属性,那么,汇编程序将使用该属性的缺省值。程序中的段名可以是唯一的,也可以与其它段同名。在同一模块中,如果有二个段同名,则后者被认为是前段的后续,这样,它们就属同一段。当同一模块出现二个同名段时,则后者的可选项属性要么与前者相同,要么不写其属性而选用前者的段属性。例6.13 同段名的作用DATA1SEGMENT;第一个数据段MSGDB "Hello, "DATA1ENDSCODE1SEGMENT;第一个代码段ASSUMECS:CODE1, DS:DATA1START:

39、MOVAX, DATA1MOVDS, AXMOVDX, offset MSGMOVAH, 9INT21HCODE1ENDSDATA1SEGMENT;第二个数据段DB "World.$"DATA1ENDSCODE1SEGMENT;第二个代码段MOVAX, 4C00HINT21HCODE1ENDSENDSTARTEND在上面的例子中,第二个数据段是第一个数据段的后续,汇编程序把它们是合二为一,上述的代码段也如此。下面,详细说明段属性的含义及其作用。6.3.1 对齐类型(ALIGN)对齐类型表示当前段对起始地址的要求,连接程序(LINK.EXE)按表6.1的地址格式来定位段的起始

40、地址。在进行段定位时,会根据其定位类型进行定位的,所以,各段之间就有可能出现一些空闲字节,即可能浪费几个字节单元。段对齐类型PARA是一个适用于所有段类型的对齐类型,它也是缺省的对齐类型。对齐类型BYTE和WORD通常用于数据段的定位,对齐类型DWORD通常用于80386及其以后CPU代码段的定位。表6.1 段对齐类型与段起始地址之间的对应关系对齐类型起始地址(二进制)功能说明最多的空闲字节数BYTExxxx xxxx xxxx xxxx xxxx下一个字节地址0WORDxxxx xxxx xxxx xxxx xxx0下一个字地址1DWORDxxxx xxxx xxxx xxxx xx00下一

41、个双字地址3PARAxxxx xxxx xxxx xxxx 0000下一个节地址15PAGExxxx xxxx xxxx 0000 0000下一个页地址1276.3.2 组合类型(COMBINE)组合类型是告诉连接程序如何把不同模块中段名相同的段合并在一起。具体的组合类型如下:NONE表示当前段在逻辑上独立于其它模块,并有其自己的基地址。NONE是缺省的组合类型。PUBLIC表示当前段与其它模块中同段名的PUBLIC类型段组合成一个段。组合的先后次序取决于LINK程序中目标模块排列的次序。在组合时,后续段的起始地址要按其对齐类型进行定位,所以,同名段之间可能有间隔。COMMON表示当前段与其它

42、模块中同名段重叠,也就是说,它们的起始地址相同。最终段的长度是同名段的最大长度。由于段覆盖,所以,前一同名段中的初始化数据被后续段的初始数据覆盖掉。STACK组合类型STACK表示当前段是堆栈栈,其组合情况与PUBLIC相同。AT 数值表达式该数值表达式是当前段所指定的绝对起始地址的段地址。6.3.3 类别(CLASS)类别是一个由程序员指定的用单引号括起来的字符串。如果一个段没有给出类别,那么,这个段的类别就为空。类别是用于段的分类,连接程序利用该类别来调整同名、同类别的段,并使它们相邻。典型的类别是"Data"和"Code"。如果指定某段的类别是&q

43、uot;Code",那么,该段最好是代码段,这样,有的调试程序(如:CodeView)就可以顺序工作。例如:DATA1SEGMENT  WORD  PUBLIC  "Data"DATA1ENDS上述段定义说明了该段的起始地址是下一个字地址、组合类型为PUBLIC、段类别是"Data"。6.3.4 段组(GROUP)段组伪指令GROUP是用于把源程序模块中若干个段结合成一个组,并对该段组定义一个段组名。段组伪指令的格式如下:段组名  GROUP  段名, 段名, 其中:段名之间要用逗号间隔,段名也

44、可以用表达式“SEG 变量”或“SEG 标号”。下面举例说明段组伪指令的使用方法和作用。例6.12 段组的作用方法1:用一个段寄存器对应二个数据段DATA1SEGMENT;第一个数据段b1DB10hDATA1ENDSDATA2SEGMENT;第二个数据段b2DB23hDATA2ENDSCODE1SEGMENTASSUMECS:CODE1, DS:DATA1;(1)START:MOVAX, DATA1MOVDS, AX (2)把数据段DATA1的段值赋给段寄存器DSMOVBL, b1;(3)引用DS来访问DATA1中的变量b1ASSUMEDS:DATA2;(4)MOVAX, DATA2

45、MOVDS, AX;(5)把数据段DATA2的段值赋给段寄存器DSMOVAL, b2;(6)引用DS来访问DATA2中的变量b2CODE1ENDSENDSTART在上例中,语句(1)说明DS与DATA1建立联系,语句(2)对DS赋值,语句(3)用DS来访问DATA1段的变量名。语句(4)说明DS与DATA2建立联系,语句(5)对DS赋值,语句(6)用DS来访问DATA2段的变量名。在该例子中,因为只使用一个段寄存器DS来对应二个数据段,所以,需要切换DS的对应关系(如:语句(4)。但我们也可以用段寄存器DS和ES来分别对应段DATA1和DATA2,这样,方法1就可变成方法2。方法2:用二个段寄

46、存器对应二个数据段DATA1SEGMENTb1DB10hDATA1ENDSDATA2SEGMENTb2DB23hDATA2ENDSCODE1SEGMENTASSUMECS:CODE1, DS:DATA1, ES:DATA2START:MOVAX, DATA1MOVDS, AX 把数据段DATA1的段值赋给段寄存器DSMOVAX, DATA2MOVES, AX;把数据段DATA2的段值赋给段寄存器ESMOVBL, b1;引用DS来访问DATA1中的变量b1MOVAL, b2;引用ES来访问DATA2中的变量b2CODE1ENDSENDSTART我们还可以用段组来简化段寄存器的使用,把段

47、DATA1和DATA2组成一个数据段。所以,把方法2再改写成方法3的形式。方法3:用一个段组组成二个数据段GSEGGROUPDATA1, DATA2;把段DATA1和DATA2定义成一个段组DATA1SEGMENTb1DB10hDATA1ENDSDATA2SEGMENTb2DB23hDATA2ENDSCODE1SEGMENTASSUMECS:CODE1, DS:GSEGSTART:MOVAX, GSEGMOVDS, AX 把段组GSEG的段值赋给段寄存器DSMOVBL, b1;引用DS来访问DATA1中的变量b1MOVAL, b2;引用DS来访问DATA2中的变量b2CODE1END

48、SENDSTART定义段组后,段组内各段所定义的标号和变量,除了与定义它们的段起始点相关外,还与段组的起始点相关。规定如下:、如果在ASSUME伪指令中说明段组与段寄存器相对应,那么,有关标号或变量的偏移量就相对于段组起点计算;、如果在ASSUME伪指令中说明段组内的某各段与段寄存器相对应,那么,有关标号或变量的偏移量就相对于该段的起点。所以,在使用段组后,程序员要谨慎使用ASSUME伪指令,并保证段寄存器的值与段组或段相一致。6.4 简化的段定义前面,我们介绍了完整的段定义格式,用完整的段定义格式虽然可以控制段的各种属性,但程序员很少会这样做。现在的汇编程序提供了一种简化的段定义方式,它使定

49、义段更简单、方便。6.4.1 存储模型说明伪指令在使用简化的段定义方式之前,必须使用存储模式说明伪指令来描述源程序所采用的存储模式。该伪指令说程序所使用的存储模式,汇编程序将用该存储模式生成相应的ASSUME和GROUP语句,同时也为其它的简化段创建等价的预定义。程序存储模式说明伪指令的格式如下: .MODEL  存储模式,语言类型 ,操作系统类型 ,堆栈类型程序可选的存储模式有:TINY、SMALL、COMPACT、MEDIUM、LARGE、HUGE和FLAT。伪指令.MODEL必须写在源程序的首部,且只能出现一次,其前内容只能是注释。如果用伪指令来指定程序所遵循的语言类型,那么,

50、将不允许子程序的嵌套定义。与子程序定义有关的内容请见第7.5节。一、存储模式如果要用汇编语言编写被高级语言调用的子程序,那么,该汇编程序的存储模式必须与该高级语言编译(或解释)程序所使用的存储模式相匹配。汇编语言程序所能使用的存储模式、符号及其相关信息如表6.2所列。在程序中,还可伪指令OPTION SEGMENT和SEGMENT来指定段的规模。有关存储模式的具体规定如下:、TINY在汇编程序MASM 6.11和TASM 4.0,该存储类型是为编写COM文件类型而设置的。程序员还可用汇编命令行选项/AT和连接命令选项/TINY来达到此目的。表6.2 存储模式的符号及其相关含义代码的位距数据的位

51、距段的宽度数据段和代码段能否合并Code DistanceData DistanceSegment WidthData & Code Combined?TinySmallCompact MediumLargeHugeFlatNEARNEAR16-bitYesNEARNEAR16-bitNoNEARFAR16-bitNoFARNEAR16-bitNoFARFAR16-bitNoFARFAR16-bitNoNEARNEAR32-bitYes、SMALL所有的数据变量必须在一个数据段之内,所有的代码也必须在一个代码段之内。在这种模型下,数据段寄存器的内容保持不变,所有转移也都是段内转移。该存

52、储类型是独立汇编语言源程序常用的存储模型。、MEDIUM所有的数据变量必须在一个数据段之内,但代码段可以有多个。在这种模型下,数据段寄存器的内容保持不变,转移可以是段间转移。、COMPACT数据段可以有多个,但代码段只能有一。、LARGE数据段和代码段都可以有多个,但一个数组的字节数不能超过64KB。、HUGE数据段和代码段都可以有多个,一个数组的字节数也可以超过64KB。、FLATFLAT存储模式在创建执行文件时,将使该程序仅含一个包括程序数据和代码的32位段,并且只能在80386及其以后的计算机系统中运行。该程序的文件类型为EXE。在使用该存储模式之前,必须先用伪指令.386、.486或其

53、它伪指令来说明更高性能的CPU类型。也就是说:FLAT模式仅在386及其以后CPU模式下才能使用。在该程序中,所有代码和数据位距的缺省值都是NEAR,子程序的类型也是NEAR,并且标识符CodeSize,DataSize和Model的值分别为:0、0和7。在FLAT存储模式下,程序将不使用段寄存器FS和GS。汇编程序在处理说明语句“.MODEL  FLAT”时,将自动生成下列段寄存器说明语句:ASSUME  CS:FLAT, DS:FLAT, SS:FLAT, ES:FLAT, FS:ERROR, GS:ERROR当然,程序员也可把该段寄存器说明语句写在其指令序列之中。二、语言类型其详细说明请见7.5.3节中所述。三、操作系统类型OS_DOS是当前唯一支持的选项值,也是该选项的

温馨提示

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

评论

0/150

提交评论