汇编和C混编教程.doc_第1页
汇编和C混编教程.doc_第2页
汇编和C混编教程.doc_第3页
汇编和C混编教程.doc_第4页
汇编和C混编教程.doc_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

用Keil在C中嵌入汇编作者:温正伟原载:早前公布了C和汇编混编的温度控制器程序,收到一些朋友的询问,他们无法在自己程序中使用我的18B20的汇编子程序或无法正常通过混编后的程序编译。其实在KEIL中嵌入汇编的方法很简单。如图一,在C文件中要嵌入汇编的地方用#pragma asm和#pragma endasm分隔开来,这样编译时KEIL就知道这中间的一段是汇编了。 图1在有加入汇编的文件中,还要设置编译该文件时的选项图2Generate Assembler SRC File 生成汇编SRC文件Assemble SRC File封装汇编文件(如图三的状态为选中)选上这两项就可以在C中嵌人汇编了,设置后在文件图示中多了三个红色的小方块。图3为了能对汇编进行封装还要在项目中加入相应的封装库文件, 在笔者的项目中编译模式是小模式所以选用C51S.LIB。这也是最常用的。这些库文件是中KEIL安装目录下的LIB目录中。 加好后就可以顺利编译了。(注:我只在7.0以上版本使用过)图4图5图6C51编译器能对C语言程序进行高效率的编译,生成高效简洁的代码,在大多数的应用场合,采用C语言编程即可完成预期的任务,但是,在有些场合还是会用到汇编,例如在下面的几种情况下,采用汇编可能会有很多好处:电子园51单片机学习网|;eWuE1、已有程序的移植:在单片机领域工作很久的工程人员可能会保留有很多的早期用汇编语言编制的程序模块,并且这些模块已经经过实际应用的验证,如果重新用C编程,可能工作量很大,这时就可以用嵌入汇编的方式把以前的汇编模块植入新的应用,可以明显的加快开发的进度。5WhX)Ouq3g44606 2、局部功能需要足够短的执行时间:在有些应用中,部分的功能模块需要有很高的执行效率,而有些汇编的指令在C中没有对应的指令,这给我们对单片机的高效操作带来困难,嵌入汇编可是我们的程序执行更有效率。&K0u.g.S(Q44606 3、对一些特定地址进行操作:在C中我们要对特定地址进行读写,一般用以下两种方式:用_AT_指令定义变量;定义指向外部端口或数据地址的指针;在汇编中只需要使用MOVX A,DPTR或MOVX DPTR,A就可以了,这样可以增强程序的可读性。?&B&oYD)x6xx&x44606 4、其他的需要汇编的应用:在这里我们不可能举出所有可能要用汇编的例子,在你的应用中,你可能在一个或多个应用中感到C语言的不足,而需要用到汇编指令,请你记住,可以在C中嵌入汇编子程序,这对你的程序非常有用。9h3;oVO$F$QUN6)s44606 首先介绍一下调用汇编的参数传递规则,见下表3KSpXPdi2Io+h Vu44606 传递的参数char、1字节指针int、2字节指针long、float一般指针第一个参数R7R6,R7R4R7R1,R2,R3第二个参数R5R4,R5R1,R2,R3第三个参数R3R2,R3无R1,R2,R3在下面的例子中我们首先给出一个C程序调用汇编的例子,先说明一下,在这个例子中,你完全可以用C完成,我用这个例子,只是为了说明嵌入汇编的方法。电子园51单片机学习网qb7h3V例1:电子园51单片机学习网juO2z C下面是C语言的主程序.Ly%b.t4s/Pd0j44606 #include !A l;L&W| s944606#include 电子园51单片机学习网8Bp*+M8s*M$N/Yextern char asm(char c,char b);8pz(l6_-z44606bit VAL;%&y7|3uH3?u5D44606void main (void)NX_P4m-UN44606S hv qLIOr44606charout=0x49;电子园51单片机学习网u &-wQ C Ta;Mchardirect;电子园51单片机学习网9O2u!IK4oeucharkey;电子园51单片机学习网%XMNbA A5_:vvSCON = 0x50;/* SCON: mode 1, 8-bit UART, enable rcvr */10位异步接收器,可变。REN=1,允许接收数据电子园51单片机学习网0I R:c?34Uj KTMOD |= 0x20;/* TMOD: timer 1, mode 2, 8-bit reload */定时器1工作于模式2电子园51单片机学习网RE!a;U3L5KTH1 = 0xfd;/* TH1: reload value for 9600 baud 11.0592MHz */电子园51单片机学习网F%c T O M3Q NTR1 = 1;/* TR1: timer 1 run */S KTH*GU44606TI = 1;/* TI: set TI to send first char of UART */SCByX#t,vR:Z44606VAL=0;电子园51单片机学习网-a&Kn.n3Hwhile (1).vB LJf446060p,E#jM+ m#T44606key=getchar();电子园51单片机学习网 3UWf!Rif(key=R)电子园51单片机学习网)BlX/G/nqt*l电子园51单片机学习网t 8u9Grn*udirect=0X01;电子园51单片机学习网I|HQout=asm(out,direct);/*汇编子程序调用*/电子园51单片机学习网Q*EjJcHyprintf (Right rotaten);电子园51单片机学习网7d)SWKA CcTi%qv-G#k(L44606if(key=L)电子园51单片机学习网y a9Z+W5mVsswU.p44606direct=0X02;u;hZ*C&U44606out=asm(out,direct);/*汇编子程序调用*/AFo9St F.D44606printf (Left rotaten);电子园51单片机学习网4r4Z:(X-p*j sQ5B.nse q;R 44606printf(%bxn,out);sp5&Yuk5K44606电子园51单片机学习网z+IR!R)tZMc N o?.M44606 下面是汇编的子程序(文件名称asmtest.asm)tF60s)D44606电子园51单片机学习网.V,_%M.3oVM;_vK NAME ASM电子园51单片机学习网JRTh%j?PR?_asm?ASMTESTSEGMENT CODE电子园51单片机学习网 Z#Doi|$Jt?BI?_asm?ASMTESTSEGMENT BITLl N3r Y;Z44606PUBLIC?_asm?BIT电子园51单片机学习网,GY9Q-OWZ+nPUBLIC_asm电子园51单片机学习网)XV|SY7z&_RSEG?BI?_asm?ASMTEST电子园51单片机学习网)e/q56vH iB1X&m?_asm?BIT:电子园51单片机学习网5Ed)!VVAL:DBIT1电子园51单片机学习网 KB RFVRSEG?PR?_asm?ASMTEST4a)zJ.LN&lYm44606_asm:电子园51单片机学习网fO/ri|N MOV A,R7)I&bxDOP44606MOV C,VALmY+!|L3U,j$k44606DJNZ R5,JP1_ Y-t Wr.m44606RRC AAN#i-R6T M44606JP1:V r7x7L$hJb.K/f44606DJNZ R5,JP2电子园51单片机学习网LL9Sv*jRLC A电子园51单片机学习网-mU WvQaeJP2:LU,jS5V2S2t44606MOV90h,A0p2DE l.W44606MOV R7,A$ubwk2944606MOVVAL,Cs&hMy$D7jH1W44606RETmlBlW44606ENDu0B reV44606 现在对这个例子简短的说明,这个例子是用来驱动一个不带细分的三相步进电机,用的是循环移位的指令来实现的,由于一个字节是8位,如果算上进位位,共9位,通过付值,可以得到这样的数100100100,大家可以看到任意取中间的3位,相邻的每3位与它都一样,这样我们就可以去中间的3位输出到端口去驱动一个三相的步进电机,通过对这个9位的二进制数进行循环移位,可以实现电机的步进,在51系列单片机中有两条指令可以帮助我们进行循环移位操作,这就是RRC A和RLC A指令,我们只要把第九位保存好,在需要移位操作时,把它付给PSW中C,再执行循环移位就可以了。al94D9wu.O44606 在这个例子中,我发现了几个需要注意的地方:6Mx-UG&y|8m44606 1、在C程序中,我不能把VAL变量设为extern类型,否则在连接时会有警告,导致数值不能传递;电子园51单片机学习网 _t)a|2、在汇编模块中,不能把SEGMENT BIT段置为OVERLAYABLE即可覆盖段,如果置为可覆盖段,那么在进入汇编模块时VAL变量值丢失;电子园51单片机学习网3DE z%Au,W3、如果把VAL变量作为函数的参数传递,出现在返回后在执行printf函数后变量值丢失;xzOPQmCl1U44606 上面是本人写的一个C程序调用汇编的小例子,在本例中的几个printf函数是为了便于在Keil C51中模拟调试时观察运行结果的,在实际应用中可以将这几条去掉,这只是一个简短的示例,目的在于介绍一下C调用汇编的用法,希望对大家有帮助,同时由于本人水平有限,在程序中有些地方可能仍有不周到之处,欢迎广大单片机高手不吝指出。-在keil中嵌入汇编 1、其实在KEIL中嵌入汇编的方法很简单。如图1,在C文件中要嵌入汇编的地方用#pragma asm和#pragma endasm分隔开来,这样编译时KEIL就知道这中间的一段是汇编了。 2、在有加入汇编的文件中,还要设置编译该文件时的选项,如图2所示。 3、Generate Assembler SRC File 生成汇编SRC文件 Assemble SRC File封装汇编文件 (如图3的状态为选中) 选上这两项就可以在C中嵌人汇编了,设置后在文件图示中多了三个红色的小方块。 4、为了能对汇编进行封装还要在项目中加入相应的封装库文件, 在笔者的项目中编译模式是小模式所以选用C51S.LIB。这也是最常用的。这些库文件是中KEIL安装目录下的LIB目录中。 加好后就可以顺利编译了下面介绍直接嵌入汇编代码的方法:1、在 C 文件中要嵌入汇编代码片以如下方式加入汇编代码: #pragma ASM ; Assembler Code Here #pragma ENDASM2、在 Project 窗口中包含汇编代码的 C 文件上右键,选择“Options for .”,点击右边的“Generate Assembler SRC File”和“Assemble SRC File”,使检查框由灰色变成黑色(有效)状态;3、根据选择的编译模式,把相应的库文件(如 Small 模式时,是 KeilC51LibC51S.Lib)加入工程中, 该文件必须作为工程的最后文件;下载 (45.49 KB)2008-4-7 15:27keil C语言与汇编语言混合编程【转】1. C语言中嵌入汇编1、在 C 文件中要嵌入汇编代码片以如下方式加入汇编代码:#pragma ASM; Assembler Code Here#pragma ENDASM2、在 Project 窗口中包含汇编代码的 C 文件上右键,选择“Options for .”,点击右边的“Generate Assembler SRC File”和“Assemble SRC File”,使检查框由灰色变成黑色(有效)状态;3、根据选择的编译模式,把相应的库文件(如 Small 模式时,是 KeilC51LibC51S.Lib)加入工程中, 该文件必须作为工程的最后文件;4、编译,即可生成目标代码。来个实例吧:i nclude void main(void)P2=1;#pragma asm MOV R7,#10DEL:MOV R6,#20 DJNZ R6,$ DJNZ R7,DEL#pragma endasmP2=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,#10DEL: MOV R6,#20 DJNZ R6,$ DJNZ R7,DEL RETEND在example.c文件中,先声明外部函数,然后直接在main中调用即可。在example.a51中,?PR?DELAY100 SEGMENT CODE; 作用是在程序存储区中定义段,DELAY100为段名,?PR?表示段位于程序存储区内PUBLIC DELAY100; 作用是声明函数为公共函数RSEG ?PR?DELAY100; 表示函数可被连接器放置在任何地方,RSEG是段名的属性段名的开头为PR,是为了和C51内部命名转换兼容,命名转换规律如下:CODE ?PR?XDATA?XDDATA?DTBIT?BIPDATA?PD3. 有参数传递的函数调用记住哦,c文件和A51文件不能使用同一个文件名,不过我还不知道为什么会这样,有高手知道得话请告知。今天说说带参数传递的函数调用,在C51和汇编之间传递参数的方式有两种,一种是通过寄存器传递参数,C51中不同类型的实参会存入相应的寄存器,在汇编中只需对相应寄存器进行操作,即达到传递参数的目的。不同类型的数据及其传递参数的寄存器如下表所示: 在C和汇编混合编程的时候,存在C语言和汇编语言的变量以及函数的接口问题。在C程序中定义的变量,编译为.asm文件后,都被放进了.bss区,而且变量名的前面都带了一个下划线。在C程序中定义的函数,编译后在函数名前也带了一个下划线。例如:extern int num就会变成 .bss _num, 1extern float nums5就会变成.bss _nums, 5extern void func ( )就会变成 _func,一 汇编和C的相互调用可以分以下几种情况:(1) 汇编程序中访问c程序中的变量和函数。在汇编程序中,用_XX就可以访问C中的变量XX了。访问数组时,可以用_XX+偏移量来访问,如_XX+3访问了数组中的XX3。在汇编程序调用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位正确设置即可回复 引用 评分 TOP tiker 发短消息 加为好友 tiker 当前离线 UID375帖子71精华1积分18081阅读权限150在线时间41 小时注册时间2007-10-12最后登录2009-4-25超级版主4# 发表于 2008-4-7 15:40 | 只看该作者 深入剖析keil c51 - 从汇编到c51【转】main()函数和启动代码 汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和链接。下面看看它和main()函数是如何编译的;/主函数如下;void main(void) while (1) 这是个无条件空循环。 把上面的main()函数编译后的汇编程序和反汇编代码整理后对照如下;?C_C51STARTUP SEGMENT CODE?PR?main?TESTMAIN SEGMENT CODE ?STACK SEGMENT IDATA RSEG ?STACK DS 1 CSEG AT 0?C_STARTUP: LJMP STARTUP1 C:0x0000 020003 LJMP STARTUP1(C:0003) RSEG ?C_C51STARTUP STARTUP1: ;该段程序把内存清零; MOV R0,#IDATALEN - 1 C:0x0003 787F MOV R0,#0x7F; CLR A C:0x0005 E4 CLR A; MOV R0,A IDATALOOP: C:0x0006 F6 MOV R0,A; DJNZ R0,IDATALOOP C:0x0007 D8FD DJNZ R0,IDATALOOP(C:0006); MOV SP,#?STACK-1 ;设制CPU的堆栈起始地址C:0x0009 758107 MOV SP(0x81),#0x07; LJMP ?C_START C:0x000C 02000F LJMP main(C:000F) RSEG?PR?main?TESTMAINmain:; void main(void) C:0x000F 80FE SJMP main(C:000F) ;main()函数现在分析上面的汇编程序就会明白c51程序是如何启动的。该程序有三个代码段;第一个代码段?C_STARTUP在0x0000地址,是CPU第一条指令的入口,它只有一条长跳转指令,直接跳到第二个代码段.第二个代码段?C_C51STARTUP是可重定位的段,该程序把内存清零,然后再设置CPU的堆栈,最后跳转到main()函数.第三个代码段就是main()函数,在keil c51编译器里main()的段地址名就是?C_START。还有一个IDATA数据段?STACK就是堆栈,?STACK用于设制CPU的堆栈起始地址,这是由keil编译器自动完成的。/*/keil c51函数的返回值是存储在r0-r7中的。多字节变量在存储器里都是低地址存高位,高地址存低位。main()函数的局部变量都是放在存储器里的,不象别的函数先选寄存器r0-r7存放,如果不够用再存入存储器里。看下面的示例;c51程序;unsigned int SumXY(unsigned int X,Y);void main(void)unsigned int a,b,c; a=0x5500; b=0xaa; while (1) c=SumXY(a,b); unsigned int SumXY(unsigned int X,Y)unsigned int Z; Z=X+Y; return Z;编译后的反汇编代码列表;C:0x0000 020027 LJMP STARTUP1(C:0027) 4: void main(void) 5: unsigned int a,b,c; 6: a=0x5500; C:0x0003 750855 MOV 0x08,#0x55 ;ram地址0x08和0x09存放变量a=0x5500。C:0x0006 750900 MOV 0x09,#0x00 7: b=0xaa; C:0x0009 750A00 MOV 0x0A,#0x00 ;ram地址0x0A和0x0B存放变量b=0x00AA。C:0x000C 750BAA MOV 0x0B,#0xAA 8: while (1) 9: 10: c=SumXY(a,b); C:0x000F AD0B MOV R5,0x0B ;寄存器R4和R5传递变量a的值。C:0x0011 AC0A MOV R4,0x0AC:0x0013 AF09 MOV R7,0x09 ;寄存器R6和R7传递变量b的值。C:0x0015 AE08 MOV R6,0x08C:0x0017 120020 LCALL SumXY(C:0020) ;调用函数SumXY(a,b)求c=a+bC:0x001A 8E0C MOV 0x0C,R6 ;函数SumXY(a,b)返回的整型值存在R6和R7里,C:0x001C 8F0D MOV 0x0D,R7 ;把返回值存入变量c,ram地址0x0C和0x0D存放变量c 11: 12: 13:C:0x001E 80EF SJMP C:000F 14: unsigned int SumXY(unsigned int X,Y) 15: unsigned int Z; 16: Z=X+Y; C:0x0020 EF MOV A,R7 ;参数变量X放在寄存器R6和R7里C:0x0021 2D ADD A,R5 ;参数变量Y放在寄存器R4和R5里C:0x0022 FF MOV R7,AC:0x0023 EE MOV A,R6C:0x0024 3C ADDC A,R4 ;计算Z=X+Y; C:0x0025 FE MOV R6,A ;局部变量Z也放在寄存器R6和R7里 17: return Z; ;由寄存器R6和R7里返回函数的值C:0x0026 22 RET 151: MOV SP,#?STACK-1 152: ; This code is required if you use L51_BANK.A51 with Banking Mode 4 153: ; EXTRN CODE (?B_SWITCH0) 154: ; CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 C:0x0027 75810D MOV SP(0x81),#0x0D 155: LJMP ?C_START C:0x002A 020003 LJMP main(C:0003)函数的入口地址,如何调用汇编函数,c和汇编的混合编程/*/c函数的函数名是一个指向函数的指针常量,它的值就是函数的入口地址。从汇编程序上看,函数名也是该汇编函数代码段的入口地址标号。调用汇编函数就是调用汇编函数的入口地址标号,但是要注意c函数名和汇编函数标号之间的转换规则。1. 不带参数的汇编函数标号和c函数名相同.2. 带参数的汇编函数标号在c函数名前加字符_,例如;如果c函数名是SumXY,汇编函数标号是_SumXY3. 再入函数的汇编函数标号在c函数名前加_?,例如;如果c函数名是DoTask,汇编函数标号是_?DoTask程序示例,该例有两个文件,一个文件是exam1.c,一个文件是funcasm.c主程序文件: exam1.cextern unsigned int SumXY(unsigned int X,Y); /声明外部汇编语言函数,和声明c函数方法相同。extern void Delay(unsigned char T);void main(void)unsigned int a,b,c; a=0x5500; b=0x00aa; while (1) Delay(100); c=SumXY(a,b); 混合编程文件: funcasm.c/c和汇编的混合编程演示./注意要把汇编语言函数放在文件前面。/求Z=X+Y的汇编语言函数,Z,X,Y是整型数。#pragma ASM PUBLIC_SumXY?PR?_SumXY?FUNCASM SEGMENT CODE RSEG ?PR?_SumXY?FUNCASM_SumXY: ;求Z=X+Y MOV A,R7 ;参数X放在寄存器R6和R7里 ADD A,R5 ;参数Y放在寄存器R4和R5里 MOV R7,A MOV A,R6 ADDC A,R4 ;扑鉠=X+Y; MOV R6,A ;局部变量Z也放在寄存器R6和R7里 RET #pragma ENDASM/c语言函数,延时函数。void Delay(unsigned char T)unsigned char i; for (i=0;iT;i+) for (i=0;iT;i+) /*/上面程序编译后的反汇编代码列表; C_STARTUP:C:0x0000 020041 LJMP STARTUP1(C:0041) main:/给变量a和b赋值 a

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论