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

汇编程序调用 C 程序详解

文章目录

1. ATPCS 规则

2. 汇编和C程序传递参数

汇编程序向 C 程序的函数传递参数

C 程序返回结果给汇编程序

代码示例

3. C 函数使用栈

4. C 语言中读写寄存器


在嵌入式开发中,经常需要在 C 程序和 ARM 汇编程序之间进行相互调用。为了保证这些调用的正确性和兼容性,ARM 提出了 ATPCS(ARM-Thumb Procedure Call Standard)规范。该规范定义了函数调用时的基本规则和寄存器使用约定。

1. ATPCS 规则

ATPCS 是 ARM 和 THUMB 指令集过程序调用的规范,它规定了函数调用时如何传递参数、如何获取参数以及如何返回值。

寄存器使用规则:

  • 在函数中,通过寄存器 R0-R3 传递参数,被调用的函数在返回前无须恢复寄存器 R0-R3 的内容。
  • 在函数中,通过寄存器 R4~R11 保存局部变量。
  • 寄存器 R12 用作函数间的 scratch 寄存器,即临时寄存器。
  • 寄存器 R13 用作堆栈指针,即 SP(Stack Pointer),在函数中寄存器 R13 不能用于其他用途。寄存器 SP 在进入函数时的值和退出函数时的值必须相等。
  • 寄存器 R14 用于存放返回地址,即 LR(Link Register),它用于存放调用函数的返回地址。函数返回时,CPU 会跳转到 LR 指向的地址继续执行调用函数。
  • 寄存器 R15 是程序计数器,即 PC(Program Counter),用于指向当前指令的地址,指令执行时自动递增。

示例代码

假设一个 C 函数 add,它接收两个整数参数并返回它们的和:

// add.c
int add(int a, int b) {return a + b;
}

汇编代码示例:

.global main.extern add  // 声明外部函数 addmain:MOV R0, #5       // 第一个参数 a = 5MOV R1, #10      // 第二个参数 b = 10BL add           // 调用 C 函数 addMOV R7, #1       // syscall: exitSWI 0            // 软件中断,退出程序
  • .global main:声明 main 函数为全局符号,以便链接器能够识别和连接它。
  • .extern add:声明外部符号 add,表明 add 函数是在外部文件中定义的。
  • MOV R0, #5MOV R1, #10:将值 5 和 10 分别加载到寄存器 R0 和 R1 中,这两个寄存器用于传递参数 a 和 b。
  • BL add:调用 C 函数 add。BL 指令(Branch with Link)会将当前 PC 值存储到 LR 中,并跳转到 add 函数的地址。
  • MOV R7, #1SWI 0:用于执行软件中断,退出程序。

函数调用前准备:

  • 在调用 add 函数之前,需要将参数准备好。根据 ATPCS 规则,前四个参数依次存放在 R0、R1、R2 和 R3 寄存器中。

函数调用:

  • 使用 BL add 指令调用 add 函数。BL 指令会保存返回地址到 LR 寄存器,并跳转到 add 函数的入口地址。

函数返回:

  • add 函数执行完毕后,返回值存放在 R0 寄存器中,CPU 会从 LR 寄存器中读取返回地址并跳转回调用函数继续执行。

2. 汇编和C程序传递参数

汇编程序向 C 程序的函数传递参数

在汇编程序中调用 C 函数时,通常需要传递参数给 C 函数。ATPCS 规范规定了如何传递参数的方法:

  • 参数少于等于 4 个时:
    • 使用寄存器 R0 至 R3 来进行参数传递。第一个参数传递到 R0,第二个参数传递到 R1,依此类推。如果只有一个参数,它将存储在 R0 中;如果有两个参数,第一个参数在 R0 中,第二个参数在 R1 中,以此类推,直到 R3。
  • 参数大于 4 个时:
    • 前四个参数按照上述方法传递。剩余的参数通过堆栈传递。调用函数时,调用者需要将这些多余参数压入堆栈。入栈的顺序是从最后一个参数开始,即最后一个参数先入栈。

C 程序返回结果给汇编程序

C 函数执行完毕后,需要将结果返回给调用它的汇编程序。返回值的传递方法也有一定的规范:

  • 结果为一个 32 位的整数时:

    • 通过寄存器 R0 返回。函数的返回值会存储在 R0 寄存器中。
  • 结果为一个 64 位整数时:

    • 通过寄存器 R0 和 R1 返回。64 位整数的低 32 位存储在 R0 中,高 32 位存储在 R1 中。
  • 结果为一个浮点数时:

    • 通过浮点寄存器 f0、d0 或 s0 返回。浮点数结果存储在浮点寄存器中。
  • 结果为一个复杂的浮点数时:

    • 通过寄存器 f0fN 或者 d0dN 返回。复杂浮点数的每一部分可能存储在不同的浮点寄存器中。
  • 对于传递更多位数的结果:

    • 通过调用者的不同寄存器来传递,具体依据实际情况和编译器的规定。

代码示例

假设一个 C 函数 multiply,它接收两个整数参数并返回它们的乘积:

// multiply.c
int multiply(int x, int y) {return x * y;
}

在汇编程序中调用这个 C 函数并传递参数 67

.global main
.extern multiply  // 声明外部函数 multiplymain:MOV R0, #6       // 第一个参数 x = 6MOV R1, #7       // 第二个参数 y = 7BL multiply      // 调用 C 函数 multiplyMOV R7, #1       // syscall: exitSWI 0            // 软件中断,退出程序
  • MOV R0, #6MOV R1, #7:将值 6 和 7 分别加载到寄存器 R0 和 R1 中。这两个寄存器用于传递参数 x 和 y。
  • BL multiply:调用 C 函数 multiply。BL 指令会将当前 PC 值存储到 LR 中,并跳转到 multiply 函数的地址。
  • MOV R7, #1SWI 0:用于执行软件中断,退出程序。

调用完 multiply 函数后,乘积结果会存储在 R0 中。通过这种方式,可以在汇编程序和 C 程序之间进行参数传递和结果返回。

3. C 函数使用栈

在 C 程序和 ARM 汇编程序的函数调用过程中,使用栈(stack)是非常重要的一部分。栈的主要作用有两个:保存现场/上下文,传递参数。

保存现场/上下文

  1. 保存现场: 在程序执行过程中,当发生函数调用时,需要暂时保存当前的执行现场,以便函数执行完毕后可以恢复到调用前的状态。这个保存的过程通常称为“保存现场”。

    例如,当 CPU 运行到某些寄存器时(如 R0~R3,LR 等),这些寄存器中可能存有重要的数据。如果直接跳转到函数去执行,而不保存这些寄存器的数据,函数执行过程中对这些寄存器的操作就会破坏原有的数据。因此,需要先将这些寄存器的数据暂时存放到栈中。

  2. 保存上下文: 保存上下文的过程和保存现场类似。上下文指的是当前程序执行的状态,包括寄存器内容、程序计数器等信息。当函数调用发生时,需要将这些上下文信息保存到栈中,以便函数执行完毕后能够准确地恢复到调用前的状态。

    因此,在函数调用之前,应该将这些寄存器的数据临时保存到栈中,等待函数执行完毕返回后,再恢复现场。这样 CPU 就可以正确地继续执行后续的指令。

传递参数

  1. 传递参数: 当参数数量大于 4 个时(不包括第 4 个参数),第 4 个参数后的参数就保存在栈中。

    传递参数的过程通常如下:

    • 当被调用函数的参数超过 4 个时,前四个参数通过寄存器 R0~R3 传递,剩余的参数依次压入栈中。
    • 在函数调用过程中,调用者会将这些参数按顺序压入栈中,函数被调用时通过从栈中读取这些参数来进行处理。

代码示例

假设一个 C 函数 func,它接收五个整数参数并返回它们的和:

// func.c
int func(int a, int b, int c, int d, int e) {return a + b + c + d + e;
}

在汇编程序中调用这个 C 函数并传递参数 1, 2, 3, 4, 5

.global main
.extern func  // 声明外部函数 funcmain:MOV R0, #1       // 第一个参数 a = 1MOV R1, #2       // 第二个参数 b = 2MOV R2, #3       // 第三个参数 c = 3MOV R3, #4       // 第四个参数 d = 4PUSH {R4}        // 保存现场,R4 用于传递第 5 个参数MOV R4, #5       // 第五个参数 e = 5BL func          // 调用 C 函数 funcPOP {R4}         // 恢复现场MOV R7, #1       // syscall: exitSWI 0            // 软件中断,退出程序
  • MOV R0, #1MOV R3, #4:将前四个参数加载到寄存器 R0~R3 中。
  • PUSH {R4}:将寄存器 R4 的内容压入栈中,以便在使用 R4 传递第 5 个参数时,不破坏原有的内容。
  • MOV R4, #5:将第 5 个参数加载到寄存器 R4 中。
  • BL func:调用 C 函数 func。BL 指令会将当前 PC 值存储到 LR 中,并跳转到 func 函数的地址。
  • POP {R4}:从栈中恢复寄存器 R4 的内容,恢复现场。
  • MOV R7, #1SWI 0:用于执行软件中断,退出程序。

通过使用栈,可以确保在函数调用过程中,参数能够正确传递,并且在函数调用完毕后,能够正确恢复程序的执行现场,从而保证程序的正常运行。

4. C 语言中读写寄存器

在嵌入式系统开发中,经常需要访问芯片上某些特定模块的寄存器。这些寄存器并不位于 CPU 内部,而是在芯片的外部模块中。我们通过指针访问这些寄存器的方式与访问普通内存一样。

寄存器地址与指针

每个寄存器都有一个固定的地址。通过定义一个指向这个地址的指针,可以对寄存器进行读写操作。

例如,假设有一个寄存器 CCM_CCGR1,它的地址为 0x20C406C,我们可以定义一个指向该地址的指针,并使用 volatile 关键字告知编译器该寄存器的值可能会被外部硬件或其他程序修改,不应进行优化。

示例代码

volatile unsigned int *CCM_CCGR1 = (volatile unsigned int *)(0x20C406C);

这里的 volatile unsigned int * 表示一个指向 unsigned int 类型的指针,并且这个指针指向的数据是 volatile 类型,即数据可能会被外部因素修改。

读写寄存器

通过这个指针,我们可以进行读写操作:

  • 读寄存器
val = *CCM_CCGR1;

这行代码将读取 CCM_CCGR1 寄存器的值,并存储到变量 val 中。

  • 写寄存器
*CCM_CCGR1 |= (3 << 30);
  • *CCM_CCGR1 通过指针访问寄存器 CCM_CCGR1
  • |= (3 << 30) 是一个位操作,将 3 左移 30 位,得到一个值,这个值在二进制中表示为 11(即第 31 位和第 30 位都为 1),然后将这个值与 CCM_CCGR1 的当前值进行按位或操作,结果将这两位设置为 1。

通过上述方法可以在 C 语言中方便地对寄存器进行读写操作,确保对硬件寄存器的正确访问和修改。这种方法广泛应用于嵌入式系统开发中,特别是在处理器需要与外部设备进行通信或控制时。

相关文章:

汇编程序调用 C 程序详解

文章目录 1. ATPCS 规则 2. 汇编和C程序传递参数 汇编程序向 C 程序的函数传递参数 C 程序返回结果给汇编程序 代码示例 3. C 函数使用栈 4. C 语言中读写寄存器 在嵌入式开发中&#xff0c;经常需要在 C 程序和 ARM 汇编程序之间进行相互调用。为了保证这些调用的正确性…...

代码随想三刷图论篇1

代码随想三刷图论篇1 98. 所有可达路径题目代码99. 岛屿数量题目代码100. 岛屿的最大面积题目代码101. 孤岛的总面积题目代码102. 沉没孤岛题目代码103. 水流问题题目代码98. 所有可达路径 题目 链接 代码 import java.util.*;class Main{public static void main(String […...

Windows 快捷键汇总

Windows 快捷键汇总 前言进阶快捷键【最好用】Chrome 常用快捷键【跟 Windows 快捷键不搭杆&#xff0c;但常用】基础快捷键扩展快捷键 前言 Coder 苦鼠标久已&#xff0c;整理汇总 Windows 快捷键包括一些常用的快捷键&#xff0c;比如“浏览器”相关的快捷键内容分为四小节&…...

微服务有哪些组件?

1.注册中心&#xff1a;用于服务的注册和发现&#xff0c;管理微服务的地址 Nacos&#xff0c;Eureka 2.配置中心&#xff1a;集中管理微服务的配置中心 Nacos config 3.远程调用&#xff1a;用于不同微服务间的通信和协作 RESTful API&#xff08;RestTemplate&#xff0…...

camera-qsc-crosstalk校准数据XTALK回写

问题背景 手机越做越紧凑&#xff0c;需要模组和芯片尺寸越做越小&#xff0c;在尺寸一定的基础上&#xff0c;高像素和大像素&#xff0c;对于手机摄像头来说&#xff0c;一直是一对矛盾的存在。 高像素&#xff1a;带来高分辨率画质大像素&#xff1a;带来暗态下高感光度和…...

混合贪心算法求解地铁线路调度

一、问题描述 城市轨道交通的繁荣发展&#xff0c;带来了车辆资源需求的日益增加。如何兼顾运营服务水平和运营成本&#xff0c;以最少的车底优质地完成运输任务成为一大严峻问题。本题在后续的描述中将由多辆动车和拖车组合而成的车组称为车底。在日常的运营组织中&#xff0…...

vue项目:关闭页面,删除本地登录信息

vue项目&#xff1a;关闭页面&#xff0c;删除本地登录信息 代码 代码 import { removeToken } from /utils/auth //区分关闭和刷新页面&#xff0c;关闭时退出登录 window.onload ()>{if(!window.sessionStorage["tempFlag"]){removeToken();location.reload()…...

获奖案例回顾|基于卫星遥感和无人机的水稻全流程风险减量项目

引言 在现代农业保险领域&#xff0c;技术创新是推动行业进步的关键。珈和科技与太平财险的合作&#xff0c;旨在利用先进的卫星遥感和无人机技术&#xff0c;解决传统农业保险面临的诸多挑战&#xff0c;从而提升保险效率和服务质量。本次分享的项目案例获得了《金融电子化》…...

全栈 Discord 克隆:Next.js 13、React、Socket.io、Prisma、Tailwind、MySQL笔记(一)

前言 阅读本文你需要有 Next.js 基础 React 基础 Prisma 基础 tailwind 基础 MySql基础 准备工作 打开网站 https://ui.shadcn.com/docs 这不是一个组件库。它是可重用组件的集合&#xff0c;您可以将其复制并粘贴到应用中。 打开installation 选择Next.js 也就是此页面…...

【Unity】制作简易计时器

一、创建计时器相关的变量 我们需要创建三个变量&#xff0c;分别是&#xff1a;计时时长、计时剩余时长、是否处于计时状态。 public float duration;//计时时长 public float remain; //计时剩余时长 public bool isCount; //是否处于计时状态 二、初始化变量 我们可以直…...

TDesign组件库日常应用的一些注意事项

【前言】Element&#xff08;饿了么开源组件库&#xff09;在国内使用的普及率和覆盖率高于TDesign-vue&#xff08;腾讯开源组件库&#xff09;&#xff0c;这也导致日常开发遇到组件使用上的疑惑时&#xff0c;网上几乎搜索不到其文章解决方案&#xff0c;只能深挖官方文档或…...

51单片机7(点亮第一个LED)

一、LED简介 1、LED&#xff0c;它是一个发光二极管&#xff0c;它具有单向导电性&#xff0c;那么通过5毫安的一个电流&#xff0c;就可以使它发光&#xff0c;那么电流越大&#xff0c;它的发光也就越强&#xff0c;但是电流不能过大&#xff0c;过大会把这个发光二极管给烧…...

基于Vue和UCharts的前端组件化开发:实现高效、可维护的词云图与进度条组件

基于Vue和UCharts的前端组件化开发&#xff1a;实现高效、可维护的词云图与进度条组件 摘要 随着前端技术的迅速发展和业务场景的日益复杂&#xff0c;传统的整块应用开发方式已无法满足现代开发的需求。组件化开发作为一种有效的解决方案&#xff0c;能够将系统拆分为独立、…...

CentOS 系统监控项

在维护和优化 CentOS 系统时&#xff0c;实时监控硬件和资源的使用情况非常重要。为了满足工作需要&#xff0c;可以定时采集 CentOS 系统相关的监控数据&#xff0c;并将其推送到 Prometheus 进行集中监控和管理。以下是日常采集项及对应的 shell 命令&#xff0c;并附上每项命…...

连锁直营店小程序赋能多店如何管理

如商超便利店卖货线下场景&#xff0c;也有不少品牌以同城多店和多地开店经营为主&#xff0c;获取店铺周围客户和散流&#xff0c;如今线上重要性凸显&#xff0c;品牌电商发展是经营的重要方式之一&#xff0c;也是完善同城和外地客户随时便捷消费的方式之一。 多个门店管理…...

决策树算法入门到精通:全面解析与案例实现

1. 介绍决策树算法 决策树的基本概念和原理 决策树是一种基于树形结构的分类和回归方法&#xff0c;通过对数据集进行递归地划分&#xff0c;每个内部节点表示一个属性上的判断&#xff0c;每个叶节点代表一种类别或者数值。 决策树在机器学习中的应用场景 分类问题&#xf…...

LangChain —— 多模态大模型的 prompt template

文章目录 一、如何直接将多模态数据传输给模型二、如何使用 mutimodal prompts 一、如何直接将多模态数据传输给模型 在这里&#xff0c;我们演示了如何将多模式输入直接传递给模型。对于其他的支持多模态输入的模型提供者&#xff0c;langchain 在类中提供了内在逻辑来转化为期…...

ssh升级

文章目录 ssh升级一、解包ssh、ssl二、更新安装ssl三、手动更新手动复制库文件四、创建符号链接五、更新库路径六、验证库文件七、设置库路径环境变量八、配置、编译、安装OpenSSH&#xff1a;意外&#xff1a;缺少 zlib 的开发库解决方法&#xff1a; 九、刷新ssh服务、查看ss…...

51单片机10(蜂鸣器介绍)

一、蜂鸣器介绍&#xff1a; 1、蜂鸣器是一种一体化结构的电子讯响器&#xff0c;采用直流电压供电&#xff0c;广泛应用于电子产品中作为发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器。 &#xff08;1&#xff09;压电式蜂鸣器&#xff0c;它主要由多谐的一个增胀器…...

Python爬虫:基础爬虫架构及爬取证券之星全站行情数据!

爬虫成长之路&#xff08;一&#xff09;里我们介绍了如何爬取证券之星网站上所有A股数据&#xff0c;主要涉及网页获取和页面解析的知识。爬虫成长之路&#xff08;二&#xff09;里我们介绍了如何获取代理IP并验证&#xff0c;涉及了多线程编程和数据存储的知识。此次我们将在…...

使用VSCode开发Django指南

使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架&#xff0c;专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用&#xff0c;其中包含三个使用通用基本模板的页面。在此…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

【力扣数据库知识手册笔记】索引

索引 索引的优缺点 优点1. 通过创建唯一性索引&#xff0c;可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度&#xff08;创建索引的主要原因&#xff09;。3. 可以加速表和表之间的连接&#xff0c;实现数据的参考完整性。4. 可以在查询过程中&#xff0c;…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动&#xff08;如演唱会、马拉松赛事、高考中考等&#xff09;期间&#xff0c;城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例&#xff0c;暖城商圈曾因观众集中离场导致周边…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...