ARM处理器作为RISC架构的典型代表,其指令集设计体现了精简、高效的特点。在嵌入式系统开发中,掌握ARM汇编语言是进行底层优化的必备技能。与x86架构不同,ARM采用加载-存储(Load-Store)架构,这意味着数据处理指令只能操作寄存器,内存访问则需要专门的加载和存储指令。
ARM处理器提供16个32位通用寄存器(R0-R15),其中R13通常作为堆栈指针(SP),R14用作链接寄存器(LR),R15是程序计数器(PC)。在异常模式下,处理器会自动切换至对应的影子寄存器组,这是ARM架构的重要特点。
寄存器使用遵循ATPCS(ARM-Thumb Procedure Call Standard)规范:
实际开发中建议使用寄存器别名(如MOV R0, SP)而非直接数字编号,这能显著提升代码可读性。
ARM处理器有两种指令集状态:
状态切换通过BX/BLX指令实现:
armasm复制 ADR R0, thumb_code + 1 ; +1表示Thumb状态
BX R0 ; 切换至Thumb状态
thumb_code:
.thumb
MOVS R0, #0x12 ; Thumb指令
处理器模式决定了特权级别和寄存器视图:
模式切换通常通过修改CPSR或触发异常实现:
armasm复制 MRS R0, CPSR ; 读取状态寄存器
BIC R0, R0, #0x1F ; 清除模式位
ORR R0, R0, #0x13 ; 设置为管理模式
MSR CPSR_c, R0 ; 写回控制域
ARM的数据处理指令包括算术运算、逻辑运算和移动操作,其通用格式为:
<操作码>{<条件码>}{S} <Rd>, <Rn>, <Operand2>
典型指令示例:
armasm复制 ADD R0, R1, R2 ; R0 = R1 + R2
SUB R3, R4, #0x20 ; R3 = R4 - 32
AND R5, R6, #0xFF ; 掩码操作
ORR R7, R8, R9, LSL #2 ; 移位合并
移位操作作为ARM的特色功能,可无缝集成到操作数中:
LDR/STR指令支持多种寻址模式:
armasm复制 LDR R0, [R1] ; 直接寻址
LDRB R2, [R3, #4]! ; 前变址字节加载
STR R4, [R5], #-8 ; 后变址存储
LDRD R6, R7, [R8, #0x20] ; 双字加载
批量加载/存储(LDM/STM)指令的高效使用:
armasm复制 STMFD SP!, {R0-R3, LR} ; 压栈保存寄存器
LDMFD SP!, {R0-R3, PC} ; 出栈并返回
ARM指令可附加条件码实现条件执行,条件码基于CPSR中的标志位:
armasm复制 CMP R0, #10 ; 设置标志位
ADDGT R1, R2, R3 ; 仅在大于时执行
MOVLE R1, #0 ; 小于等于时执行
分支指令包括:
Thumb-2作为ARMv6T2引入的混合指令集,结合了16位和32位指令:
典型Thumb-2指令示例:
armasm复制 MOVS R0, #0x55 ; 16位指令
ADD.W R1, R2, R3 ; 32位宽指令
IT EQ ; 条件执行块
MOVEQ R4, #0
ARM/Thumb交互调用示例:
armasm复制 .arm
arm_func:
BL thumb_func ; 自动处理状态切换
...
.thumb
thumb_func:
ADR.W R0, arm_func
BX R0 ; 显式状态切换
VFPv3架构提供32个64位寄存器(D0-D31),也可作为16个128位寄存器(Q0-Q15)访问。系统初始化时需要启用VFP协处理器:
armasm复制 MRC p15, 0, R0, c1, c0, 2 ; 读CPACR
ORR R0, R0, #(0xF << 20) ; 启用VFP
MCR p15, 0, R0, c1, c0, 2 ; 写回CPACR
ISB ; 确保生效
基本浮点操作:
armasm复制 VLDR D0, [R1] ; 加载双精度浮点
VADD.F64 D2, D0, D1 ; 双精度加法
VMUL.F32 S4, S2, S3 ; 单精度乘法
VCVT.F64.F32 D1, S0 ; 单精度转双精度
向量化运算示例:
armasm复制 VLD1.64 {D0-D3}, [R0]! ; 加载4个双精度数
VADD.F64 Q1, Q0, Q1 ; 128位向量加法
VST1.64 {D4-D7}, [R1]! ; 存储结果
GCC内联汇编模板:
c复制void memcpy_opt(void *dst, const void *src, size_t len)
{
asm volatile (
"1: SUBS %2, %2, #64\n"
" LDMIA %1!, {r4-r11}\n"
" STMIA %0!, {r4-r11}\n"
" BGT 1b"
: "+r"(dst), "+r"(src), "+r"(len)
:
: "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "cc"
);
}
矩阵乘法优化示例:
armasm复制 MOV R4, #0 ; i = 0
row_loop:
MOV R5, #0 ; j = 0
col_loop:
VLDR D0, [R0, R4, LSL #3] ; A[i][0]
VLDR D1, [R1, R5, LSL #3] ; B[0][j]
VMUL.F64 D2, D0, D1
...
ADD R5, R5, #1
CMP R5, #N
BLT col_loop
ADD R4, R4, #1
CMP R4, #M
BLT row_loop
数据中止(Data Abort)处理要点:
使用性能计数器(PMU)的示例:
armasm复制 MRC p15, 0, R0, c9, c12, 0 ; 读取PMCR
ORR R0, R0, #1 ; 启用计数器
MCR p15, 0, R0, c9, c12, 0
MOV R0, #0x11 ; 选择指令计数事件
MCR p15, 0, R0, c9, c12, 1 ; 设置事件类型
MOV R0, #0x80000000 ; 启用计数器0
MCR p15, 0, R0, c9, c12, 1
在嵌入式开发实践中,ARM汇编的熟练使用可以带来显著的性能提升。我曾在一个图像处理项目中,通过将关键循环用汇编重写,使性能提升了近3倍。但也要注意,现代编译器优化能力已经很强,应当先用C语言写出清晰实现,再针对热点进行汇编优化。