树莓派ARM汇编语言编程十讲(第7讲)

内容简介
树莓派单板机(Raspberry Pi Single Computer)是一种极了不起的产品,用户可以以非常低的成本获得一个Linux环境并带GPIO硬件扩展的迷你计算机系统。新一代树莓派4B还提供了良好的工业物联网和AIoT支持。树莓派单板机拥有完整的生态链,软硬件资源丰富,是嵌入式系统开发和智能硬件产品创新的很好选择。
作为嵌入式系统与嵌入式智能硬件开发基础中的基础,汇编语言是许多从事信息科学和工程领域的技术人员应该掌握的一项基本技能。目前,市场上针对树莓派单板机系统介绍C、Scratch、Python等编程语言与实践方面的资源很多,但鲜有系统针对树莓派单板机ARM汇编语言编程方面的介绍。这里以袁志勇主编的《嵌入式系统原理与应用技术》(北京航空航天大学出版社2019年1月第3版)一书中ARM汇编语言编程知识为基础,采用树莓派单板机及Linux操作系统验证平台,较系统地介绍树莓派ARM汇编语言编程技术与示例。由于准备仓促,不妥之处,还请各位不吝赐教。
第7讲:树莓派ARM C/C++语言与ARM汇编语言混合编程
第7讲目录
·GNU ARM程序常用文件格式
·GNU ARM C/C++程序内联ARM汇编程序
·树莓派ARM C++中的引用调用
·树莓派ARM C/C++程序调用ARM汇编程序

尽管树莓派应用程序大多是采用Scratch/Python/ C/C++等语言设计,但树莓派ARM体系结构提供了C、C++及Python与ARM汇编语言混合编程的支持。在实际软件开发中,我们使用较多的应用编程方式是:系统初始化或实时性强的任务采用ARM汇编语言实现,其他大多数应用编程任务采用C/C++/Python等高级语言实现。本讲主要介绍树莓派GNU ARM C/C++语言与GNU ARM汇编语言的交互编程。
一、GNU ARM程序常用文件格式
ARM程序常用文件格式及说明见表1所示。
表1 GNU ARM程序常用文件格式
TAB1.PNG
在GNU ARM程序中,C程序文件使用GNU GCC编译器对其进行编译;C++程序文件使用GNU G++编译器对其进行编译。
二、GNU ARM C/C++程序内联ARM汇编程序
将汇编语言函数直接放到C/C++语言程序内的技术称为内联汇编/内嵌汇编(Inline Assembly/Embedding Assembly)。树莓派GNU ARM汇编工具支持在ARM C、ARM C++等编程语言中内联汇编程序,内联汇编程序基本语法格式为:
asm(
“Assembly statement 1[\n][\t]”
“Assembly statement 2[\n][\t]”

“Assembly statement n[\n][\t]”
: Output operands
[: Input operands]
[: Changed registers]
);
asm汇编段各参数说明如下:
●Assembly statement-汇编语句:C语言字符串中的ARM汇编语句。
●Output operands-输出操作数列表:内联汇编代码输出值的变量或寄存器列表,它为必选项,这是因为我们总期望程序至少能做点事情并给出结果。
●Input operands-输入操作数列表: 为可选项,程序中使用的输入变量或寄存器列表。
●Changed registers -更改的寄存器列表:为可选项,当代码执行时,内联代码更改的任何其他寄存器列表。
为了保持程序良好的可读性,习惯上每条汇编指令都会占用一行并用制表符分隔,因此,汇编语句后还可以接换行符“\n”及制表符“\t”。
在内联汇编的语法规则中,作为操作数的寄存器和常量可以是char/unsigned char(8位)、short/unsigned short(16位)或int/unsigned int(32位)型的C表达式。其中,C变量类型char对应的汇编加载/存储指令是LDRSB/STRSB,类型unsigned char对应的汇编加载/存储指令是LDRB/STRB,类型short对应的汇编加载/存储指令是LDRSH/STRSH,类型unsigned short对应的汇编加载/存储指令是LDRH/STRH,类型int/unsigned int对应的汇编加载/存储指令是LDR/STR。
例:在ARM C程序中内联ARM汇编语言函数计算1+2++3+…+100,并输出结果。
程序清单见图1所示。
FIG1.PNG
图1 ARM C程序内联ARM汇编程序示例
在图1的ARM C程序内联ARM汇编程序示例中,[result]"=r"(var)中的=r表示输出寄存器结果变量result来自C程序中定义的变量var,结果变量result可以看成是var的别名。因此,当程序执行到"mov %[result],r1\n\t"内联汇编语句时,result变量中的求和结果会同时赋值给var,即变量var中存放的就是求和结果。
本示例ARM C程序内联ARM汇编程序仅有一个C程序文件,使用树莓派GCC编译器对inline_asm.c程序文件进行编译、链接,然后执行inline_asm程序,RPi终端输出计算结果为5050。
三、ARM C++中的引用调用
在树莓派中,ARM C++调用ARM汇编语言函数,除了可使用传值调用、传址调用,还可采用引用调用。在C++中,引用可看成是一种特殊类型的变量,但它不同于指针。引用(reference)通常被认为是另一个变量的别名。
1.引用的基本用法
在ARM C++中,引用有两种等价的定义格式:
格式1:数据类型 &引用名=变量名;
格式2:数据类型 &引用名(变量名);
一般情况下,定义引用时必须初始化。
例:
int a=3; //定义变量并初始化
int &m=a; //定义引用m
这里,m是一个引用,它是变量a的别名。在引用上所施加的操作,实质上就是对被引用者的操作。
例:
m=m+5; //a+5, a=8
实质上是a加5,使a值改变为8。
可以将一个引用赋给某个变量,这时该变量将具有被引用的变量的值。
例:
int n=m; //n=8
这时,n具有被引用的变量a的值,即a=8。
例:
int *p=&m; //将指针p指向a变量的地址
这是将指针p指向a变量的地址(注:指针是变量的地址,指针变量是用于存放变量地址值的一种变量。)
可见,指针是通过地址间接访问某个变量,而引用是通过别名直接访问某个变量。另外一点是引用必须初始化,而且被初始化后不再作为其它变量的别名。
一般情况下,初始化引用时需要一个相同类型的变量名。
例:树莓派ARM C++中使用基本引用示例。
程序清单见图2所示,使用GNU G++编译器对该程序进行编译、链接,然后执行程序。
FIG2.PNG
图2 树莓派ARM C++中使用基本引用示例
通过以上介绍,现将ARM C++引用的基本用法小结如下:
⑴引用在定义时要初始化;
⑵对引用的操作就是对被引用变量的操作;
⑶可将某个引用的地址赋给一个指针,而该指针则指向被引用的变量;
⑷可将某个引用给一个变量赋值,该变量的值便是被引用的变量值。
2.ARM C++中的引用调用
引用的主要用途是用来作函数的形参和函数的返回值。这样,ARM C++语言就有3种函数的调用方式,它们分别是传值调用(Call/Pass by value)、指针/传址调用(Call/Pass by address)和引用调用(Call/Pass by reference)。
使用引用作函数的形参时,调用函数的实参要用变量名,将实参的变量名赋给形参的引用,相当于在被调函数中使用了实参的别名。于是在被调函数中,对引用的改变,实质上就是直接地通过引用来改变实参的变量值。而且这种调用起到传址调用的作用,但它又比传址调用更方便、更直接。因此,在ARM C++中常使用引用作函数形参来实现在被调用函数中改变调用函数的实参值。
例:树莓派ARM C++中使用引用调用方式交换两个变量的值。
程序清单见图3所示。
FIG3.PNG
图3 树莓派ARM C++中函数的引用调用
在引用调用方式中,实参用变量名,形参用引用名,调用时将实参的变量名赋给对应的形参引用。在被调用函数中,改变引用的值就直接改变了对应的实参值。
四、树莓派ARM C/C++程序调用ARM汇编程序
在树莓派Linux中,ARM C/C++程序调用ARM汇编程序步骤如下:
(1)在ARM汇编程序中,用函数名作为汇编代码段的标识,定义汇编子程序代码(相当于C语言的函数),并在子程序最后用MOV PC,LR返回。
(2)在ARM汇编程序中,用.global伪操作导出该子程序名(相当于C语言的函数名),使得该子程序名(或函数名)能被其他的程序调用。
(3)在ARM C程序中,使用extern关键字声明该函数原型。
(4)在ARM C++程序中,使用extern ”C”声明该函数原型。
ARM C/C++程序与ARM汇编程序通过AAPCS (Procedure Call Standard for the ARM Architecture , ARM体系结构过程调用标准) 标准进行交互,AAPCS标准常用的交互规则有:
(1)堆栈规则:使用FD (Full Descending Stack,满递减堆栈) 类型堆栈;
(2)参数传递规则:如果参数数目小于等于4,则用R0-R3保存参数;当参数数目大于4时,剩余的参数压入堆栈保存;
(3)子程序结果返回规则:当子程序中的结果为32位整数时,通过R0返回;当子程序中的结果为64位整数时,通过R0、R1返回;当子程序中的结果为64位以上的更多位数结果时,需通过内存传递返回。
ARM C/C++主程序调用ARM汇编子程序的方法有三种:
(1)传值调用:通过寄存器或堆栈将数值传递给ARM汇编子程序,ARM汇编子程序无权访问ARM C/C++中数据区中的变量。
(2)传址调用:将ARM C/C++中的变量地址传递给ARM汇编子程序,ARM汇编子程序可访问ARM C/C++中数据区中的变量。
(3)引用调用:将ARM C++中变量的引用传递给ARM汇编子程序,ARM汇编子程序可直接通过引用别名访问ARM C++中数据区中的变量。注意,引用调用仅适用于ARM C++程序调用ARM汇编程序的应用场景。
例:树莓派ARM C主程序调用两个ARM汇编子程序计算三个整型数组元素的和。
程序清单:
(1)ARM C主程序文件名:mainprg.c,程序清单见图4所示。
FIG4.PNG
图4 ARM C程序调用ARM汇编程序:ARM C主程序
(2)ARM汇编程序文件名:sum.s,程序清单见图5所示。
FIG5.PNG
图5 ARM C程序调用ARM汇编程序:ARM汇编程序
采用GNU GCC编译器对ARM C程序文件及ARM汇编程序文件进行编译、链接,然后执行mainprg程序输出求和结果 (见图6)。
FIG6.PNG
图6 ARM C程序文件及ARM汇编程序文件的编译、链接与执行
一般来说,我们把有返回值的ARM汇编子程序称为ARM汇编函数;而ARM汇编子程序则无返回值。不过,ARM汇编子程序可通过“传址调用”的参数来返回一个或多个值。若只返回单个的值给ARM C/C++主程序,则建议使用ARM汇编函数,这样可更高效地执行程序。在本例ARM汇编程序文件sum.s中,thesum为ARM汇编子程序,fcnsum为ARM汇编函数。
ARM C程序和ARM汇编程序间的参数传递按照AAPCS标准进行。简单地说,若ARM汇编子程序的参数数目不多于4,用对应的R0-R3进行传递;若参数数目大于4个则需要借助堆栈;本例的ARM汇编函数fcnsum返回值通过R0返回。链接寄存器(LR)存放ARM汇编子程序调用的返回地址。testdata数组通过传址调用传递参数,常量和单一变量通过传值调用传递参数。如果被调用的ARM子程序或ARM函数中的实参有“&变量”,那么该变量通过传址调用传递参数。本例ARM汇编子程序及ARM汇编函数参数传递情况见表2所示。
表2 ARM汇编子程序及ARM汇编函数参数传递
TAB2.PNG
下面介绍树莓派ARM C++程序调用ARM汇编程序的方法,它与ARM C程序调用ARM汇编程序的不同之处在于:树莓派ARM C++程序调用ARM汇编程序要用extern“C”对被调用的函数进行声明;另外,ARM C++程序与ARM汇编程序的交互可以采用引用调用方式。
现用nano编辑器编辑一个ARM C++程序文件mainprg_r.cpp,采用GNU G++编译器对mainprg_r.cpp和sum.s文件进行编译、链接,然后执行mainprg_r程序 (见图7)。
FIG7.PNG
图7 树莓派ARM C++程序调用ARM汇编程序示例
在图7中,ARM汇编子程序thesum及ARM汇编函数fcnsum使用了extern "C"进行声明,extern "C"声明的thesum中使用了形参&s,s是被调用的thesum中实参sum_Sub的别名,实现了引用调用来传递参数。
另外,我们还可以使用ARM汇编程序调用ARM C程序 (此略)。
最后再给出作者在相关《嵌入式系统与智能硬件》、《电子创客》等讲座中曾经布置过的一个在树莓派环境下运行的课外上机题及其参考解答。
课外上机题:采用ARM C++和ARM汇编混合编程技术计算2+4+…+100并在树莓派Linux终端输出计算结果的程序。
下面分别给出采用本讲介绍过的使用ARM C++内联ARM汇编和使用ARM C++调用ARM汇编子程序两种方法的参考解答(不啰嗦,直接发代码及运行结果截图)。
(1)使用ARM C++内联ARM汇编计算2+4+…+100并输出计算结果见图8所示。
SUP_FIG1.PNG
图8 使用ARM C++内联ARM汇编计算2+4+…+100并输出结果
(2)使用ARM C++调用ARM汇编函数计算2+4+…+100并输出计算结果见图9所示。
SUP_FIG2.PNG
图9 使用ARM C++调用ARM汇编函数计算2+4+…+100并输出结果
End of This Lecture.
(作者Email联系:yuanzywhu@163.com)
发布时间:2020年4月18日
上一讲链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

袁易学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值