原创 C51嵌入汇编程序

2010-1-4 19:38 5125 9 9 分类: MCU/ 嵌入式

                                 C51嵌入汇编程序


<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

貌似现在大学开单片机课都是只学51汇编程序,基本不讲C51,还美其名曰说汇编是根本,其实这些都是bullshitC语言之所以被越来越多的人采用,就是因为大家都能看懂,而汇编一来每种不同的MCU对应不同的汇编语言,二来真正有心看汇编的人少。但这里要讲的是C51中嵌入汇编程序。


为什么要用汇编程序?


1.      精确。STC<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />89C51(传统51单片机)12MHz晶振,机器周期为1us(1MHz),如果采用单机器周期的指令时,若NOP,这样就能精确延时。


2.      效率高。C51的话,就把编译的工作交给了IDE,对于IDE究竟编译成的汇编是怎么样的,大家都不知道,只能仿真时反汇编来看。这样会做了很多不必要的工作,如果追求效率的话,还得用汇编。


下面介绍如何在51单片机C语言中调用汇编程序:


 


一.函数名的转换及其命名规则


<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" />


 

b0cfacd1-f3c4-46ad-8b80-57d3c2298dec.gif


 


二.参数传递



 


点击看大图


 


   如用C51编写的AD5764R DA采集函数


void Write_DA(char reg, char DAchannel, int DACdata)


第一个参数 reg放在R7


第二个参数DAchannel放在R5


第三个参数DACdata  放在R2R3


 


三.函数返回值传递



 

点击看大图 


 


下面利用C51编译器(Keil)将一个C源文件编译成一个相应的汇编源文件,而不是目标文件,在这个汇编文件中,可清楚地看到每一个参数的传递方法。


 


step1. 按写普通c51程序方法,建立工程,在里面导入main.func.c文件。


 

//main.c

#include "C8051F120.h"

#define uchar unsigned char  

extern uchar func(uchar x,uchar y); /*func */ 

void main(void) /*  */ 

{  

    func(0x12,0x34); /* func */

}

//func.c

#define uchar unsigned char 

uchar func(uchar x,uchar y) /* func */ 

{ 

     return (x/y); /* x/y */ 

}

 

step2. Project 窗口中包含汇编代码的 C 文件上右键,选择“Options for ...”,点击右边的“Generate Assembler SRC File”“Assemble SRC File”,使检查框由灰色变成黑色(有效)状态;

09d25a4d-7378-44c9-ae4b-6ab4c0a86002.JPG


4dfad993-30bb-43d5-9b6f-91f576f46cf6.jpg


step3. 根据选择的编译模式,把相应的库文件(如 Small 模式时,是 Keil\C51\Lib\C51S.Lib)加入工程中,该文件必须作为工程的最后文件;

b226ac76-5020-442e-bd70-e1cff04a5732.jpg


 


step4. build这个工程后将会产生一个MAIN.SRC的文件,将这个文件改名为FUNC.A51,然后在工程里去掉库文件(如C51S.Lib)func.c文件,而将FUNC.A51添加到工程里。


 


//FUNC.A51文件


; COMPILER INVOKED BY:


;        C:\Keil\C51\BIN\C51.EXE func.c BROWSE DEBUG OBJECTEXTEND SRC(.\func.SRC)


 


NAME   FUNC


 


?PR?_func?FUNC       SEGMENT CODE


       PUBLIC       _func


; #define uchar unsigned char


;


; uchar func(uchar x,uchar y) /* 函数func */


 


       RSEG  ?PR?_func?FUNC


_func:


       USING  0


                     ; SOURCE LINE # 3


;---- Variable 'y?041' assigned to Register 'R5' ----


;---- Variable 'x?040' assigned to Register 'R7' ----


; {


                     ; SOURCE LINE # 4


;      return (x/y); /* 计算x/y并返回结果 */


                     ; SOURCE LINE # 5


       MOV  A,R7


       MOV  B,R5


       DIV      AB


       MOV  R7,A


; }                 ; SOURCE LINE # 6


?C0001:


       RET   


; END OF _func


 


       END


 


step5. 检查main.c“Generate Assembler SRC File”“Assemble SRC File”是否有效,若是有效则点击使检查框变成无效状


       态;再次build这个工程,到此你已经得到汇编函数的主体,修改函数里面的汇编代码就得到你所需的汇编函数了。


(参考:如何在 KEIL C51v6.21 中调用汇编函数的一个示例


http://www.mcufan.com/article/c2asm2c.html)


 


 


    在这段汇编函数中有下面四行


NAME   FUNC                              //定义文件名


?PR?_func?FUNC    SEGMENT CODE      //声明一个段


       PUBLIC       _func                        //段入口地址


RSEG  ?PR?_func?FUNC                   //选择定义过的段


 


 


SEGMENT指令
SEGMENT
指令用来声明一个再定位段和一个可选的再定位类型。
格式: 再定位段名 SEGMENT 段类型〔再定位类型〕
其中,再定位段名用于指明所声明的段。
段类型用于指定所声明的段将处的存储器地址空间。
可用的段类型有 CODEXDATADATAIDATABIT
STACK_SEG SEGMENT IDATA
DATA_SEG SEGMENT DATA

RSEG---再定位段选择指令
再定位段选择指令为RSEG,用于选择一个已在前面定义过的再定位段作为当前段。
格式: RSEG 段名
段名必须是在前面已经声明过的再定位段。
DATA_SEG SEGMENT DATA
;声明一个再定位DATA
RSEG DATA_SEG
;选择前面声明的再定位DATA段作为当前


 



b6fc0cda-1f05-469b-addf-f111be93a687.gif


 


上面的模块名就是文件名的意思。


   函数名又分:


1:无参函数 ?PR?函数名?文件名
2:
有参函数 ?PR?_函数名?文件名
3:
再入函数 ?PR?_?函数名?文件名


 


是程序代码或数据对象的存储单位。程序代码放到代码段,数据对象放到数据段。段分两种,
  一是绝对段,一是再定位段。绝对段在汇编语言中指定,在用L51联接的时候,地址不会改变。用于如访问一个固定存储器的i/o,或提供中断向量的入口地址。而再定位段的地址是浮动的。它的地址有L51对程序模块连接时决定。 C51对源程序编译所产生的段都是再定位段,它都有段名和存储类型。 绝对段没有段名。
  
  例如,你写用C写了一个函数 void func(void) { …} , 存在func.c中,用编译器编译以后. SRC FILE中看到:


?PR? func?FUNC SEGMENT CODE //(函数放到代码段中)


写这个函数体的时候:


 RSEG ?PR? func?FUNC           //选择已定位的代码段为当前段
  _func:
  ……                //代码


 


 


又例如 你定义了全局变量


unsigned char data temp1,temp2;


unsigned char xdata temp3;


test.c文件中,编译器会为每个文件分0到多个全局数据段,相同类型的全局变量被存到同一段中。所以上面会编译成如下:


 

a3b83af9-52da-4e0c-81d8-f1eb264378e8.jpg


 


   上面编译出来的汇编还使用了以下两个的伪指令。


 


DS ---预留存储区命令
格式: 〔标号: DS  表达式值
其功能是从指定地址开始,定义一个存储区,以备源程序使用。
存储区预留的存储单元数由表达式的值决定。
TMP:  DS 1
从标号TEP地址处开始保留1个存储单元(字节)。


 


USING指令
USING
指令通知汇编器使用8051的哪一个工作寄存器组。
格式: USING 表达式 (值必须为03,默认值为0。)
USING 0
使用第0组工作寄存器。


 


    更多伪指令的使用可以参考下面网页:汇编伪指令注释


http://www.willar.com/forum_view.asp?forum_id=18&view_id=4692


 


本文还参考了下面文章:


1)     Franklin C51A51函数的相互调用


http://www.avrw.com/article/art_101_3596.htm


2)     C51嵌入汇编


http://blog.21ic.com/user1/6084/archives/2009/63605.html


3) C51和汇编的混合编程


http://blog.ednchina.com/jeddite/260843/message.aspx


4)单片机C和汇编语言混合编程


http://www.docin.com/p-5227805.html


5) KEIL的混合编程操作


http://hi.baidu.com/txz01/blog/item/868ad8d4aeea8309a08bb79c.html/cmtid/7cc6d494e1c9e612d31b7075


5) KeilC中嵌入汇编 - 电子杂院 - 博客大巴


http://kingmoon.blogbus.com/logs/5350441.html


 


 


 


本文实验程序:


C8051F120采用AD5764R DA输出程序(C51)


https://static.assets-stash.eet-china.com/album/old-resources/2010/1/4/331c9fd5-6d14-4414-a147-1ba6af5a8dd2.rar


C8051F120采用AD5764R DA输出程序(C51嵌入汇编)


 https://static.assets-stash.eet-china.com/album/old-resources/2010/1/4/e1cc25d8-197d-44b6-90e2-bbb5e44748fd.rar


 


 


    还有一种直接在C51中嵌入汇编的的方法,具体可以参考


C51中直接嵌入汇编的方法


方法与上面的有些类似


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编译,即可生成目标代码。

文章评论0条评论)

登录后参与讨论
我要评论
0
9
关闭 站长推荐上一条 /2 下一条