1、第4章 ARM系统硬件设计基础,1、汇编语言程序常用的符号,(1)常量 常量是指其值在程序运行过程中不能被改变的量。ARM汇编程序支持的常量有数字常量、逻辑常量和字符串常量。数字常量一般为32位的整数;逻辑常量只有两种取值:真或假;字符串常量为一个固定的字符串。,(2)变量,变量是指其值在程序运行过程中可以改变的量。在ARM汇编语言程序中,可使用GBLA、GBLL、GBLS伪指令声明全局变量,使用LCLA、LCLL、LCLS伪指令声明局部变量,并可使用SETA、SETL和SETS对其进行初始化。,(3)变量代换,程序中的变量可通过代换操作取得一个常量。代换操作符为“$” 。数字变量前面有一个代
2、换操作符“$” ,编译器会将该数字变量的值转换为十六进制的字符串,并将该十六进制的字符串代换“$”后的数字变量。逻辑变量前面有一个代换操作符“$” ,编译器会将该逻辑变量代换为它的取值(真或假)。字符串变量前面有一个代换操作符“$” ,编译器会将该字符串变量的值代换“$”后的字符串变量。,(3)变量代换,LCLS S1 ;定义局部字符串变量S1和S2 LCLS S2 S1 SETS “Test!” S2 SETS “This is a $S1” ;字符串变量S2的值为“This is a Test!”,4.2.1 ARM汇编器支持的伪指令,伪操作不像机器指令一样在CPU运行时执行,而是在汇编程
3、序期间进行处理的。 1、符号定义(Symbol Definition)伪操作 2、数据定义(Data Definition)伪操作 3、汇编控制(Assembly Control)伪操作 4、其他常用的伪操作,1、符号定义伪操作,符号定义伪指令用于定义ARM汇编程序中的变量、对变量赋值以及定义寄存器的别名等操作。常见的符号定义伪指令有如下几种: (1)定义全局变量 GBLA、GBLL和GBLS (2)定义局部变量 LCLA、LCLL和LCLS (3)对变量赋值 SETA、SETL、SETS,(1)GBLA、GBLL和GBLS,语法GBLA(GBLL或GBLS) 全局变量名 GBLA (Glob
4、al Arithmetic)定义一个全局的数字变量,并初始化为0 GBLL (Global Logic)定义一个全局的逻辑变量,并初始化为F(假) GBLS (Global String)定义一个全局的字符串变量,并初始化为空,(1)GBLA、GBLL和GBLS,GBLA Test1 Test1 SETA 0xaa GBLL Test2 Test2 SETL TRUE GBLS Test3 Test3 SETS “Testing”,(2)LCLA、LCLL和LCLS,语法格式LCLA(LCLL或LCLS) 局部变量名 LCLA伪指令用于定义一个局部的数字变量,并初始化为0 LCLL伪指令用于定
5、义一个局部的逻辑变量,并初始化为F(假) LCLS伪指令用于定义一个局部的字符串变量,并初始化为空,(2)LCLA、LCLL和LCLS,LCLA Test4 Test1 SETA 0xaa LCLL Test5 Test2 SETL TRUE LCLS Test6 Test3 SETS “Testing”,(3)SETA、SETL和SETS,语法变量名 SETA(SETL或SETS) 表达式 SETA伪指令用于给一个数字变量赋值 SETL伪指令用于给一个逻辑变量赋值 SETS伪指令用于给一个字符串变量赋值,(4) RLIST (Register List),语法名称 RLIST 寄存器列表 作
6、用对一个通用寄存器列表定义名称,使用该伪指令定义的名称可在ARM指令LDM/STM中使用 实例RegList RLIST R0-R5,R8,R10,2、数据定义伪操作,数据定义伪操作一般用于为特定的数据分配存储单元,同时可完成已分配存储单元的初始化。,(1) DCB (Distribute Continuous Byte),语法标号 DCB 表达式 作用分配一片连续的字节存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为0255的数字或字符串。DCB也可用“=”代替。 实例Str DCB “This is a test!”,(2) DCW,语法标号 DCW 表达式 作用分配一片连续的半
7、字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。 实例DataTest DCW 1, 2, 3,(3) DCD,语法标号 DCD 表达式 作用分配一片连续的字存储单元并用伪指令中指定的表达式初始化。其中,表达式可以为程序标号或数字表达式。DCD也可用“&”代替。 实例DataTest DCD 4, 5, 6,(4) DCFD,语法标号 DCFD 表达式 作用为双精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个双精度的浮点数占据两个字单元。 实例FDataTest DCFD 2E115, -5E7,(5) DCFS,语法标号 DCFS 表
8、达式 作用为单精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化。每个单精度的浮点数占据一个字单元 。 实例FDataTest DCFS 2E5, -5E-7,(6) DCQ,语法标号 DCQ 表达式 作用分配一片以8个字节为单位的连续存储区域并用伪指令中指定的表达式初始化。 实例DataTest DCQ 100,(7) SPACE,语法标号 SPACE 表达式 作用分配一片连续的存储区域并初始化为0。其中,表达式为要分配的字节数。SPACE也可用“”代替。 实例DataTest SPACE 100,(8) MAP,语法MAP 表达式,基址寄存器 作用定义一个结构化的内存表的首
9、地址。MAP也可用“”代替。表达式可以为标号或数学表达式,当基址寄存器选项不存在时,表达式的值即为内存表的首地址;当该选项存在时,内存表的首地址为表达式的值与基址寄存器的和。 实例MAP 0x100, R0,(9) FIELD,语法标号 FIELD 表达式 作用定义一个结构化内存表中的数据域。FILED也可用“#”代替。表达式的值为当前数据域在内存表中所占的字节数。 实例MAP 0x100A FIELD 16B FIELD 32S FIELD 256,4、其他常用的伪指令,AREA ALIGN ENTRY END EQU,EXPORT(或GLOBAL) IMPORT EXTERN GET(或I
10、NCLUDE) INCBIN,(1) AREA,语法AREA 段名 属性1, 属性2, 作用用于定义一个代码段或数据段。段名若以数字开头,则该段名需用“|”括起来,如|1_test| 。属性字段表示该代码段(或数据段)的相关属性,多个属性用逗号分隔。,(1) AREA,常用的属性如下: CODE 定义代码段 DATA 定义数据段 READONLY 指定本段为只读,代码段默认为READONLY READWRITE 指定本段为可读可写,数据段的默认为READWRITE,(1) AREA,COMMON定义一个通用的段,不包含任何的用户代码和数据。各源文件中同名的COMMON段共享同一段存储单元。 实
11、例 AREA Init,CODE,READONLY,(2) ALIGN,语法ALIGN 表达式,偏移量 作用通过添加填充字节的方式,使当前位置满足一定的对齐方式。其中,表达式的值用于指定对齐方式,可能的取值为2的幂,如1、2、4、8、16等。若未指定表达式,则将当前位置对齐到下一个字的位置。偏移量也为一个数字表达式,若使用该字段,则当前位置的对齐方式为:2的表达式次幂+偏移量。 实例AREA Init,CODE,READONLY,ALIGN3,(3) ENTRY,语法ENTRY 作用指定汇编程序的入口点 实例AREA Init,CODE,READONLYENTRY,(4) END,语法END
12、作用通知编译器已经到了源程序的结尾 实例AREA Init,CODE,READONLYEND,(5) EQU,语法名称 EQU 表达式, 类型 作用为程序中的常量、标号等定义一个等效的字符名称。其中EQU可用“*”代替。名称为EQU伪指令定义的字符名称。 实例Test EQU 50,(6) EXPORT(或GLOBAL),语法EXPORT 标号 作用声明一个全局的标号,该标号可在其他的文件中引用。标号在程序中区分大小写。 实例AREA Init,CODE,READONLYEXPORT Stest,(7) IMPORT,语法IMPORT 标号 作用通知编译器要使用的标号在其他的源文件中定义,但要
13、在当前源文件中引用。而且无论当前源文件是否引用该标号,该标号均会被加入到当前源文件的符号表中。 实例AREA Init,CODE,READONLYIMPORT main,(8) EXTERN,语法EXTERN 标号 作用通知编译器要使用的标号在其他的源文件中定义,但要在当前源文件中引用,如果当前源文件实际并未引用该标号,该标号就不会被加入到当前源文件的符号表中。 实例AREA Init,CODE,READONLYEXTERN main,(9) GET,语法GET 文件名 作用将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。可以使用INCLUDE代替GET。 实例AR
14、EA Init,CODE,READONLYGET a1.s GET C:a2.s,(10) INCBIN,语法INCBIN 文件名 作用将一个目标文件或数据文件包含到当前的源文件中,被包含的文件不作任何变动的存放在当前文件中,编译器从其后开始继续处理。 实例AREA Init,CODE,READONLYINCBIN a1.dat INCBIN C:a2.txt,(11) RN,语法名称 RN 表达式 作用给一个寄存器定义一个别名。其中,名称为给寄存器定义的别名,表达式为寄存器的编码。 实例Temp RN R0,(12) ROUT,语法名称 ROUT 作用给一个局部变量定义作用范围。在程序中未使
15、用该伪指令时,局部变量的作用范围为所在的AREA,而使用ROUT后,局部变量的作为范围为当前ROUT和下一个ROUT之间。,4.2.2 基于ARM的汇编语言程序设计,ARM汇编语言的语句格式为:标号 指令或伪指令 ;注释每条指令的助记符可以全部用大写、或全部用小写,但不允许在一条指令中大、小写混用。如果一条语句太长,可分为若干行,在行尾用续行符“” 。,汇编语言的语句格式,1、标号是代表地址的符号,必须在一行的顶格书写,其后不能添加冒号“:”。 2、程序中所有指令均不能顶格书写。 3、在ARM汇编语言中,ARM指令、伪指令、寄存器名等可以全部大写或者全部小写,但不能大小写混合使用。,4.2.3
16、 ARM汇编语言程序的基本结构,在ARM汇编程序中,以程序段为单位组织代码。段是相对独立的指令或数据序列,具有特定的名称。段可以分为代码段和数据段,代码段的内容为执行代码,数据段存放代码运行时需要用到的数据。一个汇编程序至少应该有一个代码段,当程序较长时,可以分割为多个代码段和数据段,多个段在程序编译链接时最终形成一个可执行的映象文件。,AREA Init,CODE,READONLY ENTRY Start LDR R0,= 0x3FF5000 LDR R1,= 0xFF STR R1,R0 LDR R0,= 0x3FF5008 LDR R1,= 0x01 STR R1,R0END,在汇编语言
17、程序中,用AREA伪指令定义一个段,并说明所定义段的相关属性,本例定义一个名为Init的代码段,属性为只读。ENTRY伪指令标识程序的入口点,接下来为指令序列。程序的末尾为END伪指令,该伪指令告诉编译器源文件的结束,每一个汇编程序段都必须有一条END伪指令,指示代码段的结束。,汇编语言的子程序调用,在ARM汇编语言程序中,子程序的调用一般是通过BL指令来实现的。BL 子程序名,汇编语言的子程序调用,该指令在执行时完成如下操作:将子程序的返回地址存放在连接寄存器LR中,同时将程序计数器PC指向子程序的入口点。当子程序执行完毕需要返回调用处时,只需要将存放在LR中的返回地址重新拷贝给程序计数器P
18、C即可。在调用子程序的同时,也可以完成参数的传递和从子程序返回运算的结果,通常可以使用寄存器R0R3完成。,AREA Init,CODE,READONLY ENTRY Start LDR R0,= 0x3FF5000 LDR R1,= 0xFF STR R1,R0 LDR R0,= 0x3FF5008 LDR R1,= 0x01 STR R1,R0 BL PRINT_TEXT ,PRINT_TEXTMOV PC,LREND,一个数据段的例子,AREA DataArea, DATA, NOINIT, ALIGN=2DISPBUF SPACE 200RCVBUF SPACE 200其中DATA为数
19、据段的标识。,4.2.4 基于ARM的汇编语言程序举例,以下是一个基于S3C2410的LED点灯程序。 ;LED点灯实验 ; 端口F寄存器预定义 rGPFCON EQU 0x56000050 rGPFDAT EQU 0x56000054 rGPFUP EQU 0x56000058,AREA Init,CODE,READONLY ENTRY ResetEntry ldr r0, = rGPFCON ldr r1, = 0x4000 ; 设置I/O口GPF7为输出属性 str r1, r0 ldr r0, = rGPFUP ldr r1, = 0xff ; 禁止GPF端口的上拉电阻 str r1,
20、 r0 ldr r2, = rGPFDAT,ledloopldr r1, = 0x80 str r1, r2 ;使GPF7输出高电平,D14灯亮bl delay ;调用延迟子程序ldr r1, = 0x00str r1, r2 ;使GPF7输出低电平,D14灯灭bl delay ;调用延迟b ledloop ;不断的循环,D14将不停的闪烁,;下面是延迟子程序 delay ldr r3, = 0x1ffff ;设置延迟的时间 delay1sub r3, r3, #1 ;r3=r3-1 cmp r3, #0x0 ;将r3的值与0相比较bne delay1 ;比较的结果不为0,继续调 用delay
21、1,否则执行下一条语句mov pc, lr ;返回END,4.4 基于ARM的C语言与汇编语言混合编程,汇编语言与C/C+的混合编程通常有以下几种方式: 在C/C+代码中嵌入汇编指令 在汇编程序和C/C+的程序之间进行变量的互访 汇编程序、C/C+程序间的相互调用在实际的编程应用中,使用较多的方式是:程序的初始化部分用汇编语言完成,然后用C/C+完成主要的编程任务,程序在执行时首先完成初始化过程,然后跳转到C/C+程序代码中。,4.4.1 C语言与汇编语言混合编程应遵守的规则,在C程序和ARM汇编程序之间相互调用时必须遵守ATPCS规则。ATPCS规定了一些子程序间调用的基本规则,比如:寄存器
22、的使用规则 子程序之间通过寄存器r0r3来传递参数,当参数个数多于4个时,使用堆栈来传递参数。 在子程序中,使用寄存器r4r11保存局部变量。 寄存器r12用于保存堆栈指针SP,当子程序返回时使用该寄存器出栈,记作IP。寄存器r13用作堆栈指针,记作SP。寄存器r14称为链接寄存器,记作LR。该寄存器用于保存子程序的返回地址。寄存器r15称为程序计数器,记作PC。,4.4.1 C语言与汇编语言混合编程应遵守的规则,在C程序和ARM汇编程序之间相互调用时必须遵守ATPCS规则。ATPCS规定了一些子程序间调用的基本规则,比如: 堆栈的使用规则 堆栈采用满递减类型(FD,Full Descendi
23、ng),即堆栈通过减小存储器地址而向下增长,堆栈指针指向内含有效数据项的最低地址。 参数的传递规则 整数参数的前4个使用r0r3传递,其他参数使用堆栈传递;浮点参数使用编号最小且能够满足需要的一组连续的FP寄存器传递参数 子程序的返回结果为一个32位整数时,通过r0返回;返回结果为一个64位整数时,通过r0和r1返回;依此类推。结果为浮点数时,通过浮点运算部件的寄存器F0、D0或S0返回,4.4.2 汇编程序调用C程序的方法,汇编程序调用C程序的方法为:首先在汇编程序中使用IMPORT伪指令事先声明将要调用的C语言函数;然后通过BL指令来调用C函数。,例如在一个C源文件中定义了如下求和函数:
24、int add(int x,int y)return(x+y); 调用add()函数的汇编程序结构如下: IMPORT add ;声明要调用的C函数 MOV r0,1 MOV r1,2 BL add ;调用C函数add,4.4.3 C程序调用汇编程序的方法,C程序调用汇编子程序的方法为:首先在汇编程序中使用EXPORT伪指令声明被调用的子程序,表示该子程序将在其他文件中被调用;然后在C程序中使用extern关键字声明要调用的汇编子程序为外部函数。,例如在一个汇编源文件中定义了如下求和函数: EXPORT add ;声明add子程序将被外部函数调用 add ;求和子程序addADD r0,r0,
25、r1MOV pc,lr 在一个C程序的main()函数中对add汇编子程序进行了调用: extern int add(int x,int y); /声明add为外部函数 void main()int a=1,b=2,c;c=add(a,b); /调用add子程序 ,4.4.4 C程序中内嵌汇编语句,在C语言中内嵌汇编语句可以实现一些高级语言不能实现或者高级语言不容易实现的功能以及时间紧迫的功能。 内嵌的汇编器支持大部分ARM指令和Thumb指令,但是不支持诸如直接修改PC实现跳转的底层功能,也不能直接引用C语言中的变量。,嵌入式汇编语句在形式上表现为独立定义的函数体,其语法格式为: _ _asm 指令;指令指令 ,