前语:本篇剖析的是一个最精简的发动代码,并且包括一个简略的中止处理,C程序部分省掉,要点剖析汇编部分,这是由于关于我来说,汇编代码实在是让人厌烦,可是又不能不必。
下面是对代码的剖析,赤色部分是剖析成果
//寄存的是PC的值,而PC的值是当时运转指令地址加8,尽管产生了中止,可是CPU仍然会 //运转完当时指令后才去处理中止,所以LR-4的值,正是被中止指令的下一条指令,傍边 //断处理完毕今后,回来到被中止指令的下一条指令进行履行。
//bl EINT_Handle替代,由于这个程序自身并不大,不会超越4Kb
.extern main //.extern 表明main在其他的文件中界说,在这儿要引证,至于为什么只声明晰extern而没 //有声明其他,现在还不知道,不过都声明一下,应该没问题
.text //.text 表明后边的内容编译出来放在代码段
.global _start //.global 告知编译器后边声明的是一个大局可见的姓名,由于这是发动代码,所以cpu上电后 //必需求找到_start函数,所以_start有必要声明为大局的,当然这个姓名能够改,无所谓
_start: //检查反汇编地址就能够发现,_start的地址便是0x0
b Reset //不带回来的跳转到Reset中,为什么不必回来呢,由于没必要,reset中直接进入到主函数内了
HandleUndef: //检查反汇编地址可知HandleUndef的地址位0x04,这个很好了解,由于一条指令4个字节
b HandleUndef //在这儿由所以精简的发动代码,所以一旦产生了未界说反常时,就让CPU自己玩死自己吧
HandleSWI: //反汇编地址0x08,其他剖析同上
b HandleSWI
HandlePrefetchAbort: //反汇编地址0x0C,其他剖析同上
b HandlePrefetchAbort
HandleDataAbort: //反汇编地址0x10,其他剖析同上
b HandleDataAbort
HandleNotUsed: //反汇编地址0x14,其他剖析同上
b HandleNotUsed
HandleIRQ:
b HandlerIRQ //反汇编地址0x18,看到这儿比较就有点感觉了,0x18是一般中止向量地址,这个就有很 //多当地可说了,首要这儿依然是运用B指令进行跳转,这个之所以不直接用BL的原因 ///是,中止还不太算是调用函数那么简略,产生中止的时分,要保存现场,而现场的数 //据有许多,详细为R0-R12、PC、CPSR的内容,这些都需求进栈保存,所以这些的内容不 //是BL一条指令能完结的,所以保存现场的内容就直接放到了中止程序中了。其非必须特别 //留心这个里边跳转的程序时HandlerIRQ不是HandleIRQ,假如还像上面那样,那么产生中 //断时,CPU就会不断的循环来回跳了,所以这点需求特别留心,当然还有一种办法,便是 //不要这条指令的标号HandleIRQ,直接运用一条 b HandlerIRQ即可。
HandleFIQ:
b HandleFIQ //反汇编地址位0x1C,由于本程序只剖析简略的一般中止,所以以为产生快速 //中止的时分,让CPU自己玩
Reset:
ldr sp,=4096 //由于要在汇编中调用C函数,所以要先设置仓库指针,详细为什么,就 //不详细剖析了,简略的说,C函数要运用许多的中心内存寄存运算数据,所以 //要拓荒一段仓库用。这儿还要特别说到一点,由于ARM作业方式的特色,此刻 //设置的仓库指针SP,即R14,对应的是体系方式下的仓库指针寄存器,即R14_svc
bl disable_watch_dog //封闭看门狗
msr cpsr_c,# 0xd2 //设置CPU进入到中止方式
ldr sp,=3072 //由于产生中止时,要用到仓库用于保存现场,所以需求先设置中止方式下的堆 //栈指针地址(设置仓库指针就相当于拓荒仓库了,由于仓库便是有仓库指针的 //一片一般内存区域),即设置R14_irq,特别留心R14_irq和R14_svc是不相同 //的,仅仅同名,可是指向的物理地址是彻底不同的。
msr cpsr_c,# 0xdf //从一般中止方式回来到体系方式
bl init_led //这个十分值得剖析,首要这儿运用了BL指令进行跳转,BL指令的特色便是,跳转 //的一起,会主动的将PC的值放到LR中,并且,假如后边跳转的是C函数的话,C函 //数中就不必设置回来指令了,而假如BL后边跟的是一个汇编子程序,那就需求程 //序员自己增加回来指令,这是为什么呢?假如用专业术语说,假如调用C函数, //函数运转完毕后,编译器会主动的将上面保存的LR中的内容减去4重新传递给 //PC(为什么减4下面剖析),也就完成了主动回来的功用,可是汇编函数不可,因 //为汇编的编译器没有这项功用,所以浅显的讲,为什么C言语是高档言语,除了语 //法高档人性化,配套的编译器也比较人性化,会帮程序员做一些通用的作业。而 //汇编言语就比较一根筋,一切的事儿,哪怕是最简略的事儿,都要程序员一条一 //条的把代码敲上去告知它怎样干(这也是我为什么厌烦汇编的原因,比较懒,呵 //呵),至于调用汇编子程序怎样回来怎样做,剖析在下面
bl init_irq //剖析同上
msr cpsr_c,# 0x5f //开中止,即答应一般中止IRQ
ldr lr,=halt_loop //这个也很有意思,在这儿为什么不直接用 bl main替代这两条指令呢,因 //为。。。。。想嘚瑟一下(玩笑了),主要是想学习其他一种常见常用的跳转方 //法。BL指令很好用,可是有缺点,便是跳转的规模,由于它只能跳转戋戋32MB, //换算成ARM的指令,也就只要8M的地址(由于一条指令4个字节),也就 //是-4MB~+4MB,所以规模有限,而嵌入式程序,尤其是有操作体系时,程序寄存地 //址跨度比较大,所以呢,需求有一个长跳转指令,所以就引出了LDR指令,这个命 //令的跳转规模是4G,所以知道为什么LDR长用了吧,由于不管地址在哪,它都能跳 //到,跳转规模大是LDR指令的长处,可是它也有缺点,便是它担任跳转,不能保存 //PC,假如在跳转前不往LR中放回来地址,就会呈现,跳到了子程序的方位,子程序 //履行完后,跳不回来了,所以假如运用ldr调用子程序,必定要在调用前给LR一个 //回来地址。
ldr pc,=main
halt_loop:
b halt_loop //main函数的回来地址,或许说是回来操作,让CPU原地踏步
HandlerIRQ:
sub lr,lr,# 4 //这条指令用于核算中止处理完毕后的回来地址,在这儿有个隐含的当地,即当产生中 //断时,处理器会主动的向LR寄存器中寄存被中止指令的地址加4,或许也能够了解成,
stmdb sp!,{r0-r12,lr} //入栈指令,表明将R0~R12,以及LR寄存器的内容入栈,这个指令格局暂不剖析,详细的能够参阅 //相关手册
ldr lr,=int_return //这两条指令的剖析跟上面跳转到main函数原理是相同的,能够运用一条指令
ldr pc,=EINT_Handle
int_return:
ldmia sp!,{r0-r12,pc}^ //出栈指令,跟上面的入栈指令相照应,表明将sp对应的地址内容递加(由于ARM的仓库式满递减 //方式)的顺次寄存到r0~r12,以及pc中,并且这个很有意思,依照次序,sp即r13必定不包括在 //内,所以到pc的时分,赋值的内容寄存器正好是LR,而LR已经在中止最初处理了,并且^表明将 //spsr的值复制到cpsr中,至此完结了中止的回来