目录
第一章 汇编语言的基础知识
1、计算机系统概述
硬件(Handware)
- 中央处理器CPU
- 运算器,控制器,寄存器
- 存储区
- 主存储器:RAM、ROM
- 辅助存储器:硬盘,光盘,U盘
- 外部设备
- 输入设备和输出设备
软件(Software)
- 系统软件(eg:操作系统)
- 应用软件
其中我们程序员最关心的有三个
- 寄存器(Register)
- CPU内部的高速存储单元
- 为CPU提供
数据
和地址
信息
- 存储器地址(Address)
- 由大量存储单元组成,用
编号
区分每个存储单元 - 存储器的
地址
=存储器中存储单元的编号
- 每个存储单元存放
一个字节
的数据(1Byte=8bit) - 地址采用
十六进制
表达- Inter8086有1MB的存储器容量(1MB=210KB=220B)
- 存储器地址表示为:00000H~FFFFFH
- 由大量存储单元组成,用
- 端口(Port)
- 是
I/O地址
的别称 - I/O接口电路由
接口寄存器
组成,用编号
区分各寄存器 - I/O
地址
=接口电路中寄存器的编号
- 端口采用
十六进制
表达- Intel 8086支持64K个8位端口
- I/O地址可以表示为:0000H~FFFFH
- 是
2、汇编语言介绍
以
助记符
形式表示计算机指令,汇编格式指令以及使用它们编写程序的规则就形成汇编语言
- **汇编语言程序:**用汇编语言书写的程序
- **汇编程序:**将汇编语言程序
汇编
成机器代码目标模块的程序 汇编语言程序
和汇编程序
是两个不同的概念
3、数据表示
1. BCD码
压缩
BCD码:一个字节表达两位BCD码非压缩
BCD码:一个字节表达一位BCD码(低4位表达数值,高4位常设置为0)
2. ASCII码
标准ASCII码用7位二进制编码,有128个
不可显示的控制字符:
- 前32个和最后一个编码
回车CR
:0DH换行LF
:0AH响铃BEL
:07H
可显示和打印的字符:20H开始的95个编码
-
数码
0~9
:30H~39H -
大写字母
A~Z
:41H~5AH -
小写字母
a~z
:61H~7AH -
空格
:20H -
扩展ASCII码:最高D7位为1,表达
制表符
3. 真值和机器数
**真值:**现实中真实的数值
**机器数:**计算机中用0和1数码组合表达的数
**定点数:**固定小数点的位置表达数值的机器数
- 定点整数:将小数点固定在机器数的最右侧表达的整数
- 定点小数:将小数点固定在机器数的最左侧表达的小数
浮点数:小数点浮动表达的实数
无符号数:只表达0和正整数的定点整数
有符号数:表达负整数、0和正整数的定点整数
- 符号位需要占用一个位
- 常用机器数的最高位,0表示正数、1表示负数
4. 补码
有符号数
在计算机中默认采用了补码
- **最高位表示符号:**正数用0,负数用1
- **正数补码:**直接表示数值大小(同无符号数)
- **负数补码:**将对应正数补码取反加1
4、8086微处理器
微处理器
是微机的硬件核心,主要包含指令执行的运算和控制部件,还有多种寄存器
8086内部结构有两个功能模块,完成一条指令的取指和执行功能
- 总线接口单元
BIU
:负责读取指令和操作数 - 执行单元
EU
**:**负责指令译码和执行
8086CPU所有寄存器都是16位
的,都可以存放两个字节,可存储的数据的最大值为2^16-1
1、通用寄存器
16位通用寄存器
:AX BX CX DX SI DI BP SP8 位通用寄存器
:AH BH CH DH AL BL CL DL
数据寄存器: 用来存放计算的结果
和操作数
,也可存放地址
AX | 累加器,使用频度最高,用于算术、逻辑运算以及与外设传送信息等; |
---|---|
BX | 基址寄存器,常用做存放存储器地址**;** |
CX | 计数器,作为循环和串操作等指令中的隐含计数器; |
DX | 数据寄存器,常用来存放双字长数据的高16位,或存放外设端口地址。 |
**变址寄存器:**用于存储器寻址
时提供地址
(在串操作指令中有特殊用法)
SI | 是源变址寄存器 |
---|---|
DI | 是目的变址寄存器 |
**指针寄存器:**用于寻址内存堆栈内
的数据(与SS堆栈段寄存器
联合确定堆栈段中存储单元地址)
SP | 为堆栈指针寄存器,指示栈顶的偏移地址,不能再用于其他目的,具有专用目的 |
---|---|
BP | 为基址指针寄存器,表示数据在堆栈段中的基地址 |
2、标志寄存器FLAGS
用于反映指令
执行结果
或者控制指令执行形式
-
状态标志
:记录程序执行结果的状态信息CF(进位标志):运算结果
最高位
有进位(加法)
或借位(减法)
,CF=1,针对无符号数
AF(辅助进位标志):运算时
D3位
(低半字节)有进位或者借位,AF=1ZF(零位标志):运算
结果为0
,则ZF=1SF(符号标志):运算结果
最高位为1
,SF=1PF(奇偶标志):运算结果中
低八位中
1的个数为0/偶数
时,PF=1OF(溢出标志):运算结果
超出范围
(例如8位表达的范围是+127~-128)则产生溢出,ZF=1,针对有符号数
-
控制标志
:可根据需要设置,控制指令执行方式DF:DF置0,则串操作控制处理方向,从带有最低地址的第一个元素逐个处理,否则,从高向低
IF:IF=1,CPU允许中断,IF=0,则CPU关闭中断
TF:TF=1,机器进入单步工作方式,每条机器指令执行后,显示结果及寄存器状态,若TF=0,则机器处在连续工作方式。此标志为调试机器或调试程序发现故障而设置。
3、指令指针IP
- 指示代码段中指令的
偏移地址
,通过CS:IP
指向下一条指令的物理地址 - 是一个
专用寄存器
4、段寄存器
8086有四个
段寄存器
,每个段寄存器确定了一个逻辑段的起始地址
CS(Code Segment) | 指明代码段的起始地址(CS:IP 指向下一条要执行的指令) |
---|---|
SS(Stack Segment) | 指明堆栈段的b起始地址(SS:SP 操作堆栈顶的数据) |
DS(Data Segment**)** | 指明数据段的起始地址(DS:EA 存取数据段中的数据) |
ES(Extra Segment) | 指明附加段(附加数据段)的起始地址(ES:EA 存取附加段中的数据) |
段超越前缀指令
- 默认的数据访问在
DS
段 - 默认的情况允许改变,需要使用
段超越前缀指令
CS: | ;代码段超越,使用代码段的数据 |
---|---|
SS: | ;堆栈段超越,使用堆栈段的数据 |
DS: | ;数据段超越**,**使用数据段的数据 |
ES: | ;附加段超越,使用附加段的数据 |

计算机中信息的单位
二进制位Bit | 存储一位二进制数:0或1 |
---|---|
字节Byte | 8个二进制位,D7~D0 |
字Word | 16位,2个字节,D15~D0 |
双字Dword | 32位,4个字节,D31~D0 |
- 最低有效位LSB:数据的最低位,D0位
- 最高有效位MSB**:**数据的最高位,对应字节、字、双字分别指D7、D15、D31位
数据的地址对齐
- 字单元在偶地址(xxx0B)、双字单元在模4地址(xx00B)
存储器(men)的分段管理
-
8086CPU有
20根地址线
- 最大寻址空间为2^20=1MB
- 物理寻址范围从00000H~FFFFFH
-
8086CPU将
1MB
的空间分成许多逻辑段
,各个段之间可以重叠
- 每个段的最大限制为
2^16=64KB
- 段地址低四位为0000B
- 每个段的最大限制为
-
这样,每个存储单元除了唯一的一个物理地址外,还有多个逻辑地址
-
逻辑地址
= 段基地址:段内偏移地址- 逻辑地址是16位的,因此范属围是2的16次方,即
64K
- 逻辑地址是16位的,因此范属围是2的16次方,即
-
物理地址
= 段地址 * 16 (二进制数据左移四位,十六进制左移一位) + 偏移地址- 物理地址是20位的,因此范围是2的20次方,即
1M
- 物理地址是20位的,因此范围是2的20次方,即
5、8086寻址方式
1、指令的组成
指令由
操作码
和操作数
两部分组成;
指令的助记符格式:
操作码 操作数1,操作数2 ;注释
- 有些指令不需要操作数,通常的指令都有一个或两个操作数,个别指令有3个甚至4个操作数
2、8086的机器代码格式
示例一:
示例二:
示例三:
3、立即数寻址方式
-
直接存放在机器代码中,紧跟在操作码之后的操作数为立即数(imm)
- 8位数值
i8
(00F~FFH) - 16位数值
i16
(0000H~FFFFH)
- 8位数值
-
立即数常用来给寄存器赋值
MOV AL,05H ;AL←05H MOV AX,0102H ;AX←0102H
4、存储器寻址方式
-
操作数
存放在cpu的内部寄存器reg中8位寄存器 r8
AH、AL、BH、BL、CH、CL、DH、DL 16位寄存器 r16
AX、BX、CX、DX、SI、DI、BP、SP 4个段寄存器 seg
CS、DS、SS、ES -
指令中给出操作数的
主存地址
信息
多种存储器寻址方式:
1. 直接寻址方式
有效地址
直接在指令中给出- 默认
段地址
在DS寄存器,可用段超越前缀改变
MOV AX,[2000H] ;AX←DS:[2000H] 指令代码:A1 00 20
MOV AX,ES:[2000H] ;AX←ES:[2000H] 指令代码:26 A1 00 20
2. 寄存器间接寻址方式
有效地址
放在BX(基址寄存器)或变址寄存器(SI,DI)中- 默认
段地址
在DS寄存器,可用段超越前缀改变
MOV AX,[SI] ;AX←DS:[SI]
3. 寄存器相对寻址方式
有效地址
是寄存器内容(BX/BP/SI/DI)和有符号8位或16位位移量之和
有效地址=BX/BP/SI/DI+8/16位位移量
段地址
对应BX/SI/DI寄存器默认在DS,对应BP寄存器默认在SS;可用段超越前缀改变
MOV AX,[DI+06H] ;AX←DS:[DI+06H]
MOV AX,[BP+06H] ;AX←SS:[BP+06H]
4. 基址变址寻址方式
有效地址
是基址寄存器(BX)/指针寄存器(BP)和变址寄存器(SI/DI)之和
有效地址=BX/BP+SI/DI
段地址
对应BX基址寄存器默认是DS,对应BP指针寄存器默认是SS;可用段超越前缀改变
MOV AX,[BX+SI] ;AX←DS:[BX+SI]
MOV AX,[BP+DI] ;AX←SS:[BP+DI]
MOV AX,DS:[BP+DI] ;AX←DS:[BP+DI]
5. 相对基址变址寻址方式
有效地址
是基址寄存器(BX)/指针寄存器(BP)和变址寄存器(SI/DI)和一个有符号8位或16位位移量之和
有效地址=BX/BP+SI/DI+8/16位位移量
段地址
对应BX基址寄存器默认是DS,对应BP指针寄存器默认是SS;可用段超越前缀改变
MOV AX,[BX+SI+06H] ;AX←DS:[BX+SI+06H]
5、寻址方式的多种表示方式
-
位移量可用符号表示:
MOV AX,[SI+COUNT] ;COUNT是事先定义的变量或常量(就是数值) MOV AX,[BX+SI+WNUM] ;WNUM是变量或常量
-
同一寻址方式可以写成不同的形式:
MOV AX,[BX][SI] ;MOV AX,[BX+SI] MOV AX,COUNT[SI] ;MOV AX,[SI+COUNT] MOV AX,WNUM[BX][SI] ;等同于 MOV AX,WNUM[BX+SI] ;等同于 MOV AX,[BX+SI+WNUM]
6、各种操作数的表达
-
寄存器操作数的表达
r8(任意一个8位通用寄存器) AH AL BH BL CH CL DH DL r16(任意一个16位通用寄存器) AX BX CX DX SI DI BP SP reg 代表r8或r16 seg 段寄存器CS DS ES SS -
存储器操作数的表达
m8 一个8位存储器操作数单元(所有主存寻址方式) m16 一个16位存储器操作数单元(所有主存寻址方式) mem 代表m8或m16 -
立即数的表达
i8 一个8位立即数 i16 一个16位立即数 imm 代表i8或i16 dest 目的操作数 src 源操作数
第二章 8086指令系统
1、数据传送类指令
1. 传送指令MOV
把一个字节或字的操作数从源地址传送至目的地址
;reg:通用寄存器
;mem:存储器
;imm:立即数
;seg:段寄存器
MOV reg/mem,imm
MOV reg/mem/seg,reg
MOV reg/seg,mem
MOV reg/mem,seg
注意:
-
两个操作数类型一致(对于存储器单元和立即数同时作为操作数的情况,需要显示声明;
byte ptr
为字节
类型,word ptr
为字
类型)mov byte ptr [si],0ah ;byte ptr 说明是字节操作 mov word ptr [si+2],0bh ;word ptr 说明是字操作
-
两个操作数不能都是存储器(要实现这种操作,通过寄存器实现)
mov ax,buffer1 ;ax←buffer1(将buffer1内容送ax) mov buffer2,ax ;buffer2←ax
-
段寄存器的操作有一些限制
-
不允许立即数传给段寄存器
MOV DS,100H ;非法指令:立即数不能传送段寄存器
-
不允许改变CS(代码段)的值
MOV CS,[SI] ;不允许使用的指令
-
不允许段寄存器之间的直接数据传送
MOV DS,ES ;非法指令:不允许段寄存器间传送
-
2. 交换指令XCHG
将两个地方的数据进行交换
- 寄存器与寄存器之间对换数据
- 寄存器与存储器之间对换数据
- 不能在存储器与存储器之间对换数据
XCHG reg,reg/mem ;reg<->reg/mem
3. 换码指令XLAT
将BX指定的缓冲区中、AL指定的位移处的一个字节数据取出赋给AL
XLAT ;al←ds:[bx+al]
示例:
mov bx,100h
mov al,03h
Xlat
;换码指令没有显式的操作数,但使用了BX和AL;
;因为换码指令使用了隐含寻址方式——采用默认操作数
4. 堆栈操作指令PUSH、POP
PUSH:
PUSH r16/m16/seg ;SP<SP-2 SS:[SP]←r16/m16/seg
POP:
POP r16/m16/seg ;r16/m16/seg←SS:[SP] SP←SP+2
-
堆栈的操作单位是
字
,2个字节
-
进展和出栈时,都是低地址字节送低字节,高地址字节送高字节(
小端对齐
) -
堆栈操作遵循
先进后出
原则,但可用存储器寻址方式随机存取数据 -
常见作用:
-
临时存放数据
-
传递参数
-
保存和恢复寄存器
push ax ;进入子程序后 push bx push ds ... pop ds ;返回主程序前 pop bx pop ax
-
5. 标志传送指令
用来传送标志寄存器FLAGS的内容
低八位
传送:LAHF 和 SAHF16位
传送:PUSHF 和 POPF
LAHF
AH <- FLAGS的低字节
- SF/ZF/AF/PF/CF状态标志位分别进入
AH
的第7/6/4/2/0位,而AH的5/3/1位任意
SAHF
FLAGS的低字节 <- AH
- 用
AH
的第7/6/4/2/0位响应设置SF/ZF/AF/PF/CF标志位
PUSHF
- 保存所有标志到堆栈
POPF
- 将堆栈内容取出到标志寄存器
6. 标志位操作指令
-
标志位操作指令直接对
CF、DF、IF
标志进行复 位
或置位
,常用于特定的情况 -
用于任意设置进位标志
CLC ;复位进位标志:CF←0 STC ;置位进位标志:CF←1 CMC ;求反进位标志:CF←~CF -
串操作指令中,需要使用
CLD ;复位方向标志:DF←0 STD ;置位方向标志:DF←1 -
在编写中断服务程序时,需要控制可屏蔽中断的允许和禁止
CLI ;复位中断标志:IF←0 STI ;置位中断标志:IF←1
7、地址传送指令
将存储器单元的
逻辑地址
送至指定的寄存器
有效地址传送指令 LEA
- 将存储器操作数的有效地址传送到指定的16位寄存器中
LEA r16,mem ;r16←mem的有效地址EA
- 示例:
mov bx,0400h
mov si,3ch
lea bx,[bx+si+0f62h] ;BX=0400h+003ch+0f62h=139EH
- 获得主存单元的
有效地址
;不是物理地址,也不是该单元的内容 - 可以实现计算功能
指针传送指令 LDS 和 LES
- LDS指令将主存中mem指定的字送至
r16
,并将mem的下一字送至DS寄存器
LDS r16,mem ;r16←mem DS←mem+2
- LES指令将主存中mem指定的字送至
r16
,并将mem的下一字送至ES寄存器
LES r16,mem ;r16←mem ES←mem+2
- 示例:
mov word ptr [3060h],0100h
mov word ptr [3062h],1450h
les di,[3060h] ;es=1450h,di=0100h
lds si,[3060h] ;ds=1450h,si=0100h
mem指定主存的连续4个字节作为逻辑地址(32位的地址指针),送入DS:r16或ES:r16
2、算术运算类指令
1、加法指令ADD、ADC、INC
- ADD:将源与目的操作数相加,结果送到目的操作数
ADD reg,imm/reg/mem ; reg ← reg+imm/reg/mem
ADD mem,imm/reg ; mem ← mem+imm/reg
- ADC:将源与目的操作数相加,再加上进位
CF标志
,结果送到目的操作数
ADC reg,imm/reg/mem ; reg ← reg+imm/reg/mem+CF
ADC mem,imm/reg ; mem ← mem+imm/reg+CF
- INC:对操作数
加1
(不影响CF
,按定义设置其他状态标志)
INC reg/mem ; reg/mem ← reg/mem+1
2、减法指令SUB、SBB、DEC
- SUB:将目的操作数减去源操作数,结果送到目的操作数(按照定义相应设置状态标志)
SUB reg,imm/reg/mem ; reg ← reg-imm/reg/mem
SUB mem,imm/reg ; mem ← mem-imm/reg
- SBB:将目的操作数减去源操作数,再减去
借位CF
(进位),结果送到目的操作数(按照定义相应设置状态标志)
SBB reg,imm/reg/mem ; reg ← reg-imm/reg/mem-CF
SBB mem,imm/reg ; mem ← mem-imm/reg-CF
- DEC:对操作数
减1
(不影响CF
,按定义设置其他状态标志)
DEC reg/mem ; reg/mem ← reg/mem-1
3、求补指令 NEG
用零减去操作数,然后结果返回操作数
求补运算也可以表达成:`将操作数按位取反后加1`
NEG reg/mem ; reg/mem ← 0-reg/mem
4、比较指令CMP
将目的操作数减去源操作数,按照定义相应设置状态标志
执行的功能与SUB指令相同,但结果不回送目的操作数
CMP reg,imm/reg/mem ; reg-imm/reg/mem
CMP mem,imm/reg ; mem-imm/reg
5、乘法指令MUL、IMUL
- MUL:
无符号
乘法
MUL r8/m8 ;AX ← AL*r8/m8
MUL r16/m16 ;DX.AX ← AX*r16/m16
- IMUL:
有符号
乘法
IMUL r8/m8 ;AX ← AL*r8/m8
IMUL r16/m16 ;DX.AX ← AX*r16/m16
-
乘法指令对标志的影响
MUL指令 若乘积的高一半(AH或DX)为0,则OF=CF=0;否则OF=CF=1 IMUL指令 若乘积的高一半是低一半的符号扩展,则OF=CF=0;否则均为1 -
乘法指令对其他状态标志没有定义
6、除法指令DIV、IDIV
- DIV:无符号除法
MUL r8/m8 ;AL←AX÷r8/m8的商 AH←AX÷r8/m8的余数
MUL r16/m16 ;AX←DX.AX÷r16/m16的商 DX←DX.AX÷r16/m16的余数
- IDIV:有符号除法
IMUL r8/m8 ;AL←AX÷r8/m8的商 AH←AX÷r8/m8的余数
IMUL r16/m16 ;AX←DX.AX÷r16/m16的商 DX←DX.AX÷r16/m16的余数
- 除法指令对标志没有定义
- 除法指令会产生结果溢出
- 除法错中断: 当被除数远大于除数时,所得的商就有可能超出它所能表达的范围。如果存放商的寄存器AL/AX不能表达,便产生溢出,8086CPU中就产生编号为0的内部中断
- 对DIV指令,除数为0,或者在字节除时商超过8位,或者在字除时商超过16位
- 对IDIV指令,除数为0,或者在字节除时商不在-128~127范围内,或者在字除时商不在-32768~32767范围内
7、符号扩展指令CBW、CWD
- 用一个操作数的符号位(即最高位)形成另一个操作数,后一个操作数的各位是全0(正数)或全1(负数)
- 符号扩展不改变数据大小
- 对于数据64H(表示数据100),其最高位D7为0,符号扩展后高8位都是0,成0064H(仍表示数据100)
- 对于数据FF00H(表示有符号数-256),其最高位D15为1,符号扩展后高16位都是1,成为FFFFFF00H(仍表示有符号数-256)
8、十进制调整指令
压缩BCD码调整
-
加减调整
- 使用DAA或DAS指令前,应先执行以AL为目的操作数的加法或减法指令
- DAA和DAS指令对OF标志无定义,按结果影响其他标志
- 例如CF反映压缩BCD码相加或减的进位或借位状态
mov al,68h ;al=68h,压缩BCD码表示真值68
mov bl,28h ;bl=28h,压缩BCD码表示真值28
add al,bl ;二进制加法:al=68h+28h=90h
daa ;十进制调整:al=96h 实现压缩BCD码加法:68+28=96
mov al,68h ;al=68h,压缩BCD码表示真值68
mov bl,28h ;bl=28h,压缩BCD码表示真值28
sub al,bl ;二进制减法:al=68h-28h=40h
das ;十进制调整:al=40h 实现压缩BCD码减法:68-28=40
非压缩BCD码调整
-
加减调整
- 使用AAA或AAS指令前,应先执行以AL为目的操作数的加法或减法指令
- AAA和AAS指令在调整中产生了进位或借位,则AH要加上进位或减去借位,同时CF=AF=1,否则CF=AF=0;
- 对其他标志无定义
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68
mov bl,09h ;bl=09h,非压缩BCD码表示真值9
add al,bl ;二进制加法:al=08h+09h=11h
Aaa ;十进制调整:ax=0707h 实现非压缩BCD码加法:68+9=77
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68
mov bl,09h ;bl=09h,非压缩BCD码表示真值9
sub al,bl ;二进制减法:al=08h-09h=ffh
aas ;十进制调整:ax=0509h 实现非压缩BCD码减法:68-9=59
- 乘除调整
-
AAM指令跟在字节乘MUL之后,将乘积调整为非压缩BCD码
-
AAD指令跟在字节除DIV之前,先将非压缩BCD码的被除数调整为二进制数
-
AAM和AAD指令根据结果设置SF、ZF和PF,但对OF、CF和AF无定义
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68 mov bl,09h ;bl=09h,非压缩BCD码表示真值9 mul bl ;二进制乘法:al=08h×09h=0048h aam ;十进制调整:ax=0702h 实现非压缩BCD码乘法:8×9=72
mov ax,0608h ;ax=0608h,非压缩BCD码表示真值68 mov bl,09h ;bl=09h,非压缩BCD码表示真值9 aam ;二进制扩展:ax=68=0044h div bl ;除法运算:商al=07h,余数ah=05h ;实现非压缩BCD码除法:68÷9=7(余5)
3、位操作指令
以二进制位为基本单位进行数据操作
1、逻辑或指令 AND
AND reg,imm/reg/mem | ;reg←reg∧imm/reg/mem |
---|---|
AND mem,imm/reg | ;mem←mem∧imm/reg |
- AND指令设置CF = OF = 0,根据结果设置SF、ZF和PF状态,而对AF未定义
2、逻辑与指令 OR
OR reg,imm/reg/mem | ;reg←reg∨imm/reg/mem |
---|---|
OR mem,imm/reg | ;mem←mem∨imm/reg |
- OR指令设置CF = OF = 0,根据结果设置SF、ZF和PF状态,而对AF未定义
3、逻辑异或指令XOR
- 异或不同为1,相同为0
XOR reg,imm/reg/mem | ;reg←reg⊕imm/reg/mem |
---|---|
XOR mem,imm/reg | ;mem←mem⊕imm/reg |
- XOR指令设置CF = OF = 0,根据结果设置SF、ZF和PF状态,而对AF未定义
4、逻辑非指令 NOT
NOT reg/mem | ;reg/mem←~reg/mem |
---|---|
- NOT指令是一个单操作数指令,不影响标志位
5、测试指令 TEST
- 对两个操作数执行逻辑与运算,结果不送到目的操作数
TEST reg,imm/reg/mem | ;reg∧imm/reg/mem |
---|---|
TEST mem,imm/reg | ;mem∧imm/reg |
- XOR指令设置CF = OF = 0,根据结果设置SF、ZF和PF状态,而对AF未定义
6、移位指令(shift)
- 将操作数移动一位或多位,分成逻辑移位和算术移位,分别具有左移或右移操作
SHL reg/mem,1/CL | ;逻辑左移,最高位进入CF,最低位补0 |
---|---|
SHR reg/mem,1/CL | ;逻辑右移,最低位进入CF,最高位补0 |
- 逻辑左移一位相当于无符号数乘以2,逻辑右移一位相当于无符号数除以2
SAL reg/mem,1/CL | ;算术左移,最高位进入CF,最低位补0 |
---|---|
SAR reg/mem,1/CL | ;算术右移,最低位进入CF,最高位不变 |
- 移位指令的第一个操作数是指定的被移位的操作数,可以是寄存器或存储单元
- 后一个操作数表示移位位数,该操作数为1,表示移动一位;当移位位数大于1时,则用CL寄存器值表示,该操作数表达为CL
- 按照移入的位设置进位标志CF,根据移位后的结果影响SF、ZF、PF,而对AF未定义
- 对于OF,如果进行一位移动,则按照操作数的最高符号位是否改变,相应设置溢出标志OF:如果移位前的操作数最高位与移位后操作数的最高位不同(有变化),则OF = 1;否则OF = 0。当移位次数大于1时,OF不确定
7、循环移位指令(rotate)
- 将操作数从一端移出的位返回到另一端形成循环,分成不带进位和带进位,分别具有左移或右移操作
ROL reg/mem,1/CL | ;不带进位循环左移 |
---|---|
ROR reg/mem,1/CL | ;不带进位循环右移 |
RCL reg/mem,1/CL | ;带进位循环左移 |
RCR reg/mem,1/CL | ;带进b位循环右移 |
- 按照指令功能设置进位标志CF,不影响SF、ZF、PF、AF
- 对于OF,如果进行一位移动,则按照操作数的最高符号位是否改变,相应设置溢出标志OF:如果移位前的操作数最高位与移位后操作数的最高位不同(有变化),则OF = 1;否则OF = 0。当移位次数大于1时,OF不确定
4、控制转移类指令
- 用于实现分支、循环、过程等程序结构
- 通过改变IP(和CS)值,实现程序执行顺序的改变
1、无条件转移指令 JMP
JMP label ;程序转向label标号指定的地址
- 执行JMP,使程序转到指定的目标地址处,从目标地址处开始执行指令
- 操作数label是要转移到的目标地址(目的地址、转移地址)
- 目标地址的寻址方式
- 相对寻址方式:以当前IP为基地址,加上位移量构成目标地址
- 直接寻址方式:转移地址像立即数一样,直接在指令的机器代码中
- 间接寻址方式:转移地址在寄存器或主存单元中
- 转移方式
- 段内转移——近转移(near)
- 在当前代码段64KB范围内转移
- 不需要更改CS地址,只要改变IP偏移地址
- 段内转移——短转移(short)
- 转移范围可以用一个字节表达,在段内-128~+127的范围转移
- 段间转移——远转移(far)
- 从当前代码段跳转到另一个代码段,可在1MB之内
- 更改CS和IIP
- 目标地址必须用一个32位数表达,叫做32位远指针(逻辑地址)
- 段内转移——近转移(near)
JMP指令分为四种类型
-
段内转移,相对寻址
- 位移量是紧接着JMP指令后的那条指令的偏移地址,到目标指令的偏移地址的地址位移
- 当向地址增大方向转移时,位移量为正;向地址减小方向转移时,位移量为负
JMP label ;IP←IP+位移量
-
段内转移,间接寻址
- 将一个16位寄存器或主存字单元内容送入IP寄存器,作为新的指令指针,但不修改CS寄存器的内容
JMP r16/m16 ;IP←r16/m16
-
段间转移,直接寻址
- 将标号所在段的段地址作为新的CS值,标号在该段内的偏移地址作为新的IP值;这样,程序跳转到新的代码段执行
JMP far ptr label ;IP←label的偏移地址 CS←label的段地址
-
段间转移,间接寻址
- 用一个双字存储单元表示要跳转的目标地址。这个目标地址存放在主存中连续的两个字单元中的,低位字送IP寄存器,高位字送CS寄存器
JMP far ptr mem ;IP←[mem],CS←[mem+2]
2、条件转移指令 JCC
JCC label ;条件满足,发生转移:IP←IP+8位位移量 条件不满足,顺序执行
操作数label采用 相对寻址、短转移 方式
- 表示Jcc指令后的那条指令的偏移地址,到目标指令的偏移地址的地址位移
- 距当前IP地址-128~+127个单元的范围之内
JCC指令不影响标志,但要利用标志,分为3种情况:
- 判断单个标志位状态
- 比较无符号数大小
- 比较有符号数大小
转移条件cc:单个标志状态
JZ/JE | ZF=1 | Jump if Zero/Equal |
---|---|---|
JNZ/JNE | ZF=0 | Jump if Not Zero/Not Equal |
JS | SF=1 | Jump if Sign |
JNS | SF=0 | Jump if Not Sign |
JP/JPE | PF=1 | Jump if Parity/Parity Even |
JNP/JPO | PF=0 | Jump if Not Parity/Parity Odd |
JO | OF=1 | Jump if Overflow |
JNO | OF=0 | Jump if Not Overflow |
JC | CF=1 | Jump if Carry |
JNC | CF=0 | Jump if Not Carry |
转移条件cc:两数大小关系
无符号数: | ------------------------- | -------------------------------------------- |
---|---|---|
JB/JNAE | CF=1 | Jump if Below/Not Above or Equal |
JNB/JAE | CF=0 | Jump if Not Below/Above or Equal |
JBE/JNA | CF=1或ZF=1 | Jump if Below/Not Above |
JNBE/JA | CF=0且ZF=0 | Jump if Not Below or Equal/Above |
有符号数: | ------------------------- | -------------------------------------------- |
JL/JNGE | SF≠OF | Jump if Less/Not Greater or Equal |
JNL/JGE | SF=OF | Jump if Not Less/Greater or Equal |
JLE/JNG | ZF≠OF或ZF=1 | Jump if Less or Equal/Not Greater |
JNLE/JG | SF=OF且ZF=0 | Jump if Not Less or Equal/Greater |
3、循环指令(loop)
- 操作数label采用 相对寻址、短转移 方式
- 默认利用CX计数器
JCXZ label | ;CX=0,转移到标号label |
---|---|
LOOP label | ;CX←CX-1,CX≠0,循环到标号label |
LOOPZ label | ;CX←CX-1,CX≠0且ZF=1,循环到标号label |
LOOPNZ label | ;CX←CX-1,CX≠0且ZF=0,循环到标号label |
4、子程序指令
- 子程序是完成特定功能的一段程序
- CALL指令调用子程序,RET指令返回主程序
子程序调用指令:
-
CALL指令有四种类型:
CALL label ;段内调用、相对寻址 CALL r16/m16 ;段内调用、间接寻址 CALL far ptr label ;段间调用、直接寻址 CALL far ptr mem ;段间调用、间接寻址 -
CALL指令需要保存返回地址
-
段内调用——入栈偏移地址IP
-
SP←SP-2,SS:[SP]←IP
-
段间调用——入栈偏移地址IP和段地址CS
-
SP←SP-2,SS:[SP]←IP SP←SP-2,SS:[SP]←CS
子程序返回指令:
- 根据段内和段间、有无参数,分成4种类型
RET | ;无参数段内返回 |
---|---|
RET i16 | ;有参数段内返回 |
RET | ;无参数段间返回 |
RET i16 | ;有参数段间返回 |
-
需要弹出CALL指令压入堆栈的返回地址
-
段内返回——出栈偏移地址IP
IP←SS:[SP], SP←SP+2
- 段间返回——出栈偏移地址IP和段地址CS
IP←SS:[SP],SP←SP+2
CS←SS:[SP],SP←SP+2
- 返回指令RET的参数
RET i16 ;有参数返回
-
RET指令可以带有一个立即数i16,
则堆栈指针SP将增加,即 SP←SP+i16
-
这个特点使得程序可以方便地废除若干执行CALL指令以前入栈的参数
5、中断指令
中断(Interrupt )是又一种改变程序执行顺序的方法,具有多种类型
8086外部中断:来自CPU之外的原因引起的中断,可分为:
- 可屏蔽中断:可由CPU的中断允许标志IF控制
- 非屏蔽中断:不受CPU的中断允许标志IF控制
8086内部中断:CPU内部执行程序引起的中断,可分成:
- 除法错中断:执行除法指令,结果溢出产生的 0号 中断
- 指令中断:执行中断调试指令 INT i8 产生的 i8号 中断
- 断点中断:用于断点调试(INT 3)的 3号 中断
- 溢出中断:执行溢出中断指令,OF=1产生的 4号 中断
- 单步中断:TF=1在每条指令执行后产生的 1号 中断
中断指令INT
INT i8 | ;中断调用指令:产生i8号中断 |
---|---|
IRET | ;中断返回指令:实现中断返回![]() |
INTO | ;溢出中断指令;若溢出标志OF=1,产生4号中断;否则顺序执行 |
5、处理机控制类指令
1、空操作指令 NOP
-
不执行任何操作,但占用一个字节存储单元,空耗一个指令执行周期
-
NOP常用于程序调试
- 在需要预留指令空间时用NOP填充
- 代码空间多余时也可以用NOP填充
- 还可以用NOP实现软件延时
-
事实上,NOP和 XCHG AX,AX 的指令代码一样,都是 90H
2、段超越前缀指令
- 在允许段超越的存储器操作数之前,使用段超越前缀指令,将采用指定的段寄存器寻址操作数
CS: | ;使用代码段的数据 |
---|---|
SS: | ;使用堆栈段的数据 |
DS: | ;使用数据段的数据 |
ES: | ;使用附加段的数据 |
3、封锁前缀指令 LOCK
LOCK ;封锁总线
- 这是一个指令前缀,可以放在任何指令前
- 这个前缀使得在这个指令执行时间内,8086 处理器的封锁输出引脚有效,即把总线封锁,使别的控制器不能控制总线;直到该指令执行完后,总线封锁解除
4、暂停指令 HLT
HLT ;进入暂停状态
- 暂停指令使CPU进入暂停状态,这时CPU不进行任何操作。当CPU发生复位或来自外部的中断时,CPU脱离暂停状态
- HLT指令可用于程序中等待中断。当程序中必须等待中断时,可用HLT,而不必用软件死循环。然后,中断使CPU脱离暂停状态,返回执行HLT的下一条指令
5、交权指令 ESC
ESC 6位立即数,reg/mem ;把浮点指令交给浮点处理器执行
- 浮点协处理器8087指令是与8086的整数指令组合在一起的,当8086发现是一条浮点指令时,就利用ESC指令将浮点指令交给8087执行
6、等待指令
WAIT ;进入等待状态
- 8086利用WAIT指令和测试引脚实现与8087同步运行
- 浮点指令经由8086处理发往8087,并与8086本身的整数指令在同一个指令序列;而8087执行浮点指令较慢,所以8086必须与8087保持同步
第三章 汇编语言程序格式
1、汇编语言程序的开发
编辑—汇编–连接–调试
1、汇编语言程序设计的语句格式
- 执行性语句:由
硬指令
构成的语句,通常对应一条机器指令,出现在程序的代码段中
标号: 硬指令助记符 操作数,操作数 ; 注释
- 说明性语句:由
伪指令
构成的语句它通常指示汇编程序如何汇编源程序
名字 伪指令助记符 参数,参数.. ;注释
-
标号、名字与标识符:
-
标号
:反映硬指令(逻辑地址)的标识符,后加冒号分隔 -
名字
:反映位指令(逻辑地址)和属性的标识符,后跟空格或制表符分隔,没有冒号 -
标识符
:一般最多由31个字母、数字及规定的特殊符号(如、$、?、@)组成,不能以数字开头。默认情况下,汇编程序不区别标识符中的字母大小写- 一个程序中,每个标识符的定义是唯一的,还不能是汇编语言采用的保留字
-
-
保留字:汇编程序已经利用的标识符
硬指令助记符 例如:MOV、ADD 伪指令助记符 例如:DB、EQU 操作符 例如:OFFSET、PTR 寄存器名 例如:AX、CS 预定义符号 例如:@data -
助记符
-
操作数与参数:
- 处理器指令的操作数可以是立即数、寄存器和存储单元
- 伪指令的参数可以是常数、变量名、表达式等,可以有多个,参数之间用逗号分隔
-
注释
-
分隔符:
- 语句的4个组成部分要用分隔符分开
- 标号后用冒号,注释前用分号
- 操作数之间和参数之间使用逗号分隔
- 其他部分通常采用空格或制表符
- 多个空格和制表符的作用与一个相同
- MASM支持续行符"\"
2、汇编语言的程序格式
- 完整的汇编程序由段组成,可以包含若干个代码段、数据段、附加段、堆栈段,段与段之间的顺序可以随意排列
- 独立运行的程序必须包含一个代码段,并指示程序执行的起始点,一个程序只有一个起始点
- 可执行语句必须在一个代码段中,说明性语句可位于任何一个段中
汇编源程序有两种格式:
-
简化段定义格式(MASM5.0开始支持)
;MASM 6.X支持 ;example.asm ;文件名 .model small ;程序存储模型 .stack ;定义堆栈段 .data ;定义数据段 ... ;在数据段定义数据 .code ;定义代码段 .startup ;程序起始点,建立DS、SS ... ;在代码段填入指令序列 .exit 0 ;程序结束点,返回DOS ... ;子程序代码 end ;汇编结束
;MASM 5.X支持 ;example.asm ;文件名 .model samll ;程序存储模型 .stack ;定义堆栈段 .data ;定义数据段 ... ;在数据段定义数据 .code ;定义代码段 start: move ax,@data move ds,ax ;这两句指令等价于.startup ... ;代码段,填入指令序列 move ax,4c00h int 21h ;这两句指令等价于.exit 0 ... ;子程序代码 end start ;汇编结束
-
完整段定义格式(MASM5.0以前就具有)
;MASM 5.X支持 ;example.asm ;文件名 stack segment stack ;定义堆栈段 dw 512 dup(?) ;堆栈段有512字(1024字节)空间 stack ends ;堆栈段结束 data segment ;定义数据段 ... ;在数据段定义数据 data ends ;数据段结束 code segment 'code' ;定义代码段 assume cs:code,ds:data,ss:stack start: move ax,data ;建立DS段地址 move ds,ax ... ;在代码段填入指令序列 move ax,4c00h int 21h ;利用功能调用返回DOS ... ;子程序代码 code ends ;代码段结束 end start ;汇编结束,同时指明程序起始点
3、汇编语言的开发过程
-
准备工作
- 安装开发软件包
- 进入操作系统(模拟DOS环境)
- 进入MSAM开发目录
-
源程序的
编辑
- 以ASM为扩展名
-
源程序的
汇编
-
汇编是将程序翻译成由机器代码组成的目标模块文件的过程
-
MASM 6.X提供的汇编程序是ML.EXE
-
如果源程序中没有语法错误,MASM将自动生成一个目标模块文件(.obj);否则MASM将给出相应的错误信息
ML /c lt301.asm
-
-
目标模块的
链接
- 链接程序能把一个或多个目标文件和库文件合成一个可执行文件(.EXE、.COM文件)
- 如果没有错误,则会生成一个可执行文件(.EXE),否则将提示相应的错误信息
LINK lt301.obj
ML汇编程序可以自动调用LINK连接程序,实现汇编和连接的依次进行
- 该命令除了产生模块化文件.obj和可执行文件.exe之外,还会产生列表文件.lst
- 列表文件是一种文本文件,含有源程序和目标代码
-
可执行程序的调试
- 经过汇编,连接生成的可执行程序在操作系统下只要输入文件名就可以运行
lt301.exe
- 操作系统装载该文件进入主存,并开始运行
4、DOS系统功能调用
21H中断
是DOS给用户用于调用系统功能的中断,它有近百个功能,只要包括设备管理,目录管理和文件管理这三个方面的功能ROM-BIOS
也以中断服务程序的形式,向程序员提供系统的基本输入输出程序
功能调用的步骤
- 在
AH寄存
器设置系统功能的调用号 - 在指定寄存器中设置入口参数
- 执行
INT 21H
(或ROM-BIOS
的中断向量号)实现中断服务程序的功能调用 - 根据出口参数分析功能调用执行情况
输入输出类调用
1、字符输出:
-
功能号:
AH=02H
-
入口参数:
DL=字符的ASCII码
-
功能:在显示器当前光标位置显示给定的字符,光标右移一个字符位置。
按Ctrl-Berak或Ctrl-C退出
2、字符串输出:
- 功能号:
AH=09H
- 入口参数:
DS:DX
=欲显示字符串在主存中的首地址,字符串应以$(24H)
结束 - 功能:在显示器输出指定的字符串【可以输出回车(0DH)和换行(0AH)字符产生回车和换行的作用】
3、字符输入:
-
功能号:
AH=01H
-
入口参数:
AL=字符的ASCII码
- 功能:获得按键的ASCII代码值
【调用此功能时,若无键按下,则会一致等待,直到按键后才读取该键值】
4、字符串输入:
-
功能号:
AH=0AH
-
入口参数:
DS:DX=缓冲区首地址
-
执行该功能调用时,用户按键,最后用回车确认
【该调用可以执行全部标准键盘编辑命令;用户按回车键结束输入,如按Ctrl+Break或Ctrl+c则终止】
缓冲区的定义
示例:
5、按键判断:
- 功能号:
AH=0BH
- 出口参数:
AL=0
,当前没有按键
;AL=FFH
,当前已经按键
- 功能:仅判断当前是否有按下的键,设置AL后退出
2、参量、变量和符号
1、参数
- 参数是指令的操作对象,参数之间用
逗号
分隔 - 参数可以没有,可以有1、2或多个
- 参数有数值型,主要形式是常数和数值表达式
1. 常数
表达一个固定的数值
-
十进制常数
:由0 ~ 9数字组成,以字母D或d结尾,缺省情况下,后缀D或d可以省略100 255D
-
十六进制常数
:由0~9、A~F组成,以字母H或h结尾以字母A~F开头的十六进制数,前面要用0表达,以避免与其他符号混淆64H 0FFh 0B800H
-
二进制常数
: 由0或1两个数字组成,以字母B或b结尾01101100B
-
八进制常数
:用单引号或双引号括起来的单个字符或多个字符,其数值是每个字符对应的ASCII码的值'd' = 64H 'AB'= 4142H 'Hello, Everybody !'
-
符号常数
:利用一个标识符表达的一个数值
MASM提供等价机制,用于常量定义
;等价EQU伪指令
符号名 EQU 数值表达式
符号名 EQU <字符串>
;等号=伪指令
符号名 = 数值表达式
示例:
DosWriteChar equ 2
CarriageReturn = 13
CallDOS equ <int 21h>
mov ah,2 ;mov ah,DosWriteChar
mov dl,13 ;mov dl,CarriageReturn
int 21h ;CallDOS
2. 数值表达式
指由运算符连接的各种常数所构成的表达式
算数运算符
:+ - * / MOD
mov ax,3*4+5 ;等价于 mov ax,17
;除加、减外,其他运算符的参数必须是整数
逻辑运算符
:AND OR XOR NOT
or al,03h AND 45h ;等价于 or al,01h
-
移位运算符
:SHL SHR实现
逻辑
左移、右移;移入低位高位都是0
mov al,0101b SHL (2*2) ;等价于 mov al,01010000b
关系运算符
:- EQ(等于) NE(不等于) GT(大于) LT(小于) GE(大于等于) LE(小于等于)
- 用于比较和测试符号数值
- MASM用
0FFFFH
(补码-1)表示条件为真
- MASM用
0000H
表示条件为假
mov bx,((PORT LT 5)AND 20)OR((PORT GE 5)AND 30)
;当PORT<5时,汇编结果为mov bx,20
;否则,汇编结果为mov bx,30
-
高低分离符
:- HIGH LOW HIGHWORD LOWWORD
;HIGH、LOW从一个字数值或符号常量中得到高、低字节 mov ah,HIGH 8765h ;等价于mov ah,87h ;HIGHWORD、LOWWORD取一个符号常量(不能是其他常数)的高字或低字部分 dd_value equ 0ffff1234h ;定义一个符号常量 mov ax,LOWWORD dd_value ;等价于mov ax,1234h
3. 地址型参数
- 主要形式:标号和名字(变量名、段名、过程名等)
- 硬指令的操作数有存储单元;存储单元就应该用地址型参数(存储器操作数)表达
2、变量定义伪指令
作用
:为变量申请固定长度的存储空间,并可同时将相应的存储单元初始化
格式
:变量名 位指令助记符 初值表
1. 变量名
- 变量名为用户自定义标识符,表示初值表首元素的逻辑地址;用这个符号表示地址,常称为符号地址
- 可以没有变量名。此时汇编程序会直接为初值表分配空间,无符号地址
- 设置变量名是为了方便存取它指示的存储单元
2. 位指令助记符
-
DB
——定义字节
伪指令- 用于分配一个或多个字节单元,并可以将它们初始化为指定值
- 初值表中每个数据一定是字节量(Byte),存放一个8位数据
- 可以是0~255的无符号数
- 或是-128~+127带符号数
- 也可以是字符串常数
.data ;数据段 X db 'a',-5 db 2 dup(100),? Y db 'ABC' mov al,X ;此处X表示它的第1个数据,故AL←'a' dec X+1 ;对X为始的第2个数据减1,故成为-6 mov Y,al ;现在Y这个字符串成为 'aBC'
-
DW
——定义字
伪指令- 用于分配一个或多个字单元,并可以将它们初始化为指定值
- 初值表中每个数据一定是字量(Word),一个字单元可用于存放任何16位数据
- 一个段地址
- 一个偏移地址
- 两个字符
- 0~65535之间的无符号数
- -32768~+32767之间的带符号数
.data ;数据段 count dw 8000h,?,'AB' maxint equ 64h number dw maxint array dw maxint dup(0)
-
DD
——定义双字
伪指令- 用于分配一个或多个双字单元,并可以将它们初始化为指定值
- 初值表中每个数据是一个32位的双字量
- 可以是有符号或无符号的32位整数
- 也可以用来表达16位段地址(高位字)和16位的偏移地址(低位字)的远指针
vardd DD 0,?,12345678h farpoint DD 00400078h
DF
——定义3字
伪指令- 用于为一个或多个6字节变量分配空间及初始化
- 6字节常用在32位CPU中表示一个48位远指针(16位段选择器:32位偏移地址)
- 用于为一个或多个6字节变量分配空间及初始化
DQ
——定义4字
伪指令- 用于为一个或多个8字节变量分配空间及初始化
- 8字节变量可以表达一个64位整数
- 用于为一个或多个8字节变量分配空间及初始化
DT
——定义10字
节伪指令- 用于为一个或多个10字节变量分配空间及初始化
- 10字节变量可以表达扩展精度浮点数
- 用于为一个或多个10字节变量分配空间及初始化
-
定位伪指令
:控制数据的偏移地址、-
ORG
参数:ORG伪指令是将当前偏移地址指针指向参数表达的偏移地址:ORG 100h ;从100h处安排数据或程序 ORG $+10 ;使偏移地址加10,即跳过10个字节空间MASM中,符号“$”表示当前偏移地址值
EVEN ;从偶地址开始 ALIGN n ;从n的整数倍地址开始
-
3. 初值表
-
初值表是用逗号分隔的参数
-
主要由数值常数、表达式或?、DUP组成
-
?——表示初值不确定,即未赋初值
-
DUP——表示重复初值
-
DUP的格式为:
重复次数 DUP(重复参数)
-
3、变量和标号的属性
-
地址属性
- 标号和名字对应存储单元的逻辑地址
- 逻辑地址包括:段地址和偏移地址
-
类型属性
- 标号、子程序名可以是NEAR、FAR,分别表示段内和段间
- 变量名的类型可以是BYTE(字节)、WORD(字)、DWROD(双字)
-
地址操作符:取得名字或标号的段地址和偏移地址两个属性
[ ] 将括起的表达式作为存储器地址 $ 当前偏移地址 : 采用指定的段地址寄存器 OFFSET 名字/标号 返回名字或标号的偏移地址 SEG 名字/标号 返回名字或标号的段地址 -
类型操作符:对名字或标号的类型属性进行有关设置
-
类型名 PTR 名字/标号
- PTR操作符使名字或标号具有指定的类型
- 使用PTR操作符,可以临时改变名字或标号的类型
- 类型名可以是:BYTE/WORD/DWORD/FWORD/QWORD/TBYTE或者是NEAR/FAR,还可以是由STRUCT、RECORD、UNION以及TYPEDEF定义的类型
mov al,byte ptr w_var ;w_var是一个字节变量 jmp far ptr n_label ;n_label是一个标号
-
THIS 类型名
- 利用THIS说明的操作数具有汇编时的当前逻辑地址,但具有指定的类型
b_var equ THIS byte ;按字节访问变量b_var,但与w_var的地址相同 w_var dw 10 dup(0) ;按字访问变量w_var f_jump equ THIS far ;用f_jump为段间转移(f_jump label far) n_jump: mov ax,w_var ;用n_jump为段内近转移,但两者指向同一条指令
- LABEL伪指令的功能等同于“EQU THIS”
-
SHORT 标号
-
TYPE 名字/标号
- 返回表明名字或标号类型的一个字量数值
- 对字节、字和双字变量依次返回1、2和4;
- 对短、近和远转移依次返回ff01h、ff02h和ff05h
mov ax,TYPE w_var ;汇编结果为 mov ax,2 mov ax,TYPE n_jump ;汇编结果为 mov ax,0ff02h(near标号)
- 操作符SIZEOF返回整个变量占用的字节数
- LENGTHOF返回整个变量的数据项数(即元素数) SIZEOF = LENGTHOF × TYPE
-
SIZEOF 变量名
-
LENGTHOF 变量名
-
3、程序段的定义和属性
exe程序
- 可执行程序
- 可以有独立的代码、数据和堆栈段,还可以有多个代码段或多个数据段,程序长度可以超过64KB,执行起始处可以任意指定
- 当DOS装入或执行一个程序时,DOS确定当时主存最低的可用地址作为该程序的装入起始点。此点以下的区域称为程序段。在程序段内偏移0处,DOS为该程序建立一个程序段前缀控制块PSP(Program Segment Prefix),它占256(=100h)个字节;而在偏移100h处才装入程序本身
com程序
- COM程序是一种将代码、数据和堆栈段合一的结构紧凑的程序,所有代码、数据都在一个逻辑段内,不超过64KB
- 程序开发中,需要满足一定的要求并采用相应参数才能够生成COM结构的程序
- COM文件存储在磁盘上是主存的完全影象,不包含重新定位的加载信息,与EXE文件相比其加载速度更快,占用的磁盘空间也少
- 尽管DOS也为COM程序建立程序段前缀PSP,但由于两种文件结构不同,所以加载到主存后各段设置并不完全一样
1、简化段定义伪指令合集
.MODEL存储模型位指令
-
使用简化段定义,必须有存储模型位指令
-
.model语句位于所有段定义语句之前
-
存储模型决定一个程序的规模,确定进行子程序调用、指令转移和数据访问的缺省属性
-
MASM有7种不同的存储模型:
① TINY ② SMALL
③ COMPACT ④ MEDIUM
⑤ LARGE ⑥ HUGE
⑦ FLAT
简化段定义伪指令
-
.STACK [大小] ;堆栈段开始
.DATA ;数据段开始
.CODE [段名] ;代码段开始
-
简化段定义伪指令指明一个逻辑段的开始,同时自动结束前面的一个段,采用简化段定义伪指令前,需有.model语句
-
使用简化段定义,各段名称和其他用户所需的信息可以使用MASM预定义符号
例如:@data表示由.data等定义的数据段的段名
.STARTUP 程序开始伪指令
- 按照CPU类型、存储模型、操作系统和堆栈类型,产生程序开始执行的代码;同时还指定程序开始执行的起始点
- 在DOS下,还将设置DS值,调整SS和SP值
EXIT [返回参数] 程序终止伪指令
- 产生终止程序执行返回操作系统的指令代码
- 它的可选参数是一个返回的数码,通常用0表示没有错误
- 例如.exit 0对应的代码是:
mov ax,4c00h
int 21h
- DOS功能调用的4ch子功能(返回DOS):
- 入口参数:AH=4ch,AL=返回数码
END [标号] 汇编结束伪指令
- 指示汇编程序MASM到此结束汇编过程
- 源程序的最后必须有一条END语句
- 可选的标号用于指定程序开始执行点,连接程序将据此设置CS : IP值
- 采用了.startup伪指令就不需要再用“end 标号”指明开始执行点,但还要有end伪指令
2、com程序的编写
-
利用MASM 6.x的简化段定义格式,可以非常容易地创建一个COM程序
-
遵循的规则:
-
- 采用TINY模型
- 源程序只设置代码段,无数据、堆栈等段
- 程序必须从偏移地址100h处开始执行
- 数据只能安排在代码段中,注意不能与可执行代码相冲突,通常在程序最后
3、完整段定义伪指令
完整段定义位指令
段名 segment 定位 组合 段字 '类别'
... ;语句序列
段名 ends
-
段定位(align)属性:指定逻辑段在主存储器中的边界
BYTE 段开始为下一个可用的字节地址(xxxx xxxxb) WORD 段开始为下一个可用的偶数地址(xxxx xxx0b) DWORD 段开始为下一个可用的4倍数地址(xxxxxx00b) PARA 段开始为下一个可用的节地址(xxxx 0000b) PAGE 段开始为下一个可用的页地址(0000 0000b) - 简化段定义伪指令的代码段和数据段默认采用WORD定位,堆栈段默认采用PARA定位
- 完整段定义伪指令的默认定位属性是PARA,其低4位已经是0,所以默认情况下数据段的偏移地址从0开始
-
段组合(combine)属性:指定多个逻辑段之间的关系
PRIVATE 本段与其他段没有逻辑关系,不与其他段合并,每段都有自己的段地址。这是完整段定义伪指令默认的段组合方式 PUBLIC 连接程序把本段与所有同名同类型的其他段相邻地连接在一起,然后为所有这些段指定一个共同的段地址,也就是合成一个物理段。这是简化段定义伪指令默认的段组合 STACK 本段是堆栈的一部分,连接程序将所有STACK段按照与PUBLIC段的同样方式进行合并。这是堆栈段必须具有的段组合 -
段字(use)属性
-
为支持32位段而设置的属性
-
对于16位×86cpu来说,默认是16位段,即USE16
而对于汇编32位x86 CPU指令时,它默认采用32位段,即USE32;但可以使用USE16指定标准的16位段
-
编写运行于实地址方式(8086工作方式)的汇编语言程序,必须采用16位段
-
-
段类别(class)属性
- 当连接程序组织段时,将所有的同类别段相邻分配
- 段类别可以是任意名称,但必须位于单引号中
- 大多数MASbM程序使用 ‘code’、'data’和’stack’来分别指名代码段、数据段和堆栈段,以保持所有代码和数据的连续
指定段寄存器伪指令
- 通知MASM用指定的段寄存器来寻址对应的逻辑段,即建立段寄存器与段的缺省关系
- 在明确了程序中各段与段寄存器之间的关系后,汇编程序会根据数据所在的逻辑段,在需要时自动插入段超越前缀。这是ASSUME伪指令的主要功能
- ASSUME伪指令并不为段寄存器设定初值,连接程序LINK将正确设置CS : IP和SS : SP,由于数据段通常都需要,所以在样板源程序中,首先为DS赋值;如果使用附加段,还要赋值ES
段组伪指令
-
把多个同类段合并为一个64KB物理段,并用一个组名统一存取它
-
定义段组后,段组内各段就统一为一个段地址,各段定义的变量和标号的偏移地址就相对于段组基地址计算
-
offset操作符取变量和标号相对于段组的偏移地址,如果没有段组则取得相对于段的偏移地址
offset后可以跟段组中的某个段名,表示该段最后一个字节后面字节相对于段组的偏移地址
段顺序伪指令
-
.SEG ;按照源程序的各段顺序
.DOSSEG ;按照微软使用的标准DOS规定
.ALPHA ;按照段名的字母顺序
- 段顺序伪指令确定各逻辑段在主存的前后位置
- 完整段定义格式中,默认按照源程序各段的书写顺序安排(即.seg)
- 采用.model伪指令的简化段定义格式,则是规定的标准DOS程序顺序( 即.dosseg ):
代码段 → 数据段 → 堆栈段
主存地址低端 ——> 高端
4、简化段定义格式的段属性
-
采用简化段定义格式的源程序,同样具有段定位、组合、类别以及段组等属性(表3.4),具有默认的属性
-
.MODEL伪指令除了设置程序采用的存储模型外,还具有如下语句的作用:
dgroup GROUP _data,_bss,stack
assume cs:_TEXT,ds:dgroup,ss:dgroup
第四章 基本汇编语言程序设计
1、顺序程序设计
顺序程序完全按指令书写的前后顺序执行每一条指令,是最基本、最常见的程序结构
2、分支程序设计
- 分支程序根据条件是真或假决定执行与否
- 判断的条件是各种指令,如CMP、TEST等执行后形成的状态标志
- 转移指令Jcc和JMP可以实现分支控制;还可以采用MASM 6.x提供的条件控制伪指令实现
1、单分支程序设计
- 条件成立跳转,否则顺序执行分支语句体;注意选择正确的条件转移指令和转移目标地址
2、双分支程序设计
-
条件成立跳转执行第2个分支语句体,否则顺序执行第1个分支语句体。
注意第1个分支体后一定要有一个JMP指令跳到第2个分支体后
3、多分支程序设计
- 多个条件对应各自的分支语句体,哪个条件成立就转入相应分支体执行。多分支可以化解为双分支或单分支结构的组合
3、循环程序设计
- 循环结构一般是根据某一条件判断为真或假来确定是否重复执行循环体
- 循环指令和转移指令可以实现循环控制;还可以采用MASM 6.x提供的循环控制伪指令实现
1、冒泡法
- “冒泡法”是一种排序算法,不是最优的算法,但它易于理解和实现
- 冒泡法从第一个元素开始,依次对相邻的两个元素进行比较,使前一个元素不大于后一个元素;将所有元素比较完之后,最大的元素排到了最后;然后,除掉最后一个元素之外的元素依上述方法再进行比较,得到次大的元素排在后面;如此重复,直至完成就实现元素从小到大的排序
- 这需要一个双重循环程序结构
move cx,count ;cx数组元素个数
dec cx ;外循环个数,元素个数减1
outlp: move dx,cx ;dx为内循环个数
move bx,offset array
inlp: move al,[bx] ;取前一个元素
cmp al,[bx+1] ;与后一个元素比较
jna next ;前一个不大于后一个元素,则不进行交换
xchg al,[bx+1] ;否则,进行交换
move [bx],al
next: inc bx ;下一对元素
dec dx
jnz inlp ;内循环尾
loop outlp ;外循环尾
2、串操作类指令
-
串传送 MOVES
- 把字节或字从主存的源地址传送到目的地址
MOVSB ;字节串传送:ES:[DI]←DS:[SI] ;SI←SI±1,DI←DI±1 MOVSW ;字串传送:ES:[DI]←DS:[SI] ;SI←SI±2,DI←DI±2 -
串存储 STOS
- 把AL或AX数据传送到目的地址
STOSB ;字节串存储:ES:[DI]←AL ;DI←DI±1 STOSW ;字串存储:ES:[DI]←AX ;DI←DI±2 -
串读取 LODS
- 把指定主存单元的数据传送给AL或AX
LODSB ;字节串读取:AL←DS:[SI] ;SI←SI±1 LODSW ;字串读取:AX←DS:[SI] ;SI←SI±2 -
串比较 CMPS
- 将主存中的源操作数减去目的操作数,以便设置标志,进而比较两操作数之间的关系
CMPSB ;字节串比较:DS:[SI]-ES:[DI] ;SI←SI±1,DI←DI±1 CMPSW ;字串比较:DS:[SI]-ES:[DI] ;SI←SI±2,DI←DI±2 -
串扫描 SCAS
- 将AL/AX减去目的操作数,以便设置标志,进而比较AL/AX与操作数之间的关系
SCASB ;字节串扫描:AL-ES:[DI] ;DI←DI±1 SCASW ;字串扫描:AX-ES:[DI] ;DI←DI±2 -
REP 重复前缀
- 每执行一次串指令,CX减1,直到CX=0,重复执行结束
- REP前缀可以理解为:当数据串没有结束(CX≠0),则继续传送
-
REPZ 重复前缀
- 每执行一次串指令,CX减1,并判断ZF是否为0,只要CX=0或ZF=0,重复执行结束
- REPZ/REPE前缀可以理解为:当数据串没有结束(CX≠0),并且串相等(ZF=1),则继续比较
-
REPNZ 重复前缀
- 每执行一次串指令,CX减1,并判断ZF是否为1,只要CX=0或ZF=1,重复执行结束
- REPNZ/REPNE前缀可以理解为:当数据串没有结束(CX≠0),并且串不相等(ZF=0),则继续比较
4、 子程序设计
- 把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序
- 子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率
1、过程定义位指令

过程名 proc [near|far]
...
过程名 endp
-
过程名(子程序名)为符合语法的标识符
NEAR属性(段内近调用) 的过程只能被相同代码段的其他程序调用 FAR属性(段间远调用) 的过程可以被相同或不同代码段的程序调用 -
对简化段定义格式,在微型、小型和紧凑存储模型下,过程的缺省属性为near;
-
在中型、大型和巨型存储模型下,过程的缺省属性为far
-
对完整段定义格式,过程的缺省属性为near
-
用户可以在过程定义时用near或far改变缺省属性
-
通用格式:
具有多个出口的子程序:
2、子程序的参数传递
入口参数(输入参数):主程序提供给子程序
出口参数(输出参数):子程序返回给主程序
参数的形式: ① 数据本身(传值)
**② 数据的地址(传址)**
传递的方法: ① 寄存器 ② 变量 ③ 堆栈
例题:
-
用寄存器传递参数
把参数存于约定的寄存器中,可以传值,也可以传址。
子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护)
子程序对带有入口参数的寄存器可以保护,也可以不保护;但最好一致
-
入口参数:CX=元素个数,DS:BX=数组的段地址:偏移地址
出口参数:AL=校验和
;例4.16主程序 .startup ;设置入口参数(含有DS<-数组的段地址) mov bx,offset array ;BX<-数组的偏移地址 mov CX,count ;cx<-数组的元素个数 call checksuma ;调用求和过程 mov result,al ;处理出口参数 .exit 0 ;例4.16子程序 checksuma proc xor al,al ;累加器清0 suma: add al,[bx] ;求和 inc bx ;指向下一字节 loop suma ret checksuma endp end
-
2.用变量传递参数
主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递
不通模块间共享时,需要声明
-
入口参数:count=元素个数,array=数组名(段地址:偏移地址)
出口参数:result=校验和
;例4.16 ;主程序 call checksumb ;子程序 checksumb proc push ax push bx push cx xor al,al ;累加器清0 mov bx offset array ;bx<-数组的偏移地址 mov cx,count ;cx<-数组的元素个数 sumb: add al,[bx] ;求和 inc bx loop sumb mov result,al ;保存校验和 pop cx pop bx pop ax ret checksumb endp
-
用堆栈传递参数
主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数
子程序将出口参数压入堆栈,主程序弹出堆栈取得它们
-
入口参数:顺序压入偏移地址和元素个数
出口参数:AL=校验和
;例4.16主程序 .startup mov ax,offset array ;偏移地址 push ax mov ax,count ;元素个数 push ax call checksumc add sp,4 mov result,al .exit 0 ;例4.16子程序 checksumc proc push bp mov bp,sp ;利用BP间接寻址存取参数 push bx push cx mov bx,[bp+6] ;SS:[BP+6]指向偏移地址 mov cx,[bp+4] ;SS:[BP+4]指向元素个数 xor al,al sumc: add al,[bx] inc bx loop sumc pop cx pop bx pop bp ret checksumc endp
-
3、子程序的嵌套
子程序内包含有子程序的b调用就是子程序嵌套,没有什么特殊要求
4、子程序的递归
- 当子程序直接或间接地嵌套调用自身时称为递归调用,含有递归调用的子程序称为递归子程序
- 递归子程序必须采用寄存器或堆栈传递参数,递归深度受堆栈空间的限制
5、子程序的重入
- 子程序的重入是指子程序被中断后又被中断服务程序所调用,能够重入的子程序称为可重入子程序。在子程序中,注意利用寄存器和堆栈传递参数和存放临时数据,而不要使用固定的存储单元(变量),就能够实现重入。
- 子程序的重入不同于子程序的递归。重入是被动地进入,而递归是主动地进入;重入的调用间往往没有关系,而递归的调用间却是密切相关的。递归子程序也是可重入子程序
第五章 高级汇编语言程序设计
1、高级语言特性
MASM 6.0 引入高级语言的程序设计特性
-
条件控制伪指令
.IF .ELSE .ENDIF
-
循环控制伪指令
.WHILE .ENDW .REPEAT .UNTIL
-
过程声明和过程调用伪指令
.PROTO .INVOKE
1、条件控制伪指令
.IF .ELSE .ENDIF
类似高级语言中IF、THEN、ELSE、ENDIF
的相应功能
在汇编时要展开,自动生成相应的比较和条件转移指令序列,实现程序分支
常见的操作符
操作符 | 功能 | 操作符 | 功能 | 操作符 | 功能 |
---|---|---|---|---|---|
== | 等于 | && | 逻辑与 | CARRY? | CF=1? |
!= | 不等于 | || | 逻辑或 | OVERFLOW? | OF=1? |
> | 大于 | ! | 逻辑非 | PARITY? | PF=1? |
>= | 大于等于 | & | 位测试 | SIGN? | SF=1? |
< | 小于 | () | 改变优先级 | ZERO? | ZF=1? |
<= | 小于等于 |
;单分支结构
.IF AX < 0
neg ax
.ENDIF
mov result,ax
;双分支结构
.IF ax==5
mov bx,ax
mov ax,0
.ELSE
dec ax
.ENDIF
2、循环控制伪指令
;求1~100之和
xor ax, ax
mov cx,100
.while cx!=0
add ax,cx
dec cx
.endw
mov sum,ax
;求1~100之和
xor ax, ax
mov cx,100
.repeat
add ax,cx
dec cx
.until cx==0
mov sum,ax
3、过程声明和过程调用伪指令
过程定义位指令PROC
要调用带参数过程定义的过程,不应该用CALL指令,因为比较烦琐
应该采用过程调用
INVOKE
指令使用
INVOKE
伪指令的前提是需要用PROTO
伪指令对过程进行声明
-
过程声明伪指令:用于事先声明过程的结构
PROTO 调用距离 语言类型, 参数:类型
-
过程调用伪指令:
INVOKE 过程名,参数,...
示例:
2、宏结构程序设计
宏(Macro)
是汇编语言的一个特点,它是与子程序类似又独具特色的另一种简化源程序的方法
宏汇编、重复汇编、条件汇编——统称为
宏结构
1、宏汇编
a. 宏
具有宏名的一段汇编语句序列(宏定义时书写)
b. 宏指令
汇编语句的缩写(宏调用时书写)
c. 宏展开
宏指令用这段宏代替的过程(宏汇编时实现)
宏的参数
宏的参数功能强大,颇具特色
配合宏,还有宏操作符和有关伪指令
宏操作符
与宏有关的伪指令
宏汇编与子程序的比较
**<img src="https://zsr204.oss-cn-beijing.aliyuncs.com/imgs/20200709170741.png" alt="image-20200709170741619" style="zoom:50%;" />**
2、重复汇编
-
重复汇编
指在汇编过程中,重复展开一段(基本)相同的语句 -
重复汇编没有名字,不能被调用
-
重复汇编常用在宏定义体中,也可以在一般汇编语句中使用
-
重复汇编伪指令有三个:
REPEAT
——按参数值重复FOR
——按参数个数重复FORC
——按参数的字符个数重复 -
最后,用ENDM结束
1. 按参数值重复
REPEAT 重复次数
重复体
ENDM
2. 按参数个数重复
FOR 形参,〈实参表〉
重复体
ENDM
3. 按参数字符个数重复
FORC 形参, 字符串
重复体
ENDM
3、条件汇编
条件汇编伪指令在汇编过程中,根据条件决定汇编的语句
pdata macro num
IF num lt 100 ;;如果num < 100,则汇编如下语句
db num dup (?)
ELSE ;;否则,汇编如下语句
db 100 dup (?)
ENDIF
endm
;-----------------------------------------------------------------------------------------------
pdata 12 ;宏调用①
db 12 dup(?) ;宏汇编结果①
pdata 102 ;宏调用②
db 100 dup(?) ;宏汇编结果②
宏结构的作用
宏汇编
、重复汇编
和条件汇编
为源程序的编写提供了很多方便,灵活运用它们可以编写出非常良好的源程序来- 汇编系统中有些以圆点起始的伪指令(如.startup、.exit等)实际上是一种宏结构
dstring MACRO string ;;定义字符串
db '&string&',0dh,0ah,'$'
ENDM
mainbegin MACRO dsseg ;;设置数据段地址
mov ax,dsseg
mov ds,ax
ENDM
dispmsg MACRO message
mov dx,offset message
mov ah,09h
int 21h
ENDM
mainend MACRO retnum ;;返回DOS,可不带参数
ifb <retnum>
mov ah,4ch ;;没有参数
else
mov ax,4c00h+(retnum AND 0ffh)
;; 有参数
endif
int 21h
ENDM
;----------------------------------------------------------
.model small
.stack 256
.data
msg1 equ this byte
dstring <Hello,Everybody !!>
msg2 equ this byte
dstring <You see,I made it.>
.code
start: mainbegin @data ;建立DS内容
dispmsg msg1 ;显示msg1字符串
dispmsg msg2 ;显示msg2字符串
mainend ;返回DOS
end start
3、模块化程序设计
将
程序分段
、采用子程序
或者宏结构
都是进行模块化程序设计
1. 源程序文件的包含
2. 目标代码文件的连接
-
把常用子程序写成独立的源程序文件,单独汇编,形成子程序的
目标文件.OBJ
-
主程序也经过独立汇编之后形成
目标文件
-
连接程序将所有
目标文件
连接起来,最终产生可执行文件
需要遵循的原则:
**① 声明共用的变量、过程等**
**② 实现正确的段组合**
**③ 处理好参数传递问题**
3. 子程序库的调入
把常用子程序写成独立的源文件,单独汇编形成OBJ 文件后,存入
子程序库
主程序也单独汇编形成OBJ文件
主程序连接时,调入子程序库中的
子程序模块
,产生最终的可执行文件
例题:
;Lt512c.asm
... ;宏定义
.code
extern ALdisp:near,sorting:near,input:near
;声明其他模块中的子程序
.startup
...
.exit 0
end
;sub512c1.asm
.model small
.code
public aldisp
Aldisp proc
...
Aldisp endp
end
;sub512c2.asm
.model small
.code
public sorting
sorting proc
...
sorting endp
end
;sub512c3.asm
.model small
.code
public input
input proc
...
input endp
end
例题:
4、输入输出程序设计
1、 输入输出指令
8086通过输入输出指令与外设进行数据交换,呈现给我们的外设是端口(I/O地址)
8086用于寻址外设端口的地址线为16条,端口最多为216=65536(64K)个,端口号为0000H~FFFFH
每个端口用于传送一个字节的外设数据
输入输出的寻址方式有两种:
1、直接寻址
:只用于寻址00H~FFH前256个端口,操作数i8表示端口号
2、间接寻址
:可用于寻址全部64K个端口,DX寄存器的值就是端口号
- 大于FFH的端口只能采用间接寻址方式
a. 输入指令IN
将外设数据传送给CPU内的AL/AX
IN AL,i8 | ;字节输入:AL←I/O端口(i8直接寻址) |
---|---|
IN AL,DX | ;字节输入:AL←I/O端口(DX间接寻址) |
IN AX,i8 | ;字输入:AX←I/O端口(i8直接寻址) |
IN AX,DX | ;字输入:AX←I/O端口(DX间接寻址) |
b. 输出指令OUT
将CPU内的AL/AX数据传送给外设
OUT i8,AL | ;字节输出:I/O端口←AL(i8直接寻址) |
---|---|
OUT DX,AL | ;字节输出:I/O端口←AL(DX间接寻址) |
OUT i8,AX | ;字输出:I/O端口←AX(i8直接寻址) |
OUT DX,AX | ;字输出:I/O端口←AX(DX间接寻址) |
2、程序直接控制输入输出
3、程序查询输入输出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0sT9y2qx-1598619859404)(https://zsr204.oss-cn-beijing.aliyuncs.com/imgs/20200709194057.png)]
4、中断服务程序
需要交换数据的外设,采用中断请求向处理器提出要求
处理器执行事先设计好的中断服务程序,在中断服务程序当中实现数据交换
8086可以处理256种中断,分为内部、外部两种类型
外部可屏蔽中断用于与外 设进行数据交换