ARM汇编学习四

限于喜欢 提交于 2020-02-03 16:20:03

接下来学习条件指令。
当特定条件满足时,借助条件指令, 通过跳转(分支)或执行某些特定指令来控制程序的流动方向。相关条件被描述为CPSR寄存器中的特定位的状态,这些位根据指令计算后的结果实时改变。比如,如果我们比较两个数并且他们相等,就将零标志位置位 (Z=1) ,因为在系统底层发生了a-b=0。在这个例子里两个数是相等的,但如果第一个数字比第二个大,会得出大于结论。而相反的情况下得出小于结论。当然还有很多其他的条件,比如小于等于(LE),大于等于(GE)等等。
下表列出了可能的条件指令,他们的含义以及被检测的状态标志位
在这里插入图片描述
结合上表,我们看段demo
.global main

main:
mov r0, #2 /* setting up initial variable /
cmp r0, #3 /
R0与数字3比较,2小于2,所以N位置1*/
addlt r0, r0, #1 /* 如果R0比3小就将R0自增1*/
cmp r0, #3 /* 再次比较r0和3,此时2+1=3,所以Z标志位置1,N置0 /
addlt r0, r0, #1 /
如果r0小于3给r0自增1*/
bx lr
结合注释,就很好理解了
代码中,第一个CMP比较指令执行后触发了N标志位的置位(2-3=-1),这表明r0的值比数字3要小。随后,由于条件满足,,所以执行了addlt指令。再次比较r0和3,此时2+1=3,所以Z标志位置1,N置0,此时条件不成立,结果就是第二个addlt没有执行,r0也没改变,程序退出并返回结果3

接下来我们看看Thumb模式下的条件执行
注意,只有在特定版本中( Thumb-2 )才能执行条件执行指令
语法结构: IT{x{y{z}}} cond(注:xyz指IT后最多再跟三个大写字母,大写字母可以是T,可以是E,T就是then,E就是else)
cond规定了执行IT语句块里的第一条指令需要满足的条件
x规定了执行的IT语句块中第二条指令需要满足的条件
y规定了执行IT语句块里的第三条指令需要满足的条件
z 规定了执行IT语句块里的第四条指令需要满足的条件

IT指令集的结构是: “IF-Then-(Else)”,它的语法结构由两个字母构成:
IT代表If-Then(下一条指令是条件指令)
ITT代表If-Then-Then(接下来的两条指令是条件指令)
ITE代表 If-Then-Else(接下来的两条指令是条件指令)
ITTE代表 If-Then-Then-Else (接下来的三条指令是条件指令)
ITTEE代表 If-Then-Then-Else-Else (接下来的四条指令是条件指令)
IT语句块内的每个指令必须指定一个条件后缀,该条件后缀那么相同要么在逻辑上相反。这意味着,如果使用了ITE,第一和第二指令(If-Then)必须具有相同的条件后缀,而第三条指令(else语句)必须和前面两条语句逻辑相反。
下面是ARM参考是手册下的部分指令,已经给出的中文注释
ITTE NE ; 接下来的三条指令是条件指令
ANDNE R0, R0, R1 ; ANDNE不更新条件标志位
ADDSNE R2, R2, #1 ; ANDNE更新条件标志位
MOVEQ R2, R3 ; 条件赋值指令

ITE GT ; 下面两条指令是条件指令
ADDGT R1, R0, #55 ; 如果GT为1执行条件的相加指令
ADDLE R1, R0, #48 ; 如果GT为0执行的条件相加指令

ITTEE EQ ; 下面四条指令是条件指令
MOVEQ R0, R1 ; 条件赋值指令
ADDEQ R2, R2, #10 ; 条件相加指令
ANDNE R3, R3, #1 ; 条件与指令
BNE.W dloop ; 条件分支指令只能用于IT语句块的结尾

一个典型的错误例子是这样子的:
IT NE ; 下面一条不是条件执行指令
ADD R0, R0, R1 ; 语法错误:IT语句块中没有使用条件执行指令
以下总结了条件指令和逻辑相反的指令
在这里插入图片描述

了解这些基础之后,我们看一下一个实例,代码在如下,在test7.s
.syntax unified @ this is important!这句话很关键
.text
.global _start

_start:
.code 32
add r3, pc, #1 @ increase value of PC by 1 and add it to R3
bx r3 @ branch + exchange to the address in R3 -> switch to Thumb state because LSB = 1

.code 16         @ Thumb state
cmp r0, #10      
ite eq           @ if R0 is equal 10...
addeq r1, #2     @ ... then R1 = R1 + 2
addne r1, #3     @ ... else R1 = R1 + 3

bkpt
.code32
这段示例代码以ARM状态开始。第一条指令将PC里的地址值加1后传送给r3,接着跳转到分支地址R3。这样做会导致切换到Thumb状态,因为LSB(最低有效位)是1,因此不是4个字节。使用bx指令(分支+切换)达成这个目标,分支指令执行完成后,T(Thumb)标志位被置位,我们现在处于Thumb模式
.code16
Thumb模式下首先用R0和立即数10比较。这会将N标志位置位(0-10=-10)。接着我们使用了一个 If-Then-Else语句块。这个块会跳过ADDEQ因为Z(零)标志位没有被置位,由于结果等于10,是NE的(not equal不等于0的),接着会执行ADDNE指令

这里在Gdb中单步步过这段指令会把结果搞乱,因为在ITE语句块中两条语句都执行了。所以我们不设置断点运行代码,并且单步步过每条指令会产生正确的结果:R1=3

编译链接
在这里插入图片描述
使用gdb调试
在这里插入图片描述
直接run查看寄存器r1的值,确实为3
在这里插入图片描述

接下来学习分支指令
分支指令(也叫做跳转)允许我们跳转到另一个代码段运行。当我们需要跳过(或者重复)执行代码段或者跳向特定功能的函数时就显得尤为有用。这方面最好的范例就是IFs和循环。我们先看看IF是什么情况。
示例代码如下
.global main

main:
mov r1, #2 /* setting up initial variable a /
mov r2, #3 /
setting up initial variable b /
cmp r1, r2 /
comparing variables to determine which is bigger /
blt r1_lower /
jump to r1_lower in case r2 is bigger (N==1) /
mov r0, r1 /
if branching/jumping did not occur, r1 is bigger (or the same) so store r1 into r0 /
b end /
proceed to the end /
r1_lower:
mov r0, r2 /
We ended up here because r1 was smaller than r2, so move r2 into r0 /
b end /
proceed to the end /
end:
bx lr /
THE END */
这个逻辑用c语言伪代码表示出来就是
int main() {
int max = 0;
int a = 2;
int b = 3;
if(a < b) {
max = b;
}
else {
max = a;
}
return max;
}

在test8.s
在这里插入图片描述
编译链接后使用gdb调试
在main下断点,然后run
在这里插入图片描述
可以看到接下来要执行的两条语句分别是给r1,r2赋值
在这里插入图片描述
第三条是进行比较r1,r2的大小
我们使用nexti 2直接来到复制之后的情况
可以看到此时出现了分支
在这里插入图片描述
分别是r1大于r2,和r1小于r2的情况,会把大的那个寄存器的值赋给r0
继续nexti
在这里插入图片描述
可以看到现在判断出来r2比较大,所以下一条指令会把r2的值赋给r0
nexti后此时查看r0的值,确实和r2的值一样,为0x3
在这里插入图片描述

我们再来看一个例子
代码如下
.global main

main:
mov r0, #0 /* setting up initial variable a /
loop:
cmp r0, #4 /
checking if a4 /
beq end /
proceeding to the end if a
4 /
add r0, r0, #1 /
increasing a by 1 if the jump to the end did not occur /
b loop /
repeating the loop /
end:
bx lr /
THE END */
代码逻辑用c伪代码表示出来为
int main() {
int a = 0;
while(a < 4) {
a= a+1;
}
return a;
}
代码在test9.s
编译链接后用gdb调试,在main下断点,单步调试
在这里插入图片描述在这里插入图片描述

run
在这里插入图片描述
可以看到第一条是赋0给r0,然后比较r0和4,cmp指令会执行0-4,结果不等于0,所以会Z标志会置0,或者表示为!z
我们使用nexti2然后查看
在这里插入图片描述
确实cmp的结果为!z,按照逻辑,r0会自增1,表示在汇编里就是add r0,r0,#1
我们接下来同样nexti 2
在这里插入图片描述
可以看到r0为0x1,后续的逻辑还是一样的,可以自行调试验证,此处不再演示。

参考:
azeria-labs 的arm教程
https://azeria-labs.com

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!