1、 1机器语言机器语言(Machine Language)机器语言是一种用二进制表示指令和数据,能被机器直接识别的计算机语言。它的缺点是不直观,不易理解和记忆,因此编写、阅读和修改机器语言程序都比较繁琐。但机器语言程序是计算机惟一能够直接理解和执行的程序,具有执行速度快、占用内存少等特点。2高级语言高级语言(High Level Language)如果说机器语言是面向机器的,那么高级语言(如BASIC,FORTRAN等)则是“面向过程”的语言。利用这些语言编程,程序员可以完全不考虑机器的结构特点,不必了解和熟记机器的指令系统,仅使用一些接近人们书写习惯的英语和数学表达式形式的语句去编制程序。这样
2、编写的程序与问题本身的数学模型之间有着良好的对应关系,可在各种机器上通用(不同机器之间仅做少量修改)。但是,这种用高级语言编写的源程序并不能在机器上直接执行,需要被翻译成对应的目标程序(即机器语言程序),机器才能运行。把具有这种翻译作用的程序称为解释程序或编译程序,见图5.1。图5.1 编译程序的功能示意图 由于高级语言程序是在未考虑机器的结构特点的条件下编写的,因而它就不能充分利用某种具体CPU所具有的某些特性,而通过编译或解释程序生成的目标程序往往比较冗长,占有较多的内存空间,执行时间也比较长,这就限制了它在某些场合下的运用。例如,实时的数据采集、检测和在线的实时控制等,往往要求程序的目标
3、代码尽可能少占内存并有尽可能快的执行速度,在这些场合下,使用高级语言编写的程序常常不能满足要求。3汇编语言汇编语言(Assembly Language)汇编语言是一种采用助记符表示的程序设计语言,即用助记符来表示指令的操作码和操作数,用标号或符号代表地址、常量或变量。助记符一般都是英文字的缩写,以方便人们书写、阅读和检查。实际上,用汇编语言编写的汇编语言源程序就是机器语言程序的符号表示,汇编语言源程序与其经过汇编所产生的目标代码程序之间有明显的一一对应关系,故也称汇编语言为符号语言。用汇编语言编写程序能够直接利用硬件系统的特性(如寄存器、标志、中断系统等)直接对位、字节、字寄存器或存储单元、I
4、/O端口进行处理,同时也能直接使用CPU 指令系统和指令系统提供的各种寻址方式,编制出高质量的程序,这样的程序不但占用内存空间少,而且执行速度快。当然,由于源程序和所要解决的问题的数学模型之间的关系不够直观,使得汇编语言程序设计需要较多的软件开发时间,也增加了程序设计过程中出错的可能性。用汇编语言编写的源程序也需要翻译成目标程序才能被机器执行。这个翻译过程称为汇编,完成汇编任务的程序称为汇编程序,见图5.2。图5.2 汇编程序的功能示意图 汇编程序是最早也是最成熟的一种系统软件。它除了能够将汇编语言源程序翻译成机器语言程序这一主要功能外,还能够根据用户的要求自动分配存储区域(包括程序区、数据区
5、、暂存区等);自动地把各种进位制数转换成二进制数,把字符转换成ASCII码,计算表达式的值等;自动对源程序进行检查,给出错误信息(如非法格式,未定义的助记符、标号,漏掉操作数等)等。具有这些功能的汇编程序又称为基本汇编(或小汇编ASM)。在基本汇编的基础上,进一步允许在源程序中把一个指令序列定义为一条宏指令的汇编程序,就叫做宏汇编(MASM)。它包含全部ASM功能,还增加了宏指令、结构、记录等高级汇编语言功能。本章讲述:4.1 汇编语言的格式4.2 语句行的构成4.3 指示性语句4.4 指令语句4.5 汇编语言程式设计及举例4.1 汇编语言的格式 4.1.1 8086汇编语言程序的一个例子MY
6、_DATA SEGMENT ;定义数据段SUM DB?;为符号SUM保留一个字节MY_DATA ENDS ;定义数据段结束MY_CODE SEGMENT ;定义码段 ASSUME CS:MY_CODE,;规定CS和DS的内容 DS:MY_DATAPORT_VAL EQU 3 ;端口的符号名GO:MOV AX,MY_DATA ;DS初始化为MY_DATA MOV DS,AX MOV SUM,0 ;清SUM单元CYCLE:CMP SUM,100 ;SUM单元与100相比较 JNA NOT_DONE ;若未超过,转至NOT_DONE MOV AL,SUM ;若超过,把SUM单元的内容 OUT POR
7、T_VAL,AL ;通过AL输出 HLT ;然后停机NOT DONE:IN AL,PORT_VAL ;未超过时,输入下一个字节 ADD SUM,AL ;与以前的结果累加 JMP CYCLE ;转至CYCLEMY CODE ENDS ;码段结束 END GO ;整个程序结束 分段结构分段结构 由上面的例子可以看出,汇编语言源程序的结构是分段结构形式,一个汇编语言源程序由若干段(SEGMENT)组成,每个段以SEGMENT语句开始,以ENDS语句结束。整个源程序的结尾是END语句。这里所说的汇编语言源程序中的段与前面讨论的CPU管理的存储器的段,既有联系,又在概念上有所区别。我们已经知道,微处理器
8、对存储器的管理是分段的,因此,在汇编语言程序中也要求分段组织指令、数据和堆栈,以便将源程序汇编成为目标程序后,可以分别装入存储器的相应段中。但是,以8086/8088 CPU为例,它有四个段寄存器(CS,ES,SS和DS),因此CPU对存储器按照四个物理段进行管理,即数据段、附加段、堆栈段和代码段。任何时侯CPU只能访问四个物理段。而在汇编语言源程序中,设置段的自由度比较大。例如,一个源程序中可以有多个数据段或多个代码段等等。一般来说,汇编语言源程序中段的数目可以根据实际需要而设定。为了和CPU管理的存储器物理段相区别,我们将汇编语言程序中的段称为逻辑段。在不致发生混淆的地方,有时简称为段。在
9、上面的简单源程序中只有两个逻辑段,一个逻辑段的名字是DATA,其中存放着与程序有关的数据,称为逻辑数据段;另一个逻辑段的名字是CODE,其中包含着程序的指令,称为逻辑代码段。每个段内均有若干行语句(STATEMENT),因此,可以说一个汇编源程序是由一行一行的语句组成的。下面我们来讨论汇编语言语句的类型和组成。4.1.2 8086汇编语言源程序的格式 8086的汇编语言的源程序是分段的,由若干个段形成一个源程序。源程序的一般格式为:NAME1 SEGMENT 语句 语句 NAME1 ENDS NAME2 SEGMENT 语句 语句 NAME2 ENDS END标号 每一个段有一个名称,以符号S
10、EGMENT作为段的开始,以语句ENDS作为段的结束。这两者都必须有名称,而且名称必须相同。由若干个段组成一个源程序,整个源程序以语句END作为结束。总之,8086的源程序是由若干段组成的,而一个段又是由若干个语句行组成的。所以,语句行是汇编语言源程序的基础。4.2 语句行的构成 语句行是由标记(Token)及分隔符按照一定的规则组织起来的,标记是IBM宏汇编源程序的最小的、有意义的单位。汇编语言语句的类型和格式汇编语言语句的类型和格式 1语句的类型 汇编语言源程序中的语句可以分为两种类型:指令语句和伪指令语句。(1)指令语句:它是能产生目标代码,CPU 可以执行的能完成特定功能的语句。(2)
11、伪指令语句:它是一种不产生目标代码的语句,它仅仅在汇编过程中告诉汇编程序应如何汇编。例如,告诉汇编程序已写出的汇编语言源程序有几个段,段的名字是什么;定义变量,定义过程,给变量分配存储单元,给数字或表达式命名等。显然,伪指令语句是汇编程序在汇编时使用的。由上例看出8086汇编的一个语句行由以下4个部分组成:标号(或名称)操作码 操作数 ;注释 各部分之间至少要用一个空格作为间隔。IBM宏汇编对于语句行的格式是自由的,但如果写成格式化就便于阅读,建议读者按格式化来写语句行。另外,IBM宏汇编并不要求一个语句只能写一行,一个语句可以有后续行,规定以字符“&”作为后续行的标志。4.2.1 标记 1.
12、IBM宏汇编的字符集 IBM宏汇编中所使用的字符集仅是ASCII和EBCDIC(扩展的BCD码)字符集的一个子集。它由以下几部分组成:(1)字母 包含大写的英文字母:ABCDXYZ;小写的英文字母:abcxyz。(2)数字 阿拉伯数字:0123456789。(3)特殊字符 可打印字符如图4-1中所示。非打印字符有:空格、制表符(TAB键)、回车和换行。若在源程序中包含任何不属于上列字符集中的字符,则汇编程序就把它们作为空格处理。虽然字符“&”是字符集中的一个字符,但紧跟在回车换行之后的符号“&”是代表一个连续行,所以,汇编程序也把它当做空格处理。2.界符(Delimiters)界符是一些特殊字
13、符,利用它们可以表明某个标记的结束,它们本身也有一定的意义,这一点就与分隔符(空格)不同。例子中的冒号(:)、逗号(,)都是一种界符。IBM宏汇编中的界符集如图4-2所示。IBM宏汇编中的界符集语句中有了界符就可以不再用分隔符,但为了程序更清晰可读,有时仍用分隔符。3.常量(Constants)凡是出现在8086源程序中的固定值(它在程序运行期间不会变化),就称为常量。例子中的数0、3、100等都是常量,而且是数字常量。IBM宏汇编中允许的常量为:(1)数字(整数)常量 二进制常量 以字母B结尾的由一串“0”和“1”组成的序列。例如,00101100B。十进制常量 由若干个09的数字组成的序列
14、,可以以字母D作结尾,或没有任何字母作结尾。例如,1234D或1234。八进制常量 以字母Q(或字母O)结尾,由若干个07的数字组成的序列。例如255Q,377Q等。十六进制常量 以字母H结尾,由若干个09的数字或AF的字母所组成的序列。为了避免与标识符相混淆,十六进制数在语句中必须以数字打头。所以,凡是以字母AF开始的十六进制数,必须在前面加上数字0。例如56H,0BA3FH等。(2)字符串常量 字符串常量是由包含在单引号内的1至2个ASCII字符构成的。汇编程序把它们表示成一个字节序列,一个字节对应一个字符,把引号中的字符翻译成它的ASCII码值。例如字符“A”等价于 41H,字符“AB”
15、等价于4142H。在可以使用单字节立即数的地方,就可以使用单个字符组成的字符串常量;在可以使用字立即数的地方,就可以使用两个字符组成的字符串常量。只有在初始化存储器时才可以使用多于两个字符的字符串常量。4.标识符(Identifiers)标识符是由程序员自由建立起来的、有特定意义的字符序列,如例子中的SUM、CYCLE和PORT_VAL等等。一个标识符是由最多为31个字母、数字及规定的特殊字符(?_)等组成的,而且不能用数字打头(以免与十六进制数相混淆)。5.保留字(Reserved words)保留字看上去像标识符,但是它们在语言中有特殊的意义,而且不能用它们作为标识符。如例子中的SEGME
16、NT、MOV、EQU、AL等都是保留字。实际上凡是8086的指令助记符,汇编语言中的命令(伪指令),寄存器名等都是保留字。6.注释(Comment)为了使汇编语言的源程序更便于阅读和理解,常在源程序中加上注释。注释是在分号(;)后面的任意的字符序列,直到行的结尾。在汇编时,汇编程序对它们并不进行处理。在可打印的文件中,注释和源程序一起打印。4.2.2 符号 在汇编语言源程序中,为了使程序更具有普遍性,也便于程序的修改,用户常用符号等代替存储单元、数据、表达式等等,如例中的存储单元SUM、输入输出端口PROT_VAL等就是。符号(Symbol)是一种标识符,它要符合标识符的组成规则。在实际使用中
17、的符号可以分成五类,即寄存器、变量、标号、数、其他。每个符号都具有一定的属性,以允许汇编程序使用它来代表所需的信息。1.寄存器(Registers)8086的寄存器常在操作数场出现,代表某一个操作数。每个寄存器都有一种类型特性,由这些类型可以确定它是一个字节寄存器还是一个字寄存器。8086的标志位被看作是一位寄存器。2.变量(Variable)存放在存储单元中的操作数是变量,因为它们的值是可以改变的。在程序中出现的是存储单元地址的符号,即它们的名称。所有的变量都具有三种属性:(1)段值(SEGMENT),即变量单元所在段的段地址(段的起始地址)的高16位,低4位始终为0;(2)偏移量(OFFS
18、ET),即变量单元地址与段的起始地址之间的偏移量(16位);(3)类型(TYPE),变量有三种类型:字节(BYTE)、字(WORD)和双字(DOUBLE WORD)。变量通常是用存储器初始化命令定义的。3.标号(Label)标号是某条指令所存放单元的符号地址,它是转移(条件转移或无条件转移)指令或调用(CALL)指令的目标操作数。对于汇编程序来说,标号与变量是类似的,都是存储单元的符号地址。只是标号对应的存储单元中存放的是指令;而变量所对应的存储单元中存放的是数据。所以,标号也有三种属性:(1)段值,(2)偏移量,(3)类型。标号的类型与变量不同,它的类型是NEAR或是FAR。NEAR是指转移
19、到此标号所指的语句,或调用此子程序或过程,只需要改变IP值,而不改变CS值。也即转移指令或调用指令与此标号所指的语句或过程在同一段内。FAR与NEAR不同,要转移到标号所指的语句,或调用此子程序或过程,不仅需要改变IP的值,而且需要改变CS,即是段交叉转移或调用。若没有对标号进行类型说明,就假定它为NEAR。4.数 在汇编语言源程序中的常数也常以符号的形式出现,这样就更具有通用性,更便于修改。如上例中的就是把端口地址3定义为一个符号PORT_VAL。5.其他符号 除了上述4种符号以外,在汇编语言中还经常出现一些其他符号,把它们用作汇编程序中的伪指令名字。4.2.3 表达式 表达式(Expres
20、sions)是由上面讨论过的标记(Token)、符号(Symbol)通过运算符组合起来的。粗略地说,一个表达式是一个由操作数和运算符组合的序列,在汇编时它能产生一个值。1.操作数(Operands)一个操作数可以是一个寄存器名、一个常量(数字常量或字符串常量)或一个存储器操作数。(1)常量操作数 具有数字值的操作数是常量或是表示常量的标识符(符号)。(2)存储器操作数 存储器操作数,通常是标识符,可以分成标号(Label)和变量(Variable)两种。标号是可执行的指令语句的符号地址,通常是作为转移指令JMP和调用指令CALL的目标操作数。变量通常是指存放在一些存储单元中的值,这些值在程序运
21、行过程中是可变的。变量可以具有以下几种寻址方式:直接寻址16位地址偏移量包含在指令中;基址寻址由一个基址寄存器(BX或BP)的内容,加上一个在指令中指定的8位或16位位移量,决定变量的地址;变址寻址由一个变址寄存器(SI或DI)的内容,加上一个在指令中指定的8位或16位位移量,决定变量的地址;基址变址寻址由一个基址寄存器(BX或BP)的内容,加上一个变址寄存器(SI或DI)的内容,再加上一个在指令中指定的8位或16位位移量,决定变量的地址。作为存储器操作数的标号和变量都有三种属性:段值、段内地址偏移量、类型。一个运算符取一个或多个操作数的值,以形成一个新值。在IBM宏汇编中有五种运算符。2.运
22、算符(Operators)IBM宏汇编通常有以下几种运算符:算术运算符(Arithmetic Operators);逻辑运算符(Logical Operators);关系运算符(Relational Operators);分析运算符(Analytic Operators);合成运算符(Synthetic Operators)。(1)算术运算符 这是读者十分熟悉的运算符-+(加)、-(减)、*(乘)、/(除)运算符。另一个算术运算符是MOD(求余),它产生除法以后的余数。因此19/7是2(商是2),而19MOD7是5(余数是5)。算术运算符应用于数字操作数,结果也是数字的。当算术运算符应用于存储
23、器即地址操作数时其规则就更加严格:只有当结果有明确的、有意义的物理解释时,这些运算才是有效的。(2)逻辑运算符 按位操作的逻辑运算符有:AND(与)、OR(或)、XOR(异或)和NOT(非)。逻辑运算的操作数只能是数字的,而且结果是数字的。存储器地址操作数不能进行逻辑运算。注意:AND、OR、XOR和NOT,也是8086指令的助记符。但是,作为IBM宏汇编的运算符是在程序汇编时计算的。而作为指令的助记符,则是在程序执行时计算的。(3)关系运算符 在IBM宏汇编中有以下关系运算符:相等 EQ(Equal);不等 NE(Not Equal);小于 LT(Less Than);大于 GT(Great
24、er Than);小于或等于 LE(Less Than or Equal);大于或等于 GE(Greater Than or Equal)。关系运算的两个操作数,或者都是数字的,或者是同一个段的存储器地址。结果始终是一个数字值。若关系是假,则结果为0;若关系是真,则结果为0FFFFH。(4)分析运算符 分析运算符可以把存储器操作数分解为它的组成部分,如它的段值、段内偏移量和类型。(5)合成运算符 合成运算符可以由已经存在的存储器操作数生成一个段值与偏移量相同、而类型不同的新的存储器操作数。4.2.4 语句 如前所述,一个汇编语言的源程序是由一条条语句组成的,语句(Statements)就是完成
25、一个何种动作的说明。源程序中的语句可分成两类:指令语句,汇编程序把它们翻译成机器代码,这些代码命令8086执行某些操作。如MOV、ADD、JMP等。指示性语句(伪指令),汇编程序并不把它们(也不可能)翻译成机器代码,只是用来指示、引导汇编程序在汇编时进行一些操作,如定义符号、分配存储单元、初始化存储器等等,所以伪指令本身不占用存储单元。两种语句的格式是类似的。指令语句的格式为:标号:助记符 参数,参数 ;注释 指示性语句的格式为:名称 命令 参数,参数 ;注释 在一个指令语句中的标号后面跟有冒号(:),而在一个指示性语句中的名字后面没有冒号,这就是这两种语句在格式上的主要区别。一个标号与一条指
26、令的地址符号名相联系,标号可以作为JMP指令和CALL指令的目标操作数。指示性语句中的名字与指令的地址毫无关系,绝不能转向它。在指令语句中的标号,总是任选的;但在指示性语句中的名字,可能是强制的、任选的或禁止的,这取决于实际的命令。4.3 指示性语句 在IBM宏汇编中有以下几种指示性语句(Directive statements):(1)符号定义语句(Symbol definition);(2)数据定义语句(Data definition);(3)段定义语句(Segmentation definition);(4)过程定义语句(Procedure definition);(5)结束语句(Ter
27、mination)。4.3.1 符号定义语句 1.等值语句EQU EQU语句给符号名定义一个值,或定义为别的符号名,甚至可定义为一条可以执行的指令等。EQU语句的格式为:NAME EQU EXPRESSION EQU语句在未解除前,不能重新定义。2.等号(Equal sign)语句=此语句的功能与EQU语句类似,最大特点是能对符号进行再定义。3.解除语句PURGE 已经用EQU命令定义的符号,若以后不再用了就可以用PURGE语句来解除。PURGE语句的格式为:PURCE 符号1,符号2,符号n 注意:PURGE语句本身不能有名字。用PURGE语句解除后的符号可以重新定义。4.3.2 数据定义语
28、句 数据定义语句,为一个数据项分配存储单元,用一个符号名与这个存储单元相联系,且为这个数据提供一个任选的初始值。与数据项相联系的符号名称为变量。数据定义语句的例子如下:THINGDB?;定义一个字节BIGGER_THINGDW?;定义一个字BIGGEST_THINGDD?;定义一个双字 THING是一个符号名,它与在存储器中的一个字节相联系,即它是一个字节变量。BIGGER_THING也是一个符号名,它与在存储器中的一个字相联系,即它是一个字变量。BIGGEST_THING也是一个符号名,它与在存储器中的一个双字相联系,即它是一个双字变量。由汇编程序产生的目标码,产生指令和放指令的地址。在目标
29、码产生以后,指令已经存放在存储器中,可以执行了。在指令送至存储器的时候,数据项的初始值也可以送到存储器中。这意味着目标码除了包含指令和它们的地址以外,也可以包括数据项的起始值和它们的地址。这些初始值是由数据定义语句所规定的。例如:THING DB25 不仅使THING这个符号与一个字节的存储单元相联系,而且在汇编时会把25放入与THING相联系的存储单元中。所以THING是一个字节变量,它的初始值为25。同样,以下语句:BIGGERDW4142H 在汇编时就会把41H与42H分别放至与BIGGER_THING相联系的两个连续的字节单元中。下面的语句:BIGGESTDD12345678H 在汇编
30、时就会初始化,如图4-3所示。它定义了一个双字变量,且给了初始值。当汇编程序汇编时遇到“?”号,则它仍然为数据项分配相应的存储单元(DB分配一个字节、DW分配一个字、DD分配一个双字),但并不产生一个目标码来初始化这些存储单元。即“?”号是为了保留若干个存储单元,以便存放指令执行的中间结果。通常初始值能用一个表达式来规定,因为表达式是在汇编时计算的。同样,在存储单元中可以存放存储器地址值。存放内存单元的段内偏移量需用一个字;存放全地址,则需用两个字,一个字放段地址,另一个字放段内偏移量。在实际应用中,还经常会用到由字节、字或双字构成的表。这可由在数据定义语句的参数部分,引入若干个用逗号分隔的参
31、数就可以建立一个表。下列语句定义了一个包含2的权的字节的表:POWERS_2DB1,2,4,8,16 可以用DUP来缩写若干相同的值。因而,DUP利用给出的一个初值(或一组初值)以及这些值应该重复的次数来初始化存储器。DB 100 DUP(0);100个字节全初始化为0DW100 DUP(0);100个字全初始化为0DW10 DUP(?);保留10个字 可以用DB数据定义语句在内存中定义一个字符串。字符串中的每一个字符用它的ASCII码表示,为一个字节,故字符串的定义必须用DB命令。有两种定义字符串的方法:一种是字符串中的每一个字符分别定义,每一个字符之间用逗号分隔;另一种方法是在整个字符串的
32、前后都加单引号,例如:EXAM1DBTHIS IS AN EXAMPLE IBM宏汇编对在程序中涉及的每一个存储单元与一种类型联系起来,这样能对访问存储器的指令产生正确的目标码。例如,数据定义语句:SUMDB?告诉汇编程序,SUM是字节类型的,以后当遇到如下的指令语句:INCSUM 汇编程序就产生一个字节增量指令,而不是一个字增量指令。一个存储单元的类型如下:(1)数据字节。如SUMDB?;定义一个字节 (2)数据字(两个连续的字节)。如BIGGERDW?;定义一个字 (3)数据双字(四个连续的字节)。如BIGGEST DD?;定义一个双字(4)NEAR指令单元。如CYCLE:CMP SUM,
33、100(5)FAR指令单元。一个指令单元能出现在一条JMP或CALL语句中,若这个指令单元的类型是NEAR,汇编程序将产生一个段内JMP或CALL指令;若指令单元的类型是FAR,则产生一个段交叉JMP或CALL指令。一个NEAR指令单元规定了一个长度为两个字节的指针,即此指令单元在段内的地址偏移量。获得了此地址偏移量,就可以采用段内的转移或调用。一个FAR指令单元,规定了一个长度为四个字节的指针,即此指令单元所在段的段地址和段内的地址偏移量。只有获取了这四个字节,才能得到一个FAR指令单元的全地址,实现交叉的段调用或转移。一个存储单元地址加或减一个数字值而形成的新的存储单元与初始的存储单元有着
34、相同的类型。例如,SUM+2是字节型,BIGGER-3是字型,而CYCLE+1是一个NEAR型指令单元。分析运算符把存储器地址操作数分解为它们的各个组成部分。这些运算符是:(1)SEG,(2)OFFSET,(3)TYPE,(4)SIZE,(5)LENGTH。若在一个程序中,对它的数据段有如下定义DATA_TABLESEGMENTBUFFER1DB100 DUP(0)BUFFER2DW200 DUP(20H)BUFFER3DD100 DUP(13)DATA_TABLESENDS 其中的每一个存储单元都有一些属性(或组成部分)。分析运算符SEG,返回的是一个存储单元的段地址(即它所在段的起始地址)
35、;OFFSET运算符返回的是每一个存储单元地址的段内偏移量,即它与段地址之间的偏差。故语句:SEG BUFFER1SEGBUFFER2 是相同的,它们返回的地址都是DATA_TABLES的地址。所以,若要对数据段寄存器初始化,则可以采用指令:MOVAX,SEG BUFFER1MOVDS,AX 而OFFSET是各不相同的。若要向这些缓冲区填入新的数据,可以用一些地址指针,则可以用以下指令来初始化地址指针:MOVBX,OFFSET BUFFER1MOVSI,OFFSET BUFFER2 然后,就可以用这些指针来间接寻址这些缓冲区。TYPE运算符返回一个数字值,它表示存储器操作数的类型部分。字节、字
36、和双字的类型部分,分别是它们所占有的字节数。而指令单元的类型部分的值,没有实际的物理意义。LENGTH运算符返回一个与存储器地址操作数相联系的单元数(所定义的基本单元的个数)。注意:要用LENGTH返回的存储区必须用DUP()来定义,否则返回值为1。故可以利用LENGTH运算符对计数器进行初始化。分析运算符SIZE返回一个为存储器地址操作数所分配的字节数。一般来说,若一个存储单元操作数X,则 size X=(length X)(type X)IBM宏汇编中的合成运算符为PTR和THIS,它们能建立起一些新的存储器地址操作数。PTR运算符能产生一个新的存储器地址操作数(一个变量或标号)。新的操作
37、数的段地址和段内偏移量与PTR运算符右边的操作数的对应分量相同,而类型由PTR的左边的操作数指定。不像一个数据定义语句,PTR操作数并不分配存储器,它可以给已分配的存储器一个另外的定义。段交叉转移合成运算符THIS与PTR类似,也可以建立一个新的存储器地址操作数,并且不分配存储器。用运算符THIS建立起来的新的存储器地址操作数的类型在THIS中指定,而它的段地址和段内偏移量就是汇编时的当前值。THIS运算符。THIS运算符也可指定存储器操作数的类型。使用THIS运算符可以使标号或变量更具灵活性。例如,要求对同一个数据区既可以字节为单位,又可以字为单位进行存取,则可用以下语句:TAB1 EQU
38、THIS WORDTAB2 DB 100 DUP(?)上面TAB1和TAB2实际上代表同一个数据区,其中共有100个字节,但TAB1的类型为WORD(字类型),而TAB2的类型为BYTE(字节类型)。4.3.3 段定义语句 8086的存储器是分段的,所以8086必须按段来组织程序和利用存储器,这就需要有段定义语句。段定义的主要命令有:(1)SEGMENT,(2)ENDS,(3)ASSUME,(4)ORG。SEGMENT和ENDS语句把汇编语言源程序分成段,这些段就相应于存储器段,在这些存储器段中,存放相应段的目标码。汇编程序必须知道程序的段结构,并知道在各种指令执行时将访问哪一个段由段寄存器所
39、指向。这个信息是由ASSUME语句提供的。通常在汇编语言的源程序中,至少要定义码段(指令段)、堆栈段和数据段,有时还要定义附加段。每一个段必须有一个名称,如MY_DATA、MY_CODE等。一个段由命令SEGMENT开始,由命令ENDS结束,它们必须成对出现,而且它们的语句中必须有名称,名称必须相同。最后用语句END来结束整个源程序。ASSUME语句,只是使汇编程序知道在程序执行时各个段寄存器的值,而这些段寄存器的实际值(除了码段寄存器CS以外),还必须在程序执行时,用MOV指令来赋给。一般来说,存储器段具体在哪儿是不重要的,可由汇编程序来选择。但是,在有些情况下,可能要给汇编程序一些约束,例
40、如:“不要使这个段与别的段搭接”,保证这个段所用的第一个字节在偶数地址,这样对于一个字的访问可以在一个存储器读写周期完成。或“在下列地址开始这个段”。可以把这些约束写入到源程序中。另一个语句ORG(origin),它规定了段内的起始地址。伪指令ORG的一般格式为:ORG 表达式 此语句指定了段内在它以后的程序或数据块存放的起始地址,即以语句中的表达式的值作为起始地址,连续存放,除非遇到一个新的ORG语句。5.3.3 段定义伪指令段定义伪指令 段定义伪指令的用途是在汇编语言源程序中定义逻辑段。常用的段定义伪指令有SEGMENT/ENDS和ASSUME等。1SEGMENT/ENDS 格式:段名 S
41、EGMENT 定位类型 组合类型 类别 段名 ENDS SEGMENT 伪指令用于定义一个逻辑段,给逻辑段赋予一个段名,并以后面的任选项(定位类型、组合类型、类别)规定该逻辑段的其他特性。SEGMENT 伪指令位于一个逻辑段的开始部分,而ENDS伪指令则表示一个逻辑段的结束。在汇编语言源程序中,这两个伪指令定义符总是成对出现的,二者前面的段名必须一致。两个语句之间的部分即是该逻辑段的内容。例如,对于代码段,其中主要有指令及其他伪指令;对于数据段和附加段,主要有定义数据区的伪指令等等。一个源程序中不同逻辑段的段名可以各不相同。1)定位(Align)类型 定位类型任选项告诉汇编程序如何确定逻辑段的
42、边界在存储器中的位置。定位类型共有以下四种:BYTE(边界起始地址=B)该类型表示逻辑段从一个字节的边界开始,即可以从任何地址开始。此时本段的起始地址可紧接在前一个段的后面。WORD(边界起始地址=0B)该类型表示逻辑段从字的边界开始。2字节为1个字,此时本段的起始地址必须是偶数。PARA(边界起始地址=0 0 0 0B)该类型表示逻辑段从一个节(Paragraph)的边界开始(一节等于16个字节),也即段的起始地址能被16整除。故本段的起始地址(十六进制)应为0H。如果省略定位类型任选项,则默认其为PARA。PAGE(边界起始地址=0 0 0 0 0 0 0 0B)该类型表示逻辑段从页边界开
43、始(一页等于256个字节),也即段的起始地址能被256整除。故本段的起始地址(十六进制)应为00H。2)组合(Combine)类型 SEGMENT伪指令的第二个任选项是组合类型,它告诉汇编程序当装入存储器时各个逻辑段如何进行组合。组合类型共有以下六种。(1)不组合。如果SEGMENT伪指令的组合类型任选项缺省,则汇编程序认为这个逻辑段是不组合的。也就是说,不同程序中的逻辑段,即使具有相同的段名,也分别作为不同的逻辑段装入内存,不进行组合。但是,对于组合类型任选项缺省的同名逻辑段,如果属于同一个程序模块,则被集中成为一个逻辑段。(2)PUBLIC。连接时,对于不同程序模块中的逻辑段,只要具有相同
44、的段名,就把这些段集中成为一个逻辑段装入内存。(3)STACK。组合类型为STACK时,其含意与PUBLIC基本一样,即不同程序中的逻辑段,如果段名相同,则集中成为一个逻辑段。不过组合类型STACK仅限于作为堆栈区域的逻辑段使用。顺便提一下,在执行程序(.EXE)中,堆栈指针SP设置在这个集中以后的堆栈段的(最终地址1)处。(4)COMMON。连接时,对于不同程序中的逻辑段,如果具有相同的段名,则都从同一个地址开始装入,因而各个逻辑段将发生重叠。最后,连接以后段的长度等于原来最长的逻辑段的长度,重叠部分的内容是最后一个逻辑段的内容。(5)MEMORY。该类型表示当几个逻辑段连接时,本逻辑段定位
45、在地址最高的地方。如果被连接的逻辑段中有多个段的组合类型都是MEMORY,则汇编程序只将首先遇到的段作为MEMORY段,而其余的段均当作COMMON段处理。(6)AT表达式。这种组合类型表示本逻辑段根据表达式的值定位段地址。例如AT 8A00H,表示本段的段地址为8A00H,则本段从存储器的物理地址8A000H开始装入。3)类别(Class)SEGMENT伪指令的第三个任选项是类别,类别必须放在单引号内。类别的作用是在连接时决定各逻辑段的装入顺序。当几个程序模块进行连接时,其中具有相同类别名的逻辑段被装入连续的内存区,类别名相同的逻辑段,按出现的先后顺序排列。没有类别名的逻辑段,与其他无类别名
46、的逻辑段一起连续装入内存。例如,假设一个主程序中有五个逻辑段,段名和类别名分别为:STK1段 STACKCODE1段 无DATA1段 BUFFER DATA2段 TABLE DATA3段 BUFFER 还有一个子程序,包括四个逻辑段,段名和类别名分别为:DATA4段 TABLE DATA5段 BUFFER STK2段 STACK CODE2段 无 当将上述主程序和子程序进行连接时,两个程序模块中各逻辑段装入内存的顺序见图5.3。图5.3 逻辑段按类别装入内存的示意图 SEGMENT伪指令后面还有三个任选项:定位类型、组合类型和类别。在上面的格式中,它们都放在方括号内,表示可有可无。如果有,三者
47、的顺序必须符合格式中的规定。这些任选项是给汇编程序(MASM)和连接程序(LINK)的命令。SEGMENT伪指令后面的任选项告诉汇编程序和连接程序,如何确定段的边界,以及如何组合几个不同的段等。下面分别进行讨论。4.3.4 过程定义语句 过程是程序的一部分,它们可被程序调用。每次可以调用一个过程。当过程中的指令执行完后,控制返回调用它的地方。在8086中调用过程和从过程返回的指令是CALL和RET。这些指令可以有两种情况:段内的和段交叉的。段交叉指令把过程应该返回处的段地址和段内偏移量这两者都入栈保护(CALL指令)和退栈(RET指令)。段内的调用与返回指令只入栈和退栈段内的地址偏移量。过程定
48、义语句的格式为:PROCEDURE_NAME PROCNEAR或PROCEDURE_NAME PROCFARRETPROCEDURE_NAME ENDP 伪指令 PROC与ENDP都必须有名称,两者必须成对出现,名称必须相同。利用过程调用语句可以把程序分段,以便于阅读、理解、调试和修改。若整个程序由主程序和若干个子程序组成,则主程序和这些子程序必须一起包含在码段中(除非用段交叉调用)。主程序和各个子程序都作为一个过程,用上述的过程定义语句来定义。用段内CALL指令调用的过程,必须用段内的RET指令返回,这样的过程是NEAR过程;用段交叉CALL指令调用的过程,必须用段交叉RET指令返回,这样的
49、过程是FAR过程。过程定义语句PROC和ENDP(End Procedure)限定了一个过程且指出它是一个NEAR或FAR过程。这在两方面帮助了汇编程序。4.3.5 结束语句 除了一个例外(END结束语句)以外,每一个结束语句(Termination Statements)都与某个开始语句成对出现。例如,SEGMENT和ENDS,PROC和ENDP。惟一的例外就是END语句,它标志着整个源程序的结束,它告诉汇编程序,没有更多的指令要汇编了。END语句的格式是:END表达式 其中,表达式必须产生一个存储器地址值,这个地址是当程序执行时,第一条要执行的指令的地址。汇编语言和汇编语言和DOS操作系统
50、的接口操作系统的接口 当我们编写的汇编语言源程序是在DOS环境下运行时,必须了解汇编语言是如何同DOS操作系统接口的。当我们用编辑程序把源程序输入到机器中,用汇编程序把它转换为目标程序,用连接程序对其进行连接和定位时,操作系统为每一个用户程序建立了一个程序段前缀区PSP,其长度为256个字节,主要用于存放所要执行程序的有关信息,同时也提供了程序和操作系统的接口。操作系统在程序段前缀的开始处(偏移地址0000H)安排了一条INT 20H软中断指令。INT 20H中断服务程序由DOS提供,执行该服务程序后,控制就转移到DOS,即返回到DOS管理的状态。因此,用户在组织程序时,必须使程序执行完后能去