标志位寄存器其作用就是以其不同的标志位来支持更高级的指令,使得程序员的操作更为方便。关于标志位寄存器的知识点:区分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,0020Hsbb指令:带借位减法 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:mailbox_krj@163.com ;规则:将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:mailbox_krj@163.com ;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 movsbstos指令与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],AL
MOVS(两边均为内存的特殊指令):执行一次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”去掉。