74
第第第 第第 ARM 第第第第第第第第

第六章 基于 ARM 的嵌入式程序设计

  • Upload
    gage

  • View
    147

  • Download
    4

Embed Size (px)

DESCRIPTION

第六章 基于 ARM 的嵌入式程序设计. 6.1 ARM 汇编语言的伪操作、宏指令与伪指令. 6.1.1 两种常见的 ARM 编译开发环境 6.1.2 ADS 编译环境下的伪操作和宏指令 6.1.3 GNU 编译环境下的伪操作和宏指令 6.1.4 ARM 汇编语言的伪指令. ARM 汇编语言的伪操作、宏指令与伪指令. 伪操作 —— 为汇编程序所用,在源程序进行汇编时由汇编程序处理,只在汇编过程起作用,不参与程序运行。 宏指令 —— 通过伪操作定义的一段独立的代码。在调用它时将宏体插入到源程序中。 - PowerPoint PPT Presentation

Citation preview

第六章

基于 ARM 的嵌入式程序设计

6.1 ARM汇编语言的伪操作、宏指令与伪指令

6.1.1 两种常见的 ARM 编译开发环境 6.1.2 ADS 编译环境下的伪操作和宏指令 6.1.3 GNU 编译环境下的伪操作和宏指令 6.1.4 ARM 汇编语言的伪指令

ARM汇编语言的伪操作、宏指令与伪指令

伪操作——为汇编程序所用,在源程序进行汇编时由汇编程序处理,只在汇编过程起作用,不参与程序运行。

宏指令——通过伪操作定义的一段独立的代码。在调用它时将宏体插入到源程序中。

伪指令——是汇编语言程序里的特殊指令助记符,在汇编时被合适的机器指令替代。

6.1.1 两种常见的 ARM 编译开发环境ADS/SDT IDE 开发环境:它由 ARM 公司开发,使用了 CodeWarrior 公司的编译器; EmbestIDE 开发环境 : 集成了 GNU 开发工具的 IDE 开发环境 , 由 GNU 的汇编器 as 、交叉编译器 gcc 、和链接器 ld 等组成。 这两种开发环境的编译器都完全支持 ARM指令集,但伪指令不同,代码风格不同。

6.1.2ADS 编译环境下的伪操作和宏指令 ADS 编译环境下的伪操作可分为以

下几类:符号定义( Symbol Definition )伪操作 数据定义( Data Definition )伪操作 汇编控制( Assembly Control )伪操作 信息报告( Reporting )伪操作 其他( Miscellaneous )伪操作

符号定义伪操作

GBLA , GBLL , GBLS :声明全局变量。LCLA , LCLL , LCLS :声明局部变量。SETA , SETL , SETS :给变量赋值。RLIST :为通用寄存器列表定义名称。

1 、 GBLA , GBLL , GBLS 用于声明一个 ARM 程序中的全局变量,并将

其初始化。 GBLA :声明一个全局算术变量,并初始化成 0 。 GBLL :声明一个全局逻辑变量,并初始化成 {F

ALSE} 。 GBLS :声明一个全局串变量,并初始化成空串

“”。 语法格式: <gblx> variable全局变量的作用范围为:包含该变量的源程序,全局

变量名称在作用范围内必须唯一。

2 、 LCLA , LCLL , LCLS 用于声明一个 ARM 程序中的局部变量,并将

其初始化。 LCLA :声明一个局部算术变量,并初始化成 0 。 LCLL :声明一个局部逻辑变量,并初始化成 {F

ALSE} 。 LCLS :声明一个局部串变量,并初始化成空串

“”。 语法格式: <lclx> variable局部变量的作用范围为:包含该局部变量的宏代码的一个实例

3 、 SETA , SETL , SETS 用于给 ARM 程序中的变量赋值。

SETA :给一个算术变量赋值。 SETL :给一个逻辑变量赋值。 SETS :给一个串变量赋值。

语法格式: variable <setx> expression

expression :赋给变量的值。

在向变量赋值前,必须先声明该变量。

例: GBLS s1 GBLS s2 LCLS s3 S1 SETS “strings1” S2 SETS “strings2” S3 SETS s1:CC:S2 GBLL logic logic SETL {TRUE} LCLA arithmetic arithmetic SETA 0xff

; 声明一个全局串变量 s1

; 声明一个全局串变量 s2

; 声明一个局部串变量 s3

; 串变量 s1 赋值为“ strings1”; 串变量 s2 赋值为” strings2”; 串变量 s3 赋值

; 声明一个全局逻辑变量 logic

; 声明一个局部算术变量

; 变量 logic 赋初值为 {TRUE}

; 变量 arithmetic 赋初值为 oxff

4 、 RLIST 为一个通用寄存器列表定义名称。定义的名称

可以在 LDM/STM 指令中使用。 语法格式: name RLIST {list-of-registers}

name :寄存器列表名称。 {list-of-registers} :通用寄存器列表。例:context RLIST {R0-R6} ; 将寄存器列表名称定义为 context

LDMDF SP! ,context ; 加载寄存器列表

数据定义伪操作

LTORG :声明一个数据缓冲池的开始。 SPACE :分配一块内存单元,并用 0 初始化。 MAP :定义一个结构化的内存表的首地址。 FIELD :定义结构化内存表中的一个数据域。 DCB :分配一段字节内存单元,并初始化。 DCD 、 DCDU :分配一段字内存单元,并初始

化。

1 、 LTORG 用于声明一个数据缓冲池(文字池)的开始。 语法格式: LTORG 例: start BL func …… func LDR R1,=0x8000 ; 子程序 …… MOV PC,LR ; 子程序返回 LTORG ; 定义数据缓冲池 &0x8000Data SPACE 4200 ; 从当前位置开始分配 4200 字节的

内 存单元,并初始化为 0 。 END 默认数据缓冲池为空

注意: LTORG 伪操作通常放在无条件跳转指令之后,

或者子程序返回指令之后,这样处理器不会错误地将数据缓冲池中的数据当作指令来执行。

通常 ARM 汇编编译器把数据缓冲池放在代码段的最后面,即下一个代码段开始之前,或者 END 伪操作之前。

2 、 SPACE—— 也可以用符号“ %” 表示 用于分配一块连续的内存单元供程序使用,并且将

这个内存单元的内容初始化为 0 。 语法格式: {label} SPACE expr 或 {label} % expr 其中:

{label} :是一个标号,可选项。 expr :数值表达式,用来规定内存单元的字节数。

例: AREA DataSpace,Data,READWRITE data1 SPACE 256 ; 定义大小为 256 字节的数据区

3 、 MAP 、 FIELD 使用 MAP 和 FIELD 伪操作描述数据结构。 MAP 用于定义数据结构的起始地址; FIELD 用于定义结构中的字段(各数据域的

字节长度),并可为每一个数据域指定一个标号,其它指令可引用此标号。

注: MAP 和 FIELD 伪操作只是定义数据结构并

不实际分配内存单元。

( 1 ) MAP —— 也可以用符号” ^” 表示 语法格式: MAP expr {,base-register} 或 ^ expr {,base-register} 其中:

expr :数据表达式或程序中的标号(使用 MAP之

前已定义)。 base-register :基址寄存器

若指令中没有此项时, expr 为结构的首地址。(基于绝对地址的数据结构)

若指令中包含此项时, expr+base-register 的和为结构首地址。(基于相对地址的数据结构)

例: MAP 0xC0008 ; 数据结构存放的起始地址是 0xC0008

^ datalabel+64 ; 数据结构存放的起始地址是 datalabel+64 ; ( datalabel已定义过) MAP 0x10,R9 ; 数据结构存放的起始地址是 R9 中存放的数值加上

0x10

( 2 ) FIELD —— 也可以用符号” #” 表示

用户定义数据结构中的字段。 语法格式: {label} FIELD expr

或 {label} # expr 其中:

{label} :字段的名称(地址标号)。 expr : 此字段在数据结构中所占的字节数。

由 MAP 和 FIELD 伪操作配合定义的数据结构有 3 种: 基于绝对地址的数据结构 基于寄存器相对偏移的数据结构 基于程序相对偏移的数据结构(基于 PC )。

例 1 :基于绝对地址—首地址固定MAP 0x200 ; 内存的首地址为 0x200 a FIELD 4 ; 字段 a长度为 4 ,相对位置为 0b FIELD 4 ; 字段 b长度为 4 ,相对位置为 4x FIELD 8 ; 字段 x长度为 8 ,相对位置为 8y FIELD 8 ; 字段 y长度为 8 ,相对位置为 16s FIELD 16 ; 字段 s长度为 16 ,相对位置为 2

4引用结构中的数据: LDR R0 , b

例 2 :基于寄存器相对偏移—首地址基于基址寄存器 MAP 0,R9 ; 内存的首地址为 R9 寄存器的值 a FIELD 4 ; 字段 a长度为 4 ,相对位置为 0

b FIELD 4 ; 字段 b长度为 4 ,相对位置为 4

x FIELD 8 ; 字段 x长度为 8 ,相对位置为 8

y FIELD 8 ; 字段 y长度为 8 ,相对位置为 16

s FIELD 16 ; 字段 s长度为 16 ,相对位置为 24 可通过下面的指令访问数据结构中的信息: ADR R9 , datastart ; datastart 是数据起始地址 LDR R0 , b ;LDR R0,[R9,#4]

通过在 ADR 指令中指定不同的基址寄存器的值,定义的数据结构可在程序中有多个实例。

例 3 :基于程序相对偏移—首地址基于 PCDatastruc SPACE 280 ; 分配 280 字节的内存单元MAP Datastruc ; 内存的首地址为 Datastruc 内存单

元a FIELD 4 ; 字段 a长度为 4 ,相对位置为 0

b FIELD 4 ; 字段 b长度为 4 ,相对位置为 4

x FIELD 8 ; 字段 x长度为 8 ,相对位置为 8

y FIELD 8 ; 字段 y长度为 8 ,相对位置为 16

s FIELD 256 ; 字段 s长度为 16 ,相对位置为 24

可通过下面的 指令访问数据结构中的信息: LDR R0 , b ;LDR R0, Datastruc+4

例:判断当前内存的使用情况是否超过程序分配的可用内存的方法。

startofmem EQU 1000 ; 分配内存首地址endofmem EQU 2000 ; 分配内存末地址MAP startofmem ; 内部首地址为 startofmem

a FIELD 4 ; 字段 a长度为 4 ,相对位置为 0

b FIELD 4 ; 字段 b长度为 4 ,相对位置为 4

x FIELD 8 ; 字段 x长度为 8 ,相对位置为 8

y FIELD 8 ; 字段 y长度为 8 ,相对位置为 16

s FIELD max ; 字段 s长度为 max ,相对位置为24endofstru FIELD 0 ;endofstru 用于检查内存是否越界

ASSERT endofstru<=endofmem

4 、 DCB—— 也可以用符号” =” 表示 用于定义并且初始化一个或者多个字节的内

存区域。 语法格式: {label} DCB expr{,expr}……

或 {label} = expr{,expr} 其中:

expr 表示:-128 到 255 之间的一个数值常量或者表达式。一个字符串。

当 DCB 后面紧跟一个指令时,可能需要使用 ALIGN确保指令是字对齐的。

例: short DCB 1 ; 为 short 分配了一个字节 , 并

初始 化为 1 。 string DCB “string”,0 ; 构造一个以 0 结尾的

字 符串

5 、 DCD 、 DCDU ( 1 ) DCD —— 也可以用符号” &” 表示 用于分配一段字内存单元(分配的内存单元

都是字对齐的)并初始化。 语法格式: {label} DCD expr{,expr}……

或 {label} & expr{,expr}…… 其中:

expr :数字表达式或程序中的标号。DCD 伪操作可能在分配的第一个内存单元前插入填补字节以保证分配的内存是字对齐的。

例: data1 DCD 2,4,6 ; 为 data1 分配三个字 ,

内容初始化为 2,4,6 data2 DCD label+4 ; 初始化 data2 为 label

+4对应的地址 ( 2 ) DCDU DCDU 与 DCD 的不同之处在于 DCDU 分配

的内存单元并不严格字对齐。

汇编控制伪操作 IF,ELSE及 ENDIF WHILE及WEND MACRO,MEND及MEXIT

1 、 IF,ELSE及 ENDIF IF,ELSE及 ENDIF 伪操作能够根据条件把一段源代

码包括在汇编语言程序内或者将其排除在程序之外。 “[“ 是 IF 伪操作的同义词 “|“ 是 ELSE 伪操作的同义词 “]” 是 ENDIF 伪操作的同义词

语法格式: IF logic expression; logic expression 控制选择逻辑表达式 …… ; 指令或伪指令代码段 1 {ELSE ; 可选 …… ; 指令或伪指令代码段 2 } ENDIF

IF 、 ELSE及 ENDIF 伪操作可以嵌套使用

例: IF Variable=16 ;如果 Variable=16 成立 ,则编译下面

代码 BNE SUB1 LDR R0,=SUB0 BX R0 ELSE ;否则编译下面代码 BNE SUB0 LDR R0,=SUB0 BX R1 ENDIF

2 、 WHILE及WEND WHILE及WEND 伪操作能够根据条件重复

汇编相同的一段源代码。 语法格式: WHILE logic expression

instructions or derectives

WEND

WHILE 和 WEND 伪操作可以嵌套使用

例:count SETA 1 ; 设置循环计数变量 count 初始值为

1

WHILE count<=4 ; 由 count 控制循环执行的次数

count SETA count+1 ; 将循环计数变量加 1

…… ; 代码WEND ; 结束

3 、 MACRO 、 MEND及MEXIT MACRO :标识宏定义的开始。 MEND :标识宏定义的结束。

MACRO 和 MEND 之间的代码称为宏定义体。 MEXIT :用于从宏中跳转出去。 宏体代表了一个预定义的汇编语言指令序列。

在汇编程序中通过宏的名称调用宏,编译时,宏语句被展开。

语法格式: MACRO

{$label} macroname {$parameter{, $parameter}..}

;code

……

;code

MEND

其中: {$label} :在宏被展开时 ,label 可被替换成相应的符号。 macroname :多定义的宏的名称。 $parameter :宏指令的参数。

例:(示例 26 ) MACRO

$label jump $a1,$a2

……

$label.loop1

……

BGE $label.loop1

$label.loop2

BL $a1

BGT $label.loop2

……

ADR $a2

MEND

在程序中调用此宏:

exam jump sub,det

……

examloop1……

BGE examloop1examloop2

BL sub

BGT examloop2……

ADR det

例: MACRO $abc macroabc $param1,$param2 ;code IF condition1 ;code MEXIT ; 从宏中跳出 ELSE ;code ENDIF ;code MEND

子程序比较短,而需要传递的参数比较多是可以使用宏汇编技术。

其他伪操作 1 、 CODE16 和 CODE32 CODE16 告诉汇编编译器后面的指令序列为

16 位的 Thumb 指令。 CODE32 告诉汇编编译器后面的指令序列为

32 位的 ARM 指令。 语法格式: CODE16

CODE32CODE16 和 CODE32 只是告诉编译器后面指令的类型,该伪操作本身不进行程序状态的切换。

例: AREA ChangeState, CODE, READONLY

ENTRY

CODE32 ; 下面为 32 位 ARM 指令 LDR R0,=start+1

BX R0

……

CODE16 ; 下面为 16 位 Thumb 指令start MOV R1,#10

…….

END

;切换到 Thumb状态,并跳转到 start 处执行

2 、 EQU —— 也可以用符号” *” 表示 EQU 伪操作为数字常量、基于寄存器的值和程序中

的标号定义一个字符名称。 语法格式: name EQU expr{ , type} 其中:

expr :基于寄存器的地址值、程序中的标号、 32 位的地址常量或者 32 位的常量。

name :为 expr 定义的字符名称。 type :当 expr 为 32 位常量时,可以使用 type 指示 expr

的数据的类型。取值为: CODE32 CODE16 DATA

例: abcd EQU 2 ; 定义 abcd 符号的值为 2

abcd EQU label+16 ; 定义 abcd 符号的值为 (label+16)

abcd EQU 0x1c,CODE32 ; 定义 abcd 符号的值为绝对地址 ; 值 0x1c ,而且此处为 ARM 指令

3 、 AREA 用于定义一个代码段或是数据段。 语法格式: AREA sectionname{,attr} {,attr}… 其中:

sectionname :为所定义的段的名称。 attr :该段的属性。具有的属性为:

CODE: 定义代码段。DATA: 定义数据段。READONLY: 指定本段为只读 , 代码段的默认属性。READWRITE: 指定本段为可读可写 , 数据段的默认属性。

ALIGN=expression: 指定段的对齐方式为 2expression 。 expression 的取值为 0~31 。

ASSOC=section :指定与本段相关的 ELF 段。 COMDEF: 定义一个通用的段。该段可以包含代码和数据。

在某个源文件中,同名的 COMDEF 段必须相同。 COMMON: 定一个通用段。该段不包含任何用户代码和数据。 NOINIT: 指定此数据段仅仅保留了内存单元,而没有将各初

始值写入内存单元,或者将各个内存单元值初始化为 0 。

一个大的程序可包含多个代码段和数据段。一个汇编程序至少包含一个代码段。

4 、 ENTRY 指定程序的入口点。 语法格式: ENTRY

注意: 一个程序(可包含多个源文件)中至少要有一个 ENTRY (可以有多个 ENTRY ),但一个源文件中最多只能有一个 ENTRY (可以没有 ENTRY )

5 、 END END 伪操作告诉编译器已经到了源程序

结尾。 语法格式: END 注意:  每一个汇编源程序都必须包含 END

伪操作,以表明本源程序的结束。

6 、 ALIGN ALIGN 伪操作通过填充 0 将当前的位置以某

种形式对齐。 语法格式: ALIGN {expr{,offset}} 其中:

expr :一个数字,表示对齐的单位。这个数字是2 的整数次幂,范围在 20~231 之间。(如果没有指定 expr ,则当前位置对齐到下一个字边界处。)

offset:偏移量,可以为常数或数值表达式。不指定 offset 表示将当前位置对齐到以 expr 为单位的起始位置。

例 1 : short DCB 1 ;本位操作使字对齐被破坏 ALIGN ;重新使其为字对齐 MOV R0,1 例 2 : ALIGN 8 ; 当前位置以 2 个字的方式对齐

7 、 EXPORT及 GLOBAL 声明一个源文件中的符号,使得此符号可以被

其他源文件引用。 语法格式: EXPORT/GLOBAL symbol {[weak]} 其中:

symbol :声明的符号的名称。(区分大小写) [weak] :声明其他同名符号优先于本符号被引用。

例: AREA example , CODE , READONLY EXPORT DoAdd DoAdd ADD R0 , R0 , R1

8 、 IMPORT及 EXTERN 声明一个符号是在其他源文件中定义的。 语法格式: IMPORT symbol{[weak]} EXTERN symbol{[weak]} 其中:

symbol :声明的符号的名称。

[weak] :当没有指定此项时,如果 symbol 在所有的源文件

中都没有被定义,则连接器会报告错误。当指定此项时,如果 symbol 在所有的源文件中都没有被定义,则连接器不会报告错误,而是进行下面的操作。

1 、如果该符号被 B 或者 BL 指令引用,则该符号被设置成下一条指令的地址,该 B 或 BL 指令相当于一条 NOP 指令。

2 、其他情况下此符号被设置成 0 。

9 、 GET及 INCLUDE 将一个源文件包含到当前源文件中,并将被

包含的文件在其当前位置进行汇编处理。 指令格式: GET filename INCLUDE filename 其中 :

filename: 包含的源文件名,可以使用路径信息(可包含空格)。

例: GET d:\arm\file.s

10 、 INCBIN 将一个文件包含到当前源文件中,而被包含

的文件不进行汇编处理 指令格式: INCBIN filename 其中:

filename :被包含的文件名称,可使用路径信息(不能有空格)。

通常使用此伪操作将一个可执行文件或者任意数据包含到当前文件中。

例: INCBIN d:\arm\file.txt

6.1.3 GNU 编译环境下的伪操作和宏指令 GNU 编译环境下的伪操作可分为以下几类:

常量编译控制伪操作汇编程序代码控制伪操作宏及条件编译控制伪操作其他伪操作

常量编译控制伪操作伪操作 语法格式 作 用

.byte .byte expr { , expr} … 分配一段字节内存单元,并用 expr 初始化。

.hword/.short .hword expr { , expr} … 分配一段半字内存单元,并用 expr 初始化。

.ascii .ascii expr { , expr} … 定义字符串 expr(非零结束符)。

.asciz /.string .asciz expr { , expr} … 定义字符串 expr(以 /0 为结束符)。

.float/.single .float expr { , expr} … 定义一个 32bit IEEE 浮点数 expr 。

.double .double expr { , expr} … 定义 64bit IEEE浮点数 expr 。

.word/.long /.int

.word expr { , expr} … 分配一段字内存单元,并用 expr 初始化。

.fill .fill repeat { , size}{ , value}

分配一段字节内存单元( repeat 个 size长度的内存单元),用 value 初始化每个内存单元。

.zero .zero size 分配一段字节内存单元,并用 0填充内存。

.space/.skip .space size {, value} 分配一段内存单元,用 value 将内存单元初始化。

字符编译控制伪操作伪操作 语法格式 作用

.equ/.set .equ symbol, expr

.set symbol, expr

为一个常量、标号等定义一个字符名称 例: .equ rPLLCON, 0x01d80000

.equ zhen, “student”

.global/.globl .global symbol 定义本模块中一个可被其它文件引用的全局变量例: .global Func

.extern .extern symbol 声明本文件中要使用的其它文件中的全局变量(亦可不声明)例: .extern Func

汇编程序代码控制伪操作伪操作 语法格式 作 用

.section .section expr 定义域中包含的段。

.text .text {subsection} 将操作符开始的代码编译到代码段或代码段子段。

.data .data {subsection} 将操作符开始的数据编译到数据段或数据段子段。

.bss .bss {subsection} 将变量存放到 .bss 段或 .bss 段的子段。

.code 16/.thumb .code 16.thumb

表明当前汇编指令的指令集选择 Thumb 指令集。

.code 32/.arm .code 32.arm

表明当前汇编指令的指令集选择 ARM 指令集。

.end .end 标记汇编文件的结束行,即标号后的代码不作处理。

.include .include “filename” 将一个源文件包含到当前源文件中。

.align/.balign .align {alignment} { , fill} { , max}

通过添加填充字节使当前位置满足一定的对齐方式。

宏及条件编译控制伪操作

伪操作 语法格式 作 用

.macro 、 .exitm及 .endm

.macro acroname{ parameter {, parameter }…}

….endm

.macro 伪操作标识宏定义的开始, .endm标识宏定义的结束。用 .macro及 .endm 定义一段代码,称为宏定义体。 .exitm 伪操作用于提前退出宏。

.ifdef , .else及 .endif

.ifdef condition….else….endif

当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。其中 else 可以缺省。

其他伪操作 .ltorg

在当前段 (一般为代码段 )的当前地址(字对齐)产生一个文字池。一般放在一段程序的后面。

例: mov pc, lr .ltorg src: .long 1,2,3,4,5 dst: .long 0,0,0,0 .req name , expr

为一个特定的寄存器定义名称。 例: req count, 7 ;定义 R7 为 count

6.1.4 ARM 汇编语言的伪指令 ARM 伪指令不属于 ARM 指令集中的指令,

是为了编程方便而定义的。伪指令可以像其它 ARM 指令一样使用,但在编译时这些指令将被等效的 ARM 指令代替。 ARM 伪指令有四条,分别是: ADR :小范围的地址读取伪指令。 ADRL :中等范围的地址读取伪指令。 LDR :大范围的地址读取伪指令。 NOP :空操作伪指令。

1 、 ADR——小范围的地址读取 ADR 伪指令将基于 PC相对偏移的地址值或基于寄

存器相对偏移的地址值读取到寄存器中。在汇编编译器编译源程序时, ADR 伪指令被编译器替换成一条合适的指令。通常,编译器用一条 ADD 指令或 SUB指令来实现此 ADR 伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。

语法格式: ADR{cond} register , expr 其中:

register :加载的目标寄存器。 expr :地址表达式。取值范围是参考 P212

例 1 : ……

(0x20) ADR R1,Delay

……

Delay

(0x64) MOV R0,R14

……

使用 ADR 将程序标号 Delay 所表示的地址存入 R1 。

编译后的反汇编代码:

……

ADD R1,PC,#0x3C

……

MOV R0,R14

PC+0x3C

=0x20+0x08+0x3C

=0x64

例 2 :查表 ADR R0,D_TAB ; 加载转换表地址 LDRB R1,[R0,R2] ; 使用 R2 作为参数,进行查

表 ……

D_TAB

DCB 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92

2 、 ADRL—— 中等范围的地址读取 ADRL 伪指令将基于 PC相对偏移的地址值或基于

寄存器相对偏移的地址值读取到寄存器中,比 ADR 伪指令可以读取更大范围的地址。在汇编编译器编译源程序时, ADRL 被编译器替换成两条合适的指令。若不能用两条指令实现,则产生错误,编译失败。

语法格式: ADRL{cond} register,expr 其中:

register :加载的目标寄存器。 expr :地址表达式。取值范围参考 P212

例: ……

(0x20) ADRL R1,Delay

……

Delay

(0x64) MOV R0,R14

……

使用 ADRL 将程序标号 Delay 所表示的地址存入 R1 。

编译后的反汇编代码:

……

ADD R1,PC,#0x3C

ADD R1,R1,#0

……

MOV R0,R14

ADRL 伪指令被汇编成两条指令,尽管第 2 条指令并没有意义。

3 、 LDR ——大范围的地址读取 LDR 伪指令用于加载 32 位立即数或一个地址值到

指定的寄存器。在汇编编译源程序时, LDR 伪指令被编译器替换成一条合适的指令。若加载的常数未超过 MOV 或 MVN 的范围,则使用 MOV 或 MVN指令代替该 LDR 伪指令,否则汇编器将常量放入文字池,并使用一条程序相对偏移的 LDR 指令从文字池读出常量。

语法格式: LDR{cond} register,=expr 其中:

Register :加载的目标寄存器。 expr : 32 位常量或地址表达式。

例: ……

(0x060) LDR R1,=Delay

……

Delay

(0x102) MOV R0,R14

……

使用 LDR 将程序标号 Delay 所表示的地址存入 R1 。

编译后的反汇编代码:

……

LDR R1,stack

……

Delay

MOV R0,R14

……

LTORG

stack DCD 0x102

LDR 伪指令被汇编成一条 LDR 指令 , 并在文字池中定义一个常量 ,该常量为标号 Delay 的地址。

注意: 从指令位置到文字池的偏移量必须小于 4KB 。 与 ARM 指令的 LDR相比,伪指令 LDR 的参

数有“ =” 号。

4 、 NOP—— 空操作伪指令 NOP 伪指令在汇编时将被替代成 ARM 中的空操作,比如可能是“ MOV R0,R0” 指令等。

NOP 可用于延时操作。 语法格式: NOP 例: Delay NOP ; 空操作 NOP NOP SUBS R1,R1,#1 ;循环次数减 1 BNE Delay MOV PC,LR

6.2 ARM汇编语言程序设计 一、 ARM 汇编中的文件格式 ARM 源程序文件(可简称为源文件)可以由任意一种文本编辑器来编写程序代码,它一般为文本格式。在 ARM 程序设计中,常用的源文件可简单分为以下几种 :

源程序文件 文件名 说 明

汇编程序文件 *.S 用 ARM 汇编语言编写的 ARM 程序或 Thumb 程序。

C 程序文件 *.C 用 C 语言编写的程序代码。

头文件 *.H 为了简化源程序,把程序中常用到的常量命名、宏定义、数据结构定义等等单独放在一个文件中,一般称为头文件。

二、 ARM 汇编语言语句格式 ARM 汇编语言语句格式为:{symbol}{instruction|direction|pseudo-instruction} {;comment}

其中: symbol :标号 instruction:指令 directive :伪操作 pseudo-instruction:伪指令 comment :语句的注释

书写规则: 指令、伪操作和伪指令的助记符可全部用大写字母,也可全部用小写字母,但不能在一个助记符中即用大写字母又用小写字母。

标号严格区分大小写。 标号必须在一行的顶格书写,而所有指令均不能顶格书写。

如果一条语句很长,可使用“ \” 将长语句分成若干行来写。

例:符合规则的例子

例:不符合规则的例子 start MOV R0,#1

ABC: MOV R1,#2

MOV R2,#3

Loop MOV R2,#3

B loop

ARM 汇编语言是以段( section )为单位来组织源文件的。

段是相对独立的、具有特定名称的、不可分割的指令或者数据序列。

段又可以分为代码段和数据段,代码段存放执行代码,数据段存放代码运行时需要用到的数据。

一个 ARM 源程序至少需要一个代码段,大的程序可以包含多个代码段和数据段。

举例说明 ARM 汇编语言源程序的基本结构.global _start

.text

_start

MOV r0 , #10

MOV r1 , #3

ADD r0 , r0 , r1

.end

本程序的程序体部分实现了一个简单的加法运算。