arm启动文件2440init.s分析

2019-07-13 08:04发布

 最近整理电脑,找到了一些当初学习嵌入式linux时的资料,现在共享出来。方便大家学习之用,无所谓原创,无非就是在前人的基础上,进行了系统化的分析和综合而已。不过,还是加入了不少个人学习的思路跟方法,我觉得这才是最重要的。本来想共享到百度文库,赚点积分的,无奈百度嫌我格式太乱不允许上传,就直接复制到这里了,呵呵!
最近在学习嵌入式软件,现分享自己部分成果。平台:s3c2440 mcu ;========================================= ; NAME: 2440INIT.S ; DESC: C start up codes ;      Configure memory, ISR ,stacks ;Initialize C-variables ;=========================================       ;注意:axd调试时,可以看到 指令pc地址从0x30000000开始,这是因为ram的起始地址是0x30000000. ;并且如果从nand启动,则处理器自动把nand首部的4k字节,复制到ram中,然后pc跳到0x30000000,开始执行。   ;此源文件通常包含一些宏定义和常量定义   ;通用的《启动流程图》: ;入口->屏蔽所有中断,禁止看门狗->根据工作频率设置PLL寄存器->初始化存储控制相关寄存器->初始化各模式下的栈指针 ;->设置缺省中断处理函数->将数据拷贝到RAM中,数据段清零->跳转到c语言main入口函数中     ;GET伪指令用于将一个源文件包含到当前源文件中,并将被包含文件在当前位置进行汇编处理,类似于c的include指令 ;GET INLCUDE伪指令不能 用来包含目标文件,INCBIN伪指令 可以包含目标文件, ;被INCBIN伪指令包含的文件, 不 进行汇编处理,该执行文件或数据直接放入当前文件,编译器从INCBIN后边开始继续处理 ;区分GET,INCLUDE,INCBIN的用法和作用               GEToption.inc             ;定义芯片相关配置        GETmemcfg.inc           ;定义存储器配置        GET2440addr.inc  ;定义寄存器符号     ;REFRESH寄存器[22]bit :SDRAM刷新模式 0- auto refresh; 1 - self refresh ;用于节电模式中,SDRAM自动刷新 BIT_SELFREFRESH EQU   (1<<22)   ;Pre-defined constants ;模式预定义常量,给cpsr【4-0】赋值,改变运行模式 USERMODE   EQU        0x10 FIQMODE    EQU 0x11 IRQMODE    EQU 0x12 SVCMODE    EQU        0x13 ABORTMODE  EQU       0x17 UNDEFMODE  EQU       0x1b MODEMASK   EQU       0x1f ;模式屏蔽位 NOINT      EQU    0xc0   ;1100 0000,中断屏蔽掩码   ;The location of stacks ;0x30000000 = 768M  ;定义各模式下的堆栈常量,是一个递减栈,后边标上了各个栈的大小 UserStack       EQU       (_STACK_BASEADDRESS-0x3800)  ; ~ 0x33ff4800      大小不定,跟堆大小相对应。毕竟是用户态栈 SVCStack       EQU       (_STACK_BASEADDRESS-0x2800)  ; ~ 0x33ff5800      4M UndefStack     EQU       (_STACK_BASEADDRESS-0x2400)  ; ~ 0x33ff5c00      1M AbortStack     EQU       (_STACK_BASEADDRESS-0x2000)  ; ~ 0x33ff6000      1M IRQStack       EQU       (_STACK_BASEADDRESS-0x1000)  ; ~ 0x33ff7000      4M FIQStack EQU       (_STACK_BASEADDRESS-0x0) ; ~ 0x33ff8000    4M     ;处理器分为16位 32位两种工作状态 程序的编译器也是分16位和32两种编译方式 下面程序根据处理器工作状态确定编译器编译方式 ;code16伪指令指示汇编编译器后面的指令为16位的thumb指令 ;code32伪指令指示汇编编译器后面的指令为32位的arm指令   ;Arm上电时处于ARM状态,故无论指令为ARM集或Thumb集,都先强制成ARM集,待init.s初始化完成后 ;再根据用户的编译配置转换成相应的指令模式。为此,定义变量THUMBCODE作为指示,跳转到main之前 ;根据其值切换指令模式   ;Check if tasm.exe(armasm -16 ...@ADS 1.0)is used. ;检测工作模式,根据CONFIG的数值,确定工作模式 ;{CONFIG}应该来自于ADS环境,在本环境中设置是进入时在ARM环境下,没有设置ARM/THUMB混合环境 ;关于是否设置混合编程,在环境设置选项里的ARM Assembler 选项下,由ATPCS -> ARM/Thumb interworking选项负责 ;IF ELSE ENDIF指令             GBLL    THUMBCODE        [{CONFIG} = 16         THUMBCODE SETL  {TRUE}         ;如果设置了config,则允许thumb指令,但THUMBCODE为真并不表明以下就是thumb指令,只是允许            CODE32                      ;code32表示以下是arm指令,在处理器刚开始时,必须以arm模式运行              |                                  ;此处容易产生错觉,丢掉CODE32这一行 THUMBCODE SETL  {FALSE}     ]   ;bx是带状态切换的跳转指令,跳转到Rm指定的地址执行程序,若Rm的位[0]为1,则跳转时自动将CPSR的标志T ;T置位,即把目标地址的代码解释为Thumb代码;若Rm的位[0]为0,则跳转时自动将CPSR中的标志T复位,即把 ;目标地址的代码解释为ARM代码   ;定义两个宏,宏的作用:子函数返回(无条件,有条件)。              MACRO        MOV_PC_LR              [ THUMBCODE                         ;如果允许thumb指令,则需要根据最低位设置状态。            bx lr                                   ;跳转,附带状态切换              |            mov pc,lr              ]        MEND                MACRO        MOVEQ_PC_LR   ;相等则跳转,相等与否由寄存器某些位确定,在此处,有其上一句的指令执行结果决定              [ THUMBCODE        bxeq lr              |            moveq pc,lr              ]        MEND   ;重点分析下面这个宏,它对中断处理函数的调用很重要     ;MACRO和MEND伪指令用于宏定义,MACRO标识开始,MEND标识结束。用MACRO和MEND定义的一段代码,称为 ;宏定义体,这样在程序中就可以通过宏指令多次调用该代码段。伪指令格式:   ;MACRO ;{$label} macroname {$parameter} {$parameter} ...               ;宏定义体 ;MEND   ;其中  $label 宏指令被展开时,label可被替换成相应的符号,通常为一个标号,在一个标号前使用$表示被汇编时将 ;使用相应的值替代$后的符号。 ;macroname 所定义的宏的名称 ;$parameter 宏指令的参数,当宏指令被展开时将被替换成相应的值,类似于函数中的形式参数 ;对于子程序代码较短,而需要传递的参数比较多的情况下,可以使用汇编技术。首先要用MACRO和MEND伪指令定义宏,包括宏定义 ;体代码。在MACRO伪指令之后的第一行定义宏的原型,其中包含该宏定义的名称,及需要的参数。在汇编程序中可以通过该宏定义 ;的名称来调用它,当源程序被汇编时,汇编编译器将  展开 每个宏调用,用宏定义体代替源程序中的宏定义的名称,并用实际的参数 ;值代替宏定义时的形式参数     ;在arm中,用的是满递减堆栈:stmfd,ldmfd,如果用其他的方式,arm可能不能有效识别 ;注意:满递减指的是在入栈时的操作方式,在出栈时则正好相反的次序 ;关于堆栈在数据放置方式,存取顺序上,可以参见《自学手册》P84中的实例分析 ;例子: ;STMFD sp!,{R0-R7,LR}:(满递减:先减再放数值)sp根据数据个数,减小相应个数值的数据单位(一步到位),然后利用for循环语句,从当前sp位置,依次存储R0-R7,LR.即:sp处最后指向的是R0数据处 ;LDMFD sp!,{R0-R7,LR}:复制一个变量为sp值,用该变量依次将数据存入R0-R7,LR,变量值增加,最后,变量指向下一个将要取的值,完成后sp获得该变量值; ;risc模式,这是对ram的操作     ;确切说,这是宏函数,编译时对调用语句要做相应的展开                MACRO $HandlerLabel HANDLER $HandleLabel   $HandlerLabel                            ;标号        sub  sp,sp,#4          ;留出一个空间,为了存放跳转地址给pc。见:str r0,[sp,#4] ,注意sp值并未改变               stmfd      sp!,{r0}  ;把r0中的内容入栈,保存起来               ldr     r0,=$HandleLabel    ;这是一个伪指令,不是汇编指令,目的:把$HandleLabel本身所在的地址给r0               ldr     r0,[r0]                           ;把HandleXXX所指向的内容(也就是中断程序的入口地址)放入r0               str     r0,[sp,#4]                  ;把入口地址放入刚才留出的一个空间里               ldmfd   sp!,{r0,pc}         ;出栈的方式恢复r0原值和为pc设定新值(也就完成了到ISR的转跳)。注:栈中r0内容在低地址               MEND   ;这几个变量是ads环境下自动设置的,可以见环境配置选项里:ARM Linker->Output下,RO Base,RW Base ;RW Base 没设置,因为代码段的结束便是数据段的开始,这个ads可以自动设置 ;IMPORT 引用变量        IMPORT  |Image$$RO$$Base|    ; Base of ROM code        IMPORT  |Image$$RO$$Limit|  ; End of ROM code (=start of ROM data)        IMPORT  |Image$$RW$$Base|   ; Base of RAM to initialise        IMPORT  |Image$$ZI$$Base|   ; Base and limit of area to zero initialise        IMPORT  |Image$$ZI$$Limit|           IMPORT MMU_SetAsyncBusMode        IMPORT MMU_SetFastBusMode ;想知道代码具体内容见cp15手册,并以cp15指令内容搜索2440a手册          IMPORT  Main            ;The main entry of mon program        IMPORT  RdNF2SDRAM   ;Copy Image from Nand Flash to SDRAM     ;AREA伪指令用于定义一个代码段或数据段,一个ARM源程序至少需要一个代码段,大的程序可以包含多个代码段及数据段 ;格式:AREA sectionname {,attr} {,attr}...        AREA    Init,CODE,READONLY   ;ENTRY伪指令用于指定程序的入口点 ;一个程序(可以包含多个源文件)中至少要有一个ENTRY,可以有多个ENTRY,但一个源文件中最多只有一个ENTRY.        ENTRY        ;EXPORT声明一个符号可以被其他文件引用,相当于声明了一个全局变量。GLOBAL与EXPORT相同     ;格式:EXPORT symbol{[WEAK]}   [WEAK]声明其他的同名符优先于本符号被引用 ;导出符号__ENTRY          EXPORT __ENTRY __ENTRY ResetEntry        ;1)Thecode, which converts to Big-endian, should be in little endian code.        ;2)Thefollowing little endian code will be compiled in Big-Endian mode.        ;  The code byte order should be changed as thememory bus width.        ;3)Thepseudo instruction,DCD can not be used here because the linker generates error.        ;条件编译,在编译成机器码前就设定好  大小端转换 ;判断ENDIAN_CHANGE是否已定义,ASSERT 是断言伪指令,语法是:ASSERT +逻辑表达式 ,def 是逻辑伪操作符,格式为::DEF:label,作用是:判断label是否定义过               ASSERT :DEF:ENDIAN_CHANGE        [ENDIAN_CHANGE                                             ;definedin option.inc 默认是FALSE,所以此句不会加入代码中               ASSERT  :DEF:ENTRY_BUS_WIDTH            ;断言指令,检测是否定义该变量,若未定义,报错               [ENTRY_BUS_WIDTH=32                      ;definedin option.inc                      b     ChangeBigEndian       ;DCD 0xea000007                ;如果是大端,则这是第一条指令,先设置成大端,再到复位指令               ]                 [ENTRY_BUS_WIDTH=16                      andeq      r14,r7,r0,lsl #20   ;DCD 0x0007ea00               ]                 [ENTRY_BUS_WIDTH=8                      streq       r0,[r0,-r10,ror #1] ;DCD 0x070000ea               ]        |               b     ResetHandler ;本硬件用的是小端模式,这是第一个执行语句,直接跳转到复位指令处 0X00        ]        b     HandlerUndef ;handler for Undefined mode            0X04        b     HandlerSWI          ;handlerfor SWI interrupt                   0X08        b     HandlerPabort ;handler for PAbort,指令预取中止    0X0C                    b     HandlerDabort       ;handler for DAbort,数据中止          0X10        b     .                           ;reserved 保留未用 注意小圆点         0X14        b     HandlerIRQ    ;handlerfor IRQ interrupt                          0X18        b     HandlerFIQ    ;handlerfor FIQ interrupt                          0X1C ;这7个中断,每个中断都有固定的中断入口地址,它们位于代码的最前端,不允许另作他用 ;@0x20        b     EnterPWDN   ;Must be                                               0x20   ;下面是改变大小端的程序,采用直接定义  <机器码>  的方式,为什么这么做就得问三星了 ;反正我们程序里这段代码也不会去执行,不用去管它 ;每一个汇编指令,都对应着一个二进制机器码,这里没有使用指令,直接用了机器码,含义未知 ChangeBigEndian ;@0x24        [ENTRY_BUS_WIDTH=32               DCD       0xee110f10     ;0xee110f10 => mrc p15,0,r0,c1,c0,0               DCD       0xe3800080    ;0xe3800080 => orr r0,r0,#0x80;  //Big-endian               DCD       0xee010f10     ;0xee010f10 => mcr p15,0,r0,c1,c0,0 ;对存储器控制寄存器操作,指定内存模式为Big-endian ;因为刚开始CPU都是按照32位总线的指令格式运行的,如果采用其他的话,CPU别不了,必须转化 ;但当系统初始化好以后,则CPU能自动识别          ]        [ENTRY_BUS_WIDTH=16               DCD0x0f10ee11               DCD0x0080e380               DCD0x0f10ee01 ;因为采用Big-endian模式,采用16位总线时,物理地址的高位和数据的地位对应 ;所以指令的机器码也相应的高低对调          ]        [ENTRY_BUS_WIDTH=8               DCD0x100f11ee               DCD0x800080e3               DCD0x100f01ee        ]        DCD0xffffffff  ;swinv 0xffffff is similarwith NOP and run well in both endian mode.        DCD0xffffffff        DCD0xffffffff        DCD0xffffffff        DCD0xffffffff        bResetHandler                     ;设置成大端后,再次跳到复位指令处                 ;本文件底部定义了一个数据区(在文件最后),34个字空间,存放相应中断服务程序的首地址。每个字 ;空间都有一个标号,以Handle***命名。 ;这是宏实例   在这里Handler***就是通过HANDLER这个宏和Handle***建立联系的.   ;详细分析: ;    这是宏示例,也就是宏的调用指令,当编译时编译器会把宏调用指令展开 ;    这是向量中断   ;展开方式(举例):   ;HandlerFIQ   HANDLERHandleFIQ   ;展开后变成:   ;HandlerFIQ                                ;标号,由 " b       HandlerFIQ  "指令使用(见上,复位处) ;         sub    sp,sp,#4              ;留出一个空间,为了存放跳转地址给pc。见:strr0,[sp,#4] ,注意sp值并未改变 ;      ;         stmfd sp!,{r0}      ;把r0中的内容入栈,保存起来 ;      ;         ldr    r0,=HandleFIQ  ;HandleFIQ标号,在本文件最下方定义 ;      ;         ldr    r0,[r0]               ;把HandleXXX所指向的内容(也就是中断程序的入口地址)放入r0 ;      ;         str    r0,[sp,#4]       ;把入口地址放入刚才留出的一个空间里 ;      ;        ldmfd  sp!,{r0,pc}            ;出栈的方式恢复r0原值和为pc设定新值(也就完成了到ISR的转跳)。注:栈中r0内容在低地址 ;      ; 后边的语句展开方式,同上。编译后,代码都展开放置   HandlerFIQ    HANDLERHandleFIQ HandlerIRQ    HANDLERHandleIRQ HandlerUndef HANDLER HandleUndef HandlerSWI   HANDLERHandleSWI HandlerDabort       HANDLER HandleDabort HandlerPabort HANDLER HandlePabort       ;非向量中断总入口(需要自己判断中断类型,而不是直接跳转到相应程序) ;产生中断后,需要中断服务程序自己来判断,到底是哪个中断请求,根据的就是INTOFFSET寄存器中的偏移,再计算中断服务地址 IsrIRQ        sub  sp,sp,#4      ;reserved for PC,预留返回指针的存储位置        stmfd      sp!,{r8-r9}          ldr   r9,=INTOFFSET    ;the interrupt request source offset        ldr   r9,[r9]        ldr   r8,=HandleEINT0         ;HandleEINT0 ,在本文件最下边定义的        add  r8,r8,r9,lsl #2        ;r9中只是偏移单位的个数,需要*4变成具体字节偏移(相对于EINT0)        ldr   r8,[r8]        str    r8,[sp,#8]                     ;pc值放在了高位置        ldmfd      sp!,{r8-r9,pc}            LTORG        ;LTORG用于声明一个文字池,在使用LDR伪指令时,要在适当的地方加入LTORG声明文字池,这样就会把要加载的数据保存在 ;文字池内,再用ARM的《加载指令》读出数据。(若没有使用LTORG声明文字池,则汇编器会在程序末尾自动声明) ;LTORG 伪指令常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器就不会错误地将文字池中的数据当做指令来执行 ;注:在此,文字池内存储的是INTOFFSET宏所代表的值:0x4a000014  。毕竟,当把指令编译成二进制代码时,arm指令(32位) ;不能既表示出指令内容,又表示出数据地址(32位)。估计在编译时,会被汇编成其他的加载指令,再编译成机器码 ;LTORG 只要单独写出来就可以了,其他的交给编译器来做,而且它跟它下面的代码没有任何关系     ;======= ; ENTRY ;=======             ResetHandler                       ldr   r0,=WTCON       ;watch dog disable   编译时就是 ldr r0,=53000000;伪指令有=号        ldr   r1,=0x0                 ;这些宏定义都位于2440addr.inc中。   区分:变量定义&& 宏定义        str    r1,[r0]          ldr   r0,=INTMSK        ldr   r1,=0xffffffff  ;all interrupt disable   要理解子中断和中断之间的关系        str    r1,[r0]          ldr   r0,=INTSUBMSK        ldr   r1,=0x7fff             ;allsub interrupt disable        str    r1,[r0]          [{FALSE}               ;rGPFDAT= (rGPFDAT & ~(0xf<<4)) | ((~data & 0xf)<<4);               ;Led_Display               ldr   r0,=GPBCON               ldr   r1,=0x155500               str    r1,[r0]               ldr   r0,=GPBDAT               ldr   r1,=0x0               str    r1,[r0]        ]          ;Toreduce PLL lock time, adjust the LOCKTIME register.        ldr   r0,=LOCKTIME        ldr   r1,=0xffffff                  str    r1,[r0]          [PLL_ON_START               ;defined inoption.inc {TRUE},选择要不要设置频率值               ;Added for confirm clock divide. for 2440.               ;Setting value Fclk:Hclk:Pclk               ldr   r0,=CLKDIVN               ldr   r1,=CLKDIV_VAL        ; 0=1:1:1, 1=1:1:2, 2=1:2:2, 3=1:2:4, 4=1:4:4, 5=1:4:8,6=1:3:3, 7=1:3:6.               str    r1,[r0]          ;programhas not been copied, so use these directly               [CLKDIV_VAL>1              ; meansFclk:Hclk is not 1:1.                      mrcp15,0,r0,c1,c0,0                      orrr0,r0,#0xc0000000;R1_nF:OR:R1_iA                      mcrp15,0,r0,c1,c0,0                      |                      mrcp15,0,r0,c1,c0,0                      bicr0,r0,#0xc0000000;R1_iA:OR:R1_nF                      mcrp15,0,r0,c1,c0,0               ] ; 在配置UPLLCON和MPLLCON寄存器时,必须先配置UPLLCON,然后再配置MPLLCON,而且两者之间要有7 nop的间隔。(这是2440文档明确要求的)               ;ConfigureUPLL               ldr   r0,=UPLLCON               ldr   r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV)                      ;Fin = 12.0MHz, UCLK =48MHz,对于usb来说必须是48MHz               str    r1,[r0]               nop  ; Caution: After UPLL setting, at least7-clocks delay must be inserted for setting hardware be completed.               nop               nop               nop               nop               nop               nop               ;ConfigureMPLL               ldr   r0,=MPLLCON                    ;计算公式是固定的,可计算得到               ldr   r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)                           ;Fin = 12.0MHz, FCLK= 400MHz               str    r1,[r0]        ]          ;Checkif the boot is caused by the wake-up from SLEEP mode.        ldr   r1,=GSTATUS2             ;这个寄存器数值表示哪个信号引起的复位动作产生        ldr   r0,[r1]        tst    r0,#0x2        ;Incase of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler.        bne  WAKEUP_SLEEP          EXPORTStartPointAfterSleepWakeUp StartPointAfterSleepWakeUp          ;Setmemory control registers                           ;ldr  r0,=SMRDATA  ;(等效于下边的指令)             adrl  r0,SMRDATA       ;be careful!中等范围的地址读取伪指令,用法类似于ldr(大范围地址读取)伪指令        ldr   r1,=BWSCON       ;BWSCONAddress        add  r2, r0, #52      ;Endaddress of SMRDATA    共有13个寄存器地址(4字节)需要赋值,13*4=52字节   0        ldr   r3, [r0], #4            ;这些都是后变址指令        str    r3, [r1], #4        cmp r2, r0        bne  %B0                                   ;当<的时候,跳转到0标号处继续执行   ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;       When EINT0 is pressed,  Clear SDRAM ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; check if EIN0 button is pressed      ldr   r0,=GPFCON   ;input,无上拉电阻        ldr   r1,=0x0        str    r1,[r0]        ldr   r0,=GPFUP        ldr   r1,=0xff        str    r1,[r0]          ldr   r1,=GPFDAT        ldr   r0,[r1]        bic   r0,r0,#(0x1e<<1)  ; bit clear        tst    r0,#0x1        bne%F1               ;若不相等,则向下跳到1标号,跳过下边代码                 ; Clear SDRAM Start          ldr   r0,=GPFCON        ldr   r1,=0x55aa        str    r1,[r0] ;      ldr   r0,=GPFUP ;      ldr   r1,=0xff ;      str    r1,[r0]        ldr   r0,=GPFDAT        ldr   r1,=0x0        str    r1,[r0]     ;LED=****          movr1,#0        movr2,#0        movr3,#0        movr4,#0        movr5,#0        movr6,#0        movr7,#0        movr8,#0               ldr   r9,=0x4000000   ;64MB      ;这几条指令目的是:擦除sdram的所有数据        ldr   r0,=0x30000000 0            stmia       r0!,{r1-r8}        subs r9,r9,#32        bne  %B0   ;Clear SDRAM End   1          ;Initializestacks        bl    InitStacks   ;=========================================================== ;OM0是flash选择开关,OM0接地时从nand 启动,悬空时(核心板上有上拉电阻)从nor启动 ;OM1在核心板上,始终是接地.为0 ;OM1:OM0取值:00 nandflash mode;01 16bit nor;10 32bit nor;11 test mode ;详见:s3c2440 用户手册 5.memory controller 一节          ldr   r0, =BWSCON        ldr   r0, [r0]        ands r0, r0, #6        ;OM[1:0]!= 0, NOR FLash boot do not read nand flash        bne  copy_proc_beg                     adr   r0, ResetEntry        ;OM[1:0] == 0, NAND FLash boot        cmp r0, #0                           ;ifuse Multi-ice,        bne  copy_proc_beg              ;donot read nand flash for boot        ;nop        ;ands指令,加s表示结果影响cpsr寄存器的值                     ;=========================================================== ;把nand中的数据,拷贝到ram中 nand_boot_beg        [{TRUE}               blRdNF2SDRAM                              ]          ldr   pc, =copy_proc_beg   ;这里的一段代码时对内存数据的初始化,涉及代码段,数据段,bss段等 ;因对这里的变量设置等有异议,暂时未全面分析,但是基本原理想通,就是一个比较地址,复制数据的过程 copy_proc_beg        adr   r0, ResetEntry        ldr   r2, BaseOfROM        cmp r0, r2        ldreq       r0, TopOfROM        beq  InitRam          ldrr3, TopOfROM 0            ldmia      r0!, {r4-r7}        stmia       r2!, {r4-r7}        cmp r2, r3        bcc  %B0               sub  r2, r2, r3        sub  r0, r0, r2                                    InitRam          ldr   r2, BaseOfBSS        ldr   r3, BaseOfZero      0        cmp r2, r3        ldrcc       r1, [r0], #4        strcc r1, [r2], #4        bcc  %B0                mov r0,   #0        ldr   r3,   EndOfBSS 1            cmp r2,   r3        strcc r0, [r2], #4        bcc  %B1               ldr   pc, =%F2              ;gotocompiler address 2        ;      [CLKDIV_VAL>1              ; meansFclk:Hclk is not 1:1. ;      bl    MMU_SetAsyncBusMode ;      | ;      blMMU_SetFastBusMode     ; default value. ;      ]          ;===========================================================       ; Setup IRQ handler ; 把中断服务函数的总入口地址,赋给HandleIRQ地址(文件最低端定义)        ldr   r0,=HandleIRQ      ;Thisroutine is needed        ldr   r1,=IsrIRQ     ;ifthere is not 'subs pc,lr,#4' at 0x18, 0x1c        str    r1,[r0]           [:LNOT:THUMBCODE              bl    Main       ;Do not use main() because ......              b     .     ]       [THUMBCODE    ;for start-up code for Thumbmode              orr   lr,pc,#1              bx    lr              CODE16              bl    Main       ;Do not use main() because ......              b     .               CODE32     ]     ;function initializing stacks InitStacks  ; 初始化栈空间(各个模式下的),为c函数运行做准备        ;Donot use DRAM,such as stmfd,ldmfd......        ;SVCstackis initialized before        ;Undertoolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'        mrs  r0,cpsr        bic   r0,r0,#MODEMASK        orr   r1,r0,#UNDEFMODE|NOINT        msr  cpsr_cxsf,r1           ;UndefMode        ldr   sp,=UndefStack             ; UndefStack=0x33FF_5C00          orr   r1,r0,#ABORTMODE|NOINT        msr  cpsr_cxsf,r1           ;AbortMode        ldr   sp,=AbortStack             ; AbortStack=0x33FF_6000          orr   r1,r0,#IRQMODE|NOINT        msr  cpsr_cxsf,r1           ;IRQMode        ldr   sp,=IRQStack        ;IRQStack=0x33FF_7000          orr   r1,r0,#FIQMODE|NOINT        msr  cpsr_cxsf,r1           ;FIQMode        ldr   sp,=FIQStack         ;FIQStack=0x33FF_8000          bic   r0,r0,#MODEMASK|NOINT        orr   r1,r0,#SVCMODE        msr  cpsr_cxsf,r1           ;SVCMode        ldr   sp,=SVCStack        ;SVCStack=0x33FF_5800          ;USERmode has not be initialized.          mov pc,lr        ;TheLR register will not be valid if the current mode is not SVC mode.                 LTORG   SMRDATA DATA   ;配置存储器的管理方式 ; Memory configuration should be optimizedfor best performance ; The following parameter is not optimized. ; Memory access cycle parameter strategy ; 1) The memory settings is  safe parameters even at HCLK=75Mhz. ; 2) SDRAM refresh period is forHCLK<=75Mhz.          DCD(0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))        DCD((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))   ;GCS0        DCD((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))   ;GCS1        DCD((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))   ;GCS2        DCD((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))   ;GCS3        DCD((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))   ;GCS4        DCD((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))   ;GCS5        DCD((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))    ;GCS6        DCD((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))    ;GCS7        DCD((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Tsrc<<18)+(Tchr<<16)+REFCNT)          DCD0x32          ;SCLK power saving mode, BANKSIZE 128M/128M          DCD0x30          ;MRSR6 CL=3clk        DCD0x30          ;MRSR7 CL=3clk        ;分配一个字的空间,并用后边的数值来初始化该空间   ,这里命名有些混乱 BaseOfROM   DCD       |Image$$RO$$Base| TopOfROM    DCD       |Image$$RO$$Limit| BaseOfBSS     DCD       |Image$$RW$$Base| BaseOfZero    DCD       |Image$$ZI$$Base| EndOfBSS     DCD       |Image$$ZI$$Limit|          ALIGN        ;Function for entering power down mode ; 1. SDRAM should be in self-refresh mode. ; 2. All interrupt should be maksked forSDRAM/DRAM self-refresh. ; 3. LCD controller should be disabled forSDRAM/DRAM self-refresh. ; 4. The I-cache may have to be turned on. ; 5. The location of the following code mayhave not to be changed.   ;void EnterPWDN(int CLKCON); EnterPWDN        movr2,r0              ;r2=rCLKCON        tstr0,#0x8             ;SLEEP mode?        bneENTER_SLEEP   ENTER_STOP        ldrr0,=REFRESH        ldrr3,[r0]              ;r3=rREFRESH        movr1, r3        orrr1, r1, #BIT_SELFREFRESH        strr1, [r0]             ;Enable SDRAMself-refresh          movr1,#16                   ;wait untilself-refresh is issued. may not be needed. 0     subsr1,r1,#1        bne%B0          ldrr0,=CLKCON          ;enter STOP mode.        strr2,[r0]          movr1,#32 0     subsr1,r1,#1   ;1) wait until the STOP mode isin effect.        bne%B0        ;2) Or wait here until theCPU&Peripherals will be turned-off                      ;   Entering SLEEP mode, only the reset bywake-up is available.          ldrr0,=REFRESH ;exit from SDRAM self refresh mode.        strr3,[r0]          MOV_PC_LR   ENTER_SLEEP        ;NOTE.        ;1)rGSTATUS3 should have the return address after wake-up from SLEEP mode.          ldrr0,=REFRESH        ldrr1,[r0]              ;r1=rREFRESH        orrr1, r1, #BIT_SELFREFRESH        strr1, [r0]             ;Enable SDRAMself-refresh          movr1,#16                   ;Wait untilself-refresh is issued,which may not be needed. 0     subsr1,r1,#1        bne%B0          ldr   r1,=MISCCR        ldr   r0,[r1]        orr   r0,r0,#(7<<17)  ;Set SCLK0=0, SCLK1=0, SCKE=0.        str    r0,[r1]          ldrr0,=CLKCON          ; Enter sleep mode        strr2,[r0]          b.                 ;CPU will die here.     WAKEUP_SLEEP        ;ReleaseSCLKn after wake-up from the SLEEP mode.        ldr   r1,=MISCCR        ldr   r0,[r1]        bic   r0,r0,#(7<<17)  ;SCLK0:0->SCLK, SCLK1:0->SCLK,SCKE:0->=SCKE.        str    r0,[r1]          ;Setmemory control registers       ldr   r0,=SMRDATA      ;be careful!        ldr   r1,=BWSCON       ;BWSCONAddress        add  r2, r0, #52      ;Endaddress of SMRDATA 0        ldr   r3, [r0], #4        str    r3, [r1], #4        cmp r2, r0        bne  %B0          movr1,#256 0     subsr1,r1,#1   ;1) wait until the SelfRefreshis released.        bne%B0          ldrr1,=GSTATUS3       ;GSTATUS3 has the startaddress just after SLEEP wake-up        ldrr0,[r1]          movpc,r0        ;===================================================================== ; Clock division test ; Assemble code, because VSYNC time is veryshort ;=====================================================================        EXPORTCLKDIV124        EXPORTCLKDIV144        CLKDIV124               ldr    r0, = CLKDIVN        ldr     r1, = 0x3         ;0x3 = 1:2:4        str     r1, [r0] ;      waituntil clock is stable        nop        nop        nop        nop        nop          ldr     r0, = REFRESH        ldr     r1, [r0]        bic          r1, r1, #0xff        bic          r1, r1, #(0x7<<8)        orr          r1, r1, #0x470 ; REFCNT135        str     r1, [r0]        nop        nop        nop        nop        nop        mov     pc, lr   CLKDIV144        ldr     r0, = CLKDIVN        ldr     r1, = 0x4         ;0x4 = 1:4:4        str     r1, [r0] ;      waituntil clock is stable        nop        nop        nop        nop        nop          ldr     r0, = REFRESH        ldr     r1, [r0]        bic          r1, r1, #0xff        bic          r1, r1, #(0x7<<8)        orr          r1, r1, #0x630 ; REFCNT675 - 1520        str     r1, [r0]        nop        nop        nop        nop        nop        mov     pc, lr            ALIGN       ;定义数据段 ;^ 标志等价于MAP伪指令 ;MAP用于定义一个结构化的内存表首地址,此时内存表的位置计数器值,也变成该首地址值,就相当于在这个地址处操作 ;#于FIELD同义,用于定义一个结构化的内存表的数据域,后边数字表示该数据占用的字节数 ;Handle*** 在此就是一个标号,为了标示数据量 ;用法:把对应的终端处理函数的首地址,放到这里的对应的预留空间处,当发生中断时,就能根据宏函数,直接跳转              AREARamData, DATA, READWRITE          ^   _ISR_STARTADDRESS              ; _ISR_STARTADDRESS=0x33FF_FF00 HandleReset   #   4 HandleUndef #   4 HandleSWI            #   4 HandlePabort    #   4 HandleDabort    #   4 HandleReserved  #   4 HandleIRQ            #   4 HandleFIQ            #   4   ;Do not use the label 'IntVectorTable', ;The value of IntVectorTable is differentwith the address you think it may be. ;IntVectorTable ;@0x33FF_FF20 HandleEINT0        #   4 HandleEINT1        #   4 HandleEINT2        #   4 HandleEINT3        #   4 HandleEINT4_7     #   4 HandleEINT8_23   #   4 HandleCAM          #   4            ;Added for 2440. HandleBATFLT      #   4 HandleTICK          #   4 HandleWDT          #   4 HandleTIMER0     #   4 HandleTIMER1     #   4 HandleTIMER2     #   4 HandleTIMER3     #   4 HandleTIMER4     #   4 HandleUART2     #   4 ;@0x33FF_FF60 HandleLCD           #   4 HandleDMA0        #   4 HandleDMA1        #   4 HandleDMA2        #   4 HandleDMA3        #   4 HandleMMC          #   4 HandleSPI0           #   4 HandleUART1              #   4 HandleNFCON             #   4            ;Added for 2440. HandleUSBD         #   4 HandleUSBH         #   4 HandleIIC             #   4 HandleUART0       #   4 HandleSPI1          #   4 HandleRTC           #   4 HandleADC          #   4 ;@0x33FF_FFA0        END       若有不妥之处,敬请告知!QQ:570886026                                                                                            独舞