⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 vector.s

📁 ARM lED 跑马灯程序及 BOOT LOADER
💻 S
字号:
ModeMask 		EQU	0x1F ;系统模式
SVC32Mode		EQU	0x13 ;管理模式
IRQ32Mode		EQU	0x12 ;中断模式
FIQ32Mode		EQU	0x11 ;快中断模式
User32Mode		EQU	0x10 ;用户模式
Abort32Mode		EQU	0x17 ;异常模式终止
Undef32Mode		EQU	0x1B ;未定义模式
IRQ_BIT			EQU	0x80 ;禁止开启irq
FIQ_BIT			EQU	0x40 ;禁止开启Fiq
	GBLS	MainEntry    ;全局符号,编译的时候用,运行的时候大概就没有用了
MainEntry	SETS	"main" ;设置这个符号的值,预编译啊
	IMPORT	$MainEntry     ;main是c语言里面定义的,外部定义的东西应该要引入

;**********************************************************
;检查是否使用tasm.exe进行编译
	
 GBLL    THUMBCODE ;设置全局逻辑符号
    [ {CONFIG} = 16	;预编译中的#if...#else...#endif相当于,if CONFIG = 16 then
THUMBCODE SETL	{TRUE}
    CODE32   ;使用32位arm代码,编译器就靠这个标志把下面的代码转换成32位代码了
    |        ;else

THUMBCODE SETL	{FALSE}
    ]        ;end if

    [ THUMBCODE  ;if THUMBCODE = TRUE then
    CODE32   ;for start-up code for Thumb mode
    ]        ;end if
 	    
;******************************************************
	AREA	SelfBoot,	CODE,	READONLY ;声明一段,是代码段,名字是SelfBoot,只读属性,个人感觉只读属性是在编译的时候用到,让编译器及时检查到错误,声明这个段则是为了连接器把各个模块的各个段按一定的顺序连接起来,段名可能会在连接器连接参数用到,表明要把这个段放在最开始,是启动段
	
	IMPORT	UDF_INS_VECTOR ;引入外部标号,这个标号是map出来的,map不占空间,只是定义了一系列的标号,而且标号指向的地址是可控的,由map的参数和后续的#联合指定,这是未定义指令异常的处理程序的标号
	IMPORT	SWI_SVC_VECTOR ;同上,软中断处理程序的标号
	IMPORT	INS_ABT_VECTOR ;同上,预取指终止异常的处理程序标号
	IMPORT	DAT_ABT_VECTOR ;同上,数据终止异常处理程序标号
	IMPORT	IRQ_SVC_VECTOR ;同上,IRQ中断处理程序标号
	IMPORT	FIQ_SVC_VECTOR ;同上,FIQ中断处理程序标号			
	;说说标号,标号,就是地址值,所以我说标号指向的地址,好像说的不太确切,表达有问题啊,总之,程序里出现的标号,在编译时期将会全部被替换成偏移地址,为什么说偏移地址啊
;因为还没有确定程序的执行时的位置嘛,在连接时期,这些偏移地址将会由连接器重写,变成确切的运行时地址,更有甚者,变成可重定位的地址
	ENTRY ;入口就是这里了,在ads的一个配置文件里面配置,另外一个boot.s的我不知道是什么意思
	IF :DEF: |ads$version| ;也是预编译指令,这里ads$version是ads的版本号,意思就是说如果这段程序用ads编译的话,就干嘛干嘛,否则就干嘛干嘛,这里我当然是用ads编译的拉,ads这么好用
	ELSE ;如果是用ads编译的话,就什么都不作,直接else
	EXPORT	__main ;如果是用非ads编译器编译的话,就导出一个__main这样的符号,也可以叫做标号吧
__main ;这里定义了一个__main标号,可能是考虑到其他编译器有这个需求
	ENDIF	;	
ResetEntry  ;开机就转到这里,也不一定是转到这里,开机时pc=0x0的,当然要把它烧到0x0才行,RO与这个标号的地址还有些差别,下面说到
	b	SYS_RST_HANDLER ;上面是标号,这里才是代码,跳转到SYS_RST_HANDLER,实际的机器代码只是一个相对于当前PC的偏移,一开机就跳到SYS_RST_HANDLER了
	b	UDF_INS_HANDLER ;
	b	SWI_SVC_HANDLER
	b	INS_ABT_HANDLER
	b	DAT_ABT_HANDLER
	b	.               ;很简单的几个字当前地址,NND,害我找了这么久,这里就是死循环嘛,这个b .可以表示成b PC-8
	b	IRQ_SVC_HANDLER
	b	FIQ_SVC_HANDLER

;******************************************************
	MACRO	;这里是定义了一个宏,宏者,编译时替换
$Label	HANDLER	$Vector ;这里有两个变量$Label,$Vector,就是标号和向量,应该很好理解了,向量就是中断服务入口,一般是map出来的,这个bootloader是没有用到这个宏,但在sysinit.s里map了一些$Vector
$Label   ;
	sub	lr, lr, #4	;看上面,现在时LR=LR-4,中断返回地址直接放到LR里了,下面要用到哦		
	stmfd	sp!, {r0-r3, lr} ;r0-r3,lr寄存器入栈,这个栈,感觉是客栈的意思,我也搞不懂为什么叫堆栈!对了,为什么要r0-r3啊,这个似乎跟c语言函数调用有关,c语言函数的前4个参数是用r0-r3传递的,第5个参数或以后的就会用栈传递,明白没?约定而已
	ldr	r0, =$Vector     ;$Vector就是map出来的入口拉,嘿嘿
	ldr	pc, [r0]         ;可能这里少了一句话mov lr, pc,这里是设计上的巧妙了,2级中断跳转,等一下要说说这个,这里似乎有个小问题,现在跳转到子程序指令,那子程序怎样返回啊,残念,可能忘了一句话,应该在前面插入一句mov lr, pc,我是看下面其他的服务程序想到的
	ldmfd	sp!, {r0-r3, pc}^ ;	出栈,老子要退房了,呵呵,上面是开房啊,但是这里有个小变化哦,lr变成pc了,说白了就是要跳转嘛,中断返回拉
;有个^,是要将异常模式的SPSR复制到CPSR,并且是传回用户模式的寄存器,呵呵,后面那句话我猜是这样的,bootloader运行完以后要进入usr或sys模式,中断一般都是在这两个模式下触发的
;而除了usr和sys模式,其他模式都叫做异常模式,产生异常才进入异常模式嘛,其他时候都是正常模式咯,很明了吧,我是猜的哦,ps:usr和sys模式共用一套物理寄存器	
	MEND  ;结束
	
UDF_INS_HANDLER    ;未定义指令中断入口
	stmfd	sp!, {r0-r3, lr} ;既然知道是未定义指令,那当然是执行了这条指令以后拉,后面返回时不用重新执行了 LR->PC
	ldr	r0, =UDF_INS_VECTOR  ;这个UDF_INS_VECTOR是map出来的,看看上面
	mov	lr, pc  ;想想,这时pc是什么啊,呵呵,就是下面那句话的地址啊ldmfd sp!, {r0-r3, pc}^,子程序返回用吧
	ldr	pc, [r0]
	ldmfd	sp!, {r0-r3, pc}^ ;出栈
SWI_SVC_HANDLER  ;软中断入口
	stmfd	sp!, {r0-r3, lr} ;r0-r3,lr寄存器入栈
	ldr	r0, =SWI_SVC_VECTOR  ;软中断入口地址
	mov	lr, pc
	ldr	pc, [r0]
	ldmfd	sp!, {r0-r3, pc}^
INS_ABT_HANDLER   ;取指终止异常
	sub	lr, lr, #4
	stmfd	sp!, {r0-r3, lr}
	ldr	r0, =INS_ABT_VECTOR
	mov	lr, pc
	ldr	pc, [r0]
	ldmfd	sp!, {r0-r3, pc}^
DAT_ABT_HANDLER  ;数据终止异常
	sub	lr, lr, #4
	stmfd	sp!, {r0-r3, lr}
	ldr	r0, =DAT_ABT_VECTOR
	mov	lr, pc
	ldr	pc, [r0]
	ldmfd	sp!, {r0-r3, pc}^
IRQ_SVC_HANDLER ;IRQ异常
	sub	lr, lr, #4
	stmfd	sp!, {r0-r12, lr}	
	mrs	r0, spsr ;读出spsr
	stmfd	sp!, {r0} ;spsr入栈,可能是要保护一些状态吧
	ldr	r0, =IRQ_SVC_VECTOR
	ldr	pc, [r0]	;奇怪,这里为什么不要返回了,可能是IRQ_SVC_VECTOR处理程序里有返回
FIQ_SVC_HANDLER  ;FIQ异常中断
	sub	lr, lr, #4
	stmfd	sp!, {r0-r12, lr}	
	mrs	r0, spsr
	stmfd	sp!, {r0}
	ldr	r0, =IRQ_SVC_VECTOR
	ldr	pc, [r0]
				
;*******************************************************
SYS_RST_HANDLER ;开机就来到这里了,看看中断向量表的第一句
	mrs	r0, cpsr				;enter svc mode and disable irq,fiq
	bic	r0, r0, #ModeMask
	orr	r0, r0, #(SVC32Mode :OR: IRQ_BIT :OR: FIQ_BIT) ;这种语法,狂汗,不过猜也应该猜到了,里面的OR是预处理指令
	msr	cpsr_c, r0
	
	IMPORT	InitSystem  ;引入外部符号InitSystem,在sysinit.s里,下面要执行啊
	bl	InitSystem	    ;有连接的跳转,我要返回的,里面要设置pll,栈,各bank的时序等,也是很重要的哦,虽然现在也能运行,但是...
	
	adr	r0, ResetEntry  ;伪指令拉,取到标号flash.ResetEntry的地址,注意,这里是相对于当前PC的寻址,跟RO无关,从这里开始,应该就是要设置程序的运行环境了,比如复制全局变量到RW域,初始化ZI域,更有甚者把代码段RO搬到RAM里运行
	ldr	r1,	BaseOfROM   ;这是RO$$Base
	cmp	r0,	r1          ;判断运行时的入口与RO入口是否相等
	ldreq	r0, TopOfROM ;相等的话,就RO$$Limit->r0;
	beq	InitRamData     ; 如果ResetEntry与BaseOfROM相同,就转到初始化RW段不复制
	;TopOfROM DCD |Image$$RO$$Limit是cmp r0, r2 决定了,RO和R2相等才执行,beq InitRam也是一样。  
    ;刚开始的时候,代码自动加载到内部4K SDRAM执行,并且是映射到0×00000000的,所以刚上电时候,ResetEntry和baseofrom并不相等,这说明还没有拷贝到64M的外部SDRAM运行。所以代码根据这个条件就会把RO BASE设置的段拷贝到内存中执行。最后跳转到C语言中的main函数执行。 
    ;这是启动代码最关键的部分。代码是怎么拷贝到内存中运行的
			
	ldr	r2,	=CopyProcBeg ;不相等的话,就要处理一下了,copy程序的起始地址,这里ram.CopyProcBeg似乎跟RO有关的哦,你可以假设一下如果当前程序在flash里运行,但是ro设置在ram里面,会出现什么情况呢
	sub	r1, r2, r1       ;得到ram.CopyProcBeg到RO$$Base的长度->r1,段基址,计算偏移量
	add	r0, r0, r1	     ;这就是实际代码存放的位置的偏移,当然这个偏移是在flash.CopyProcBeg这里 r0为FLASH的起始地址,算出拷备程序在flash中的地址,该值赋给R0
	ldr	r3,	=CopyProcEnd ;	ram.CopyProcEnd->r3 拷备程序的结束地址
     ;将拷贝程序复制到RAM中
0   
	ldmia	r0!, {r4-r7} ;这时,r0指向flash.CopyProcBeg,将以r0所指的连续空间的内容写到R4-R7中 并最后地址写回到R0中
	stmia	r2!, {r4-r7} ;r2指向ram.CopyProcBeg 将以r4-r7的内容写到r2所指的连续空间中 并最后地址写回到R2中
	cmp	r2, r3           ; 比较R2 R3 是否是拷贝到终止地址,把flash.CopyProcBeg-flash.CopyProcEnd这段代码复制到ram.CopyProcBeg-ram.CopyProcEnd
	bcc	%B0	             ;向前跳转到标号0处 直到 R2=R3
	;开始使用FLASH中拷贝程序副本 将所有的剩余的代码复制到RAM中
	ldr	r3, TopOfROM		;RO$$Limit->r3 将程序指针指向 RAM 中拷贝程序的起始地址
	ldr	pc, =CopyProcBeg    ;ram.CopyProcBeg->pc 跳转拉,好像是长跳转哦,超过了32M了,要用ldr
	;本段代码由实际的烧入的地址拷贝到r0-base所指定的位置拷贝copyprocbeg后的代码即stage2的代码
;***********************************************
CopyProcBeg	;这个看名字好像就是叫复制代码段吧,看看就知道了
0	
	ldmia	r0!, {r4-r11} ;这时r0指向flash.CopyProcEnd,呵呵,应为上面用了“!”,r0会自增的嘛
	stmia	r2!, {r4-r11} ;这时r2指向ram.CopyProcEnd
	cmp	r2, r3            ;拷贝代码段,flash.CopyProcEnd-flash.code_end复制到ram.CopyProcEnd-RO$$Limit,不知道这里为什么这样写,先拷贝这段复制程序,再拷贝代码段,一下子复制完不好吗?
	bcc	%B0	              ;好像我想到了,他要在ram里面运行,因为是个循环复制,所以速度会快很多,呵呵,我真聪明
CopyProcEnd             
	
	sub	r1, r2, r3      ;不知道用意是什么,r2不就是等于r3吗,这样r1=0了,可能是这样吧,用bcc的话,r2不一定就等于r3,因为是用r4-r11复制内存,由于复制的内存大小不一定是11-4+1=8的倍数,所以要调整一下位置
	sub	r0, r0, r1		;r0=flash.code_end
	
InitRamData	              ;复制RW的内容
	ldr	r2, BaseOfBSS     ;r2=RW$$Base
	ldr	r3, BaseOfZero	   ;r3=ZI$$Base,ZI是紧跟着RW的,连接器里没有设置ZI的
0
	cmp	r2, r3           ;从flash.data循环复制全局变量到ram.data中
	ldrcc	r1, [r0], #4 ;r1是中转,r0=flash.code_end,好像有点小小的问题,bin的内容好像都是紧靠的,不管RO和RW设置的多分开,在bin里,代码段后面紧接着就是已初始化全局变量段,要验证一下
	strcc	r1, [r2], #4
	bcc	%B0	

	mov	r0,	#0          ;初始化r0,呵呵,因为ZI段的初始值都是0啊
	ldr	r3,	EndOfBSS    ;r3=ZI$$Limit
1	
	cmp	r2,	r3          
	strcc	r0, [r2], #4 ;从上面执行下来,r2=ZI$$Base=RW$Limit
	bcc	%B1			     ;将ZI$$Base-ZI$$Limit全部置0
		 		 		
	ldr	pc,	GotoMain	 ;终于,终于,我要飞升了
	;总之,上面的搬运程序就是要把flash里面的代码数据复制到RO,RW指定的位置去,一般是搬运到ram里面
 
; |-------------------------|->ram开始结束
;;|... |
;;|-------------------------|->ZI$$Limit
 ;|ram.未初始化全局变量段 |
 ;|-------------------------|->RW$$Limit ZI$$Base
 ;|ram.已初始化全局变量段 |
 ;|-------------------------|->RW$$Base
 ;|... |
 ;|-------------------------|->RO$$Limit
 ;|ram.代码段 |
 ;|-------------------------|->0xc000000 ram开始 RO$$Base
 ;|... |
 ;|-------------------------|->flash结束
 ;|... |
 ;|-------------------------|->flash.data结束
 ;|flash.已初始化全局变量段 |
 ;|-------------------------|->代码结束,flash.data开始,flash.code_end
 ;|flash.代码段 |
 ;|-------------------------|->0x0 flash开始 flash.ResetEntry
GotoMain	DCD	$MainEntry 

;***********************************************
	IMPORT	|Image$$RO$$Base|	; ROM code start	
	IMPORT	|Image$$RO$$Limit|	; RAM data starts after ROM program
	IMPORT	|Image$$RW$$Base|	; Pre-initialised variables
	IMPORT	|Image$$ZI$$Base|	; uninitialised variables
	IMPORT	|Image$$ZI$$Limit|	; End of variable RAM space


BaseOfROM	DCD	|Image$$RO$$Base|
TopOfROM	DCD	|Image$$RO$$Limit|
BaseOfBSS	DCD	|Image$$RW$$Base|
BaseOfZero	DCD	|Image$$ZI$$Base|
EndOfBSS	DCD	|Image$$ZI$$Limit|

	EXPORT	GetBaseOfROM
	EXPORT	GetEndOfROM
	EXPORT	GetBaseOfBSS
	EXPORT	GetBaseOfZero
	EXPORT	GetEndOfBSS
	
GetBaseOfROM   ;一些子函数,没有什么用的,也有可能后面用到
	ldr	r0, BaseOfROM
	mov	pc, lr	
GetEndOfROM
	ldr	r0, TopOfROM
	mov	pc,	lr
GetBaseOfBSS
	ldr	r0,	BaseOfBSS
	mov	pc,	lr
GetBaseOfZero
	ldr	r0,	BaseOfZero
	mov	pc,	lr
GetEndOfBSS
	ldr	r0,	EndOfBSS
	mov	pc,	lr
	
;***********************************************	
	
	END

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -