本帖最后由 heise 于 2014-8-18 02:54 編輯
啟動代碼是芯片復位后進入C語言的main()函數前執行的一段代碼,主要為運行C語言程序提供基本運行環境。啟動代碼文件:startup.s。startup.s包含異常向量表和系統初始化代碼,保存C語言使用的堆和棧的開始位置,包括異常處理程序和目標板特殊的代碼。匯編學習:ARM偽指令,在匯編程序中經常會被使用,包括以下幾條: —AREA
—ALIGN — CODE16 、CODE32
—ENTRY
—END
—EQU
—EXPORT (或GLOBAL )
—IMPORT
—EXTERN
— GET(或INCLUDE )
—INCBIN
—KEEP:告訴編譯器將局部符號包含在目標文件的符號表中
—NOFP:禁止源程序中包含浮點運算指令
—REQUIRE:指定段之間的相互依賴關系
— REQUIRE8及PRESERVE8:指示當前代碼中(要求)數據棧8字節對齊
—RN
—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的發展和軟件的發展都有一個基礎,因此出現了向下兼容的現象。80386與80286相比,只在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段必須相同。
通?梢杂肁REA偽操作將程序分為多個ELF格式的段。段名稱可以相同,這時這些同名的段被放在同一個ELF段中。一個大的程序可以包括多個代碼段和數據段,一個匯編語言程序至少要包含一個段。
使用示例:
AREAInit , CODE , READONLY
該偽操作定義了一個代碼段,段名為 Init ,屬性為只讀 =================================================================== SPACE :
語法格式:
標號 SPACE 表達式
SPACE 偽指令用于分配一片連續的存儲區域并初始化為 0 。其中,表達式為要分配的字節數。
SPACE 也可用 “ % ” 代替。
使用示例:
DataSpace SPACE 100 ;分配連續100 字節的存儲單元并初始化為 0 。
=================================================================== PRESERVE8:
字節對齊關鍵詞,以前用ADS編譯器的時候可以不用,但是后來的keil編譯器時需要加上(譬如用周立功模板時,將ADS工程轉到keil工程時就必須加上)。指定當前文件堆棧8字節對齊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 updatepotentially breaks 8 byte stack alignment
3700000044 STMFD sp!,{r2,r3,lr}
THUMB:告訴匯編器下面是32為的Thumb指令,如果需要匯編器將插入位以保證對齊 CODE16、CODE32 [THUMB]:
語法格式:
CODE16 (或CODE32 )
CODE16 偽指令通知編譯器,其后的指令序列為 16 位的Thumb 指令。
CODE32 偽指令通知編譯器,其后的指令序列為 32 位的ARM 指令。
若在匯編源程序中同時包含ARM 指令和 Thumb 指令時,可用CODE16 偽指令通知編譯器其后的指令序列為 16 位的Thumb 指令, CODE32 偽指令通知編譯器其后的指令序列為 32 位的ARM 指令。因此,在使用 ARM 指令和Thumb 指令混合編程的代碼里,可用這兩條偽指令進行切換,但注意他們只通知編譯器其后指令的類型,并不能對處理器進行狀態的切換。
使用示例:
AREAInit ,CODE , READONLY
……
CODE32 ;通知編譯器其后的指令為32 位的 ARM 指令
LDRR0 ,=NEXT + 1 ;將跳轉地址放入寄存器 R0
BXR0 ;程序跳轉到新的位置執行,并將處理器切換到 Thumb 工作狀態
……
CODE16 ;通知編譯器其后的指令為16 位的 Thumb 指令
NEXTLDR R3,=0x3FF
……
END ;程序結束 =================================================================== EXPORT(或GLOBAL)
語法格式:
EXPORT 標號{[WEAK]}
EXPORT 偽指令用于在程序中聲明一個全局的標號,該標號可在其他的文件中引用。 EXPORT可用GLOBAL 代替。標號在程序中區分大小寫, [WEAK] 選項聲明其他的同名標號優先于該標號被引用。
使用示例:
AREAInit ,CODE , READONLY
EXPORTStest ;聲明一個可全局引用的標號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、IF、ELSE、ENDIF
語法格式:
IF 邏輯表達式
指令序列1
ELSE
指令序列2
ENDIF
IF 、ELSE 、 ENDIF 偽指令能根據條件的成立與否決定是否執行某個指令序列。當 IF 后面的邏輯表達式為真,則執行指令序列 1 ,否則執行指令序列 2 。其中, ELSE 及指令序列 2 可以沒有,此時,當 IF 后面的邏輯表達式為真,則執行指令序列 1 ,否則繼續執行后面的指令。
IF 、ELSE 、 ENDIF 偽指令可以嵌套使用。
使用示例:
GBLLTest ;聲明一個全局的邏輯變量,變量名為 Test……
IFTest =TRUE
指令序列1
ELSE
指令序列2
ENDIF
X:LAND:Y 表示將X和Y做邏輯與的操作。 X:LOR:Y 表示將X和Y做邏輯或的操作。 。篖NOT:Y 表示將Y做邏輯非的操作。 X:LEOR:Y 表示將X和Y做邏輯異或的操作。 :LNOT: 邏輯預算符 :DEF:
IF :LNOT::DEF:NO_CRP ;如果宏判斷是否定義NO_CRP #ifndef
AREA |.ARM.__at_0x02FC|, CODE, READONLY ;自定義只讀代碼段
CRP_Key DCD 0xFFFFFFFF
;加密等級見上注釋 ENDIF
=================================================================== PROC 過程就是子程序。一個過程可以被其它程序所調用(用CALL指令),過程的最后一條指令一般是返回指令(RET)。 過程定義偽指令的格式為 <過程名> PROC [類型] … … RET <過程名> ENDP 注意:PROC和ENDP必須成對出現 過程的類型有兩種: NEAR——(默認類型)表示段內調用 FAR——表示段間調用 調用一個過程的格式為: CALL <過程名> IMPORT
語法格式:
IMPORT 標號 {[WEAK]}
IMPORT 偽指令用于通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用,而且無論當前源文件是否引用該標號,該標號均會被加入到當前源文件的符號表中。
標號在程序中區分大小寫,[WEAK] 選項表示當所有的源文件都沒有定義這樣一個標號時,編譯器也不給出錯誤信息,在多數情況下將該標號置為0 ,若該標號為 B 或 BL 指令引用,則將 B 或 BL指令置為NOP 操作。
使用示例:
AREAInit , CODE , READONLY
IMPORTMain ;通知編譯器當前文件要引用標號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 |
ARM是RISC結構,數據從內存到CPU之間的移動只能通過L/S指令來完成,也就是ldr/str指令。比如想把數據從內存中某處讀取到寄存器中,只能使用ldr
比如:ldr r0, 0x12345678
就是把0x12345678這個地址中的值存放到r0中。
而mov不能干這個活,mov只能在寄存器之間移動數據,或者把立即數移動到寄存器中,這個和x86這種CISC架構的芯片區別最大的地方。
x86中沒有ldr這種指令,因為x86的mov指令可以將數據從內存中移動到寄存器中。
另外還有一個就是ldr偽指令,雖然ldr偽指令和ARM的ldr指令很像,但是作用不太一樣。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,x為0或1)。指令的地址的最后一位必為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所示。
圖1.1 在__rt_entry()函數中的運行情況 __main()是編譯系統提供的一個函數,負責完成庫函數的初始化和初始化應用程序執行環境,最后自動跳轉到main()。所以說,前者是庫函數,后者就是我們自己編寫的main()主函數; =================================================================== ALIGN
語法格式:
ALIGN{ 表達式{ ,偏移量 }}
ALIGN 偽指令可通過添加填充字節的方式,使當前位置滿足一定的對其方式 | 。其中,表達式的值用于指定對齊方式,可能的取值為 2 的冪,如1 、 2 、4 、 8 、16 等。若未指定表達式,則將當前位置對齊到下一個字的位置。偏移量也為一個數字表達式,若使用該字段,則當前位置的對齊方式為:2 的表達式次冪+偏移量。
使用示例:
AREAInit ,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
。。。。。。 B . ;跳轉到當前行 ENDP
EXPORT __user_initial_stackheap
__user_initial_stackheap
;
; <h> Stack Configuration
; <o> Stack Size (in Bytes)<0x0-0xFFFFFFFF:8>
; </h>
Stack_Size EQU 0x00000200 ;定義statck_size標號為ox200的空間作為?臻g AREA STACK, NOINIT, READWRITE, ALIGN=3 ;定義一個數據段,按8字節對齊 ;AREA 段名 屬性 1 ,屬性 2 ,…… ;AREA 偽指令用于定義一個代碼段或數據段。其中,段名若以數字開頭,則該段名需用 “ | ” 括起來,如 |1_test| 。 ;屬性字段表示該代碼段(或數據段)的相關屬性,多個屬性用逗號分隔。常用的屬性如下: ;— CODE 屬性:用于定義代碼段,默認為 READONLY 。 ;— DATA 屬性:用于定義數據段,默認為 READWRITE 。 ;— READONLY 屬性:指定本段為只讀,代碼段默認為 READONLY 。 ;— READWRITE 屬性:指定本段為可讀可寫,數據段的默認屬性為 READWRITE 。 ;— ALIGN 屬性:使用方式為 ALIGN 表達式。在默認時, ELF (可執行連接文件)的代碼段和數據段是按字對齊的,表達式的取值范圍;為0 ~ 31 ,相應的對齊方式為 2 表達式次方。 ;— COMMON 屬性:該屬性定義一個通用的段,不包含任何的用戶代碼和數據。各源文件中同名的 COMMON 段共享同一段存儲單元。 ;一個匯編語言程序至少要包含一個段,當程序太長時,也可以將程序分為多個代碼段和數據段。 Stack_Mem SPACE Stack_Size ;保留stack_size大小的堆?臻g,分配連雪的存儲空間,并初始化為0 ;SPACE 偽指令用于分配一片連續的存儲區域并初始化為 0 。其中,表達式為要分配的字節數。 ;SPACE 也可用 “ % ” 代替。
__initial_sp
; <h> Heap Configuration
; <o> Heap Size (inBytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000000 AREA HEAP,NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size ;堆分析同上 __heap_limit ;代表堆棧地址的標號
PRESERVE8 ;制定當前文件堆棧按照8個字節對齊
THUMB ;指示編譯器為thumb指令
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY ;自定義只讀數據段放到數據段中為于0地址
EXPORT __Vectors ;EXPORT 標號 {[WEAK]} ;EXPORT 偽指令用于在程序中聲明一個全局的標號,該標號可在其他的文件中引用。 ;EXPORT可用GLOBAL 代替。標號在程序中區分大小寫, ; [WEAK] 選項聲明其他的同名標號優先于該標號被引用。 __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 WAKEUP_IRQHandler ; 15 wakeup sources for all the
DCD WAKEUP_IRQHandler ; I/O pins starting from PIO0 (0:11)
DCD WAKEUP_IRQHandler ; all 40 are routed to the sameISR
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler
DCD WAKEUP_IRQHandler ; PIO1 (0:11)
DCD CAN_IRQHandler ;CAN
DCD SSP1_IRQHandler ;SSP1
DCD I2C_IRQHandler ; I2C
DCD TIMER16_0_IRQHandler ; 16-bit Timer0
DCD TIMER16_1_IRQHandler ; 16-bit Timer1
DCD TIMER32_0_IRQHandler ; 32-bit Timer0
DCD TIMER32_1_IRQHandler ; 32-bit Timer1
DCD SSP0_IRQHandler ; SSP0
DCD UART_IRQHandler ; UART
DCD USB_IRQHandler ; USB IRQ
DCD USB_FIQHandler ; USB FIQ
DCD ADC_IRQHandler ; A/D Converter
DCD WDT_IRQHandler ; Watchdog timer
DCD BOD_IRQHandler ; Brown Out Detect
DCD FMC_IRQHandler ; IP2111 Flash Memory Controller
DCD PIOINT3_IRQHandler ; PIO INT3
DCD PIOINT2_IRQHandler ; PIO INT2
DCD PIOINT1_IRQHandler ; PIO INT1
DCD PIOINT0_IRQHandler ; PIO INT0 ;標號DCD (或 DCDU ) 表達式 ;DCD (或 DCDU )偽指令用于分配一片連續的字存儲單元并用偽指令中指定的表達式初始化。其中,表達式可以為程序標號或數字表達式。 ;DCD 也可用 “ & ” 代替。 ;用DCD 分配的字存儲單元是字對齊的,而用 DCDU 分配的字存儲單元并不嚴格字對齊。 ;// <h> CORERead Protection level(CRP)
;// <o> CRP_Level:
;// <0xFFFFFFFF=>Disabled
;// <0x12345678=>CRP1
;// <0x87654321=>CRP2
;// <0X43218765=>CRP3(OTP mode!!!!NOTIC)
;//</h> ;//CRP_Level EQU 0xFFFFFFFF IF :LNOT::DEF:NO_CRP ;如果宏判斷是否定義NO_CRP #ifndef
AREA |.ARM.__at_0x02FC|, CODE, READONLY ;自定義只讀代碼段
CRP_Key DCD 0xFFFFFFFF
;加密等級見上注釋 ENDIF
AREA |.text|,CODE, READONLY
; Reset Handler
Reset_Handler PROC
;PROC 子程序開始偽指令 EXPORT Reset_Handler [WEAK]
IMPORT __main ;IMPORT 標號 {[WEAK]} ;IMPORT 偽指令用于通知編譯器要使用的標號在其他的源文件中定義,但要在當前源文件中引用,而且無論當前源文件是否引用該標號,該標號均會被加入到當前源文件的符號表中。 ;標號在程序中區分大小寫,[WEAK] 選項表示當所有的源文件都沒有定義這樣一個標號時,編譯器也不給出錯誤信息,在多數情況下將該標號置為 0 ,若該標號為 B 或 BL 指令引用,則將 B 或 BL指令置為NOP 操作。
;__main為運行時提供的汗水,完成堆棧,堆的初始化 LDR R0, =__main ;使用=標示目前為偽指令,不是標準指令,=等于@取地址,把main 的地址給R0
BX R0 ;BX帶狀態切換的跳轉指令
ENDP ;為子程序結束
; Dummy Exception Handlers (infinite loops which can bemodified)
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 EXPORT WAKEUP_IRQHandler [WEAK]
EXPORT CAN_IRQHandler [WEAK]
EXPORT SSP1_IRQHandler [WEAK]
EXPORT I2C_IRQHandler [WEAK]
EXPORT TIMER16_0_IRQHandler [WEAK]
EXPORT TIMER16_1_IRQHandler [WEAK]
EXPORT TIMER32_0_IRQHandler [WEAK]
EXPORT TIMER32_1_IRQHandler [WEAK]
EXPORT SSP0_IRQHandler [WEAK]
EXPORT UART_IRQHandler [WEAK] EXPORT USB_IRQHandler [WEAK]
EXPORT USB_FIQHandler [WEAK]
EXPORT ADC_IRQHandler [WEAK]
EXPORT WDT_IRQHandler [WEAK]
EXPORT BOD_IRQHandler [WEAK]
EXPORT FMC_IRQHandler [WEAK]
EXPORT PIOINT3_IRQHandler [WEAK]
EXPORT PIOINT2_IRQHandler [WEAK]
EXPORT PIOINT1_IRQHandler [WEAK]
EXPORT PIOINT0_IRQHandler [WEAK]
WAKEUP_IRQHandler
CAN_IRQHandler
SSP1_IRQHandler
I2C_IRQHandler
TIMER16_0_IRQHandler
TIMER16_1_IRQHandler
TIMER32_0_IRQHandler
TIMER32_1_IRQHandler
SSP0_IRQHandler
UART_IRQHandler
USB_IRQHandler
USB_FIQHandler
ADC_IRQHandler
WDT_IRQHandler
BOD_IRQHandler
FMC_IRQHandler
PIOINT3_IRQHandler
PIOINT2_IRQHandler
PIOINT1_IRQHandler
PIOINT0_IRQHandler
B . ENDP
ALIGN
; User Initial Stack & Heap
IF :DEF:__MICROLIB
;是否使用外部Microlib,在編譯器中設置 ;有時候使用外部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
|