LPC1114启动代码分析

启动代码是芯片复位后进入C语言的main()函数前执行的一段代码,主要为运行C语言程序提供基本运行环境。启动代码作用一般是:

    1)堆和栈的初始化;2)向量表定义;3)地址重映射及中断向量表的转移;4)设置系统时钟频率;5)中断寄存器的初始化;6)进入C应用程序。

    启动代码文件:startup.s,startup.s包含异常向量表和系统初始化代码,保存C语言使用的堆和栈的开始位置,包括异常处理程序和目标板特殊的代码。

    汇编学习:ARM伪指令,在汇编程序中经常会被使用,包括以下几条:    

        — AREA    

       — ALIGN    

       — CODE16 、 CODE32    
      — ENTRY    
      — END    
      — EQU    
      — EXPORT (或 GLOBAL )    
      — IMPORT    
      — EXTERN    
      — GET (或 INCLUDE )    
      — INCBIN    
      —KEEP:告诉编译器将局部符号包含在目标文件的符号表中
      — NOFP:禁止源程序中包含浮点运算指令
      — REQUIRE:指定段之间的相互依赖关系
      — REQUIRE 8及PRESERVE8:指示当前代码中(要求)数据栈8字节对齐
      — RN    
      — ROUT    

       

=============================================================================================

END 
语法格式: 
END 
END 伪指令用于通知编译器已经到了源程序的结尾。 
使用示例: 
AREA Init , CODE , READONLY 
…… 
END ;指定应用程序的结尾

=============================================================================================

ENTRY 
语法格式: 
ENTRY 
ENTRY 伪指令用于指定汇编程序的入口点。在一个完整的汇编程序中至少要有一个 ENTRY (也可以有

多个,当有多个 ENTRY 时,程序的真正入口点由链接器指定),但在一个源文件里最多只能有一个 ENTRY

(可以没有)。 
使用示例: 
AREA Init , CODE , READONLY 
ENTRY ;指定应用程序的入口点 
……

=============================================================================================

GET(或INCLUDE) 
语法格式: 
GET 文件名 
GET 伪指令用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。

可以使用 INCLUDE 代替 GET 。 
汇编程序中常用的方法是在某源文件中定义一些宏指令,用 EQU 定义常量的符号名称,用 MAP和

FIELD 定义结构化的数据类型,然后用 GET 伪指令将这个源文件包含到其他的源文件中。使用方法与 C 语

言中的 “ include ” 相似。 
GET 伪指令只能用于包含源文件,包含目标文件需要使用 INCBIN 伪指令 
使用示例: 
AREA Init , CODE , READONLY 
GET a1.s ;通知编译器当前源文件包含源文件a1.s 
GE T C:\a2.s ;通知编译器当前源文件包含源文件C:\ a2.s …… 
END

=============================================================================================

INCBIN 
语法格式: 
INCBIN 文件名 
INCBIN 伪指令用于将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不作任何变动的

存放在当前文件中,编译器从其后开始继续处理。 
使用示例: 
AREA Init , CODE , READONLY 
INCBIN a1.dat ;通知编译器当前源文件包含文件a1.dat 
INCBIN C:\a2.txt ;通知编译器当前源文件包含文件C:\a2.txt…… 
END

=============================================================================================

RN 
语法格式: 
名称 RN 表达式 
RN 伪指令用于给一个寄存器定义一个别名。采用这种方式可以方便程序员记忆该寄存器的功能。其中

,名称为给寄存器定义的别名,表达式为寄存器的编码。 
使用示例: 
Temp RN R0 ;将R0 定义一个别名Temp

=============================================================================================

 ROUT 
语法格式: 
{ 名称 } ROUT 
ROUT 伪指令用于给一个局部变量定义作用范围。在程序中未使用该伪指令时,局部变量的作用范围为

所在的 AREA ,而使用 ROUT 后,局部变量的作为范围为当前 ROUT 和下一个 ROUT 之间。

=============================================================================================

EQU其作用类似于 C 语言中的#define

Expression_name EQU Expression

  此后程序中凡需要用到该表达式指出,就可以用表达式名来代替了。可见,EQU的引入提高了程序的可读性,也使其容易修改。

  上式中的表达式可以是任何有效的操作数格式,可以是任何可求出常数值的表达式,也可以是任何有效的助记符。举例如下:

  CONSTANT EQU 256           数值赋以符号名

  DATA EQU HEIGHT+12        地址表达式赋以符号名

  ALPAHA EQU 7

EQU不是指令集,而是伪指令,一般我们常使用的MASM5.0以上都常用这个伪指令。它不是80X86的指令集合。而汇编在第一次扫描时只扫描了指令,而将伪指令中的东西作为动态内容作了标记而已。所以在第一次扫描所得到的清单中是没有看到它占用内存的。所以不会计算其中的数据的。而第二次扫描才能得到。

  指令集是属于机器CPU的,因有的,一个类型CPU就有这样一个指令集。而伪指令则是由汇编软件提供的,比如MASM5.0中提供了EQU的伪指令,那么汇编时是由于MASM5.0进行运算的。而计算空间时所得到的清单文件是关于指令的,所以伪指令并没有计算在内。

  不同类型的CPU会有不同的指令集,不管你使用什么样的汇编软件,同一个类型 CPU指令集是不会变的!而伪指令是由汇编软件提供,不同的汇编软件有不同的伪指令集。

  CPU的发展和软件的发展都有一个基础,因此出现了向下兼容的现象。8038680286相比,只在80286指令集的基础上增加了几个指令而成的。而软件也是,MASM6.0只是在5.0部分伪指令集的基础上增加了几条伪指令而已。但6.0却还有一大进步就是将5.0中的两次扫描一次完成,也就是说6.0只有一次扫描,而5.0却是两次扫描。

=============================================================================================

AREA

       AREA    STACK, NOINIT, READWRITE, ALIGN=3

语法格式:    
      AREA  段名 属性 1 ,属性 2 ……    
      AREA 伪指令用于定义一个代码段或数据段。其中,段名若以数字开头,则该段名需用 “ | ” 括起来,如 |1_test| 。还有一些代码段具有约定的名称,如|.text|表示C语言编译器产生的代码段或者是与C语言库相关的代码段。属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。常用的属性如下:    
    — CODE 属性:用于定义代码段,代码段的默认属性为 READONLY     
    — DATA  属性:用于定义数据段,数据段的默认属性为 READWRITE     
    — NOINIT 属性:指定本数据段仅仅保留了内存单元,而没有将各初始值写入内存单元,或者将各内存单元初始化为0.
    — READONLY 属性:指定本段为只读,代码段默认为 READONLY     
    — READWRITE 属性:指定本段为可读可写,数据段的默认属性为 READWRITE     
    — ALIGN 属性:使用方式为 ALIGN 表达式。在默认时, ELF (可执行连接文件)的代码段和数据段是按字对齐的,表达式的取值范围为 0  31 ,相应的对齐方式为 2   表达式次方。如表达式=3时为8字节对齐。
    —ASSOC=section。指定与本段相关的ELF段。任何时候连接section段也必须包括sectionname
    — COMMON 属性:该属性定义一个通用的段,不包含任何的用户代码和数据。连接器将其初始化为0。各源文件中同名的 COMMON 段共享同一段内存单元,连接器为其分配合适的尺寸。
    — COMDEF 属性:该属性定义一个通用的段,该段可以包含代码或数据。在各源文件中,同名的COMDEF段必须相同。
常可以用AREA伪操作将程序分为多个ELF格式的段。段名称可以相同,这时这些同名的段被放在同一个ELF段中。一个大的程序可以包括多个代码段和数据段,一个汇编语言程序至少要包含一个段    
    使用示例:    
    AREA Init  CODE  READONLY    
    该伪操作定义了一个代码段,段名为 Init ,属性为只读=============================================================================================

SPACE    
    语法格式:    
    标号 SPACE     表达式    
    SPACE 伪指令用于分配一片连续的存储区域并初始化为 0 。其中,表达式为要分配的字节数。    
    SPACE 也可用    代替。    
    使用示例:    
    DataSpace SPACE 100 ;分配连续100 字节的存储单元并初始化 0 

=============================================================================================

PRESERVE8:指定当前文件堆栈8字节对齐

    字节对齐关键词,以前用ADS编译器的时候可以不用,但是后来的keil编译器时需要加上(譬如用周立功模板时,将ADS工程转到keil工程时就必须加上)。

REQUIRE8 指令指定当前文件要求堆栈八字节对齐。它设置 REQ8 编译属性以通知链接器。

PRESERVE8 指令指定当前文件保持堆栈八字节对齐,它设置 PRES8 编译属性以通知链接器。

链接器检查要求堆栈八字节对齐的任何代码是否仅由保持堆栈八字节对齐的代码直接或间接地调用。

语法

REQUIRE8 {bool} PRESERVE8 {bool} 其中: 
bool是一个可选布尔常数,取值为 {TRUE} 或 {FALSE} 

用法

如果您的代码保持堆栈八字节对齐,在需要时,可使用 PRESERVE8 设置文件的 PRES8 编译属性。 如果您的代码不保持堆栈八字节对齐,则可使用 PRESERVE8 {FALSE} 确保不设置 PRES8 编译属性。

Note

如果您省略 PRESERVE8  PRESERVE8 {FALSE},汇编程序会检查修改 sp 的指令,以决定是否设置 PRES8 编译属性。 ARM 建议明确指定 PRESERVE8

您可以通过以下形式启用警告:

armasm --diag_warning 1546

您将会收到类似以下警告:

"test.s", line 37: Warning: A1546W: Stack pointer update potentially breaks 8 byte stack alignment
37 00000044         STMFD    sp!,{r2,r3,lr}

 =============================================================================================

THUMB告诉汇编器下面是32为的Thumb指令,如果需要,汇编器将插入位以保证对齐

CODE16CODE32  [THUMB]
    语法格式:    
    CODE16 (或CODE32     
    CODE16 伪指令通知编译器,其后的指令序列为 16 位的Thumb 指令。    
    CODE32 伪指令通知编译器,其后的指令序列为 32 位的ARM 指令。    
    若在汇编源程序中同时包含ARM 指令和 Thumb 指令时,可用CODE16 伪指令通知编译器其后的指令序列为 16 位的Thumb 指令, CODE32 伪指令通知编译器其后的指令序列为 32 位的ARM 指令。因此,在使用 ARM 指令和Thumb 指令混合编程的代码里,可用这两条伪指令进行切换,但注意他们只通知编译器其后指令的类型,并不能对处理器进行状态的切换。    
    使用示例:    
    AREA Init CODE  READONLY    
    ……    
    CODE32
 ;通知编译器其后的指令为32 位的 ARM 指令    
    LDR R0 ,=NEXT  1 ;将跳转地址放入寄存器 R0    
    BX R0 ;程序跳转到新的位置执行,并将处理器切换到 Thumb 工作状态    
    ……    
    CODE16
 ;通知编译器其后的指令为16 位的 Thumb 指令    
    NEXT LDR R3,=0x3FF    
    ……    
    END
 ;程序结 

=============================================================================================

EXPORT(或GLOBAL    
    语法格式:    
    EXPORT 标号{[WEAK]}    
    EXPORT 伪指令用于在程序中声明一个全局的标号,该标号可在其他的文件中引用。 EXPORT可用GLOBAL 代替。标号在程序中区分大小写, [WEAK] 选项声明其他的同名标号优先于该标号被引用。    
    使用示例:    
    AREA Init CODE  READONLY    
    EXPORT Stest ;声明一个可全局引用的标号Stest……    
    END    

==================================================================================================

DCD(或DCDU    
    语法格式:    
    标号DCD (或 DCDU  表达式    
    DCD (或DCDU )伪指令用于分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。 DCD 也可用 “ & ” 代替。    
    DCD 分配的字存储单元是字对齐的,而用 DCDU 分配的字存储单元并不严格字对齐。    
    使用示例:    
    DataTest DCD 4 

5 

6 ;分配一片连续的字存储单元并初始化。

==================================================================================================

汇编控制( Assembly Control )伪指令    
    汇编控制伪指令用于控制汇编程序的执行流程,常用的汇编控制伪指令包括以下几条:    
    — IF ELSE  ENDIF    
    — WHILE WEND    
    — MACRO MEND    
    — MEXIT    
    
1 IFELSEENDIF    
    语法格式:    
    IF 逻辑表达式    
    指令序列1    
    ELSE    
   
 指令序列2    
    ENDIF    
    IF
 ELSE  ENDIF 伪指令能根据条件的成立与否决定是否执行某个指令序列。当 IF 后面的逻辑表达式为真,则执行指令序列 1 ,否则执行指令序列 2 。其中, ELSE 及指令序列 2 可以没有,此时,当 IF 后面的逻辑表达式为真,则执行指令序列 1 ,否则继续执行后面的指令。    
    IF ELSE  ENDIF 伪指令可以嵌套使用。    
    使用示例:    
    GBLL Test ;声明一个全局的逻辑变量,变量名为 Test……    
    IF Test = TRUE    
   
 指令序列1    
    ELSE    
   
 指令序列2    
    ENDIF

   XLANDY 表示将XY做逻辑与的操作。

  XLORY 表示将XY做逻辑或的操作。

  :LNOTY 表示将Y做逻辑非的操作。

XLEORY 表示将XY做逻辑异或的操作。

:LNOT: 逻辑预算符

:DEF:

                IF      :LNOT::DEF:NO_CRP    ;如果宏判断是否定义NO_CRP 

                AREA    |.ARM.__at_0x02FC|, CODE, READONLY  ;自定义只读代码段
CRP_Key         DCD     0xFFFFFFFF     ;加密等级见上注释

               ENDIF

 

==================================================================================================

PROC 

 过程就是子程序。一个过程可以被其它程序所调用(CALL指令),过程的最后一条指令一般是返回指令(RET)

  过程定义伪指令的格式为

    <过程名>    PROC  [类型]

                …

                …  

                RET

    <过程名>    ENDP

  注意:PROCENDP必须成对出现

过程的类型有两种:

    NEAR——(默认类型)表示段内调用

    FAR——表示段间调用

   调用一个过程的格式为:

       CALL  <过程名>

================================================================================================== 

IMPORT    
    语法格式:    
    IMPORT  标号 {[WEAK]}    
    IMPORT 伪指令用于通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。    
    标号在程序中区分大小写,[WEAK] 选项表示当所有的源文件都没有定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为 0 ,若该标号为 B  BL 指令引用,则将 B  BL指令置为 NOP 操作。    
    使用示例:    
    AREA Init  CODE  READONLY    
    IMPORT Main ;通知编译器当前文件要引用标号Main,但Main 在其他源文件中定义……    
    END   

==================================================================================================

ldr       只能在当前PC的4KB范围内跳转
B     只能在当前PC的32M围内跳转
label  标号实际上就是个地址
eg:

合法:

ldr r1,[r2]
ldr r1,[r2,#0x4];不能超过0xfff,否侧编译不能通过或者linker时有错
ldr r1
,[r2,#label];所以这个经常是编译不能通过,因为label的值一般都大于0xfff
ldr r1
,[r2],#0x4
ldr r1
,label ;label这个地址里面的内容赋给r1
ldr伪指令
ldr r1
,=0x2000014
ldr r1
,=label ;label这个地址值赋给r1

    ARMRISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。比如想把数据从内存中某处读取到寄存器中,只能使用ldr
比如:ldr r0, 0x12345678
就是把0x12345678这个地址中的值存放到r0中。
mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。
x86中没有ldr这种指令,因为x86mov指令可以将数据从内存中移动到寄存器中。
另外还有一个就是ldr伪指令,虽然ldr伪指令和ARMldr指令很像,但是作用不太一样。ldr伪指令可以在立即数前加上=,以表示把一个地址写到某寄存器中,比如:
ldr r0, =0x12345678
这样,就把0x12345678这个地址写到r0中了。所以,ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。

================================================================================================

BX{<cond>} Rm
cond>为指令执行的条件码。当<cond>忽略时指令为无条件执行。
Rm>该寄存器中为跳转的目标地址。当<Rm>寄存器的bit[0]0,目标地址处的指令为ARM指令;
<Rm>寄存器的bit[0]1时,目标地址处的指令为Thumb指令。

ARM指令是字对齐(指令的地址后两位为[1:0]=0b00),Thumb是半字对齐(指令的地址后两位为[1:0]=0bx0,x01)。指令的地址的最后一位必为0

=================================================================================================

__main函数的作用

1.1  问题描述
     __main函数的作用是什么呀?
1.2  问题剖析
     __main函数是C/C++运行时库的一个函数,嵌入式系统在进入应用主程序之前必须有一个初始化的过程,使用__main标号引导系统时必须将应用程序的入口定义为main()
    在初始化的过程中,__main函数的作用主要有两点:
    (1)  完成对映像文件的初始化操作
     在介绍映像文件的初始化操作之前,先介绍以下几个概念:
     1.  映像文件
     链接器把多个目标文件链接成一个映像文件。
     2.  加载地址和执行地址
     映像文件可以有两种地址:加载地址和执行地址。加载地址是映像文件在存储器中的存储地址;执行地址就是映像文件运行时的地址。
     3.  加载域和执行域
     文件加载的存储区叫加载域,文件运行的存储区叫执行域。
     4.  从加载地址到执行地址
     在结构比较简单的系统中,加载地址就是执行地址;而在复杂系统中,程序运行前,常常会把映像文件的一部分或全部从存储区域移出去,此时执行地址就不再是加载地址。
     知道以上几个概念,__main函数对映像文件的初始操作就不难理解了。对于加载地址和执行地址不同的映像文件,__main函数会把加载地址的代码和数据复制到执行地址中,并且对被链接器指定为需要初始化为0段,进行清零操作。
     (2)  调用__rt_entry函数,进入用户程序。__rt_entry函数的运行流程如图 1.1所示。


 

 

LPC11C14(Cortex-m0--ARM7)启动代码分析 - www.3xlyw.net - xiebingsuccess的博客

  
 1.1  __rt_entry()函数中的运行情况

__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()。所以说,前者是库函数,后者就是我们自己编写的main()主函数;

==================================================================================================

 ALIGN    
    语法格式:    
    ALIGN { 表达式{ ,偏移量 }}    
    ALIGN 伪指令可通过添加填充字节的方式,使当前位置满足一定的对其方式 | 。其中,表达式的值用于指定对齐方式,可能的取值为 2 的幂,如1  2 4  8 16 等。若未指定表达式,则将当前位置对齐到下一个字的位置。偏移量也为一个数字表达式,若使用该字段,则当前位置的对齐方式为: 2 的表达式次幂+偏移量。    
    使用示例:    
    AREA Init CODE  READONLY ALIEN  3 ;指定后面的指令为8 字节对齐。    
    指令序列    
    END 

=================================================================================================

__user_initial_stackheap() 返回:

· r0 中的堆基址

· r1 中的堆栈基址,即堆栈区中的最高地址

· r2 中的堆限制

· r3 中的堆栈限制,即堆栈区中的最低地址。

================================================================================================

BX      LR

如果LR的值不是0xffffxxxx类型的,则PC跳至LR[31:1],而根据LR[0:0]则决定跳转后处理器进入的状态。如果LR[0:0]=1,则进入Thumb状态,否则进入ARM状态。 CM3中不支持ARM状态,所以LR[0:0]必须是1——也就是LR必须是奇数  
CM3中,如果以0xffff开头则有特殊的含义,命名为EXC_RETURN,它指示正在从异常返回,并决定返回的方式,在《Cortex-M3权威指南》中有重点介绍

================================================================================================

__initial_sp

通过定义一个等于堆栈顶部的符号 __initial_sp 来指定初始堆栈指针

 __initial_sp EQU 0x100000        ; equal to the top of the stack 

__heap_base

过分别定义符号 __heap_base  __heap_limit 来指定堆的开头和末尾。 完成后,您可以按通常方式使用堆函数。 

__heap_limit 

必须指向堆区中最后一个字节后面的字节。

 EXPORT __heap_base 
 __heap_base EQU 0x400000        ; equal to the start of the heap 
     EXPORT __heap_limit 
 __heap_limit EQU 0x800000       ; equal to the end of the heap 

===============================================================================================

;这个函数中,EXPORT  为符号导出,导出的符号须有相应的定义(符号地址),就像是C语言中的extern,extern某个函数或者某个变量,首先这个函数或变量需要有个实体后,才能导出。

Default_Handler PROC

 EXPORT  WAKEUP_IRQHandler         [WEAK]

                EXPORT  CAN_IRQHandler            [WEAK]
                         。。。。。。

WAKEUP_IRQHandler
CAN_IRQHandler

          。。。。。。

                     ENDP

 ===============================================================================================

Cortex-m3启动代码

启动代码文件名是STM32F10X.S,它的作用先总结下,然后再分析。启动代码作用一般是:1)堆和栈的初始化;2)向量表定义;3)地址重映射及中断向量表的转移;4)设置系统时钟频率;5)中断寄存器的初始化;6)进入C应用程序。
   (1)按启动代码的次序,先看堆和栈的初始化:
Stack_Size      EQU     0x00000200       ;定义Stack_Size为0x00000200
                AREA    STACK, NOINIT, READWRITE, ALIGN=3   ;定义栈,可初始为0,8字节对齐
Stack_Mem       SPACE   Stack_Size       ;分配0x200个连续字节,并初始化为0
__initial_sp   ;汇编代码地址标号
 
Heap_Size       EQU     0x00000000
                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

                PRESERVE8 ;指定当前文件堆栈8字节对齐
                THUMB     ;告诉汇编器下面是32位的Thumb指令,如果需要,汇编器将插入位以保证对齐
  (2)中断向量表定义
AREA    RESET, DATA, READONLY ;定义复位向量段,只读
                EXPORT  __Vectors   ;定义一个可以在其他文件中使用的全局标号。此处表示中断地址
__Vectors       DCD     __initial_sp              ; 给__initial_sp分配4字节32位的地址0x0
                DCD     Reset_Handler             ; 给标号Reset Handler分配地址为0x00000004
                DCD     NMI_Handler               ; 给标号NMI Handler分配地址0x00000008
                DCD     HardFault_Handler         ; Hard Fault Handler
                DCD     MemManage_Handler         ; MPU Fault Handler
                DCD     BusFault_Handler          ; Bus Fault Handler
                DCD     UsageFault_Handler        ; Usage Fault Handler
                DCD     0                         ; 这种形式就是保留地址,不给任何标号分配
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     0                         ; Reserved
                DCD     SVC_Handler               ; SVCall Handler
                DCD     DebugMon_Handler          ; Debug Monitor Handler
                DCD     0                         ; Reserved
                DCD     PendSV_Handler            ; PendSV Handler

                DCD     SysTick_Handler           ; SysTick Handler
                ; External Interrupts
                DCD     WWDG_IRQHandler           ; Window Watchdog
                DCD     PVD_IRQHandler            ; PVD through EXTI Line detect
                DCD     TAMPER_IRQHandler         ; Tamper
                DCD     RTC_IRQHandler            ; RTC
                DCD     FLASH_IRQHandler          ; Flash
                DCD     RCC_IRQHandler            ; RCC
                DCD     EXTI0_IRQHandler          ; EXTI Line 0
                DCD     EXTI1_IRQHandler          ; EXTI Line 1
                DCD     EXTI2_IRQHandler          ; EXTI Line 2
                DCD     EXTI3_IRQHandler          ; EXTI Line 3
                DCD     EXTI4_IRQHandler          ; EXTI Line 4
                DCD     DMAChannel1_IRQHandler    ; DMA Channel 1
                DCD     DMAChannel2_IRQHandler    ; DMA Channel 2
                DCD     DMAChannel3_IRQHandler    ; DMA Channel 3
                DCD     DMAChannel4_IRQHandler    ; DMA Channel 4
                DCD     DMAChannel5_IRQHandler    ; DMA Channel 5
                DCD     DMAChannel6_IRQHandler    ; DMA Channel 6
                DCD     DMAChannel7_IRQHandler    ; DMA Channel 7
                DCD     ADC_IRQHandler            ; ADC
                DCD     USB_HP_CAN_TX_IRQHandler  ; USB High Priority or CAN TX
                DCD     USB_LP_CAN_RX0_IRQHandler ; USB Low  Priority or CAN RX0
                DCD     CAN_RX1_IRQHandler        ; CAN RX1
                DCD     CAN_SCE_IRQHandler        ; CAN SCE
                DCD     EXTI9_5_IRQHandler        ; EXTI Line 9..5
                DCD     TIM1_BRK_IRQHandler       ; TIM1 Break
                DCD     TIM1_UP_IRQHandler        ; TIM1 Update
                DCD     TIM1_TRG_COM_IRQHandler   ; TIM1 Trigger and Commutation
                DCD     TIM1_CC_IRQHandler        ; TIM1 Capture Compare
                DCD     TIM2_IRQHandler           ; TIM2
                DCD     TIM3_IRQHandler           ; TIM3
                DCD     TIM4_IRQHandler           ; TIM4
                DCD     I2C1_EV_IRQHandler        ; I2C1 Event
                DCD     I2C1_ER_IRQHandler        ; I2C1 Error
                DCD     I2C2_EV_IRQHandler        ; I2C2 Event

                DCD     I2C2_ER_IRQHandler        ; I2C2 Error
                DCD     SPI1_IRQHandler           ; SPI1
                DCD     SPI2_IRQHandler           ; SPI2
                DCD     USART1_IRQHandler         ; USART1
                DCD     USART2_IRQHandler         ; USART2
                DCD     USART3_IRQHandler         ; USART3
                DCD     EXTI15_10_IRQHandler      ; EXTI Line 15..10
                DCD     RTCAlarm_IRQHandler       ; RTC Alarm through EXTI Line
                DCD     USBWakeUp_IRQHandler      ; USB Wakeup from suspend
  (3)中断向量表的转移
AREA    |.text|, CODE, READONLY ;代码段定义

; Reset Handler
Reset_Handler   PROC ;标记一个函数的开始
                EXPORT  Reset_Handler             [WEAK];[WEAK] 选项表示当所有的源文件都没有 定义这样一个标号时,编译器也不给出错误信息,在多数情况下将该标号置为 0 ,若该标号为 B 或 BL 指令引用,则将 B 或 BL指令置为 NOP 操作。EXPORT提示编译器该标号可以为外部文件引用。
                IMPORT  __main ;通知编译器要使用的标号在其他文件
                LDR     R0, =__main;使用“=”表示LDR目前是伪指令不是标准指令。这里是把__main的地址给RO。
                BX      R0;BX是ARM指令集和THUMB指令集之间程序的跳转
                ENDP

; Dummy Exception Handlers (infinite loops which can be modified)               
NMI_Handler     m  ;"m"其实就是PROC表示汇编函数的开始
                EXPORT  NMI_Handler               [WEAK]
                B       .
                ENDP
HardFault_Handler\      ;"\"是换行的意思
                PROC
                EXPORT  HardFault_Handler         [WEAK]
                B       . ;"."号到底是什么含义呢,目前还没查到资料。可能是保留地址,供以后修改的吧
                ENDP
MemManage_Handler\
                PROC
                EXPORT  MemManage_Handler         [WEAK]
                B       .
                ENDP

                BusFault_Handler\
                PROC
                EXPORT  BusFault_Handler          [WEAK]
                B       .
                ENDP
UsageFault_Handler\
                PROC
                EXPORT  UsageFault_Handler        [WEAK]
                B       .
                ENDP
SVC_Handler     PROC
                EXPORT  SVC_Handler               [WEAK]
                B       .
                ENDP
DebugMon_Handler\
                PROC
                EXPORT  DebugMon_Handler          [WEAK]
                B       .
                ENDP
PendSV_Handler  PROC
                EXPORT  PendSV_Handler            [WEAK]
                B       .
                ENDP
SysTick_Handler PROC
                EXPORT  SysTick_Handler           [WEAK]
                B       .
                ENDP
Default_Handler PROC
                EXPORT  WWDG_IRQHandler           [WEAK]
                EXPORT  PVD_IRQHandler            [WEAK]
                EXPORT  TAMPER_IRQHandler         [WEAK]
                EXPORT  RTC_IRQHandler            [WEAK]
                EXPORT  FLASH_IRQHandler          [WEAK]
                EXPORT  RCC_IRQHandler            [WEAK]
                EXPORT  EXTI0_IRQHandler          [WEAK]
                EXPORT  EXTI1_IRQHandler          [WEAK]

                EXPORT  EXTI2_IRQHandler          [WEAK]
                EXPORT  EXTI3_IRQHandler          [WEAK]
                EXPORT  EXTI4_IRQHandler          [WEAK]
                EXPORT  DMAChannel1_IRQHandler    [WEAK]
                EXPORT  DMAChannel2_IRQHandler    [WEAK]
                EXPORT  DMAChannel3_IRQHandler    [WEAK]
                EXPORT  DMAChannel4_IRQHandler    [WEAK]
                EXPORT  DMAChannel5_IRQHandler    [WEAK]
                EXPORT  DMAChannel6_IRQHandler    [WEAK]
                EXPORT  DMAChannel7_IRQHandler    [WEAK]
                EXPORT  ADC_IRQHandler            [WEAK]
                EXPORT  USB_HP_CAN_TX_IRQHandler  [WEAK]
                EXPORT  USB_LP_CAN_RX0_IRQHandler [WEAK]
                EXPORT  CAN_RX1_IRQHandler        [WEAK]
                EXPORT  CAN_SCE_IRQHandler        [WEAK]
                EXPORT  EXTI9_5_IRQHandler        [WEAK]
                EXPORT  TIM1_BRK_IRQHandler       [WEAK]
                EXPORT  TIM1_UP_IRQHandler        [WEAK]
                EXPORT  TIM1_TRG_COM_IRQHandler   [WEAK]
                EXPORT  TIM1_CC_IRQHandler        [WEAK]
                EXPORT  TIM2_IRQHandler           [WEAK]
                EXPORT  TIM3_IRQHandler           [WEAK]
                EXPORT  TIM4_IRQHandler           [WEAK]
                EXPORT  I2C1_EV_IRQHandler        [WEAK]
                EXPORT  I2C1_ER_IRQHandler        [WEAK]
                EXPORT  I2C2_EV_IRQHandler        [WEAK]
                EXPORT  I2C2_ER_IRQHandler        [WEAK]
                EXPORT  SPI1_IRQHandler           [WEAK]
                EXPORT  SPI2_IRQHandler           [WEAK]
                EXPORT  USART1_IRQHandler         [WEAK]
                EXPORT  USART2_IRQHandler         [WEAK]
                EXPORT  USART3_IRQHandler         [WEAK]
                EXPORT  EXTI15_10_IRQHandler      [WEAK]
                EXPORT  RTCAlarm_IRQHandler       [WEAK]
                EXPORT  USBWakeUp_IRQHandler      [WEAK]
WWDG_IRQHandler

PVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FLASH_IRQHandler
RCC_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMAChannel1_IRQHandler
DMAChannel2_IRQHandler
DMAChannel3_IRQHandler
DMAChannel4_IRQHandler
DMAChannel5_IRQHandler
DMAChannel6_IRQHandler
DMAChannel7_IRQHandler
ADC_IRQHandler
USB_HP_CAN_TX_IRQHandler
USB_LP_CAN_RX0_IRQHandler
CAN_RX1_IRQHandler
CAN_SCE_IRQHandler
EXTI9_5_IRQHandler
TIM1_BRK_IRQHandler
TIM1_UP_IRQHandler
TIM1_TRG_COM_IRQHandler
TIM1_CC_IRQHandler
TIM2_IRQHandler
TIM3_IRQHandler
TIM4_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler

USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
EXTI15_10_IRQHandler
RTCAlarm_IRQHandler
USBWakeUp_IRQHandler
                B       .
                ENDP
                ALIGN

  (4)堆和栈的初始化
IF      :DEF:__MICROLIB        ;“DEF”的用法——:DEF:X 就是说X定义了则为真,否则为假
                
                EXPORT  __initial_sp
                EXPORT  __heap_base
                EXPORT  __heap_limit
               
                ELSE
               
                IMPORT  __use_two_region_memory
                EXPORT  __user_initial_stackheap
__user_initial_stackheap
                LDR     R0, =  Heap_Mem
                LDR     R1, =(Stack_Mem + Stack_Size)
                LDR     R2, = (Heap_Mem +  Heap_Size)
                LDR     R3, = Stack_Mem
                BX      LR
                ALIGN ;填充字节使地址对齐
                ENDIF
END 
==============================================================================
“.”代表 address of current instruction 也就是当前指令地址
文中了(B   .)表示循环,有点像C里面的while(1);语句。

猜你喜欢

转载自blog.csdn.net/xiebingsuccess/article/details/88824643
今日推荐