啓動檔案使用的 ARM 彙編指令彙總
1. Stack—棧
Stack_Size EQU 0x00000800
AREA STACK, NOINIT, READWRITE, ALIGN = 3
Stack_Mem SPACE Stack_Size
__initial_sp
|
開闢棧的大小爲 0X00000800(2KB),名字爲 STACK, NOINIT 即不初始化,可讀可寫, 8(2^3)位元組對齊。
棧的作用是用於區域性變數,函數呼叫,函數形參等的開銷,棧的大小不能超過內部SRAM 的大小。如果編寫的程式比較大,定義的區域性變數很多,那麼就需要修改棧的大小。如果某一天,你寫的程式出現了莫名奇怪的錯誤,並進入了硬 fault 的時候,這時你就要考慮下是不是棧不夠大,溢位了。
EQU:宏定義的僞指令,相當於等於,類似與 C 中的 define。
AREA:告訴彙編器彙編一個新的程式碼段或者數據段。 STACK 表示段名,這個可以任意命名; NOINIT 表示不初始化; READWRITE 表示可讀可寫, ALIGN=3,表示按照 2^3對齊,即 8 位元組對齊。
SPACE:用於分配一定大小的記憶體空間,單位爲位元組。這裏指定大小等於 Stack_Size。
標號__initial_sp 緊挨着 SPACE 語句放置,表示棧的結束地址,即棧頂地址,棧是由高向低生長的。
2. Heap 堆
Heap_Size EQU 0x00000400
AREA HEAP, NOINIT, READWRITE, ALIGN = 3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
|
開闢堆的大小爲 0X00000400(512 位元組),名字爲 HEAP, NOINIT 即不初始化,可讀可寫, 8(2^3)位元組對齊。 __heap_base 表示對的起始地址, __heap_limit 表示堆的結束地址。堆是由低向高生長的,跟棧的生長方向相反。
堆主要用來動態記憶體的分配,像 malloc()函數申請的記憶體就在堆上面。這個在 GD32裏面用的比較少。
PRESERVE8: 指定當前檔案的堆疊按照 8 位元組對齊。
THUMB:表示後面指令相容 THUMB 指令。 THUBM 是 ARM 以前的指令集, 16bit,現在 Cortex-M 系列的都使用 THUMB-2 指令集, THUMB-2 是 32 位的,相容 16 位和 32 位的指令,是 THUMB 的超集。
3. 向量表
; /* reset Vector Mapped to at Address 0 */
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size |
定義一個數據段,名字爲 RESET,可讀。並宣告 __Vectors、 __Vectors_End 和__Vectors_Size 這三個標號具有全域性屬性,可供外部的檔案呼叫。
EXPORT: 宣告一個標號可被外部的檔案使用,使標號具有全域性屬性。如果是 IAR 編譯器,則使用的是 GLOBAL 這個指令。
當內核響應了一個發生的異常後,對應的異常服務例程(ESR)就會執行。爲了決定 ESR的入口地址, 內核使用了―向量表查表機制 機製‖。這裏使用一張向量表。向量表其實是一個WORD(32 位整數)陣列,每個下標對應一種異常,該下標元素的值則是該 ESR 的入口地址。向量表在地址空間中的位置是可以設定的,通過 NVIC 中的一個重定位暫存器來指出向量表的地址。在復位後,該暫存器的值爲 0。因此,在地址 0 (即 FLASH 地址 0) 處必須包含一張向量表,用於初始時的異常分配。要注意的是這裏有個另類: 0 號型別並不是什麼入口地址,而是給出了復位後 MSP 的初值。
__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 handler */
DCD WWDGT_IRQHandler ; 16:Window Watchdog Timer
DCD LVD_IRQHandler ; 17:LVD through EXTI Line detect
DCD TAMPER_IRQHandler ; 18:Tamper Interrupt
DCD RTC_IRQHandler ; 19:RTC through EXTI Line
DCD FMC_IRQHandler ; 20:FMC
DCD RCU_IRQHandler ; 21:RCU
DCD EXTI0_IRQHandler ; 22:EXTI Line 0
DCD EXTI1_IRQHandler ; 23:EXTI Line 1
DCD EXTI2_IRQHandler ; 24:EXTI Line 2
DCD EXTI3_IRQHandler ; 25:EXTI Line 3
DCD EXTI4_IRQHandler ; 26:EXTI Line 4
DCD DMA0_Channel0_IRQHandler ; 27:DMA0 Channel 0
DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel 1
DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel 2
DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel 3
DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel 4
DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel 5
DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel 6
DCD ADC0_1_IRQHandler ; 34:ADC0 and ADC1
DCD CAN0_TX_IRQHandler ; 35:CAN0 TX
DCD CAN0_RX0_IRQHandler ; 36:CAN0 RX0
DCD CAN0_RX1_IRQHandler ; 37:CAN0 RX1
DCD CAN0_EWMC_IRQHandler ; 38:CAN0 EWMC
DCD EXTI5_9_IRQHandler ; 39:EXTI Line 5 to EXTI Line 9
DCD TIMER0_BRK_TIMER8_IRQHandler ; 40:TIMER0 Break and TIMER8 global
DCD TIMER0_UP_TIMER9_IRQHandler ; 41:TIMER0 Update and TIMER9 global
DCD TIMER0_TRG_CMT_TIMER10_IRQHandler ; 42:TIMER0 Trigger and Commutation and TIMER10 global
DCD TIMER0_Channel_IRQHandler ; 43:TIMER0 Channel Capture Compare
DCD TIMER1_IRQHandler ; 44:TIMER1
DCD TIMER2_IRQHandler ; 45:TIMER2
DCD TIMER3_IRQHandler ; 46:TIMER3
DCD I2C0_EV_IRQHandler ; 47:I2C0 Event
DCD I2C0_ER_IRQHandler ; 48:I2C0 Error
DCD I2C1_EV_IRQHandler ; 49:I2C1 Event
DCD I2C1_ER_IRQHandler ; 50:I2C1 Error
DCD SPI0_IRQHandler ; 51:SPI0
DCD SPI1_IRQHandler ; 52:SPI1
DCD USART0_IRQHandler ; 53:USART0
DCD USART1_IRQHandler ; 54:USART1
DCD USART2_IRQHandler ; 55:USART2
DCD EXTI10_15_IRQHandler ; 56:EXTI Line 10 to EXTI Line 15
DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm through EXTI Line
DCD USBFS_WKUP_IRQHandler ; 58:USBFS WakeUp from suspend through EXTI Line
DCD TIMER7_BRK_TIMER11_IRQHandler ; 59:TIMER7 Break Interrupt and TIMER11 global
DCD TIMER7_UP_TIMER12_IRQHandler ; 60:TIMER7 Update Interrupt and TIMER12 global
DCD TIMER7_TRG_CMT_TIMER13_IRQHandler ; 61:TIMER7 Trigger and Commutation Interrupt and TIMER13
DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare
DCD ADC2_IRQHandler ; 63:ADC2
DCD EXMC_IRQHandler ; 64:EXMC
DCD SDIO_IRQHandler ; 65:SDIO
DCD TIMER4_IRQHandler ; 66:TIMER4
DCD SPI2_IRQHandler ; 67:SPI2
DCD UART3_IRQHandler ; 68:UART3
DCD UART4_IRQHandler ; 69:UART4
DCD TIMER5_IRQHandler ; 70:TIMER5
DCD TIMER6_IRQHandler ; 71:TIMER6
DCD DMA1_Channel0_IRQHandler ; 72:DMA1 Channel0
DCD DMA1_Channel1_IRQHandler ; 73:DMA1 Channel1
DCD DMA1_Channel2_IRQHandler ; 74:DMA1 Channel2
DCD DMA1_Channel3_IRQHandler ; 75:DMA1 Channel3
DCD DMA1_Channel4_IRQHandler ; 76:DMA1 Channel4
DCD ENET_IRQHandler ; 77:Ethernet
DCD ENET_WKUP_IRQHandler ; 78:Ethernet Wakeup through EXTI line
DCD CAN1_TX_IRQHandler ; 79:CAN1 TX
DCD CAN1_RX0_IRQHandler ; 80:CAN1 RX0
DCD CAN1_RX1_IRQHandler ; 81:CAN1 RX1
DCD CAN1_EWMC_IRQHandler ; 82:CAN1 EWMC
DCD USBFS_IRQHandler ; 83:USBFS
DCD 0 ; 84:Reserved
DCD DMA1_Channel5_IRQHandler ; 85:DMA1 Channel5
DCD DMA1_Channel6_IRQHandler ; 86:DMA1 Channel6
DCD USART5_IRQHandler ; 87:USART5
DCD I2C2_EV_IRQHandler ; 88:I2C2 Event
DCD I2C2_ER_IRQHandler ; 89:I2C2 Error
DCD 0 ; 90:Reserved
DCD 0 ; 91:Reserved
DCD 0 ; 92:Reserved
DCD 0 ; 93:Reserved
DCD DCI_IRQHandler ; 94:DCI
DCD CAU_IRQHandler ; 95:CAU
DCD HAU_TRNG_IRQHandler ; 96:HAU and TRNG
DCD 0 ; 97:Reserved
DCD UART6_IRQHandler ; 98:UART6
DCD UART7_IRQHandler ; 99:UART7
DCD 0 ; 100:Reserved
DCD 0 ; 101:Reserved
DCD 0 ; 102:Reserved
DCD 0 ; 103:Reserved
DCD TLI_IRQHandler ; 104:TLI
DCD TLI_ER_IRQHandler ; 105:TLI error
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
|
__Vectors 爲向量表起始地址, __Vectors_End 爲向量表結束地址,兩個相減即可算出向量表大小。
向量表從 FLASH 的 0 地址開始放置,以 4 個位元組爲一個單位,地址 0 存放的是棧頂地址, 0X04 存放的是復位程式的地址,以此類推。從程式碼上看,向量表中存放的都是中斷服務函數的函數名,可我們知道 C 語言中的函數名就是一個地址。
DCD:分配一個或者多個以字爲單位的記憶體,以四位元組對齊,並要求初始化這些記憶體。在向量表中, DCD 分配了一堆記憶體,並且以 ESR 的入口地址初始化它們。
4. 復位程式
AREA |.text|, CODE, READONLY |
定義一個名稱爲.text 的程式碼段,可讀。
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP |
復位子程式是系統上電後第一個執行的程式,呼叫 SystemInit 函數初始化系統時鐘,然後呼叫 C 庫函數_mian,最終呼叫 main 函數去到 C 的世界。
WEAK:表示弱定義,如果外部檔案優先定義了該標號則首先參照該標號,如果外部檔案沒有宣告也不會出錯。這裏表示覆位子程式可以由使用者在其他檔案重新實現,這裏並不是唯一的。
IMPORT:表示該標號來自外部檔案,跟 C 語言中的 EXTERN 關鍵字類似。這裏表示 SystemInit 和__main 這兩個函數均來自外部的檔案。
SystemInit()是一個標準的庫函數,在 system_gd32f20x.c這個庫檔案總定義。主要作用是設定系統時鐘,這裏呼叫這個函數之後,微控制器的系統時鐘配被設定爲 120M。__main 是一個標準的 C 庫函數,主要作用是初始化使用者堆疊,並在函數的最後呼叫main 函數去到 C 的世界。這就是爲什麼我們寫的程式都有一個 main 函數的原因。
LDR、 BLX、 BX 是 CM4 內核的指令,具體作用見下表:
5. 中斷服務程式
在啓動檔案裏面已經幫我們寫好所有中斷的中斷服務函數,跟我們平時寫的中斷服務函數不一樣的就是這些函數都是空的,真正的中斷復服務程式需要我們在外部的 C 檔案裏面重新實現,這裏只是提前佔了一個位置而已。
如果我們在使用某個外設的時候,開啓了某個中斷,但是又忘記編寫配套的中斷服務程式或者函數名寫錯,那當中斷來臨的時,程式就會跳轉到啓動檔案預先寫好的空的中斷服務程式中,並且在這個空函數中無限回圈,即程式就死在這裏。
;/* dummy Exception Handlers */
NMI_Handler 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
; /* external interrupts handler */
EXPORT WWDGT_IRQHandler [WEAK]
EXPORT LVD_IRQHandler [WEAK]
EXPORT TAMPER_IRQHandler [WEAK]
EXPORT RTC_IRQHandler [WEAK]
EXPORT FMC_IRQHandler [WEAK]
EXPORT RCU_IRQHandler [WEAK]
EXPORT EXTI0_IRQHandler [WEAK]
EXPORT EXTI1_IRQHandler [WEAK]
EXPORT EXTI2_IRQHandler [WEAK]
EXPORT EXTI3_IRQHandler [WEAK]
EXPORT EXTI4_IRQHandler [WEAK]
EXPORT DMA0_Channel0_IRQHandler [WEAK]
EXPORT DMA0_Channel1_IRQHandler [WEAK]
EXPORT DMA0_Channel2_IRQHandler [WEAK]
EXPORT DMA0_Channel3_IRQHandler [WEAK]
EXPORT DMA0_Channel4_IRQHandler [WEAK]
EXPORT DMA0_Channel5_IRQHandler [WEAK]
EXPORT DMA0_Channel6_IRQHandler [WEAK]
EXPORT ADC0_1_IRQHandler [WEAK]
EXPORT CAN0_TX_IRQHandler [WEAK]
EXPORT CAN0_RX0_IRQHandler [WEAK]
EXPORT CAN0_RX1_IRQHandler [WEAK]
EXPORT CAN0_EWMC_IRQHandler [WEAK]
EXPORT EXTI5_9_IRQHandler [WEAK]
EXPORT TIMER0_BRK_TIMER8_IRQHandler [WEAK]
EXPORT TIMER0_UP_TIMER9_IRQHandler [WEAK]
EXPORT TIMER0_TRG_CMT_TIMER10_IRQHandler [WEAK]
EXPORT TIMER0_Channel_IRQHandler [WEAK]
EXPORT TIMER1_IRQHandler [WEAK]
EXPORT TIMER2_IRQHandler [WEAK]
EXPORT TIMER3_IRQHandler [WEAK]
EXPORT I2C0_EV_IRQHandler [WEAK]
EXPORT I2C0_ER_IRQHandler [WEAK]
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT SPI0_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT USART0_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT EXTI10_15_IRQHandler [WEAK]
EXPORT RTC_Alarm_IRQHandler [WEAK]
EXPORT USBFS_WKUP_IRQHandler [WEAK]
EXPORT TIMER7_BRK_TIMER11_IRQHandler [WEAK]
EXPORT TIMER7_UP_TIMER12_IRQHandler [WEAK]
EXPORT TIMER7_TRG_CMT_TIMER13_IRQHandler [WEAK]
EXPORT TIMER7_Channel_IRQHandler [WEAK]
EXPORT ADC2_IRQHandler [WEAK]
EXPORT EXMC_IRQHandler [WEAK]
EXPORT SDIO_IRQHandler [WEAK]
EXPORT TIMER4_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT UART3_IRQHandler [WEAK]
EXPORT UART4_IRQHandler [WEAK]
EXPORT TIMER5_IRQHandler [WEAK]
EXPORT TIMER6_IRQHandler [WEAK]
EXPORT DMA1_Channel0_IRQHandler [WEAK]
EXPORT DMA1_Channel1_IRQHandler [WEAK]
EXPORT DMA1_Channel2_IRQHandler [WEAK]
EXPORT DMA1_Channel3_IRQHandler [WEAK]
EXPORT DMA1_Channel4_IRQHandler [WEAK]
EXPORT ENET_IRQHandler [WEAK]
EXPORT ENET_WKUP_IRQHandler [WEAK]
EXPORT CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_EWMC_IRQHandler [WEAK]
EXPORT USBFS_IRQHandler [WEAK]
EXPORT DMA1_Channel5_IRQHandler [WEAK]
EXPORT DMA1_Channel6_IRQHandler [WEAK]
EXPORT USART5_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT DCI_IRQHandler [WEAK]
EXPORT CAU_IRQHandler [WEAK]
EXPORT HAU_TRNG_IRQHandler [WEAK]
EXPORT UART6_IRQHandler [WEAK]
EXPORT UART7_IRQHandler [WEAK]
EXPORT TLI_IRQHandler [WEAK]
EXPORT TLI_ER_IRQHandler [WEAK]
;/* external interrupts handler */
WWDGT_IRQHandler
LVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FMC_IRQHandler
RCU_IRQHandler
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
DMA0_Channel0_IRQHandler
DMA0_Channel1_IRQHandler
DMA0_Channel2_IRQHandler
DMA0_Channel3_IRQHandler
DMA0_Channel4_IRQHandler
DMA0_Channel5_IRQHandler
DMA0_Channel6_IRQHandler
ADC0_1_IRQHandler
CAN0_TX_IRQHandler
CAN0_RX0_IRQHandler
CAN0_RX1_IRQHandler
CAN0_EWMC_IRQHandler
EXTI5_9_IRQHandler
TIMER0_BRK_TIMER8_IRQHandler
TIMER0_UP_TIMER9_IRQHandler
TIMER0_TRG_CMT_TIMER10_IRQHandler
TIMER0_Channel_IRQHandler
TIMER1_IRQHandler
TIMER2_IRQHandler
TIMER3_IRQHandler
I2C0_EV_IRQHandler
I2C0_ER_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
SPI0_IRQHandler
SPI1_IRQHandler
USART0_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
EXTI10_15_IRQHandler
RTC_Alarm_IRQHandler
USBFS_WKUP_IRQHandler
TIMER7_BRK_TIMER11_IRQHandler
TIMER7_UP_TIMER12_IRQHandler
TIMER7_TRG_CMT_TIMER13_IRQHandler
TIMER7_Channel_IRQHandler
ADC2_IRQHandler
EXMC_IRQHandler
SDIO_IRQHandler
TIMER4_IRQHandler
SPI2_IRQHandler
UART3_IRQHandler
UART4_IRQHandler
TIMER5_IRQHandler
TIMER6_IRQHandler
DMA1_Channel0_IRQHandler
DMA1_Channel1_IRQHandler
DMA1_Channel2_IRQHandler
DMA1_Channel3_IRQHandler
DMA1_Channel4_IRQHandler
ENET_IRQHandler
ENET_WKUP_IRQHandler
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_EWMC_IRQHandler
USBFS_IRQHandler
DMA1_Channel5_IRQHandler
DMA1_Channel6_IRQHandler
USART5_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
DCI_IRQHandler
CAU_IRQHandler
HAU_TRNG_IRQHandler
UART6_IRQHandler
UART7_IRQHandler
TLI_IRQHandler
TLI_ER_IRQHandler
B .
ENDP
|
B:跳轉到一個標號。這裏跳轉到一個‘.’,即表示無線回圈
6. 使用者堆疊初始化
ALIGN:對指令或者數據存放的地址進行對齊,後面會跟一個立即數。預設表示 4 位元組對齊。
; user Initial Stack & Heap
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 PROC
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ENDP
ALIGN
ENDIF
END
|
首先判斷是否定義了__MICROLIB ,如果定義了這個宏則賦予標號__initial_sp(棧頂地址)、 __heap_base(堆起始地址)、 __heap_limit(堆結束地址)全域性屬性,可供外部檔案呼叫。有關這個宏我們在 KEIL 裏面設定。然後堆疊的初始化就由 C 庫函數_main 來完成。
如 果沒 有 定 義 __MICROLIB , 則 才 用 雙 段 存 儲 器 模 式 , 且 聲 明 標 號__user_initial_stackheap 具有全域性屬性,讓使用者自己來初始化堆疊。
IF,ELSE,ENDIF:彙編的條件分支語句,跟 C 語言的 if ,else 類似
END:檔案結束
部分內容參考部落格:https://www.cnblogs.com/wenshinlee/p/8859227.html
https://blog.csdn.net/sun_boy_boy_sun/article/details/86667980