汇编可以让开发人员从根源上理解程序的运行逻辑,本文介绍如何在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=2var 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这行删掉。
|