0%

第三章_汇编语言程序格式_2

汇编语言程序的开发

语句类型

指令语句:完成一定操作功能,能够翻译成机器代码

指示语句:提供有关信息,并不能翻译成机器代码

定义数据段部分

1
2
DATA SEGMENT
DATA ENDS

定义代码段

1
2
3
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
CODE ENDS

定义源程序

1
2
3
4
5
START:
MOV AX,DATA
MOV DS,AX
INT 21H
END START

完整代码组成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DATA SEGMENT      ; 段定义开始(DATA段)
BUF1 DB 34H ; 第1个加数
BUF2 DB 2AH ; 第2个加数
SUM DB 1 ; 准备用来存放和数的单元
DATA ENDS ; 段定义结束(DATA段)

CODE SEGMENT ; 段定义开始(CODE段)
ASSUME CS:CODE, DS:DATA ; 规定DATA、CODE分别为数据段和代码段
START:
MOV AX,DATA
MOV DS,AX ; 将数据段的起始地址显式加载到DS寄存器中
MOV AL,BUF1 ; 取第1个加数
ADD AL,BUF2 ; 和第2个加数相加
MOV SUM,AL ; 存放结果
MOV AH,4CH ; 设置DOS功能号
INT 21H ; 返回DOS状态
CODE ENDS ; 段定义结束(CODE段)

END START ; 整个源程序结束

解释: MOV AX,DATA MOV DS,AX 为什么不能直接 MOV DS, DATA

因为在汇编语言中,DATA 只是一个符号,代表着数据段的起始地址,而不是一个立即数。段寄存器(如 DS)只能接受立即数作为其操作数,而不是一个符号。

参数、变量和标号

符号定义语句伪指令

等值语句

语句格式:符号名 EQU 表达式

上面的替换是前替后

功能:用符号名来表示EQU右边的表达式。后面的程序中一旦出现该符号名,汇编程序将把它替换成该表达式。


1.常数或数值表达式

1
2
COUNT  EQU  5
NUM EQU COUNT+5

2.地址表达式

1
ADR1  EQU  DS:[BP+14]

上面说叫在DS数据段中以BP作基址寻址的一个存储单元。这句话是什么意思


3.变量、寄存器名或指令助记符

1
2
CREG  EQU  CX;在后面的程序使用CREG就是使用CX
CBD EQU DAA;DAA为十进制调整指令。

敲重点:在同一源程序中,同一符号不能用EQU定义多次。!!!

等号语句

格式:符号名=表达式

敲重点

  1. 等号语句与等值语句具有相同的作用。但等号语句可以对一个符号进行多次定义
  2. 等值语句与等号语句都不会为符号分配存储单元。因此所定义的符号没有段、偏移量和类型等属性

解除定义伪指令PURGE

功能:解除指定符号的定义


1
2
3
Y1 EQU 7
PURGE Y1
Y1 EQU 128

数据定义伪指令

常数

  1. 十进制缺省

  2. 实数

    整数部分 • 小数部分E ±指数部分

    举例:2.134 E +10这个到底怎么读?

  3. 字符串常数

    1. 单引号或者双引号都可以

    2. 以各个字符的ASCII码值存储在内存。

变量

变量的定义与预置

  1. 命名
  2. 预制存储单元(伪指令 DB、DW、DD、DQ和DT等),含义是一次分配出多少存储单元


1
2
3
4
5
VAR_DATA SEGMENT
DATA1 DB 12H ; 定义 DATA1,包含一个字节的数据 12H
DATA2 DB 20H,30H ; 定义 DATA2,包含两个字节的数据 20H 和 30H
DATA3 DW 5678H ; 定义 DATA3,包含一个字的数据 5678H
VAR_DATA ENDS

注: 1. 汇编中声明的变量名也是有地址的(类比C语言中指针的指针)

  1. 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
2
DATA_A  DB   10H  DUP(?)	; 分配16个字节单元(10H = 16D)
DATA_B DB 20H DUP('AB') ; 分配20H*2=40H个 字节,其内容为重复字符串‘AB’

注:DUP的嵌套使用

变量的使用

在指令语句中引用
直接引用变量名

在指令语句中直接引用变量名就是对其存储单元的内容进行存取


举例:

1
2
3
4
5
6
DA1 DB 0FEH    ; 将 0FEH 存储在 DA1 中
DA2 DW 52ACH ; 将 52ACH 存储在 DA2 中
...
MOV AL, DA1 ; 将 DA1 中的数据 (0FEH) 传送到 AL 中, 而不是DA1的地址,这一点很重要
MOV BX, DA2 ; 将 DA2 中的数据 (52ACH) 传送到 BX 中


变量名出现在编制寻址

变量名作为地址


举例:

1
2
3
4
5
例如:
DA3 DB 10H DUP(?)
DA4 DW 10H DUP(1)
MOV DA3[SI],AL ; 将AL的内容送入从DA3 开始再偏移(SI)的存储单元中
ADD DX,DA4[BX][DI] ; 将从DA4开始再偏移(BX)+(DI)的字存储单元的内容与DX的内容相加,结果送回DX中。

总结:

在伪指令语句中引用

之前声明的变量名赋给新变量名,新变量得到的是原变量的偏移地址(新变量大小是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)距离属性(也叫类型属性)

它表示该标号可以被段内还是段间的指令调用。

  1. NEAR(近):该标号只能作段内转移,也就是说只能是与该标号所指指令同在一个逻辑段的转移指令和调用指令才能使用它。缺省的情况下都默认是NEAR 如 SUB1:MOV AX,30H (也可以显式声明:标号名 LABEL NEAR)

  2. FAR(远):该标号可以被非本段的转移和调用指令使用。需要显式指明。格式为: 标号名 LABEL FAR


举例:

1
2
SUB1_FAR    LABEL    FAR
SUB1: MOV AX,30H

SUB1_FAR与SUB1两个标号具有相同的段属性和偏移量属性,即相同的逻辑地址。被转移指令或调用指令访问时,是指同一个入口地址,但SUB1-FAR可以被其它段的指令调用。


表达式与运算符

算术运算符

  1. 运算符“+”和“-”也可作单目运算符,表示数的正负。

  2. 使用“+”、“-”、“*”、和“/”运算符时,参加运算的数和运算结果都是整数。

  3. “/”运算为取商的整数部分,而“MOD”运算取除法运算的余数。

  4. "SHR"和"SHL"为逻辑移位运算符

    注意:移位运算符与移位指令区别在于, 移位运算符的操作对象是某一具体的数(常数),而移位指令是对一个寄存器或存储单元


    举例:

    移位运算符

    1
    2
    3
    4
    5
    NUM=11011011B

    MOV AX, NUM SHL 1
    MOV BX, NUM SHR 2
    ADD DX, NUM SHR 6

    移位指令

    1
    SHL AX 1

  5. 下标运算符“[ ]”具有相加的作用

    作用:将表达式1与表达式2的值相加后形成一个存储器操作数的地址。

    下面这两个语句等效

    1
    2
    MOV   AX, DA_WORD[20H]
    MOV AX, DA_WORD+20H

    敲重点, MOV中什么时候是取地址里所保存变量的值,什么时候是取地址

    总结:寄存器之间如果是[]连接就是取地址里的值,如果是+连接就是取地址

逻辑运算符

逻辑运算符有NOT、AND、OR和XOR等四个,它们执行的都是按位逻辑运算。

1
2
3
4
5
MOV  AX, NOT 0F0H   => MOV  AX, 0FF0FH
MOV AL, NOT 0F0H => MOV AL, 0FH
MOV BL, 55H AND 0F0H => MOV BL, 50H
MOV BH, 55H OR 0F0H => MOV BH, 0F5H
MOV CL, 55H XOR 0F0H => MOV CL, 0A5H

关系运算符

关系运算符包括:EQ(等于)、NE(不等于)、LT(小于)、 LE(小于等于)、GT(大于)、 GE(大于等于)

  1. 关系运算符用于比较两个表达式的大小。关系运算符比较的两个表达式必须同为常数或同一逻辑段中的变量
  2. 如果是常量的比较,则按无符号数进行比较;如果是变量的比较,则比较它们的偏移量的大小。
  3. 关系运算符得到结果如果是true,将所有位改为1,相反全部改为0

数值返回运算符

SEG运算符

取变量或标号所在段的段基值。


举例:


OFFSET运算符

该运算符的作用是取变量或标号在段内的偏移量。


举例:


TYPE运算符

该运算符的作用为取变量或标号的类型属性,并用数字形式来表示。对变量来说就是取它的字节长度。


举例:

1
2
3
4
5
6
7
V1    DB    'ABCDE'
V2 DW 1234H, 5678H
V3 DD V2 ; 存放V2的段基值和偏移量
……
MOV AL, TYPE V1
MOV CL, TYPE V2
MOV CH, TYPE V3

等价于

1
2
3
MOV  AL,01H
MOV CL,02H
MOV CH,04H

LENGTH运算符

该运算符只能加在变量的前面,且只能管最外层。比如如果变量是用重复数据操作符DUP说明的,则返回外层DUP给定的值。如果没有用DUP说明,则返回值总是1。


举例:

1
2
3
4
5
6
7
8
9
K1  DB   10H DUP(0)
K2 DB 10H, 20H, 30H, 40H
K3 DW 20H DUP(0, 1, 2 DUP(0))
K4 DB 'ABCDEFGH'
……..
MOV AL, LENGTH K1; (AL)=10H
MOV BL, LENGTH K2; (BL)=1
MOV CX, LENGTH K3; (CX)=20H
MOV DX, LENGTH K4; (DX)=1

SIZE运算符

该运算符只能作用于变量,SIZE取值等于LENGTH和TYPE两个运算符返回值的乘积


举例:

1
2
3
4
5
6
7
8
9
K1  DB   10H DUP(0)
K2 DB 10H, 20H, 30H, 40H
K3 DW 20H DUP(0, 1, 2 DUP(0))
K4 DB 'ABCDEFGH'
……..
MOV AL, SIZE K1 ; (AL)=10H
MOV BL, SIZE K2 ; (BL)=1
MOV CL, SIZE K3 ; (CL)=20H*2=40H
MOV DL, SIZE K4 ; (DL)=1

属性修改运算符

PTR运算符

临时修改,类似于C语言强转


举例:

1
2
3
4
5
6
7
8
DA_BYTE    DB   20H DUP(0)
DA_WORD DW 30H DUP(0)
……
MOV AX, WORD PTR DA_BYTE[10]
ADD BYTE PTR DA_WORD[20], BL ; DA_WORD本来大小是一个字大小,强转成1个字节大小
INC BYTE PTR [BX]
SUB WORD PTR [SI], 100
JMP FAR PTR SUB1 ; 指明SUB1不是本段中的地址

HIGH/LOW运算符

将一个数据分离出高字节和低字节。

如果表达式为一个常量,则将其分离成高8位和低8位;如果表达式是一个地址(段基值或偏移量)时,则分离出它的高字节和低字节。


举例:

1
2
3
4
5
6
7
8
9
10
11
12
DATA SEGMENT
CONST EQU 0ABCDH
DA1 DB 10H DUP(0)
DA2 DW 20H DUP(0)
DATA ENDS
…….
MOV AH, HIGH CONST
MOV AL, LOW CONST
MOV BH, HIGH (OFFSET DA1)
MOV BL, LOW (OFFSET DA2)
MOV CH, HIGH (SEG DA1)
MOV CL, LOW (SEG DA2)

等价于

1
2
3
4
5
6
MOV AH, 0ABH
MOV AL, 0CDH
MOV BH, 00H
MOV BL, 10H
MOV CH, 09H
MOV CL, 26H

注:HIGH/LOW运算符不能用来分离一个变量、寄存器或存储器单元的高字节与低字节。

THIS运算符

THIS运算符一般与等值运算符EQU连用,用来定义一个变量或标号的类型属性。所定义的变量或标号的段基值和偏移量与紧跟其后的变量或标号相同。


举例:

1
2
3
4
5
6
7
DATA_BYTE  EQU THIS BYTE
DATA_WORD DW 10 DUP(0)
……
MOV AX, DATA_WORD
MOV BL, DATA_BYTE
…...

问题:什么意思?