3.2.3 段定义伪指令
8086/8088系统的存储器是分段的,所以8086/8088必须按段来组织程序和使用存储器,这就需要有段定义语句。段定义语句的主要伪指令有SEGMENT、ENDS、AS-SUME和ORG。
SEGMENT和ENDS语句成对使用,把汇编语言源程序分成段。这些段就相当于存储器段,在这些段中可分为代码段、数据段、附加段和堆栈段。代码段中存放指令和伪指令、宏指令,其他段中存放伪指令。
汇编程序为什么要关心存储器段呢?这是由于首先,若有一个段内的转移或调用指令,在指令中只包含新的指令单元的16位段内偏移地址;而在一个段间的转移和调用指令,还必须包含段地址。其次,使当前(即现行)数据段和当前堆栈段的数据访问指令,对于8086/8088结构来说是最优的,因为它只包含数据单元的16位段内偏移地址,任何别的访问指令,访问处在四个当前的可寻址段中的某一个段的数据单元,在机器码指令中还必须附加一个段超越前缀(增加一个8位字节)。
因此汇编程序必须知道程序的段结构,并知道在各种指令执行时将访问哪一个段——由段寄存器指出这个信息,并应由ASSUME语句提供。
下面的程序是一个简单的例子,它说明了如何使用SEGMENT、ENDS和ASSUME伪指令来定义代码段、数据段、附加段和堆栈段。
【例3-25】
DATA SEGMENT
A DB ?
B DW ?
C DD ?
DATA ENDS
EXTRA SEGMENT
ALPHA DB ?
BETA DW?
GAMMA DD?
EXTRA ENDS
STACK SEGMENT
DB 100 DUP(?)
TOP EQU$
STACK ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:EXTRA,SS:STACK
START:MOV AX,SEG A
MOV DS,AX
MOV AX,SEG ALPHA
MOV ES,AX
MOV AX,STACK
MOV SS,AX
MOV SP,OFFSET TOP
…
CODE ENDSEND START
在例3-25中,分为四段,每一段都有段定义伪指令SEGMENT和ENDS来分段,此伪指令的一般格式为:
段名 SEGMENT
…
段名 ENDS
其中每一个段都应有个段名(Seg ment_name),在SEGMENT和ENDS伪指令前都要用同一个段名,如:
CODE SEGMENT
…
CODE ENDS
这是一个段名为CODE的代码段,在SEGMENT与ENDS之间,对于数据段、附加段和堆栈段来说,一般是存储单元定义、初始化数据、分配单元数等伪指令,对于代码段来说,主要是指令序列和伪指令。
例3-25中运用了ASSUME伪指令,它的格式为:
ASSUME 段寄存器名:定义的段名,…,段寄存器名:定义的段名
其中,段寄存器名(Segment_registername)必须是CS、DS、ES和SS,而段名(Segment_name)相应于分段时,写在SEGMENT和ENDS前面的段名,如例3-25,CS:CODE,DS:DATA,等等。
任何对存储器或堆栈访问的指令,都将使用CS、DS、ES和SS段寄存器的值才能形成真正的物理地址。因此在这些指令之前,必须要首先对这些段寄存器设定它们的值,即段的起始地址。ASSUME伪指令就是使汇编程序在汇编指令时,能知道各段寄存器的值。由于ASSUME伪指令只是指定某个段分配给哪个段寄存器,但它并不能把段地址装入段寄存器中,所以DS、ES和SS段寄存器的值还必须通过MOV指令来赋予。如例3-25中从START开始的六条指令,就是给DS、ES和SS赋值的语句。注意,代码段寄存器CS不能这样做,代码段的赋值是在程序初始化时自动完成的。
ASSUME伪指令除了告诉汇编程序各段寄存器的值之外,还可以在指令中省掉很多跨段越前缀代码。如例3-25中需把DS数据段A单元的内容送到附加段ALPHA单元中去,如果DS、ES没有用ASSUME说明,那么必须要加访问数据所需要的段名,否则就会出错,程序如下:
【例3-26】
DATA SEGMENT
A DB 12H
DATA ENDS
EXTRA SEGMENT
ALPHA DB ?
EXTRA ENDS
CODE SEGMENT
ASSUME CS:CODE
START:MOV AX,DATA
MOV DS,AX
MOV AX,EXTRA
MOV ES,AX
MOV BL,DS:A
MOV ES:ALPHA,BL
…
CODE ENDS
END START
然而,利用ASSUME伪指令,就可以只告诉汇编程序一次,访问这些变量需要使用哪些段寄存器,而不需在每条指令中,在每个数据前加上段名,如下面程序改写为:
DATA SEGMENT
A DB 12H
DATA ENDS
EXTRA SEGMENT
ALPHA DB ?
EXTRA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA,ES:EXTRA
START:MOV AX,DATA
MOV DS,AX
MOV AX,EXTRA
MOV ES,AX
MOV BL,A
MOV ALPHA,BL
…
CODE ENDS
END START
段内的偏移地址是从段名SEGMENT以下,以0000H作为开始,以后每分配一个字节,偏移地址加1。
【例3-27】 DATA SEGMENT
AAA DB 12H
BBB DW ?
CCC DD 9C56H
DATA ENDS
AAA单元的偏移地址为0000H,也即存放12H数据单元的地址。BBB为空字单元,它的偏移地址为0001H,CCC为双字单元,该单元偏移地址为0003H。为了给存储单元设置起始偏移地址,可以用ORG伪指令。
格式:ORG〈表达式〉
其中表达式必须是一个可计算得到正整数的、数值范围为0~65535的表达式。
【例3-28】 DATA SEGMENT
ORG 100H
AAA DB 12H
BBB DW ?
CCC DD 9C56H DATA ENDS
此时因用了ORG伪指令重新设置DATA数据段的起始偏移地址为100H,所以AAA单元偏移地址为0100H,BBB单元偏移地址为0101H,CCC单元偏移地址为0103H。
伪指令ORG可设置于代码段、数据段的任何地方。这表明程序和数据要从某一特定的地址开始存放,但在大多数程序中都不需要ORG,汇编程序都从每段的0000H开始存放程序和数据。
在汇编程序对源程序汇编的过程中,使用地址计数器来保存当前正在汇编的指令地址。地址计数器值可用“$”来表示,汇编语言允许用户直接用“$”来引用地址计数器的当前值,因此ORG$+8可以表示从当前地址开始跳过8个字节存储单元。在指令和伪指令中也可直接用“$”表示地址计数器的当前值,如:
1000:2543 JNE $+6…
1000:2549 ……则转移地址是JNE指令的首地址加上6(即2549H),即在指令中使用的“$”表示本指令中的第一个字节的偏移地址(2543H)。而在伪指令中的参数字段中使用$,它表示地址计数器的当前值,如:
【例3-29】 ARRAY DW 1,2,$+4,3,4,$+4
若汇编时ARRAY分配的偏移地址为0074H,则汇编后ARRAY数组的存储情况如图3-7所示。
图3-7 ARRAY数组汇编后的存储情况
注意,ARRAY数组中的两个$+4得到的结果是不同的,这是由于“$”的值在不断变化的缘故。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。