在32位的ARM架构系统中,通用寄存器中有一个寄存器比较特殊,就是r15,它也是PC寄存器。
PC是program counter。也叫做ip,instruction pointer。
当ARM处理器执行一条指令时,在其执行结束时可能会发生两件事。 如果指令没有修改pc,则pc只会增加4,因为在32位ARM中,指令为32位宽,所以每条指令之间有4个字节。 如果指令修改了pc,则使用pc的新值。当然64位的ARM就是增加8了每次指令过后。
如果一条指令确实将pc修改为例如pc + 4以外的其他值,那么可以运行该程序的另一条指令。 更改pc值的过程称为分支。 在ARM中,这是使用分支指令完成的。
可以分为无条件分支和有条件分支。
无条件分支就是强制跳转了,我们主要来看下有条件分支。
有条件分支
这里涉及到一个寄存器cpsr(urrent Program Status Register)。
其包含条件flag:
N (negative), Z (zero), C (carry) and V (overflow)
N:如果指令结果为负数,则启用,否则禁用。
Z:如果指令的结果为零,则将启用。 如果非零,则禁用。
C:如果指令的结果产生的值需要完全表示第33位,则将启用C。 例如,加法溢出了32位整数范围。 对于减法,有一个特殊情况,即无借位减法使能C,否则将其禁用:否则,将较大的数减为较小的值将启用C,但如果反之亦然,则将其禁用。
V:如果指令结果产生的值不能用32位二进制补码表示,则将启用V。
汇编指令
EQ(相等)Z为1 NE(不相等)Z为0 GE(大于或等于,用两个补码表示)。当V=N。 LT(低于,两个补码)。这与GE相反,V不等于N跳转 GT(大于,是两个补码)。Z=0,N=V LE(小于或等于,用两个补码表示)。Z=0或者V不等于N MI(负/负)N为1 PL(正/正或零)N为0 VS(溢出设置)V为1 VC(清除溢出)V为0 HI(较高)C为1并且Z为0 LS(较低或相同)C为0或Z为1 CS / HS(进位设置/更高或相同)当C为1 CC / LO(进位清除/降低)当 C为0 |
32位示例
.text
.global main
main:
mov r1, #2 /* r1 ← 2 */
mov r2, #2 /* r2 ← 2 */
cmp r1, r2 /* update cpsr condition codes with the value of r1-r2 */
beq case_equal /* branch to case_equal only if Z = 1 */
case_different :
mov r0, #2 /* r0 ← 2 */
b end /* branch to end */
case_equal:
mov r0, #1 /* r0 ← 1 */
end:
bx lr
as -g -o com.o com.s
ld -o com com.o
当r1和r2寄存器相等的时候才会跳转。
64位示例
我们再来看下64位的示例代码。
.arch armv8-a
.global _start
.text
_start:
mov x1, #2 /* r1 ← 2 */
mov x2, #2 /* r2 ← 2 */
cmp x1, x2 /* update cpsr condition codes with the value of r1-r2 */
beq case_equal /* branch to case_equal only if Z = 1 */
case_different :
mov x0, #2 /* r0 ← 2 */
b end /* branch to end */
case_equal:
mov x0, #1 /* r0 ← 1 */
mov x8, 93
svc 0
as -g -o com64.o com64.s
ld -o com64 com64.o