CPU会自动识别产生的异常,并将当前的PC值定位到异常向量表的某一个位置,但是由于异常的产生是随机的,我们并不知道什么时间会出现在什么类型的异常,所以需要一个异常向量表(也有说是中断向量表),来完成对不同异常处理函数入口地址的映射。
中断也是异常的一种,中断有硬中断(由硬件产生的中断)和软中断(软件产生的中断)之分。ARM有七种不同的中断源,在中断向量表中对应的地址范围是0X00 ~ 0X1C,本章只介绍软中断。软中断执行由SWI指令产生,用于用户模式下的程序调用特权操作指令。
简单实现软中断
我们可以通过软件仿真来查看程序的执行过程,首先硬件复位后,PC指向0X00(异常向量表中的第一个位置),执行跳转rest函数,通过swi触发软中断,CPU帮我们将PC指向0X08这个地址,这里有是一个跳转指令,跳转到swi_hander函数。
所以,只要是异常产生,CPU就会帮我们把PC指针定位到异常向量表中某一个位置,这里实现地址的映射,指向什么函数入口,需要我们自己来实现。
.text .global _start _start: @异常向量表 0X00 - 0X1C b rest @0x00 系统复位后的函数入口 rest nop @0x04 b swi_handler @0x08 软中断函数地址入口 nop @0x012 nop @0x014 nop @0x018 nop @0x01c swi_handler: stmfd sp!,{r0,lr} @将r0 lr的值进行压栈保存 sp!表示将 sp栈顶指针自动偏移 mov r0,#5 ldmfd sp!,{r0,pc} @栈中的值将分别出栈赋值给 r0,pc rest: ldr sp,=stack_base @将地址栈底地址装入sp mov r0,#3 swi 2 @swi 触发软中断 b rest .data .space 32 @栈空间分配 stack_base: @栈底指针 .end
.global _start 声明全局变量_start,整个工程可见
@ 表示一个函数 swi_handler: @... @... rest: @... @...
@我们使用栈来做现场的保存与恢复 stmfd sp!,{r0,lr} @将r0 lr的值进行压栈保存 @... @... ldmfd sp!,{r0,pc} @栈中的值将分别出栈赋值给 r0,pc
软中断的进阶使用
做了一些改进
- 出栈时恢复现场,同时恢复工作模式
- 利用软中断号来实现不同的功能
- 解决 b指令的缺陷 -- 无法操作地址长度较大的函数地址
由于产生软中断会将工作模式成为SUV模式,如果想要中断结束后恢复之前的模式,需要在出栈ldm后面加上 ^ ; swi 2产生软中断,2是软中断号,存放在这条指令空间中。由于异常发生时,处理器会把当前PC的值保存到LR,所以我们通过LR中的值来拿到软中断号; 一个b指令空间只有4个字节,其中一部分还有存放操作码,一些地址长度比较大的函数就无法实现了,我们用LDR指令来实现跳转。
.text .global _start _start: b rest @0x00 系统复位后的函数入口 rest nop @0x04 ldr pc,_swi_handler @0x08 软中断函数地址入口 nop @0x012 nop @0x014 nop @0x018 nop @0x01c _swi_handler: .word swi_handler @一个字的空间存放地址 do_swi: mov r3, #3 mov r4, #4 mov pc,lr swi_handler: stmfd sp!,{r0,lr} @将r0 lr的值进行压栈保存 sub r0,lr,#4 @取出软中断号,并且比较软中断号 ldr r0,[r0] bic r0,#0xff000000 @清除高八位数据 cmp r0,#2 bleq do_swi @处理软中断 ldmfd sp!,{r0,pc}^ @栈中的值将分别出栈赋值给 r0,pc ^代表出栈恢复入栈时的模式(User) rest: ldr sp,=stack_base @Suv模式下 mrs r0,cpsr and r0,r0,#0xFFFFFFE0 orr r0,r0,#0x10 msr cpsr,r0 @进入User模式 mov r0,#3 swi 2 @swi 触发软中断 中断号为 2 b rest .data .space 200 @栈空间分配 stack_base: @栈底指针 .end