条件执行

在之前讨论CPSR寄存器那部分时,我们大概提了一下条件执行这个词。条件执行用来控制程序执行跳转,或者满足条件下的特定指令的执行。相关条件在CPSR寄存器中描述。寄存器中的比特位的变化决定着不同的条件。比如说当我们比较两个数是否相同时,我们使用的Zero比特位(Z=1),因为这种情况下发生的运算是a-b=0。在这种情况下我们就满足了EQual的条件。如果第一个数更大些,我们就满足了更大的条件Grater Than或者相反的较小Lower Than。条件缩写都是英文首字母缩写,比如小于等于Lower Than(LE),大于等于Greater Equal(GE)等。 下面列表是各个条件的含义以及其检测的状态位(条件指令都是其英文含义的缩写,为了便于记忆不翻译了):
05.条件执行与分支 - 图1
我们使用如下代码来实践条件执行相加指令:

  1. .global main
  2. main:
  3. mov r0, #2 /* 初始化值 */
  4. cmp r0, #3 /* 将R0和3相比做差,负数产生则N位置1 */
  5. addlt r0, r0, #1 /* 如果小于等于3,则R0加一 */
  6. cmp r0, #3 /* 将R0和3相比做差,零结果产生则Z位置一,N位置恢复为0 */
  7. addlt r0, r0, #1 /* 如果小于等于3,则R0加一R0 IF it was determined that it is smaller (lower than) number 3 */
  8. bx lr

上面代码段中的第一条CMP指令将N位置一同时也就指明了R0比3小。之后ADDLT指令在LT条件下执行,对应到CPSR寄存器的情况时V与N比特位不能相同。在执行第二条CMP前,R0=3。所以第二条置了Z位而消除了N位。所以ADDLT不会执行R0也不会被修改,最终程序结果是3。

分支和循环指令

分支指令(也叫分支跳转)允许我们在代码中跳转到别的段。当我们需要跳到一些函数上执行或者跳过一些代码块时很有用。这部分的最佳例子就是条件跳转IF以及循环。先来看看IF分支。

  1. .global main
  2. main:
  3. mov r1, #2 / 初始化 a /
  4. mov r2, #3 / 初始化 b /
  5. cmp r1, r2 / 比较谁更大些 /
  6. blt r1_lower / 如果R2更大跳转到r1_lower /
  7. mov r0, r1 / 如果分支跳转没有发生,将R1的值放到到R0 /
  8. b end / 跳转到结束 /
  9. r1_lower:
  10. mov r0, r2 / R2的值放到R0 /
  11. b end / 跳转到结束 /
  12. end:
  13. bx lr / THE END /

上面的汇编代码的含义就是找到较大的数,类似的C伪代码是这样的:

  1. int main() {
  2. int max = 0;
  3. int a = 2;
  4. int b = 3;
  5. if(a < b) {
  6. max = b;
  7. }
  8. else {
  9. max = a;
  10. }
  11. return max;
  12. }

再来看看循环中的条件分支:

  1. .global main
  2. main:
  3. mov r0, #0 / 初始化 a /
  4. loop:
  5. cmp r0, #4 / 检查 a==4 /
  6. beq end / 如果是则结束 /
  7. add r0, r0, #1 / 如果不是则加1 /
  8. b loop / 重复循环 /
  9. end:
  10. bx lr / THE END /

对应的C伪代码长这样子:

  1. int main() {
  2. int a = 0;
  3. while(a < 4) {
  4. a= a+1;
  5. }
  6. return a;
  7. }

B/BX/BLX

有三种类型的分支指令:

  • Branch(B) 简单的跳转到一个函数
  • Branch link(BL) 将下一条指令的入口(PC+4)保存到LR,跳转到函数
  • Branch exchange(BX) 以及 Branch link exchange(BLX) 与B/BL相同,外加执行模式切换(ARM与Thumb)

需要寄存器类型作为第一操作数:BX/BLX reg BX/BLX指令被用来从ARM模式切换到Thumb模式。

  1. .text
  2. .global _start
  3. _start:
  4. .code 32 @ ARM模式
  5. add r2, pc, #1 @ PC+1放到R2
  6. bx r2 @ 分支切换到R2
  7. .code 16 @ Thumb模式
  8. mov r0, #1

上面的代码将当前的PC值加1存放到了R2中(此时PC指向其后两条指令的偏移处),通过BX跳转到了寄存器指向的位置,由于最低有效位为1,所以切换到Thumb模式执行。

条件分支指令

条件分支指令是指在满足某种特定条件下的跳转指令。指令模式是跳转指令后加上条件后缀。我们用BEQ来举例吧。下面这段汇编代码对一些值做了操作,然后依据比较结果进行条件分支跳转。

  1. .text
  2. .global _start
  3. _start:
  4. mov r0, #2
  5. mov r1, #2
  6. add r0, r0, r1
  7. cmp r0, #4
  8. beq func1
  9. add r1, #5
  10. b func2
  11. func1:
  12. mov r1, r0
  13. bx lr
  14. func2:
  15. mov r0, r1
  16. bx lr