ARM启动代码分析-philips的LPC2xxx系列

 

ARM启动代码分析-philips的LPC2xxx系列

/**********************************************************************************************

*File:         startup.s

*Author:    Embest w.h.xie 2005.02.21

*Desc:       lpc22xx\lpc212x\lpc211x\lpc210xstartup 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          @ keepinterrupt 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                    //进行堆栈初始化工作

 

#------------------------------------------------------------------------------

#- 初始化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值,返回

 

# 软中断处理代码

 

SWI_Exception:

               STMFD   SP!, {R2-R3,LR}               //把R2,R3,LR值入栈

#0号软中断的处理程序

CMP     R0, #0                             //将R0和0比较

               //以下4行带EQ条件的代码均为当R0为0时应该执行的语句

               MRSEQ   R2, SPSR                       //把SPSR读入到R2中

               STREQ   R2, [R1]                  /把R2的值存入到[R1]中

               ORREQ   R2, R2, #0x80               //把R2的Bit7位置1

               MSREQ   SPSR_c, R2                    //把R2的值写入到SPSR_c中,即禁止IRQ

               #1号软中断的处理程序

               CMP    R0,  #1                               //比较R0值和1

               LDREQ   R2, [R1]                  //相等则把[R1]中的数据存入R2中

               MSREQ   SPSR_c, R2                  //相等把R2的值写入到SPSR_c中,恢复IRQ

              

                          #11号软中断的处理程序

               CMP     R0, #11                    //比较R0的值和11

               MRSEQ   R2, SPSR                       //相等则把SPSR的值转存入到R2中

               BICEQ   R2, R2, #0x1F                 //相等则把R2的Bit0~Bit4全部清零

               ORREQ   R2, R2, #Mode_SYS     //相等则把R2与#Mode_SYS相与再存入R2

               MSREQ   SPSR_c, R2            //相等则把R2的值存入SPSR_c中,即进入系统模式

               #12号软中断的处理程序

               CMP     R0, #12                    //比较R0的值和12

               MRSEQ   R2, SPSR                       //相等则把SPSR的值存入R2

               BICEQ   R2, R2, #0x1F                 //相等则把R2的Bit0~Bit4清零

               ORREQ   R2, R2, #Mode_USR   //相等则把R2与#Mode_USR相与再存入R2中

               MSREQ   SPSR_c, R2                  //相等则把R2存入SPSR_c,即进入用户模式

                             

LDMFD   SP!, {R2-R3,PC}             //恢复R2、R3、PC值,返回

 

 

   .END                                                                         //汇编代码段结束

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值