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?TEST
fun2:
; SOURCE LINE # 12
; {
; SOURCE LINE # 13
; }
; SOURCE LINE # 14
RET
; END OF fun2
;
; void fun1(unsigned char i)
RSEG ?PR?_fun1?TEST
_fun1:
USING 0
; SOURCE LINE # 16
;—- Variable ‘i?240’ assigned to Register ‘R7’ —-
; {
; SOURCE LINE # 17
; fun2();
; SOURCE LINE # 18
LCALL fun2
?C0003:
; while(i–);
; SOURCE LINE # 19
MOV R6,AR7
DEC R7
MOV A,R6
JNZ ?C0003
; }
; SOURCE LINE # 20
?C0005:
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?TEST
_fun1:
USING 0
; SOURCE LINE # 14
MOV i?140,R7
; {
; SOURCE LINE # 15
; fun2();
; SOURCE LINE # 16
LCALL fun2
?C0002:
; while(i–);
; SOURCE LINE # 17
MOV R7,i?140
DEC i?140
MOV A,R7
JNZ ?C0002
; }
; SOURCE LINE # 18
?C0004:
RET
; END OF _fun1
;
; void fun2(void)
RSEG ?PR?fun2?TEST
fun2:
; SOURCE LINE # 20
; {
; SOURCE LINE # 21
; }
; SOURCE LINE # 22
RET
; END OF fun2
fun2()在fun1()调用后界说,因fun1()调用fun2()时不知道fun2()对寄存器的运用状况,则以为fun2()改动了一切的寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、, R6、 R7)。由于fun1()以为fun2()改动了寄存器的值(包含R7),因而i尽管经过R7传递,但因已因调用fun2()而改动,所以不能再存在R7了,而上在RAM中额定的用一个Byte来存储。
这也就解说了在开始时的那个问题,参数i的存储是看问题而定的。
哈哈,是否很风趣呢。在节省RAM方面,这但是一个很有用的特性哦。(咱们是否也为自己的节省了1Byte的RAM)
这个比如还解说了第二个特性,函数调用函数时除在仓库中存入回来地址之外,不在仓库中保存其它任何寄存器(ACC、 B、 DPH、 DPL、 PSW、 R0、 R1、 R2、 R3、R 4、 R5、R6、R7)的内容。函数在调用函数前,尽量不在这些寄存器中保存有用的数据,真实无法防止,则把有用数据存入固定的RAM中。
关于中止函数问题,当你看到下面的程序相差55 Byte时,不知你会怎么想的。
例2:
void OSTImeDly(void); //using 1
staTIc void TImer0OVInt(void) interrupt 1 //using 1
{
TR0 = 0;
TH0 = 100;
TL0 = 100;
TR0 = 1;
OSTImeDly();
}
void OSTimeDly(void) //using 1
{
}
与
void OSTimeDly(void) //using 1
{
}
static void Timer0OVInt(void) interrupt 1 //using 1
{
TR0 = 0;
TH0 = 100;
TL0 = 100;
TR0 = 1;
OSTimeDly();
}
它们的汇编代码分别是,
; static void Timer0OVInt(void) interrupt 1 //using 1
RSEG ?PR?Timer0OVInt?TEST
USING 0
Timer0OVInt:
PUSH ACC
PUSH B
PUSH DPH
PUSH DPL
PUSH PSW
MOV PSW,#00H
PUSH AR0
PUSH AR1
PUSH AR2
PUSH AR3
责任编辑;zl