目录
0,标识符的概念
标号,内存变量名,子程序名和宏名都是标识符。由字母,数字,规定的特殊字符(?,@,_,$)等组成。
开头不能是数字,不能用汇编语言的保留字:指令助记符,伪指令定义符,寄存器名及一些特殊含义的字符串。
不区分字母大小写。
一,简单内存变量的定义
定义数据变量语句实在程序中经常使用的伪指令语句,一般格式:
[变量名] 数据定义符 表达式1[, 表达式2, …, 表达式n] ;注释
1)数据定义符用于确定内存单元的数据类型,常用的定义符有: dd ,dw, db。
2)表达式是定义内存单元时的初值表达式,一个定义语句可以有多个初值表达式,各表达之间必须用逗号分开,
如果某个存储单元没有初值表达式,则必须用一个“?”来表示。
1,字节变量
定义字节变量的定义符为db / byte(define byte),每个字节只占一个字节单元。
counter db 6
db 'a', 'd', 'odh', '$'
table db 1, 3, 4, 7, 9, 11
在第二行前面并没有给出变量名,但是可以从counter继续往后数,来访问内存单元,因为他们是一片连续的存储单元。
这和高级语言的变量定义有点区别,在高级语言中,一定要用某个标识符来说明变量,也必须用该变量名来访
问其所对应 的存储单元。
定义符db还可以定义一种特殊的数据形式--字符串,在定义字符串时,必须用双引号或者单引号括起来,括号内
字符的ASCII码将一次存放在相应的字节单元内。
MSG1 db ‘I am a student.’
2,字变量(16bit)
定义符为 dw / word (define word), 每个字占用两个连续的字节单元。
Word1 dw 89h, 1909h, -1
dw 0abcdh, ? , 0
3,双字变量
定义符为dd / dword (define doubleworld),每个双字变量占用4个字节。
DW1 dd 12345678h, ?
DW2 dd 0abcd1234h
后面的三个基本不咋用
4, 六字节变量
定义符为df / fword(define farword)。每个六字节变量占用六个连续字节。
5,8字节变量 dq/qword
6, 10字节变量 dt/tbyte
二,调整偏移量伪指令
作用:是在内存变量定义时用来调整内存变量起始偏移量的,他们是在把源程序汇编成目标时起作用。
常用的调整偏移量伪指令有:even, align, org。
目的: 为了更有效地读取内存单元的内容。
1,偶对齐伪指令even (用的频率高)
格式 even
伪指令的作用是:告诉汇编程序(Assember),本伪指令下面的内存变量从下一个偶地址单元开始分配。
如果下一个偏移量是偶地址,那么,该伪指令不起作用,否则,汇编程序将空出一个字节,
从下一偶地址开始为其后变量分配内存单元。
2,对齐伪指令align
格式 align num ;num 必须时2的幂,如 2, 4, 8, 16
伪指令的作用是:告诉汇编程序,本伪指令下面的内存变量必须从下一个能被Num整除的地址开始分配。
如果下一个地址正好能被Num整除,那么,该伪指令不起作用,否则,汇编程序将空出若干个字节,
直到下一个地址能被Num整除为止。
3,调整偏移量伪指令org
格式 org 数值表达式
4,偏移量计数器的值
"$ "来引用偏移量计数器的值。
三, 复合内存变量的定义
1,重复说明符 dup
格式 count dup (表达式,表达式,表达式)
解释:count是重复次数,(表达式, 表达式, …, 表达式)是被重复的部分,“表达式”可以是存储单元的初值,
也可以是含义另一个DUP的式子。如果在表达式的括号中有多个表达式,那么,它们之间要用逗号','分开。
例如:
BUFFER | DB | 100 DUP(?) |
STRING | DB | 120 DUP('ABCDE'), 0 |
DATA1 | DW | 50 DUP(10H, 20 DUP(1,2,3), 20H) |
POINTS | DD | 12, 30 DUP(0) |
2,结构类型的定义
dup只能用于重复同一类型的变量说明,它不可以重复不同数据类型的变量说明。(ps,就是定义数组用的吧)
为了把一组不同类型的变量说明组合在一起,汇编语言提供了一种复合类型说明符: struc (定义struct的)
1)用struc 和ends 可以把一系列数据定义语句括起来作为一种新的、用户定义的结构类型。
一般说明格式如下:
结构名 STRUC [Alignment][, NONUNIQUE]
数据定义语句序列
结构名 ENDS
解释:结构名是一个合法的标识符,且具有唯一性。结构名代表整个结构类型,前后两个结构名必须一致。
结构内被定义的变量为结构字段,变量名即为字段名。
一个结构中允许含有任意多个字段,各字段的类型和所占字节数也都可任意。如果字段有字段名,
则字段名必须唯一。每个字段可独立存取。
参数:
对齐方式(Alignment):可用1、2或4来指定结构中字段的字节边界(Byte boundary),
其缺省值为1。
NONUNIQUE:要求结构中的字段必须用全名才能访问,见本小节中的“结构类型字段的引用”。
下面这个例子是很好理解的。
2)结构类型变量的定义
在定义某个结构类型后, 程序员就可以说明该结构类型的内存变量,它的说明形式如下:
[变量名] 结构名 <[字段值表]>
解释:1)、 | 变量名即为该结构类型的变量名,它可省缺。如果省缺,则不能用符号名来访问该内存单元; |
2)、 | 字段值表是给字段赋初值,中间用逗号','分开,其字段值的排列顺序及类型应与该结构说明时各字段相一致; |
3)、 | 如果结构变量中某字段用其说明时的缺省值,那么,可用逗号来表示;如果所有字段都如此,则可省去字段值表,但必须保留一对尖括号"<"、">"。 |
例如: | |||
COURSE1 | COURSE <> | ;使用缺省的初值 | |
COURSE2 | COURSE <1, 'Pascal', 60> | ||
COURSE3 | COURSE <2, , 84> | ;使用缺省的课程名 | |
PEASON1 | PEASON | <1000, '张 三', 34> |
3)结构类型字段的引用
定义了结构类型的变量后,若要访问其结构中的某个字段,则可采用如下形式:
结构变量名.字段名 ;中间有个点
该引用方式与高级语言的字段引用方式完全一致,我们还可用偏移量来访问其中的某个字段,
但此方法不直观,变动性大,所以,一般情况下,不提倡使用此方法。
例如:
EXAM1 | STRUC | |||
F1 | DW ? | |||
F2 | DB ? | |||
EVEN | ;偶对齐 | |||
F3 | DW ? | |||
EXAM1 | ENDS | |||
E1 | EXAM1 <1234H,'A',8765H> | ;定义结构EXAM1的一个变量E1 |
下面二种方法都可以把结构变量E1中字段的内容赋给寄存器AX,但如果在字段F3之前增加或减少了字段,
那么,这些引用需要改变吗?
(1)、用字段名直接引用
MOV AX, E1.F3
(2)、用字段的偏移量间接引用
LEA SI, E1
MOV AX, [SI+4] ;其中4是字段F3的偏移量
3, 联合类型的定义
联合数据类型是一种特殊的数据类型。
它可以实现:以一种数据类型存储数据,以另一种数据类型来读取数据。
根据不同的需要,以不通的数据类型来读取联合类型中的数据。
1)联合类型的说明
格式:
[联合类型名] UNION [Alignment] [,NONUNIQUE]
数据定义语句序列
[联合类型名] ENDS
联合类型中的各字段相互覆盖,即同样的存储单元被多个不同的字段所对应,并且其每个字段的偏移量都为0。
联合类型所占的字节数是其所有字段所占字节数的最大值。
在联合类型的最外层定义中,在伪指令union和ends的前面一定要书写该联合类型名,
而在其嵌套定义的内层,伪指令union和ends之前一定不能写联合类型名。
2)联合类型变量的定义
联合数据类型的变量只能用第一个字段的数据类型来进行初始化。
3)联合类型字段的引用
定义了联合类型的变量后,就可根据需要,以不通的数据类型或字段名来存取该联合类型中的数据。
引用其字段的具体形式如下:
联合类型变量名.字段名
MOV | U1.WW, 1234H | ;给数据类型变量赋字数据 |
MOV | AL, U1.BB | ;AL=34H |
MOV | BX, U1.WW | ;BX=1234H |
MOV | U1.BB, 'A' | ;U1的值1241H,41H是'A'的ASCII码 |
4, 记录类型的定义
1)记录类型的说明 record
记录名 record 字段 [, 字段 ...] ;字段表示: 字段名:宽度 [=表达式]
例子: color record blink: 1 , back: 3= 0, intense; 1 = 1, Fore :3
解释: | 1、记录名代表该记录类型; |
2、记录类型可以由多个字段组成,每个字段之间要用逗号','分开; | |
3、字段的属性包括字段名、宽度和初值; | |
4、字段的“宽度”表示该字段所占的二进制位数,它必须是一个常数,并且所有字段的宽度之和不能大于16;如果记录的总宽度大于8,则系统为该记录类型分配二个字节,否则,只分配一个字节; | |
5、初值表达式给出的是该字段的缺省值。如果初值超过了该字段的表示范围,那么,在汇编时将产生错误提示信息;如果某字段没有初值表达式,则其初值为0 |
2)记录变量的定义
在程序中,必须先说明记录类型,然后才能定义该记录类型的变量。
记录变量是把其二进制位分成一个或多个自断的字节或字变量。其定义格式与其他类型变量的定义
方式类似,
[变量名] 记录名 <[字段值表]>
解释: | 1、变量名即为该记录类型的变量名,它可省缺。如果省缺,则不能用符号名来访问该内存单元; |
2、字段值表是给字段赋初值,中间用逗号','分开,其字段值的排列顺序及大小应与该记录说明时各字段相一致; | |
3、如果记录变量的某字段用其说明时的缺省值,那么,可用逗号来表示;如果所有字段都如此,则可省去字段值表,但必须保留一对尖括号"<"、">"。 |
例如:
color1 color <>, <1, 7, 5, 0>, <1, , 0, 7>
3)记录的专用操作符
操作符WIDTH和MASK是作用于记录类型的两个专用保留字,利用它们可得到记录类型的不同属性。
操作符WIDTH返回记录或其字段的二进制位数,即其宽度。其一般书写格式如下:
WIDTH 记录名 或 WIDTH 记录字段名
假设有前面定义的记录类型COLOR,那么,WIDTH COLOR的值为8,WIDTH BACK的值为3,WIDTH BLINK的值为1。
操作符MASK返回一个8位或16位二进制数。在该二进制数中,被指定记录或字段使用的对应位的值为1,否则,其值为0。其一般书写格式如下:
MASK 记录名 或 MASK 记录字段名
假设有前面定义的记录类型FLOAT,那么,MASK EXP的值为000FH,MASK DATA的值为1FE0H,WIDTH DSIGN的值为2000H。
-
记录字段
记录字段名是一个特殊的操作符,它本身也是操作数,其返回值是该字段移到所在记录的最低位所需要的位数,即该字段最低位在记录中的位置。
假设有前面定义的记录类型FLOAT,那么,有:
MOV CL, EXP | 相当于 | MOV CL, 0 | |
MOV CL, DATA | 相当于 | MOV CL, 5 |
5,标号
标号是一种特殊的标识符,它代表代码段中的某个具体位置,它主要用于表明转移的目标位置。其说明形式如下:
标号: 汇编语言指令 ;注释
解释:标号必须是一个合法的标识符,在其后面紧跟一个冒号":",冒号与汇编语言指令之间要有分隔符。
通常用若干个空格、TAB来作分隔符,一般用分隔符使有关内容对齐为宜。
四,内存变量和标号的属性
变量是一个符号地址,其值会根据其数据类型来对应从该地址以后的若干个存储单元中所存的数据。
标号也是一个符号地址,它所对应的存储单元中存放的是指令代码。
它们都是一个符号地址,代表一个存储单元的地址,所以都具有存储单元的属性。以及各自的特性:
1,段属性操作符
段属性操作符(SEG)返回该标识符所在段的段地址。我们一般只会取内存变量所在段的段地址,
而很少取标号所在段的段地址。
2,偏移量属性操作符
偏移量属性操作符(OFFSET)返回该标识符离它所在段的段地址有多少字节。一般情况,程序员只会取内存变量的偏移量,而不太关心标号的偏移量。
3,类型属性操作符
类型属性操作符(TYPE)是返回该变量所占字节数,或标号的“远”(FAR)、“近”(NEAR)类型。
4,长度属性操作符
长度属性操作符(LENGTH)是针对内存变量的操作符,它返回重复操作符DUP中的重复数。
如果有嵌套的DUP,则只返回最外层的重复数;如果没有操作符DUP,则返回1。
5,容量属性操作符
容量属性操作符(SIZE)也是针对内存变量的操作符。它的返回值按下列公式计算:
SIZE 变量 = (LENGTH 变量) × (TYPE 变量)
6,强制属性操作符
在程序中,我们有时需要对同一个存储单元以不同的属性来访问,或对一些不确定的存储属性需要显式指定等,
这时,我们就需要强制属性操作符PTR。该操作符的作用有点象C语言中的类型强制方法。
对于指令:
MOV [BX], 1H,
其目标操作数[BX]是寄存器间接寻址方式,它指向一个存储单元。
在作传送操作时,是把“1H”扩展成8位作字节传送,还是扩展成16位作字传送呢?这就使该指令具有二义性,
因为[BX]指向的存储单元可以字节或字的首地址。含有该指令的程序在汇编时,可能会产生警告或出错信息。
为了使指令中存储单元操作数具有明确的属性,我们可以使用强制属性操作符PTR。其一般格式为:
数据类型 PTR 地址表达式
其中:数据类型是前面所学的各种数据类型,常用的数据类型有:BYTE、WORD、DWORD、NEAR和FAR等。
为了明确指令中存储单元的属性,可把指令“MOV [BX], 1H”可改写成:
MOV byte ptr [BX], 1H 或 MOV word ptr [BX], 1H
在指令中用操作符PTR强制后,不管其后的地址表达式原数据类型是什么,在本指令中就以PTR前面的类型为准。
该强制属性只在本指令有效,是一种临时性的属性,它不会改变原内存单元的定义属性。
7,存储单元别名操作符
在程序中,如果需要以另一种数据类型来访问某一存储单元时,可用强制属性操作符PTR来实现。但如果在程序中要经常以某种其它的数据类型来访问该存储单元的话,那么,就必须在每次访问时都要加上强制属性操作符PTR。这样做虽然可行,但在编写程序时就显得比较麻烦。
为了克服上述不便,汇编提供了一个操作法this,它为同一存储单元取另一别名,
该别名具有其自身的数据属性,但其段地址和偏移量是不变的。
格式:
this 数据类型 ;常用的数据类型有:BYTE、WORD、DWORD、NEAR和FAR等
例子:
wbuffer equ this word ;equ是一个等价符号定义语句
buffer db 20 dup(?)
1,表达式
分为:数值表达式 和 地址表达式
进制伪指令radix
伪指令radix用来设置证书的缺省进制,宏汇编开始的时候默认的证书进制是十进制。
格式:
.radix exp ;其中:伪指令前面要用点‘.’开始,exp的值必须是区间[2, 16]内的一个整数。
如果某整数已显式地表明了其进制,则该默认进制对其不起作用。
1,数值表达式
包括常量: 80h
算术运算符:+, - , : , *, / 和mod(取模)
关系运算符:eq, ne, lt, gt, le, ge (6个)
相等,不等,小于,大于,小于等于,大于等于
和常量,算术运算符一起组成数值表达式,
若关系不成立,则该数值表达式的计算结果为0;否则,其结果为0FFFFH。
逻辑运算符:and, or, not, xor, shl, shr (6个)
(逻辑) 与 或 非 异或 左移位, 右移位
其他:HIGH(高8位)、LOW(低8位),SEG(段地址)、OFFSET(偏移量),
TYPE(标识符类型)、LENGTH(变量长度)、SIZE(变量容量)
WIDTH(记录/记录字段宽度)、MASK(记录/记录字段的屏蔽位)等。
运算符和操作符的优先级
在汇编语言中,有许多各种运算符和操作符,它们的优先级按从高到低的排列如下:
优先级:高 | LENGTH、SIZE、WIDTH、MASK、()、[]、.(用于结构字段)、<>(用于记录类型) | |
| PTR、SEG、OFFSET、TYPE、THIS、:(用于段超越前缀) | |
*、/、MOD、SHL、SHR | ||
HIGH、LOW 向下 | ||
+、- | ||
EQ、NE、LT、LE、GT、GE | ||
NOT 向下 | ||
AND | ||
OR、XOR | ||
优先级:低 | SHORT |
2,地址表达式
地址表达式是计算存储单元地址的表达式,它可由标号、变量名和由括号括起来的基址或变址寄存器组成。
其计算结果表示一个存储单元的地址,而不是该存储单元的值。
2,符号定义语句
1)等价语句 equ
符号名 equ 表达式
注意:等价语句不会给符号名分配存储空间,符号名不能与其他符号同名,也不能被重新定义。
2)用符号名代表常量或表达式
当把一个常量或表达式定义成一个具有一定含义的符号名后,在程序中就可以用该符号名来代表
该常量或表达式。
例子:
number equ 100
buff_len equ number+2
cr equ 13 ;给“回车”符的ascii码定义一个符号名
lf equ 10 ;给“换行”符的ascii码定义一个符号名
……
buffer db number, ?, number dup(?)
……
3,等号语句