指令Instruction
和寻址方式:
Opcode
)和操作数(Operand
)两部分,操作码指出操作的性质,操作数给出操作的对象。寻址方式:
下面主要以 MOV
指令(源操作数)为例来说明8086的寻址方式。8088
的指令与8086
完全兼容,各种寻址方式也完全相同。
操作数直接包含在指令中,它是一个 8
位或 16
位的常数,也叫立即数。
8
位)中或两个连续的存储单元( 16
位)中去。A~F
打头的 16
进制数字出现在指令中时,前面一定要加一个数字 0
。例如:MOV AL, 26H
。将 8
位立即数 26H
送到 AL
寄存器中。
例如,将 FF00H
送到 AX
的指令必须写成:MOV AX,0FF00H
。
例如:MOV CX, 2A50H
。将立即数 2A50H
送到 CX
中,指令的机器码存放及执行过程如图3。
立即数寻址说是寻址,但是和地址没有太大关系。
操作数包含在寄存器中,由指令指定寄存器的名称。
AX、BX、CX、DX、SI、DI、SP、BP
AH、AL、BH、BL、CH、CL、DH、DL
例:MOV DX,AX
。设指令执行前AX = 3A68H,DX=18C7H
。则指令执行后 DX=3A68H
,AX=3A68H
(保持不变)
例:MOV CL, AH
。 将 AH
中的 8
位数据传送到 CL
寄存器。
注意:源操作数的长度必须与目的操作数一致,否则会出错。 例如,MOV CX,AH
是错误的。虽然 CX
放得下 AH
中的 8
位数据,但汇编程序不知道应该将它放入 CH
还是 CL
。
以下几种寻址方式,操作数都放在存储器中,需用不同的方法求出操作数的物理地址,来获得操作数。
操作数的偏移地址也称为有效地址 EA
(Effective Address
)。在直接寻址方式下,存储单元的有效地址直接由指令给出,默认使用的段寄存器为数据段寄存器 DS
。操作数的物理地址 = 16 × D S + E A 16×DS+EA 16×DS+EA
例:MOV AX, [2000H]
。指令中直接给出有效地址 EA
,这里右边 EA=2000H
,必须加 [ ]
,表示不是立即数,而是偏移地址。左边当然还是寄存器寻址。设 DS=3000H
,则源操作数的物理地址=
16 × 3000 H + 2000 H = 32000 H 16×3000H+2000H=32000H 16×3000H+2000H=32000H
因目的操作数是 16
位寄存器 AX
寻址,所以将存储单元中的一个字送进 AX
。若(32000H)=34H
(数据的低位字节的地址),(32001H)=12H
,则执行指令后,AX=1234H
。
例:MOV AL, [2000H]
。假设条件同上例,指令执行后将 32000H
单元中的字节送到 AL
,结果使 AL=34H
。执行过程示意图如下:
段超越前缀:如果要对代码段、堆栈段或附加段寄存器所指出的存储区进行直接寻址,应在指令中指定段超越前缀。例如,数据若放在附加段中,则应在有效地址前加说明符 ES:
,计算物理地址时要用 ES
作基地址,而不是默认值 DS
。
例如:MOV AX, ES:[500H]
。该指令的源操作数的物理地址 =16×ES+500H
。
符号地址:允许用符号地址代替数值地址,也就是给存储单元起一个名字,如 AREA1
,寻址时只要使用其名字,不必记住具体数值。
例如:MOV AX,AREA1
。指令执行后,将从有效地址为 AREA1
的存储单元中取出一个字送到 AX
中去。程序中事先应用说明语句也叫做伪指令来加以说明。例:
AREA1 DW 0867H
...
MOV AX, AREA1
这里的 DW
伪指令语句用来定义变量。MOV
指令执行后将 AREA1
单元中内容送到 AX
,结果 AX=0867H
。比起直接寻址用方括号+数值,我们更推荐使用符号地址。
指令中给出的寄存器中的值不是操作数本身,而是操作数的有效地址 EA
,需要求出地址并根据地址得到操作数。寄存器名称外同样必须加方括号,可用的寄存器有:BX、BP、SI、DI
。应遵守以下约定:
BX
、SI
或 DI
,则默认操作数存放在数据段中,则物理地址=16×DS+BX
;或 =16×DS+SI
;或 =16×DS+DI
。MOV BX, [SI]
。设 DS=1000H
,SI=2000H
,(12000H)=318BH
,则:物理地址 = 16×DS+SI = 10000H+2000H = 12000H
。指令执行后,BX = 318BH
,指令执行过程如图:BP
进行间接寻址,则默认操作数在堆栈段中。例如:MOV AX, [BP]
。操作数的物理地址 =16×SS+BP
。指令中也可以指定段超越前缀。例如:MOV BX,DS: [BP]
源操作数物理地址 =16×DS+BP
;MOV AX,ES: [SI]
源操作数物理地址 16×ES+SI
。它与寄存器间接寻址十分相似,,可用的寄存器有:BX、BP、SI、DI
,但在有效地址上还要加一个 8/16
位的位移量。
例:MOV BX, COUNT[SI]
:
DS=3000H,SI=2000H
,位移量 COUNT=4000H
,(36000H)=5678H
16×DS+SI+COUNT
=30000H+2000H+4000H=36000H
。执行结果 BX=5678H
,执行过程如图。DS * 16 + SI
为数组首地址,后面的 COUNT
为数组偏移量。上述指令也可用 MOV BX, [COUNT+SI]
这种形式来表示。
有效地址是一个基址寄存器( BX
或 BP
)和一个变址寄存器( SI
或 DI
)的内容之和,两个寄存器均由指令指定。
BX
时,代表数据段,段址寄存器用 DS
,则:物理地址 = 16×DS+BX+SI
或 =16×DS+BX+DI
BP
时,代表堆栈段,段址寄存器应使用 SS
,则:物理地址 = 16×SS+BP+SI
或 = 16×SS+BP+DI
例:MOV AX, [BX][SI]
。设 DS=3000H
,BX=1200H
,SI=0500H
, (31700H)=ABCDH
,则:物理地址 = 16×DS+BX+SI
= 30000H+1200H+0500H = 31700H
。执行结果:AX=ABCDH
,指令执行过程如图。
有效地址是基址和变址寄存器的内容,再加上 8/16
位位移量之和。
BX
时,用 DS
作段寄存器,则: 物理地址 = 16×DS+BX+SI+8位或16位位移量
或 = 16×DS+BX+DI+8位或16位位移量
BP
时,应使用 SS
作段寄存器,则: 物理地址 = 16×SS+BP+SI+8位或16位位移量
或 = 16×SS+BP+DI+8位或16位位移量
例:MOV AX,MASK[BX][SI]
DS=2000H
,BX=1500H
,SI=0300H
,MASK=0200H
,(21A00H)=26BFH
16×DS+BX+SI+MASK
= 20000H+1500H+0300H+0200H=21A00H
AX=26BFH
,指令执行过程如图:相对基址变址寻址:涉及操作数的地址时,常使用方括号,带 [ ]
的地址必须遵循下列规则:
[2000H]
。BX、BP、SI、DI
可以出现在 []
内,既可单独出现,也可几个寄存器组合(只能相加),或寄存器与常数相加,但 BX
和 BP
不允许出现在同个[]内,SI
和 DI
也不能同时出现。6[BX][SI] / [BX+6][SI] / [BX+SI+6]
[ ]
内包含BP,则隐含使用 SS
提供基地址,它们的物理地址 = 16×SS+EA
。包含 BP
的操作数有 3 3 3 种形式:
DISP[BP+SI]
;EA=BP+SI+DISPDISP[BP+DI]
;EA=BP+DI+DISPDISP[BP]
;EA=BP+DISP其中,DISP
表示 8 8 8 位或 16 16 16 位位移量,也可以为 0 0 0 。这种情况下,也允许用段超越前缀将 SS
修改为 CS
、DS
或 ES
中的一个,计算物理地址时,应将上式中的 SS
改为相应的段寄存器。其余情况均隐含使用DS提供基地址,它们的物理地址计算方法 = 16×DS+EA
。
这类操作数可以有以下几种形式:
[DISP]
;EA=DISPDISP[BX+SI]
;EA=BX+SI+DISPDISP[BX+DI]
;EA=BX+DI+DISPDISP[BX]
;EA=BX+DISPDISP[SI]
;EA=SI+DISPDISP[DI]
;EA=DI+DISP同样,也可用段超越前缀将式中的DS修改为CS、ES或SS中的一个。
指令中不指明操作数,但具有隐含规定的寻址方式。例如,DAA
它对 AL
中的数据进行十进制调整,结果仍保留在 AL
中。
隐含寻址方式常常用于BIOS和DOS系统调用中。
8086有直接端口和间接端口两种寻址方式:
端口地址由指令直接提供,它是一个 8
位立即数 n= 00 ~ FFH
。例 IN AL, 63H
即 AL
←端口 63H
中的内容。
被寻址的端口号由寄存器 DX
提供,端口号 =0000~ FFFFH
。例:
MOV DX, 213H ; DX = 端口地址号213H
IN AL, DX ; AL ← 端口213H中的内容
上述寻址方式都针对源操作数。目的操作数可用除了立即寻址方式之外的所有寻址方式指定,所以一条指令可以有几种寻址方式。例:MOV [BX], AL
。这里,源操作数为寄存器寻址,目的操作数为寄存器间接寻址方式。
将在本章后面讨论控制转移指令时介绍。
总结一下,上面的七种寻址方式,有几条原则:
[直接寻址 符号地址]
、[BX BP]
、[SI DI]
三组任意搭配。比如说:
[立即数]
或者 符号地址
,就是直接寻址;[BX]
、[BP]
,就是寄存器间接寻址,除了 BP
,寄存器间接寻址都是默认与数据段搭配;[SI]
、[DI]
,也是寄存器间接寻址;直接数[BX], 符号地址[BX]
或者 直接数[BP], 符号地址[BP]
就是寄存器相对寻址;[BX][SI], [BX][DI]
或者 [BP][SI], [BP][DI]
,就是基址变址寻址;直接数[SI], 符号地址[SI]
或者 直接数[DI], 符号地址[DI]
就是寄存器相对寻址;BX
时,段址寄存器用 DS
;如果基址寄存器为 BP
时,段址寄存器应使用 SS
;CS
或 ES
。计算机只能识别二进制表示的机器语言指令,也称为机器码。编程时,一般可不必了解指令的机器码。
若要透彻了解计算机的工作原理,看懂包含机器码的程序清单,对程序进行正确的调试、排错等,就要了解机器语言。
对8086指令进行二进制编码时,可以对每种基本类型给出一个编码格式,对照格式填入不同的数字来表示不同的寻址方式、数据类型等,就能求得每条指令的机器码。
(略)
8086的指令共有六大类:数据传送指令、算术运算指令、逻辑运算和移位指令、字符串处理指令、控制转移指令、处理器控制指令。
本章除详细介绍各类指令外,还将介绍部分伪指令,并给出许多短小的程序设计例子,以便更好理解指令功能。
除 SAHF
和 POPF
指令外,对标志位均没有影响。
指令格式:MOV 目的, 源
指令功能:目的操作数←源操作数
注意:MOV
指令允许数据传送的途径如下图。但 CS
不能做目的操作数。指令中至少要有一项明确说明传送的是字节还是字。
例: 由于 DATA
表示数据段的段址,是一个 16 16 16 位立即数,不能被直接送进 DS
,需要先送进另一个数据寄存器(如 AX
),再传到 DS
中。
MOV AL, 'B' ;AL←将字符B的ASCII码(42H)
MOV AX, DATA ;DATA为直接寻址, 取DATA的内容; AX为寄存器寻址
MOV DS, AX
在汇编语言程序中,数据通常存放在数据段中。下面将举例介绍数据段的基地址、段中各变量的偏移地址、变量定义等概念。例如,下面是某个程序的数据段:
DATA SEGMENT ;数据段开始
AREA1 DB 14H, 3BH ;定义了两个字节define byte
AREA2 DB 3 DUP(0) ;定义了三个字节, 都是重复的0 define byte
ARRAY DW 3100H, 01A6H ;定义了两个字,四个字节
STRING DB 'GOOD' ;定义了一系列字节ASCII码
DATA ENDS ;数据段结束
数据占用存储空间的情况如图:
代码说明如下:
SEGMENT
开始,ENDS
结束, DATA \text{DATA} DATA 是数据段的段名。DB
伪操作符用来定义字节变量。DW
定义字变量,低字节在低位地址,高字节在高位地址。DUP
复制操作符,前面的 3
说明在存储器中保留 3
个字节单元,初值均为 0 0 0 。汇编后, DATA \text{DATA} DATA 被赋予具体的段地址,各变量将自偏移地址 0000H
开始依次存放,各符号地址也被赋予确定的值,等于它们在数据段中的偏移量。
还有一个必须掌握的用法:MOV DX,OFFSET ARRAY
。 这里不是直接寻址!
ARRAY
的偏移地址送到 DX
,其中,OFFSET
为属性操作符,表示应把其后的符号地址的值(而不是内容)作为操作数。ARRAY
的值如上图,则指令执行后,符号地址 ARRAY
的偏移量 0005H
被送到了 DX
中。OFFSET
为 SEG
。例:设 AREA1
和 AREA2
的值如上图,说明以下指令功能:
MOV AL, AREA1 ;AL ← AREA1中的内容 14H
MOV AREA2, AL ;0002H单元 ← 14H
MOV AX, TABLE[BP][DI] ;将地址为16×SS+BP+DI+TABLE的字单元中的内容送进AX
注意,不能直接把一个内存单元中的值直接送到另一个内存单元之中,需要用一个寄存器中转。
指令格式: PUSH 源
指令功能: 将源操作数推入堆栈,源操作数必须是一个字
16
位通用寄存器、段寄存器或存储器中的数据字,但不能是立即数(后来开放了,能够进栈立即数)。PUSH
操作后,使 SP←SP-2
,再把源操作数压入 SP
指示的位置上。指令格式: POP 目的
指令功能:把当前 SP
所指向的一个字送到目的操作数中
16
位通用寄存器、段寄存器或存储单元,但不能是 CS
。SP←SP+2
,SP
向高地址方向移动,指向新的栈顶。例:设 SS=2000H SP=40H AX=25FEH BX=3120H
,依次执行指令:
PUSH BX
PUSH AX
POP BX
指令格式:XCHG 目的, 源
指令功能: 源操作数和目的操作数相交换
注意:交换可以在寄存器之间、寄存器与存储器之间进行,但段寄存器不能作为操作数,也不能直接交换两个存储单元中的内容。
例:设AX=2000H,DS=3000H,BX=1800H,(31A00H)=1995H
,执行指令 XCHG AX,[BX+200H]
:
3000×10H+1800H+200H=31A00H
,其中数据=1995H
AX=1995H,(31A00H)=2000H
指令格式:
XLAT 转换表
:“转换表”为表格首地址XLAT
:“转换表”可省略不写指令功能: 将 1 1 1 个字节从一种代码转换成另一种代码。
XLAT
指令前,应建立一个表格,最多 256
个字节;BX ← 转换表始址
,AL ← 表头地址到要找的某项间的位移量
;AL
中。整个过程见下面的例子。
例:下表是十进制数字 0~9
的 LED
七段码对照表,试用 XLAT
指令求数字 5
的七段码值。
先用 DB
伪指令建 1 1 1 个表格,存放 0~9
的七段码值。表格起始地址为 TABLE
,数字 0~9
的七段码存放在相对于 TABLE
的位移量为 0~9
的单元中。
程序如下:
TABEL DB 40H, 79H, 24H, 30H, 19H ;七段码表格
DB 12H, 02H, 78H, 00H, 18H
...
MOV AL, 5 ;AL ← 数字5的位移量
MOV BX, OFFSET TABLE ;BX ← 表格首地址
XLAT TABLE ;查表得AL=12H
指令格式:
① IN AL, 端口地址
:AL
←从 8
位端口读入 1
字节;或 IN AX, 端口地址
:AX←从 16
位端口的地址读入1
个字。属于直接端口寻址。
② IN AL, DX
:端口地址存放在 DX
中或 IN AX, DX
。属于间接端口寻址。
格式① ,端口地址(00~FFH
)直接包含在 IN
指令里,共允许寻址 256
个端口。当端口地址大于 FFH
时,必须用格式②寻址,即先将端口号送入 DX
,再执行输入操作,DX
允许范围 0000~ FFFFH
。
例:用 IN
指令从输入端口读取数据的例子。
;以A~F打头的16进制数字出现在指令中时,前面一定要加一个数字0
IN AL, 0F1H ; AL←从F1H端口读入1字节
IN AX, 80H ; AL←80H端口内容
; AH←81H端口内容
MOV DX, 310H ; 端口地址310H先送入DX
IN AL, DX ; AL←310H端口内容
例: IN
指令也可以用符号表示地址。例如要求从一个模/数(A/D)转换器中读取 1
字节数字到 AL
中。
ATOD EQU 54H ;A/D转换器端口地址为54H
;EQU类似于C语言的#define预编译指令,不存在54H这一个内存单元
IN AL, ATOD ;将54H端口的内容读入AL中
指令格式:
① OUT 端口地址, AL
:8
位端口← AL
内容;或 OUT 端口地址, AX
:16
位端口 ← AX
内容;
② OUT DX,AL
:DX
=端口地址;或 OUT DX, AX
例:用 OUT
指令对输出端口进行操作的例子。
OUT 85H, AL ;85H端口←AL内容
MOV DX, 0FF4H ;DX指向端口0FF4H
OUT DX, AL ;FF4H端口←AL内容
MOV DX, 300H ;DX指向16位端口
OUT DX, AX ;300H端口←AL内容
;301H端口←AH内容
这是一类专用于传送地址码的指令,可以用来传送操作数的段地址和偏移地址。
指令格式:LEA 目的,源
指令功能: 取源操作数地址的偏移量,送到目的操作数
源操作数必须是存储单元,目的操作数是一个除段寄存器之外的 16
位寄存器。
例 设:SI=1000H,DS=5000H,(51000H)=1234H
,指令执行结果如下:
LEA BX, [SI] ;[SI]的偏移地址为1000H,BX←1000H
MOV BX, [SI] ;偏移地址为1000H单元的内容为1234H ;指令执行后,BX←1234H
;MOV BX, OFFSET [SI] 这样写是语法错误
例 下面两条指令是等价的,它们都取(存储单元) TABLE
的偏移地址,送到 BX
中。 OFFSET
是一个预编译操作,必须在编译的时候要知道 TABLE
的偏移地址。
LEA BX, TABLE
MOV BX, OFFSET TABLE
例 某数组含 20
个元素,每个元素占一个字节,序号为 0~19
。设 DI
指向数组开头处,如要把序号为 6
的元素的偏移地址送到 BX
中,不能直接用 MOV
指令来实现,因为 MOV
的 OFFSET
是一个预编译指令,必须要求后面的偏移地址可以在预编译时确定下来。需要使用下面指令:
LEA BX, 6[DI]
指令格式:LDS 目的, 源
指令功能: 从源操作数指定的存储单元中,取出 1 1 1 个 4
字节地址指针,后两个字节(事先设置好的段地址)送进目的寄存器 DS
,前两个字节(事先设置好的偏移地址)送入指令中指定的目的寄存器中。
源操作数必须是存储单元,目的操作数必须是 16
位寄存器,常用 SI
寄存器,但不能用段寄存器。
例 设:DS=1200H,(12450H)=F346H,(12452H)=0A90H
。执行指令:
LDS SI, [450H]
结果: 存储单元前 2
字节内容为 F346H
,SI←F346H
,后 2
字节内容为 0A90H ,DS←0A90H
。
指令格式:LES 目的, 源
指令功能:与 LDS
指令的操作基本相同,但段寄存器为 ES
,目的操作数常用 DI
。
例 设 DS=0100H,BX=0020H,(01020H)=0300H,(01022H)=0500H
LES DI, [BX]
存储单元前 2
字节内容为 0300H ,DI←0300H
,后 2
字节内容为 0500H ,ES←0500H
。
指令格式:LAHF
指令功能:把标志寄存器(16位)的 SF、ZF、AF、PF
和 CF
传送到 AH
寄存器的相应位。操作示意图如图。
指令格式:SAHF
指令功能:把 AH
内容存入标志寄存器。指令功能与 LAHF
的操作相反。
指令格式:PUSHF
指令功能:把整个标志寄存器(一个字)的内容推入堆栈,并使 SP←SP-2
。
指令格式:POPF
指令功能:把 SP
所指的一个字,传送给标志寄存器 FLAGS
,并使得 SP←SP+2
。
算术运算指令可处理四种类型的数,表示方法见下表:
注意:
8
位或 16
位,如果是带符号数,则用补码表示。 可以写成十六进制。BCD
码十进制数。系统提供加、减、乘、除四种基本运算指令,还有各种调整指令,见表。
指令格式:ADD 目的, 源
指令功能:目的←源+目的
指令格式:ADC 目的,源
指令功能:目的←源+目的+CF
说明:
32
的数相加,就先加低十六位数,然后连同进位参与到高十六位数的相加中。例 列举上述两加法指令的实例,说明其用法。
ADD AL, 18H ;AL←AL+18H
ADC BL, CL ;BL←BL+CL+CF
ADC AX, DX ;AX←AX+DX+CF
ADD AL, COST[BX]
;将AL内容和物理地址=DS:(COST+BX)的存储字节相加,结果送到AL中
ADD COST[BX], BL ;将BL与物理地址=DS:(COST+BX)的存储字节相加,结果留在该存储单元
它们影响标志位:CF、OF、PF、SF、ZF
和AF
。
例 试用加法指令对两个 8
位 16
进制数 5EH
和 3CH
求和,分析指令执行后对标志位的影响。程序如下:
MOV AL, 5EH ;AL=5EH (94)
MOV BL, 3CH ;BL=3CH (60)
ADD AL, BL ;结果AL=9AH
ZF=0
,运算结果非0;AF=1
,低 4
位向高 4
位有进位;CF=0
,D7
位没有向前产生进位;SF=1,D7=1
;PF=1
,结果中有偶数个 1
;OF=1
,由两个数以及它们结果的符号决定,当两个加数符号相同,而结果的符号与之相反时,OF=1
。指令格式:INC 目的
指令功能:目的 ← 目的 + 1
目的操作数可以是通用寄存器或内存(存储单元)。指令执行后影响 AF、OF、PF、SF和ZF
,但进位标志 CF
不受影响。
例 INC指令的例子。
INC BL ;BL寄存器中内容增1
INC CX ;CX寄存器中内容增1
INC BYTE PTR[BX] ;内存字节单元内容增1
INC WORD PTR[BX] ;内存字单元内容增1
指令中只有一个操作数,如果是内存单元,则要用 PTR
操作符说明是字还是字节。
指令格式:AAA
指令功能: 用 ADD
或 ADC
指令对两个非压缩 BCD
数或以 ASCII
码表示的十进制做加法后,结果在 AL
中,用此指令将 AL
中的结果进行调整。另外,若 AF=1
,表示有进位,则进到 AH
中。
例 非压缩十进制数的 9
可表示成 0000 1001
,5
则为 0000 0101
,高 4
位均为 0
。设AH=0
,若 AL= BCD 9,BL= BCD 5
,求两数之和。运算过程:
例 求 ASCII
码表示的数 9(39H)
与 5(35H)
之和。设 AH=0
,则运算过程:
如想把 AX
中的结果表示成 ASCII
码,只要在 AAA
指令后加一条指令:
OR AX, 3030H
就可使 AX
中的结果变成了ASCI码 3134H
。
指令格式:DAA
指令功能:对两个压缩 BCD
数相加后的结果(已在 AL
中)进行调整。
注意:要对 AL
中高半字节和低半字节分别进行调整。
例 若 AL=BCD 38,BL=BCD 15
,求两数之和。运算过程:
例 若 AL=BCD 88,BL=BCD 49
,求两数之和。运算过程:
指令格式:SUB 目的, 源
指令功能:目的 ← 目的 - 源
例:
SUB AX, BX ;AX←AX-BX
SUB DX, 1850H ;DX←DX-1850H
SUB BL, [BX] ;BL中内容减去物理地址=DS:BX处的字节,结果存入BL
指令格式:SBB 目的, 源
指令功能: 目的 ← 目的 - 源 - CF
例:
SBB AL, CL ;AL←AL-CL-CF
SBB主要用于多字节减法中。
指令格式:DEC 目的
指令功能:目的 ← 目的 - 1
例:
DEC BX ;BX←BX-1
DEC WORD PTR[BP] ;堆栈段中位于[BP]偏置处的字减1
;DEC对内存单元进行操作, 用PTR说明目的数大小
指令格式:NEG 目的
指令功能:目的 ← 0 - 目的
例:
NEG AX ;将AX中的数取负(改变数的符号位)
NEG BYTE PTR[BX] ;对数据段中位于[BX]偏置处的字节取负
指令格式:CMP 目的, 源
指令功能:目的 - 源
结果不会送到目的,仅反映在标志位上。
例:
CMP AL, 80H ;AL与80H作比较
CMP BX, DATA1 ;BX与数据段中偏移量为DATA1处的字比较
比较指令主要用在希望比较两个数的大小,而又不破坏原操作数的场合。
指令格式:AAS
指令功能:在用 SUB
或 SBB
指令,对两个非压缩 BCD
数,或以 ASCII
码表示的十进制数相减后,对 AL
中所得结果进行调整,如有借位,则 CF
置 1
。
例 设 AL=BCD 3, CL=BCD 8
,求两数之差。很显然,结果为 BCD 5
,但要向高位借位。运算过程:
指令格式:DAS
指令功能:在用 SUB
或 SBB
指令,对两个压缩 BCD
数相减(结果已存在 AL
)后,进行调整。同样,它也要对 AL
中高半字节和低半字节分别进行调整。
例 设 AL=BCD 56,CL=BCD 98
,求两数之差。运算过程:
补码统一了有符号数和无符号数的加减法,但是乘除法却做不到。
指令格式:MUL 源
指令功能:把源操作数和累加器中的数,都当成无符号数,然后将两数相乘。其中有一个操作数一定是累加器。
1
个字节,则 AX ← AL * 源
1
个字, 则 (DX,AX) ← AX * 源
BYTE
或 WORD
,说明是字节还是字。例:
MUL DL ;AX←AL*DL
MUL CX ;(DX,AX)←AX*CX
MUL BYTE[SI] ;AX←AL*(内存中某字节), BYTE说明字节乘法
MUL WORD[BX] ;(DX,AX)←AX*(内存中某字), WORD说明字乘法
MUL
指令执行后影响 CF
和 OF
标志。如果结果的高半部分不为零,则 CF
、OF
均置 1
。否则,CF
、OF
均清 0
。通过测试这两个标志,可检测并去除结果中的无效前导零。
例:设 AL=55H,BL=14H
,计算它们的积。只要执行下面这条指令:
MUL BL
结果:AX=06A4H
。由于 AH=06H≠0
,高位部分有效,所以置 CF=1
和 OF=1
。
指令格式:IMUL 源
指令功能:把源操作数和累加器中的数,都作为带符号数,进行相乘。
MUL
相同,最后给乘积赋予正确的符号。0
或全 1
,则置 CF=1,OF=1
。 若结果高半部分为全 0
或全 1
,则使 CF=0,OF=0
。这样来决定是否需要保存积的高半部分。例 设 AL=-28,BL=59
,试计算它们的乘积。
IMUL BL ;AX=F98CH=-1652, CF=1, OF=1
指令格式:AAM
指令功能:对存于 AL
的两个非压缩 BCD
数相乘的积进行调整,结果在 AX
中,高位放 AH
,低位在 AL
。两个 ASCII
码数相乘前,应先屏蔽掉每个数字的高半字节。
调整过程: 把 AL
内容除以 10
,商放在 AH
中,余数在 AL
中。即
指令执行后,将影响 ZF、SF和PF
。
例 求两个非压缩十进制数 09
和 06
之乘积。可用如下指令实现:
MOV AL,09H ;置初值
MOV BL,06H
MUL BL ;AL←09与06之乘积36H
AAM ;调整得AH=05H(十位), AL=04H(个位)
最后,可在 AX
中得到正确结果 AX=0504H
,即BCD数 54
。
如果 AL
和 BL
中分别存放 9
和 6
的ASCII码,则求两数之积时要用以下指令实现:
AND AL,0FH ;屏蔽高半字节
AND BL,0FH
MUL BL ;相乘
AAM ;调整
如要将结果转换成ASCII码,可再用指令 OR AX, 3030H
来实现,使 AX=3534H
。
8086/8088
指令系统中,不允许采用压缩十进制数做乘法,乘法调整指令仅此一条。
指令格式:DIV 源
指令功能: 对两个无符号二进制数进行除法操作。
如果源操作数为字节,被除数必须事先放在 AX
中,并且有:( 16 16 16 位除以 8 8 8 位,结果可能是 16 16 16 位,AL放不下,会产生除法错)
AL ← AX/源(字节)的商
AH ← AX/源(字节)的余数
要是被除数只有 8
位,必须把它放在 AL
中,并将 AH
清 0
,然后相除。(8位除以8位)
若源操作数为字,被除数必须放在 DX
和 AX
中,并且有:( 32 32 32 位除以 16 16 16 位,结果可能是 32 32 32 位,AX放不下,会产生除法错)
AX ← (DX,AX)/源(字)的商
DX ← (DX,AX)/源(字)的余数
要是被除数只有 16
位,除数也是 16
位,则必须将 16
位被除数事先放入 AX
,再将 DX
清 0
(无符号扩展),然后相除。(16位除以16位)
与被除数和除数一样,商和余数都是无符号数。
指令格式:IDIV 源
指令功能:功能与 DIV
相同,但操作数都必须是带符号数,商和余数也都是带符号数,而且规定余数的符号和被除数的符号相同。
进行除法操作时,如果商超过了目标寄存器 AL
或 AX
所能存放数的范围,计算机会自动产生除法错中断,相当于执行了除数为 0
的运算,所得的商和余数都不确定。
例 两个无符号数 7A86H
和 04H
相除的商,应为 1EA1H
。若用 DIV
指令进行计算,即
MOV AX, 7A86H
MOV BL, 04H
DIV BL
这时,由于 BL
中的除数 04H
为字节,而被除数为字,商 1EA1H
大于 AL
中能存放的最大无符号数 FFH
,结果将产生除法错误中断。
对于带符号数除法IDIV指令,字节操作时要求被除数为 16
位;字操作时要求被除数为 32
位。如果被除数不满足这个条件,不能简单地将高位置 0
,而应该先用下面的符号扩展指令 (Sign Extension
)将被除数转换成除法指令所要求的格式,再执行除法指令。
指令格式:CBW
指令功能:把 AL
中字节的符号位扩充到 AH
的所有位,这时 AH
被称为是 AL
的符号扩充。
CBW
指令执行后,不影响标志位。
指令格式:CWD
指令功能:把 AX
中字的符号位扩充到 DX
寄存器的所有位中去。
注意,CBW和CWD是针对有符号数除法说的,无符号数除法直接扩充零即可。
例 编程求 -38/3
的商和余数。
MOV AL, 11011010B ;被除数=-38
MOV CH, 00000011B ;除数+3
CBW ;将AL符号扩展到AH中, 使AX=1111 1111 1101 1010B
IDIV CH ;AX/CH 16位除以8位
;AL=1111 0100B = -12 (商)
;AH=1111 1110B = -2(余数)
指令格式:AAD
指令功能:在做除法前把 BCD
码转换成二进制数。
前面介绍的调整指令,都是在用加法、减法和乘法指令后,紧跟着用一条 AAA、AAS或AAM
指令,对运算结果进行调整。而除法的 ASCII
调整指令不同,它是在除法之前进行的。
在把 AX
中的两位非压缩 BCD
数除以一个非压缩 BCD
数之前,先用 AAD
指令,把 AX
中的被除数调整成二进制数,并存入 AL
,然后才能用 DIV
指令进行运算。调整的过程为:
AL←AH×10+AL
AH←00
本指令根据 AL
寄存器的结果影响 SF、ZF和PF
。
例 设 AX
中存有两个非压缩 BCD
数 0307H
,即十进制数 37
,BL
中存有一个非压缩 BCD
数05H
,若要完成 AX/BL
的运算,可用以下指令:
AAD
DIV BL
第1条指令先将 AX
中的两个 BCD
数转换成二进制数,03×10+7=37=25H
,并将 25H→AL
,显然经调整后的被除数 25H
才真正代表 37
,再用 DIV
指令做除法,可得正确的结果:
AL=7(商)
AH=2(余数)
逻辑运算和移位指令,对字节或字操作数进行按位操作,见表3.7。
指令格式:NOT 目的
指令功能:目的←目的取反
目的操作数可以是 8
位或 16
位寄存器或存储器,对存储器操作数要说明类型。
例 NOT
指令只有一个操作数,介绍几种用法。
NOT AX ;AX←AX取反
NOT BL ;BL←BL取反
NOT BYTE PTR[BX] ;对存储器字节单元内容取反后送回该单元
以下为双操作数指令。源操作数可以是 8
或 16
位立即数、寄存器、存储器,目的操作数只能是寄存器或存储器,两个操作数不能同时为存储器。
指令执行后,均将 CF
和 OF
清 0 0 0 ,ZF
、SF
和 PF
反映操作结果,AF
未定义,源操作数不变。
指令格式:AND 目的, 源
指令功能:目的←目的∧源
主要用于使操作数的某些位保留(和“1”相与),而使某些位清除(和“0”相与)。
例 设 AX
中是数字 5
和 8
的 ASCII
码,即 AX=3538H
,将它们转换成 BCD
码,结果仍放回AX
。指令如下:
AND AX, 0F0FH ;AX←0508H, 屏蔽高4位, 截得低4位
指令格式:OR 目的, 源
指令功能:目的←目的∨源
主要用于使操作数的某些位保留(和“0”相或),而使某些位置1(和“1”相或)。
例 设 AX
中存有两个 BCD
数 0508H
,要将它们分别转换成 ASCII码
,结果仍在 AX
中。可用如下指令实现:
OR AX, 3030H ;AX←3538H
指令格式:XOR 目的, 源
指令功能:对两个操作数进行按位逻辑异或运算,结果送回目的操作数,即目的←目的 XOR 源
用于使操作数的某些位保留(和“0”相异或),而使某些位取反(和“1”相异或)。
例 若 AL
中存有某外设端口的状态信息,其中 D1
位控制扬声器发声,要求该位在 0
和 1
之间来回变化,原来是 1
变成 0
,原来是 0
变成 1
,其余各位保留不变。可用以下指令实现:
XOR AL, 00000010B
指令格式:TEST 目的, 源
指令功能:目的∧源,并修改标志位,但不回送结果
它常用在要检测某些条件是否满足,但又不希望改变原有操作数的情况下。
例 设 AL
寄存器中存有报警标志。若 D7=1
,表示温度报警,程序要转到温度报警处理程序T_ALARM
;D6=1
,则转压力报警程序 P_ALARM
。为此,可用 TEST
指令来实现这种功能:
TEST AL, 80H ;查AL的D7=1?
JNZ T_ALARM ;是1(非零),则转温度报警程序
TEST AL, 40H ;D7=0,D6=1?
JNZ P_ALARM ;是1,转压力报警
其中,JNZ
为条件转移指令,表示结果非 0
(ZF=1
)则转移。
可对寄存器或存储器中的字或字节的各位进行算术移位或逻辑移位,移动的次数由指令中的计数值决定,如图3.17。
指令格式:SAL 目的, 计数值
指令格式:SHL 目的, 计数值
指令功能:以上两条指令的功能完全相同。均将目的操作数的各位左移,每移一次,最低位 LSB
补 0 0 0 ,最高位 MSB
进标志位 CF
。移动一次,相当于将目的操作数乘以 2
。
计数值表示移位次数,可以是 1
。若大于 1
,则用 CL
存放,需要事先将次数存入 CL
。移位次数最多为 31
(即 00011111B
)。
MOV AH, 00000110B ;AH=06H
SAL AH, 1 ;将AH内容左移一位后, AH=0CH
MOV CL, 03H ;CL←移位次数3
SHL DI, CL ;将DI内容左移3次
SAL BYTE PTR[BX], 1 ;将内存单元字节左移1位
指令格式:SHR 目的, 计数值
指令功能:使目的操作数各位右移,每移一次,最低位进入 CF
,最高位补 0
。
右移次数由计数值决定,同 SAL/SHL
指令一样。若目的操作数为无符号数,每右移一次,使目的操作数除以2。
例 用右移的方法做除法 133/8=16…5
,即:
MOV AL, 10000101B ;AL=133
MOV CL, 03H ;CL=移位次数
SHR AL, CL ;右移3次,AL=10H,余数5丢失
指令格式:SAR 目的, 计数值
指令功能:每移位一次,最低位进入 CF
,但最高位(即符号位)保持不变,而不是补0。相当于对带符号数进行除2操作。
例 用SAR指令计算 -128/8=-16
的程序段如下:
MOV AL, 10000000B ;AL=-128
MOV CL, 03H ;右移次数为3
SAR AL, CL ;算术右移3次后, AL=0FH=16
算术逻辑移位指令,移出的操作数数位均丢失。循环移位指令则把数位从操作数的一端移到其另一端,从操作数中移走的位不会丢失。
ROL BX, CL ;将BX中的数, 不带进位位左移规定次数
ROR WORD PTR[SI], 1 ;将内存单元的字, 不带进位右移1次
目的操作数可以是 8/16
位的寄存器操作数或内存操作数,计数值含义同上,即 1
或由 CL
指定。
ROL
和 ROR
为小循环移位指令,没有把 CF
包含在循环中;RCL
和 RCR
为大循环指令,把 CF
作为整个循环的一部分参加循环移位。CF
的值由最后一次被移出的值决定。
例 设 CF=1,AL=1011 0100B
,
ROL AL, 1
,则 AL=0110 1001B
,CF=1
,OF=1
;ROR AL, 1
,则 AL=0101 1010B
,CF=0,OF=1
;RCR AL, 1
,则 AL=1101 1010B
,CF=0,OF=0
;MOV CL, 3
和 RCL AL, CL
,则 AL=1010 0110B
,CF=1
,OF
不确定。总的来说,移位指令 shl, sal, shr, sar; rol, ror, rcl, rcr
指令的格式都是一致的,都是 指令 目的, 1 或 CL
。
字符串是指一系列存放在存储器中的字或字节数据。
使用字符串操作指令时,可用指令中的源串和目的串名(即操作数)来表明是字节还是字,也可在指令助记符后加 B
说明是字节,加 W
说明是字操作,每种指令都有3种格式。
5条1字节字符串操作指令见表3.8。
字符串指令执行时,必须遵守以下的隐含约定:
DS:SI
。ES:DI
。SI
和 DI
会自动修改,指向下一待操作单元。DF
标志控制字符串处理的方向:
DF=0
递增。执行一次字节串操作,SI
、DI
各 +1
;字串操作,SI
和 DI
各 +2
;DF=1
递减。执行一次字节串操作,SI
、DI
各 -1
;字串操作,SI
和 DI
各 -2
。STD
指令使 DF=1
,CLD
指令使 DF=0
。CX
=要处理的字符串长度(字节或字数)。为加快串运算指令的执行速度,可在基本指令前加重复前缀,使数据串指令重复执行。每重复执行一次,SI
和 DI
都根据方向标志自动修改,CX
的值则自动减 1
。能与基本指令配合使用的重复前缀有:
REP
无条件重复 (Repeat)REPE/REPZ
相等/结果为零则重复 (Repeat while Equal/Zero)REPNE/REPNZ
不相等/结果非零则重复 (Repeat while Not Equal/Not Zero)指令格式:MOVS 目的串, 源串
指令功能:把源串中的一个字节或字,传送到目的串中且自动修改指针 SI
和 DI
。
利用 MOVS
指令,能很方便地将数据从内存的某一地址(源地址)传送到另一个地址(目的地址),还能自动修改源和目的地址;若使用重复前缀,可用一条指令传送一批数据。
例 要求把数据段中以 SRC_MESS
为偏移地址的一串字符 “HELLO!
”,传送到附加段中以 NEW_LOC
开始的单元中。实现该操作的程序如下:
比较发现,使用有重复前缀 REP
的 MOVSB
指令,程序更简洁。
指令格式:CMPS 目的串, 源串
指令功能:将源串中数据减去目的串数据,但不改变两数据串的原始值,结果反映在标志位上。操作后源串和目的串指针会自动修改。
常用此指令来比较两个串是否相同,并由其后的条件转移指令,根据 CMPS
执行后的标志位值,决定程序的转向。
CMPS
指令前可加重复前缀, 下面每两条指令功能相同:
REPE CMPS
;CX≠0
(未比完)且串相等时重复比较REPZ CMPS
;ZF=1
(两串相等),则重复比较REPNE CMPS
;若CX≠0
(串没有结束)和串不相等重复比较REPNZ CMPS
;ZF=0
,则重复比较例 比较两个字符串,一个是在程序中设定的口令串 PASSWORD
,另一个是从键盘输入的字符串 IN-WORD
,若输入串与口令串相同,程序开始执行。否则程序驱动扬声器发声,警告用户口令不符,拒绝往下执行。这可以用 CMPS
指令来实现,有关程序段如下:
DATA SEGMENT ;数据段
PASSWORD DB '8086 CPUI' ;口令串
IN_WORD DB '8088 CPU' ;从键盘输入的串
COUNT EQU 8 ;串长度
DATA ENDS
CODE SEGMENT ;代码段
ASSUME DS:DATA, ES:DATA
...
LEA SI, PASSWORD ;源串指针
LEA DI, IN_WORD ;目的串指针
MOV CX, COUNT ;串长度
CLD ;地址增
REPZ CMPSB ;CX≠0且串相等时重复比较
JNE SOUND ;若不相等,转发声程序
OK: ... ;比完且相等,往下执行
...
SOUND: ... ;使PC机扬声器发声
... ;并退出
CODE ENDS
指令格式:SCAS 目的串
指令功能:从 AL
(字节操作)或 AX
(字操作)寄存器的内容,减去 ES:DI
为指针的目的串元素,结果反映在标志位上,但不改变源操作数。串操作后目的串指针 DI
会自动修改。
SCAS
指令,可在内存中搜索所需要的数据(关键字)。指令执行前,必须事先将它存在 AL
(字节)或 AX
(字)中。SCAS
指令前也可加重复前缀。例 在某字符串中搜寻字符 A
。若有,搜索次数送到 BX
;若无,将 BX
清 0
。设字符串始址 STRING
的偏址为 0
,字符串长度为 CX
。程序段如下:
MOV DI, OFFSET STRING ;DI=字符串偏移地址
MOV CX, COUNT ;CX=字符串长度
MOV AL, 'A' ;AL=关键字A的ASCII码
CLD ;清方向标志
REPNE SCASB ;CX≠0(没查完)和
;ZF=0(不相等)时重复
JZ FIND ;若ZF=1,已搜到,转出
MOV DI, 0 ;若ZF=0,没搜到,DI←0
FIND:
MOV BX, DI ;BX←搜索次数
HLT ;停机
指令格式:LODS 源串
指令功能:把数据段中以 SI
作为指针的串元素,传送到 AL
(字节操作)或 AX
(字操作)中,同时修改 SI
。
为该指令加重复前缀没有意义。因为每重复传送一次数据,累加器中的内容就被改写,执行重复传送操作后,只能保留最后写入的那个数据。
指令格式:STOS 目的串
指令功能:将累加器 AL
或 AX
中的一个字节或字,传送到以 ES:DI
为目标指针的目的串中,同时修改 DI
,以指向串中的下一个单元。
STOS
指令与 REP
重复前缀连用,即执行指令 REP STOS
,能方便地用累加器中的一个常数,对一个数据串进行初始化。例如,初始化为全 0
的串。
例 数据段中有个数据块, 存有 8
位带符号数,始址BLOCK
,要求将正、负数分开,正数送到附加段中始址为 PLUS_DATA
的缓冲区,负数送到附加段中始址为 MINUS_DATA
的缓冲区。
数据块可看成一个数据串,用 SI
作源串指针,DI
和 BX
作正、负数目的缓冲区指针,CX
控制循环次数。程序段如下:
START:
MOV SI, OFFSET BLOCK ;SI为源串指针
MOV DI, OFFSET PLUS_DATA
;DI为正数目的区指针
MOV BX, OFFSET MINUS_DATA
;BX为负数目的区指针
MOV CX, COUNT ;CX放循环次数
CLD
GOON:
LODS BLOCK ;AL←取源串的一个字节
TEST AL, 80H ;是负数?
JNZ MINUS ;是,转MINUS
STOSB ;非负数,将字节送正数区
JMP AGAIN ;处理下一个字节
MINUS:
XCHG BX, DI ;交换正负数指针
STOSB ;负数送入负数区
XCHG BX, DI ;恢复正负数指针
AGAIN:
DEC CX ;次数减1
JNZ GOON ;未处理完,继续传送
HLT ;已完,停机
程序中,正负数的存储均使用 STOSB
指令,该指令必须以 SI
为源指针,DI
为目的指针。但存储负数时,负数区的目的指针在 BX
中,因此要用 XCHG
指令将 BX
内容送进 DI
,让 DI
指向负数区,同时也把 DI
中的正数区目的指针保护起来。
执行 STOSB
指令后,再用 XCHG
指令将 BX
和 DI
交换回来,以便下次转回 GOON
标号后,LODS
指令仍能正确执行。
指令格式:JMP 目的
指令功能: 无条件地转移到目的地址去执行。
这类指令又分成两种类型:
NEAR
)转移。转移指令的目的地址和 JMP
指令在同一代码段中,转移时仅改变 IP
的内容,段地址 CS
的值不变。FAR
)转移。转移指令的目的地址和 JMP
指令不在同一段中,转移时,CS
和 IP
的值都要改变,程序要转移到另一个代码段去执行。就转移地址提供的方式而言,又可分为两种方式:
16
位寄存器或存储单元中,CPU必须根据寄存器或存储器寻址方式,间接地求出转移地址。同样,这种转移类型又可分为段内间接转移和段间间接转移。所以无条件转移指令可分成段内直接转移 SHORT/NEAR PTR
、段内间接转移 WORD PTR
、段间直接转移 FAR PTR
和段间间接转移 DWORD PTR
四种不同类型和方式,如表所示。
指令格式:
JMP SHORT 标号
立即短转移JMP NEAR PTR 标号
(或JMP 标号
) 立即近转移段内相对转移指令,目的操作数均用标号表示。
IP+8位/16位位移量(DISP)
。-128~+127
字节内,称为短转移,指令中只需要用8位位移量,在标号前加说明符SHORT
。16
位,称为近转移,目的地址与当前IP的距离在 -32768~+32767
字节之间。可加说明符NEAR PTR
,也可省略。这类指令用得最多。例 给出一个含有一条无条件转移指令的简单程序的列表文件,它是由汇编语言源程序经汇编程序翻译后产生的。即
转向的 16
位地址存放在一个 16
位寄存器或字存储器单元中。
用寄存器寻址的段内转移指令,转向的地址存放在寄存器中,执行操作:IP←寄存器内容
。
例:JMP BX
,若指令执行前,BX=4500H
;指令执行时,IP←4500H
,程序转到代码段内偏移地址为 4500H
处执行。
用存储器间接寻址的段内转移指令,先计算出存储单元的物理地址,再从中取一个字送到 IP
。即IP←字存储单元内容。
例:JMP WORD PTR 5[BX]
:WORD PTR
说明是字操作:
DS=2000H, BX=100H, (20105H)=4F0H
IP=(20000H+100H+5H) = (20105H)=4F0H
,转到段内 IP=4F0H
处执行。指令中用远标号直接给出转向的 CS:IP
,程序从一个代码段转到另一个代码段。例:
JMP FAR PTR PROG_F
FAR PTR
说明 PROG_F
为远标号。指令执行的操作:
IP←PROG_F
的段内偏移量CS←PROG_F
所在段的段地址PROG_F
的逻辑地址=3500H:080AH
,则:指令执行后,IP=080AH,CS=3500H
,程序转到 3500:080AH
处执行。操作数为存储器(这里不存在寄存器的使用),要转移的目标地址 CS:IP
存放在存储器中,需要加说明符 DWORD PTR
,表示转向地址需要双字。
例:JMP DWORD PTR [SI+0125H]
,设指令执行前,CS=1200H,IP=05H,DS=2500H,SI=1300H
;内存单元 (26425H)=4500H
,(26427H)=32F0H
,指令中的位移量 DISP=0125H
。执行过程如图:
把某些能完成特定功能又常用的程序段,编写成独立模块,称为过程(Procedure)或子程序(Subroutine
)。在主程序中用 CALL
语句调用这些过程,格式为:CALL 过程名
。
过程以 PROC
开头,ENDP
结束。过程中要安排一条返回指令 RET
,过程执行完后能正确返回主程序。若在过程运行中又调用另一过程,称为过程嵌套。
主程序和过程在同一代码段,称为近调用,不在同一段则称为远调用。过程调用的寻址方式与转移指令类似,但没有段内短调用。由于调用结束后需返回原程序继续运行,要执行保护和恢复返址操作,比转移JMP更复杂。
CALL
指令分两步执行:
CALL
下面指令的地址推入堆栈:
SP←SP-2
,IP
入栈SP←SP-2
,CS
入栈;SP←SP-2
,IP
入栈CALL
指令的目的操作数提供,寻址方法与 JMP
指令类似。RET
指令时,从栈中弹出返址,使程序返回主程序继续执行。也有两种情况:
IP
,并且使 SP←SP+2
IP
,并且使 SP←SP+2
;再弹出 1 1 1 个字→ CS
,并使SP←SP+2
下面举例说明 CALL
和 RET
指令的 4
种寻址方式。
例:CALL PROG-N ;PROG-N是一个近标号
。该指令含 3 3 3 个字节,编码格式为:
设调用前:CS:IP=2000H:1050H,SS:SP=5000H:0100H
,PROG-N
与 CALL
指令之间的字节距离等于 1234H
(即 DISP=1234H
)。则执行CALL指令的过程:
SP←SP-2
,即新的 SP=0100H-2=00FEH
, 返回地址的 IP
入栈。由于存放 CALL
指令的内存首地址为CS:IP=2000:1050H
,该指令占 3
字节,所以返回地址为 2000:1053H
,即 IP=1053H
。于是 1053H
被推入堆栈。IP
值和位移量 DISP
计算出新的 IP
值,作为子程序的入口地址,即 IP = IP+DISP = 1053H+1234H = 2287H
。RET
指令的寻址方式与 CALL
一样,在本例中也是段内直接调用。执行过程如下:
IP←(SP和SP+1)
单元内容 即返址 IP=1053H
从栈中弹出SP←SP+2,SP=00FEH+2=0100H
,即恢复原 SP
结果,返回 CALL
下面的那条指令,即从 2000:1053
处继续执行程序,如上图。
例 下面是两条段内间接调用指令的例子,返址在寄存器或内存中。
CALL BX
CALL WORD PTR[BX+SI]
它们执行的操作分三步,前两步与直接调用相同,第三步不同,具体为:
SP←SP-2
IP
入栈IP←EA
,计算出目的地址的有效地址 EA
,送入 IP
,以此转移。设:DS=1000H, BX=200H, SI=300H, (10500H)=3210H
CALL BX
转移地址在BX中,此调用指令执行后,IP←0200H
,转到段内偏移地址为 0200H
处执行。
CALL WORD PTR[BX+SI]
子程序入口地址在内存字单元中,其值为 (16×DS+BX+SI) = (10000H+0200H+0300H) = (10500H) = 3210H
, 即 EA=3210H
。此指令执行后,IP←3210H
,转到段内偏移地址为3210H
处执行。对应的RET指令执行的操作与段内直接过程的返回指令类似。
例 CALL FAR PTR PROG_F ;PROG_F是一个远标号
。该指令含 5
个字节,编码格式为:
设调用前:CS:IP=1000:205AH
,SS:SP=2500:0050H
,标号 PROG-F
所在单元的地址指针CS:IP=3000:0500H
。
存放CALL指令的内存首址为 1000:205AH
,由于该指令长度为5个字节,所以返回地址应为 1000:205FH
。执行远调用CALL指令的过程如图3.22所示,具体为:
SP←SP-2
即 SP=0050H-2=004EH
CS
入栈 即 CS=1000H
入栈SP←SP-2
即 SP←004CH
IP
入栈 即 IP=205FH
入栈PROG-F
的段地址和偏移地址送 CS:IP
。即 CS←3000H,IP←0500H
。执行子程序 PROG-F
。过程 PROG-F
中的 RET
指令的寻址方式也是段间直接调用,返回时执行的操作为:
SP←SP+2
即 SP←004C+2=004EH
IP
←栈中内容,即 IP←205FH
SP←SP+2
,SP←004EH+2=0050H
CS
←栈中内容,CS←1000H
所以程序转返回地址 CS:IP=1000:205FH
处执行。
操作数必须是存储单元,从该单元开始存放的双字表示过程的入口地址,指令中用 DWORD PTR
说明是对存储单元进行双字操作。
例 CALL DWORD PTR[BX]
DS=1000H,BX=200H,(10200H)=31F4H,(10202)=5200H
。CS:IP
推入堆栈,再转向过程入口。DS×16+BX=10000H+200H=10200H
从中取得的双字就是过程入口地址,即:
IP←(10200H)
即 IP=31F4H
CS←(10202H)
即 CS=5200H
8086还有另一种带参数的返回指令,形式为:
RET n
n
称为弹出值,它让CPU在弹出返回地址后,再从堆栈中弹出 n
个字节的数据,也就是让 SP
再加上 n
。n可以是0000~FFFFH范围内的任何一个偶数。
例如,指令 RET 8
,表示从堆栈中弹出地址后,再使 SP
的值加上 8
。
将上条指令执行后的状态标志,作为测试条件,来决定是否转移。当条件成立,程序转向指令中给出的目的地址去执行;否则,仍顺序执行。
条件转移均为段内短转移,转移指令与目的地址必须在同一代码段中。转移距离范围为 -128~+127
字节。8
位偏移量需用符号扩展法扩展到 16
位后才能与 IP
相加。
在指令中,目的地址均用标号表示,指令格式: 条件操作符 标号
。条件转移指令共18条,归类成直接标志转移和间接标志转移两大类。
助记符中直接给出标志状态测试条件,以 CF、ZF、SF、OF
和 PF
等5个标志的10种状态为判断条件,形成10条指令,如表。有的指令有两种助记符,代表同样的指令,如JZ/JE。
例 求 AL
和 BL
中的两数之和,若有进位,则 AH
置1,否则 AH
清0。程序如下:
ADD AL, BL ;两数相加
JC NEXT ;若有进位,转NEXT
MOV AH, 0 ;无进位,AH清0
JMP EXIT ;往下执行
NEXT:
MOV AH, 1 ;有进位,AH置1
EXIT: … ;程序继续进行
不在指令助记符中直接给出标志状态位的测试条件,但仍以某一个或几个标志的状态组合为测试条件,若条件成立则转移,否则顺序往下执行。
间接标志转移指令共有8条,列于表中。每条指令都有两种不同的助记符。在无符号数比较测试指令中,指令助记符中的“A”是英文Above的缩写,表示“高于”之意,“B”是英文Below的缩写,表示“低于”之意。
例 设 AL=F0H,BL=35H
,执行指令
CMP AL, BL ;AL-BL
JAE NEXT ;AL大于等于BL,则转到NEXT
JAE/JNB
根据 CF
标志是否为0决定转移。若 CF=0
,即无进位,则转移,这与直接标志转移指令中的 JNC
功能完全一样。同样,JB/JNAE
与 JC
指令的功能相同。
带符号数进行比较时,不能仅根据 SF
或 OF
标志来判定,而要将它们组合起来考虑。指令助记符中,“G”(Great than)表示“大于”,“L”(Less than)表示“小于”。
例 设某学生的英语成绩已存放在 AL
中,如低于 60
分,打印F(FAIL);高于或等于 85
分,打印G(GOOD);在 60~84
分之间,打印P(PASS)。程序为
CMP AL, 60 ;与60分比较
JB FAIL ;<60,转FAIL
CMP AL, 85 ;≥60,与85分比较
JAE GOOD ;≥85,转GOOD
MOV AL, 'P' ;其它,将AL←'P'
JMP PRINT ;转打印程序
FAIL: MOV AL, 'F' ;AL←'F'
JMP PRINT ;转打印程序
GOOD: MOV AL, 'G' ;AL←'G'
PRINT: ... ;打印存在AL中的字符
例:设某温度控制系统中,从温度传感器输入一个8位二进制摄氏温度值。当温度低于100℃时,打开加热器;温度升到100℃或以上时,关闭加热器。温度传感器端口号为 320H
,控制加热器的输出信号连到端口 321H
的最低有效位,当它置 1
加热器打开,清 0
则关闭。实现上述温度控制的程序:
GET-TEM:
MOV DX, 320H ;DX指向温度输入端口
IN AL, DX ;读取温度值
CMP AL, 100 ;与100 ℃比较
JB HEAT_ON ;<100 ℃,加热
JMP HEAT_OFF ;≥100 ℃,停止加热
HEAT-ON:
MOV AL, 01H ;D0位置1,加热
MOV DX, 321H ;加热器口地址
OUT DX, AL ;打开加热器
JMP GET_TEMP ;继续检测温度
HEAT-OFF:
MOV AL, 00 ;D0位置0,停止加热
MOV DX, 321H
OUT DX, AL ;关闭加热器
... ;进行其它处理
例 在首地址为 TABLE
的10个内存字节单元中, 存放了10个带符号数,统计其中正数、负数和零的个数,结果存入PLUS、NEGT和ZERO单元。
TABLE DB 01H, 80H, 0F5H, 32H, 86H
DB 74H, 49H, 0AFH, 25H, 40H
PLUS DB 0 ;存正数个数
NEGT DB 0 ;存负数个数
ZERO DB 0 ;存0的个数
...
MOV CX, 10 ;数据总数
MOV BX, 0 ;BX清0
AGAIN:
CMP TABLE[BX], 0 ;取一个数与0比
JGE GRET_EQ ;≥0,转GRET_EQ
INC NEGT ;<0,负数个数加1
JMP NEXT ;往下执行
GRET-EQ:
JG P-INC ;>0,转P-INC
INC ZERO ;=0,零个数加1
JMP NEXT ;往下执行
P-INC:
INC PLUS ;正数个数加1
NEXT:
INC BX ;数据地址指针加1
DEC CX ;数据计数器减1
JNZ AGAIN ;未完,继续统计
是一组增强型条件转移指令,用来控制程序段的重复执行,重复次数由 CX
中的内容决定,转移目标都是短标号,偏移量都是负值,即只能向前转移。均不影响任何标志。
指令格式:LOOP 短标号
指令功能:重复执行一系列指令。重复次数放在 CX
中,执行一次指令,CX-1
。如减1后 CX≠0
,转到指令给定的标号处继续循环;CX=0
,结束循环,转去执行 LOOP
指令后的那条指令。
一条 LOOP
指令相当于执行以下两条指令:
DEC CX
JNZ 标号
例 商店里有8种商品,价格为 83元,76元,65元,84元,71元,49元,62元和58元
,要将每种商品提价7元,编程计算每种商品提价后的价格。先将商品原价按 BCD
码形式,依次存放在以 OLD
开始的8个存储单元中,新价格存放进以 NEW
开始的8个单元,然后用 LOOP
指令来实现8次循环。即
OLD DB 83H, 76H, 65H, 84H
DB 71H, 49H, 62H, 58H
NEW DB 8 DUP(?)
...
MOV CX, 08H ;共8种商品
MOV BX, 00H ;BX作指针,初值为0
NEXT:
MOV AL, OLD[BX] ;读入一个商品的原价
ADD AL, 7 ;加上提价因子
DAA ;调整为十进制数
MOV NEW[BX], AL ;存放结果
INC BX ;地址指针加1
LOOP NEXT ;未加满8次,继续循环
... ;已加完8次
循环操作也可以只含一条指令,即 LOOP
指令自身,这样的程序段常用来实现延时。例如:
MOV CX, 10 ;循环次数为10
DELAY:
LOOP DELAY ;本指令重复执行10次
例 用循环和跳转指令,编写控制PC机扬声器发声的程序。在PC机中,61H
口的 D1
和 D0
位接到扬声器接口电路上在 D0=0
的情况下,当 D1=1
时,扬声器被接通;当 D1=0
时,则断开。通过控制 D1
位的值,就能产生一个由1和0构成的二进制序列,使扬声器发声。61H
口中的其它位则用来控制PC机的内部开关状态、奇偶校验及键盘状态等。要将这些状态保存起来。
控制扬声器发声程序:
IN AL, 61H ;AL←从61H口读取数据
AND AL, 0FCH ;保护D7~D2位,D0位清0
MORE:
XOR AL, 02 ;触发D1位使之在0和1间变化
OUT 61H, AL ;控制扬声器开关通断
MOV CX,260 ;CX=循环次数
DELAY:
LOOP DELAY ;循环延时
JMP MORE ;再次触发
本例中,LOOP
指令重复执行 260
遍,起延时作用,使开关通断维持一定时间。否则开关动作太快,发出的声音频率太高,人耳听不出来。
指令格式:LOOPE 标号
或 LOOPZ 标号
指令功能:LOOPE
是相等时循环,LOOPZ
是结果为0时循环。
它们能完成相同功能,具有不同助记符,用来控制重复执行一组指令。
指令执行前,先将重复次数送入 CX
,每执行一次 CX-1
,若 -1
后 CX≠0
和 ZF=1
,则转到指令指定的标号处重复执行;若 CX=0
或 ZF=0
,便退出循环,执行本指令后的那条指令。
例 设1个50字节组成的数组存在 ARRAY
开始的内存中,测试数组中的元素,若为 0
,且不是最后 1
个,便继续进行下个元素的测试,直到找到第一个非零元素或查完了为止。
ARRAY DB ××, ××, ... ;含50个元素的数组
MOV BX, OFFSET ARRAY ;BX指向数组开始单元
DEC BX ;指针-1
MOV CX, 50 ;CX=元素个数
NEXT:
INC BX ;指向数组的下个元素
CMP [BX], 00H ;数组元素与0比较
LOOPE NEXT ;若元素为0和CX≠0,循环
... ;否则,结束查找
指令格式:LOOPNE 标号
或 LOOPNZ 标号
指令功能:LOOPNE
是不相等循环,而 LOOPNZ
是结果 ZF≠1
循环,它们也是一对功能相同但形式不一样的指令。
指令执行前,应将重复次数送入 CX
,每执行一次,CX
自动 -1
,若 -1
后 CX≠0
和 ZF=0
,则转移到标号所指定的地址重复执行;若 CX=0
或 ZF=1
,则退出循环,顺序执行下一条指令。
例 设一个由 17
个字符组成的字符串存放在以 STRING
开始的内存中,查找字符串中是否包含空格符。若没有找到空格符和尚未查完,则继续查找,直到找到第一个空格符或查完了, 才退出循环。
STRING DB 'Personal Computer' ;字符串
...
MOV BX, OFFSET STRING ;BX指向字符串始址
DEC BX ;BX-1
MOV CX, 17 ;CX=字符串长度
NEXT:
INC BX ;指向下一个字符
CMP [BX], 20H ;字符串元素与空格比较
LOOPNE NEXT ;若不是空格和CX≠0,循环
... ;找到空格或CX已为0
指令格式:JCXZ 标号
指令功能:若 CX
寄存器为 0
,则转移到指令中标号所指定的地址处,否则将往下顺序执行。
它不对 CX
寄存器进行自动减 1
操作。
这条指令用在循环程序开始处。为了使程序跳过循环,只要事先把 CX
寄存器清 0
。
计算机在执行正常程序过程中,由于某些事件发生,需要暂时中止当前程序的运行,转到中断服务程序去为临时发生的事件服务。中断服务程序执行完,又返回正常程序继续运行。此过程称为中断。
8086的中断有两种:
NMI
或可屏蔽中断引脚 INTR
引入。INT n
,利用它直接产生8086的内部中断。软件中断指令,n
为中断类型号,范围 0~255
。可安排在程序的任何位置上。
当带符号数进行算术运算后,若 OF=1
,则可由 INTO
指令产生类型为 4
的中断; 若 OF=0
,则 INTO
指令不产生中断。
为此,在带符号数进行加减法运算之后,必须安排一条 INTO
指令,一旦溢出就能及时向CPU提出中断请求,CPU可做出相应的处理。
中断返回指令IRET。被安排在中断服务程序的出口处,指令执行后,从堆栈中依次弹出程序断点和 FLAGS
的内容,使CPU继续执行原来被打断的程序。
8086提供一组标志操作指令,可直接对 CF
,DF
和 IF
标志位进行设置或清除等操作,但不包含TF标志,如下表。
利用 CLC
指令,使进位标志 CF
清 0
,CMC
指令使 CF
取反,STC
指令则使 CF
置 1
。
方向标志 DF
在执行字符串操作指令时用来决定地址的修改方向,CLD
指令使 DF
清 0
(递增),而 STD
指令则使 DF
置 1
(递减) 。
中断允许标志 IF
决定CPU能否响应可屏蔽中断请求,指令 CLI
使 IF
清 0
,禁止CPU响应这类中断。STI
使 IF
置 1
,允许CPU响应。
指令格式:ESC 外部操作码, 源操作数
指令功能:换码指令实现8086对8087协处理器的控制。用于浮点数的运算。
通常跟在 ESC
指令之后。ESC
指令执行后,8086 CPU处于等待状态,不断检测 TEST
引脚,若为高电平,则重复执行 WAIT
指令,处理器处于等待状态;如变为低电平,便退出等待状态,执行下条指令。
可加在任何指令的前面,用于多处理器的环境。凡带有 LOCK
前缀的指令在执行过程中,将禁止其它处理器使用总线。
使CPU进入暂停状态,当下列情况之一发生时,则脱离暂停状态:
RESET
线上加复位信号;NMI
引脚上出现中断请求信号;INTR
引脚上出现中断请求信号。HLT
指令来等待中断的出现。单字节指令,执行时耗费 3
个时钟周期的时间,但不完成任何操作。