ARM实验-ARM主程序调用ARM/C语言子程序

一、实验名称:ARM主程序调用ARM/C语言子程序

二、实验目的:

  1. 了解ARM应用程序框架。
  2. 了解ARM汇编程序函数和C语言程序函数相互调用时,遵循的ATPCS标准;
  3. 了解和掌握ARM汇编程序调用C语言程序函数的基本方法;
  4. 了解和掌握ARM汇编程序调用C语言程序函数的参数传递过程。

 三、实验原理:

  1. ARM工程

由于C语言便于理解,有大量的支持库,所以它是当前ARM程序设计所使用的主要编程语言。

对硬件系统的初始化、CPU状态设定、中断使能、主频设定 以及RAM控制参数初始化等C程序力所不能及的底层操作,还是要由汇编语言程序 来完成。

ARM工程 的各种源文件之间的关系,以及最后形成可执行文件的过程如下图1所示:

图1 汇编语言和C语言混合编译链接示意图

在应用系统的程序设计中,若所有的编程任务均用汇编语言 来完成,其工作量是可想而知的,这样做也不利于系统升级或应用软件移植。

通常汇编语言部分完成系统硬件的初始化;高级语言部分完成用户的应用。

执行时,首先执行初始化部分,然后再跳转到C/C++部分。整个程序结构显得清晰明了,容易理解。为方便工程开发,ARM公司的开发环境ARM ADS为用户提供了一个可以选用的应用程序框架。该框架把为用户程序做准备工作的程序分成了: 启动代码 和 应用程序初始化 两部分。

用于硬件初始化的汇编语言部分叫做 启动代码;用于应用程序初始化的C部分叫做初始化部分。整个程序如下图2所示:

图2 ARM应用程序框架

  1. 过程调用标准ATPCS

在ARM工程中,C程序调用汇编函数和汇编程序调用C函数是经常发生的事情。为此人们制定了ARM-Thumb过程调用标准ATPCS(ARM-Thumb Procedure Call Standard)。

  1. ATPCS规定,ARM的数据堆栈为FD型堆栈,即递减满堆栈。
  2. ATPCS标准规定,对于参数个数不多于4的函数,编译器必须按参数在列表中的顺序,自左向右为它们分配寄存器R0~R3。其中函数返回时,R0还被用来存放函数的返回值。
  3. 如果函数的参数多于4个,那么多余的参数则按自右向左的顺序压入数据堆栈,即参数入栈顺序与参数顺序相反。
  4. 根据ATPCS的C语言程序调用汇编函数,参数由左向右依次传递给寄存器R0~R3的规则。
  1. 子程序的调用与返回

人们把可以多次反复调用的、能完成指定功能的程序段称为“子程序”。把调用子程序的程序称为“主程序”。

为进行识别,子程序的 第1条指令 之前必须赋予一个 标号,以便其他程序可以用这个标号调用子程序。

在 ARM 汇编语言程序中,主程序一般通过 BL 指令来调用子程序。该指令在执行时完成如下操作:将子程序的返回地址存放在连接寄存器 LR 中,同时将程序计数器 PC 指向子程序的入口点。

为使子程序执行完毕能 返回 主程序的调用处,子程序末尾 处应有 MOV、LDMFD 等指令,并在指令中将返回地址重新复制到 PC 中。

在调用子程序的同时,也可以使用 R0~R3 来进行 参数的传递 和从子程序返回 运算结果。

四、实验内容:

  1. ARM指令主程序调用ARM指令子程序;
  2. ARM指令主程序调用C语言子程序,输入的6个参数为1、2、3、4、5、6;
  3. 子程序的参数个数要求至少6个, C语言子程序实现的功能为:(i1+i2+i3+i4)*i5-i6。
  4. 分析通过反汇编得到的C程序的ARM指令代码段,了解参数传递过程。

五、实验器材(设备、元器件):

  1. PC机一台;
  2. Keil MDK-ARM uVision4开发工具。

六、实验步骤:

  1. 打开Keil MDK-ARM uVision4开发工具;
  2. 新建一个工程文件;
  3. 在新建的工程文件中,添加新的源程序文件
  4. 编写代码
  5. 选择“Build target”菜单对编写好的工程文件进行编译链接。
  6. 点击““Start/Stop Debug Section””按键,对程序进行跟踪调试,在调试界面,单步执行,对CPU各寄存器的值的变化、以及相关内存的变化进行分析比较,判断程序的执行是否符合预期要求。

七、实验结果与分析(含重要数据结果分析或核心代码流程分析)

  1. ARM指令主程序调用ARM指令子程序;程序代码

(1)程序代码如代码1所示:

 AREA lab3,CODE,READONLY

 ENTRY  

 MOV R0,#0x01

 MOV R1,#0x02

 BL arm_func  ;调用ARM子程序

 B final

arm_func ;ARM子程序用来实现将R0和R1的值相加保存在R6中

 ADD R6,R0,R1

 BX LR

final

 END

(2)运行过程及结果界面截图

图1 ARM指令主程序调用ARM指令子程序运行结果截图

(3)实验结果分析

执行程序后,首先将R0,R1分别初始化为0x01、0x02,之后调用ARM子程序arm_func,将R0跟R1的内容相加送到R6,此时可以看到R6的内容为0x03结果正确。

(4)实验结论

实验结果与期望结果一致,ARM子程序执行正确。

   2.ARM指令主程序调用C语言子程序;

  1. 程序代码

ARM指令主程序如代码2所示

代码2  ARM指令主程序调用C语言子程序的RM指令主程序

 PRESERVE8

 IMPORT c_func   ;声明c_func为外部引用符号

 AREA lab3,CODE,READONLY

 ENTRY  

 LDR SP,=0x40000100 ;初始化堆栈指针SP

 MOV R0,#0x01

 MOV R1,#0x02

 MOV R2,#0x03

 MOV R3,#0x04

 MOV R4,#0x05

 MOV R5,#0x06

 STMFD SP!,{R4,R5} ;多余两个参数压栈

 BL c_func ;调用c程序

 MOV R0,R0

 END

C语言子程序代码如代码3所示

代码3  RM指令主程序调用C语言子程序的C语言子程序

#include<stdio.h>

int c_func(int i1,int i2,int i3,int i4,int i5,int i6){

return (i1 + i2 + i3 + i4) * i5 - i6;

}
  1. 运行过程及结果界面截图

图2是程序即将调用C子程序的截图,将R0-R5分别初始化为0x01、0x02、0x03、0x04、0x05、0x06,由于参数个多余四个,因此将多余的两个参数压栈。

 

图2 程序即将调用C子程序的截图

图3是程序跟踪进入C语言子程序的截图,可以看到函数已经接收到了六个参数值。

图3 程序跟踪进入C语言子程序的截图

图4是调用C程序返回后的截图,函数返回值0x2C保存到R0中。

图4 程序跟踪进入C语言子程序的截图

  1. 实验结论

函数的接收到的六个参数分别为0x01、0x02、0x03、0x04、0x05、0x06,经(i1+i2+i3+i4)*i5-i6运算后结果为0x2C并返回,从R0的值为0x2C可以看出,ARM程序成功接收到C语言函数返回值。

七、总结及心得体会:

(1)心得体会:通过本次实验我进一步了解了ARM应用程序框架,掌握了在ARM汇编程序函数和C语言程序函数相互调用时需要遵循的ATPCS标准;同时我也了解和掌握ARM汇编程序调用C语言程序函数的基本方法以及ARM汇编程序调用C语言程序函数的参数传递过程。

(2)思考题:

1)在ARM汇编程序中,参数多余4个(比如为6个)时,多余的参数会按照从右向左的顺序压栈。

2)调用C语言函数前SP寄存器的值以及C函数返回后SP寄存器的值没有发生变化,进入C语言函数时,会先进行现场保护将会用到的寄存器压栈保存,此时SP的值变小,C语言函数执行完之后,会进行恢复现场的出栈操作,SP值变大,恢复到调用C语言函数前的值。

3)如果参数多余4个的话,前四个参数通过R0-R3寄存器传递,剩下的参数通过LDR指令从栈中依次取出;在C语言函数入口使用STMDB指令将函数中所用到的其他寄存器压栈保存依次来保护现场,之后C语言函数出口,使用LDMIA指令进行出栈进行恢复现场。

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

实名吃香菜

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

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

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

打赏作者

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

抵扣说明:

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

余额充值