标志操作指令
8086/8088指令集中,有一部分指令是专门对标志寄存器或标志位进行的,包括四条标志寄存器传送指令和七条专门用于设置或清除某些标志位的指令。
1. 标志传送指令
标志传送指令属于数据传送指令组。
1)指令LAHF(Losa AH with Flags)
指令LAHF采用固定寻址方式,指令格式如下:
LAHF
该条指令把标志寄存器的低8位(包括符号标志SF﹑零标志ZF﹑辅助进位标志AF﹑奇偶标志PF和进位标志CF)传送到寄存器AH的指定位,即相应地传送至寄存器AH的位7﹑6﹑4﹑2和0,其他的位(位5﹑3和1)的内容无定义,如图2.15所示。
图2.15 LAHF指令示意图
这条指令本身不影响这些标志和其他标志。
2)指令SAHF(Store Ahinto Flags)
指令SAHF采用固定寻址方式,其格式如下:
SAHF
该条指令与指令LAHF刚好相反,把寄存器AH的指定位送至标志寄存器低8位的SF﹑ZF﹑AF﹑PF和CF标志位。因而这些标志的内容就要受到影响,并取决于AH中相应位的状态。但这条指令不影响溢出标志OF﹑方向标志DF﹑中断允许标志IF和追踪标志TF,也即不影响标志寄存器的高位字节。例如:
MOV AH, 0CH
SAHF ;CF=1,PF=0,AF=0,ZF=1,SF=1
3)指令PUSHF
指令PUSHF的格式如下:
PUSHF
该条指令把标志寄存器的内容压入堆栈,即先把堆栈指针寄存器SP的值减2,然后把标志寄存器的内容送入由SP所指的栈顶。
这条指令不影响标志。
4)指令POPF
指令POPF的格式如下:
POPF
该条指令把当前堆栈顶的一个字传送到标志寄存器,同时相应地修改堆栈指针,即把堆栈指针寄存器SP的值加2。
在执行该指令后,标志寄存器各位会发生相应变化。
这条指令和PUSHF指令一起可以保存和恢复标志寄存器的内容,即保存和恢复各标志的值。另外,这两条指令也可以用来改变追踪标志TF。在8086/8088指令系统中,没有专门设置和清除TF标志的指令,为了改变TF标志,可先用PUSHF指令将标志压入堆栈,然后设法改变栈顶字单元中的第8位(把整个标志寄存器看成是一个字),再用POPF指令把该字弹回到标志寄存器,这样其余的标志不受影响,而只有TF标志按需要改变了。
2. 标志位操作指令
标志位操作指令属于处理器控制指令组,它们仅对指令规定的标志产生指令规定的影响,对其他标志没有影响。
1)清进位标志指令CLC(CLear Carry flag)
清进位标志指令的格式如下:
CLC
该条指令使进位标志为0。
2)置进位标志指令STC(SeT Carry flag)
置进位标志指令的格式如下:
STC
该条指令使进位标志为1。
3)进位标志取反指令CMC(CoMplement Carry flag)
进位标志取反指令的格式如下:
CMC
该条指令使进位标志取反。如CF为1,则使CF为0,则CF为1。
4)清方向标志CLD(Clear Direction flag)
清方向标志指令的格式如下:
CLD
该条指令使方向标志DF为0。从而在执行串操作指令时,使地址按递增方式变化。
5)置方向标志STD(SeT Direction flag)
置方向标志指令的格式如下:
STD
该条指令使方向标志DF为1。从而在执行串操作指令时,使地址按递减方式变化。
6)清中断允许标志CLI(Clear Interrupt enable flag)
清中断允许标志指令的格式如下:
CLI
该条指令使中断允许标志IF为0,于是CPU就不响应来自外部装置的可屏蔽中断。但对不可屏蔽中断和内部中断都没有影响。
7)置中断允许标志STI(SeT Interrupt enable flag)
置中断允许标志指令的格式如下:
STI
该条指令使中断允许标志IF为1,则CPU可以响应可屏蔽中断。
2.4.4 加减运算指令
8086/8088提供加﹑减﹑乘和除四种基本算术运算操作。这些操作都可用于字节或字的运算,也可以用于无符号数的运算或有符号数的运算。有符号数用补码表示。加减运算指令不再分为无符号数运算指令和有符号数运算指令,而乘除运算指令还分为无符号数运算指令和有符号数运算指令。另外,8086/8088还提供了各种十进制算术运算调整指令。
关于加减运算指令,有如下几点通用说明,请予以注意:
图2.16 参与加减运算的操作数
①加减运算指令对无符号数和有符号数的处理一视同仁。既作为无符号数而影响标志CF和AF,也作为有符号数影响标志OF和SF,当然总会影响标志ZF。加减运算指令也要影响标志PF。有些指令稍有例外。
②可参与加减运算的操作数如图2.16所示。总是只有通用寄存器或存储单元可用于存放运算结果。如果参与运算的操作数有两个,则最多只能有一个是存储器操作数。
③如果参与运算的操作数有两个,则它们的类型必须一致,即同时为字节,或同时为字。
④存储器操作数可采用2.3节介绍的四种存储器操作数寻址方式。
1.加法指令
1)普通加法指令ADD(ADDiton)
普通加法指令的格式如下:
ADD OPRD1, OPRD2
这条指令完成两个操作数相加,结果送至目的操作数OPRD1,即:
OPRD1<=OPRD1 OPRD2
例如:
ADD AL,5
ADD AL,AH
ADD DI,DI
ADD BL,VARB ;VARB是字节变量
ADD VARW,SI ;VARW是字变量
ADD [BX SI-3],AX
我们用下面的程序片段说明加法指令及其对标志的影响,同时说明8位数据寄存器与16位数据寄存器间的关系。安排的注释用于说明对应指令执行完后受影响的寄存器和标志位的变化,为了便于说明,采用16位进制的形式表示数据。
MOV AX,7896H ;AX=7896H,即AH=78H,AL=96H
;各标志位保持不变
ADD AL,AH ;AL=0EH,AH=78H,即AX=780EH
;CF=1,ZF=0,SF=0OF=0,AF=0,PF=0
ADD AH,AL ;AH=86H,AL=0EH,即AX=860EH
;CF=0,ZF=0,SF=1,OF=1,AF=1,PF=0
ADD AL,0F2H ;AL=00H,AH=86H,即AX=8600H
;CF=1,ZF=1,SF=0,OF=0,AF=1,PF=1
ADD AX,1234H ;AX=9834H,即AH=98,AL=34H
;CF=0,ZF=0,SF=1,OF=O,AF=O,PF=0
2)带进位加指令ADC(Add with Carry)
带进位加指令的格式如下;
ADC OPRD1,OPRD2
这条指令与ADD指令类似,完成两个操作数相加,但还要把进位标志CF的现行值加上去,把结果送至目的操作数OPRD1,即:
OPRD1<=OPRD2 CF
例如:
ADC AL,[SI]
ADC DX,AX
ADC DX,VARW ;VARW是字变量
ADC指令主要用于多字节运算中。尽管在8086/8088中可以进行16位运算,但16位二进制数能表达的整数的范围还是很有限的,为了扩大数的范围,仍然需要多字节运算。例如,有两个四字节的数相加,加法要分两次进行,先进行低两字节相加,然后再做高两字节相加。在高两字节相加时,要把低两字节相加以后可能出现的进位考虑进去,用ADC指令实现这点很方便。
下面的程序片段实现两个四字节数相加,注意传送指令不影响标志:
MOV AX,FIRST1 ;FIRST1是存放第一个数低两字节的变量
ADD AX,SECOND1 ;SECOND1是存放第二个数低两字节的变量
MOV THIRD1,AX ;保存低两字节相加的结果到THIRD1变量中]
MOV AX,FIRST2 ;FIRST2是存放第一个数高两字节的变量
ADC AX,SECOND2 ;SECOND2是存放第二个数高两字节的变量
MOV THIRD2,AX ;保存结果的高两字节到THIRD2变量中
3)加1指令INC(INCrement)
加1指令的格式如下:
INC OPRD
这条指令完成对操作数OPRD加1,然后把结果送回OPRD,即:
OPRD<=OPRD 1
例如:
INC AL
INC VARB ;VARB是字节变量
操作数DST可以是通用寄存器,也可以是储存单元。这条指令执行的结果影响标志ZF﹑SF﹑OF﹑PF和AF,但它不影响CF。
该指令主要用于调整地址指针和计数器。
例,假设有100个16位无符号数存放在从1234:5678H开始的内存中,现需要求它们的和。设把32位的和保存在DX(高位)和AX寄存器中。
下面的程序片段能实现上述功能:
……
MOV AX,1234H
MOV DS,AX ;置数据段寄存器值
MOV SI,5678H ;置指针初值
MOV AX,0 ;清32位累加和
MOV DX,AX
MOV CX,100 ;置数据个数计数器
NEXT:ADD AX,[SI] ; 求和
ADC DX,0 ;加上可能的进位
INC SI ;调整指针
INC SI
DEC CX ;计数器减1
JNZ NEXT ; 如果不为0,那么就继续累加下一个数据
……
2. 减法指令
1)普通减法指令SUB(SUBtraction)
普通减法指令的格式如下:
SUB OPRD1, OPRD2
这条指令完成两个操作数相减,从OPRD1中减去OPRD2,结果送到目标操作数OPRD1中,即:
OPRD1<=OPRD1-OPRD2
例如:
SUB AH,12
SUB BX,BP
SUB AL,[BX]
SUB BX,VARW ;VARW是字变量
SUB [BP-2],AX
我们用下面的程序片段说明减法指令及其对标志的影响,同时再次说明8位数据寄存器于16位数据寄存器间的关系。安排的注释用于说明对应指令执行完受影响的寄存器和标志位的变化,为了便于说明,还采用16进制的形式表示数据。
MOV BX,9048H; BX=9048H,即BH=90H,BL=48H
SUB BH,BL ;BH=48H,BL=48H,即BX=4848H
;CF=0,ZF=0,SF=0OF=1,AF=1,PF=1
SUB BL,BH ;BL=00H,BH=48H,即BX=4800H
;CF=0,ZF=1,SF=0,OF=0,AF=0,PF=1
SUB LB,5 ;BL=FBH,BH=48H,即BX=48FBH
;CF=1,ZF=0,SF=1,OF=0,AF=1,PF=0
SUB BX,8F34H ;BX=B9C7H,即BH=B9H,BL=C7H
;CF=1,ZF=0,SF=1,OF=1,AF=0,PF=0
2)带进(借)位减指令SBB(SuBtract with Borrow)
带借位指令的格式如下:
SBB OPRD1, OPRD2
这条指令与SUB指令类似,在操作数OPRD1减去操作数OPRD2的同时还要减去借位(进位)标志CF的现行值,即:
例如:
SBB AL, DL
SBB DX, AX
该指令主要用于多字节数相减的场合。
3)减1指令DEC(DECrement)
减1指令的格式如下:
DEC OPRD
这条指令把操作数OPRD减1,并把结果送回OPRD,即:
OPRD<=OPRD—1
例如:
DEC BX
DEC VARB ;VARB是字节变量
操作数OPRD可以是通用寄存器,也可以是存储单元。在相减时,把操作数作为一个无符号数对待。这条指令执行的结果影响标志ZF﹑SF﹑OF﹑PF和AF,但它不影响CF。
该指令主要用于调整地址指针和计数器。
4)取补指令NEG(NEGate)
取补指令的格式如下:
NEG OPRD
这条指令对操作数取补,就是用零减去操作数OPRD,再把结果送回OPRD,也即:
OPRD<=0—OPRD
例如:
NEG AL
NEG VARW[SI] ;有效地址是变量VARW的位移加SI的值
如在字节操作是对—128取补,或在字操作是对—32768取补,则操作数没有变化,但OF被置位。操作数可以是通用寄存器,也可以是存储单元。此指令的执行结果影响CF﹑ZF﹑SF﹑OF﹑AF和PF,一般总使CF为1,除非操作数为0。
5)比较指令CMP(CoMPare)
比较指令的格式如下:
CMP OPRD1,OPRD2
这条指令完成操作数OPRD1减去操作数OPRD2,运算结果不送到OPRD1,但影响标志CF﹑ZF﹑SF﹑OF﹑AF和PF。例如:
CMP SI,DI
CMP CL,5
CMP DX,[BP—4]
比较指令主要用于比较两个数的关系,是否相等,谁大谁小。在执行了比较指令后,可根据ZF是否置位,判断两者是否相等;如果两者是无符号数,则可根据CF判断大小;如果两者是有符号数,则要根据SF和OF判断大小。 例,设有两个64位数按“高高低低”原则存放同一个段的两个缓冲区DATA1和DATA2中,现需要计算DATA1—DATA2。下面的程序计算DATA1—DATA2,结果存放在DATA1中,可能发生的借位保留在CF中:
……
MOV CX,4 ;64位分成4个字
SUB BX,BX ;清指针,同时清CF
NEXT:MOV AX,DATA2[BX] ;取减数
SBB DATA1[BX],AX ;带借位减
INC BX ;调整指针
INC BX
DEX CX ;是否已处理完4个字?
JNZ NEXT ;没完继续
……