指令系统与程序编制.ppt

上传人:牧羊曲112 文档编号:5766478 上传时间:2023-08-18 格式:PPT 页数:89 大小:1.21MB
返回 下载 相关 举报
指令系统与程序编制.ppt_第1页
第1页 / 共89页
指令系统与程序编制.ppt_第2页
第2页 / 共89页
指令系统与程序编制.ppt_第3页
第3页 / 共89页
指令系统与程序编制.ppt_第4页
第4页 / 共89页
指令系统与程序编制.ppt_第5页
第5页 / 共89页
点击查看更多>>
资源描述

《指令系统与程序编制.ppt》由会员分享,可在线阅读,更多相关《指令系统与程序编制.ppt(89页珍藏版)》请在三一办公上搜索。

1、2,L E C T U R E,指令系统与程序编制,陈子为,对本部分的要求,一般采用C语言开发,应重点掌握用C语言开发嵌入式系统对于初学者和嵌入式应用人才:只需要初步学习和掌握基本的指令系统以及基本的汇编程序设计技术,能够达到一边查找指令手册、一边阅读汇编源代码的程度即可一般启动代码都是采用汇编语言编写的,应能够理解和修改启动代码,1 ARM处理器寻址方式,寻址方式分类,寻址方式是根据指令中给出的地址码字段来实现寻找真实操作数地址的方式。ARM处理器具有8种基本寻址方式。1.寄存器寻址;2.立即寻址;3.寄存器移位寻址;4.寄存器间接寻址;5.基址寻址;6.多寄存器寻址;7.堆栈寻址;8.相对

2、寻址。,操作数的值在寄存器中,指令中的地址码字段指出的是寄存器编号,指令执行时直接取出寄存器值来操作。寄存器寻址指令举例如下:MOV R1,R2;将R2的值存入R1 SUB R0,R1,R2;将R1的值减去R2的值,;结果保存到R0,寻址方式分类寄存器寻址,MOV R1,R2,0 xAA,立即寻址指令中的操作码字段后面的地址码部分即是操作数本身,也就是说,数据就包含在指令当中,取出指令也就取出了可以立即使用的操作数(这样的数称为立即数)。立即寻址指令举例如下:SUBSR0,R0,#1;R0减1,结果放入R0,;并且影响标志位 MOVR0,#0 xFF000;将立即数0 xFF000;装入R0寄

3、存器,寻址方式分类立即寻址,MOV R0,#0 xFF00,0 xFF00,从代码中获得数据,寄存器移位寻址是ARM指令集特有的寻址方式。当第2个操作数是寄存器移位方式时,第2个寄存器操作数在与第1个操作数结合之前,选择进行移位操作。寄存器移位寻址指令举例如下:MOV R0,R2,LSL#3;R2的值左移3位,结果;放入R0,即是R0=R28 ANDS R1,R1,R2,LSL R3;R2的值左移R3位,;然后和R1相“与”操作,结果放入R1,寻址方式分类寄存器移位寻址,MOV R0,R2,LSL#3,0 x08,0 x08,逻辑左移3位,寄存器间接寻址指令中的地址码给出的是一个通用寄存器的编

4、号,所需的操作数保存在寄存器指定地址的存储单元中,即寄存器为操作数的地址指针。寄存器间接寻址指令举例如下:LDRR1,R2;将R2指向的存储单元的数据读出;保存在R1中 SWPR1,R1,R2;将寄存器R1的值和R2指定的存;储单元的内容交换,寻址方式分类寄存器间接寻址,LDR R0,R2,0 xAA,基址寻址就是将基址寄存器的内容与指令中给出的偏移量(4K)相加/减,形成操作数的有效地址。基址寻址用于访问基址附近的存储单元,常用于查表、数组操作、功能部件寄存器访问等。寄存器间接寻址是偏移量为0的基址加偏移寻址。基址寻址指令举例如下(前索引寻址):LDRR2,R3,#0 x0C;读取R3+0

5、x0C地址上的存;储单元的内容,放入R2 STRR1,R0,#-4!;先R0=R0-4,然后把R0的;值寄存到保存到R1指定的存储单元,寻址方式分类基址寻址,LDR R2,R3,#0 x0C,0 xAA,将R3+0 x0C作为地址装载数据,基址寻址指令举例如下:LDRR0,R1,#4;R0=R1,R1R14;后索引基址寻址;ARM这种自动索引机制不消耗额外的时间LDR R0,R1,R2;R0=R1+R2,多寄存器寻址一次可传送几个寄存器值,允许一条指令传送16个寄存器的任何子集或所有寄存器。多寄存器寻址指令举例如下:LDMIAR1!,R2-R7,R12;将R1指向的单元中的数据读出到;R2R7

6、、R12中(R1自动加4)STMIAR0!,R2-R7,R12;将寄存器R2R7、R12的值保存到R0;指向的存储单元中(R0自动加4),寻址方式分类多寄存器寻址,LDR R1!,R2-R4,R6,0 x40000010,堆栈是一个按特定顺序进行存取的存储区,操作顺序为“后进先出”。堆栈寻址是隐含的,它使用一个专门的寄存器(堆栈指针)指向一块存储区域(堆栈),指针所指向的存储单元即是堆栈的栈顶。存储器堆栈可分为两种:向上生长:向高地址方向生长,称为递增堆栈向下生长:向低地址方向生长,称为递减堆栈,寻址方式分类堆栈寻址,0 x12345678,0 x12345678,低地址,高地址,堆栈指针指向

7、最后压入的堆栈的有效数据项,称为满堆栈;堆栈指针指向下一个待压入数据的空位置,称为空堆栈。,0 x12345678,所以可以组合出四种类型的堆栈方式:满递增:堆栈向上增长,堆栈指针指向内含有效数据项的最高地址。指令如LDMFA、STMFA等;空递增:堆栈向上增长,堆栈指针指向堆栈上的第一个空位置。指令如LDMEA、STMEA等;满递减:堆栈向下增长,堆栈指针指向内含有效数据项的最低地址。指令如LDMFD、STMFD等;空递减:堆栈向下增长,堆栈指针向堆栈下的第一个空位置。指令如LDMED、STMED等。,相对寻址是基址寻址的一种变通。由程序计数器PC提供基准地址,指令中的地址码字段作为偏移量,

8、两者相加后得到的地址即为操作数的有效地址。相对寻址指令举例如下:BL SUBR1;调用到SUBR1子程序 BEQ LOOP;条件跳转到LOOP标号处.LOOP MOV R6,#1.SUBR1.,寻址方式分类相对寻址,2、ARM汇编语言程序结构,;文件名:TEST1.S;功能:实现两个寄存器相加;说明:使用ARMulate软件仿真调试 AREAExample1,CODE,READONLY;声明代码段Example1 ENTRY;标识程序入口 CODE32;声明32位ARM指令START MOVR0,#0;设置参数 MOVR1,#10LOOPBLADD_SUB;调用子程序ADD_SUB BLOOP

9、;跳转到LOOPADD_SUB ADDSR0,R0,R1;R0=R0+R1 MOVPC,LR;子程序返回 END;文件结束,使用“;”进行注释,标号顶格写,实际代码段,声明文件结束,;文件名:TEST1.S;功能:实现两个寄存器相加;说明:使用ARMulate软件仿真调试 AREAExample1,CODE,READONLY;声明代码段Example1 ENTRY;标识程序入口 CODE32;声明32位ARM指令START MOVR0,#0;设置参数 MOVR1,#10LOOPBLADD_SUB;调用子程序ADD_SUB BLOOP;跳转到LOOPADD_SUB ADDSR0,R0,R1;R0

10、=R0+R1 MOVPC,LR;子程序返回 END;文件结束,ARM是三地址指令格式,指令的基本格式如下:,S,其中号内的项是必须的,号内的项是可选的。各项的说明如下:,opcode:指令助记符;cond:执行条件;S:是否影响CPSR寄存器的值;Rd:目标寄存器;Rn:第1个操作数的寄存器;operand2:第2个操作数;,例:,ARM指令的基本格式如下:,ARM指令集条件码,S,使用条件码“cond”可以实现高效的逻辑操作(节省跳转和条件语句),提高代码效率。所有的ARM指令都可以条件执行,而Thumb指令只有B(跳转)指令具有条件执行 功能。如果指令不标明条件代码,将默认为无条件(AL)

11、执行。,指令条件码表,条件代码标志,N,Z,C,V,Negative,Zero,Carry,oVerflow,CPSR,指令条件码表,无符号数高、低 有符号数大、小,C代码:If(a b)a+;Elseb+;,对应的汇编代码:CMPR0,R1;R0(a)与R1(b)比较ADDHIR0,R0,#1;若R0R1,则R0=R0+1ADDLSR1,R1,#1;若R01,则R1=R1+1,示例:,4 常用指令介绍,(1)存储器访问指令,Load-store 结构 从存储器中读某个值,操作完后再将其放回存储器中,内核只对存放在寄存器的数据进行处理。ARM处理器是典型的RISC处理器,对存储器的访问只能使用

12、加载和存储指令实现。ARM7处理器是冯诺依曼存储结构,RAM存储空间及I/O映射空间统一编址,除对RAM操作以外,对外围IO、程序数据的访问均要通过加载/存储指令进行。存储器访问指令分为单寄存器操作指令和多寄存器操作指令。,ARM存储器访问指令单寄存器加载 LDR,ARM存储器访问指令单寄存器存储,从寻址方式的地址计算方法分,加载/存储指令有以下4种格式:零偏移。如:LDR Rd,Rn 前索引偏移。如:LDR Rd,Rn,#0 x04!程序相对偏移。如:LDR Rd,labe1 后索引偏移。如:LDR Rd,Rn,#-0 x04注意:必须保证字数据操作的地址是32位对齐的。,LDR和STR指令

13、应用示例:1.加载/存储字和无符号字节指令LDRR2,R5;将R5指向地址的字数据存入R2STRR1,R0,#0 x04;将R1的数据存储到R0+0 x04地址LDRBR3,R2,#-1;将R2指向地址的字节数据存入R3,R2R21STRBR0,R3,-R8 ASR 2;R0-R3-R8/4,存储R0的最低有效字节2.加载/存储半字和有符号字节指令LDRSB R1,R0,R3;将R0+R3地址上的字节数据存入R1,;高24位用符号扩展LDRH R6,R2,#2;将R2指向地址的半字数据存入R6,高16位用0扩展;读出后,R2=R2+2STRH R1,R0,#2!;将R1的半字数据保存到R0+2

14、地址,;只修改低2字节数据,然后R0=R0+2,ARM存储器访问指令单寄存器存储,ARM存储器访问指令多寄存器存取 LDM/STM,多寄存器加载/存储指令可以实现在一组寄存器和一块连续的内存单元之间传输数据。LDM为加载多个寄存器;STM为存储多个寄存器。允许一条指令传送16个寄存器的任何子集或所有寄存器。它们主要用于现场保护、数据复制、常数传递等。,多寄存器加载/存储指令格式如下:LDMcond Rn!,reglist STMcond Rn!,reglistcond:指令执行的条件;模式:控制地址的增长方式,一共有8种模式;!:表示在操作结束后,将最后的地址写回Rn中;reglist:表示寄

15、存器列表,可以包含多个寄存器,它们使用“,”隔开,如R1,R2,R6-R9,寄存器由小到大排列;:可选后缀。允许在用户模式或系统模式下使用。它有以下两个功能:1)若op是LDM且寄存器列表包含R15时,那么除了正常的多寄存器传送外,还将SPSR也复制到CPSR中。这用于异常处理返回,仅在异常模式下使用。2)数据传入或传出的是用户模式下的寄存器,而不是当前模式的寄存器。,多寄存器加载/存储指令的8种模式如下表所示,右边四种为堆栈操作、左边四种为数据传送操作。,进行数据复制时,先设置好源数据指针和目标指针,然后使用块拷贝寻址指令LDMIA/STMIA、LDMIB/STMIB、LDMDA/STMDA

16、、LDMDB/STMDB进行读取和存储。进行堆栈操作操作时,要先设置堆栈指针(SP),然后使用堆栈寻址指令STMFD/LDMFD、STMED/LDMED、STMFA/LDMFA和STMEA/LDMEA实现堆栈操作。,(2)ARM数据处理指令,数据处理指令大致可分为3类:数据传送指令;算术逻辑运算指令;比较指令。数据处理指令只能对寄存器的内容进行操作,而不能对内存中的数据进行操作。所有ARM数据处理指令均可选择使用S后缀,以使指令影响状态标志。,ARM数据处理指令数据传送,注:当后缀S时,这些指令根据结果更新标志N和Z,在计算Operand2时更新标志C,不影响标志V。,ARM数据处理指令数据传

17、送,MOV指令将8位图立即数(参看“第2操作数:#immed_8r常数表达式”)或寄存器传送到目标寄存器(Rd),可用于移位运算等操作。指令格式如下:MOVcondS Rd,operand2 MOV指令举例如下:MOVR11,#0 xF000000B;R1=0 xF000000B MOVR0,R1;R0=R1 MOVSR3,R1,LSL#2;R3=R12,并影响标志位 MOVPC,LR;PC=LR,子程序返回,ARM数据处理指令算术运算,注:这些指令影响N,Z,C和V标志位。,ARM数据处理指令逻辑运算指令,注:当后缀S时,这些指令根据结果更新标志N和Z,在计算Operand2时更新标志C,不

18、影响标志V。,应用示例:ANDS R0,R0,#0 x01;R0=R0R2=R1&R3,ORR R0,R0,#0 x0F;将R0的低4位置1,EORR1,R1,#0 x0F;将R1的低4位取反,BIC R1,R1,#0 x0F;将R1的低4位清零,其它位不变,(3)乘法指令,ARM7TDMI具有三种乘法指令,分别为:32 32位 乘法指令;32 32位 乘加指令;32 32位 结果为64位的乘/乘加指令。,ARM指令乘法指令,MUL R1,R2,R3;R1=R2R3,MLA R1,R2,R3,R0;R1=R2R3+R0,UMULL R0,R1,R5,R8;(R1、R0)=R5R8,UMLAL

19、R0,R1,R5,R8;(R1、R0)=R5R8+(R1、R0),SMULLR2,R3,R7,R6;(R3、R2)=R7R6,SMLALR2,R3,R7,R6;(R3、R2)=R7R6+(R3、R2),(4)分支指令,在ARM中有两种方式可以实现程序的跳转,一种是使用分支指令直接跳转,另一种则是直接向PC寄存器赋值实现跳转。分支指令有以下三种:分支指令B;带链接的分支指令BL;带状态切换的分支指令BX。,ARM指令分支指令,(5)杂项指令,ARM指令集中有三条指令作为杂项指令,在实际应用中这三条指令非常重要。它们如下所示:,ARM杂项指令软中断指令,SWI指令用于产生SWI异常,使得CPU模式

20、变换到管理模式,并且将CPSR保存到管理模式的SPSR中,然后程序跳转到SWI异常入口。不影响条件码标志。该指令主要用于用户程序调用操作系统的系统服务,操作系统在SWI异常处理程序中进行相应的系统服务。,SWIcondimmed_24,SWI指令格式,SWI指令编码,指令执行的条件码,指令传递的参数(24位立即数,其值为02241),执行时CPU忽略该参数。,示例,ARM杂项指令状态寄存器读指令,在ARM处理器中,只有MRS指令可以对状态寄存器CPSR和SPSR进行读操作。通过读CPSR可以了解当前处理器的工作状态。读SPSR寄存器可以了解到进入异常前的处理器状态。该指令不影响条件码。,MRS

21、cond Rd,psr,MRS指令格式,应用示例:MRS R1,CPSR;将CPSR状态寄存器读取,保存到R1中 MRS R2,SPSR;将SPSR状态寄存器读取,保存到R2中,条件代码标志,保留,控制位,溢出标志 oVerflow,进位或借位扩展 Carry,零 Zero,负或小于 Negative,IRQ禁止 Interrupt,FIQ禁止 Fast,状态位 Thumb,模式位 Mode,N,Z,C,V,I,T,F,CPSR/SPSR寄存器的格式,每个异常模式还带有一个程序状态保存寄存器(SPSR),它用于保存在异常发生之前的CPSR。CPSR和SPSR通过特殊指令(MRS、MSR)进行访

22、问。,CPSR_c,CPSR_f,ARM杂项指令状态寄存器写指令,在ARM处理器中,只有MSR指令可以对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。,MSRcond psr_fields,#immed_8r,MSR指令格式1,MSRcond psr_fields,Rm,MSR指令格式2,指令执行的条件码,CPSR或SPSR,指定传送的区域,可以为以下字母(必须小写)的一个或者组合:c 控制域屏蔽字节(psr7.0)x 扩展域屏蔽字节(psr15.8)s 状态域屏蔽字节(p

23、sr23.16)f 标志域屏蔽字节(psr31.24),保存要传送到状态寄存器指定域数据的源寄存器,要传送到状态寄存器指定域的立即数,CPSR_f,CPSR_c,(1),(2),(3),(4),ARM杂项指令状态寄存器写指令,在ARM处理器中,只有MSR指令可以对状态寄存器CPSR和SPSR进行写操作。与MRS配合使用,可以实现对CPSR或SPSR寄存器的读-修改-写操作,可以切换处理器模式、或者允许/禁止IRQ/FIQ中断等。,应用示例1:;子程序:使能IRQ中断ENABLE_IRQ MRS R0,CPSR BIC R0,R0,#0 x80 MSR CPSR_c,R0 MOV PC,LR,应

24、用示例2:;子程序:禁能IRQ中断DISABLE_IRQ MRS R0,CPSR ORR R0,R0,#0 x80 MSR CPSR_c,R0 MOV PC,LR,1.将CPSR寄存器内容读出到R0;,2.修改对应于CPSR中的I控制位;,3.将修改后的值写回 CPSR寄存器的对应控制域;,4.返回上一层函数;,5 ARM伪指令,ARM伪指令,ARM伪指令不属于ARM指令集中的指令,是为了编程方便而定义的。伪指令可以像其它ARM指令一样使用,但在编译时这些指令将被等效的一条或多条ARM指令所代替。ARM伪指令有四条,分别为ADR伪指令、ADRL伪指令、LDR伪指令、NOP伪指令。,ARM伪指令

25、大范围的地址读取,LDR伪指令用于加载32位的立即数或一个地址值到指定寄存器。在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。,应用示例(加载常量):,LDR R2,=0 xFF0;MOV R2,#0 xFF0LDR R0,=0 xFF000000;MOV R0,#0 xFF000000LDR R1,=0 xFFFFFFFE;MVN R1,#0 x1,ARM伪指令大范围的地址读取,应用示例(加载地址):,.LDR R1

26、,=InitStack.InitStack MOV R0,LR.,使用伪指令将程序标号InitStack的地址存入R1,ARM伪指令大范围的地址读取,应用示例(加载地址):,编译后的反汇编代码:,.LDR R1,=InitStack.InitStack MOV R0,LR.,.0 x60 LDR R1,0 xb4.0 x64 MOV R0,LR.0 xb4 DCD 0 x64,使用伪指令将程序标号InitStack的地址存入R1,地址,程序代码,ARM伪指令大范围的地址读取,应用示例(加载地址):,编译后的反汇编代码:,.LDR R1,=InitStack.InitStack MOV R0,L

27、R.,.0 x60 LDR R1,0 xb4.0 x64 MOV R0,LR.0 xb4 DCD 0 x64,使用伪指令将程序标号InitStack的地址存入R1,LDR伪指令被汇编成一条LDR指令,并在文字池中定义了一个常量,该常量为InitStack标号的地址,6 嵌入式系统中的C语言编程,ARM支持的基本数据类型整数类型:char 长度为8位的字节数据;short 长度为16位的半字数据;int 长度为32位的字数据;long 长度为32位的字数据;long long 长度为64位的双字数据;浮点数类型:float 长度为32位的浮点数;double 长度为64位的浮点数;,一般在con

28、fig.h中定义所用到的数据类型。例如:#define U32 unsigned int 或 typedef signed short int16;,应用指针直接操作内存或寄存器 例如:直接访问地址单元0 xF000FF00:U8*p=(U8*)0 xF000FF00;*p=5;#define pISR_EINT4567(*(unsigned*)_ISR_STARTADDR+0 x74)pISR_EINT4567=(int)Eint4567Isr;直接访问寄存器:#define IODIR(*(volatile unsigned long*)0 xE0028008)IODIR=BEEPCON;

29、/设置I/O为输出,一般在LPC22xx.h中事先定义(例如 LPC2294.h)。一次定义,以后可以反复使用。只要是同一个系列的ARM(例如都是LPC22xx系列),则本文件基本不需要修改,可以拿来直接使用。,一般在程序的开头会根据实际硬件电路的连接关系用#define预处理指令设置。例如:#define BEEPCON=0 x00000080/*P0.7引脚控制蜂鸣器*/,Volatile的用法volatile在英文字典的解释为“易变的,反复无常的(性格)”。编译器有一种技术叫数据流分析,分析程序中的变量在哪里赋值、哪里使用、哪里失效,分析结果可以用于常量合并,常量传播等优化。当它觉察到你

30、的代码没有修改变量的值时,它就可能在你访问变量时提供上次访问的缓冲值,这能够提高程序的效率。但这些优化可能会带来问题(特别是对硬件寄存器操作的程序中),这时需要用volatile关键词来禁止做这些优化。告诉编译器:变量已经变化,不要用缓存值(变量可能会随时改变,不要对其优化,而是每次用的时候去读写该变量。),使用volatile变量的场合硬件寄存器通常要加volatile说明。#define IO0DIR(*(volatile unsigned long*)0 xE0028008)在中断服务程序中修改的供其它程序检测的变量需要加volatile。例如:中断服务程序常常通过改变一些全局变量来通知

31、应用程序某个外部事件已经发生,这些全局变量不应该被优化。多任务环境中各任务间共享的标志应该加volatile。,预编译(处理)指令#define 与#undef 使用#define预处理命令定义宏,例如定义寄存器的物理地址,用来设置或清除寄存器的值,也可以用它来定义屏蔽位。例如:#define IO0DIR(*(volatile unsigned long*)0 xE0028008)#define BEEPCON(0 x17)IO0DIR=BEEPCON;/设置I/O为输出#define TxRxD1(PINSEL0&0 xFFF0FFFF)|(0 x0516)/不改变原来引脚的设置,将P0.

32、8、P0.9设置为TxD1、RxD1功能,#undef 用来删除#define定义的宏,是该宏名以后不再使用。,例:定义数码管每个段的位#define SEG_A(110)#define SEG_B(111)#define SEG_G(115)#define DISP_0(SEG_A|SEG_B|SEG_C|SEG_D|SEG_E|SEG_F)#define DISP_1(SEG_B|SEG_C)#define DISP_9(SEG_A|SEG_B|SEG_C|SEG_D|SEG_F|SEG_G)#define DISP_A(SEG_A|SEG_B|SEG_C|SEG_E|SEG_F|SEG_

33、G),宏也可以有参数,如#define 宏名(参数表)文本串即定义了1个宏函数。在这种情况下,一旦用“#define”定义了1个宏函数,后续的程序中出现的宏函数都会由指定的串来代替。例如,下面的宏函数定义:#define max(x,y)(xy)?x:y)每次“max(x,y)”出现在程序中,都要完成对x,y值的(xy)?x:y)操作。,当编写大的软件系统时,经常使用的只有几步的处理过程常常作为函数。但是,尽管是短函数,也总须要使用堆栈空间,至少是保留返回地址。建议像这种情况使用宏函数。宏函数同主程序分开,用预处理命令定义,然后通过编译程序把它们嵌入到使用宏的函数中。通过使用宏函数,就可以减少

34、和函数调用有关的开销,从而减少了相应的运行时间。,#if 和#endif条件编译指令,例如:#define LCD_TYPE MLCD_320_240#if(LCD_TYPE=MLCD_320_240)#define SCR_XSIZE(640)#define SCR_XSIZE(480)#define LCD_XSIZE(320)#define LCD_XSIZE(240)#if(LCD_TYPE=CLCD_240_320)#define SCR_XSIZE(640)#define SCR_XSIZE(480)#define LCD_XSIZE(240)#define LCD_XSIZE(3

35、20)#endif,技巧:在程序调试中,可以用#if临时注释掉一段代码,如下所示:#if 0#endif当需要这段代码时,只需将0变为1即可。,逻辑操作符与位操作符逻辑操作符与(/设置LCD控制寄存器的最低位来打开LCD位异或将某个位或某些位取反,右移实现除法(除以2n)ARM没有提供除法指令,ARM编译器通过调用C库函数来实现除法。其中,有符号除法调用_rt_sdiv,无符号除法调用_rt_udiv 如果除数是2的n次方,编译器就会调用移位操作来完成除法运算(无符号除法比有符合除法的效率高)。求反将每一位取反。例如:HC595_SendDat(DISP_TABi);/输出LED显示数据,提高

36、循环语句的效率采用减计数循环(count-down-to-zero loops),并采用简单的中止条件(simple termination conditions)。,在多层循环中,应将最长的循环放在最内层,最短的循环放在最外层。这样可以减少CPU跨切循环的次数。,如果循环体内有逻辑判断,并且循环次数大,应把循环判断移到循环体外。,提高switch语句的效率当switch语句中的case标号很多时,为了减少比较的次数,可以把发生频率相对高的条件放到第一位或者把整个switch语句转化为 嵌套switch语句。,标志C库的应用,ARM编译器支持ANSI C库和C+库ANSI C库中与目标硬件无关

37、的库函数可以直接使用ANSI C库中与目标硬件相关的库函数需要修改后才能使用。主要有两大类:与输入/输出相关的函数调试时:键盘和信息输出窗口(例如PC机的频幕)printf()实际目标系统(开发板):LCD、串口等与存储器相关的函数为了更好的利用Flash、RAM等存储设备,要在编程时将必要的代码定位在特定的位置分散加载(scatterloading)设置动态内存区(堆栈)的函数与目标系统的存储器分布直接相关,需要重新编写。,标准C库的位置 标准C库存放在软件安装路径下的两个子目录下面:系统头文件路径为:x:ARMADSv1_2Include系统头文件对应的系统库文件的路径为:x:ARMADS

38、v1_2Libarmlib 在链接时,armlink有一个参数,即-libpath可以指定库文件的路径。,_irq 定义中断处理函数 _irq void IRQHandler(void)unsigned int*base=(unsigned int*)0 x80000000;if(*base=1)C_int_handler();*(base+1)=0;当中断发生时,所有需要被保护的寄存器,编译器都会自动保存。同时,在中断处理完后,会把LR4的值装入到PC,把SPSR的值复制到CPSR来实现返回。用这个关键词定义的中断处理函数,不需要考虑现场的保护。有两个限制:函数不能有参数和返回值;不能用作可

39、重入的中断处理函数定义,因为它不保存SPSR的值。,ARM编译器对C语言的扩展,_swi 定义软件中断 _swi(0)int multiply_two(int,int);_ swi(1)int add_two(int,int);_ swi(2)int add_multiply_two(int,int,int,int);软件中断处理函数为:void C_SWI_Handler(int swi_num,int*regs)switch(swi_num)case 0:regs0=regs0*regs1;break;case 1:regs0=regs0+regs1;break;case 2:regs0=

40、(regs0*regs1)+(regs2*regs3);break;,返回,_inline 定义内联函数 把_inline这个关键词定义的函数在被调用的地方展开。可以消除函数调用的开销。,int main(void)disable_IRQ();enable_IRQ();,_register 声明一个变量,告诉编译器尽量把该变量保存到寄存器里。_value_in_regs 告诉编译器即使函数的返回值多于四个,也使用寄存器返回,而不用堆栈。,该方法可以在C程序中直接实现用C语言无法实现的一些硬件控制功能,也可以用该方法在C程序中的关键部分用汇编语句代替C语句以优化这个程序。,(1)在C程序中直接嵌

41、入汇编语句,7 C与汇编的相互调用(混合编程),内联汇编(inline assemble),void main()printf(“additiion result is:%dn”,add(1,2);,asm(“instruction;instruction”);/ARM C中使用,C语言中内嵌汇编示例,#include void my_strcpy(char*src,const char*dst)int ch;_asmloop:LDRB ch,src,#1STRB ch,dst,#1CMP ch,#0BNE loop;,int main(void)const char*a=Hello Worl

42、d!;char b20;_asmMOV R0,aMOV R1,bBL my_strcpy,R0,R1;printf(Original String:%sn,a);printf(“Copied String:%sn,b);return 0;,嵌入式汇编(embedded assemble),void main()const char*a=“hello world!”;char b20;my_strcpy(a,b);printf(“Original string:%sn”,a);printf(“Copied string:%sn”,b);,应满足ATPCS(The ARM-THUMB Proced

43、ure Call Standard)的调用规则。见x:ARMADSv1_2PDFspecsATPCS.pdf,loop,(2)ATPCS规则,ATPCS(The ARM-THUMB Procedure Call Standard)APCS(The ARM Procedure Call Standard)规定C与汇编之间的相互调用的规则,包括:如何通过寄存器传递函数参数和函数返回值;寄存器的使用规则堆栈的使用规则参数的传递规则函数返回值规则,寄存器的使用规则R0R3用来传递参数;R4R11用来保存局部变量;R12用作子程序内部调用的scratch寄存器;R13(sp)用作堆栈指针;R14(lr)

44、用作链接寄存器,保存子程序的返回地址。R15(pc)用作程序计数器,保存着下一条要取的指令的地址。由于R4R11用来保存局部变量,对于一个遵循ATPCS规则的函数,在进入该函数的时候,必须保存R4R11中被函数破坏的寄存器,然后在函数返回时,恢复它们的值。STMFD sp!,r4-r11,lr LDMFD sp!,r4-r11,pc如果有些寄存器在函数中没有被用到,那么就没有必要保存这些寄存器。,堆栈的使用规则APCS规定的堆栈是FD(Full Descending)类型。STMFD sp!,r4-r11,lr LDMFD sp!,r4-r11,pc对堆栈的操作是字节(8bit)对齐的,在汇编

45、程序中用PRESERVE8来告诉链接器,本汇编程序的数据堆栈是字节(8bit)对齐的。,参数的传递规则当子程序(被调函数)的参数的个数小于或等于4时,用R0R3来传递参数。,汇编调用C函数:mov r3,#100mov r2,#100mov r1,#100mov r0,#100BL sum,C函数:int sum(int a,int b,int c,int d)return a+b+c+d;,汇编调用C函数:STMFD sp!,r4mov r4,#100STMIA sp,r4mov r3,#100mov r2,#100mov r1,#100mov r0,#100BL sum,C函数:int s

46、um(int a,int b,int c,int d,int e)return a+b+c+d+e;,当子程序(被调函数)的参数的个数超过4时就必须用堆栈来传递参数。其中,将前4个参数用R0R3来传递,剩余的参数压入堆栈中。入栈的顺序和参数的顺序刚好相反(最后一个参数先入栈)。,参数传递总结,函数返回值规则如果结果为32位的整数,函数值可以通过R0寄存器返回;如果结果为64位的整数,函数值可以通过R0和R1这两个寄存器返回。,函数返回值总结,C语言中调用汇编函数,(3)C语言中调用汇编函数,#include extern void strcopy(char*d,const char*s);in

47、t main()const char*srcstr=First string-source;char dststr=Second string-destination;/*dststr is an array since were going to change it*/printf(Before copying:n);printf(%sn%sn,srcstr,dststr);strcopy(dststr,srcstr);printf(After copying:n);printf(%sn%sn,srcstr,dststr);return 0;,AREA SCopy,CODE,READONLY

48、 EXPORT strcopystrcopy;r0 points to destination string;r1 points to source string LDRB r2,r1,#1;load byte and update address STRB r2,r0,#1;store byte and update address;CMP r2,#0;check for zero terminator BNE strcopy;keep going if not MOV pc,lr;Return END,根据ATPCS标准,函数前4个参数通过R0R3来传递,其它参数通过堆栈(FD)传递,在C

49、程序中调用汇编函数的步骤如下:在汇编语言中,用该函数名作为汇编代码段的标识,用汇编语句定义函数代码;汇编函数最后用“MOV pc.lr”返回。在汇编函数中用EXPORT导出函数名。在C语言主调函数中用extern关键词声明函数原型。,汇编程序中调用C语言函数汇编程序的设计要遵循ATPCS,保证程序调用时参数的正确传递。在汇编程序中使用IMPORT伪指令引入将要调用的C函数。下面是一个汇编程序调用C程序的例子。其中在汇编程序中设置好各参数的值。本例中有5个参数,分别使用寄存器R0存放第一个参数,R1存放第2个参数,R2存放第3个参数,R3存放第4个参数,第5个参数利用数据栈传送。由于利用数据栈传

50、递参数,在程序调用结束后要调用数据栈指针。,(4)汇编程序中调用C语言函数,/C函数g()返回5个整数的和int g(int a,int b,int c,int d,int e)return a+b+c+d+e;汇编程序调用c函数g(),计算5个整数i,2*i,3*i,4*i,5*i的和EXPORT fAREA f,CODE,READONLYIMPORT g;使用伪指令IMPORT引入C函数名gSTR lr,sp,#-4!;保存返回地址ADD r1,r0,r0;假设进入程序f时,r0中的值为i,r1值设为2*iADD r2,r1,r0;r2的值设为3*iADD r3,r1,r2;r3的值设为5

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号