段定义伪指令是表示一个段开始和结束的命令,80x86有两种段定义的方式:完整段定义和简化段定义,分别使用不同的段定义伪指令来表示各种段。
?
?
?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
?
?
[ ]中的内容是可选的,一般情况下,这些说明可以不用。但是,如果需要用连接程序把本程序与其他程序模块相连接时,就需要提供类型和属性的说明。
?
?
?表??·定位类型:说明段的起始边界值(物理地址)。
定位类型 |
说 明 |
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 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
?? 2 存储模型与简化段定义伪指令
?
?
较新版本的汇编程序(MASM5.0与MASM6.0)除支持完整段定义伪指令外,还提供了一种新的简单易用的存储模型和简化的段定义伪指令。
?
?
1.存储模型伪指令
存储模型的作用是什么呢?存储模型决定一个程序的规模,也确定进行子程序调用、指令转移和数据访问的缺省属性(NEAR或FAR)。当使用简化段定义的源程序格式时,在段定义语句之前必须有存储模型 .MODEL语句,说明在存储器中应如何安放各个段。
?
?
MODEL伪指令的常用格式如下:
.?.MODEL?存储模型
?
?
2.
简化的段伪指令
简化的段定义语句书写简短,语句.CODE、.DATA和.STACK分别表示代码数据段和堆栈段的开始,一个段的开始自动结束前面一个段。采用简化段指令之前必须有存储模型语句.MODEL。
?
?
3.与简化段定义有关的预定义符号
汇编程序给出了与简化段定义有关的一组预定义符号,它们可在程序中出现,并由汇编程序识别使用。有关的预定义符号如下:
(1)@code 由.CODE 伪指令定义的段名或段组名。
(2)@data 由.DATA 伪指令定义的段名,或由 .DATA 、.DATA?、
.CONST和 .STACK所定义的段组名。
(3)@stack 堆栈段的段名或段组名。
?
?
4.简化段定义举例
?
?
1.
存储模型伪指令
?
?
?表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 |
创建一个数据段 |
段名是:_DATA |
.DATA? |
创建无初值变量的数据段 |
段名是:_BSS |
.FARDATA?[段名] |
建立有初值的远调用数据段 |
可指定段名,如不指定,则将以FAR_DATA命名。 |
.FARDATA??[段名] |
建立无初值的远调用数据段 |
可指定段名,如不指定,则将以FAR_BSS命名。 |
.CONST |
建立只读的常量数据段 |
段名是:CONST |
.STACK?[大小] |
创建一个堆栈段并指定堆栈段大小 |
段名是:stack。如不指定堆栈段大小,则缺省值为1KB |
?3.与简化段定义有关的预定义符号
?
?
?下面的举例说明预定义符号的使用方法。在完整的段定义情况下,在程序的一开始,需要用段名装入数据段寄存器,如例4.1中的
mov ax,data_seg1
mov ds,ax
若用简化段定义,则数据段只用.data来定义,而并未给出段名,此时可用
mov ax,@data
mov ds,ax
这里预定义符号@data就给出了数据段的段名。
?
?
4.简化段定义举例
?
?
例
.MODEL SMALL
.STACK 100H ; 定义堆栈段及其大小
.DATA ; 定义数据段
.
.
.
.CODE ; 定义代码段
START: ; 起始执行地址标号
MOV AX, @DATA ; 数据段地址
MOV DS, AX ; 存入数据段寄存器
.
.
.
MOV AX, 4C00H
INT 21H
END START ; 程序结束
?
?
从例可以看出,简化段定义比完整的段定义简单得多。但由于完整的段定义可以全面地说明段的各种类型与属性,因此在很多情况下仍需使用它。
?
?
段组定义伪指令
?
?
段组定义伪指令能把多个同类段合并为一个64KB的物理段,并用一个段组名统一存取它。段组定义伪指令GROUP的格式如下:
?
?
段组名 GROUP 段名 [, 段名
…]
?
?
我们已经知道在各种存储模型中,汇编程序自动地把各数据段组成一个段组DGROUP,以便程序在访问各数据段时使用一个数据段寄存器DS,而GROUP伪指令允许用户自行指定段组。
例 ?将两个数据段DSEG1 和DSEG2合并在一个段组DATAGROUP中。
?
?
;----------------------------------------------------
?
?
DSEG1 SEGMENT WORD PUBLIC ‘DATA‘
.
.
.
DSEG1 ENDS
;---------------------------------------------------
?
?
DSEG2 SEGMENT WORD PUBLIC ‘DATA‘
.
.
.
DSEG2 ENDS
MOV AX, @DATA ; 数据段地址
MOV DS, AX ; 存入数据段寄存器
.
.
.
;---------------------------------------------------
?DATAGROUP GROUP DSEG1, DSEG2?;组合成段组
?
?
CSEG SEGMENT PARA PUBLIC ‘CODE‘
?ASSUME CS : CSEG, DS : DATAGROUP
START: MOV AX,?DATAGROUP
?MOV DS, AX
;DS赋值为段组地址
?.
?.
?.
?MOV AX, 4C00H
INT 21H
?CSEG ENDS
;-----------------------------------------------------
END START
?
?
利用GROUP伪指令定义段组后,段组内统一为一个段地址,各段定义的变量和标号都可以用同一个段寄存器进行访问。
程序开始和结束伪指令
?
?
在程序的开始可以用NAME或TITLE作为模块的名字,其格式为:
?
?
NAME?模块名
TITLE文件名
?
?
表示源程序结束的伪指令的格式为:
?
?
END?[标号]
注意:NAME及TITLE伪指令并不是必需的,如果程序中既无NAME又无TITLE伪指令,则将用源文件名作为模块名。程序中经常使用TITLE,这样可以在列表文件中打印出标题来。
?
?
END伪指令中的"标号"指示程序开始执行的起始地址。如果多个程序模块相连接,则只有主程序的END要加上标号,其他子程序模块则只用END而不必指定标号。例4.1~4.3的最后使用了END START伪指令。汇编程序将在遇END时结束汇编,并且程序在运行时从START开始执行。
数据定义及存储器分配伪指令
?
?
80x86提供了各种数据及存储器分配伪指令,这些伪指令在汇编程序对源程序进行汇编期间,由汇编程序完成数据类型定义及存储器分配等功能。
?
?
数据定义及存储器分配伪指令的格式是:
[变量] 助记符
操作数[, …,操作数] [ ;注释]
?
?
?下面介绍ORG伪指令以及常用的数据定义伪指令。
?
?
ORG(origin)
ORG伪指令用来表示起始的偏移地址,紧接着ORG的数值就是偏移地址的起始值。ORG伪操作常用在数据段指定数据的存储地址,有时也用来指定代码段的起始地址。
?
?
DB(define byte)
DB伪指令用来定义字节,对其后的每个数据都存储在一个字节中。DB能定义十进制数、二进制数、十六进制数和ASCII字符,二进制数和十六进制数要分别用"B"和"H"表示,ASCII字符用单引号(‘ ‘)括起来。DB还是唯一能定义字符串的伪操作,串中的每个字符占用一个字节。
?
?
?DW(define word)
DW伪指令用来定义字,对其后的每个数据分配2个字节(1个字),数据的低8位存储在低字节地址中,高8位存储在高字节地址中,如下例中的变量DATA8的数据存储在0070字地址中,其中0070字节存储0BAH,0071字节存储03H。DW还可存储变量或标号的偏移地址。见左面DW伪指令的例子。
?
?
DD(define doubleword)
DD伪指令用来定义双字,对其后的每个数据分配4个字节(2个字)。该伪指令同样将数据转换为十六进制,并根据低地址存储低字节,高地址存储高字节的规则来存放数据。如下例DATA15的存储情况是:00A8:0F2H,00A9H:57H,00AAH:2AH,00ABH:5CH。
用DD存入地址时,第一个字为偏移地址,第二个字为段地址。
?
?
DQ(define quadword)
DQ伪指令用来定义4字,即64位字长的数据,DQ之后的每个数据占用8个字节(4个字)。
?
?
DT(define ten bytes)
DT伪指令用来为压缩的BCD数据分配存储单元,它虽然可以分配10个字节(5个字),但最多只能输入18个数字,要注意的是,数据后面不需要加"H"。左面是DQ和DT的例子。
?
?
DUP(duplicate)
DUP伪指令可以按照给定的次数来复制某个(某些)操作数,它可以避免多次键入同样一个数据。例如,把6个FFH存入相继字节中,可以用下面两种方法,显然用DUP的方法更简便些。
?
?
存入6字节的FFH
DATA20 DB 0FFH 0FFH 0FFH 0FFH 0FFH 0FFH;
DATA21 DB 6 DUP(0FFH)
?
?
DUP操作一般用来保留数据区,如用数据定义伪指令"DB 64 DUP(?)"可为堆栈段保留64个字节单元。DUP还可以嵌套,其用法见左例。
?
?
PTR属性操作符
PTR指定操作数的类型属性,它优先于隐含的类型属性。其格式为:
?
?
类型?PTR?变量[ ± 常数表达式]
?
?
其中类型可以是BYTE、WORD、DWORD、FWORD、QWORD或TBYTE,这样变量的类型就可以指定了。
?
?
LABEL伪指令
LABEL可以使同一个变量具有不同的类型属性。其格式为:
?
?
变量名 LABEL 类型
或 标号
LABEL 类型
?
?
其中变量的数据类型可以是BYTE,WORD,DWORD,标号的代码类型可以是NEAR或FAR。
数据定义及存储器分配伪指令格式中的"变量"是操作数的符号地址,它是可有可无的,它的作用与指令语句前的标号相同,区别是变量后面不加冒号。如果语句中有变量,那么汇编程序将操作数的第一个字节的偏移地址赋于这个变量。
?
?
"注释"字段用来说明该伪指令的功能,它也不是必须有的。
"助记符"字段说明所用伪指令的助记符。
?
?
DB(define byte)
请看下面数据定义的例子,注意DB定义的每个数据的存储情况,左边第一列是汇编程序为数据分配的字节地址,第二列是相应地址中存储的数据或ASCII字符(均用十六进制表示)。变量DATA7定义了3个数据和一个字符串,每个数据或串用","分开,它们分别存储在偏移地址002E开始的6个字节单元中。
?
?
?表
; DB 例子的列表文件 0000 19 DATA1 DB 25 ; 十进制数 0001 89 DATA2 DB 10001001B ; 二进制数 0002 12 DATA3 DB 12H ; 十六进制数 0010 ORG 0010H ; 指定偏移地址为10h 0010 32 35 39 31 DATA4 DB ‘2591‘ ; ASCII码数 0018 ORG 0018H ; 指定偏移地址为18h 0018 00 DATA5 DB ? ; 保留一个字节 0020 ORG 0020H ; 指定偏移地址为20h 0020 4D 79 20 6E 61 6D DATA6 DB ‘My name is Joe‘ ; ASCII码字符 65 20 69 73 20 4A 6F 65 002E 0A 10 02 31 30 42 DATA7 DB 10,10H,10B,‘10B‘ ; 不同的数据类型 |
?DW(define word)
?
?
?表?
?
?
; DW 伪指令例子的列表文件 0070 0RG 70H ;指定起始地址 0070 03BA DATA8 DW 954 ; 十进制数 0072 0954 DATA9 DW 100101010100B ; binary 0074 253F DATA10 DW 253FH ; 十六进制数 0076 FFFB DATA11 DW -5 ; 负数 0080 ORG 80H 0080 0009 FFFF 0007 000C DATA12 DW 9,-1,7,0CH,00100000B,100,‘HI‘ 0020 0064 4849 ; 各种类型数据 |
?DD(define doubleword)
?
?
?表
?
?
?
?
; DD例子的列表文件 00A0 ORG 00A0H ; 指定起始地址 00A0 FF030000 DATA13 DD 1023 ; 十进制数 00A4 5C960800 DATA14 DD 10001001011001011100B ; 二进制数 00A8 F2572A5C DATA15 DD 5C2A57F2H ; 十六进制数 00AC 23000000 89470300 DATA16 DD 23H,34789H,65533 ; 各种数据 FDFF0000 |
DT(define ten bytes)
?
?
?表
?
?
; DQ、DT例子的列表文件 00C0 ORG 00C0H 00C0 C223450000000000 DATA17 DQ 4523C2H ; 十六进制数 00C8 4948000000000000 DATA18 DQ ‘HI‘ ; ASCII字符 00D0 0000000000000000 DATA19 DQ ? ; 分配8个字节单元 00E0 ORG 00E0H 00E0 2998564379860000 DATA20 DT 867943569829 ; 压缩的BCD数 0000 00EA 0000000000000000 DATA21 DT ? ; 分配10个字节单元 0000 |
?DUP(duplicate)
?
?
?表
?
?
; DUP例子的列表文件 0100 ORG 0100H ; 数据区的起始地址 0100 0020[ DATA22 DB 32 DUP(?) ; 保留32字节 ?? ] 0120 ORG 0120H 0120 0005[ DATA23 DB 5 DUP(2 DUP(99)); 存入10个字节的99 0002[ 63 ] ] 012A 0008[ ATA24 DW 8 DUP(?) ; 保留8个字节 ???? ] |
?对数据定义伪指令前面的变量还要注意它的类型属性问题。变量表示该伪指令中的第一个数据项的偏移地址,此外,它还具有一个类型属性,用来表示该语句中的每一个数据项的长度(以字节为单位表示),因此DB伪指令的类型属性为1,DW为2,DD为4,DQ为8,DT为10。变量表达式的属性和变量是相同的。汇编程序可以用这种隐含的类型属性来确定某些指令是字指令还是字节指令。
?
?
下例中变量OPER1为字节类型属性,OPER2为字类型属性,所以第一条MOV指令应为字节指令,第二条MOV指令应为字指令。而第三条指令的变量表达式OPER1+1为字节类型属性,AX却为字寄存器,第四条指令的OPER2为字类型属性,AL为字节寄存器,因此,汇编程序将指示这两条MOV指令出错:"类型不匹配"。
?
?
OPER1 DB ?, ?
OPER2 DW ?, ?
.
.
.
MOV OPER1, 0 ;字节指令
MOV OPER2, 0 ;字指令
MOV AX, OPER1+1 ;错误指令:类型不匹配
MOV AL, OPER2 ;错误指令:类型不匹配
?
?
PTR属性操作符
下例中的两条MOV指令把OPER1+1的类型属性指定为字,把OPER2的类型属性指定为字节,这样指令中两个操作数的属性就一致了,汇编时就不会出错了。
?
?
OPER1 DB ?, ?
OPER2 DW ?, ?
.
.
.
MOV AX, WORD PTR OPER1+1
MOV AL, BYTE PTR OPER2
?
?
LABEL伪指令
?
?
例如:
BYTE_ARRAY LABEL BYTE
WORD_ARRAY DW 50 DUP (?)
?
?
在50个字数组中的第一个字节的地址赋予两个不同类型的变量名:字节类型的变量BYTE_ARRAY和字类型变量WORD_ARRAY。
?
?
在程序中访问数组单元时,要按指令类型来选择变量,如下面两条指令:
?
?
MOV WORD_ARRAY + 2,0 ; 字指令,
; 把该数组的第3个和第4个字节置0
MOV BYTE_ARRAY + 2,0 ; 字节指令,
; 把该数组的第3个字节置0
表达式赋值伪操作EQU
?
?
EQU是一个赋值伪操作(伪指令),它给一个数据标号赋于一个常数值,但这个常数不占用存储单元。当这个数据标号出现在程序中时,汇编程序即用它的常数值代替数据标号。EQU可以在数据段之外使用,甚至可用在代码段中间。
?
?
= 伪操作
?
?
?赋值伪操作"="的作用与EQU类似。它们之间的区别是,EQU伪操作中的标号名是不允许重复定义的,而=伪操作是允许重复定义的。
?
?
使用EQU操作的优点可从下面的例子中看出:
?
?
COUNT EQU 25
COUNTER DB COUNT
MOV AL, COUNT
?
?
假定在数据段和代码段中要多次使用一个数据(如25),那么在编程时凡是用到25的地方都可用数据标号COUNT来表示。如果程序想修改这个数据,那么只需修改EQU的赋值,而无须修改程序中其它部分,如COUNTER和MOV语句就不必修改。
?
?
EQU还可给表达式赋予一个名字,EQU的用法举例如下:
?
?
DATA EQU HEIGHT + 12 ; 地址表达式赋以符号名
ALPHA EQU 7 ; 常数赋以符号名
BETA EQU ALPHA-2 ; 把7-2=5赋以符号名BETA
ADDR EQU VAR + BETA ; VAR+5赋以符号名ADDR。
B EQU [BP + 8] ; 变址引用赋以符号名 B
P8 EQU DS:[BP + 8] ; 加段前缀的变址引用赋以符号名P8
?
?
注意:在EQU语句的表达式中,如果有变量或标号的表达式,则在该语句前应该先给出它们的定义。如上例,ALPHA必须在BETA之前定义,否则汇编程序将指示出错。
?
?
例如,
TMP EQU 5
TMP EQU TMP+1 则是错误语句,因为TMP已赋值为5,就不能再把它定义为其它数值。
而 TMP = 5
TMP = TMP+1 则是允许使用的,因为=伪操作允许重复定义。第一个语句TMP的值为5,第二个语句TMP的值就为6了。
地址计数器与对准伪指令
?
?
?1.地址计数器$
?
?
在汇编程序对源程序汇编的过程中,使用地址计数器来保存当前正在汇编的指令的地址。地址计数器的值在汇编语言中可用$来表示。
?
?
当$用在伪指令的参数字段时,它所表示的是地址计数器的当前值
?
?
2.EVEN伪指令
?
?
EVEN伪指令使下一个变量或指令开始于偶数字节地址。
?
?
3. ALIGN伪指令
?
?
ALIGN伪指令使它后面的数据或指令从2的整数倍地址开始。其格式为:
?
?
ALIGN 2n (n为任意整数)
1.地址计数器$
?
?
汇编语言允许用户直接用$来引用地址计数器的值,例如指令:
?
?
JMP?$+ 6
?
?
它的转向地址是JMP指令的首地址加上6。当$用在指令中时,它表示本条指令的第一个字节的地址。在这里,$+ 6必须是另一条指令的首地址。否则,汇编程序将指示出错信息。
?
?
当$用在伪指令的参数字段时,则和它用在指令中的情况不同,它所表示的是地址计数器的当前值。例如指令:
?
?
ARRAY DW?1, 2, $+ 4, 3, 4, $+ 4
?
?
假设汇编时ARRAY 分配的偏移地址为0074H,则汇编后,$+ 4所在的两个字单元:
(ARRAY+4)=0078+4=007CH
(ARRAY+0A)=007E+4=0082H
?
?
应当注意,ARRAY数组中的两个$+ 4得到的结果是不同的,这是由于$的值是在不断变化的缘故。当在指令中用到$时,它只代表该指令的首地址,而与$本身所在的字节无关。
?
?
2.EVEN伪指令
?
?
例如:
DATA_SEG SEGMENT
BYTE_DAT DB ?
EVEN
WORD_DAT DW 100 DUP (?)
DATA_SEG ENDS
?
?
一个字的地址最好从偶地址开始,所以对于字数组为了保证它从偶地址开始,可以在DW定义之前用EVEN伪指令来达到这一目的。
?
?
3. ALIGN伪指令
?
?
例如:
.
ALIGN 4
ARRAY DD 100 DUP (?)
?
?
ALIGN伪指令保证了双字数组ARRAY地址边界从4的倍数开始。
?
?
ALIGN伪指令是将当前偏移地址指针指向2的乘方的整数倍的地址,如果源地址指针以指向2的乘方的整数倍的地址,则不作调整;否则将指针加以一个数,使地址指针指向下一个2的乘方的整数倍的地址。
?
?
当然,ALIGN 2和EVEN是等价的。
基数控制伪指令
?
?
?.RADIX伪指令
?
?
.RADIX可以把默认的基数改变为2~16范围内的任何基数。其格式如下:
.RADIX?基数值
?
?
其中基数值用十进制数来表示。
例如:
MOV BX, 0FFH
;16进制数标记为H
MOV BL, 10000101B ;二进制数标记为B
MOV BX, 178 ;10进制为默认的基数,可无标记
.RADIX 16
;以下程序默认16进制数
MOV BX, 0FF ;16进制为默认的基数,可无标记
MOV BX, 178D
;10进制数应加标记D
?
?
应当注意,在用 .RADIX 16把基数定为十六进制后,十进制数后面都应跟字母D。在这种情况下,如果某个十六进制数的末字符为D,则应在其后跟字母H,以免与十进制数发生混淆。
?
?
来自 <http://blog.csdn.net/xinfeiyang060502118/article/details/7279020>