82
1 第 4 第 第第第第第第第第 本本本本本本 本本本本本本本本本 本本本本本本本本 本本本本本 C 本本本本本本本本 Linux 本本 ARM 本本 本本本本本本本本本

第4章 汇编语言程序设计

  • Upload
    camila

  • View
    160

  • Download
    5

Embed Size (px)

DESCRIPTION

第4章 汇编语言程序设计. 本章主要内容: 汇编语言源程序格式 汇编语言上机过程 汇编语言与 C 语言混合编程技术 Linux 下的 ARM 汇编 程序优化与性能测试. 文件类型. 扩展名. 汇编语言源文件. . s. C 语言源文件. . c. C++ 源文件. . cpp. 引入文件. . INC. 头文件. . h. ARM 源程序文件. 汇编源程序示例Ⅰ. 汇编源程序示例 Ⅱ. 汇编源程序示例 Ⅲ. 汇编源程序示例 Ⅳ. ARM 的汇编语言程序一般由几个段组成,每个段均由 AREA 伪操作定义。 - PowerPoint PPT Presentation

Citation preview

Page 1: 第4章 汇编语言程序设计

1

第 4 章 汇编语言程序设计 本章主要内容: 汇编语言源程序格式 汇编语言上机过程 汇编语言与 C 语言混合编程技术 Linux 下的 ARM 汇编 程序优化与性能测试

Page 2: 第4章 汇编语言程序设计

2

ARM 源程序文件 文件类型 扩展名汇编语言源文件 .s

C语言源文件 .c

C++源文件 .cpp

引入文件 .INC

头文件 .h

Page 3: 第4章 汇编语言程序设计

3

汇编源程序示例Ⅰ

Page 4: 第4章 汇编语言程序设计

4

汇编源程序示例Ⅱ

Page 5: 第4章 汇编语言程序设计

5

汇编源程序示例Ⅲ

Page 6: 第4章 汇编语言程序设计

6

汇编源程序示例Ⅳ ARM 的汇编语言程序一般由几个段组成,每个段均由

AREA 伪操作定义。 段可以分为多种,如代码段、数据段、通用段,每个段又有不同的属性,象代码段的默认属性为 READONLY ,数据段的默认属性为 READWRITE 。 本程序定义了两个段,第一个段为代码段 codesec ,它在存储器中存放用于程序执行的代码以及 main 函数的本地字符串;第二个段为数据段 constdatasec ,存放了全局的字符串,由于本程序没有对数据进行写操作,该数据段定义属性为 READONLY 。

Page 7: 第4章 汇编语言程序设计

7

汇编语言的行构成Ⅰ格式: [ 标签 ] 指令 / 伪操作 / 伪指令 操作数 [; 语句的注释 ] 所有的标签必须在一行的开头顶格写,前面不能留空格,后面也不能跟 C 语言中的标签一样加上“ :” ; ARM 汇编器对标识符的大小写敏感书写标号及指令时字母的大小写要一致; 注释使用“ ;” 符号,注释的内容从“ ;” 开始到该行的结尾结束。

Page 8: 第4章 汇编语言程序设计

8

汇编语言的行构成Ⅱ 标签 标签是一个符号,可以代表指令的地址、变量、数据的地址和常量。一般以字母开头,由字母、数字、下划线组成。当符号代表地址时又称标号,可以以数字开头,其作用范围为当前段或者在下一个 ROUT 伪操作之前。 指令 / 伪操作 指令 / 伪操作是指令的助记符或者定义符,它告诉

ARM 的处理器应该执行什么样的操作或者告诉汇编程序伪指令语句的伪操作功能。

Page 9: 第4章 汇编语言程序设计

9

汇编语言的标号 标号代表地址。标号分为段内标号和和段外标号。段内标号的地址值在汇编时确定,段外编号的地址值在链接时确定 。 在程序段中,标号代表其所在位置与段首地址的偏移量。根据程序计数器( PC )和偏移量计算地址即程序相对寻址。 在映像中定义的标号代表标号到映像首地址的偏移量。映像的首地址通常被赋予一个寄存器,根据该寄存器值与偏移量计算地址即寄存器相对寻址。 此外在宏中也可以使用局部符号。局部标号是 0~99 的十进位数开始,可以重复定义。 局部标号引用格式: % {F|B}{A|T} N{routname}

Page 10: 第4章 汇编语言程序设计

10

汇编语言的常量 程序中的常量是指其值在程序的运行过程中不能被改变的量。 数字常量:数字常量有 3 种表示方式:• 十进制数,如 1 、 2 、 123• 十六进制数,如 0x123,0xabc• n 进制数,形式为 n_XXX,n 的范围是 2 到 9 , XXX 是具体数字 字符常量:由单引号及中间的字符组成,包括 C 语言中的转义字符,如’ a’,’\n’ 字符串常量:由一对双引号及中间的字符串表示,中间也可以使用 C 语言中的转义字符,比如:“ abcdef\0xa\r\

n” 逻辑常量: {TRUE},{FALSE}, 注意带大括号

Page 11: 第4章 汇编语言程序设计

11

汇编程序的变量代换Ⅰ 这里所说的变量,是相对于汇编程序的“变量”,是用于汇编程序进行处理的,但一旦编译到程序中,则不会改变,成为常量。 如果在字符串变量的前面有一个 $ 字符,在汇编时编译器将用该字符串变量的内容代替该串变量。 如果在数字变量前面有一个代换操作符“ $” ,编译器会将该数字变量的值转换为十六进制的字符串,并用该十六进制的字符串代换“ $” 后的数字变量。 如果需要将“ $” 字符 加入到字符串中,可以用“ $$” 代替,此时编译器将不再进行变量代换,而是把“ $$”看作一个“ $”. 一般的,在两个“ |” 之间的“ $”并不进行变量的代换,但如果“ |” 在双引号内,则将进行变量代换。 使用“ .”来表示字符串中变量名的结束。

Page 12: 第4章 汇编语言程序设计

12

汇编程序的变量代换Ⅱ

Page 13: 第4章 汇编语言程序设计

13

伪指令 在 ARM 汇编语言源程序中有些特殊助记符,它们没有相对应的操作码或者机器码,通常称为伪指令,它们所完成的操作称为伪操作。伪指令在源程序中的作用是为完成汇编程序作各种准备工作的,由汇编程序在源程序的汇编期间进行处理,仅在汇编过程中起作用。 符号定义伪指令 数据定义伪指令 汇编控制伪指令 信息报告伪指令 宏指令以及其他伪指令。

Page 14: 第4章 汇编语言程序设计

14

符号定义伪指令Ⅰ 用于定义 ARM 汇编程序中的变量、对变量赋值以及定义寄存器的别名等。 用于定义局部变量的 LCLA 、 LCLL 、 LCLS ; 用于定义全局变量的 GBLA 、 GBLL 、 GBLS ; 用于对变量赋值的 SETA 、 SETL 、 SETS ; 为通用寄存器列表定义名称的 RLIST 。

Page 15: 第4章 汇编语言程序设计

15

符号定义伪指令Ⅱ

1. LCLA 、 LCLL 、 LCLS 格式: LCLA/LCLL/LCLS 局部变量名 说明: LCLA 、 LCLL 、 LCLS 伪指令用于定义一个汇编程序中的局部变量,并初始化,其中: LCLA 定义一个局部的数字变量,初始化为 0 ; LCLL 定义一个局部的逻辑变量,初始化为 F ; LCLS 定义一个局部的字符串变量,初始化为空串;这三条伪指令用于声明局部变量,在其局部作用范围内变量名必须唯一。

Page 16: 第4章 汇编语言程序设计

16

符号定义伪指令Ⅲ

2. GBLA 、 GBLL 、 GBLS 格式: GBLA/GBLL/GBLS 变量名 说明: GBLA 、 GBLL 、 GBLS 伪操作定义一个汇编程序中的全局变量,并初始化,其中: GBLA 定义一个全局数字变量,并初始化为 0 ; GBLL 定义一个全局逻辑变量,并初始化为“ F” ; GBLS 定义一个全局字符串变量,并初始化为空串;

这三条伪指令用于定义全局变量,因此在整个程序范围内变量名必须唯一。

Page 17: 第4章 汇编语言程序设计

17

符号定义伪指令Ⅳ 3. SETA 、 SETL 、 SETS 格式:变量名 SETA/SETL/SETS 表达式 说明: SETA :给一个数字变量赋值; SETL :给一个逻辑变量赋值; SETS :给一个字符串变Ⅳ量赋值;

格式中的变量名必须为已经定义过的全局或局部变量,表达式为将要赋给变量的值。

Page 18: 第4章 汇编语言程序设计

18

符号定义伪指令Ⅴ4. RLIST 格式:名称 RLIST { 寄存器列表 } 说明: RLIST 可用于对一个通用寄存器列表定义名称,该名称可在 ARM 指令 LDM/STM 中使用。在 LDM/

STM 指令中,列表中的寄存器为根据寄存器的编号由低到高访问次序,与列表中的寄存器排列次序无关。

Page 19: 第4章 汇编语言程序设计

19

数据定义伪指令Ⅰ 用于为数据分配存储单元,同时也可完成已分配存储单元的初始化。 DCB DCW/DCWU DCD/DCDU DCQ/DCQU DCFS/DCFSU DCFD/DCFDU SPACE FIELD MAP

Page 20: 第4章 汇编语言程序设计

20

数据定义伪指令Ⅱ 1. DCB: 标号 DCB 表达式 说明: DCB 用于分配一块字节单元并用伪指令中指定的表达式进行初始化。其中,表达式可以为使用双引号的字符串或 0——255 的数字 ,DCB 可用“ =” 代替。 2. DCW/DCWU: 标号 DCW/DCWU 表达式 说明: DCW 分配一段半字存储单元并用表达式值初始化,它定义的存储空间是半字对齐的。

Page 21: 第4章 汇编语言程序设计

21

数据定义伪指令Ⅲ 3. DCD/DCDU : 标号 DCD/DCDU 表达式 说明: DCD 伪指令用于分配一块字存储单元并用伪指令中指定的表达式初始化,它定义的存储空间是字对齐的。 DCD 也可用“ &” 代替。 4. DCQ/DCQU : 标号 DCQ/DCQU 表达式 说明: DCQ 用于分配一块以 8 个字节为单位的存储区域并用伪指令中指定的表达式初始化,它定义的存储空间是字对齐的。 DCQU功能跟 DCQ 类似,只是分配的存储单元不严格字对齐。

Page 22: 第4章 汇编语言程序设计

22

数据定义伪指令Ⅳ 5. DCFD/DCFDU: 标号 DCFD/DCFDU 表达式 说明: DCFD 用于为双精度的浮点数分配一片连续的字存储单元并用伪指令中指定的表达式初始化,它定义的存储空间是字对齐的,每个双精度的浮点数占据两个字单元。 DCFDU 功能跟

DCFD 类似,只是分配的存储单元不严格字对齐。 6. DCFS/DCFSU: 标号 DCFS/DCFSU 表达式 说明: DCFS 用于为单精度的浮点数分配一片连续的字存储单元并用表达式初始化 ,它定义的存储空间是字对齐的 ,每个单精度浮点数使用一个字单元 .DCFSU 功能跟 DCFS 类似,只是分配的存储单元不严格字对齐。

Page 23: 第4章 汇编语言程序设计

23

数据定义伪指令Ⅴ 7. SPACE: 标号 SPACE 表达式 说明: SPACE 用于分配一片连续的存储区域并初始化为 0,表达式为要分配的字节数 ,SPACE 也可用“%”代替。 8. MAP: MAP 表达式 [,基址寄存器 ] 说明: MAP 定义一个结构化的内存表的首地址, “^”可以用来代

替 MAP 。 9. FILED: 标号 FIELD 字节数 说明: FIELD 用于定义一个结构化内存表中的数据域,“ #” 可用来代替 FILED 。

Page 24: 第4章 汇编语言程序设计

24

汇编控制伪指令Ⅰ 汇编控制伪操作用于指引汇编程序的执行流程: MACRO 、 MEND IF 、 ELSE 、 ENDIF  WHILE 、 WEND  MEXIT

Page 25: 第4章 汇编语言程序设计

25

汇编控制伪指令Ⅱ 1. MACRO 、 MEND MACRO [$ 标号 ] 宏名 [$参数 1 , $参数 2 ,…… ] 指令序列 MEND 说明: MACRO 表明一个宏定义的开始, MEND 则表示一个宏的结束,

MACRO 、 MEND 前呼后应可以将一段代码定义为一个整体,又称宏,然后就可以在程序中通过宏的名称及参数调用该段代码。 MACRO 和 MEND 之间的代码称为宏定义体,在宏定义体的第一行声明宏的原型,宏的原型包含宏名、所需的参数。在源程序被编译时,汇编器将宏调用展开,用宏定义中的指令序列替换程序中的宏调用,并将实际参数的值传递给宏定义中的参数。注意宏操作可以嵌套使用,并可以在编译时用选项加以控制。

Page 26: 第4章 汇编语言程序设计

26

汇编控制伪指令Ⅲ 2. IF 、 ELSE 、 ENDIF IF 逻辑表达式

代码段 1 ELSE

代码段 2 ENDIF 说明: IF 、 ELSE 、 ENDIF 伪操作能根据逻辑表达式的成立与否决定是否在编译时加入某个指令序列。 IF 、 ELSE 、 ENDIF可以分别用“ [” 、“ |” 、“ ]” 代替。 IF 、 ELSE 、 ENDIF伪指令可以嵌套使用。

Page 27: 第4章 汇编语言程序设计

27

汇编控制伪指令Ⅲ 3. WHILE 、 WEND: WHILE 逻辑表达式

代码段 WEND 说明: WHILE 和 WEND 伪指令能根据逻辑表达式的成立与否决定是否循环执行这个代码段。 WHILE 、 WEND 伪指令可以嵌套使用。 4. MEXIT: MEXIT 说明: MEXIT 用于从宏中退出。

Page 28: 第4章 汇编语言程序设计

28

其他伪指令Ⅰ 1. ASSERT: ASSERT 逻辑表达式 说明: ASSERT 用来表示程序的编译必须满足一定的条件,如果逻辑表达式不满足,则编译器会报错。 2. ALIGN: ALIGN [ 表达式 [,偏移量 ]] 说明: ALIGN 伪操作可以通过填充字节使当前的位置满足一定的对齐方式。其中,表达式的值为 2 的幂,如 1 、 2 、 4 、 8 、 16等,用于指定对齐方式。如果伪操作中没有指定表达式,则编译器会将当前位置对齐到下一个字的位置。偏移量也是个数字表达式,如果存在偏移量,则当前位置的自动对齐到: 2 的表达式值次方+偏移量。

Page 29: 第4章 汇编语言程序设计

29

其他伪指令Ⅱ 3. AREA: AREA 段名 属性,…… 说明: AREA 用于定义一个代码段、数据段或者特定属性的段。如果段名以数字开头,那么该段名需用“ |” 字符括起来,如 |7wolf| ,用 C的编译器产生的代码一般也用“ |” 括起来。属性部分表示该代码段 /数据段的相关属性,多个属性可以用“,”分隔。 常见属性如下:•  DATA :定义数据段。•  CODE :定义代码段。• READONLY :表示本段为只读。• READWRITE :表示本段可读写。• ALIGN= 表达式:对齐方式为 2 表达式次方,例如:表达式 =3 ,则对齐方式为 8 字节对齐。表达式的取值范围为 0——31 。• COMMON 属性:定义一个通用段,这个段不包含用户代码和数据。

Page 30: 第4章 汇编语言程序设计

30

其他伪指令Ⅲ 4. CODE16 、 CODE32: CODE16/CODE32 说明: CODE16 伪操作指示编译器后面的代码为 16 位的 Thumb指令。 CODE32 伪操作指示编译器后面的代码为 32 位的 ARM 指令。 5. ENTRY: ENTRY 说明: ENTRY 用于指定汇编程序的入口。在一个完整的汇编程序中至少要有一个 ENTRY ,程序中也可以有多个,此时,程序的真正入口点可在链接时指定,但在一个源文件里最多只能有一个 ENTRY 或者没有 ENTRY 。

Page 31: 第4章 汇编语言程序设计

31

其他伪指令Ⅳ 6. END: END 说明:“ END” 告诉编译器已经到了源程序的结尾。 7. EQU : 名称 EQU 表达式 [,类型 ] 说明: EQU 用于将程序中的数字常量、标号、基于寄存器的值赋予一个等效的名称,这一点类似于 C 语言中的# define ,可用“ *” 代替 EQU 。 如果表达式为 32 位的常量,我们可以指定表达式的数据类型,类型域可以有以下三种: CODE16/CODE32/DATA

Page 32: 第4章 汇编语言程序设计

32

其他伪指令Ⅴ 8. EXPORT: EXPORT 标号 [,WEAK] 说明: EXPORT 在程序中声明一个全局标号,其他的文件中的代码可以 该标 号可被引用。用户也可以 用 GLOBAL 代替 EXPORT 。

[,WEAK] 可选项声明其他文件有同名的标号,则该同名标号优先于该标号被引用。 9. IMPORT: IMPORT 标号 [, WEAK] 说明: IMPORT 告诉编译器这个标号要在当前源文件中使用,但标号是在其他的源文件中定义的。不管当前源文件是否使用过该标号,这个标号都会加入到当前源文件的符号表中。 [, WEAK]选项表示如果所有的源文件都没有找到这个标号的定义,编译器也不会提示错误信息。编译器在多数情况下将该标号置为 0 ,如果这个标号被 B或

BL 指令引用,则将B或 BL 指令替换为 NOP 操作。

Page 33: 第4章 汇编语言程序设计

33

其他伪指令Ⅵ 10. EXTERN: EXTERN 标号 [,WEAK] 说明: EXTERN 告诉编译器所使用的标号要在当前源文件中引用 ,但该标号是在其他的源文件中定义的。与 IMPORT 不同的是,如果当前源文件实际上没有引用该标号,该标号就不会被加入到当前文件的符号表中。 [, WEAK]选项意义同 IMPORT 。 11.  RN: 名称 RN 表达式 说明: RN 用于给一个寄存器定义一个别名 ,以便程序员记忆该寄存器的功能。其中,名称为给寄存器定义的别名,表达式为寄存器的编码。

Page 34: 第4章 汇编语言程序设计

34

其他伪指令Ⅶ 12. GET/INCLUDE : GET 文件名 说明: GET将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置展开进行汇编处理。 INCLUDE 和 GET 作用等效的。我们通常这样使用这个伪指令:在某源文件中定义一些宏指令,用

MAP 和 FIELD 定义结构化的数据类型,用 EQU 定义常量的符号名称 ,然后 用 GET/INCLUDE 将这 个 源 文 件 包含到 其他的 源 文 件中。 GET/INCLUDE只能用于包含源文件,包含其他文件则需要使用 INCBIN 伪指令。

13. INCBIN: INCBIN 文件名 说明: INCBIN将一个数据文件或者目标文件包含到当前的源文件中,编译时被包含的文件不作任何变动的存放在当前文件中,编译器从后面开始继续处理。

Page 35: 第4章 汇编语言程序设计

35

其他伪指令Ⅷ 14. ROUT: [名称 ] ROUT 说明: ROUT 可以给一个局部变量定义作用范围。在程序中未使用该伪指令时,局部变量的作用范围为所在的 AREA ,而使用

ROUT 后,局部变量的作为范围为当前 ROUT 和下一个 ROUT之间。

Page 36: 第4章 汇编语言程序设计

36

汇编语言上机过程 用 ARM 汇编语言编写的源程序,要使之运行必须经过以下几个步骤: 1.编辑汇编源程序,保存为文件名后缀是“ .s” 的文件; 2. 调用汇编程序对源程序进行汇编,生成目标文件; 3. 把目标文件链接成 ELF 格式的可执行映像文件 ( 生成可以放进 ARM软件仿真器进行调试的映象文件 ),该文件可以进行调试; 4. 用 fromelf工具把可执行的 ELF 映像文件转换成二进制映像文件; 5. 下载可执行的二进制映像文件到 ARM 的目标板;

Page 37: 第4章 汇编语言程序设计

37

ARM 的开发工具 ADS1.2 ADS1.2 是 ARM公司推出的一套 ARM 汇编、 C 、 C++ 的集成开发环境。包含了几个有用的开发工具,包括: CodeWarrior IDE for the ARM Developer Suite :为

ARM的程序员管理、开发软件工程项目提供了一个简单直观、灵活的用户界面。  AXD Debuger : AXD是一个功能强大、使用方便的调试器。

Page 38: 第4章 汇编语言程序设计

38

编辑汇编语言源程序 可以使用简单的 Windows自带的记事本程序来编辑

ARM 的汇编程序: 单击开始菜单程序附件记事本

敲入汇编代码保存为 .s文件 也可以使用 CodeWarrior IDE来编辑汇编程序: 单击 File菜单的 New菜单项 单击 Project标签页 单击工具栏的 New Text按钮 敲入汇编代码保存为 hello.s文件 单击 Project “菜单,选择 Add *.s to project”

Page 39: 第4章 汇编语言程序设计

39

编译汇编语言源程序Ⅰ ARM 的编译器有如下几种 armcc:ARM C 编译器 ,具有优化功能 , 兼容 ANSI C tcc:Thumb 的 C 编译器 ,同样具有优化功能兼容 ANSI C 。 armcpp:ARM C++ 编译器,遵循 ANSI C++ 或者 EC++ 标准 tcpp:Thubm 的 C++ 编译器,遵循 ANSI C++ 或者 EC++标准 armasm:支持 ARM 和 Thumb 的汇编器 这些编译器输出的是 ELF 格式的目标文件,可以包括

RAWF2 格式的调试信息。同时通过特殊的控制选项可以输出汇编语言文件或者列表文件。

Page 40: 第4章 汇编语言程序设计

40

编译汇编语言源程序Ⅱ 4 种 ARM C 和 C++ 编译器 (armcc , tcc , armcpp 和

tcpp)的命令通用语法 : compiler [PCS-options] [source-language] [search-paths] [preprocessor-options] [output-format] [target-options]

[debug-options] [code-generation-options] [warning-options] [additional-checks] [error-options] [source] [-via filename]

用户通过命令行操作选项指引编译器运行。所有的选项都”是以符号 -”开始,有些选项后面还跟有参数。在大多数情况下, ARM C 和 C++编译器允许在选项和参数之间存在空格。

Page 41: 第4章 汇编语言程序设计

41

编译汇编语言源程序Ⅲ PCS-options :指定了要使用的过程调用标准; source-language :指定了编译器可以接受的编写源程序的语言种类。对于 C 编译器默认的语言是 ANSI C ,对于

C++ 编译器默认是 ISO 标准 C++ ; search-paths :该选项指定了对包含的文件 (包括源文件和头文件 )的搜索路径; preprocessor-options :该选项指定了预处理器的行为,其中包括预处理器的输出和宏定义等特性; output-format :该选项指定了编译器的输出格式,可以使用该项生成汇编语言输出列表文件和目标文件; target-options :该选项指定目标处理器或 ARM 体系结构; debug-options :该选项指定调试信息表是否生成,和该调试信息表生成时的格式;

Page 42: 第4章 汇编语言程序设计

42

编译汇编语言源程序Ⅳ code-generation-options :该选项指定了例如优化,字节顺序和由编译器产生的数据对齐格式等选项; warning-options :该选项决定警告信息是否产生; additional-checks :该选项指定了几个能用于源码的附加检查,例如检查数据流异常,检查没有使用的声明等; error-options :该选项可以关闭指定的可恢复的错误,或者将一些指定的错误降级为警告; source :该选项提供了包含有 C 或 C++ 源代码的一个或多个文件名,默认的,编译器在当前路径寻找源文件和创建输出文件。如果源文件是用汇编语言编写的 (也就是说该文件的文件名是以 .s作为扩展名 ),汇编器将被调用来处理这些源文件。 -via filename : WINDOWS系统对命令行的长度有限制,可以通过

filename的文件读取命令行选项。用户可以对 -via进行嵌套调用,在文件 filename中再通过 -via filename2包含了另外一个文件。

Page 43: 第4章 汇编语言程序设计

43

编译汇编语言源程序Ⅴ armcc 的编译选项如下: -c 只编译而不进行连接 -C 预处理时不删除注释 -D<symbol> 定义 symbol, 作用相当于 define -E 只处理 C 的源代码 -f<options> 打开编译器自定义的选项 -g<options> 在目标文件中生成调试信息以供调试器使用 -I<directory> directory 是头文件所在的路径 -J<directory> 不包含编译器默认的包含路径而使用 directory -o<file> 输出文件名为 file -OO 最小优化,包含最多的调试信息 -O1 限制优化,包含部分调试信息 -O2 最大的优化 -S 生成汇编语言文件 -U<symbol> 功能相当于 #undef symbol -W<options> 禁止所有或部分警告信息

Page 44: 第4章 汇编语言程序设计

44

C 语言 armcc 编译示例 (1)armcc –c –g hello.c –o hello.o 把 hello.c 源文件编译成包括 DWARF 格式调试表的 ELF 格

式的目标文件 hello.o 。目标文件是 32 位的 arm 码。 -c 告诉编译器,只编译不链接; -g 告诉编译器增加源码级别的调试信息 ELF: Executable and Linking Format DWARF: Debugging With Attributed Record Formats

Page 45: 第4章 汇编语言程序设计

45

C 语言 armcc 编译示例 (2)armcc –thumb –c –g hello.c –o hello.o 把 hello.c 源文件编译成包括 DWARF 格式调试表的 ELF 格

式的目标文件 hello.o 。目标文件是 16 位的 thumb 码。 -c 告诉编译器,只编译不链接; -g 告诉编译器增加源码级别的调试信息 ELF: Executable and Linking Format DWARF: Debugging With Attributed Record Formats

Page 46: 第4章 汇编语言程序设计

46

汇编语言 armasm 编译示例(1)

armasm –32 –g hello.s –o hello.o 把 hello.s 源文件编译成包括 DWARF 格式调试表的 ELF 格

式的目标文件 hello.o 。 目标文件是 32 位的 arm 码。 -g: 告诉编译器增加源码级别的调试信息

Page 47: 第4章 汇编语言程序设计

47

汇编语言 armasm 编译示例(2)

armcc –16 –g hello.c –o hello.o 把 hello.c 源文件编译成包括 DWARF 格式调试表的 ELF 格

式的目标文件 hello.o 。 目标文件是 16 位的 thumb 码。 -g 告诉编译器增加源码级别的调试信息

Page 48: 第4章 汇编语言程序设计

48

连接装配汇编程序Ⅰ 使用 armlink程序对 ARM的汇编源程序进行连接,它也可以将多个 .o目标文件连接生成最终的可执行文件。 术语 : 映像文件 (image) :是指一个可执行文件,在执行的时候被加载到处理器 中 。 一 个 映 像 文 件 可 以 有 多 个 线 程 。 它 是 ELF(Executable and

linking format) 格式的。 段 (Section) :描述映像文件的代码或数据块。 RO : Read-only缩写。 RW :是 Read-write缩写。 ZI :是 Zero-initialized缩写。

Page 49: 第4章 汇编语言程序设计

49

连接装配汇编程序Ⅱ Read Only Position Independent(ROPI) :只读数据中代码和的地址在运行时候可以改变。 Read Write Position Independent(RWPI) :一个段中的可读 / 写的数据地址在运行期间可以改变。 输入段 (input section) :它包含着代码,初始化数据或描述了在应用程序运行之前必须要初始化为 0 的一段内存。 输出段 (output section) :它包含了一系列具有相同的 RO , RW 或 ZI 属性的输入段。 域 (Regions) :在一个映像文件中,一个域包含了 1至 3 个输出段。多个域组织在一起,就构成了最终的映像文件。 加载时地址:映像文件载入存储器时的地址。 运行时地址:映像文件运行时的地址。

Page 50: 第4章 汇编语言程序设计

50

连接装配汇编程序Ⅲ 命令语法: armlink [-partial] [-help] [-o file] [-elf] [-vsn] [-reloc][-ro-base

address] [-ropi] [-rw-base address] [-rwpi] [-split] [-scatter file][-debug|-nodebug][-remove RO/RW/ZI/DBG]|-noremove] [-entry location ] [-keep section-id] [-first section-id] [-last section-id] [-libpath pathlist] [-scanlib|-noscanlib] [-locals|-nolocals] [-callgraph] [-info topics] [-map] [-symbols] [-symdefs file] [-edit file] [-xref] [-xreffrom object(section)] [-xrefto object(section)] [-errors file] [-list file] [-verbose] [-unmangled |-mangled] [-match crossmangled][-via file] [-strict] [-unresolved symbol][-MI|-LI|-BI] [input-file-list]

Page 51: 第4章 汇编语言程序设计

51

连接装配汇编程序Ⅳ armlink的使用: armlink *.o -o *.axf 使用 ADS1.2集成开发环境进行编译、连接: 激活 Targets标签单击 Debug左边的红色标签激活

files 标签,右键单击 *.s 文件单击 Compile 菜单项 Make 按钮

Page 52: 第4章 汇编语言程序设计

52

armlink 链接示例 (1)armlink –ro-base 0x8000 test1.o test2.o –o test.axf 把两个目标文件 test1.o 和 test2.o 链接成一个可执行的映

像文件 test.axf 。 -ro-base: 表示代码段 (RO) 在装载域 (load view) 和运行

域 (execution view) 里的起始地址; -o: 表示输出文件为 test.axf

Page 53: 第4章 汇编语言程序设计

53

汇编程序的运行 生成的 *.axf 文件是 ARM 的 ELF 格式的可执行映像文件;这个文件可以载入 AXD 进行仿真调试。使用 armsd在终端模拟它在 ARM目标机上的运行 :

执行 armsd -exec *.axf 直接在 CodeWarrior IDE 中运行映象文件:在 project窗口中点击 run按钮。在 AXD 上调试成功之后,我们可以通过 fromelf 工具将

ELF 文件转换为二进制格式文件下载到目标板执行。转化的命令如下: fromelf *.axf -bin *

Page 54: 第4章 汇编语言程序设计

54

汇编程序的调试 使用 AXD 进行调试 : 编译、连接成功之后,我们可以点击 project 窗口的 debug按钮启

动 AXD 进行调试 : 主框架窗口上方的调试工具栏有几个常用的按钮: go :使程序运行直到下一个断点停止 step in :进入函数内部运行 step: 单步调试,每次移动一行 step out :跳出循环或函数 run to cursor: 运行到光标所在的位置然后停止。

Page 55: 第4章 汇编语言程序设计

55

汇编语言与 C 语言混合编程技术 ARM 体系结构支持 ARM 的汇编语言与 C 与

C++ 的混合编程 .一般的在一个完整的程序设计的中,除了初始化部分用汇编语言完成外,其大部分的编程任务一般都用 C 或 C++ 完成。

Page 56: 第4章 汇编语言程序设计

56

汇编程序中访问 C 程序变量Ⅰ 在汇编的源程序中调用 C 语言风格的字符串需要使用IMPORT 伪 操 作 。 IMPORT 相 当 于 C 语 言 中 的extern关键字,告诉编译器引用的符号不是在本文件中定义的,而是在其他的源文件中定义的。

IMPORT symbol [,WEAK]symbol 是声明的符号的名称 ;[,WEAK] 指示编译器如果发现 symbol 在所有的源文件中都没有找到,那么它也不会产生任何的错误信息。示例见下页。

Page 57: 第4章 汇编语言程序设计

57

汇编程序中访问 C 程序变量ⅡC语言代码文件 str.c, 里面只有一个简单的字符串的定义char *strhello="Hello world!\n\0";汇编代码文件 hello.s1 AREA ||.text||, CODE, READONLY 2 main PROC3 STMFD sp!,{lr}4 LDR r0,strtemp5 LDR r0,[r0]6 BL _printf7 LDMFD sp!,{pc}8 strtemp9 DCD strhello10 ENDP11 EXPORT main12 IMPORT strhello13 IMPORT _main14 IMPORT __main15 IMPORT _printf16 IMPORT ||Lib$$Request$$armlib||, WEAK17                END

Page 58: 第4章 汇编语言程序设计

58

C 程序中内嵌汇编指令Ⅰ 在 ARM 的 C 语言程序中我们可以使用关键字 __asm来加入一段汇编语言的程序,格式如下: __asm {

instruction/*comment*/ …

}

Page 59: 第4章 汇编语言程序设计

59

C 程序中内嵌汇编指令Ⅱ在 C 语言中嵌入的 ARM 汇编需要注意一些问题:在汇编指令中,可以使用表达式,使用逗号“,”作为分隔符 ;如果一条指令占用了多行,那么应该使用符号“ \”续行,如果一行中有多个汇编指令,那么应该使用“ ;”将多个指令隔开 ,汇编中不能再使用“;”作为注释行的开头,而应该使用 C 语言中的“ /**/” 或者“ //” 进行注释 ;不要企图使用一个物理寄存器去改变一个 C 变量;对于内嵌的汇编代码用到的寄存器,编译器在编译时会自动加入保存和恢复这些寄存器的代码而不用用户去管理,除了寄存器 CPSR 和SPSR ,其他寄存器都必须先赋值然后再读取,否则编译时将出现错误。

Page 60: 第4章 汇编语言程序设计

60

C 程序中内嵌汇编指令Ⅲ内嵌的 ARM 汇编与纯粹的 ARM 汇编的区别有 :内嵌的 ARM 汇编不支持 ADR 、 ADRL 伪指令;十六进制数使用 0x 作为前缀,而后者使用 &;编译器在编译函数时使用 R0 、 R1 、 R2 、 R3 、 IP 和 LR 存放中间结果,因此使用这些寄存器时要特别小心;注意 C 语言中的 C 语言变量不要和物理寄存器同名,否则可能出现混乱;内嵌的汇编代码不能使用 PC 寄存器返回当前指令的地址;STM 和 LDM 指令中不能使用 C 语言表达式;不支持 BX 和 BLX ;用户可以改变处理器的模式,但仍然需要自己把处理器模式恢复过来,编译器不会自动做处理;嵌汇编指令常量前面的符号“ #” 可以省略;嵌汇编指令不支持内存分配的伪操作。

Page 61: 第4章 汇编语言程序设计

61

C 程序调用汇编程序 为了满足 ARM 汇编、 C 与 C++ 之间的互相调用,必须保证编写的代码遵循

APCS ( ARM 过程调用标准)。 APCS规定了子程序调用的基本规则,这些规则包括子程序调用过程中寄存器、数据栈的使用规则以及参数的传递规则。

Page 62: 第4章 汇编语言程序设计

62

Linux 下的 ARM 汇编 Linux 下的 ARM 汇编指令与 Window 下的汇编相比 ,在 ARM 处理器的指令使用方面是大同小异的 ,不同的是编译工具和 ARM 汇编的伪操作 ,其语法更类似于 gas 风格的汇编 . GNU提供的基于 ARM平台的编译工具如下: 汇编器 arm-elf-as ,作用是将汇编语言程序转化为 ELF 格式的可重定位目标代码。 C 编译器 arm-elf-gcc ,是 ARM 的交叉编译器,运行于 Linux平台,它在编译时,首先通过预处理程序对源代码进行处理,然后调用 ccl将处理后的程序转化为汇编代码,最后通过 arm-elf-as将汇编代码编译为目标代码。 连接器 arm-elf-ld ,可以根据链接定位文件的代码区 数据区 BSS区和栈区等定位信息,将多个可重定位的目标模块链接成一个单一的,绝对定位的, ELF 格式的目标程序。 二进制转换工具 arm-elf-objcopy ,是目标格式转化工具,它能够将

ELF 格式的文件转化为其他格式的文件。

Page 63: 第4章 汇编语言程序设计

63

Linux 汇编程序中的标号 标号只能由 a ~ z , A ~ Z , 0 ~ 9 “, .”, _等字符组成 .当标号为 0~ 9 的数字时为局部标号 ,局部标号可以重复出现 ,使用方法如下 :标号 f: 在引用的地方向前的标号;标号 b: 在引用的地方向后的标号使用局部符号的例子 (“@” 符号后的语句为注释 ):1:

subs r0,r0,#1 @ 每次循环使 r0=r0-1 bne 1f @跳转到 1 标号去执行

局部标号代表它所在的地址 , 因此也可以当作变量或者函数来使用。

Page 64: 第4章 汇编语言程序设计

64

Linux 汇编程序中的分段名 段名:汇编系统预定义的段名有 : .text 、 .data 、 .bss 、 .sdata 、 .sbss等 ,每一个段以段名为开始 ,以下一个段名或者文件结尾为结束 . 用户可以通过 .section 伪操作来自定义一个段 ,格式如下 : .section section_name [, "flags"[, %type[,flag_specific_arguments]]] 示例: .section .mysection @ 自 定 义 数 据 段 , 段 名 为 “ .mysection” .align 2 .strtemp: .ascii "Temp string \n\0"

Page 65: 第4章 汇编语言程序设计

65

Linux 汇编程序中的宏定义 格式如下 : .macro 宏名 参数名列表 @伪指令 .macro 定义一个宏 宏体 .endm @.endm 表示宏结束 如果宏使用参数 ,那么在宏体中使用该参数时

“添加前缀 \” 。宏定义时的参数还可以使用默认值。 可以使用.exitm伪指令来退出宏。

Page 66: 第4章 汇编语言程序设计

66

Linux 汇编程序中的常数 十进制数以非 0 数字开头 ,如 :123 和 9876 ; 二进制数以 0b开头 ,其中字母也可以为大写; 八进制数以 0 开始 ,如 :0456,0123 ; 十六进制数以 0x 开头 ,如 :0xabcd,0X123f ; 字符串常量需要用引号括起来 ,中间也可以使用转义字符 ,如 : “You are welcome!\n” ; 当前地址以” .” 表示 ,在汇编程序中可以使用这个符号代表当前指令的地址; 表达式 : 在汇编程序中的表达式可以使用常数或者数值 , “-”表示取负数 , “~” 表示取补 ,”<>” 表示不相等 ,其他的符号如 :+、 -、 *、 / 、 %、 <、 <<、 >、 >>、 |、 &、^、 !、 ==、 >=、 <=、 &&、 ||跟 C 语言中的用法相似。

Page 67: 第4章 汇编语言程序设计

67

Linux 下 ARM 汇编的常用伪操作 数据定义伪操作: .byte , .short , .long , .quad , .float , .string/.asciz/.ascii ,重复定义伪操作 .rept ,赋值语句 .equ/.set ; 函数的定义 ; 对齐方式伪操作 .align ; 源文件结束伪操作 .end ; .include伪操作; if伪操作; .global/ .globl 伪操作 ; .type伪操作 ; 列表控制语句 ; 区别于 gas汇编的通用伪操作 ,下面是 ARM特有的伪操作 : .reg , .unreq , .code , .thumb , .thumb_func , .thu

mb_set , .ltorg , .pool

Page 68: 第4章 汇编语言程序设计

68

数据定义伪操作Ⅰ 1. .byte: 单字节定义 ,如: .byte 1,2,0b01,0x34,072,’s’ ; 2. .short: 定义双字节数据 ,如 :.short 0x1234,60000 ; 3. .long: 定义 4 字节数据 ,如 :.long 0x12345678,23876565 4. .quad: 定义 8 字节 ,如 :.quad 0x1234567890abcd 5. .float :定义浮点数 ,如 : .float 0f-314159265358979323846264338327\ 95028841971.693993751E-40 @ - pi 6. .string/.asciz/.ascii :定义多个字符串 ,如 : .string “abcd”, “efgh”, “hello!” .asciz “qwer”, “sun”, “world!” .ascii “welcome\0”

Page 69: 第4章 汇编语言程序设计

69

数据定义伪操作Ⅱ 7. .rept: 重复定义伪操作 ,格式如下 : .rept 重复次数 数据定义 .endr /* 结束重复定义 */ 例如 : .rept 3 .byte 0x23 .endr 8. .equ/.set: 赋值语句 ,格式如下 : .equ(.set) 变量名 ,表达式 例如 : .equ abc 3 /* 让 abc=3*/

Page 70: 第4章 汇编语言程序设计

70

函数的定义伪操作Ⅰ 函数的定义 ,格式如下 : 函数名 : 函数体 返回语句 一般的 , 函数如果需要在其他文件中调用 , 需要用到 .global 伪操作将函数声明为全局函数。为了不至于在其他程序在调用某个 C 函数时发生混乱 ,对寄存器的使用我们需要遵循 APCS准则 . 函数编译器将处理为函数代码为一段 .global 的汇编码。

Page 71: 第4章 汇编语言程序设计

71

函数的定义伪操作Ⅱ函数的编写应当遵循如下规则 :a1-a4 寄存器以及浮点寄存器 f0-f3( 如果存在浮点协处理器 )在函数中是不必保存的;如果函数返回一个不大于一个字大小的值,则在函数结束时应该把这个值送到 r0 中; 如果函数返回一个浮点数,则在函数结束时把它放入浮点寄存器 f0 中;如果函数的过程改动了 sp 、 fp 、 sl 、 lr 、 v1-

v6 和 f4-f7,那么函数结束时这些寄存器应当被恢复为包含在进入函数时它所持有的值。

Page 72: 第4章 汇编语言程序设计

72

.align&.end&.include&.incbin

.align: 用来指定数据的对齐方式 ,格式如下 : .align [expression] 数据的对齐方式为 2 的 expression次幂 ,如果 expression 的值是 0 或者没有 , 那么 ARM 编译器将默认采用 expression=2. .end: 表明源文件的结束。 .include: 可以将指定的文件在使用 .include 的地方展开 ,一般是头文件 , 例如 : .include “myarmasm.h” .incbin 伪操作可以将原封不动的一个二进制文件编译到当前文件中 ,使用方法如下 : .incbin "file"[,skip[,count]] skip 表明是从文件开始跳过 skip 个字节开始读取文件 ,count 是读取的字数 .

Page 73: 第4章 汇编语言程序设计

73

if 伪操作Ⅰ 根据一个表达式的值来决定是否要编译下面的代码 ,用 .

endif 伪 操 作来表 示条件判断的 结 束 , 中 间 可 以 使用 .else来决定 .if 的条件不满足的情况下应该编译哪一部分代码 ..if 有多个变种 : .ifdef symbol @判断 symbol 是否定义 .ifc string1,string2 @ 字符串 string1 和 string2 是否相等 ,字 符串可以用单引号括起来 .ifeq expression @判断 expression 的值是否为 0.ifeqs string1,string2 @判断 string1 和 string2 是否相等 ,字符 串必须用双引号括起来

Page 74: 第4章 汇编语言程序设计

74

if 伪操作Ⅱ .ifge expression @判断 expression 的值是否大于等于 0 .ifgt absolute expression @判断 expression 的值是否大于 0 .ifle expression @判断 expression 的值是否小于等于 0 .iflt absolute expression @判断 expression 的值是否小于 0 .ifnc string1,string2 @判断 string1 和 string2 是否不相等 ,其用法跟 .ifc恰好相反。 .ifndef symbol, .ifnotdef symbol @ 判断是否没 有 定 义

symbol, 跟 .ifdef恰好相反 .ifne expression @ 如果 expression 的值不是 0, 那么编译器将编译下面的代码 .ifnes string1,string2 @ 如果字符串 string1 和 string2 不相 等 ,那么编译器将编译下面的代码 .

Page 75: 第4章 汇编语言程序设计

75

.global & .type & .title & .list .global/ .globl :用来定义一个全局的符号,格式如下 : .global symbol 或者 .globl symbol .type :用来指定一个符号的类型时函数类型或者是对象类型 ,对象类型一般是数据 , 格式如下 : .type 符号 ,类型描述 列表控制语句 :• .title :用来指定汇编列表的标题 ,例如 : .title “my program”• .list :用来输出列表文件 .

Page 76: 第4章 汇编语言程序设计

76

ARM 特有的伪操作Ⅰ1. .reg: 用来给寄存器赋予别名 ,格式如下 : 别名 .req 寄存器名 2. .unreq: 用来取消一个寄存器的别名 ,格式如下 :        .unreq 寄存器别名  注意被取消的别名必须事先定义过 , 否则编译器就会报错 ,这个伪操作也可以用来取消系统预制的别名 , 例如 r0, 但如果没有必要的话不推荐那样作 .3 . .code 伪操作用来选择 ARM 或者 Thumb 指令集 ,格式如下 :            .code 表达式  如果表达式的值为 16 则表明下面的指令为 Thumb 指令 ,如果表达式的值为 32 则表明下面的指令为 ARM 指令 .4. .thumb 伪操作等同于 .code 16, 表明使用 Thumb 指令 ,类似的 .arm等同于 .code 32

Page 77: 第4章 汇编语言程序设计

77

ARM 特有的伪操作Ⅱ 5. .force_thumb 伪操作用来强制目标处理器选择 thumb的指令集而不管处理器是否支持 6. .thumb_func 伪操作用来指明一个函数是 thumb 指令集的函数 7. .thumb_set 伪操作的作用类似于 .set, 可以用来给一个标志起一个别名 ,比 .set 功能增加的一点是可以把一个标志标记为 thumb 函数的入口 ,这点功能等同于 .thumb_func 8. .ltorg 用于声明一个数据缓冲池 (literal pool) 的开始 ,它可以分配很大的空间。 9. .pool 的作用等同 .ltorg

Page 78: 第4章 汇编语言程序设计

78

程序示例 @文件 "hello.S"

.section .rodata @只读数据段

.align 2 .strhello:

.ascii "Hello world ! \n\0" .text

.align 2

.global main

.type main,function main:

stmfd sp!, {lr} @保存返回地址ldr r0, .strhellotemp @取字符串的地址作为参数bl printf @调用 printf打印ldmfd sp!, {pc} @恢复返回地址

.strhellotemp:.word .strhello @ 存储字符串 .strhello 的地址

Page 79: 第4章 汇编语言程序设计

79

程序优化 编译器对程序进行一定的优化是非常有必要的,高级语言的程序可以被转化为汇编语言的指令形式。优化技术是全面而非片面的。通常优化的方法有以下几种: 简化算术表达式 废代码的清除 循环优化

Page 80: 第4章 汇编语言程序设计

80

性能测试 对嵌入式系统进行性能测试可以衡量系统的实时性,分析嵌入式软件的优劣,同时分析程序的执行时间可以帮助我们分析 CPU 功耗等特性。 精确的测量程序的执行时间意义不大,我们可以用下面的三个概念分析程序的执行时间: 平均执行时间:多次测量程序的执行时间取平均值。 最坏执行时间:程序的大量输入序列所产生的最长的程序执行时间。有些情况下,产生最坏执行时间的输入数值并不容易确定,需要对程序的代码进行分析产生特殊的测试用例。 最佳执行时间:大量输入序列的测试中记录嵌入式程序运行最短的那一个时间。

Page 81: 第4章 汇编语言程序设计

81

思考题1.WINDOWS 下的 ARM 汇编中注释语句与 Linux as ARM 汇编的注释语句有什么区别 ?2.什么是伪指令和伪操作 ,在 ARM 的汇编程序中有哪几种伪指令 ?3.如何定义寄存器列表 ,试举一个使用寄存器列表的例子 ,要求实现4 个字的内存拷贝。4.如何定义一个宏 ,宏与子程序的区别是什么 ?5.ARM 汇编中如何定义一个段 ,段有哪几种属性 ?6.在一个汇编源文件中如何包含另一个文件中的内容 ?7.分别编写一个函数和一个宏 , 实现字符串的拷贝。8.程序优化有哪几种方法 ? 9.解释归纳变量 , 同族的归纳变量及基本的归纳变量。

Page 82: 第4章 汇编语言程序设计

82

10.试将下面这段 C 代码翻译为等效的汇编代码 :int Factorial(int i) // 计算 i的阶乘{if(i==0){return 1;}return i*Factorial(i-1);}

11. 用汇编语言编写一个函数实现数据块复制的功能 ,以字为单位。12.试用 Linux as 的 ARM 汇编语言的语法实现下面的功能 :从终端输入一个字符 ,如果字符为大写字母 ,则转化为小写字母后输出 ,如果字符为小写字母 ,则转化为大写字母后输出 ,如果字符为1-9 的数字 ,则原样输出 ,如果字符为 0,则程序退出。13.举例说明在高速缓存命中率低时对程序性能的影响。