一起學mini2440裸機開發(二)--MDK自帶的S3C2440.s分析
上一節,咱們在建立工程的時候,預設的是使用MDK自帶的啟動程式碼,這些啟動程式碼到底做了什麼工作呢?在這裡我想探究一下,探究不全沒什麼事,能看懂個大概就行了。
我先申明一下,其實我並不是頭一次學ARM裸機,我先前已經按照韋東山的使用arm-linux-gcc在linux下編譯裸機程式的方法走了一遍了,用那個方法的話對以後的uboot移植非常有幫助,但是有一個不方便的就是,使用Linux系統下編譯裸機程式,很多自帶的庫函式不能用,比如print()函式我都不能用,主要是我的水平不行,不會用,其實可以用的。所以現在想認真的再用編譯器學一下ARM裸機,裸機學好了,對驅動開發很有幫助的。
所以,很可能我講的可能細節上照顧不到沒接觸過ARM裸機的,在此深感抱歉。我也只是想把自己的學習弄成筆記而已。
言歸正傳,下面進行S3C2440.s的分析,初學者這一節可以略過。
1、首先,瞭解一下這個檔案都要完成那些功能。
①看門狗初始化(可以選擇是否初始化)
②時鐘初始化(可以選擇是否初始化)
③儲存控制器初始化(可以選擇是否初始化)
④GPIO口初始化(可以選擇是否初始化)
⑤堆疊初始化(沒有選擇性,必須初始化)
⑥跳轉到C檔案的main函式執行
2、其實知道上面檔案做了哪些工作就行了,下面具體分析一下
;/*****************************************************************************/
;/* S3C2440.S: Startup file for Samsung S3C2440 */
;/*****************************************************************************/
;/*
;*啟動程式碼S3C2440.S是在CPU復位後執行的。這個檔案會根據以下的
;*SET標誌來進行翻譯執行。
;*NO_CLOCK_SETUP:啟動程式碼不初始化時鐘(這種情況大多出現在時鐘已經在script.ini檔案中初始化時)
;*NO_MC_SETUP:啟動程式碼不初始化暫存器控制器。
;*NO_GP_SETUP:啟動程式碼不初始化GPIO口
;* RAM_INTVEC:啟動程式碼將異常向量表從執行地址處複製到RAM中去
CPSR中的低8位。I:IRQ中斷禁止位,置位禁止。F:FIQ中斷禁止位,置位禁止。 T:CPU狀態位(ARM或者THUMB)。M4-M0:工作模式選擇位
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
I | F | T | M4 | M3 | M2 | M1 | M0 |
M[4:0] | 工作模式 |
10000 | 使用者模式 |
10001 | 快中斷模式 |
10010 | 中斷模式 |
10011 | 管理模式 |
10111 | 資料訪問中止模式 |
11011 | 未定義指令中止模式 |
11111 | 系統模式 |
;狀態暫存器CPSR中的標準的模式位和中斷位的巨集定義
Mode_USR EQU 0x10 ;使用者模式
Mode_FIQ EQU 0x11 ;快中斷模式
Mode_IRQ EQU 0x12 ;中斷模式
Mode_SVC EQU 0x13 ;管理模式
Mode_ABT EQU 0x17 ;資料訪問中止模式
Mode_UND EQU 0x1B ;未定義指令中止模式
Mode_SYS EQU 0x1F ;系統模式
I_Bit EQU 0x80 ; when I bit is set, IRQ is disabled
F_Bit EQU 0x40 ; when F bit is set, FIQ is disabled
;棧(Stack)設定。不同工作模式的堆疊暫存器sp不一樣。
;設定棧空間
UND_Stack_Size EQU 0x00000000 ;未定義模式
SVC_Stack_Size EQU 0x00000008 ;管理模式棧長度
ABT_Stack_Size EQU 0x00000000 ;資料訪問中止模式棧長度
FIQ_Stack_Size EQU 0x00000000 ;快中斷模式棧長度
IRQ_Stack_Size EQU 0x00000080 ;中斷模式棧長度
USR_Stack_Size EQU 0x00000400 ;使用者模式棧長度
ISR_Stack_Size EQU (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
FIQ_Stack_Size + IRQ_Stack_Size) ;所有的堆疊大小進行相加,得到總堆疊大小
;/*******************************************************************************************************
;arm的彙編程式由段組成,段是相對獨立的指令或資料單位,每個段由AREA偽指令定義,並定義段的屬性
;READWRITE(讀寫)READONLY(只讀)NOINIT(不初始化記憶體單元或將記憶體寫0)
;*********************************************************************************************************/
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;定義棧段,段名為STACK,位元組對齊方式
Stack_Mem SPACE USR_Stack_Size ;SPACE指令用於分配一塊記憶體單元。這裡分別為這兩個棧分配對應
__initial_sp SPACE ISR_Stack_Size ;長度的記憶體空間
Stack_Top EQU Stack_Mem + ISR_Stack_Size ;//定義棧開始地址(最大地址,堆疊向下訪問)
;堆(heap)配置
;堆大小(單位:位元組)
Heap_Size EQU 0x00000000 ;系統的堆空間設定//定義堆空間大小(配合最後的動態記憶體申請使用)
AREA HEAP, NOINIT, READWRITE, ALIGN=3 ;定義堆段,段名:HEAP,不初始化記憶體,可讀寫,位元組對齊
__heap_base
Heap_Mem SPACE Heap_Size ;申請堆的記憶體空間
__heap_limit
;-----------------------儲存器設定 ------------------------------------
IRAM_BASE EQU 0x40000000 ;記憶體基地址
;-----------------------看門狗定義 ----------------------------
WT_BASE EQU 0x53000000 ; 看門狗暫存器基地址
WTCON_OFS EQU 0x00 ; 看門狗控制暫存器 對應基地址的偏移值
WTDAT_OFS EQU 0x04 ; 看門狗資料暫存器 對應基地址的偏移值
WTCNT_OFS EQU 0x08 ; 看門狗計數暫存器 對應基地址的偏移值
;看門狗時鐘設定
WT_SETUP EQU 1 ;看門狗設定
WTCON_Val EQU 0x00000000 ;看門狗控制暫存器相關
WTDAT_Val EQU 0x00008000 ;看門狗資料暫存器相關
;----------------------- 時鐘管理定義 ----------------
CLOCK_BASE EQU 0x4C000000 ; 時鐘暫存器基地址
LOCKTIME_OFS EQU 0x00 ;PLL鎖定時間計數器對應基地址的偏移值
MPLLCON_OFS EQU 0x04 ; MPLL配置暫存器對應基地址的偏移值
UPLLCON_OFS EQU 0x08 ; UPLL配置暫存器對應基地址的偏移值
CLKCON_OFS EQU 0x0C ; 時鐘控制器對應基地址的偏移值
CLKSLOW_OFS EQU 0x10 ; 慢時鐘控制暫存器對應基地址的偏移值
CLKDIVN_OFS EQU 0x14 ; 時鐘分頻控制器對應基地址的偏移值
CAMDIVN_OFS EQU 0x18 ; 攝像時鐘分頻控制器對應基地址的偏移值
;時鐘相關暫存器設定值的巨集定義
CLOCK_SETUP EQU 0 ;時鐘設定 ;在以後的實驗裡,我會將它設定為1,這樣做是為了讓系統在執行時就對時鐘進行初始化
LOCKTIME_Val EQU 0x0FFF0FFF ;PLL鎖定時間計數器 值
MPLLCON_Val EQU 0x00043011 ;MPLL控制暫存器 值 ;設定FCLK=300MHz
UPLLCON_Val EQU 0x00038021 ;UPLL控制暫存器 值
CLKCON_Val EQU 0x001FFFF0 ;時鐘控制器 值
CLKSLOW_Val EQU 0x00000004 ;慢時鐘控制器 值
CLKDIVN_Val EQU 0x0000000F ; 時鐘分頻控制器 值 ;設定分頻比為1:3:6,UCLK=48MHz
CAMDIVN_Val EQU 0x00000000 ;攝像時鐘分頻器 值
;----------------------- 儲存器控制設定 -------------------------
MC_BASE EQU 0x48000000 ; 儲存器控制器基地址
BWSCON_OFS EQU 0x00 ; 位寬和等待控制暫存器 偏移值
BANKCON0_OFS EQU 0x04 ;BANK0控制暫存器 偏移值
BANKCON1_OFS EQU 0x08 ; BANK1控制暫存器 偏移值
BANKCON2_OFS EQU 0x0C ; BANK2控制暫存器 偏移值
BANKCON3_OFS EQU 0x10 ; BANK3控制暫存器 偏移值
BANKCON4_OFS EQU 0x14 ; BANK4控制暫存器 偏移值
BANKCON5_OFS EQU 0x18 ; BANK5控制暫存器 偏移值
BANKCON6_OFS EQU 0x1C ; BANK6控制暫存器 偏移值
BANKCON7_OFS EQU 0x20 ;BANK7控制暫存器 偏移值
REFRESH_OFS EQU 0x24 ; SDRAM重新整理控制暫存器 偏移值
BANKSIZE_OFS EQU 0x28 ; BANKSIZE暫存器 偏移值
MRSRB6_OFS EQU 0x2C ; SDRAM控制暫存器 偏移值
MRSRB7_OFS EQU 0x30 ; SDRAM控制暫存器 偏移值
;儲存控制器的相應的設定值
MC_SETUP EQU 0 ;儲存控制器設定
BWSCON_Val EQU 0x22000000 ;匯流排寬度和等待控制器 值
BANKCON0_Val EQU 0x00000700 ;Boor ROM控制器 值
BANKCON1_Val EQU 0x00000700 ;BANK1控制 值
BANKCON2_Val EQU 0x00000700 ;BANK2控制 值
BANKCON3_Val EQU 0x00000700 ;BANK3控制 值
BANKCON4_Val EQU 0x00000700 ;BANK4控制 值
BANKCON5_Val EQU 0x00000700 ;BANK5控制 值
BANKCON6_Val EQU 0x00018005 ;BANK6控制 值
BANKCON7_Val EQU 0x00018005 ;BANK7控制 值
REFRESH_Val EQU 0x008404F3 ;DRAM/SDRAM重新整理控制
BANKSIZE_Val EQU 0x00000032 ;儲存器大小控制
MRSRB6_Val EQU 0x00000020 ;SDRAM的模式設定暫存器 控制
MRSRB7_Val EQU 0x00000020 ;SDRAM的模式設定暫存器 控制
;----------------------- I/O 口設定----------------------------------
GPA_BASE EQU 0x56000000 ; GPA Base Address
GPB_BASE EQU 0x56000010 ; GPB Base Address
GPC_BASE EQU 0x56000020 ; GPC Base Address
GPD_BASE EQU 0x56000030 ; GPD Base Address
GPE_BASE EQU 0x56000040 ; GPE Base Address
GPF_BASE EQU 0x56000050 ; GPF Base Address
GPG_BASE EQU 0x56000060 ; GPG Base Address
GPH_BASE EQU 0x56000070 ; GPH Base Address
GPJ_BASE EQU 0x560000D0 ; GPJ Base Address
GPCON_OFS EQU 0x00 ; 控制暫存器相對應於上面(A-J)基地址的偏移值
GPDAT_OFS EQU 0x04 ; 資料暫存器相對應於上面(A-J)基地址的偏移值
GPUP_OFS EQU 0x08 ; 上拉控制暫存器相對應於上面(B-J)基地址的偏移值
; I/O埠 設定
GP_SETUP EQU 0
;埠A
GPA_SETUP EQU 0
GPACON_Val EQU 0x000003FF
;埠B
GPB_SETUP EQU 0
GPBCON_Val EQU 0x00000000
GPBUP_Val EQU 0x00000000 ;埠B上拉開啟
;埠C
GPC_SETUP EQU 0
GPCCON_Val EQU 0x00000000
GPCUP_Val EQU 0x00000000 ;埠C上拉開啟
;埠D
GPD_SETUP EQU 0
GPDCON_Val EQU 0x00000000
GPDUP_Val EQU 0x00000000 ;埠D上拉開啟
;埠E
GPE_SETUP EQU 0
GPECON_Val EQU 0x00000000
GPEUP_Val EQU 0x00000000 ;埠E上拉開啟
;埠F
GPF_SETUP EQU 0
GPFCON_Val EQU 0x00000000
GPFUP_Val EQU 0x00000000 ;埠F上拉開啟
;埠G
GPG_SETUP EQU 0
GPGCON_Val EQU 0x00000000
GPGUP_Val EQU 0x00000000 ;埠G上拉開啟
;埠H
GPH_SETUP EQU 0
GPHCON_Val EQU 0x00000000
GPHUP_Val EQU 0x00000000 ;埠H上拉開啟
;埠J
GPJ_SETUP EQU 0
GPJCON_Val EQU 0x00000000
GPJUP_Val EQU 0x00000000 ;埠J上拉開啟
;----------------------- 這才是真正的程式開始的地方,前邊都是一些巨集定義和準備工作--------------------------------------------------
;彙編程式資料8位元組對齊
PRESERVE8 ;C和彙編有8位對齊的要求,這個偽指令可以滿足此要求
; 段定義和程式入口點
; 啟動程式碼必須連線到第一個地址才能執行。在MDK配置選項裡邊預設的連線就是從這段程式碼開始的,別輕易改啊
AREA RESET, CODE, READONLY ;定義程式段,段名為RESET,只讀
ARM ;ARM模式執行程式
IF :LNOT::DEF:__EVAL ;這是要引用咱們的編譯器MDK自動生成的兩個符號
IMPORT ||Image
IMPORT ||Image RW$$Length|| ;RW段地址,長度
ENDIF
; 異常向量表
Vectors LDR PC, Reset_Addr ;復位異常, 地址0x0000 0000
LDR PC, Undef_Addr ;未定義異常, 地址0x0000 0004//關於地址,我不確定,因為前面說的是8位元組對齊,是不是0x0000 0008呢
LDR PC, SWI_Addr ;軟體中斷, 地址0x0000 0008
LDR PC, PAbt_Addr ;指令預取中斷,地址0x0000 000C
LDR PC, DAbt_Addr ;資料異常中斷,地址0x0000 0010
IF :DEF:__EVAL ;如果定義了__EVAL變數
DCD 0x4000 ;DCD常用於分配一塊連續的記憶體單元,分配2K空間
ELSE
DCD ||Image
||Image RW$$Length||
ENDIF
LDR PC, IRQ_Addr ;普通中斷
LDR PC, FIQ_Addr ;快中斷
IF :DEF:__RTX
IMPORT SWI_Handler
IMPORT IRQ_Handler_RTX
ENDIF
Reset_Addr DCD Reset_Handler ;Label DCD expr是分配一塊連續的記憶體單元,並用expr初始化,Label是一個標號,代表的是
Undef_Addr DCD Undef_Handler ;所分配的記憶體單元的首地址。這裡實際上是將相應的異常中斷的處理程式的入口地址賦值給前邊的標號
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0 ; Reserved Address
IF :DEF:__RTX
IRQ_Addr DCD IRQ_Handler_RTX
ELSE
IRQ_Addr DCD IRQ_Handler
ENDIF
FIQ_Addr DCD FIQ_Handler ;將快中斷處理函式FIQ_Handler的地址賦值給FIQ_Addr
Undef_Handler B Undef_Handler
IF :DEF:__RTX
ELSE
SWI_Handler B SWI_Handler
ENDIF
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler PROC
EXPORT IRQ_Handler [WEAK]
B .
ENDP
FIQ_Handler B FIQ_Handler
; 復位異常處理程式,每次復位後程序都要從此處開始執行
EXPORT Reset_Handler ;定義一個全域性函式變數
Reset_Handler
;----------------------------------------------------------------------------------------------------
;看門狗配置
;若WT_SETUP不等於0,執行此語句,否則執行下一個語句
;前面已經定義WT_SETUP=1,所以會執行這一段
; ---------------------------------------------------------------------------------------------------
IF WT_SETUP != 0
LDR R0, =WT_BASE ;載入看門狗地址
LDR R1, =WTCON_Val ;載入看門狗控制暫存器資料
LDR R2, =WTDAT_Val ;載入看門狗資料暫存器資料
STR R2, [R0, #WTCNT_OFS] ;將WTDAT_Val配置給看門狗計數暫存器
STR R2, [R0, #WTDAT_OFS] ;將WTDAT_Val配置給看門狗資料暫存器
STR R1, [R0, #WTCON_OFS] ;將WTCON_Val配置給看門狗控制暫存器
ENDIF
;----------------------------------------------------------------------------------------------------
;時鐘設定
;如果邏輯上沒有定義NO_CLOCK_SETUP並且CLOCK_SETUP!=0,則執行下面程式
;前面定義CLOCK_SETUP=0,則會跳過這段程式。
;所以咱們以後可以自己設定時鐘頻率,我是傾向於自己設定的,因為這也是一個知識點,
;並且S3C2440的時鐘配置是一個很重要的知識。在這裡我猜測,既然沒有時鐘初始化,那麼咱們下載到
;SDRAM後,應該是以晶振12MHz來工作的,當然只是猜測,錯了的時候再改
;後面幾個實驗後,我返回來再說一句,我的猜測是對的,系統預設的是不對系統時鐘進行初始化,為了方便,
;在以後的實驗中,我都將CLOCK_SETUP修改為1,這樣子我就不用重寫程式碼了。
;系統初始化之後的結果就是FCLK=300MHz,HCLK=100MHz,PCLK=50MHz
;-----------------------------------------------------------------------------------------------------
IF (:LNOT:(:DEF:NO_CLOCK_SETUP)):LAND:(CLOCK_SETUP != 0)
LDR R0, =CLOCK_BASE ;載入時鐘基地址
LDR R1, =LOCKTIME_Val ;載入PLL鎖定時間計數值
STR R1, [R0, #LOCKTIME_OFS] ;將PLL鎖定時間值配置到PLL鎖定時間計數器
MOV R1, #CLKDIVN_Val
STR R1, [R0, #CLKDIVN_OFS] ;配置時鐘分頻器
LDR R1, =CAMDIVN_Val
STR R1, [R0, #CAMDIVN_OFS] ;配置攝像頭分頻控制暫存器
LDR R1, =MPLLCON_Val
STR R1, [R0, #MPLLCON_OFS] ;配置MPLL配置暫存器
LDR R1, =UPLLCON_Val
STR R1, [R0, #UPLLCON_OFS] ;配置UPLL配置暫存器
MOV R1, #CLKSLOW_Val
STR R1, [R0, #CLKSLOW_OFS] ;配置慢時鐘配置暫存器
LDR R1, =CLKCON_Val
STR R1, [R0, #CLKCON_OFS] ;配置時鐘控制暫存器
ENDIF
;-----------------------------------------------------------------------------------------------------
;儲存控制器設定
;如果沒有定義NO_MC_SETUP且CLOCK_SETUP!=0,則執行下面的程式。
;此處前面定義CLOCK_SETUP=0,也就是說這一段又不執行,真假的,這是要鬧哪樣?
;----------------------------------------------------------------------------------------------------
IF (:LNOT:(:DEF:NO_MC_SETUP)):LAND:(CLOCK_SETUP != 0)
LDR R0, =MC_BASE ;載入儲存控制器基地址
LDR R1, =BWSCON_Val
STR R1, [R0, #BWSCON_OFS] ;配置匯流排寬度和等待控制暫存器
LDR R1, =BANKCON0_Val
STR R1, [R0, #BANKCON0_OFS] ;配置BANK0控制暫存器
LDR R1, =BANKCON1_Val
STR R1, [R0, #BANKCON1_OFS] ;配置BANK1控制暫存器
LDR R1, =BANKCON2_Val
STR R1, [R0, #BANKCON2_OFS] ;配置BANK2控制暫存器
LDR R1, =BANKCON3_Val
STR R1, [R0, #BANKCON3_OFS] ;配置BANK3控制暫存器
LDR R1, =BANKCON4_Val
STR R1, [R0, #BANKCON4_OFS] ;配置BANK4控制暫存器
LDR R1, =BANKCON5_Val
STR R1, [R0, #BANKCON5_OFS] ;配置BANK5控制暫存器
LDR R1, =BANKCON6_Val
STR R1, [R0, #BANKCON6_OFS] ;配置BANK6控制暫存器
LDR R1, =BANKCON7_Val
STR R1, [R0, #BANKCON7_OFS] ;配置BANK7控制暫存器
LDR R1, =REFRESH_Val
STR R1, [R0, #REFRESH_OFS] ;配置DRAM/SDRAM重新整理控制暫存器
MOV R1, #BANKSIZE_Val
STR R1, [R0, #BANKSIZE_OFS] ;配置可調的Bank大小控制暫存器
MOV R1, #MRSRB6_Val
STR R1, [R0, #MRSRB6_OFS] ;配置SDRAM模式控制暫存器
MOV R1, #MRSRB7_Val
STR R1, [R0, #MRSRB7_OFS] ;配置SDRAM模式控制暫存器
ENDIF
;-----------------------------------------------------------------------------------------------------------
;I/O埠配置
;如果沒有定義NO_GP_SETUP且GP_SETUP!=0,則執行下面的程式
;好吧,前邊定義了GP_SETUP=0,我現在想知道,他到底執行什麼??程式碼是我直接複製過來的,
;為什麼我看到有的人的程式碼都定義為1呢?淡定,接著分析
;--------------------------------------------------------------------------------------------------------------
IF (:LNOT:(:DEF:NO_GP_SETUP)):LAND:(GP_SETUP != 0)
IF GPA_SETUP != 0
LDR R0, =GPA_BASE ;配置埠A
LDR R1, =GPACON_Val ;A口有25個口,做I/O時只能做輸出口,不能做輸入口
STR R1, [R0, #GPCON_OFS]
ENDIF
IF GPB_SETUP != 0
LDR R0, =GPB_BASE ;配置埠B
LDR R1, =GPBCON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPBUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
IF GPC_SETUP != 0
LDR R0, =GPC_BASE ;配置埠C
LDR R1, =GPCCON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPCUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
IF GPD_SETUP != 0
LDR R0, =GPD_BASE ;配置埠D功能
LDR R1, =GPDCON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPDUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
IF GPE_SETUP != 0
LDR R0, =GPE_BASE ;配置埠E
LDR R1, =GPECON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPEUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
IF GPF_SETUP != 0
LDR R0, =GPF_BASE ;配置埠F
LDR R1, =GPFCON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPFUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
IF GPG_SETUP != 0
LDR R0, =GPG_BASE ;配置埠G
LDR R1, =GPGCON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPGUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
IF GPH_SETUP != 0
LDR R0, =GPH_BASE ;配置埠H
LDR R1, =GPHCON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPHUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
IF GPJ_SETUP != 0
LDR R0, =GPJ_BASE ;配置埠J
LDR R1, =GPJCON_Val
STR R1, [R0, #GPCON_OFS]
LDR R1, =GPJUP_Val
STR R1, [R0, #GPUP_OFS]
ENDIF
ENDIF
;----------------------------------------------------------------------------------------------------
;將異常向量表複製到內部RAM
;如果定義了RAM_INTVEC就執行下一條程式,我突然看到在咱們S3C2440.s程式碼的開頭給介紹了4個SET標誌,其中一個就是
;RAM_INTVEC,反正在這段程式中沒有定義。要麼就是需要咱們人為定義,要麼就是編譯器自己定義的
;本檔案程式碼中沒有定義,所以不復制
;---------------------------------------------------------------------------------------------------
IF :DEF:RAM_INTVEC
ADR R8, Vectors ; 讀取向量源地址,Vectors是一個標號,在程式的前面可以看到
LDR R9, =IRAM_BASE ; 讀取片上SRAM的基地址
LDMIA R8!, {R0-R7} ; 批量載入異常向量
STMIA R9!, {R0-R7} ; 批量儲存異常向量
LDMIA R8!, {R0-R7} ;批量載入程式入口地址
STMIA R9!, {R0-R7} ; 批量儲存程式入口地址
ENDIF
;---------------------------------------------------------------------------------------------------------
;配置相應模式棧的大小
;下面這一段程式碼主要是設定各個異常模式的堆疊,注意在設定的時候需要禁止IRQ和FIQ
;這段程式碼也是系統復位後執行的第一段程式碼。執行完這段程式碼後系統處於系統模式,
;並且IRQ和FIQ都是禁止的。
;---------------------------------------------------------------------------------------------------------
LDR R0, =Stack_Top ;載入棧頂指標地址,在前邊已經定義
;進入未定義模式,並設定其棧指標,將(Mode_UND | I_Bit | F_Bit)賦值給CPSR_c即CPSR_c即CPSR[7:0]
MSR CPSR_c, #Mode_UND:OR:I_Bit:OR:F_Bit
MOV SP, R0 ;棧頂指標賦值給SP指標
SUB R0, R0, #UND_Stack_Size ;棧頂指標減去未定義棧的大小就等於下一個模式的SP指標位置
;進入資料異常中斷模式,並設定其棧指標
MSR CPSR_c, #Mode_ABT:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #ABT_Stack_Size
;進入FIQ中斷模式,並設定其棧指標
MSR CPSR_c, #Mode_FIQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #FIQ_Stack_Size
;進入IRQ中斷模式,並設定其棧指標
MSR CPSR_c, #Mode_IRQ:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #IRQ_Stack_Size
;進入管理模式,並設定其棧指標
MSR CPSR_c, #Mode_SVC:OR:I_Bit:OR:F_Bit
MOV SP, R0
SUB R0, R0, #SVC_Stack_Size
;進入使用者模式,並設定其棧指標
MSR CPSR_c, #Mode_USR
MOV SP, R0
SUB SL, SP, #USR_Stack_Size
; 進入使用者模式
MSR CPSR_c, #Mode_USR
IF :DEF:__MICROLIB ;如果定義了__MICROLIB
EXPORT __initial_sp ;那麼就宣告__initial_sp
ELSE
MOV SP, R0 ;否則就設定使用者模式棧指標
SUB SL, SP, #USR_Stack_Size
ENDIF
;----------------------------------------------------------------------------------------------
;開始正式進入C程式碼區
;反彙編以後C程式中的main函式名就變成了__main
;-----------------------------------------------------------------------------------------------
IMPORT __main ;宣告__main函式
LDR R0, =__main ;載入__main函式入口地址
BX R0 ;跳轉到__main處
IF :DEF:__MICROLIB ;如果定義了__MICROLIB
EXPORT __heap_base ;則宣告__heap_base
EXPORT __heap_limit ;宣告__heap_limit
ELSE
;--------------------------------------------------------------------------------------------------------
;使用者初始化堆與棧,用於動態申請記憶體使用
;__use_two_region_memory是MDK的庫函式
;__user_initial_stackheap也是一個Z庫函式,它的返回值有
; *堆基址(heap base) -----> R0
; *棧基址(stack base) -----> R1 一般為棧的最高地址
; *堆頂(heap limit) ----> R2
; *棧頂(stack limit) ----> R3
;---------------------------------------------------------------------------------------------------------
AREA |.text|, CODE, READONLY
IMPORT __use_two_region_memory
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem ;堆記憶體起始地址----->R0
LDR R1, =(Stack_Mem + USR_Stack_Size) ;棧起始地址------> R1
LDR R2, = (Heap_Mem + Heap_Size) ;棧頂地址 -----> R3
LDR R3, = Stack_Mem ;棧頂地址 ----->R3
BX LR ;子程式返回
ENDIF
END
至此,將S3C2440.s已經分析完了,也知道他做什麼功能了,挺好的!