收藏 分享(赏)

汇编语言之程序的基本结构.doc

上传人:精品资料 文档编号:10511272 上传时间:2019-11-24 格式:DOC 页数:31 大小:72.17KB
下载 相关 举报
汇编语言之程序的基本结构.doc_第1页
第1页 / 共31页
汇编语言之程序的基本结构.doc_第2页
第2页 / 共31页
汇编语言之程序的基本结构.doc_第3页
第3页 / 共31页
汇编语言之程序的基本结构.doc_第4页
第4页 / 共31页
汇编语言之程序的基本结构.doc_第5页
第5页 / 共31页
点击查看更多>>
资源描述

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

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

3、段定义的一般格式如下:SEGMENT 对齐类型 组合类型 类别 ;段内的具体内容段名段名 ENDS其中:“段名”必须是一个合法的标识符,前后二个段名要相同。可选项“对齐类型”、“组合类型”和“类别”的说明作用请见 6.3 节中的叙述。一个数据段的定义例子:SEGMENTword1DW 1, 9078H, ?DB 21, WorldDATA1byte1DD 12345678HDATA1ENDS一个代码段的例子:SEGMENTMOV AX, DATA1 ;把数据段 DATA1 的段值送 AXMOV DS, AX ;把 AX 的值送给 DS,即:DS 存储数据段的段值MOV AX, 4C00HCOD

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

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

6、DATA1ENDSSEGMENT ;定义数据段 DATA2word2 DW 1234hDATA2word3 DW 9876hDATA2ENDSSEGMENT ;定义数据段 DATA3DATA3byte2 DB ?DATA3ENDSSEGMENT ;编写代码段 CODE1ASSUMECS:CODE1, DS:DATA1, ES:DATA2 ;(1)MOV AX, DATA1 ;(2)MOV DS, AX ;(3)MOV AX, DATA2 ;(4)MOV ES, AX ;(5)MOV AX, word1 ;访问段 DATA1 中的字变量word1MOV word2, AX ;访问段 DATA2

7、中的字变量word2ASSUMEDS:DATA3, ES:NOTHING ;(6)MOV AX, DATA3MOV DS, AXMOV BL, byte2 ;访问段 DATA3 中的字节变量byte2MOV AX, 4C00H ;(7)CODE1INT 21H ;(8) CODE1 ENDS语句(1)和(6)分别说明了段和段寄存器之间的对应关系,其中语句(6)重新说明语句(1)所指定的对应关系。二组语句(2)和(3)、(4)和(5)实现对段寄存器 DS 和 ES 赋初值。ASSUME 说明语句只起说明作用,它不会对段寄存器赋值,所以,必须对有关段寄存器赋值。在以后的其它源程序中也都是用此方法来

8、实现对数据段寄存器赋值的。语句(7)和(8)是调用中断 21H 的 4CH 号功能来结束本程序的执行,程序的返回代码由寄存器 AL 来确定。结束本程序执行的指令是所有主模块必须书写的语句。注意:代码段寄存器不能由程序员在源程序中对其赋值,其值是由操作系统在装入它进入系统运行时自动赋值的。6.1.3 堆栈段的说明堆栈段是一个特殊的段,在程序中可以定义它,也可以不定义。除了要生成 COM 型执行文件的源程序外,一个完整的源程序一般最好定义堆栈段。如果在程序中不定义堆栈段,那么,操作系统在装入该执行程序时将自动为其指定一个 64K 字节的堆栈段。在程序没有定义堆栈段的情况下,在由连接程序生成执行文件

9、时,将会产生一条如下的警告信息,但程序员可以不理会它,所生成的执行文件是可以正常运行的。warning xxxx: no stack segment (其中:xxxx 是错误号)在源程序中,可用以下方法来定义堆栈段。方法 1:SEGMENTDB 256 DUP(?) ;256 是堆栈的长度,可根据需要进行改变STACK1TOP LABEL WORDSTACK1ENDS以上堆栈段的定义如图 6.1 所示。由于堆栈是按地址从大到小的存储单元顺序来存放内容的,所以,在堆栈存储单元说明语句之后,再说明一个栈顶别名,这样,对栈顶寄存器 SP 的赋值就很方便。在源程序的代码段中,还要添加如下程序段,才能把

10、段 STACK1 当作堆栈段来使用。图 6.1 堆栈段定义示意图ASSUMESS:STACK1 ;可在代码段的段指定语句中一起说明CLI ;禁止响应可屏蔽中断MOV AX, STACK1MOV SS, AXMOV SP, offset TOP ;给堆栈段的栈顶寄存器 SP 赋初值STI ;恢复响应可屏蔽中断方法 2:STACK1SEGMENT STACK ;定义一个堆栈段,其段名为 STACK1DB 256 DUP(?)STACK1ENDS上述段定义说明了该段是堆栈段,系统会自动把段寄存器 SS 和栈顶寄存器SP 与该堆栈段之间建立相应的关系,并设置其初值,而不用在代码段对它们进行赋值。除了以

11、上二种方法外,还有一种更简洁的方法来定义堆栈段,有关内容请见 第 6.4.2 节中的叙述。6.1.4 源程序的结构下面的程序是一个完整的源程序,其功能是在屏幕上显示字符串“Hello, World.”。读者可参考此结构编写自己的简单程序。例 6.2 在屏幕上显示字符串“HELLO,WORLD.”解:可运行下面的控件,用鼠标左键单击程序中的某一行,可阅读其含义;单击“内存”可切换内存内容的显示方式。伪指令 END 表示源程序到此为止,汇编程序对该语句之后的任何内容都不作处理,所以,通常情况下,伪指令 END 是源程序的最后一条语句。伪指令 END 后面可附带一个在程序中已定义的标号,由该标号指明

12、程序的启动位置。如果源程序是一个独立的程序或主模块,那么,伪指令 END 后面一定要附带一个标号;如果源程序仅是一个普通模块,那么,其 END 后面就一定不能附带标号。6.2 程序的基本结构在学习高级语言程序设计时,我们知道了程序的三大主要结构:顺序结构、分支结构和循环结构。在汇编语言的源程序也同样有此三大结构,所不同的是它们的表现形式不同。用高级语言编写程序时,由于不使用“转移语句”而使这三种结构清晰明了。但在汇编语言的源程序中,很难不使用“转移语句”(除非是一些只有简单功能的程序),有时甚至会有各种各样的“转移语句”。由于存在这些转移语句,就使得:汇编语言源程序的基本结构显得不太明确。如果

13、源程序的编写者思维混乱,编写出来的源程序在结构上就会显得杂乱无章,反之,如果编写者条理清晰,安排的操作井然有序,那么,编写出来的程序在结构上就会一目了然。总之,不论是高级语言的源程序,还是汇编语言的源程序,其程序的三大基本结构也还是万变不离其宗的。6.2.1 顺序结构顺序结构是最简单的程序结构,程序的执行顺序就是指令的编写顺序,所以,安排指令的先后次序就显得至关重要。另外,在编程序时,还要妥善保存已得到的处理结果,为后面的进一步处理直接提供前面的处理结果,从而避免不必要的重复操作。例 6.3 编写程序段,完成下面公式的计算(其中:变量 X 和 Y 是 32 位有符号数,变量 A,B 和 Z 是

14、 16 位有符号数)。A(X-Y+24)/Z 的商,B(X-Y+24)/Z 的余数解:SEGMENTX DD ?Y DD ?Z DW ?A DW ?B DW ?DATA1DATA1ENDSSEGMENTMOV AX, XMOV DX, X+2 ;用(DX:AX)来保存 32 位变量 X 的数值SUB AX,YSBB DX, Y+2 ;(DX:AX)-(Y+2:Y)ADD AX, 24DADC DX, 0 ;(DX:AX)+24IDIVZMOV A, AXMOV B, DXCODE1CODE1ENDS在编程序时,常常需要交换二变量之值。假设需要交换值的变量名为:var1 和 var2,临时增加的

15、变量名为 temp。常用的算法如下:temp = var1var1 = var2var2 = temp例 6.4 假设有二个字变量 word1 和 word2,编写程序段实现交换其值的功能。解:方法 1:用汇编语言指令简单“直译”上面的 交换数据方法SEGMENTword1DW ?word2DW ?temp DW ?DATA1DATA1ENDSSEGMENTMOV AX, word1MOV temp, AX ;上二语句实现语句“temp=word1”MOV AX, word2MOV word1, AX ;上二语句实现语句“word1=word2”MOV AX, tempMOV word2, A

16、X ;上二语句实现语句“word2=temp”CODE1CODE1ENDS这种方法虽然也能完成功能,但显然其不能充分利用汇编语言的特点,程序效率很低。方法 2:用汇编语言指令的特点来直接编译SEGMENTword1DW ?word2DW ?DATA1DATA1ENDSSEGMENTMOV AX, word1XCHG AX, word2MOV word1, AX ;能 XCHG word1, word2 来代替这三条指令吗?CODE1CODE1ENDS该方法充分利用了汇编语言的特点,不仅省去了中间变量 temp 的定义,而且程序的效率也提高了。6.2.2 分支结构分支结构是一种非常重要的程序结构

17、,也是实现程序功能选择所必要的程序结构。由于汇编语言需要书写转移指令来实现分支结构,而转移指令肯定会破坏程序的结构,所以,编写清晰的分支结构是掌握该结构的重点,也是用汇编语言编程的基本功。在程序中,当需要进行逻辑分支时,可用每次分二支的方法来达到程序最终分多支的要求,也可是用地址表的方法来达到分多支的目的。一、显示转移指令实现的分支结构在高级语句中,分支结构一般用 IF 语句来实现,在汇编语言中,课用无条件转移指令或条件转移指令实现的分支结构。如图 6.2 给出了二种常用的分支结构。在编写分支程序时,要尽可能避免编写“头重脚轻”的结构,即:当前分支条件成立时,将执行一系列指令,而条件不成立时,

18、所执行的指令很少。这样就使后一个分支离分支点较远,有时甚至会遗忘编写后一分支程序。这种分支方式不仅不利于程序的阅读,而且也不便将来的维护。所以,在编写分支结构时,一般先处理简单的分支,再处理较复杂的分支。对多分支的情况,也可遵循“由易到难”的原则。因为简单的分支只需要较少的指令就能处理完,一旦处理完这种情况后,在后面的编程过程中就可集中考虑如何处理复杂的分支。 (a) if endif 结构 (b) ifelseendif 结构图 6.2 分支结构的二种结构例 6.5 已知字节变量 CHAR1,编写一程序段,把它由小写字母变成大写字母。解:SEGMENTCHAR1 DB ?DATA1DATA1

19、 ENDSSEGMENTMOV AL, CHAR1CMP AL, aJB nextCMP AL, zJA nextCODE1SUB CHAR1, 20H ;指令 AND CHAR1, 0DFH 也可以next:CODE1 ENDS例 6.6 编写一程序段,计算下列函数值。其中:变量 X 和 Y 是有符号字变量。解:SEGMENTX DW ?Y DW ?DATA1DATA1 ENDSSEGMENTCODE1MOV AX, XCMP AX, 0JGE case23ADD AX, 10 ;第一种情况的计算结果JMP resultCMP AX, 10DJG case3MOV BX, 30DIMULBX

20、 ;第二种情况的计算结果case23:JMP resultcase3: SUB AX, 9 ;第三种情况的计算结果MOV Y, AX ;把计算结果保存到变量 Y 中result:CODE1 ENDS例 6.7 把下列 C 语言的语句改写成等价的汇编语言程序段(不考虑运算过程中的溢出)。If (a+b 0 else a = 21;其中:变量 a,b 和 c 都是有符号的整型(int)变量。解:SEGMENTA DW ?B DW ?C DW ?DATA1DATA1 ENDSSEGMENTMOV AX, AADD AX, BJLE _ELSE ;ADD 指令会改变算术标志位 TESTC, 1 ;C%

21、2=0,也就是:看 C 的最低位是否为 0JNZ _ELSEMOV A, 62DCODE1JMP NEXT_ELSE: MOV A, 21DNEXT: CODE1 ENDS例 6.8 用地址转移表实现下列 C 语言的 switch 语句,其中:变量 A 和 B 是有符号的整型(int)变量。switch(a%8)b = 32;case 0: break;case 1:b = a + 43;case 2: break;b = 2*a;case 3: break;b-;case 4: break;case 5:case 6:case 7:printf(“Function 5_6_7”);break

22、;解:SEGMENTA DW ?B DW ?TableDW case0. case12, case12, case3DW case4, case567, case567, case567MSG DB Function 5_6_7$DATA1DATA1 ENDSSEGMENTMOV AX, AMOV BX, AXAND BX, 7 ;得到 BX 的低三位,实现 a%8 的计算SHL BX, 1 ;由于地址表是字类型,其下标要乘 2CODE1JMP TableBX;利用地址表实现多路转移MOV B, 32Dcase0:JMP nextADD AX, 43DMOV B, AXcase12:JMP n

23、extSHL AX, 1MOV B, AXcase3:JMP nextDEC Bcase4:JMP nextLEA DX, MSGMOV AH, 9INT 21Hcase567:JMP nextnext: CODE1 ENDS用地址表实现多路转移的关键在于:转移入口的地址表和转移情况可整数化。如果这二个要求有一个不满足,或很难构造,则无法使用该方法。为了改善汇编语言源程序的结构,减少显式转移语句所带来混乱,在宏汇编 MASM 6.11 系统中,增加了表达分支结构的伪指令。该伪指令的书写格式与高级语言的书写方式相类似,汇编程序在汇编时会自动增加转移指令和相应的标号。理解并掌握该知识,对将来学习编

24、译原理课程也有一定的帮助。分支伪指令的具体格式如下:格式 1:.IF condition ;以英文“句号”开头指令序列 ;条件“condition“成立时所执行的指令序列.ENDIF格式 2:.IF condition指令序列 1.ELSE指令序列 2 ;条件“condition“不成立时所执行的指令序列.ENDIF格式 3:.IF condition1指令序列 1.ELSEIF condition2指令序列 2 ;条件“condition2“成立时所执行的指令序列.ENDIF其中:条件表达式“condition”的书写方式与 C 语言中条件表达式的书写方式相似,也可用括号来组成复杂的条件表达

25、式。条件表达式中可用的操作符有:=(等于)、!=(不等)、(大于)、=(大于等于)、=a 显示整个大写字母表MOV AH, 09HMOV DX, OFFSET MSG3INT 21H.WHILE 1 ;循环条件为永真的循环MOV AH, 07HINT 21H ;不带回显地从键盘读一个字符.BREAK .IF AL = 13 ;如果输入“回车”键,则终止循环.CONTINUE .IF (AL9);如果字符不是数字字符,则继续循环MOV DL, ALMOV AH, 02HINT 21H ;显示所输入的数字字母.ENDWMOV AX, 4C00HINT 21HENDSCODE1END START6.

26、3 段的基本属性在通常情况下,一个复杂的应用程序会由若干个模块组成,一个模块又会含有多个段。而不同模块的段之间、同一模块的段之间往往存在某种联系,这种联系就要体现在段属性的说明上。段定义的一般格式如下:SEGMENT 对齐类型 组合类型 类别段名段名 ENDS段属性“对齐类型”、“组合类型”和“类别”要按此顺序说明,但这些可选项可根据需要选择书写。如果源程序中不指定某个属性,那么,汇编程序将使用该属性的缺省值。程序中的段名可以是唯一的,也可以与其它段同名。在同一模块中,如果有二个段同名,则后者被认为是前段的后续,这样,它们就属同一段。当同一模块出现二个同名段时,则后者的可选项属性要么与前者相同

27、,要么不写其属性而选用前者的段属性。例 6.13 同段名的作用SEGMENT ;第一个数据段DATA1MSG DB “Hello, “DATA1 ENDSSEGMENT ;第一个代码段CODE1ASSUME CS:CODE1, DS:DATA1MOV AX, DATA1MOV DS, AXMOV DX, offset MSGMOV AH, 9START:INT 21HCODE1 ENDSSEGMENT ;第二个数据段DATA1DB “World.$“DATA1 ENDSSEGMENT ;第二个代码段MOV AX, 4C00HCODE1INT 21HENDSEND STARTCODE1END在上

28、面的例子中,第二个数据段是第一个数据段的后续,汇编程序把它们是合二为一,上述的代码段也如此。下面,详细说明段属性的含义及其作用。6.3.1 对齐类型(ALIGN)对齐类型表示当前段对起始地址的要求,连接程序(LINK.EXE)按表 6.1 的地址格式来定位段的起始地址。在进行段定位时,会根据其定位类型进行定位的,所以,各段之间就有可能出现一些空闲字节,即可能浪费几个字节单元。段对齐类型 PARA 是一个适用于所有段类型的对齐类型,它也是缺省的对齐类型。对齐类型 BYTE 和 WORD 通常用于数据段的定位,对齐类型 DWORD 通常用于 80386 及其以后 CPU 代码段的定位。表 6.1

29、段对齐类型与段起始地址之间的对应关系对齐类型 起始地址(二进制) 功能说明 最多的空闲字 节数BYTE xxxx xxxx xxxx xxxx xxxx 下一个字节地址 0WORD xxxx xxxx xxxx xxxx xxx0 下一个字地址 1DWORD xxxx xxxx xxxx xxxx xx00 下一个双字地址 3PARA xxxx xxxx xxxx xxxx 0000 下一个节地址 15PAGE xxxx xxxx xxxx 0000 0000 下一个页地址 1276.3.2 组合类型(COMBINE)组合类型是告诉连接程序如何把不同模块中段名相同的段合并在一起。具体的组合类型

30、如下:NONE 表示当前段在逻辑上独立于其它模块,并有其自己的基地址。NONE 是缺省的组合类型。PUBLIC 表示当前段与其它模块中同段名的 PUBLIC 类型段组合成一个段。组合的先后次序取决于 LINK 程序中目标模块排列的次序。在组合时,后续段的起始地址要按其对齐类型进行定位,所以,同名段之间可能有间隔。COMMON 表示当前段与其它模块中同名段重叠,也就是说,它们的起始地址相同。最终段的长度是同名段的最大长度。由于段覆盖,所以,前一同名段中的初始化数据被后续段的初始数据覆盖掉。STACK 组合类型 STACK 表示当前段是堆栈栈,其组合情况与 PUBLIC相同。AT 数值表达式 该数

31、值表达式是当前段所指定的绝对起始地址的段地址。6.3.3 类别(CLASS)类别是一个由程序员指定的用单引号括起来的字符串。如果一个段没有给出类别,那么,这个段的类别就为空。类别是用于段的分类,连接程序利用该类别来调整同名、同类别的段,并使它们相邻。典型的类别是“Data“和“Code“。如果指定某段的类别是“Code“,那么,该段最好是代码段,这样,有的调试程序(如:CodeView)就可以顺序工作。例如:SEGMENT WORD PUBLIC “Data“DATA1DATA1 ENDS上述段定义说明了该段的起始地址是下一个字地址、组合类型为 PUBLIC、段类别是“Data“。6.3.4

32、段组(GROUP)段组伪指令 GROUP 是用于把源程序模块中若干个段结合成一个组,并对该段组定义一个段组名。段组伪指令的格式如下:段组名 GROUP 段名, 段名, 其中:段名之间要用逗号间隔,段名也可以用表达式“SEG 变量”或“SEG 标号”。下面举例说明段组伪指令的使用方法和作用。例 6.12 段组的作用方法 1:用一个段寄存器对应二个数据段SEGMENT ;第一个数据段DATA1b1 DB 10hDATA1 ENDSSEGMENT ;第二个数据段DATA2b2 DB 23hDATA2 ENDSSEGMENTCODE1ASSUME CS:CODE1, DS:DATA1 ;(1)MOV

33、AX, DATA1MOV DS, AX ;(2)把数据段 DATA1 的段值赋给段寄存器DSMOV BL, b1 ;(3)引用 DS 来访问 DATA1 中的变量 b1ASSUME DS:DATA2 ;(4)MOV AX, DATA2MOV DS, AX ;(5)把数据段 DATA2 的段值赋给段寄存器DSMOV AL, b2 ;(6)引用 DS 来访问 DATA2 中的变量 b2START:ENDSCODE1END START在上例中,语句(1)说明 DS 与 DATA1 建立联系,语句(2)对 DS 赋值,语句(3)用 DS 来访问 DATA1 段的变量名。语句(4)说明 DS 与 DAT

34、A2 建立联系,语句(5)对 DS 赋值,语句(6)用 DS 来访问 DATA2 段的变量名。在该例子中,因为只使用一个段寄存器 DS 来对应二个数据段,所以,需要切换 DS 的对应关系(如:语句(4)。但我们也可以用段寄存器 DS 和 ES 来分别对应段 DATA1 和 DATA2,这样,方法 1 就可变成方法 2。方法 2:用二个段寄存器对应二个数据段SEGMENTDATA1b1 DB 10hDATA1 ENDSSEGMENTDATA2b2 DB 23hDATA2 ENDSSEGMENTCODE1ASSUME CS:CODE1, DS:DATA1, ES:DATA2MOV AX, DATA

35、1MOV DS, AX ;把数据段 DATA1 的段值赋给段寄存器 DSMOV AX, DATA2MOV ES, AX ;把数据段 DATA2 的段值赋给段寄存器 ESMOV BL, b1 ;引用 DS 来访问 DATA1 中的变量 b1MOV AL, b2 ;引用 ES 来访问 DATA2 中的变量 b2START:ENDSCODE1END START我们还可以用段组来简化段寄存器的使用,把段 DATA1 和 DATA2 组成一个数据段。所以,把方法 2 再改写成方法 3 的形式。方法 3:用一个段组组成二个数据段GSEG GROUPDATA1, DATA2 ;把段 DATA1 和 DATA

36、2 定义成一个段组DATA1 SEGMENTb1 DB 10hDATA1 ENDSSEGMENTDATA2b2 DB 23hDATA2 ENDSSEGMENTCODE1ASSUME CS:CODE1, DS:GSEGMOV AX, GSEGMOV DS, AX ;把段组 GSEG 的段值赋给段寄存器 DSSTART:MOV BL, b1 ;引用 DS 来访问 DATA1 中的变量 b1MOV AL, b2 ;引用 DS 来访问 DATA2 中的变量 b2ENDSCODE1END START定义段组后,段组内各段所定义的标号和变量,除了与定义它们的段起始点相关外,还与段组的起始点相关。规定如下:

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

38、源程序所采用的存储模式。该伪指令说程序所使用的存储模式,汇编程序将用该存储模式生成相应的 ASSUME 和 GROUP 语句,同时也为其它的简化段创建等价的预定义。程序存储模式说明伪指令的格式如下: .MODEL 存储模式,语言类型 ,操作系统类型 ,堆栈类型程序可选的存储模式有:TINY、SMALL、COMPACT、MEDIUM、LARGE、HUGE和 FLAT。伪指令.MODEL 必须写在源程序的首部,且只能出现一次,其前内容只能是注释。如果用伪指令来指定程序所遵循的语言类型,那么,将不允许子程序的嵌套定义。与子程序定义有关的内容请见 第 7.5 节。一、存储模式如果要用汇编语言编写被高级

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

40、a DistanceSegment WidthData 取数据段的段值MOV DS, AX ;把给段寄存器 DS 赋值MOV DX, offset MSGMOV AH, 9HINT 21hMOV AX, 4C00HINT 21hEND另外,在汇编程序 MASM 中,还提供了二组简化的代码伪指令:.STARTUP 和.EXIT。、.STARTUP在代码段的开始,用于自动初始化寄存器 DS、SS 和SP;、.EXIT用于结束程序的运行,它等价于下列二条语句:MOV AH, 4CHINT 21h当使用汇编程序 TASM 时,以上二条伪指令分别改为:STARTUPCODE 和EXITCODE。假设使用

41、汇编程序 MASM,那么,例 6.15 可改写成例 6.16 的形式。例 6.16.MODEL SMALL.STACK 128.DATAMSG DB “Simplified Segment Directives.$“.CODE.STARTUP ;自动初始化寄存器DS、SS 和 SPMOV DX, offset MSGMOV AH, 9HINT 21h.EXITEND6.5 源程序的辅助说明伪指令除了以上一些使用率较高的伪指令外,还有一些使用频率不太高的其它伪指令。下面仅列举几个这样的伪指令。6.5.1 模块名定义伪指令 NAME模块名定义伪指令 NAME 说明该源程序的模块名。该伪指令的一般格

42、式如下:NAME 模块名字符串6.5.2 页面定义伪指令 PAGE在源程序的开始,可用伪指令 PAGE 说明每页的最大行数、每行的字符数。该伪指令的一般格式为:PAGE 行数, 宽度其中:“行数”的取值范围为10, 255,“宽度”的取值范围为60, 132。如:伪指令“PAGE 60, 80”说明每页最多有 60 行,每行最多有 80 个字符。如果要在某指定行之后强行换页的话,那么,可在该行的下面书写不带操作数的伪指令 PAGE。6.5.3 标题定义伪指令 TITLE标题定义伪指令 TITLE 说明打印的标题,该标题可有 60 个字符。该伪指令在源程序头部只能书写一次,其一般格式如下:TIT

43、LE 标题字符串如果程序中没有使用 NAME 伪操作,则汇编程序将用“标题字符串”的前六个字符串作为其模块名。如果程序中既无 NAME 伪操作,也无 TITLE 伪指令,那么,源文件名将作为模块名。在汇编程序 TASM 环境下,标题定义伪指令是%TITLE。6.5.4 子标题定义伪指令 SUBTTL/SUBTITLE子标题定义伪指令 SUBTTL/SUBITLE 说明打印页上的子标题,该子标题也可有 60 个字符,它在每页的第三行打印。该伪指令的一般格式如下:SUBTTL/SUBTITLE 标题字符串6.6 习题6.1、简述 ASSUME 伪指令的作用,用该语句说明的段寄存器不用对其赋段地址的

44、初值? 6.2、用二种定义方法堆栈段,并给出相应的初始化语句或说明。 6.3、把下列 C 语句的语句改写成功能相同的汇编语言程序片段(其中:变量都为整型变量)。 1)、h = (key 2)、k = (k + 1 0xabcd) / 56;3)、for (i = s = 0; i 0; i-) s += i * 2; 6.4、把下列 C 语言的语句改写成等价的汇编语言程序段(不考虑运算过程中的溢出)。 If (a 10 c = 2;else a = 21-(+c);b-;其中:变量 a、b 和 c 都是有符号的整型(int)变量。6.5、假设内存单元中有三个字 a、b 和 c,编写一个程序,它

45、可判断它们能否构成一个三角形,若能,CF 为 1,否则,CF 为 0。 6.6、假设有三个无符号字存放在以 Buffer 为开始的缓冲区中,编写一个程序把它们从低到高排序好。 6.7、编写一个程序,它把一位十六进制数转化成相应的数字字符或大写字母。 6.8、编写一个程序,它把一个合法的十六字符转化成相应的数值。 6.9、编写一个程序,它可统计 32 数 DX:AX 中二进制位是 1 的位数。 6.10、编写一个程序,它把 CH 和 CL 中的二进制位依次交叉存入 AX 中。 6.11、编写一个程序,求出从内存单元 1000:0000 开始的 1024 个字的 32 位累加和,并把该值存入程序中的变量 Data 中。 6.12、假设从变量 Buff 开始存放了 200 个字,编写一个程序统计出其正数、0 和负数的个数,并把它们分别存入 N1、N2 和 N3 中。 6.13、用双重循环把下三角乘法表存入从 product 开始的 45 个字节中。 6.14、表示源程序结束的伪指令是什么?在其后所编写的指令在被汇编吗? 6.15、汇编语言程序一定会从代码段的第一条指令开始执行吗?如果不是,如何指定程序的入口地址? 6.16、编写一个程序,它把字符串 String 两端的空格删除(字符串以 0 结束)

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报