汇编语言前四章学习笔记
汇编语言
第一章 基础知识
1. 1 一般概念
计算机程序设计语言包括机器语言、高级语言、汇编语言
机器语言 | 高级语言 | 汇编语言 | |
---|---|---|---|
形式 | 二进制 | 像自然语言 | 介于两者之间 |
优点 | 运行速度最快 长度最短 | 简单 | |
缺点 | 不便理解 | 效率低 |
- 一条汇编对应一条机器
- 不同类型的计算机有不同的机器指令系统和汇编语言,即汇编语言硬件相关
1.3 进位计数制
B 二进制 O 八进制 D 十进制 H 十六进制
一般默认十进制
10——>2
- 减权定位法
整数
小数
- 小数:乘积取整法
2——>10
-
整数:逐次乘积相加法
-
小数:逐次除基相加法
1.4带符号数的表示
正负号表示的是真值,0/1表示的是机器数
原码
8位 第一位0/1表正负,表示范围-127~127
0有两种表示000000000(+0) 10000000(-0)
补码
- 正数
与原码相同 - 负数
真值:各位变反,最低位+1
原码:符号位不变,其余各位变反,最低位+1
- 表示范围
八位补码表示范围 10000000~01111111 即-128~127
0的补码:00000000
-1的补码:11111111 - 补码的加减运算
[x+y] = [x]+[y]
[x-y] = [x]-[y] = [x]+[-y]
[-y]是对[y]执行一次求补运算:原数连同符号位一起(无论正负)按位求反,再在最低位+1
加法
减法
1.6基本逻辑运算
- “与”运算
- “或”运算
- “非”运算
- “异或”运算:相同为0,不同为1
第二章 IBM-PC微机的结构功能
2.1 IBM-PC微机的结构功能
一、微机的基本结构
一般计算机的五大部件:运算器、控制器、存储器、输入/输出设备
运算器+控制器集成在一个集成电路芯片上,是中央处理器(微处理器)CPU
系统采用总线结构
- 中央处理器CPU【运算器+控制器】
分析主存储器命令,控制各部件操作 - 主存储器【若干存储单元】
存放程序和数据
存储单元数量决定存储器容量。每个存储单元唯一编号标识,称存储单元的地址
以字节为基本单位,每个字节是一个基本单元 210=1024=1K个单元
习惯上将CPU+主存储器=主机** - I / O设备
不同设备速度差别大,因此要用I / O接口(主机和 I / O设备之间的逻辑控制单元)和系统总线相连 - 系统总线
将** 中央处理器、CPU 主存储器、I / O设备串联起来
分为:地址总线、数据总线、控制总线**
二、Intel 8086/8088 CPU结构
指令是构成汇编语言的最基本单位
CPU执行指令的两步骤:从存储器取指令、执行指令
执行步骤的两方式:串行方式、指令流水线方式
- 串行方式
特点:总线和CPU都有大量空闲时间 - 指令流水线方式
执行单元EU:分析执行指令
总线接口单元BIU:CPU与存储器、I / O设备的信息传送,取指令 存数据
特点:资源利用率高
2.2 CPU寄存器结构及其功能
一、通用寄存器(8个)
- 数据寄存器
包括AX、BX、CX、DX四个16位寄存器。每个可以当作2个独立的8位寄存器,共8个
可用于存放操作数和运算结构
MOV AX ,BX; 将BX的内容放入AX
ADD CH,DH; CH,DH相加放入CD
在有些指令中,隐含指定寄存器
LOOP循环中,CX默认为循环次数计数用
特定使用:个别指令对寄存器有特殊作用,且必须在指令中指出其名字
移位指令 SHL AX,CL;CL被固定用作移位次数
- 指针寄存器
一般用来存放16位地址,在存放20位物理地址时被作为偏移量使用
分为堆栈指针SP和基站指针BP
堆栈指针SP:堆栈操作时隐含使用,用来指向堆栈顶部单元
基站指针BP:指堆栈中一段存储单元,也可用作存放操作数和运算结果
- 变址寄存器
16位寄存器SI 源变址寄存器、DI 目的变址寄存器,用作地址指针 ,也可用作存放操作数和运算结果
二、段寄存器 (4个)
8086/8088 CPU将存储器划为若干段使用,每个存储段用一个段寄存器指明该段的起始位置(段基址)
CPU在访问存储器时必须指明两个内容:
(1)所用段寄存器(目标存储单元属于哪一段)
(2)偏移量(目标存储单元与基地址的距离)
一个程序可将存储器划成任意多个存储段,但程序执行的任意时刻最多只能有4段(CS DS ES SS)
三、指令指针IP
CPU从存储器取指令时,段寄存器CS是代码段基址指针,指令指针IP提供偏移量,共同构成指令
CPU从存储器取一条指令,IP自动指向下一条
注意:IP的内容不能直接访问
四、标志寄存器
用来反映CPU在程序运行时的某种状态,8086/8088 CPU中其长度为16位,但只定义了9位
标志位分为状态标志【CF PF AF ZF SF OF】,控制标志【TF DF IF】
状态标志位
- 进位标志位CF:算术中最高位(字15位,字节7位)产生进位或借位时置1,否则置0
- 奇偶标志位PF:低8位中的个数为时偶置1,否则置0
- 辅助进位标志位AF【只反映低8位】:低字节的低4位(即第3位)向上进位借位置1,否则置0
- 零值标志位ZF:运算结果各位全为0 时置1,否则置0
- 符号标志位SF:运算结果负数时置1,否则置0(由于第7 / 15位是符号位,所以第7 / 15位相同)
- 溢出标志位OF:当运算结果超过机器用补码所能表示范围(字节-128~+127,字-32768~+32767)时置1,否则置0
注意:溢出和进位不同
控制标志位
- 单步标志位TF(Trace Flag):跟踪位,供调试程序用;TF为1时程序一步一停
- 中断允许标志位IF:TF为1时CPU可响应可屏蔽中断,否则不允许响应可屏蔽中断
- 方向标志位DF:规定了串操作指令的增减方向;DF为0时变址寄存器(Si、DI)内容递增,反之递减
2.3 存储器组织结构
一、存储器的组成
- 若干存储单元构成存储器,存储单元数量决定存储器容量
- 每个存储单元存放相同长度的二进制数,一般是8位(1字节)
- 每个存储单元有唯一的地址,Intel 8086/8088 CPU有20根地址线,即可产生20位地址码,存储器寻址能力2**20
- 任何两个相邻字节单元构成一个字单元,字单元的地址为两个字节单元中较小地址字节单元的地址
16位长数据的存放规则:低8位放较低地址字节单元,高8位放较高地址
- 在定义一个地址时必须指出是字节还是字
二、存储器的段结构
Intel 8086/8088可寻址存储空间1MB,即20位长地址吗,但CPU内部寄存器为16位,即能直接访问的最大地址空间是64K
- Intel 8086/8088将1MB存储空间划分成若干个最大长度64K字节的段
- 每个段基址必须是一个小节的首址
在存储器中规定从0地址开始,每16个字节单元称为1小节,所以1MB内存有64K小节
- 内存中的一个物理单元可以映像到一个或多个逻辑段中
- 任意时刻,一个程序只能访问4个当前段中的内容,即代码段、数据段、堆栈段、附加段
三、逻辑地址物理地址及其关系
- 物理地址
每个存储单元的物理地址唯一,即其20位地址【Intel 8086/8088物理地址范围:00000H~0FFFFFH】 - 逻辑地址=段基值(段基址的高16位)+偏移量
逻辑地址表示方法 :段基值:偏移量 例,3267H:0A0H表示该逻辑单元位于段起始地址32670H,段内偏移量0A0H个字节 - 逻辑地址转换物理地址
20位段基址(段基值左移4位形成)+16位偏移地址=物理地址
- 逻辑地址的来源
2.4 堆栈及其操作方法
堆栈用于暂存数据以及在过程调用或处理中断时保存断点信息
一、堆栈的构造
- 堆栈分为专用堆栈存储器、软件堆栈
专用堆栈存储器是专门设计的,软件堆栈是用软件在内存中划出的堆栈区域 - 栈底是堆栈存储区的最大地址单元
二、堆栈的组织
- 堆栈由堆栈段寄存器SS指示的一段存储区
- 顶由堆栈指针SP指示
SP中内容表示栈顶和堆栈段基址之间的距离(字节数),SP最大时堆栈为空,SP=0时堆栈全满
SP初始化时指向栈底+2单元,其值是堆栈的长度,由于SP是16位,因此堆栈长度≤64K字节 - 数据在堆栈中以字为单位存放,低 8 位放较低地址单元,高 8 位放较高地址单元
当程序中要求的堆栈长度超过一个堆栈段的最大长度 64KB 时,可设置几个堆栈段。通过改变堆栈段寄存器 SS到达另一堆栈段。当改变了堆栈段寄存器 SS,必须赋SI新值。
三、堆栈操作
- 设置堆栈
对堆栈段寄存器SS和堆栈指针SP赋值
STACK1 SEGMEMT PARA STACK //说明本段为堆栈段
DB 100 DUP(0)//将程序指定的字节单元数100赋值SP
STACK1 ENDS
- 进栈PUSH
PUSH AX; 将寄存器AX中的内容压入堆栈
PUSH DATA-WORD; 将字存储单元DATA-WORD中的内容压入堆栈
PUSHF; 将标志寄存器内容压入堆栈
首先SP-2(指向一个空的堆栈字单元),然后存入
3. 出栈POP
POP AX; 将寄存器AX中的内容推出堆栈
POP DATA-WORD; 将字存储单元DATA-WORD中的内容推出堆栈
POPF; 将标志寄存器内容推出堆栈
物理实现时首先存入,然后SP+2
第三章寻址方式与指令系统
3.1 寻址方式
指令=操作码+操作数
寻址:寻找指令中所需操作数
Intd 8086 / 8088 各指令中提供操作数的方法有以下四种
( l )立即数操作数 ― 操作数在指令代码中提供
( 2 )寄存器操作数 ― 操作数在 CPU 的通用寄存器或段寄存器中
( 3 )存储器操作数 ― 操作数在内存的存储单元中
( 4 ) I / O 端口操作数 ― 操作数在输入/输出接口的寄存器中
- 立即数寻址
操作数在指令代码中 寻址速度快,但立即数只能用作源操作数,不能做目的操作数
MOV AX,20A0H 将16位立即数20A0H放入AX中
- 寄存器寻址
操作数在 CPU 通用寄存器或段寄存器中 ,完全在CPU中完成执行速度快
MOV AX, BX
MOV DS, AX
存储单元逻辑地址表示形式:段基值:偏移量(有效地址 EA )
有效地址 EA 是以下三个地址分量的几种组合,由 CPU执行单元 EU 计算得出(1)位移量:指令中直接给出一个 8 位或 16 位数。一般源程序中以操作数名字的形式出现
(2)基址:基址寄存器 BX 或基址指针 BP 提供
(3)变址:源变址寄存器 SI 或目的变址寄存器 Dl 提供三个地址分量组合时若涉及两个或两个以上,则将进行16位加法
以下是由这三个地址分量的不同组合所形成的四种寻址方式
- 存储器寻址
操作数有效地址EA只有位移量地址分量
符号表示
常数表示
MOV AX, DS:[64H]; 从当前数据段开始偏移100个字节的字单元内容送入AX ,不是MOV AX, 64H
- 寄存器间接寻址
事先将偏移量存放在某寄存器中
运行过程中只要修改寄存器内容就可以完成一条指令对不同存储单元的操作
若指令中用BP寄存器,则隐含使用SS段寄存器,其他情况下默认使用DS段寄存器
- 基址寻址 / 变址寻址(寄存器相对寻址)
EA= 基址分量 / 变址分量+指令中位移量
位移量可以使其很好地访问数组和表格数据
默认使用规则同寄存器间接寻址
位移量为常数时不用加方括号
- 基址变址寻址
EA= 基址+变址+位移量
有两个地址分量可以修改,最灵活,可以方便的访问二维数组
若指令中用BX寄存器,则隐含使用DS段寄存器;若指令中用BP寄存器,则隐含使用SS段寄存器
MOV CX, 100H[BX][DI];
MOV TABLE[BX][DI],AX;
错误用法
MOV AX, ARRAY[BX][DI]; 都是基址分量 MOV AX,TABLE[SI][DI]; 都是变址分量
小结
7. 串操作寻址方式
用SI做地址指针指向源操作数,用DI做地址指针指向目的串,操作完成后自动更改
8. I / O端口寻址
(1)存储器编制法:把I / O端口视为存储器的一个单元;灵活但占用空间
(2) I / O端口编制法:与存储器地址分开,使用专门的I输入输出指令
8086/8088即采用此方式,有两种寻址方式:
- 直接端口寻址:指令中直接给出端口地址,一般用2位16进制表示,或符号表示;可访问端口数0~255
IN AL, 25H; 从25H端口中取出数据放入AL
- 寄存器间接端口寻址:把I / O端口地址先送入DX,DX作间接寻址寄存器
MOV DX, 378H; 从378H端口中取出数据放入DX OUT DX, AL; 输出数据
若访问端口地址大于255,则必须用寄存器间接端口寻址
3.2 指令系统
一、传送类指令
将信息传送到一个寄存器或存储单元中,可分4类:
- 通用数据传送指令
MOV DEST, SRC; 把SRC的值传入DEST
MOV指令对标志寄存器的各位无影响
MOV指令可以是字 / 字节数据传送均可,但源操作数和目的操作数长度必须一致
可分为以下几种情况
- 立即数传送到通用寄存器或存储单元
MOV AH,10H; MOV AX, 2345H; MOV M-BYTE,64H; M-BYTE是偏移量
注意:
- 立即数只能做源操作数【因为立即数本身不涉及操作结果】
- 立即数不能传给段寄存器【段地址很重要,不能轻易改变】
- 寄存器之间传送
MOV AH, CD; MOV DS, AX; MOV ES, BX; MOV AX, CS; 错误事例 MOV CS, AX;
注意: 段寄存器CS只能做源操作数,不能做目的操作数【段地址很重要,不能轻易改变】
3. 寄存器和存储单元之间传送MOV AL, [SI]; 有[]代表存储器 MOV [DI], AH; MOV AX, 10[BX]; 基址寻址或变址寻址10[BX] MOV TABLE[BP], BX; 基址寻址或变址寻址 MOV DS,[SI][BX]; 基址寻址或变址寻址 错误事例 MOV [BX],[BP][SI]; 两个都是寄存器
总结MOV指令注意事项
( l )立即数只能作源操作数,且它不能传送给段寄存器
( 2 )段寄存器CS 只能作源操作数,段寄存器之间不能直接传送
( 3 )存储单元之间不能直接传送数据
( 4 ) MOV 指令不影响标志位
- 交换指令
XCHG DEST, SRC; 源操作数和目的操作数两者内容相互交换,即(DEST )<=>(SR )
指令对标志寄存器各位无影响
数据交换可以在寄存器之间或寄存器与存储器单元之间进行,但不能在存储单元之间直接进行数据交换
寄存器只能使用通用寄存器
为完成两存储器(DATA_1, DATA_2)之间的数据交换
MOV AL, DATA_1; XCHG AL, DATA_2; XCHG AL, DATA_1; 或MOV DATA_1, AL;
- 标志传送指令
共4条,都是无操作数指令,全部依靠隐含
- 去标识寄存器指令
LAHF
将标志寄存器的低 8 位送入 AH 寄存器 ZF 、 AF 、 PF 和CF 分别送入 AH 的第 7 、6、4、2、0位,而 AH 的第 5 、 3 、 1 位不确定
2. 存储标志寄存器指令SAHF
将寄存器 AH 中的第 7 、 6 、 4 、 2 、0位分别送入标志寄存器的 SF 、 ZF 、 AF 、 PF 和 CF 各标志位,而标志寄存器高8位不受影响
3. 标志进栈指令PUSHF
先将堆指针 SP 减 2 ,使其指向堆栈顶部的空字单元,然后将 16 位标志寄存器的内容送 SP 指向的字单元
4. 标志出栈指令POPF
将由 SP 指向的堆栈顶部的一个字单元的内容送入标志寄存器,然后 SP 内容加 2
- 地址传送指令
共3条,将存储单元地址送寄存器
- 装入有效地址
LEA DEST, SRC;
将SRC存储单元地址中的偏移量,即有效地址EA 传送到一个16位通用寄存器中
- 源操作数SRC必须是一个字节或字存储器操作数(地址),DEST必须是一个16位通用寄存器 > 指令执行对标志寄存器各位无影响
LEA AX, [BX][SI]; 源操作数使用的是基址址寻址方式,讲起形成的有效地址传入AX,即AX<=(BX)+(SI)
- 不是将BX 和SI 所寻找的存储单元内容送入AX.
- 装入地址指令
LDS DEST, SRC; DEST<=(SRC) DS<=(SRC)+2 LES DEST, SRC; DEST<=(SRC) ES<=(SRC)+2
DEST是任意一个16位通用寄存器,SRC必须是操作数寄存器
> LDS SI, TABLE[BX]; DEST<=(SRC) DS<=(SRC)+2 LES DEST, SRC; DEST<=(SRC) ES<=(SRC)+2
二、算数运算类指令
8086/8088指令系统中有加、减、乘、除指令,可以对字节数据或字数据进行运算
参加运算的数有无符号均可,带符号数用补码表示
参加运算的数可以是二进制数 / 十进制数(以BCD码表示)
- 加法指令 :目的操作数和源操作数相加存放到目的操作数,源操作数保持不变
ADD DEST,SRC; DEST<=(DEST)+(SRC)
相加的结果将影响标志寄存器的CF、PF、AF、ZF、SF和OF。
DEST只能是通用寄存器或存储器操作数,不能是立即数.
SRC可以是通用寄存器、存储器或立即数操作数
DEST和SRC不能都为存储器操作数
- 带进位加法指令:功能与ADD基本相同,但结果要加上进位标志CF的值
ADC DEST,SRC; DEST<=(DEST)+(SRC)+CF
根据相加的结果设置标志寄存器中的CF、PF、AF、ZF、SF和OF
参加运算的进位CF是本条指令执行之前的值
用ADC指令可实现数据长度大于16位的两数相加
- 加1指令:单操作数指令,将目的操作数加1,并送回到目的操作数
INC DEST; DEST<=(DEST)+1
INC指令根据指令执行结果设置PF、AF、ZF、SF和OF标志,但不影响CF ;主要用于某些计数器的计数和修改地址指针
目的操作数可是任意的8位、16位通用寄存器或存储器操作数
目的操作数被视为带符号二进制数
- 减法指令:目的操作数的内容减源操作数的内容,结果送入目的操作数,源操作数保持不变
SUB DEST,SRC; DEST<=(DEST)一(SRC)
操作结果将影响标志位CF、PF、AF、ZF、SF和OF
目的操作数DEST和源操作数SRC可以是8位或16位的通用寄存器、存储器操作数,但两者不能同时为存储器操作数
立即数只能作源操作数
SUB AX,BX
SUB AH,10H
SUB DX,DA-WORD
SUB DA-BYTE,BL
- 注意:减法指令影响借位标志,若采用变减为加的运算则产生的进位与CF标志结果相反
- 带借位减法:功能与SUB指令基本相同,不同的是在两个操作数相减后再减去进位标志CF的值
SBB DEST,SRC; DEST<=(DEST)-(SRC)-CF
- 注意:该CF的值是本条指令执行前的结果
根据指令执行结果设置PF、AF、ZF、SF、OF和CF
主要用于长度大于16位的数相减,即将低16位相减的结果引入高位部分的减法中
- 减1指令:单操作数指令,将目的操作数的内容减1后送回到目的操作数
该指令将DEST看作是带符号二进制数。
DEC DEST; DEST<=(DEST)一1
根据指令执行结果设置PF、AF、ZF、SF和OF,但不影响CF
类似INC指令,主要用于计数和修改地址指针,计数方向与INC相反
DEST可以是8位或16位的通用寄存器存储器操作数
- 求负数指令:用零减去目的操作数的内容,并送回目的操作数
NEG DEST; DEST<=0-(DEST)
NEG指令将影响标志PF、AF、ZF、SF、CF和OF
对进位标志CF的影响:只有当操作数为零时,进位标志CF被置零,其它情况都被置1
对溢出标志OF的影响:当字节操作数为-128,或字操作数为-32768时,执行NEG指令的结果操作数将无变化,但溢出标志OF被置1.
由于机器中带符号数用补码表示,求操作数的负数就是求补操作,因此,NEG指令也叫取补指令
DEST可以是任意一个8位或16位的通用寄存器或存储器操作数,被视为带符号的操作数
例2一个32位带符号数存放在DAW开始的四个字节存储单元中,DAW字节单元存放最低字节。求该数的负数,并存入原存储单元中。
NEG WORD PTR DAW 低字节取反,WORD PTR表示低字节 MOV AX,0 SBB AX,DAW+2 考虑借位,DAW+2表示高字节 MOV DAW+2,AX
三、位操作类指令
- 逻辑运算指令
共有4条,指令格式分别是:
逻辑“与”指令 ANDDEST,SRC
逻辑“或”指令 OR DEST,SRC
逻辑“异或”指令 XORDEST,SRC
逻辑“非”指令 NOT DEST
DEST和SRC可以是8位或16位的通用寄存器或存储器操作数,但两者不能同时为存储器操作数
SRC可以为立即数
均按位执行逻辑运算
逻辑指令对标志位的影响:
NOT指令对标志无影响,其余三条指令将根据结果影响SF、ZF和PF,而CF和OF总是置0,AF不确定
逻辑运算指令除用来实现各种逻辑运算之外,还常用于对字节或字数据的某些位的组合、分离或位设置
AND AH,OF0H; 分离出AH中的高4位.
AND AH,OFH; 分离出AH中的低4位
OR AH,01H; 将AH中最低位置1
AND AL,7FH; 将AL的最高位置0
XOR AX,OFFH; 将AX的低字节变反
XOR BX,8000H; 将BX的符号位变反
下面的程序段将中断标志位F清0,其它标志位保持不变
PUSHF; 将标志寄存器压栈
POP AX; 将栈中的标志字送AX
AND AX,OFDFFH; 将AX的第9位清0
PUSH AX; 将第9位清0后的AX内容压栈
POPF; 将堆栈中的值返回到标志寄存器
- 测试指令:源操作数与目的操作数进行按位“逻辑与”运算,但运算的结果不送入目的操作数,即目的操作数内容保持不变,只对标志位的影响与AND指令相同
TEST DEST SRC
用于测试某一操作数的一位或几位的状态
检查AL寄存器的最低位是否为0,如果为0,则程序转移到ZERO处执行
TEST AL,01
JZ ZERO
……
ZERO: ……
检查标志寄存器的PF位(第2位)是否为0,如果为0,则执行后标志ZF为1. 因此通过测试ZF标志即可
LAHF
TEST AH,04H
JZ TARGET
- 移位/循环移位指令
共有8条,分为3类:
-
- 算术移位
算术左移 SALDEST,COUNT
算术右移 SAR DEST,COUNT
-
- 逻辑移位
逻辑左移 SHL DEST,COUNT
逻辑右移 SHR DEST,COUNT
** 逻辑左移SHL与算术左移SAL功能相同 **
-
- 循环移位
-
- 小循环
循环左移 ROL DEST,COUNT 循环右移 ROR DEST,COUNT
-
- 大循环
带进位循环左移 RCL DEST,COUNT 带进位循环右移 RCR DEST,COUNT
这8条指令具有以下几个共同点:
-
- DEST为操作对象,它可以是字节或字操作数,通用寄存器或存储器操作数
-
- COUT用来决定移位/循环的位数,即确定移位的次数
当移位次数为1时,使用常数1或寄存器CL
当移位次数大于1时,必须使用寄存器CL
SAL AX,1; 将AX的内容左移1位,其中最高中位移入CF中,而低位补0
MOV CL,2
SAR AX,CL; 将AX的内容算术右移2位。
-
- 在执行移位时,根据指令不同,每移位一次,最高位(左移)或最低位(右移)都要送到进位标志CF
MOV AL,10010011B
SHL AL,1; 执行后CF标志为1
SAR AL,1; 执行后CF标志为0
-
- 前4条移位指令根据移位结束后修改标志位CF\PF、ZF、SF和OF,而AF不确定
后4条循环移位指令根据移位结束后的结果仅修改CF和OF*
- 前4条移位指令根据移位结束后修改标志位CF\PF、ZF、SF和OF,而AF不确定
对溢出标志位OF的影响:
移位次数为1时,移位前后操作数的符号位发生变化,则OF被置1,否则置0。移位次数大于1时,OF不确定。
MOV AL,11000000B; (AL)=-64
MOV BL,01111111B; (BL)=127
SAL AL,1; (AL)=10000000B=-128,OF=0
SAL BL,1; (BL)=11111110B=-2,OF=1
指令SAL和SAR当移位次为n时相当于乘以2"或除以2",因此被叫做算术移位指令
为了保持其算术运算结果的正确性,移位后的结果不能发生溢出
设AX中存放一个带符号数,若要实现(AX)×5÷2,可由以下几条指令完成:
MOV DX,AX
SAL AX,1
SAL AX,1
ADD AX,DX
SAR AX,1
对于多字节或多字数据的移位,需要使用带进位循环移位指令。
下面程序段对从存储单元M开始的三字数据执行左移一位:
SAL M,1
RCL M+2,1
RCL M+4,1
![图片](assets/IMG_42.png)
下面的程序段实现将上述三字数据右移一位:
SAR M+4,1
RCR M+2;1
RCR M,1
四、处理器控制类
- 标志位操作指令
都是无操作数指令,操作数隐含为标志寄存器的某个标志位,能直接操作的标志位有CF、I F和DF
-
- 清除进位标志
CLC; 置CF为0
- 2.置1进位标志
STC; 置CF为1
-
- 进位标志取反
CMC; CF的值取反
-
- 清除方向标志
CLD; 置DF为0
-
- 置1方向标志
STD; 置DF为1
-
- 清除中断标志
CLI; 置IF为0
-
- 置1中断标志
STI; 置F为1
3.3指令编码
汇编:将汇编语言程序转换为机器语言程序的过程
汇编程序:在计算机中实现汇编过程的系统程序
Intels8086/8088汇编指令的编码格式有四种基本格式
- 双操作数指令编码格式
- 单操作数指令编码格式
- 与AX或AL有关的指令编码格式
- 其它指令编码格式
一、双操作数指令编码格式
像MOV、ADD、AND等双操作数指令,操作数可以是以下两种情形:
一个操作数在寄存器中,另一操作数在寄存器或存储器中.
目的操作数在寄存器或存储器中,源操作数是立即数.
这类指令的机器目标代码长度为2一6个字节
整个指令编码可以包含4个部分,但其中某些部分在一些指令的编码中可以没有
- 操作特征部分
这部分为指令编码的首字节,它又分为以下三个段:
-
- OPCODE 操作码字段
长度6bit,表示该指令所执行的功能和两个操作数的来源
- OPCODE 操作码字段
如果指令的源操作数是立即数,则需要使用指令编码的第2字节中REG字段作辅助操作码
前面例子中的最后两条指令,虽然其OPCODE字段相同,但其辅助操作码字段不同
-
- 方向字段
该字段与第2部分寻址特征一起决定源操作数和目的操作数来源
- 当源操作数为立即数Imm时,d字段无效,它被并入操作码字段
- 方向字段
-
- 字 / 字节字段W
当W=1时,表示两操作数长度为字
当W=0时,表示两操作数长度为字节
- 字 / 字节字段W
- 寻址特征部分
与操作特征部分的方向字段d结合,指定两个操作数分别使用什么寻址方式,使用哪个寄存器
它包括MOD、REG和R / M主个字段,REG字段确定一个操作数,而MOD和R/M字段确定另一个操作数
当d=1时,目的操作数由REG学段确定,源操作数由MOD和R / M序段确定
当d=0时,目的操作数由MOD和R / M字段确定,源操作数由REG学段确定
-
- REG字段
由REG字段确定的一个操作数是某一通用寄存器的内容,即使用的是寄存器寻址方式
第一部分中的W字段决定操作数是字或是字节
两者配合使用可以有16种组合,也即可以分别指定16个寄存器之一。如下表所示:
如果REG字段被用于指定段寄存器(用于MOV指令),则它的编码与指定的段寄存器如下:
- REG字段
-
- 寻址方式字段MOD和寄存器/存储器字段R / M
这两个字段共同确定一个操作数。该操作数可以在寄存器或存储器中
MOD、R/M和W字段共同确定操作数的寻址方式和所使用的寄存器,如下表所示:
- 寻址方式字段MOD和寄存器/存储器字段R / M
注意:在表中没有使用BP作寄存器间接寻址方式.如果在指令中使用了BPI,则将其汇编为[BP+0],即基址寻址
当MOD=11时,操作数为16个寄存器之一的内容
当MOD=00,01,11时,操作数为存储器单元,可有24种有效地址EA计算方法。disp8、disp16分别为8位和16位位移量
- 位移量部分
根据寻址特征中MOD和R/M字段确定的有效地址计算方法,位移量可以是以下三种情况之一:没有位移量、1字节位移量disp8、2字节位移量disp16 - 立即数部分
如果指令的源操作数为立即数,则指令编码中包含有该部分。
它总是位于指令编码的最后1~2字节
MOV M-WORD,OAABBH 将16位立即数送存储单元,目的操作数为直接寻址方式
查附录B可知:指令操作码为1100011字操作,W=1
源操作数为立即数,REG序段为辅助操作码000
设M-WORD存储单元的偏移量为0010H
由于目的操作数为直接寻址,根据前面的MOD和R / M字段编码表可知MOD=00 R / M=110
则整个指令的编码为
用16进制数表示为:C7061000BBAA共16个字节
MOV DS,AX 将通用寄存器AX的内容送入段寄存器DS, 即REG字段必须用于指定DS,为011
MOD和R/M用于指定AX,即MOD=11 R/M=000 d=1,w被作为OPCODE查表为100011d0
整个指令编码为:
MOV AX,ES: [BX] 寄存器间址的存储单元内容送通用寄存器AX
指令中使用段前缀ES,即由ES代替数据段DS,指令编码的第一个字节就为段前缀标记代码
段前缀标记字节的前3位和后3位被固定为001和110,中间两位被用来指定不同 的段寄存器
该指令编码为:
16进制目标代码为:26 8B 07
二、单操作数指令编码格式
适用于只有一个操作数的指令,如INC、DEC、移位/循环等指令
指令编码为2~3字节
操作特征部分:
包括OPCODE、V和W三个字段,其中V字段只有移位循环指令中才有该字段,其它指令中没有该字段
V=0时,指令中使用常数1作为移位或循环次数
V=1时,指令中使用寄存器C工作移位次数
由于单操作数指令只一个操作数,因此寻址特征部分不需要REG序段,该字段被用作辅助操作码
INC AL 将寄存器AL内容加1,查表可知其操作码和辅助操作码分别为1111111和000
该指令编码为
16进制目标代码为:FE C0
SHR AL,CL 对寄存器AL内容执行逻辑右移,移位次数由CL给出,即V字段为1
由MOD和RM确定AL,即MOD=11 R / M=000
查附表可知:操作码和辅助操作码分别为110100和101
指令编码为:
16进制目标代码为:D2 E8
三、与AX或AL有关的指令编码格式
用于隐含指定AX/AL作为一个操作数的双操作数指令,其编码格式为:
采用这种编码格式的指令,除一个操作数隐含指定为AX/AL外,另一个操作数可以是立即数或存储单元
立即数:则编码中应有1~2字节的立即数
存储单元:只能使用直接寻址方式,位移量由disp字段给出
AND AL,0FH 将寄存器AL的内容与立即数0FH进行逻辑“与”
指令编码中包含了立即数(8位)部分,而不包含位移量.
查附录二可知其操作码部分为0010010,指令编码如下:
16进制目标代码为:24 0F
四、其它指令编码格式
比上述三种编码格式更简单的指令编码格式,如标志位操作指令、堆栈操作指令等,一般只有一个字节
CLC清进位标志,该指令的编码只有一个字节的操作码
在有些单字节指令的编码中,将该字节划出部分位作为REG字段
PUSH指令
若压入堆栈的是通用寄存器,则编码格式为:
若压栈的是段寄存器则编码格式为:
第四章 汇编语言程序格式
4.1汇编语言语句种类及其格式
汇编语言的语句可以分为指令语句和伪指令语句
一、指令语句
每一条指令语句在汇编时都要产生一个可供CPU执行的机器目标代码,又叫可执行语句
指令语句的一般格式为:
一条指令语句最多可以包含4个字段
- 标号字段【可选字段】
后面必须有“:”
标号是一条指令的符号地址,代表了该指令的第一个字节存放地址。
标号一般放在一个程序段或子程序的入口处,控制程序的执行转到该程序位置
在转移指令或子程序调用指令中,可直接引用这个标号
ADDR1: MOV AL, 100
- 指令助记符字段【** 必选项**】
表示语句要求CPU完成什么具体操作,如MOV、ADD、SHL等。
有些指令还可以在指令助记符的前面加前缀,实现一定的附加操作
- 操作数字段
一条指令可以有一个操作数、两个操作数或者无操作数
如ADD以MOV指令需要两个操作数,INC、NOT指令只需一个操作数,而CLC指令不需要操作数
- 注释字段【** 可选项**】
该字段以分号;开始。
为阅读程序的人加上一些说明性内容
注释字段不会产生机器目标代码,不影响程序和指令的功能
注释字段可以是一条指令的后面部分,也可以是整个语句行
LABEL1: ADD AX,BX; 功能为AX<=(AX)+(BX)
;后面的程序段将完成一次对存储器的访问
二、伪指令语句
伪指令语句又叫命令语句
伪指令本身并不产生对应的机器目标代码,仅告诉汇编程序对其后面的指令语句和伪指令语句的操作数应该如何处理
一条伪指令语句可以包含四个字段,如下所示:
- 符号名字段【** 伪指令语句可选项**】
根据伪指令的不同,符号名可以是常量名、变量名、过程名、结构名和记录名等
一条伪指令语句的符号名可以作其它伪指令语句或指令语句的操作数,这时它表示一个常量或存储器地址
- 符号名后面没有冒号“:”,这是与指令语句的重要区别
- 伪指令符字段【伪指令语句必选项】
规定了汇编程序所要完成的具体操作
- 操作数字段【** 是否需要、需要几个由伪指令符字段来决定**】
操作数可以是一个常数(二进制、十进制、十六进制等)、字符串、常量名、变量名、标号和一些专用符号(如BYTE 、FAR、PARA等)
- 注释字段【** 伪指令语句可选项**】
该字段必须以分号开始,作用与指令语句的注释字段相同
三、标识符
指令语句中的标号和伪指令语句中符号名统称标识符
标识符是由若干个字符构成的
标识符构成规则:
- 字符的个数为131个:
- 第一个字符必须是字母、问号、@或下划线“_”这4种字符之一;
- 从第二个字符开始,可以是字母、数字、@或下划线“_”或问号“?”
保留字:CPU中各寄存器名(如AX、CS等),指令助记符(如MOV、ADD),伪指令符(如SEGMENT、DB)、表达式中的运算符(如GE、EQ)以及属性操作符(如PTR、OFFSET等)
4.2汇编语言数据
数据是指令和伪指令语句中操作数的基本组成部分
一个数据由数值和属性两部分构成
在说明数据时不仅要指定其数值,还需说明它的属性,比如是字节数据还是字数据
在汇编语言中常用的数据形式有:常数、变量和标号
一、常数
常数在汇编期间值已完全确定,且在程序运行过程中不会发生变化
常数有以下几种形式:
-
二进制数:以字母B结尾,如01001001B
-
八进制数:以字母0或0结尾,如63102540
-
十进制数:以字母D结尾,或者没有结尾字母,如2007D、2007
-
十六进制数:以字母H结尾,如3FEH
*如果常数的第一个数字为字母,为了与标识符加以区别,必须在其前面冠以数字“0” -
实数 一般格式为:
2.134E + 10
汇编程序在汇编源程序时,可以把实数转换为4字节、8字节或10字节的二进制数形式存放
- 字符串常数:用引号(单引号或双引号)括起来的一个或多个字符,这些字符以它的ASCⅡ码值存储在内存
’B’在内存中为42H,`ABC’为41H 42H 43H
在内存中的存储如图所示:
常数在程序中可以用在以下几种情况:
-
- 作指令语句的源操作数
MOV AX, 0B2FOH ADD AH, 64H
-
- 在指令语句的直接寻址方式变址(基址)寻址方式或基址变址寻址方式中作位移量
MOVBX,32H [SI] MOV 0ABH [BX], CX ADC DX,1234H [BP][DI]
-
- 在数据定义伪指令中使用
DB 10H DW 3210H
- 在数据定义伪指令中使用
二、变量
变量用来表示存放数据的存储单元,这些数据在程序运行期间可以被改变
程序中以变量名的形式来访问变量,因此,可以认为变量名就是存放数据的存储单元地址
- 变量的定义与预置
定义变量就是给变量在内存中分配一定的存储单元。也就是给这个存储单元赋与一个符号名,即变量名,同时还要将这些存储单元预置初值
定义变量使用数据定义伪指令DB、DW、DD、DQ和DT等
一般格式:其中表达式1、2是给存储单元赋值
VAR DATA SEGMENT
DATA1 DB 12H
DATA2 DB 20H, 30H
DATA3 DW 5678H
VAR_DATA ENDS
当变量被定义后三个属性:
-
- 段属性
它表示变量存放在哪一个逻辑段中
例如上面例子中的变量DATA1、DATA2和DATA3三个变量都存放在VAR-DATA逻辑段中
-
- 偏移量属性(OFFSET)
它表示变量所在位置与段起始点之间的字节数
如上述例子中,变量DATA1的偏移量为0,DATA2为1,DATA3为3
段属性和偏移量属性就构造了变量的逻辑地址
-
- 类型属性
它表示变量占用存储单元的字节数。
其中DB伪指令定义的变量为字节,DWV定义的变量为字,DD定义的为双字(4字节),DQ定义的为4宇,DT定义的为5字。
给变量赋初值的表达式的4种形式:
-
- 数值表达式
DATA1 DB 32, 30H; DATA1的内容为32(20H),DATA1+1单元内容为30H
-
- ?表达式
不带引号的问号“?”表示可以预置任意内容
DA-BYTE DB ?,?,? 表示让汇编程序分配三个字节存储单元。这些存储单元的内容的值为任意值。
-
3.字符串表达式
对于DB伪指令,字符串为用引号括起来的不超过255个字符
给每一个字符分配一个字节单元
字符串按从左到右,将字符的ASCⅡ编码值以地址递增的排列顺序依次存放
STRING1 DB ‘ABCDEF’
对于DW伪指令可以给两个字符组成的字两个字节存储单元。
- 两个字符的存放顺序是前一个字符放在高地址,后一字符放低地址单元
STRING2 DW ‘AB’ , ‘CD’ , ‘EF’
对于DD伪指令,只能给两个序符组成的字符串分配4个字节单元
- 两个字符存放在较低地址的两个字节单元中,存放顺序与DW伪指令相同,而较高地址的两个字节单元存放0
STRING3 DD ‘AB’, ‘CD’
-
DW和DD伪指令不能用两个以上字符构成的字符串赋初值,否则将出错
-
- DUP表达式
DUP称为重复数据操作符.
使用DUP表达式的一般格式为:
其中:表达式1是重复的次数,表达式2是重复的内容。
例如:
DUP还可以嵌套使用,即表达式2又可以是一个带DUP的表达武。
DATA_C DB 10H DUP(4 DUP(2), 7); 重复10H个数字序列“2,2,2,2,7”,共占用10H*5=50H个字节
- 变量的使用
-
- 在指令语句中引用
在指令语句中直接引用变量名就是对其存储单元的内容进行存取
DA1 DB 0FEH
DA2 DW 52ACH
MOV AL, DAI; 将OFEH传送到AL中
MOV BX,DA2; 将52ACH传送到BX中
当变量出现在变址(基址)寻址或基址变址寻址的操作数中时表示取用该变量的偏移量。
-
- 在伪指令语句中引用
- 在伪指令语句中引用
三、标号
标号写在一条指令的前面,它就是该指令在内存的存放地址的符号表示,也就是指令地址的别名
标号主要用在程序中需要改变程序的执行顺序时,用来标记转移的目的地,即作转移指令的操作数。
MOV CX,100
LAB:MOV AX,BX
……
LOOP LAB
JNE NEXT; 不为零转移
……
NEXT: ……
每个标号具有三属性
- 段属性(SEG)
表示该标号所代表的地址在哪个逻辑段中,即段基值。
- 偏移量属性(OFFSET)
它表示该标号所代表的地址在段内与段起点间的字节数,即地址的偏移量。
- 距离属性(也叫类型属性)
它表示该标号可以被段内还是段间的指令调用。
- NEAR(近):该标号只能作段内转移,也就是说只能是与该标号所指指令同在一个逻辑段的转移指令和调用指令才能使用它。
- FAR(远):该标号可以被非本段的转移和调用指令使用。加标号的距离属性可以有两种方法来指定:
a.隐含方式.
当标号加在指令语句前面时,它隐含为NEAR属性SUB1;MOV AX,30H SUB1的距离属性为NEAR也就是它只能被本段的转指令和调用指令访问
b.用LABE伪指令给标号指定距离属性
格式:标号名 LABEL类型类型为NEAR或FAR。该语句应与指令语句连用。
4.3 符号定义语句
在源程序设计中,使用符号定义语句可以将常数或表达式等内容用某个指定的符号来表示。在8086/8088汇编语言中有两种符号定义语句。
一、等值语句
语句格式:符号名EQU表达式
功能:用符号名来表示EQU右边的表达式。后面的程序中
一旦出现该符号名,汇编程序将把它替换成该表达式。表达式可以是任何形式,常见的有以下几种情况。
- 常数或数值表达式
COUNT EOU 5
NUM EOU COUNT+5
- 地址表达式
ADR1 EQU DS:[BP+14]
ADR1被定义为在DS数据段中以BP作基址寻址的一个存储单元。
- 变量、寄存器名或指令助记符
CREG EOU CX; 在后面的程序使用CREG就是使用CX
CBD EQU DAA; DAA为十进制调整指令。
- 注意:在同一源程序中,同一符号不能用EQU定义多次。
错误用法
CBD EOU DAA
CBD EQU ADD
二、等号语句
格式:符号名=表达式
等号语句与等值语句具有相同的作用。但等号语句可以对一个符号进行多次定义。
CONT = 5
NUM = 14H
NUM=NUM+10H
下面是错误用法:
CBD-DAA
CBD-ADD
等号语句不能为助记符定义别名
- 注意:等值语句与等号语句都不会为符号分配存储单元。因此所定义的符号没有段、偏移量和类型等属性。
4.4表达式与运算符
表达式是指令或伪指令语句操作数的常见形式, 由常数、变量、标号等通过操作运算符连接而成。
- 注意:任何表达式的值在程序被汇编的过程中进行计算确定,而不是到程序运行时才计算。
8086/8088宏汇编语言中的操作运算符非常丰富,可以分为以下五类。
一、算术运算符
+、一、*、/、MOD、SHL、SHR、[]
-
运算符“+”和“”也可作单目运算符,表示数的正负
-
使用“+”、“_”、“大”、和“”运算符时,参加运算的数和运算结果都是整数
-
“运算为取商的整数部分,而“MOD”运算取除法运算的余数
NUM=15*8; NUM=120
NUM=NUM/7; NUM+17
NUM=NUM MOD 3; NUM=2
NUM=NUM+5; NUM-7
NUM=-NUM-3; NUM=-10
NUM=-NUM-NUM; NUM-20
- “SHR”和“SHL”为逻辑移位运算符
SHR为右移,左边移出来的空位用0补入
SHL为左移,右边移出来的空位用0补入
- 注意:移位运算符与移位指令区别。移位运算符的操作对象是某一具体的数(常数),在汇编时完成移位操作。而移位指令是对一个寄存器或存储单元内容在程序运行时执行移位操作。
NUM=11011011B
......
MOV AX, NUM SHL 1 ; 不能改成:SHL NUM.1
MOVBX, NUM SHR 2
ADD DX, NUM SHR 6
上面的指令序列等效下面三条指令。
MOV AX, 110110110B
MOV BX, 00110110B
ADD DX, 3
- 下标运算符“[]”具有相加的作用
可以用寄存器来存放下标变量
下面几个语句是等价的
MOV AX,ARRAY[BX][SI]; 基址变址寻址
MOV AX,ARRAY[BX+SI]
MOV AX,[ARRAY+BX][SI]
MOV AX,[ARRAY+SI][BX]
MOV AX[ARRAY+BX+SI]
下面是几个错误的语句
MOV AX,ARRAY+BX+SI
MOV AX,ARRAY+BX[SI]
MOV AX, ARRAY+DA_WORD
二、逻辑运算符
逻辑运算符有NOT、AND、OR和XOR等四个,执行的都是按位逻辑运算。
MOV AX,NOT 0F0H => MOV AX,OFFOFH
MOV AX, 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)或“假”(全0)
MOV AX, 0FH EQ 1111B => MOV AN,OFFFFH
MOV BX, 0FH NE 1111B => MOV BX,0
VAR DW NUM LT 0ABH
该语句在汇编时,根据符号常量NUM的大小来决定VAR存储单元的值
当NUM<OABH时,则变量VAR的内容为0FFFFH,否则VAR的内容为0
关系运算符用来比较两个表达式的大小。关系运算符比较的两个表达式必须同为常数或同一逻辑段中的变量。
如果是常量的比较,则按无符号数进行比较;如果是变量的比较,则比较它们的偏移量的大小。
关系运算的结果只能是“真”(全1)或“假”(全0)
MOV AX, 0FH EQ 1111B => MOV AN,OFFFFH
MOV BX, 0FH NE 1111B => MOVBX,0
VAR DW NUM LT 0ABH
该语句在汇编时,根据符号常量NUM的大小来决定VAR存储单元的值,当NUM<OABH时,则变量VAR的内容为0 FFFFH,否则VAR的内容为0。
四、数值返回运算符
该类运算符有5个,它们将变量或标号的某些特征值或存储单元地址的一部分提取出来。
- SEG运算符
作用:取变量或标号所在段的段基值
- OFFSET运算符
作用:取变量或标号在段内的偏移量
DATA SEGMENT
VAR1 DB 20H DUP(0)
VAR2 DW 5A49H
ADDR DW VAR2; 将VAR2的偏移量20H存入ADDR中
.......
MOV BX,YVAR2;(BX)=5A49H
MOV SI,OFFSET VAR2; (SI)=20H
MOV DI,ADDR; DI的内容与SI相
MOV BP,OFFSET ADDR; (BP)=22H
3.TYPE运算符
作用:取变量或标号的类型属性,并用数字形式表示。对变量来说就是取它的字节长度
- LENGTH运算符
该运算符用于取变量的长度
如果变量是用重复数据操作符DUP说明的,则LENGTH运算取外层DUP给定的值
如果没有用DP说明,则LENGTH:运算返回值总是1
- SIZE运算符
该运算符只能作用于变量,SIZE取值等于LENGTH.和TYPE两个运算符返回值的乘积。
对于上面例子,加上以下指令:
五、属性修改运算符
这一类运算符用来对变量、标号或存储器操作数的类型属性进行修改或指定
- PTR运算符
使用格式:类型 PTR 地址表达式
作用:将地址表达式所指定的标号、变量或用其它形式表示的存储器地址的类型属性修改为“类型”所指的值。
类型可以是BYTE、WORD、DWORD、NEAR和FAR。
这种修改是临时的,只在含有该运算符的语可内有效
- HIGH/LOW运算符
使用格式:HIGH表达式
LOW表达式
-
如果表达式为一个常量,则将其分离成高8位和低8位
-
如果表达式是一个地址《段基值或偏移量)时,则分离出它的高字节和低字节
- THIS运算符
THIS运算符一般与等值运算符EQU连用,用来定义一个不变量或标号的类型属性。所定义的变量或标号的段基值和偏移量紧跟其后的变量或标号相同。
标号LFAR与LNEAR具有相同的逻辑地址值,但类型不同。LNEAR只能被本段中的指令调用,而LFAR可以被其它段的指令调用
六、运算符的优先级
在一个表达式中如果存在多个运算符时,在计算时就有先后顺序问题。
不同的运算符具有不同的运算优先级别。
汇编程序在计算表达式时,按以下规则进行运算:
先执行优先级别高的运算,再算较低级别运算;
相同优先级别的操作,按照在表达式中的顺序,从左到右进行;
可以用圆括号改变运算的顺序。
4.5程序的段结构
8086/8088在管理内存时,按照逻辑段进行划分,不同的逻辑段可以用来存放不同目的的数据。在程序中使用四个段寄存器CS,DS,ES和SS来访问它们。
在源程序设计时,使用伪指令来定义和使用这些逻辑段。
一、段定义伪指令
伪指令SEGMENT和ENDS用于定义一个逻辑段。使用时必须配对,分别表示定义的开始与结束。
一般格式:
段定义伪指令语句各部分的作用如下:
- 段名
段名是由用户自己任意选定的,符合标识符定义规则的一个名称。
最好选用与该逻辑段用途相关的名称。如第一个数据段为DATA1,第二个数据为DATA2等。
一个段的开始与结尾用的段名必须一致。
- 定位类型
定位类型用于决定段的起始边界,即第一个可存放数据的位置(不是段基址)。它可以有4种取值。
-
- PAGE : 表示该段从一个页面的边界开始
由于一个页面为256个字节,并且页面编号从0开始,因此,PAGE定位类型的段起始地址的最后8位二进制数一定为0,即以00H结尾的地址。
-
- PARA : 表示该段从一个小节的边界开始
如果用户未选定位类型,则缺省为PARA。
-
- VORD : 表示该段从一个偶数字节地址开始,即段起始单元地址的最后一位二进制数一定是0。
-
- BYTE:表示该段起始单元地址可以是任一地址值。
-
注意:定位类型为PAGE和PARA时,段起始地址与段基址相同。定位类型为VORD和BYTE时,段起始地址与段基址可能不同
- 组合类型
组合类型说明符用来指定段与段之间的连接关系和定位。它有六种取值选择。
-
- 若未指定组合类型,表示本段与其它段无连接关系。在装入内存时,本段有自己的物理段,因此有自己的段基址
-
2.PUBLIC: 在满足定位类型的前提下,将与该段同名的段邻接在一起,形成一个新的逻辑段,共用一个段基址。段内的所有偏移量调整为相对于新逻辑段的段基址。
-
3.COMMON: 产生一个覆盖段。在多个模块连接时,把该段与其它也用COMMON说明的同名段置成相同的段基址,这样可达到共享同一存储区。共享存储区的长度由同名段中最大的段确定。
-
- STACK:把所有同名段连接成一个连续段,且系统自动对SS段寄存器初始化为该连续段的段基址。并初始化堆栈指针SP。
用户程序中应至少有一个段用STACK说明,否则需要用户程序自己初始化SS和SP。
-
- AT表达式:表示本段可定位在表达式所指示的小节边界上。表达式的值也就是段基值。
-
- MEMORY: 表示本段在存储器中应定位在所有其它段之后的最高地址上。如果有多个用MEMORY说明的段,则只处理第一个用MEMORY说明的段。其余的被视为COMMON
- 类别名
类别名为某一个段或几个相同类型段设定的类型名称。系统在进行连接处理时,把类别名相同的段存放在相邻的存储区,但段的划分与使用仍按原来的设定
类别名必须用单引号引起来。所用字符串可任意选定,但它不能使用程序中的标号、变量名或其它定义的符号。
在定义一个段时,段名是必须有的项,而定位类型、组合类型和类别名三个参数是可选项。各个参数之间用空格分隔。各参数之间的顺序不能改变。
上述源程序经LINK程序进行连接处理后,程序被装入内存的情况如图所示。
如果在段定义中选用了PARA定位类型说明,则在一个段的结尾与另一个段的开始之间可能存在一些空白,图中以蓝色框表示。CODE段的组合类型为MEMORY,因此被装入在其它段之后。
二、段寻址伪指令
段寻址伪指令ASSUME的作用是告诉汇编程序,在处理源程序时,定义的段与哪个寄存器关联
ASSUME并不设置各个段寄存器的具体内容,段寄存器的值是在程序运行时设定的。
一般格式
ASSUME 段寄存器名:段名,段寄存器名:段名
其中段寄存器名为CS,DS,ES和SS四个之一,段名是用SEGMENT/ENDS伪指令定义的段名。
例如:
DATA1 SEGMENT
VAR1 DB 12H
DATA1 ENDS
DATA2 SEGMENT
VAR2 DB 34H
DATA2 ENDS
CODE SEGMENT
VAR3 DB 56H
ASSUME CS: CODE, DS: DATAL, ES: DATA2
START:....
....
INC VAR1; 该指令汇编时,VAR1使用的是DS
INC VAR2; 该指令被汇编时,VAR2使用的是ES,指令编码中有段前缀ES
INC VAR3 该指令汇编时,VAR3使用的是CS,即指令编码中有段前缀CS
....
CODE ENDS
END START
在一个代码段中可以有几条ASSUME伪指令,对于前面的设置,可以用ASSUME改变原来的设置。
一条ASSUME语句不一定设置全部段寄存器,可以选择其中个或几个段寄存器
可以使用关键字NOTHING将前面的设置删除
例如:
ASSUME ES: NOTHING; 删除前面对ES与某个定义段的关联
ASSUME OTHING;删除全部4个段寄存器的设置
三、段寄存器的装入
段寄存器的初值(段基值)装入需要用程序的方法来实现。四个段寄存器的装入方法略有不同。
- DS和ES的装入
在程序中,使用数据传送语句来实现对DS和ES的装入。
例如:
DATA1 SEGMENT
DBYTE1 DB 12H
DATA1 ENDS
DATA2 SEGMENT
DBYTE2 DB 14H DUP(?)
DATA2 ENDS
CODE SEGMENT
ASSUME CS: CODE, DS: DATA1
START: MOV AX, DATAI
MOV DS,AX
MOV AX,DATA2
MOV ES,AX
MOV AL.DBYTE1
MOV DBYTE2[2], AL ;该指令在汇编时出错,因为在ASSUME指令中未指定ES与DATA2的联系
....
CODE ENDS
为了改正上述程序中的错误,可以在变量DBYTE2前加一个段前缀说明,即:
MOV ES:DBYTE2[2], AL
- SS的装入
SS的装入有两种方法
-
- 在段定义伪指令的组合类型项中,使用STACK参数,并在段寻址伪指令ASSUME语句中把该段与SS段寄存器关联
STACK1 SEGMENT PARA STACK
DB 40H DUP(?)
STACK1 ENDS
....
CODE SEGMENT
ASSUME CS:CODE, SS:STACK1
.....
SS将被自动装入STACK1段的段基值,堆栈指针SP也将指向堆栈底部+2的存储单元
上例中(SP)=40。
-
- 如果在段定义伪指令的组合类型中,未使用STACK参数,或者是在程序中要调换到另一个堆栈,这时,可以使用类似于DS和ES的装入方法。
- CS的装入
CPU在执行指令之前根据CS和IP的内容来从内存中提取指令,即必须在程序执行之前装入CS和P的值。因此,CS和IP的初始值不能用可执行语句来装入。
装入CS和IP一般有下面两种情况。
-
- 由系统软件按照结束伪指令指定的地址装入初始的CS和IP
任何一个源程序都必须以END伪指令来结束。
格式:END 起始地址
起始地址可以是一个标号或表达式,它与程序中第一条指令语句前所加的标号必须一致。
END伪指令的作用是标识源程序结束和指定程序运行时的起始地址。
当程序被装入内存时,系统软件根据起始地址的段基值和偏移量分别被装入CS和IP中。
例如:
CODE SEGMENT
ASSUME CS:CODE,......
START: ....
.....
CODE ENDS
END START
二、使用DOS系统功能调用实现返回
执行DOS功能调用4CH,也可以控制用户程序结束,并返回DOS操作系统
在程序结束时,使用两条指令:
MOV AH, 4CH
INT 21H
代码段的结构为:
CODE SEGMENT
ASSUME CS: CODE......
BEGIN: MOV AX,DATA
MOV DS,AX
MOV AH,4CH
INT 21H
CODE ENDS
END BEGIN
4.6过程定义伪指令(PROC/ENDP)
在程序设计过程中,常常将具有一定功能的程序段设计成一个子程序。
在MASM宏汇编程序中,用过程(PROCEDURE)来构造子程序。
过程定义伪指令格式如下:
过程名PROC INEAR / FAR
...
RET
过程名ENDP
** 过程名是子程序的名称,被用作过程调用指令CALL的目的操作数
它类同一个标号,具有段、偏移量和距离三个属性。
距离属性使用NEAR和FAR来指定,若没有指定,则隐含为NEAR**
-
NEAR过程只能被本段指令调用,而FAR过程可以供其它段的指令调用
-
每一个过程中必须包含有返回指令RET,其作用是控制CPU从子程序中返回到调用该过程的主程序
4.7 当前位置计数器S与定位伪指令ORG(Origin)
汇编程序在汇编源程序时,每遇到一个逻辑段,就要为其设置一个位置计数器,它用来记录该逻辑段中定义的每一个数据或每一条指令在逻辑段中的相对位置。
- 在源程序中,使用符号$来表示位置计数器的当前值。因此,$被称为当前计数器。它位于不同的位置具有不同的值。
位置计数器$在使用上完全类似变量的使用.
定位伪指令ORG一一用来改变位置计数器的值。
格式:ORG数值表达式
作用:将数值表达式的值赋给当前位置计数器$,ORG语句为其后的数据或指令设置起始偏移量
表达式的值必须为正值,表达式中也可以包含有当前位置计数器的现行值$
DATA1 SEGMENT ORG 30H DB1 DB 12H,34H; DB1在DATA1段内的偏移量为30H ORG+20H; 保留20H个字节单元,其后再存放'ABCD.… STRING DB ’ABCDEFGHI' COUNT EQU S-STRING; 计算STRING的长度 DB2 DW $; 取$的偏移量,类似变量的用法 DB3 DB$; ** 此语句错误!** DATA1 ENDS CODE SEGMENT ASSUME CS: CODE...... ORG 10H START: MOV AX, DATA MOV DS, AX CODE ENDS END START
4.8标题伪指令TITLE
语句格式:TITLE 标题名
作用:给所在程序指定一个标题,以便在列表文件的每一页的第一行都显示这个标题。
其中标题是用户任意选用的字符串,字符个数不能超过60
4.9 从程序返回操作系统的方法
为了使程序运行结束后,能够正确地返回到操作系统,需要在程序中加上一些必要的语句。一般有以下两种方法 :
一、使用程序段前缀PSP(P1 rogram Segment Prefix)实现返回
DOS系统将一个EXE文件(可执行文件)装入内存时,在该文件的前面生成一个程序段前缀PSP,其长度为100H字节。同时让DS和ES都指向PSP的开始,而CS指向该程序的代码段,即第一条可执行指令。
如图所示。PSP中一开始就是一条中断指令INT20H,执行该指令将终止用户程序,返回DOS系统
为了使程序执行完后,正确返回DOS,需要做以下三个操作:
- 将用户程序编制成一个过程,类型为FAR
- 将PSP的起始逻辑地址压栈,即将INT20H指令的地址压栈
- 在用户程序结尾处,使用一条RET指令。执行该指令将使保存在堆栈中的PSP的起始地址弹出到CS和IP中
程序结构:
DATA SEGMENT
.....
DATA ENDS
STACK1 SEGMENT STACK
.....
STACK1 ENDS
CODE SEGMENT
BEGIN PROC FAR
ASSUMEE CS:CODE,DS:DATA,SS:STACK1
PUSH DS
MOV AX,0
PUSH AX
MOV AX,DATA
MOV DS,AX
…
RET
BEGIN ENDP
CODE ENDS
END BEGIN