标志寄存器(8086CPU)
作用
- 用来存储相关指令的某些执行结果
- 用来为CPU执行相关指令提供行为依据
- 用来控制CPU的相关工作方式
大小
标志寄存器有16位
使用方式
标志寄存器是按位起作用,也就是说每一个位都有专门的含义,记录特定的i西南西
flag的1、3、5、12、13、14、15位在8086CPU中没有使用,不具有任何含义。而0、2、4、6、7、8、9、10、11位都具有特殊的含义
ZF标志,在第6位,结果为0则为1,否则为0
zf (Zero Flag) 是零标志位,在第6位;它记录相关指令执行后,其结果是否为0,如果为0,那么
zf = 1
;否则zf = 0
例如
mov ax, 1 sum ax, 1
执行后, 结果为0,则zf = 1
mov ax, 2 sum ax, 1
执行后,结果不为0,则zf = 0
注意
在指令执行的过程中,有些指令对标志寄存器有影响,比如:add、sum、mul、div、inc、or、and等,他们大都是运算指令;而有些指令对标志寄存器没有影响,比如:mov、push、pop等,他们大都是传送指令。在使用一条指令的时候,要注意这条指令的全部功能,其中包括,执行结果对标志寄存器的哪些标志位产生了影响。
PF标志,在第2位,结果中1的个数为偶数则为1,否则为0
pf( Parity Flag)表示奇偶标志位,在第2位;他记录的是相关指令执行后,如果所有的bit位中1的个数是否为偶数。如果1的个数位偶数,
pf = 1
,如果为奇数,那么pf = 0
例如:
mov al, 1 add al, 10
执行后结果为1011B,其中有3(奇数)个1,则pf = 0
mov al, 1 or al, 2
执行后,结果为:11B,其中有2(偶数)个1,则pf = 1
SF 标志,在第7位,结果为负则为1,否则为0;有符号运算有效
sf(Symbol Flag)表示符号标志位,在第7位;它记录相关指令执行后,其结果是否为负。如果为负,则
sf = 1
;如果非负,sf = 0
通常CPU计算的时候有两种方式,一种是有符号的计算,一种是无符号的计算。
SF标志,就是CPU对有符号数运算结果的一种记录,他记录数据的正负。
如果我们将数据进行无符号运算,SF的值则没有意义,虽然相关指令影响了他的值
例子:
mov al, 10000001B add al, 1
执行后,结果为10000010B
,sf = 1
,表示:如果指令进行是有符号运算,那么结果为负
mov al, 10000001B add al, 01111111B
执行后,结果为0,sf = 0;表示:如果指令记性的是有符号数运算,那么结果为0
CF标志,在第0位,存储进位或借位的值
cf(Carry Flag)表示进位标志位,在第0位。在进行无符号运算的时候,他记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值
对于位数为N的无符号数来说,其对应的二进制信息的最高位,即第N-1为,就是他的最高位,而假想存在的第N位,就是相对于最高有效位的更高位
我们都知道,在两个数相加的时候,会往更高的位进位;比如98H + 98H
,将产生进位。由于这个进位值在8位数中无法进行报错。我之前认为这个数就丢失了。其实CPU在运算的时候,并不丢弃这个值,而是记录在CF标志位上面。
例如
mov al, 98H add al, al
执行后:(al) = 30H, CF = 1
,CF 记录了从最高有效位向更高位的进位值
再次执行add al, al
执行后:(al) = 60H, CF = 0
, CF记录了从最高有效位向更高位的进位值
而在做减法的时候,有可能向更高位借位;比如,两个8位数据97H - 98H
,将产生借位。借位后相当于计算197H-98H
;CF来记录这个借位值,例如下面的指令
mov al, 97H sub al, 98H ; 执行后:(al) = FFH, CF=1, CF记录了向更高位的借位值 sum al, al ; 执行后:(al) = 0, CF = 0, CF 记录了向更高位的借位值
OF标志,在第11位,结果溢出则为1,否则为0;没理解透彻
of( Overflow Flag)表示溢出标志位,在第11位。它记录了有符号数运算的结果是否产生了溢出,如果产生了溢出,
of = 1
;如果没有,OF = 0
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
。对于无符号数运算,0F0 + 88H
有进位CF= 1
, 对于有符号运算,0F0H + 88H
发生溢出,OF = 1
mov al, 0F0H add al, 78H
add指令执行后:CF = 1, OF = 0
。对于无符号运算,0F0 + 78H
有进位,CF = 1
;对于有符号运算,0F0 + 78H
不会发生溢出, OF = 0
每一个有符号数,似乎都对应着一个无符号数,我们如何知道CPU在执行运算指令的时候,进行的是有符号运算还是无符号运算呢??????????????
DF标志
df( Direction Flag), 第10位。在串处理指令中, 控制每次操作后si、di的递减
df = 0
: 每次操作后si、di递增
df = 1
:每次操作后si、di递减
通过cld和std指令来进行设置DF的标志
然后用req movsb/movsw进行复制数据
串传送指令movsb
将
ds:si
指向的内存单元中的字节送入es:di
中,然后根据标志寄存器df
位的值,将si
和di
递增或递减
格式:movsb
功能:执行movsb
指令相当于执行下面几步操作
((es) * 16 + (di)) = ((ds) * 16 + (si))
- 如果
df = 0
则:(si) = (si) + 1
,(di) = (di) + 1
- 如果
df = 1
则:(si) = (si) - 1
,(di) = (di) - 1
movsb
的功能可以这样描述mov es:[di], byte ptr ds:[si]
,但是8086CPU没有这样的指令。
如果df = 0
inc si inc di
如果df = 1
dec si dec di
串传送指令movsw
movsw功能是将
ds:si
指向的内存单元中的字节送入es:di
中,然后根据标志寄存器df
位的值,将si
和di
递增2或递减2
movsw
的功能可以这样描述mov es:[di]. word ptr es:[si]
8086不支持这样的指令,这里只是描述
如果df = 0
add si, 2 add di, 2
如果df = 1
sub si, 2 sub di, 2
movsb和movsw进行的是串传送操作中的一个步骤,一般来说,movsb
和movsw
都和req
配合使用,格式如下
reg movsb
用汇编语法来描述req movsb
的功能是:
s: movsb loop s
cld和std设置DF标志位
cld 指令:将标志寄存器df位,置0
std 指令:将标志寄存器的df位,置1
adc 指令,带位加法指令,用于计算特别大的数据
带进位加法指令,它利用了CF位上记录的进位值
指令格式:abc 操作对象1, 操作对象了
功能:操作对象1 = 操作对象1 + 操作对象2 + CF
比如指令:adc ax, bx
实现的功能就是:(ax) = (ax) + (bx) + CF
例:
mov ax, 2 mov bx, 1 sub bx, ax adc ax, 1
执行后,(ax) = 4
。adc执行时,相当于计算:(ax) + 1 + CF = 2 + 1 + 1 = 4
mov ax, 1 add ax, ax adc ax, 3
执行后,(ax = 5)
. adc执行时,相当于计算: (ax) + 3 + CF = 2 + 3 + 0 = 5
mov al, 98H add al, al adc al, 3
执行后,(al) = 34H
。adc执行时,相当于计算:(al) + 3 + CF = 30H + 3 + 1 = 34H
可以看出,adc指令比add指令多加了一个CF位的值。
为啥要加呢?为啥要有这样一条指令呢? 看一下两个数据0198H和0183H
可以看出加法是分两步进行的
- 低位相加
- 高位相加再加上低位相加产生的进位值
下面的指令和add ax, bx
具有相同的值
add al, bl adc ah, bh
CPU提供adc指令的目的,就是来进行加法的第二步运算的,比如我们需要计算特别大的数据的时候可以采用这种方式
sbb 指令, 带借位减法指令,用于运算特别大的数据
带位减法指令,利用CF位上记录的借位值
指令格式: sbb 操作对象1,操作对象2
功能:操作对象1 = 操作对象1 - 操作对象2 - CF
比如指令sbb ax, bx
实现的功能是:(ax) = (ax) - (bx) - CF
cmp指令,这个玩意有点复杂(P234, 11.8)
cmp是比较指令,cmp的功能相当于减法指令,只是不保存结果。指令执行后将对标志寄存器产生影响
指令格式: cmp 操作对象1,操作对象2
功能:计算操作对象1-操作对象2但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。
比如,指令cmp ax, ax
做(ax)-(ax)
的运算,结果为0,但并不在ax中保存,仅仅影响flag的想改给为。指令执行后:zf = 1, pf = 1, sf = 0, cf = 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
如果(ax) = (bx)
则(ax) - (bx) = 0
, 所以zf = 1
;
如果(ax) != (bx)
则(ax) - (bx) != 0
, 所以zf = 0
;
如果(ax) < (bx)
则(ax) - (bx) 将产生借位
, 所以cf = 1
;
如果(ax) >= (bx)
则(ax) - (bx) 不必借位
, 所以cf = 0
;
如果(ax) > (bx)
则(ax) - (bx)既不借位,结果又不为0
, 所以cf = 1并且zf = 0
;
如果(ax) <= (bx)
则(ax) - (bx) 既可能借位,结果可能为0
, 所以cf = 1或zf = 1
;
指令cmp ax, bx
的逻辑含义是比较ax和bx中的值,如果执行后
zf = 1
,说明(ax)=(bx)
zf = 0
,说明(ax)!=(bx)
cf = 1
,说明(ax)<(bx)
cf = 0
,说明(ax)>=(bx)
cf = 0并且zf = 0
,说明(ax)>(bx)
cf = 1或zf = 1
,说明(ax)<=(bx)
以上是无符号的比较逻辑
cmp既可以对无符号进行比较,也可以对有符号进行比较,上面所说的是对无符号数进行比较时,相关标志位对比结果的记录
有符号的比较结果
如果因为溢出导致实际结果为负,那么逻辑上真正的结果必然为正。
如果因为溢出导致实际结果为正,那么逻辑上真正的结果必然为负
pushf和popf
pushf的功能是将标志寄存器的值压栈,而popf是从栈中弹出数据,送入标志寄存器中
pushf和popf,为直接访问标志寄存器提供了一种方法