;/*!
; \file startup_gd32e10x.s
; \brief start up file
;
; \version 2017-12-26, V1.0.0, firmware for GD32E10x
;*/
;
;/*
; Copyright (c) 2017, GigaDevice Semiconductor Inc.
;
; All rights reserved.
;
; Redistribution and use in source and binary forms, with or without modification,
;are permitted provided that the following conditions are met:
;
; 1. Redistributions of source code must retain the above copyright notice, this
; list of conditions and the following disclaimer.
; 2. Redistributions in binary form must reproduce the above copyright notice,
; this list of conditions and the following disclaimer in the documentation
; and/or other materials provided with the distribution.
; 3. Neither the name of the copyright holder nor the names of its contributors
; may be used to endorse or promote products derived from this software without
; specific prior written permission.
;
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
;AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
;WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
;IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
;INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
;NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
;PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
;WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
;ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
;OF SUCH DAMAGE.
;*/
; <h> Stack Configuration
; <o> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
; 定義棧的大小
Stack_Size EQU 0x00000400
;定義名字爲STACK的獨立區域,其實就是棧區,棧區沒有初始化,可讀可寫,並且以2^3=8字節對齊
AREA STACK, NOINIT, READWRITE, ALIGN=3
;申請了Stack_Size的空間,也就是0x400 大概 1024的棧空間。(SPACE申請一片內存空間但是不賦值)
Stack_Mem SPACE Stack_Size
;這裏的__initial_sp定了一個標籤(label),代碼當前指令的地址,後面會用DCD賦值,但是賦值什麼呢?
;初始化棧頂指針,指向堆棧頂,因爲棧是向下增長的,所以棧頂在最下面,堆是向上增長的,所以這裏也是堆頂?
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
;定義堆的大小
Heap_Size EQU 0x00000400
AREA HEAP, NOINIT, READWRITE, ALIGN=3
;定義_heap_base標籤,後面會賦值?
__heap_base
Heap_Mem SPACE Heap_Size
;定義__heap_limit標籤,後面會賦值?
__heap_limit
;Preserve意思是保留維持,這裏的PRESERVE8是字節對齊的關鍵字,指定當前文件保持堆棧8字節對齊
PRESERVE8
; 告訴編譯器使用THUMB指令集,THUMB 必須位於使用新語法的任何Thumb代碼之前
THUMB
; /* reset Vector Mapped to at Address 0 */
;復位段,只包含數據,只讀,沒搞明白這個東西,這個確實是復位段,但是復位段有什麼作用,還是隻是起了名字而已
AREA RESET, DATA, READONLY
; EXPORT 命令聲明一個符號,可由鏈接器用於解釋各個目標和庫文件中的符號引用,相當於聲明瞭一個全局變量。 GLOBAL 於 EXPORT相同
;爲什麼引號EXPORT __Vectors ???
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
;關鍵字DCD代表申請一個字的空間,後面的函數名即爲中斷服務函數入口地址。另外中斷向量表一般存放在Flash 0地址
;第一行,用DCD申請一個字的空間,然後用後面的表達式(__initial_sp,這其實是一個地址->棧頂指針)初始化
__Vectors DCD __initial_sp ; Top of Stack
;下面是Reset_Handler,復位函數,關於這兩個地址,__initial_sp和Reset_Handler,程序上電後,首先加載SP和PC,
;ARM規定從0地址處加載SP,從偏移爲4的地址(0x00000004)處加載PC。然後將程序控制權交給程序。我們知道0地址處存放
;__initial_sp???0x00000004地址處存放Reset_Handler,加載PC後,程序跳轉到Reset_Handler開始運行。
;後面看下Reset_Handler函數
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
;uC/OS-II中任務切換的時候會調用到,其中做了現場保存,並且在遇到其他中斷和SysTick中斷衝突時候其緩衝作用
DCD PendSV_Handler ; PendSV Handler
;系統tick中斷,想起來有人又疑問Systick中斷和Timer中斷的區別,喏,這裏就是區別,SysTick中斷是ARM核心內部中斷,
;你那些Timer啥的都是外部中斷,Timer當然作用更豐富一些
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 through EXTI Line detect
DCD RTC_IRQHandler ; 19:RTC through EXTI Line
DCD FMC_IRQHandler ; 20:FMC
DCD RCU_CTC_IRQHandler ; 21:RCU and CTC
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 Channel0
DCD DMA0_Channel1_IRQHandler ; 28:DMA0 Channel1
DCD DMA0_Channel2_IRQHandler ; 29:DMA0 Channel2
DCD DMA0_Channel3_IRQHandler ; 30:DMA0 Channel3
DCD DMA0_Channel4_IRQHandler ; 31:DMA0 Channel4
DCD DMA0_Channel5_IRQHandler ; 32:DMA0 Channel5
DCD DMA0_Channel6_IRQHandler ; 33:DMA0 Channel6
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:EXTI5 to EXTI9
DCD TIMER0_BRK_TIMER8_IRQHandler ; 40:TIMER0 Break and TIMER8
DCD TIMER0_UP_TIMER9_IRQHandler ; 41:TIMER0 Update and TIMER9
DCD TIMER0_TRG_CMT_TIMER10_IRQHandler ; 42:TIMER0 Trigger and Commutation and TIMER10
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:EXTI10 to EXTI15
DCD RTC_Alarm_IRQHandler ; 57:RTC Alarm
DCD USBFS_WKUP_IRQHandler ; 58:USBFS Wakeup
DCD TIMER7_BRK_TIMER11_IRQHandler ; 59:TIMER7 Break and TIMER11
DCD TIMER7_UP_TIMER12_IRQHandler ; 60:TIMER7 Update and TIMER12
DCD TIMER7_TRG_CMT_TIMER13_IRQHandler ; 61:TIMER7 Trigger and Commutation and TIMER13
DCD TIMER7_Channel_IRQHandler ; 62:TIMER7 Channel Capture Compare
DCD 0 ; 63:Reserved
DCD EXMC_IRQHandler ; 64:EXMC
DCD 0 ; 65:Reserved
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 0 ; 77:Reserved
DCD 0 ; 78:Reserved
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
;__Vectors_End就是當前指令的地址,可以用來計算這個向量的size,當然這個size你也可以數一數有多少個DCD,然後乘以4得到
__Vectors_End
;EQU就不說了,這個理解爲賦值語句就可以了,__Vectors_Size的大小
__Vectors_Size EQU __Vectors_End - __Vectors
;這裏用AREA指示彙編程序彙編新的代碼節,指定的節名是“.text",注意這裏語法上如果節名中首位是數字的話必須在首尾加入'|'
;這裏沒有數字而是習慣性的表示節名的方法 ,表示由C編譯器生成的代碼節。CODE屬性表示定義的是代碼節,READONLY只讀
AREA |.text|, CODE, READONLY
;/* reset Handler */
;proc是定義子程序的僞指令
Reset_Handler PROC
;如果EXPORT帶有WEAK的話,其他文件中有定義Reset_Handler有同名函數的話就用其他文件中的函數
;如果沒有,則就用本地的Reset_Handler函數,這個好像是C++中的繼承,不同的是,一旦其他文件中有
;同名的函數,則本地的函數就徹底無用了
EXPORT Reset_Handler [WEAK]
;IMPORT表示這是一個外部變量的標號,不是本程序中定義的 ,這兩個函數都在外面有定義
;可以看出在main函數調用之前調用了systeminit
IMPORT SystemInit
IMPORT __main
;這裏說一下LDR,說的多一點,ARM是RISC(精簡指令集計算機)結構,只有載入(loader)和存儲(store)指令可以訪問存儲器,
;數據處理指令只對寄存器的內容進行操作。這裏利用LDR僞指令(注意不是LDR指令)用“="表示地址,把SystemInit的地址寫入到R0中
;需要把內存中的地址讀到寄存器中只能使用LDR,不能用mov,mov只能在寄存器之間移動數據
LDR R0, =SystemInit
;BLX 帶鏈接和帶狀態切換的跳轉,首先將當前指令的下一條指令地址保存在LR(R14)寄存器中,然後跳轉,這體現了BLX的帶鏈接性,
;BLX同時又是帶狀態切換的跳轉,從BLX R0;跳轉到R0中的地址(注意我說的不是R0地址),如果R0[0]=1,則進入Thumb狀態
;BLX 同時兼容了BL和BX的功能,爲什麼這裏用帶鏈接功能的跳轉呢,下面跳轉到main函數的時候爲什麼用的BX,因爲main函數後面就
;沒有要執行的指令了,不必保存PC到LR中了,PC中保存的時候當前指令的下一條指令,在執行函數的時候執行完返回的時候要知道進入函數
;之前的下一條指令地址
BLX R0
;下面LDR是將內存中的__main函數的地址存儲到LDR中,再囉嗦一句,ARM指令集是精簡指令集,從內存搬運數據到寄存器只能用load(LDR)的方法
LDR R0, =__main
;BX R0,帶狀態切換的跳轉,R0[0](R0的最低爲如果是1的話)=1的話,則狀態切換爲Thumb狀態 ,爲什麼切換爲Thumb狀態,因爲Thumb是十六位指令
;AMR指令是32位指令,更低位數的指令代表更高的代碼密度,更高的代碼密度意味着更低的功耗,所以能用thumb的情況下就用thumb,這是我的理解,
;歡迎斧正
BX R0
;ENDP表示這個子程序結束了,這個很容易理解。最後總結下,相當於這行了Reset_Handler之後再執行SystemInit和main
ENDP
;/* dummy Exception Handlers */
;EXPORT func [WEAK],其他文件中有此fun函數的話就用其他文件中的,當前文件中的fun不起作用
NMI_Handler PROC
EXPORT NMI_Handler [WEAK]
; B . 這裏有個'.'指的是當前行地址,就跳到當前行
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 ,這個首先是個子程序,在什麼情況下會調用到此函數
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_CTC_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 EXMC_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 CAN1_TX_IRQHandler [WEAK]
EXPORT CAN1_RX0_IRQHandler [WEAK]
EXPORT CAN1_RX1_IRQHandler [WEAK]
EXPORT CAN1_EWMC_IRQHandler [WEAK]
EXPORT USBFS_IRQHandler [WEAK]
;/* external interrupts handler */
WWDGT_IRQHandler
LVD_IRQHandler
TAMPER_IRQHandler
RTC_IRQHandler
FMC_IRQHandler
RCU_CTC_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
EXMC_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
CAN1_TX_IRQHandler
CAN1_RX0_IRQHandler
CAN1_RX1_IRQHandler
CAN1_EWMC_IRQHandler
USBFS_IRQHandler
;B .跳轉到當前位置,網上有人說是跳到這裏無限循環,我自己理解是跳到這裏補再返回了,不存在什麼無限循環,只是不能往下跳轉了而已
B .
ENDP
;這裏ALIGN表示自己對齊,ALIGN 後面什麼都沒有?默認是4字節對齊?8字節對齊?網上有網友給出答案
;ALIGN:對指令或者數據存放的地址進行對齊,後面會跟一個立即數。缺省表示4字節對齊
ALIGN
; user Initial Stack & Heap
;IF ELSE ENDIF彙編的這些指令已經和C語言差不多了,^_^
;如何才能確定有沒有定義__MICORLIB,在編譯器中有選項Use MicroLIB,如果有勾選則__MICROLIB會被定義,外面
;就可以用__initial_sp,__heap_base,__heap_limit這些在本文件中聲明的label
IF :DEF:__MICROLIB
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
;__use_two_region_memory 指定棧和堆是分兩個區域,雙區模式,也有單區模式的
IMPORT __use_two_region_memory
;EXPORT表示可以導入到外面,被其他文件所調用,類似於C語言中的extern
EXPORT __user_initial_stackheap
;看下這個PROC子程序
__user_initial_stackheap PROC
;將Heap_Mem地址也就是堆的基地址放入到R0寄存器中,R0~R15的寄存器要看下功能
LDR R0, = Heap_Mem
;R1中放的是棧的最高地址
LDR R1, =(Stack_Mem + Stack_Size)
;R2中放的是堆的最高地址
LDR R2, = (Heap_Mem + Heap_Size)
;R3中放的是棧區的基地址
LDR R3, = Stack_Mem
;BX LR的作用等同於mov PC,LR,即跳轉到LR中存放的地址處。
;LR是鏈接寄存器(Link Register LR),在ARM體系結構體中LR的特殊用途有兩種:一種是用來保存子程序的返回地址,這裏在外面調用這個子程序的時候
;子程序的返回地址已經自動保存到LR(R14)寄存器中了;二是當異常發生時候,LR中保存的值等於異常發生時PC的值減去4(或者減去2),因此在各種模式下可以
;根據LR的值返回到異常發生前的響應位置繼續執行。
;當前通過BL或者BLX指令(注意這裏是帶L(鏈接)的)調用子程序時,硬件自動將子程序返回地址保存在R14寄存器中。在子程序返回時候,把LR的值複製到程序
計數器PC即可實現子程序返回。
BX LR
;ENDP 子程序結束
ENDP
;模式4字節對齊,如果是ALIGN 3,則是2^3字節對齊
ALIGN
;ENDIF 和前面IF ELSE 對應
ENDIF
;END 表示彙編程序結束,後面再有也不會彙編了
END