2回答

0收藏

[求助] Keil C51的一些有趣特性

51单片机 51单片机 4436 人阅读 | 2 人回复 | 2013-04-06

  关于C51函数在调用前和调用后定义产生的代码不一样这个问题,我刚才也碰到了。自己试了下,发现在调用前定义确实会是代码更少,执行时间更少。至于是什么原因,我也不知道。百度了一下,发下下面这篇文章的主人也发现了这个问题,不过他也没能给出确切的解释。有人说可以勾选Global  Register Coloring来解决,不过我试了下,好像没区别。不知道有人可以解释这个问题吗?
  
        Keil C51的一些有趣特性

作者:yanfeng
E-mail:szq106@163.com

  Keil c51号称作为51系列单片机最好的开发环境,大家一定都很熟悉。它的一些普通的特性大家也都了解,(书上也都说有)如:因为51内的RAM很小,C51的函数并不通过堆栈传递参数(重入函数除外),局部变量也不存储在堆栈中,而是存在于固定的RAM中及寄存器中。那么看一下下面的程序。

void fun1(unsigned char i)
{
       …
}

  正常情况参数i通过R7传入函数,那么它的实际地址在什么地方呢?就是R7吗?回答这个问题之前我们先来了解keil c51的几个有趣的特性(不考虑重入函数)。

一、              函数在调用前定义与在调用后定义产生的代码是有很大差别的(特别是在优化级别大于3级时)。(本人也不太清楚为什么,大概因为在调用前定义则调用函数已经知道被调用函数对寄存器的使用情况,则可对函数本身进行优化;而在调用后进行定义则函数不知被调用函数对寄存器的使用情况,它默认被调用函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)都已经改变,因此不在这些寄存器中存入有效的数据)

二、              函数调用函数时除在堆栈中存入返回地址之外,不在堆栈中保存其它任何寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)的内容。(除非被调用函数使用了using特性)

三、              中断函数是一个例外,它会计算自身及它所调用的函数对寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)的改变,并保存相应它认为被改变了的寄存器。

四、              使用C写程序时,尽量少使用using n (n=0,1,2,3)特性。(这个特性在本人使用的过程中存在一些问题,不知算不算是一个小bug)
以下的试验都是在(环境 keil c51 v7.20)中,优化级为default下完成。

  先看第一个特性问题。
例1:
void fun2(void)
{

}

void fun1(unsigned char i)
{
       fun2();
       while(i--);
}

  它的汇编代码如下:
; void fun2(void)
       RSEG  ?PR?fun2?
fun2:
; {
                     ; SOURCE
; }
       RET      
; END OF fun2

; void fun1(unsigned char i)
       RSEG  ?PR?_fun1?
_fun1:
       USING    0
;---- Variable 'i?240' assigned to Register 'R7' ----
; {
;     fun2();
       LCALL       fun2
;     while(i--);
       MOV         R6,AR7
       DEC         R7
       MOV         A,R6
       JNZ         ?C0003
; }
       RET      
; END OF _fun1

  从中可以看到fun2()在fun1()前先定义,fun1()知道fun2()对寄存器的使用情况,知道R7没有改变,而参数i存于R7中,即i既是R7。(;---- Variable 'i?140' assigned to Register 'R7' ----)
  看另一情况
void fun2(void);

void fun1(unsigned char i)
{
       fun2();
       while(i--);
}

void fun2(void)
{

}
  汇编代码如下:

; void fun1(unsigned char i)
       RSEG  ?PR?_fun1?
_fun1:
       USING    0
       MOV         i?140,R7
; {
;     fun2();
       LCALL       fun2
?C0002:
;     while(i--);
       MOV         R7,i?140
       DEC         i?140
       MOV         A,R7
       JNZ         ?C0002
; }
?C0004:
       RET      
; END OF _fun1

; void fun2(void)
       RSEG  ?PR?fun2?
fun2:
; {
                     ; SOURCE
; }


分享到:
回复

使用道具 举报

回答|共 2 个

倒序浏览

沙发

zca123

发表于 2013-4-6 17:36:57 | 只看该作者

mark。改天看
板凳

dwwzl

发表于 2013-4-7 08:23:47 | 只看该作者

值得一看!!!
用技术,爱生活!
您需要登录后才可以回帖 注册/登录

本版积分规则

关闭

站长推荐上一条 /2 下一条