《汇编语言(第四版)》---王爽 第十一章标志寄存器 详细笔记+代码 ~后续章节笔记,课后检测,实验代码持续更新中

《汇编语言(第四版)》—王爽 第十一章标志寄存器

第十一章、标志寄存器

flag和其他的寄存器不一样,其他的寄存器是用来存放数据的,都是整个寄存器具有一个含义

而flag寄存器是按照位起作用的,也就是说,他的每一位都有专门的含义,记录着特定的信息

8086CPU中的flag寄存器:

在这里插入图片描述
flag寄存器中的1、3、5、12、13、14、15位在8086CPU中没有使用,不具备任何的含义,而其他位都有自己的含义

标志在不同数值下的显示:

在这里插入图片描述

11.1、ZF标志

zero flag

flag的第6位是ZF,零标志位。它记录相关指令执行后,其结果是否为0。如果结果为0,那么zf=1;如果结果不为0,那么zf=0。

理解:1的含义表示的是正确、“是0”,0的含义表示的是错误、“非0”,那么当ZF = 0的时候表示的就是0标志是错误的即为非0,而ZF = 1的时候表示的是0标志是正确的,那么此时的话就是为0

例如:

mov ax,1
sub ax,1
执行之后ax寄存器中的数值为0,则此时ZF = 1,表示“结果是0”

mov ax,2
sub ax,1
执行之后ax寄存器中的数值为1,则此时ZF = 0,表示“结果不是0”

在8086CPU 的指令集中,有的指令的执行是影响标志寄存器的,比如,add、sub、mul、div、inc、or、and 等,它们大都是运算指令(进行逻辑或算术运算);有的指令的执行对标志寄存器没有影响,比如,mov、push、pop等,它们大都是传送指令。在使用一条指令的时候,要注意这条指令的全部功能,其中包括,执行结果对标志寄存器的哪些标志位造成影响。

扫描二维码关注公众号,回复: 14627540 查看本文章

11.2、PF标志

flag的第二位是PF,奇偶标志位

他记录指令执行之后的,结果中的所有的二进制位中1的个数

  • 为偶数时,PF = 1
  • 为奇数时,PF = 0

例如:

mov al,1
add al,10		;十进制的10
执行之后结果为00001011B其中有3个1,则此时有奇数个1,那么PF = 0

mov al,1
or al,2		;十进制的2  为0010
执行之后的结果为00000011B,其中两个1,为偶数,那么PF = 1

sub al,al
执行之后的结果为00000000,其中有0个1,为偶数,那么PF = 1

11.3、SF标志

flag的第七位是SF,符号标志位 set flag

他记录的指令执行完之后

  • 结果为负数:SF = 1
  • 结果为整数:SF = 0

我们知道计算机中通常用补码的形式来表示符号数据,计算机中的一个数据可以看作有符号数,也可以看作无符号数

00000001B,可以看作为无符号数1,或有符号数+1;

10000001B,可以看作为无符号数129,也可以看作有符号数-127。

这也就是说,对于同一个二进制数据,计算机可以将它当作无符号数据来运算,也可以当作有符号数据来运算。

mov al,10000001B
add al,1

结果:(al)=10000010B

可以将add指令进行的运算当作无符号数的运算,那么add 指令相当于计算129+1,结果为130(10000010B);也可以将add指令进行的运算当作有符号数的运算,那么add 指令相当于计算-127+1,结果为-126(10000010B)。

不管我们如何看待,CPU在执行add等指令的时候,就已经包含了两种含义,也将得到用同一种信息来记录的两种结果。关键在于我们的程序需要哪一种结果。

SF标志,就是CPU对有符号数运算结果的一种记录,它记录数据的正负。在我们将数据当作有符号数来运算的时候,可以通过它来得知结果的正负。如果我们将数据当作无符号数来运算,SF的值则没有意义,虽然相关的指令影响了它的值。

这也就是说,CPU在执行add等指令时,是必然要影响到SF标志位的值的。至于我们需不需要这种影响,那就看我们如何看待指令所进行的运算了。

11.4、CF标志

flag的第0位是CF,进位标志位

进位只是相对于无符号数而言

一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高的位的进位值,或者从更高位的借位值

对于位数为N的无符号数来说,其对应的二进制信息的最高位,即第N-1位,就是它的最高有效位,而假想存在的第N位,就是相对于最高有效位的更高位

在这里插入图片描述
我们知道,当两个数据相加的时候,有可能产生从最高有效位向更高位的进位。比如,两个8位数据:98H+98H,将产生进位。由于这个进位值在8位数中无法保存,我们在前面的课程中,就只是简单地说这个进位值丢失了。其实CPU在运算的时候,并不丢弃这个进位值,而是记录在一个特殊的寄存器的某一位上。8086CPU就用flag 的CF位来记录这个进位值。比如,下面的指令:

mov al,98H
add al,al		
;执行之后(al) = 30H ;CF = 1,CF记录了最高位向更高位的进位值
add al,al
;执行之后(al) = 60H ;CF = 0

另外一种情况就是两个数做减法的时候,有可能向更高位借位

例如:97H - 98H,此时需要向更高位借位,借位之后,相当于计算197H - 98H,而flag的CF位也可以用来记录这个借位值,比如下面的指令:

mov al,97H
sub al,98H	;执行之后(al)=FFH,CF=1,CF记录了向更高位借位值 
sub al,al	;执行之后(al)=0H,CF=0,CF记录了向更高位借位值

11.5、OF标志

overflow flag溢出标志

溢出只是相对于有符号数而言

在进行有符号数运算的时候,如果结果超出了机器所能表示的最大范围称为溢出。

产生溢出的例子:

mov al,98
add al,99
执行之后将产生溢出,因为add al,99进行的有符号数的运算是:
(al) = (al) + 99 = 98 + 99 = 197
197超过了机器能表示的8位的最大范围:-128~127
那么此时进行的有符号数的运算就发生了溢出,这个运算结果就不正确了

由于在进行有符号数运算时,可能发生溢出而造成结果的错误。则CPU需要对指令执行后是否产生溢出进行记录。

flag的第11位是OF,溢出标志位。一般情况下,OF记录了有符号数运算的结果是否发生了溢出。如果发生溢出,OF=1;如果没有,OF=0。

一定要注意CF 和 OF的区别:CF是对无符号数运算有意义的标志位,而OF是对有符号数运算有意义的标志位。例如:

mov al,98
add al,99
add指令执行后:CF=0,OF=1。
前面我们讲过,CPU在执行add等指令的时候,就包含了两种含义:无符号数运算和有符号数运算。对于无符号数运算,CPU用CF位来记录是否产生了进位;对于有符号数运算,CPU用OF位来记录是否产生了溢出,当然,还要用SF位来记录结果的符号。对于无符号数运算,98+99没有进位,CF=0;对于有符号数运算,98+99发生溢出,OF=1。
mov al,0F0H
add al,88H
add指令执行之后:CF = 1,OF = 1对于无符号数运算而言,有进位,CF = 1,对于有符号数运算而言,发生溢出OF = 1

CF和 OF所表示的进位和溢出,是分别对无符号数和有符号数运算而言的,它们之间没有任何关系。

11.6、ADC指令

adc是带进位的加法指令,它利用CF位上记录的进位值

格式:adc 操作对象1,操作对象2

功能:操作对象1 = 操作对象1 + 操作对象2 + CF

例如:adc ax,bx实现的是:(ax) = (ax) + (bx) + CF

在这里插入图片描述
先来看一下CF的值的含义。在执行adc指令的时候加上的CF的值的含义,是由adc指令前面的指令决定的,也就是说,关键在于所加上的CF 值是被什么指令设置的。显然,如果CF 的值是被sub指令设置的,那么它的含义就是借位值;如果是被add 指令设置的,那么它的含义就是进位值。

在这里插入图片描述
可以看出,加法可以分两步来进行:①低位相加;②高位相加再加上低位相加产生的进位值。

与下面的指令有相同的效果

add al,bl
adc ah,bh

CPU提供adc指令的目的,就是用来进行加法的第二步运算的

编程任务一:编程计算1EF000和+201000H结果放在AX(高16位),BX(第16位)中

mov ax,001EH
mov bx,0F000H
add bx,1000H
adc ax,0020H

由于加法可以分两步来进行:①低位相加;②高位相加再加上低位相加产生的进位值。

所以我们先计算将第一个数值的低位放置在BX寄存器中,然后率先进行计算。而将第一个数的高位放在AX寄存器中,执行ADC指令。此时即可算出答案

编程任务二:编程计算1EF0001000H + 2010001EF0H,结果放在ax(最高16位),bx(次高16位),cx(低16位)

mov ax,001EH
mov bx,0F000H
mov cx,1000H
add cx,1EF0H
adc bx,1000H
adc ax,0020H

原理:

(1)先将低16位相加,完成后,CF中记录本次相加的进位值;

(2)再将次高16位和CF(来自低16位的进位值)相加,完成后,CF中记录本次相加的进位值;

(3)最后高16位和CF(来自次高16位的进位值)相加,完成后,CF中记录本次相加的进位值。

编程任务三:编程计算两个128位数据的和

在这里插入图片描述

inc和 loop指令不影响CF位,思考一下,上面的程序中,能不能将4个inc 指令,用add si,2和add di,2取代,答案是不可以,因为ADD指令会对CF造成影响

11.7、SBB指令

sbb指令是带借位的减法指令,它利用了CF位上记录的借位值

格式:sbb 操作对象1,操作对象2

功能:操作对象1 = 操作对象1 - 操作对象2 - CF

比如:sbb ax,bx实现的功能就是(ax) = (ax) - (bx) - CF

编程任务一:编写程序计算003E1000H - 00202000H

mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H

还是从低位开始减起,然后结合CF再次进行高位的运算

sbb和adc是基于相同的思路设计的两条指令,在应用思路上和adc类似

11.8、cmp指令

cmp指令

cmp是比较指令,功能相当于是减法指令,但是不保存结果

cmp指令执行之后,将对标志寄存器产生影响,其他相关的指令通过识别这些被影响的标志寄存器位来得知比较结果

cmp指令格式:

cmp 操作对象1,操作对象2

功能:计算机操作对象1 - 操作对象2但是不保存结果,仅仅根据计算的结果对标志寄存器进行设置

例一:cmp ax,ax做ax-ax的操作,但是结果不会存放到ax中,只是影响flag

执行之后:

  • ZF = 1
  • PF = 1
  • CF = 0
  • SF = 0
  • OF = 0

例二:

mov ax,8
mov bx,3
cmp ax,bx
执行之后:ax中存放的数据依旧为8

  • ZF = 0
  • PF = 1
  • SF = 0
  • CF = 0
  • OF = 0

通过cmp指令执行后,标志的数值,我们可以看出比较的结果

例如:cmp ax,bx

在这里插入图片描述
现在我们可以看出比较指令的设计思路,即:通过做减法运算,影响标志寄存器,标志寄存器的相关位记录了比较的结果。

反过来看上面的例子。执行之后

在这里插入图片描述

同add、sub指令一样,CPU在执行 cmp 指令的时候,也包含两种含义:进行无符号数运算和进行有符号数运算。所以利用cmp指令可以对无符号数进行比较,也可以对有符号数进行比较。上面所讲的是用cmp进行无符号数比较时,相关标志位对比较结果的记录。

下面我们再来看一下如果用cmp来进行有符号数比较时,CPU用哪些标志位对比较结果进行记录。我们以 cmp ah,bh为例进行说明。

例如:cmp ah,bh

在这里插入图片描述

根据上述的例子我们是否可以总结为SF = 1就说明操作对象1<操作对象2呐?

答案是否定的,例如:(ah) = 22H,(bh) = 0A0H

则此时(ah) - (bh) = 34-(-96) = 130 = 82H,82H是-126的补码,所以SF = 1,此时发生了溢出

两个有符号数A和B相减,得到的是负数,那么可以肯定A<B,这个思路没有错误,关键在于我们根据什么来断定得到的是一个负数。CPU 将cmp 指令得到的结果记录在flag 的相关标志位中。我们可以根据指令执行后,相关标志位的值来判断比较的结果。单纯地考查sf 的值不可能知道结果的正负。因为sf记录的只是可以在计算机中存放的相应位数的结果的正负。比如 add ah,al执行后,sf记录的是 ah中的8位二进制信息所表示的数据的正负。cmp ah,bh 执行后,sf 记录的是(ah)-(bh)所得到的8位结果数据的正负,虽然这个结果没有在我们能够使用的寄存器或内存单元中保存,但是在指令执行的过程中,它暂存在CPU内部的暂存器中。

所得到的相应结果的正负,并不能说明,运算所应该得到的结果的正负。这是因为在运算的过程中可能发生溢出。如果有这样的情况发生,那么,sf 的值就不能说明任何问题。

我们知道实际结果的正负之所以不能够说明逻辑上真正的结果的正负,关键原因在于发生了溢出,如果没有溢出的话,那么实际结果的正负就会和逻辑上真正结果的正负一致

所以我们在考察SF正负的同时也要考察OF有没有溢出

以cmp ah,bh为例:

  • 如果SF = 1且OF = 0
    • 说明没有溢出,逻辑上真正的结果的正负 = 实际的结果的正负
  • 如果SF = 1且OF = 1
    • 说明发生了溢出,逻辑上真正的结果的正负与实际的结果的正负相反
    • 说明了(ah)>(bh)
  • 如果SF = 0且OF = 0
    • 说明没有溢出,逻辑上真正的结果的正负 = 实际的结果的正负
  • 如果SF = 0且OF = 1
    • 说明发生了溢出,逻辑上真正的结果的正负与实际的结果的正负相反
    • 说明了(ah)<(bh)

上面,我们深入讨论了cmp指令在进行有符号数和无符号数比较时,对flag 相关标志位的影响,和CPU 如何通过相关的标志位来表示比较的结果。在学习中,要注意领会8086CPU这种工作机制的设计思想。实际上,这种设计思想对于各种处理机来说是普遍的。

下面的内容中我们将学习一些根据cmp指令的比较结果(即 cmp指令执行后,相关标志位的值)进行工作的指令。

11.9检测比较结果的条件转移指令

在这里插入图片描述

编程练习:编程实现如果(ah) = (bh),则(ah) = (ah) + (ah),否则(ah) = (ah) + (bh)功能

cmp ah,bh
je  s
add ah,bh
jmp short OK
s:add ah,ah
OK:ret
这个程序的核心是ZF = 1时发生跳转	
相等则转移这种逻辑含义,是通过和cmp指令配合使用来体现的,因为cmp指令为ZF = 1赋予了“两个数相等的含义”

究竟在je 之前使不使用cmp指令,在于我们的安排。je检测的是zf位置,不管je前面是什么指令,只要CPU执行je指令时,zf=1,那么就会发生转移

例如:

mov ax,0
add ax,0
je s
inc ax
s:inc ax
执行之后AX寄存器中的数值为0,则ZF = 1,则需要进行跳转

CPU提供了cmp指令,也提供了je等条件跳转指令,如果可以将它们进行配合使用,则可以实现根据比较结果进行转移的功能

对于jne、jb、jnb、ja、jna等指令和 cmp指令配合使用的思想和je 相同

根据有符号数的比较结果进行转移的条件转移指令的工作原理和无符号的相同,只是检测了不同的标志位。

11.10、DF标志和串传送指令

flag的第十位是DF,方向标志位

在传处理指令中,控制每次操作之后si di的增减

  • DF = 0:每次操作后si,di递增
  • DF = 1:每次操作后si,di递减

格式:movsb

功能:以字节位单位传送

  • ((es) * 16 + (di)) = ((ds) * 16 + (si))
    • 表示的是将段地址位ds偏移地址位si的地址,送到段地址位es,偏移地址位di的内存中
  • 如果DF = 0则:(si) = (si) +1,(di) = (di) +1
  • 如果DF = 1则: (si) = (si) -1,(di) = (di) -1

当然我们也可以传送一个字:movsw,则此时每次递增递减的数值就变成了2

一般来说movsb和mpvsw都是配合rep进行使用,格式:rep movsb

rep的作用是根据cx寄存器中的数值,重复执行后面的串传送指令

CPU提供了两条指令对DF进行设置:

  • cld指令:表示将标志寄存器DF位置设置为0
  • std指令:表示将婊子寄存器DF位置设置为1

编程一:用串传输指令将data段中的第一个字符串复制到它后面的空间中

data segment 
	db 'welcome to masm'
	db 16 dup(0)
data ends

需要考虑的信息:

  • 传送的原始位置为:ds:si —>data:0
  • 传送的目的位置为:es:di —>data:16
  • 传送的长度为:cx —>16
  • 传送的方向为:DF —>正方向传输,所以要使得si,di递增,所以设置为DF = 0
code segment

start: mov ax,data
	   mov ds,ax
	   mov si,0		;ds:si指向data:0
	   mov es,ax	
	   mov di,16	;es:di指向data:16
	   mov cx,16
	   
	   cld			;设置DF = 0,正方向
	   rep movsb	
	   mov ax,4C00H
	   int 21H
code ends
end start	

编程练习二:用串传送指令,将F000H段中的最后16个字符复制到data段中

data segment
	db 16 dup(0)
data ends

分析:由于要传送的指令在F000H段的最后16个单元中,那么它的最后一个字符的位置为:F000:FFFF

我们可以将ds:si指向F000H的最后一个单元,将es:di指向data段中的最后一个字节,然后逆向传送16个字节即可,即将DF设置为1

  • 传送的原始位置为:ds:si —>F000H:FFFF
  • 传送的目的位置为:es:di —>data:15
  • 传送的长度为:cx —>16
  • 传送的方向为:DF —>逆方向传输,所以要使得si,di递减,所以设置为DF = 1
code segment
start: mov ax,0F000H
	   mov ds,ax
	   mov si,FFFFH
	   mov ax,data
	   mov es,ax
	   mov di,15
	   mov cx,16
	   
	   std
	   rep movsb
	   
	   mov ax,4C00H
	   int 21H
code ends
end start

11.11、pushf和popf

pushf:将标志寄存器的值压栈

popf:从栈中弹出数据,送入到标志寄存器中

两者为直接访问标志寄存器提供了一种方法

11.12、标志寄存器在Debug中的表示

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_60363168/article/details/127758915