当前位置: 首页 > news >正文

【嵌入式】读代码之startup_stm32f103xb.s

startup_stm32f103xb.s里的代码风格怪是正常的。
因为现在看到的不是 C / C++ / C#,而是:

ARM 汇编 + IAR 汇编语法

它比 C 更贴近 CPU,所以它不是在写“类、函数、对象、语句块”,而是在写:

  • 这个符号公开不公开
  • 这段代码放哪个段
  • 某个标签地址叫什么
  • 把哪个地址装进寄存器
  • 跳到哪里执行

一、先把代码整体翻译成人话

EG:

THUMB PUBWEAK Reset_Handler SECTION .text:CODE:REORDER:NOROOT(2) Reset_Handler LDR R0, =SystemInit BLX R0 LDR R0, =__iar_program_start BX R0

它的意思:

这是在定义复位处理函数Reset_Handler
芯片上电后会先执行这里。
它先调用SystemInit(),再跳到__iar_program_start,后面才会逐步进入 C 运行时和main()


二、要先换一种“读代码思维”

以前读 C++ / C#,通常是这样读:

  • 这是一个函数
  • 这个函数里有变量、分支、循环、对象调用

而读汇编时,要换成这种方式:

  • 这是一个标签还是伪指令
  • 这是在操作哪个寄存器
  • 这是在调用还是跳转
  • CPU 执行完这一行,下一步去哪

也就是说,汇编更像是在看:

CPU 的动作脚本


三、逐行讲,这几行到底怎么读


1)THUMB

THUMB

这一行不是业务逻辑,不是“执行语句”。

它是在告诉汇编器:

后面用Thumb 指令集来汇编

对 Cortex-M3 这类内核,运行的主要就是 Thumb 指令。
所以你先把它理解成:

“后面这些代码,按 ARM 的 Thumb 模式来解释”

这行你知道用途就够了,先别深究。


2)PUBWEAK Reset_Handler

PUBWEAK Reset_Handler

这一行也不是 CPU 运行时干的事,它更像是给链接器/工程系统看的声明。

你可以先拆成两部分理解:

  • PUB:公开这个符号
  • WEAK:这是一个弱定义

合起来大概就是:

Reset_Handler这个符号我先提供一个默认版本,而且它是弱的。

“弱”是什么意思?

意思是:

如果别的地方有一个更强的同名定义,别的地方可以把这里覆盖掉。

在启动文件里,大量中断函数都是这么定义的,作用就是:

  • 默认先给你一个版本
  • 你自己写了同名中断函数,就用你自己的

比如TIM3_IRQHandler默认是个死循环,但你在 C 文件里自己写了TIM3_IRQHandler(),最终就会用你的。这个启动文件里有大量这种弱定义中断入口。


3)SECTION .text:CODE:REORDER:NOROOT(2)

SECTION .text:CODE:REORDER:NOROOT(2)

这也是“工程组织信息”,不是业务逻辑。

它是在说:

接下来这段内容要放到.text这个代码段里

可以先粗理解成:

  • .text:代码段
  • CODE:这是代码,不是数据
  • REORDERNOROOT(2):IAR 的段属性控制

现在不用深究这些参数细节
先把它理解成一句话就够:

“下面定义的Reset_Handler放到代码段里去。”


4)Reset_Handler

Reset_Handler

这一行非常像 C 里的函数名,但它在汇编里更准确地说叫:

标签(label)

可以把它理解成一个“地址名字”。

就是说:

  • 这一行所在的位置,有个名字叫Reset_Handler
  • 别的地方可以跳到这里来执行

在向量表里,第二项放的就是这个Reset_Handler的地址,所以芯片上电后会从这里开始执行。启动文件向量表里明确把第二项设成了Reset_Handler

所以它虽然看起来像函数名,但底层本质更像:

“这是一个程序入口地址标签”


5)LDR R0, =SystemInit

LDR R0, =SystemInit

这句很关键。

先分开读

  • LDR:加载
  • R0:寄存器 0
  • =SystemInit:这里不是取变量内容,而是把SystemInit的地址拿出来

可以先把这句翻译成:

SystemInit这个函数的地址,装到寄存器R0

这里R0就像一个很小很快的 CPU 内部临时变量。

如果用 C 的感觉类比,可以粗略想成:

p=&SystemInit;

当然这不是严格等价,只是为了帮助建立感觉。


6)BLX R0

BLX R0

这句的意思是:

调用R0指向的函数

因为上一句已经把SystemInit的地址放到R0里了,所以这句实际上就是:

调用SystemInit()

BLX可以怎么记

可以先这样记:

  • B:Branch,跳转
  • L:Link,带返回地址
  • X:切换/兼容不同指令状态的分支形式

现在不用抠那么细,先把它理解成:

这是一次“函数调用”式跳转

所以这两句连起来:

LDR R0, =SystemInit BLX R0

就等价于脑子里熟悉的:

SystemInit();

7)LDR R0, =__iar_program_start

LDR R0, =__iar_program_start

这句和前面一模一样的套路:

__iar_program_start这个入口地址装进R0

这个__iar_program_start不是写的普通函数,它是IAR 运行库的启动入口。启动文件里也把它声明成了外部符号。

它后面会继续做:

  • 数据段初始化
  • BSS 清零
  • 运行库准备
  • 最后到main()

所以它比main()更靠前。


8)BX R0

BX R0

这一句意思是:

跳到R0指向的地址去执行

因为上一句已经把__iar_program_start的地址放进R0,所以这里就是:

跳到__iar_program_start

BLX不同,这里用的是BX,现在可以先粗理解成:

  • BLX:更像“调用函数”,会保存返回关系
  • BX:更像“直接转过去,不打算回来了”

所以这两句连起来就是:

LDR R0, =__iar_program_start BX R0

相当于:

把控制权正式交给 IAR 运行时启动入口,后面不回这个Reset_Handler


四、把整段合起来看

所以整段:

THUMB PUBWEAK Reset_Handler SECTION .text:CODE:REORDER:NOROOT(2) Reset_Handler LDR R0, =SystemInit BLX R0 LDR R0, =__iar_program_start BX R0

可以完整翻译成:

使用 Thumb 指令集。
这里定义了一个弱符号Reset_Handler,并把它放在代码段里。
当复位进入Reset_Handler后,先调用SystemInit()做系统级初始化,然后跳转到__iar_program_start,由 IAR 运行库继续完成 C 环境初始化并最终进入main()


五、为什么它看起来和 C++ / C# 差别这么大

因为层级完全不同。

C++ / C# 是“告诉程序要做什么”

比如:

SystemInit();main();

这种写法隐藏了很多细节。


汇编是“告诉 CPU 每一步怎么做”

比如:

  • 把函数地址放进寄存器
  • 用分支指令跳过去
  • 决定是不是保留返回关系

所以它不再是“像人写给人看的业务语言”,而更像:

“写给 CPU 的动作清单”


六、可以先建立一个最小词典

现在先记住下面几个就够了:

R0

CPU 的寄存器,临时存东西用

LDR

装载,把某个值/地址放进寄存器

B

跳转

BL

带返回地址的跳转,像函数调用

BX

跳到某个寄存器指向的位置

BLX

通过寄存器进行函数调用式跳转

label:

一个地址名字,比如Reset_Handler

SECTION

告诉工具链,这段内容放哪一类段

PUBWEAK

弱定义、可被覆盖的公开符号


七、现在最需要抓住的,不是语法细节,而是执行顺序

要先看懂这条主线,

复位后执行顺序

  1. 进入Reset_Handler
  2. SystemInit()
  3. 跳到__iar_program_start
  4. 运行库初始化
  5. 进入main()

八、 C 风格 类比一次

这段汇编,粗略类比成 C,可以想成:

voidReset_Handler(void){SystemInit();__iar_program_start();// 后面再到 main()}

注意,这只是帮助理解的类比,不是完全等价。

因为真实汇编里做的是:

  • 通过寄存器取地址
  • 再跳转
  • 这是启动链路的一部分

但从“作用”上看,这样理解是对的。


把这几句再拆成“寄存器视角”的执行动画版 ,比如:

  • 执行前 R0 是什么
  • 执行后 R0 里变成什么
  • PC 又是怎么变化的

先认识两个主角

1)R0 是什么

R0 是 ARM CPU 里的一个通用寄存器。
你可以先把它理解成:

CPU 手边的一个小抽屉,用来临时放数据或地址


2)PC 是什么

PC = Program Counter,程序计数器。
它表示:

CPU 下一条要执行的指令地址

你可以把 PC 理解成“代码执行的指针”。

CPU 执行代码时,本质上就是:

  • 看 PC 指向哪里
  • 执行那条指令
  • 然后修改 PC
  • 再去执行下一条

整个执行过程,像动画一样串起来


起点:已经进入Reset_Handler

此时:

  • MSP 已经装好了
  • PC 来到Reset_Handler
  • CPU准备执行复位处理逻辑

动作 1
LDR R0, =SystemInit

结果:

  • R0 =SystemInit的地址
  • PC 指向下一条

动作 2
BLX R0

结果:

  • PC 跳到SystemInit
  • 开始执行SystemInit()
  • SystemInit()执行完后返回

动作 3

返回后继续执行:

LDR R0, =__iar_program_start

结果:

  • R0 =__iar_program_start的地址
  • PC 指向下一条

动作 4
BX R0

结果:

  • PC 直接跳到__iar_program_start
  • 后续由 IAR 运行库接管
  • 再往后才会到main()

为什么汇编非得这么绕,不直接写函数名调用?

因为汇编就是更底层,它本质上就是在显式地告诉 CPU:

  • 地址放到哪个寄存器
  • 再根据寄存器跳过去

它不帮你隐藏细节。

而 C/C++ 编译器会替你把这些底层动作都生成掉,所以你平时看不到。


最小表格

指令R0 变化PC 变化作用
LDR R0, =SystemInitR0 =SystemInit地址PC 到下一条准备调用SystemInit
BLX R0R0 不一定变PC 跳到SystemInit调用SystemInit()
LDR R0, =__iar_program_startR0 =__iar_program_start地址PC 到下一条准备跳到运行库入口
BX R0R0 不一定变PC 跳到__iar_program_start转交控制权

http://www.jsqmd.com/news/527797/

相关文章:

  • 用Dobot机械臂+Python+OpenCV打造你的AI画家:从拍照到素描全流程解析
  • Redis 缓存一致性方案设计思路
  • 编译原理实验避坑指南:算符优先分析法Java实现中的5个常见错误与调试方法
  • OFA视觉问答(VQA)一文详解:ModelScope模型本地化部署实践
  • 优优推联系方式查询指南:如何通过官方渠道获取服务信息并理解其数字营销业务 - 十大品牌推荐
  • 如何在不同业务场景下理解和拆解核心指标
  • 优优推联系方式查询:了解其数字营销服务组合与选择合作方时的通用考量指南 - 十大品牌推荐
  • 多模态排序从入门到精通:通义千问3-VL-Reranker-8B完整使用教程
  • HAL+Cubemx+RTC实时时钟(掉电不丢失)
  • 谈谈定时任务实战问题及解决方案、实现原理
  • HoRain云--SVN生命周期全解析:从创建到消亡
  • 程序员内功心法:一篇讲透数据结构,从底层逻辑到高级应用
  • T5403气压传感器I²C驱动开发与嵌入式工程实践
  • Hunyuan-OCR-WEBUI案例展示:多语言混合文档的精准识别效果
  • IDEA 2022 Services窗口不显示端口?3种方法实测对比(附Spring Boot项目配置模板)
  • 照着用就行:毕业论文全流程神器——千笔·降AIGC助手
  • PatchTST:以“词”为基,Transformer如何重塑长时序预测新范式
  • 【MCP 2.0安全接入黄金法则】:20年协议安全专家亲授3步极速合规上线(含国密SM4/SM2实测基准)
  • 快速部署次元画室:基于Qwen3-32B的动漫角色设计终端,开箱即用
  • 如何安全解锁华为设备Bootloader:面向普通用户的完整指南
  • Realistic Vision V5.1 虚拟摄影棚:基于Skills智能体的自动化工作流构建
  • 终极游戏模组管理方案:XXMI启动器让你的游戏体验提升90%
  • C++的std--ranges开销路径
  • 魔兽争霸III现代系统适配与性能优化实战指南
  • 3大核心功能解析:从零开始掌握Ryujinx模拟器实战技巧
  • E2E自驾规控30讲:导论
  • 深度学习环境一键搞定:预装PyTorch1.13,支持模型训练验证
  • 别再只会用FFT了!用MATLAB玩转信号功率谱分析:从周期图到Welch法的保姆级实战
  • 大麦抢票自动化工具:高效抢票与配置指南
  • Qwen2.5-32B-Instruct保姆级教程:Ubuntu20.04环境部署