keil C中嵌入汇编程序的方法(转载)
1. C语言中直接嵌入汇编程序段
1、在 C 文件中要嵌入汇编代码片以如下方式加入汇编代码:
#pragma ASM
; Assembler Code Here
#pragma ENDASM
2、在 Project 窗口中包含汇编代码的 C 文件上右键,选择“Options for ...”,点击右边的“Generate Assembler SRC File”和
“Assemble SRC File”,使检查框由灰色变成黑色(有效)状态;
3、根据选择的编译模式,把相应的库文件(如 Small 模式时,是 Keil\\C51\\Lib\\C51S.Lib)加入工程中, 该文件必须作为工程的最后文件;
4、这点也是本人要重要说明的!即一定要将c:\\keil\\C51下的STARTUP.A51文件加入项目
4、编译,即可生成目标代码。
来个实例吧:
#i nclude void main(void) { P2=1; #pragma asm MOV R7,#10 DEL:MOV R6,#20 DJNZ R6,$ DJNZ R7,DEL #pragma endasm P2=0; } 2 . 无参数传递的函数调用 C51调用汇编函数 1.无参数传递的函数调用 先来个例子:其中example.c和example.a51为项目中的两个文件 ***********************example.c*********************************************** extern void delay100(); main() {delay100;} ***********************example.a51*********************************************** PR?DELAY100 SEGMENT CODE; // 在程序存储区中定义段 PUBLIC DELAY100; //声明函数 RSEG ?PR?DELAY100; //函数可被连接器放置在任何地方 DELAY100: MOV R7,#10 DEL: MOV R6,#20 DJNZ R6,$ DJNZ R7,DEL RET END 在example.c文件中,先声明外部函数,然后直接在main中调用即可。 在example.a51中, PR?DELAY100 SEGMENT CODE; 作用是在程序存储区中定义段,DELAY100为段名,?PR?表示段位于程序存储区内 PUBLIC DELAY100; 作用是声明函数为公共函数 RSEG ?PR?DELAY100; 表示函数可被连接器放置在任何地方,RSEG是段名的属性 段名的开头为PR,是为了和C51内部命名转换兼容,命名转换规律如下: CODE -?PR? XDATA-?XD DATA-?DT BIT-?BI PDATA-?PD 3. 有参数传递的函数调用 在写这片文章之前,写了个试验程序,但总是通不过,查看汇编代码发现c文件中的语句根本没有被编译进去,怎么也找不到原因,郁闷 ~~ 最后在网上搜了个试验程序,把我的程序复制过去,可以编译成功,奇怪了,在我的project里就是不行,我注意到我的project编译后 出现一条WARNING: *** WARNING L7: MODULE > MODULE: 8.obj (8) 而同样的程序代码在另外一个project中没有WARNING,肯定是这条WARNING语句导致的,里面提到NAME,难道和名字有关,马上把A51文 件改个名字(原来c文件和a51文件名字一样),编译,哈哈,WARNING不见了, 查看汇编代码,一切按预想的进行,唉,一个名字害得我不浅啊 ,记住哦,c文件和A51文件不能使用同一个文件名,不过我还不知道为什么会这样,有高手知道得话请告知,还是进行今天的作业吧! 今天说说带参数传递的函数调用,在C51和汇编之间传递参数的方式有两种,一种是通过寄存器传递参数,C51中不同类型的实参会存入 相应的寄存器,在汇编中只需对相应寄存器进行操作,即达到传递参数的目的。 不同类型的数据及其传递参数的寄存器如下表所示: 参数类型 char int long/float 通用指针 第1个 R7 R6&R7 R4-R7 R1-R3 第2个 R5 R4&R5 R4-R7 R1-R3 第3个 R3 R2&R3 -- R1-R3 举个例子吧,void delay(unsigned char i, unsigned int j) 当执行语句delay(10,1000)时,10会存入R7中,1000高位会存入R4中 ,低位存入R5中。在汇编语句中从这几个寄存器中取数,再进行操作就行了,说起来也很简单的嘛,呵呵~ 来个最简单的实例吧,没什么意义,傻瓜式的程序: ****************************main.c********************************************* extern void DELAY(unsigned char i,unsigned int j); main() { DELAY(10,1000); while(1); } **********************DELAY.A51******************************************** PR?_DELAY?DELAY SEGMENT CODE PUBLIC _DELAY RSEG ?PR?_DELAY?DELAY _DELAY: DJNZ R4,$ DJNZ R5,$ DJNZ R7,$ RET END 还要说的是,函数名前要加下划线,表示是有参数传递的函数调用! 4. 函数的返回值传递参数 (2)函数返回值所用的寄存器 返回值类型 寄存器 说明 Bit C 由具体标志位返回 char/unsigned char / 1 byte 指针 R7 int/unsigned int / 2 byte 指针 R6&R7 高位在R6 long/unsigned long / 3 byte 指针 R4-R7 高位在R4 float R4-R7 32bit IEEE格式,指数和符号位在R7 通用指针 R1-R3 存储类型在R3,高位在R2 实例: ********************main.c**************************************** unsigned int example(unsigned char i) { return(i*i); } main() {example(80); #pragma asm DJNZ R7,$ DJNZ R6,$ #pragma endasm while(1); } 函数返回值在R6,R7中。 有时候用到需要精确延时之类的子程序时,用C语言比较难控制,这时候就可以在C中嵌入汇编 比较常用的keil中嵌入汇编的方法如下所示: 如图一,在C文件中要嵌入汇编的地方用#pragma asm和#pragma endasm分隔开来,这样编译时KEIL就知道这中间的一段是汇编了。 在有加入汇编的文件中,还要设置编译该文件时的选项 Generate Assembler SRC File 生成汇编SRC文件 Assemble SRC File 封装汇编文件 (如图三的状态为选中) 选上这两项就可以在C中嵌人汇编了,设置后在文件图示中多了三个红色的小方块。 为了能对汇编进行封装还要在项目中加入相应的封装库文件, 在笔者的项目中编译模式是小模式所以选用C51S.LIB。这也是最常用的。这些库 文件是中KEIL安装目录下的LIB目录中。 加好后就可以顺利编译了。(注:我只在7.0以上版本使用过) 汇编与C语言混合编程的关键问题 1 C程序变量与汇编程序变量的共用 为了使程序更易于接口和维护,可以在汇编程序中引用与C程序共享的变量: .ref_to_dce_num,_to-dte_num,_to_dce_buff,_to_dte_buff 在汇编程序中引用而在C程序可直接定义的变量: unsigned char to_dte_buff[BUFF_SIZE]; //DSP发向PC机的数据 int to_dte_num; //缓冲区中存放的有效字节数 int to_dte_store; //缓冲区的存放指针 int to_dte_read; //缓冲区的读取指针 这样经过链接就可以完成对应。 2 程序入口问题 在C程序中,程序的入口是main()函数。而在汇编程序中其入口由*.cmd文件中的命令决定,如:-e main_start;程序入口地址为 main _start。这样,混合汇编出来的程序得不到正确结果。因为C到ASM的汇编有默认的入口c-int00,从这开始的一段程序为C程序的运行做准备工 作。这些工作包括初始化变量、设置栈指针等,相当于系统壳不能跨越。这时可在*.cmd文件中去掉语句:-e main_start。如仍想执行某些汇 编程序,可以C函数的形式执行,如: main_start(); //其中含有其他汇编程序 但前提是在汇编程序中把_main_start作为首地址,程序以rete结尾(作为可调用的函数)的程序段,并在汇编程序中引用_main_start, 即.ref _main_start。 3 移位问题 在C语言中把变量设为char型时,它是8位的,但在DSP汇编中此变量仍被作为16位处理。所以会出现在C程序中的移位结果与汇编程序移位 结果不同的问题。解决的办法是在C程序中,把移位结果再用0X00FF去“与”一下 即可。 4 堆栈问题 在汇编程序中对堆栈的依赖很小,但在C程序中分配局部变量、变量初始化、传递函数变量、保存函数返回地址、保护临时结果功能都是靠 堆栈完成。而C编译器无法检查程序运行时堆栈能否溢出。 5 程序跑飞问题 编译后的C程序跑飞一般是对不存在的存储区访问造成的。首先要查.MAP文件与memory map图对比,看是否超出范围。如果在有中断的程序 中跑飞,应重点查在中断程序中是否对所用到的寄存器进行了压栈保护。如果在中断程序中调用了C程序,则要查汇编后的C程序中是否用到了 没有被保护的寄存器并提供保护(在C程序的编译中是不对A、B等寄存器进行保护的)。 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- haog.cn 版权所有 赣ICP备2024042798号-2
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务