S3C2440之启动文件

33 篇文章 1 订阅

这是从一个大神手里获取的启动文件的注释,相当丰富,有利于学习汇编和了解ARM的启动过程,

下面一起来看一下程序:

;=========================================
; NAME: 2440INIT.S
; DESC: C start up codes
;       Configure memory, ISR ,stacks
;	Initialize C-variables
; HISTORY:
; 2002.02.25:kwtark: ver 0.0
; 2002.03.20:purnnamu: Add some functions for testing STOP,Sleep mode
; 2003.03.14:DonGo: Modified for 2440.
;=========================================
;板子上电后就会从这里开始执行,主要完成基本初始化,还有判断是从nor还是nand启动,
;再实现把程序搬到SDRAM当中,在搬运成功后再跳到main函数里面执行。
;我们现在开始来看看它的具体代码吧!

;GET和INCLUDE的功能是相同的,功能都是引进一些编译过的文件。
	GET option.inc
	GET memcfg.inc
	GET 2440addr.inc
	
;定义SDRAM工作在Reflesh模式下,SDRAM有两种刷新模式:selfreflesh,autoreflesh。
;后者是在其使用过程当中设置的。
BIT_SELFREFRESH EQU	(1<<22)

;下面是对arm处理器模式寄存器对应的常数进行赋值,arm处理器有一个CPSR寄存器,
;它的后五位决定了处理器处于哪个模式下。可以看出常数的定义都不会超过后5位的。
;Pre-defined constants
USERMODE    EQU 	0x10
FIQMODE     EQU 	0x11
IRQMODE     EQU 	0x12
SVCMODE     EQU 	0x13
ABORTMODE   EQU 	0x17
UNDEFMODE   EQU 	0x1b
MODEMASK    EQU 	0x1f
NOINT       EQU 	0xc0

GPBCON      EQU     0x56000010
GPBDAT		EQU 	0x56000014

;各个异常模式的堆栈
;The location of stacks
UserStack	EQU	(_STACK_BASEADDRESS-0x3800)	;0x33ff4800 ~
SVCStack	EQU	(_STACK_BASEADDRESS-0x2800)	;0x33ff5800 ~
UndefStack	EQU	(_STACK_BASEADDRESS-0x2400)	;0x33ff5c00 ~
AbortStack	EQU	(_STACK_BASEADDRESS-0x2000)	;0x33ff6000 ~
IRQStack	EQU	(_STACK_BASEADDRESS-0x1000)	;0x33ff7000 ~
FIQStack	EQU	(_STACK_BASEADDRESS-0x0)	;0x33ff8000 ~

;------------------------------------------------------------------------------

UND_Stack_Size  EQU     0x00000400   ;
SVC_Stack_Size  EQU     0x00000400
ABT_Stack_Size  EQU     0x00000400
FIQ_Stack_Size  EQU     0x00000400
IRQ_Stack_Size  EQU     0x00000400
USR_Stack_Size  EQU     0x00004000

Stack_Size      EQU     (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \
                         FIQ_Stack_Size + IRQ_Stack_Size + USR_Stack_Size)

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size

Stack_Top       EQU     Stack_Mem + Stack_Size


Heap_Size       EQU     0x00000000

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
Heap_Mem        SPACE   Heap_Size

;-------------------------------------------------------------------------------
;这一段是统一arm的工作状态和对应的软件编译方式(16位编译环境使用tasm.exe编译)。
;arm处理器的工作状态分为两种:32位,arm执行字对齐的arm指令集;16位,arm执行半字
;对齐的Thumb指令集。不同的工作状态,编译方式也不一样。所以下面的程序就是判断
;arm的工作方式来确定它的编译方式。
;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.
	GBLL    THUMBCODE ;定义THUMBCODE 这个变量GBLL 声明一个全局逻辑变量并初始化为{FALSE}
	[ {CONFIG} = 16   ;"["表示"if","|"表示"else","]"表示"endif",对于CONFIG是在ADS编译中定义的内部变量。
THUMBCODE SETL  {TRUE}
	    CODE32
 		|
THUMBCODE SETL  {FALSE}
    ]                  ;如果ARM是在16位的工作状态的话,就使全局变量THUMBCODE设置为ture。

 		MACRO          ;这个是宏定义的关键字
	MOV_PC_LR          ;作用是子程序返回
 		[ THUMBCODE    ;当目标程序是Thumb时,就要使用BX跳转返回,并转换模式。
	    bx lr          
 		|
;	    mov	pc,lr      ;目标程序是ARM指令集,直接把lr赋给pc就可以了。 
		bx lr
 		]
	MEND               ;宏定义的结束标志。

 		MACRO
	MOVEQ_PC_LR        ;这个是带“相等”条件的子程序返回。和上面说的类似。
 		[ THUMBCODE
        bxeq lr
 		|
	    moveq pc,lr
 		]
	MEND

;在宏定义下面的handlexxx HANDLER handlexxx都会展成以下的程序段,
;这段程序主要把中断服务程序的入口地址传送给pc,在程序的用34字节
;空间来存放中断服务程序的入口地址,每个字空间都会有一个标号,
;以handlerxxx开头的。
;这个宏用于第一次查表过程的实现中断向量的重定向,如果你比较细心
;的话就会发现在_ISR_STARTADDRDSS=0x33ff_ff里定义的第一级中断向量
;表是采用形如Handle***的方式的,而程序的ENTRY处(程序开始处)采用的是
;b Hadnler***的方式。;在这里Handler**就是通过Handler这个宏和Handler***进行联系的。
;这种方式的优点是真正定义的向量数据在内存空间里,而不是在ENTRY处的
;ROM(FLASH)空间里,这样,我们就可以在程序里灵活的改动向量的数据了。

 		MACRO
$HandlerLabel HANDLER $HandleLabel

$HandlerLabel

	sub	sp,sp,#4	            ;decrement sp(to store jump address);先预留空间,为了存储跳转地址。
	
	;把工作寄存器按入堆栈。
	stmfd	sp!,{r0}	        ;PUSH the work register to stack(lr does not push because it return to original address)
	
	;这两句的功能是把中断程序的入口地址先放在中间变量r0处。
	ldr     r0,=$HandleLabel    ;load the address of HandleXXX to r0
	
	;把HandlerXXX所指向的内容放入到r0
	ldr     r0,[r0]	            ;load the contents(service routine start address) of HandleXXX
	
	;把中断服务程序的入口地址按入堆栈。 
	str     r0,[sp,#4]        ;store the contents(ISR) of HandleXXX to stack

	;最后把堆栈中的中断程序入口地址弹给pc寄存器,这样就可以执行相应的中断服务程序了。  
	ldmfd   sp!,{r0,pc}       ;POP the work register and pc(jump to ISR)
	MEND

;S3C2440有两种中断模式:一种有中断向量表的,一种则没有。有表的话实时性比较好。
;当一个外部中断0发生后,程序自动跳转到地址0x20处,0x20地址单元的指令为“ldr pc, = HandlerEINT0”,
;因此程序跳转到HandlerEINT0处执行这个宏操作,就是把外部中断地址赋给PC。

;一个arm程序是由R0,RW,ZI三个段组成。其中R0为代码段,RW是已经初始化的全局变量,
;ZI是未初始化的全局变量,BOOTLOADER要将RW段复制到RAM中并将ZI段清零。

;编译器使用下列段来记录各段的起始地址和结束地址
;|Image$$RO$$Base|(ADS用这种写法), |Image$$ER_ROM1$$RO$$Base|  (MDK用这种写法)  ; RO 段起始地址
;|Image$$RO$$Limit|(ADS用这种写法),|Image$$ER_ROM1$$RO$$Limit| (MDK用这种写法)  ;RO 段结束地址加1
;|Image$$RW$$Base| (ADS用这种写法),|Image$$RW_RAM1$$RW$$Base|  (MDK用这种写法)  ;RW 段起始地址
;|Image$$RW$$Limit| (ADS用这种写法),|Image$$RW_RAM1$$ZI$$Limit|(MDK用这种写法)  ;RW 段结束地址加1
;|Image$$ZI$$Base|  (ADS用这种写法),|Image$$RW_RAM1$$ZI$$Base| (MDK用这种写法)  ; ZI 段起始地址
;|Image$$ZI$$Limit| (ADS用这种写法),|Image$$RW_RAM1$$ZI$$Limit|(MDK用这种写法)  ; ZI 段结束地址加1

	IMPORT  |Image$$ER_ROM1$$RO$$Base|	 ; Base of ROM code
	IMPORT  |Image$$ER_ROM1$$RO$$Limit|  ; End of ROM code (=start of ROM data)
	IMPORT  |Image$$RW_RAM1$$RW$$Base|   ; Base of RAM to initialise
	IMPORT  |Image$$RW_RAM1$$ZI$$Base|   ; Base and limit of area
	IMPORT  |Image$$RW_RAM1$$ZI$$Limit|  ; to zero initialise

;引入外部变量mmu的快速总线模式和同步总线模式两个变量
	IMPORT	MMU_SetAsyncBusMode
	IMPORT	MMU_SetFastBusMode	;hzh
;引入外部标号我们所熟知的main函数
	IMPORT  __main    ; The main entry of mon program

	PRESERVE8
	
;定义arm汇编程序段,段名叫init段,为只读段
	AREA    RESET,CODE,READONLY

;	ENTRY
	
	EXPORT	__ENTRY     ;导出__ENTRY标号
__ENTRY
ResetEntry
	;1)The code, which converts to Big-endian, should be in little endian code.
	;2)The following little endian code will be compiled in Big-Endian mode.
	;  The code byte order should be changed as the memory bus width.
	;3)The pseudo instruction,DCD can not be used here because the linker generates error.
	
	;判断模式改变是否定义过(ASSERT是伪指令,:DEF:lable判断lable是否定义过了)
	ASSERT	:DEF:ENDIAN_CHANGE
	[ ENDIAN_CHANGE						;如果ENDIAN_CHANGE为真,则执行以下
	    ASSERT  :DEF:ENTRY_BUS_WIDTH    ;判断是否定义了总线宽度
	    [ ENTRY_BUS_WIDTH=32            ;如果存储器是32位的总线宽度
		b	ChangeBigEndian                 ;DCD 0xea000007
	    ]

	    [ ENTRY_BUS_WIDTH=16            ;如果存储器是16位的总线宽度
		andeq	r14,r7,r0,lsl #20           ;DCD 0x0007ea00
	    ]

	    [ ENTRY_BUS_WIDTH=8             ;如果是存储器是8位总线宽度
		streq	r0,[r0,-r10,ror #1]         ;DCD 0x070000ea
	    ]
	|                                   ;如果总线宽度没有定义的话,就直接跳转到复位中断
	    b	ResetHandler                ;程序执行的地跳跳转指令
    ]
	b	HandlerUndef  	                ;handler for Undefined mode
	b	HandlerSWI	                    ;handler for SWI interrupt
	b	HandlerPabort	                ;handler for PAbort
	b	HandlerDabort	                ;handler for DAbort
	b	.		                        ;reserved
	b	HandlerIRQ	                    ;handler for IRQ interrupt
	b	HandlerFIQ	                    ;handler for FIQ interrupt

;@0x20
	b	EnterPWDN	    ; Must be @0x20.  ;进入powerdown模式
;以上8条跳转指令,是8个异常中断处理向量,一定要按照顺序排好,
;据我了解,每次出现异常的话,是由硬件自行查表的。

ChangeBigEndian
;@0x24
	[ ENTRY_BUS_WIDTH=32
	    DCD	0xee110f10	               ;0xee110f10 => mrc p15,0,r0,c1,c0,0
	    DCD	0xe3800080	               ;0xe3800080 => orr r0,r0,#0x80;  //Big-endian
	    DCD	0xee010f10	               ;0xee010f10 => mcr p15,0,r0,c1,c0,0
	]
	[ ENTRY_BUS_WIDTH=16
	    DCD 0x0f10ee11
	    DCD 0x0080e380
	    DCD 0x0f10ee01
	]
	[ ENTRY_BUS_WIDTH=8
	    DCD 0x100f11ee
	    DCD 0x800080e3
	    DCD 0x100f01ee
    ]
	DCD 0xffffffff  ;swinv 0xffffff is similar with NOP and run well in both endian mode.
	DCD 0xffffffff
	DCD 0xffffffff
	DCD 0xffffffff
	DCD 0xffffffff
	b ResetHandler
	
HandlerFIQ      HANDLER HandleFIQ
HandlerIRQ      HANDLER HandleIRQ
HandlerUndef    HANDLER HandleUndef
HandlerSWI      HANDLER HandleSWI
HandlerDabort   HANDLER HandleDabort
HandlerPabort   HANDLER HandlePabort

;下面这段程序很重要,他是实现第二次查表的程序。arm把所有中断都归为一个IRQ和一个FIRQ中断异常,
;我们为了要知道具体的中断,从而才可以跳到中断对应的中断服务程序。
IsrIRQ
	sub	sp,sp,#4         ;reserved for PC  ;保留pc寄存器的值
	stmfd	sp!,{r8-r9}    ;把r8 r9按入堆栈

	ldr	r9,=INTOFFSET    ;把中断偏移INTOFFSET的地址装入r9里面
	ldr	r9,[r9]          ;取出INTOFFSET单元里面的值给r9
	ldr	r8,=HandleEINT0  ;向量表的入口地址赋给r8
	add	r8,r8,r9,lsl #2  ;求出具体中断向量的地址
	ldr	r8,[r8]          ;中断向量里面存储的中断服务程序的入口地址赋给r8
	str	r8,[sp,#8]       ;按入堆栈
	ldmfd	sp!,{r8-r9,pc} ;堆栈弹出,跳转到相应的中断服务程序


	LTORG                 ;声明文字池
;板子上电后就,程序就执行0x00处的b ResetHandler
;=======
; ENTRY
;=======
ResetHandler
	ldr	r0,=WTCON        ;关闭看门狗; watch dog disable 
	ldr	r1,=0x0
	str	r1,[r0]

	ldr	r0,=INTMSK
	ldr	r1,=0xffffffff   ;关闭所有中断;all interrupt disable
	str	r1,[r0]

	ldr	r0,=INTSUBMSK
	ldr	r1,=0x7fff		  ;关闭所有子中断;all sub interrupt disable
	str	r1,[r0]         

	;[ {FALSE}
	; Led_Display
	ldr	r0,=GPBCON
	ldr	r1,=0x15400
	str	r1,[r0]           ;使GPB10~GPB4为输出口,GPB3~GPB0为输入口
	ldr	r0,=GPBDAT
	ldr	r1,=0x3F
	str	r1,[r0]

	ldr	r0,=GPBDAT
	;ldr	r1,=0xFFFF
	ldr r1,=0x0
	str	r1,[r0]          ;使GPB10~GPB4输出为低电平,GPB3~GPB0输入为低电平
	;]

;LOCKTIME是pll的lock time计数器。为了减少pll的lock time,调整LOCKTIME寄存器。
	;To reduce PLL lock time, adjust the LOCKTIME register.
	ldr	r0,=LOCKTIME
	
	;赋给这个值后,UPLL和MPLL的locktime的值都会设定好了。具体为什么是设定这个值,你就去问问三星公司吧。
	ldr	r1,=0xffffff
	str	r1,[r0]

    [ PLL_ON_START    ;设置CLKDIVN的值在PLL锁存时间之后有效。
	; Added for confirm clock divide. for 2440.
	; Setting value Fclk:Hclk:Pclk
	ldr	r0,=CLKDIVN
	ldr	r1,=CLKDIV_VAL		; 0=1:1:1, 1=1:1:2, 2=1:2:2, 3=1:2:4, 4=1:4:4, 5=1:4:8, 6=1:3:3, 7=1:3:6.
	str	r1,[r0]
;	MMU_SetAsyncBusMode and MMU_SetFastBusMode over 4K, so do not call here
;	call it after copy, hzh
;   可以看出是对FCLK、PCLK以及HCLK三者的比率设置。只要通过对CLKDIVN执行操作就可以得到相应需要的比率了。
;	[ CLKDIV_VAL>1 		;如果 Fclk:Hclk不是1:1的话执行下面; means Fclk:Hclk is not 1:1.
;	bl MMU_SetAsyncBusMode
;	|
;	bl MMU_SetFastBusMode	; default value.
;	]
	;program has not been copied, so use these directly, hzh
	[ CLKDIV_VAL>1 		    ; means Fclk:Hclk is not 1:1.
	mrc p15,0,r0,c1,c0,0
	orr r0,r0,#0xc0000000   ;R1_nF:OR:R1_iA
	mcr p15,0,r0,c1,c0,0
	|
	mrc p15,0,r0,c1,c0,0
	bic r0,r0,#0xc0000000   ;R1_iA:OR:R1_nF
	mcr p15,0,r0,c1,c0,0
	]
	;这里可以看出,如果FCLK:HCLK不是1:1的关系的话,就要转成异步总线模式。反之,如果是这个比例关系的话,就转成快速总线模式。
	
	;配置 UPLL  ;Configure UPLL Fin=12.0MHz UFout=48MHz
	ldr	r0,=UPLLCON     
	ldr	r1,=((U_MDIV<<12)+(U_PDIV<<4)+U_SDIV) ;这里就是非常熟悉的PMS啦,Fin = 12.0MHz, UCLK = 48MHz
	str	r1,[r0]
	nop	; Caution: After UPLL setting, at least 7-clocks delay must be inserted for setting hardware be completed.
	nop
	nop
	nop
	nop
	nop
	nop

	;配置 MPLL
    ;Configure MPLL Fin=12.0MHz MFout=304.8MHz一定要使最后的频率为16.9344MHz,不然你甭想用USB接口了 
	ldr	r0,=MPLLCON       ;对MPLL进行配置
	ldr	r1,=((M_MDIV<<12)+(M_PDIV<<4)+M_SDIV)  ;Fin=16.9344MHz
	str	r1,[r0]
    ]

  ;判断是否是从休眠模式唤醒的,对GSTATUS2[2]的检测就可以判断出是否从休眠模式唤醒的。
	;Check if the boot is caused by the wake-up from SLEEP mode.
	ldr	r1,=GSTATUS2
	ldr	r0,[r1]
	tst	r0,#0x2
	
	;In case of the wake-up from SLEEP mode, go to SLEEP_WAKEUP handler.
	bne	WAKEUP_SLEEP      ;如果是的话就跳转。

	EXPORT StartPointAfterSleepWakeUp   ;定义一个外部的StartPointAfterSleepWakeUp
StartPointAfterSleepWakeUp

;=============================================================================== 
;设置内存控制器等寄存器的值,因为这些寄存器是连续排列的,所以采用如下办法对这些 
;寄存器进行连续设置.其中用到了SMRDATA的数据,这在代码后面有定义 
;=============================================================================== 
	;Set memory control registers
 	;ldr	r0,=SMRDATA
 	adrl	r0, SMRDATA	;be careful!, hzh
	ldr	r1,=BWSCON	;BWSCON Address
	;SMRDATA数据的结束地址,共有52字节的数据 
	add	r2, r0, #52	;End address of SMRDATA

0
	ldr	r3, [r0], #4
	str	r3, [r1], #4
	cmp	r2, r0
	bne	%B0
;上面这段代码的作用就是设置存储控制器。在代码的后面有一个SMRDATA的数据区,
;用r0来定义它的起始地址,用r2来定义它的结束地址。r3是代表那13个存储控制器.
;代码很明显,就是把内存的数据赋给这13个存储控制器里面的。


;================================================================================ 
;如果 EINT0 产生(这中断就是我们按键产生的), 就清除SDRAM 
;================================================================================ 
; check if EIN0 button is pressed

  ldr	r0,=GPFCON
	ldr	r1,=0x0
	str	r1,[r0]           ;对GPF设置为输入的功能
	ldr	r0,=GPFUP
	ldr	r1,=0xff
	str	r1,[r0]           ;禁止上拉电阻

	ldr	r1,=GPFDAT
	ldr	r0,[r1]
  bic	r0,r0,#(0x1e<<1)  ;bic是r0与#(0x1e<<1)的反码按位相与。; bit clear
	tst	r0,#0x1           ;这里就是测试最后一位是否为0,为0时说明是有按键按下了。
	bne %F1               ;当按键0没有被按下的时候,就跳转啦。

	

; Clear SDRAM Start
; 这就是清零内存的代码 
	mov r1,#0
	mov r2,#0
	mov r3,#0
	mov r4,#0
	mov r5,#0
	mov r6,#0
	mov r7,#0
	mov r8,#0
	
	ldr	r9,=0x4000000   ;64MB
	ldr	r0,=0x30000000
0	
	stmia	r0!,{r1-r8}
	subs	r9,r9,#32 
	bne	%B0
;很明显可以看出,程序利用r1~r8这几个寄存器把0x30000000到0x34000000的内存全部清零了。

;Clear SDRAM End

1

 		;Initialize stacks
	bl	InitStacks          ;初始化堆栈

;===========================================================
	;bl	Led_Test			;又是LED,注掉了 

;======================================================================= 
; 下面又有看头了,这个初始化程序好像被名曰hzh的高手改过 
; 能在NOR NAND 还有内存中运行,当然了,在内存中运行最简单了. 
; 在NOR NAND中运行的话都要先把自己拷到内存中. 
; 此外,还记得上面提到的|Image$$RO$$Base|,|Image$$RO$$Limit|...吗? 
; 这就是拷贝的依据了!!! 
;========================================================================= 	
	ldr	r0, =BWSCON
	ldr	r0, [r0]
	
	; ;OM[1:0] != 0, 从NOR FLash启动或直接在内存运行
	ands	r0, r0, #6		;OM[1:0] != 0, NOR FLash boot
	
	;不需要从NAND FLASH启动就在这里跳转啦
	bne	copy_proc_beg		;不用读取NAND FLASH;do not read nand flash
	
	;OM[1:0] == 0,就从NAND FLash启动 
	adr	r0, ResetEntry		;OM[1:0] == 0, NAND FLash boot
	
	; ;再比较入口是否为0地址处 ,如果不是则是用仿真器
	cmp	r0, #0				;if use Multi-ice, 

;========================================================================== 
;如果不是,则表示主板设置了从NAND启动,但这个程序由于其它原因, 
;并没有从NAND启动,这种情况最有可能的原因就是用仿真器. 
;========================================================================== 	
	;仿真器也不需要在NAND FLASH启动
	bne	copy_proc_beg		;do not read nand flash for boot
	;nop

;如果是0才是真正从NAND 启动,因为其4k被复制到0地址开始的stepingstone 内部sram中
; 注意adr得到的是相对地址,非绝对地址 == if use Multi-ice,
;=========================================================== 
nand_boot_beg				; 这一段代码完成从NAND读代码到RAM
	mov	r5, #NFCONF			;首先设定NAND的一些控制寄存器 
	;set timing value
	ldr	r0,	=(7<<12)|(7<<8)|(7<<4)
	str	r0,	[r5]
	;enable control
	ldr	r0, =(0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0)
	str	r0, [r5, #4]
	
	bl	ReadNandID			;按着读取NAND的ID号,结果保存在r5里 
	mov	r6, #0				;r6设初值0. 
	ldr	r0, =0xec73			;期望的NAND ID号
	cmp	r5,	r0              ;这里进行比较
	beq	%F1					;相等的话就跳到下一个1标号处 
	ldr	r0, =0xec75			;这是另一个期望值 
	cmp	r5, r0
	beq	%F1					;相等的话就跳到下一个1标号处
	mov	r6, #1			 
1	
	bl	ReadNandStatus		;读取NAND状态,结果放在r1里
	
	mov	r8, #0				 ;r8设初值0,意义为页号
	ldr	r9, =ResetEntry		 ;r9设初值为初始化程序入口地址
;========================================================================= 
; 注意,在这里使用的是ldr伪指令,而不是上面用的adr伪指令,它加载的是ResetEntry 
; 的决对地址,也就是我们期望的RAM中的地址,在这里,它和|Image$$RO$$Base|一样 
; 也就是说,我如我们编译程序时RO BASE指定的地址在RAM里,而把生成的文件拷到 
; NAND里运行,由ldr加载的r9的值还是定位在内存. 
;========================================================================= 


2	
	ands	r0, r8, #0x1f	 ;凡r8为0x1f(32)的整数倍-1,eq有效,ne无效 
	bne		%F3				 ;这句的意思是对每个块(32页)进行检错 
	mov		r0, r8			 ;r8->r0
	bl		CheckBadBlk		 ;检查NAND的坏区
	cmp		r0, #0			  ;比较r0和0 
	addne	r8, r8, #32		  ;存在坏块的话就跳过这个坏块
	bne		%F4				  ;没有的话就跳到标     号4处
3	
	mov	r0, r8				  ;当前页号->r0
	mov	r1, r9				  ;当前目标地址->r1 
	bl	ReadNandPage		  ;读取该页的NAND数据到RAM 
	add	r9, r9, #512		  ;每一页的大小是512Bytes ;SDRAM address increase
	add	r8, r8, #1			  ;r8指向下一页;flash page add 1 
4	
	cmp	r8, #256			  ;比较是否读完256页即128KBytes;copy 256 pages  except bad block
	bcc	%B2					  ;如果r8小于256(没读完),就返回前面的标号2处 
	
	mov	r5, #NFCONF			  ;DsNandFlash;DsNandFlash
	ldr	r0, [r5, #4]
	bic r0, r0, #1
	str	r0, [r5, #4]
	ldr	pc, =copy_proc_beg	  ;调用copy_proc_beg 
	
;=========================================================== 	
;===========================================================
;Nor Flash操作部分
;在前面也看到copy_proc_beg这个标号出现很多次,
;这个标号下面的代码完成的功能就是把nand flash的内容拷贝到ram当中。
;ADR装载的是当时运行的时候的地址
;LDR装载的是连接时候的绝对地址
;ResetEntry 是一个程序开头的地址,用了ADR,所以生成的是一个偏移量,也就是当时运行
;的时候的地址,如果确实从从nor flash启动的话,那么这个地址就应该为 0,如果这程序
;是用仿真器下载到 0x30000000 的SDRAM中运行的话,那么这个地址就应该等于 0x30000000
copy_proc_beg
	adr	r0, ResetEntry
	ldr	r2, BaseOfROM
	
;接着代码判断刚才的两个地址是否相等。我们可以分两种情况来看
;1,如果从norflash启动,那么 ResetEntry=0 BaseOfROM=0x3000000,显然不相同
;2,如果用工具将这个文件加载到 0x30000000 的话,那么这个时候 ResetEntry=0
;BaseOfROM=0x3000000 显然这两个地址是相同的。
;这么做的效果就是:如果从nor flash启动的话,那么就将程序代码 copy 到运行时地址,也
;就是 |Image$$RO$$Base| ,如果地址是相同的,那么就不需要执行 COPY 动作了。
;
	cmp	r0, r2            ;两个进行比较

	ldreq	r0, TopOfROM  ;如果相同的话,为r0赋上R0的结束位置,也是RW的起始位置。 
	                      ;如果相等的话(在内存运行),TopOfROM->r0
	beq	InitRam	          ;同时跳到InitRam 

;========================================================= 
;下面这个是针对代码在NOR FLASH时的拷贝方法 
;功能为把从ResetEntry起,TopOfROM-BaseOfROM大小的数据拷到BaseOfROM 
;TopOfROM和BaseOfROM为|Image$$RO$$Limit|和|Image$$RO$$Base| 
;|Image$$RO$$Limit|和|Image$$RO$$Base|由连接器生成 
;为生成的代码的代码段运行时的起启和终止地址 
;BaseOfBSS和BaseOfZero为|Image$$RW$$Base|和|Image$$ZI$$Base| 
;|Image$$RW$$Base|和|Image$$ZI$$Base|也是由连接器生成 
;两者之间就是初始化数据的存放地放 
;======================================================= 
	ldr r3, TopOfROM      ;以下代码是针对代码在NOR FLASH时的拷贝方法。

	
0	
;R0就是当前代码的开始地址,R2是目标地址,R3-R2 就是代码的总长度,
;这里是包含了RO+RW的总大小,ZI不需要纳入输出段的。
	ldmia	r0!, {r4-r7}
	stmia	r2!, {r4-r7}
	cmp	r2, r3
	bcc	%B0               ;这几段代码的功能就是把ResetEntry的内容搬到BaseOfROM(R0的起始位置,后面有声明的)。

;下面两句是修正
;原来是每4个字节一次赋值操作,这样很容易就产生边界问题,如果代码段的长度不是4的倍数的话
;就会出问题,而且问题的关键不是复制多1-2个的问题,问题的是影响到下面RW段的复制操作的
;所以必须修正,修正办法很简单,先算出现在超出多少,然后减回去 ......这就是2个减法语句的作用了。
;这两句结束之后,R0的数值就是指向代码段的末尾,加载段。所以接着处理RW段就OK了
	sub	r2, r2, r3         ;r2=BaseOfROM-TopOfROM=(-)代码长度
	sub	r0, r0, r2		   ;r0=ResetEntry-(-)代码长度=ResetEntry+代码长度	
	                       ;这里使 ResetEntry的位置往下移,为了后面的数据拷贝做准备。 


;====================================================================================== 
;====================================================================================== 
;接着进行的就是重定位RW段。默认状态RW是紧跟着RO段的,但是是可以以不同的地址定位的。
;首先,他不判断是否有效,一定进行定位操作,也就是说,就算是RW跟在RO后面也做一次重定位
;操作。这个只是写代码的人的喜好。
;下面分析一下这个COPY也有个精巧的地方,那就是,我怎么知道RW段应该从什么地方开始COPY
;要COPY多少,我们从编译器得到的只是运行时地址,而不是实际加载域的地址。怎么得到加载域
;的地址?
;诀窍就在于,这个时候RO的运行域和加载域是一样的!!!(这是针对下载到SDRAM的情况)
;那么 TopOfROM 本来是运行域的 RO段的尾段!注意,这里不是RW运行域的开始段!这时的加载
;域和运行域是相同的,所以这个时候 TopOfROM 其实就是 RW 的加载域!!!要注意我们的前提
;是 运行域和加载域是一样的 时候才有这个结论,RW的加载域是紧跟着RO的,RW的运行域就可以
;简单的从编译器传过来的参数 BaseOfBSS 里面得到,既然加载域和运行域的实际地址都知道了
;那么接着的COPY动作就很简单了。	
InitRam	

	ldr	r2, BaseOfBSS
	ldr	r3, BaseOfZero	
	
0
	cmp	r2, r3
	ldrcc	r1, [r0], #4
	strcc	r1, [r2], #4
	bcc	%B0	              ;可以看出这一段是对ResetEntry里面定义好的数据拷贝到RW段。

;接着就是将所有的ZI 段全部清零,这个很简单,因为只需要知道运行域就OK,
;而这个参数是直接从编译器传递而来的。
	mov	r0,	#0
	ldr	r3,	EndOfBSS
1	
	cmp	r2,	r3
	strcc	r0, [r2], #4
	bcc	%B1               ;如果拷贝完数据后还剩下多余的空间的话,就往里面填充0

;下面这个操作的最后一步,就是从加载域地址(也就是当前地址)跳转到运行域地址,理解好LDR指令的话,
;这里就能理解了,2 这个标号的代表的是运行域地址,这个值只绝对的,编译的时候决定好的,
;所以将这个地址加载到 PC的话实现的就是从 加载域 到运行域 的无缝连接。
;好了,下面接着的地址就是在运行域执行了,通常就是 SDRAM中了,这种COPY技术其实在bootloader
;中是很常用的。如果理解了,那么uboot 和vivi上面的工作原理,其实是一样,不过只是作者不同
;代码的风格也不同,但原理是不变的。
	ldr	pc, =%F2		  ;goto compiler address
2
	
;	[ CLKDIV_VAL>1 		; means Fclk:Hclk is not 1:1.
;	bl	MMU_SetAsyncBusMode
;	|
;	bl MMU_SetFastBusMode	; default value.
;	]
	
	;bl	Led_Test

;===========================================================
;=========================================================== 
; 进入C语言前的最后一步了,就是把我们用说查二级向量表 
; 的中断例程安装到一级向量表(异常向量表)里. 
  	; Setup IRQ handler
	ldr	r0,=HandleIRQ       ;This routine is needed
	ldr	r1,=IsrIRQ	        ;if there is not 'subs pc,lr,#4' at 0x18, 0x1c
	str	r1,[r0]             ;这三条语句很明显就是说明了,HandleIRQ这个中断向量
	                        ;的存储单元被赋上了IsrIRQ标号的地址,这样发生IRQ中
	                        ;断后就会直接去到二级表,去确认具体发生哪个中断。


;	;Copy and paste RW data/zero initialized data
;	ldr	r0, =|Image$$RO$$Limit| ; Get pointer to ROM data
;	ldr	r1, =|Image$$RW$$Base|  ; and RAM copy
;	ldr	r3, =|Image$$ZI$$Base|
;
;	;Zero init base => top of initialised data
;	cmp	r0, r1      ; Check that they are different
;	beq	%F2
;1
;	cmp	r1, r3      ; Copy init data
;	ldrcc	r2, [r0], #4    ;--> LDRCC r2, [r0] + ADD r0, r0, #4
;	strcc	r2, [r1], #4    ;--> STRCC r2, [r1] + ADD r1, r1, #4
;	bcc	%B1
;2
;	ldr	r1, =|Image$$ZI$$Limit| ; Top of zero init segment
;	mov	r2, #0
;3
;	cmp	r3, r1      ; Zero init
;	strcc	r2, [r3], #4
;	bcc	%B3


;    [ :LNOT:THUMBCODE
; 		bl	main	;到这里,我们就看到了进入MAIN函数了。;Do not use main() because ......
 		;ldr	pc, =Main	;hzh
; 		b	.
;    ]

;    [ THUMBCODE	 ;for start-up code for Thumb mode
; 		orr	lr,pc,#1
; 		bx	lr
; 		CODE16   
;可以看到以上代码表示如果arm是在THUMBCODE指令模式下的话,就进行模式转换。 
; 		bl	main	;Do not use main() because ......
; 		b	.
;		CODE32
;    ]
;到这里,我已经把2440init.s的启动代码分析了一遍了。如有任何错误的话,请大家指出!谢谢!
;--------------------------------------------------------------------
           
                LDR     R0, =__main
                BX      R0
;--------------------------------------------------------------------


;function initializing stacks
InitStacks
	;Do not use DRAM,such as stmfd,ldmfd......
	;SVCstack is initialized before
	;Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'
	mrs	r0,cpsr
	bic	r0,r0,#MODEMASK
	orr	r1,r0,#UNDEFMODE|NOINT
	msr	cpsr_cxsf,r1		  ;UndefMode
	ldr	sp,=UndefStack		; UndefStack=0x33FF_5C00

	orr	r1,r0,#ABORTMODE|NOINT
	msr	cpsr_cxsf,r1		;AbortMode
	ldr	sp,=AbortStack		; AbortStack=0x33FF_6000

	orr	r1,r0,#IRQMODE|NOINT
	msr	cpsr_cxsf,r1		;IRQMode
	ldr	sp,=IRQStack		; IRQStack=0x33FF_7000

	orr	r1,r0,#FIQMODE|NOINT
	msr	cpsr_cxsf,r1		;FIQMode
	ldr	sp,=FIQStack		; FIQStack=0x33FF_8000

	bic	r0,r0,#MODEMASK|NOINT
	orr	r1,r0,#SVCMODE
	msr	cpsr_cxsf,r1		;SVCMode
	ldr	sp,=SVCStack		; SVCStack=0x33FF_5800

	;USER mode has not be initialized.

 ;	mov	pc,lr
    bx  lr
	;The LR register will not be valid if the current mode is not SVC mode.
	
;===========================================================
ReadNandID
	mov      r7,#NFCONF
	ldr      r0,[r7,#4]		 ;NFChipEn();
	bic      r0,r0,#2
	str      r0,[r7,#4]
	mov      r0,#0x90		   ;WrNFCmd(RdIDCMD);
	strb     r0,[r7,#8]
	mov      r4,#0			   ;WrNFAddr(0);
	strb     r4,[r7,#0xc]
1							;while(NFIsBusy());
	ldr      r0,[r7,#0x20]
	tst      r0,#1
	beq      %B1
	ldrb     r0,[r7,#0x10]	;id  = RdNFDat()<<8;
	mov      r0,r0,lsl #8
	ldrb     r1,[r7,#0x10]	;id |= RdNFDat();
	orr      r5,r1,r0
	ldr      r0,[r7,#4]		  ;NFChipDs();
	orr      r0,r0,#2
	str      r0,[r7,#4]
	;	mov	pc,lr
    bx  lr	
	
ReadNandStatus
	mov		 r7,#NFCONF
	ldr      r0,[r7,#4]		  ;NFChipEn();
	bic      r0,r0,#2
	str      r0,[r7,#4]
	mov      r0,#0x70		    ;WrNFCmd(QUERYCMD);
	strb     r0,[r7,#8]	
	ldrb     r1,[r7,#0x10]	;r1 = RdNFDat();
	ldr      r0,[r7,#4]		  ;NFChipDs();
	orr      r0,r0,#2
	str      r0,[r7,#4]
	;	mov	pc,lr
    bx  lr

WaitNandBusy
	mov      r0,#0x70		    ;WrNFCmd(QUERYCMD);
	mov      r1,#NFCONF
	strb     r0,[r1,#8]
1							;while(!(RdNFDat()&0x40));	
	ldrb     r0,[r1,#0x10]
	tst      r0,#0x40
	beq		 %B1
	mov      r0,#0			  ;WrNFCmd(READCMD0);
	strb     r0,[r1,#8]
	;	mov	pc,lr
    bx  lr

CheckBadBlk
	mov		r7, lr
	mov		r5, #NFCONF
	
	bic      r0,r0,#0x1f	  ;addr &= ~0x1f;
	ldr      r1,[r5,#4]	  	;NFChipEn()
	bic      r1,r1,#2
	str      r1,[r5,#4]

	mov      r1,#0x50		    ;WrNFCmd(READCMD2)
	strb     r1,[r5,#8]
	mov      r1, #5;6		    ;6->5
	strb     r1,[r5,#0xc]	  ;WrNFAddr(5);(6) 6->5
	strb     r0,[r5,#0xc]	  ;WrNFAddr(addr)
	mov      r1,r0,lsr #8	  ;WrNFAddr(addr>>8)
	strb     r1,[r5,#0xc]
	cmp      r6,#0			    ;if(NandAddr)		
	movne    r0,r0,lsr #16	;WrNFAddr(addr>>16)
	strneb   r0,[r5,#0xc]
	
;	bl		WaitNandBusy	;WaitNFBusy()
	;do not use WaitNandBusy, after WaitNandBusy will read part A!
	mov	r0, #100
1
	subs	r0, r0, #1
	bne	%B1
2
	ldr	r0, [r5, #0x20]
	tst	r0, #1
	beq	%B2	

	ldrb	r0, [r5,#0x10]	;RdNFDat()
	sub		r0, r0, #0xff
	
	mov      r1,#0			;WrNFCmd(READCMD0)
	strb     r1,[r5,#8]
	
	ldr      r1,[r5,#4]		;NFChipDs()
	orr      r1,r1,#2
	str      r1,[r5,#4]
	
;	mov		pc, r7
	bx      r7

ReadNandPage
	mov		 r7,lr
	mov      r4,r1
	mov      r5,#NFCONF

	ldr      r1,[r5,#4]		;NFChipEn()
	bic      r1,r1,#2
	str      r1,[r5,#4]	

	mov      r1,#0			;WrNFCmd(READCMD0)
	strb     r1,[r5,#8]	
	strb     r1,[r5,#0xc]	;WrNFAddr(0)
	strb     r0,[r5,#0xc]	;WrNFAddr(addr)
	mov      r1,r0,lsr #8	;WrNFAddr(addr>>8)
	strb     r1,[r5,#0xc]	
	cmp      r6,#0			;if(NandAddr)		
	movne    r0,r0,lsr #16	;WrNFAddr(addr>>16)
	strneb   r0,[r5,#0xc]
	
	ldr      r0,[r5,#4]		;InitEcc()
	orr      r0,r0,#0x10
	str      r0,[r5,#4]
	
	bl       WaitNandBusy	;WaitNFBusy()
	
	mov      r0,#0			;for(i=0; i<512; i++)
1
	ldrb     r1,[r5,#0x10]	;buf[i] = RdNFDat()
	strb     r1,[r4,r0]		;copy to SDRAM   ********
	add      r0,r0,#1
	bic      r0,r0,#0x10000
	cmp      r0,#0x200
	bcc      %B1
	
	ldr      r0,[r5,#4]		;NFChipDs()
	orr      r0,r0,#2
	str      r0,[r5,#4]
		
;	mov		 pc,r7
	bx       r7

;--------------------LED test
	EXPORT	Led_Test
Led_Test
	mov	r0, #0x56000000
	mov	r1, #0x5500
	str	r1, [r0, #0x50]
0	
	mov	r1, #0x50
	str	r1, [r0, #0x54]
	mov	r2, #0x100000
1
	subs	r2, r2, #1
	bne	%B1
	
	mov	r1, #0xa0
	str	r1, [r0, #0x54]
	mov	r2, #0x100000
2
	subs	r2, r2, #1
	bne	%B2
	b	%B0
;	mov	pc, lr
	bx  lr
;===========================================================

	LTORG			;声明文字池

;GCS0->SST39VF1601
;GCS1->16c550
;GCS2->IDE
;GCS3->CS8900
;GCS4->DM9000
;GCS5->CF Card
;GCS6->SDRAM
;GCS7->unused

SMRDATA DATA
; Memory configuration should be optimized for best performance
; The following parameter is not optimized.
; Memory access cycle parameter strategy
; 1) The memory settings is  safe parameters even at HCLK=75Mhz.
; 2) SDRAM refresh period is for HCLK<=75Mhz.

	DCD (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
	DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))   ;GCS0
	DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))   ;GCS1
	DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))   ;GCS2
	DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))   ;GCS3
	DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))   ;GCS4
	DCD ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))   ;GCS5
	DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))    ;GCS6
	DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))    ;GCS7
	DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Tsrc<<18)+(Tchr<<16)+REFCNT)

	DCD 0x32	    ;SCLK power saving mode, BANKSIZE 128M/128M

	DCD 0x30	    ;MRSR6 CL=3clk
	DCD 0x30	    ;MRSR7 CL=3clk
	
BaseOfROM	DCD	|Image$$ER_ROM1$$RO$$Base|
TopOfROM	DCD	|Image$$ER_ROM1$$RO$$Limit|
BaseOfBSS	DCD	|Image$$RW_RAM1$$RW$$Base|
BaseOfZero	DCD	|Image$$RW_RAM1$$ZI$$Base|
EndOfBSS	DCD	|Image$$RW_RAM1$$ZI$$Limit|

	ALIGN
	
;Function for entering power down mode
; 1. SDRAM should be in self-refresh mode.
; 2. All interrupt should be maksked for SDRAM/DRAM self-refresh.
; 3. LCD controller should be disabled for SDRAM/DRAM self-refresh.
; 4. The I-cache may have to be turned on.
; 5. The location of the following code may have not to be changed.

;void EnterPWDN(int CLKCON);
EnterPWDN
	mov r2,r0		;r2=rCLKCON
	tst r0,#0x8		;SLEEP mode?
	bne ENTER_SLEEP

ENTER_STOP
	ldr r0,=REFRESH
	ldr r3,[r0]		;r3=rREFRESH
	mov r1, r3
	orr r1, r1, #BIT_SELFREFRESH
	str r1, [r0]		;Enable SDRAM self-refresh

	mov r1,#16			;wait until self-refresh is issued. may not be needed.
0	subs r1,r1,#1
	bne %B0

	ldr r0,=CLKCON		;enter STOP mode.
	str r2,[r0]

	mov r1,#32
0	subs r1,r1,#1	;1) wait until the STOP mode is in effect.
	bne %B0		;2) Or wait here until the CPU&Peripherals will be turned-off
			;   Entering SLEEP mode, only the reset by wake-up is available.

	ldr r0,=REFRESH ;exit from SDRAM self refresh mode.
	str r3,[r0]

	MOV_PC_LR

ENTER_SLEEP
	;NOTE.
	;1) rGSTATUS3 should have the return address after wake-up from SLEEP mode.

	ldr r0,=REFRESH
	ldr r1,[r0]		;r1=rREFRESH
	orr r1, r1, #BIT_SELFREFRESH
	str r1, [r0]		;Enable SDRAM self-refresh

	mov r1,#16			;Wait until self-refresh is issued,which may not be needed.
0	subs r1,r1,#1
	bne %B0

	ldr	r1,=MISCCR
	ldr	r0,[r1]
	orr	r0,r0,#(7<<17)  ;Set SCLK0=0, SCLK1=0, SCKE=0.
	str	r0,[r1]

	ldr r0,=CLKCON		; Enter sleep mode
	str r2,[r0]

	b .			;CPU will die here.


WAKEUP_SLEEP
	;Release SCLKn after wake-up from the SLEEP mode.
	ldr	r1,=MISCCR
	ldr	r0,[r1]
	bic	r0,r0,#(7<<17)  ;SCLK0:0->SCLK, SCLK1:0->SCLK, SCKE:0->=SCKE.
	str	r0,[r1]

	;Set memory control registers
 	ldr	r0,=SMRDATA	;be careful!, hzh
	ldr	r1,=BWSCON	;BWSCON Address
	add	r2, r0, #52	;End address of SMRDATA
0
	ldr	r3, [r0], #4
	str	r3, [r1], #4
	cmp	r2, r0
	bne	%B0

	mov r1,#256
0	subs r1,r1,#1	;1) wait until the SelfRefresh is released.
	bne %B0

	ldr r1,=GSTATUS3 	;GSTATUS3 has the start address just after SLEEP wake-up
	ldr r0,[r1]

;	mov pc,r0
	bx  r0
;=====================================================================
; Clock division test
; Assemble code, because VSYNC time is very short
;=====================================================================
	EXPORT CLKDIV124
	EXPORT CLKDIV144
	
CLKDIV124
	
	ldr     r0, = CLKDIVN
	ldr     r1, = 0x3		; 0x3 = 1:2:4
	str     r1, [r0]
;	wait until clock is stable
	nop
	nop
	nop
	nop
	nop

	ldr     r0, = REFRESH
	ldr     r1, [r0]
	bic		r1, r1, #0xff
	bic		r1, r1, #(0x7<<8)
	orr		r1, r1, #0x470	; REFCNT135
	str     r1, [r0]
	nop
	nop
	nop
	nop
	nop
;	mov     pc, lr
    bx      lr

CLKDIV144
	ldr     r0, = CLKDIVN
	ldr     r1, = 0x4		; 0x4 = 1:4:4
	str     r1, [r0]
;	wait until clock is stable
	nop
	nop
	nop
	nop
	nop

	ldr     r0, = REFRESH
	ldr     r1, [r0]
	bic		r1, r1, #0xff
	bic		r1, r1, #(0x7<<8)
	orr		r1, r1, #0x630	; REFCNT675 - 1520
	str     r1, [r0]
	nop
	nop
	nop
	nop
	nop
;	mov     pc, lr
	bx      lr

	ALIGN

	AREA RamData, DATA, READWRITE

	^   _ISR_STARTADDRESS		; _ISR_STARTADDRESS=0x33FF_FF00
HandleReset 	#   4
HandleUndef 	#   4
HandleSWI		#   4
HandlePabort    #   4
HandleDabort    #   4
HandleReserved  #   4
HandleIRQ		#   4
HandleFIQ		#   4

;Do not use the label 'IntVectorTable',
;The value of IntVectorTable is different with the address you think it may be.
;IntVectorTable
;@0x33FF_FF20
HandleEINT0		#   4
HandleEINT1		#   4
HandleEINT2		#   4
HandleEINT3		#   4
HandleEINT4_7	#   4
HandleEINT8_23	#   4
HandleCAM		#   4		; Added for 2440.
HandleBATFLT	#   4
HandleTICK		#   4
HandleWDT		#   4
HandleTIMER0 	#   4
HandleTIMER1 	#   4
HandleTIMER2 	#   4
HandleTIMER3 	#   4
HandleTIMER4 	#   4
HandleUART2  	#   4
;@0x33FF_FF60
HandleLCD 		#   4
HandleDMA0		#   4
HandleDMA1		#   4
HandleDMA2		#   4
HandleDMA3		#   4
HandleMMC		#   4
HandleSPI0		#   4
HandleUART1		#   4
HandleNFCON		#   4		; Added for 2440.
HandleUSBD		#   4
HandleUSBH		#   4
HandleIIC		#   4
HandleUART0 	#   4
HandleSPI1 		#   4
HandleRTC 		#   4
HandleADC 		#   4
;@0x33FF_FFA0

; User Initial Stack & Heap
;Arm 系统中常用的段分类说明    .text段是代码段。它用来放程序代码(code)。它通常是只读的。 
;.data段是数据段。它用来存放初始化了的(initailized)全局变量(global)和初始化了的静态变量 (static)。它是可读可写的。 
;.bss段是全局变量数据段。它用来存放未初始化的(uninitailized)全局变量(global)和未初始化的静 态变量(static)。它也是可读可写的。
;bss是英文Block Started by Symbol的缩写。之所以把bss跟data 分开来,是因为系统会为这些bss段的变量的初值清零。 
;.rodata段是常量数据段。它用来存放常量(const)。它也是只读的。
; 在ARM的集成开发环境中,
;1、只读的代码段称为Code段,即上述的.text段。 
;2、只读的常量数据段,被称作RO Data段,即上述的.constdata段。 以上两个段统称为RO段(Read Only),放在ROM或FLASH等非易失性器件中。 
;3、可读可写的初始化了的全局变量和静态变量段,被称作RW Data段(ReadWrite),即上述的.data段 。 
;4、可读可写的未初始化的全局变量和静态变量段,被称作ZI Data段(Zero Init),即上述的.bss段。 
;因为这个段里的变量要被初始化为零,所以叫ZI段。 以上两个段统称为RW段,而在运行时,它必须重新装载到可读可写的RAM中。

                AREA    |.text|, CODE, READONLY

                IMPORT  __use_two_region_memory
                EXPORT  __user_initial_stackheap
__user_initial_stackheap

                LDR     R0, =  Heap_Mem
                LDR     R1, =(Stack_Mem + USR_Stack_Size)
                LDR     R2, = (Heap_Mem +      Heap_Size)
                LDR     R3, = Stack_Mem
                BX      LR

	END

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值