第四章指令系统与汇编语言

Download Report

Transcript 第四章指令系统与汇编语言

第4章 指令系统与汇编语言
4.1 指令系统概述
4·2 寻址方式
4·3 8086/8088指令系统
4·4 汇编语言简述
本章学习目标
1
8086CPU指令系统的寻址方式及地址计算方法
2
8086CPU指令及其应用
3
汇编语言基本格式
4
汇编语言程序设计基本步骤和基本方法
5
顺序程序、分支程序、循环程序、子程序的
基本结构和设计方法
6
常用DOS中断调用
返回本章
4.1
指令系统概述
4.1.1 指令的格式
4.1.2 指令中的操作数
返回本章
4.1.1
指令的格式
指令的格式与机器的字长、存储器的容量及指令的
性质都有很大的关系。计算机是通过执行指令来完成各种
任务的,微处理器处理的所有对象除指令外都可以认为是
数据,所以计算机所要完成的任务就是对各种数据的处理。
因此,一条指令至少要包括这样几个信息:运算数据的来
源、运算结果的去向及执行的操作。
指令要执行的操作称为操作码,也称为指令码。它说
明所执行的操作的性质和功能,如加、减、乘、除等。操
作码有的占用一个字节,有的占用两个字节。一台计算机
有几十或几百条指令,每条指令都有一个对应的操作码,
计算机通过识别操作码来决定要进行的操作。
返回本节
参加运算的数据,亦即操作的对象称为操作数。数
据的来源及运算结果的去向都统称为操作数的地址,也
可简称为操作数。通常将存放结果的操作数称为目标操
作数,相应的另一个操作数就称为源操作数。
除操作码和操作数外,在遇到转移、子程序调用等
非顺序执行的情况时,指令中还应给出下一条要执行的
指令的地址(在程序顺序执行时,下条要执行的指令的
地址由指令指针IP指出)。
总之,一条指令主要包括了两种信息,即操作码和
操作数(或称地址码)。操作码部分描述操作的性质,
操作数描述了操作的对象,它可以是直接参加运算的数
据,也可以是数据的地址。
2.单操作数指令
格式: 操作码
操作数
这里操作数,它有两种情况:一是该操作数既表示运算
数据的地址,也表示存放运算结果的地址。如加1指令:
INC AX。该指令执行的结果是将累加器AX中的内容加1然
后再送回AX;二是指令中的操作数是源操作数,而目标
操作数被隐含了,即指令实际上是双操作数指令,默认
了另一个操作数,运算的结果也默认存放在隐含的操作
数地址中。如乘法指令: MUL BL,该指令的执行是将累
加器AL的内容与BL内容相乘,结果送AX。
3.双操作数指令
格式: 操作码
操作数1
操作数2
指令中的操作数2是源操作数,它本身就是参加运算的
一个数据,或是其中一个操作数的地址;操作数1是目
标操作数,它有两个含义,表示另一个操作数的地址及
存放运算结果的目标地址。这是最常见的指令格式,微
处理器的基本指令系统中多数指令都是双操作数指令。
例如:加法指令ADD AX, BX。
微型计算机中的指令都是以上三种格式。在大型机或
某些功能较强的中、小型机的指令格式就不限于以上三
种,而可以是三操作数以至多操作数的指令格式。
返回本节
4.1.2 指令中的操作数
8086指令系统中的指令均为以上三种格式。其操作数既
可以是数据,也可以是参加运算的数据的地址。根据它们的
性质,将操作数分为这样三类:立即数操作数、寄存器操作
数和存储器操作数。
1.立即数操作数
所谓立即数是指具有固定数值的操作数,即常数。它可
以是字节(8位)或字(16位),可代表无符号数或有符号
数。立即数操作数直接存放在指令代码中,跟在操作码之后,
它作为指令的一部分存放在代码段里。
在指令中,立即数操作数表示参加运算的数据而不是数
据的地址,所以只能用作源操作数,而不能作为目标操作数。
例: MOV AX,1234H
返回本节
注意:
立即数无法作为目标操作数
立即数可以是无符号或带符号数, 其值应在允许范
围内
错误例:
MOV AX, 12345H
MOV 1234H, AX
2.寄存器操作数
寄存器操作数表示参加运算的数存放在指令给出的
寄存器中,即寄存器的内容为操作数,可以是16位或8
位。寄存器操作数既可以用做源操作数,也可以用做
目标操作数。
例:MOV AX,BX
MOV DL,CH
返回本章
3.存储器操作数
存储器操作数是指操作数存放在内存中,指令中需要指
明操作数的地址。存储器操作数在指令中既可作为源操作数,
也可以作为目标操作数。操作的数据可以是字节、字或双字,
分别存放在1个、2个或4个存储单元中。地址值或寄存器
表示形式:[地址值或寄存器]
例:MOV AX,[1234H]
;[1234H]表示操作数在地址为
1234H的存储器单元中
在8086中,大多数指令不允许源操作数和目标操作数同
时为存储器作数,也就是说,不允许从存储器到存储器的操
作。若有这样的需要,可以先将其中一个存储器的内容传送
到某个通用寄存器中,然后再把这个寄存器与另一个存储器
的内容作为操作数执行希望的操作。
从前面章中已经知道,能够惟一标识一个存储器单元的
是它的物理地址,而物理地址是由段基地址和偏移地址计算
得出。所以,要寻找一个存储在存储器中的操作数,必须首
先确定数据所在的段,即确定有关的段寄存器。
一般情况下,若指令中没有指明所涉及的段寄存器,则
CPU就采用默认的段寄存器来确定数据所在的段。各种存储器
操作所约定的默认段寄存器、段超越(即显式地指明段寄存
器)所允许的段寄存器以及指令的有效地址所在的寄存器见
表4-1。
表4-1 段寄存器使用的一些基本约定
返回本节
4.2 寻址方式
4.2.1 寻找操作数的寻址方式
4.2.2 寻找转移地址的寻址方式
返回本章
4.2.1
寻找操作数的寻址方式
1.立即寻址
立即寻址是指指令中的源操作数是参加操作的一个立
即数,由指令直接给出。它作为指令的一部分,紧跟在指
令的操作码之后,存放于内存的代码段中,在CPU取指令
时随指令码一起取出并直接参加运算。
指令中的立即数可以是8位或16位的整数。若为16位,
则存放时低8位存放在低地址单元,高8位存放在高地址单
元。
例如: MOV AX,3344H
;表示将16位的立即数
3344H送人累加器AX。指令执行后
AH=33H,AL=44H。
2.寄存器寻址
寄存器寻址中指令的操作数在CPU的内部寄存器。它
们可以是数据寄存器(8位或16位),也可以是地址指针、
变址寄存器或段寄存器。
通用寄存器主要用于存放操作的数据,通用寄存器中
的AX,BX,CX,DX既可以作为4个16位寄存器,用来存放字操
作数,也可以当作8个8位寄存器
(AH,AL,BH,BL,CH,CL,DH,DL),用来存放字节操作数。
SI,DI,BP,SP只能存放字操作数。
例: MOV SI, AX
;将AX的内容送到寄存器SI中
3.直接寻址
直接寻址指令是在指令中直接给出操作数的16位偏移
地址,该地址与指令的操作码一起存放在内存的代码段,
也是低8位在低地址,高8位在高地址。但是,若操作数本
身在指令中无特殊声明,默认存放在内存的数据段中。
例: MOV AX,[2233H]
;将数据段中偏移地址EA为
2233H和2234H两单元的内容送到AX中。
假如(DS)=2000H,则所寻找的操作数的物理地址
PA=(DS)*16+EA
=20000H+2233H=22233H
4.寄存器间接寻址
寄存器间接寻址与寄存器寻址方式不同,指令中指定
的寄存器的内容不是操作数,而是操作数的偏移地址。也
就是说操作数的偏移地址放在寄存器中,操作数本身则在
存储器中。存放存储器地址的寄存器有时也称为地址指针。
寄存器间接寻址方式可用的寄存器只允许是SI、 DI、 BX
和BP 4个,它们可简称为间址寄存器。选择不同的间址寄
存器,涉及的段寄存器将有所不同。
在默认情况下,选择SI, DI, BX为间址寄存器时,操
作数在数据段,段基地址由DS决定,操作数的物理地址为:
PA=(DS)*16+(BX)或(SI)或(DI)
在默认情况下,选择BP为间址寄存器,则操作数在堆
栈段,段基地址由SS决定,操作数的物理地址为:
PA=(SS)*16+(BP)
例: MOV AX,[SI]
假如(DS)=6000H,(SI)=1200H
操作数的物理地址PA=(DS)*16+EA
=(DS)*16+(SI)
=6000H*10H+1200H=61200H
61200单元的内容(61200H)=44送AL, 61201单元的内容
(61200H)=33送AH, 即指令执行的结果为(AX)=3344H。
5.寄存器相对寻址
采用这种寻址方式时,存放于主存中的操作数的偏移
地址等于间址寄存器的内容加上指令中给出的一个8位或
16位的地址位移量,位移量紧随操作码一起存放在代码段
中。操作数默认在哪一个段,则仍由所使用的间址寄存器
决定(使用BP则默认在堆栈段,其他的到默认在数据段)。
位移量也可看作相对值,故把这种带位移量的寄存器
间接寻址方式称为寄存器相对寻址。
PA=(DS)*16+(BX)或(SI)或(DI)+位移量
或 PA=(SS)*16+(BP) + 位移量
例: M0V AX,DATA[BX]
假如:(DS)=6000H,(BX)=1000H,DATA=08H,则
物理地址PA =(DS)*16+EA
=(DS)*16+(BX)+DATA
=60000H+1000H+08H=61008H
在汇编语言中,相对寻址指令的书写格式允许有几种不
同的形式。以下几种写法是完全等价的:
M0V AX,DATA[BX]
M0V AX,[BX]DATA
M0V AX,DATA+[BX]
M0V AX,[BX]+DATA
M0V AX,[DATA+BX]
M0V AX,[DATA+BX]
6.基址变址寻址
这种寻址方式由一个基址寄存器(BX或BP)的内容和一个
变址寄存器(SI或DI)的内容相加形成操作数在主存中的偏移
地址。数据的段基址在默认情况下则由指令中使用的基址寄
存器决定,即若使用BX作基址寄存器则数据默认在数据段,
段寄存器用(DS);若使用BP作基址寄存器则数据默认在堆
栈段,段寄存器用(SS)。
例:指令MOV AX,[BX][SI]
使用基址变址寻址方式时,8086CPU不允许将两个基址寄
存器或两个变址寄存器组合在一起寻址,即指令中不允许同
时出现两个基址寄存器或两个变址寄存器。例如,以下指令
是非法的:
MOV AX,[BX][BP];错误!同时出现两个基址寄存器
MOV AX,[SI][DI];错误!同时出现两个变址寄存器
7. 基址变址相对寻址
这种寻址方式事实上是上一种方式的扩充。指令中
指定了一个基址寄存器和一个变址寄存器,同时还给出
一个8位或16位的位移量,将三者相加就得到操作数在主
存中的偏移地址。至于默认的段寄存器,仍由所用的基
址寄存器决定。
例: MOV AX,DATA[BX][DI]
与寄存器间接寻址方式类似,基址变址相对寻址指令同
样也可以表示成多种形式,例如:
MOV AX,DATA[BX][SI]
MOV AX, [BX+DATA][SI]
MOV AX, [BX+SI+DATA]
MOV AX, [BX]DATA[SI]
MOV AX, [BX+SI][DATA]
同样,基址变址相对寻址也不允许在指令中同时出
现两个基址寄存器或两个变址寄存器。即下列指令也是
非法的:
MOV AX,DATA[SI][DI]
MOV AX,[BX][BP]DATA
返回本节
4.2.2 寻找转移地址的寻址方式
在一般情况下,当BIU取走一条指令后,指令指针IP会
自动加1指向代码段中下一条要执行指令的地址,使程序按
照预先设定好的次序,由低地址到高地址顺序执行。但当
遇到转移指令,需要改变程序的这种执行顺序,转移到一
个新的地址再顺序执行。因为BIU是严格按照CS和IP所给出
的地址去取指令的,所以,只要修改IP的内容或同时修改
CS和IP的内容就能够实现程序转移到新地址继续执行的目
的。寻找转移地址的寻址方法就是如何找出转移的目标地
址。
程序的转移可以是在当前代码段内,这种转移称为段内
转移,只需改变IP的内容;程序也可以转移到另一个代码
段,这称为段间转移,需同时修改CS和IP的内容。因此转
移地址的寻址相应地有段内寻址和段向寻址两种方式。
1.段内寻址方式
1)段内直接寻址
段内直接寻址也称为相对寻址。转移的地址是当前IP
的内容加上指令给定的8位或16位位移量,形成新的IP,并
使CS的内容保持不变。位移量可以为正,也可以为负。如
果位移量是8位,称为段内直接短转移,转移范围为
-128~+127;若位移量为16位,称为段内直接近转移,转移
范围为-32768~+32767。指令的格式表示为:
JMP NEAR PTR NEXT
JMP SHORT EXIT
其中,NEXT和EXIT均为转向的符号地址(标号),在
机器指令中,用位移量来表示。位移量就是程序中的标号
与转移指令之间的距离,由汇编程序计算得到。
段内直接寻址方式适合于条件转移或无条件转移类指
令,但条件转移指令只能是段内短转移。
2)段内间接寻址
此时程序转移的地址存放在寄存器或存储单元中。执
行时用寄存器或存储单元的内容取代当前IP的值。这个寄
存器或存储单元的内容可以用数据寻址方式中除立即数以
外的任何一种寻址方式取得。指令的格式表示为:
JMP BX
JMP WORD PTR [BX+TABLE]
其中,WORD PTR为操作符,用以指出其后的寻址方式
所取得的转向地址是一个字的有效地址。
2.段间寻址方式
使用段间寻址方式是表示程序转移的目标地址不在当
前代码段内。亦即不仅要改变指令指针IP的内容,代码段
寄存器CS的内容也要修改。段间寻址同样可分为直接寻址
和间接寻址两种方式。
1)段间直接寻址
这种寻址方式是直接用指令中(NEXT)给出的16位的段
地址以及16位的偏移地址来取代当前CS和IP的内容。指令
的格式表示为:
JMP FAR PTR NEXT
其中,NEXT 为转向的符号地址,FAR PTR为段间转移
的操作符。
2)段间间接寻址
此时转移的目标地址存放在存储器的连续4个单元中,
高地址单元存放新的CS值,低地址单元存放新的IP值。
指令的格式表示为:
JMP DWORD PTR [BX+TABLE]
其中,DWORD PTR为双字操作符,用以指出其后的寻
址方式所取得的转向地址需取双字的段间转移指令。
[BX+TABLE]说明数据寻址方式为寄存器相对寻址方式。
返回本节
4.3 8086/8088指令系统
4.3.1 数据传送类指令
4.3.2 算术运算类指令
4.3.3 逻辑运算(位操作)类指令
4.3.4 串操作类指令
4.3.5 控制转移类指令
4.3.6 处理器控制类指令
返回本章
4.3.1 数据传送类指令
数据传送类指令的功能是把数据、地址或立即数
传送到寄存器或存储单元。根据传送的内容可以分为
四类,通用数据传送指令、累加器专用传送指令、地
址传送指令和标志寄存器传送指令。
1.通用数据传送指令
(1)MOV传送指令
指令格式:MOV dst,src
;dst←(src)
其中dst表示目的操作数,src表示源操作数。该
指令把源操作数传送至目的操作数。这种传送实际上
是进行数据的“复制”,源操作数内容不变,目的操
作数内容与源操作数内容相同。
在MOV指令中源操作数可以为:存储器、寄存器、
段寄存器和立即数;目标操作数可以为:存储器、寄
存器(不能为IP)和段寄存器(不能为CS)。除了目
标操作数和源操作数不能同时为存储器、段寄存器;
目标操作数不允许为立即数;不允许立即数送段寄存
器以外,可以任意搭配。数据传送的方向如图4-8所示。
图4-8 MOV数据传送的方向
需说明一点,对于代码段寄存器CS和指令指针寄存
器IP,通常无需用户利用传送指令改变其中的内容。
但是CS可以作为源操作数。
①.立即数传送到通用寄存器:立即数可以是8位或16位数
据、常量、ASCII码字符、符号名等。数据的类型必须与寄
存器字长一致。
例如:
MOV AL,
MOV CL,
MOV SI,
存器
MOV DX,
32H
'D'
COUNT
;8位立即数52H 送AL寄存器
;字符’D’的 ASCII码值送CL寄存器
;COUNT为一个符号常数,其值送SI寄
3250H
;16位立即数3250H送DX寄存器
②.通用寄存器之间传送:此类指令可采用8位或16位寄存
器,两个寄存器之间传送要求数据类型必须匹配。
例如:
MOV AX, BX
;16位寄存器之间传送
MOV DL, CL
;8位寄存器之间传送
③.通用寄存器和存储单元之间传送:可以将数据从
存储单元传送到通用寄存器,也可以从通用寄存器传
送到存储单元。
例如:MOV AX, [2100H]
; 将指定存储单元中
的数据传送到AX寄存器中
MOV [5210H], SI
; 将SI寄存器中的内
容传送到指定的存储单元中这类传送指令中,两个操
作数的类型要一致。
④.立即数传送到存储单元:源操作数可以是8位或
16位立即数。
例如:MOV ARRAY, 32H;如果在数据段中己定义ARRAY
为内存单元的符号地址,则将立即数32H送给ARRAY单
元。
⑤.段寄存器与通用寄存器间的数据传送:采用16位寄
存器,且只能是字操作。段寄存器CS不能作为目的操作数。
例如:MOV DS, AX
数据段寄存器DS中
;将累加器AX中的内容传送到
⑥.段寄存器与存储单元间的数据传送:
例如:MOV [SI], DS
指示的字单元中
MOV
ES, [BX]
的内容传送到ES中
;将DS中的内容传送到SI所
;将BX所指示的存储单元中
注意:MOV指令可以在CPU内部或CPU和存储器之间
传送字或字节数据。应该注意:立即数不能直接送段
寄存器;目的操作数不允许用立即数寻址,也不允许
用CS寄存器;MOV指令在两个存储单元之间不能直接传
送数据;不允许在两个段寄存器之间直接传送数据:
MOV指令不影响标志位。
(2)XCHG交换指令
指令格式:XCHG oprl,opr2 ;将源地址与目的地
址中的内容互换。即(oprl)→←(opr2)
其中oprl和opr2表示操作数。该指令中必须有一个
操作数是在寄存器中,可以是两个寄存器间,也可以是
寄存器和存储单元之间。可以实现字节数据交换,也可
以实现字数据交换。指令执行结果不影响标志位。
例如:XCHG AX,BX
;将寄存器AX内容与BX内容互
相交换
若给定(AX)=325AH,(BX)=2130H
则指令执行后(AX) =2130H, (BX) =325AH。
(3)堆栈操作指令
堆栈是存储器中的一个特殊区域,用于存入和取出
数据。堆栈是以“先进后出”的方式进行数据操作的。
从8086的堆栈组织来看,堆栈操作必须以“字”为单位
进行,堆栈是从高地址向低地址方向生长的。它只有一
个出入口,堆栈指针寄存器SP始终指向堆栈的栈顶单元。
堆栈操作指令是用来完成压入和弹出堆栈操作的。
8086/8088指令系统中提供了完成这两种操作的相应指令。
①.PUSH入栈指令
指令格式及操作:
PUSH src ;SP←SP-2,((SP)+1, (SP))←(src)
指令完成的操作是“先移后入”,即先将堆栈指针SP减2,
然后再将操作数src中的双字节数压入由SP指出的栈顶中。
指令中的操作数src可以是通用寄存器和段寄存器,也可以
是由某种寻址方式所指示的存储单元,但不能是立即数。
例如:
PUSH AX ;SP←(SP)-2,((SP)+1)← (AH),
((SP))←(AL)
PUSH CS
PUSH [SI]
②.POP出栈指令
指令格式及操作:
POP dst ;(dst)←((SP)+1, (SP)),SP←SP+2
指令完成的操作是“先出后移”,即先将堆栈指针SP
所指示的栈顶存储单元弹出一个双字节数到操作数dst中,
然后将堆栈指针SP加2。指令中的操作数dst可以是通用
寄存器和段寄存器(但不能是CS),也可以是存储单元,
同样不能是立即数。
例如:POP BX
若给定(SS)=2000H,(SP)=0100H,(BX)=78C2H,
(20100H)=6B48H
则指令执行后(BX)=6B48H., (SP) =0102H
2.累加器专用传送指令
累加器AX通常作为数据传输的核心,8086指令系统
中的输入/输出指令和换码指令是专门通过累加器来执行
的。
(1)输入指令
Ⅰ.直接寻址的输入指令:IN Acc,port ;Acc←
(port)
此指令将8/16位数据直接经输入端口port(地址0~
255)送入到AL/AX累加器中。Acc为8位或16位累加器。
例如:IN AL, 30H ;将端口地址为30H(宽度
为8位)的端口中的数据读入到AL中。
Ⅱ.间接寻址的输入指令:IN Acc, DX ;Acc←
((DX))
此指令是从DX 内容指定的端口中将8/16位数据
送入到AL/AX累加器中。Acc为8位或16位累加器。当
端口地址〉255时,必须用此方式。
例如:MOV DX, 1FFH ;将端口地址1FFH先送
给DX寄存器
IN AL, DX
;将DX所指示的的端口
中的数据读入到AL中。
(2)输出指令
Ⅰ.直接寻址的输出指令:OUT port, Acc ;
port← (Acc)
此指令将AL/AX累加器中8/16位数据输出到指令
指定的I/O端口port(地址0~255)中。
例如:OUT 20H, AL
; AL中的内容输出到端口
地址为20H的字节端口中。
Ⅱ.间接寻址的输出指令:OUT DX, Acc ;
((DX))←(Acc)
此指令将AL/AX累加器中8/16位数据输出到DX内容
指定的I/O端口中。当端口地址〉255时,必须用此方式。
例如:MOV DX, 1FFH ;
(3)XLAT
换码指令
指令格式:XLAT
;执行操作AL←(BX+AL)
换码指令可将(BX)为首址,(AL)为位移量的字
节存储单元中的数据送AL寄存器。
一般用来实现码制之间的转换,又称为查表转换指令。
可将AL中的一个值转换为内存表格中的某一个值,再送回
AL中。
3.地址传送指令
(l)LEA有效地址送寄存器:LEA reg16,mem;将存储
器操作数mem的有效地址传送到16位的通用寄存器reg16中。
例如:LEA BX,[SI+BP]
;执行后将(SI)+(BP)的结
果送到BX中。
LEA BX,buffer
;执行后将符号地址buffer的
有效地址送到BX中。
(2)LDS地址指针送寄存器:LDS reg16,mem32;其中
mem为32位存储器操作数,reg16为16位通用寄存器。此指
令执行的操作是将mem32指示的前两个字节单元的内容送入
指令中指定的通用寄存器中,后两个字节的内容送入DS 中。
例如:LDS BX,ARR[SI]
若给定ARR=0010H,(SI)=0020H,(DS)=2000H, (BX)
=6AE0H
(20030H.) =0080H,(20032H)=4000H
则指令执行后(BX) =0080H, (DS)=4000H
(3)LES地址指针送寄存器:LDS reg16,mem32;指令
执行的操作与LDS指令大致相似,不同之处是以ES代替DS。
例如:LES DI,[BX]
若给定
(DS)=2000H,(BX)=0020H,(20020H)=18H,(20021H)=A5H,
(20022)=00H,(20023H)=50H,(ES)=4000H
则指令执行后(DI)=A518H,(ES)=5000H。
4.标志寄存器传送指令
标志寄存器传送指令共有4条。这些指令都是单字节指令
,指令的操作数以隐含形式规定,字节操作数隐含为AH寄存
器。
(1)取标志指令:LAHF
;将标志寄存器FLAG中的5个
状态标志位SF, ZF, AF, PF及CF分别取出传送到累加器AH的
对应位,如图4-9所示。
A
H
FL
AG
ⅹ ⅹ ⅹ ⅹ OF DF IF TF SF ZF ⅹ AF ⅹ PF ⅹ CF
图4-9 LAHF 指令操作示意图
(2)置标志位指令:SAHF ;SAHF指令的传送方向与
LAHF方向相反,将AH寄存器中的第7, 6, 4, 2, 0位分别
传送到标志寄存器对应位。SAHF指令将影响标志位,
FLAG寄存器中的SF、 ZF、 AF、PF和CF将被修改成AH寄
存器中对应位的值,但OF、 DF、IF和TF不受影响。
(3)标志压入堆栈指令:PUSHF ;PUSHF指令先将SP
减2,然后将标志寄存器FLAG中的内容(16位)压入堆栈
中。
(4)标志弹出堆栈指令:POPF ;POPF指令的操作与
PUSHF指令相反,它将堆栈内容弹出到标志寄存器FLAG,
然后加2。POPF指令对状态标志位有影响。
PUSHF和POPF指令可用来保护调用过程以前标志寄存器
的值。过程返回后再恢复标志位,或用来修改标志寄存
器中相应标志位的值。
返回本节
4.3.2 算术运算类指令
8086的算术运算类指令包括加、减、乘、除4种基本运
算指令,以及进行BCD码十进制数运算的指令。
算术运算指令涉及无符号数和带符号数两种类型的数据,
加减运算采取同一套指令,乘除运算有各自不同的指令。加
减法运算在执行过程中有可能产生溢出,对于无符号数,如
果加法运算最高位向前产生进位,减法运算最高位向前有借
位,则表示出现溢出,用CF标志位可检测无符号数是否溢出;
对于带符号数,采用补码运算,符号位参加运算,溢出则表
示运算结果发生错误,用OF标志位可检测带符号数是否溢出。
算术运算指令会影响标志位,其规则如下:
•运算结果向前产生进位或借位时,CF=1;
•最高位向前进位和次高位向前进位不同时,OF=1;
•若运算结果为0, ZF=1;
•若运算结果最高位为1, SF=1;
•若运算结果中有偶数个1,PF=1。
1.加法指令
(1)不带进位加法指令:ADD dst, src ;(dst)
←(dst)+(src) 将源操作数src和目的操作数dst数相加,
结果放回目的操作数。
注意,两个存储器操作数不能直接相加,段寄存器不能
参加运算。在使用时还要注意两个操作数类型保持一致。
例如:
ADD AL, BL
;两个寄存器字节数据相加
ADD AL, [0210H];内存单元与寄存器字节数据相加
ADD [SI],AX ;寄存器与内存单元字数据相加
ADD BYTE PTR[SI],34H;立即数与内存单元字节数据相
加
(2)带进位的加法指令:ADC dst, src
;(dst)
←(dst)+(src)+ (CF)将源操作数与目的操作数及进位标志
CF的值相加,结果放在目的操作数中。源操作数及目的操作
数的类型与ADD指令相同。
ADC指令主要用于多字节(或多字)加法运算中。如果低
字节(或字)相加时产生进位,则在下一次高字节(或字)
相加时将这个进位加进去。
(3)加1指令:INC opr ;指令对opr中的内容加1。
opr只能为通用寄存器或存储器操作数,不能为立即数,
也不能是段寄存器。该指令常用在循环程序中修改地址指针
或用作循环计数器。
例如:INC AL
INC CX
INC BYTE PTR [SI+BX]
2.减法指令
(1)不带借位减法指令SUB dst, src
;(dst)
←(dst)-(src) 将目的操作数dst减去源操作数src,结果放
回目的操作数。
例如:SUB AX, BX ;执行AX内容减去BX内容,结果送到
AX中。
(2)带借位的减祛指令:SBB dst, src
;(dst)
←(dst)-(src)- (CF)被减数dst减去减数src,同时还要减去
借位CF。SBB指令主要用于多精度数减法中。
例如:SBB DX, CX ;执行(DX)-(CX)-CF,结果送到DX
中。
(3)减1指令: DEC opr ;(opr)←(opr)-1将操作数减
1,结果送回操作数。此指令通常也用于循环程序中修改地址
指针和循环次数。
例如:DEC CX
; CX中的内容减1后,送回CX
DEC BYTE PTR[SI] ;将SI所指示字节单元中的内容减1后,
送回该单元。
(4)求补指令:NEG opr ;(opr)←0-(opr) 用0减去
操作数,结果送回操作数,相当于将opr中的内容连同符号位
按位取反后,末位加1。
opr只能为通用寄存器或存储器操作数。该指令实际是求相
反数。常利用该指令求负数的绝对值。例如:假设原来
AL=0FFH,执行NEG AL后,结果为AL=01H。
(4)比较指令:CMP oprl,opr2;执行操作:(oprl )(opr2)
该指令与SUB指令一样执行减法操作,但该指令不保存结果,
即指令执行后,两个操作数的内容不会改变。这条指令是根
据操作的结果设置状态标志位,按比较结果使程序产生条件
转移。
例如:CMP AL,0 ;AL和0进行比较
JGE NEXT
;若AL≥0则转到NEXT位置执行
3.乘法运算类指令
乘法指令包括对无符号数和带符号数相乘的指令。进行
乘法运算时,如果两个8位数相乘,结果为16位;如某两个16
位数相乘,结果为32位。
乘法指令中有两个操作数,但指令中只给出乘数,被乘
数隐含给出。8位数相乘时被乘数先放入AL中,乘积存放到AX
中;16位数相乘时被乘数先放入AX寄存器中,乘积放到DX和
AX两个寄存器中,且DX中存放高16位,AX中存放低16位。
(1)无符号数乘法指令:MUL src
若src为字节数据,则执行AX←(src)x(AL)
若src为字数据,则执行DX、 AX←(src)x(AX)
src可采用寄存器操作数、存储器操作数,但不能使用立即
数和段寄存器。
例如: MUL AH ;完成(AH) x(AL);结果送AX
MUL BX ;完成(BX) x (AX),结果送DX, AX
(2)带符号数乘法指令:IMUL src
执行功能与MUL相同。
4.除法运算类指令
8086CPU执行除法时规定:除数长度只能是被除数长度的一
半。当被除数为16位时,除数应为8位;当被除数为32位时,
除数应为16位。
(1)无符号数除法指令:DIV src ;DIV指令的被除数、
除数、商和余数全部为无号数。
执行操作如下:
src为字节数据时: AL← (AX)/(src)
;保存商
AH ← (AX)/(src)
;保存余数
src为字数据时: AX← (DX)(AX)/(src) ;保存商
DX← (DX)(AX)/(src) ;保存余数
(2)带符号数除法指令:IDIV src
与DIV指令相同,但被除数、除数、商和余数均为带符号数,
且余数的符号位与被除数相同。
5.符号扩展指令
在前面介绍的各种二进制算术运算中,两个操作数的字
长应该符合规定的关系。例如:在加法、减法和乘法运算中,
两个操作数的字长必须相等。在除法指令中,被除数必须是
除数的双倍字长。因此,有时需要将一个8位数扩展成为16
位,或者将一个16位数扩展成为32位。
对于无符号数,扩展字长比较简单,只需添上足够个数
的零即可。例如,以下两条指令将AL中的一个8位无符号数
扩展成为16位,存放在AX中。
MOV AL ,0FBH
;(AL)=11111011B
XOR AH ,AH
;(AH)=00000000B
对于带符号数,扩展字长需用符号扩展指令。符号扩展
指令是指用一个操作数的符号位形成另一个操作数,后一个
操作数的各位是全0(正数)或全1(负数),其结果是使数
据位数加长,但数据大小并没有改变。符号扩展指令的执行
不影响标志位。
(1)字节转换为字指令:CBW
执行操作: AL中的符号位扩展到AH中。若AL中的
D7=0,则(AH)=00H;若AL中的D7=1,则(AH)=FFH。
(2)字转换为双字指令:CWD
执行操作:AX中的符号位扩展到DX中。若AX中的
D15=0,则(DX)=0000H,若AX中的D15=0,则(DX)
=FFFFH。
6.十进制调整指令
算术运算中如果使用BCD数,其结果必须用十进制调整
指令,否则结果无意义。
(1)压缩型BCD码加法调整指令:DAA
这条指令执行前,先执行加法指令(ADD或ADC)将压缩
型BCD码相加,结果存放在AL寄存器中。DAA指令紧跟在加
法指令之后,执行时,先对结果进行测试,若结果中的低
四位是十六进制数码A-F,或者是AF=1,则AL寄存器内容加
06H;如果AL寄存器中高四位是十六进制数码的A-F,或者
是CF=1,则AL寄存器的内容加60H。
DAA指令影响OF以外的其他状态标志位。
例如:ADD AL, BL
DAA
若给定(AL)=28H,(BL)=69H,执行ADD指令后,
(AL)=91H,AF=1。
执行DAA指令,因AF=1,调整操作AL←(AL) +06H,则AL
中的内容为97H,高四位≤09H,不必进行调整。
(2)非压缩型BCD码加法调整指令:AAA
指令执行之前,先执行ADD或ADC指令,把两个非压缩型
BCD码相加,将结果存放在AL寄存器中,调整后的结果放到
AX中。指令执行后,只影响AF和CF标志位。
AAA指令的调整原则:
若AL的低4位>9,或AF=1,则AL←(AL)+06H;CF=AF=1;
同时AH←(AH) +1;AL的高4位为0。
例如:ADD AL, BL
AAA
若给定(AX)=0505H, (BL)=09H。ADD指令执行后,(AL)
=0EH,不是压缩型BCD码;执行AAA指令后,(AX)=0104H,
为14的非压缩型BCD码。
(3)压缩型BCD码减法调整指令:DAS
指令执行之前,先执行减法指令(SUB或SBB指令)把两个
压缩型BCD码数相减,并将结果存放在AL寄存器中。DAS指令
影响OF以外的状态标志位。
DAS指令的调整方法是:如果AF=1,或者AL寄存器的低4位
是十六进制的A-F,则AL寄存器中的内容减06H,且将AF标志
位置1。如果CF=l,或者AL寄存器的高4位是十六进制的A-F,
则AL寄存器中的内容减60H,并将CF标志位置1。
例如:SUB AL, AH
DAS
若给定(AL) =97H, (AH)=39H, SUB指令执行后,(AL)
=5EH, AF=1,AL中的内容不是压缩BCD码格式,需进行调整。
执行DAS指令,完成AL←(AL)-06H后,(AL)=58H,CF=0且
AL中高4位≤09H,不必进行调整。
(4)非压缩型BCD码减法调整指令:AAS
指令执行前,先执行SUB或SBB指令,减法指令把两个非压
缩型BCD码相减,将结果存放在AL寄存器中,调整后的结果
放到AX中。指令执行后,只影响AF和CF标志位。
AAS指令的调整原则是:若AL的低4位>9,或AF=1,AL←
(AL)-06H ; CF=AF=1;同时AH←(AH)-1; AL的高4位
为0。
例如:SUB AL, CL
AAS
若给定(AX)=0205H,(CL)=08H,SUB指令执行后,AF=1,
CF=1,(AL)=0FDH,执行AAS指令后, (AX) =0107H。
返回本节
4.3.3 位操作指令
位操作指令是对8位或16位的寄存器或存储单元中的内容按
位进行操作。这类指令包括逻辑运算指令、移位指令和循环
移位指令三组。
1.逻辑运算类指令
逻辑运算指令可对8位或16位数进行逻辑运算,并且是按位
进行操作的。
(1)逻辑与指令:AND dst, src
;(dst)←(dst)
∧ (src)
AND指令完成源操作数和目的操作数按位进行逻辑“与”运
算,将运算结果送回目的操作数。该指令执行结果影响CF、
OF、 SF、 PF和ZF,使CF=0,OF=0,其他状态标志位按结果
进行设置。
利用AND指令可将操作数中的某些位保持不变,而使其他一
些数位清0,称之为屏蔽。为做到这一点,只需将要屏蔽的
位和“0”进行“与”,而将要保留的位和“1”进行“与”即
可。
例如: AND AL,0FH
若给定(AL)=38H,则指令执行后,(AL)=08H,将
AL寄存器中内容的高4位屏蔽掉了。
(2)逻辑或指令:OR dst, src
∨(src)
;(dst)←(dst)
OR指令完成源操作数和目的操作数按位进行逻辑“或”
运算,将运算结果送回目的操作数。该指令执行结果影响状
态标志位,与AND相同。
利用OR指令可将操作数中的某些位保持不变,而使其他
一些位置1。
例如: OR AL,80H
若给定(AL) =25H,则指令执行后,(AL) =A5H,达到将
位7置1的目的。
(3)逻辑异或指令:XOR dst, src
XOR指令完成源操作数和目的操作数按位进行逻辑“异或”
运算,并将运算结果送回目的操作数,该指令执行结果影响
状态标志位,与AND相同。
利用XOR指令可将操作数中的某些位保持不变,而使其他一
些数位按位取反。为此:可将欲“求反”的位和“1”进行异
或,将保持不变的位和“0”进行异或。
例如:若要使AL寄存器中的第0、1位求反,其它位保持不
变,则只需将AL和00000011B(即03H)异或即可。
MOV AL, 0FH
XOR AL, 03H
则指令执行后(AL)=00001100B
4)逻辑非指令:NOT dst
NOT指令为单操作数指令,对给定的数逐位取反。该指令执
行后不影响任何标志位。例如:NOT AL
若给定(AL)=01101011B,则指令执行后,(AL)
=10010100B。
(5)测试指令:TEST
dst, src
; (dst) ∧ (src)
TEST指令完成源操作数和目的操作数按位进行逻辑“与”
运算,运算结果不送回目的操作数,因此两个操作数的内容
均保持不变。该指令执行结果影响SF、 PF和ZF,使CF=0,
OF=0。利用该指令,可以在不改变原有操作数的情况下,用
来检测某一位或某几位是“0”还是“1”。
例如:测试DL寄存器第2位是否为0。
TEST DL,00000100B
JZ EXIT
2.移位指令
(1)逻辑左移指令:SHL dst, 1/ CL
指令功能为:将dst中的二进制位向左移动1位或CL中指定
的位数。左移时,操作数的最高位移出到CF中,最低位补0
。其中dst可以是通用寄存器或存储器操作数,但不能是立
即数和段寄存器。操作数可以是8位或16位。如果移动的位
数超过1位,则移位次数应放入CL中。
SHL指令将影响CF、OF、SF、ZF、PF标志位。利用左移1位
操作可实现将操作数乘2的运算。
例如:SHL
AL,1
;将AL中的内容向左移动1位,相当
于(AL) X2
MOV CL, 4
;移位次数送CL
SHL AL, CL
;将AL中的内容向左移动4位
,空出位补0。
(2)逻辑右移指令:SHR
dst, 1/ CL
指令功能为:将dst中的二进制位向右移动1位或CL寄存
器中指定的位数。右移一位时,操作数的最低位移出送到CF
中,最高位补0,操作如图4-11所示。
SHR指令将影响CF、OF、SF、 ZF、 PF标志位。利用逻
辑右移l位操作可实现将无符号操作数除以2的运算。
例如:SHR BL, 1;将BL中的内容向右移动1位,相当于
(BL) /2。
0
dst
图 4-11
CF
SHR操作示意图
(3)算术左移指令:SAL dst,1/ CL
该指令所执行的操作与逻辑左移指令一样。
(4)算术右移指令:SAR dst,1/ CL
指令功能为:将dst中的二进制位向右移动1位或CL寄存
器中指定的位数。右移一位时,操作数的最低位移出送到
CF中,最高位补符号位。与SHR指令的主要区别是,最高位
保持不变。操作如图4-12所示。
SAR指令将影响ZF、SF、PF、OF、CF标志位。移出的二进
制位送入CF中,其他标志位根据结果进行设置。利用算术
右移1位操作可实现将带符号操作数除以2的运算。
dst
图 4-12
CF
SHR操作示意图
3.循环移位指令
有以下4种循环移位指令,它们均影响CF、 OF、SF、ZF
、PF标志位。
(1)循环左移指令:ROL dst, 1/ CL
指令功能为:将dst中的二进制位向左移动1位或CL寄存器
中指定的位数。左移时,操作数的最高位移出,送到CF中
,同时送至最低位,操作如图4-13所示。
CF
dst
图4-13
ROL操作示意图
(2)循环右移指令:ROR dst, 1/ CL
指令功能为:将dst中的二进制位向右移动1位或CL寄存
器中指定的位数。右移时,操作数的最低位移出送到CF中
,同时送至最高位,操作如图3-14所示。
dst
图4-14
ROR操作示意图
CF
(3)带进位循环左移指令:RCL dst, 1/ CL
指令功能为:将dst中的二进制位向左移动1位或CL寄存器
中指定的位数。左移时,操作数的最高位移出送到CF中,CF
中原有的内容送至最低位,操作如图4-15所示。
CF
dst
图4-15 RCL操作示意图
(4)带进位循环右移指令:RCR dst,1/ CL
指令功能为:将dst中的二进制位向右移动1位或CL寄存器
中指定的位数。右移时,操作数的最低位移出送到CF中,CF
中原有的内容送至最高位,操作如图4-16所示。
dst
图4-16
CF
RCR操作示意图
返回本节
4.3.4 串操作类指令
8086指令系统中设置了串操作指令,其操作对象是内存
中地址连续的字节串或字串。在每次基本操作后,能够自动
修改地址指针,为下一次操作做准备。串操作指令可以加上
重复前缀,此是指令规定的操作将一直重复下去,直到完成
预定的重复此书。串操作指令具有以下几个共同的特点:
用SI寄存器指向源操作数,用DI寄存器指向目的操作数
。源操作数常用在现行的数据段,隐含段寄存器DS,但也允
许超越段前缀。目的操作数总是在现行的附加段,隐含段寄
存器ES,不允许超越段前缀。
每一次操作以后修改地址指针,是增量还是减量取决于
方向标志位DF。当DF=0时,地址指针增量,即字节操作时地
址指针加1,字操作时地址指针加2。当DF=1时,地址指针减
量,即字节操作时地址指针减1,字操作时地址指针减2。
有的串操作指令可加重复前缀REP,则指令规定的操作重
复进行,重复操作的次数由CX寄存器决定。
若串操作指令的基本操作影响标志ZF(如CMPS、SCAS),
则可加重复前缀REPE/REPZ或REPNE/REPNZ,此时操作重复进
行的条件不仅要求(CX)≠0,而且同时要求ZF的值满足重
复前缀中的规定。(REPZ要求(ZF)=1, REPNZ要求(ZF)
=0)。
串操作指令可以写上操作数,也可以只在指令助记符后
加上字母“B”(字节操作)或"W"(字操作)。加上字母“B”
或“W”后,指令助记符后面不允许再写操作数。
1.重复操作前缀
指令格式:REP
Sting Primitive
其中: String Primitive可为MOVS、STOS或LODS串操作
操作功能为:REP前缀使后面的串指令重复执行,其次数预
先送入CX寄存器中,每执行一次串操作指令,CX寄存器中的
内容自动减1,一直重复到(CX)=0,指令才结束。
2. CLD清除方向标志指令
指令格式:CLD
执行操作:该指令使DF=0,在执行串操作指令时,可使地
址自动增量。
3. STD设置方向标志指令
指令格式:STD
执行操作:该指令使DF=1,在执行串操作指令时,可使地
址自动减量。
4.串传送指令
指令格式:MOVS
dst,src
MOVSB
;字节传送
MOVSW
;字传送
该指令的功能为将一个字节或一个字从存储器的某个区域
传送到另一个区域,然后根据方向标志自动修改地址指针。
执行的操作为:
(DI)←((SI));SI所指示单元的内容传送到DI所指
示单元,然后修改地址指针。
字节操作时:SI←(SI)士1, DI ←(DI)士1
字操作时: SI←(SI)士2,DI ←(DI)士2
方向标志位DF=0时,用“+”,方向标志DF=1时,用“-”。
该指令的执行不影响标志位。
MOVS给出源操作数和目的操作数,此时指令执行字节操作
还是字操作,决定于这两个操作数定义时的类型。串传送指
令在执行前,都必须把SI指向源操作数(源串),DI指向目
的操作数(目标串),源串放在数据段,目标串放在附加段,
并将DF置1或清0。
例4.1 在数据段中有一个字符串,其长度为17,要求把它
们传送到附加段中的一个缓冲区中。
Datarea segment
;define data segment
Mess1
db
‘personnal computer $’
Datarea ends
Extra segment
;define extra segment
Mess2
db
17 dup(?)
Extra ends
Code segment
Assume cs:code , ds:datarea, es:extra
Start: mov
ax, datarea
mov
ds, ax
mov ax, extrea
mov
ds, ax
lea si, mess1
lea
di, mess2
mov
cx, 17
cld
rep movsb
mov
ah ,4CH
int 21H
Code ends
End start
5. 存入串指令
指令格式:STOS dst
STOSB
;字节存储
STOSW
;字存储
STOSB,字节存储STOSW;字存储
该指令的功能是将累加器AL或AX的值送存到内存缓冲区的
某个位置上,指令执行的操作为:
字节操作:(DI) ← (AL) ;AL中的内容送到DI所指示的
字节单元
DI← (DI) 士1 ;DI指针加1或减1调整
字操作: (DI) ← (AX) ;AX中的内容送到DI所指示的
字单元
DI←(DI)士2;DI指针加2或减2调整
该指令把AL或AX中的内容存入由DI指示的附加段中的字节
数据或字数据,并根据DF的值及数据类型来修改DI中的内容。
在该指令执行前,要将存入的内容预先放到AL或AX中,并设
置DF、DI初始值。
6. 从串取指令
指令格式: LODS src
LODSB
;从串中取字节
LODSW
;从串中取字
该指令把SI指示的数据段中的字节数据或字数据传送至AL
或AX,并根据DF的值及数据类型来调整SI中的内容。该指令
的执行不影响标志位。执行操作为:
字节操作:AL-((SI))
;SI所指示字节单元内容送至AL
SI←(SI)士1
;SI指针加1或减1调整
字操作: AX-((SI))
;SI所指示字单元内容送至AX
SI←(SI)士2
;SI指针加2或减2调整
LODS指令用来从数据段取出一个字或字节送至AL或AX,一
般不和重复操作前缀指令配合使用。
7.相等/为零时重复操作前缀
指令格式:REPE/REPZ String Primitive
其中:String Primitive可为CMPS、SCAS串操作
操作功能为:当(cx)不等于0且比较相等的情况下,重复执
行串指令,最大的比较次数送入cx寄存器中。
8.不相等/不为零时重复操作前缀.
指令格式:REPNE/REPNZ String Primitive
其中:String Primitive可为CMPS, SCAS串操作
执行操作:当(CX)不等于0且比较不相等的情况下,重复执
行串指令,最大的比较次数送入CX寄存器中。
9. 串比较指令
指令格式:CMPS src, dst
CMPSB
;字节串比较
CMPSW
;字串比较
该指令将(SI)指定的在数据段与(DI)指定的在附加段中两
个字符串中相应的元素逐个进行比较(即相减),但结果不
回送,而反映在状态标志位上。指令的操作为:
字节操作:((SI))-((DI))
;SI所指示单元的内容减去
DI所指示单元内容
(SI) ←(SI)士1
;SI指针加1或减1调整
(DI) ←(DI)士1 ;DI指针加1或减1调整
字操作: ((SI))- ((DI))
;SI所指示单元的内容减去
DI所指示单元内容
(SI) ←(SI)士2
;SI指针加2或减2调整
(DI) ←(DI)士1
;DI指针加2或减2调整。
CMPS和REPE/REPZ或REPNE/REPNZ指令配合使用,可用来判
断两个字符串是否相等。
例4.2 已知在内存中有两个字符串STR1和STR2,比较两个字
符串是否相等,若相等,将FLAG单元置1,否则置0。程序段
如下。
MOV
FLAG, 1
;标志单元首先置1
LEA
SI, STR1
;SI指针指向源字符串首单元
LEA
DI, STR2
;DI指针指向目标字符串首单元
MOV
CX, CN
;字符串长度送CX
CLD
;DF标志位清0
REPZ
CMPSB
;按字节进行比较
JZ
NEXT
;字符串相等,转至NEXT
MOV
FLAG, 0
;否则将FLAG单元清0
NEXT: HLT
10.串搜索指令
指令格式:SCAS
dst
SCASB
;字节串搜索
SCASW
;字串搜索
该指令完成AL或AX中内容与由(DI)指定的在附加段中的一
个字节(或字)进行比较,并不保存结果,只影响状态标志位。
执行的操作为:
字节操作:AL- ((DI))
;AL中的内容减去DI所指
示字节单元内容
(DI) ←(DI)士1
;DI指针加1或减1调整
字操作:
AX- ((DI))
;AX中的内容减去DI所
指示字单元内容
(DI) ←(DI)士2
;DI指针加2或减2调整
在该指令执行前,AL或AX中设置被搜索的内容,DI指向被
搜索的字符串的首单元,并设置DF值。
返回本节
4.3.5 控制转移类指令
一般情况下,程序是按指令顺序地逐条执行的,但实际
上经常需要改变程序的执行流程。控制转移类指令用来改变
程序执行的方向,即修改IP和CS的值。
按转移位置可将转移指令分为段内转移和段间转移。若
指令给出改变IP中内容的信息,转移的目标位置和转移指令
在同一个代码段,则称为段内转移;如指令给出改变IP中内
容的信息,又给出改变CS中内容的信息,转移的目标位置和
转移指令不在同一个代码段,则称为段间转移。
根据转移指令的功能,可分为无条件转移指令、条件转
移指令、循环控制指令、子程序调用和返回指令。
1.JMP无条件转移指令
JMP指令控制处理机转移到指定的位置去执行程序,指令中
必须给出转移位置的目标地址,通常有以下5种形式:
(1)JMP SHORT opr
;段内直接短转移
opr为转移目标地址,可直接使用符号地址(标号)。
SHORT为属性运算符,指示汇编程序将符号地址汇编成8位偏
移量。指令执行时,转移的目标地址为当前的IP值与指令中
给定的8位偏移量之和。
例如:
JMP SHORT hello
┋
hello: MOV AL,3
┋
(2)JMP NEAR PTR opr ;段内直接近转移
其中NEAR PTR为属性运算符,指示汇编程序将符号地址汇
编成16位偏移量。指令执行时,转移的目标地址为当前的IP
值与指令中给定的16位偏移量之和。偏移地址紧跟在指令操
作码之后,由汇编程序计算得出。
(3)JMP WORD PTR
opr
;段内间接转移
有效地址EA的值是由opr的寻址方式决定的。若是寄存器寻
址,指令中直接给出寄存器号,寄存器中的内容送到IP中;
若是存储器寻址,按存储器寻址方式计算出有效地址和物理
地址,用这个物理地址去读取内存中的数据送给IP指针。
例如:JMP WORD PTR[BX]
若给定(IP)=0012H,(BX)=0100H,(DS)=2000H,
(20100H)=80H,(20101H) =00H,目标地址为存储器寻址。
计算EA=(BX)=0100H,PA=DS x 10H+EA=20100H
所以指令执行后(IP) =0080H.
(4)JMP FAR PTR opr ;段间直接转移
执行操作:IP ← opr的偏移地址
CS ← opr所在段的段地址
opr同段内直接寻址方式一样,在书写时可直接使用符号
地址,在汇编时opr所对应的偏移量和所在代码段的段地址
放在操作码之后,需要4个字节的存储单元。这种寻址方式
称为段间直接寻址。
(5)JMP DWORD PTR opr ;段间间接转移
执行操作:IP ← (EA)
CS ← (EA+2)
EA的值是由opr的寻址方式决定的。按寻址方式计算出有效
地址和物理地址,用这个物理地址去读取内存中连续的两个
字数据,其中低位字数据送给IP,高位字送给CS。
例如:JMP DWORD PTR[BX+20H]
若给定(CS)=3000H,(IP)=0012H,(BX)=0100H,(DS)
=2000H,(20120H)=A0H,(20121H)=00H,(20122H)=00H,
(20123H)=50H
计算:EA=(BX)+20H=0120H,PA=DS x10H+EA=20120H
指令执行后:(IP)=00A0H,(CS) =5000H
2.条件转移指令
条件转移指令是根据上一条指令所设置的条件码来测试,
被测试的内容为状态标志位。满足测试条件则转移到指令中
指定的位置去执行,如果不满足条件则顺序执行下一条指令。
所有的条件转移指令格式相同,即JCC opr 。
指令助记符中的“CC“是条件和(YES/NO)的结合。包括三
组:单个条件标志判断(ZF、SF、PF、OF、CF);无符号数
比较大小(Above/Below、Equal);有符号数比较大小
(Greet/Low、Equal)。
opr为指令标号,汇编时计算出8位偏移量放到指令操作
码之后,因此目标地址和转移指令的下一条指令的地址的偏
移范围应为-128+127。
这种指令的执行过程包括:第一步,测试规定的条件;
第二步,如果条件满足,则转移到目的地址;否则,继续顺
序执行。
表4-2
条件转移指令
指令名称
助记符
转移条件
备注
相等/零转移
JE/JZ
不等于/非零转移
JNE/JNZ
负转移
JS
正转移
JNS
偶转移
JP/JPE
奇转移
JNP/JPO
溢出转移
JO
不溢出转移
JNO
进位转移
JC
不进位转移
JNC
低于/不高于等于转移
JB/JNAE
无符号数
高于或等于/不低于转移
JAE/JNB
无符号数
高于/不低于等于转移
JA/JNBE
无符号数
低于或等于/不高于转移
JBE/JNA
无符号数
大于/不小于等于转移
JG/JNLE
有符号数
大于或等于/不小于转移
JGE/JNL
有符号数
小于/不大于等于转移
JL/JNGE
有符号数
小于或等于/不大于转移
JLE/JNG
有符号数
例4.3 己知在内存中有两个无符号字节数据X1和X2,比较两
个数是否相等,若相等,则将RESULT单元置1,否则置0。程
序段如下:
MOV AL, Xl
;将第一个数取出送至AL中
CMP AL,X2
;和第二个数进行比较
JZ
NEXT
;相等则转到NEXT位置执行
MOV RESULT, 0
;否则,将RESULT单元置0
JMP EXIT
;然后转到EXIT位置
NEXT:MOV RESULT, 1
;将RESULT单元置1
EXIT:HLT
例4.4 已知在内存中有两个无符号字节数据NUM1和NUM2,找
出其中的最大数送到MAX单元。程序段如下:
MOV
AL, NUM1
;将第一个数取出送到AL中
CMP
AL, NUM2
;和第二个数进行比较
JA
NEXT
;第一个数大于第二个数别转到
NEXT位置
MOV
AL, NUM2
;否则将第二个数取出送到AL中
JMP
EXIT
NEXT:MOV MAX. AL
;AL中为最大数,送到MAX单元
EXIT:
本题若改为带符号数,则程序段应为:
MOV AL, NUM1
;将第一个数取出送到AL中
CMP AL, NUM2
;和第二个数进行比较
JG
NEXT
;第一个数大于第二个数别转到
NEXT位置
MOV AL, NUM2
;否则将第二个数取出送到AL中
JMP EXIT
NEXT:MOV MAX, AL
;AL中为最大数,送到MAX单元
EXIT:
3.测试CX的值为0转移指令
指令格式:JCXZ opr
测试条件:若(CX)=0 ,则转移到指定位置。
4.循环控制指令
循环控制指令用于控制程序的循环,其控制转向的目的地
址是在以当前IP内容为中心的-128+127的范围内,指令采用
CX作为计数器,每执行一次循环,CX内容减1,直到为0,循
环结束。8086指令系统中主要有三种循环控制语句。
(1) LOOP循环控制指令
指令格式:LOOP opr
执行操作:CX←(CX)-1
若(CX)≠0,则转到指定位置去执行,否则顺序执行。
LOOP语句用在循环次数固定的循环结构中,循环次数送入
CX,语句标号opr为循环体的入口。
(2) LOOPZ(LOOPE)当为零或相等时循环控制指令
指令格式:LOOPZ opr
执行操作:CX←(CX)-1
若(CX)≠0且ZF=1,则转到指定位置去执行,否则顺序执
行。
(3) LOOPNZ(LOOPNE)当不为零或不相等时循环控制指
令
指令格式:LOOPNZ opr
执行操作:CX←(CX)-1
若(CX)≠0且ZF=0,则转到指定位置去执行,否则顺序执
行。
例4.5 在内存中有一个具有N个字节的数据串,首单元地址
为DATABUF,找出第一个不为0的数据的地址送到ADDR单元中。
程序段如下:
;循环初始化
MOV SI, OFFSET DATABUF
MOV CX,N
MOV AL,0
DEC SI
;循环体
LP: INC
SI
;指针增1
CMP
AL, [SI]
;内存中的数据和AL中的内容
比校
LOOPZ LP
;为0且未比较到末尾转LP位
置继续
;循环后处理
JZ
EXIT
;否则判断ZF,若为1,转EXIT
MOV
ADDR,SI
;ZF为0,SI中的内容送ADDR
EXIT: HLT
在这个程序中,循环结束的情况有两种:一种是找到了不
为0的数据,一种是N个数据比较结束后未找到,所以退出循
环后要判断ZF是否为1,ZF等于1说明所有数据都为0,否则
就是找到了不为0的数据,并且由SI指示。
5.子程序调用和返回指令
在程序设计过程中,通常把功能分解为若干个小模块。每
一个小功能模块对应一个过程。在汇编语言中,过程又称为
子程序。程序中可由调用程序(称为主程序)调用这些子程
序,子程序执行完毕后返回主程序继续执行。
子程序调用分为段内和段间调用。
指令格式为:CALL NEAR PTR opr
;段内调用
CALL FAR PTR opr
;段间调用
RET
;子程序返回
其中,opr为子程序名(即子程序第一条指令的符号地址)
例4.6 在主程序中执行一条段内调用语句,语句形式如
下:
MAIN
PROC FAR
MOV AX, DATA
MOV DS, AX
┋
CALL DISPLAY
┋
DISPLAY PROC NEAR
PUSH AX
PUSH BX
┋
RET
;定义主程序
;调用子程序
;调用子程序
;保护现场
;子程序返回
返回本节
4.3.6 处理器控制类指令
这类指令主要通过修改状态标志位,控制CPU的功能。
1.标志位操作指令
(1)进位标志CF设置指令
CLC :对CF位清0
STC: 对CF位置1
(2)方向标志DF设置指令
CLD: 对DF位清0
STD: 对DF位置1
(3)中断允许控制标志IF设置指令
CLI:对IF位清0
STI:对IF位置1
2.其他CPU控制指令
HLT:暂停指令,使程序暂停执行,CPU进入暂停状态,这
时CPU不进行任何操作。当CPU发生复位或发生外部中断时
CPU脱离暂停状态,HLT指令可用于程序中等待中断。该指令
不影响标志位。
WAIT:等待指令,该指令在CPU测试引脚为高电平(无效)
时,使CPU进入等待状态。这时,CPU不做任何操作。当CPU
测试引脚为低电平(有效)时,CPU脱离等待状态,继续执
行WATT指令后面的指令。
NOP:空操作指令,只做空操作,不执行其他任
何操作,占用一个字节存储单元,空耗一个指令执行
周期。该指令常用于程序调试,还可以实现软件延时。
LOCK:封锁指令,这是一个指令前缀,可以加在
任何指令之前,CPU执行该指令时封锁总线。
ESC:交权指令,该指令将处理器的控制权交给协处
理器。通常为了提高系统的浮点运算能力,在8086系
统中可加入浮点运算协处理器8087,若8086CPU发现
是一条浮点指令时,就利用ESC指令将浮点指令交给
8087执行。
返回本节
返回本章
4.4 汇编语言简述
4.4.1 汇编语言语句格式
4.4.2 汇编语言常用伪指令
4.4.3 汇编语言源程序的结构
4.4.4 常用的DOS系统功能调用
4.4.5 汇编语言程序设计
返回本章
4.4.1 汇编语言语句格式
1.汇编语言和汇编程序的基本概念
汇编语言是一种面向CPU指令系统的程序设计语言,它采
用指令助记特来表示操作码和操作数,用符号地址表示操作
数地址。用汇编语言编写的程序能够直接利用硬件系统的特
性,直接对位、字节、字寄存器、存储单元、I/O端口等进行
处理,同时也能直接使用CPU指令系统和指令系统提供的各种
寻址方式编制出高质量的程序,这种程序不但占用内存空间
少,而且执行速度快。
汇编语言编写的源程序(*.ASM)在输入计算机后,它是
不能为机器所识别地,需要将其翻译成用二进制代码表示的
目标程序(*.OBJ),这个翻译过程称为汇编,完成汇编任务的
程序称为汇编程序。汇编程序是将汇编语言源程序翻译成机
器能够识别的目标程序的一种系统程序。
汇编程序除了能够将汇编语言源程序翻译成机器语言程
序这一主要功能外,还能根据用户的要求自动分配存储区域,
包括程序区、数据区、暂存区等;自动把各种进制数转换成
二进制数,把字符转换成ASCII码,计算表达式的值等;自
动对源程序进行检查,给出错误信息,如非法格式、未定义
的助记符、标号、漏掉的操作数等。具有这些功能的汇编程
序称为基本汇编ASM(Assembler)。
在基本汇编的基础上,进一步允许在源程序中把一个指
令序列定义为一条宏指令,并包含大量伪指令的汇编程序,
叫作宏汇编MASM (MacroAssembler )。它包含全部基本汇编
ASM的功能,还增加了宏指令、结构、记录等高级汇编语言
功能。
汇编程序以汇编语言源程序文件作为输入,并由它产生
两种输出文件:目标程序文件和源程序列表文件。目标程序
文件经连接定位后形成可执行文件由计算机执行;源程序列
表文件将列出源程序、目标程序的机器语言代码及符号表。
符号表是汇编程序所提供的一种诊断手段,它包括程序中所
用的所有符号和名字,以及这些符号和名字所指定的地址。
如果程序出错,可以较容易地从这个符号表中检查出错误。
目标文件(*.OBJ)虽然已是二进制文件,但它还不能直
接上机运行,必须经过连接程序(LINK)把目标文件与库文
件或其他目标文件连接在一起形成可执行文件(*.EXE),
这个文件可以由DOS装入存储器,并在机器上运行。
2.汇编语言语句格式
汇编语言的语句可以由1-4部分组成:
[名字] 操作符 [操作数] [;注释]
其中带方括号的部分表示任选项。
如:
NEXT:MOV AL,[SI]
;这是一条指令,
功能是取一个字节
DATA1 DB 0F8H,60H,74H
;这是一条伪指
令,功能是定义数组
(1)名字
名字是一个符号,表示本条语句的符号地址。名字可以是
标号和变量,统称为标识符。它是由字母打头的字符串。在
汇编语言程序中,指令语句的名字叫标号,之后要用冒号
“:”标号在代码段中定义;而伪指令语句中名字叫变量,
之后不要加冒号“:” 标号在除代码段以外的段中中定义。
并非每条指令语句必须有标号,但如果一条指令前面有一个
标号,则程序中其他地方就可以引用这个标号,如:转移、
循环语句中。
标号和变量具备3种属性:段、偏移量、类型
段属性:该属性定义了标号和变量的段起始地址,其值必须
在一个段寄存器中。标号的段是它所出现的对应代码段,由
CS指示。变量的段通常由DS或者FS指示。
偏移属性:该属性表示标号和变量相距段起始地址的字节数,
该数是一个16位无符号数。
类型属性:该属性对于标号而言,用于指出该标号是在本段
内引用还是在其他段中引用。标号的类型有NEAR(段内引用)
和FAR(段间引用)。
对于变量,其类型属性说明变量占几个字节长度,这一属性
由定义变量的伪指令确定。
(2)操作符
操作符可以是机器指令、伪指令和宏指令的助记符。机器
指令是CPU指令系统中的指令,汇编程序将其翻译成对应的机
器码。伪指令则不能翻译成对应的机器码它只是在汇编过程
中完成相应的控制操作,又称为汇编控制指令。宏指令则是
有限的一组指令(机器指令、伪指令)定义的代号, 汇编时
将根据其定义展开成相应的指令。
(3)操作数
操作数是操作符的操作对象。当有两个或两个以上的操作
数时,各操作数之间用逗号隔开。操作数一般有常数、寄存
器、标号、变量和表达式等几种形式。
常数:常数是指令中出现的那些固定值,可以分为数值常数
和字符串常数两类。在8086宏汇编中,数值常数允许有二进
制、八进制、十进制、十六进制常数。在指令中,常数通常
被称为立即数,它只能用作源操作数,不能作为目标操作数。
它的允许取值范围由指令中的目标操作数的形式自动确定为8
位或16位。
寄存器:CPU的寄存器可以作为指令的操作数,8086/8088CPU
寄存器有:
8位寄存器:AH、AL、BH、BL、CH、CL、DH、DL
16位寄存器:AX、BX、CX、DX、SI、DI、BP、SP、DS、
ES、SS、CS
存储器操作数:包括标号和变量。它可以作为源操作数,也
可以作为目标操作数,但不能同时充当源操作数和目标操作
数。标号是可执行的指令性语句的符号地址,它可以作为
JMP、LOOP、CALL指令的转向目标操作数。变量是存储器中
某个数据区的名字,通过变量可访问存储单元中的数据,这
些数据在程序运行期间是可以改变的。变量通过标识符来引
用,可以作为存储器访问指令的源操作数和目标操作数。
表达式:由常数、寄存器、标号、变量与一些运算符相结合
而成,一般有数字表达式和地址表达式两种。数值表达式产
生一个数值结果,只有大小,没有属性。地址表达式的结果
不是一个单纯的数值,而是一个表示存储地址的变量或标号,
它有三种属性:段、偏移量、类型。
(4)注释
以“;”开头的说明部分,是语句的非执行部分,可以根
据需要来写。一般情况下,注释用来说明一段程序或几条语
句的功能,以增加程序的可读性,便于修改和调试。
3.汇编语言的运算符
8086宏汇编有算术运算符、逻辑运算符、关系运算符、分析
运算符和综合运算符共5种,如表4-3所示。
(1)算术运算符用于完成算术运算,其中加、减、乘、除运
算都是整数运算,结果也是整数。除法运算得到的是商的整
数部分。求余运算是指两数整除后所得到的余数。
(2)逻辑运算符的作用是对操作数进行按位操作。逻辑运算
符只用于数值表达式中对数值进行按位逻辑运算,并得到一
个数值结果。其中NOT(非)是单操作数运算符,其他3个逻
辑运算符为双操作数运算符。
(3)关系运算符都是双操作数运算,它的运算对象只能是两
个性质相同的项目。关系运算的结果只能是两种情况:即关
系成立或不成立。当关系成立时,运算结果为1,否则为0。
(4)分析运算符是对存储器地址进行运算的。它可以将存储
器地址的3个重要属性,即段、偏移量和类型分离出来,或取
得它所定义的存储空间的大小,返回到所在的位置做操作数
使用。
分析运算符的相关功能如下:
SEG运算符可以得到一个标号或变量的段基址。例如:
MOV AX,SEG OPER1
OFFSET运算符可以得到一个标号或变量的偏移量。
MOV BX,OFFSET DATA1
TYPE运算符分离出其后变量或标号的类型。如果是变量,
将返回该变量的类型对应字节数;如果是标号,则返回代表
标号类型的数值。它们之间的关系见表4-4所示
表4-4
TYPE返回值与类型的关系
LENGTH运算符放在数组变量的前面,可以求出该数组中所
包含的变量或结构的个数。
SIZE运算符在变量已经用重复操作符DUP加以说明后,可
以得到分配给该变量的、字节总数。如果未用DUP加以说明
,则得到的结果是TYPE运算的结果。
(5)综合运算符可以用来建立和临时改变变量或标号的类型
及存储器操作数的存储单元类型,而忽略当前的属性,所
以又称为属性修改运算符。属性修改运算符的相关功能如
下:
PTR运算符用来指定或修改存储器操作数的类型。例如:
VAR1 DB 30H,40H
VAR2 DW 2050H
MOV
AX,WORD PTR VAR1
MOV BL,BYTE PTR VAR2
在此例中,VAR1为字节变量,对应VAR1存储单元保存的数
据为30H,对应VAR1+l存储单元保存的数据为40H;VAR2为字
变量,对应VAR2存储单元保存的数据为2050H。
利用PTR运算符可以建立新的存储器操作数,这个操作数
与原来的同名操作数具有相同的段和偏移量,但可以有不同
的类型。不过这个新类型只在当前语句中有效。
在前面第一条传送指令中,从字节变量VARl存储单元和
VARl+l存储单元中取出一个字数据,赋给字寄存器AX;在前
面第二条传送指令中从字变量VAR2存储单元中取出一个字节
数据,赋给字节寄存器BL。
则有:(AX)=4030H,(BL)=50H
段属性前缀(又称段超越前缀)运算符“:”跟在段寄存器
名(DS、ES、SS、CS)之后,表示段前缀,用以给一个存储
器操作数指定一个段属性,而不管其原来隐含的段是什么。
例如:
MOV AX,ES:[SI]
SHORT运算符用来修饰JMP指令中跳转地址的属性,指出跳转
地址是在下一条指令地址的-128+127个字节范围之内。
THIS运算符和PTR运算符一样,可以用来建立一个特殊类型
的存储器地址操作数,而不实际为它分配新的存储单元。用
THIS建立的存储器地址操作数的段和偏移量部分与目前所能
分配的下一个存储单元的段和偏移量相同,但类型由THIS指
定。例如:
AREA1 EQU THIS WORD
AREA2 DB 10 DUP(?)
此例中,AREAl和AREA2实际上代表同一个数据区,共有10个
字节,但AREA1的类型为WORD,而AREA2的类型为BYTE。
HIGH和LOW被称为字节分离运算符,它们将一个16位的数或
表达式的高字节和低字节分离出来。
汇编过程中,汇编程序先计算表达式的值,然后再翻译指
令。在计算表达式的值时,如果一个表达式同时具有多个运
算符,则按以下规则运算:
 优先级高的先运算,优先级低的后运算。
 优先级相同时,按表达式中从左到右的顺序运算。
 括号内的表达式应优先计算。
返回本节
4.4.2 汇编语言常用伪指令
通常所说的机器指令是发送给CPU的命令,在运行时由CPU
执行,每条指令对应CPU的一种特定的操作,例如传送、加法、
减法等;而伪指令是发送给汇编程序的命令,在汇编过程中
由汇编程序进行处理,在汇编过程中实现数据定义、分配存
储区、指示程序结束等功能。汇编以后,每条CPU指令产生一
一对应的目标代码;而伪指令则不产生与之相应的目标代码。
下面介绍一些常用的基本伪指令。
1.数据定义伪指令
数据定义伪指令用来定义一个变量的类型,并将所需要的
数据放入指定的存储单元中,也可以只给变量分配存储单元,
而不赋予特定的值。
数据定义伪指令的一般格式为:
[变量名] 助记符 操作数[,操作数…][;注释]
方括号中的变量名为任意选项,它代表所定义的第一个单
元的地址。变量名后面不要跟冒号“:”。助记符后面的操
作数可以不止一个,如果有多个操作数时,相互之间应该用
逗号“,”分开。注释项也是任选的。
助记符说明所用伪操作的助记符,常用的有以下几种:
定义字节变量伪操作DB (Define Byte):用于定义变量
的类型为字节变量BYTE,并给变量分配字节或字节串,DB伪操
作后面的操作数每个占有1个字节。
定义字变量伪操作DW (Define Word):用于定义变量的
类型为字变量WORD, DW伪操作后面的操作数每个占有一个字,
即2个字节。在内存中存放时,低位字节在前,高位字节在后。
定义双字变量伪操作DD(Define Double word):用于
定义变量的类型为双字变量, DD伪操作后面的操作数每个占
有2个字,即4个字节。在内存中存放时,低位字在前,高位
字在后。
定义四字变量伪操作DQ (Define Quadruple word):用于
定义变量的类型为4字变量,DQ伪操作后面的操作数每个占有4
个字,即8个字节。在内存中存放时,低位字在前,高位字在
后。
定义十字节变量伪操作DT (Define Ten b少):用于定义
变量的类型为10个字节, DT伪操作后面的每个操作数占有10
个字节。一般用于存放压缩的BCD码。
数据定义伪指令后面的操作数可以是常数、表达式或字符
串,但每项操作数的值不能超过由伪指令所定义的数据类型限
定的范围。例如,DB伪指令定义数据的类型为字节,则其范围
应该是:无符号数:0一255;带符号数: -128+127。
给变量赋初值时,如果使用字符串,则字符串必须放在单引号
中。另外,超过两个字符的字符串只能用DB伪指令定义。
例4.7 分析数据定义伪指令的使用。
B1 DB 20H,30H
;存入两个字节20H,30H
B2 DB 2*3+5
;存入表达式的值0BH
S1 DB ‘ABCDE’
;存入5个字符,即5个字符相应
的ASCІІ码值
W1 DW 2000H,2030H ;存入两个字2000H,2030H
S2
DB ‘AB’
;存入两个字符的ASCІІ码41H,42H
S3
DW ‘AB’
;存入42H,41H
在数据定义的第1和第2条语句中,分别将常数和表达式的
值赋予一个字节变量。第3句的操作数是包含5个字符的字符
串。在第6、7句,注意伪指令DB, DW的区别,虽然操作数均
为“AB”两个字符,但存入变量的内容各不相同。
除了常数、表达式和字符串外,问号“?”也可以作为数
据定义伪指令的操作数。问号“?”,表示单元无确定的值,
此时仅给变量保留相应的存储单元,而不赋予变量某个确定
的初值。
当同样的操作数重复多次时,可以采用重复操作符“DUP”
来表示。
其使用格式为:n DUP(初值[,初值…]
圆括号中为重复的内容,n为重复次数。如果用“n DUP
(?)”作为数据定义伪指令的惟一操作数,则汇编程序产
生一个相应的数据区,但不赋予任何初始值。此外,重复操
作符“DUP”可以嵌套。
例4.8 分析重复操作符DUP的使用和存储单元的初始化。
BUF1
DB ?
;分配1个字节变量
存储单元,不赋初值
BUF2
DB 5 DUP(0)
;分配5个字节变量
存储单元,赋初值为0
BUF3
DW 8 DUP(?)
;分配8个字变量存
储单元,不赋初值
BUF4
DW 10 DUP(1, 2, ?);分配30个字变量存
储单元,其初始数据为“1, 2, ?”,重复10次
BUF5 DB 20 DUP(2,2DUP(4),6);分配80个字节变
量存储单元,其初始数据为“2, 4, 4,6”,重复20次。
2.符号定义伪指令
符号定义伪指令用于为程序中多次出现的同一个常量或表
达式定义一个标识符,以便在源程序中以标识符来代替对应
的常量或表达式。
常用的符号定义伪指令有EQU、=、LABLE。
(1)等值语句
格式: 符号名
EQU 表达式
EQU伪指令的作用是把该指令右边的数值等价赋值给它左
边的符号。其中,表达式可以是一个常数,一个可以求出常
数的表达式,一个寄存器名或一个指令助记符。
例4.9 分析EQU伪指令的作用。
COUNT1 EQU 20
;COUNT1代替常数
20
COUNT2 EQU
COUNT1+10
;COUNT2代替数值
表达式,此处值为30
C
EQU
CX
;C代替寄存器CX
M
EQU
MOV
;M代替指令助记符
MOV
B
EQU DS:[BP+20] ;代替地址表达式
DS:[BP+20]
VAL
EQU
ASCII-TABLE
;VAL代替变量
ASCII-TABLE
需要注意的是,一个符号一经EQU伪指令赋值后,在整个程
序中,不允许再对同一符号重新赋值。
(2)“=” 伪指令
格式:符号名=表达式
= 伪指令,其功能与EQU相似,很多情况下可以互相代替,
主要区别在于它可以对同一个名字可以重复定义。
例: COUNT
EQU 20
COUNT EQU 20+1 ;错误
但: COUNT=20
COUNT=20+1
;正确
EQU 、 = 伪指令只对程序中某些符号进行等价说明,并
不实际分配存储单元。因此用EQU 、 = 伪指令定义的符号
不占用存储单元。
(3)定义符号名伪指令LABLE
格式:变量名或标号 LABLE 类型
功能:其用途是在原来标号或变量的基础上定义一个类型不
同的新的标号或变量。不实际为它分配新的存储单元。用
LABLE 建立的存储器地址操作数的段和偏移量部分与目前所
能分配的下一个存储单元的段和偏移量相同,通常与数据定
义伪指令连用。
变量的类型可以是BYTE、 WORD、DWORD,标号的类型可以是
NEAR、FAR.。
利用LABLE伪指令可以使同一个数据区兼有BYTE和WORD两种
属性,这样,在以后的程序中可根据不同的需要分别以字节
为单位,或以字为单位存取其中的数据。
例4.10 用LABLE伪指令定义变量。
VALl
LABLE
BYTE
;VAL1是字节型变量
VAL2
DW 20 DUP(?)
;VAL2是字型变量
VALl和VAL2变量的存储地址相同,但类型不同。
例4.11 用LABLE伪指令定义标号。
FNEXT
LABEL
FAR
NNEXT: ADD AX ,BX
FNEXT与紧跟其后的标号NNEXT具有相同的段属性和偏移地
址属性,但类型不同。段内转移用标号NNEXT,当进行段间转
移时用标号FNEXT。
3.段定义伪指令
段定义伪指令指示汇编程序如何按段组织程序和使用存储
器 。
段定义伪指令在汇编语言程序中定义逻辑段,用它来指定
段的名称和范围,并指明段的定位类型、组合类型及类别。
(1)段定义伪指令SEGMENT/ENDS
格式:段名 SEGMENT
[定位类型] [组合类型] [类别]
…(段内语句系列)
段名 ENDS
SEGMENT伪指令用于定义一个逻辑段,给逻辑段赋予一个
段名,并以后面的任选项规定该逻辑段的其他特性。
SEGMENT伪指令位于一个逻辑段的开始,ENDS伪指令则表示
一个逻辑段的结束。这两个伪操作总是成对出现,缺一不可,
两者前面的段名必须一致。
段名是为该段起的名字,用来指出汇编程序为该段分配的
存储区起始位置。
SEGMENT伪指令后面还有三个任选项,三者的顺序必须符合
格式中的规定。这些任选项是给汇编程序和连接程序的命令,
它告诉汇编程序和连接程序,如何确定解决边界,以及如何
组合几个不同的段等。
1)定位类型:定位类型选项告诉汇编程序如何确定逻辑段
的边界在存储器中的位置,用来规定对段起始边界的要求。
有以下4种选择:
BYTE:表示逻辑段从字节的边界开始,即可以从任何地址
开始。此时本段的起始地址紧接在前一个段的后面。
WORD:表示逻辑段从字的边界开始。两个字节为一个字,
此时本段的起始地址最低一位必须是0,即从偶地址开始。
PARA:表示逻辑段从一个节的边界开始。通常16个字节称
为一个节,故本段的起始地址最低4位必须为0,应为xxxx0H。
PAGE:表示逻辑段从页边界开始。通常256个字节称为一页。
故本段的起始地址最低8位必须为0,应为xxx00H.
如果省略定位类型任选项,则默认值为PARA。
2)组合类型:SEGMENT伪指令的第2个任选项是组合类型,
它告诉汇编程序,当装入存储器时各个逻辑段如何进行组合。
共有以下6种选择:
NONE:表示本段与其他逻辑段不发生关系,每段都有自己
的基地址。这是任选项默认的组合类型。
PUBLIC:连接时,对于不同程序模块中的逻辑段,只要具
有相同的类别名,就把这些段顺序连接成为一个逻辑段装入
内存。
STACK:组合类型为STACK时,其含义与PUBLIC基本一样,
即不同程序中的逻辑段,如果类别名相同,则顺序连接成为
一个逻辑段。不过组合类型STACK仅限于作为堆栈区域的逻辑
段使用。
COMMON:连接时,对于不同程序中的逻辑段,如果具有相
同的类别名,则都从同一个地址开始装入,因而各个逻辑段
将发生重叠。最后,连接以后的段的长度等于原来的逻辑段
的长度,重叠部分的内容是最后一个逻辑段的内容。
MEMORY:几个逻辑段连接时,连接程序将把本段定位在被
连接在一起的其他所有段之上,如果被连接的逻辑段中有多
个段的组合类型都是MEMORY,则汇编程序只将首先遇到的段
作为MEMORY段,而其余的段均当作COMMON段来处理。
AT表达式:这种组合类型表示本逻辑段根据表达式求值的
结果定位段基址。例如AT 5800H,表示本段的段基址为5800H,
则本段从存储器的物理地址58000H开始装入。
3)类别:SEGMENT伪指令的第3个任意选项是类别,类别必
须放在单引号内。类别的作用是在连接时决定各逻辑段的装
入顺序。当几个程序模块进行连接时,其中具有相同类别名
的逻辑段,按出现的先后顺序排列,被装入连续的内存区。
没有类别名的逻辑段,与其它无类别名的逻辑段一起连续装
入内存。
(2)段分配伪指令ASSUME
格式: ASSUME 段寄存器:段名 [,段寄存器:段
名…..]
功能:ASSUME伪指令告诉汇编程序,将某一个段寄存器设
置为某一个逻辑段的段址,即明确指出源程序中的逻辑段与
物理段之间的关系,当汇编程序汇编一个逻辑段时,可利用
相应的段寄存器寻址该逻辑段中的指令或数据。
其中:段名为程序中由SEGMENT定义的段之段名;段寄存器
名为CS、DS、ES、SS的一个。
注意:ASSUME伪指令只是定义了哪个段寄存器指向哪一个
段,并没有给段寄存器装入实际的值,所以在操作的时候,
要指令来完成给段寄存器赋初值。一般由ASSUME设定过的段
寄存器都应赋值,但CS寄存器是一个例外,CS的值在程序初
始化时由汇编程序自动给出,因此一般不在程序中赋值,堆
栈段可以利用系统的设置的堆栈。
例4.12 用ASSUME伪指令建立代码段、数据段与CS和DS
的对应关系。
DATA1
SEGMENT
A
DB 1,2,3
DATA1
ENDS
DATA2
SGEMENT
B
DB ‘123ABC’
DATA2
ENDS
CODE
SEGMENT
ASSUME
DS:DATA1,ES:DATA2,CS:CODE
START: MOV
AX,DATA1
MOV
DS,AX
;DATA1→DSh
MOV
AX,DATA2
MOV
ES,AX
;DATA2→ES
┆
CODE
ENDS
END START
4.过程定义伪指令
在程序设计中,经常将一些重复出现的语句组定义为子程
序,又称为过程,可以采用CALL指令来调用。
过程定义伪指令PROC/ENDP
格式:过程名 PROC [NEAR]/FAR
…(语句系列)
RET
…(语句系列)
过程名 ENDP
其中,PROC伪指令定义一个过程,赋予过程一个名字,并
指出该过程的类型属性为NEAR或FAR。如果没有特别指明类型,
则认为过程的类型是NEAR。伪指令ENDP标志过程的结束。上
述两个伪指令前面的过程名必须一致。
当一个程序段被定义为过程后,程序中其他地方就可以用
CALL指令来调用这个过程。调用一个过程的格式为:CALL过
程名。
过程名实质上是过程入口的符号地址,它和标号一样,也有
3种属性:段属性、偏移量属性、类型属性。过程的类型属
性可以是NEAR或者FAR.
一般来说,被定义为过程的程序段中应该有返回指令RET,
但不一定是最后一条指令,也可以有不止一条RET指令。执
行RET指令后,控制返回到原来调用指令的下一条指令。过
程的定义和调用均可以嵌套。
5.结构定义伪指令
结构就是相互关联的一组数据的某种组合形式。使用结构,
需要进行以下几方面的工作:
(1)结构的定义:用伪指令STRUC和ENDS把相关数据定义
语句组合起来,便构成一个完整的结构。
格式:结构名 STRUC
…(数据定义语句序列)
结构名 ENDS
例4.13 用结构制作一张学生成绩表,学生的信息包括姓名、
学号、各门课成绩。
STUDENT STRUC
NAME1
DB ’WANG’
NUMBER
DB ?
ENGLISH
DB ?
MATHS
DB ?
COMPUTER
DB ?
STUDENT ENDS
此例中,STUDENT称为结构名,数据定义语句序列中的变量
名叫作结构字段名。
(2)结构的预置:结构的定义完成以后,就如同在某些高级
语言中完成了某些数据类型的定义,在汇编语言中,结构这
个数据类型是通过结构变量来使用的。
对结构进行预置的格式如下:
结构变量名 结构名 (字段值表)
其中:
结构名是结构定义使用的名字。
结构变量名是程序中具体使用的变量,它与具体的存储空间
及数据相联系,程序中可直接引用它。
字段值表用来给结构变量赋初值,表中各字段的排列顺序及
类型应该与结构定义时一致,各字段之间以逗号分开。
通过结构预置语句,可以对结构中某些字段进行初始化。
但通过预置进行结构变量的初始化有一定的限制和规定:在
结构定义中具有一项数据的字段才能通过预置来代替初始定
义的值,而用DUP定义的字段或一个字段后有多个数据项的
字段,则不能在预置时修改其定义时的值。
例4.14 结构定义中的结构变量初值的预置。
DATA STRUC
A1 DB 20H
;简单元素,可以修改
A2 DB 10H,30H
;多重元素,不能修改
A3 DW ?
;简单元素,可以修改
A4 DB ‘ABCDE'
;可用同长度的字符串修改
A5 DW DUP(?) ;多重元素,不能修改
若有些字段的内容采用定义时的初值,则在预置语句中这
些字段的位置仅写一个逗号即可。若所有的字段都如此,则
仅写一对尖括号即可。
例4.15 对前面定义的STUDENT结构,采用结构变量来代
表学生的信息。设有三个学生,则可有:
S1 STUDENT <’ZHOU’,11,87,90,89>
S2 STUDENT <’WANG’,12,68,83,71>
S3 STUDENT <’CHEN’,13,92,86,95>
这样,就在存储器中为3个学生建立了成绩档案,把他们
的姓名、学号及3门课成绩都放在了指定的位置。
(3)结构的引用:程序中引用结构变量,和其他变量一
样,可直接写结构变量名。若要引用结构变量中的某一字段,
则采用如下形式:
结构变量名·结构字段名
或者,先将结构变量的起始地址的偏移量送到某个地址
寄存器,然后再用:
[地址寄存器]·结构字段名
例如:若要引用结构变量S1中的ENGLISH字段,以下两种
用法都是正确的:
(1)MOV AL, S1·ENGLISH
(2)MOV BX, OFFSET S1
MOV AL,[BX] ·ENGLISH
6.模块定义与连接伪指令
在编写规模较大的汇编语言源程序时,可以将整个程序
划分为几个独立的源程序,称之为模块,然后将各模块分别
进行汇编,生成各自的目标程序,最后将它们连接成为一个
完整的可执行程序。在一个模块中定义的符号可以被另一个
模块引用,通常称这类符号为外部符号,而将那些在一个模
块中定义,只在同一模块中引用的符号称为局部符号。为了
进行模块之间连接和实现相互的符号访问,以便进行变量传
送,通常使用以下几个伪指令:
NAME伪指令:用于给源程序汇编以后得到的目标程序指
定一个模块名,连接时需要使用这个目标程序的模块名。
格式:NAME 模块名
END伪指令:该语句标志整个程序的结束,是源程序的最
后一条语句。即告诉汇编程序汇编工作到此结束。
格式:END [地址表达式]
其中的地址表达式为一定义的标号或标号加、减常数。地
址表达式为可选项,若选用地址表达式,则表示此程序是主程
序,它可以单独执行,其地址表达式为该程序的启动地址,即
程序开始运行的第一条指令的地址,例如,“END START”,
此START是第一条可执行指令的标号;若不选用表达式,则说
明此程序为一子程序,不能单独运行,只能被其它程序调用。
PUBLIC伪指令:说明本模块中的某些符号是公共的,即这
些符号可以提供给将被连接在一起的其他模块使用。
格式:PUBLIC 符号[,…]
EXTRN伪指令:说明本模块中所用的某些符号是外部的,
即这些符号在将被连接在一起的其他模块中定义,在定义这些
符号的模块中还必须用PUBLIC伪指令加以说名。
格式:EXTRN 名字:类型[,…]
7.程序计数器$和ORG伪指令
(1)程序计数器$:汇编程序在汇编过程中,每遇到一个
新的段名,就在段表中添入该段名,同时为该段设置一个初
值为0的汇编地址计数器(也称位置计数器),用来记录正在
汇编的数据或指令的目标代码在当前段内的偏移地址。对申
请分配存储器的语句及产生目标代码的语句,都将其占用的
存储器的字节数累加在该段的汇编地址计数器中。随着汇编
的进行,汇编地址计数器的值不断变化,字符“$“便是表
示汇编地址计数器的当前值,它可以在数值表达式中使用。
例如:
ARRAY DW
4000H,5000H,6000H
COUNT EQU ($ - ARRAY)/ 2
该例中,COUNT为3,即ARRAY所占的字数。
汇编地址计数器的值可以用定位伪指令ORG设置。
(2)定位伪指令ORG
格式:ORG
表达式
功能:将数值表达式的值赋给汇编地址计数器$。
段内存储器的分配是从0开始依次顺序分配的。因此,位置
计数器的值是从0开始递增累计的。但在程序设计中,若需要
将存储单元分配在指定位置,而不是从位置计数器的当前值
开始,便可以使用ORG语句,利用ORG伪指令可以改变位置计
数器的值。例如:
ORG
0100H
;从偏移地址0100H处开始存放
ORG
$+8
;表示跳过8个字节的存储区
返回本节
4.4.3 汇编语言源程序的结构
汇编语言源程序经汇编、连接后,按照生成不同的可执行
程序的形式,可以分为:生成exe可执行程序的源程序格式和
生成COM程序的源程序格式两种。
1.exe程序的汇编语言源程序格式
NAME 模块名(可有可无)
数据段名 SEGMENT
变量定义
数据空间预置
数据段名
ENDS
附加段名 SEGMENT
变量定义
数据空间预置
附加段名
ENDS
堆栈段名
SEGMENT
堆栈段空间预置
堆栈段名 ENDS
代码段名
SEGMENT
ASSUME CS:代码段名,DS:数据段名, ES:附
加段名,SS:堆栈段名
start:指令1
指令2
…
指令n
…
MOV AH, 4CH
INT 21H
代码段名
ENDS
END
start
说明:
①任何一个汇编语言源程序由若干段组成,最多有4个段:
数据段、附加段、堆栈段和代码段,前3个段将根据具体程序
的需要可有可无,但程序至少有一个代码段。各个段的定义
由伪指令SEGMENT和ENDS来完成。
②当有变量定义或预置数据空间时,应在数据段或附加段
中进行定义。
③如果使用堆栈,用户最好自己设置专用的堆栈空间,也
可由系统自行分配堆栈空间。
④代码段中,用ASSUME指令指出各段寄存器与当前被使用
的逻辑段的对应关系,但并不能把段地址装入相应的寄存器
中,所以,在任何程序的开始,都要使用指令给DS、ES赋值:
MOV
AX,数据段名
MOV
DS,AS
;有数据段时,用这两条命令将数
据段首址放入DS
MOV
AX,附加段名
MOV
ES,AX
;有附加段时,用这两条命令将
其段首址放入ES
⑤CS是系统在加载程序后由系统自动置入,不能使用如下
指令:
MOV
AX,代码段名
MOV CS,AX
⑥在DOS环境下,通常采用DOS中断21H的4CH号中断功能调
用使汇编语言返回DOS,即采用如下两条指令:
MOV AH, 4CH
INT 21H
如果不是主模块,这两条指令可以不用。
由于8086的1MB存储空间是分段管理的,汇编语言源程序存
放在存储器中,无论是取指令码还是存取操作数,都要访问
内存。因此,汇编语言源程序的编写必须遵照存储器分段管
理的规定,分段进行编写。
2.Com程序的汇编语言源程序格式
NAME
模块名(可有可无)
段名 SEGMENT
ASSUME
段寄存器地址说明
ORG 100H
Start: JMP
begin
变量定义
数据空间预置
begin: 指令1
指令2
…
指令n
…
MOV AH, 4CH
INT 21H
段名 ENDS
END
start
①Com程序的源程序形式不允许分段(或者说:只有一个
段),程序中用到的数据定义、存储空间预置、堆栈区域以
及程序代码均在仅有的一个段内。
②程序开始运行的起点必须是100H,程序结束使用END。
③为了符合编程的习惯,将变量定义和数据空间预置(相
当于EXE程序格式的数据段)放到了程序的前面并用JMP指令
跳过(设置JMP指令是为了保证程序的入口地址100H)。
3.exe和com程序比较
exe和com程序有如下不同:
exe程序分为1~4个段;com程序不允许分段。
exe程序的入口地址由系统自行安排;com程序的入口地址
必须为100H。
exe程序每个段均可占用64KB的存储空间(段与段也可以重
叠使用),一个程序最多可分配256KB的存储空间;com程
序所占用的总空间不允许超过64KB。
exe程序,用户可以设置堆栈也可以不设置;com程序,用
户不必设置堆栈,在程序装入时,由系统自动把SP建立在
该段之末。
com程序的装入速度比exe程序要快。
com程序可以直接在调试程序debug中用a或e命令建立。
用户在建立com源程序之后,同样经汇编、连接形成exe程
序,然后可以通过exe2bin程序来建立com程序,方法如下:
返回本节
c>exe2bin exe程序名 程序名.com
4.4.4 常用的DOS系统功能调用
对8086系列机来说,MS-DOS操作系统是最主要的操作
系统。MS-DOS操作系统除了具有较为完整的文件管理功能
之外,同时还为各种应用程序、外围设备等提供软件接口。
DOS为用户提供了80多个功能子程序,可供汇编语言设
计时直接调用。这样,程序员就不必编写这些繁杂的程序,
也不必为此去搞清有关的设备、电路及接口等,而只需遵
循DOS规定的调用方法就可以直接调用了。
DOS功能模块调用格式:
MOV AH,功能号
<置相应参数>
INT 21H
可见,调用这些子程序时,应该注意以下几方面的内容:
(1) 子程序的功能号送入AH寄存器
(2) 入口参数
(3) INT 21H
(4)相应中断子程序运行完毕,可按规定取得出口参数。
1.单字符输入并回显(1号调用)
格式:MOV AH,1
INT 21H
功能:等待从标准设备(键盘)输入一个字符,该字符的
ASCII码送AL,并在标准设备(屏幕)显示该字符。
出口参数:AL的内容为输入字符的ASCII码
执行上述指令,系统扫描键盘,等待有键按下,一旦有键
按下,就将键值(相应键符的ASCII码)读入。先检查是否
CTrl + Break,若是,则退出命令执行;否则,将键值送入
AL寄存器,同时回显在屏幕上。从键盘输入字符的ASCII码
送入寄存器AL中,并送显示器显示。
2.单字符显示(2号调用)
格式:MOV DL,待显示字符的ASCII码
MOV AH,2
INT
21H
功能:将DL寄存器中的字符送显示器显示,当DL中的字符
为CTrl + Break时,终止程序的执行。
3.打印字符(5号调用)
格式:MOV
DL , 被打印字符的ASCII码
MOV
AH , 5
INT
21H
功能:将DL中的字符送打印机接口,输出打印。
4.输入字符但不显示(8号调用)
格式:MOV
AH , 8
INT
21H
功能:执行上述指令,系统扫描键盘,等待有键按下,
一旦有键按下,就将键值(相应键符的ASCII码)读入。先
检查是否CTrl + Break,若是,则退出命令执行;否则,将
键盘输入字符的ASCII码送入寄存器AL中。
出口参数:AL的内容为输入字符的ASCII码
5.显示字符串(功能号09)
格式: MOV
DX , 要显示的字符串的首地址
MOV
AH ,09
INT
21H
功能:在标准的输出设备上显示某个字符串
入口参数:DX的内容为要显示的字符串的首地址。
说明:DX的内容是要显示字符串的首地址的偏移量,段
地址由DS提供。要显示的字符串以$为作为结束标志。
例4.16 下面是一个应用9号调用的源程序,分析程序运行
结果:
DATA SEGMENT
STR
DB 0DH,0AH,‘Nice to meet you!
$’
DATA ENDS
CODE SEGMENT
ASSUME
DS:DATA,CS:CODE
BEGIN:MOV AX ,DATA
MOV DS,AX
;将数据段首地址送入DS
LEA
DX,STR
MOV AH ,9
;换行、回车、显示字符串
INT 21H
MOV AH,4CH
;终止当前程序,返回DOS
操作系统
INT
21H
CODE
ENDS
6.字符串输入(10号调用)
格式:LEA DX,缓冲区首偏移地址
MOV AH,10
INT 21H
功能:从键盘上输入一字符串到用户定义的输入缓冲区中。
说明:DX是偏移地址,段首址由DS确定。以$为作为结束标
志。在调用之前,应该在缓冲区的第一个字节设置待接受字
符的个数(范围1~255)。存储区的第二个字节放置键入实际
字符的个数。输入的字符是在第三个字节开始存放的,直遇
到回车符为止,但记入第三单元的字符个数不包括回车符。
若实际输入的字符少于设置的字符,多余的字节置为0;若实
际输入的字符多于设置的字符个数,则多余的被忽略。
调用0AH系统功能调用前,应在数据段调用一个缓冲存储区,
这个缓冲区应该以以下形式定义:
DATA
SEGMENT
BUF
DB 81
DB
?
;定义缓冲区的长度(不能为0)
;用于存储实际输入的字符个
数
DB
81
DUP(?)
;定义具有缓冲区长度的
存储区
┆
DATA
ENDS
7.返回DOS调用(4CH号调用)
格式:MOV AH,4CH
INT 21H
功能:终止当前程序并返回调用程序。
返回本节
4.4.5 语言程序上机过程
1.汇编语言的工作环境
硬件环境:8086汇编语言程序一般多在IBM PC/XT及其兼容
机上运行,因此要求机器具有一些基本配置就可以了,汇编
语言对机器硬件环境没有特殊要求。
软件环境:指支持汇编语言程序运行和帮助建立汇编语言
源程序的一些软件,主要包括以下几个方面:
DOS操作系统:汇编语言程序的建立和运行都是在DOS操
作系统的支持下进行的。操作系统使用DOS 5.0以上版本或
Windows操作系统命令提示符。
编辑程序:编辑程序是用来输入和建立汇编语言源程序
的一种通用的系统软件,通常源程序的修改也是在编辑状态
进行的。编辑程序可用EDIT、UltraEdit、WORD等文本编辑
器。
汇编程序:8086的汇编程序有基本汇编ASM.EXE和宏汇
编MASM.EXE两种。基本汇编不支持宏操作,因此,一般选用
宏汇编MASM.EXEo
连接程序:8086汇编语言使用的连接程序是LINK.EXE。
调试程序:这类程序作为一种辅助工具,帮助编程者进
行程序的调试,通常用动态调试程序DEBUG.COM。
2.汇编语言上机步骤
一般情况下,在计算机上运行汇编语言程序的步骤如图417所示:
编辑
程序
汇编程序
*.AS
M
编辑
程序
*.OB
J
图4-17 汇编语言
上机步骤
连接
程序
*.EX
E
(1)用编辑程序(例如UltraEdit)建立扩展名为.ASM的
汇编语言源程序文件。注意源程序文件必须以.ASM为扩展名
存盘。
(2)用汇编程序(例如MASM.EXE )将汇编语言源程序文
件汇编成用机器码表示的目标程序文件,其扩展名为 .OBJ。
(3)如果在汇编过程中出现语法错误,根据错误的信息提
示(如错误位置、错误类型、错误说明),用编辑软件重新
调入源程序进行修改。没有错误时采用连接程序(例如
LINK.EXE )把目标文件转化成可执行文件,其扩展名为.EXE
。
(4)生成可执行文件后,在DOS命令状态下直接键入文件
名就可以执行该文件。
4.4.5 汇编语言程序设计
程序有三种基本结构,即顺序结构、分支结
构和循环结构。任何复杂的程序都是由基本结
构组成的。因此,一定要掌握基本程序设计的
方法。对于汇编语言程序设计而言,更要掌握
好基本程序的设计,这是编写复杂程序的基础
。
1.顺序结构程序设计
顺序程序是最简单程序设计。顺序程序的最
大特点:程序从运行开始到结尾一直是按顺序
逐条执行指令,且每条指令只执行一次。限于
这种特点,在进行顺序程序设计时应合理安排
指令的先后顺序,以完成相应的功能。
在较为复杂的程序中,顺序程序是逐段出现
,它主要完成一些简单操作或过程的准备、任
务的过渡、结果的存储及程序结束等。它是程
序的基本组成部分。顺序结构可以用图4-17来
表示。
图4-17顺序结
构流程图
例4.17 在字存储单元A、B和C中分别存放着一个
有符号数,求(C-89H+A×B)/A,商和余数分别
存入字存储单元D和E。
分析:直接用有符号数运算指令完成即可。程序
如下:
DATA
A
B
C
D
E
DATA
CODE
SEGMENT
DW
2
DW
-1
DW
1
DW
?
DW
?
ENDS
SEGMENT
ASSUME CS:CODE,DS:DATA
START: MOV
AX,DATA
CODE
MOV
MOV
SUB
CWD
MOV
MOV
MOV
IMUL
ADD
ADC
IDIV
MOV
MOV
MOV
INT
ENDS
END
DS,AX
AX,C
AX,89H
;扩展为双字
CX,DX
BX,AX
AX,A
B
AX,BX
DX,CX
A
D,AX
E,DX
AH,4CH
21H
START
;结果暂存至CX,BX
;求A×B
;与前面结果相加
;AX是商,DX是余数
;保存结果
例4.18 在字节存储单元DAT中有两位十六进制的数据为
6AH,要求将该数据显示到屏幕上。
分析:编程中经常要用到数据显示的问题,通常的做法
是将要显示的数据转换为ASCII码后,再调用相关的中断来
显示。在二、十、十六等常用进制的数据中,最为复杂的是
十六进制数据的显示,查阅ASCII表可知,数据0~9的ASCII
码为该数据加上30H,数据A~F的ASCII码为该数据加上37H。
对于从键盘上输入的数据,由于是以ASCII码形式输入的,
通常转换为数据本身再进行处理,其转换的方法较多,但通
常的方法是上述过程的逆过程,即对30H~39H(0~9的
ASCII码)减去30H,数据41H~46H(A~F的ASCII码)减去
37H。
要实现该十六进制数据的显示,一般是调用DOS或BIOS
提供的中断,而这些中断几乎都要求将要显示的内容转换为
ASCII码。以下用DOS系统功能调用的2号功能完成字符显示。
程序如下:
DATA
SEGMENT
DAT
DB
6AH
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START: MOV AX,DATA
MOV DS,AX
MOV DL,DAT
MOV AL,DL
;暂存至AL寄存器
MOV CL,4
SHR DL,CL
ADD DL,30H
;高位十六进制数据‘6’
转换为ASCII码
MOV
INT
MOV
AND
ADD
转换为ASCII码
INT
MOV
INT
CODE ENDS
END
AH,02H
21H
DL,A
DL,0FH
DL,37H
21H
AH,4CH
21H
START
;显示
;低位十六进制数据‘A’
;显示
2.分支程序设计
分支程序使计算机具备判断能力,计算机可以根据给定的
条件进行判断,并作出相应的处理,它可以把程序分成不同
的处理段,实现情况不同的处理,从而使计算机具有一定的
‘思维’能力。这是顺序程序所能不能实现的。汇编语言的
分支主要由转移指令实现。
分支程序包括单重分支和多重分支。单重分支是最简单的
分支结构(二中选一,也称为二路分支),如果条件成立则
完成某项操作,否则执行其它操作或后续指令。单重分支结
构可以用图4-18来表示。
条件?
No
No
条件?
Yes
Yes
处理 1
处理
(a) 条件不成立执行后续指令
(b)条件不同执行不同操作
图4-18 单重分支流程图
处理 2
在程序设计中,更多的情况是对几个条件同时进行判断从而
确定程序的转移方向(N中选一,也称为多路分支),此时
可以采用数个单重分支的组合来完成,这就是多重分支
图4-19 多重分支流程图
开始
把第一个数→AX
Y
AX>Y?
N
AX←Y
L1
N
L3
AX←Z
AX>Z?
Y
L2
AX→MAX
结束
图4-20 三个数据中找最大值的流程图
例4.19 试编一程序,求三个带符号字数据中的最大值,并
将最大值存入MAX字单元中。
设三个带符号数分别在三个字变量X、Y、Z中存储。
程序流程图如图4-20所示。
程序如下:
STAC SEGMENT STACK
DB 200 DUP(0)
STACK ENDS
DATA SEGMENT
X
DW 00ABH
Y
DW –5
Z
DW 200
MAX
DW ?
DATA ENDS
CODE SEGMENT
ASSUME DS:DATA,SS:STACK,CS:CODE
START: MOV
MOV
MOV
CMP
JG
MOV
L1:
CMP
JG
MOV
EXIT: MOV
MOV
INT
CODE ENDS
END
AX,DATA
DS,AX
AX,X
AX,Y ;AX>Y?
L1
AX,Y
AX,Z ;AX>Z?
EXIT
AX,Z
MAX,AX
AH,4CH
21H
START
例4.20 编写计算下面函数值的程序:
1

Y  0
 1

x0
x0
x0
设输入数据为X、输出数据Y,且皆为字节变量。程序流程
图如图4-21所示。
程序如下:
图4-21 分段函数程序流程图
程序如下:
DATA
SEGMENT
X
DB -10
Y
DB ?
DATA
ENDS
STACK
SEGMENT STACK
DB 200 DUP(0)
STACK
ENDS
CODE
SEGMENT
ASSUME DS:DATA,SS:STACK,CS:CODE
START: MOV
AX,DATA
MOV
DS,AX
CMP
X,0
;与0进行比较
JGE
A1
;X≥0转A1
MOV
Y,-1 ;X <0时,-1→Y
JMP
EXIT
A1:
JG
A2
;X>0转A2
MOV
Y,0
;X=0时,0→Y
JMP
EXIT
A2:
MOV
Y,1
;X>0,1→Y
EXIT:
MOV
AH,4CH
INT
21H
CODE
ENDS
END
START
例4.21 在存储单元DAT中存放着一位十六进制数,将其显示到屏幕上。
分析:本题只需对该位十六进制数进行判断,方法同例4.6相同。程序如下:
DATA
SEGMENT
DAT
DB
0CH
DATA
ENDS
CODE
SEGMENT
ASSUME CS:CODE,DS:DATA
START: MOV
AX,DATA
MOV
DS,AX
MOV
AL,DAT
CMP
AL,9
JG
OTHR
ADD
AL,30H ;小于10则转换为数字(0~9)
JMP
DISP
OTHR:
ADD
AL,37H ;大于10则转换为大写字母(A~F)
DISP:
MOV
DL,AL
MOV
AH,2
INT
21H
DONE:
MOV
AH,4CH
INT
21H
CODE
ENDS
END START
3. 循环程序设计
在进行程序设计时,会出现某段程序反复多次执行的情况,
如果这段程序是连在一起反复执行的,则可用循环程序结构
来实现。其方法是用重复次数或某个条件控制循环程序的执
行。
循环程序是多种多样的,但一般认为循环程序由四部分构
成:
①初始化部分
主要为循环程序作准备工作,如置循环次数、地址指针或
关键字,寄存器置初值及标志位设置等。
②循环工作部分
是实际进行工作的部分,是循环结构的主体,使用循环程
序的目的就是要重复执行这段操作。
③参数调整部分
为保证每次循环的正常执行,相关信息(如计数器的值、
操作数的地址指针等)要发生有规律的变化,为下一次
循环做准备。
以上2、3两部分有时统称为循环处理部分。
④循环控制部分
循环条件的检查,以判断是否继续循环。循环控制部分
是循环程序设计的关键。每个循环程序必须选择一个恰
当的循环控制条件来控制循环的运行和结束。有时循环
次数是已知的,可使用计数器来控制;有时循环次数是
未知的,应根据具体情况设置控制循环结束的条件。
以上2、3、4三部分有时统称为循环体。循环程序根据循环
控制条件的不同,通常又分两种结构,如图4-22所示。
初始化
初始化
No
循环条件满足?
处 理
Yes
处理 2
Yes
循环条件满足?
No
(a) 先工作后判断循环条件
(b) 先判断循环条件后工作
图4-22 常用循环结构
例4.22 求1~400的所有偶数的和,结果保存到RSLT存储单元。
分析:1~400间共有200个偶数,故需要加200次,也就是说循环
次数为200。由于程序中CX寄存器同时充当了循环计数器和200个偶
数值,使每一次循环CX的值减2,故CX初值为400。程序如下:
DATA SEGMENT
RSLT
DW
?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV
AX,DATA
MOV
DS,AX
MOV
CX,400
XOR
AX,AX
NEXT:
ADD
AX,CX
DEC
CX
DEC
CX
JNZ
NEXT
MOV
RSLT,AX
MOV
AH,4CH
INT
21H
CODE ENDS
END
START
;计算偶数和
;200个数未完则继续循环
;保存结果
第二种方法:
DATA
SEGMENT
RSLT DW
?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV AX,DATA
MOV DS,AX
MOV BX,0
MOV CX,200
XOR AX,AX
NEXT:
INC
BX
INC
BX
ADD AX,BX
;计算偶数和
LOOP NEXT ;200个数未完则继续循环
MOV RSLT,AX
;保存结果
MOV AH,4CH
INT
21H
CODE ENDS
END START
例4.23 求10个无符号字节数据的最大值。要求把最大值放
入MAX单元。
分析:求最大值通常采用逐个比较法,即把第一个数做比较
标准,与其后的数比较。如标准数大,则再与下一个数比较;
如果标准数小,则取大数做标准数。由于循环次数已知,故
可采用图4-22(a)的结构。程序流程图如图4-23所示。程
序如下:
设:BUF:数据区首址。
MAX:最大值存放单元的地址
CX:作为循环控制计数器
BX:作为数据地址的指针,即存放的是要用的数据地址
AH:存放临时最大值的寄存器
开始
BX←数据首地址
AH ←(BX)
CX ←循环次数9
BX ←BX+1(形成
下一个数据的地址)
LOP
AH>=[BX]?
AH ←[BX]
Y
N
NEXT
CX←CX-1
CX=0?
N
Y
MAX←AH
结束
图4-23 求最大值流程图
DATA SEGMENT
BUF
DB
12,0ABH,0CDH, 99H,0
DB
1,56H,0CCH, 76H,56H
MAX
DB
?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE, DS:DATA
START: MOV AX,DATA
MOV DS,AX
LEA BX,BUF
MOV AH, [BX]
MOV CX, 9
LOP: INC
BX
;形成第二个数的地址
CMP AH, [BX] ;AH与下一个数比较
JAE NEXT
;AH大于第下一个数
MOV AH, [BX]
NEXT:LOOP LOP
;判断比较完否?
MOV MAX, AH ;送结果
MOV AH,4CH
INT
21H
CODE ENDS
END
START
例4.24 在以DAT为首地址的字缓冲区中存有一批数据,以‘*’
作为结束,编程找出其最大的值,结果存入RSLT单元。
分析:本例与上例的不同在于数据个数未知,但有一个条件
知道,就是该批数据以‘*’结束,故属于条件控制循环,循
环次数未知的结构,即图4-22(b)的结构。程序如下:
DATA SEGMENT
DAT
DW
1234H,0ABH,0ABCDH,9999H,0
DW
1111H,1010H, 1,5678H, 0BBCCH
DW
9876H,5678H, 0FFH,‘*’
RSLT
DW
?
DATA ENDS
CODE SEGMENT
ASSUME CS:CODE,DS:DATA
START:
MOV
AX,DATA
MOV
DS,AX
LEA
SI,DAT
;SI做地址指针
MOV
AX,[SI]
NEXT:
CMP
WORD PTR[SI],‘*’
JZ
SAVE
CMP
AX,[SI]
JGE
GOON
;AX大于下一个数则转GOON
MOV
AX,[SI]
;否则将大值付给AX
GOON:
ADD
SI,2
JMP
NEXT
;循环
SAVE:
MOV
RSLT,AX;保存结果
MOV
AH,4CH
INT
21H
CODE ENDS
END
START
例4.25 试编写一程序,要求比较两个字符串STR1和STR2所
含字符是否相同,若相同则显示‘MATCH!’,若不相同则显
示‘NO MATCH!’。其流程图如图4-24所示。
主程序
子程序
断点
CALL指令
RET指令
图4-25 主程序与子程序的调用关系
图4-24 比较两个字符串流程图
DATA
Str1
COUNT
Str1
mess1
mess2
DATA
CODE
ASSUME
START:
MATCH:
DISP:
CODE
SEGMENT
DB
‘good bye!’
EQU $- Str1
DB
‘good bye!’
DB
‘MATCH!’,13,10,’$’
DB
‘NO MATCH!’,13,10,’$’
ENDS
SEGMENT
CS:CODE,DS:DATA,ES:DATA
MOV
AX,DATA
MOV
DS,AX
MOV
ES,AX
LEA
SI,Str1
LEA
DI,Str2
MOV
CX,COUNT
CLD
REPZ
CMPSB
JZ
MATCH
LEA
DX,mess2
JMP
short DISP
LEA
DX,mess1
END
START
MOV
AH,09
INT
21H
MOV
AH,4CH
INT
21H
ENDS
END
START
4.子程序设计
在程序设计中,常把多处用到的同一个程序段或者具有一
定功能的程序段单独存放在某一存储区域中,需要执行的时
候,使用调用指令转到这段程序来执行,执行完再返回原来
的程序,这个程序段就称为子程序。
调用子程序的程序段称为主程序。主程序中调用指令的下
一条指令的地址称为返回地址,有时也称为断点。
子程序是一个过程,它是由调用程序或主程序用CALL指令
调用的。而子程序的返回是由RET指令实现的。主程序与子
程序的调用关系如图4-25所示。
下面通过一段程序实例,来说明子程序的基本结构和主程
序与子程序之间的调用关系。
例4.26 设计一个子程序,完成统计一组字数据中的正数和0的个数。
DATA SEGMENT
ARR DW -123,456,67,0,-34,-90,89,67,0,256
CN EQU ($-ARR)/2
ZER DW
?
PLUS DW
?
DATA
ENDS
CODE SEGMENT
ASSUME DS:DATA, CS:CODE
START: MOV
AX,DATA
MOV
DS,AX
MOV
SI,OFFSET ARR ;数组首地址送Si
MOV
cx,CN
;数组元素个数送CX
CALL
PZN
;调用近过程PZN
MOV
ZER,BX
;0的个数送ZER
MOV
PLUS,AX
;正数的个数送PLUS
MOV AH,4CH
INT 21H
;返回DOS;
;子程序名:PZN; 子程序功能:统计一组数据中的正数和0的个数
;入口参数:数组首地址在Si中,数组个数在CX中
;出口参数:正数个数在AX中,0的个数在BX中
PZN PROC NEAR
PUSH SI
PUSH DX.
PUSH CX
;保护现场
XOR AX,AX
XOR BX,BX ;计数单元清0
PZN0:MOV DX,[SI] ;取一个数组元素送DX
CMP DX,0 ;DX中内容和0比较
JL
PZN1
;小于0转PZN1
JZ
ZN
;等于0转ZN
INC AX
;否则为正数,AX中内容加1
JMP PZN1
;转PZN1
ZN: INC
BX
;为0,BX中内容加1
PZN1:ADD SI,2
;数组指针加2调整
LOOP PZN0
;循环控制
POP CX
POP DX
POP SI
;恢复现场
RET
;返回主程序
PZN
ENDP
;子程序定义结束
CODE
ENDS
;代码段结束
END START
;汇编结束
从本例可以看出子程序的基本结构包括以下几个部分:
(1)子程序说明:用来说明子程序的名称、功能、入口参
数、出口参数、占用工作单元的情况,明确该子程序的功能
和调用方法。
(2)现场保护及恢复:由于汇编语言所处理的对象主要是
CPU寄存器或内存单元,主程序在调用子程序时已经占用了
一定的寄存器,子程序执行时又要用到这些寄存器,执行完
毕返回主程序后,为了保证主程序按原有的状态继续正常执
行,需要对这些寄存器的内容加以保护,这就是保护现场;
子程序执行完毕后再恢复这些被保护的寄存器的内容,称为
恢复现场。现场保护及恢复通常采用堆栈操作。
(3)子程序体:这一部分内容用来实现相应的子程序功能。
(4)子程序返回:子程序的返回语句RET和主程序中的调用
语句CALL相互对应,才能正确实现子程序的调用和返回。返
回指令用来恢复被中断位置的地址。
返回本节
本章小节
本章围绕8086指令系统和寻址方式讲述了汇编语言的基
本概念及各种指令的使用。8086CPU指令格式有零操作数指令、
单操作数指令和双操作数指令3种;指令中的操作数有立即数
操作数、寄存器操作数、存储器操作数3种类型。
指令通常并不直接给出操作数,而是给出操作的存放地
址。寻找操作数地址的方式称为寻址方式。8086有立即数寻
址、寄存器寻址、直接寻址、寄存器间接寻址、寄存器相对
寻址、基址变址寻址、基址变址相对寻址7种基本寻址方式。
要明确各种寻址方式的区别和特点,重点掌握存储器寻址方
式中的有效地址和物理地址的计算方法。
指令系统是程序设计的基础,Intel 8086CPU的指令系统
按功能分为数据传送类、算术运算类、逻辑运算(位操作)
类、串操作类、控制转移类、处理器控制类6类指令。要熟悉
指令的格式、功能,正确理解和运用各种指令。
汇编语言是面向机器的程序设计语言,它使用指令助
记符、符号地址及标号编制程序,要熟悉汇编语言源程序
的基本格式,正确运用语句格式来书写程序段,掌握伪指
令的功能和应用,掌握常用DOS中断调用,并通过上机操
作,熟悉编辑程序、汇编程序、连接程序和调试程序等软
件工具的使用,掌握源程序的建立、汇编、连接、运行、
调试等技能。
掌握汇编语言程序设计的一般步骤,3种基本的程序
结构的设计方法。顺序结构是按照语句实现的先后次序执
行一系列的操作,是最简单的一种结构。分支结构是程序
设计中常用的结构之一,它有单重分支和多重分支两种形
式。循环结构用来实现需要重复执行的操作,通常由循环
初始化、循环处理部分、循环修改部分和循环控制部分4
部分组成。