欢迎光临
我们一直在努力

ARM架构——汇编语言

目录

一、ARM 汇编语法与核心概念

1.1 伪操作

1.2 立即数与移位操作

1.2.1 立即数

1.2.2 移位操作

二、数据操作与运算

2.1 数据传送与逻辑指令

2.2 算术运算指令

2.3 条件判断指令

2.3.1 CPSR 标志位

2.3.2 CMP 指令

三、流程控制

3.1 do-while 循环

3.2 while/for 循环

四、函数调用

4.1 跳转指令

4.2 ARM 栈类型与初始化

4.3 现场保护与恢复

五、汇编与 C 语言互调

5.1 汇编调用 C 函数

5.2 C 语言调用汇编函数

六、工作模式与异常处理

6.1 工作模式切换

6.2 异常向量表

七、常见问题 FAQ


在嵌入式开发中,ARM 架构占据核心地位,而汇编语言是解锁 ARM 内核底层能力的关键 —— 它的核心使命是编写启动代码,为 C 语言运行搭建基础环境(初始化异常向量表、配置栈指针、开启中断、切换工作模式、引导 C 主函数)。

一、ARM 汇编语法与核心概念

1.1 伪操作

伪操作并非 CPU 执行指令,而是告诉汇编器如何组织代码 / 数据,核心伪操作如下:

伪操作作用说明
area reset, code, readonly 定义段:reset 为段名(通常用于复位向量),code 表示代码段,readonly 只读属性
code32 后续指令使用 32 位 ARM 指令集(对应 thumb 为 16 位指令集)
entry 标记程序入口(复位后首个执行的指令位置)
end 标记汇编文件结束
preserve8 栈 8 字节对齐(解决 C / 汇编互调时的编译报错)

基础框架示例:

area reset, code, readonly ; 定义复位段
code32 ; 32位ARM指令集
entry ; 程序入口

; 核心代码(启动流程/业务逻辑)

end ; 文件结束

1.2 立即数与移位操作

1.2.1 立即数

汇编中 "立即数" 特指 12 位立即数(imm12),判断标准:

  • 将数值转为二进制后,存在偶数位循环右移,使得移位后高 24 位全 0,低 8 位为有效数据(imm8)。
  • 合法示例:0x80000000(循环右移 30 位后,低 8 位为0x01)、0xF0(无需移位,低 8 位直接有效)
  • 非法示例:0x101(无偶数位循环右移能让高 24 位全 0)

1.2.2 移位操作

ARM 指令支持寄存器移位后参与运算,无需单独指令,常见类型:

移位类型含义格式示例
LSL 逻辑左移(高位丢弃) mov r0, r1, lsl #3(左移 3 位)
LSR 逻辑右移(低位丢弃) add r0, r1, lsr r2(按 r2 值右移)
ASR 算术右移(符号位扩展) mov r0, r1, asr #2
ROR 循环右移(低位补高位) sub r0, r1, ror #4
RRX 扩展右移(带进位标志) mov r0, r1, rrx(无需移位量)

二、数据操作与运算

ARM 汇编指令围绕 "数据传送、运算、逻辑操作" 展开,以下是高频指令详解:

2.1 数据传送与逻辑指令

指令功能描述示例代码
MOV 数据传送(寄存器 / 立即数→寄存器)

mov r0, #0x8(立即数传 r0)、

mov r1, r0(寄存器传寄存器)

MVN 按位取反后传送 mvn r0, #0xFF(r0=0xFFFFFF00)
BIC 指定位清 0 bic r0, r0, #(1<<31)(清 r0 第 31 位)
ORR 指定位置 1 orr r0, r0, #0x80000000(置 r0 第 31 位)

2.2 算术运算指令

核心支持加减运算,支持立即数 / 寄存器 / 移位寄存器作为操作数:

; ADD指令示例
mov r0, #0x0F ; r0=15
mov r1, #0xF0 ; r1=240
mov r2, #1 ; r2=1
add r3, r0, #0xF0 ; 立即数运算:r3=15+240=255
add r4, r0, r1 ; 寄存器运算:r4=255
add r5, r0, r1, lsl #1 ; 移位寄存器运算:r1左移1位(480),r5=15+480=495
add r6, r0, r1, lsl r2 ; 按寄存器移位:r2=1,效果同上

; SUB指令示例(格式与ADD一致)
sub r7, r0, #0x05 ; r7=15-5=10

2.3 条件判断指令

2.3.1 CPSR 标志位

ARM 通过 CPSR 寄存器(当前程序状态寄存器)的条件标志位实现分支判断,核心标志位:

标志位含义置 1 条件
N(符号) 结果为有符号负数 运算结果最高位(bit31)=1
Z(零值) 结果为 0 运算结果全 0
C(进位) 无符号运算进位 / 无借位 加法进位、减法无借位
V(溢出) 有符号运算溢出 两正数相加得负数、两负数相加得正数

分支判断

2.3.2 CMP 指令

CMP <Rn>, <操作数> 等价于 SUBS <Rn>, <操作数>(减法不保存结果,仅更新 CPSR 标志位),配合条件码实现分支:

mov r0, #10
mov r1, #20
cmp r0, r1 ; 比较r0和r1(10-20),Z=0、N=1、C=0、V=0
movge r2, r0 ; GE(大于等于):N==V,此处不执行
movlt r2, r1 ; LT(小于):N!=V,执行r2=20

常见条件码:eq(等于)、ne(不等于)、gt(大于)、lt(小于)、al(无条件执行,默认)。

三、流程控制

ARM 汇编通过 "标签 + 跳转指令" 实现循环,需满足循环三要素:结束条件、趋向终结的语句、循环体。

3.1 do-while 循环

C 语言对照:

int i=0, sum=0;
do{
sum += i;
i++;
}while(i <= 100);

汇编实现:

mov r0, #0 ; i=0(r0存储循环变量)
mov r1, #0 ; sum=0(r1存储累加结果)
loop ; 循环标签
add r1, r1, r0 ; 循环体:sum += i
add r0, r0, #1 ; 趋向终结:i++
cmp r0, #100 ; 结束条件判断:i<=100?
ble loop ; 满足则跳转循环(ble=小于等于)

3.2 while/for 循环

C 语言对照:

int i=0, sum=0;
while(i <= 100)
{
sum += i;
i++;
}

汇编实现:

mov r0, #0 ; i=0
mov r1, #0 ; sum=0
loop
cmp r0, #100 ; 先判断:i>100?
bgt finish ; 满足则退出循环(bgt=大于)
add r1, r1, r0 ; 循环体:sum += i
add r0, r0, #1 ; i++
b loop ; 跳转循环
finish
b finish ; 程序暂停

四、函数调用

ARM 函数调用的核心是栈管理和返回地址保存,需理解跳转指令差异、栈类型及现场保护逻辑。

4.1 跳转指令

指令功能差异应用场景
b 无条件跳转,不保存返回地址 同一函数内分支跳转
bl 跳转前将返回地址(PC+4)存入 LR 寄存器 函数调用(需返回主调函数)
bx 跳转并切换指令集(ARM↔Thumb),通过寄存器传递目标地址 函数返回(bx lr = mov pc, lr)

基础函数调用示例:

b main ; 跳转到main函数(不返回)
func ; 被调函数
mov r0, #1
mov r1, #2
add r3, r0, r1 ; r3=3
bx lr ; 返回主调函数(lr存储返回地址)
main ; 主调函数
mov r0, #100
mov r1, #200
bl func ; 调用func,lr=当前PC+4
mov r3, #300 ; 函数返回后执行

4.2 ARM 栈类型与初始化

ARM 内核(如 S3C2440)采用满减栈(Full Descending),栈操作规则:

  • 入栈:先 sp -= 4(栈指针下移),再存储数据
  • 出栈:先读取数据,再 sp += 4(栈指针上移)

栈指针初始化:

栈需指定连续内存区域,通过 ldr 指令加载合法地址(直接 mov sp, #0x40001000 报错,因非 12 位立即数):

ldr sp, =0x40001000 ; 栈基地址=0x40001000(需在IRAM范围内)
; Keil配置:Target→IRAM1 Start=0x40000000,Size=0x1000

4.3 现场保护与恢复

函数调用时,被调函数可能修改主调函数的寄存器,或嵌套调用覆盖 LR,需通过压栈 / 弹栈保护现场:

  • 保护现场:stmfd sp!, {r0-r12, lr}(将寄存器组和 LR 入栈)
  • 恢复现场:ldmfd sp!, {r0-r12, pc}(弹栈恢复寄存器,PC=LR 实现返回)

嵌套函数示例:

b main
func1 ; 二级被调函数
mov r0, #10
mov r1, #20
cmp r0, r1
movge r2, r0 ; r2=20
movlt r2, r1
bx lr
func0 ; 一级被调函数
stmfd sp!, {r0-r12, lr} ; 保护现场
bl func1 ; 嵌套调用func1(LR被覆盖)
ldmfd sp!, {r0-r12, pc} ; 恢复现场并返回
main
ldr sp, =0x40001000 ; 初始化栈指针
stmfd sp!, {r0-r12, lr} ; 主调函数保护现场
bl func0 ; 调用func0
ldmfd sp!, {r0-r12, lr} ; 恢复现场
finish
b finish
end

五、汇编与 C 语言互调

嵌入式开发中,汇编负责底层初始化,C 语言负责业务逻辑,两者互调是核心场景。

5.1 汇编调用 C 函数

步骤与规则:

  • C 函数声明:extern void c_add(int a, int b);(C 文件中实现)
  • 汇编导入:import c_add(Keil 要求显式导入 C 函数)
  • 参数传递:前 4 个参数通过 r0-r3 传递,超过 4 个的参数入栈
  • 栈对齐:添加 preserve8 伪操作(解决 8 字节对齐报错)
  • 现场保护:调用前压栈,返回后弹栈
  • 完整示例:

    • 汇编文件(start.s):

    area reset, code, readonly
    code32
    preserve8 ; 栈8字节对齐,避免编译报错
    entry
    import c_add ; 导入C函数

    main
    ldr sp, =0x40001000 ; 初始化栈
    stmfd sp!, {r0-r12, lr} ; 保护现场
    ; 传参:r0=1, r1=2, r2=3, r3=4, r4=5(第5个参数入栈)
    mov r0, #1
    mov r1, #2
    mov r2, #3
    mov r3, #4
    mov r4, #5
    stmfd sp!, {r4} ; 第5个参数入栈
    bl c_add ; 调用C函数
    ldmfd sp!, {r4} ; 弹出第5个参数
    ldmfd sp!, {r0-r12, lr} ; 恢复现场
    finish
    b finish
    end

    • C 文件(main.c):

    #include <stdio.h>
    // C函数实现
    void c_add(int a, int b, int c, int d, int e)
    {
    int sum = a + b + c + d + e;
    // sum=15(可通过调试查看)
    }

    编译配置(Keil):

  • 新建工程,删除自动生成的启动文件(避免冲突)
  • 添加 start.s 和 main.c
  • Target→ROM1:Start=0x0,Size=0x2000
  • Linker→勾选 "Use Memory Layout from Target Dialog"
  • Debug→Use Simulator,取消 "Run to main"
  • 5.2 C 语言调用汇编函数

    步骤与规则:

  • 汇编导出:export func1(声明函数可被外部调用)
  • C 声明:extern int func1(int a, int b);(声明汇编函数)
  • 参数传递:C 函数参数通过 r0-r3 传入汇编
  • 返回值:汇编函数通过 r0 返回结果
  • 完整示例:

    • 汇编文件(func.s):

    area func, code, readonly
    code32
    export func1 ; 导出函数,供C调用
    func1 ; 汇编函数:返回a和b的最大值
    cmp r0, r1 ; r0=a, r1=b
    movge r0, r0 ; 若a>=b,返回a(r0不变)
    movlt r0, r1 ; 若a<b,返回b(r0=r1)
    bx lr ; 返回C函数
    end

    • C 文件(main.c):

    #include <stdio.h>

    extern int func1(int a, int b); // 声明汇编函数
    int main()
    {
    int max = func1(10, 20); // 调用汇编函数,max=20
    return 0;
    }

    六、工作模式与异常处理

    6.1 工作模式切换

    ARM 内核支持 7 种工作模式,通过 CPSR 寄存器的 M 域(bit0-bit4)控制,切换步骤:

    • 读取 CPSR:mrs r0, cpsr
    • 清除 M 域:bic r0, r0, #0x1F(0x1F=5 位全 1)
    • 设置目标模式:orr r0, r0, #0x10(0x10 = 用户模式)
    • 写入 CPSR:msr cpsr, r0

    切换到用户模式示例:

    mrs r0, cpsr ; 读取当前CPSR
    bic r0, r0, #0x1F ; 清除M域
    orr r0, r0, #0x10 ; 设置为用户模式(USR模式)
    msr cpsr, r0 ; 写入生效

    6.2 异常向量表

    异常向量表是 ARM 处理中断 / 异常的核心,复位后 CPU 首先读取 0x0 地址的向量表,常见异常如软中断(SWI):

    ; 简化异常向量表
    area reset, code, readonly
    code32
    entry
    b reset_handler ; 复位异常(0x0)
    b . ; 未定义指令异常(0x4)
    b . ; 软中断异常(0x8):swi #7触发
    ; … 其他异常向量
    reset_handler
    ; 启动流程(初始化栈、异常向量表等)

    七、常见问题 FAQ

    1. 什么是立即数?如何判断某数是非法是12位立即数?

    • 12 位立即数是 ARM 指令支持的立即数格式。
    • 判断标准:二进制存在偶数位循环右移,使高 24 位全 0、低 8 位有效;反之则为非法(如 0x101)。

    2. b、bl、bx指令的区别是什么?

    • b:无条件跳转,不保存返回地址;
    • bl:跳转前保存返回地址到 LR,用于函数调用;
    • bx:跳转并切换指令集(ARM↔Thumb),常用于函数返回(bx lr)。

    3. ARM内核采用的栈是哪种栈?

    • 采用满减栈(Full Descending):入栈先 sp-4 再存数据,出栈先读数据再 sp+4。

    4. CPSR中条件标志位,分别在什么情况下被置位

    • N:结果最高位为 1(有符号负数);
    • Z:结果全 0;
    • C:无符号加法进位 / 减法无借位;
    • V:有符号运算溢出(两正相加得负、两负相加得正)。

    5. ARM汇编调用C语言函数以及C语言函数调用汇编编写的函数,函数参数和返回值如何处理?

    • 参数:前 4 个通过 r0-r3 传递,超过 4 个入栈;
    • 返回值:通过 r0 寄存器返回。
    赞(0)
    未经允许不得转载:171主机测评 » ARM架构——汇编语言
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址