标志位寄存器其作用就是以其不同的标志位来支持更高级的指令,使得程序员的操作更为方便。关于标志位寄存器的知识点:区分CF标志位(进位标志)与OF标志位(溢出标志)
CF标志位用在加减法的进位与借位操作上:
adc指令(add carry):带进位加法
adc ax,bx ==> (ax)=(ax)+(bx)+CF
eg:1E F000 1000H + 20 1000 1EF0H
要求ax、bx、cx分别存放结果的高16位,中间16位,低十六位。
mov ax,001EH
mov bx,0F000H
mov cx,1000H
add cx,1EF0H
adc bx,1000H
adc ax,0020H
sbb指令:带借位减法
sbb ax,bx ==> (ax)=(ax)-(bx)-CF
eg:003E 1000H - 0020 2000H
mov bx,1000H
mov ax,003EH
sub bx,2000H
sbb ax,0020H
应用:大数的加法运算:
;大数加法:两个128位数据进行相加
;2017年5月6日15:51:04
;Author:Kangruojin
;Version:v1.1
;Mail:[email protected]
;规则:将ds:[si]指向第一个数的存储单元,ds:[di]指向第二个数的存储单元
;由低地址到高地址单元依次存放128位数据由低到高的各个字(总共8个字),
;结果存放在在第一个数的存储空间中
assume cs:code
data segment
db 0FEH,0EDH,0A2H,0C3H,0D4H,0E5H,0F6H,0CCH,0CFH,0EDH,012H,064H,0FFH,01DH,0AAH,033H
;db 16 dup (0FFH) ;ds:[0]开始存储第一个操作数2^128-1
dw 0 ;存最终的进位
db 0FFH,01DH,0AAH,033H,0CFH,0EDH,012H,064H,0D4H,0E5H,0F6H,0CCH,0FEH,0EDH,0A2H,0C3H
;db 16 dup (0FFH) ;ds:[130]开始存储第二个操作数2^128-1
data ends
code segment
start:
mov ax,data
mov ds,ax
call add_128bit
mov ax,4C00H
int 21H
;========================================
add_128bit:
mov si,0
mov di,18
sub ax,ax ;将CF设置为0
mov cx,8
add_loop:
mov ax,ds:[si]
adc ax,ds:[di]
mov ds:[si],ax
inc si
inc si
inc di
inc di ;注意:不能将两个inc di和两个inc si合并为一个add si,2与add di,2.因为这种操作会对CF产生影响
loop add_loop
mov ax,0
adc ax,0 ;最后这一步是要将最终有可能存在的进位OF保存起来
mov ds:[si],ax
ret
;========================================
code ends
end start
CMP指令(compare比较指令):不保存结果到寄存器或者内存单元中。只影响标志位。(虽然不保存最终结果,但是会将中间结果暂存在CPU内部的暂存器上)
①无符号数比较(检测zf和cf):
eg:cmp ax,bx
zf=1,说明(ax)=(bx)
cf=1,说明(ax)<(bx)
zf=0并且cf=0,说明(ax)>(bx)
如果zf=1说明(ax)-(bx)=0为真,即(ax)=(bx),其它解释相似。
②有符号数比较(检测zf、sf和of):
eg:cmp ah,bh
sf=1并且of=0,说明(ah)<(bh)
sf=1并且of=1,说明(ah)>(bh)
sf=0并且of=1,说明(ah)<(bh)
sf=0并且of=0,说明(ah)≥(bh)
zf=0,说明(ah)=(bh)
test reg/mem,reg/imm
与cmp指令相似,只不过两个操作数进行与操作,不保存结果值,但影响标志寄存器的值,经常用来测试某位是不是1。
jcxz属于条件转移指令,但是它监测的是cx的值,而不是标志位寄存器。下面的条件转移指令属于根据检测不同标志位的值来决定是否转移的条件转移指令。并且这些条件跳转指令与cmp、test指令是配合使用的,没有cmp、test的话,这些指令没有直接意义。
比如:
对于C语言的:
short a=5,b=3;
if(a>b) ;if(a<=b)
a=a+b;
else
a=a-b;
类似的对于汇编来说可能就是:
mov ax,5
mov bx,3
cmp ax,bx
ja calc ;大于就跳转到加法
sub ax,bx ;没有跳转到加法就执行该指令
jmp short over ;结束
calc:
add ax,bx
over:
...
与标志位有关的跳转指令(五个标志位:cf、of、pf、sf、zf)这其中包含有符号与无符号的操作:
je、jz 两数相等(ZF == 1)则跳转
jne、jnz 两数不等(ZF == 0)则跳转js 结果为负数跳转(SF == 1)
jns 结果为正数跳转(SF == 0)jp、jpe 结果的1的个数为偶数则跳转(PF == 1)
jnp、jpo 结果的1的个数为奇数则跳转(PF == 0)
注意:PF值只受低八位影响,odd奇数,even偶数jo 溢出则跳转(OF == 1)
jno 不溢出则跳转(OF == 0)无符号数:
jb、jnae 小于则跳转(CF == 1)
jnb、jae 不小于则跳转(CF == 0)
jbe、jna 小于等于则跳转(CF == 1 || ZF == 1)
jnbe、ja 大于则跳转(CF == 0 && ZF == 0)有符号数:
jl、jnge 小于则跳转(SF != OF)
jnl、jge 不小于则跳转(SF == OF)
jle、jng 小于等于则跳转(ZF == 1 || OF != SF)
jnle、jg 大于则跳转(ZF == 0 && SF == OF)j:jmp跳转
z:zero零
e:equal等于
n:not非,不是
s:signel符号
p:parity奇偶校验
o:odd奇数
e:even偶数
o:overflow溢出
b:below低于
a:above高于
l:lesser小于
g:greater大于
简单应用:
;2017-5-6 20:22:30
;Mail:[email protected]
;Author:Kangruojin
;Version:v1.1
;统计数据段中,值与8的关系:
;将值为8的个数存放到ax中
;将值大于8的个数存放到bx中
;将值小于8的个数存放到dx中
assume cs:code
data segment
db 8,12,8,4,7,8,2,8,9,8,3,1,8,16,8,15
data ends
code segment
start:
mov ax,data
mov ds,ax
call compare_eight
mov ax,4C00H
int 21H
;==============================
compare_eight:
mov si,0
mov ax,0
mov bx,0
mov dx,0
mov cx,16
again:
cmp byte ptr ds:[si],8
jne notequal ;不等于则跳过ax自加
inc ax ;统计等于8的个数
jmp short over ;该步统计了就直接跳到over,进行下一个
notequal:
jnb notless
inc bx ;统计小于8的个数
jmp short over ;该步统计了就直接跳到over,进行下一个
notless:
inc dx ;统计大于8的个数
over:
inc si
loop again
ret
code ends
end start
执行一次movsb等价于:
mov al ds:[si]
mov es:[di],al ;al只是一个中间temp,不一定是al
;df为0时自加,df为1时自减
inc si ;dec si
inc di ;dec di
执行一次movsw等价于:
mov ax ds:[si]
mov es:[di],ax ;ax只是一个中间temp,不一定是ax
;df为0时加,df为1时减
add si,2 ;sub si,2
add di,2 ;sub di,2
上面两个指令能自动加减si和di是因为检测了df标志位,如果df标志位为0则自加si和di,如果df标志位为1则自减si和di。
而设置df标志位的指令为:
cld:置为0,clear df
std:置为1,set df
rep指令:重复指令,ECX(CX)中存放重复的次数:每执行一次重复的指令,ecx自减一次,减到0则结束
rep指令与movsb/movsw配合使用:
rep movsb
//等价于
s:movsb
loop s
rep movsw
//等价于
s:movsw
loop s
所以说用rep、movs、cld/std指令的配合,在设定cx以后就可以自动完成我们自己用循环对ds到es的数据复制。
eg:将data段的前16个字节内容复制到后十六个字节
mov ax,data
mov ds,ax
mov es,ax
mov si,0
mov di,16
mov cx,16
s:
mov al,ds:[si]
mov es:[di],al
inc si
inc di
loop s
;用我们新的方式做:简洁了许多
mov ax,data
mov ds,ax
mov es,ax
mov si,0
mov di,16
mov cx,16
cld
rep movsb
stos指令与movs指令类似(2017年7月31日17:49:01新增笔记):
STOS:执行一次edi、esi的值会自动增加/减小1/2/4
STOS <==> STOS DWORD PTR ES:[EDI] ==> MOV ES:[EDI],EAX
STOSD<==> STOS DWORD PTR ES:[EDI] ==> MOV ES:[EDI],EAX
STOSW<==> STOS WORD PTR ES:[EDI] ==> MOV ES:[EDI],AX
STOSB<==> STOS BYTE PTR ES:[EDI] ==> MOV ES:[EDI],ALMOVS(两边均为内存的特殊指令):执行一次edi、esi的值会自动增加/减小1/2/4
MOVS <==> MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
MOVSD<==> MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
MOVSB<==> MOVS BYTE PTR ES:[EDI],BYTE PTR DS:[ESI]
MOVSW<==> MOVS WORD PTR ES:[EDI],WORD PTR DS:[ESI]
pushf与popf指令:
pushf:将标志寄存器的内容入栈
popf:将栈中的值出栈到标志寄存器中
pushad:将通用寄存器全部依次压栈(保存现场)
popad:将栈顶开始的元素依次出栈通用寄存器中(恢复现场)
为直接访问标志寄存器提供了便利的方式。想使用哪一位就直接将其他位“and 0”去掉。