keil把c语言函数转成汇编

汇编可以让开发人员从根源上理解程序的运行逻辑,本文介绍如何在keil环境下如何把一个c文件中的某一个函数,转换为汇编函数,并编译运行。

右击某个c文件,选择Option for File。。。

  图1

然后把下图中的Generate Assembler SRC File(生成汇编源文件)打黑色勾。(默认是灰色勾)

图2

然后执行编译,这样就会在工程的输出文件夹(一般会被命名为OBJ)下,找到与.c文件同名的.s文件,这个C文件中的所有函数,都会被一一转换为汇编函数。

以我的ad.c为例,里面有个test()函数,这个函数中会调用另外一个函数uint16_t AD_getValue1(void),还会对一个全局变量var做写操作。

图3

转换出的汇编函数如下:

图4

由这段代码可以看到在汇编中是如何调用C函数的,以及汇编是如何读写c语言中的全局变量var的。

写var的代码是这两行:

图5

首先LDR指令从|L0.260|标签处载入var变量的内存地址,然后VSTR指令把s0的值写入该内存地址处。汇编中的标签和C语言中的goto语句标签,是类似的。

那么|L0.260|标签定义了什么?在s文件中搜索可见:

图6

有上图可见,这个标签对应的flash地址处,通过DCD命令占用了一个32bit的空间,这个空间中的值被初始化成了全局变量var的地址。DCD本身并不是一个可执行语句,只是一个占位而已。

上图还可以看到,对于C语言中定义的很多常量,也用DCD命令给保存下来了。

还要再提一点:对于C中定义的全局变量var,在汇编中是这样的:

        AREA ||.data||, DATA, ALIGN=2
var
        DCD      0x00000000

解析一下以上代码:var在c语言中是一个变量的名字,编译为汇编后我们发现它就是就是一个标签,这个标签其实就是一个内存地址,也即c语言中var变量的内存地址。换句话说,这个标签的值其实就等于c语言中对var变量取地址&var得到的值。

该地址处的4个字节被0x00000000填充。也就等价于c语言中var变量被初始化为0。为什么var是个内存地址,而不是flash地址,是因为这段汇编代码被定义为了DATA段,DATA段中都是内存,详情可学习AREA汇编伪指令。

这样我们再回过头来看图6中的|L0.260|标签就更清晰了,|L0.260|本身就是个地址,这个地址处存储的值是汇编标签var,也即,|L0.260|地址处存放的就是C变量var变量的内存地址。

接下来我们就可以把c语言中的这个test()函数,用汇编给替换一下:

float var;
void test(void)
{
	volatile float k = 1.5f;
	
	var = AD_getValue1() * k;
}

//前缀__asm代表这是个汇编语言编写的函数,以便编译时供编译器识别
__asm void test(void)
{
	THUMB
    REQUIRE8
    PRESERVE8
    //本汇编函数所有要调用的c函数,必须全部用IMPORT指令导入,不然编译报错
    IMPORT AD_getValue1[CODE]
    
    //全局变量也必须IMPORT导入
    IMPORT var
    
//以下代码直接从生成的.s文件中对应的test函数拷贝而来,见前文图4,我加了注释
        PUSH     {r3,lr}//PC指针存储到如r3寄存器
        VMOV.F32 s0,#1.50000000//把常量1.5加载到浮点寄存器s0中
        VSTR     s0,[sp,#0]//把s0中的值,存储到(sp指针+0)指向的内存处
        BL       AD_getValue1//调用c函数,其返回值(uint16类型)会被存到r0寄存器
        VMOV     s0,r0//把R0寄存器中的数转存到浮点寄存器s0中
        VCVT.F32.U32 s0,s0//把s0中的值由uint类型转成float类型
        VLDR     s1,[sp,#0]//把(sp指针+0)内存处的值加载到s1中
        VMUL.F32 s0,s0,s1//等价于s0=s0*si
        LDR      r0,|L0.260|/从|L0.260|标签处载入全局变量var的地址
        VSTR     s0,[r0,#0]//把s0中的值存储到var中
        POP      {r3,pc}//函数返回
//以上代码需要调用|L0.260|标签处的数据,也手动复制进来:
    |L0.260|
        DCD      var
}

至此,这个汇编函数__asm void test(void)和C语言函数void test(void),功能完全一致了。把这个c函数void test(void)删掉后,只保留汇编函数,仍然可以编译通过。这样我们就实现了:在一个c源文件中,既有c函数,又有汇编函数共存。

本文由【暴躁的野生猿】发表于CSDN,如有非法转载请帮忙举报谢谢。搜索题目可找到原文。

如果编译时看到以下报错:

error: A1875E: Register Rn must be from R0 to R7 in this instruction

 报错对应的代码为LDR      r0,|L0.260|,这行代码本身并没有问题,问题出在|L0.260|这个标签没有4字节对齐。

同时伴随上述报错的,还有一个警告:

 warning: A1581W: Added 2 bytes of padding at address 0x2aa

这个你警报的意思是ROM没有4字节对齐,编译器自动添加了2字节的空白。这个问题来源于图6中第一行DCW,这个指令占用了2字节的位置,导致后面的DCD无法4字节对齐了。解决方法就是直接把DCW这行删掉。

  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值