ARM启动代码分析-philips的LPC2xxx系列12006-7-24 14:30:00
/**********************************************************************************************
*File: startup.s
*Author: Embest w.h.xie 2005.02.21
*Desc: lpc22xx/lpc212x/lpc211x/lpc210x startup code
*History:
* note modify
:
cui jian jie 2006-4-25
*comment:
**********************************************************************************************/
#
处理器的七种工作方式的常量定义
.EQU Mode_USR, 0x10 #
用户模式
.EQU Mode_FIQ, 0x11 #FIQ
模式
.EQU Mode_IRQ, 0x12 #IRQ
模式
.EQU Mode_SVC, 0x13 #
超级用户模式
.EQU Mode_ABT, 0x17 #
终止模式
.EQU Mode_UND, 0x1B #
未定义模式
.EQU Mode_SYS, 0x1F #
系统模式
#
中断屏蔽位
.EQU I_Bit, 0x80 //IRQ
中断控制位,当被置位时,
IRQ
中断被禁止
.EQU F_Bit, 0x40 //FIQ
中断控制位,当被置位时,
FIQ
中断被禁止
#
状态屏蔽位
.EQU T_bit, 0x20 //T
位,置位时在
Thumb
模式下运行,清零时在
ARM
下运行
#
定义程序入口点
.globl _start
.code 32
.TEXT
_start:
#
中断向量表
Vectors:
LDR PC, Reset_Addr //
把
Reset_Addr
地址处的内容放入
PC
中
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
.long 0xb9205f80 @ keep interrupt vectors sum is 0
LDR PC, [PC, #-0xff0] //
当前
PC
值减去
0xFF0
等于
IRQ
中断入口地址
LDR PC, FIQ_Addr
#
地址表
Reset_Addr: #
该地址标号存放
Reset_Handler
程序段的入口地址
.long Reset_Handler
Undef_Addr: #
该地址标号存放
Undef_Handler
程序段的入口地址
.long Undef_Handler
SWI_Addr: #
该地址标号存放
SWI_Handler
程序段的入口地址
.long SWI_Handler
PAbt_Addr: #
该地址标号存放
PAbt_Handler
程序段的入口地址
.long PAbt_Handler
DAbt_Addr:
.long DAbt_Handler
.long 0
IRQ_Addr: #
地址标号处存放一个无效的数据
.long 0
FIQ_Addr: #
该地址标号存放
FIQ_Handler
程序段的入口地址
.long FIQ_Handler
Undef_Handler:
B Undef_Handler
PAbt_Handler:
B PAbt_Handler
DAbt_Handler:
B DAbt_Handler
#
软中断的中断服务子程序入口地址
SWI_Handler:
STMFD sp!, {r0-r3, r12, lr} //
入栈,现场数据保护
MOV r1, sp //
把堆栈指针
SP
存入
R1
中
MRS r0, spsr //
把
SPSR
值存入
R0
,
SPSR
值为产生软中断时的
CPSR
TST r0, #T_bit //
判断
R0
(
SPSR
)的
T
位是否为
0
#SPSR
的
T
位不为
0
,工作在
Thumb
模式下
LDRNEH r0, [lr,#-2] //SPSR
的
T
位不为
0
,则
[lr-2]-
〉
r0
BICNE r0, r0, #0xFF00 // SPSR
的
T
位不为
0
,清除
r0
的
Bit8~Bit15
位
# SPSR
的
T
位为
0
,工作在
ARM
模式下
LDREQ r0, [lr,#-4] // SPSR
的
T
位为
0
,则
[lr-4] -
〉
r0
BICEQ r0, r0, #0xFF000000 // SPSR
的
T
位为
0
,清除
r0
的
Bit24~Bit131
位
# R0 is interrupt number //R0
是中断号
# R1 is stack point //R1
是堆栈指针
BL SWI_Exception //
进入软中断处理程序
LDMFD sp!, {r0-r3, r12, pc}^ //
出栈,现场数据恢复
#
快速响应中断的中断服务自程序的入口地址
FIQ_Handler:
STMFD SP!, {R0-R3, LR} //
入栈的现场保护
# BL FIQ_Exception //
进入
FIQ
的中断处理程序
LDMFD SP!, {R0-R3, LR} //
出栈,恢复现场
SUBS PC, LR, #4 //
返回到主程序
#
复位后程序处理的入口地址
Reset_Handler:
BL RemapSRAM //
进行存储器映射的操作
#
下面几行代码用来判断当前的工作模式
MRS R0, CPSR //
读
CPSR
到寄存器
R0
AND R0, R0, #0x1F //R0 = R0 AND 0x1F
CMP R0, #Mode_USR //
比较
R0
和
#Mode_USR
,二者相减
//
如果相等则说明当前处在用户模式下,需要通过产生
11
号软中断进入系统模式。因为下面的初始化堆栈
//
需要在不同的工作模式下切换,而在用户模式下不能直接切换,只有系统模式可以,所以要通过产生
11
//
号软中断切换到用户模式。
SWIEQ #11
BL InitStack //
进行堆栈初始化工作
ARM启动代码分析-philips的LPC2xxx系列32006-7-24 14:33:00
#------------------------------------------------------------------------------
#-
初始化
C
变量
#------------------------
#-
下表由连接器自动产生
#- RO:
只读
=
代码区。
#- RW:
可读可写
=
预先初始化的数据
(
初始化的全局变量
)
和预先被清零的数据
(
未初始化的全局变量
)
。
.
#- ZI:
预先被清零的数据区
(
未初始化的全局变量
)
#-
预先被初始化的数据区定位在代码区之后。
#-
预先被清零的数据区定位在预先被初始化的数据区之后。
#-
注意数据区的位置
:
#- I
如果用
ARM SDT,
当链接器选择
no -rw-base
时
,
数据区被映射在代码区之后
#-
你可以把数据区房子内部的
SRAM( -rw-base=0x40 or 0x34)
中
#-
或者放在外部的
SRAM( -rw-base=0x2000000 )
中。
#-
注意:为了提高代码的密度,预先被初始化的数据必须尽可能的少。
#------------------------------------------------------------------------------
#
该部分程序功能:先判断当前是在
RAM
中运行还是在
FLASH
中运行,如果在
FLASH
中运行,先把
FLASH
#
中的预先赋值的
RW
段数据和未赋值的
ZI
段数据都搬移到
RAM
区中,再把
ZI
段数据全部清零;如果程
#
序就是在
RAM
中运行,则直接把
ZI
段数据清零。
.extern Image_RO_Limit /* ROM
区中数据段的起始地址
*/
.extern Image_RW_Base /* RW
段起始地址
*/
.extern Image_ZI_Base /* ZI
段的起始地址
*/
.extern Image_ZI_Limit /* ZI
段的结束地址加
1 */
ldr r0, =Image_RO_Limit /*
取
ROM
区中数据段的首地址
*/
ldr r1, =Image_RW_Base /*
取
RAM
区中
RW
段的目标首地址
*/
ldr r3, =Image_ZI_Base /*
取
RAM
区中
ZI
段的首地址
*/
cmp r0, r1 /*
比较
ROM
区中数据段首地址和
RAM
区中
RW
段目标首地址
*/
beq NoRW /*
相等代表当前是在
RAM
中运行
*/
LoopRw: cmp r1, r3 /*
不相等则和
RAM
区中
ZI
段的目标地址比较
*/
ldrcc r2, [r0], #4 /*
如果
r1<r3
,则把
r0
地址上的数据读出到
r2
中,然后
r0=r0+4*/
strcc r2, [r1], #4 /*
如果
r1<r3
,则把
r2
内数据写入道
r1
地址中,然后
r1=r1+4*/
bcc LoopRw /*
如果
r1<r3
,则跳转到
LoopRw
继续执行
*/
NoRW: ldr r1, =Image_ZI_Limit /*
取
ZI
段的结束地址
*/
mov r2, #0 /*
将
r2
赋
0*/
LoopZI: cmp r3, r1 /*
将
ZI
段清零
*/
strcc r2, [r3], #4 /*
如果
r3<r1
,将
r2
内容写入到
r3
地址单元中,然后
r3=r3+1*/
bcc LoopZI /*
如果
r3<r1(
即
C=0)
,则跳转到
LoopZI */
.extern Main /*
声明外部变量
*/
B Main /*t
跳转到用户的主程序入口
*/
#
为每一种模式建立堆栈,
ARM
堆栈指针向下生长
InitStack:
MOV R1, LR //
把该子程序返回地址保留在
R1
中
LDR R0, =Top_Stack //
取栈定地址到
R0
中
#
进入未定义模式,并禁止
FIQ
中断和
IRQ
中断
MSR CPSR_c, #Mode_UND|I_Bit|F_Bit
#
设置未定义模式下堆栈的栈顶指针
MOV SP, R0
SUB R0, R0, #UND_Stack_Size #
未定义模式下堆栈深度
#
进入终止模式,并禁止禁止
FIQ
中断和
IRQ
中断
MSR CPSR_c, #Mode_ABT|I_Bit|F_Bit
#
紧接着未定义模式下的堆栈,设置终止模式下栈顶指针
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size #
终止模式下堆栈深度
#
进入
FIQ
模式,并禁止
FIQ
中断和
IRQ
中断
MSR CPSR_c, #Mode_FIQ|I_Bit|F_Bit
#
紧接着终止模式下的堆栈,设置下
FIQ
模式下栈顶指针
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size #FIQ
模式下的堆栈深度
#
进入
IRQ
模式,并禁止
FIQ
中断和
IRQ
中断
MSR CPSR_c, #Mode_IRQ|I_Bit|F_Bit
#
紧接着
FIQ
模式下的堆栈,设置
IRQ
模式下的栈顶指针
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size #IRQ
模式下的堆栈深度
#
进入超级用户模式,并禁止
FIQ
中断和
IRQ
中断
MSR CPSR_c, #Mode_SVC|I_Bit|F_Bit
#
紧接着
IRQ
模式下的堆栈,设置超级用户下的栈顶指针
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size #
超级用户下的堆栈深度
#
设置进入用户模式
MSR CPSR_c, #Mode_USR
#
紧接着超级用户模式下的堆栈,设置用户模式下的栈顶指针,剩余的空间都开辟为堆栈
MOV SP, R0
MOV PC, R1 #
堆栈初始化子程序返回
#
重映射
SRAM
区
RemapSRAM:
MOV R0, #0x40000000 //RAM
区首地址
LDR R1, =Vectors //
向量表首地址
#
下面一段程序是把从
0x00000000
开始的
64
个字节(
FLASH
中的中断向量表和地址表)搬移到以
#0x40000000
为首地址的
RAM
区中
LDMIA R1!, {R2-R9} //
把以
[R1]
为首地址的
32
个字节数据装载到
R2-R9
中
STMIA R0!, {R2-R9} //
把
R2-R9
中的数据存入以
[R0]
为首地址的单元中
LDMIA R1!, {R2-R9} //
把以
[R1]
为首地址的
32
个字节数据装载到
R2-R9
中
STMIA R0!, {R2-R9}
把
R2-R9
中的数据存入以
[R0]
为首地址的单元中
#
下面几行代码设置存储器映射控制寄存器
LDR R0, =MEMMAP //
取
MEMMAP
地址到
R0
MOV R1, #0x02
STR R1, [R0] //
给
MEMMAP
赋值为
0x02
,设置中断向量从
RAM
区从新映射
mov pc, lr //
跳转到主程序
#
下面一段程序代码是进入软中断来切换系统的工作模式,当希望从一种模式切换入另一种模式时,可以通
#
过调用下面对应标号的程序段进入软中断。在软中断处理程序中会根据所给定的中断号处理,执行
SWI #num
后软中断号被存入
R0
中。
.globl disable_IRQ
.globl restore_IRQ
.globl ToSys
.globl ToUser
#
禁止
IRQ
disable_IRQ:
STMFD SP!, {LR} //
把
LR
值压入堆栈
swi #0 //
产生
0
号软中断,
0 -
〉
R0
LDMFD SP!, {pc} //
恢复
PC
值,返回
#
恢复
IRQ
restore_IRQ:
STMFD SP!, {LR} //
把
LR
值压入堆栈
swi #1 //
产生
1
号软中断,
1 –
〉
R0
LDMFD SP!, {pc} //
恢复
PC
值,返回
#
进入系统工作模式
ToSys:
STMFD SP!, {LR} //
把
LR
值压入堆栈
swi #11 //
产生
11
号软中断,
11 –
〉
R0
LDMFD SP!, {pc} //
恢复
PC
值,返回
#
进入用户工作模式
ToUser:
STMFD SP!, {LR} //
把
LR
值压入堆栈
swi #12 //
产生
12
号软中断,
11 –
〉
R0
LDMFD SP!, {pc} //
恢复
PC
值,返回