汇编11:标志寄存器
标志寄存器
CPU内部有一种特殊的寄存器名为标志寄存器,它有以下几种作用:
1、存储相关指令的某些执行结果
2、为CPU执行相关指令提供行为依据
3、控制CPU的相关工作方式
8080CPU中的标志寄存器是flag,它有16位,其中存储的信息通常被称为程序状态字PSW,这种寄存器的每一位都有含义,它并不整体代表某种含义,flag寄存器的示意图如下:
ZF标志
flag的第6位是ZF,被称为零标志位。相关指令执行后,如果结果为0,那么zf=1;如果结果为1,那么zf=0。zf的真正含义是“计算结果是否为0”,如果是就为1.
当add、sub、mul、div、inc、or、and等指令执行时,结果就会影响ZF(或者标志寄存器),它们大多都是逻辑或算术运算。但是传送指令mov、push、pop等对该寄存器就没有影响。
PF标志
flag的第2位是PF,被称为奇偶标志位。相关指令执行后,其结果中所有的bit位中1的个数如果是偶数,pf=1,如果是奇数,pf=0.
SF标志
flag的第7位是SF,被称为符号标志位。相关指令执行后,结果如果为负,sf=1,如果为正,sf=0。
CF标志
flag的第0位是CF,被称为进位标志位。在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值或借位值。
做加法时:
mov al,98H
add al,al 执行后al的值是30H,进位值是1,CF=1
add al,al 执行后al的值是60H,此时没有进位,所以CF=0
做减法时有可能向更高位借位,如一个小数减去一个大数,此时小数要向高位借1,97H-98H,借位后其实是做197H-98H,这个借位值就记录在cf中
mov al,97H
sub al 98H 执行后al的值是FFH,cf为1
OF标志
OF标志是用来标记溢出的,也就是在有符号数运算的时候,结果超过了机器所能表示的范围称为溢出。对于8位的有符号数据,机器所能表示的范围就是-128-127,对于16位就是-32768-32767。如果发生溢出就OF=1,如果没有就等于0.
注意区分OF标志和CF标志,CF是对无符号数运算有意义的标志位,OF是对有符号数运算有意义的标志位。当两个数相加时,会因为有符号(补码转换)或者无符号规则产生不同的运算式,就会有不同的结果,一次逻辑或算术运算造成的OF和CF影响不一定是一致的。
adc指令和sbb指令
adc是带进位加法指令,它的使用格式是:
adc 操作对象1 操作对象2
相当于操作对象1=操作对象1+操作对象2+CF,它利用了CF位上记录的进位值。
有了adc指令,我们就可以实现更大的数据相加,如计算1EF000H+201000H,结果放在ax(高16位)和bx(低16位)中,我们需要先将低16位相加,然后将高16位和进位值相加:
mov ax,011EH
mov bx,0F000H
add bx,1000H
adc ax,0020H
sbb是带借位减法指令,它利用了CF位上记录的借位值。它的使用格式是:
sbb 操作对象1 操作对象2
相当于操作对象1=操作对象1-操作对象2-CF。
有了sbb指令,我们可以实现更大的数据相减,如计算003E1000H-00202000H,结果放在ax和bx中,我们需要将低16位相减,然后将高16位相减的结果减去借位值:
mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H
cmp指令
cmp是比较指令,格式:
cmp 操作对象1 操作对象2
它相当于减法指令,但是不保存结果,只是根据执行结果影响几个标志位:zf、pf、sf、cf和of。根据zf的值能看出两个操作对象是否是同一个值。
当cmp进行的是无符号数的运算时,根据cf的值就能看出两个数谁大谁小(进位或者借位一定能推断大小)。当cmp进行的是有符号数的运算时,同时根据sf和of两个值就能推断出谁大谁小(必须同时考察结果的正负和是否溢出来推断)。
检测标志位的条件转移指令
之前我们学习的jcxz就是一个条件转移指令,它根据cx寄存器中的数值是否为0,决定到底是否跳转到对应的标号处。还有很多条件转移指令,大多数条件转移指令都检测相关的标志位,分为无符号数比较(检测zf和cf的值)和有符号数比较(检测sf、of和zf的值),下面是常用的根据无符号数的比较结果进行转移的条件转移指令:
cmp因为能修改标志寄存器中的值,所以经常和条件转移指令配合在一起使用,但是这只是一种建议,将转移指令放在运算指令的后面也一样可以实现if语句一般的功能。
DF标志和串传送指令
flag的第10位是DF,它代表方向标志位,在串处理指令中,控制每次操作后si和di的增减,当DF等于0时,每次操作后si和di递增;当DF等于1时,每次操作后si和di递减。
串传送指令movsb代表执行以下操作:
将ds:si指向的内存单元中的字节送入es:di中,然后根据df的值,完成si和di的递增或递减1。
串传送指令movsw和上面的类似,只不过是传送一个字:
将ds:si指向的内存单元中的字送入es:di中,然后根据df的值,完成si和di的递增或递减2。
这两个串传送指令都经常配合rep使用,格式如下:
rep movsb
相当于执行:
s:movsb
loop s
可见rep指令给串传送指令赋予循环的概念。
8086CPU还提供了cld指令和std指令来分别将df标志设置为0和1。
如果我们要用串传送指令将数据段中的第一个字符串复制到它后面的空间中:
data segment
db 'Welcome to masm!'
db 16 dup (0)
data ends
再使用串传送指令前我们要分析好各数据的设置:
1、传送的原始位置:ds:si,应该是data:0
2、传送的目的位置:es:di,应该是data:0010
3、传送的长度:cx,应该是16
4、传送的方向:df,应该是0,每次递增si和di
然后就可以完成程序:
mov ax,data
mov ds,ax
mov si,0
mov es,ax
mov di,16
mov cx,16
cld
rep movsb
pushf和popf
pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据,送入标志寄存器中,这两个指令为直接访问标志寄存器提供了一种方法。
debug中的标志寄存器
在debug过程中可以查看标志寄存器的值,但是不会直接显示是0还是1,而是对应不同的标志位,不同值会有不同的名字对应: