ASM - 段定义伪指令
段定义伪指令是表示一个段开始和结束的命令,80x86有两种段定义的方式:完整段定义和简化段定义,分别使用不同的段定义伪指令来表示各种段。
4.2.1.1 完整的段定义伪指令
完整段定义伪指令的格式如下:
段名 SEGMENT
.
.
.
段名 ENDS
段名由用户命名。对于数据段、附加段和堆栈段来说,段内一般是存储单元的定义、分配等伪指令语句;对于代码段中则主要是指令及伪指令语句。
定义了段还必须说明哪个段是代码段,哪个段是数据段。ASSUME伪指令就是建立段和段寄存器关系的伪指令,其格式为:
ASSUME 段寄存器名: 段名,…
段寄存器名必须是CS、DS、ES和SS中的一个,而段名必须是由SEGMENT定义的段名。
· 定位类型 :说明段的起始边界值(物理地址)。
·组合类型:说明程序连接时的段组合方法。
·类别:在单引号中给出连接时组成段组的类型名。连接程序可把相同类别的段的位置靠在一起。
例4.1
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg1 segment ; 定义数据段
.
.
.
data_seg1 ends
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg2 segment ; 定义附加段
.
.
.
data_seg2 ends
; * * * * * * * * * * * * * * * * * * * * * * *
code_seg segment ; 定义代码段
assume cs:code_seg, ds:data_seg1, es:data_seg2
start: ; 程序执行的起始地址
; set DS register to current data segment
mov ax, data_seg1 ; 数据段地址
mov ds, ax ; 存入DS寄存器
; set ES register to current extra segment
mov ax, data_seg2 ; 附加段地址
mov es, ax ; 存入ES寄存器
.
.
.
code_seg ends ; 代码段结束
; * * * * * * * * * * * * * * * * * * * * * * * * * *
end start
由于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 ; 存入ES寄存器
注意,在程序中不需要用指令装入代码段的段地址,因为在程序初始化时,装入程序已将代码段的段地址装入CS寄存器了。
为了对段定义作进一步地控制,SEGMENT伪指令还可以增加类型及属性的说明,其格式如下:
段名 SEGMENT [定位类型][组合类型]['类别']
.
.
.
段名 ENDS
[ ]中的内容是可选的,一般情况下,这些说明可以不用。但是,如果需要用连接程序把本程序与其他程序模块相连接时,就需要提供类型和属性的说明。
表4.2.1 ·定位类型:说明段的起始边界值(物理地址)。
注意:
定位类型的缺省项是PARA,即在未指定定位类型的情况下,则连接程序默认为PARA。BYTE和WORD用于把其它段(通常是数据段)连入一个段时使用;DWORD一般用于运行在80386及后继机型上的程序。
表4.2.2 ·组合类型:说明程序连接时的段组合方法。
注意:组合类型的缺省项是PRIVATE。
例4.2 在连接之前已定义两个目标模块如下:
模块1 SSEG SEGMENT PARA STACK
DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
模块2 DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
以上两个模块分别汇编后产生 .OBJ 文件,经连接程序连接后产生的 .EXE模块如下:
模块1 CSEG SEGMENT PARA 'Code'
模块2 CSEG SEGMENT PARA 'Code'
模块1+2 DSEG1 SEGMENT PARA PUBLIC 'Data'
模块1 DSEG2 SEGMENT PARA
模块2 DSEG2 SEGMENT PARA
模块1 SSEG SEGMENT PARA STACK
------------------------------------------------------
4.2.1.2 存储模型与简化段定义伪指令
较新版本的汇编程序(MASM5.0与MASM6.0)除支持完整段定义伪指令外,还提供了一种新的简单易用的存储模型和简化的段定义伪指令。
1. 存储模型伪指令
存储模型的作用是什么呢?存储模型决定一个程序的规模,也确定进行子程序调用、指令转移和数据访问的缺省属性(NEAR或FAR)。当使用简化段定义的源程序格式时,在段定义语句之前必须有存储模型 .MODEL语句,说明在存储器中应如何安放各个段。
MODEL伪指令的常用格式如下:
. .MODEL 存储模型
表4.2.3 MASM 5.0和MASM 6.0支持的存储模型:
注意:Small 模型是一般应用程序最常用的一种模型,因为只有一个代码段和一个数据段,所以数据和代码都是近访问的。这种模型的数据段是指数据段、堆栈段和附加段的总和。
在DOS下用汇编语言编程时,可根据程序的不同特点选择前6种模型,一般可以选用SMALL模型。另外,TINY模型将产生COM程序,其他模型产生EXE程序。FLAT模型只能运行在32位x86 CPU上,DOS下不允许使用这种模型。当与高级语言混合编程时,两者的存储模型应当一致。
2. 简化的段伪指令
简化的段定义语句书写简短,语句.CODE、.DATA和.STACK分别表示代码数据段和堆栈段的开始,一个段的开始自动结束前面一个段。采用简化段指令之前必须有存储模型语句.MODEL。
表4.2.4 简化段伪指令的格式如下表:
3.与简化段定义有关的预定义符号
汇编程序给出了与简化段定义有关的一组预定义符号,它们可在程序中出现,并由汇编程序识别使用。有关的预定义符号如下:
(1)@code 由.CODE 伪指令定义的段名或段组名。
(2)@data 由.DATA 伪指令定义的段名,或由 .DATA 、.DATA?、
.CONST和 .STACK所定义的段组名。
(3)@stack 堆栈段的段名或段组名。
下面的举例说明预定义符号的使用方法。在完整的段定义情况下,在程序的一开始,需要用段名装入数据段寄存器,如例4.1中的
mov ax,data_seg1
mov ds,ax
若用简化段定义,则数据段只用.data来定义,而并未给出段名,此时可用
mov ax,@data
mov ds,ax
这里预定义符号@data就给出了数据段的段名。
4.简化段定义举例
例4.3
.MODEL SMALL
.STACK 100H ; 定义堆栈段及其大小
.DATA ; 定义数据段
.
.
.
.CODE ; 定义代码段
START: ; 起始执行地址标号
MOV AX, @DATA ; 数据段地址
MOV DS, AX ; 存入数据段寄存器
.
.
.
MOV AX, 4C00H
INT 21H
END START ; 程序结束
从例4.3可以看出,简化段定义比完整的段定义简单得多。但由于完整的段定义可以全面地说明段的各种类型与属性,因此在很多情况下仍需使用它。
4.2.1.1 完整的段定义伪指令
完整段定义伪指令的格式如下:
段名 SEGMENT
.
.
.
段名 ENDS
段名由用户命名。对于数据段、附加段和堆栈段来说,段内一般是存储单元的定义、分配等伪指令语句;对于代码段中则主要是指令及伪指令语句。
定义了段还必须说明哪个段是代码段,哪个段是数据段。ASSUME伪指令就是建立段和段寄存器关系的伪指令,其格式为:
ASSUME 段寄存器名: 段名,…
段寄存器名必须是CS、DS、ES和SS中的一个,而段名必须是由SEGMENT定义的段名。
· 定位类型 :说明段的起始边界值(物理地址)。
·组合类型:说明程序连接时的段组合方法。
·类别:在单引号中给出连接时组成段组的类型名。连接程序可把相同类别的段的位置靠在一起。
例4.1
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg1 segment ; 定义数据段
.
.
.
data_seg1 ends
; * * * * * * * * * * * * * * * * * * * * * * *
data_seg2 segment ; 定义附加段
.
.
.
data_seg2 ends
; * * * * * * * * * * * * * * * * * * * * * * *
code_seg segment ; 定义代码段
assume cs:code_seg, ds:data_seg1, es:data_seg2
start: ; 程序执行的起始地址
; set DS register to current data segment
mov ax, data_seg1 ; 数据段地址
mov ds, ax ; 存入DS寄存器
; set ES register to current extra segment
mov ax, data_seg2 ; 附加段地址
mov es, ax ; 存入ES寄存器
.
.
.
code_seg ends ; 代码段结束
; * * * * * * * * * * * * * * * * * * * * * * * * * *
end start
由于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 ; 存入ES寄存器
注意,在程序中不需要用指令装入代码段的段地址,因为在程序初始化时,装入程序已将代码段的段地址装入CS寄存器了。
为了对段定义作进一步地控制,SEGMENT伪指令还可以增加类型及属性的说明,其格式如下:
段名 SEGMENT [定位类型][组合类型]['类别']
.
.
.
段名 ENDS
[ ]中的内容是可选的,一般情况下,这些说明可以不用。但是,如果需要用连接程序把本程序与其他程序模块相连接时,就需要提供类型和属性的说明。
表4.2.1 ·定位类型:说明段的起始边界值(物理地址)。
定位类型
|
说 明
|
BYTE
|
段可以从任何地址边界开始
|
WORD
|
段从字边界开始,即段的起始边界值为偶数
|
DWORD
|
段从双字的边界开始,即段的起始边界值为4的倍数
|
PARA
|
段从小段边界开始,即段的起始边界值为16 (或10H) 的倍数
|
PAGE
|
段从页边界开始,即段的起始边界值为256 (或100H) 的倍数
|
注意:
定位类型的缺省项是PARA,即在未指定定位类型的情况下,则连接程序默认为PARA。BYTE和WORD用于把其它段(通常是数据段)连入一个段时使用;DWORD一般用于运行在80386及后继机型上的程序。
表4.2.2 ·组合类型:说明程序连接时的段组合方法。
组合类型
|
说 明
|
PRIVATE
|
该段为私有段,连接时将不与其它模块中的同名段合并
|
PUBLIC
|
该段连接时将与其它同名段连接在一起,连接次序由连接命令指定
|
COMMON
|
该段在连接时与其它同名段有相同的起始地址,所以会产生覆盖
|
AT 表达式
|
段地址=表达式的值,其值必为16位但AT不能用来指定代码段
|
MEMORY
|
与PUBLIC同义
|
STACK
|
将多个同名堆栈段连接在一起,SP设置在第一个堆栈段的开始
|
注意:组合类型的缺省项是PRIVATE。
例4.2 在连接之前已定义两个目标模块如下:
模块1 SSEG SEGMENT PARA STACK
DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
模块2 DSEG1 SEGMENT PARA PUBLIC 'Data'
DSEG2 SEGMENT PARA
CSEG SEGMENT PARA 'Code'
以上两个模块分别汇编后产生 .OBJ 文件,经连接程序连接后产生的 .EXE模块如下:
模块1 CSEG SEGMENT PARA 'Code'
模块2 CSEG SEGMENT PARA 'Code'
模块1+2 DSEG1 SEGMENT PARA PUBLIC 'Data'
模块1 DSEG2 SEGMENT PARA
模块2 DSEG2 SEGMENT PARA
模块1 SSEG SEGMENT PARA STACK
------------------------------------------------------
4.2.1.2 存储模型与简化段定义伪指令
较新版本的汇编程序(MASM5.0与MASM6.0)除支持完整段定义伪指令外,还提供了一种新的简单易用的存储模型和简化的段定义伪指令。
1. 存储模型伪指令
存储模型的作用是什么呢?存储模型决定一个程序的规模,也确定进行子程序调用、指令转移和数据访问的缺省属性(NEAR或FAR)。当使用简化段定义的源程序格式时,在段定义语句之前必须有存储模型 .MODEL语句,说明在存储器中应如何安放各个段。
MODEL伪指令的常用格式如下:
. .MODEL 存储模型
表4.2.3 MASM 5.0和MASM 6.0支持的存储模型:
存储模型
|
功 能
|
适用操作系统
|
Tiny (微型) | 所有数据和代码都放在一个段内,其访问都为NEAR型,整个程序≤64K,并会产生.COM文件。 | MS-DOS |
Small (小型) | 所有代码在一个64KB的段内,所有数据在另一个64KB的段内(包括数据段,堆栈段和附加段)。 | MS-DOS Windows |
Medium (中型) | 所有代码>64K时可放在多个代码段中,转移或调用可为FAR型。所有数据限在一个段内,DS可保持不变。 | MS-DOS Windows |
Compact(紧凑型) | 所有代码限在一个段内,转移或调用可为NEAR型。数据>64K时,可放在多个段中。 | MS-DOS Windows |
Large (大型) | 允许代码段和数据段都可超过64K,被放置在有多个段内,所以数据和代码都是远访问。 | MS-DOS Windows |
Huge (巨型) | 单个数据项可以超过64K,其它同Large模型 | MS-DOS Windows |
Flat (平展型) | 所有代码和数据放置在一个段中,但段地址是32位的,所以整个程序可为4GB。MASM 6.0支持该模型。 | OS/2 WindowsNT |
注意:Small 模型是一般应用程序最常用的一种模型,因为只有一个代码段和一个数据段,所以数据和代码都是近访问的。这种模型的数据段是指数据段、堆栈段和附加段的总和。
在DOS下用汇编语言编程时,可根据程序的不同特点选择前6种模型,一般可以选用SMALL模型。另外,TINY模型将产生COM程序,其他模型产生EXE程序。FLAT模型只能运行在32位x86 CPU上,DOS下不允许使用这种模型。当与高级语言混合编程时,两者的存储模型应当一致。
2. 简化的段伪指令
简化的段定义语句书写简短,语句.CODE、.DATA和.STACK分别表示代码数据段和堆栈段的开始,一个段的开始自动结束前面一个段。采用简化段指令之前必须有存储模型语句.MODEL。
表4.2.4 简化段伪指令的格式如下表:
简化段伪指令
|
功 能
|
注释
|
.CODE [段名] | 创建一个代码段 | 段名为可选项,如不给出段名,则采用默认段名。对于多个代码段的模型,则应为每个代码段指定段名。 |
.DATA | 创建一个数据段 | 段名是:_DATA |
.DATA? | 创建无初值变量的数据段 | 段名是:_BSS |
.FARDATA [段名] | 建立有初值的远调用数据段 | 可指定段名,如不指定,则将以FAR_DATA命名。 |
.FARDATA? [段名] | 建立无初值的远调用数据段 | 可指定段名,如不指定,则将以FAR_BSS命名。 |
.CONST | 建立只读的常量数据段 | 段名是:CONST |
.STACK [大小] | 创建一个堆栈段并指定堆栈段大小 | 段名是:stack。如不指定堆栈段大小,则缺省值为1KB |
3.与简化段定义有关的预定义符号
汇编程序给出了与简化段定义有关的一组预定义符号,它们可在程序中出现,并由汇编程序识别使用。有关的预定义符号如下:
(1)@code 由.CODE 伪指令定义的段名或段组名。
(2)@data 由.DATA 伪指令定义的段名,或由 .DATA 、.DATA?、
.CONST和 .STACK所定义的段组名。
(3)@stack 堆栈段的段名或段组名。
下面的举例说明预定义符号的使用方法。在完整的段定义情况下,在程序的一开始,需要用段名装入数据段寄存器,如例4.1中的
mov ax,data_seg1
mov ds,ax
若用简化段定义,则数据段只用.data来定义,而并未给出段名,此时可用
mov ax,@data
mov ds,ax
这里预定义符号@data就给出了数据段的段名。
4.简化段定义举例
例4.3
.MODEL SMALL
.STACK 100H ; 定义堆栈段及其大小
.DATA ; 定义数据段
.
.
.
.CODE ; 定义代码段
START: ; 起始执行地址标号
MOV AX, @DATA ; 数据段地址
MOV DS, AX ; 存入数据段寄存器
.
.
.
MOV AX, 4C00H
INT 21H
END START ; 程序结束
从例4.3可以看出,简化段定义比完整的段定义简单得多。但由于完整的段定义可以全面地说明段的各种类型与属性,因此在很多情况下仍需使用它。