汇编语言程序的开发
语句类型
指令语句:完成一定操作功能,能够翻译成机器代码
指示语句:提供有关信息,并不能翻译成机器代码
定义数据段部分
1 | DATA SEGMENT |
定义代码段
1 | CODE SEGMENT |
定义源程序
1 | START: |
完整代码组成
1 | DATA SEGMENT ; 段定义开始(DATA段) |
解释: MOV AX,DATA MOV DS,AX 为什么不能直接 MOV DS, DATA
因为在汇编语言中,
DATA
只是一个符号,代表着数据段的起始地址,而不是一个立即数。段寄存器(如DS
)只能接受立即数作为其操作数,而不是一个符号。
参数、变量和标号
符号定义语句伪指令
等值语句
语句格式:符号名 EQU 表达式
上面的替换是前替后
功能:用符号名来表示EQU右边的表达式。后面的程序中一旦出现该符号名,汇编程序将把它替换成该表达式。
1.常数或数值表达式
1 | COUNT EQU 5 |
2.地址表达式
1 | ADR1 EQU DS:[BP+14] |
上面说叫在DS数据段中以BP作基址寻址的一个存储单元。这句话是什么意思
3.变量、寄存器名或指令助记符
1 | CREG EQU CX;在后面的程序使用CREG就是使用CX |
敲重点:在同一源程序中,同一符号不能用EQU定义多次。!!!
等号语句
格式:符号名=表达式
敲重点
- 等号语句与等值语句具有相同的作用。但等号语句可以对一个符号进行多次定义。
- 等值语句与等号语句都不会为符号分配存储单元。因此所定义的符号没有段、偏移量和类型等属性。
解除定义伪指令PURGE
功能:解除指定符号的定义
1 | Y1 EQU 7 |
数据定义伪指令
常数
十进制缺省
实数
整数部分 • 小数部分E ±指数部分
举例:2.134 E +10这个到底怎么读?
字符串常数
单引号或者双引号都可以
以各个字符的ASCII码值存储在内存。
变量
变量的定义与预置
- 命名
- 预制存储单元(伪指令 DB、DW、DD、DQ和DT等),含义是一次分配出多少存储单元
1 | VAR_DATA SEGMENT |
注: 1. 汇编中声明的变量名也是有地址的(类比C语言中指针的指针)
- DATA2可以理解成1个数组,这个数组中有2个字节,分别是20H和30H
变量的属性
段属性
它表示变量存放在哪一个逻辑段中。
例如上面例子中的变量DATA1、DATA2和DATA3三个变量都存放在VAR-DATA逻辑段中。
偏移量属性(OFFSET)
它表示变量所在位置与段起始点之间的字节数。
如上述例子中,变量DATA1的偏移量为0,DATA2为1,DATA3为3。
类型属性
它表示变量占用存储单元的字节数。其中DB伪指令定义的变量为字节,DW定义的变量为字,DD定义的为双字(4字节),DQ定义的为4字,DT定义的为5字。
变量赋值表达式
数值表达式
例如:DATA1 DB 32,30H DATA1的内容为32(20H),DATA1+1单元内容为30H.
?表达式
不带引号的问号“?”表示可以预置任意内容。
例如:DA-BYTE DB ?,?,? 表示让汇编程序分配三个字节存储单元。这些存储单元的内容的值为任意值。
但是在emu8086中无法使用,所以直接写作DA-BYTE DB 3
字符串表达式
DB
对于DB伪指令,字符串为用引号括起来的不超过255个字符。给每一个字符分配一个字节单元。字符串按从左到右映射为从低到高存放
举例:
STRING1 DB ‘ABCDEF’
DW
DW伪指令可以给两个字符组成的字符串分配两个字节存储单元。
两个字符的存放顺序是前一个字符放在高地址,后一字符放低地址单元。
举例:
STRING2 DW ‘AB’, ‘CD’, ’EF’
为什么41H比42H高?
DD
给两个字符组成的字符串分配4个字节单元。(就是会强制多分配)
两个字符存放在较低地址的两个字节单元中。存放顺序与DW伪指令相同,而较高的两个字节单元都存放0。
举例:
STRING3 DD ‘AB’, ‘CD’
注:DW和DD伪指令不能用两个以上字符构成的字符串赋初值,否则将出错。
DUP表达式
表达式1是重复的次数,表达式2是重复的内容。
举例:
1 | DATA_A DB 10H DUP(?) ; 分配16个字节单元(10H = 16D) |
注:DUP的嵌套使用
变量的使用
在指令语句中引用
直接引用变量名
在指令语句中直接引用变量名就是对其存储单元的内容进行存取
举例:
1 | DA1 DB 0FEH ; 将 0FEH 存储在 DA1 中 |
变量名出现在编制寻址
变量名作为地址
举例:
1 | 例如: |
总结:
在伪指令语句中引用
之前声明的变量名赋给新变量名,新变量得到的是原变量的偏移地址(新变量大小是1个字)或偏移地址+段地址(新变量大小是2个字)
举例: 1
2
3
4
5
6 设上述语句所在段的段基值为0915H,NUM的偏移量为0004H
NUM DB 75H ; 将 75H 存储在 NUM 中
ARRAY DW 20H DUP(0) ; 定义一个大小为 20 的字数组,每个单元里有4个存储单元。并全部初始化为 0
ADR1 DW NUM ; 将 NUM 的偏移地址存储在 ADR1 中
ADR2 DD NUM ; 将 NUM 的偏移地址和段地址存储在 ADR2 中
ADR3 DW ARRAY[2] ; 将 ARRAY 的第 2 个元素的偏移地址存储在 ADR3 中
详细解释
1 | ARRAY DW 20H DUP(0) |
1 | ADR1 DW NUM |
1 | ADR2 DD NUM |
1 | ADR3 DW ARRAY[2] |
注意ARRAY[ 1 ]和ARRAY + 1的区别
居然不是从0开始数?
标号
定义:标号主要用在程序中需要改变程序的执行顺序时,用来标记转移的目的地,即作转移指令的操作数。
属性
(1)段属性(SEG)
表示该标号所代表的地址在哪个逻辑段中,即段基值。
(2)偏移量属性(OFFSET)
它表示该标号所代表的地址在段内与段起点间的字节数,即地址的偏移量。
(3)距离属性(也叫类型属性)
它表示该标号可以被段内还是段间的指令调用。
NEAR(近):该标号只能作段内转移,也就是说只能是与该标号所指指令同在一个逻辑段的转移指令和调用指令才能使用它。缺省的情况下都默认是NEAR 如 SUB1:MOV AX,30H (也可以显式声明:标号名 LABEL NEAR)
FAR(远):该标号可以被非本段的转移和调用指令使用。需要显式指明。格式为: 标号名 LABEL FAR
举例:
1 | SUB1_FAR LABEL FAR |
SUB1_FAR与SUB1两个标号具有相同的段属性和偏移量属性,即相同的逻辑地址。被转移指令或调用指令访问时,是指同一个入口地址,但SUB1-FAR可以被其它段的指令调用。
表达式与运算符
算术运算符
运算符“+”和“-”也可作单目运算符,表示数的正负。
使用“+”、“-”、“*”、和“/”运算符时,参加运算的数和运算结果都是整数。
“/”运算为取商的整数部分,而“MOD”运算取除法运算的余数。
"SHR"和"SHL"为逻辑移位运算符
注意:移位运算符与移位指令区别在于, 移位运算符的操作对象是某一具体的数(常数),而移位指令是对一个寄存器或存储单元
举例:
移位运算符
1
2
3
4
5NUM=11011011B
…
MOV AX, NUM SHL 1
MOV BX, NUM SHR 2
ADD DX, NUM SHR 6移位指令
1
SHL AX 1
下标运算符“[ ]”具有相加的作用
作用:将表达式1与表达式2的值相加后形成一个存储器操作数的地址。
下面这两个语句等效
1
2MOV AX, DA_WORD[20H]
MOV AX, DA_WORD+20H敲重点, MOV中什么时候是取地址里所保存变量的值,什么时候是取地址
总结:寄存器之间如果是[]连接就是取地址里的值,如果是+连接就是取地址
逻辑运算符
逻辑运算符有NOT、AND、OR和XOR等四个,它们执行的都是按位逻辑运算。
1 | MOV AX, NOT 0F0H => MOV AX, 0FF0FH |
关系运算符
关系运算符包括:EQ(等于)、NE(不等于)、LT(小于)、 LE(小于等于)、GT(大于)、 GE(大于等于)
- 关系运算符用于比较两个表达式的大小。关系运算符比较的两个表达式必须同为常数或同一逻辑段中的变量
- 如果是常量的比较,则按无符号数进行比较;如果是变量的比较,则比较它们的偏移量的大小。
- 关系运算符得到结果如果是true,将所有位改为1,相反全部改为0
数值返回运算符
SEG运算符
取变量或标号所在段的段基值。
举例:
OFFSET运算符
该运算符的作用是取变量或标号在段内的偏移量。
举例:
TYPE运算符
该运算符的作用为取变量或标号的类型属性,并用数字形式来表示。对变量来说就是取它的字节长度。
举例:
1 | V1 DB 'ABCDE' |
等价于
1 | MOV AL,01H |
LENGTH运算符
该运算符只能加在变量的前面,且只能管最外层。比如如果变量是用重复数据操作符DUP说明的,则返回外层DUP给定的值。如果没有用DUP说明,则返回值总是1。
举例:
1 | K1 DB 10H DUP(0) |
SIZE运算符
该运算符只能作用于变量,SIZE取值等于LENGTH和TYPE两个运算符返回值的乘积
举例:
1 | K1 DB 10H DUP(0) |
属性修改运算符
PTR运算符
临时修改,类似于C语言强转
举例:
1 | DA_BYTE DB 20H DUP(0) |
HIGH/LOW运算符
将一个数据分离出高字节和低字节。
如果表达式为一个常量,则将其分离成高8位和低8位;如果表达式是一个地址(段基值或偏移量)时,则分离出它的高字节和低字节。
举例:
1 | DATA SEGMENT |
等价于
1 | MOV AH, 0ABH |
注:HIGH/LOW运算符不能用来分离一个变量、寄存器或存储器单元的高字节与低字节。
THIS运算符
THIS运算符一般与等值运算符EQU连用,用来定义一个变量或标号的类型属性。所定义的变量或标号的段基值和偏移量与紧跟其后的变量或标号相同。
举例:
1 | DATA_BYTE EQU THIS BYTE |
问题:什么意思?