定位类型
|
说 明
|
BYTE
|
段可以从任何地址边界开始
|
WORD
|
段从字边界开始,即段的起始边界值为偶数
|
DWORD
|
段从双字的边界开始,即段的起始边界值为4的倍数
|
PARA
|
段从小段边界开始,即段的起始边界值为16 (或10H) 的倍数
|
PAGE
|
段从页边界开始,即段的起始边界值为256 (或100H) 的倍数
|
组合类型
|
说 明
|
PRIVATE
|
该段为私有段,连接时将不与其它模块中的同名段合并
|
PUBLIC
|
该段连接时将与其它同名段连接在一起,连接次序由连接命令指定
|
COMMON
|
该段在连接时与其它同名段有相同的起始地址,所以会产生覆盖
|
AT 表达式
|
段地址=表达式的值,其值必为16位但AT不能用来指定代码段
|
MEMORY
|
与PUBLIC同义
|
STACK
|
将多个同名堆栈段连接在一起,SP设置在第一个堆栈段的开始
|
存储模型
|
功 能
|
适用操作系统
|
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 |
简化段伪指令
|
功 能
|
注释
|
.CODE [段名] | 创建一个代码段 | 段名为可选项,如不给出段名,则采用默认段名。对于多个代码段的模型,则应为每个代码段指定段名。 |
.DATA | 创建一个数据段 | 段名是:_DATA |
.DATA? | 创建无初值变量的数据段 | 段名是:_BSS |
.FARDATA [段名] | 建立有初值的远调用数据段 | 可指定段名,如不指定,则将以FAR_DATA命名。 |
.FARDATA? [段名] | 建立无初值的远调用数据段 | 可指定段名,如不指定,则将以FAR_BSS命名。 |
.CONST | 建立只读的常量数据段 | 段名是:CONST |
.STACK [大小] | 创建一个堆栈段并指定堆栈段大小 | 段名是:stack。如不指定堆栈段大小,则缺省值为1KB |
程序开始和结束伪指令
在程序的开始可以用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例子的列表文件 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 |
; 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例子的列表文件 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个字节 ???? ] |
表达式赋值伪操作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,以免与十进制数发生混淆。