1. 程式人生 > >一起學mini2440裸機開發(二)--MDK自帶的S3C2440.s分析

一起學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

RO$$Length||      ;RO段地址,長度,可以再MDK配置選項Linker看到這兩個的定義
                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

RO$$Length||+\      ;否則分配空間大小為RO輸出區的位元組長度與RW輸出區的位元組長度之和
                        ||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已經分析完了,也知道他做什麼功能了,挺好的!