在Arm汇编语言开发中,字面量(Literals)是直接嵌入源代码中的常量值,它们不需要预先定义就可以直接使用。作为嵌入式开发的基础元素,字面量的正确使用直接影响代码效率和可读性。
Arm汇编支持多种类型的字面量表示方式:
MOV R0, #123将十进制123存入R0寄存器MOV R1, #0x7B(等同于十进制123)5_204表示五进制数204123.4{TRUE}或{FALSE}'A'(实际存储ASCII值0x41)"Hello"注意:虽然单字符可以用字符串形式表示(如
#"a"),但多字符字符串(如#"ab")作为立即数会导致汇编错误。这是因为Arm指令对立即数有严格限制。
Arm架构对立即数有特殊编码规则,理解这些限制对编写高效代码至关重要:
A32指令集限制:
0xFF000000T32指令集限制:
0xFF0)0xXYXYXYXY)当遇到无法直接表示的立即数时,通常需要拆解为多个指令或使用literal pool(后文详述)。
在嵌入式开发中,ELF(Executable and Linkable Format)是可执行文件的通用格式。Arm汇编通过分段(Section)来组织代码和数据:
这些段在链接阶段会根据分散加载(Scatter-loading)规则被放置到内存的特定位置。
AREA指令定义段的起始,其完整语法为:
assembly复制AREA sectionname, attributes, alignment
关键属性:
CODE/DATA:指定段类型READONLY/READWRITE:内存访问权限ALIGN=n:指定对齐方式(如ALIGN=4表示4字节对齐)命名规则:
MainCode|.data|典型代码段定义示例:
assembly复制AREA Init, CODE, READONLY, ALIGN=4
ENTRY
MOV R0, #0x12
...
理解以下特性对内存布局优化很重要:
一个完整的Arm汇编模块通常包含以下部分:
A32示例:
assembly复制AREA A32ex, CODE, READONLY
ENTRY
start
MOV R0, #10 ; 参数设置
MOV R1, #3
ADD R0, R0, R1 ; R0 = R0 + R1
stop
MOV R0, #0x18 ; 半主机调用参数
LDR R1, =0x20026
SVC #0x123456 ; 触发半主机调用
END
A64示例(注意寄存器命名差异):
assembly复制AREA A64ex, CODE, READONLY
ENTRY
start
MOV W0, #10 ; 使用W系列32位寄存器
MOV W1, #3
ADD W0, W0, W1
stop
MOV X0, #0x18 ; 64位参数传递
HLT #0xF000 ; A64半主机调用
END
在开发环境中,通过特定指令序列实现与调试器的交互:
SVC指令(A32默认编号0x123456,T32用0xAB)HLT #0xF000当立即数超出单指令范围时,可采用以下方法:
MOV+MOVT组合:
assembly复制MOV R0, #0x5678 ; 低16位
MOVT R0, #0x1234 ; 高16位
LDR伪指令:
assembly复制LDR R0, =0x12345678 ; 自动选择最优加载方式
提示:LDR伪指令会自动判断——能用MOV/MVN就生成单指令,否则存入literal pool
字面量池是汇编器在代码段中嵌入的常量数据区,通过LTORG指令显式控制其位置:
assembly复制AREA Example, CODE
LDR R0, =0x12345678 ; 需要literal pool
... ; 大量代码
LTORG ; 确保字面量在LDR范围内
关键规则:
ADR指令:
assembly复制ADR R0, local_label
ADRL伪指令:
assembly复制ADRL R1, far_label
MOV32伪指令:
assembly复制MOV32 R2, symbol_name
LDR伪指令:
assembly复制LDR R3, =external_symbol
assembly复制AREA JumpTab, CODE
ENTRY
MOV R0, #1 ; 跳转索引
CMP R0, #max_entries
BHS out_of_range
ADR R1, JumpTable ; 获取跳转表基址
LDR PC, [R1, R0, LSL#2] ; PC = JumpTable + index*4
JumpTable
DCD func00
DCD func01
func00
... ; 功能0实现
func01
... ; 功能1实现
END
| 特性 | UAL(A32/T32) | A64 |
|---|---|---|
| 条件执行 | BEQ label |
B.EQ label |
| 寄存器命名 | R0-R15 | W0-W30(32位)/X0-X30(64位) |
| 指令宽度 | 支持.W/.N指定 | 固定32位 |
| PC访问 | 可直接操作PC | 无PC寄存器 |
A32条件执行:
assembly复制CMP R0, #5
ADDEQ R1, R2, R3 ; 条件执行加法
A64条件选择:
assembly复制CMP W0, #5
CSEL W1, W2, W3, EQ ; W1 = (W0==5) ? W2 : W3
assembly复制AREA Subr, CODE
ENTRY
MOV R0, #10 ; 参数1
MOV R1, #20 ; 参数2
BL add_func ; 调用子程序
... ; 结果在R0中
add_func
ADD R0, R0, R1 ; 实现加法
BX LR ; 返回
END
经验:在密集计算场景中,合理规划寄存器使用可以减少内存访问,我在实际项目中通过寄存器优化使关键算法性能提升约15%。
段属性优化:
DATA并READWRITEREADONLY属性NOINIT节省镜像体积立即数加载选择:
调试技巧:
.ltorg伪指令确保字面量池位置可控ALIGN 4保证跳转表对齐性能关键点:
在最近的一个电机控制项目中,通过精心设计段布局和立即数加载策略,我们将中断延迟降低了约8%。这主要得益于:
FASTCODE段ALIGN 32保证缓存行对齐