第3章ARM汇编语言程序设计 GNU 汇编ppt课件.ppt
,ADS/SDT IDE开发环境:它由ARM公司开发,使用了CodeWarrior公司的编译器;集成了GNU开发工具的IDE开发环境:它由GNU的汇编器as、交叉编译器gcc、和链接器ld等组成。,汇编语言都具有一些相同的基本特征。一条指令一行。使用标号(label)给内存单元提供名称,从第1列开始书写。指令必须从第2列或能区分标号的地方开始书写。注释跟在指定的注释字符后面(ARM使用的是“;”/”),一直书写到行尾。ARM汇编语言基本的的语句格式如下:symbol instruction|directive|pseudo-instruction;comment 符号 指令、伪指令或伪操作;/注释,符号由大小写字母、数字及下画线组成,符号不能用数字开头。符号区分大小写,同名的大、小写符号会被编译器认为是两个不同的符号。符号在其作用范围内必须唯一。自定义的符号名不能与系统的保留字相同。符号名不应与指令或伪指令同名。,伪操作(Directive)是ARM汇编语言程序里的一些特殊的指令助记符,其作用主要是为完成汇编程序做各种准备工作,对源程序运行汇编程序处理,而不是在计算机运行期间由处理器执行。不同的编译程序所使用的伪操作有所不同。,常量编译控制伪操作,程序代码控制伪操作,宏及条件编译伪指令,其他伪指令,GNU ARM汇编伪操作,Linux汇编行结构任何汇编行都是如下结构::comment:注释LinuxARM汇编中,任何以冒号结尾的标识符都被认为是一个标号,而不一定非要在一行的开始。【例1】定义一个add”的函数,返回两个参数的和。.section.text,“x”.globaladdgivethesymboladdexternallinkageadd:ADDr0,r0,r1addinputargumentsMOVpc,lrreturnfromsubroutineendofprogram,Linux汇编程序中的标号,Linux 汇编程序中的标号 标号只能由az,AZ,09,“.”,”_”等字符组成。当标号为09的数字时为局部标号,局部标号可以重复出现,使用方法如下:标号f:在引用的地方向前的标号 标号b:在引用的地方向后的标号【例2】使用局部符号的例子,一段循环程序 1:subs r0,r0,#1 每次循环使r0=r0-1 bne 1f 跳转到1标号去执行 局部标号代表它所在的地址,因此也可以当作变量或者函数来使用。,Linux汇编程序中的分段,(1).section伪操作用户可以通过.section伪操作来自定义一个段,格式如下:.sectionsection_name,flags,%type,flag_specific_arguments每一个段以段名为开始,以下一个段名或者文件结尾为结束。这些段都有缺省的标志(flags),连接器可以识别这些标志。(与armasm中的AREA相同)。下面是ELF格式允许的段标志a允许段,w可写段 x:执行段例子:.section.mysection 自定义数据段,段名为“.mysection”,预定义段.text、.data、.bss,语法格式.text subsection.data subsection初始化数据.bss subsection 作用.text、.data和.bss将汇编系统预定义的段名编译到相应的代码段、数据段和bss段。注意:源程序中.bss段应该在.text之前。说明bss段通常是指用来存放程序中未初始化的全局变量的一块内存区域 数据段通常是指用来存放程序中已初始化的全局变量的一块内存区域,举例.section.data.section.bss.section.text.global _start _start:,.end,语法格式.end作用 表明源文件的结束,如果该标号之后还有代码,不会被编译到执行文件中,.include语法格式.include filename 作用可以将指定的文件在使用位置处展开,一般是头文件.incbin语法格式.incbin file,skip,count 作用可以将原封不动的一个二进制文件编译到当前文件中。其中,skip表明是从文件开始跳过skip个字节开始读取文件,count是读取的字数,.if、.else/.endif,语法格式.if 条件表达式 代码段1.else 代码段2.endif,.macro、.exitm和.endm,语法格式.macro 宏名 参数名列表 伪操作.macro定义一个宏 宏体.endm.endm表示宏结束 说明如果宏使用参数,那么在宏体中使用该参数时添加前缀“”。宏定义时的参数还可以使用默认值,可以使用.exitm伪指令来退出宏举例,.macro SHIFTLEFT a,b.if b0 MOV a,a,ASR#-b.exitm.endif MOV a,a,LSL#b.endm,.string/.asciz/.ascii,语法格式.string/.asciz/.ascii 表达式,表达式.作用.string/.asciz/.ascii定义多个字符串。注意:ascii伪操作定义的字符串需要自动添加结尾字符0举例.string abcd,hello,.equ、.set,语法格式.equ(.set)常量名,表达式 作用.equ和.set用于为程序中标号定义名称举例.equ abc 3 让abc=3,.global/.globl,语法格式.global/.globl symbol 作用.global和.globl用来定义一个全局的符号,.extern,语法格式.extern label 作用.extern用于声明一个外部标号,.ltorg、.pool,语法格式.ltorg/.pool 作用.ltorg和.pool用于声明一个数据缓冲池的开始,它可以分配很大的空间,伪指令是ARM处理器支持的汇编语言程序里的特殊助记符,它不在处理器运行期间由机器执行,只是在汇编时将被合适的机器指令代替成ARM或Thumb指令,从而实现真正的指令操作。ARM汇编语言伪指令如表3-2所示。,在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。ADR伪指令中的地址是基于PC或寄存器的,当ADR伪指令中的地址是基于PC时,该地址与ADR伪指令必须在同一个代码段中。地址表达式expr的取值范围如下:当地址值是字节对齐时,其取指范围为255B255B;当地址值是字对齐时,其取指范围为1020B1020B。,ARM伪指令小范围的地址读取,ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。,.ADR R0,Delay.Delay MOV R0,r14.,应用示例(源程序):,使用伪指令将程序标号Delay的地址存入R0,.0 x20 ADD r1,pc,#0 x3c.0 x64 MOV r0,r14.,ARM伪指令小范围的地址读取,ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。,.ADR R1,Delay.Delay MOV R0,r14.,应用示例(源程序):,编译后的反汇编代码:,使用伪指令将程序标号Delay的地址存入R0,地址,程序代码,ARM伪指令小范围的地址读取,ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。,.ADR R0,Delay.Delay MOV R0,r14.,应用示例(源程序):,.0 x20 ADD r0,pc,#0 x3c.0 x64 MOV r0,r14.,编译后的反汇编代码:,使用伪指令将程序标号Delay的地址存入R0,ADR伪指令被汇编成一条指令,ARM伪指令小范围的地址读取,ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。,应用示例2(查表):ADR R0,DISP_TAB;加载转换表地址 LDRB R1,R0,R2;使用R2作为参数,进行查表 DISP_TAB:.word 0 xC0,0 xF9,0 xA4,0 xB0,0 x99,0 x92,0 x82,0 xF8,在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。示例:LDR r1,=0 xff;将0 xff读取到r1中;编译后得到MOV r1,0 xff示例:LDR r1,=ADDR;将外部地址ADDR读取到R1中 汇编后将得到:;LDR r1,PC,OFFSET_TO_LPOOL;LPOOL.word ADDR,.text.global _start_start:movr0,#9movr1,#15loop:cmpr0,r1/比较r0 r1subltr1,r1,r0/若r0 r1 则 r1=r1 r0subgtr0,r0,r1/若r1 r0 则 r0=r0 r1bneloop/若 r0 r1不相等则继续循环stop:bstop.end,用预先设定的行标与B、BL结合可以设计各种循环结构。例如:LOOP ADD R0,R0,R1;R0=R0+R1 CMP R0,#3;比较R0和#3 BLS LOOP;if R03 then 跳转到LOOP循环.end,在ARM汇编语言程序中,子程序的调用一般是通过BL指令来实现的。在程序中,使用指令:BL 子程序名 即可完成子程序的调用。该指令在执行时完成如下操作:将子程序的返回地址存放在连接寄存器LR中,同时将程序计数器PC指向子程序的入口点,当子程序执行完毕需要返回调用处时,只需要将存放在LR中的返回地址重新复制给程序计数器PC即可。在调用子程序的同时,也可以完成参数的传递和从子程序返回运算的结果,通常可以使用寄存器R0R3完成。以下是使用BL指令调用子程序的汇编语言源程序的基本结构:BL PRINT_TEXT;跳转到子程序 PRINT_TEXT,并保存PC至LR PRINT_TEXT;子程序入口 MOV PC,LR;子程序运行完毕将PC置为LR,准备返回.end,【例3-3】给出一个输出Hello World的程序。.section HelloWorld,;声明代码段.equ SWI_WriteC 程序源代码结束,堆栈指令初始化INITSTACK:MOV R0,LR;保存返回地址;设置管理模式堆栈MSR CPSR_C,#0 xD3LDR SP,StackSvc;设置中断模式堆栈MSR CPSR_C,#0 xD2LDR SP,StackIrq,C语言的优点是运行速度快、编译效率高、移植性好和可读性强。C语言支持模块化程序设计,支持自顶向下的结构化程序设计方法。因此,在嵌入式程序设计中经常会用到C语言程序设计。嵌入式C语言程序设计是利用基本的C语言知识,面向嵌入式工程实际应用进行程序设计。也就是说它首先是C语言程序设计,因此必须符合C语言基本语法,只是它是面向嵌入式的应用而设计的程序。为了使单独编译的C语言程序和汇编程序之间能够相互调用,必须为子程序之间的调用规定一定的规则。ATPCS就是ARM程序和Thumb程序中子程序调用的基本规则。,PCS即Procedure Call Standard(过程调用规范),ATPCS即ARM-Thumb Procedure Call Standard。ATPCS规定了一些子程序之间调用的基本规则,这些基本规则包括子程序调用过程中寄存器的使用规则,数据栈的使用规则,参数的传递规则。,基本ATPCS规定了在子程序调用时的一些基本规则,包括以下3个方面的内容:各寄存器的使用规则及其相应的名字,数据栈的使用规则,参数传递的规则。相对于其他类型的ATPCS,满足基本ATPCS的程序的执行速度更快,所占用的内存更少。但是它不能提供以下的支持:ARM程序和Thumb程序相互调用,数据以及代码的位置无关的支持,子程序的可重入性,数据栈检查的支持。,1寄存器的使用规则ATPCS中定义的寄存器如表3-3所示。表3-3ATPCS中定义的寄存器其中:R0R3:用于传参,r0用于返回值。R4R11:通用变量寄存器。R12:用作过程调用中间临时过渡寄存器IP。R13:堆栈指针。R14:连接寄存器。R15:PC。另外,R9、R10和R11还有一个特殊作用,分别记为:静态基址寄存器SB,数据栈限制指针SL和桢指针FP。,子程序通过寄存器R0R3来传递参数,这时寄存器可以记作A0A3,被调用的子程序在返回前无须恢复寄存器R0R3的内容。在子程序中,使用R4R11来保存局部变量,这时寄存器R4R11可以记作V1V8。如果在子程序中使用到V1V8的某些寄存器,子程序进入时必须保存这些寄存器的值,在返回前必须恢复这些寄存器的值,对于子程序中没有用到的寄存器则不必执行这些操作。在Thumb程序中,通常只能使用寄存器R4R7来保存局部变量。寄存器R12用作子程序间临时过渡寄存器,记作IP,在子程序的连接代码段中经常会有这种使用规则。,寄存器R13用作数据栈指针,记做SP,在子程序中寄存器R13不能用作其他用途。寄存器SP在进入子程序时的值和退出子程序时的值必须相等。寄存器R14用作连接寄存器,记作LR。它用于保存子程序的返回地址,如果在子程序中保存了返回地址,则R14可用作其他的用途。寄存器R15是程序计数器,记作PC,它不能用作其他用途。ATPCS中的各寄存器在ARM编译器和汇编器中都是预定义的。,栈指针通常可以指向不同的位置。当栈指针指向栈顶元素(即最后一个入栈的数据元素)时,称为FULL栈。当栈指针指向与栈顶元素相邻的一个元素时,称为Empty栈。数据栈的增长方向也可以不同,当数据栈向内存减小的地址方向增长时,称为Descending栈;当数据栈向着内存地址增加的方向增长时,称为Ascending栈。综合这两种特点可以有以下4种数据栈,即FD、ED、FA、EA。ATPCS规定数据栈为FD类型,并对数据栈的操作是8字节对齐的,下面是一个数据栈的示例及相关的名词。,数据栈栈指针(stack pointer):指向最后一个写入栈的数据的内存地址。数据栈的基地址(stack base):指数据栈的最高地址。由于ATPCS中的数据栈是FD类型的,实际上数据栈中最早入栈数据占据的内存单元是基地址的下一个内存单元。数据栈界限(stack limit):数据栈中可以使用的最低的内存单元地址。已占用的数据栈(used stack):数据栈的基地址和数据栈栈指针之间的区域,其中包括数据栈栈指针对应的内存单元。数据栈中的数据帧(stack frames):在数据栈中,为子程序分配的用来保存寄存器和局部变量的区域。,(1)参数个数可变的子程序参数传递规则对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0R3来进行参数传递;当参数超过4个时,还可以使用数据栈来传递参数。在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3;如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈。按照上面的规则,一个浮点数参数可以通过寄存器传递,也可以通过数据栈传递,也可能一半通过寄存器传递,另一半通过数据栈传递。,(2)参数个数固定的子程序参数传递规则对于参数个数固定的子程序,参数传递与参数个数可变的子程序参数传递规则不同,如果系统包含浮点运算的硬件部件,浮点参数将按照下面的规则传递:各个浮点参数按顺序处理;为每个浮点参数分配FP寄存器;分配的方法是,满足该浮点参数需要的且编号最小的一组连续的FP寄存器。第1个整数参数通过寄存器R0R3来传递,其他参数通过数据栈传递。,(3)子程序结果返回规则 结果为一个32位的整数时,可以通过寄存器R0返回。结果为一个64位整数时,可以通过R0和R1返回,依此类推。结果为一个浮点数时,可以通过浮点运算部件的寄存器f0、d0或者s0来返回。结果为一个复合的浮点数时,可以通过寄存器f0fN或者d0dN来返回。对于位数更多的结果,需要通过调用内存来传递。,在编译或汇编时,使用/intework告诉编译器或汇编器生成的目标代码遵守支持ARM程序和Thumb程序混合使用的ATPCS,它用在以下场合:程序中存在ARM程序调用Thumb程序的情况;程序中存在THUMB程序调用ARM程序的情况;需要连接器来进行ARM状态和Thumb状态切换的情况;在下述情况下使用选项nointerwork:程序中不包含Thumb程序;用户自己进行ARM程序和Thumb程序切换。需要注意的是:在同一个C/C+程序中不能同时有ARM指令和Thumb指令。,在嵌入式系统开发中,目前使用的主要编程语言是C和汇编,C+已经有相应的编译器,但是现在使用还是比较少的。在稍大规模的嵌入式软件中,如含有OS,大部分的代码都是用C语言编写的,主要是因为C语言的结构比较好,便于人的理解,而且有大量的支持库。尽管如此,很多地方还是要用到汇编语言,如开机时硬件系统的初始化,包括CPU状态的设定,中断的使能,主频的设定,以及RAM的控制参数及初始化,一些中断处理方面也可能涉及汇编。另外一个使用汇编的地方就是一些对性能非常敏感的代码块,这是不能依靠C编译器的生成代码,而要手工编写汇编,达到优化的目的。而且,汇编语言是和CPU的指令集紧密相连的,作为涉及底层的嵌入式系统开发,熟练对应汇编语言的使用也是必须的。,在C语言中内嵌的汇编指令包含大部分的ARM和Thumb指令,不过其使用与汇编文件中的指令有些不同,存在一些限制,主要有下面几个方面。不能直接向PC寄存器赋值,程序跳转要使用B或者BL指令。在使用物理寄存器时,不要使用过于复杂的C表达式,避免物理寄存器冲突。R12和R13可能被编译器用来存放中间编译结果,计算表达式值时可能将R0到R3、R12及R14用于子程序调用,因此,要避免直接使用这些物理寄存器。一般不要直接指定物理寄存器,而让编译器进行分配。,GCC 内嵌汇编,内嵌汇编语法如下:_asm_(汇编语句模板:输出部分:输入部分:破坏描述部分)共四个部分:汇编语句模板,输出部分,输入部分,破坏描述部分,各部分使用“:”格开,汇编语句模板必不可少,其他三部分可选,如果使用了后面的部分,而前面部分为空,也需 要用“:”格开,相应部分内容为空。例如:移位操作 asm(mov%result,%value,ror#1:result“=r”(y)/*移位结果*/:value“r”(x)/*移位值*/:/*无破坏描述*/);asm(mov%0,%1,ror#1:=r(result):r(value);,GCC 内嵌汇编,汇编语句模板汇编语句模板由汇编语句序列组成,语句之间使用“;”、“n”或“nt”分开。指 令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1,%9。指令中使用占位符表示的操作数,总被视为long型(4个字节),为了增加扩展性,占位符可使用%symbol代替。mov%result,%value,ror#1输出部分 输出部分描述输出操作数,不同的操作数描述符之间用逗号格开,每个操作数描述符由限定字符串和 C 语言变量组成。每个输出操作数的限定字符串必须包含“=”表示他是一个输出操作数。result=r(y),GCC 内嵌汇编,输入部分输入部分描述输入操作数,不同的操作数描述符之间使用逗号格开,每个操作数描述符由限定字符串和C语言表达式或者C语言变量组成。value r(x)破坏描述部分破坏描述符用于通知编译器我们使用了哪些寄存器或内存,由逗号格开的字符串组成,每个字符串描述一种情况,一般是寄存器名;除寄存器外还有“memory”,intvalue_convert(intx)inty;asmvolatile(mov%result,%value,ror#1:result=r(y):valuer(x):);returny;intmain()printf(GCCARMInlineAssemblerCookBookDemo!n);intx=4;printf(callfuncvalue_convertwithinputx:%d,outputy:%dn,x,value_convert(x);return0;,程序编译运行后的输出:,内嵌汇编不用单独编辑汇编语言文件,比较简洁,但是有诸多限制,当汇编的代码较多时一般放在单独的汇编文件中,这时就需要在汇编和C之间进行一些数据的传递,最简便的办法就是使用全局变量。汇编中使用C定义的全局变量。,在C中调用汇编文件中的函数,要做的主要工作有两个,一是在C中声明函数原型,并加extern关键字;二是在汇编中用EXPORT导出函数名,并用该函数名作为汇编代码段的标识,最后用mov pc,lr返回。然后,就可以在C中使用该函数了。从C的角度,并不知道该函数的实现是用C还是汇编。更深的原因是因为C的函数名起到表明函数代码起始地址的作用,这个和汇编的label是一致的。,【例3-6】在C中调用汇编的函数(函数不多于4个参数)。#include extern void strcopy(char*d,const char*s);int main()const char*srcstr=First string-source;char dststr=Second string-destination;/*下面将 dststr 作为数组进行操作*/printf(Before copying:n);printf(%sn%sn,srcstr,dststr);strcopy(dststr,srcstr);printf(After copying:n);printf(%sn%sn,srcstr,dststr);return(0);,.global strcopystrcopy:;R0 指向目的字符串;R1 指向源字符串LDRB R2,R1,#1;加载字节并更新源字符串指针地址STRB R2,R0,#1;存储字节并更新目的字符串指针地址CMP R2,#0;判断是否为字符串结尾BNE strcopy;如果不是,程序跳转到 strcopy 继续复制MOV pc,lr;程序返回,【例3-8】在汇编语言中调用C语言的函数(参数不多于4个)。;prog1_asm.asm.extern prog1_c;声明prog1_c函数.text _start:.global prog1_asmprog1_asm:STR lr,sp,#-4!;保存当前lrldr r0,=0 x1;参数1ldr r1,=0 x2;参数2ldr r2,=0 x3;参数3bl prog1_c;调用C函数LDR pc,sp,#4;将lr装进pc(返回main函数).end,/prog1_c.cvoid prog1_c(int p1,int p2,int p3)printk(%0 x%0 x%0 xrn,p1,p2,p3);/输出参数值/main.cint main()prog1_asm();while(1);,【例3-9】在汇编语言中调用C语言的函数(参数多于4个)。;prog2_asm.asm.extern prog2_c;声明prog2_c函数.section.text _start:.global prog2_asmprog2_asm:STR lr,sp,#-4!;保存当前lrldr r0,=0 x1;参数1ldr r1,=0 x2;参数2ldr r2,=0 x3;参数3ldr r3,=0 x4;参数4ldr r4,=0 x6,str r4,sp,#-4!;参数6入栈ldr r4,=0 x5str r4,sp,#-4!;参数5入栈bl prog2_cADD sp,sp,#4;清除栈中参数5,本语句执行完后sp指向参数6ADD sp,sp,#4;清除栈中参数6,本语句执行完后sp指向lrLDR pc,sp,#4;将lr装进pc(返回main函数).end,/prog2_c.cvoid prog2_c(int p1,int p2,int p3,int p4,int p5,int p6)printk(%0 x%0 x%0 x%0 x%0 x%0 xrn,p1,p2,p3,p4,p5,p6);/输出参数值/main.cint main()prog2_asm();while(1);,1用汇编语言设计程序实现10!(10的阶乘)。2实现字符串的逆序复制 TEXT1=HELLO=TEXT2=OLLEH。3用调用子程序的方法实现1!+2!+3!+10!。4什么是内嵌汇编?使用内嵌汇编时需要注意什么?5C语言与汇编语言混合编程时的参数传递规则有哪些?,启动程序设计,启动代码,启动代码内容启动代码工作流程,启动代码内容,(1)启动代码简介,广州致远电子有限公司为LPC2000系列芯片编写的启动代码由3个文件组成。,(1)startup.s异常向量表定义、各模式堆栈初始化、跳转到C程序main入口等。(2)target.c目标板初始化,如时钟分频、PLL设置、VIC设置等。(3)irq.s用于管理中断嵌套。,(1)startup.s,.text.global _start.global irq_handler;中断向量表_start:b reset ldr pc,_undefined_instruction ldr PC,_software_interrupt LDR PC,_prefetch_abort ldr PC,_data_abort LDR PC,_no_used ldr PC,_irq LDR PC,_fiq_undefined_instruction:.word _undefined_instruction_software_interrupt:.word _software_interrupt_prefetch_abort:.word _prefetch_abort_data_abort:.word _data_abort_no_used:.word _no_used_irq:.word _irq_fiq:.word _fiq,汇编入口,异常向量表,地址跳转表,(1)startup.s,reset:mrs r0,cpsr bic r0,r0,#0 x1f orr r0,r0,#0 xd3 msr cpsr,r0 enable svc mode of cpuinit_stack:ldr r0,stacktop get stack top pointer mov sp,r0 sub r0,r0,#512 512byte for irq mode of stack msr cpsr_c,#0 xd2 mov sp,r0 sub r0,r0,#512 512byte for fiq mode of stack msr cpsr_c,#0 xd1 msr cpsr_c,#0 x10 512byte for user mode of stack mov sp,r0 b mainstacktop:.word stack+4*512.datastack:.space 4*512,启动后运行的程序,启动后运行的程序,(1)startup.s,;普通中断.align5irq_handler:sub lr,lr,#4stmfd sp!,r0-r12,lrbldo_irqldmfd sp!,r0-r12,pc,IRQ处理程序,在相应的C语言中实现,启动代码工作流程,复位后,启动代码工作及内容:,(1)复位后,PC0,根据异常向量表,跳转到复位处理程序。(2)执行复位程序。堆栈初始化 跳到C程序main入。(3)执行用户程序。,复位,(1)ResetInit的地址转入PC,(2)程序跳转到ResetInit,(3)各模式堆栈初始化,启动代码工作流程,(1)ResetInit的地址转入PC,(2)程序跳转到ResetInit,(3)各模式堆栈初始化,(4)进行目标板初始化,启动代码工作流程,(1)ResetInit的地址转入PC,(2)程序跳转到ResetInit,(3)各模式堆栈初始化,(4)进行目标板初始化,(5)跳转到main入口,6.4.2 启动代码工作流程,;ROM_LOAD 为加载区的名称,其后面的0 x00000000 表示加载区的起始地址(存放程序代码的起始地址)ROM_LOAD 0 x0;ROM_EXEC 描述了执行区的地址,放在第一块位置定义ROM_EXEC 0 x00000000;从起始地址开始放置向量表(即Startup.o(vectors,+First),其中Startup.o 为Startup.s 的目标文件);+First表示Vector段放在最前面Startup.o(vectors,+First);接着放置其它代码(即*(+RO),*是通配符,类似WINDOW下搜索用的通配符*(+RO),scatter文件分析.scf,;变量区IRAM 的起始地址为0 x40000000IRAM 0 x40000000;放置Startup.o(MyStacks)Startup.o(MyStacks);+0表示接着上一段,UNINIT 表示不初始化STACKS_BOTTOM+0 UNINIT;放置AREA StackBottom,DATA,NOINITStartup.o(StackBottom),scatter文件分析.scf,