ARM ContexM指令集

以下内容为STM32的启动文件所涉及到的指令

参考文档:ARM汇编指南v5.06

image-20230928160955674

ARM-ContexM系列启动文件深入解析 startup_stm32h750xx.s

注释部分

;********************************************************************************
;* File Name          : startup_stm32h750xx.s
;* @author  MCD Application Team
;* Description        : STM32H7xx devices vector table for MDK-ARM toolchain. 
;*                      This module performs:
;*                      - Set the initial SP
;*                      - Set the initial PC == Reset_Handler
;*                      - Set the vector table entries with the exceptions ISR address
;*                      - Branches to __main in the C library (which eventually
;*                        calls main()).
;*                      After Reset the Cortex-M processor is in Thread mode,
;*                      priority is Privileged, and the Stack is set to Main.
;* <<< Use Configuration Wizard in Context Menu >>>   
;******************************************************************************
;* @attention
;*
;* Copyright (c) 2018 STMicroelectronics.
;* All rights reserved.
;*
;* This software is licensed under terms that can be found in the LICENSE file
;* in the root directory of this software component.
;* If no LICENSE file comes with this software, it is provided AS-IS.
;*
;*******************************************************************************

; Amount of memory (in bytes) allocated for Stack
; Tailor this value to your application needs
; <h> Stack Configuration
;   <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

以上为启动文件的注释部分

这个文件包含了为 MDK-ARM 工具链中的 STM32H7xx 设备准备的向量表。

  • 设置初始堆栈指针(SP)。
  • 设置初始程序计数器(PC),其值等于 Reset_Handler
  • 使用异常的 ISR 地址设置向量表条目。
  • 分支到 C 库中的 __main(最终会调用 main() 函数)。

处理器状态描述

复位后,Cortex-M 处理器处于线程模式,优先级为特权级,堆栈设置为主堆栈。

Stack_Size        EQU     0x400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp

EQU指令

通过查找可查看该指令的使用方法

image-20230928161439270

该指令 为数字常亮,寄存器相对值或者PC相对值提供符号名称,以上还提供了语法以及示例代码片

使用EQU定义常量,类似于C语言中的 #define

Stack_Size        EQU     0x400

所以以上汇编语句解释为:定义一个常量Stack_Size 并赋值 0x400 (1024)

指定栈空间大小

AREA指令

AREA指令指示汇编程序汇编一个新的代码段或数据段

语法

image-20230928162708313

sectionname 该部分名称:

arrt可选项较多,这里只展示用到的:

image-20230928163140923

NOINIT解释为 该数据段未初始化的或被初始化为零

image-20230928163459769

READWRITE 解释为 该部分可读可写,这是代码区域的默认设置

image-20230928163708707

ALIGN=expression 解释为 用于指定该区域(或称为 section)的对齐边界,默认情况下,ELF(可执行与可链接格式)的 section 是按照四字节边界对齐的。expression 可以是从 0 到 31 的任何整数值。

例如,如果 expression 是 10,那么 section 将会按照 2的10次方=1024 字节,即 1KB 的边界进行对齐。

我们这里是ALIGN=3 即8字节对齐

ALIGN=expression 属性允许开发者指定内存区域的对齐边界,这对于优化内存访问和满足特定硬件要求是非常重要的。

注意:注意事项:

  • 对于 ARM 指令 section,不应使用 ALIGN=0ALIGN=1
  • 对于 Thumb 指令 section,不应使用 ALIGN=0
AREA    STACK, NOINIT, READWRITE, ALIGN=3

所以以上代码可解释为:STACK 区域是未初始化的或被初始化为零,这个区域是可读写的,并且其起始地址是 8 字节对齐的。

SPACE指令

该指令保留一个归零的内存快。

image-20230928164741914

{label} SPACE expr

label :可选标签 expr 要填充的字节数或0

Stack_Mem       SPACE   Stack_Size

以上代码解释为:在内存中为堆栈预留空间的。Stack_Size这是一个之前用 EQU 定义的常量,表示要预留的空间的大小,单位是字节。

__initial_sp

  • 这是一个标签,表示初始堆栈指针的位置。在这个例子中,由于 ARM Cortex-M 架构使用的是全降序堆栈,__initial_sp 通常会被设置为堆栈区域的末尾。(栈顶,向下生长)

为什么要这么写?

  • 在 ARM Cortex-M 微控制器中,__initial_sp 的值会被放在向量表的第一个位置,即启动地址 0x00000000 处。
  • 当微控制器复位时,这个值会被自动加载到堆栈指针寄存器(SP)中,以初始化堆栈。
  • 通过这种方式,__initial_sp 为程序提供了一个初始的、有效的堆栈空间,这对于程序的运行是至关重要的。

因此,__initial_sp 的定义方式是基于 ARM 架构的工作原理和堆栈的使用方式。这样的定义方式确保了程序能够在启动时获得一个正确初始化的堆栈指针。

__heap_base

__heap_limit

Heap_Size      EQU     0x200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

以上所涉及到的指令与初始化堆栈指令相同,应该可以理解是什么作用,这里不同点在于为什么要这样写

__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit
  1. __heap_base

    • 这是一个标签,表示堆区域的开始地址。
  2. Heap_Mem SPACE Heap_Size

    • HEAP 区域中预留了 Heap_Size 0x200(512字节)的空间。Heap_Mem 是这块空间的开始地址。
  3. __heap_limit

    • 这是一个标签,表示堆区域的结束地址。

为什么要这么写?

  • __heap_base__heap_limit 用于定界堆区域。这两个标签分别表示堆的开始和结束,它们可以被运行时环境用来管理堆内存的分配和释放。
  • Heap_Mem SPACE Heap_Size 用于实际预留堆内存。这块内存可以被动态分配给程序中需要动态分配内存的部分,例如,通过 malloc 函数分配的内存。
  • 在运行时,堆管理器会使用 __heap_base__heap_limit 来确定堆的边界,以便知道哪些地址可以安全地分配,以及何时堆已满。

这样的组织方式为运行时环境提供了必要的信息,以正确、有效地管理堆内存。

为什么 __initial_sp 不需要像 __heap_base__heap_limit 那样定义:

  • 堆栈是全降序的,所以 __initial_sp 通常会被设置为堆栈区域的末尾。这意味着,不需要像 __heap_limit 那样单独标记堆栈的结束位置。
  • 堆栈的开始位置(即堆栈的底部)通常是固定的,由 AREA 定义的区域和 SPACE 伪指令决定,所以也不需要像 __heap_base 那样单独标记堆栈的开始位置。

PRESERVE8指令

THUMB 指令

image-20230928171449363

PRESERVE8 指令用于指定当前文件是否保持堆栈的八字节对齐。

  • 如果代码保持八字节对齐的堆栈,应使用 PRESERVE8PRESERVE8 {TRUE} 来设置 PRES8 构建属性。
  • 如果代码不保持八字节对齐的堆栈,应使用 PRESERVE8 {FALSE} 来确保不设置 PRES8 构建属性。

PRESERVE8 ; 表示当前文件保持堆栈的八字节对齐

PRESERVE8 {FALSE} ; 表示当前文件不保持堆栈的八字节对齐

THUMB

image-20230928172212740

ARM指令集分为 32位的ARM指令,16位的THUMB指令,以及后来的既支持32位又支持16位的THUMB 2指令

THUMB 指令在 ARM 汇编中用于声明使用 Thumb 指令集,但它不特指 Thumb-2。

           PRESERVE8
            THUMB

AREA指令扩展

image-20230928173109599

定义的内存区域的名称 RESET

DATA 这表示该区域用于存储数据。DATA 区域通常用于存储初始化的数据。

image-20230928173446724

READONLY指示该区域不能被写入,只是可读的

image-20230928173655391

      AREA    RESET, DATA, READONLY

以上解释为:定义了一个名为 RESET 的只读数据区域。这个区域通常会包含程序的启动代码和其他不会在运行时被修改的数据。

EXPORT指令

EXPORT 指令用于声明一个符号(例如变量或函数)是全局的,这意味着这个符号可以被其他的源文件访问和使用。

image-20230928174503355

            EXPORT  __Vectors
            EXPORT  __Vectors_End
            EXPORT  __Vectors_Size
  1. EXPORT __Vectors

    • 这声明了一个名为 __Vectors 的全局符号。通常,这个符号代表了向量表的开始位置。向量表通常包含了一系列的地址,这些地址指向了处理器异常和中断的处理函数。
  2. EXPORT __Vectors_End

    • 这声明了一个名为 __Vectors_End 的全局符号。这个符号通常代表了向量表的结束位置。
  3. EXPORT __Vectors_Size

    • 这声明了一个名为 __Vectors_Size 的全局符号。这个符号通常用于表示向量表的大小。

用途:

  • __Vectors

    • 其他模块或链接器脚本可能会使用这个符号来定位向量表的开始位置。
  • __Vectors_End__Vectors_Size

    • 这些符号可以被用来计算向量表的大小,或者用于确保其他数据和代码不会与向量表重叠。

总结:

通过 EXPORT 指令导出的这些符号,允许其他源文件、模块或链接器脚本访问向量表的相关位置和信息,这对于系统的配置和运行是至关重要的。

DCD指令

DCD指令分配一个或多个内存字,按四字节边界对齐,并定义内存初始运行时候的内容

image-20230928182400487

__Vectors       DCD     __initial_sp                      ; Top of Stack
                DCD     Reset_Handler                     ; Reset Handler
                DCD     NMI_Handler                       ; NMI Handler
                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                                 ; Reserved
                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 interrupt ( wwdg1_it)                                         
                DCD     PVD_AVD_IRQHandler                ; PVD/AVD through EXTI Line detection                        
                DCD     TAMP_STAMP_IRQHandler             ; Tamper and TimeStamps through the EXTI line            
                DCD     RTC_WKUP_IRQHandler               ; RTC Wakeup through the EXTI line                       
                DCD     FLASH_IRQHandler                  ; FLASH                                         
                ……………………………………
                省略内容
                ……………………………………
                DCD     LPUART1_IRQHandler                ; LP UART1 interrupt                                                
                DCD     0                                 ; Reserved                                                                              
                DCD     CRS_IRQHandler                    ; Clock Recovery Global Interrupt                                   
                DCD     ECC_IRQHandler                    ; ECC diagnostic Global Interrupt                                              
                DCD     SAI4_IRQHandler                   ; SAI4 global interrupt                                                
                DCD     0                                 ; Reserved                                 
                DCD     0                                 ; Reserved                                    
                DCD     WAKEUP_PIN_IRQHandler             ; Interrupt for all 6 wake-up pins 
                

__Vectors_End
  • __Vectors:向量表的标签,表示向量表的开始位置。
  • __Vectors_End向量表的标签,表示向量表的结束位置。
  • DCD:用于定义一个 32 位的字(word)。它用于在内存中存储一个 32 位的值。
  • __initial_sp:表示初始堆栈指针的值
  • 各种 Handler

    • DCD Reset_HandlerDCD SysTick_Handler 是 Cortex-M 处理器的系统异常处理函数。
    • DCD 0 ; Reserved 表示这些位置是保留的,没有分配给任何异常处理函数。
    • DCD WWDG_IRQHandlerDCD WAKEUP_PIN_IRQHandler 是特定于设备的外部中断处理函数。

这段代码主要定义了 ARM Cortex-M 微控制器的向量表,包括系统异常、保留位置和外部中断。向量表的每个条目都包含了一个指向相应处理函数的地址。这些地址在复位时被加载,以及在相应的异常或中断发生时被调用。

异常与架构相关通常不会变,中断由厂商设计,同种内核可能存在差异

AREA指令扩展

EQU指令前面已经提到过

AREA这里的CODE

image-20230928185003580

__Vectors_Size  EQU  __Vectors_End - __Vectors

                AREA    |.text|, CODE, READONLY

这段代码定义了向量表的大小,并且定义了一个新的代码区域。下面是对这段代码的详细解释:

1. __Vectors_Size EQU __Vectors_End - __Vectors

  • __Vectors_Size 是一个符号,用 EQU 定义为 __Vectors_End - __Vectors 的值。
  • __Vectors_End__Vectors 分别是向量表的结束和开始位置的标签。
  • 所以,__Vectors_Size 代表了向量表的大小(以字节为单位)。

2. AREA |.text|, CODE, READONLY

  • AREA 指令用于定义一个新的内存区域。
  • |.text| 是这个区域的名称。这个名称通常用于存放程序的代码部分。
  • CODE 表示这个区域用于存放代码。
  • READONLY 表示这个区域是只读的,通常代码区域都是只读的,以防止程序在运行时修改自己的代码。

总结

这段代码计算了向量表的大小,并且定义了一个名为 .text 的只读代码区域。这是 ARM 汇编语言程序组织内存和代码的一种常见方式。

看下面的代码片,指令较多,详解如下

; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler                    [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

PROC指令

image-20230928190017232

image-20230928190043169

PROC指令标记着函数的开始

Reset_Handler    PROC
    ; 这里是 Reset_Handler 过程的体,包含了一系列汇编指令
ENDP

EXPORT指令

前面提过这个指令

       EXPORT  Reset_Handler                    [WEAK]

image-20230928191058139

  • EXPORT 指令用于将 Reset_Handler 符号导出,使其成为一个全局符号。这意味着 Reset_Handler 可以在其他汇编或 C 文件中被访问或链接。
  • Reset_Handler 通常是程序启动时执行的第一个函数,它负责进行一些初始化操作。
  • [WEAK] 属性指定 Reset_Handler 为一个弱符号。
  • 弱符号允许用户在其他地方重新定义。如果存在同名的强符号,那么链接器会使用那个强符号的定义,而忽略弱符号的定义。
  • 如果没有其他地方定义同名的强符号,那么链接器会使用这个弱符号的定义。
  • 在嵌入式系统中,Reset_Handler 通常是在复位时由硬件调用的第一个函数,它会进行一些必要的系统初始化。
  • 通过将 Reset_Handler 定义为弱符号,用户可以在他们自己的代码中提供 Reset_Handler 的一个不同实现,以覆盖默认的实现。

示例:

如果你有一个默认的 Reset_Handler 实现,但在某些情况下,你想要提供一个不同的实现,你可以在另一个文件中定义一个同名的强符号,这个新定义会覆盖原来弱符号的定义。

IMPORT指令

 IMPORT  SystemInit
 IMPORT  __main

image-20230928192045703

IMPORT 指令用于导入在其他模块或文件中定义的符号。这允许你在当前模块中引用这些符号,而不需要知道它们的具体实现或地址。

IMPORT SystemInit

  • 这条指令导入了一个名为 SystemInit 的符号。SystemInit 通常是一个函数,用于进行系统初始化,例如配置系统时钟、初始化 RAM 等。
  • SystemInit 的具体实现可能在其他的汇编文件或 C 文件中,但可以在当前模块中被调用。

IMPORT __main

  • 这条指令导入了一个名为 __main 的符号。__main 通常是 C 运行时库提供的一个函数,它负责初始化 C 环境(例如初始化全局变量),然后调用 main 函数。
  • __main 的具体实现是由 C 运行时库提供的,用户通常不需要关心其实现细节。

LDR指令

image-20230928192706939

LDR有多重用法,这里只解释用到的这种

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0

LDR R0, =SystemInit 这条指令有一个特殊的用法。这条指令的意图是将 SystemInit 的地址加载到寄存器 R0 中。

  • LDR:是 Load Register 的缩写,用于从内存中加载数据到寄存器。
  • R0:是目标寄存器,用于存储加载的数据。
  • =SystemInit:这里的等号 = 表示取 SystemInit 的地址。SystemInit 通常是一个函数,这里是获取这个函数的地址。

实际操作:

实际上,由于 LDR 指令通常用于从内存地址加载数据,直接加载一个符号地址(如函数地址)需要一些额外的步骤。汇编器会进行一些处理来实现这一点:

  1. 汇编器会在某个地方存储 SystemInit 的地址值。
  2. LDR R0, =SystemInit 会被转换为加载该地址值到 R0 寄存器的操作。

示例:

假设 SystemInit 函数的地址是 0x08004000,那么汇编器和链接器会处理为类似以下的操作:

LDR R0, =0x08004000  ; 将 SystemInit 函数的地址加载到 R0 寄存器中

用途:

这种用法通常出现在需要调用函数或者访问变量的场合。,在启动文件中,需要调用 SystemInit 函数来进行一些硬件的初始化操作。在这种情况下,使用 LDR R0, =SystemInit 来获取 SystemInit 函数的地址,然后通过 BLX R0 或其他跳转指令来调用该函数。

这里一样就不解释了

LDR     R0, =__main

BLX、BX指令

                   LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0

BLX (Branch with Link and Exchange) 指令:

  • 作用BLX 指令用于调用函数,并且可以在 ARM 和 Thumb 指令集之间切换。它会将返回地址(下一条要执行的指令的地址)保存到链接寄存器 LR 中。
  • 特点BLX 可以实现状态切换,即从 ARM 状态跳转到 Thumb 状态,或从 Thumb 状态跳转到 ARM 状态。

BX (Branch and Exchange) 指令:

  • 作用BX 指令用于实现分支跳转,并且可以在 ARM 和 Thumb 指令集之间切换。但与 BLX 不同,BX 不会保存返回地址。
  • 特点BX 通常用于实现函数返回操作,它可以从一个状态的函数返回到另一个状态的调用者。
  • BLX 用于函数调用,会保存返回地址到 LR 寄存器,并可以在 ARM 和 Thumb 之间切换。
  • BX 用于分支跳转,通常用于函数返回,不会保存返回地址,并可以在 ARM 和 Thumb 之间切换。

LDR R0, =SystemInit

这条指令将 SystemInit 函数的地址加载到 R0 寄存器中。

BLX R0

这条指令使用 BLX 来调用 SystemInit 函数。BLX 指令会将下一条指令的地址(即 LDR R0, =__main 的地址)保存到链接寄存器 LR 中,然后跳转到 R0 寄存器中的地址(即 SystemInit 函数的地址)执行。如果 SystemInit 是 Thumb 模式的代码,而当前是 ARM 模式,那么还会进行状态切换。

SystemInit 函数通常用于进行一些系统初始化的工作,例如配置时钟、初始化内存等。

LDR R0, =__main

SystemInit 函数执行完毕并返回后,这条指令将 __main 函数的地址加载到 R0 寄存器中。__main 是由 C 运行时库提供的,它会进行一些额外的初始化工作,然后调用 main 函数。

BX R0

最后,BX R0 指令用于跳转到 R0 寄存器中的地址(即 __main 函数的地址)执行。如果 __main 是 Thumb 模式的代码,而当前是 ARM 模式,那么还会进行状态切换。BX 指令不会保存返回地址,因为在这个上下文中,__main 函数通常不会返回。

ENDP指令

ENDP 指令用于标记一个过程(Procedure)或函数(Function)的结束。它与 PROC 指令配对使用。

Function_Name    PROC
    ; 函数或过程的具体指令
                 ENDP
NMI_Handler     PROC
                EXPORT  NMI_Handler                      [WEAK]
                B       .
                ENDP
HardFault_Handler\
                PROC
                EXPORT  HardFault_Handler                [WEAK]
                B       .
                ENDP

以上的B .没有遇到过

B .

这是一个无限循环,B . 指令表示无条件跳转到当前地址,即创建了一个无限循环。这通常用于异常处理函数中,当异常发生时,如果没有提供具体的处理逻辑,程序会停在这个无限循环中。

Default_Handler PROC                                      

                EXPORT  WWDG_IRQHandler                   [WEAK]                                       
                EXPORT  PVD_AVD_IRQHandler                [WEAK]      
                
                EXPORT  TAMP_STAMP_IRQHandler             [WEAK]   
                
                ……………………
                省略部分
                ……………………
WWDG_IRQHandler   
                
PVD_AVD_IRQHandler   

TAMP_STAMP_IRQHandler    

WAKEUP_PIN_IRQHandler

                B       .

                ENDP

                ALIGN
  • 为这些处理程序(例如 WWDG_IRQHandler)提供自定义的处理逻辑,您可以在您的代码中定义一个同名的函数。由于这个新定义没有 [WEAK] 属性,它将覆盖原来的弱定义。
  • 如果不提供自定义的处理程序,那么当相关的中断发生时,控制权将转到 Default_Handler,并在 B . 指令处无限循环。

ALIGN指令

ALIGN 指令在 ARM 汇编中用于将当前位置对齐到指定的边界。

image-20230928202845242

image-20230928202902497

image-20230928202915796

这个指令会确保接下来的代码或数据从一个四字节边界开始。如果当前位置已经是四字节对齐的,那么 ALIGN 指令不会有任何效果。如果当前位置不是四字节对齐的,那么会在当前位置和下一个四字节边界之间插入适量的填充字节

;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
                 IF      :DEF:__MICROLIB
                
                 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
 IF      :DEF:__MICROLIB
      ………………
      ………………
 ELSE   
 
      ………………
      ………………

这一行是一个条件编译指令,它会检查是否定义了 __MICROLIB 符号。进行条件选择

:DEF: 是一个条件符号,用于检查一个符号是否被定义

在keil中表现为

image-20230928203631151

IF      :DEF:__MICROLIB
                
                 EXPORT  __initial_sp
                 EXPORT  __heap_base
                 EXPORT  __heap_limit
                
                 ELSE
                
                 IMPORT  __use_two_region_memory
                 EXPORT  __user_initial_stackheap

勾选之后执行

 EXPORT  __initial_sp
 EXPORT  __heap_base
 EXPORT  __heap_limit

EXPORT 指令用于声明符号,使得这些符号可以被其他模块访问。这里,如果 __MICROLIB 被定义,那么 __initial_sp__heap_base__heap_limit 这三个符号会被导出。

否则执行

 IMPORT  __use_two_region_memory
 EXPORT  __user_initial_stackheap
  • IMPORT 指令用于声明在其他模块中定义的符号。这里,如果 __MICROLIB 没有被定义,那么会导入 __use_two_region_memory 符号。
  • 同时,__user_initial_stackheap 符号会被导出。

__use_two_region_memory__user_initial_stackheap 解释 :

相关链接:https://developer.arm.com/documentation/dui0475/i/CJAGBBEG

__use_two_region_memory__user_initial_stackheap 是 ARM 编译器和运行时系统中的特殊符号,它们与内存管理和系统初始化有关。

__use_two_region_memory

这个符号通常与 ARM 的两区域内存模型相关。在两区域内存模型中,栈和堆是分开的,它们各自占用内存的不同区域。如果系统配置为使用这种模型,那么 __use_two_region_memory 符号通常会被定义。这个符号主要用于编译时,帮助编译器理解内存模型的配置,以生成正确的代码。

__user_initial_stackheap

__user_initial_stackheap 是一个用户可以提供的函数,用于初始化和配置栈和堆的内存区域。当 ARM 运行时系统启动时,它会调用这个函数来设置栈和堆的初始位置和大小。这个函数通常会返回一个结构,其中包含了栈和堆的初始位置和大小信息。

这个函数通常在系统不使用 MicroLib 时被定义和使用。MicroLib 是 ARM 提供的一个小型 C 库,适用于资源受限的嵌入式系统。

标号__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
  • LDR R0, =Heap_Mem:将 Heap_Mem 的地址加载到 R0 寄存器中。Heap_Mem 是堆的起始地址。
  • LDR R1, =(Stack_Mem + Stack_Size):将 Stack_Mem + Stack_Size 的结果加载到 R1 寄存器中。这是栈的结束地址。
  • LDR R2, = (Heap_Mem + Heap_Size):将 Heap_Mem + Heap_Size 的结果加载到 R2 寄存器中。这是堆的结束地址。
  • LDR R3, = Stack_Mem:将 Stack_Mem 的地址加载到 R3 寄存器中。Stack_Mem 是栈的起始地址。
  • BX LR 指令用于从当前子程序返回。LR(Link Register)通常存储着返回地址。
  • ALIGN 确保四字节对其
  • ENDIF:标志着一个 IF 条件编译块的结束。
  • END:标志着汇编文件的结束。
如果觉得我的文章对你有用,请随意赞赏