无网不进  
软硬件开发

上一节,咱们在建立工程的时候,默认的是使用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已经分析完了,也知道他做什么功能了,挺好的!

posted on 2018-05-10 14:13  无网不进  阅读(381)  评论(0)    收藏  举报