使用小模式的关键字是 SMALL。 在小模式下,所有的变量(除非是显式地进行了存储位置申
明),都存放在8051芯片的内部存储器中。而8051对内部存储器的访问速度是最快的(典型情况下
为1或2个时钟周期),并且产生的访问这些变量的代码也是最少的,指令是最短的。请参看下面的
循环代码:
for (i = 0; i < 100; i++)
{
do_nothing ();
}
在不同的编译模式下(小模式和巨模式),将生成不同的目标代码。
stmt level source
1 #pragma small
2
3 void do_nothing (void);
4
5
6 void func (void)
7 {
8 1 unsigned char i;
9 1
10 1 for (i = 0; i < 100; i++)
11 1 {
12 2 do_nothing ();
13 2 }
14 1 }
; FUNCTION func (BEGIN)
; SOURCE LINE #10
0000 E4 CLR A
0001 F500 R MOV i,A
0003 ?C0001:
0003 E500 R MOV A,i
0005 C3 CLR C
0006 9464 SUBB A,#064H
0008 5007 JNC ?C0004
; SOURCE LINE #12
000A 120000 E LCALL do_nothing
; SOURCE LINE #13
000D 0500 R INC i
000F 80F2 SJMP ?C0001
; SOURCE LINE #14
0011 ?C0004:
0011 22 RET
; FUNCTION func (END)
在小模式下,变量I被放置在内部数据存储器中。访问变量I的指令,如:
MOV A,i
和 INC I
只需要2字节。每条指令运行只需要1个时钟周期。整个代码长度只有17字节。
下面是同样的代码在巨模式下的编译结果:
; FUNCTION func (BEGIN)
; SOURCE LINE #10
0000 E4 CLR A
0001 900000 R MOV DPTR,#i
0004 F0 MOVX @DPTR,A
0005 ?C0001:
0005 900000 R MOV DPTR,#i
0008 E0 MOVX A,@DPTR
0009 C3 CLR C
000A 9464 SUBB A,#064H
000C 500B JNC ?C0004
; SOURCE LINE #12
000E 120000 E LCALL do_nothing
; SOURCE LINE #13
0011 900000 R MOV DPTR,#i
0014 E0 MOVX A,@DPTR
0015 04 INC A
0016 F0 MOVX @DPTR,A
0017 80EC SJMP ?C0001
; SOURCE LINE #14
0019 ?C0004:
0019 22 RET
; FUNCTION func (END)
在巨模式下,变量i被放置在外部数据存储器中。为访问变量I,编译器必须首先加载数据指针
指向正确的地址(参见偏移0001h到0004h)。这2条指令就需要4个时钟周期。使变量i 增量的操
作需要6 字节的存储空间及7个时钟周期。总的代码大小达到25 字节。变量定位原则是将需要频繁
访问的数据对象放置在8051的内部数据存储器中。硬件访问内部数据存储器要比访问外部数据存储
器有效的多。内部数据存储器由寄存器组、位变量区域、堆栈和其它用户自定义变量共享。可以使
用关键字data来指定。
由于受存储容量的限制(内部数据存储器为128字节,最多256字节)。 在整个软件中使用的
所有该类型变量都将占据这个区域,并且总的尺寸不能超过相应芯片的物理限制。在某些情况下,
就不得不将一些数据对象放置到外部数据存储器中。有2种办法可以进行该操作。
一是:改变存储模式,让编译器来自动完成该操作。这是最简单的方法,但需要产生更多的代码并
降低运行速度。
二是:手工将需要放置在外部数据存储器中的数据对象使用关键字xdata进行申明。这种方式不会
产生额外的代码。
变量尺寸
8051系列是8位的CPU。使用8位的数据类型将比使用其它类型的数据要有效的多(如:char
和unsigned char就比使用int或long类型有效)。所以,在任何情况下应该首先使用最短的数
据类型。
C51编译器直接支持所有的字节操作指令。除非特别指明,否则字节类型不会被转换成整数类
型来操作。参见INTPROMOTE关键字。
例如:两个字节类型的数据乘操作,将直接使用8051的单指令MUL AB。而如果使用其它类型的数
据,则会导致对编译器库函数的调用。
无符号类型Unsigned
8051系列的处理器并不直接支持浮点数的运算。因此,编译器必须对浮点数运算产生额外的
代码。因此,任何时候都必须首先考虑使用无符号数据类型。
局部变量
在遇到循环或其它临时计算操作时,应尽可能地使用局部变量。作为编译器优化处理过程的一
部分,临时变量将被编译器尽可能地放置在寄存器中。而寄存器访问是最快速的。在具体程序中,
可以申明为unsigned char和unsigned int变量类型。
其它原因
通常最影响程序代码质量的原因并不是编译器生成代码的效率,而是软件本身为解决其目标问
题而使用的算法类型。采用更有效的算法,通常比其它方式来达到减少代码尺寸和提高运行速度的
目的更为有效。例如:heap排序算法总是比bubble算法更有效。