1、搞清楚ARM中的MACRO伪指令,这个伪指令就是我们在汇编中的宏定义,我们都知道宏的实现能够避免代码的重复型以及代码的可修复性。关于ARM汇编中的宏定义基本的形式如下:
MACRO
{$label} macroname {$parameter} {$parameter}…
Code
MEND
其中$label 宏指令被展开时,label可被替换为相应的符号,一般为一个标号
macroname 所定义的宏的名称
$parameter 宏指令的参数,当宏指令被展开时被替换成对应的值。
2、依据上面的定义我们可以知道当前这段代码定义了一个宏指令,HANDLER,其中标号为$HandlerLabel,参数为$HandleLabel
基本的实现代码分析如下:
sub
sp,sp,#4;
在栈中预留一个区域,用来保存PC的值
stmfd
sp!,{r0}
;
由于r0还需要被使用,因此需要被压栈
ldr
r0,=$HandleLabel ;这里的ldr是一个伪指令,主要是将标号$HandleLabel的地址加载到r0中,这也是压栈r0的原因。
ldr
r0,[r0]
;这是ARM的ldr指令,主要是将$HandleLabel对应地址中的内容加载到r0中。如果在$HandleLabel中保存的是一个中断处理函数的地址,那么只需要将这个值加载到PC即可实现了中断任务跳转,实际上这个过程就是采用了异常处理的第二种方式:
即加载PC的方式,而不是简单的跳转方式。
str
r0,[sp,#4]
;store the contents(ISR) of HandleXXX to stack
ldmfd
sp!,{r0,pc}
OP the work register and pc(jump to ISR)
这两句代码正是这段代码的精髓。基本形式如下:
str
r0,[sp,#4],是指将r0的内容,也就是异常处理函数的地址保存到栈中的SP-4位置处,这个位置也恰好是之前sub
sp,sp,#4;
用来预留给保存PC值的位置,这时将异常处理函数的地址保存在这个地址处,接下来的ldmfd
sp!,{r0,pc}刚好就是将栈中的内容加载到R0和PC中,这样也就实现了将异常处理函数地址加载到PC.实现了跳转过程。
| 高地址
| SP_0/SP_3
| …
| SP_1
| Handle_addr
| SP_2
| R0
|
| 低地址
|
从上面的分析可以知道这种中断处理的方式,并不是中断处理中的简单跳转方式(因为跳转范围的局限性)而是采用更新PC值的形式实现的。
接下来分析IRQ,这种在我们实际开发中使用比较多的中断形式进行分析。
首先可以发现存在:
1、b
HandlerIRQ
;handler for IRQ interrupt
这种情况下发生在中断产生过程中,是在IRQ向量中执行的,也就是在0x18处执行,其中HandlerIRQ实质上是一个标号,对应一个具体的地址。其中保存的内容就是对应IRQ处理函数的地址。但是在代码中只有一个HandlerIRQ,形式如下:
HandlerIRQ
HANDLER HandleIRQ
2、HandlerIRQ
HANDLER HandleIRQ
根据上面的宏定义,可以将这句代码进行扩展,得到如下的形式:
HandlerIRQ
sub
sp,sp,#4
stmfd
sp!,{r0}
ldr
r0,= HandleIRQ
ldr
r0,[r0]
str
r0,[sp,#4]
ldmfd
sp!,{r0,pc}
|