Presentation is loading. Please wait.

Presentation is loading. Please wait.

第4章 汇编语言程序设计 汇编语言源程序格式 汇编语言上机过程 汇编语言与C语言混合编程技术.

Similar presentations


Presentation on theme: "第4章 汇编语言程序设计 汇编语言源程序格式 汇编语言上机过程 汇编语言与C语言混合编程技术."— Presentation transcript:

1 第4章 汇编语言程序设计 汇编语言源程序格式 汇编语言上机过程 汇编语言与C语言混合编程技术

2 常用ARM源程序文件类型 文件类型 扩展名 汇编语言源文件 .s C语言源文件 .c C++源文件 .cpp 引入文件 .INC 头文件
.h

3 汇编语言程序的结构1

4 汇编语言程序的结构2

5 汇编语言程序的结构3

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

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

8 汇编语言的行构成2 标签 标签是一个符号,可以代表指令的地址、变量、数据的地址和常量。 一般以字母开头,由字母、数字、下划线组成。
当符号代表地址时又称标号,可以以数字开头,其作用范围为当前段或者在下一个ROUT伪操作之前。 指令/伪操作 指令/伪操作是指令的助记符或者定义符,它告诉ARM的处理器应该执行什么样的操作或者告诉汇编程序伪指令语句的伪操作功能。

9 汇编语言的标号1 标号代表地址。 标号分为段内标号和段外标号。段内标号的地址值在汇编时确定,段外编号的地址值在链接时确定 。
在程序段中,标号代表其所在位置与段首地址的偏移量。根据程序计数器(PC)和偏移量计算地址即程序相对寻址。 在映像中定义的标号代表标号到映像首地址的偏移量。映像的首地址通常被赋予一个寄存器,根据该寄存器值与偏移量计算地址即寄存器相对寻址。 例如: loop SUBS r0,r0,#1 ;每次循环使r0=r0-1 BNE loop ;跳转到loop 标号去执行

10 汇编语言的标号2 在宏中也可以使用局部符号。 局部标号是0~99的十进位数开始,可以重复定义。 局部标号引用格式:
%{F|B}{A|T} N{routname} % :局部标号引用操作。 F :编译器只向前搜索。 B :编译器只向后搜索。 A :编译器搜索宏的所有嵌套层次。 T :编译器搜索宏的当前层。 例如: 01 SUBS r0,r0,#1 ;每次循环使r0=r0-1 BNE %B01 ;跳转到01标号去执行

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

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

13 汇编程序的变量代换2 字符串“aaa str1:$str1. l1:$l1,a1:$num1.ccc”中的3个变量将在编译时被替换。
程序运行后看到下面结果: aaa str1:bbb l1:T,a1: Fccc

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

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

16 符号定义伪指令1-1 (1)LCLA、LCLL和LCLS 格式: LCLA/LCLL/LCLS 局部变量名
其中: LCLA定义一个局部的数字变量,初始化为0。 LCLL定义一个局部的逻辑变量,初始化为F。 LCLS定义一个局部的字符串变量,初始化为空串。 这3条伪指令用于声明局部变量,在其局部作用范围内变量名必须惟一,例如在宏内。

17 符号定义伪指令1-2 例如: MACRO TEST LCLA num1 ;定义一个局部的数字变量,变量名为 ; num1
LCLL l2 ;定义一个局部的逻辑变量,变量名为l2 LCLS str3 ;定义一个局部的字符串变量,变量名 ;为str3 num1 SETA 0xabcd ;将该变量赋值为0xabcd l2 SETL {FALSE} ;将该变量赋值为真 str3 SETS "Hello!" ;将该变量赋值为“Hello!” MEND

18 符号定义伪指令2-1 (2)GBLA、GBLL和GBLS 格式: GBLA/GBLL/GBLS 变量名
其中: GBLA定义一个全局数字变量,并初始化为0。 GBLL定义一个全局逻辑变量,并初始化为F。 GBLS定义一个全局字符串变量,并初始化为空串。 这3条伪指令用于定义全局变量,因此在整个程序范围内变量名必须惟一。

19 符号定义伪指令2-2 例如: GBLA num1 ;定义一个全局的数字变 ;量,变量名为num1
num1 SETA 0xabcd;将该变量赋值为0xabcd GBLL l2 ;定义一个全局的逻辑变 ;量,变量名为l2 l2 SETL {FALSE} ;将该变量赋值为假 GBLS str3 ;定义一个全局的字符串变 ;量,变量名为str3 str3 SETS "Hello!" ;将该变量赋值为“Hello!”

20 符号定义伪指令3-1 (3)SETA、SETL和SETS 格式: 变量名 SETA/SETL/SETS 表达式 说明:
格式中的变量名必须为已经定义过的全局或局部变量,表达式为将要赋给变量的值。

21 符号定义伪指令3-2 例如: LCLA num1 ;定义一个局部的数字 ;变量,变量名为num1
num1 SETA 0x1234 ;将该变量赋值 ;为0x1234 LCLS str3 ;定义一个局部的字符串变 ;量,变量名为str3 str3 SETS “Hello!” ;将该变量赋值为 ;“Hello!”

22 符号定义伪指令4 (4)RLIST 格式: 名称 RLIST {寄存器列表}
说明:RLIST可用于对一个通用寄存器列表定义名称,该名称可在ARM指令LDM/ STM中使用。在LDM/STM指令中,列表中的寄存器为根据寄存器的编号由低到高访问次序,与列表中的寄存器排列次序无关。 例如: pblock RLIST {R0-R3,R7,R5,R9} ;将寄存器列表名称定义为pblock,可在ARM指令 ;LDM/STM中通过该名称访问寄存器列表

23 数据定义伪指令 作用:为数据分配存储单元,同时初始化。 有如下几种: DCB 字节分配 DCW/DCWU 半字(2字节)分配
DCD/DCDU 字(4字节)分配 DCQ/DCQU 8个字节分配 DCFS/DCFSU 单精度浮点数分配 DCFD/DCFDU 双精度浮点数分配 SPACE 分配一块连续的存储单元 FIELD 定义一个结构化的内存表的数据域 MAP 定义一个结构化的内存表首地址

24 数据定义伪指令1 (1)DCB 格式: 标号 DCB 表达式 说明:分配一块字节单元并用伪指令中指定的表达式进行初始化。
表达式可以为使用双引号的字符串或0~255的数字。 DCB可用“=”代替。 例如: Array1 DCB 1,2,3,4,5 ;数组 str1 DCB "Your are welcome!" ;构造字符串并分配空间

25 数据定义伪指令2 (2)DCW/DCWU 格式: 标号 DCW/DCWU 表达式
例如: Arrayw1 DCW 0xa,-0xb,0xc,-0xd ;构造固定数组并分配半字存储单元

26 数据定义伪指令3 (3)DCD/DCDU 格式: 标号 DCD/DCDU 表达式
例如: Arrayd1 DCD 1334,234,345435 ;构造固定数组并分配字为单元的存储单元 Label DCD str1;该字单元存放str1的地址

27 数据定义伪指令4 (4)DCQ/DCQU 格式: 标号 DCQ/DCQU 表达式
例如: Arrayd1 DCQ , ;构造固定数组并分配字为单元的存储空间。 ;注意:DCQ不能给字符串分配空间

28 数据定义伪指令5 (5)DCFD/DCFDU 格式: 标号 DCFD/DCFDU 表达式
每个双精度的浮点数占据两个字单元。 DCFDU功能与DCFD类似,只是分配的存储单元不严格字对齐。 例如: Arrayf1 DCFD E2 Arrayf2 DCFD ,1.45

29 数据定义伪指令6 (6)DCFS/DCFSU 格式: 标号 DCFS/DCFSU 表达式
每个单精度浮点数使用一个字单元。 DCFSU功能与DCFS类似,只是分配的存储单元不严格字对齐。 例如: Arrayf1 DCFS 6E2 ,-9E-2,-.3 Arrayf2 DCFSU 1.23,6.8E9

30 数据定义伪指令7 (7)SPACE 格式: 标号 SPACE 表达式
例如: freespace SPACE 1000 ;分配1000字节的存储空间

31 数据定义伪指令8 (8)MAP 格式: MAP 表达式 [,基址寄存器] 说明:MAP定义一个结构化的内存表的首地址。
表达式可以为程序中的标号或数学表达式,基址寄存器为可选项,当基址寄存器选项不存在时,表达式的值即为内存表的首地址,当该选项存在时,内存表的首地址为表达式的值与基址寄存器的和。 MAP可以与FIELD伪操作配合使用来定义结构化的内存表。 例如: MAP x130,R2 ;内存表首地址为0x130+R2

32 数据定义伪指令9 (9)FIELD 格式: 标号 FIELD 字节数 说明:FIELD用于定义一个结构化内存表中的数据域。
FIELD常与MAP配合使用来定义结构化的内存表:FIELD伪指令定义内存表中的各个数据域,MAP则定义内存表的首地址,并为每个数据域指定一个标号以供其他的指令引用。 需要注意的是MAP和FIELD伪指令仅用于定义数据结构,并不分配存储单元。 例如: MAP 0xF10000 ;定义结构化内存表首地址为0xF10000 count FIELD 4 ;定义count的长度为4字节,位置为0xF1000+0 x FIELD 4 ;定义x的长度为4字节,位置为0xF1004 y FIELD 4 ;定义y的长度为4字节,位置为0xF1008

33 汇编控制伪指令 作用:指引汇编程序的执行流程。 常用的伪操作包括: (1)MACRO和MEND:宏定义的开始与结束。
(2)IF、ELSE和ENDIF:根据逻辑表达式的成立与否决定是否在编译时加入某个指令序列。 (3)WHILE和WEND:根据逻辑表达式的成立与否决定是否循环执行这个代码段。 (4)MEXIT:从宏中退出。

34 其他伪指令 在汇编程序中经常会使用一些其他的伪指令,包括以下18条: ASSERT AREA ALIGN CODE16/CODE32
ENTRY END EQU IMPORT EXPORT/GLOBAL EXTERN INCBIN GET/INCLUDE RN ROUT ADR ADRL LDR NOP

35 其他伪指令1 (1)ASSERT 格式: ASSERT 逻辑表达式
例如: ASSERT ver>7 ;保证ver>7

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

37 其他伪指令3 (3)ALIGN 格式: ALIGN [表达式[,偏移量]]
表达式的值为2的幂,如1、2、4、8、16等,用于指定对齐方式。 如果伪操作中没有指定表达式,则编译器会将当前位置对齐到下一个字的位置。偏移量也是个数字表达式,如果存在偏移量,则当前位置自动对齐到2的表达式值次方+偏移量。 例如: AREA ||.data||,DATA,READWRITE,ALIGN=2

38 其他伪指令4 (4)CODE16/CODE32 格式: CODE16/CODE32
说明:CODE16伪操作指示编译器后面的代码为16位的Thumb指令。CODE32伪操作指示编译器后面的代码为32位的ARM指令。 如果在汇编源代码中同时包含Thumb和ARM指令时,可以用“CODE32”通知编译器后的指令序列为32位的ARM指令,用“CODE16”伪指令通知编译器后的指令序列为16位的Thumb指令。 CODE16/CODE32不能对处理器进行状态的切换。 例如: CODE32 ; 32位的ARM指令 AREA ||.text||,CODE,READONLY LDR R0,=0x8500; BX R0 ;程序跳转,并将处理器切换到Thumb状态 CODE16 ;16位的Thumb指令 ADD R3,R3,1 END ;源文件结束

39 其他伪指令5-1 (5)ENTRY 格式: ENTRY 说明:ENTRY用于指定汇编程序的入口。

40 其他伪指令5-2 下面的代码使用了ENTRY:
AREA subrout, CODE, READONLY ; name this block of code mar k ENTRY ; first instruction to execute start MOV r0, # ; Set up parameters MOV r1, #3 BL doadd ; Call subroutine stop MOV r0, #0x ; angel_SWIreason_ReportException LDR r1, =0x ; ADP_Stopped_ApplicationExit SWI 0x ; ARM semihosting SWI doadd ADD r0, r0, r ; Subroutine code MOV pc, lr ; Return from subroutine END ; Mark end of file

41 其他伪指令6 (6)END 格式: END 说明:END告诉编译器已经到了源程序的结尾。 例如:
AREA constdata,DATA,READONLY END ;结尾

42 其他伪指令7 (7)EQU 格式: 名称 EQU 表达式 [,类型]
说明:EQU用于将程序中的数字常量、标号、基于寄存器的值赋予一个等效的名称,这一点类似于C语言中的#define. 可用“*”代替EQU。 如果表达式为32位的常量,我们可以指定表达式的数据类型,类型域可以有以下3种:CODE16/CODE32/DATA 例如: num1 EQU 1234 ;定义num1为1234 addr5 EQU str1+0x50 d1 EQU 0x2400,CODE32 ;定义d1的为0x2400,且该处为32位的ARM指令

43 其他伪指令8 (8)EXPORT/GLOBAL 格式: EXPORT/GLOBAL 标号 [,WEAK]
例如: AREA ||.text||,CODE,READONLY main PROC ENDP EXPORT main ;声明一个可全局引用的函数main END

44 其他伪指令9 (9)IMPORT 格式: IMPORT 标号 [,WEAK]
说明:告诉编译器,这个标号要在当前源文件中使用,但标号是在其他的源文件中定义的。 [,WEAK]:如果所有的源文件都没有找到这个标号的定义,编译器也不会提示错误信息。 例如: AREA mycode,CODE,READONLY IMPORT _ printf ;通知编译器当前文件要引用函数_ printf END

45 其他伪指令10 (10)EXTERN 格式: EXTERN 标号 [,WEAK]
说明:告诉编译器,标号要在当前源文件中引用,但是该标号是在其他的源文件中定义的。 与IMPORT不同的是,如果当前源文件实际上没有引用该标号,该标号就不会被加入到当前文件的符号表中。 [,WEAK]:即使所有的源文件都没有找到这个标号的定义,编译器也不给出错误信息。 例如: AREA ||.text||,CODE,READONLY EXTERN _ printf,WEAK ;告诉编译器当前文件要引用标号,如果找不到,则不提示错误 END

46 其他伪指令11 (11)GET/INCLUDE 格式: GET 文件名
使用方法:在某源文件中定义一些宏指令,用MAP和FIELD定义结构化的数据类型,用EQU定义常量的符号名称,然后用GET/INCLUDE将这个源文件包含到其他的源文件中。 使用方法与C语言中的“#include”相似。 GET/INCLUDE只能用于包含源文件,包含其他文件则需要使用INCBIN伪指令。 例如: AREA mycode,DATA,READONLY GET E:\code\prog1.s ;通知编译器在当前源文件包含源文件E:\code\ prog1.s GET prog2.s ;通知编译器当前源文件包含可搜索目录下的prog2.s END

47 其他伪指令12 (12)INCBIN 格式: INCBIN 文件名
例如: AREA constdata,DATA,READONLY INCBIN data1.dat ;源文件包含文件data1.dat INCBIN E:\DATA\data2.bin ;源文件包含文件E:\DATA\data2.bin END

48 其他伪指令13 (13)RN 格式: 名称 RN 表达式 说明:RN用于给一个寄存器定义一个别名,以便程序员记忆该寄存器的功能。
名称为给寄存器定义的别名,表达式为寄存器的编码。 例如: count RN R1 ;给R1定义一个别名count

49 其他伪指令14 (14)ROUT 格式: [名称] ROUT 说明:ROUT可以给一个局部变量定义作用范围。
在程序中未使用该伪指令时,局部变量的作用范围为所在的AREA,而使用ROUT后,局部变量的作用范围为当前ROUT和下一个ROUT之间。

50 其他伪指令15 (15)ADR 小范围地址读取 格式:
ADR{<cond>} <Rd>,< expr>; 说明:将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值(expr 地址表达式)读取到目标寄存器Rd中。 当地址值是非字对齐时,取值范围在-255~255字节之间; 当地址值是字对齐时,取值范围在-1 020~1 020字节之间。 在汇编编译源程序时,ADR伪指令被编译器替换成一条合适的指令。 通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能。若不能用一条指令实现,则产生错误,编译失败。 对于基于PC相对偏移的地址值时,给定范围是相对当前指令地址后两个字处(因为ARM7TDMI为三级流水线)。 可以用ADR加载地址实现查表。 例如: LOOP MOV R1,#0xF0 ADR R2,LOOP ;将LOOP的地址放入R2 ADR R3,LOOP + 4

51 其他伪指令16 (16)ADRL 中等范围地址读取 格式:
ADRL{<cond>} <Rd>,< expr>; 说明:类似于ADR, 但比ADR读取更大范围的地址。 当地址值是非字对齐时,取值范围在-64KB~64 KB之间; 地址值是字对齐时,取值范围在-256KB~256 KB之间。 在汇编编译源程序时,ADRL伪指令被编译器替换成两条合适的指令。 若不能用两条指令实现ADRL伪指令功能,则产生错误,编译失败。 可以用ADRL加载地址,实现程序跳转。 例如: ADRL R0,DATA_BUF ADRL R1,DATA_BUF+80 DATA_BUF SPACE ;定义100字节缓冲区

52 其他伪指令17 (17)LDR 大范围地址读取 格式:
LDR{<cond>} <Rd>,< =expr/label-expr >; 说明:加载32位的立即数或一个地址值到目标寄存器Rd。 在汇编编译源程序时,LDR伪指令被编译器替换成一条合适的指令。 若加载的常数未超出MOV或MVN的范围,则使用MOV或MVN指令代替该LDR伪指令;否则汇编器将常量放入文字池,并使用一条程序相对偏移的LDR指令从文字池读出常量。 LDR用于加载芯片外围功能部件的寄存器地址(32位立即数),以实现各种控制操作。从PC到文字池的偏移量必须小于4 KB。 与ARM指令的LDR相比,伪指令的LDR的参数有“=”符号。 例如: LDR R0,=0x ;加载32位立即数0x LDR R0,=DATA_BUF+60 ;加载DATA_BUF地址+60 LTORG ;声明文字池

53 其他伪指令18 (18)NOP 空操作 格式: NOP ; 说明:不产生任何有意义的操作,只是占用一个机器时间。
NOP伪指令在汇编时将会被替代成ARM中的空操作,比如可能为“MOV R0,R0”指令等。

54 汇编语言上机过程 用ARM汇编语言编写的源程序,要使之运行必须经过以下几个步骤: 1.编辑汇编源程序,保存为文件名后缀是“.s”的文件;
2.调用汇编程序对源程序进行汇编,生成目标文件; 3.连接目标文件,生成可以放进ARM软件仿真器进行调试的映象文件或者可下载到ARM的目标板执行的二进制文件; 4.对生成的最终文件进行调试。

55 ARM的开发工具ADS1.2 ADS1.2是ARM公司推出的一套ARM汇编、C、C++的集成开发环境。 包含了几个有用的开发工具,包括:
(1)CodeWarrior IDE for the ARM Developer Suite :为ARM的程序员管理、开发软件工程项目提供了一个简单直观、灵活的用户界面。 (2)AXD Debuger:AXD是一个功能强大、使用方便的调试器。

56 编辑汇编语言源程序 可以使用简单的Windows自带的记事本程序来编辑ARM的汇编程序: 单击开始菜单程序附件记事本
也可以使用CodeWarrior IDE来编辑汇编程序: 单击File菜单的New菜单项 单击Project标签页 单击工具栏的New Text按钮 敲入汇编代码保存为hello.s文件 单击Project菜单,选择“Add *.s to project”

57 编译汇编语言源程序 ARM的编译器有如下几种: (1)armcc:ARM C编译器,具有优化功能,兼容ANSI C
(2)tcc:Thumb的C编译器,同样具有优化功能兼容ANSI C (3)armcpp:ARM C++编译器,遵循ANSI C++或者EC++标准 (4)tcpp:Thubm的C++编译器,遵循ANSI C++或者EC++标准 (5)armasm:支持ARM和Thumb的汇编器 这些编译器输出的是ELF格式的目标文件,可以包括RAWF2格式的调试信息。同时通过特殊的控制选项可以输出汇编语言文件或者列表文件。

58 连接装配汇编程序 使用armlink程序对ARM的汇编源程序进行连接,它也可以将多个.o目标文件连接生成最终的可执行文件。 术语 :
映像文件(image):是指一个可执行文件,在执行的时候被加载到处理器中。一个映像文件可以有多个线程。它是ELF(Executable and linking format)格式的。 段(section):描述映像文件的代码或数据块。 域(Regions):在一个映像文件中,一个域包含了1~3个输出段。多个域组织在一起,就构成了最终的映像文件。 加载时地址:映像文件载入存储器时的地址。 运行时地址:映像文件运行时的地址。

59 汇编程序的运行 两种运行方法: (1)生成的*.axf文件是ARM 的ELF 格式的可执行映像文件;这个文件可以载入AXD 进行仿真调试。使用armsd在终端模拟它在ARM目标机上的运行。 (2)也可以直接在CodeWarrior IDE中运行映像文件:在Project窗口中单击Run按钮,可以执行AXD并在Console窗口中看到运行结果。 在AXD上调试成功之后,我们可以通过fromelf 工具将ELF 文件转换为二进制格式文件下载到目标板执行。

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

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

62 汇编程序中访问C程序变量1 在汇编的源程序中调用C语言风格的字符串需要使用IMPORT伪操作。
IMPORT相当于C语言中的extern关键字,告诉编译器引用的符号不是在本文件中定义的,而是在其他的源文件中定义的。 伪操作的格式 : IMPORT symbol [,WEAK] symbol是声明的符号的名称; [,WEAK]指示编译器如果发现symbol在所有的源文件中都没有找到,那么它也不会产生任何的错误信息。 示例见下页。

63 汇编程序中访问C程序变量2 将str.c和hello.s导入CodeWarrior IDE编译运行,即可看到下面的运行结果:
char *strhello="Hello world!\n\0"; 汇编代码文件hello.s AREA ||.text||, CODE, READONLY 2 main PROC STMFD sp!,{lr} LDR r0,=strtemp LDR r0,[r0] BL _printf LDMFD sp!,{pc} 8 strtemp 9 DCD strhello ENDP EXPORT main IMPORT strhello IMPORT _main IMPORT _main IMPORT _printf IMPORT ||Lib$$Request$$armlib||, WEAK 17 END 将str.c和hello.s导入CodeWarrior IDE编译运行,即可看到下面的运行结果: Hello world!

64 C程序中内嵌汇编指令1 在ARM的C语言程序中可以使用关键字__asm来加入一段汇编语言的程序。 格式: __asm {
instruction/*comment*/ }

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

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

67 C程序调用汇编程序1 为了满足ARM汇编、C与C++之间的互相调用,必须保证编写的代码遵循APCS(ARM过程调用标准)。
注意:如果函数有4个参数,则将分别用r0 、r1、r2和r3来传递,如果参数多于4个,则多余的参数将被压入堆栈。 程序实例见下页。

68 C程序调用汇编程序2 文件strtest.c 1 #include <stdio.h>
2 extern void strcopy(char *d, const char *s); 3 int main() 4 { const char *srcstr = "First string - source"; char dststr[] = "Second string - destination"; /* dststr is an array since we're going to change it */ 7 printf("Before copying:\n"); printf(" '%s'\n '%s'\n",srcstr,dststr); strcopy(dststr,srcstr); printf("After copying:\n"); printf(" '%s'\n '%s'\n",srcstr,dststr); return 0; 14 }

69 C程序调用汇编程序3 文件scopy.s 1 AREA SCopy, CODE, READONLY 2 EXPORT strcopy
; 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, # ; check for zero terminator BNE strcopy ; keep going if not MOV pc,lr ; Return END

70 复习题 1.什么是伪指令和伪操作?在ARM的汇编程序中有哪几种伪指令?
2.如何定义寄存器列表,试举一个使用寄存器列表的例子,要求实现4个字的内存复制。 3.如何定义一个宏,宏与子程序的区别是什么? 4.ARM汇编中如何定义一个段,段有哪几种属性? 5.在一个汇编源文件中如何包含另一个文件中的内容? 6.分别编写一个函数和一个宏,实现字符串的复制。 7.用汇编语言编写一个函数实现数据块复制的功能,以字为单位。

71 请同学们认真复习 本章结束


Download ppt "第4章 汇编语言程序设计 汇编语言源程序格式 汇编语言上机过程 汇编语言与C语言混合编程技术."

Similar presentations


Ads by Google