[从零学习汇编语言] - 标志寄存器

2023-10-27


前言

点赞再看,养成习惯!

今天我们来讲一下常见CPU组成中的最后一个寄存器:标志寄存器.


一、标志寄存器的简介

CPU内部的寄存器中,有一种特殊的寄存器(对于不同的CPU可能其构造和数量均不同),其主要有三中作用:

  1. 用来存储相关指令的某些执行结果
  2. 用来为CPU执行相关指令提供行为依据
  3. 用来控制CPU的相关工作方式

这种寄存器在我们8086CPU中被称为标志寄存器。8086CPU的标志寄存器有16位,其中存储的信息通常被称为程序状态字(PSW),与其他寄存器不同的是,其他寄存器都是整个寄存器用来存放特定具数据,而标志寄存器则是按照每一位来记录特定信息。比如8086CPU的结构图如下:

在这里插入图片描述
flag寄存器中的1,3,5,12,13,14,15位在8086CPU中并没有被应用,不具备任何含义。而剩余各位则都具有其特殊含义,接下来我们就要详细的讲一下剩下的其他各位。

二、标志位详解

2.1 ZF标志

ZF标志位是flag寄存器的第六位,我们可以将其理解为零标志位。它主要是用来记录相关指令执行后,其结果是否为0,如果结果为0则 zf = 1 否则为0。比如我们可以执行如下程序:

assume cs:code
code segment 
		main:
		     mov ax,1
			 sub ax,1
code ends 
end main 

运行结果:
在这里插入图片描述
运行结果中的nz代表当前结果值非0,zr代表当前结果值为0。

2.2 PF标志

奇偶标志位,它负责记录相关指令执行后,其结果的所有bit位中1的个数是否为偶数,如果1为偶数则pf=1 反之为0。

assume cs:code
code segment 
     main :  mov al,2
			 add al,10
code ends 
end main

运行结果:
在这里插入图片描述
我们通过运行结果对比可以知晓:PO代表基数,PE代表偶数

2.3 SF标志

flag寄存器的第七位是符号标志位,它用来标志程序的运行结果是否为负,若为负则sf=1,反之为0。

assume cs:code
code segment 
     main :  mov al,1
			 sub al,2
code ends 
end main

运行结果:
在这里插入图片描述
我们通过运行结果对比可以知晓:NC代表整数,CY代表负数

2.4 CF标志

在学习CF标志之前,我们要先搞清楚一个基础概念:有符号运算无符号运算

2.4.1 无符号运算

什么是无符号运算?无符号运算更像是数的绝对值,比如我们的二进制串1111 1111在无符号运算的时候其表达的含义就是十进制的255,此时用来参与运算的1111 1111是不会考虑正负的。

2.4.2 有符号运算

但是同样是1111 1111如果我们想要其表达出正负,按照约定我们就需要通过二进制串的第一位来表示数的正负,正数为0,负数为1。同时为了通过加法代替减法(比如 1 + (-1) 等价于1 - 1),负数在表达时会采用其真码补码来进行表示。比如我们说数字-1,其真码就是1000 0001,其反码就是1111 1110,进一步我们能推出其补码就是1111 1111 。是不是很熟悉,没错这就是我们无符号运算中的255


我们再回到CF标志位,CF标志位处于flag寄存器的第0位,其主要是用来标识无符号计算时运算结果向更高位进位(或借位)的状态。比如8位寄存器中我们进行一个简单的加法:十六进制的 80 + 7F,对应的程序源码就是:

assume cs:code
code segment 
     main :  mov al,80H
			 mov bl,7FH
             add al,bl
code ends 
end main

运行结果:
在这里插入图片描述

那么如果是无符号运算的80+80呢?我们再来试一下:

assume cs:code
code segment 
     main :  mov al,80H
			 mov bl,80H
             add al,bl
code ends 
end main

运行结果:
在这里插入图片描述
我们预期的结果应该是十六进制的100,但是此时由于AL寄存器只能显示八位数字,因此只能显示出低位的00,而消失不见的1此时其实就由CF标志位进行标注。

2.5 OF标志

OF标志位处于flag寄存器的第11位,它的作用是记录有符号运算时的溢出情况,比如我们一个8位寄存器可以表达十进制有效范围为:+127 到 -128 ,那如果我们进行的运算超过了这个范围怎么办?这种情况我们就称之为发生了模溢出,此时我们就需要使用OF寄存器进行记录。
我们简单的通过计算十进制的127+1(即十六进制的7F+1)来模拟一下溢出场景。

assume cs:code
code segment 
     main :  mov al,7fH
			 mov bl,1H
             add al,bl
code ends 
end main

运行结果:
在这里插入图片描述
此时运算结果为二进制的1000 0000即十进制的-128。很明显不符合我们的预期,这就是由于值溢出导致的。

2.5.1 CF标志及OF标志的区别

很多小伙伴 其实并不能很好的区分CF标志位的进位和OF标志位的溢出的概念。首先如果我们使用的是8位寄存器,那么其结构应该如下图所示:
在这里插入图片描述
我们以数据1111 1111为例,如果是无符号运算,此时数据结构如下:
在这里插入图片描述
此时八位数据全部参与运算,其10进制值为255,如果此时我们将该数据+1,则结果应是:
在这里插入图片描述
此时结果值为十进制的256,而由于索引为8的内存单元已经超过了寄存器的长度限制,因此需要借位,因此CF标志需要更改为1,表示发生了借位。


我们再来看下如果此时进行的是符号运算,则同样是元素1111 1111,由于其首位为符号位1表示负数,因此其表现形式为补码,其真值则为-1
在这里插入图片描述

如果此时我们将值+1则其结果为:在这里插入图片描述
此时由于第七位就是我们的符号位,因此溢出的第八位我们无需关注,因此此时数据0000 0000 表示的值便是十进制的0,此时还并没有发生值的溢出。如果我们此时将数据更改为0111 1111,其符号位为正数,则其真值为十进制的+127

在这里插入图片描述
此时值+1则为1000 0000,符号位发生了改变,其真值为十进制的- 128 ,明显不符合我们127 + 1 = 128,发生了值溢出
在这里插入图片描述

此时按照规定,OF符号位的值应为1

2.6 DF标志

DF标志是Flag寄存器的第十位,被称为方向标志位,其作用就是我们在进行串操作的时候进行SI,DI的递减,如果df=0 则每次操作后si,递增;如果df=1,每次操作后si,di递减。其具体应用我们会在稍后通过新的指令进行讲解。

2.7 DOSBOX中的flag标志位表示

在这里插入图片描述

三、Flag标志位的衍生指令

3.1 adc指令

adc 指令是带进位加法指令,它就是利用了CF标志位的进位功能。


指令格式: adc 对象1,对象2
如: adc ax,bx
作用: 对象1 = 对象1+对象2 +cf
如:adc ax,bx -> ax = ax + bx +cf
我们来简单的写个程序:

assume cs:code
code segment 

	main : mov ax,1
		   mov bx,2
		   sub ax,bx ; 此时ax = ffff 发生了借位,cf = 1
		   adc ax,bx ; ax = ffff + 2 + 1 = 2    此时发生进位,cf=1

code ends 
end main 

3.2 sbb指令

sbb 指令是带进位(借位)信息的减法指令,它与adc指令类似,也是利用CF的值进行计算。


指令格式: sbb 对象1,对象2
如: sbb ax,bx
作用: 对象1 = 对象1 - 对象2 - cf
如:sbb ax,bx -> ax = ax - bx - cf
我们来简单的写个程序:

assume cs:code
code segment 

	main :  mov  ax ,2 
            mov bx ,1 
			sbb bx,ax  ; bx = ffff 此时发生借位 cf = 1
			sbb ax,ax ; ax = ax - ax - 1 = ffff

code ends 
end main 

3.3 cmp指令

cmp是比较指令,其功能相当于减法指令,只不过减法的值并不会被保存到寄存器中。当cmp指令执行后,其比较结果会影响flag寄存器,这样我们就可以通过flag寄存器获知比较的结果(主要影响CF,ZF,OF,AF,PF)。那我们该如何判断比较大小的结果呢?
比如:

mov ax,1
mov bx,1
cmp ax,bx

3.3.1 相等

由于cmp ax,bx = ax - bx = 0
因此 zf = 1 说明 ax 等于bx

3.3.2 无符号比较

比如 cmp ax,bx
若CF = 1 由于cmp进行的是减法运算,因此表示此时运算发生了借位,因此ax 小于 bx 反之若CF= 0 则ax >= bx(需要判断zf的值来判断是否相等)

3.3.3 有符号比较

cmp ax,bx
若 SF = 0 OF = 0 则表示无负数,无溢出,则代表ax > bx
若 SF = 1 OF = 0 则表示有负数,无溢出 则 ax < bx
若 SF = 0 OF = 1 则表示无负数 有溢出 则 ax<bx
若 SF = 1 OF = 1 则表示有负数 有溢出 则 ax > bx

3.4 状态位条件转移指令

我们之前有使用过jcxz 转移指令,其通过判断cx寄存器的值来判断是否需要转移,而汇编语言中还存在一些基于Flag寄存器状态位来判断是否转移的指令。

指令 含义 相关状态位的值
je 等于则转移 zf = 1
jne 不等于则转移 zf = 0
jb 低于则转移 cf = 1
jnb 不低于则转移 cf = 0
ja 高于则转移 cf = 0 或 zf=0
jna 不高于则转移 cf =1 或 zf =1

其使用方式与jcxz指令无异,比如我们要判断如果 ax = bx 则cx = 1 反之则为 2

assume cs:code
code segment 
	main :   mov ax,2
		     mov bx,1
		     mov cx,0 
		     cmp ax,bx
		     je equals
		     mov cx,2
  equals :   mov cx,1

code ends 
end main 

3.5 数据传送指令

数据传送指令有很多种,比如movsb,movsw等,它们的使用场景大体相同,我们以movsb举例:

movsb 是一个串传送指令,起作用相当于:

  1. es16 + di = ds16 + si
  2. 如果df = 0 则 di+1 , si+1
  3. 如果df=1 则 di-1 ,si-1

我们用简单易懂的方式翻译一下:当我们调用movsb的时候,我们会将ds:si的数据复制到es:di 。同理的movsw表示为两个字长,因此di及si的递增递减应为2。

这里再提一下rep指令,起作用与loop类似,是根据cx寄存器的值重复执行其后续的串转移指令。

3.6 pushf和popf

pushf的含义就是push flag,它的作用就是将标志寄存器的值压栈,与之相对的就是popf,其作用就是将标志寄存器的值弹栈。两个指令配合使用是为了可以获取当前flag寄存器的值以作判断。比如:

assume cs:code
code segment

	main: 
	      mov ax,2
		  mov bx,1
		  sub ax,bx
		  pushf
		  pop ax
code ends

end main

结语

今天的内容就到此结束了,有疑问的小伙伴欢迎评论区留言或者私信博主,博主会在第一时间为你解答。

码字不易,感到有收获的小伙伴记得要关注博主一键三连,不要当白嫖怪哦~

如果大家有什么意见和建议请评论区留言或私聊博主,博主会第一时间反馈的哦

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[从零学习汇编语言] - 标志寄存器 的相关文章

随机推荐