007.GNU C内联汇编杂谈|千篇笔记实现嵌入式全栈/裸机篇

内联汇编就像一把尖刀,它允许我们在C的高级抽象和CPU底层硬件之间自由穿梭~

1. 什么时候用到?

嵌入式开发,经常有这样的场景:

  • 访问CPU特殊寄存器, 比如通过CPSR切换工作模式,通过CP15配置协处理器(MMU、缓存等)

  • 通过nop指令精准延时

  • 中断的入口封装、现场保护等

  • 特殊硬件指令比如swi

这个时候,在C/C++编程中,就需要使用到内联汇编去处理这样的场景。

2. 核心语法

C 复制代码
__asm__ volatile (
    "汇编指令模板\n\t"   /* 第1部分:你要执行的指令 */
    : 输出操作数列表     /* 第2部分:C 变量 <- 寄存器 (由内向外写) */
    : 输入操作数列表     /* 第3部分:寄存器 <- C 变量 (由外向内读) */
    : 破坏描述部(Clobber)/* 第4部分:告诉编译器你弄脏了哪些东西 */
);

举例1,

C 复制代码
unsigned long cpsr, out, in = 1;
__asm__ volatile (
    // 读取cpsr
    "mrs %0, cpsr\n\t"
    // 禁用IRQ/FIQ, 切换至IRQ模式
    "msr cpsr_c, #0b11010010\n\t"
    // 设置IRQ模式的SP
    "ldr sp, =0x40001000\n\t"
    // 恢复原来模式和原始中断状态
    "msr cpsr, %0\n\t"
    // 纯凑数
    "mov %1, %2\n\t"
    : "=&r" (cpsr), "=r" (out)
    : "r" (in)
    : "sp", "cc", "memory"
);

举例2,

C 复制代码
void restore_irq_mask(unsigned long old_cpsr)
{
    __asm__ volatile (
        "msr cpsr, %0\n\t"
        : 
        : "r" (old_cpsr)
        : "cc", "memory"
    );
}

2.1 汇编指令模板

就是ARM汇编指令, 但是操作数变成了占位符(%0, %1, %2...)

多天指令之间用\n\t分割

占位符对应输出和输入列表中出现的顺序, 参见举例1中的占位符用法

2.2 操作数约束

上述举例中在输出和输入部分有这样的符号组合,有些是修饰符(只用于输出部分),有些是约束字符

C 复制代码
: "=&r" (cpsr), "=r" (out)
: "r" (in)
约束符号 说明
"r" 让编译器随便挑一个通用寄存器(R0-R12),把变量放进去
"m" 内存操作数。告诉编译器这个变量在内存里,不要放进寄存器
"I" 立即数
修饰符 说明
"=" 只写(Write-only)。表示这条汇编会把新值写入这个 C 变量,变量之前的值不重要了
"+" 读写(Read-write)。表示这条汇编既会先读后写这个 C 变量(此时变量即是输入数又是输出数)
"&" 早占(Earlyclobber)。这是一个高级且救命的修饰符!它告诉编译器:"在这个汇编块执行完之前,这个输出寄存器就已经被写入了。所以,千万不要把任何输入变量分配到同一个物理寄存器上,否则输入数据会被提前覆盖!"比如在上述举例中"=&r" (cpsr),cpsr如果使用了编译器自动分配的寄存器r2,那么"r" (in)就不会分配给r2,以免r2被提前写入发生值改变,让in失效

2.3 破坏描述部

编译器可能会聪明过头,悄悄把很多C变量还存在寄存器中。如果汇编中修改了某些寄存器又不告诉编译器,那么执行就可能出现问题。

那么就需要用一些口令告诉编译器你修改了什么部分。

破坏描述符 说明
"cc" 告诉编译器,这段汇编修改了 CPSR 的标志位(N, Z, C, V),比如你用了 adds 或 cmp 指令。编译器之后就不会依赖之前的条件判断了
"memory" 内存屏障。告诉编译器,这段汇编偷偷修改了内存。编译器会强制将缓存在寄存器里的所有变量重新写回内存,并在汇编执行后重新从内存读取,防止读到脏数据
具体寄存器名(如 "r1", "lr") 如果你在汇编里硬编码(写死)使用了某个物理寄存器,必须在这里声明,编译器就会在执行汇编前把那个寄存器压栈保护起来
相关推荐
清风6666663 小时前
基于单片机的自动存包柜设计
单片机·嵌入式硬件·mongodb·毕业设计·课程设计·期末大作业
笨笨饿3 小时前
42_C语言查找算法
linux·服务器·c语言·人工智能·mcu·学习方法·嵌入式软件
计算机安禾3 小时前
【数据结构与算法】第33篇:交换排序(二):快速排序
c语言·开发语言·数据结构·数据库·算法·矩阵·排序算法
嵌入式×边缘AI:打怪升级日志3 小时前
Linux 常用命令学习笔记(续):查找、压缩、vi 编辑器与其他命令
linux·笔记·学习
萧行之3 小时前
Linux 下 Miniconda3 清华源极速安装教程(含报错解决、一键复制)
linux·运维·服务器
LiuYouth_1233 小时前
耳机双链接A、B手机,A有业务的情况下,B手机播放音乐需要外放 -- 基于中科蓝汛897X
单片机
点灯小铭3 小时前
基于单片机的火焰与温度联动检测及声光灭火控制系统
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
算法鑫探4 小时前
2025 图形(蓝桥杯十六届C组程序题 C 题)
c语言·数据结构·算法·新人首发
ZzzZZzzzZZZzzzz…4 小时前
MySQL备份还原方法2----LVM
linux·运维·数据库·mysql·备份还原