第十一章 标志寄存器
第十一章 标志寄存器
11.0 绪论
标志寄存器的作用:
(1) 存储相关指令的执行结果
(2) 为CPU执行相关指令提供行为依据
(3) 控制CPU的相关工作方式
标志寄存器(flag)中存储的信息叫做程序状态字(PSW)
flag按位起作用,结构如图11.1
其中的1、3、5、12、13、14、15位在8086CPU中不起作用
11.1~11.5 标志寄存器的各个标志位
1.ZF标志(zero flag,零标志位)
记录相关指令执行后,结果是否为0
如果为0,那么zf=1
如果不为0,那么zf=0
(注:在计算机中1表示逻辑真(TRUE),0表示逻辑假(FALSE))
add sub mul div inc or and 等运算指令影响标志寄存器
mov push pop 等传送指令不影响标志寄存器
2.PF标志(parity flag,奇偶标志位)
记录相关指令执行后,其结果中的所有bit位中1的个数是否为偶数
若1的个数为偶数,pf=1
若1的个数为奇数,pf=0
3.SF标志(sign flag,符号标志位)
附注2 补码总结、原码反码补码
注:此时要指名有符号数的长度(byte/word/dword/qword)
1. 用数据的最高位作为符号位,0表示正数,1表示负数
2. 将数在机器中的编码称为机器数(如10000001),将原来一般的书写方式表示的数称为真值(如-0000001).
3. 原码:原本的数字不变,最高位作为符号位
如(当长度为byte时)1100110的原码=01100110,-1100110的原码=11100110
原码中0会出现重码(00000000(+0)和10000000(-0))
4. 反码:正数的反码就是它本身,负数的反码是将原码除了符号位以外每个为按位取反
如(当长度为byte时)1100110的反码=01100110,-1100110的反码=10011001
反码中0会出现重码(00000000(+0)和11111111(-0))
5. 补码:正数的补码就是它本身,负数的补码是将反码加1
如(当长度为byte时)1100110的补码=01100110,-1100110的补码=10011010
补码中每个数都是唯一的
*负数补码也可以用0向一个”假想的最高位的前一位”借1,与自己相反数的补码相减得到
如(当长度为byte时)-1100110的补码=(1)00000000-01100110=10011010
最高位的前一位看作1
6. 正数的补码取反加1后,为其对应的负数的补码,负数的补码取反加1后为其绝对值
7. n位二进制数的各个编码能表示的有符号数的范围如下:
原码 -2^(n-1)-1~2^(n-1)-1
反码 -2^(n-1)-1~2^(n-1)-1
补码 -2^(n-1)~2^(n-1)-1
记录相关指令执行后结果是否为负(此时将结果看作有符号数),
如果为负sf=1
如果为非负数,sf=0
对于同一二进制数,可以当作有符号数计算,也可以当作无符号数计算
在将结果当作无符号数时,sf的值没有意义
检测点11.1
写出下面每条指令执行后,ZF、PF、SF、等标志位的值。
sub al,al al=0h ZF=1 PF=1 SF=0
mov al,1 al=1h ZF=1 PF=1 SF=0
push ax ax=1h ZF=1 PF=1 SF=0
pop bx bx=1h ZF=1 PF=1 SF=0
add al,bl al=2h ZF=0 PF=0 SF=0
add al,10 al=12h ZF=0 PF=1 SF=0
mul al ax=144h ZF=0 PF=1 SF=0
4.CF标志(carry flag,进位标志位)
在进行无符号数运算时,它记录了运算结果的最高有效位向更高位的进位值(或借位值)
如:1111 1111B+1B=1 00000000B
结果中的1就在假想的最高位,即CF标志位中
如果是97H-98H,则向CF借位,cf=1,运算变为197H-98H=FFH
有进位或借位时cf=1,吗,没有则cf=0
5.OF标志(overflow flag,溢出标志位)
在进行有符号数运算时,若结果超过了机器所能表示的范围叫做溢出
如99+98=197>127,超过了8位有符号数的范围,便是溢出
溢出时of=1,没有溢出of=0
注意:要分清判断对象是有符号数还是无符号数
检测点11.2
写出下面每条指令执行后,ZF、PF、SF、CF、OF等标志位的值。
al CF OF SF ZF PF
sub al,al 0h/0000 0000b 0 0 0 1 1
mov al,10h 10h/0010 0000b 0 0 0 1 1
add al,90h a0h/1010 0000b 0 0 1 0 1
mov al,80h 80h/1000 0000b 0 0 1 0 1
add al,80h 0h/0000 0000b 1 1 0 1 1
mov al,0fch 0fch/1111 1100b 1 1 0 1 1
add al,05h 1h/0000 0001b 1 0 0 0 0
mov al,7dh 7dh/1111 1101b 1 0 0 0 0
add al,0bh 88h/1000 1000b 0 1 1 0 1
11.6 adc指令
adc为带进位加法指令,利用了cf位上记录的进位值
格式 adc oper1,oper2
功能 oper1=oper1+oper2+CF
使用:进行大数相加,可以将加法分为两步:(1)低位相加;(2)高位相加再加上低位相加的进位值
例1:32位加法:计算1EF000H+201000H,结果在ax(高16位),bx(低16位)
mov ax,001EH
mov bx,0F000H
add bx,1000H
adc ax,0020H
adc指令执行后可能产生进位设置cf
例2:编写子程序,进行128位加法
名称:add128
功能:两个128位数据相加
参数:ds:si指向存储第一个数的空间(此时需要8个字单元),由低地址到高地址依次存放128位数据由低到高的各个字,运算结果存储在第一个数的存储空间中。
ds:di指向存储第二个数的内存空间
Program 11-1 ADD128程序
add128:
push ax
push cx
push si
push di
sub ax,ax;将cf设为0
mov cx,8
s:mov ax,[si]
adc ax,[di]
mov [si],ax
inc si
inc si
inc di
inc di
loop s
pop di
pop si
pop cx
pop ax
ret
inc和loop指令不影响cf位,上面的4个inc指令不能用add代替,因为add影响cf位,会破坏运行结果
11.7 sbb指令
sbb是带借位减法指令,利用了cf记录的借位值
格式 sbb oper1,oper2
功能 oper1=oper1-oper2-cf
应用思路与adc类似
11.8~11.9 cmp指令和条件转移指令
1. cmp为比较指令,功能相当于减法指令,不保存结果但对标志寄存器产生影响
格式 cmp oper1,oper2
功能 计算oper1-oper2,不保存结果,仅仅设置flag
2.▲▲▲cmp指令比较结果通过标志寄存器位的反馈:执行cmp ax,bx
比较内容 | cmp指令结果 | flag寄存器相关位的内容 |
(ax)==(bx) | (ax)-(bx)==0 | zf=1 |
(ax)!=(bx) | (ax)-(bx)!=0 | zf=0 |
(ax)<(bx) | (ax)-(bx)<0 产生借位 | cf=1 |
(ax)>=(bx) | (ax)-(bx)>=0 不借位但结果可能为0 | cf=0 |
(ax)>(bx) | (ax)-(bx)>0 不借位结果也不为0 | cf=0且zf=0 |
(ax)<=(bx) | (ax)-(bx)<=0可能借位也可能结果为0 | cf=1或zf=1 |
3.无符号数的比较:执行cmp 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) |
4.有符号数的比较:执行cmp ah,bh
要注意有没有溢出,没有溢出时实际结果的正负和数学运算上(数学运算时没有溢出)的正负才一致,若溢出了说明不一致
标志寄存器 | 结论 |
zf=1 | (ah)==(bh) |
zf=0 | (ah)!=(bh) |
of=0且sf=1 | (ah)<(bh) 这时没有溢出,说明(ah)<(bh)在数学计算上确实是负数 |
of=1且sf=1 | (ah)>(bh) 溢出导致了实际结果为负,说明在数学计算上(不考虑溢出)结果为正 |
of=1且sf=0 | (ah)<(bh) 溢出导致了实际结果为正,说明在数学计算上(不考虑溢出)结果为负 |
of=0且sf=0 | (ah)>=(bh) |
5.条件转移指令的用法
套路:
cmp oper1,oper2
jxxx 标号
6.根据无符号数的比较结果进行转移
指令 | 含义 | 检测的标志位 |
je/jz | 等于则转移 | zf=1 |
jne/jnz | 不等于则转移 | zf=0 |
jnae/jc/jb | 低于则转移(小于) | cf=1 |
jae/jnc/jnb | 不低于则转移(大于等于) | cf=0 |
ja/jnbe | 高于则转移(大于) | cf=0且zf=0 |
jna/jbe | 不高于则转移(小于等于) | cf=1或zf=1 |
7.根据有符号数的比较结果转移
指令 | 含义 | 检测的标志位 |
jl/jnge | 小于则转移 | sf=1且of=0 |
jnl/jge | 不小于则转移(大于等于) | sf=0且of=0 |
jle/jng | 小于等于则转移 | sf=0或of=0 |
jnle/jg | 不小于等于则转移(大于) | sf=1且of=1 |
8.根据单个标志位转移
指令 | 含义 | 检测的标志位 |
js | 结果为负 | sf=0 |
jns | 结果为非负 | sf=1 |
jo | 结果溢出 | of=1 |
jno | 结果不溢出 | of=0 |
jp | 奇偶位为1 | pf=1 |
jnp | 奇偶位为0 | pf=0 |
*以上指令中每个字母的含义:
j=jump,e=euqal,n=not,b=below(低于),a=above(高于),l=less(小于),g=greater(大于),s=sign,c=carry,
p=parity,o=overflow,z=zero
检测点11.3
(1)补全下面的程序,统计F000:0处32个字节中,大小在[32,128]的数据个数。
mov ax,0f000h
mov ds,ax
mov bx,0;ds:bx指向第一个字节
mov dx,0;初始化累加器
mov cx,32
s: mov al,[bx]
cmp al,32;和32进行比较
jb s0 ;如果低于al转到s0,继续循环
cmp al,128;和128进行比较
ja s0 ;如果高于al转到s0,继续循环
inc dx
s0:inc bx
loop s
[32,128]是闭区间,包括两端点的值
(32,128)是开区间,不包括两端点的值
检测点11.3
(2)补全下面的程序,统计F000:0处32个字节中,大小在(32,128)的数据个数。
mov ax,0f000h
mov ds,ax
mov bx,0;ds:bx指向第一个字节
mov dx,0;初始化累加器
mov cx,32
s: mov al,[bx]
cmp al,32;和32进行比较
jna s0 ;如果不高于al转到s0,继续循环
cmp al,128;和128进行比较
jnb s0 ;如果不低于al转到s0,继续循环
inc dx
s0:inc bx
loop s
11.10 DF标志(direction flag,方向标志位)和串传送指令
1.DF标志在处理指令中控制每次操作后si、di的增减
df=0,每次操作后si、di递增
df=1,每次操作后si、di递减
2. 串传送指令
格式:movsb
功能:将ds:si指向的内存单元中的字节送入es:di中,再根据df的值将si和di递增或递减,即
mov es:[di],byte ptr ds:[si];8086不支持该指令,此处用作功能描述,是伪代码
if(df==0):
inc si
inc di
if(df==1):
dec si
dec di
格式:movsw
功能:与movsb类似,不过传送的是字,即
mov es:[di],word ptr ds:[si];8086不支持该指令,此处用作功能描述,是伪代码
if(df==0):
add si,2
add di,2
if(df==1):
sub si,2
sub di,2
一般movsb和movsw与rep配合使用,格式:rep movsb或rep movsw
相当于:
s:movsb
loop s
rep根据cx的值重复执行串传送指令
设置df位的方法:
(1) cld指令(clean):将df置为0;(2)std指令(setup):将df置为1
11.11 pushf将标志寄存器的值压栈,popf从栈中弹出数据送入标志寄存器
检测点11.4
下面指令执行后,(ax)= 45h
mov ax,0
push ax
popf
mov ax,0fff0h
add ax,0010h
pushf
pop ax
and al,11000101B
and ah,00001000B
推算过程:
popf后,标志寄存器中,本章节介绍的那些标志位都为0(但是此时标志寄存器并不是所有位置都为0,没学过的位置用*先代替),向下进行,那么pushf将计算后的当时状态的标志寄存器入栈,然后pop给ax,这是ax是寄存器的值(这个值中包含了我们的*号),接下来就是对那些没有学过的标志位的屏蔽操作,这就是最后两条指令的意义所在,将不确定的位置都归0,那么只剩下我们能够确定的位置了,所以,结果就可以推理出来了。
mov ax,0
push ax
popf
mov ax,0fff0h
add ax,0010h
pushf
pop ax 0 0 0 0 of df if tf sf zf 0 af 0 pf 0 cf
0 0 0 0 0 0 * * 0 1 0 * 0 1 0 1
ax=flag=000000** 010*0101b
and al,11000101B al=01000101b=45h
and ah,00001000B ah=00000000b=0h
11.12标志寄存器在debug中的显示
标志 | 值为1的标记 | 值为0的标记 |
of | OV,overflow | NV,not overflow |
sf | NG,negative | PL,positive |
zf | ZR,zero | NZ,not zero |
pf | PE,even | PO,odd |
cf | CY,carry | NC,not carry |
df | DN,down | UP |
实验11 编写子程序
实验11(letterc子程序)小写改成大写
编写一个子程序,将包含任意字符,以0结尾的字符串的小写字母转变成大写字母。
Program ex-11-1 自主解法
assume cs:codesg
datasg segment
db "Beginner's All-purpose Symbolic Instruction Code.",0
datasg ends
stack segment
db 64 dup(0)
stack ends
codesg segment
begin:
mov ax,datasg
mov ds,ax
mov si,0 ;ds:si指向第一个字节
mov ax,stack
mov ss,ax
mov sp,64
call letterc
mov ax,4c00h
int 21h
letterc:
push ax
push si
letc_start:
mov al,[si]
;if(al==0)
cmp al,0
je ok
;if(97<=al && al<=122)
;测试第一个条件
cmp al,97
jae s1
;else
jmp next
;测试第二个条件
s1: cmp al,122
jbe s2
;else
jmp next
s2:;满足两个条件,进行转换
sub al,32
mov [si],al
jmp next
next:;下一个
inc si
jmp letc_start
ok: pop si
pop ax
ret
codesg ends
end begin
Program ex-11-2 参考解法
assume cs:codesg
datasg segment
db "Seginner's All-purpose Symbolic Instruction Code.",'0'
datasg ends
codesg segment
begin:
mov ax,datasg
mov ds,ax
mov si,0;ds:si指向第一个字节
call letterc
mov ax,4c00h
int 21h
;名称:letterc
;功能:将以0结尾的字符中的小写字母转变成大写字母
;参数:ds:si指向字符串首地址
letterc:push ax
push si
let:cmp byte ptr [si],0;和0进行比较
je let0;如果等于0则转到let0,结束
cmp byte ptr [si],61h;和61h进行比较
jb let1;如果低于60h则转到let1,继续循环
cmp byte ptr [si],86h;和86h进行比较
ja let1;如果高于86h则转到let1,继续循环
mov al,[si]
sub al,20h;转为大写字母
mov [si],al
let1:
inc si
jmp let
let0:
pop si
pop ax
ret
codesg ends
end begin
