计算机按照严格的顺序执行指令。流控制改变了默认的顺序执行方式。前面已
经介绍了强制跳转到程序中某个非顺序位置的无条件分支。以及依据测试结果
进行跳转的条件分支。这里将介绍子程序调用和返回指令,它们会跳转到一个
指令块、执行这些指令,然后返回到子程序调用指令后的一个位置来修改控制
流。
无条件分支
ARM无条件分支指令格式为B target,target指分支目标地址(branch target
address,BTA)。
下面代码说明了如何使用无条件分支指令:
... do this ; 一些代码
... then that ; 另一些代码
B Next ; 跳过下面的指令
... ; ...被略过的代码
... ; ...被略过的代码
Next ... ; 分支目标地址,由标号Next表示
在高级语言中,无条件分支叫作goto,它被认为是一种比较糟糕的编程风格。
然而,在汇编语言中,无条件分支是很难避免的。
条件分支
下面给出了高级语言中实现条件行为的典型例子:
IF(X == Y)
THEN Y = Y + 1
ELSE Y = Y + 2
ARM汇编语言表示:
CMP r1,r2 ; 假设r1包括y,r2包括x,将它们比较
BNE plus2 ; 如果不相等则跳转到ELSE部分
ADD r1,r1,#1 ; 如果相等则继续,y加1
B leave ; 现在跳过ELSE部分
plus2 ADD r1,r2,#2 ; ELSE部分,y加2
leave ... ; 从这里继续
条件分支指令测试处理器中条件码寄存器中的标志位,如果测试结果为真则转移成功
ARM有4条测试与比较指令CMP、CMN、TST、TEQ ,这些指令会显式更新条件
码标志,因此无需在指令后添加S
相等测试指令TEQ
确定两个操作数是否相等,如果相等将Z位置1,否则将Z位清0
如,指令TEQ r1,r2完成RTL操作[r1] -- [r2],如果r1和r2相等,Z位被置1。
TEQ与CMP指令类似,测试时TEQ不影响溢出标志的状态而仅修改Z位。相反地,
CMP会更新溢出标志。
比较指令CMP
用第一个源操作数减去第二个,然后更新条件码。
如,指令CMP r1,r2计算[r1] -- [r2],然后设置CPSR中的N、Z、C和V位。
测试指令TST
通过与操作来比较两个操作数,然后根据结果更新标志位。可以用TST来测试一个字中的每一位。
如,由于小写ASCII字母的第5位为1,所以通过下面的代码来判断r0中的ASCII字母是否为小写字母:
TST r0,#2_00100000 ; r0与00100000进行与操作,测试第5位的状态
BEQ LowerCase ; 如果第5位为1则跳转到小写字母处理部分
取负并比较指令CMN
在进行比较操作之前先将第二个源操作数取负。
如,指令CMN r1,r2计算[r1] - [-r2],然后设置CPSR。注意[r1] - [-r2]的值与[r1] +
r2\]的相同。 ### 分支与循环结构 用经典的循环结构来介绍流控制概念是最合适的,循环是结构化编程的核心。 下面代码说明了FOR、WHILE和UNTIL循环的结构 #### for MOV r0,#10 ; 设置循环计数器 Loop code ... ; 循环体 SUBS r0,r0,#1 ; 循环计数器减1并设置状态标志 BNE Loop ; 继续直到计数值为0------不为0时跳转 Post loop ; 计数值为0的后续代码 #### while Loop CMP r0,#0 ; 循环开始执行测试 BEQ WhileExit ; 测试结果为true则退出 code ... ; 循环体 B Loop ; 为true时重复 WhileExit Post loop ... ; 退出 #### until Loop code ... ; 循环体 CMP r0,#0 ; 循环末尾进行测试 BNE Loop ; 重复直到UNTIL为true Post loop ... ; 退出 #### 组合循环 组合循环将上面3中循环的特点结合在一起。 FOR部分指定了最大计数值,限制了循环的执行次数。 WHILE部分测试r1中的初始条件,如果条件不为true则立即退出。 UNTIL部分则在循环体末尾r2为true时退出。 MOV r0,#10 ; 设置循环计数器 LoopStart CMP r1,#0 ; 以WHILE测试开始 BEQ ComboExit ; 为true退出循环 code ... ; 循环体 CMP r2,#0 ; 测试UNTIL条件 BEQ ComboExit ; 为true退出循环 SUBS r0,r0,#1 ; 循环计数器减1并设置状态标志 BNE LoopStart ; 继续直到计数器为0------不为0则转移 ComboExit Post loop ... ; 退出 #### 条件执行 汇编语言程序员在指令助记符后添加合适的条件以指明条件执行模式 如:ADDEQ r1,r2,r3 指定仅当条件码中的Z位因为前一个结果为0而被置为1时,加法操作才会被执行。 其RTL形式为:IF Z = 1 THEN \[r1\] \<- \[r2\] + \[r3
条件执行和移位操作可以组合在一起,因为指令中的分支和移位字段是无关的
如:ADDCC r1,r2,r3 LSL r4
其RTL形式为:IF C = 0
THEN [r1] <- [r2] + [r3] X 2[r4]
ARM的条件执行模式使得在高级语言中实现条件操作更容易。
(1)考虑下面的C代码段:
If(P == Q)
X = P -- Y;
如果r1为P,r2为Q,r3为X,r4为Y,则可以写为:
CMP r1,r2 ; 比较P == Q
SUBEQ r3,r1,r4 ; 为true则r3 = r1 - r4,为false,减法被转换为空操作
考虑一个更复杂例子,一个带有组合条件的C代码段:
If((a == b) && (c == d))
e++;
可以写为:
CMP r0,r1 ; 比较a == b
CMPEQ r2,r3 ; 如果a == b,则比较c == d
ADDEQ r4,r4,#1 ; 如果a == b且c == d,则e加1
不使用条件执行,则可写为:
CMP r0,r1 ; 比较a == b
BNE Exit ; a !=b则退出
CMP r2,r3 ; 比较c == d
BNE Exit ; c!=d则退出
ADD r4,r4,#1 ; 否则e加1
Exit
处理一些带有多个条件的C代码段:
If(a == b) e = e + 4;
If (a < b) e = e + 7;
If(a > b) e = e + 12;
可以写为:
CMP r0,r1 ; 比较a == b
ADDEQ r4,r4,#4 ; 如果a == b,则e = e + 4
ADDLE r4,r4,#7 ; 如果a < b,则e = e + 7
ADDGT r4,r4,#12 ; 如果a > b,则e = e + 12
不使用条件执行,则可写为:
CMP r0,r1 ; 比较a == b
BNE Test1 ; 不相等则跳转到Test1进行下一次测试
ADD r4,r4,#4 ; a ==b,则e = e + 4
B ExitAll ; 退出
Test1 BLT Test2 ; 如果a < b,则跳转到Tset2
ADD r4,r4,#12 ; 此处a > b,因此e = e + 12
B ExitAll ; 退出
Test2 ADD r4,r4,#7 ; 此处a < b,因此e =e + 7
ExitAll