您的位置 首页 硬件

IAR下的汇编/单片机发动代码汇编

1、IAR汇编指令SFB和SFESFBSegmentbegin段开始语法格式SFB(segment[{+

1、
IAR汇编指令SFB和SFE
SFB Segment begin 段开端
语法格局
SFB(segment [{+|-} offset])
参数
segment: 可重定位段的段名, 有必要在SFB运用前已界说
offset : 从开端地址的偏移, 是一个可选参数, 当偏移量省掉时, 能够不增加小括号
描绘
SFB 右边能够承受一个操作数, 并且这个操作数有必要是一个可重位段的段名.
这个操作符核算段的首字节地址. 这个操作产生在衔接时.
NAME demo
RSEG CODE
start: DC16 SFB(CODE)
即便上面的代码和多个其他的模块进行衔接, start标号处仍被置为段的首字节地址
语法格局
SFE (segment [{+|-} offset])
参数
segment: 可重定位段的段名, 有必要在SFB运用前已界说
offset : 从开端地址的偏移, 是一个可选参数, 当偏移量省掉时, 能够不增加小括号
描绘
SFE在其右边接纳一个操作数. 操作数有必要是一个可重定位段的段名. SFE操作符将段开端地址和段巨细相加. 这个操作在衔接时产生.
SFE accepts a single operand to its right. The operand must be the name of a relocatable segment. The operator evaluates to the segment start address plus the segment size. This evaluation takes place at linking time.
NAME demo
RSEG CODE
end: DC16 SFE(CODE)
即便当上面的代码被和多个模块想衔接时, end标号依然会被置为段最终一个字节的地址. Even if the above code is linked with many other modules, end will still be set to the address of the last byte of the segment.
段MY_SEGMENT的巨细能够经过以下办法核算而得:
SFE(MY_SEGMENT)-SFB(MY_SEGMENT)
——————————————————————————–
arm中的几种跳转
arm汇编的跳转指令无非是b和ldr。可是假如没有满足了解,他人灵敏的用一下你就犯晕了。
首要咱们要知道两者的两个本质差异:
1、b是方位无关的,ldr不是方位无关的。
2、b的规模只能是+—32MB,而ldr是4GB。
在arm的发动汇编的中止向量表是必定用跳转指令的,可是便是这儿也有许多完结办法:
办法1:
B InitReset ; 0x00 Reset handler
undefvec:
B undefvec ; 0x04 Undefined Instruction
swivec:
B swivec ; 0x08 Software Interrupt
pabtvec:
B pabtvec ; 0x0C Prefetch Abort
dabtvec:
B dabtvec ; 0x10 Data Abort
rsvdvec:
B rsvdvec ; 0x14 reserved
irqvec:
B IRQ_Handler_Entry ; 0x18 IRQ
这个我很简单了解,完结的标号油InitReset和IRQ_Handler_Entry,其他没有完结的在原地跳转。
办法二:
LDR pc, =resetHandler ; Reset
LDR pc, Undefined_Addr ; Undefined instructions
LDR pc, SWI_Addr ; Software interrupt (SWI/SYS)
LDR pc, Prefetch_Addr ; Prefetch abort
LDR pc, Abort_Addr ; Data abort
B . ; RESERVED
LDR pc, =irqHandler ; IRQ
LDR pc, FIQ_Addr ; FIQ
LDR PC,[PC,#0x18]
Undefined_Addr: DCD Undefined_Handler
SWI_Addr: DCD SWI_Handler
Prefetch_Addr: DCD Prefetch_Handler
Abort_Addr: DCD Abort_Handler
FIQ_Addr: DCD FIQ_Handler
咱们留意到两种ldr
一种LDR PC,=label,这时把LDR作为伪指令,他要被翻译成:
LDR PC,[PC,offset_to_label2]
label2:DCD resetHandler
这种label是在程序中符号的了,如resetHandler和irqHandler
还有一种LDR PC,label,这时直接把label地址内存内容copy到PC中
这种label都是 label:DCD label2 ,这些label2能够在任何地方完结
咱们来了解b和ldr两者的不同
1,b是方位无关,由于他的跳转都是相对PC来的,而ldr不是方位无关的,由于他的跳转是依据DCD里边的值,这个值是衔接的时分承认的,他是跟衔接地址有关的
2,b的规模只能是32M,是由于指令里边给偏移的空间只要24bit,而ldr是一个32bit的DCD,所以他是32bit的
留意:像上面办法二
LDR pc, =resetHandler ; Reset
resetHandler:
….
….
假如运转地址是0,那么LDR pc, =resetHandler还在以基址为0的空间运转
可是履行完了,假定装载地址是0x3000000,所以resetHandler的基址不是0,而是0x30000000
所以resetHandler标号今后的指令应该存在以0x3000000为基址的空间,pc越过去了
这种技巧在at91的remap形式常常用到
——————————————————————————–
了解发动代码(ADS) 所谓发动代码,便是处理器在发动的时分履行的一段代码,主要任务是初始化处理器形式,设置仓库,初始化变量等等.由于以上的操作均与处理器体系结构和体系装备密切相关,所以一般由汇编来编写. 具体到S64,发动代码分红两部分,一是与ARM7TDMI内核相关的部分,包含处理器各反常向量的装备,各处理器形式的仓库设置,如有必要,仿制向量到RAM,以便remap之后处理器正确处理反常,初始化数据(包含RW与ZI),最终跳转到Main.二是与处理器外部设备相关的部分,这和厂商的联络比较大.尽管都采用了ARM7TDMI的内核,可是不同的厂家整合了不同的片上外设,需求不同的初始化,其间比较重要的是初始化WDT,初始化各子体系时钟,有必要的话,进行remap.这一部分与一般控制器的初始化相似,因而,本文不作要点描绘. 在进行剖析之前,请承认如下相关概念: S64片上FLASH开端于0x100000,共64kB,片上RAM开端于0x200000,共16kB. S64复位之后,程序会从0开端履行,此刻FLASH被映射到0地址,因而,S64能够获得指令并履行.显着,此刻仍是驻留在0x100000地址.假如运用remap指令,将会把RAM映射到0地址,相同的这时0地址的内容也仅仅RAM的镜像. S64的FLASH能够确保在最差状况时以30MHz进行单周期拜访,而RAM能够确保在最大速度时的单周期拜访. OK,以下开端剖析发动代码. 一,处理器反常 S64将反常向量至于0地址开端的几个直接,这些是必需求处理的.由于复位向量坐落0,也需求一条跳转指令.具体代码如下: RESET B SYSINIT ; Reset B UDFHANDLER ; UNDEFINED B SWIHANDLER ; SWI B PABTHANDLER ; PREFETCH ABORT B DABTHANDLER ; DATA ABORT B . ; RESERVED B VECTORED_IRQ_HANDLER B . ; ADD FIQ CODE HERE UDFHANDLER B . SWIHANDLER B . PABTHANDLER B . DABTHANDLER B . 请留意,B指令经汇编后会替换为当时PC值加上一个修正值(+/-),所以这条指令是代码方位无关的,也便是不论这条指令是在0地址仍是在0x100000履行,都能跳转到指定的方位,而LDR PC,=???将向PC直接装载一个标号的值,请留意,标号在编译往后将被替换为一个与RO相对应的值,也便是说,这样的指令不管在哪里履行,都只会跳转到一个指定的方位.下面举一个具体的比如来阐明两者的差异: 假定有如下程序: RESET B INIT 或许 LDR PC,=INIT … INIT … 其间RESET为开端时的代码,也便是这条代码的偏移为0,设INIT的偏移量为offset.假如将这段程序依照RO=0x1000000编译, 那么B INIT可了解为ADD PC, PC, #offset,而LDR PC,=INIT可被了解为 MOV PC,#(RO+offset) .显着当体系复位时,程序从0开端运转,而0地址有FLASH的副本,履行B INIT将把PC指向坐落0地址处的镜像代码方位,也即INIT;假如履行LDR PC,=INIT将会将PC直接指向坐落FLASH中的原始代码.因而以上两者都能正确运转.下面将RO设置为0x200000,编译后生成代码,仍是得烧写到FLASH中,也便是仍是0x100000,体系复位后从0地址履行,仍是FLASH的副本,此刻履行B INIT,将跳到副本中的INIT方位履行,此处有对应的代码;可是假如履行LDR PC,=INIT,将向PC加载0x200000+offset,这将使得PC跳到RAM中,而此刻由于代码没有仿制,RAM中的指定方位并没有代码,程序无法运转. 二,处理器形式 ARM的处理器可作业于多种形式,不同形式有不同的仓库 ,以下设置各形式及其仓库. 预界说一些参数: MODUSR EQU 0x10 MODSYS EQU 0x1F MODSVC EQU 0x13 MODABT EQU 0x17 MODUDF EQU 0x1B MODIRQ EQU 0x12 MODFIQ EQU 0x11 IRQBIT EQU 0x80 FIQBIT EQU 0x40 RAMEND EQU 0x00204000 ; S64 : 16KB RAM VECTSIZE EQU 0x100 ; UsrStkSz EQU 8 ; size of USR stack SysStkSz EQU 128 ; size of SYS stack SvcStkSz EQU 8 ; size of SVC stack UdfStkSz EQU 8 ; size of UDF stack AbtStkSz EQU 8 ; size of ABT stack IrqStkSz EQU 128 ; size of IRQ stack FiqStkSz EQU 16 ; size of FIQ stack 修正这些值即可修正相应形式仓库的尺度. 以下为各形式代码: SYSINIT ; MRS R0,CPSR BIC R0,R0,#0x1F MOV R2,#RAMEND ORR R1,R0,#(MODSVC :OR: IRQBIT :OR: FIQBIT) MSR cpsr_cxsf,R1 ; ENTER SVC MODE MOV sp,R2 SUB R2,R2,#SvcStkSz ORR R1,R0,#(MODFIQ :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER FIQ MODE MOV sp,R2 SUB R2,R2,#FiqStkSz ORR R1,R0,#(MODIRQ :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER IRQ MODE MOV sp,R2 SUB R2,R2,#IrqStkSz ORR R1,R0,#(MODUDF :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER UDF MODE MOV sp,R2 SUB R2,R2,#UdfStkSz ORR R1,R0,#(MODABT :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER ABT MODE MOV sp,R2 SUB R2,R2,#AbtStkSz ;ORR R1,R0,#(MODUSR :OR: IRQBIT :OR: FIQBIT) ;MSR CPSR_cxsf,R1 ; ENTER USR MODE ;MOV sp,R2 ;SUB R2,R2,#UsrStkSz ORR R1,R0,#(MODSYS :OR: IRQBIT :OR: FIQBIT) MSR CPSR_cxsf,R1 ; ENTER SYS MODE MOV sp,R2 ; 三,初始化变量 编译完结之后,衔接器会生成三个根本的段,分别是RO,RW,ZI,并会在image中次序摆放.显着,RW,ZI在运转开端时并不坐落指定的RW方位,因而有必要初始化 LDR R0,=|Image$$RO$$Limit| LDR R1,=|Image$$RW$$Base| LDR R2,=|Image$$ZI$$Base| 1 CMP R1,R2 LDRLO R3,[R0],#4 STRLO R3,[R1],#4 BLO � MOV R3,#0 LDR R1,=|Image$$ZI$$Limit| 2 CMP R2,R1 STRLO R3,[R2],#4 BLO � 四,仿制反常向量 由于代码于RAM运转时,有显着的速度优势,并且变量能够动态装备,因而能够经过remap将RAM映射到0,使得出现反常时ARM从RAM中获得向量. IMPORT |Image$$RO$$Base| IMPORT |Image$$RO$$Limit| IMPORT |Image$$RW$$Base| IMPORT |Image$$RW$$Limit| IMPORT |Image$$ZI$$Base| IMPORT |Image$$ZI$$Limit| COPY_VECT_TO_RAM LDR R0,=|Image$$RO$$Base| LDR R1,=SYSINIT LDR R2,=0x200000 ; RAM START 0 CMP R0,R1 LDRLO R3,[R0],#4 STRLO R3,[R2],#4 BLO � 这段程序将SYSINIT之前的代码,也便是反常处理函数,悉数仿制到RAM中, 这就意味着不能将RW设置为0x200000,这样会使得向量被冲掉. 四,在RAM中运转 假如有必要,且代码满足小,能够将代码置于RAM中运转,由于RAM中自身没有代码,就需求将代码仿制到RAM中: COPY_BEGIN LDR R0,=0x200000 LDR R1,=RESET ; =|Image$$RO$$Base| CMP R1,R0 ; BLO COPY_END ; ADR R0,RESET ADR R2,COPY_END SUB R0,R2,R0 ADD R1,R1,R0 LDR R3,=|Image$$RO$$Limit| 3 CMP R1,R3 LDRLO R4,[R2],#4 STRLO R4,[R1],#4 BLO � LDR PC,=COPY_END COPY_END 程序首要获得RESET的衔接地址,判别程序是否时是在RAM中运转,办法是与RAM开端地址比较,假如小于,那么就越过代码仿制. 在仿制代码的时分需求留意,在这段程序完毕之前的代码没有必要仿制,由于这些代码都现已履行过了,所以,先获得COPY_END,作为仿制开端地址,然后核算其相对RESET的偏移,然后以RO的值加上这个偏移,便是仿制目的地的开端地址,然后开端仿制. 五,开端主程序 以上过程完结,就能够跳转到main运转 IMPORT Main LDR PC,=Main B . 六,器材初始化 主程序首要要进行器材的初始化,对S64而言,应该先初始化WDT,由于默许状况下,WDT是翻开的,然后是各设备的时钟分配,最终应该remap
——————————————————————————–
RemapSRAM:
MOV R0, #0x40000000 //RAM 区首地址
LDR R1, =Vectors //向量表首地址
#下面一段程序是把从0x00000000 开端的64 个字节(FLASH 中的中止向量表和地址表)搬移到以
#0x40000000 为首地址的RAM 区中
LDMIA R1!, {R2-R9} //把以[R1]为首地址的32 个字节数据装载到R2-R9 中
STMIA R0!, {R2-R9} //把R2-R9 中的数据存入以[R0]为首地址的单元中
LDMIA R1!, {R2-R9} //把以[R1]为首地址的32 个字节数据装载到R2-R9 中
STMIA R0!, {R2-R9} ////把R2-R9 中的数据存入以[R0]为首地址的单元中
——————————————————————————–
ARM的发动代码剖析
信任运用过MDK的朋友都会发现,在新建恣意一个ARM芯片的工程时,MDK开发环境都会主动的在代码中参加一个Startup.s的文件。
咱们不能小看这个代码的功用了,这个代码中便是对应芯片的发动代码了。
本文就以在MDK开发环境下关于LPC2103的发动代码剖析了。
在MDK开发环境下,体系复位后,都是会将ARM芯片切换到体系形式下作业。发动代码会在体系复位后当即履行,其功用如下:
1、界说中止和反常向量。
2、装备CPU时钟源(关于一些设备来说)。
3、运用内存重映射指令复制ROM中的反常向量到RAM中(例如:Atmel)。
4、初始化外部总线控制器和履行内存重映射(假如有必要)。
5、假如有必要,初始化其它外设。
6、为一切的形式预留和初始化仓库。
7、清零数据初始化(仅针对GNU)。
8、跳转到C言语函数履行。
本文仅仅在讨论第一个过程(界说中止和反常向量)。由于这个将会在之后咱们要讲到的在uC/OS-II中进行中止函数的编写有联系,其他过程,假如咱们有爱好,能够到网上去查阅相关材料。
下面代码便是我在MDK中,复制出来的一段初始化反常向量的代码了,具体代码如下:
AREA
RESET, CODE, READONLY
ARM
Vectors
LDRPC, Reset_Addr
LDRPC, Undef_Addr
LDRPC, SWI_Addr
LDRPC, PAbt_Addr
LDRPC, DAbt_Addr
NOP
;LDRPC, IRQ_Addr
LDRPC, [PC, #-0x0FF0]
LDRPC, FIQ_Addr
Reset_Addr DCD Reset_Handler
Undef_Addr DCD Undef_Handler
SWI_Addr DCD SWI_Handler
PAbt_Addr DCD PAbt_Handler
DAbt_Addr DCD DAbt_Handler
DCD 0
IRQ_Addr DCD IRQ_Handler
FIQ_Addr DCD FIQ_Handler
Undef_Handler B Undef_Handler
SWI_Handler B SWI_Handler
PAbt_Handler B PAbt_Handler
DAbt_Handler B DAbt_Handler
IRQ_Handler B IRQ_Handler
FIQ_Handler B FIQ_Handler
下面就进行一步一步代码剖析:
1、
AREA
RESET, CODE, READONLY
ARM
这两段话表达的意思是:在代码段中界说一个段名为RESET的只读段,该代码段中的代码都在ARM指令集下进行。
2、Vectors
LDR PC, Reset_Addr
LDR PC, Undef_Addr
LDR PC, SWI_Addr
LDR PC, PAbt_Addr
LDR PC, DAbt_Addr
NOP
;LDR PC, IRQ_Addr
LDR PC, [PC, #-0x0FF0]
LDR PC, FIQ_Addr
上面的代码是ARM内核界说的反常像量表
反常类型
反常向量地址
该地址中的内容
复位
0x00000000(或0xFFFF0000)
LDR PC, Reset_Addr
未界说反常
0x00000004(或0xFFFF0004)
LDR PC, Undef_Addr
软件中止
0x00000008(或0xFFFF0008)
LDR PC, SWI_Addr
指令预取反常
0x0000000C(或0xFFFF000C)
LDR PC, PAbt_Addr
数据预取反常
0x00000010(或0xFFFF0010)
LDR PC, DAbt_Addr
预留
0x00000014(或0xFFFF0014)
NOP
IRQ中止
0x00000018(或0xFFFF0018)
LDR PC, [PC, #-0x0FF0]
FIQ中止
0x0000001C(或0xFFFF001C)
LDR PC, FIQ_Addr
咱们留意一下,在中止向量地址中有必要保存一条ARM指令集。在对应的反常向量地址中将跳转到对应的反常处理程序。
当然咱们在这儿也能够运用B指令,可是咱们要留意一下,运用LDR指令能够完结±4GB地址空间完结。可是运用B履行跳转的话,就只能在±32M地址空间。所以主张咱们运用LDR指令完结跳转。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/qianrushi/yingjian/259051.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部