第3章 8086/8088指令系统
3.1 复习笔记
8086/8088CPU的指令系统共包含92种基本指令,按照功能可将它们分为六大类:数据传送类、算术运算类、逻辑运算和移位、串操作、控制转移类和处理器控制。
一、指令的基本构成
1指令的一般格式
一条指令通常由两个部分组成,如图3-1所示。第一部分为操作码(或称指令码),用于指出指令要进行何种操作。另一部分是指令操作的对象,称为操作数。
图3-1 指令格式
8086指令的长度在1~7个字节之间。操作码占用一个字节或两个字节。指令的长度主要决定于操作数的个数及所采用的寻址方式。
(1)指令在格式上就有以下3种形式。
①零操作数指令。指令在形式上只有操作码,操作数是隐含存在的。
②单操作数指令。指令中仅给出一个操作数,另一个操作数隐含存在。
③双操作数指令。格式如图3-1所示。
(2)指令中的操作数类型
8086指令中的操作数主要有3种类型:立即数操作数、寄存器操作数和存储器操作数。
①立即数操作数
具有固定数值的操作数,即常数。
②寄存器操作数
8086CPU的8个通用寄存器和4个段寄存器可以作为指令中的寄存器操作数,可作为源操作数和目标操作数。通用寄存器存放参加运算的数据或数据所在存储器单元的偏移地址。段寄存器存放当前操作数的段基地址。
③存储器操作数
存储器操作数是指参加运算的数据是存放在内存中的,可作为源操作数和目标操作数。
2指令的执行时间
一条指令的执行时间应包括取指令、取操作数、执行指令及传送结果几个部分。
3种类型的操作数中,寄存器操作数的指令执行速度最快,立即数操作数次之,存储器操作数指令的执行速度最慢。
二、寻址方式
寻址方式,是指获得操作数所在的地址的方法。在8088/8086系统中,一般将寻址方式分为两种:
(1)寻找操作数的地址;
(2)寻找要执行的下一条指令的地址。
1立即寻址
立即寻址(Immediate Addressing)方式只针对源操作数。此时源操作数是一个立即数,它作为指令的一部分,紧跟在指令的操作码之后、存放于内存的代码段中,如图3-2所示
图3-2 立即寻址方式示意图
2直接寻址
直接寻址(Direct Addressing)方式表示参加运算的数据存放在内存中,存放的地址由指令直接给出,即指令中的操作数是存储器操作数。
直接寻址指令中的数值是操作数的l6位偏移地址,而不是数据本身。为了区分,指令系统规定偏移地址必须用方括号括起来。
3寄存器寻址
在寄存器寻址(Register Addressing)方式下,指令的操作数为CPU的内部寄存器,可以是数据寄存器(8位或16位),也可以是地址指针、变址寄存器或段寄存器。
采用寄存器寻址方式,虽然指令操作码在代码段中,但操作数在内部寄存器中,指令执行时不必通过访问内存就可取得操作数,故执行速度较快。
4寄存器间接寻址(简称间址寄存器或地址指针)
寄存器间接寻址(Register Indirect Addressing)是用寄存器的内容表示操作数的偏移地址。寄存器间接寻址方式中存放操作数偏移地址的寄存器只允许是SI、DI、BX和BP。选择不同的间址寄存器涉及的段寄存器不同。在默认情况下,选择SI、DI、BX作间址寄存器时,操作数在数据段,段基地址由DS决定;选择BP作间址寄存器,则操作数在堆栈段,段基地址由SS决定。
【例3-1】已知DS=6000H,SI=1200H,执行指令:MOV AX,[SI]。
因为指令中没有指定段重设,所以寻址时使用默认的段寄存器DS。由已知条件可计算出操作数的物理地址=60000H+1200H=61200H。指令执行情况如图3-3所示。
图3-3 寄存器间接寻址示意图
执行结果:AX=3344H。
若操作数存放在附加段,则本例中的指令应表示成:MOV AX,ES:[SI]
例3-1中,若间址寄存器采用BP,则操作数默认存放在堆栈段。
5寄存器相对寻址
在寄存器相对寻址方式中,操作数在内存中的存放地址(偏移地址)由间址寄存器的内容加上指令中给出的一个8位或16位的位移量组成。
寄存器相对寻址常用于存取表格或一维数组中的元素——把表格的起始地址作为位移量,元素的下标值放在间址寄存器中(反过来也可以)。
6基址-变址寻址
基址-变址寻址方式由一个基址寄存器(BX或BP)的内容和一个变址寄存器(SI或DI)的内容相加而形成操作数的偏移地址,称为基址-变址寻址。在默认的情况下,指令中若用BX作基址寄存器,则段地址在DS中;如果用BP作基址寄存器,则段地址在SS中。
【例3-2】指令MOV AX,[BX][SI]的寻址过程如图3-4所示。
图3-4 基址变址寻址示意图
设:DS=8000H,BX=2000H,SI=1000H。则操作数的物理地址=80000H+2000H+l000H=83000H。指令执行后:AL=[83000H],AH=[83001H]。
注意:使用基址-变址方式时,不允许将两个基址寄存器或两个变址寄存器组合在一起寻址,即指令中不允许同时出现两个基址寄存器或两个变址寄存器。
7基址-变址-相对寻址
基址-变址-相对寻址方式是基址-变址寻址方式的扩充。指令中指定一个基址寄存器和一个变址寄存器,还给出一个8位或16位的位移量,将三者相加就得到操作数的偏移地址。
【例3-3】指令MOV AX,5[DI][BX]的寻址过程示例。
该指令将段地址为DS、偏移地址为BX+DI+5的连续两个存储单元的内容送到AX,指令执行情况的示意图如图3-5所示。
图3-5 基址变址相对寻址示意图
使用这种寻址方式可以很方便地访问二维数组。
与寄存器间接寻址方式类似,基址-变址-相对寻址指令也可以表示成多种形式。同样地,基址-变址-相对寻址也不允许在指令中同时出现两个基址寄存器或两个变址寄存器。
8隐含寻址
有些指令的操作码中不仅包含了操作的性质,还隐含了部分操作数的地址。这种将一个操作数隐含在指令码中的寻址方式就称为隐含寻址。
三、8086指令系统
1数据传送指令
数据传送类指令按功能可分为四小类:通用数据传送指令、目标地址传送指令、标志传送指令和输入/输出指令。
(1)通用数据传送指令
通用数据传送指令包括一般传送指令MOV、堆栈操作指令PUSH和POP、交换指令XCHG、查表转换指令XLAT和字位扩展指令。
①一般传送指令MOV
MOV指令格式及操作:
MOV dest,src;(dest)←(src)
MOV指令可实现的操作:
a.寄存器与寄存器或寄存器与段寄存器之间的传送。
b.寄存器与存储器之间的传送。
c.立即数到寄存器的传送。
d.立即数到存储器的传送。
e.存储器与段寄存器之间的传送。
MOV指令对操作数的要求:
a.MOV指令中两个操作数字长必须相同。
b.两个操作数不能同时为存储器操作数。
c.不能用立即数直接给段寄存器赋值。
d.两个操作数不能同时为段寄存器。
e.一般情况下,指令指针IP及代码段寄存器CS的内容不通过MOV指令修改。
f.通常情况下,FLAGS整体不能作为操作数。
②堆栈操作指令PUSH和POP
堆栈用以存放寄存器或存储器中暂时不用又必须保存的数据。堆栈不能任意存取,必须遵循原则:
a.堆栈的存取必须是一个字(16位),而且只能是寄存器或存储器操作数,不能是立即数。
b.向堆栈中存放数据时,总是从高地址向低地址方向增长,而从堆栈取数据时则方向正好相反。
c.堆栈段在内存中的位置由SS决定,堆栈指针SP总是指向栈顶。
d.对堆栈的操作遵循“后进先出(LIFO)”的原则。
堆栈操作指令格式:PUSH src;POP dest。指令中的操作数src和dest必须为字操作数(16位)。
堆栈指令的执行过程:压栈指令PUSH OPRD。PUSH指令是将指令中指定的字操作数压入堆栈。出栈指令POP OPRD。POP指令是将当前栈顶的一个字送到指定的目标地址,并紧接着修改堆栈指针,以使SP指向新的栈顶位置。
③交换指令XCHG指令
格式:XCHG OPRD1,OPRD2;(OPRD1)←→(OPRD2)
交换指令的操作是将源地址与目标地址中的内容进行互换。
交换指令对操作数的要求:
a.源操作数和目标操作数可以是寄存器或存储器,但不能同时为存储器。
b.不能为段寄存器操作数。
c.两个操作数字长必须相同,可以是字节交换,也可以是字交换。
④查表转换指令XLAT
XLAT是字节的查表转换指令,可以根据表中元素的序号查出表中相应元素的内容。
指令格式:
XLAT;将偏移地址为BX+AL所指单元的内容送到AL中;
XLAT src_table;(src_table表示要查找的表的首地址)
(2)输入/输出指令
输入/输出(I/O)指令是专门面向输入/输出端口进行读写的指令,共有两条:IN和OUT。在8088的I/O指令中,允许用两种形式来表示端口地址,或称为两种寻址方式。
①直接寻址:指令中I/O端口地址为8位,此时允许寻址256个端口,端口地址范围为0~FFH。
②寄存器间接寻址:端口地址为16位,由DX寄存器指定,可寻址64K个端口,地址范围为0~FFFFH。间接寻址方式适用范围较大,编制程序时要尽量采用这种方式。
IN指令格式:
IN acc,port;直接寻址,port为用8位立即数表示的端口地址
IN acc,DX;间接寻址,16位端口地址由DX给出
注意:采用间接寻址的IN/OUT指令只能用DX寄存器作为间址寄存器。
③取偏移地址指令
指令格式:LEA reg16,mem
④其他传送指令
除以上传送类指令外,8086指令系统中还有一些其他的数据传送指令。
2算术运算指令
(1)加法运算指令
加法运算指令有普通加法指令ADD、带进位位的加法指令ADC及加1指令INC。双操作数指令对操作数的要求与MOV指令基本相同,但段寄存器不能作为加法指令的操作数。
①普通加法指令ADD
指令格式:ADD OPRD1,OPRD2;OPRD1←OPRD1+OPRD2
ADD指令的执行是将源操作数和目标操作数相加,结果送回目标地址中。
源操作数OPRD2和目标操作数OPRD1均可以是8位或16位的寄存器或存储器操作数,源操作数还可以是立即数,可以是无符号数,也可以是带符号数。
②带进位位的加法指令ADC指令格式:
指令格式:ADC OPRD1,OPRD2;OPRD1←OPRD1+OPRD2+CF
ADC指令主要用于多字节加法运算。
③加1指令INC
指令格式:INC OPRD;OPRD←OPRD+1
INC指令是将指定操作数的内容加1,再送回该操作数。这里,操作数OPRD可以是寄存器或存储器操作数;可以是8位,也可以是16位;但不能是段寄存器,也不能是立即数。
INC指令不影响CF标志位,但对其他状态标志AF、OF、PF、SF及ZF会产生影响。
(2)减法指令
8088/8086共有5条减法指令:不考虑借位的普通减法指令SUB、考虑借位的减法指令SBB、减l指令DEC、求补指令NEG以及比较指令CMP。
①不考虑借位的减法指令SUB
指令格式:SUB OPRD1,OPRD2;OPRD←OPRD1←OPRD2
SUB指令是一条双操作数指令,其功能是用目标操作数减去源操作数,并将结果送到目标操作数所在地址中。
②考虑借位的减法指令SBB
指令格式:SBB OPRD1,OPRD2;OPRD1←OPRD1←OPRD2-CF
SBB指令的功能是用目标操作数减去源操作数以及标志位CF的值,并将结果送到目标操作数所在的地址中。SBB指令主要用于多字节的减法运算。
③减1指令DEC
指令格式:DEC OPRD;OPRD←OPRD-1
DEC指令与INC指令一样,是一条单字节指令,其功能是将操作数的值减1,结果再送回该操作数所在地址。该指令对操作数的要求及对标志位的影响与INC指令相同。
(3)求补指令NEG
NEG指令的操作是用0减去操作数OPRD,结果送回该操作数所在地址。
指令格式为NEG OPRD;OPRD←0-OPRD
操作数OPRD可以是寄存器或存储器操作数。利用该指令可以得到负数的绝对值。
NEG指令对6个状态标志位均有影响。应用时应注意:
①执行NEG指令后,一般都会使CF为1。除非给定的操作数为0才会使CF为0。
②当指定的操作数的值为80H(-128)或为8000H(-32768),则执行NEG指令后结果不变,即仍为80H或8000H,但OF置1,其他情况下OF均置0。
(4)比较指令CMP
指令格式:CMP OPRD1,OPIRD2;OPRD1-OPRD2,结果不送回OPRD1
CMP指令是用目标操作数减源操作数,但相减的结果不送回目标操作数,即指令执行后两操作数内容不变,而只是影响6个状态标志位。
比较指令主要是用来比较两个数的大小关系。判断两个有符号数大小关系的方法:当OF⊕SF=0时,被减数大于减数;当OF⊕SF=1时,减数大于被减数。
(5)乘法指令
乘法指令包括无符号数乘法和有符号数乘法指令两种,采用隐含寻址方式,隐含的目标操作数为AX(与DX),而源操作数由指令给出。
对无符号数乘法,如果乘积的高半部分(在字节相乘时为AH,在字相乘时为DX)不为0,则CF=OF=1,代表AH或DX中包含乘积的有效数字;否则CF=OF=0。对有符号数乘法,若乘积的高半部分是低半部分的符号位的扩展,则CF=OF=0;否则CF=OF=1。
无符号数乘法指令的格式:MUL OPRD
指令的操作:
a.字节乘法
AX←OPRD×AL
b.字乘法
DX:AX←OPRD×AX
源操作数OPRD可以是8位或16位的寄存器或存储器。乘法指令要求两操作数字长相等,且不能为立即数。
(7)除法指令
除法指令也包括无符号数的除法指令和有符号数的除法指令两种,同样采用隐含寻址方式,隐含了被除数,而除数由指令给出,要求除数不能为立即数。
无符号数除法指令的格式:DIV OPRD
操作数OPRD可以是8位或16位的寄存器或存储单元的内容
指令的操作为字节除法
AL←AX/OPRD,AH←AX%OPRD(%为取余数操作)
字除法
AX←DX:AX/OPRD,DX←DX:AX%OPRD(%为取余数操作)
(8)其他算术运算指令
除以上指令外,8086指令系统还具有其他一些算术运算指令。
3逻辑运算和移位指令
逻辑运算和移位指令包括逻辑运算指令和移位指令两大部分,移位指令中又分为非循环移位指令和循环移位指令。
8088/8086提供的逻辑运算指令共有5条,包括AND(逻辑“与”)、OR(逻辑“或”)、NOT(逻辑“非”)、XOR(逻辑“异或”)及TEST(测试)指令。这些指令可对8位或16位的寄存器或存储器单元中的内容进行按位操作。
(1)逻辑“与”指令AND
指令格式:AND OPRD1,OPRD2;OPRD1←OPRD1∧OPRD2
AND指令使源操作数和目标操作数按位相“与”,结果送回目标操作数中。AND指令在程序中的主要应用:
①实现两操作数按位相“与”。
②使目标操作数中某些位保持不变,把其他位清0。
③使操作数不变,但影响6个状态标志位,并使CF=OF=0。
(2)逻辑“或”指令OR
指令格式:OR OPRD1,OFRD2;OPRD1←OPRRD1∨OPRD2
OR指令实现对源操作数和目标操作数按位相“或”,结果送回目标操作数中。对应AND指令,OR指令在程序中的主要应用:
①实现两操作数按位相“或”。
②使目标操作数某些位保持不变,将另外一些位置1。
③使操作数不变,但影响6个状态标志位,并使CF=OF=0。
(3)逻辑“非”指令NOT
指令格式:NOT OPRD
NOT指令是单操作数指令,它将指定的操作数0PRD按位取反,再送回该操作数。
(4)逻辑“异或”指令XOR
指令格式:XOR OPRD1,OPRD2;OPRD1←OPRD1⊕OPRD2
XOR指令将源操作数和目标操作数按位进行“异或”运算,结果送回目标操作数。
“异或”操作的原则是:两位操作数相同时结果为0,不同时结果为1。
(5)测试指令TEST
TEST指令的格式、对操作数的要求及完成的操作和AND指令类似,区别是:TEST指令不将“与”的结果送回目标操作数,而只影响标志位。故这条指令常用于在不破坏目标操作数内容的情况下检测操作数中某些位是“1”还是“0”。
(6)移位指令
移位指令包括非循环移位指令和循环移位指令两类。
①非循环移位指令
8086/8088有4条非循环移位指令:算术左移指令SAL(Shift Arithmetic Left)、算术右移指令SAR(Shift Arithmetic Right)、逻辑左移指令SHL(Shift Logic Left)、逻辑右移指令SHR(Shift Logic Right)。逻辑移位指令针对的是无符号数,算术移位指令针对有符号数。
a.算术左移和逻辑左移指令SAL/SHL。SAL和SHL执行完全相同的操作,其指令格式为SHL OPRD,1;SAL OPRD,1;或SHL OPRD,CL;SAL OPRD,CL。
SHL/SAL指令执行的操作是将目的操作数的内容左移1位或CL所指定的位,每左移1位,左边的最高位移入标志位CF,而在右边的最低位补零。
b.逻辑右移指令SHR。SHR指令格式与SHL相同,将指令中的目标操作数视为无符号数。其操作是将目标操作数顺序向右移1位或CL指定的位数,每右移l位,右边的最低位移入标志位CF,而在左边的最高位补零。
c.算术右移指令SAR。SAR指令是将指令中目标操作数视为有符号数,格式与SHR相同。指令的操作是将目标操作数顺序向右移1位或CL指定的位数,操作数最低位移入标志位CF。
(7)循环移位指令
8088CPU有4条循环移位指令:不带进位标志位CF的循环左移指令ROL、不带进位标志位CF的循环右移指令ROR、带进位标志位CF的循环左移指令RCL、带进位标志位CF的循环右移指令RCR。
①不带CF的循环左移指令ROL
指令格式:ROL OPRD,1或ROL OPRD,CL
ROL指令将目标操作数向左循环移动1位或由CL指定的位数,最高位移入CF,同时再移入最低位构成循环,进位标志CF不在循环之内。
②不带CF的循环右移指令ROR
指令格式:ROR OPRD,1或ROR OPRD,CL
ROR指令将目标操作数向右循环移动1位或由CL指定的位数,最低位移入CF,同时再移入最高位构成循环。
③带CF的循环左移指令RCL
指令格式:RCL OPRD,1或RCL OPRD,CL
RCL指令将目标操作数连同进位标志位CF一起向左循环移动1位或CL指定位数,最高位移入CF,而CF原来的值移入最低位。
④带CF的循环右移指令RCR
指令格式:RCR OPRD,1或RCR OPRD,CL
RCR指令将目标操作数连同进位标志位CF一起向右循环移动1位或CL指定位数,最低位移入CF,而CF原来的值移入最高位。
4串操作指令
(1)串操作指令的共同特点
存储器中的地址连续的若干单元的字符或数据称为字符串或数据串。串操作指令就是用来对串中每个字符或数据做同样操作的指令。
使用串操作指令关键的要点是:应预先设置源串指针(DS、SI)、目标串指针(ES、DI)、重复次数(CX)以及操作方向(DF)。
(2)重复操作前缀
在串操作指令前面加一个适当的重复操作前缀,能够使该指令重复执行。
用于串操作指令的重复操作前缀分为两类:无条件重复前缀(1条)及有条件重复前缀:
①REP:无条件重复前缀。重复执行指令规定的操作,直到(CX)=0。
②REPE/REPZ:相等/结果为零时重复,ZF=1,且CX≠0时重复。
③REPNE/REPNZ:不相等/结果不为零时重复,ZF=0,且CX≠0时重复。
(3)串操作指令
串操作指令是8086指令系统中唯一一组能直接处理源和目标操作数都在存储单元的指令。串操作指令共有5条。
①串传送指令
3种指令格式:MOVS OPRD1,OPRD2或NOVSB或MOVSW
第一种格式中,OPRD1为目标串地址,OPRD2为源串地址。
MOVSB指令一次完成一个字节的传送,MOVSW一次完成一个字的传送。
串传送指令实现内存单元到内存单元的数据传送,解决了MOV指令不能直接在内存单元之间传送数据的限制。
②串比较指令
3种格式:CMPS OPRD1,OPRD2或CMPSB或CMPSW
串比较指令与比较指令CMP的操作类似,CMP指令比较的是两个数据,而CMPS进行的是两个数据串的比较。
串比较指令通常和条件重复前缀REPE(REPZ)或REPNE(REPNZ)连用,用来检查两个字符串是否相等。
③串扫描指令
3种格式:SCAS OPRD;OPRD为目标串或SCASB或SCASW
SCAS指令的执行与CMPS指令类似,也是进行比较操作。只是SCAS指令是用累加器AL或AX的值与目标串(由ES:DI指定)中的字节或字进行比较,比较结果不改变目标操作数,只影响标志位。
④串装入指令
3种格式:LODS OPRD;OPRD为源串或LODSB或LODSW
LODS指令把由DS:SI指向的源串中的字节或字取到累加器AL或AX中,并在这之后根据DF的值自动修改指针SI,以指向下一个要装入的字节或字。
⑤串存储指令
3种格式:STOS OPRD;OPRD为目标串或STOSB或STOSW
STOS指令把累加器AL中的字节或AX中的字存到由ES:DI指向的存储器单元中,并在这之后根据DF的值自动修改指针DI的值(增量或减量),以指向下一个存储单元。
5程序控制指令
程序控制指令包括转移指令、循环控制指令、过程调用指令和中断控制指令四大类,用于程序的分支转移、循环控制及过程调用等。
(1)无条件转移指令JMP
JMP(Jump)指令的操作是无条件地使程序转移到指定的目标地址,并从该地址开始执行新的程序段。寻找目标地址的方法有两种:①直接的方式;②间接的方式。
①段内直接转移
指令格式:
JMP LABEL
LABEL是一个标号,也称为符号地址,表示转移的目的地。
指令的操作是将IP的当前值加上计算出的地址位移量形成新的IP,并使CS保持不变,从而使程序按新地址继续运行(即实现了程序的转移)。
②段内间接转移
指令格式:JMP OPRD
OPRD是16位的寄存器或者存储器地址,可以采用各种寻址方式。指令的执行是用指定的16位寄存器内容或存储器两单元内容作为转移目标的偏移地址,用其内容取代原来IP的内容,从而实现程序的转移。
③段间直接转移
采用段间直接转移时,指令中直接提供了要转移的16位段地址和16位偏移地址。
指令格式:JNP FAR LABEL
FAR表明其后的标号LABEL是一个远标号,即它在另一个代码段内。汇编程序根据LABEL的位置确定出LABEL所在的段基地址和偏移地址,然后将段地址送入CS。偏移地址送入IP,结果使程序转移到另一个代码段(CS:IP)继续执行。
④段间间接转移
指令格式:JNP OPRD
OPRD是一个32位的存储器地址。指令的执行是将指定的连续4个内存单元的内容送入IP和CS中(低字内容送IP,高字内容送CS),从而程序转移到另一个代码段继续执行。
(2)条件转移指令JCC
所有的条件转移都是直接寻址方式的短转移,即只能在以当前IP值为中心的-128~+127字节范围内转移。条件转移指令不影响标志位。
由于条件转移指令是根据状态标志位的状态决定是否转移的,因此在使用时,其前一条指令应是执行后能够对相应状态标志位产生影响的指令。
(3)循环控制指令
循环控制指令是在循环程序中用来控制循环的。其控制转向的目标地址是以当前IP内容为中心的-128~+127字节范围内。循环次数必须预先送入CX寄存器中。一般情况下,循环控制指令放在循环程序的开始或结尾。循环控制指令共有3条,它们均不影响标志位。
①LOOP指令
指令格式:LOOP LABEL
LABEL相当于一个近地址标号。指令的执行是先将CX内容减1,再判断CX是否为0,若CX≠0,则转至目标地址继续循环;否则就退出循环,执行下一条指令,即LOOP指令相当于以下两指令的组合。
DEC CX
JNZ NEXT
②LOOPZ(或LOOPE)指令
指令格式:LOOPZ LABEL或LOOPE LABEL
LOOPZ指令在执行时先使CX内容减1,再根据CX中的值及ZF的值来决定是否继续循环。继续循环的条件是CX≠0,且ZF=1;若CX=0或者ZF=0,则退出循环。
③LOOPNZ(或LOOPNE)指令
指令格式:LOOPNZ LABEL或LOOPNE LABEL
LOOPNZ指令与LOOPZ指令类似,只是其中ZF条件与之相反。它先将CX内容减1,然后再判断CX和ZF的内容,当CX≠0且ZF=0的条件下,就转至目标地址继续循环;否则退出循环。
(4)过程调用和返回
在编程过程中,为了节省内存单元,往往将程序中常用到的具有相同功能的部分独立出来。编写成一个模块,称之为子程序(或过程)。程序执行中,主程序在需要时可随时调用这些子程序;子程序执行完以后,又返回到主程序继续执行。8086/8088指令系统为实现这一功能提供了调用指令CALL和返回指令RET。
①调用指令CALL执行时,CPU先将下一条指令的地址(称为返回地址)压入堆栈保护起来,然后将子程序入口地址赋给IP(或CS和IP)中,以便转到子程序执行。
②返回指令RET一般在子程序末尾,执行RET时,CPU将堆栈顶部保留的返回地址弹出到IP(或CS和IP)中,这样即可返回到CALL的下一条指令,继续执行主程序。由于子程序可能与主程序同在一个段内,也可能不同在一个段内。所以与无条件转移指令一样,CALL指令也有4种形式,即段内直接调用、段内间接调用、段间直接调用以及段间间接调用。
①段内直接调用
指令格式:CALL NEAR PROC
PROC是一个近过程的符号地址,表示指令调用的过程是在当前代码段内。指令在汇编后会得到CALL指令的下一条指令与被调用过程的入口地址之间相差的16位相对位移量。
CALL指令执行时,首先将下面一条指令的偏移地址压入堆栈,然后将指令中16位的相对位移量和当前IP的内容相加,新的IP内容即为所调用过程的入口地址(确切地说是入口地址的偏移地址)。
②段内间接调用
指令格式:CALL OPRD
OPRD为16位寄存器或两个存储器单元的内容。这个内容代表的是一个近过程的入口地址。指令的操作是将CALL指令的下面一条指令的偏移地址压入堆栈,若指令中的操作数(OPRD)是一个16位通用寄存器,则将寄存器的内容送IP;若是存储单元,则将存储器的两个单元的内容送IP。
③段间直接调用
指令格式:CALL FAR PROC
PROC是一个远过程的符号地址,表示指令调用的过程在另外的代码段内。
指令在执行时先将CALL指令的下一条指令的地址,即CS和IP寄存器的内容压入堆栈,然后用指令中给出的段地址取代CS的内容,偏移地址取代IP的内容。执行过程如下。
SP←SP-2,([SP+1]:[SP])←CS;CS←被调用过程入口的段地址
SP←SP-2,([SP+1]:[SP])←IP;IP←被调用过程入口的偏移地址
④段间间接调用
指令格式:CALL OPRD
OPRD为32位的存储器地址。指令的操作是将CALL指令的下一条指令的地址,即CS和IP的内容压入堆栈,然后把指令中指定的连续4个存储单元中的内容送IP及CS。低地址的两个单元内容为偏移地址,送入IP;高地址的两个单元内容为段地址,送入CS。
⑤返回指令RET
指令格式:RET
返回指令执行与调用指令相反的操作。对于近过程(与主程序在同一段内),用RET返回主程序时,只需从堆栈顶部弹出一个字的内容给IP作为返回的偏移地址;对于远过程(与主程序不在同一段),用RET返回主程序时,则先从堆栈顶部弹出一个字的内容给IP作为返回的偏移地址,再弹出一个字的内容给CS作为返回段地址。
(5)中断指令
中断是指在程序运行期间因某种随机或异常的事件,要求CPU暂时中止正在运行的程序转去执行一组专门的中断服务程序来处理这些事件,处理完毕后又返回到原被中止处继续执行原程序的过程。
引起中断的事件称为中断源,它可以是在CPU内部,也可以是在CPU外部。内部中断源引起的中断称为内部中断;外部中断源引起的中断就称为外部中断。8086/8088中断系统分为外部中断(或叫硬件中断)和内部中断(或叫软件中断)。外部中断主要用来处理外设和CPU之间的通信。内部中断包括运算异常及中断指令引起的中断。
中断指令用于产生软件中断,以执行一段特殊的中断处理过程。中断指令的主要用途:
a.用户程序可通过中断指令调用操作系统提供的特殊子程序(称为系统功能调用)。特殊子程序为用户程序提供了控制台输入/输出、文件系统、软硬件资源管理、通信等丰富的服务。
b.用来实现一些特殊的功能,如调试程序时单步运行、断点等。
c.调用BIOS提供的硬件低层服务。
8086/8088指令系统提供了3条与软件中断相关的指令。
①INT指令
指令格式:INT n
n为中断向量码(也称中断类型码),是一个常数,取值范围为0~255。
指令执行时,CPU根据n的值计算出中断向量的地址。然后从该地址中取出中断服务程序的入口地址,并转到该中断服务子程序去执行。中断向量地址的计算方法是将中断向量码n乘4。INT指令的具体操作步骤如下。
SP←SP-2,([SP+1]:[SP])←FLAGS;把标志寄存器的内容压入堆栈
TF←0,IF←0
清除IF和TF,保证不会中断正在执行的中断子程序,并且不响应单步中断
SP←SP-2,([SP+1]:ESP])←CS
SP+SP-2,([SP+1]:[SP])←IP
把断点地址(即INT指令的下一条指令的地址)压入堆栈
IP←([n×4+1]:[n×4])CS←(En×4+3]:[n×4+2])
由n×4得到中断向量地址,并进而得到中断处理子程序的入口地址
以上操作完成后,CS:IP就指向中断服务程序的第一条指令,此后CPU开始执行中断服务子程序。
从CPU执行中断指令的过程可以看出,INT指令的基本操作与存储器寻址的段间间接调用指令所不同的有以下3点:
a.INT指令要把标志寄存器FLAGS压入堆栈,而CALL指令不保存FLAGS内容。
b.INT影响IF和TF标志,而CALL指令不影响。
c.中断服务程序入口地址放在内存的固定位置,以便通过中断向量码找到它,而CALL指令可任意指定子程序入口地址的存放位置。
②中断返回指令IRET
中断返回指令IRET用于从中断服务子程序返回到被中断的程序继续执行。任何中断服务子程序无论是由外部中断引起的,还是内部中断引起的,其最后一条指令都是IRET指令。该指令首先将堆栈中的断点地址弹出到IP和CS,接着将INT指令执行时压入堆栈的标志字弹出到标志寄存器以恢复中断前的标志状态。指令的操作为
IP←([SP+1]:[SP]),SP←SP+2
CS←([SP+1]:[SP]),SP←SP+2
FLAGS←([SP+1]:[SP]),SP←SP+2
6处理器控制指令
处理器控制指令用来对CPU进行控制,共分为两大类:标志操作指令和外部同步指令。各指令的功能如表3-1所示。
表3-1 处理器控制指令