【Cortex-M3】C语言函数调用过程汇编层面分析

Cortex-M3 SoC的启动过程 介绍从C语言程序的编写到在裸机上运行的详细流程 基本概念 几种不同类型的存储器 在过去的20年里,嵌入式系统一直使用ROM(EPROM)作为它们的存储设备,然而近年来Flash全面代替了ROM(EPROM)在嵌入式系统中的地位,用作存储Bootloader以及操作系统或者程序代码或者直接当硬盘使用(U盘)。 ... 阅读详情

前言

本文通过一个代码示例,介绍函数调用过程在汇编层面是如何实现的。

正文

示例代码如下:

int add_val(int pa, int pb, int pc, int pd)
{
    volatile int tmp;
	
	tmp = pa + pb + pc + pd;

    return tmp;
}

int mymain()
{
	volatile int a = 1;
	volatile int b = 2;
	volatile int c = 3;
	volatile int d = 4;
	volatile int e;
	
	e = add_val(a, b, c, d);
	
	return e;
}

对应的汇编代码如下:
mymain函数
在这里插入图片描述

add_val函数
在这里插入图片描述
C语言代码用Keil5编写,对应的反汇编代码在编译项目时生成的.dis文件中,如何生成.dis可以百度查看。

mymain()函数首先声明了5个4字节的变量:

volatile int a = 1;
volatile int b = 2;
volatile int c = 3;
volatile int d = 4;
volatile int e;

对应的汇编代码中将SP栈指针减20,空出20个字节来存储a、b、c、d、e这5个变量。

SUB      sp,sp,#0x14

图像化如下:
请添加图片描述
从此之后,在汇编代码里,

SP+0x00表示变量e的地址
SP+0x04表示变量d的地址
SP+0x08表示变量c的地址
SP+0x0C表示变量b的地址
SP+0x10表示变量a的地址;

给变量a初始化汇编代码如下:

MOVS     r0,#1
STR      r0,[sp,#0x10]

首先给通用寄存器R0赋值为1,然后将R0的写入到SP+0x10这个地址处,也就是将变量a赋值为1。
通用寄存器R0-R12都是32位的。
其他变量初始化过程类似。

函数调用过程中传递参数是通过寄存器R0-R3传递的,mymain函数调用add_val函数传递了a、b、c、d4个变量的数值,所以在调用add_val函数之前需要将a、b、c、d赋值给R0、R1、R2、R3。
对应汇编代码如下:

LDRD     r3,r2,[sp,#4]

将SP+0x04开始4个字节赋值给R3,然后再取4个字节赋值给R2

LDRD     r1,r0,[sp,#0xc]

将SP+0x0C开始4个字节赋值给R1,然后再取4个字节赋值给R0
这个过程就将a、b、c、d赋值给了R0、R1、R2、R3
然后通过

BL       add_val ; 0x8000a5c

跳转到add_val函数开始指令,也就是将PC指针赋值为0x8000a5c。
add_val对应汇编代码如下:

PUSH     {r3,r4,lr}
MOV      r4,r0
ADDS     r0,r4,r1
ADD      r0,r0,r2
ADD      r0,r0,r3
STR      r0,[sp,#0]
LDR      r0,[sp,#0]
POP      {r3,r4,pc}

这段汇编是经过了优化,不优化代码如下:

PUSH     {r4,lr}
SUB      sp,sp,#4
MOV      r4,r0
ADDS     r0,r4,r1
ADD      r0,r0,r2
ADD      r0,r0,r3
STR      r0,[sp,#0]
LDR      r0,[sp,#0]
ADD      sp,sp,#4
POP      {r4,pc}

开始进入函数的PUSH {r4,lr} 与 退出函数的 POP {r4,pc} 相对应,LR是链接寄存器,用于保存函数调用时的返回地址,开始将LR压栈,最后将LR出栈并赋值给PC,也就完成了函数返回。
将R4压栈是因为在函数调用过程中有一个协议:

R0-R3、R12、R14(LR) 寄存器,子函数是可以随便使用的,主函数在调用子函数时要有心理准备这些寄存器的内容是可能被子函数修改的,所以如果有需要,这些寄存器的内容主函数在调用子函数之前要保护起来,所以这些寄存器被称为“调用者保护寄存器”

R4-R11寄存器,子函数要保证在进入子函数前和退出子函数后,这些寄存器的内容是不变的,所以这些寄存器被称为“被调用者保护寄存器”

因为add_val用到了R4,所以在执行开始就将R4压栈,在退出函数时对R4进行了出栈。以此来保证R4在子函数执行前后内容不变。

而优化代码将:

PUSH     {r4,lr}
SUB      sp,sp,#4

变成

PUSH     {r3,r4,lr}

是因为将寄存器R3压栈和SP-4(将SP栈指针数值减4)效果是一样的,但是压栈动作的执行效率比减法运算执行效率高。
最后在add_val函数中,将返回数值赋值给了R0,然后通过 POP {pc},将LR赋值给PC完成函数返回。
回到主函数后,主函数读取R0的内容获得子函数的返回参数,并做进一步处理。

总结

1、主函数调用子函数,传递参数是通过R0、R1、R2、R3寄存器传递的。
2、子函数通过return返回给主函数的参数,是通过R0寄存器返回的。
3、子函数被调用后,首先要执行PUSH {lr},将LR寄存器的数据保存,最后通过POP {pc}的方式取出返回地址并返回。
4、子函数中使用到“被调用者保护寄存器”R4-R11时,需要在开始执行时把使用的寄存器压栈,退出函数时再将这些寄存器出栈。
5、函数中定义的变量,汇编代码会通过移动SP的方式将存放这些变量的空间空出来,之后访问这些变量也是以SP为基准来表示他们的地址值。
6、一个函数开始运行后,新的SP与旧的SP之间的内容如下:

函数运行空间内容
第一段链接寄存器LR
第二段本函数中用到的“被调用者保护寄存器”R4-R11
第三段函数中定义的变量
ARM架构—— Cortex-M3与Cortex-M4特点概述 哈佛结构是一种将程序指令存储和数据存储分开的存储器结构。哈佛结构是一种并行体系结构,它的主要特点是将程序和数据存储在不同的存储空间中,即程序存储器和数据存储器是两个独立的存储器,每个存储器独立编址、独立访问。两个存储器相对应的是系统的4条总线,即程序的数据总线与地址总线数据的数据总线与地址总线。这种分离的程序总线和数据总线可允许在一个机器周期内同时获得指令字(来自程序存储器)和操作数(来自数据存储器),从而提高了执行速度,进而提高了数据的吞吐率。 阅读详情

相关推荐

Cortex-M系列:在C文件中插入汇编

本文旨在阐述如何在Cortex-M系列CPU的C文件中插入汇编程序。通过观察C语言生成的汇编程序,在必要时刻知道如何写更为复杂的汇编代码。通过学习ARM工具链生成的汇编程序,可实现较高效的汇编指令学习,更重要的在于知道程序有多大的优化空间。特别地,理解数组处理方式,能更深刻地理解数组越界到底会有怎样的影响;理解文本池访问的方式,能更直观地知道常量以及const关键字在汇编中是如何体现的。 本文承...

NoDistanceY的博客 1790

Cortex-M3寄存器组、汇编语言与C语言的接口介绍

学uCOS的任务切换时涉及到汇编代码。为了能理解汇编代码,我在网上了解了Cortex-M3寄存器组、C与汇编的接口的知识,在这里分享给大家。 先来介绍Cortex-M3寄存器组: Cortex-M3拥有16个通用寄存器R0-R15。 R0-R12都是32位通用寄存器,用于数据操作。 R13是堆栈指针。在CM3处理器内核中共有两个堆栈指针,于是也就支持两个堆栈。当引用R13(SP)时,你...

chenxr的博客 1675

嵌入式软件堆栈使用情况的估算方法 - 堆栈操作汇编代码分析(ARM Cortex-M处理器)

介绍了嵌入式软件堆栈使用情况的估算方法。为了方便理解这种估算方法,还对相应的堆栈操作汇编代码(ARM Cortex-M处理器)进行分析和说明

mmmx777的专栏 3618

【ARM-Cortex-M3/4】汇编基础与常用指令集

令用于产生软中断,从而实现在用户模式变换到特权模式,CPSR保存到管理模式的SPSR 中,执行转移到SWI向量,在其它模式下也可使用SWI 指令,处理同样地切换到特权模式。恢复寄存器列表, 先出栈的数据(即原先最后入栈的r0)保存到r0, 最后出栈的数据(即原先最先入栈的r14)保存到r14。即C语言extern,指示编译器当前的符号不是在本源文件中定义的,而是在其他源文件中定义的,在本源文件中可能引用该符号。symbol 要保留的局部标号(若没有该项,除了基于寄存器处的所有符号将包含在目标文件的符号表)

种一棵树最好的时间是十年前,其次是现在 2092

Cortex-M​ 函数调用的入栈与出栈操作

这种分工平衡了性能和灵活性:硬件确保关键操作的高效性,而软件通过编译器实现复杂的上下文管理。开发者需理解这一机制,以编写高效且稳定的代码(尤其是在混合使用 C 和汇编时)。指令调用函数时,**硬件会自动将返回地址(即下一条指令的地址)保存到。,而硬件仅负责部分关键操作。(Branch with Link)或。(Link Register)**中。

创新改变世界! 811

Cortex-M 上编写汇编函数

手动管理寄存器和栈,并注意与 C 语言的交互。清晰的代码结构和注释(尤其是栈操作和寄存器使用)能显著降低调试难度。建议先通过 C 代码生成反汇编,参考编译器生成的代码风格。系列单片机中使用汇编语言编写函数时,需要特别注意寄存器的使用、栈管理、调用约定以及与 C 语言的兼容性。在 Cortex-M 上编写汇编函数时,需严格遵循。

创新改变世界! 767

[004] [ARM-Cortex-M3/4] 汇编基础与常用指令集

```assembly 标号 操作码{cond}{S} 操作数1, 操作数2, … ;注释 ``` - `标号`:可选,**必须顶格**写,作用是让汇编器计算程序转移的地址(可以是C函数名) - `cond`:可选,即指令执行条件,如果不写则使用默认条件`AL`(无条件执行)。 - `S`:可选,表示指令执行后,会修改PSR寄存器 - `操作码`:即**指令助记符**,前面**必须至少有一个空格** - `操作数`:第1个操作数一般为本指令的执行结果存储处。形成操作数的有效地址的方法称为操作数的寻址

柯西的彷徨的博客 8219

《ARM Cortex-M3与Cortex-M4权威指南》

1.1 什么是ARM Cortex-M处理器1.1.1 Cortex-M3和Cortex-M4处理器1.1.2 Cortex-M处理器家族1.1.3 处理器和微控制器的区别1.1.4 ARM和微控制器供应商1.1.5 选择Cortex-M3和Cortex-M4微控制器1.2 Cortex-M处理器的优势1.2.1 低功耗1.2.2 性能1.2.3 能耗效率1.2.4 代码密度1.2.5 中断1.2.6 易于使用1.2.7 可扩展性1.2.8 调试特性。

qq_40844444的博客 2059

【Cortex-M3】中断处理时栈空间操作过程分析

中断发生后,压栈行为分两步:第一步:处理器自动压栈“调用者保护寄存器”,顺序为xPSRLRR12R3R2R1R0。第二步:中断服务函数首先压栈LR(这时的LR已经和进入中断前的LR不一样了),然后压栈一些用到的“被调用者保护寄存器”R4-R11。

inputA的博客 2670

Cortex M3汇编程序示例

Cortex M3汇编程序示例,在LPC1768上调试通过,附有详细的备注说明,可作为想深入了解Cortex M3芯片和ARM汇编的入门学习资料。

Agent 团队管理手册:如何设定目标、分配角色与解决冲突

本文从Agent团队的底层逻辑出发,完整讲解了Agent团队管理的三大核心模块:目标设定(分层拆解+对齐校验)、角色分配(能力匹配+最优算法)、冲突处理(分类处理+仲裁投票),并提供了可直接运行的完整代码示例。通过本文的方法,你可以快速搭建一个高效协作的Agent团队,大幅提升复杂任务的完成效率和准确率。Agent团队是未来AI应用的核心形态,现在正是落地的黄金期,建议你从一个小场景开始动手实践,逐步优化自己的Agent团队管理方法论。

AI天才研究院 234

ARM中C语言和汇编语言的混合编程

1.内联汇编 在C语言中内嵌汇编注意事项: 不能直接向PC寄存器赋值,程序跳转要使用B或者BL指令 在使用物理寄存器时,不要使用过于复杂的C表达式,避免物理寄存器冲突 R12和R13可能被编译器用来存放中间编译结果,计算表达式值时可能将R0到R3、R12及R14用于子程序调用,因此要避免直接使用这些物理寄存器 一般不要直接指定物理寄存器,而让编译器进行分配 示例程序:

andrewbytecoder 9054

【无人机智能避障】基于深度强化学习的城市复杂环境路径规划实战

本文探讨了基于深度强化学习的无人机在城市复杂环境中的智能避障与路径规划。针对传统算法难以应对动态与非结构化障碍的痛点,文章详细介绍了如何利用DQN算法,通过设计合理的状态空间、奖励函数,并结合经验回放、目标网络等技巧,训练无人机自主学会在楼宇间安全、高效飞行的策略。

weixin_28224217的博客 734

ARM Cortex-M3从汇编到C,从Boot到应用的教程

* 本仓库计划实现的内容: * 描述Cortex-M3的指令集和通用寄存器。 * 针对M3内核,使用汇编从复位开始写boot引导C语言main()函数。 * 不使用任何芯片厂商提供的开发包,自己写Boot,自行移植C语言库函数,自己写所有驱动和应用。 * 移植一款RTOS操作系统。

qq582880551的博客 1561

GB28181协议实战:5分钟搞定海康摄像头SIP注册失败问题(附Wireshark抓包分析)

本文详细解析了海康摄像头在GB28181协议下SIP注册失败的7个关键原因及解决方案,包括网络可达性、鉴权参数、NAT穿越等核心问题。通过Wireshark抓包分析,提供快速定位方法,帮助工程师高效解决SIP注册失败问题,提升实时图像联调效率。

weixin_29212867的博客 553

从芯片选型到光学集成:手持激光测距仪硬件设计全解析

本文全面解析手持激光测距仪的硬件设计,从芯片选型到光学集成,详细介绍了相位法测距技术的核心原理与方案选择。通过模块化设计、信号链路优化及PCB布局技巧,实现毫米级精度。文章还分享了量产化关键控制点,帮助工程师规避常见设计陷阱,提升产品可靠性。

weixin_30425949的博客 445

[007] [ARM-Cortex-M3/4] C与汇编深入分析

[ARM-Cortex-M3/4] ATPCS子程序调用规则](https://blog.youkuaiyun.com/kouxi1/article/details/123039106?spm=1001.2014.3001.5502)规定了函数调用过程中参数的传递、局部变量的保存、数据栈的使用等规则。 | 寄存器 | 别名 | 特殊名称 | 使用规则 | | ------ | ---- | -------- | -

柯西的彷徨的博客 1788

3步搞定.NET Core在UOS上的部署?手把手教你从零到上线!

本文详细介绍了如何在统信UOS系统上部署.NET Core应用程序的完整流程。首先,通过安装.NET Core SDK,确保UOS系统能够编译和运行C#代码。接着,创建一个简单的控制台应用,并验证其本地运行情况。最后,通过发布应用、传输文件到UOS服务器,并解决权限问题,确保应用能够在UOS上顺利运行。文章还提供了常见问题的解决方案,如依赖缺失、权限不足和服务启动失败等,帮助开发者避免常见陷阱。通过本文的指导,开发者可以轻松将.NET Core应用部署到UOS系统,并实现24/7的稳定运行。

java专栏 587

C语言调用子函数时入/出栈(保护/恢复现场)全过程分析:以Cortex-M3为例

使用C语言调用子函数是如何保护/恢复现场的呢?本文以Cortex-M3为例,逐行汇编代码分析C语言调用子函数时入/出栈(保护/恢复现场)全过程。

kevin1499的博客 2183

HX711重量传感器原理图设计,已量产(压力传感器)

最近调通了一款针对家用、物联网场景的重量传感套件,核心用了 HX711 这颗经典的称重 AD 芯片,还加了用户最需要的 “一键校准” 功能 —— 之前帮朋友做家用米桶余量报警时,用户总吐槽校准步骤太繁琐,这次的方案把操作简化到只按一个键,试下来体验好了不少。正好拆解下这套硬件的设计逻辑,供做同类产品的朋友参考。

不脱发的程序猿 368

V90伺服 EPOS模式下回原(详细配置+SCL源代码)

/ 输出上限 100.0%rSetPoint : Real;// 设置目标值工程量。// 通道转换参考值。Td : Real;

RXXW_Dor的博客 2665

C语言函数不同个数、大小形参对执行速度的影响:以Cortex-M3为例从汇编角度分析原因

C语言函数不同个数、大小形参对执行速度的影响:以Cortex-M3为例从汇编角度分析原因。

kevin1499的博客 1292

华为OD机考双机位A卷 - 流浪地球(Java & Python& JS & C/C++ & GO )

华为OD机考双机位A卷 - 流浪地球(OD上机考试双机位A卷)。提供在线OJ刷题。Java、C++、Python、GO语言、C语言、JavaScript题解,并提供完整思路讲解。

算法大师! 4041

[001-Cortex_M3-GCC汇编] 第一个点灯程序

背景使用Cortex-M3已经有一段时间了,大大小小也做过几个项目,可以说对这个系列的片子是有一定的了解。相对于以前的8位单片机来说,其存储空间,外设种类都有较大的提升,这对于产品的应用显得非常便利。最近,因为项目上的原因做了一些汇编工作,个人觉得如果想把Cortex-M3系列的芯片用好,汇编方面的理论知识还是需要学习和补充的,因此就有了写这一系列文档的动力,权当总结一下汇编的基础知识和实用样例。

p_fly的博客 2587

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

ARM汇编概述:Cortex-M3/M4实战指南

前索引([Rn, #4]!后索引([Rn], #4):地址 = Rn,然后更新Rn = Rn + 4。Cortex-M3/M4架构遵循Load-Store原则,即数据处理指令只操作寄存器,与内存的数据交换必须通过LDR(从内存加载数据到寄存器)和STR(将寄存器数据存储到内存)指令完成。{cond}:可选,条件执行后缀(如MOVNE R0, #0xFF表示"若Z=0(前序运算结果非0),则执行")。操作对象是寄存器:核心寄存器仅需掌握R0-R7(数据)、SP/LR/PC(控制)和APSR(条件标志)。

2501_94606652的博客 885

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

《ARM Cortex - M3与Cortex - M4权威指南》第20章 汇编和混合语言工程

multiply.s。

qq_40844444的博客 1017

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

上一篇: 【UCOSIII源码阅读笔记】第二篇
下一篇: 【Cortex-M3】中断处理时栈空间操作过程分析
inputA
博客等级 码龄7年 297粉丝 44原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值