s3c2440的Bootloader启动第一阶段代码
1、包含头文件
GET option.inc
GET memcfg.inc
GET 2440addr.inc
/*
注释:
汇编程序指令不能顶格写
*.inc代表头文件,汇编语言专用
GET 伪指令用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。可以使用 INCLUDE代替 GET。
汇编程序中常用的方法是在某源文件中定义一些宏指令,用 EQU 定义常量的符号名称,用 MAP 和FIELD 定义结构化的数据类型,然后用 GET 伪指令将这个源文件包含到其他的源文件中。GET 伪指令只能用于包含源文件,包含目标文件需要使用 INCBIN 伪指令
*/
2、设置SDRAM自刷新
BIT_SELFREFRESH EQU (1<<22)
/*这句话的意思是:“BIT_SELFREFRESH”就是“将刷新控制寄存器的第22位置1”的意思。SDRAM:同步动态随机存储器,相当于c里面 #define BIT_SELFREFRESH (1<<22);就是第22位置1的意思。
动态存储器(Dynamic RAM)都存在刷新问题。这里主要采用自动刷新方式。每隔一段时间向SDRAM发一条刷新命令。
EQU伪指令的作用类似于C语言中的#define.用于为一个常量定义名称
*/
/*
(1)ARM 有7 种模式,用户模式,快速中断模式,中断模式,管理模式,中止模式,未定义模式和系统模式(咱们这里只用了6种);
(2)系统堆栈的初始化主要是给各个处理器模式分配堆栈空间。堆栈是为中断或程序跳转服务的,当发生中断或程序跳转时,需要将当前处理器的状态及一些参数保持在堆栈中,当中断处理完毕以后或程序执行完后返回时,再将堆栈保存的现场数据进行恢复,以保证原来的程序正确运行
*/
/*对CPSR的后8位进行设置*/
;Pre-defined constants //预定义6中工作模式
USERMODE EQU 0x10 //用户模式 00010000
FIQMODE EQU 0x11 //快速中断模式 00010001
IRQMODE EQU 0x12 //中断模式 00010010
SVCMODE EQU 0x13 //监管模式 00010011
ABORTMODE EQU 0x17 //异常中断模式 00010111
UNDEFMODE EQU 0x1b //未定义模式 000011011
MODEMASK EQU 0x1f // 模式掩码,多出来的 000011111
NOINT EQU 0xc0 //取消中断 11000000
;The location of stacks //设置6中工作模式的堆栈的起始地址
/*定义各种模式下使用的堆栈起始地址,_STACK_BASEADDRESS是由option.inc定义的,在option.inc中定义了_STACK_BASEADDRESS EQU 0x33ff8000*/
//堆栈是为中断或程序跳转服务的,当发生中断或程序跳转时,需要将当前处理器的状态及一些参数保持在堆栈中,当中断处理完毕以后或程序执行完后返回时,再将堆栈保存的现场数据进行恢复,以保证原来的程序正确运行
//堆栈初始化及ARM工作模式切换
UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~
SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~
UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~
AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~
IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~
FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~
/*下面一段要遇到的问题提前注释如下:
1、tasm.exe是什么程序,有什么作用???
就是THUMB汇编编译文件
2、CONFIG是在哪里定义了???
CONFIG为汇编器的内置变量,内置变量有很多
如果要汇编成ARM指令则值为32,如果要汇编成THUMB指令则值为16。
3、[、]、| 的用法怎样???
"["表示"if","|"表示"else","]"表示"endif"
ARM板上电时处于ARM状态,故无论指令为ARM集还是THUMB集,都先强制成ARM集,待init.s初始化完成后,再转换;下面这一段是为了统一目前的处理工作状态和软件编译方式。
*/
//检查在tasm.exe里是否设置了采用THUMB(16位)代码,判断是不是thumb指令
;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.
GBLL THUMBCODE
/*定义THUMBCODE全局变量(逻辑型),注意,其中,GBLL为伪指令,定义全局逻辑型变量*/
[ {CONFIG} = 16
//如果发现是用16位代码的话,表示目前使用的是tasm.exe编译,thumb 编译方式
THUMBCODE SETL {TRUE}
//把THUMBCODE设置为TURE,SETL也为伪指令,用于给一个逻辑变量赋值
CODE32 //CODE32;指示汇编编译器后面的指令为32位的ARM指令,
伪指令本身并不进行程序状态的切换.要进行状态切换,可以使用BX指令操作. (暂且把处理器设置成为ARM模式,以方便初始化)
| //否则是ARM模式
THUMBCODE SETL {FALSE}
]
/*
这段代码的意思:说白了整个预编译段就是为了说明要ARM的当前状态和编译器的状态统一而已。这里他只是给了个标准的例子,实际大多数情况不需要啦。但是上面的config==16只说明你编译器当前使用的是TASM.EXE,但现在由于 Thumb-capable ARM processors start in ARM state(注意是MCU的状态和编译器无关),所以要使两者一致编译出来的代码才能被运行,使用CODE32 使编译器也处于ARMASM.EXE状态,CODE16/32只会影响编译器,只有BX会改变processors state,只要两者一致就好。这段代码根据是否使用了TASM.EXE设置了全局变量THUMBCODE 的值,但是由于ARM PROCESSOR处于ARM state,所以后面都要用CODE32标识指令。
*/
// 宏定义MOV_PC_LR,作用:子程序返回
// THUMB模式和ARM模式的子程序返回的方法是不同的,所以这里判断是不//是THUMB模式。
MACRO //宏定义
MOV_PC_LR //宏名
[ THUMBCODE //判断THUMBCODE是否为真;
bx lr
//若lr最低位为1,则转入THUMB指令;若lr最低位为0,则转入ARM指令。//参考资料“常用汇编指令及汇编P27”
/*如果目标地址是THUMB指令,在ARM模式中,要用BX指令转THUMB,是跳到THUMB指令,并转换模式。lr为链接寄存器,也可以使用其他寄存器,例如RM*/
|
mov pc,lr
/*否则,就是目标地址是ARM模式,就直接把函数返回地址赋给PC,PC为程序计数器,作用是存放下一条要执行的指令的首地址;*/
]
MEND //宏定义结束
//宏定义MOVEQ_PC_LR,作用:带相等条件判断的子程序返回。与宏定义MOV_PC_LR类似
/*注意:这里多出来的“EQ”是指CPSR程序状态寄存器的Z是否等于1,如果等于1则执行bx或mov等指令,作用于上个宏类似。*/
MACRO //宏定义
MOVEQ_PC_LR //宏名
[ THUMBCODE
bxeq lr
|
moveq pc,lr
]
MEND
//宏定义结束
//下面就是开始建立中断向量表的过程
下面这个宏是用于第一次查表过程的实现中断向量的重定向,你会发现在_ISR_STARTADDRESS=0x33FF_FF00里定义的第一级中断向量表是采用型如Handle***的方式的。而在程序的ENTRY处(程序开始处)采用的是b Handler***的方式.在这里Handler***就是通过HANDLER这个宏和Handle***进立联系的. 这种方式的优点就是真正定义的向量数据在内存空间里,而不是在ENTRY处的ROM(FLASH)空间里, 这样,我们就可以在程序里灵活的改动向量的数据了.其中HANDLER是一个宏,用于查找中断处理程序的入口地址.这些地址存放在由HandleXXX指向的表项中,该表定位在RAM高端,基地址为_ISR_STARTADDRESS.假如_ISR_STARTADDRESS为 0x800000000,当IRQ中断时,根据b HandlerFIQ,先跳转,再根据^ _ISR_STARTADDRESS基地址+HandleIRQ 的偏移地址(4*6)得到的中断地址 ;0x80000000+0x00000024=0x80000024
这个宏是用于第一次查表过程的实现中断向量的重定向。那么这样做有什么优点呢?
这种方式的优点就是真正定义的向量数据放到内存空间RAM里,而不是在ENTRY(地址0x0)处的ROM(FLASH)空间里, 这样,我们就可以在程序里灵活的改动中断向量的数据了(因为ROM是只读的,而RAM为可读可写的)。
在这里HANDLER是一个宏,就负责查找中断处理程序的入口地址。这些中断入口地址存放在由HandleXXX指向的表项中,该表定位在RAM高端,它的基地址为_ISR_STARTADDRESS。
例如:_ISR_STARTADDRESS为0x800000000,当IRQ中断时,根据b HandlerFIQ,先跳转再根据_ISR_STARTADDRESS基地址+HandleIRQ的偏移地址(4*6)得到的中断地址0x80000000+0x00000024=0x80000024
/*大致的作用就是把宏的第一个参数$HandlerLabel 转换成一个标号,然后让程序跳转到第二个参数$HandleLabel (为一个地址)对应的值的地址去*/
//这样做的目的是为了后面多种中断不需要每一个都写这么多代码
//该代码主要实现跳转功能,把异常处理程序地址HandleXXX送到PC中。例如//产生IRQ中断时,PC会被强制设置为0x18,执行指令:b HandlerIRQ
MACRO
$HandlerLabel HANDLER $HandleLabel
/*注解:$HandlerLabel 为标号,字符串型;HANDLER 为宏名;$HandleLabel 为参数,指针型。*/
/*在ARM体系中,每个指针变量占4个字节,这就解释了这里为什么每个标号分配了四个字节的空间,里面放的就是函数指针*/
$HandlerLabel //定义标号
sub sp,sp,#4 ; //把栈顶指针减4,留出一个字的空间(用于保存跳转地址的值)sp=sp-4
stmfd sp!,{r0}
//首先把sp减4,然后把将要使用的r0寄存器入栈 ,堆栈寻址方式,要加{}。
ldr r0, =$HandleLabel
//给寄存器r0赋值,将HandleXXX的址址放入r0,加载指定地址上的数据(字),放入Rd中
ldr r0, [r0]
//给寄存器r0赋值
//把HandleXXX所指向的内容(也就是中断程序的入口)放入r0
//加[]表示r0寄存器里的地址所对应的值,不加[]表示r0寄存器里的地址
str r0,[sp,#4]
//;store the contents(ISR) of HandleXXX to stack把中断服