在KEIL C51 中 C语言里加入汇编语言(2006-12-26 13:33:50)
分类:C51编程
1. 通过使用预处理指令#asm
和#endasm来使用汇编语言。用户编写的汇编语言可以紧跟在#asm之后,而在#endasm之前结束。如下所示:
# asm
# endasm
在#asm和#endasm之间的语句将作为汇编语言的语句输出到由编译器产生的汇编语言文件中。
2.通过使用预处理指令# pragma asm和函数_asm()来使用汇编语言。在程序的开头加上预处理指令#pragma
asm,在该预处理指令之前只能有注释和其它预处理指令。 _asm()函数可按以下方式使用。 _asm(汇编语言字符串)
在汇编语言字符串中,可以通过回车和换行符把各个语句分开。
在C语言中使用汇编语言,可以操作C语言中的全局变量或完成用C语言难于完成的功能,但要注意以下几点:
① #asm不允许嵌套使用。
② 当使用asm语句时,编译系统并不输出目标模块,而只输出汇编源文件。
③ _asm只能用小写字母,如果写成大写,就作为普通变量。
④ #asm#endasm和_asm只能用在函数内。
C与汇编混合编程的一点总结
在C和汇编混合编程的时候,存在C语言和汇编语言的变量以及函数的接口问题。在C程序中定义的变量,编译为.asm文件后,都被放进了.bss区,而且变量名的前面都带了一个下划线。在C程序中定义的函数,编译后在函数名前也带了一个下划线。例如:
extern int num就会变成 .bss _num, 1 extern float nums[5]就会变成.bss _nums,
5 extern void func ( )就会变成 _func, 一 汇编和C的相互调用可以分以下几种情况: (1)
汇编程序中访问c程序中的变量和函数。
在汇编程序中,用_XX就可以访问C中的变量XX了。访问数组时,可以用_XX+偏移量来访问,如_XX+3访问了数组中的XX[3]。
在汇编程序调用C函数时,如果没有参数传递,直接用_funcname 就可以了。如果有参数传递,
则函数中最左边的一个参数由寄存器A给出,其他的参数按顺序由堆栈给出。返回
值是返回到A寄存器或者由A寄存器给出的地址。同时注意,为了能够让汇编语言能访问到C语言中定义的变量和函数,他们必须声明为外部变量,即加extern
前缀。 (2) c程序中访问汇编程序中的变量
如果需要在c程序中访问汇编程序中的变量,则汇编程序中的变量名必须以下划线为首字符,并用GLOBAL使之成为全局变量。
如果需要在c程序中调用汇编程序中的过程,则过程名必须以下划线为首字符,并且,要根据c程序编译时使用的模式是stack-based
MODEL还是register argument MODEL来正确地编写该过程,使之能正确地取得调用参数。 (3) 在线汇编
在C程序中直接插入 asm(“ ***
”),内嵌汇编语句,需要注意的是这种用法要慎用,在线汇编提供了能直接读写硬件的能力,如读写中断控制允许寄存器等,但编译器并不检查和分析在线汇编语言,插入在线汇编语言改变汇编环境或可能改变C变量的值可能导致严重的错误。
二 汇编和C接口中寻址方式的改变:
需要注意的是,在C语言中,对于局部变量的建立和访问,是通过堆栈实现的,它的寻址是通过堆栈寄存器SP实现的。而在汇编语言中,为了使程序代码变得更为精简,TI在直接寻址方式中,地址的低7位直接包含在指令中,这低7位所能寻址的具体位置可由DP寄存器或SP寄存器决定。具体实现可通过设置ST1寄存器的CPL位实现,CPL=0,DP寻址,CPL=1,SP寻址。在DP寻址的时候,由DP提供高9位地址,与低7位组成16位地址;在SP寻址的时候,16位地址是由SP(16位)与低7位直接相加得来。
由于在C语言的环境下,局部变量的寻址必须通过SP寄存器实现,在混合编程的时候,为了使汇编语言不影响堆栈寄存器SP,通常的方式是在汇编环境中使用DP方式寻址,这样可以使二者互不干扰。编程中只要注意对CPL位正确设置即可
本人在调试混合编程的程序时,苦于资料太少,结果折腾了两天才调通。对于混合编程的方法初有体会,不敢独享,特发此贴,以飨众网友。
C调用汇编有两种方法,一是直接在C中插入汇编语句,而是用汇编编写一个子程序,供C调用。方法一适用于类似看门狗复位这样的只需一两句汇编就能完成的场合;方法二更
为通用,比如用汇编编写延时子程序,就能实现精确延时。下面讨论一下方法二的注意事项。 1。函数声明:
C中在函数前加extern声明此函数为外部函数,在汇编中要声明函数名为全局变量,如: extern void delay(void)
; globl delay ; in asm _delay: ; delay function begins 2 参数传递
:一般是用寄存器传递参数的,比如ImageCraft,就用人R16,R17,R18,R19充电输入参数,用R16,R17传递返回结果。如果返回结果需要多个字节才能表示,最好用SRAM充电结果,即在汇编中,把结果存入SRAM中,在C中读取相应的地址。
3 保存寄存器。 一些寄存器在调用子程序时,内容是不能被改变的,这些寄存器称为Reserved
register,如果汇编子程序用到了这些寄存器,就要在程序入口出将它们入栈,程序返回时再出栈。 关于在 KEIL C51
中嵌入汇编以及C51与A51间的相互调用 如何在 KEIL C51(v6.21) 中调用汇编函数的一个示例 [ycong_kuang]
有关c51调用汇编的方法已经有很多帖子讲到,但是一般只讲要点,很少有对整个过程作详细描述,对于初学者是不够的,这里笔者
通过一个简单例子对这个过程进行描述,希望能对初学者有所帮助。几年来,在这个论坛里笔者得到很多热心人指导,因此也希望
藉此尽一点绵薄之力。
在这个例子里,阐述了编写c51程序调用汇编函数的一种方法,这个外部函数的入口参数是一个字符型变量和一个位变量,返回值是
一个整型变量。例中,先用c51写出这个函数的主体,然后用SRC控制指令编译产生asm文件,进一步修改这个asm文件就得到我们所
要的汇编函数。该方法让编译器自动完成各种段的安排,提高了汇编程序的编写效率。 step1.
按写普通c51程序方法,建立工程,在里面导入main.c文件和CFUNC.c文件。 相关文件如下: //main.c文件
#include < reg51.h > #define uCHAR
unsigned CHAR
#define uint unsigned int extern uint AFUNC(uCHAR v_achr,bit
v_bflag); void main() { bit BFLAG; uCHAR mav_chr; uint mvintrslt;
mav_chr=0xd4; BFLAG=1; mvintrslt=AFUNC(mav_chr,BFLAG); }
//CFUNC.c文件 #define uCHAR unsigned CHAR #define uint unsigned int
uint AFUNC(uCHAR v_achr,bit v_bflag) { uCHAR tmp_vchr; uint
tp_vint; tmp_vchr=v_achr; tp_vint=(uint)v_bflag; return
tmp_vchr+(tp_vint<<8); } step2. 在
Project 窗口中包含汇编代码的 C 文件上右键,选择“Options for ...”,点击右边的“Generate
Assembler SRC File”和“Assemble SRC File”,使检查框由灰色变成黑色(有效)状态; step3.
根据选择的编译模式,把相应的库文件(如 Small 模式时,是
KeilC51LibC51S.Lib)加入工程中,该文件必须作为工 程的最后文件;
step4.
build这个工程后将会产生一个CFUNC.SRC的文件,将这个文件改名为CFUNC.A51(也可以通过编译选项直接产生CFUNC.A51文
件),然后在工程里去掉库文件(如C51S.Lib)和CFUNC.c,而将CFUNC.A51添加到工程里。
//CFUNC.SRC文件如下 .CFUNC.SRC generated from: CFUNC.c NAME CFUNC
?PR?_AFUNC?CFUNC SEGMENT CODE ?BI?_AFUNC?CFUNC SEGMENT BIT
OVERLAYABLE PUBLIC ?_AFUNC?BIT PUBLIC _AFUNC RSEG ?BI?_AFUNC?CFUNC
?_AFUNC?BIT: v_bflag?041: DBIT 1 ; #define uCHAR unsigned CHAR ;
#define uint unsigned int ; ; uint AFUNC(uCHAR v_achr,bit v_bflag)
RSEG ?PR?_AFUNC?CFUNC _AFUNC: USING 0 ; SOURCE LINE # 5 ;----
Variable 'v_achr?040' assigned to Register 'R7' ---- ; { ; SOURCE
LINE # 6 ; uCHAR tmp_vchr; ; uint tp_vint; ; ; tmp_vchr=v_achr; ;
SOURCE LINE # 10 ;---- Variable 'tmp_vchr?042' assigned to Register
'R5' ---- MOV R5,AR7
; tp_vint=(uint)v_bflag; ; SOURCE LINE # 11 MOV C,v_bflag?041 CLR A
RLC A ;---- Variable 'tp_vint?043' assigned to Register 'R6/R7'
---- ; return tmp_vchr+(tp_vint<<8);
; SOURCE LINE # 12 MOV R6,A MOV R4,#00H CLR A ADD A,R5 MOV R7,A MOV
A,R4 ADDC A,R6 MOV R6,A ; } ; SOURCE LINE # 13 ?C0001: RET ; END OF
_AFUNC END step5. 检查main.c的“Generate Assembler SRC File”和“Assemble
SRC File”是否有效,若是有效则点击使检查框变成无效状
态;再次build这个工程,到此你已经得到汇编函数的主体,修改函数里面的汇编代码就得到你所需的汇编函数了。 参考文献:
1.徐爱钧,彭秀华。单片机高级语言C51windows环境编程与应用,电子工业出版社
.................................................................................................................
keil中汇编函数调用c51函数 [ycong_kuang]
第一步在工程里多了一个被汇编调用的c51的函数文件(c51func.c),至于汇编函数还是先用c51编写出主体
(a51func.c),这样汇编程序接口和段都交给编译器处理,你只管在编译成汇编代码后按你的要求改写汇编代码就行了。 例程如下:
//main.c #include < reg51.h > #define
uCHAR unsigned CHAR #define uint unsigned int extern uint
AFUNC(uCHAR v_achr,bit v_bflag); void main() { bit BFLAG; uCHAR
mav_chr; uint mvintrslt; mav_chr=0xd4; BFLAG=1;
mvintrslt=AFUNC(mav_chr,BFLAG); } //a51FUNC.c #define uCHAR
unsigned CHAR #define uint unsigned int extern uint CFUNC(uint);
uint AFUNC(uCHAR v_achr,bit v_bflag) //c51写的汇编函数,最终要变成汇编代码 { uCHAR
tmp_vchr; uint tp_vint;
tmp_vchr=v_achr; tp_vint=(uint)v_bflag; return CFUNC(tp_vint);
//这里调用一个c51函数 } //c51FUNC.c #define uCHAR unsigned CHAR #define
uint unsigned int uint CFUNC(uint v_int) //被汇编函数调用c51函数 { return
v_int<<2; }