// 在这个汇编函数中,用来做中断模式下的现场保护和恢复,并且调用真正的中断处理程序
IRQ_handle:
// 设置IRQ模式下的栈
ldr sp, =IRQ_STACK
// 保存LR
// 因为ARM有流水线,所以PC的值会比真正执行的代码+8,
sub lr, lr, #4
// 保存r0-r12和lr到irq模式下的栈上面
stmfd sp!, {r0-r12, lr}
// 在此调用真正的isr来处理中断
bl irq_handler
// 处理完成开始恢复现场,其实就是做中断返回,关键是将r0-r12,pc,cpsr一起回复
ldmfd sp!, {r0-r12, pc}
看到上面的汇编代码中的
sub lr, lr, #4
是不是有点难理解?
首先我们要明白ARM7处理器采用三级流水线的方式来增加处理器对指令处理的速度。
(我在我的另一篇博客中《ARM汇编概念(1): 什么是链接地址、运行地址、存储地址? 什么是位置无关码、位置有关码? ldr和adr的理解?》有提到)
补充知识:
ARM正在执行第1条指令的同时对第2条指令进行译码,并将第3条指令从存储器中取出。
无论处理器处于何种状态,程序计数器R15(PC)总是指向“正在取指”的指令,而不是指向“正在执行”的指令或者正在“译码”的指令。
理解了三级流水线的工作原理后,我们知道实际指令在执行阶段,pc指向的应该是pc+8的地址。但是小编在理解了这些后,还是不明白为什么汇编代码中不是sub lr, lr, #8,而是sub lr, lr, #4 ???
所以,我们还需要理解“”中断”处理的机制
假设:我们程序运行处于SVC模式,正在执行代码1(此时的pc处于代码3的地址,因为三级流水线的原因)。这时候突然一个“中断”,使得程序运行处于IQR模式,中断了下一个代码2的运行。等待中断程序执行结束后,我们需要再次回到代码2的地址继续执行代码2,但是我们该如何回到原来中断的地址(代码2的地址)呢?
这里我们需要知道LR寄存器的作用了,它是用来暂存原来模式下(SVC模式)的下一个指令的PC(中断返回地址)
但是如果我们直接将PC保存到LR寄存器,那么我们是其实保存的地址是代码3的地址,但我们需要保存的地址是地址2,所以我们可以根据上面的代码示意图可以很清楚地理解了为什么是减4,而不是减8。