5.2 汇编语言源程序的格式在第四章介绍指令系统时曾给出若干程序举例,但是,它们仅仅是一些程序片段,并不是完整规范的汇编语言源程序。
下而给出一个比较简单,然而比较规范的汇编语言源程序。
例5.1要求将两个五字节16进制数相加,可以编写出以下汇编语言源程序。
DATA SEGMENT ;定义数据段DATA1 DB 0F8H,60H,0ACH,74H,3BH ;被加数DATA2 DB 0C1H,36H,9EH,0D5H,20H ;加数DATA ENDS ;数据段结束CODE SEGMMENT ;定义代码段ASSUME CS:CODE,DS:DA TASTART:MOV AX,DATAMOV DS,AX ;初始化DSMOV CX,5 ;循环次数送CXMOV SI,0;置SI初值为0CLC;清CF标志LOOPER:MOV AL,DATA2[SI];取一个字节加数ADC DA TA1[SI],AL;与被加数相加INC SI;SI加1DEC CX;CX减1JNZ LOOPER;若不等于0,转LOOPERMOV AH,4CHINT21H;返回DOS CODE END;代码段结束END START;源程序结束5.2.1 分段结构由上面的例子可以看出,汇编语言源程序的结构是分段结构形式,一个汇编语言源程序由若干段(SEGMENT)组成,每个段以SEGMENT语句开始,以ENDS语句结束。
整个源程序的结尾是END语句。
这里所说的汇编语言源程序中的段与前面讨论的CPU管理的存储器的段,既有联系,又在概念上有所区别。
我们已经知道,微处理器对存储器的管理是分段的,因而,在汇编语言程序中也要求分段组织指令、数据和堆栈,以便将源程序汇编成为目标程序后,可以分别装入存储器的相应段中。
但是,以8086/8088 CPU为例,它有四个段寄存器(CS,ES,SS和DS),因此CPU对存储器按照四个物理段进行管理,即数据段,附加段,堆栈段和代码段。
任何时侯CPU只能访问四个物理段。
而在汇编语言源程序中,设置段的自由度比较大。
例如一个源程序中可以有多个数据段或多个代码段等等。
一般来说,汇编语言源程序中段的数目可以根据实际需要而设定。
为了和CPU管理的存储器物理段相区别,我们将汇编语言程序中的段称为逻辑段。
在不致发生混淆的地方,有时简称为段。
在上面的简单源程序中只有两个逻辑段,一个逻辑段的名字是DATA,其中存放着与程序有关的数据,称为逻辑数据段;另一个逻辑段的名字是CODE,其中包含着程序的指令,称为逻辑代码段。
每个段内均有若干行语句(STA TEMENT),因此,可以说一个汇编源程序是由一行一行的语句组成的。
下面我们来讨论汇编语言语句的类型和组成。
5.2.2 汇编语言语句的类型和格式一.语句的类型汇编语言源程序中的语句可以分为两种类型:指令语句,伪指令语句。
⒈指令语句:它是能产生目标代码,CPU 可以执行的能完成特定功能的语句。
⒉伪指令语句:它是一种不产生目标代码的语句,它仅仅在汇编过程中告诉汇编程序应如何汇编。
例如,告诉汇编程序已写出的汇编语言源程序有几个段,段的名字是什么;定义变量,定义过程,给变量分配存储单元,给数字或表达式命名等。
所以伪指令语句是为汇编程序在汇编时用的。
二.语句的格式指令语句与伪指令语句的格式是类似的。
一般情况下,汇编语言的语句可以由1~4部分构成:[名字]? 助记符? [操作数]? [;注释]其中带方括号的部分表示任选项,既可以有,也可以没有。
例5.1中有如下语句:LOOPER:MOV AL,DATA2[SI];取一个字节加数DA TA1DB0F8H,60H,0ACH,74H,3BH;被加数第一条语句是指令语句,其中“LOOP ER:”是名字,“MOV”是指令助记符,“AL,DA TA2[SI]”是操作数,“;”后面是注释部分;第二条语句是伪指令语句,其中“DATA1”是名字,“DB”是伪指令定义符,“0F8H,60H,0ACH,74H,3BH”是操作数,“;”后面是注释部分。
下面对汇编语言中的各个组成部分进行讨论。
⒈名字汇编语言语句的第一个组成部分是名字(Name)。
在指令语句中,这个名字是一个标号。
指令语句中的标号实质上是指令的符号地址。
并非每条指令语句必须有标号,但如果一条指令前面有一标号,则程序中其它地方就可以引用这个标号。
在例5.1中,START、LOOPER 就是标号。
标号后面通常有一个冒号。
标号有三种属性:段、偏移量和类型。
①标号的段属性是定义标号在程序段的段地址,当程序中引用一个标号时,该标号的段值应在CS寄存器中。
②标号的偏移量属性表示标号所在段的起始地址到定义该标号的地址之间的字节数。
偏移量是一个16位无符号数。
③标号的类型属性有两种:NEAR和FAR。
前一种标号可以在段内被引用,地址指针为2个字节;后一种标号可以在其它段被引用,地址指针为4字节。
如果定义一个标号时后跟冒号,则汇编程序确认其类型为NEAR。
伪指令语句中的名字可以是变量名、段名、过程名。
与指令语句中的标号不同,这些伪指令语句中的名字并不总是任选的,有些伪指令规定前面必须有名字,有些则不允许有名字,也有一些伪指令的名字是任选的。
即不同的伪指令对于是否有名字有不同的规定。
伪指令语句的名字后面通常不跟冒号,这是它和标号的一个明显区别。
很多情况下伪指令语句中的名字是变量名,变量名代表存储器中一个数据区的名字,例如例5.1中的DATA1、DA TA2就是变量名。
变量也有三种属性:段、偏移量和类型。
①变量的段属性是变量所代表的数据区所在段的段地址。
由于数据区一般在存储器的数据段中,因此变量的段地址常常在DS和ES寄存器中。
②变量的偏移量属性是该变量所在段的起始地址与变量的地址之间的字节数。
③变量的类型属性有BYTE(字节)、WORD(字)、DWORD (双字)、QWORD (四字)、TBYTE(十字)等,表示数据区中存取操作对象的大小。
⒉助记符汇编语言语句中的第二个组成部分是助记符(Memonic)。
在指令语句中的第二部分是CPU指令系统中指令的助记符,例如:MOV ADC等等。
助记符约有90多种,在第四章中已经进行了详细的讨论。
在伪指令语句中的第二部分是伪指令的定义符,例如:DB、SEGMENT、ENDS 、END 等都是伪指令定义符。
它们在程序中的作用是定义变量的类型、定义段以及告诉汇编程序结束汇编等。
关于伪指令的作用和使用方法,将在本章第三节进行讨论。
⒊操作数汇编语言语句中的第三个组成部分是操作数。
在指令语句中是指令的操作数,可能有单操作数或双操作数,也可能无操作数;而在伪指令中可能有更多个操作数。
当操作数不止一个时,相互之间应该用逗号隔开。
可以作为操作数的有:常数、寄存器、标号、变量和表达式等。
⑴常数常数就是指令中出现的哪些固定值,可以分为数值常数和字符串常数两类。
例如,立即数寻址时所有的立即数,直接寻址时所有的地址,ASCII字符串都是常数,常数是除了自身的值以外,没有其它属性的数值。
在源程序中,数值常数按其基数的不同,可有二进制数,八进制数,十进制数,十六进制数等几种不同表示形式。
汇编语言用不同的后缀加以区别。
还应指出,汇编语言中的数值常数的第一位必须是数字,否则汇编时将被看成是标识符,如常数B7H应写成0B7H,FFH应写成0FFH。
字符串常数是由单引号括起来的一串字符。
例如…ABCDEFG‟和…179‟。
单引号内的字符在汇编时都以ASCII的代码形式存放在存储单元中。
如上述两字符串的ASCII代码为41H,42H,43H,44H,…,48H和31H,37H,39H。
字符串最长允许有255个字符。
⑵寄存器8086/8088CPU的寄存器可以作为指令的操作数。
⑶标号由于标号代表一条指令的符号地址,因此可以作为转移(无条件转移或条件转移)、过程调用CALL以及循环控制LOOP指令的操作数。
⑷变量因为变量是存储器中某个数据区的名字,因此在指令中可以作为存储器操作数。
⑸表达式汇编语言语句中的表达式,按其性质可分为两种:数值表达式和地址表达式。
数值表达式产生一个数值结果,只有大小,没有属性。
地址表达式的结果不是一个单纯的数值,而是一个表示存储器地址的变量或标号,它有三种属性:段、偏移量和类型。
表达式中常用的运算符有以下几种:1) 算术运算符常用的算术运算符有:+(加),-(减),*(乘),/(除)和MOD(模除,即两个整数相除后取余数)等。
以上算术运算符可用于数值表达式,运算结果是一个数值。
在地址表达式中通常只使用其中的+和-(加和减)两种运算符。
2) 逻辑运算符逻辑运算符有:AND(逻辑“与”),OR(逻辑“或”)、XOR(逻辑“异或”)和NOT(逻辑“非”)。
逻辑运算符只用于数值表达式中对数值进行按位逻辑运算,并得到一个数值结果。
对地址进行逻辑运算是没有意义的。
3) 关系运算符关系运算符有:EQ(等于),NE(不等),LT(小于),GT(大于),LE(小于或等于),GE(大于或等于)等。
参与关系运算的必须是两个数值或同一段中的两个存储单元地址,但运算结果只可能是两个特定的数值之一:当关系不成立(假)时,结果为0(全0);当关系成立(真)时,结果为0FFFFH(全1)。
例如:MOV AX,4 EQ 3 ;关系不成立,故(AX)←0MOV AX,4 NE 3 ;关系成立,故(AX)←0FFFFH4) 分析运算符分析运算符用于分析一个存储器操作数的属性,如段值,偏移量和类型等,或取得它所定义的存储空间的大小。
分析运算符有SEG、OFFSET、TYPE、SIZE和LENGTH等。
① SEG运算符利用运算符SEG可以得到一个标号或变量所在段的段地址。
例如下面两条指令将变量ARRAY的段地址送DS寄存器。
MOV AX,SEG ARRAYMOV DS,AX② OFFSET运算符利用运算符OFFSET可以得到一个标号或变量的偏移地址,例如:MOV DI,OFFSET DA TA1③ TYPE运算符运算符TYPE的运算结果是一个数值,这个数值与存储器操作数类型属性的对应关系见表5.1。
下面是使用TYPE运算符的例子:V AR DW;变量V AR的类型为字ARRAY DD10 DUP(?);变量ARRAY的类型为双字STR DB'THIS IS TEST';变量STR的类型为字节…MOV AX,TYPE V AR;(AX)←2MOV BX,TYPE ARRAY;(BX)←4MOV CX,TYPE STR;(CX)←1…其中的DW、DD、DB等为伪指令定义符,这将在第三节中介绍。
表5.1TYPE返回值与类型的关系TYPE返回值存储器操作数的类型1 BYTE2 WORD4 DWORD6 FWORD8 QWORD10 TBYTE-1 NEAR-2 FAR④ LENGTH运算符如果一个变量已用重复操作符DUP说明其变量的个数,则利用LENGTH 运算符可得到这个变量的个数。