x86-64条件码是如何决定跳转的

前言

本文讲述x86-64条件码是如何决定跳转的,虽然主要是将决定跳转的逻辑,但是会分为以下几个部分:

  • 条件码设置规则
  • 指令对条件码的影响
  • 条件码的组合
  • 条件码的使用

条件码设置规则

我们在文章x86-64如何设置条件码,详细介绍了几种条件码的设置规则,规则如下:

  • CF:进位标志。最近的操作使最高位产生了进位。可用来检查无符号操作的溢出,CF = sub^最高位进位,sub表示是否是减法
  • ZF:零标志。最近的操作得出的结果为0。
  • SF:符号标志。最近的操作得到的结果为负数,只需要设置SF等于最高位即可
  • OF:溢出标志。最近的操作导致一个补码溢出,可以是正溢出或者负溢出,OF = 最高位发生了进位^次高位发生了进位能表示补码的溢出状态

指令对条件码的影响

条件码是通过算术运算或者逻辑操作来改变的,可以参考x86-64的算数和逻辑操作,除了leaq之外,所有的操作都可能改变标志位,除了文章中的这个操作,还有两类指令也可以改变标志位:

  • CMP S, D
    该指令和SUB指令的意思是一样的,但是不改变D的值,只改变条件码,这相当于一个比较指令,下面是不同的位模式的写法
    • cmpb:比较字节
    • cmpw:比较字
    • cmpl:比较双字
    • cmpq:比较四字
  • TEST S, D
    该指令和AND指令的意思是一样的,但是不改变D的值,只改变条件码,这相当于一个逻辑操作,下面是不同的位模式的写法
    • testb:测试字节
    • testw:测试字
    • testl:测试双字
    • testq:测试四字

下面总结一下所有指令对条件码的影响:
AND OR XOR NOT TEST
因为这几个指令都是逻辑操作,不是运算指令,所以
进位标志CF=0
溢出标志OF=0
影响零标志ZF和符号标志SF

ADD SUB IMUL CMP
这几个指令都是运算指令,所以
影响进位标志CF
影响溢出标志OF
影响零标志ZF
影响符号标志SF

INC DEC
进位标志CF=0
影响溢出标志OF
影响零标志ZF

SAL SHL SAR SHR
对于移位操作,进位标志CF将设置为最后一个被移除的位
溢出标志OF=0

条件码的组合

虽然逻辑操作和移位操作都会设置标志位,但是用的最多的跳转指令还是根据CMP指令来进行比较的,通过cmp S, D设置标志位,我们通过标志位的组合可以比较S和D的关系

  • ZF = 0:如果是比较操作,说明比较的两个值相等,如果是数值操作,说明当前值为0

  • ZF = 1:如果是比较操作,说明比较的两个值不相等,如果是数值操作,说明当前值不为0

  • SF = 0: 无论是什么操作,都说明最高位是0,对于有符号数的比较操作,能说明结果是个负数

  • SF = 1: 无论是什么操作,都说明最高位是1,对于有符号数的比较操作,能说明结果是个非负数

  • SF^OF:针对有符号数的比较操作,说明D小于S,下面给出证明过程:
    我们把D-S看成D+(-S)

    • OF=0,说明没有发生溢出

      SF=0,最高位是0,说明结果大于等于0,即 D − S ≥ 0 D-S\ge0 DS0 D ≥ S D\ge S DS
      SF=1,最高位是1,说明结果小于0,即 D − S < 0 D- S<0 DS<0 D < S DD<S

    • OF=1,说明发生了溢出,符号数溢出的条件,文章计算机中的整数运算有详细介绍

      SF=0,最高位是0,说明结果大于等于0,根据符号数溢出的条件, D < 0 D<0 D<0并且 − S < 0 -S<0 S<0的时候结果大于等于0才会发生溢出,所以 D < S DD<S
      SF=1,最高位是1,说明结果小于0,根据符号数溢出的条件, D ≥ 0 D\ge0 D0并且 − S ≥ 0 -S\ge0 S0的时候结果小于0才会发生溢出,所以 D ≥ S D\ge S DS

    把所有条件分析完,我们很容易得处SF^OF=1$\rightarrow$D,同样的道理,得出下一条

  • ~(SF^OF):针对有符号数的比较操作,说明D大于等于S

  • ~(SF^OF)&~ZF我们可以把当前的条件和ZF标志位的非取并集,就能去掉等于0的情况,即D大于S

  • (SF^OF)|ZF我们可以把DZF标志位取或集,就能加上等于0的情况,即D小于等于S

  • CF:CF是针对于无符号整数的比较操作,CF=1说明发生了借位,所以D小于S

  • ~CF:CF=0说明没有发生了借位,所以D大于等于S

  • CF|ZFD小于等于S

  • ~CF&~ZFD大于S

以上这些都是针对于比较操作的标志位组合代表的意思,x86-64中的大部分跳转都是根据这些组合处理的,当然也有些别的情况我们也会根据标志位进行判断,比如

testq %rax, %rax

虽然testq指令不是比较操作,但是通过设置标志位SF和ZF,我们相当于进行了下面的比较操作

cmpq 0, %rax

但是只能进行通过SF和ZF标志位进行的比较,比如判断rax是否大于0,等于0,小于0

条件码的使用

条件码通常不会直接读取,常用的使用方法有三种:

  1. 可以根据条件码的某种组合,将一个字节设置为0或者1,这一类指令叫做SET指令 ,一条SET指令的目的操作数是低位单字节寄存器,或者一个字节的内存位置,常见的set指令如下:

    sete D:或者叫setz D,将ZF的值传给D,等于0
    setne D:或者叫setnz D,将~ZF的值传给D,不等于0
    sets D:将SF的值传给D,负数
    setns D:将~SF的值传给D,非负数
    setg D:或者叫setnle D,将~(SF^OF)&~ZF的值传给D,有符号数大于
    setge D:或者叫setnl D,将~(SF^OF)的值传给D,有符号数大于等于
    setl D:或者叫setnge D,将SF^OF的值传给D,有符号数小于
    setle D:或者叫setng D,将(SF^OF)|ZF的值传给D,有符号数小于等于
    seta D:或者叫setnbe D,将~CF&~ZF的值传给D,无符号数大于
    setae D:或者叫setnb D,将~CF的值传给D,无符号数大于等于
    setb D:或者叫setnae D,将CF的值传给D,无符号数小于
    setbe D:或者叫setna D,将CF|ZF的值传给D,无符号数小于等于

  2. 可以根据当前标志位的状态跳转到指定地址,这种一般就是使用跳转指令,常见的跳转指令如下, Label为跳转的标签,其实就是跳转的地址:

    jmp Label: 直接跳转,不需要判断标志位
    je Label:ZF=1的时候跳转
    jne Label:ZF=0的时候跳转
    js Label:SF=1的时候跳转
    jns Label:SF=0的时候跳转
    jge Label:~(SF^OF)=1的时候跳转
    jg Label:~(SF^OF)&~ZF=1的时候跳转
    jle Label:(SF^OF)|ZF=1的时候跳转
    jl Label:SF^OF=1的时候跳转
    jae Label:CF=0的时候跳转
    ja Label:~CF&~ZF=1的时候跳转
    jbe Label:CF|ZF=1的时候跳转
    jb Label:CF=1的时候跳转

你可能感兴趣的:(程序设计-汇编语言,汇编)