第4章伪指令与源程序格式汇总文档格式.docx
- 文档编号:19274315
- 上传时间:2023-01-05
- 格式:DOCX
- 页数:23
- 大小:31.55KB
第4章伪指令与源程序格式汇总文档格式.docx
《第4章伪指令与源程序格式汇总文档格式.docx》由会员分享,可在线阅读,更多相关《第4章伪指令与源程序格式汇总文档格式.docx(23页珍藏版)》请在冰豆网上搜索。
movds,ax
movdx,offsetstring
movah,9
int21h
movah,4ch
int21h
codeends
endstart;
汇编结束,程序起始点start:
1.段定义伪指令
汇编程序在把源程序转换为目标程序时,必须确定标号和变量(代码段和数据段的符号地址)的偏移地址,连接程序对针目标程序把不同的段和模块连接在一起,确定各个段的段地址。
段地址确定了,其中的指令、标号和变量的段地址也就确定了,这样就形成一个可执行程序。
为此,需要段定义伪指令。
段定义伪指令格式:
segment_nameSEGMENT
…
segment_nameENDS
其中segment_name由用户确定,大写的为关键字。
段定义伪指令两句成对出现,两句之间为其它指令。
为了确定用户定义的段和哪个段寄存器的关系,用ASSUME伪指令来实现。
ASSUME伪指令格式:
ASSUMEregister_name:
segment_name…,register_name:
segment_name
其中register_name为段寄存器名,必须是CS,DS,ES和SS。
而segment_name则必须是由段定义伪指令定义的段中的段名。
ASSUME伪指令只是指定把某个段分配给哪一个段寄存器,它并不能把段地址装入段寄存器中,所以在代码段中,还必须把段地址装入相应的段寄存器中。
为此,还需要用两条MOV指令完成这一操作。
但是,代码段不需要这样做,代码段的这一操作是在程序初始化时完成的。
一般情况下,使用上述的段定义伪指令就可以了,如果需要对段定义作进一步地控制,SEGMENT伪指令还可以增加类型及属性的说明,其格式如下:
segment_nameSEGMENT[定位类型][组合类型][使用类型][“类别”]
如果需要用连接程序把本程序与其他程序模块相连接时,就需要使用这些说明,具体内容安排在第6章有关子程序的多模块设计中介绍。
2.简化的段定义伪指令
MASM5.0以上版本还支持一种简化的段定义方法,把例4.1程序用简化的段定义方法可以改写如下:
例4.2
.modelsmall;
定义存储模型为small
.data;
.code;
movax,@data;
endstart
首先用.MODEL伪指令说明在内存中如何安排各个段,存储模型为SMALL的意思是:
所有数据都放在一个64KB的数据段,所有代码都放在另一个64KB的代码段,数据和代码都为近访问。
这是最常用的一种模型。
.DATA伪指令用来定义数据段,但没有给出段名,默认段名是_DATA。
@DATA表示段名_DATA,在指令中表示段地址。
简化段定义的表达能力不如SEGMENT伪指令那样完整而清楚,所以很多时候还是用SEGMENT伪指令。
有关简化段定义的更多说明在第6章有关子程序的多模块设计中介绍。
4.1.3程序开始和结束伪指令
在前面例子中,都没有使用表示程序开始的伪指令。
用户根据需要可以在程序的开始用NAME或TITLE伪指令定义该程序模块名。
NAME的格式为:
NAMEmodule_name
其中module_name为模块的名字。
如果程序中没有使用NAME伪指令,也可使用TITLE伪指令来指定模块名,其格式为:
TITLEtext
其中text中的前六个字符被汇编程序作为模块的名字。
TITLE伪指令的另一个作用是在列表文件的每一页上打印标题。
标题text最多可有60个字符。
如果程序中既无NAME又无TITLE伪指令,则用源文件名作为模块名。
所以NAME及TITLE伪指令不是必要的。
表示源程序结束的伪操作的格式为:
END[label]
汇编程序将在遇到END时结束汇编。
其中标号label指示程序开始执行的起始地址。
如果是多个程序模块相连接,则只有主程序需要使用标号,其他子程序模块则只用END而不能指定标号。
4.1.4数据定义与存储器单元分配伪指令
我们知道,指令语句的一般格式是:
[标号:
]操作码操作数[;
注释]
这一类伪指令的格式是:
[变量]操作码N个操作数[;
其中变量字段是可有可无的,它用符号地址表示。
其作用与指令语句前的标号相同。
但它的后面不跟冒号。
操作码字段说明所用伪操作的助记符,即伪操作,说明所定义的数据类型。
常用的有以下几种:
DB伪操作用来定义字节,其后的每个操作数都占有一个字节(8位)。
DW伪操作用来定义字,其后的每个操作数占有一个字(16位,其低位字节在第一个字节地址中,高位字节在第二个字节地址中,即数据低位在低地址,数据高位在高地址)。
DD伪操作用来定义双字,其后的每个操作数占有两个字(32位)。
DF伪操作用来定义6个字节的字,其后的每个操作数占有48位。
DQ伪操作用来定义4个字,其后的每个操作数占有4个字(64位),可用来存放双精
度浮点数。
DT伪操作用来定义1O个字节,其后的每个操作数占有1O个字节,为压缩的BCD码。
(需要说明的是,MASM6允许DB,DW,DD,DF,DQ,DT伪操作分别用BYTE,WORD,DWORD,FWORD,QWORD,TBYTE代替)。
这些伪操作可以把其后跟着的数据存人指定的存储单元,形成初始化数据;
或者只分
配存储空间而并不确定数值。
下面举例说明各种用法。
例4.3操作数为常数、数据表达式。
D_BYTEDB10,5,10H
D_WORDDW14,100H,-5,0ABCDH
D_DWORDDD4×
8
程序中默认的数据为十进制数,10H为十六进制数,用DB定义的数据的值不能超出一个字节所能表示的范围。
数据10的符号地址是D_BYTE,数据5的符号地址是D_BYTE+1。
数据可以是负数,均为补码形式存放。
允许数据表达式,如4×
8,等价为32。
当数据第一位不是数字,应在前面加0,如0ABCDH。
数据在内存中的存放如图4.1所示。
D_BYTED_WORDD_DWORD
↓↓↓
0A
05
10
0E
00
01
FB
FF
CD
AB
20
图4.1例4.3的汇编结果
例4.4操作数为字符串。
问号‘?
’仅预留空间。
数据在内存中的存放如图4.2所示。
MESSAGEDB'
HELLO'
?
DB‘ABCD’
MESSAGE
↓
43
45
4C
4F
--
41
42
44
图4.2例4.4的汇编结果
例4.5用操作符复制操作数。
数据在内存中的存放如图4.3所示。
ARRAYDB2DUP(1,3,2DUP(4,5))
ARRAY
03
04
图4.3例4.5的汇编结果
例4.6指令中使用隐含类型属性。
OPER1DB?
?
OPER2DW?
┇
MOVOPER1,0
MOVOPER2,0
MOVOPER2,AX
第一条指令将使OPER1字节单元清零,第二条指令将使OPER2字单元清零,因为OPER2为字类型变量,第三条指令两个操作数类型一致,无需说明。
例4.7在指令中使用类型属性操作符指定操作数类型。
OPER1DB3,4
OPER2DW5678H,9
MOVAX,OPER1
MOVBL,OPER2
MOV[BX],0
前两条指令操作数类型不匹配,第三条指令的目标操作数类型不明确,所以都是错误的。
解决的办法是可在指令中对操作数类型作临时性指定,以使操作数类型匹配和明确。
这三条指令可改为:
MOVAX,WORDPTROPER1
MOVBL,BYTEPTROPER2
MOVBYTEPTR[BX],0
使用类型属性操作符WORDPTR,BYTEPTR可对操作数类型进行重新指定。
指令执行结果:
AX=0403H,BL=78H。
实际上一个变量也可以定义成不同类型,以方便使用。
这可以用LABEL伪操作来定义,格式为:
nameLABELtype
例4.8把变量定义成不同类型,指令中可灵活选用。
指令执行结果如图4.4所示。
OPR_BLABELBYTE
OPR_WDW4DUP(0)
MOVAX,1234H
MOVOPR_B,AL
MOVOPR_W+2,AX
OPR_B
OPR_W
图4.4
(1)例4.8的数据定义
34
12
图4.4
(2)例4.8的指令执行结果
OPR_BLABELBYTE伪操作使得OPR_B和OPR_W指向同一个内存单元。
4.1.5表达式赋值伪指令
汇编语言程序也允许表达式,以方便程序设计。
可以用赋值伪操作给表达式赋予一
个名字。
其格式如下:
Expression_nameEQUExpression
上式中的表达式必须是有效的操作数格式或有效的指令助记符,此后,程序中凡需要用到该表达式之处,就可以用表达式名来代替了。
举例如下:
VALEQU86
DATAEQUVAL+5
ADDREQU[BP+VAL]
此后,指令MOVAX,ADDR就代表MOVAX,[BP+86],可见,EQU伪操作的引入提高了程序的可读性,也更加易于程序的修改。
必须注意:
在EQU语句的表达式中,如果有变量或标号的表达式,必须先定义后引用。
另一个更为简洁的赋值伪操作是=,格式同EQU,只是用=替换EQU。
它们之间的区别是EQU伪操作中的表达式名是不允许重复定义的,而=伪操作则允许重复定义。
例如,VAL=53
VAL=VAL+53
VAL可以多次被伪操作=赋值,而EQU则不允许重复定义。
4.1.6汇编地址计数器与定位伪指令
1.地址计数器$
在汇编程序对源程序汇编的过程中,为了按序存放程序中定义的数据变量和指令,使用16位的地址计数器(locationcounter)来保存当前正在汇编的指令的偏移地址。
当开始汇编或在每一段开始时,把地址计数器初始化为零,以后在汇编过程中,每处理一条指令,地址计数器就增加一个值,此值为该指令所需要的字节数。
地址计数器的值可用$来表示,汇编语言允许用户直接用$来引用地址计数器的值。
如在指令中引用$,JMP$+8的转向地址是本条指令的首地址加上8。
显然$+8必须是另一条指令的首地址,否则汇编程序将指示出错。
当$用在伪操作的参数字段时,它所表示的是地址计数器的当前值。
例4.9考察$的作用,假定$初值=0,数据在内存中的存放如图4.5所示。
ARRAYDW3,$+7,7
COU=$
NEWDWCOU
ARRAYNEW
↓↓
09
07
06
图4.5例4.9的汇编结果
2.ORG伪操作
ORG伪操作用来设置当前地址计数器的值,其格式为:
ORGconstantexpression
如常数表达式的值为n,则ORG伪操作可以使下一个字节的地址为n。
例4.10考察ORG伪操作,数据在内存中的存放如图4.6所示。
ORG0
DB3
ORG4
BUFFDB5
ORG$+6
VALDB9
BUFFVAL
图4.6例4.10的汇编结果
可以看成是从4号单元开始定义了一个名为BUFF长度为6个字节的键盘输入缓冲区。
3.EVEN伪操作
EVEN伪操作使下一个变量或指令开始于偶数地址。
一个字的地址最好从偶数地址开始。
例如:
EVEN
ARRAYDW80DUP(?
)
4.ALIGN伪操作
ALIGN伪操作使下一个变量的地址从4的倍数开始,这可以用来保证双字数组边界从4的倍数开始,其格式为:
ALIGNboundary
其中Boundary必须是2的幂。
ALIGN8
4.1.7基数控制伪指令
汇编程序默认的数为十进制数,所以在程序中使用其他基数表示的常数时,需要专门给以标记如下:
(1)二进制数:
由一串0和1组成,其后跟以字母B,如00101001B。
(2)十进制数:
由0~9的数字组成的数,一般情况下,后面不必加上标记,在指定了其
它基数的情况下,后面跟字母D,例如23D。
(3)十六进制数:
由0~9及A~F组成的数,后面跟字母H。
这个数的第一个字符
必须是0~9,所以如果第一个字符是A~F时,应在其前面加上数字0,如0FFFFH。
RADIX伪操作可以把默认的基数改变为2~16范围内的任何基数。
其格式
如下:
.RADIXexpression
其中表达式用来表示基数值(用十进制数表示)。
注意:
在用.RADIX把基数定为十六进制后,十进制数后面都应跟字母D。
在这种情况下,如果某个十六进制数的末字符为D,则应在其后跟字母H,以免与十进制数发生混淆。
4.1.8过程定义伪指令
子程序又称过程,可以把一个程序写成一个过程或多个过程,这样可以使程序结构更加清晰,基本的过程定义伪指令的格式为:
procedure_namePROCAttribute
procedure_nameENDP
其中过程名(procedure_name)为标识符,起到标号的作用,是子程序入口的符号地址。
属性(Attribute)是指类型属性,可以是NEAR或FAR。
例4.1的程序段可以改写为过程,见下例:
例4.11
data
mainprocfar;
定义过程main
movax,data
movdx,offsetstring
movah,9
int21h
mainendp
endmain;
汇编结束,程序起始点main
该程序的过程部分也可写成:
mainprocfar
pushds;
ds进栈
movax,0;
0进栈
pushax
ret;
返回
该过程对DOS来说是一个远过程,这里在过程的开始把当前DS的值和0压入堆栈,过程的最后用RET返回到DOS命令状态。
这是一个固定用法。
RET指令使堆栈中的2个字(0和DS的值)弹出到IP和CS,实际上是执行了该处的退出程序的指令INT20H。
增强功能的过程定义在第六章介绍。
但一般情况下,都是使用基本的过程定义。
4.2语句格式
程序中用得最多的是指令和有关数据定义的伪指令,这些语句基本上可以由4项组成,格式如下:
[name]operation0perand[;
comment]
[名字]操作操作数[;
名字项是一个符号,可以是指令的标号,也可以是变量名。
操作项是一个操作码的助记符,它可以是指令、伪指令或宏指令名。
操作数项由一个或多个表达式组成,它提供该操作所要求的操作数或相关信息。
注释项用来说明程序或语句的功能。
上面带方括号的两项是可有可无的,各项之间必须用‘空格’隔开。
下面分别说明各项的表示方法。
4.2.1名字项和操作项
1.名字项
名字项用下列字符来表示:
字母A~Z
数字O~9
专用字符?
,·
,@,-,$
除数字外,所有字符都可以放在源语句的第一个位置。
名字中如果用到·
,则必须是第一个字符。
名字项可以是标号或变量,用来表示本语句的符号地址。
如果是指令的标号,后面跟冒号:
。
作为一个地址符号,显然应有3种属性:
段、偏移及类型。
段属性,定义该地址符号的段起始地址,此值必须在一个段寄存器中。
偏移属性,偏移地址是从段起始地址到定义该地址符号的位置之间的字节数。
对于16位段是16位无符号数。
类型属性,对于标号,用来指出该标号是在本段内引用还是在其他段中引用的。
如是在段内引用的,则称为NEAR,对于16位段,指针长度为2字节。
如在段外引用,则称为FAR,对于16位段,指针长度为4字节(段地址2字节,偏移地址2字节)。
对于变量,类型属性定义该变量所保留的字节数。
如BYTE(1个字节)、WORD(2个字节)、DWORD(4个字节)、FWORD(6个字节)、QWORD(8个字节)、TBYTE(1O个字节),这一点在数据定义伪操作中已作了说明。
作为一个地址符号,在同一个程序中,显然不能重复定义,即同样的标号或变量的定义只允许出现一次,否则汇编程序会指示出错。
2.操作项
操作项可以是指令、伪指令或宏指令的助记符。
对于指令,汇编程序将其翻译为机器
语言指令。
对于伪指令,汇编程序将根据其所要求进行处理。
对于宏指令,则将根据宏定义展开。
有关宏指令的具体内容将在第7章专门介绍。
4.2.2表达式和操作符
操作数项是指令的最复杂最灵活的一项。
操作数项由一个或多个表达式组成。
对于指令,
操作数项一般给出操作数地址,它们通常不超过两个。
对于伪操作或宏指令,则给出它们所要求的参数。
操作数项可以是常数、寄存器、标号、变量。
这些我们已经知道。
操作数项还可以是表达式,而表达式是常数、寄存器、标号、变量与一些操作符(运算符)相组合的序列。
在汇编期间,汇编程序按照一定的优先规则对表达式进行计算后可得到一个数值或一个地址,如是数值,这个表达式就是数字表达式,如是地址,这个表达式就是地址表达式。
那么表达式有哪些操作符呢?
下面介绍一些常用的操作符在表达式中的作用。
特别注意的是,表达式在汇编阶段起作用,只有正确的表达式才能通过汇编。
1.算术操作符
算术操作符有+,-,*,/和MOD。
其中MOD是指除法运算后得到的余数,如7/5的商为1,而7MOD5为2(余数)。
要注意的是,算术操作符在表达式中的使用,其结果必须有明确的物理意义时才是有效的,下面举例说明:
例4.12算术操作符的使用
设有如下定义:
VAL=4
DA1DW6,2,9,3
DA2DW15,17,24
上面定义的VAL是常数,我们无需确定它的位置就可以使用。
DA1和DA2是变量的符号地址,它们在内存中有确定的位置,我们只有根据它们的地址才能访问。
MOVAX,DA1*4;
错,地址乘或除,没有意义
MOVAX,DA1*DA2;
MOVAX,DA1+DA2;
错,地址相加,没有意义
MOVAX,BX+VAL;
错,BX+VAL须用指令实现
MOVAX,[BX+VAL];
地址表达式,汇编成MOVAX,[BX+4]
MOVAX,DA1+VAL;
地址表达式,汇编成直接寻址指令,执行后,AX=9
MOVAX,VAL*4/2;
数字表达式,汇编成MOVAX,8
MOVAX,[VAL*4/2];
数字表达式,汇编成MOVAX,8
MOVCX,(DA2-DA1)/2;
地址相减,得到DA1存储区长度,汇编成MOVCX,4
2.逻辑与移位操作符
逻辑操作符有AND,OR,XOR和NOT;
移位操作符有SHL和SHR。
它们都是按位操作的。
只能用于数字表达式中。
逻辑操作符要求汇编程序对其前后两个操作数(或表达式)作指定的逻辑操作。
例4.13逻辑操作符的使用
MOVAX,BXAND0FFH;
错,BXANDVAL须用指令实现
MOVAX,VALAND0F0H;
汇编成MOVAX,0
ANDAX,VALOR0F0H;
汇编成ANDAX,0F4H
移位操作符的格式是:
expressionSHI(或SHR)numshift
汇编程序将expression左移或
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第4章 伪指令与源程序格式汇总 指令 源程序 格式 汇总