汇编语言——标志寄存器
标志寄存器
flag 和其他寄存器不一样,其他寄存器是用来存放数据的,都是整个寄存器具有一个含义。
而flag寄存器是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息。
8086CPU的flag寄存器的结构:
ZF标志
flag的第6位是ZF,零标志位(zero flag)。
它记录相关指令(add、sub、mul、div、inc、or、and操作)执行后,结果是否为0 。ZF = 1结果不为0,ZF = 0结果为0。
mov、push、pop等传送指令不修改标志寄存器的信息。
PF标志
flag的第2位是PF,奇偶标志位。
它记录指令执行后,结果的所有二进制位中1的个数是否为偶数,如果为偶数则PF = 1,为奇数,PF = 0。
SF标志
flag的第7位是SF,符号标志位。
它记录指令执行后,结果是否为负(就是看它二进制的第一位是不是1),如果为负数则SF = 1,结果为正,SF = 0。
例如:
我们可以将add指令进行的运算当作无符号数的运算,那么add指令相当于计算129+1,结果为130(10000010B),
也可以将add指令进行的运算当作有符号数的运算,那么add指令相当于计算-(125+1)(补码=-(原码取反+1)),结果为-126(10000010B)。
CF标志
flag的第0位是CF,进位标志位。
一般情况下,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。
比如,两个8 位数据:98H+98H,将产生进位。
由于这个进位值在8位数中无法保存,其实CPU在运算的时候,并不丢弃这个进位值,而是记录在一个特殊的寄存器的某一位上。
8086CPU 就用flag的CF位来记录这个进位值。
比如,两个 8 位数据:97H-98H,将产生借位,借位后,相当于计算197H-98H。
而flag的CF位也可以用来记录这个借位值。
CF在减法时记录借位,加法时记录进位
判断加法是否有进位:两个八位数据相加是否大于255(2^8)
判断减法是否有借位:两个八位数据相加是否小于-255
OF标志
在进行有符号数运算的时候,如结果超过了机器所能表示的范围称为溢出。
mov al,98
add al,99
add指令运算的结果是(al)=0C5H ,因为进行的是有符如果我们用add 指令进行的是有符号数运算,则98+99=-59这样的结果让人无法接受。
造成这种情况的原因,就是实际的结果 197,作为一个有符号数,在 8 位寄存器al中存放不下。
由于在进行有符号数运算时,可能发生溢出而造成结果的错误。
则CPU需要对指令执行后是否产生溢出进行记录。
flag的第11位是OF,溢出标志位。
一般情况下,OF记录了有符号数运算的结果是否发生了溢出。如果发生溢出,OF=1,如果没有,OF=0。
一定要注意CF和OF的区别:
CF是对无符号数运算有意义的标志位,对于无符号数运算,CPU用CF位来记录是否产生了进位;
而OF是对有符号数运算有意义的标志位,对于有符号数运算,CPU 用 OF 位来记录是否产生了溢出,当然,还要用SF位来记录结果的符号。
判断OF是否为1的方法:如果两个正8位数据相加,值为负数时或两个负8位数据相加,值为时数时OF的值为1
1 al CF OF SF ZF PF 2 sub al,al 0h/0000 0000b 0 0 0 1 1 3 mov al,10h 10h/0010 0000b 0 0 0 1 1 4 add al,90h a0h/1010 0000b 0 0 1 0 1 5 mov al,80h 80h/1000 0000b 0 0 1 0 1 6 add al,80h 0h/0000 0000b 1 1 0 1 1 7 mov al,0fch 0fch/1111 1100b 1 1 0 1 1 8 add al,05h 1h/0000 0001b 1 0 0 0 0 9 mov al,7dh 7dh/1111 1101b 1 0 0 0 0 10 add al,0bh 88h/1000 1000b 0 1 1 0 1
指令
adc指令
adc是带进位加法指令 ,它利用了CF位上记录的进位值。
格式:adc 操作对象1,操作对象2
功能:操作对象1=操作对象1+操作对象2+CF
比如:adc ax,bx 实现的功能是:(ax)=(ax)+(bx)+CF
我们来看一下两个数据:0198H和0183H如何相加的:
01 98
+ 01 83
1
-----------------
03 1B
可以看出,加法可以分两步来进行:
(1)低位相加;
(2)高位相加再加上低位相加产生的进位值。
下面的指令和add ax , bx具有相同的结果:
add al,bl ; 低位相加产生进位
adc ah,bh ; 再用adc,高位相加时,加上进位
看来CPU提供 adc 指令的目的,就是来进行加法的第二步运算的。
adc指令和add指令相配合就可以对更大的数据进行加法运算。
sbb指令
sbb是带借位减法指令,它利用了CF位上记录的借位值。
格式:sbb 操作对象1,操作对象2
功能:操作对象1=操作对象1–操作对象2–CF
比如:sbb ax,bx
实现功能:(ax) = (ax) – (bx) – CF
cmp指令***
cmp 是比较指令,功能相当于减法指令,只是不保存结果。
cmp 指令执行后,将对标志寄存器产生影响。
可以通过其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。
格式:cmp 操作对象1,操作对象2
功能:计算操作对象1–操作对象2 但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。
其实,我们通过cmp 指令执行后,相关标志位的值就可以看出比较的结果。
例如:cmp ax,bx
以上可以得出:
上面有CF的值可以判断大小,那么通过OF和SF也可以知道(了解就行)
(1)如果SF=1,而OF=0
OF=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负;
因SF=1,实际结果为负,所以逻辑上真正的结果为负,所以(ah)<(bh)。
(2)如果SF=1,而OF=1
OF=1 ,说明有溢出,逻辑上真正结果的正负≠实际结果的正负;(即OF只要是1,那么逻辑结果就是反的)
因 SF=1 ,实际结果为负,实际结果为负,而又有溢出,这说明是由于溢出导致了实际结果为负,
简单分析一下,就可以看出,如果因为溢出导致了实际结果为负,那么逻辑上真正的结果必然为正。这样,SF=1,OF = 1 ,说明了(ah)>(bh)。
(3)如果SF=0,而OF=1
OF=1 ,说明有溢出,逻辑上真正结果的正负≠实际结果的正负;
因SF=0,实际结果非负,而OF=1说明有溢出,则结果非 0 ,所以,实际结果为正。实际结果为正,而又有溢出,这说明是由于溢出导致了实际结果非负,
简单分析一下,就可以看出,如果因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负。这样,SF=0,OF = 1 ,说明了(ah)<(bh)。
(4)如果SF=0,而OF=0
OF=0,说明没有溢出,逻辑上真正结果的正负=实际结果的正负;
因SF=0,实际结果非负,所以逻辑上真正的结果必然非负。所以(ah)≥(bh)。
检测比较结果的条件转移指令
转移时 转移指令 + 标号
DF标志和串传送指令
flag的第10位是DF,方向标志位。
在串处理指令中,控制每次操作后si,di的增减。
DF = 0:每次操作后si,di递增;
DF = 1:每次操作后si,di递减。
格式1: movsb
功能:(以字节为单位传送)
相当于汇编指令:mov es:[di],byte ptr ds:[si];8086
(1) ((es)×16 + (di)) = ((ds) ×16 + (si))
(2) 如果DF = 0则: (si) = (si) + 1,(di) = (di) + 1 相当于 如果DF=0:inc si,inc di
如果DF = 1则: (si) = (si) - 1,(di) = (di) - 1 相当于 如果DF=1:dec si,dec di
movsb 的功能是将 ds:si 指向的内存单元中的字节送入 es:di中,然后根据标志寄存器DF位的值,将 si和di递增或递减。
格式2:movsw
功能:(以字为单位传送)
相当于汇编指令:mov es:[di],word ptr ds:[si]
(1) ((es)×16 + (di)) = ((ds) ×16 + (si))
(2) 如果DF = 0则: (si) = (si) + 2,(di) = (di) + 2 相当于 如果DF=0:add si,2 add di,2
如果DF = 1则: (si) = (si) - 2,(di) = (di) - 2 相当于 如果DF=1:add si,2 add di,2
movsw 的功能是将 ds:si 指向的内存单元中的字送入 es:di中,然后根据标志寄存器DF位的值,将 si和di递增或递减。
movsb 和 movsw与rep配合使用
rep movsb
用汇编语法来描述rep movsb的功能就是:
s : movsb
loop s
rep movsw
用汇编语法来描述rep movsw的功能就是:
s : movsw
loop s
rep的作用是根据cx的值,重复执行后面的串传送指令。
由于每执行一次movsb指令si和di都会递增或递减指向后一个单元或前个单元,则rep movsb就可以循环实现(cx)个字符的传送。
由于flag的DF位决定着串传送指令执行后,si和di改变的方向,所以我们可以通过DF的值来改变传送的方向
相关指令:
cld指令:将标志寄存器的DF位置0
std指令:将标志寄存器的DF位置1
示例1:用串传送指令,将data段中的第一个字符串复制到它后面的空间中。
data segment
db ‘Welcome to masm!
db 16 dup (0)
data ends
1 mov ax,data 2 mov ds,ax 3 mov si,0 ;ds:si指向data:0 4 mov es,ax 5 mov di,16 ;es:di指向data:16 6 mov cx ,16 ;(cx)=16,rep循环16次 7 cld ;设置DF=0,正向传送 8 rep movsb
示例二:用串传送指令,将F000H段中的最后16个字符复制到data段中。
data segment
db 16 dup (0)
data ends
1 mov ax,0f000h 2 mov ds,ax 3 mov si,0ffffh ;ds:si指向f000:ffff 4 mov ax,data 5 mov es,ax 6 mov di,15 ;es:di指向data:15 7 mov cx ,16 ;(cx)=16,rep循环16次 8 std ;设置DF=1,逆向传送 9 rep movsb
pushf与popf
pushf :将标志寄存器的值压栈;
popf :从栈中弹出数据,送入标志寄存器中。
pushf和popf,为直接访问标志寄存器提供了一种方法。