📄 vector.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 + -