1、单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,*,第,10,章,ARM,高级语言程序设计基础,10.1 ARM,汇编程序实例,数据块复制,本程序将数据从源程序区,src,复制到目标数据区,dst,复制时,以,8,个字为单位进行,.,对于最后所剩不足,8,个字的数据,以字为单位进行复制,这时程序跳转到,coyewords,处执行,.,在进行以,8,个字为单位的数据复制时,保存了所有的,8,个工作寄存器,.,程序如下,(ADS,下,),。,AREA,Block,CODE,READONLY,;,设置本段程序的名称及属性,NUM EQU 20 ;,设置将要复制的字
2、数,ENTRY ;,标识程序入口点,LDR R0,=,src,;R0,寄存器指向源数据区,src,LDR R1,=,dst,;R1,寄存器指向目标数据区,dst,MOV R2,#NUM ;R2,指定将要复制的字数,MOV SP,#,设置数据栈指针,用于保存工作寄存器数值,Bcopy,MOVS R3,R2,LSR#3;,需要进行的以,8,个字为单位的复制次数,BEQ,Cword,;,对于剩下不足,8,个字的数据,跳转到,Cword,以,;,字为单位复制,STMFD SP!,R4-R11;,保存工作寄存器,Ocopy,LDMIA R0!,R4-R11;,从源数据区读取,8,个字的数据,放到,8,个
3、寄存,;,器中,并更新目标数据区指针,R0,STMIA R1!,R4-R11;,将这,8,个字数据写到目标数据区中,并更新目,;,标数据区指针,R1,SUBS R3,R3,#1 ;,将块复制次数减,1,BNE,Ocopy,;,循环,直到完成以,8,个字为单位的块复制,LTMFD SP!R4-R11 ;,恢复工作寄存器值,Cword,ANDS R2,R2,#7 ;,剩下不足,8,个字的数据的字数,BEQ stop ;,数据复制完成,Wcopy,LDR R3,R0,#4 ;,从源数据区读取,18,个字的数据,放,R3,寄存,;,器中,并更新目标数据区指针,R0,STR R3,R1,#4 ;,将这,
4、R3,中数据写入到目标数据区中,并更新,;,目标数据区指针,R1,SUBS R2,R2,#1 ;,将字数减,1,BNE,Wcopy,;,循环,直到完成以字为单位的数据复制,Stop ;,程序结束处理,MOV R0,#0 x18 ;,本条与下条指令的作用是参数传递,LDR R1,=&20026,SWI 0 x123456 ;,将,CPU,的控制权交给调试器,AREA,Bdata,DATA,READWRITE,;,定义数据区,Bdata,src,DCD 1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,1,2,3,4,dst,DCD 0,0,0,0,0,0,0,0,0,0,0,0,0
5、0,0,0,0,0,0,0,;,这里的,DCD,定义源数据区,src,及目标,;,数据区,dst,END ;,结束汇编,10.1 ARM,汇编程序实例,利用跳转表实现程序跳转,在程序中常常需要根据一定的参数选择执行不同的子程序,.,本例通过跳转表实现程序跳转,.,跳转表中存放的是各函数的地址,选择不同子程序的参数是该子程序在跳转表中的偏移量,.,在本例中,R3,寄存器中存放的是跳转表的基地址,(,首地址,其中存放的是第一个子程序的地址,).,(ADS,下,),AREA,Jump,CODE,READONLY,;,设置本段程序的名称及属性,NUM EQU 2 ;,跳转表中的子程序个数,ENTRY
6、 ;,程序执行的入口点,Start MOV R0,#0 ;,设置,3,个参数,R0,选择调用哪个子程序,MOV R1,#3 ;R1,为子程序要用的参数,MOV R2,#2 ;R2,为子程序要用的参数,BL,Func,;,调用子程序,Func,进行算术运算,Stop MOV R0,#0 x18 ;,本条与下条指令的作用是参数传递,LDR R1,=&20026,SWI 0 x123456 ;,将,CPU,的控制权交给调试器,Func,CMP R0,#NUM ;,判断,R0,是否在有效范围之内,MOVHS PC,LR ;,如果超出范围则程序返回,ADR R3,JTable ;,读取跳转表的基地址,L
7、DR PC,R3,R0,LSL#2;,根据参数,R0,的值跳转到相应的,;,子程序,JTable,DCD,DoAdd,;,当参数,R0,为,0,时上面的代码将选择,DoAdd,DCD,DoSub,;,当参数,R0,为,1,时上面的代码将选择,DoSub,DoAdd,ADD R0,R1,R2 ;,子程序,DoAdd,执行加法操作,MOV PC,LR ;,子程序返回,DoSub,SUB R0,R1,R2 ;,子程序,DoSub,执行减法操作,MOV PC,LR ;,子程序返回,END ;,结束汇编,10.2 C,语言与汇编的混合编程,简介,在嵌入式程序设计中,C,语言编程和,ARM,汇编语言编程都
8、是,必需的,在某些情况下,还需要,C,语言与汇编语言的混合编程,.,灵活地运用,C,语言和汇编语言之间的关系进行嵌入式编程有,利于对嵌入式系统以及相关模块的编程开发,.,在需要,C,语言和,汇编语言混合编程时,如果汇编代码比较简单,则可直接利用,内嵌汇编来进行混合编程,.,如果汇编代码比较复杂,则可将汇,编语言程序和,C,语言程序分别以文件的形式加到一个工程里,通过,ATPCS,来完成汇编语言程序与,C,语言程序之间的调用,.,10.2 C,语言与汇编的混合编程,ATPCS,介绍,ATPCS(ARM-Thumb Produce Call Standard),是,ARM,程序和,Thumb,程序
9、中子程序调用的基本规则,目的是为了使单独编译,的,C,语言和汇编语言程序之间能够相互调用,.,这些基本规则包括,子程序调用过程中寄存器的使用规则,数据栈的使用规则和参,数的传递规则,.,10.2 C,语言与汇编的混合编程,寄存器的使用规则,子程序间通过寄存器,R0 R3,来传递参数。这时,寄存器,R0 R3,可记作,a0 a3,。被调用的子程序在返回前无须恢复寄存器,R0 R3,的内容。,在子程序中,使用寄存器,R4 R11,来保存局部变量。这时,寄存器,R4 R11,可以记作,v1 v8,。如果在子程序中使用了寄存器,v1 v8,中某些寄存器,则子程序进入时必须保存这些寄存器的值,在返回前必
10、须恢复这些寄存器的值。在,Thumb,程序中,通常只能使用寄存器,R4 R7,来保存局部变量。另外,R9,、,R10,和,R11,还有一个特殊的作用,分别记为:静态基址寄存器,SB,、数据栈限制指针,SL,和桢指针,FP,。,寄存器,R12,用作子程序间的,scratch,寄存器,(,用于保存,SP,在函数返回时使用该寄存器出栈,),记作,IP.,在子程序间的链接代码段中常有这种使用规则,.,寄存器,R13,用作堆栈指针,记作,SP,。在子程序中寄存器,R13,不能用作其他用途。寄存器,SP,在进入子程序时的值和退出子程序的值必须相等。,10.2 C,语言与汇编的混合编程,寄存器的使用规则,寄
11、存器,R14,称为链接寄存器,记作,LR,,它用于保存子程序的返回地址。如果在子程序中保存了返回地址,寄存器,R14,则可以用作其他用途。,寄存器,R15,为程序计数器,记作,PC,,它不能用作其他用途。,表,:,寄存器的名称及使用规则,寄存器,别名,特殊名,使用规则,R0,A1,参数,/,结果,/scratch,寄存器,1,R1,A2,参数,/,结果,/scratch,寄存器,2,R2,A3,参数,/,结果,/scratch,寄存器,3,R3,A4,参数,/,结果,/scratch,寄存器,4,表续,:,寄存器的名称及使用规则,寄存器,别名,特殊名,使用规则,R4,V1,R5,V2,R6,V
12、3,R7,V4,WR,ARM,状态局部变量寄存器,4,Thumb,状态工作寄存器,R8,V5,ARM,状态局部变量寄存器,5,R9,V6,SB,ARM,状态局部变量寄存器,6,在支持,RWPI,的,ATPCS,中为静态基址寄存器,R10,V7,SL,ARM,状态局部变量寄存器,7,在支持数据栈检查的,ATPCS,中为数据栈限制指针,R11,V8,FP,ARM,状态局部变量寄存器,8/,帧指针,R12,IP,子程序内部调用的,scratch,寄存器,R13,SP,数据栈指针,R14,LR,链接寄存器,R15,PC,程序计数器,10.2 C,语言与汇编的混合编程,数据栈的使用规则,根据堆栈指针指向
13、位置的不同,堆栈可分为满栈和空栈,2,种,.,当堆栈指针指向栈顶元素,即指向最后一个入栈的数据元,素时,称为满栈,当堆栈指针指向与栈顶元素相邻的一个可用,数据单元时,称为空栈,.,根据数据栈增长方向的不同也可分为,递增堆栈和递减堆栈,2,种,.,当数据栈向内存地址减少的方向增,长时,称为递减堆栈,当数据栈向内存地址增加的方向增长时,称为递增堆栈,.,10.2 C,语言与汇编的混合编程,综合这,2,种特点则可有以下,4,种数据栈,:,FD,满递减,ED,空递减,FA,满递增,EA,空递增,ATPCS,规定数据栈为,FD,类型,并且对数据栈的操作是,8,字节对齐,.,异常中断的处理程序可使用中断程
14、序的数据栈,但要保证中断程序的数据栈足够大,.,因此要求包含外部调用的程序必须满足下列条件:,(,1,)外部接口程序的数据栈必须是,8,字节对齐的。,(,2,)本程序生成的数据栈也必须是,8,字节对齐的。在汇编程序中可以使用,PRESERVE8,伪指令告诉连接器,本汇编程序数据栈是,8,字节对齐的。,10.2 C,语言与汇编的混合编程,参数传递规则,根据参数个数是否固定可将子程序分为参数个数固定的子程序,和参数个数可变的子程序,.,这,2,种子程序的参数传递规则是不同的,1),参数个数固定的子程序参数传递规则,对于参数个数固定的子程序,参数传递与参数个数可变的子程序,参数传递规则不同,.,如果
15、系统包含浮点运算的硬件部件,浮点参,数将按照下面的规则传递,:,各个浮点参数按顺序处理,为每个浮点参数分配,FP,寄存器,.,分配的方法是,满足该浮点参数需要的切编号最小的一组连续的,FP,寄存器,.,10.2 C,语言与汇编的混合编程,参数传递规则,根据参数个数是否固定可将子程序分为参数个数固定的子程序,和参数个数可变的子程序,.,这,2,种子程序的参数传递规则是不同的,2),参数个数可变的子程序参数传递规则,对于参数个数可变的子程序,当参数不超过,4,个时,可使用寄存,器,R0R3,来传递参数,当参数超过,4,个时,还可使用数据栈来传,递参数,.,在参数传递时,将所有参数看作是存放在连续的
16、内存字单元中,的字单元中的字数据,.,然后,依次将各字数据传送到寄存器,R0,R1,R2,R3,中,如果参数多于,4,个,将剩余的字数据传送到数据栈中,入,栈的顺序与参数顺序相反,即最后一个字数据先入栈,.,10.2 C,语言与汇编的混合编程,参数传递规则,根据参数个数是否固定可将子程序分为参数个数固定的子程序,和参数个数可变的子程序,.,这,2,种子程序的参数传递规则是不同的,3),子程序结果返回规则,结果为一个,32,位的整数时,可通过寄存器,R0,返回,;,结果为一个,64,位整数时,可通过寄存器,R0,和,R1,返回,依次类推,;,结果为一个浮点数,可通过浮点运算部件的寄存器,F0,D
17、0,或者,S0,来返回,;,结果为复合型的浮点数,(,如复数,),时,可通过寄存器,F0Fn,或者,D0,Dn,;,对于位数更多的结果,需要通过内存来传递,.,10.2 C,语言与汇编的混合编程,注意,在同一个,C,源程序中不能同时包含,ARM,指令和,Thumb,指令,但汇,编可以,.,如果程序遵守支持,ARM,程序和,Thumb,程序混合使用的,ATPCS,则,程序中,ARM,子程序和,Thumb,子程序可相互调用,.,对于,C,源程序,只要在编译时指定,apcs/interwork,选项,编译器生成的代码会自,动遵守支持,ARM,程序和,Thumb,程序混合使用的,ATPCS.,而对于汇
18、编源程序,必须保证编写的代码遵守支持,ARM,程序和,Thumb,程,序混合使用的,ATPCS.,10.2 C,语言与汇编的混合编程,内嵌汇编,在语言中嵌入汇编语言程序可实现一些高级语言没有的功,能,并可提高执行效率,armcc,和,armcpp,内嵌汇编器支持完整的,ARM,指令集,;,tcc,和,tcpp,用于,Thumb,指令集,但是内嵌汇编器并不支,持诸如直接修改,PC,实现跳转的底层功能,.,内嵌的汇编指令包括大部,分,ARM,指令和,Thumb,指令,但是不能直接引用,C,语言的变量定义,数,据交换必须通过,ATPCS,进行,.,嵌入式汇编在形式上表现为独立定义,的函数体,.,1
19、0.2 C,语言与汇编的混合编程,内嵌汇编指令的语法格式,_ _,asm,指令,;,指令,.,指令,各指令用,”,;”,分隔,如果一条指令占据多行,除最后一行外都要使用,连字符,”,”.,在汇编指令段中可使用,C,语言的注释语句,.,需要特别注意,的是,_ _,asm,是两个下划线,.,10.2 C,语言与汇编的混合编程,内嵌汇编指令的特点,1),操作数,在内嵌的汇编指令中,操作数可以是寄存器,常量或,C,语言表,式,.,它们可以是,char,short,或,int,类型,而且都是作为无符号数进行,操作,如果需要有符号数,用户需要自己处理与符号有关的操,作。编译器将计算这些表达式的值,并为其分
20、配寄存器。当汇编,指令中同时用到了物理寄存器和,C,语言的表达式,要注意使用的表,达式不要过于复杂。,内嵌汇编指令的特点,2),物理寄存器,在内嵌的汇编指令中,使用物理寄存器有以下限制:,不能直接想,PC,寄存器赋值,程序的跳转只能通过,B,指令和,BL,指令实现。,在使用物理寄存器的内嵌汇编指令中,不要使用过于复杂的,C,语言表达式。因为当表达式过于复杂时,将会需要较多的物理寄存器,这些寄存器可能与指令中的物理寄存器的使用冲突。当编译器发现了寄存器的分配冲突时,会产生相应的错误信息,报告寄存器分配冲突。,编译器可能会使用,R12,寄存器或,R13,寄存器存放编译的中间结果,在计算表达式值时可
21、能会将寄存器,R0R3,,,R12,以及,R14,用于子程序调用。因此在内嵌的汇编指令中,不要将这些寄存器同时指定为指令中的物理寄存器,.,内嵌汇编指令的特点,2),物理寄存器,在内嵌的汇编指令中,使用物理寄存器有以下限制:,在内嵌的汇编指令中使用物理寄存器时,如果有,C,语言变量使用了该物理寄存器,则编译器将在合适的时候保存并恢复该变量的值。需要注意的是,当寄存器,SP,,,SI,,,FP,以及,SB,用作特定的用途时,编译器不能恢复这些寄存器的值。,通常在内嵌的汇编指令中不要指定物理寄存器,因为这可能会影响编译器分配寄存器,进而可能影响代码的效率。,3),常量,在内嵌的汇编指令中,常量前的
22、符号,#,可以省略。如果在一个表达式中使用符号“,#”,,则该表达式必须是一个常量。,内嵌汇编指令的特点,4),标号,C,语言程序中的标号可被内嵌的汇编指令使用。但是只有指令,B,可使用,C,语言程序中的标号,指令,BL,不能,C,语言程序中的标号。指令,B,使用,C,语言程序中的标号时,语法格式如下:,Bcondlabel,5),内存单元的分配,内嵌汇编器不支持汇编语言中用于内存分配的伪操作。所用的内存单元的分配都是通过,C,语言程序完成的,分配的内存单元通过变量供内嵌的汇编器使用,.,内嵌汇编指令的特点,6),指令展开,内嵌的汇编指令中如果包含常量操作数,则该指令可能会被汇编器展开成几条指
23、令。例如,指令“,ADD R0,,,R0,,,#1023,”,可能会被展开成下面的指令序列:,ADD R0,,,R0,,,#1024,SUB R0,,,R0,,,#01,乘法指令,MUL,可能会被展开成一系列的加法操作和移位操作。事实上,除了与协处理器相关的指令外,大部分,ARM,和,Thumb,指令中包含常量操作数都可能被展开成多条指令。,内嵌汇编指令的特点,7)SWI,和,BL,指令的使用,在内嵌的,SWI,和,BL,指令中,除了正常的操作数域外,还必须增加下面,3,个可选的寄存器列表:,第,1,个寄存器列表中的寄存器用于存放输入的参数,第,2,个寄存器列表中的寄存器用于存放返回的结果,第
24、3,个寄存器列表中的寄存器供被调用的子程序作为工作寄存器,这些寄存器的内容可能被调用的子程序破坏。,内嵌汇编指令的特点,8),内嵌汇编器与,armasm,汇编器的区别,内嵌汇编器与,armasm,汇编器的区别如下:,内嵌汇编器不支持通过“,.”,指示符或,PC,获取当前指令地址,不支持,LDR,Rn,=expression,伪指令,而使用,MOV,Rn,=expression,指令向寄存器赋值。,不支持标号表达式,不支持,ADR,和,ADRL,伪指令,不支持,BX,和,BLX,指令,不可以向,PC,赋值,使用,0 x,前缀替代,&,表示十六进制,当使用,8,位移位常量导致,CPSR,中,的,
25、ALU,标志位需要更新时,,NZCV,标志中的,C,不具有真实意义,10.2 C,语言与汇编的混合编程,内嵌汇编注意事项,(1),必须小心使用物理寄存器,如,R0R3,,,LR,和,PC,计算汇编代码中的,C,语言表达式时,会使用这些物理寄存器并会修改,CPSR,中的,NZCV,标志位。例如:,_ _,asm,MOV R0,x,ADD y,R0,x/y,当计算,x/y,时,,R0,会被修改,从而影响,R0+x/y,的结果,用一个,C,语言的变量代替,R0,就可以解决这个问题。例如:,_ _,asm,MOV,var,x,ADD,y,var,x/y,10.2 C,语言与汇编的混合编程,内嵌汇编注意
26、事项,(2),不要使用寄存器寻址变量,尽管有时寄存器明显对应某个变量,但不能直接使用寄存器代替变量,.,int,bad_f(int,x),/*x,存放在,R0,中,*,/,_ _,asm,ADD R0,R0,#1,/*,发生寄存器冲突*,/,return x;,/*x,存放在,R0,中*,/,10.2 C,语言与汇编的混合编程,内嵌汇编注意事项,(2),不要使用寄存器寻址变量,尽管根据编译器规则似乎可确定,R0,对应,x,,但这样的代码会使汇编器认为发生了寄存器冲突,用其他寄存器代替,R0,存放参数,x,,则使得该函数将,x,原封不动地返回。,int,bad_f(int,x),_ _,asm,
27、ADD x,x,#1,return x;,10.2 C,语言与汇编的混合编程,内嵌汇编注意事项,(3),使用内嵌汇编时,编译器自己会保存和恢复它可能用到的寄存器,用户无须保存和恢复寄存器。事实上,除了,CPSR,和,SPSR,寄存器,对物理寄存器没写就读都会引起汇编器报错。,int,f(int,x),_ _,asm,STMFD SP,R0;,对,R0,的保存是非法的,因为发生了写之前读,ADD R0,x,#1,EOR x,R0,x,LDMFD SP!R0,;,对,R0,的恢复是不需要的,return x;,10.2 C,语言与汇编的混合编程,内嵌汇编注意事项,(4)LDM,和,STM,指令的寄
28、存器列表只允许物理寄存器,内嵌汇编可以修改处理器模式,协处理器状态和,FP,,,SL,及,SB,等,ATPCS,寄存器。但是编译器在编译时并不了解这些变化,所以必须保证在执行,C,语言代码前恢复相应被修改的处理器模式,.,(5),汇编语言用“,”作为操作数分隔符,如果有带“,”的,C,语言表达式作为操作数,必须用“()”将其归为一个汇编操作数。例如:,_ _,asmADD,x,y,(f(),z,),其中,(,f(),z,),为,C,、,C+,语言表达式。,10.3,C,语言与,ARM,汇编语言间相互调用,汇编程序访问,C,语言全局变量,全局变量只能通过地址间接调用。为了访问,C,中的全局变量,
29、首先通过,.extern,伪指令引入全局变量,然后将其地址装入寄存器中。根据变量的类,型,可以通过下面的装载和存储指令访问。,对,unsigned char,类型变量,使用,LDRB/STRB,访问,对,unsigned short,类型变量,使用,LDRH/STRH,访问,(,对体系结构,3,使用,LDRB/STRB,访问,),对,unsigned,int,类型变量,使用,LDR/STR,访问,对,char,类型变量,使用,LDRSB/STRSB,访问,对,short,类型变量,使用,LDRSH/STRSH,访问,小于,8,个字的结构体可以通过,LDM/STM,指令来访问整个变量,结构体中的
30、变量,也可以通过相应类型的装载和存储指令来访问,此时必须知道此成员与结构体,起始地址的偏移量。,10.3,C,语言与,ARM,汇编语言间相互调用,汇编程序访问,C,语言全局变量,例子,:,汇编程序访问,C,语言全局变量,(ADS,下,),AREA,globals,CODE,READONLY,EXPORT,asmsubroutine,;,用,EXPORT,伪操作声明该变量可被其他文件引用,;,相当声明了一个全局变量,IMPORT,globvar,;,用,IMPORT,伪操作声明该变量是在其他文件中定,;,义的,在本文件中可能要用到该变量,asmsubroutine,LDR R1,=,globva
31、r,;,读取,globvar,的地址到,R1,中,LDR R0,R1 ;,将其值读入到寄存器,R0,中,ADD R0,R0,#2,STR R0,R1 ;,修改后再将寄存器,R0,的值赋予变量,globvar,MOV PC,LR,END,10.3,C,语言与,ARM,汇编语言间相互调用,C,程序调用汇编程序的例子,C,程序调用汇编程序应首先通过,extern,声明要调用的汇编程序,模块,声明中形参个数要与汇编模块中需要的变量个数一致,,且参数传递要满足,ATPCS,规则;然后再在,C,程序正文中调用。,例子,:,一个,C,程序调用汇编程序的串拷贝例子。,例子,:,一个,C,程序调用汇编程序的串拷
32、贝例子。,C,程序为,:,#include,extern void,strcopy(char,*d,char*s);,用,extern,声明一个函,数为外部函数,可被其,他文件中的函数调用,int,main(),char*,srcstr,=First string-source;,char*,dststr,=Second string-destination;,printf(Before,copying:n);,printf,(%,sn,%,sn,srcstr,dststr,);,strcopy(dststr,srcstr,);,调用汇编函数,strcopy,(),printf(After,c
33、opying:n);,printf,(%,sn,%,sn,srcstr,dststr,);,return(0);,例子,:,一个,C,程序调用汇编程序的串拷贝例子。,ARM,汇编语言模块,:,(ADS,下,),AREA,SCopy,CODE,READONLY,EXPORT,strcopy,;,用,EXPORT,伪操作声明该变量可被其他文件引用,相,;,;,当于声明了一个全局变量,Strcopy,;R0,指向目的数据串,,R1,指向源数据串,LDRB R2,R1,#1;,字节加载,并更新地址,STRB R2,R0,#1;,字节保存,并更新地址,CMP R2,#0;,检查,R2,是否等于,0,BN
34、E,strcopy,;,若条件不成立,则继续执行,MOV PC,LR;,从子程序返回,END,根据,ATPCS,函数的前,4,个参数在,R0-R3,中,C,语言代码源程序可保存为,strtest.c,汇编语言程序是,scopy.s,10.3,C,语言与,ARM,汇编语言间相互调用,汇编程序调用,C,程序的例子,汇编程序调用,C,语言模块,在调用之前首先必须根据,C,语言模块,中需要的参数个数以及,ATPCS,参数传递规则,完成参数的传递,即前,4,个参数通过,R0R3,传递,后面的参数通过堆栈传递;然后,再利用,B/BL,指令进行调用。,例:定义汇编语言将要调用的,C,函数功能为返回,5,个参
35、数之和,,其函数原型为:,int,g(int,a,int,b,int,c,int,d,int,e),return a+b+c+d+e;,汇编语言要完成的功能是求取,i+2i+3i+4i+5i,的结果,.,程序如下:,(ADS,下,),EXPORT f,AREA,f,CODE,READONLY,IMPORT g ;i,在,R0,中,STR LR,sp,#-4!;,预先保存,LR,ADD R1,R0,R0;R1=2*i(,第,2,个参数,),ADD R2,R1,R0;R2=3*i(,第,3,个参数,),ADD R3,R1,R2;R3=5*I(,第,5,个参数,),STR R3,sp,#-4!;,第
36、5,个参数入栈,ADD R3,R1,R1;R3=4*i(,第,4,个参数,),BL g;,调用,C,语言函数,g(),ADD sp,sp,#4;,第,5,个参数出栈,LDR PC,sp,#4;,返回,END,程序执行结束,结果保存于寄存器,R0,中。,10.4,基本,IO,程序(含启动代码),下面我们通过基本,IO,程序来介绍完整的,ARM,程序的编写过程。,启动代码,启动代码是用汇编语言编写的一段程序,一般完成堆栈初始化、,系统变量初始化、中断系统初始化、地址重映射、,I/O,初始化以及,外围初始化等操作,并引导程序进入,C,语言编写的主程序。设计时,可以根据需要进行适当的删减,.,通常来
37、说,启动代码的大致流程如下:,系统变量的初始化,中断向量重映射,中断向量表,转到,C,入口地址,初始化各模式栈指针,10.4,基本,IO,程序(含启动代码),由于,ARM7,处理器的中断向量位于地址,0 x000000000 x0000001C,处,因此应,将中断向量表置于此处。当程序工作于用户,RAM,模式,或用户外部存储器模式,时,需要进行中断向量的重映射。,中断向量表的程序如下:,Vectors:LDR PC,Reset_Addr,LDR PC,Undef_Addr,LDR PC,SWI_Addr,LDR PC,PAbt_Addr,LDR PC,DAbt_Addr,DCD 0 xb920
38、5f80 ,保留向量,LDR PC,PC,#-0 xff0,LDR PC,FIQ_Addr,中断向量表,10.4,基本,IO,程序(含启动代码),其中,DCD 0 xb9205f80,为保留向量,这个向量在,ARM,文件中标识,为保留,该位置被,Boot,装载程序用作有效的用户程序关键字。,LPC2000,芯片规定,当向量表中所有的数据累加和为,0,,且,ISP,外部硬件条件不满足时,,Boot,装载程序将执行用户程序。,可以注意到,IRQ,向量使用的指令(,LDR PC,PC,#-0 xff0,),与其他向量不同,正常情况下,这条指令所在的地址为,0 x00000018,,当,CPU,执行这
39、条指令但还没有跳转时,,PC,的值为,0 x00000020,,,0 x00000020-0 x00000ff0,为,0 xfffff030,,这,正是向量中断控制器,VIC,的向量中断地址寄存器,VICVectAddr,,,这个寄存器保存当前将要服务的,IRQ,中断服务程序的入口地址,,这样就可直接跳转到需要的中断服务程序中。,中断向量表,10.4,基本,IO,程序(含启动代码),调试时,通常使程序运行于,RAM,空间(,LPC2200,芯片,RAM,的起,始地址为,0 x40000000,),此时需要进行异常向量的重映射,,使从,0 x00000000,开始的,32,个字节的异常向量及额外
40、保留的,32,个字节,共,64,字节映射到,0 x40000000,处,同时将存储映射模,式配置为用户,RAM,模式。,中断向量的重映射,10.4,基本,IO,程序(含启动代码),中断向量重影射的程序如下,:,RemapSRAM,:STMFD SP!,R0-R1,LR,REMAPS:MOV R0,#0 x40000000,LDR R1,=Vectors,LDMIA R1!,R2-R9,STMIA R0!,R2-R9,LDMIA R1!,R2-R9,STMIA R0!,R2-R9,LDR R0,=MEMMAP,MOV R1,#0 x02,STR R1,R0,LDMFD SP!,R0-R1,PC,
41、中断向量的重映射,10.4,基本,IO,程序(含启动代码),编程前,需明确,ARM,使用的堆栈为满降序栈。要定义各工作模式,的栈指针,首先需要使处理器处于相应的工作模式下,因此程序,中需要进行工作模式的切换;另外,要注意只有当处理器处于特,权模式(非用户模式)下的时候,才能进行工作模式的切换。当,系统复位或软件中断响应时,处理器进入的是管理模式,此时,,可以进行各种模式的切换,由此可以设置各种工作模式下的栈指,针。,初始化各模块栈指针的程序如下,:,InitStack,:MOV R1,LR,LDR R0,=,Top_Stack,为各种工,作模式设置堆栈,初始化各模栈指针,#,进入未定义指令工作
42、模式并设栈指针,MSR,CPSR_c,#,Mode_UND|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,UND_Stack_Size,#,进入中止工作模式并设置栈指针,MSR,CPSR_c,#,Mode_ABT|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,ABT_Stack_Size,#,进入,FIQ,工作模式并设置栈指针,MSR,CPSR_c,#,Mode_FIQ|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,FIQ_Stack_Size,#,进入,IRQ,工作模式并设置栈指针,MSR,CPSR_c,#,Mode_IRQ|
43、I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,IRQ_Stack_Size,#,进入管理工作模式并设置栈指针,MSR,CPSR_c,#,Mode_SVC|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,SVC_Stack_Size,#,进入用户工作模式并设置栈指针,MSR,CPSR_c,#,Mode_USR,MOV SP,R0,MOV PC,R1,.,equ,Top_Stack,(0 x40004000-0 x20),.,equ,Top_Stack,0 x00000004 4,个字节,.,equ,Top_Stack,0 x00000020 32,个字节
44、equ,Top_Stack,0 x00000004,.,equ,Top_Stack,0 x00000004,.,equ,Top_Stack,0 x00000100 256,个字节,.,equ,Top_Stack,0 x00000200 512,个字节,10.4,基本,IO,程序(含启动代码),中断向量重影射的程序如下,:,启动代码的部分内容可以放到,C,程序中完成,诸如初始化中断向量,控制器、初始化,C,程序变量等。汇编程序到,C,程序的入口非常简,单,如下:,.extern Main,B Main,注意,C,程序的名称:,Main,,这里不要使用,main,。因为若使用,main,,则
45、GCC,编译器会先调用,GCC,运行时库函数,_,gccmain,,然,后再执行,main,函数,编译器若找不到,_,gccmain,函数,则会提示,“,undefined reference to _,gccmain,”,错误。,转到,C,入口地址,10.4,基本,IO,程序(含启动代码),完整的启动代码,下面是完整的启动代码,Startup.s,程序。,(GNU,下,),#7,种工作模式,.EQU,Mode_USR,0 x01,标准工作模式以及中断允,许位,I,和,F,的定义,.EQU,Mode_FIQ,0 x11,.EQU,Mode_IRQ,0 x12,.EQU,Mode_SVC,0
46、x13,.EQU,Mode_ABT,0 x17,.EQU,Mode_UND,0 x1B,.EQU,Mode_SYS,0 x1F,#,中断屏蔽位及状态位,.EQU,I_Bit,0 x80 I=1,,则禁止,IRQ,中断,.EQU,F_Bit,0 x40 F=1,,则禁止,FIQ,中断,.EQU,T_bit,0 x20 T=1,,为,Thumb,状,态,,=0,为,ARM,状态,.EQU MEMMAP 0 xE01FC040,.EQU,Top_Stack,(0 x40004000-0 x20),栈顶地址,保留,32,个字节,.EQU,UND_Stack_Size,0 x00000004,.EQU,
47、SVC_Stack_Size,0 x00000020,.EQU,ABT_Stack_Size,0 x00000004,.EQU FIQ_Stack_Size,0 x00000004,.EQU IRQ_Stack_Size,0 x00000100,.EQU USR_Stack_Size,0 x00000200,.global _start,.code 32,.text,_start:,程序入口,Vectors:LDR PC,Reset_Addr,LDR PC,Undef_Addr,LDR PC,SWI_Addr,LDR PC,PAbt_Addr,LDR PC,DAbt_Addr,DCD 0 xb
48、9205f80 ,保留向量,LDR PC,PC,#-0 xff0,LDR PC,FIQ_Addr,Reset_Addr,:.long,Reset_Handler,Undef_Addr,:.long,Undef_Handler,SWI_Addr,:.long,SWI_Handler,PAbt_Addr,:.long,PAbt_Handler,DAbt_Addr,:.long,DAbt_Handler,.long 0 ,保留地址,FIQ_Addr,:.long,FIQ_Handler,Undef_Handler,:B,Undef_Handler,PAbt_Handler,:B,PAbt_Handl
49、er,DAbt_Handler,:B,DAbt_Handler,SWI_Handler,:B,SWI_Handler,;,li,FIQ_Handler,:B,FIQ_Handler,Reset_Handler,:BL,InitStack,BL,RemapSRAM,.extern Main,B Main,#*,InitStack,:MOV R1,LR,LDR R0,=,Top_Stack,MSR,CPSR_c,#,Mode_UND|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,UND_Stack_Size,MSR,CPSR_c,#,Mode_ABT|I_Bit|F_Bit,
50、MOV SP,R0,SUB R0,R0,#,ABT_Stack_Size,MSR,CPSR_c,#,Mode_FIQ|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,FIQ_Stack_Size,MSR,CPSR_c,#,Mode_IRQ|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,IRQ_Stack_Size,MSR,CPSR_c,#,Mode_SVC|I_Bit|F_Bit,MOV SP,R0,SUB R0,R0,#,SVC_Stack_Size,MSR,CPSR_c,#,Mode_USR,MOV SP,R0,MOV PC,R1,#*,Remap






