文档库 最新最全的文档下载
当前位置:文档库 › arm7,44b0x,bootloader,完全分析

arm7,44b0x,bootloader,完全分析

;开始写注释了,先娱乐一下,了解什么叫touch,linux里有个命令叫touch,就是摸的意思嘛,
;一般情况下我们的手的是脏的(55我的手已经洗的很干净了的啊),谁管你,反正是你摸过的文件都是脏的,
;所以touch的意思就是把文件弄脏,狂晕

ModeMask EQU 0x1F ;模式位掩码,在那个标志寄存器中的东东,妈的,叫状态寄存器还是标志寄存器啊!
SVC32Mode EQU 0x13 ;意思很明确了,开机就进入这个模式
IRQ32Mode EQU 0x12 ;同上,IRQ中断触发这个模式,有几个引脚可以做到,呵呵
FIQ32Mode EQU 0x11 ;现在我还不是很理解fiq模式什么时候要用,反正就是有这个东东,大概我想是在停电的时候用吧,所谓的掉电保护
User32Mode EQU 0x10 ;用户模式,我是不想用的,老子是管理员
Abort32Mode EQU 0x17 ;终止模式,又分两种情况,预取指终止和数据终止,反正就是取到不该取的或者内存越界的指令或数据就那样了,简单说就是他妈的内存才1M你就取2M处的指令,不错才怪
Undef32Mode EQU 0x1B ;没定义的模式,新的模式哦,可以自己定义新的指令哦,,不太记得是不是这个了
IRQ_BIT EQU 0x80 ;这个嘛,禁止开启irq
FIQ_BIT EQU 0x40 ;同上

;好累啊,写这么多东西会写死我的,要是我大学的时候,时间多就不说了,唉,谁叫我又浪费了大学的美好时光呢,连个女朋友都没有!!!失败

GBLS MainEntry ;全局符号,编译的时候用,运行的时候大概就没有用了
MainEntry SETS "main" ;设置这个符号的值,预编译啊
IMPORT $MainEntry ;main是c语言里面定义的,外部定义的东西应该要引入

;**********************************************************
;检查是否使用tasm.exe进行编译

GBLL THUMBCODE ;设置全局逻辑符号
[ {CONFIG} = 16 ;预编译中的#if...#else...#endif相当于,好久没有用basic了,if CONFIG = 16 then
THUMBCODE SETL {TRUE} ;THUMBCODE = TRUE
CODE32 ;使用32位arm代码,编译器就靠这个标志把下面的代码转换成32位代码了
| ;else
THUMBCODE SETL {FALSE} ;THUMBCODE = FALSE
] ;end if

[ THUMBCODE ;if THUMBCODE = TRUE then
CODE32 ;for start-up code for Thumb mode 这句话写错了吧,还是我理解有问题?感觉有点多余
] ;end if

;******************************************************
AREA SelfBoot, CODE, READONLY ;声明一段,是代码段,名字是SelfBoot,只读属性,个人感觉只读属性是在编译的时候用到,让编译器及时检查到错误,声明这个段则是为了连接器把各个模块的各个段按一定的顺序连接起来,段名可能会在连接器连接参数用到,表明要把这个段放在最开始,是启动段

IMPORT UDF_INS_VECTOR ;引入外部标号,这个标号是map

出来的,map不占空间,只是定义了一系列的标号,而且标号指向的地址是可控的,由map的参数和后续的#联合指定,这是未定义指令异常的处理程序的标号
IMPORT_SVC_VECTOR ;同上,软中断处理程序的标号
IMPORT INS_ABT_VECTOR ;同上,预取指终止异常的处理程序标号
IMPORT DAT_ABT_VECTOR ;同上,数据终止异常处理程序标号
IMPORT IRQ_SVC_VECTOR ;同上,IRQ中断处理程序标号
IMPORT FIQ_SVC_VECTOR ;同上,FIQ中断处理程序标号
;说说标号,标号,就是地址值,所以我说标号指向的地址,好像说的不太确切,表达有问题啊,总之,程序里出现的标号,在编译时期将会全部被替换成偏移地址,为什么说偏移地址啊
;因为还没有确定程序的执行时的位置嘛,在连接时期,这些偏移地址将会由连接器重写,变成确切的运行时地址,更有甚者,变成可重定位的地址(猜的)
;连接器,也就是把各个段,各个模块连接起来,形象点就是堆叠起来,这个连接和链接是什么区别啊,我语文不太好,惭愧!
;所以,一般来说编译是对单文件来说的,连接是对多文件来说的
;说到连接,我又想起了另外一件事,就是所谓的加载代码,拿linux来说,我们在一个壳里输入命令,就是shell,用户接口嘛,当运行一个程序的时候,其实是由这个壳来加载程序
;加载程序进入了一个统一的初始化代码(不知道是不是统一的,我乱说的),初始化代码负责一些初始化工作,这不是废话吗?好像有个比较重要的工作就是向父进程(这里就是shell)
;申请一个新进程,然后进入程序真正入口。初始化代码相当于程序的外壳,此壳非彼壳,当程序退出的时候,初始化代码会负责相应的销毁,比如说kill这个进程,具体很多都忘了,大概就是有这些东西吧
;所以,当我们在linux连接程序的时候,隐含着连接一个初始化程序,叫做startup.o什么的,忘了
;当然,在这里的连接,就没有这个startup.o,为什么,因为没有那个壳啊,都是裸机运行,老子就是王,要什么初始化,老子自己初始化自己
;唉,又扯了一大段,TMD什么时候才到头啊


ENTRY ;入口就是这里了,在ads的一个配置文件里面配置,另外一个boot.s的我不知道是什么意思
IF :DEF: |ads$version| ;也是预编译指令,这里ads$version是ads的版本号,意思就是说如果这段程序用ads编译的话,就干嘛干嘛,否则就干嘛干嘛,这里我当然是用ads编译的拉,ads这么好用
ELSE ;如果是用ads编译的话,就什么都不作,直接else
EXPORT __main ;如果是用非ads编译器编译的话,就导出一个__main这样的符号,也可以叫做标号吧
__main ;这里定义了一个__main标号,可能是考虑到其他编译器有这

个需求,也就是一定要有个main入口,跟c语言一样,注意,这里我说的是main入口,但这里明明是__main啊,这个嘛,C语言的标号(或者说函数名)在编译后都会在前面加__的,明白没,所以c的main在符号表里就表现为__main,这也是汇编调用c的规则嘛
ENDIF ;如果这里定义了__main,那么c语言里就不能在用main了,答案同上,可以用Main,MAIN之类的
ResetEntry ;开机就转到这里,也不一定是转到这里,开机时pc=0x0的,当然要把它烧到0x0才行,RO与这个标号的地址还有些差别,下面说到
b SYS_RST_HANDLER ;上面是标号,这里才是代码,跳转到SYS_RST_HANDLER,实际的机器代码只是一个相对于当前PC的偏移,一开机就跳到SYS_RST_HANDLER了
b UDF_INS_HANDLER ;唉,不想说了,反正就是一个跳转,下面有定义
b_SVC_HANDLER ;不说了
b INS_ABT_HANDLER ;不说了
b DAT_ABT_HANDLER ;不说了
b . ;各位大叔大婶注意了,这个点很诡异,因为我找了好久才找到它的说明,很简单的几个字”当前地址“,NND,害我找了这么久,这里就是死循环嘛
b IRQ_SVC_HANDLER ;不说了
b FIQ_SVC_HANDLER ;不说了,累了

;想想还是扯一下吧,这个b .可以表示成b PC-8,想想为什么?三级流水线啊,取指,解码,执行,太他妈流畅了。但是跳转会破坏当前流水线的哦,破坏流水线就意味着重新填满流水线,再执行
;上面是异常跳转表,俗称中断向量表,这里其实有点称谓上的混乱,samsung有个所谓的向量中断模式,也有个非向量中断模式
;熟悉8086的兄弟可能会想,tmd干嘛搞个非向量中断模式啊,并且中断向量不就是一些跳转的目的地址吗,这里怎么变成指令了
;我也纳闷,太tmd别扭了,但后来还是忍着恶心接受了,arm就要这样,你又能怎样,不过这样似乎更加自然点
;向量中断模式相对于irq来说,fiq我不知道,好像也有。当使用向量中断模式时候,我们还需要再定义一些中断向量,产生中断,则把对应的跳转指令覆盖到irq那,就是替换b IRQ_SVC_HANDLER这句话
;然后直接跳转到服务程序,非中断向量模式是直接触发 b IRQ_SVC_HANDLER,还要检查一些寄存器的位来判断中断产生源,恶心啊
;太多了,这里我说的不是很清楚,不过还有一个温馨小提示:irq中断检查的时机是在执行当前指令之前的那一刻,所以中断回来以后要重新执行被中断的指令,还要收到3级流水线的影响,这点在异常返回指令有所体现
;这里还有个问题,我常常把中断和异常混在一起,但资料上说是有区别的,要注意

;******************************************************

;这里是定义了一个宏,宏者,编译时替换
;这里有两个变量$Label,$Vector,就是标号和向量,应该很好

理解了,向量就是中断服务入口,一般是map出来的,这个bootloader是没有用到这个宏,但在sysinit.s里map了一些$Vector
;下面回顾一下arm7响应中断或异常时硬件自动作的事情
;R14_ = Return Link 将下一条指令的地址存入相应异常连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行
;SPSR_ = CPSR 将CPSR复制到相应异常的SPSR中
;CPSR[4:0] = Exception Mode Number 根据异常类型,强制设置CPSR的运行模式位
;CPSR[5] = 0 ;arm态
;If == Reset or FIQ then
;CPSR[6] = 1 ;禁止fiq
;CPSR[7] = 1 ;禁止irq
;PC = Exception Vector Address 强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处
;其中我们要注意第一句话:将下一条指令的地址存入相应异常连接寄存器LR,如果是irq中断,上面说了,是在当前指令执行前的那一刻检查的,那么当前PC已经指向执行当前指令地址+8的位置了,ps:PC总是指向取指的地址,而不是执行指令的地址
;则存入LR下一条指令的地址就是PC-4,注意这里当前指令还没有执行,所以中断回来以后,要重新执行当前指令,也就是LR-4,或者说是PC-8,我们需要LR-4->PC
;但是,上面是说irq的,对软中断,或者说bl指令,返回地址又有点区别,先歇歇吧,慢慢来,不要搞混了哦,^_^
中断指令产生的异常,注意,异常产生在执指令的那一刻,也就是说中断回来以后不用重新执了,所以了解了吧,LR里面存储的就下一条指令的地址,返回时直接LR->PC就OK了
;还有一个比较特殊的是数据中止异常,返回形式是LR-8->PC,传说是导致数据中止的指令是当前指令的前一条指令,乱七八糟的
;虽然我很想把异常检查时机深入到时序图里面解释,可惜,我还在为这个发愁呢,要复习功课了
;中断返回似乎要我们手动来搞的,郁闷
MACRO
$Label HANDLER $Vector
$Label
sub lr, lr, #4 ;看上面,现在时LR=LR-4,中断返回地址直接放到LR里了,下面要用到哦
stmfd sp!, {r0-r3, lr} ;r0-r3,lr寄存器入栈,这个栈,感觉是客栈的意思,我也搞不懂为什么叫堆栈!对了,为什么要r0-r3啊,这个似乎跟c语言函数调用有关,c语言函数的前4个参数是用r0-r3传递的,第5个参数或以后的就会用栈传递,明白没?约定而已
ldr r0, =$Vector ;$Vector就是map出来的入口拉,嘿嘿
;可能这里少了一句话mov lr, pc
ldr pc, [r0] ;这里是设计上的巧妙了,2级中断跳转,等一下要说说这个,这里似乎有个小问题,现在跳转到子程序指令,那子程序怎样返回啊,残念,可能忘了一句话,应该在前面插入一句mov lr, pc,我是看下面其他的服务程序想到的
ldmfd sp!, {r0-r3,

pc}^ ;出栈,老子要退房了,呵呵,上面是开房啊,但是这里有个小变化哦,lr变成pc了,说白了就是要跳转嘛,中断返回拉
;有个^,是要将异常模式的SPSR复制到CPSR,并且是传回用户模式的寄存器,呵呵,后面那句话我猜是这样的,bootloader运行完以后要进入usr或sys模式,中断一般都是在这两个模式下触发的
;而除了usr和sys模式,其他模式都叫做异常模式,产生异常才进入异常模式嘛,其他时候都是正常模式咯,很明了吧,我是猜的哦,ps:usr和sys模式共用一套物理寄存器
MEND ;不说了

;现在要实现上面的诺言了,解释一下2级中断这个东西,作人要厚道哦
;在sysinit.s最后,我们发现用map定义了很多的标号,并且都是4个字节的,为什么是4个字节啊,为什么不是8个16个啊,呵呵
;4个字节可以用来存储一个跳转地址嘛,所以,猜到没?在这些标号指向的地址A里,存放的又是一个地址B,这就是所谓的指针啊,是不是很像?
;地址B才是真正的服务函数的入口,为什么这样定义啊,我试着尽我最大努力去解释让看众明白吧,虽然真的是不好解释
;这里扯一下我对这个不好解释的看法:其实我认为用不好解释个词,是因为我的水平不够,没办法很好的解释清楚,也让我想到了上课的老师,台上的老师很多都是水平不够,搞的台下的一片糊涂,最后还冒出一句话,这是数学问题,问你们的数学老师去,tmd我最烦这样的话,自己解释不清楚就推到别人身上,垃圾
;扯远了,回来,sysinit.s里map了一大堆的标号,用的是一串连续的每个元素都是4个字节的内存单元,这片内存一般都是指在sdram里的,这样,我们就很方便的修改服务程序的入口
;要知道,修改flash可不是那么容易的事,并且flash速度那个慢,并且,我用的44b0x事没有内存remap功能的,也就是说中断发生时cpu默认先指向flash那里的向量表的
;明白没?这样我们用2级跳转就可以很方便的修改中断服务入口,貌似ads的c语言里有个关键字是修饰中断服务子函数的,那个函数的入口跟这里的第2级跳转地址有关
;还有个小小的问题:这里有个ldr r0,=$Vector指令,看清楚了,这可不是cpu指令,是个编译器的伪指令,cpu指令是没有那个=号的
;上面我解释了map是不占内存空间的,但是如果你用ads观察反汇编的话,会发现map占了一些空间,why,我解释错了吗?当然没有,都说了map出来的是标号,而标号会在编译时期替换为地址值
;要是你再问为什么会替换成地址啊,那我真的是解释不清楚了,问你们的编译原理老师去^_^
;所以,标号是不占实际的内存空间的,那这里又说map占了一些空

间,原来是ldr伪指令的作怪,arm指令是固定长度的32位,所以一条cpu指令根本不可能进行32位寻址(应该说指令里面的偏移不能达到32位),因为指令操作码也占了一些位啊,当然这里还有个8位图立即数的概念
;先不扯那边,既然一条指令不能32位寻址,那么我怎样才能ldr到32位的数呢,ldr伪指令帮我们在附近安排了一段内存存放要操作的32位数,然后变成一条真实的cpu指令去读这个安排好的内存,当然段内存一定要在真实指令的附近,不然又会受到32位寻址的困扰了
;传说中cpu的ldr指令可以寻址4k的地址,这样不就可以间接32位寻址了吗,嘿嘿,真聪明,所以啊,map占的空间是因为ldr伪指令造成的,如果我们把ldr伪指令去掉,反编译就会发现map不占用那部分内存了
;这里看众可能会逻辑混乱,那是我水平的问题啊,真是不好意思
;8位图立即数:符合8位图规则的数都叫8位图立即数,什么叫8位图规则?一个8位的立即数通过左移右移得到的数,这是节省指令位的方法啊,呵呵,绝,不扯了,不然我真的要出书了


UDF_INS_HANDLER ;未定义指令中断入口
stmfd sp!, {r0-r3, lr} ;既然知道是未定义指令,那当然是执行了这条指令以后拉,后面返回时不用重新执行了 LR->PC
ldr r0, =UDF_INS_VECTOR ;这个UDF_INS_VECTOR是map出来的,看看上面
mov lr, pc ;想想,这时pc是什么啊,呵呵,就是下面那句话的地址啊ldmfd sp!, {r0-r3, pc}^,子程序返回用吧
ldr pc, [r0] ;不说了
ldmfd sp!, {r0-r3, pc}^ ;看看上面吧 stmfd sp!, {r0-r3, lr} ;看看上面吧
ldr r0, _SVC_VECTOR ;...
mov lr, pc ;...
ldr pc, [r0] ;...
ldmfd sp!, {r0-r3, pc}^ ;...
INS_ABT_HANDLER ;取指终止异常
sub lr, lr, #4 ;...
stmfd sp!, {r0-r3, lr} ;...
ldr r0, =INS_ABT_VECTOR ;...
mov lr, pc ;...
ldr pc, [r0] ;...
ldmfd sp!, {r0-r3, pc}^ ;...
DAT_ABT_HANDLER ;数据终止异常
sub lr, lr, #4 ;这里应该是sub lr, lr, #8吧,不知道是我错了吗?,具体我也不是很清楚为什么会这样,后面用到的时候再探讨一下吧
stmfd sp!, {r0-r3, lr} ;...
ldr r0, =DAT_ABT_VECTOR ;...
mov lr, pc ;...
ldr pc, [r0] ;...
ldmfd sp!, {r0-r3, pc}^ ;...
IRQ_SVC_HANDLER ;IRQ异常
sub lr, lr, #4 ;...
stmfd sp!, {r0-r12, lr} ;...
mrs r0, spsr ;读出spsr
stmfd sp!, {r0} ;spsr入栈,可能是要保护一些状态吧
ldr r0, =IRQ_SVC_VECTOR ;...
ldr pc, [r0] ;奇怪,这里为什么不要返回了,可能是IRQ_SVC_VECTOR处理程序里有返回
FIQ_SVC_HANDLER ;...
sub lr, lr, #4 ;...
stmfd sp!, {r0-r12, lr} ;...
mrs r0, spsr ;...
stmfd sp!, {r0} ;...
ldr r0, =IRQ_SVC_VECTOR ;...
ldr pc, [r0] ;...


;*******************************************************
SYS_RST_HANDLER ;开机就来到这里了,看看中断向量表的第一句
mrs r0, cpsr ;enter svc mode and disable irq,fiq
bic r0, r0, #ModeMask
orr r0, r0, #(SVC32Mode :OR: IRQ_BIT :OR: FIQ_BIT) ;这种语法,狂汗,不过猜也应该猜到了,里面的OR是预处理指令
msr cpsr_c, r0

IMPORT InitSystem ;引入外部符号InitSystem,在sysinit.s里,下面要执行啊
bl InitSystem ;有连接的跳转,我要返回的啵,里面要设置pll,栈,各bank的时序等,也是很重要的哦,虽然现在也能运行,但是...

;PLL锁相环,用来信号提纯,当然里面还夹了个倍频电路,为什么要这样,这是电子工程师考虑的东西了,高频会对电路产生影响,如引入了高频噪声,
;为了电路稳定,arm内核虽然可以达到66Mhz,但是外围却用低频震荡,一般是用10Mhz,cpu内部有个倍频电路,通过设置寄存器可控,使得10Mhz变成66Mhz,或者其他的频率
;cpu内部的个模块的时钟信号也是可以使能的,可单独关闭某个模块的时钟,比如串口模块,这样,就可以实现低功耗模式,比如掉电模式
;bank,银行这个东西困扰了我好久,最近才看明白,如果要说的话也是一大堆的东西,那现在我只说一些关键点
;44b0x这个cpu开放给我们的只有前256M的内存空间,其他的可能是cpu内部用了,总之,这256M又分为8个部分,每部分32M,称作bank0-band7,每bank又可以单独设置其时序和数据线宽度
;以方便接入不同的外设,这样,256M总共用到了25+3根地址线,即A[0:24],A[25:27],后面那三根作片选,它没有引出来,cpu内部作了一个译码电路,所以A[25:27]变成了nGCS0-nGCS7的片选信号脚
;需要更多的外设的时候可以在外部再接入译码电路,这是我想到的,嘿嘿
;这里说到时序,我又惭愧了,都怪以前没有好好学,这里说的设置时序也就是设置各种操作等待时间长短,比如说建立时间,保持时间之类的,反正我也不懂,不误导大家了,回去好好看书

;下面开始代码数据搬运了,这里要简单介绍一下为什么要搬,唉,十万个为什么看多了,我就是要钻牛角尖,怎么拉,不服?
;接近胜利了,加油...
;上面说了,连接器的工作就是把各段代码堆叠起来,使其成为一个整体。怎么叠,那是连接器的事了,不过一般是这样的 代码段(RO)->已初始化为非0值的全局变量段(RW)->未初始化全局变量段(ZI),按顺序叠,最后变成一个bin文件
;说道全局变量,我又想到了局部变量,局部变量又叫自动变量,这个名字够cool,何谓自动,就是变量在栈中创建,退出函数,因为栈要维持平衡,栈顶指针跳回到函数执行前的位置,变量就自动销毁,够自动吧

;回到上面来,由于最后连接成的bin文件要全部烧写到flash里面,当然用jtag调试的例外,flash当然不能用作变量存储的地方,即使能用我也不用,太他妈的慢了。
;所以连接时我们要设置RO,RW的值,一般把RW设置在RAM的地方,比如0xc000000以后的地方,我的板是这样的啊,不知道你的是怎样的,当然,也能把RO设置在RAM,这样可以加快运行速度嘛
;连接器会根据我们设置的RO,RW值设置各标号的地址值,这就是所谓的运行时地址,但是我们的bin是烧到flash里面的啊,当然全局变量的值也烧进去了,我怎么把真实的代码数据和设置的RO,RW联系起来啊
;呵呵,这就是搬运,从flash搬运到RO,RW指定的位置,RO,RW的值可以当成常量读出来,这当然是连接器给的拉。
;如果不搬,想想会出现什么情况,呵呵:我在C语言里定义的全局变量出错了,初始值不对啊!!!,可能程序跑飞就是因为这个哦
;有个问题是要注意的:cpu是从0x0位置开始执行代码的,bootloader的代码一般是与位置无关的代码,比如说b指令,那是一个相对跳转指令,所以即使我把RO设置在其他地方,代码照样运行,最起码我要保证在搬运完之前的代码都是与地址无关才行啊,想想是不是这样,不要乱

adr r0, ResetEntry ;伪指令拉,取到标号flash.ResetEntry的地址,注意,这里是相对于当前PC的寻址,跟RO无关,从这里开始,应该就是要设置程序的运行环境了,比如复制全局变量到RW域,初始化ZI域,更有甚者把代码段RO搬到RAM里运行
ldr r1, BaseOfROM ;这是RO$$Base
cmp r0, r1 ;判断运行时的入口与RO入口是否相等
ldreq r0, TopOfROM ;相等的话,就RO$$Limit->r0;
beq InitRamData ;相等的话,就转到初始化RW段

ldr r2, =CopyProcBeg ;不相等的话,就要处理一下了,嘿嘿,这里ram.CopyProcBeg似乎跟RO有关的哦,你可以假设一下如果当前程序在flash里运行,但是ro设置在ram里面,会出现什么情况呢
sub r1, r2, r1 ;得到ram.CopyProcBeg到RO$$Base的长度->r1
add r0, r0, r1 ;flash.ResetEntry+r1->r0;这就是实际代码存放的位置的偏移,当然这个偏移是在flash.CopyProcBeg这里,别乱,等下我用图来解释一下
ldr r3, =CopyProcEnd ;ram.CopyProcEnd->r3
0
ldmia r0!, {r4-r7} ;这时,r0指向flash.CopyProcBeg
stmia r2!, {r4-r7} ;r2指向ram.CopyProcBeg
cmp r2, r3 ;把flash.CopyProcBeg-flash.CopyProcEnd这段代码复制到ram.CopyProcBeg-ram.CopyProcEnd
bcc %B0

ldr r3, TopOfROM ;RO$$Limit->r3
ldr pc, =CopyProcBeg ;ram.CopyProcBeg->pc 跳转拉,好像是长跳转哦,超过了32M了,要用ldr

;***********************************************
CopyProcBeg ;这个看名字好像就是叫复制代码段吧,看看就知道了
0
ldmia r0!, {r

4-r11} ;这时r0指向flash.CopyProcEnd,呵呵,应为上面用了“!”,r0会自增的嘛
stmia r2!, {r4-r11} ;这时r2指向ram.CopyProcEnd
cmp r2, r3 ;拷贝代码段,flash.CopyProcEnd-flash.code_end复制到ram.CopyProcEnd-RO$$Limit,不知道这里为什么这样写,先拷贝这段复制程序,再拷贝代码段,一下子复制完不好吗?
bcc %B0 ;好像我想到了,他要在ram里面运行,因为是个循环复制,所以速度会快很多,呵呵,我真聪明
CopyProcEnd

sub r1, r2, r3 ;不知道用意是什么,r2不就是等于r3吗,这样r1=0了,可能是这样吧,用bcc的话,r2不一定就等于r3,因为是用r4-r11复制内存,由于复制的内存大小不一定是11-4+1=8的倍数,所以要调整一下位置
sub r0, r0, r1 ;r0=flash.code_end

InitRamData ;复制RW的内容
ldr r2, BaseOfBSS ;r2=RW$$Base
ldr r3, BaseOfZero ;r3=ZI$$Base,ZI是紧跟着RW的,连接器里没有设置ZI的
0
cmp r2, r3 ;从flash.data循环复制全局变量到ram.data中
ldrcc r1, [r0], #4 ;r1是中转,r0=flash.code_end,好像有点小小的问题,bin的内容好像都是紧靠的,不管RO和RW设置的多分开,在bin里,代码段后面紧接着就是已初始化全局变量段,要验证一下
strcc r1, [r2], #4 ;
bcc %B0 ;

mov r0, #0 ;初始化r0,呵呵,因为ZI段的初始值都是0啊
ldr r3, EndOfBSS ;r3=ZI$$Limit
1
cmp r2, r3 ;
strcc r0, [r2], #4 ;从上面执行下来,r2=ZI$$Base=RW$Limit
bcc %B1 ;将ZI$$Base-ZI$$Limit全部置0

ldr pc, GotoMain ;终于,终于,我要飞升了

;不管怎么样,我还是要用图来解释一下这段搬运过程,不过似乎有点复杂哦,我是说用文本来作图
;总之,上面的搬运程序就是要把flash里面的代码数据复制到RO,RW指定的位置去,一般是搬运到ram里面
|-------------------------|->ram开始结束
|... |
|-------------------------|->ZI$$Limit
|ram.未初始化全局变量段 |
|-------------------------|->RW$$Limit ZI$$Base
|ram.已初始化全局变量段 |
|-------------------------|->RW$$Base
|... |
|-------------------------|->RO$$Limit
|ram.代码段 |
|-------------------------|->0xc000000 ram开始 RO$$Base
|... |
|-------------------------|->flash结束
|... |
|-------------------------|->flash.data结束
|flash.已初始化全局变量段 |
|-------------------------|->代码结束,flash.data开始,flash.code_end
|flash.代码段 |
|-------------------------|->0x0 flash开始 flash.ResetEntry

GotoMain DCD $MainEntry ;

;***********************************************
IMPORT |Image$$RO$$Base| ; ROM code start
IMPORT |Image$$RO$$Limit| ; RAM data starts after ROM program
IMPORT |Image$$RW$$Base| ; Pre-initialised variabl

es
IMPORT |Image$$ZI$$Base| ; uninitialised variables
IMPORT |Image$$ZI$$Limit| ; End of variable RAM space


BaseOfROM DCD |Image$$RO$$Base|
TopOfROM DCD |Image$$RO$$Limit|
BaseOfBSS DCD |Image$$RW$$Base|
BaseOfZero DCD |Image$$ZI$$Base|
EndOfBSS DCD |Image$$ZI$$Limit|

EXPORT GetBaseOfROM
EXPORT GetEndOfROM
EXPORT GetBaseOfBSS
EXPORT GetBaseOfZero
EXPORT GetEndOfBSS

GetBaseOfROM ;一些子函数,没有什么用的,也有可能后面用到
ldr r0, BaseOfROM
mov pc, lr
GetEndOfROM
ldr r0, TopOfROM
mov pc, lr
GetBaseOfBSS
ldr r0, BaseOfBSS
mov pc, lr
GetBaseOfZero
ldr r0, BaseOfZero
mov pc, lr
GetEndOfBSS
ldr r0, EndOfBSS
mov pc, lr

;***********************************************

END

相关文档