当计算机正在当前指令时,此时发生了异常,那么在执行完当前指令后就要进行异常处理。跳转到相应的异常中断处理程序,同时保存当前执行现场,中断执行完成,返回执行现场的下条指令。
这里先了解异常相应的优先级,当多个异常发生时,由优先级的高低依次响应处理
异常中断处理过程(这里暂不考虑异常中断嵌套问题,默认为在ARM下处理异常):
----》当发生中断异常
----》(1)自动拷贝CPSR寄存器中数据到SPSR_
----》(2)修改CPSR寄存器中的[4:0]=mode,[T]=0设置为arm态,设置中断禁止[7:6];
----》(3)保存返回地址到(R14)lr_
----》(4)根基异常类型,跳转异常向量表
异常向量表在RAM启动代码中是这样写的:
Vectors ( 地址偏移量 )
LDR PC, Reset_Addr ; 0x00000000
LDR PC, Undef_Addr ;0x00000004
LDR PC, SWI_Addr ;0x00000008
LDR PC, PAbt_Addr ;0x0000000c
LDR PC, DAbt_Addr ;0x00000010
NOP ;0x00000014 ; Reserved Vector
LDR PC, IRQ_Addr ;0x00000018
LDR PC, FIQ_Addr ;0x0000001c
----》(5)进入相应的处理程序后,处理先保护现场(压栈)
----》(6)进行相应的中断处理(中断处理操作)
----》(7)处理结束后,将SPSR加载到CPSR
----》(8)恢复现场(出栈)
----》(9)将lr_给pc(恢复pc地址继续执行)。
在中断异常处理过程中,在进行处理程序时,要进行现场保护,(不能原破坏指令流水线),即压栈操作,处理完后要恢复现场(出栈恢复)。
以模拟FIQ发生压栈为例:
MRS R0,CPSR ;/* read CPSR value */
BIC R0,R0,#0x1f ;/* clear low 5 bit */
ORR R0,R0,#0x11 ;/* set the mode as FIQ mode */
MSR CPSR_cxfs,R0
LDR LR,=Ret
B Vectors+0x1c ;/* 0x1c为相对异常向量表中基地址的偏移量跳转至 FIQ mode */
FIQ_Handler
STMFD SP!, {R0-R3, LR} ;保护现场,压栈保护公共寄存器,LR_fiq=返回地址 FIQ模式
BL FIQ_Handle ;调用中断处理程序ISR (进行相应的操作)
LDMFD SP!, {R0-R3, pc}^ ;恢复现场,出栈恢复LR_fiq,R0-R3
SUBS PC, LR, #4 ;调整返回地址SPSR_fiq->CPSR, PC=LR_fiq-4
在不同的异常类型,LR链接寄存器的中的值与恢复流水线执行指令的偏移量不同。由于ARM的流水线特性(ARM7:三级流水线),pc在执行一条指令时,已经指向当前指令后的第二条指令,所以,当IRQ异常发生时,链接寄存器lr的值其实是最后执行的指令的地址+8.返回时应该跳到异常处理前最后一条指令的下一条指令。
下表给出了各种异常应使用的lr偏移:
异常 地址 用法
复位 - 没有定义
数据中止 lr - 8 指向导致数据中止异常的那条指令(数据中断,处理方式一般是尝试着再次进行对数据存取操作,因此lr - 8)
FIQ lr - 4 FIQ处理程序的返回地址
IRQ lr - 4 IRQ处理程序的返回地址
预取指中止 lr - 4 指向导致预取指中止异常的那条指令
SWI lr 指向SWI指令的下一条指令
未定义指令 lr 指向未定义指令的下一条指令
另外,
如果指令以pc为目标,且有后缀s,那么cpsr将自动从spsr中恢复,可以节省指令。用LDM的后缀^也有同样的效果。