之前有剖析过44b0下的这个发动代码,不同不是非常大,今日再从头看了一遍。发动代码与 Bootloader不同,首要是指进入C言语之前的汇编代码,网上都称为是bootloader的stage1,一般通用的内容包含:
1. 界说程序进口点
2. 设置反常向量表
3. 初始化存储体系
4. 初始化用户程序的履行环境
5. 初始化仓库指针寄存器,改动处理器的方法
6. 设置FIQ/IRQ中止处理程序进口
7. 进入C程序
1)编译器挑选
GBLL THUMBCODE
[ {CONFIG} = 16
THUMBCODE SETL {TRUE}
CODE32
|
THUMBCODE SETL {FALSE}
]
因为处理器分为16位 32位两种作业状况,程序的编译器也是分16位和32两种编译方法,所以这段程序用于依据处理器作业状况确认编译器编译方法,程序不难了解,首要解释一下 符号“[ | ]”的意思,上面的程序是指:
if({CONFIG} = 16 )
{ THUMBCODE SETL {TRUE}
CODE32 }
else
THUMBCODE SETL {FALSE}
还 是没有不明白CONFIG怎样区别是16位仍是32位?哪里决议它的取值?应该是编译器指定的这个值。
2)宏界说
MACRO
$HandlerLabel HANDLER $HandleLabel
$HandlerLabel
sub sp,sp,#4
stmfd sp!,{r0}
ldr r0,=$HandleLabel
ldr r0,[r0]
str r0,[sp,#4]
ldmfd sp!,{r0,pc}
MEND
$HandlerLabel是宏的地址标 号,HANDLER是宏名,$HandleLabel是宏的参数,$标号在宏指令翻开时,标号会替换为用户界说的符号。在尔后,一切遇 到$HandlerLabel HANDLER $HandleLabel这种方法的表达式都会被翻开成$HandlerLabel到MEND之间的函数。
例如:ADC_IRQ HANDLER HandleADC即代表如下函数,句子ldr pc,=ADC_IRQ的效果也便是跳转到这个函数:
ADC_IRQ
sub sp,sp,#4
stmfd sp!,{r0}
ldr r0,=HandleADC
ldr r0,[r0]
str r0,[sp,#4]
ldmfd sp!,{r0,pc}
这段程序用于把ADC中止服务程序的首地址装载到pc中,跳转到中止处理函数,称之为 “加载程序”。HandleADC是一个地址标号,它的内容便是ADC中止服务程序的地址标号,即文件最终的那个表HandleADC # 4所示,将HandleADC # 4中的4换成中止服务程序的地址标号便是,程序在这儿界说了一个数据区,寄存各种中止服务程序的首地址。每个字空间都有一个标号,以Handle***命 名。
3)寄存器及仓库设置
依照上面的次序,可以比较简单读懂发动代码的效果,首要便是经过设置特别功用寄存器来到达对体系参数的 设定。顺次制止看门狗,中止,设定时钟操控寄存器,存储器操控寄存器等等。
因为各个作业方法下的仓库指针是彼此独立的,所以要别离进入各个方法下 设置其仓库指针,基本上都差不多,比方未界说指令方法下的设置:
mrs r0,cpsr
bic r0,r0,#MODEMASK
orr r1,r0,#UNDEFMODE|NOINT
msr cpsr_cxsf,r1
ldr sp,=UndefStack
UnderStack是在程序后边用UnderStack # 256树立的一个仓库空间的首地址,这部分空间树立在RAM中,256字节空间的仓库巨细。
4)初始化用户程序的履行环境
之前在 44B0里的发动代码里还有包含把ROM里的程序拷贝到RAM中并跳转到RAM运转程序,也便是把加载状况下的程序依照编译衔接时的设置从头排布成运转时 的程序状况,以到达符号可以正确衔接的意图,这儿是触及到了所谓的映像文件,可是2410这儿没有这一段,即程序的加载态便是它的运转态,所以要求烧写程 序时有必要要把它烧写在设置的RO地址上,不然程序将不能正确履行。下面这段程序完成RW数据初始化,仅仅把数据段复制到高地址,假如没有设置RW的话这段 代码也不会履行。
;Copy and paste RW data/zero initialized data
ldr r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
ldr r1, =|Image$$RW$$Base| ; and RAM copy
ldr r3, =|Image$$ZI$$Base|
;Zero init base => top of initialised data
cmp r0, r1 ; Check that they are different
beq %F2
1
cmp r1, r3
ldrcc r2, [r0], #4
strcc r2, [r1], #4
bcc %B1
2
ldr r1, =|Image$$ZI$$Limit|
mov r2, #0
3
cmp r3, r1 ; Zero init
strcc r2, [r3], #4
bcc %B3
b %F1(B1)的意思是在接近的地址标号跳转,F是向后寻觅,B是向前寻觅。
5) 说说映象文件
用ADS编译发生的映像文件有.axf、.bin、.hex等等格局,就拿要直接烧进Flash里的.bin文件来说,在其他书上看 到有这么句话“映像文件一般由域组成,域由最多三个输出段(RO,RW,ZI)组成,输出段又由输入段组成。”
关于这段话,前两句是比较好了解 的,域便是整个映像文件,关于大部分程序来说就只有一个域,也便是烧进Flash里的那部分东东,称作加载域;输出段便是用AREA界说的RO,Rw,一 般就这两个,拿前面的bootloader来说,全体结构是这样的:
AREA Init,CODE,READONLY ;<--RO段
ENTRY
Entry ;<--CODE部分
… …
SMRDATA DATA ;<--DATA部分
… …
AREA RamData, DATA, READWRITE ;<--RW段
… …
但是关于 输出段又由输入段组成却着实糊涂了好一阵,输入段是指源程序的代码(CODE)部分和数据(DATA)部分。上面这个结构中,在RO输出段的Entry下 开端一系列的汇编指令操作,这个应该是CODE输入段,而SMRDATA DATA引领DCD用于拓荒一片数据存储空间,这部分应该是DATA输入段,它与RW里的数据不同之处在于这部分数据不能被修正。
在ADS编译前 在ARM-Linker里的Ro_Base和Rw_Base两个地址值,便是指两个输出段的开始地址,即程序是依照你设置的这种方法排布在内存中的,各个 地址标号依据这两个值确认。但是,用Ultraedit翻开bin文件却发现其实Rw是跟在Ro后边的,也便是说,这两个段并没有依照你设置的地址开始, 由此引出映像文件的加载域和运转时域两个概念。