LPC2xxx启动代码分析

 

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 值,返回
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值