主要记录一下今天遇到的关于B指令和相对地址偏移的问题
B跳转指令是一个相对跳转指令
最高处4为(28~31)为条件码,后4位(24~27)“1010”表示B跳转,“1011”表示BL跳转
剩下24位(0~23)表示相对偏移地址,偏移地址是这样计算的:
先将24位补充为32位,然后左移两位,再与PC寄存器的相加,保存到PC寄存器
计算公式如下
(srcAddr + 8) + (x << 2) = desAddr
srcAddr表当前指令地址,x表偏移值
那srcAddr + 8 是什么意思呢?这就牵涉到PC寄存器
要解释这个,先了解一下ARM三级流水线
有点复杂,看一张稍微简单的图
ARM下每条指令4个字节,每条指令都要经过取指、译码、执行三个过程
比如上图中的指令
0000: add r0, r1, #5
0004: sub r2, r3, r6
0008: cmp r2, #3
当第一行指令被执行的时候,第二行指令被译码,第三行取指
而PC寄存器保存的是取指的指令的位置,也就是假设第一行代码的地址为0000,那么现在PC的值为0008
如果是使用BL执行了正常程序的跳转,那么执行这条BL指令时,由于是正常的跳转指令,所以cpu会将返回地址存放在LR中,即当前指令地址加4,当从子程序跳转回来的时候,那么就需要将保存在LR寄存器中的值恢复给PC寄存器,
mov PC, LR 这样的指令返回
IRQ异常发生时,因为这个异常是在指令执行时候发生的,PC的值等于当前执行指令加8,然后将这个值保存在LR中。但是LR寄存器中保存的是PC+8,指向的是后面的第二条指令,如果不进行减4处理,将会漏执行一条指令,所以PC恢复的时候就需要LR减4,所以正常从子程序返回的时候会使用如:
SUBS PC, LR,#4 返回到当前指令的下一条指令
未定义指令异常时,因为这个异常发生在指令译码阶段,所以,此时PC的值就是未定义指令加4,然后保存到LR(参考流水线图);因为该指令未定义,所以返回时就不应该返回到这条未定义指令,而是返回到它的下一条指令,R14中保存的刚好就是下一条指令的地址,所以就不用计算了,直接将R14赋值给PC就行了
预取指令异常是在流水线的执行阶段时才进入异常,所以PC的值是当前执行指令地址加8,所以返回时应该返回到下一条指令,所以PC恢复的时候就需要R14减4
数据中止异常,这个异常是在本指令执行完成后才发生的,表示当前存储器的访问不能完成,从流水线图可以看出,当第一条指令执行完成时,当前PC值已经指向了第一条指令地址加12的地址,LR中保存的其实是第四条指令的地址了,所以从异常返回时,需要从第一条指令的下一条指令(第二条指令)开始执行,所以PC恢复的时候就需要R14减8