这里我用到的汇编代码是来自YL9200开发板的资料,先提出该汇编文件:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
相信有很多人和我一样,刚刚入手arm的时候非常忙然,无知得连问题都问不出来,其实我现在就是这样,所以才决定做一个这样的分析,一是记录自己的学习过程,一是把成果与大家共享。
9200的资料不多,至少文字版的资料不多,我们学校图书馆关于ARM的书几乎全是三星2410和2440,让像我这样上手就是工业级的9200芯片学习者有点苦恼。不过网上有很多共享者们提供了很多信息,所以只要善于学习,还是能够学好的,至少我是这么认为呢,虽然现在我还不是很懂。
说说我的情况,我刚做完自己的一个9200硬件电路板,有时间也把相关经历共享一下。现在开始学习软件,虽然对汇编,c语言等都有一定的基础,但是读起相关的代码还是有很多困难,所以我决定记录一下,以防以后笔记丢失,当然共享才是我的真正目的,呵呵~~~
好,进入正题。
先正题分析一下9200的这个启动代码包含的功能模块:
1:定义程序入口地址
2:系统时钟初始化
3:定义中断向量表
4:堆栈初始化
5:映像文件运行域初始化
6:进入主程序
这个顺序有点乱,分析的时候会给出对应的代码
接着讲,这次让我们来具体分析一下,我们就按照该文件的执行顺序来进行分析:
首先,程序入口地址进入后,第一条指令便是
b ResetHandler
直接跳到标号ResetHandler处,执行复位后的处理程序,这段程序是:
ResetHandler
;Set up User Mode, set User Mode Stack and disable interrupts
msr CPSR_c, #(SVCMODE|I_BIT|F_BIT)
这句代码做了一件事,让系统进入管理模式,并且屏蔽IRQ和FIQ中断,这里用到了cpsr寄存器,该寄存器是程序状态寄存器,具体用法我的博客的另外一篇文章有简单的介绍。用到的汇编指令msr是将寄存器的值或者立即数放入程序状态寄存器中。
接下来,程序将顺序执行,有如下代码:
ldr r1, = AT91C_BASE_CKGR ; Get the CKGR Base Address
ldr r0, = AT91C_CKGR_MOSCEN:OR:AT91C_CKGR_OSCOUNT
str r0, [r1, #CKGR_MOR]
该段代码的作用是使能主振荡器,并制定慢时钟的周期数作为主振荡器的启动时间。
接下来,重定向异常向量表:
; relocate exeception table
adr r0, ResetEntry
mov r1, #0
mov r2, #16
copyex
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
adr指令是获取ReserEntry标号地址赋予r0,接下来进行的是一个循环,目的是将ResetEntry地址对应的指令赋予地址0x0处,即起始地址处,同理,循环16次即把其他的中断服务程序首地址也放在起始地址处,每次地址加4,即一个字。而起始地址处是9200固定的中断跳转的地方。所以,本段代码实现了将中断服务程序的首地址放到中断向量表中,有中断的时候会执行跳转,到中断服务程序继续执行。
接下来进行基本的初始化:
IMPORT LowLevelInit
; ldr r1, =0x204000;=SVCStack
; bic r1, r1, #3 ; Insure word alignement
; mov sp, r1 ; Init stack SYS
mov sp, #0x204000
bl LowLevelInit
先声明lowlevelinit函数来自外部,实际上来自init.c文件中,然后初始化了管理模式的堆栈,然后进行函数调用,执行一些初始化工作,该初始化工作的内容在这里就不多讲了,主要包含PLL初始化,中断控制器的初始化,SDRAM初始化,isr初始化,时钟计数器初始化,调试串口初始化,串口配置等工作。
然后进行的是堆栈初始化:
;Initialize stacks
bl InitStacks
也采用的是程序调用的方式,程序代码如下:
;function initializing stacks
InitStacks
;Don't use DRAM,such as stmfd,ldmfd......
;SVCstack is initialized before
;Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'
mrs r0,cpsr ;读取程序状态寄存器
bic r0,r0,#MODEMASK ;清楚寄存器的低五位
orr r1,r0,#UNDEFMODE|NOINT ;修改寄存器的值,使工作在未定义模式,禁用中断
msr cpsr_cxsf,r1 ;UndefMode;回写到程序状态寄存器中,执行改变工作模式的操作
ldr sp,=UndefStack ;获得未定义模式的堆栈地址
orr r1,r0,#ABORTMODE|NOINT
msr cpsr_cxsf,r1 ;AbortMode
ldr sp,=AbortStack
orr r1,r0,#IRQMODE|NOINT
msr cpsr_cxsf,r1 ;IRQMode
ldr sp,=IRQStack
orr r1,r0,#FIQMODE|NOINT
msr cpsr_cxsf,r1 ;FIQMode
ldr sp,=FIQStack
bic r0,r0,#MODEMASK|NOINT
orr r1,r0,#SVCMODE
msr cpsr_cxsf,r1 ;SVCMode
ldr sp,=SVCStack
;USER mode has not be initialized.
mov pc,lr
;The LR register won't be valid if the current mode is not SVC mode.
该段程序主要实现的是各种模式下的堆栈的初始化工作。学过单片机我们知道,我们在进行函数调用和中断的时候,都会用到堆栈,为什么我们用单片机的时候没有涉及到堆栈的初始化呢,那是因为我们在用单片机的时候,基本上只有一种模式,,一个堆栈,而ARM则有很多种模式,各个模式的堆栈是不一样的,所以就涉及到了各个模式要初始化不同的堆栈地址,这就是我们这段程序的目的。
根据第一个未定义模式的注释我们可以了解后面几种模式的操作,都是一样的,读取,修改,回写的方法。最后的一个mov指令进行子程序返回。
下一次再接着讲,今天先到这儿~~~~
文章评论(0条评论)
登录后参与讨论