首先看head.S,是用汇编写成的,基本定义了整个程序的框架。
.extern main .text .global _start_start: b Reset @一开始先定义中断向量表,这个必须按照arm说明书上规定的终端向量位置来写,如果写的位置不对,是无法进行中断跳转的,比如在地址0x0位置就一定是 “复位中断” HandleUndef: b HandleUndef @0x04 未定义指令终止模式的向量地址 HandleSWI: @0x08 管理模式的向量地址,通过SWIft指令进入此向量地址 b HandleSWI HandlePrefetchAbort: b HandlePrefetchAbort @0x0c指令预取终止导致的异常的向量地址 HandleDataAbort: b HandleDataAbort @0x10数据访问终止导致的异常的向量地址 HandleNotUsed: b HandleNotUsed @0x14保留 b HandleIRQ @0x18这个就是我们要用到的IRQ中断,0x18是它的向量地址,有人可能要问为什么这里的写法和上面不同,没有HandleIRQ:这个标签。这是因为我们会在下面详细定义,如果像上面那样写,其实是相当于,假如发生了DataAbort中断,就只能在一个死循环里不断运行。 HandleFIQ: @0x1c快中断模式的向量地址 b HandleFIQ Reset: @这里就开始详细定义系统复位指令 ldr r0,=0x53000000 @下面这3句汇编实现了一个功能:关看门狗 mov r1,#0x0 str r1,[r0] msr cpsr_c,#0xd2 @在arm汇编中,唯一能更改cpsr的指令就是msr, cpsr_c是CPSR的低8位, 下面我们来认真分析一下,首先把0xd2换算成2进制,就是110 10010,可参见CPSR各bit的定义,可知CPSR的低5位是工作模式位,如果定义为10010就是进入IRQ(中断模式), 于是这条指令的意义就是进入中断模式 ldr sp,=3072 @进入中断模式后,这里设置了中断模式下的栈指针,请注意,中断模式是有自己的堆栈寄存器的,就是r13,在这里设置好堆栈指针是因为我们后面会用到 msr cpsr_c,#0xdf @回到系统模式 ldr sp,=1024*4 @设置系统模式的栈指针,注意,在这里设置栈指针也是为后面使用C语言服务。 bl init_irq @跳转到init_irq,这里直接跳转到了C语言的标签,init_irq,其实就是init_irq函数, 这个函数会在后文详细解释,作用是初始化IRQ中断 msr cpsr_c,#0x5f @这里打开了IRQ中断,同样把0x5f换算成2进制,010 11111,后5位不变,依然在系统模式下,前3位,第一位就是IRQ禁止位,设置为0就是打开IRQ中断。 ldr lr,=halt_loop @设置返回地址,arm的返回地址都放在lr寄存器中,lr寄存器就是r14 ldr pc,=main @调用main函数 halt_loop: @下面是一个死循环 b halt_loop HandleIRQ: @注意,这里就是我们前面提到的,详细定义处理IRQ中断的标签。 sub lr, lr,#4 @计算返回地址,由于进入异常工作模式时,连接寄存器中保存了前一工作模式的一个指令地址,将它减去一个适当的值,最后付给pc寄存器就可以完成退出中断,具体减去的值需要查表 stmdb sp!, { r0-r12,lr } @这一条指令一步就完成了保存现场的任务,将R0~R12还有lr的数据保存在堆栈寄存器中,堆栈寄存器在保存第一个值之前增加,增长方式为向下增长。sp指针我们在之前定义过了,现在就用到了 ldr lr,=int_return @设置调用EINT_Hanle函数后的返回地址 ldr pc,=EINT_Handle @调用中断服务函数,同样是一个C函数,在interrupt.c中,之后会详细介绍. int_return: ldmia sp!, { r0-r12,pc }^ @中断返回,^表示将spsr的值复制到cpsr,同样,相当于一步恢复现场