汇编语言程序格式Word格式.docx
《汇编语言程序格式Word格式.docx》由会员分享,可在线阅读,更多相关《汇编语言程序格式Word格式.docx(25页珍藏版)》请在冰豆网上搜索。
2汇编语言程序上机运行过程
3COM文件
第一节 汇编程序功能
建立,运行汇编语言程序
汇编程序的主要功能:
1.检查源程序,给出出错信息。
2.产生目标文件(.obj)和列表文件(.lst)。
3.展开宏指令。
第二节 伪指令
汇编语言程序中的语句可以由指令、伪指令和宏指令组成。
上一章我们介绍了8086指令系统中的6类指令,每一条指令都对应一种CPU操作。
伪指令又称为伪操作,它是在对源程序汇编期间由汇编程序处理的操作,它们可以完成如处理器选择、定义程序模式、定义数据、分配存储区、指示程序结束等功能。
宏指令是由用户按照宏定义格式编写的一段程序,其中语句可以是指令、伪指令,甚至是已定义的宏指令。
宏指令将在第七章中介绍。
伪指令和指令的区别在于,每一条指令必须生成机器代码,然后在程序运行期间由CPU来执行其操作;
而伪指令是在汇编期间由汇编程序执行的操作命令,除了数据定义及存储器分配伪指令分配存储器空间外,其它伪指令不生成目标码。
和各种指令一样,伪指令也是程序设计不可缺少的工具。
下面介绍一些常用的伪指令。
2.1处理器选择伪操作
.8086选择8086指令系统
.286选择80286指令系统
.286P选择保护模式下的80286指令系统
.386选择80386指令系统
.386P选择保护模式下的80386指令系统
.486选择80486指令系统
.486P选择保护模式下的80486指令系统
.586选择Pentium指令系统
.586P选择保护模式下的Pentium指令系统
2.2段定义伪指令
段定义伪指令是表示一个段开始和结束的命令,80x86有两种段定义的方式:
完整段定义和简化段定义,分别使用不同的段定义伪指令来表示各种段。
1.完整段定义伪指令的格式如下:
由于ASSUME伪指令只是指定某个段分配给哪一个段寄存器,它并不能把段地址装入段寄存器中,所以在代码段中,还必须把段地址装入相应的段寄存器中:
MOV AX,DATA_SEG1 ;
数据段地址
MOV DS,AX ;
存入DS寄存器
MOV AX,DATA_SEG2 ;
附加段地址
MOV ES,AX ;
存入ES寄存器
如果程序中还定义了堆栈段STACK_SEG,也需要把段地址装入SS中:
MOV AX,STACK_SEG ;
堆栈段地址
MOV SS,AX ;
注意,在程序中不需要用指令装入代码段的段地址,因为在程序初始化时,装入程序已将代码段的段地址装入CS寄存器了。
而DS和ES是指向一个称为“PSP”(程序段前缀)的一块区域的首地址。
为了对段定义作进一步地控制,SEGMENT伪指令还可以增加类型及属性的说明,其格式如下:
段名 SEGMENT [定位类型][组合类型]['
类别'
]
…
段名 ENDS
[]中的内容是可选的,一般情况下,这些说明可以不用。
但是,如果需要用连接程序把本程序与其他程序模块相连接时,就需要提供类型和属性的说明。
·
定位类型:
说明段的起始边界值(物理地址)。
定位类型
说明
BYTE
段可以从任何地址边界开始
WORD
段从字边界开始,即段的起始边界值为偶数
DWORD
段从双字的边界开始,即段的起始边界值为4的倍数
PARA
段从小段边界开始,即段的起始边界值为16(或10H)的倍数
PAGE
段从页边界开始,即段的起始边界值为256(或100H)的倍数
注意:
定位类型的缺省项是PARA,即在未指定定位类型的情况下,则连接程序默认为PARA。
BYTE和WORD用于把其它段(通常是数据段)连入一个段时使用;
DWORD一般用于运行在80386及后继机型上的程序。
组合类型:
说明程序连接时的段组合方法。
组合类型
PRIVATE
该段为私有段,连接时将不与其它模块中的同名段合并
PUBLIC
该段连接时将与其它同名段连接在一起,连接次序由连接命令指定
COMMON
该段在连接时与其它同名段有相同的起始地址,所以会产生覆盖
AT表达式
段地址=表达式的值,其值必为16位但AT不能用来指定代码段
MEMORY
与PUBLIC同义
STACK
将多个同名堆栈段连接在一起,SP设置在第一个堆栈段的开始
注意:
组合类型的缺省项是PRIVATE。
例:
在连接之前已定义两个目标模块如下:
模块1 SSEG SEGMENT PARA STACK
DSEG1 SEGMENT PARA PUBLIC'
Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA'
Code'
模块2 DSEG1 SEGMENT PARAPUBLIC'
以上两个模块分别汇编后产生.OBJ文件,经连接程序连接后产生的.EXE模块如下:
模块1 CSEG SEGMENT PARA'
模块2 CSEG SEGMENT PARA'
模块1+2 DSEG1 SEGMENT PARAPUBLIC'
模块1 DSEG2 SEGMENT PARA
模块2 DSEG2 SEGMENT PARA
模块1 SSEG SEGMENT PARASTACK
2存储模型与简化段定义伪指令
较新版本的汇编程序(MASM5.0与MASM6.0)除支持完整段定义伪指令外,还提供了一种新的简单易用的存储模型和简化的段定义伪指令。
⑴.存储模型伪指令
存储模型的作用是什么呢?
存储模型决定一个程序的规模,也确定进行子程序调用、指令转移和数据访问的缺省属性(NEAR或FAR)。
当使用简化段定义的源程序格式时,在段定义语句之前必须有存储模型.MODEL语句,说明在存储器中应如何安放各个段。
MODEL伪指令的常用格式如下:
. .MODEL存储模型
表 MASM5.0和MASM6.0支持的存储模型:
存储模型
功能
适用操作系统
Tiny(微型)
所有数据和代码都放在一个段内,其访问都为NEAR型,整个程序≤64K,并会产生.COM文件。
MS-DOS
Small(小型)
所有代码在一个64KB的段内,所有数据在另一个64KB的段内(包括数据段,堆栈段和附加段)。
Windows
Medium(中型)
所有代码>64K时可放在多个代码段中,转移或调用可为FAR型。
所有数据限在一个段内,DS可保持不变。
Compact(紧凑型)
所有代码限在一个段内,转移或调用可为NEAR型。
数据>64K时,可放在多个段中。
Large(大型)
允许代码段和数据段都可超过64K,被放置在有多个段内,所以数据和代码都是远访问。
Huge(巨型)
单个数据项可以超过64K,其它同Large模型
Flat(平展型)
所有代码和数据放置在一个段中,但段地址是32位的,所以整个程序可为4GB。
MASM6.0支持该模型。
OS/2
WindowsNT
Small模型是一般应用程序最常用的一种模型,因为只有一个代码段和一个数据段,所以数据和代码都是近访问的。
这种模型的数据段是指数据段、堆栈段和附加段的总和。
在DOS下用汇编语言编程时,可根据程序的不同特点选择前6种模型,一般可以选用SMALL模型。
另外,TINY模型将产生COM程序,其他模型产生EXE程序。
FLAT模型只能运行在32位x86CPU上,DOS下不允许使用这种模型。
当与高级语言混合编程时,两者的存储模型应当一致。
⑵.简化的段伪指令
简化的段定义语句书写简短,语句.CODE、.DATA和.STACK分别表示代码数据段和堆栈段的开始,一个段的开始自动结束前面一个段。
采用简化段指令之前必须有存储模型语句.MODEL。
表 简化段伪指令的格式如下表:
简化段伪指令
注释
.CODE[段名]
创建一个代码段
段名为可选项,如不给出段名,则采用默认段名。
对于多个代码段的模型,则应为每个代码段指定段名。
.DATA
创建一个数据段
段名是:
_DATA
.DATA?
创建无初值变量的数据段
_BSS
.FARDATA[段名]
建立有初值的远调用数据段
可指定段名,如不指定,则将以FAR_DATA命名。
.FARDATA?
[段名]
建立无初值的远调用数据段
可指定段名,如不指定,则将以FAR_BSS命名。
.CONST
建立只读的常量数据段
CONST
.STACK[大小]
创建一个堆栈段并指定堆栈段大小
stack。
如不指定堆栈段大小,则缺省值为1KB
⑶.与简化段定义有关的预定义符号
汇编程序给出了与简化段定义有关的一组预定义符号,它们可在程序中出现,并由汇编程序识别使用。
有关的预定义符号如下:
(1)@code由.CODE伪指令定义的段名或段组名。
(2)@data由.DATA伪指令定义的段名,或由.DATA、.DATA?
、
.CONST和.STACK所定义的段组名。
(3)@stack堆栈段的段名或段组名。
下面的举例说明预定义符号的使用方法。
在完整的段定义情况下,在程序的一开始,需要用段名装入数据段寄存器,如例1中的
mov ax,data_seg1
mov ds,ax
若用简化段定义,则数据段只用.data来定义,而并未给出段名,此时可用
mov ax,@data
这里预定义符号@data就给出了数据段的段名。
⑷.简化段定义举例
例:
.MODEL SMALL
.STACK 100H ;
定义堆栈段及其大小
.DATA ;
定义数据段
.
.CODE ;
定义代码段
START:
;
起始执行地址标号
MOV AX,@DATA ;
MOV DS,AX ;
存入数据段寄存器
.
MOV AX,4C00H
INT 21H
END START ;
程序结束
从例3可以看出,简化段定义比完整的段定义简单得多。
但由于完整的段定义可以全面地说明段的各种类型与属性,因此在很多情况下仍需使用它。
3段组定义伪指令
段组定义伪指令能把多个同类段合并为一个64KB的物理段,并用一个段组名统一存取它。
段组定义伪指令GROUP的格式如下:
段组名 GROUP 段名[,段名…]
我们已经知道在各种存储模型中,汇编程序自动地把各数据段组成一个段组DGROUP,以便程序在访问各数据段时使用一个数据段寄存器DS,而GROUP伪指令允许用户自行指定段组。
利用GROUP伪指令定义段组后,段组内统一为一个段地址,各段定义的变量和标号都可以用同一个段寄存器进行访问。
2.3程序开始和结束伪指令
在程序的开始可以用NAME或TITLE作为模块的名字,其格式为:
NAME 模块名
TITLE 文件名
表示源程序结束的伪指令的格式为:
END [标号]
注意:
NAME及TITLE伪指令并不是必需的,如果程序中既无NAME又无TITLE伪指令,则将用源文件名作为模块名。
程序中经常使用TITLE,这样可以在列表文件中打印出标题来。
END伪指令中的"
标号"
指示程序开始执行的起始地址。
如果多个程序模块相连接,则只有主程序的END要加上标号,其他子程序模块则只用END而不必指定标号。
例1~3的最后使用了ENDSTART伪指令。
汇编程序将在遇END时结束汇编,并且程序在运行时从START开始执行。
MASM6.0版的汇编程序还增加了定义程序的入口点和出口点的伪操作。
.STARTUP用来定义程序的初始入口点,并且产生设置DS,SS和SP的代码。
若程序中使用了.STARTUP,则结束程序的END伪操作中不必再指定程序的入口点标号。
.EXIT[return_value]
2.4数据定义及存储器分配伪指令
DB(definebyte)
DB伪指令用来定义字节,对其后的每个数据都存储在一个字节中。
DB能定义十进制数、二进制数、十六进制数和ASCII字符,二进制数和十六进制数要分别用"
B"
和"
H"
表示,ASCII字符用单引号('
'
)括起来。
DB还是唯一能定义字符串的伪操作,串中的每个字符占用一个字节。
DW(defineword)
DW伪指令用来定义字,对其后的每个数据分配2个字节(1个字),数据的低8位存储在低字节地址中,高8位存储在高字节地址中,如下例中的变量DATA8的数据存储在0070字地址中,其中0070字节存储0BAH,0071字节存储03H。
DW还可存储变量或标号的偏移地址。
见上面DW伪指令的例子。
DD(definedoubleword)
DD伪指令用来定义双字,对其后的每个数据分配4个字节(2个字)。
该伪指令同样将数据转换为十六进制,并根据低地址存储低字节,高地址存储高字节的规则来存放数据。
如下例DATA15的存储情况是:
00A8:
0F2H,00A9H:
57H,00AAH:
2AH,00ABH:
5CH。
用DD存入地址时,第一个字为偏移地址,第二个字为段地址。
DQ(definequadword)
DQ伪指令用来定义4字,即64位字长的数据,DQ之后的每个数据占用8个字节(4个字)。
DT(definetenbytes)
DT伪指令用来为压缩的BCD数据分配存储单元,它虽然可以分配10个字节(5个字),但最多只能输入18个数字,要注意的是,数据后面不需要加"
。
DUP(duplicate)
DUP伪指令可以按照给定的次数来复制某个(某些)操作数,它可以避免多次键入同样一个数据。
例如,把6个FFH存入相继字节中,可以用下面两种方法,显然用DUP的方法更简便些。
存入6字节的FFH
DATA20DB0FFH0FFH0FFH0FFH0FFH0FFH;
DATA21DB6DUP(0FFH)
PTR属性操作符
PTR指定操作数的类型属性,它优先于隐含的类型属性。
其格式为:
类型PTR变量[±
常数表达式]
其中类型可以是BYTE、WORD、DWORD、FWORD、QWORD或TBYTE,这样变量的类型就可以指定了。
如下例:
(P131例14)
LABEL伪指令
LABEL可以使同一个变量具有不同的类型属性。
其中变量的数据类型可以是BYTE,WORD,DWORD。
2.5表达式赋值伪操作EQU
EQU是一个赋值伪操作(伪指令),它给一个数据标号赋于一个常数值,但这个常数不占用存储单元。
当这个数据标号出现在程序中时,汇编程序即用它的常数值代替数据标号。
EQU可以在数据段之外使用,甚至可用在代码段中间。
使用EQU操作的优点可从下面的例子中看出:
COUNT EQU 25
COUNTER DB COUNT
MOV AL, COUNT
=伪操作
赋值伪操作"
="
的作用与EQU类似。
它们之间的区别是,EQU伪操作中的标号名是不允许重复定义的,而=伪操作是允许重复定义的。
在EQU语句的表达式中,如果有变量或标号的表达式,则在该语句前应该先给出它们的定义。
如上例,ALPHA必须在BETA之前定义,否则汇编程序将指示出错。
例如, TMP EQU5
TMP EQUTMP+1则是错误语句,因为TMP已赋值为5,就不能再把它定义为其它数值。
而TMP=5
TMP=TMP+1则是允许使用的,因为=伪操作允许重复定义。
第一个语句TMP的值为5,第二个语句TMP的值就为6了。
2.6地址计数器与对准伪指令
1.
ORG伪操作:
生成COM程序:
2.EVEN伪指令
EVEN伪指令使下一个变量或指令开始于偶数字节地址。
3.ALIGN伪指令
ALIGN伪指令使它后面的数据或指令从2的整数倍地址开始。
ALIGN2n(n为任意整数)
2.7基数控制伪指令
.RADIX伪指令
.RADIX可以把默认的基数改变为2~16范围内的任何基数。
其格式如下:
.RADIX基数值
其中基数值用十进制数来表示。
应当注意,在用.RADIX16把基数定为十六进制后,十进制数后面都应跟字母D。
在这种情况下,如果某个十六进制数的末字符为D,则应在其后跟字母H,以免与十进制数发生混淆。
4.3汇编语言程序格式
3.1.汇编语言源程序语句的格式
(1)名字项
源程序中用下列字符来表示名字:
字母A~Z
数字0~9
专用字符?
、·
、@、-、$
除数字外,所有字符都可以放在源语句的第一个位置。
名字中如果用到·
则必须是第一个字符。
可以用很多字符来说明名字,但只有前面的31个字符能被汇编程序所识别。
一般说来,名字项可以是标号或变量。
它们都用来表示本语句的符号地址,都是可有可无的,只有当需要用符号地址来访问该语句时它才需要出现。
·
标号:
标号在代码段中定义,后面跟着冒号:
,它也可以用LABEL或EQU伪操作来定义。
此外,它还可以作为过程名定义,这将在以后的章节中加以说明。
变量:
变量在数据段或附加数据段中定义,后面不跟冒号。
它也可以用LABEL或EQU伪操作来定义。
变量经常在操作数字段出现。
(2)操作项
操作项可以是指令、伪指令或宏指令的助记符。
对于指令,汇编程序将其翻译为机器语言指令。
对于伪指令,汇编程序将根据其所要求的功能进行处理。
对于宏指令,则将根据其定义展开。
宏指令在第七章中将会专门论述。
(3)操作数项
操作数项由一个或多个表达式组成,多个操作数项之间一般用逗号分开。
对于指令,操作数项一般给出操作数地址,它们可能有一个,或二个,或三个,或一个也没有。
对于伪操作或宏指令,则给出它们所要求的参数。
操作数项可以是常数、寄存器、标号、变量或由表达式组成。
(4)注释项
注释项用来说明一段程序、一条或几条指令的功能。
对于汇编语言程序来说,注释项的作用是很明显的,它可以使程序容易被读懂,因此汇编语言程序必须写好注释。
注释应该写出本条(或本段)指令在程序中的功能和作用,而不应该只写指令的动作。
3.2表达式
(1)算术操作符
算术操作符有+、-、*、/和MOD。
MOD是指除法运算后得到的余数,如19/7的商是2,而19MOD7则为5(余数)。
(2)逻辑操作符
AND、OR、XOR、NOT、SHL、SHR
(3)关系操作符
关系操作符有:
EQ(相等)、NE(不等)、LT(小于)、GT(大于)、LE(小于或等于)、GE(大于或等于)。
(4)数值回送操作符
TYPE、LENGTH、SIZE、OFFSET、SEG
(5)属性操作符
属性操作符主要有:
PTR、段操作符、SHORT、THIS、HIGH、LOW等。
操作符的优先级(p143)
操作符的优先级别从高到低排列如下:
1.在圆括号中的项,方括号中的项,结构变量(变量,字段。
),然后是LENGTH、SIZE、WIDTH和MASK。
2.名:
(段取代)。
3.PTR,OFFSET,SEG,TYPE,THIS及段操作符。
HIGH和LOW。
5.乘法和除法:
*,/,MOD。
6.加法和减法:
+,-。
7.关系操作:
EQ,NE,LT,LE,GT,GE。
8.逻辑:
NOT。
9.逻辑:
AND。
10.逻辑:
OR,XOR。
11.SHORT。
3.3汇编语言源程序格式举例(p