这是U-Boot的1.3.4的启动代码。最近正在抽空移植UBoot,希望最后能够移植到2410上。毕竟应该有个像样的BOOT才行嘛。看到网上很流行这个代码,毕竟LINUX和WINCE也都在使用它,所以我选择它也绝不是凭空头脑发热作出的选择。当拿到它时,发现是一个很全并且很冗余的东西。说它很全,你自己下回来看看就知道了,说它冗余是因为我只用到2410,其他的当然也就没有用了。本篇就直接把分析出的Start.S放出来了,是为了下一步做好准备,也是为了记下LINUX ARM中的ASSEMBLER的一些用法,避免天长日久忘记了不该忘记的就不好了。在阅读过程中,需要注意要辩证的来看本篇内容,因为我不能保证100%都是合理的正确的哟^_^

#include <config.h>
#include <version.h>
#include <status_led.h>

/*
 *************************************************************************
 *
 * Jump vector table as in table 3.1 in [1]
 *
 *************************************************************************
 */

/*
 * 指定入口函数
 */
.globl _start   /*使用.globl来声明全局标志. 目的:声明的_start标号可以被外部使用*/
_start:
    b       start_code              /*start_code子程序(以前叫reset)用于将CPU重置到Supervisor模式下,并禁止IRQ和FIQ中断请求*/
    ldr pc, _undefined_instruction  /*让pc指向_undefined_instruction地址处*/
    ldr pc, _software_interrupt     /*让pc指向_software_interrupt地址处*/
    ldr pc, _prefetch_abort         /*让pc指向_prefetch_abort地址处*/
    ldr pc, _data_abort             /*让pc指向_data_abort地址处*/
    ldr pc, _not_used               /*让pc指向_not_used地址处*/
    ldr pc, _irq                    /*让pc指向_irq地址处*/
    ldr pc, _fiq                    /*让pc指向_fiq地址处*/


_undefined_instruction: .word undefined_instruction /*在当前标号_undefined_instruction所在的地址处放入四字节的数据,这个数据就是undefined_instruction标号的地址.意思就是说在当前_undefined_instruction对应的地址中放的是undefined_instruction的地址*/
_software_interrupt:    .word software_interrupt    /*道理同上*/
_prefetch_abort:        .word prefetch_abort        /*道理同上*/
_data_abort:            .word data_abort            /*道理同上*/
_not_used:              .word not_used              /*道理同上*/
_irq:                   .word irq                   /*道理同上*/
_fiq:                   .word fiq                   /*道理同上*/

    .balignl 16,0xdeadbeef  /*(如果我没有记错的话,ARM中是32位寻址方式,即地址是每32位为一单元,例如PC当前为0x0,pc下一个就是0x04,8位对应一个字节,也对应一个地址(即地址按字节编址的意思),实际上就是一个地址对应一个字节内容. 只是在寻址时是以32位为单位进行的).本句的目的是保证以下语句所对应的存储空间地址是以16个字节来对齐的.如果当前的地址正好是16的倍数的话就什么也不用管了,如果恰好当前地址还差四字节就正好是16的倍数,那么此时就使用0xdeadbeef死牛(外国人真有意思. 0xdeadbeef正好占四个字节)来一起填充进去,以便补齐地址符合16的倍数的要求.  [这里还需要深入地调试验证一下]*/ 


/*
 *************************************************************************
 *
 * Startup Code (called from the ARM reset exception vector)
 *
 * do important init only if we don't start from memory!
 * relocate armboot to ram
 * setup stack
 * jump to second stage
 *
 *************************************************************************
 */
/*将来该标记用来作为代码段的开始处*/
_TEXT_BASE:
    .word   TEXT_BASE   /*在当前标号_TEXT_BASE所在的地址处放入TEXT_BASE标号的地址*/

/*预处理标号 目的:让_armboot_start指向_start标号所在的地址*/
.globl _armboot_start   /*全局声明标志. 声明的该标号_armboot_start可以被外部使用*/
_armboot_start:
    .word _start        /*在当前标号_armboot_start所在的地址处放入_start标号的地址*/

/*
 * These are defined in the board-specific linker script.
 */
/*预处理标号 目的:让_bss_start指向__bss_start标号所在的地址*/
.globl _bss_start       /*全局声明标志. 声明的该标号_bss_starts可以被外部使用*/
_bss_start:
    .word __bss_start   /*在当前标号_bss_start所在的地址处放入__bss_start标号的地址*/

/*预处理标号 目的:让_bss_end指向_end标号所在的地址*/
.globl _bss_end         /*全局声明标志. 声明的该标号_bss_end可以被外部使用*/
_bss_end:
    .word _end          /*在当前标号_bss_end所在的地址处放入_end标号的地址*/

#ifdef CONFIG_USE_IRQ   /*如果定义了CONFIG_USE_IRQ*/
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START  /*预处理标号 目的:让IRQ_STACK_START指向地址0x0badc0de(这个需要根据硬件更改)*/
IRQ_STACK_START:
    .word   0x0badc0de

/* IRQ stack memory (calculated at run-time) */
/*预处理标号 目的:让FIQ_STACK_START指向地址0x0badc0de(这个需要根据硬件更改)*/
.globl FIQ_STACK_START
FIQ_STACK_START:
    .word 0x0badc0de
#endif


/*
 * the actual start code  //真正的启动代码
 */
start_code:
    /*
     * set the cpu to SVC32 mode    //设置CPU的工作模式为SVC32模式.
     */
    mrs r0,cpsr         /*将cpsr寄存器中的值转到r0寄存器中*/
    bic r0,r0,#0x1f /*目的是将r0的bit[4:0]清0,其它位不变,然后再放入r0中. 简记:bic的功能,想将源寄存器(中间的寄存器)中的数的某位清0,就将最右边的数的某位置1即可,然后再送往目的寄存器(最左边的寄存器)就行了*/
    orr r0,r0,#0xd3 /*0xD3=110[1 0011] | CPSR的位定义见2410的手册P48 Figure2-6 |  此处目的:是在r0寄存器上面运行后的数的基础上,将其对应的bit[7,6,4,1,0]位都置1. 看点:bit[4:0]=10011就是让S3C2410工作在Supervisor模式下,bit[7:6]=11是禁止IRQ和FIQ中断请求*/
    msr cpsr,r0     /*将上面处理后的r0中的值再转到CPSR寄存器中*/

    /*bl coloured_LED_init  //目的是完成彩色LED的初始化.我的板没有,也就不用了*/
    /*bl red_LED_on         //目的是完成红色LED的显示.我的板没有,也就不用了*/

//如果定义了CONFIG_AT91RM9200DK,CONFIG_AT91RM9200EK,CONFIG_AT91RM9200DF中的任意一个,就会执行其中的语句.我才不用他们呢 呵呵
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF)
    /*
     * relocate exception table
     */
    ldr r0, =_start         /*将标号_start对应的值放入r0寄存器中*/
    ldr r1, =0x0            /*将立即数0放入r1寄存器中*/
    mov r2, #16             /*将立即数16,即十六进制0x10放入r2寄存器中*/
copyex:
    subs r2, r2, #1          /*将r2中的值减1后,再放入r2里面。 S:决定本条指令是否影响CPSR的条件标志位,这里放上s就是说该指令会影响更新CPSR的条件标志位,如没有s就不更新CPSR的条件标志位了*/
    ldr r3, [r0], #4        /*将r0中值对应的地址中的数据放入r3,然后r0中值+4再给r0.也就是两步:r3<-[r0], r0<-r0+4*/
    str r3, [r1], #4        /*将r3中值存入r1中所对应的地址中. 然后r1中值+4再给r1. 同样两步:r3->[r1], r1<-r1+4  实际上面一句和本句的目的无非就是把r0对应地址中的数据转给r1对应的地址中*/
    bne copyex  /*这里实际上是在测试"subs r2,r2,#1",目的是看r2有没有递减16次,没有就返回copyex处继续执行.因为当r2减1到0后,0-1=-1肯定就影响到了标志位,而bne测试的就是条件标志位^_^*/
#endif

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
/* turn off the watchdog 关闭看门狗*/

# if defined(CONFIG_S3C2400)        /*如果定义了CONFIG_S3C2400,就执行下面的语句*/
#  define pWTCON        0x15300000
#  define INTMSK        0x14400008  /* Interupt-Controller base addresses */
#  define CLKDIVN   0x14800014  /* clock divisor register */
#else                               /*如果定义了CONFIG_S3C2410,也就属于其他情况了,就执行下面的语句*/
#  define pWTCON        0x53000000  /*"WATCHDOG TIMER CONTROL REGISTER看门狗定时器控制寄存器"的地址0x53000000 见S3C2410手册P408*/
#  define INTMSK        0x4A000008  /*"INTERRUPT MASK REGISTER中断屏蔽寄存器"的地址:0x4A000008 见S3C2410手册P333*/
#  define INTSUBMSK 0x4A00001C      /*针对INTMAK具体化的一个中断请求屏蔽寄存器,其地址0x4A00001C 见S3C2410手册P340*/
#  define CLKDIVN   0x4C000014      /*CPU时钟分频控制寄存器,地址0x4C000014 见S3C2410手册P213*/
# endif

    ldr     r0, =pWTCON             /*将看门狗寄存器的地址转给r0*/
    mov     r1, #0x0                /*将0x0放入r1中*/
    str     r1, [r0]                /*将r1寄存器中的值存入r0所表示的地址中,此时r0中的值意思是个地址 实际上pWTCON的bit[5]为0,就已经关闭看门狗了^_^*/

    /*
     * mask all IRQs by setting all bits in the INTMR - default
     */
    mov r1, #0xffffffff             /*将立即数0xFFFFFFFF放入r1中*/
    ldr r0, =INTMSK                 /*将INTMSK所代表的值放入r0中 实际上这句就等于"ldr r0, #0x4A000008"*/
    str r1, [r0]                    /*将r1寄存器中的值存入r0所表示的地址中,就是把0xFFFFFFFF存入INTMSK寄存器中关闭所有中断*/
# if defined(CONFIG_S3C2410)        /*如果定义的是CONFIG_S3C2410,就执行此if中的句子*/
    ldr r1, =0x3ff                  /*将立即数0x3FF放入r1中*/
    ldr r0, =INTSUBMSK              /*将INTSUBMSK所代表的值放入r0中 实际上这句就等于"ldr r0, =0x4A00001C"或"ldr r0,#4A00001C*/
    str r1, [r0]                    /*将r1寄存器中的值存入r0所表示的地址中,就是把0x3FF存入INTSUBMSK寄存器中,以便屏蔽INTSUBMSK的bit[9:0]对应的中断请求. 问题是为什么没有屏蔽bit[10]对应的那个INT_ADC对应的中断请求,估计是认为不可能发生吧 呵呵*/
# endif

    /* FCLK:HCLK:PCLK = 1:2:4 */ 
    /* default FCLK is 120 MHz ! 缺省的系统主频是120MHz*/
    ldr r0, =CLKDIVN    /*将CLKDIVN所代表的值放入r0中 实际上这句就等于"ldr r0, =0x4C000014" 像这种用法实际上就是为将来r0中的值作为地址来使用做好铺垫*/
    mov r1, #3          /*将立即数0x03放入r1中*/
    str r1, [r0]        /*将r1中的值放入r0所表示的地址中,就是把0x03存入CLKDIVN寄存器中,以便让bit[1:0]=11,最终实现FCLK:HCLK:PCLK = 1:2:4的比例^_^*/
#endif  /* CONFIG_S3C2400 || CONFIG_S3C2410 */

    /*
     * we do sys-critical inits only at reboot,
     * not when booting from ram!
     * 我们仅仅在重启时对系统做临界初始化,而不在RAM运行中完成.
     */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT   /*如果定义了CONFIG_SKIP_LOWLEVEL_INIT,就跳到cpu_init_crit去执行其对应的子程序*/
    bl  cpu_init_crit
#endif


#ifndef CONFIG_AT91RM9200   /*如果没有定义CONFIG_AT91RM9200,就编译下面的内容*/
#ifndef CONFIG_SKIP_RELOCATE_UBOOT      /*如果没有定义了CONFIG_SKIP_RELOCATE_UBOOT,就编译下面的内容*/
relocate:               /* relocate U-Boot to RAM   重新部署U-Boot到内存RAM中   */
/*注意这里的r0中的值要保留住,后面搬运要用.作为起始地址_start*/
    adr r0, _start      /* 将_start对应的地址放入r0中,这里没必要使用ldr,因为_start标号就在眼前,呵呵 */
    ldr r1, _TEXT_BASE      /*将标号_TEXT_BASE所对应的地址放入r1中*/
    cmp     r0, r1                  /*让r0中的值减去r1中的值,从而影响CPSR中对应的条件标志位. 如果r0的值减去r1的值为0,那么就使CPSR中的bit[30]对应的Z位置1,因为Z=1表示运算的结果为零,Z=0表示运算的结果不为零. 见电子书 */
    beq     stack_setup /* 判断当前CPSR中的条件标志位,如果Z=1,就跳转到stack_setup标号处去执行. 实际上这里就是说如果r0和r1中的值相等的话,就跳转到stack_setup标号处去执行. */

    ldr r2, _armboot_start  /* 将_armboot_start标号对应的地址放入r2中 */
    ldr r3, _bss_start  /* 将_bss_start标号对应的地址放入r3中 */
    sub r2, r3, r2      /* 将r3中的值-r2中的值,然后将差值放入r2中 */
    add r2, r0, r2      /* 将r0中的值+r2中的值,然后将和放入r2中. 实际上就是为了得到: [_start标号所在的地址+(_bss_start标号所在的地址-_armboot_start标号所在的地址)]. 这里就是找到代码的结束地址放入r2中了         */

/*简记:将r0所表示的地址处的代码搬运到r1所表示的地址处. r0中的值就是代码的开始地址,r2中的值就是代码的结束地址,r3就是代码被搬运的目标地址*/
copy_loop:
    ldmia   r0!, {r3-r10}       /* 将r0对应地址中的值先放入r3中,然后将r0中地址递增4字节后放入r0中再将对应地址中的数据给r4,以此类推直到放入r10中.共递减了七次    */
    stmia   r1!, {r3-r10}       /* 将r3中的数据存入r1对应的地址中,然后r1对应的地址递增4字节后放入r0中(假设为a)处,再将r4中的数据存入地址a处,依次类推直到r10中的数据也放入相应的地址处.    关于IA的含义事后递增,参见*/
    cmp r0, r2      /* until source end addreee [r2] | 这里直接简记成:比较r0中值和r2中的值.    */
    ble copy_loop   /* 这里直接简记成:如果上句r0中的值<=r2中的值的话,就跳转到copy_loop继续搬运代码. le的条件助记符的含义参见    */
#endif  /* CONFIG_SKIP_RELOCATE_UBOOT */
#endif


/* Set up the stack | 设置堆栈    */
stack_setup:
    ldr r0, _TEXT_BASE      /*将_TEXT_BASE标号所在地址放入r0中 */
    sub r0, r0, #CFG_MALLOC_LEN /*将r0中值减去CFG_MALLOC_LEN代表的立即数后放入r0中.这个常量的具体定义在文件board/fads.h */
    sub r0, r0, #CFG_GBL_DATA_SIZE /*再将r0中值减去CFG_GBL_DATA_SIZE代表的立即数后放入r0中*/
#ifdef CONFIG_USE_IRQ/*如果定义了CONFIG_USE_IRQ,就再将r0中值减去CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ代表的立即数后放入r0中*/
    sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
    sub sp, r0, #12     /*继续将r0中的值减去12后放入sp里面.即保留3个字(12个字节的长度. 因为这里说的一个字是32位,占4个字节)  */

clear_bss:/*clear_bss标号和clbss_1标号的功能:完成BSS段的清零*/
    ldr r0, _bss_start      /* 将_bss_start标号所对应的位置放入r0中 */
    ldr r1, _bss_end        /* 将_bss_end标号所对应的位置放入r1中 */
    mov r2, #0x00000000     /* 将立即数0x0放入r2中 */

clbss_l:
    str r2, [r0]        /*再将r2中的数转到r0所表示的地址处 */
    add r0, r0, #4      /* 将r0中的值+4后,再放入r0. 实际上就是让r0中表示的地址下移四个字节*/
    cmp r0, r1          /* 简记:比较r0的值和r1的值 */
    ble clbss_l         /* 简记:如果上句r0的值<=r1的值,就继续跳到clbss_1去完成对BSS段清零的功能*/

    ldr pc, _start_armboot  /* 让pc指向_start_armboot继续执行*/

_start_armboot: .word start_armboot/*在当前标号_start_armboot所在的地址处放入四字节的数据,这个数据就是start_armboot标号的地址.意思就是说在当前_start_armboot对应的地址中放的是start_armboot的地址. 将汇编中的_start_armboot标号映射到C语言中start_armboot()函数.该函数位于lib_arm目录下的文件board.c中*/


/*
 *************************************************************************
 *
 * CPU_init_critical registers
 *
 * setup important registers
 * setup memory timing
 *
 *************************************************************************
 */

/*
 *这里的话,一句两句说不清楚,怕把你和我都搞晕了.还是不说的啦,只要知道下面一句的功能就行了.  
 *功能:设置CP15寄存器 这里完成的功能:失效Icache和Dcache,禁能MMU和cache  
 */ 
#ifndef CONFIG_SKIP_LOWLEVEL_INIT   /*如果没有定义CONFIG_SKIP_LOWLEVEL_INIT,就执行里面的语句. 之所以我还废话,只是可以方便你可以随时随地断章的来阅读该部分,凡事有始有终嘛*/  
cpu_init_crit:  
    /*  
     * flush v4 I/D caches  | 失效指令cache和数据cache  
     */ 
    mov r0, #0  
    mcr p15, 0, r0, c7, c7, 0   /* flush v3/v4 cache */ 
    mcr p15, 0, r0, c8, c7, 0   /* flush v4 TLB */ 

    /*  
     * disable MMU stuff and caches |   禁能MMU和cache  
     */ 
    mrc p15, 0, r0, c1, c0, 0  
    bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)  
    bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)  
    orr r0, r0, #0x00000002 @ set bit 2 (A) Align  
    orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache  
    mcr p15, 0, r0, c1, c0, 0  

    /*  
     * before relocating, we have to setup RAM timing  
     * because memory timing is board-dependend, you will  
     * find a lowlevel_init.S in your board directory.  
     */ 
    mov ip, lr  
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK) || defined(CONFIG_AT91RM9200DF)  

#else  
    bl  lowlevel_init  
#endif  
    mov lr, ip  
    mov pc, lr  
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */  

/*  
 *************************************************************************  
 *  
 * Interrupt handling | 中断处理  
 *  
 *************************************************************************  
 */ 

@  
@ IRQ stack frame.  
@  
#define S_FRAME_SIZE    72  

#define S_OLD_R0    68  
#define S_PSR       64  
#define S_PC        60  
#define S_LR        56  
#define S_SP        52  

#define S_IP        48  
#define S_FP        44  
#define S_R10       40  
#define S_R9        36  
#define S_R8        32  
#define S_R7        28  
#define S_R6        24  
#define S_R5        20  
#define S_R4        16  
#define S_R3        12  
#define S_R2        8  
#define S_R1        4  
#define S_R0        0  

#define MODE_SVC 0x13  
#define I_BIT    0x80  

/*  
 * use bad_save_user_regs for abort/prefetch/undef/swi ...  
 * use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling  
 */ 
/*宏定义的开始*/   
    .macro  bad_save_user_regs  /* 将sp指向地址-72,然后再给sp. 也就是就空出72个字节. 72=4*13 */ 
    sub sp, sp, #S_FRAME_SIZE  
    stmia   sp, {r0 - r12}      /* 将r0~r12共13个寄存器的数据都转到sp上句空出的空间里面.具体过程是:先将r0中断数据转到sp指向的地址处,然后将sp调整到sp+4位置处,再将r1中的数据转过去,以此类推... */ 
    ldr r2, _armboot_start      /* 将_arboot_start标号对应的地址放入r2中 */ 
    sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)  /* 将r2中的值-CONFIG_STACKSIZE+CFG_MALLOC_LEN,再放入r2*/ 
    sub r2, r2, #(CFG_GBL_DATA_SIZE+8)   /* 再将r2中的值-CFG_GBL_DATA_SIZE+8后放入r2中 */ 
    ldmia   r2, {r2 - r3}       /* 将r2中的值(表示的地址)对应的开始的2个地址数据转给r2,r3中 */ 
    add r0, sp, #S_FRAME_SIZE       /* 将sp+(S_FRAME_SIZE的值)放入r0中 */ 

    add r5, sp, #S_SP           /* 将sp+(S_SP的值)放入r5中 */ 
    mov r1, lr                  /* 将lr所代表的值放入r1中 */ 
    stmia   r5, {r0 - r3}       /* 将r0~r3中的数据放入r5所表示的地址处 */ 
    mov r0, sp                  /* 将sp中的值放入r0中 */ 
    .endm  

    .macro  irq_save_user_regs  
    sub sp, sp, #S_FRAME_SIZE   /* 将sp指向地址-72,然后再给sp. 也就是就空出72个字节. 72=4*13 */ 
    stmia   sp, {r0 - r12}      /* 将r0~r12共13个寄存器的数据都转到sp上句空出的空间里面.具体过程是:先将r0中断数据转到sp指向的地址处,然后将sp调整到sp+4位置处,再将r1中的数据转过去,以此类推... */ 
    add     r7, sp, #S_PC       /* 将sp+S_PC的值放入r7中 */ 
    stmdb   r7, {sp, lr}^       /*DB:表示先减1,再传送.结合本句就是:将让r7中的值减1(因为r8所对应的地址将在下一句用来存放lr的数据^_^),再把sp放入此时r7所表示的地址处,然后r7中的值再减1,再把lr的值放入此时r7表示的地址处. 符号^的意思:因为当前寄存器中没有涉及R15,在STM指令中它的意思就是告诉CPU,我们操作的是用户模式<见 ARM体系结构与编程 P25>下的寄存器r7. 简记:将sp和lr中的值保护起来*/ 
    str     lr, [r7, #0]        /* 将lr中的数据存入r7所表示的地址中. 实际上是r7中的值+0,还是r7中的数 */ 
    mrs     r6, spsr            /* 将spsr中数据放入r6中 */ 
    str     r6, [r7, #4]        /* 将r6中的数据存入(r7中的值+4)所表示的地址中 注意:这里可没有改变r7中原有的值哟*/ 
    str     r0, [r7, #8]        /* 将r0中的数据存入(r7中的值+8)所表示的地址中 */ 
    mov r0, sp                  /* 将sp中的值放入r0中 */ 
    .endm  

    .macro  irq_restore_user_regs   /* 恢复 */ 
    ldmia   sp, {r0 - lr}^          /* 将sp所对应的堆栈的数据送到r0~r14个寄存器中,因为lr就是r14. IA:表示先传送,再递增地址(4字节递增 32位嘛) ^:这里不涉及R15,所以只是想告诉CPU使用的是用户模式下的寄存器r0~r14*/ 
    mov r0, r0                      /* 这句干嘛?  */ 
    ldr lr, [sp, #S_PC]             /* 将sp的值(就是堆栈里面的地址)+S_PC后的新地址(当然还是堆栈里面)的数据放入lr中*/ 
    add sp, sp, #S_FRAME_SIZE       /* 将sp的值+S_FRAME_SIZE后,再放入sp里面 */ 
    subs    pc, lr, #4              /*将lr中的值-4后,放入pc里面。 S:决定本条指令是否影响CPSR的条件标志位,这里放上s就是说该指令会影响更新CPSR的条件标志位,如没有s就不更新CPSR的条件标志位了*/ 
    .endm  

    .macro get_bad_stack              
    ldr r13, _armboot_start     /* 将标号_armboot_start的对应的地址放入r13中 */ 
    sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN) /* 将r13中的值-(CONFIG_STACKSIZE+CFG_MALLOC_LEN)后,再放入r13中 */ 
    sub r13, r13, #(CFG_GBL_DATA_SIZE+8) /* 再将r13中的值-(CFG_GBL_DATA_SIZE+8)后,再放入r13中 */ 

    str lr, [r13]           /* 将lr中的数据存入此时r13中所表示的地址中 */ 
    mrs lr, spsr            /* 将spsr中的数据放入lr中 */ 
    str     lr, [r13, #4]   /* 再将lr中的数据放入(r13中值+4)所表示的地址中 */ 

    mov r13, #MODE_SVC          /* 将立即数MODE_SVC放入r13中 */ 
    @ msr   spsr_c, r13         /* 本行注释掉了 */ 
    msr spsr, r13               /* 将r13中的数据放入spsr中 */ 
    mov lr, pc                  /* 将pc中的值放入lr中 */ 
    movs    pc, lr              /* 将lr中的值放入pc中 s:决定指令的操作将会影响CPSR中条件标志位的值. 不明白这里仅仅是传送没有运算,怎么会影响CPSR中的条件标志位的值呢? */ 
    .endm  

    .macro get_irq_stack        /*设置IRQ*/ 
    ldr sp, IRQ_STACK_START     /* 将IRQ_STACK_START标号对应的地址放入sp中 */ 
    .endm  

    .macro get_fiq_stack        /*设置FIQ*/ 
    ldr sp, FIQ_STACK_START     /* 将FIQ_STACK_START标号对应的地址放入sp中 */ 
    .endm  

/*  
 * exception handlers  
 */ 
    .align  5   /* 下面的一句使用2的5次方=32位对齐,即四字节对齐. 也就是保证每句占用四字节的空间 */ 
undefined_instruction:  /* 未定义指令 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_undefined_instruction    /* 跳转到C语言中的函数do_undefined_instruction() 位于本目录下的interrupts.c*/ 

    .align  5  
software_interrupt:     /* 软件中断 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_software_interrupt       /* 跳转到C语言中的函数do_software_interrupt() 位于本目录下的interrupts.c*/ 

    .align  5  
prefetch_abort:         /* 预取指令 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_prefetch_abort           /* 跳转到C语言中的函数do_prefetch_abort() 位于本目录下的interrupts.c*/ 

    .align  5  
data_abort:             /* 数据 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_data_abort   /* 跳转到C语言中的函数do_data_abort() 位于本目录下的interrupts.c*/ 

    .align  5  
not_used:               /* 保留 */ 
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_not_used     /* 跳转到C语言中的函数do_not_used(). 位于本目录下的interrupts.c*/ 

#ifdef CONFIG_USE_IRQ   /* 如果定义了CONFIG_USE_IRQ 也就是说你想使用IRQ中断 */  

    .align  5  
irq:                    /* IRQ */ 
    get_irq_stack       /* 调用宏get_irq_stack */ 
    irq_save_user_regs  /* 调用宏irq_save_user_regs */ 
    bl  do_irq          /* 跳转到C语言中的函数do_irq(). 位于本目录下的interrupts.c*/ 
    irq_restore_user_regs   /* 调用宏irq_restore_user_regs */ 

    .align  5  
fiq:                    /* FIQ*/ 
    get_fiq_stack       /* 调用宏get_fiq_stack */ 
    /* someone ought to write a more efficiency fiq_save_user_regs 意思:应该有人写一个更有效的fiq_save_user_regs. 因为目前对于IRQ和FIQ都使用的同一个irq_save_user_regs*/ 
    irq_save_user_regs  /* 调用宏irq_save_user_regs */ 
    bl  do_fiq          /* 跳转到C语言中的函数do_fiq(). 位于本目录下的interrupts.c*/ 
    irq_restore_user_regs   /* 调用宏irq_restore_user_regs */ 

#else  

    .align  5  
irq:  
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_irq          /* 跳转到C语言中的函数do_irq(). 位于本目录下的interrupts.c*/ 

    .align  5  
fiq:  
    get_bad_stack       /* 调用宏get_bad_stack */ 
    bad_save_user_regs  /* 调用宏bad_save_user_regs */ 
    bl  do_fiq          /* 跳转到C语言中的函数do_fiq(). 位于本目录下的interrupts.c*/ 

#endif  

来源:http://www.suseng.cn/Blog/Eastar/Article/178.aspx