C/C++与汇编混合编程有什么好处?

点击蓝字

7a35ec16b65f1aa38f962288b58ab390.png

关注我们

1 导语 

当需要C/C++与汇编混合编程时,可以有以下两种处理策略:

若汇编代码较短,则可在C/C++源文件中直接内嵌汇编语言实现混合编程。

若汇编代码较长,可以单独写成汇编文件,最后以汇编文件的形式加入项目中,通过ATPCS规定与C程序相互调用及访问。

2 内嵌汇编语言指令

用C/C++程序嵌入汇编程序中可以实现一些高级语言没有的功能,提高程序执行效率。armcc编译器的内嵌汇编器支持ARM指令集,tcc编译器的内嵌汇编器支持Thumb指令集。

2.1 内嵌汇编指令的语法格式

在ARM的C语言程序中可以使用关键字__asm来加入一段汇编语言的程序,格式如下:

[cpp] view plain copy

1. __asm  

2. {  

3.     指令 [;指令]      /* comments */  

4.     ...  

5.   

6.     指令  

7. }  

其中,{ }中的指令都为汇编指令,一行允许写多条汇编指令语句,指令语句之间要用分号隔开。在汇编指令段中,注释语句采用C语言的注释格式。ARM C++程序中除了可以使用关键字__asm来标识一段内嵌汇编指令程序外,还可以使用关键词asm来表示一段内嵌汇编指令。

格式如下:asm ("指令");

其中,asm后面的括号中必须是一条汇编指令语句,并且不能包含注释语句。

2.2 使能/禁止IRQ中断实例

[cpp] view plain copy

1. void enable_IRQ(void) //使能中断程序  

2. {  

3.     int tmp;              //定义临时变量,后面使用  

4.     __asm                 //内嵌汇编程序的关键词  

5.     {  

6.         MRS tmp, CPSR     //把状态寄存器加载给tmp  

7.         BIC tmp, tmp, #80 //将IRQ控制位清0  

8.         MSR CPSR_c, tmp   //加载程序状态寄存器  

9.     }  

10. }  

11.   

12. void disable_IRQ(void) //禁止中断程序  

13. {  

14.     int tmp;              //定义临时变量,后面使用  

15.     __asm                 //内嵌汇编程序的关键词  

16.     {  

17.         MRS tmp, CPSR     //把状态寄存器加载给tmp  

18.         ORR tmp, tmp, #80 //将IRQ控制位置1  

19.         MSR CPSR_c, tmp   //加载程序状态寄存器  

20.     }  

21. }  

2.3 内嵌汇编注意事项      

后缀.S文件中的汇编指令是用armasm汇编器进行汇编的,而C语言程序中的内嵌汇编指令则是用内嵌汇编器进行汇编的。这两种汇编器存在一定的差异,所以在内嵌汇编时要注意以下几点。

2.3.1 小心使用物理寄存器

必须小心使用物理寄存器,如R0~R3、IP(R12)、LR(R14)和CPSR中的N、Z、C、V标志位。因为计算汇编代码中的C表达式时,可能使用这些物理寄存器,并会修改N、Z、C、V标志位。

如计算:

y=x+x/y;

[cpp] view plain copy

1. __asm  

2. {  

3.     MOV R0, x         //把x的值给R0  

4.     ADD y, R0, x/y    //计算x/y时R0的值会被修改  

5. }  

2.3.2 内嵌汇编程序中允许使用C变量        

在计算x/y时R0会被修改,从而影响R0+x/y的结果。内嵌汇编程序中允许使用C变量,用C变量来代替寄存器R0可以解决上述问题。这时内嵌汇编器将会为变量var分配合适的存储单元,从而避免冲突的发生。如果内嵌汇编器不能分配合适的存储单元,它将会报告错误。

[cpp] view plain copy

1. int var;  

2. __asm  

3. {  

4.     MOV var, x      //把x的值给R0  

5.     ADD y, var, x/y //计算x/y时R0的值会被修改  

6. }  

2.3.3 不需要保存和恢复用到的寄存器

对于在内嵌汇编语言程序中用到的寄存器,编译器在编译时会自动保存和恢复这些寄存器,用户不用保存和恢复这些寄存器。除了CPSR和SPSR寄存器外,其他物理寄存器在读之前必须先赋值,否则编译器会报错。

[cpp] view plain copy

1. int fun (int x)  

2. {  

3.     __asm  

4.     {  

5.         STMFD SP!, {R0}   //保存R0,先读后写,汇编出错  

6.         ADD R0, x, #1  

7.         EOR x, R0, x  

8.         LDMFD SP!, {R0}   //多余的  

9.     }  

10.     return x;  

11. }  

3. 汇编与C/C++程序的变量相互访问

3.1 汇编程序访问C/C++程序变量

在C/C++程序中声明的全局变量可以被汇编程序通过地址间接访问。具体访问方法/步骤如下:

     1) 在C/C++程序中声明全局变量。

     2) 在汇编程序中使用IMPORT/EXTERN伪指令声明引用该全局变量。

     3) 使用LDR伪指令读取该全局变量的内存地址。

     4) 根据该数据的类型,使用相应的LDR指令读取该全局变量;使用相应的STR指令存储该全局变量的值。对于不同类型的变量,需要采用不同选项的LDR和STR指令,如下表所示。

对于结构,如果知道各个数据项的偏移量,可以通过存储/加载指令访问。如果结构所占空间小于8个字,可以使用LDM和STM一次性读写。

读取C的一个全局变量,并进行修改,然后保存新的值到全局变量中:

[cpp] view plain copy

1. AREA Example4, CODE, READONLY  

2.      EXPORT AsmAdd  

3.      IMPORT g_cVal      @声明外部变量g_cVal,在C中定义的全局变量  

4. Add  

5.      LDR R1, =g_cVal    @装载变量地址  

6.      LDR R0, [R1]       @从地址中读取数据到R0  

7.      ADD R0, R0, #1     @加1操作  

8.      STR R0, [R1]       @保存变量值  

9.      MOV PC, LR         @程序返回  

10. END  

3.2 C/C++程序访问汇编程序数据

在汇编程序中声明的数据可以被C/C++程序所访问。具体访问方法/步骤如下:

     1) 在汇编程序中用EXPORT/GLOBAL伪指令声明该符号为全局标号,可以被其他文件应用。

     2) C/C++程序中定义相应数据类型的指针变量。

     3) 对该指针变量赋值为汇编程序中的全局标号,利用该指针访问汇编程序中的数据。

假设在汇编程序中定义了一块内存区域,并保存一串字符,汇编代码如下: 

[cpp] view plain copy

1. EXPORT Message        @声明全局标号  

2. Message DCB "HELLO$"  @定义了5个有效字符,$为结束符

[cpp] view plain copy

1. extern char* Message;  

2. int MessageLength()  

3. {  

4.     int Length = 0;  

5.     char *pMessage;         //定义字符指针变量  

6.     pMessage = Message;     //指针指向Message 内存块的首地址  

7.       

8.     /*while循环,统计字符串的长度*/  

9.     while(*pMessage != '$') //$为字符串的结束符  

10.     {  

11.         Length++;  

12.         pMessage++;  

13.     }  

14.     return(Length); //返回字符串的长度  

15. }  

4. 汇编与C/C++程序的函数相互调用

C/C++程序和ARM汇编程序之间相互调用必须遵守ATPCS(ARM/Thumb Procedure Call Standard)规则。使用ADS的C语言编译器编译的C语言子程序会自动满足用户指定的ATPCS类型。而对于汇编语言来说,完全要依赖用户来保证各个子程序满足选定的ATPCS类型。具体来说,汇编程序必须满足以下3个条件才能实现与C语言的相互调用:

1) 在子程序编写时必须遵守相应的ATPCS规则。

2) 堆栈的使用要遵守相应的ATPCS规则。

3) 在汇编编译器中使用-atpcs选项。

4.1 ATPCS基本规则

ATPCS基本规则见ATPCS。

4.2 C程序调用汇编程序

汇编程序的设置要遵循ATPCS规则,保证程序调用时参数的正确传递,在这种情况下,C程序可以调用汇编子函数。C程序调用汇编程序的方法如下:

1) 汇编程序中使用EXPORT伪指令声明本子程序可外部使用,使其他程序可调用该子程序。

2) 在C语言程序中使用extern关键字声明外部函数(声明要调用的汇编子程序),才可调用此汇编的子程序。

[cpp] view plain copy

1. #include <stdio.h>  

2. extern void strcopy(char *d, const char *s); 

//声明外部函数,即要调用的汇编子程序  

3. int main(void)  

4. {  

5.     const char *srcstr = "First ource";          //定义字符串常量  

6.     char dststr[] = "Second string-destination"; //定义字符串变量  

7.     printf("Before copying:\n");  

8.     printf("src=%s, dst=%s\n", srcstr, dststr);  

//显示源字符串和目标字符串的内容  

9.     strcopy(dststr, srcstr);  //调用汇编子程序R0=dststr, R1=srcstr  

10.     printf("After copying:\n");  

11.     printf("src=%s, dst=%s\n", srcstr, dststr);  //显示复制后的结果  

12.     return(0);  

13. }  

strcopy实现代码如下:

[cpp] view plain copy

1.       AREA Example, CODE, READONLY @声明代码段Example  

2.       EXPORT strcopy      @声明strcopy,以便外部函数调用  

3.   

4. strcopy     @ R0为目标字符串的地址, R1为源字符串的地址  

5.   

6.       LDRB R2, [R1], #1    @读取字节数据,源地址加1  

7.       STRB R2, [R0], #1    @保存读取的1字节数据,目标地址加1  

8.       CMP R2, #0           @判断字符是否复制完毕  

9.       BNE strcopy          @没有复制完,继续循环复制  

10.     MOV PC, LR   

4.3 汇编程序调用C程序

汇编程序设置要遵循APTCS规则,保证程序调用时参数的正确传递。汇编程序调用C程序的方法如下:

  • 在汇编程序中使用IMPORT伪指令声明将要调用的C程序函数。

  • 在调用C程序时,要正确设置入口参数,然后使用BL指令调用。

[cpp] view plain copy

1. int sum(int a, int b, int c, int d, int e)  

2. {  

3.     return(a+b+c+d+e); //返回5个变量的和  

4. }  

[cpp] view plain copy

1.  AREA Example, CODE, READONLY  

2.   IMPORT sum      @ 声明外部标号sum,即C函数sum()  

3.   EXPORT CALLSUM  

4.   UM  

5.      STMFD SP!, {LR} @LR寄存器入栈  

6.      MOV R0, #1         @设置sum函数入口参数,R0为参数a  

7.      MOV R1, #2         @R1为参数b  

8.      MOV R2, #3         @R2为参数c  

9.      MOV R3, #5         @参数 e=5,保存到堆栈中  

10.      STR R3, {SP, #-4}!  

11.      MOV R3, #4         @R3为参数d, d=4  

12.      BL sum             @调用C程序中的sum函数,结果放在R0中  

13.      ADD SP, SP, #4     @调整堆栈指针  

14.      LDMFD SP, {PC}     @程序返回  

15. END  

以上程序使用了5个参数,分别使用寄存器R0存储第1个参数,R1存储第2个参数,R2存储第3个参数,R3存储第4个参数,第5个参数利用堆栈传送。由于利用了堆栈传递参数,在程序调用结束后要调整堆栈指针。汇编程序中调用了C程序的sum子函数,实现了1+2+3+4+5,最后相加结果保存在R0寄存器中。

*声明:本文于网络整理,版权归原作者所有,如来源信息有误或侵犯权益,请联系我们删除或授权事宜。

b2d133e103f942eb980e44ab2d513f30.png

f06e398ce0c45d9ac77e41717653a4d4.gif

戳“阅读原文”我们一起进步

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/337443.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos 7.6安装java_Hadoop的安装

为了方便后面使用Hadoop的shell命令&#xff0c;我先介绍Hadoop的安装。Hadoop有多种安装模式&#xff0c;这里介绍伪分布式的安装。我测试过Ubutun、Centos和WSL&#xff0c;都可以正常安装Hadoop的所有版本。所有一般不会出现版本对应的问题。Hadoop是基于Java语言进行编写的…

Python3 解题:字符串压缩

Python3 解题&#xff1a;字符串压缩原题 https://leetcode-cn.com/problems/compress-string-lcci/题目&#xff1a; 字符串压缩。利用字符重复出现的次数&#xff0c;编写一种方法&#xff0c;实现基本的字符串压缩功能。比如&#xff0c;字符串aabcccccaaa会变为a2b1c5a3。若…

C++软件分析师异常分析工作经验汇总

点击蓝字关注我们最近几年工作当中很大一部分内容是排查软件运行过程中遇到的各种异常&#xff0c;积累了一定的经验&#xff0c;在此给大家分享一下。本文将详细讲述Windows系统中软件异常的分类以及常用的排查方法&#xff0c;给大家提供一个借鉴与参考。1、软件异常的分类常…

java fix_Java中的低延迟FIX引擎

java fix总览 Chronicle FIX是我们的Low Latency FIX引擎和Java数据库。 是什么使它与众不同&#xff1f; 是为Java中的超低GC *设计的。 支持字符串和日期时间的方式可以最大程度地减少垃圾和开销。 可自定义为仅包含您期望的字段。 使用通常在二进制解析器和生成器中使用…

linux 查看防火墙状态_每天五分钟学习Linux系列之 - 系统安全配置

20年IT从业&#xff0c;二哥的团队使用最多的系统就是Linux&#xff0c;开发&#xff0c;运维的小伙伴们都离不开Linux系统&#xff0c;特别是大数据和人工智能领域更是如此&#xff0c;但由于日常工作忙&#xff0c;小伙伴们没有太多成块的时间系统的学习Linux, 并且现版CentO…

Python3求解旋转矩阵问题

Python3求解旋转矩阵问题原题 https://leetcode-cn.com/problems/spiral-matrix/ 给定一个包含 m x n 个元素的矩阵&#xff08;m 行, n 列&#xff09;&#xff0c;请按照顺时针螺旋顺序&#xff0c;返回矩阵中的所有元素。 示例 1: 输入: [[ 1, 2, 3 ],[ 4, 5, 6 ],[ 7, 8…

只针对异常的情况才使用异常_如何以及何时使用异常

只针对异常的情况才使用异常本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的旅程&#xff01; 在这里查看…

C++红黑树模拟实现map和set

点击蓝字关注我们一、红黑树及其节点的设计对于底层都是红黑树的map和set来说&#xff0c;他们之间存在的最大的区别就是&#xff1a;对于set是K模型的容器&#xff0c;而map是KV模型的容器。为了更好的灵活兼容实现map和set&#xff0c;就需要在红黑树以及树节点上进行特别的设…

c语言连接mysql_聊聊数据库MySQL、SqlServer、Oracle的区别,哪个更适合你?

一、MySQL优点&#xff1a;体积小、速度快、总体拥有成本低&#xff0c;开源&#xff1b;支持多种操作系统&#xff1b;是开源数据库&#xff0c;提供的接口支持多种语言连接操作 &#xff1b;MySQL的核心程序采用完全的多线程编程。线程是轻量级的进程&#xff0c;它可以灵活地…

Python3解题:二叉树路径总和问题

Python3解题&#xff1a;二叉树路径总和问题原题 https://leetcode-cn.com/problems/path-sum-ii/ 给定一个二叉树和一个目标和&#xff0c;找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树&#xff…

绩效工作流_流绩效–您的想法

绩效工作流上周&#xff0c;我介绍了一些有关Java 8流性能的基准测试结果。 你们和gal足够有兴趣留下一些想法&#xff0c;还有哪些可以介绍。 这就是我所做的&#xff0c;这里是结果。 总览 最后一篇文章的序言也适用于此。 阅读它&#xff0c;以找出所有数字为何撒谎&#…

C语言访问MCU寄存器的两种方式

点击蓝字关注我们单片机的特殊功能寄存器SFR&#xff0c;是SRAM地址已经确定的SRAM单元&#xff0c;在C语言环境下对其访问归纳起来有两种方法。1、采用标准C的强制类型转换和指针来实现采用标准C的强制转换和指针的概念来实现访问MCU的寄存器&#xff0c;例如:#define DDRB (*…

python中return true的用法_Return True/False何时使用它而不是Return

类比&#xff1a;函数是一个准备好执行任务并给出答案的可克隆助手。任务由函数的参数定义&#xff08;括号内的内容&#xff09;。让我们重写这些名称以赋予它们语义意义&#xff08;即说明我们期望的名称&#xff09;。在def isXGreaterThanY(..... 在这里&#xff0c;任务的…

08_优先队列

08_优先队列 一、优先队列最大优先队列最大优先队列API设计 最小优先队列最小优先队列API设计最小优先队列代码实现 索引优先队列索引优先队列实现思路索引优先队列API设计索引优先队列代码实现 一、优先队列 :::info 普通的队列是一种先进先出的数据结构&#xff0c;元素在队…

Python3实现红黑树[下篇]

Python3实现红黑树[下篇]我写的红黑树的上篇在这里&#xff1a;https://blog.csdn.net/qq_18138105/article/details/105190887 这是我近期看的文章 https://www.cnblogs.com/gcheeze/p/11186806.html 我看了很多关于红黑树删除的文章和博客&#xff0c;介绍得是相当相当的复…

C语言内存泄露很严重,如何应对?

点击蓝字关注我们1. 前言最近部门不同产品接连出现内存泄漏导致的网上问题&#xff0c;具体表现为单板在现网运行数月以后&#xff0c;因为内存耗尽而导致单板复位现象。**一方面&#xff0c;内存泄漏问题属于低级错误&#xff0c;此类问题遗漏到现网&#xff0c;影响很坏&…

通用编程_通用编程准则

通用编程本文是我们名为“ 高级Java ”的学院课程的一部分。 本课程旨在帮助您最有效地使用Java。 它讨论了高级主题&#xff0c;包括对象创建&#xff0c;并发&#xff0c;序列化&#xff0c;反射等。 它将指导您完成Java掌握的旅程&#xff01; 在这里查看 &#xff01; 目…

python发送邮件outlook_通过Python发送Outlook电子邮件?

I am using Outlook 2003. What is the best way to send email (through Outlook 2003) using Python? 解决方案 For a solution that uses outlook see TheoretiCALs answer below. Otherwise, use the smtplib that comes with python. Note that this will require your e…

Python3实现快速排序 通俗易懂

Python3实现快速排序快速排序 是一种交换排序&#xff0c;属于分治算法。 思路&#xff1a; 对于要排序的元素集合&#xff0c;指定集合的第一个元素为基准点&#xff0c;通过一系列的扫描和交换(见下面的代码)&#xff0c;让基准点左边的元素比基准点小&#xff0c;让基准点右…

C++队列queue用法详解(超详细)

点击蓝字关注我们一、定义queue是一种容器转换器模板&#xff0c;调用#include< queue>即可使用队列类。二、queue初始化queue<Type, Container> (<数据类型&#xff0c;容器类型>&#xff09;初始化时必须要有数据类型&#xff0c;容器可省略&#xff0c;省…