第十一章 标志寄存器
标志寄存器
1)用来存储相关指令的某些执行结果
2)用来为cpu执行的相关指令提供行为依据
3)用来控制cpu的相关工作方式
标志寄存器flag存储程序状态字(PSW)
8086点flag寄存器各位有不同的含义
CF,PF,AF,ZF,SF,TF,IF,DF,OF
11.1 ZF标志
flag的第六位是ZF,0标志位。记录相关指令执行后,其结果是否为0.如果为0则ZF = 1
例如
mov ax, 1
sub ax, 1
执行以后结果为0 zf = 1
mov ax, 2
sub ax, 1
执行后结果不为0,zf = 0
有些指令会影响zf的比如add, sub, mil, div, inc, or, and等运算指令
mov, push, pop对ZF没有影响不会修改其值,保留原值
11.2 PF标志
奇偶标志位,表示进行相关运算以后,其结果所含的bit为重1的个数是否为偶数,
如果1的个数为偶数则PF=1,如果为奇数,则PF=0
mov al, 1
add al, 10
执行结果为00001011B 含有奇数个1 PF = 0
mov al, 1
or al, 2
执行结果为00000011B 含有偶数个1 则PF = 1
11.3 SF标志
表明相关指令执行后,结果是否为负。如果为负sf=1,如果非负,sf=0
有些指令会影响flag的多个标记为
例如sub al, al
ZF, PF, SF都要收到影响 分别是 1, 1, 0
11.4 CF标志
进行无符号运算的时候,最高位是否进位,或者从更高位借位。
mov al, 98h
add al, all 执行后 al=30h, cf=1,cf记录了从最高有效位向更高位的进位值。
add al, al 执行后 al = 60h, cf = 0
当两个数执行减法的时候,有可能向更高位借位。比如,两个8位数据:97h - 98h CF也记录了这个借位值。
mov al, 97h
sub al, 98h ; 执行后al = ffh, CF = 1 ,CF记录了向更高位借位值
sub al, al ;执行后al=0, cf=0
11.5 OF标志
记录了有符号运算的结果是否发生了溢出。如果发生了溢出则OF=1 如果没有OF = 0
CF是对无符号数有意义的标志位,而OF是对有符号数有意义的标志位(判断两个操作数的符号,和结果的符号。如果两个操作数符号不同则OF必位0.如果结果的符号和两个操作数的符号不同则结果必为1)
SF记录结果的符号
11.6 adc指令
带进位的加法指令,利用了CF位上记录的进位值(可以扩展成32位,64位加法使用)
指令格式 adc 操作对象1, 操作对象2
功能: 操作对象1 = 操作对象1 + 操作对象2 + CF
比如 adc ax, bx (ax) = (ax) + (bx) + CF
例
mov ax, 2
mov bx, 1
sub bx, ax ; bx = 0ffh, cf = 1
add ax, 1 ; ax = 2 + 1 + 1(CF) = 4
mov ax, 1
add ax, ax ; ax = 2, cf = 0
add ax, 3 ; ax = 5 ,cf = 0
mov al, 98H
add al, al ; al = 30h, cf = 1, of = 1
add al, 3 ; al = 30h+3+1 = 34h
以下指令具有和add ax, bx相同的效果
add al, bl
add ah, bh
例如计算 1ef000h + 201000h 结果放在ax(高16bit)和bx(低16bit)中
mov ax, 001eh mov bx, 0f000h add bx, 1000h adc ax, 0020h
adc也可能产生进位CF
计算 1E F000 1000 H + 20 10000 1EF0H 结果放在ax(最高16), bx(次高16), cx(低16位)
mov ax, 001eh mov bx, 0f000h mov cx, 1000h add cx, 1ef0h adc bx, 1000h adc ax, 0020h
编写一个程序进行两个128位数据的相加运算
名称:add128
功能:两个128位数据进行相加
参数:ds:si指向存储第一个数的内存空间,因为数据是128位,所以需要8个字单元,由低地址到高地址依次存放128位数据由低到高的各个字。运算结果存储在第一个数的存储空间中。
ds:di指向存储第二个数的内存空间
add128: push ax push cx push si push di sub ax, ax ; set CF to Zero 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 retinc 和 loop不影响标志位CF
11.7 sbb指令
带借位的减法指令,他利用了CF位上记录的借位值。
指令格式:sbb操作对象1, 操作对象2
功能:操作对象 1 = 操作对象 1 - 操作对象2 - CF
比如sbb ax, bx ; (ax) = (ax) - (bx) - CF
例如计算003E 1000H - 0020 2000H
mov bx, 1000H
mov ax, 003eH
sub bx, 2000H
sub ax, 0020H
11.8 cmp指令
cmp是比较指令,相当于减法指令,只是并不保存结果。cmp指令执行以后,将对标志寄存器产生影响。
cmp指令格式:cmp 操作对象1, 操作对象2
功能:计算操作对象1-操作对象2并不保存结果,仅仅根据计算结果对标志寄存器进行设置。
例如 cmp ax, ax ; Zf = 1, pf = 1, sf = 0, cf = 0, of = 0
下面指令:
mov ax, 8
mov bx, 3
cmp ax, bx ; zf = 0, pf =1, sf = 0, cf = 0, of = 0
例如 cmp ax, bx
如果 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, 所以 cf = 0
如果ax > bx, 则 ax- bx >0, cf = 0且 zf = 0
如果ax<= bx, 则 ax - bx <=0, cf = 1 或 zf = 1
执行cmp的时候:也可以表示进行无符号运算和 进行有符号运算
如果利用cmp进行有符号数的比较
cmp ah, bh
如果 ah == bh, 则 ah - bh = 0, 所以 zf = 1
如果 ah != bh, 则 ah - bh != 0, 所以 zf = 0
如果ah < bh, ah - bh <0 sf = 1
如果 ah = 22h, bh = 0a0h(-96)
ah - bh = 34 - (-96) = 82H (-126) < 0 但是实际上 ah > bh
如果运算结果发生了溢出则所得到的结果sf就不能说明问题
总结cmp ah, bh sf of是如何来说明比较结果的。
当of = 0 sf = 1 则 ah < bh
当of=0, sf = 0 则 ah >= bh
当of=1 sf = 1,因为溢出导致实际结果位负,那么逻辑上的真正结果必然为正。因此 ah > bh
当of =1, sf = 0,因为溢出导致了实际结果为正,那么逻辑上真正的结果必然为负,因此 ah < bh
11.9 检测比较结果的条件转移指令
所有条件转移指令的转移位移应该都是 -128 ,127
常见的无符号数的比较结果进行转移的条件转移指令(zf cf)
je 等于则转移 zf = 1
jne 不等于则转移 zf = 0
jb 低于则转移 cf = 1 (below)
jnb 不低于则转移 cf = 0
ja 高于则转移 cf = 0 且 zf=0 (above)
jna 不高于则转移 cf = 1 或 zf = 1
编程实现如下功能
如果 ah == bh 则 ah = ah + ah
否则 ah = ah + bh
cmp ah, bh je s add ah, bh jmp short OK s: add ah, ah OK: ;...
在代码设计的时候可以将 cmp 和 jxx 联合一起使用相当于高级语言的if语句
例子统计data段中数值为8段字节的个数,用ax保存统计结果
assume cs:code data segment db 8, 11, 8, 1, 8, 5, 63, 38 data ends stack segment db 32 dup (0) stack ends code segment start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, 20h mov ax, 0 mov si, 0 mov cx, 8 s: cmp byte ptr ds:[si], 8 je cnt jmp short next cnt: inc ax next: inc si loop s mov ax, 4c00h int 21h code ends end start
运行结果
2)编程,计算data段中数值大于8段字节的个数,用ax保存统计的结果
assume cs:code data segment db 8, 11, 8, 1, 8, 5, 63, 38 data ends stack segment db 32 dup (0) stack ends code segment start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, 20h mov ax, 0 mov si, 0 mov cx, 8 s: cmp byte ptr ds:[si], 8 jna next inc ax next: inc si loop s mov ax, 4c00h int 21h code ends end start
3)编程,统计data段中数值小于8段字节的个数,用ax保存统计结果
assume cs:code data segment db 8, 11, 8, 1, 8, 5, 63, 38 data ends stack segment db 32 dup (0) stack ends code segment start: mov ax, data mov ds, ax mov ax, stack mov ss, ax mov sp, 20h mov ax, 0 mov si, 0 mov cx, 8 s: cmp byte ptr ds:[si], 8 jnb next inc ax next: inc si loop s mov ax, 4c00h int 21h code ends end start
11.10 DF标志和串传送指令
DF方向标志位,在串处理中,控制每次操作后si,di的递减。
df=0 每次操作后si,di递增
df=1 每次操作后si,di递减
movsb串传送指令
功能:执行movsb指令相当于以下几个步骤
1)((es)*16 + (di)) = ((ds)*16+(si)) 把ds:si的值传送到es:di
2) 如果df=0, 则 (si)=(si)+1 , (di) = (di) + 1
反之 (si) = (si) - 1, (di) = (di) -1
也可以传送一个字
格式:movsw
将ds:si指向的内存字单元传入es:di中,然后根据标志寄存器将si和di 递增或递减2
movsb 和 movsw 要配合rep (还有一个传送双字的movsd)
rep movsb
相当于 s: movsb
loop s
也就是根据cx的值来执行循环 rep movsb 传送cx个字节
同理也可以传送 rep movsw 和 (rep movsd)
相当于 s:movsw
loop s
cld指令,可以将df设置位0 (递增)
std指令,可以将df设置位1 (递减)
movsX 系的指令需要用到的寄存器
DS,ES,SI,DI,CX(配合REP) 和标志寄存器的DF
1)将data段的字符传送到他后面的空间中
assume cs:code data segment db 'Welcome to masm!' db 16 dup (0) data ends stack segment db 32 dup (0) stack ends code segment start: mov ax, data mov ds, ax mov es, ax mov ax, stack mov ss, ax mov sp, 20h mov si, 0 mov di, 10h mov cx, 10h cld rep movsb mov ax, 4c00h int 21h code ends end start
执行结果
2)编程,用串传送指令,将F000H段中的最后16个字符复制到data段中。
assume cs:code data segment db 16 dup (0) data ends stack segment db 32 dup (0) stack ends code segment start: mov ax, 0f00h mov ds, ax mov ax, data mov es, ax mov ax, stack mov ss, ax mov sp, 20h mov si, 0fffh 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中的表示
NV UP EI PL NZ NA PO NC
OF DF SF ZF PF CF
值为1 值为0
of OV NV
sf NG PL
zf ZR NZ
pf PE PO
cf CY NC
df DN UP
实验11 编写子程序
将包含人意字符,以0结尾的字符串中的小鞋字符转变成大写字母
名称:letterc
功能:将以0结尾的字符串中的小鞋字母转变成大写字母
参数:ds:di指向字符串首地址
assume cs:code datasg segment db "Beginner's All-purpose Symbolic Instruction Code.", 0 datasg ends stack segment db 64 dup (0) stack ends code segment begin: mov ax, datasg mov ds, ax mov ax, stack mov ss, ax mov sp, 40h mov si, 0 call letterc mov ax, 4c00h int 21h ;***************************************************************************** ; sub procedure letterc ;***************************************************************************** letterc: push si push ax pushf lts: mov al, ds:[si] cmp al, 0 je letterc_ok ; meet the end of the string '\0' cmp al, 'a' jb next cmp al, 'z' ja next and al, 11011111B ; conver to upper case mov ds:[si], al ; apply change to data next: inc si jmp short lts letterc_ok: popf pop ax pop si ret code ends end begin
运行结果
将此实验和上一个单元中的show_str等例子集成
assume cs:code datasg segment db "Beginner's All-purpose Symbolic Instruction Code.", 0 datasg ends stack segment db 64 dup (0) stack ends code segment begin: mov ax, datasg mov ds, ax mov ax, stack mov ss, ax mov sp, 40h mov si, 0 call cls mov dh, 2 ; line 2 mov dl, 0 ; point the column 0 mov cl, 7 ; 0 000 0 111B = 7 set the bkcolor(black) color(white) mov si, 0 ; point to the start of the string call show_str call letterc mov dh, 3 call show_str mov ax, 4c00h int 21h ;***************************************************************************** ; sub procedure letterc ;***************************************************************************** letterc: push si push ax pushf lts: mov al, ds:[si] cmp al, 0 je letterc_ok ; meet the end of the string '\0' cmp al, 'a' jb next cmp al, 'z' ja next and al, 11011111B ; conver to upper case mov ds:[si], al ; apply change to data next: inc si jmp short lts letterc_ok: popf pop ax pop si ret ;***************************************************************************** ; sub memset16 ; di:si point to the start of the memory ; cx is the length of the memory ; ax is the value of the memory ;***************************************************************************** memset16: push si push ax push cx jcxz memret16 ; if the cx is equal to zero just return setvalue16: mov ds:[si], ax add si, 2 loop setvalue16 memret16: pop cx pop ax pop si ret ;***************************************************************************** ; sub show_str ;***************************************************************************** show_str: push ax push bx push cx push dx push bp push si push di push es mov ax, 0b800h ; load the first address to vram mov es, ax mov bp, 0 ; reset bp to 0 mov ax, 0 ; reset al ah mov al, dh mov bl, 0a0h mul bl ; dh x 160 = line offset add bp, ax ; add the line offset mov ax, 0 ; reset al ah mov al, dl mov bl, 2 mul bl ; dl x 2 = column offset add bp, ax ; add the column offset mov di, 0 mov ah, cl ; store the color info change: mov cl, [si] mov ch, 0 jcxz ok ; check whether pointer to '\0' mov al, cl ; store the char mov es:[bp][di], ax ; inc si add di, 2 jmp short change ok: pop es pop di pop si pop bp pop dx pop cx pop bx pop ax ret ;***************************************************************************** ; sub cls. ; clean the screen ;***************************************************************************** cls: push ds ; clean the screen push ax push cx push si mov cx, 690h mov ax, 0b800h mov ds, ax mov al, ' ' mov ah, 7 mov si, 0 call memset16 pop si pop cx pop ax pop ds ret code ends end begin
运行结果