在用keil C51开发SS881X/SS880X系列芯片的时候,对于一些全局变量的初始化,会有如下两种不同的声音:

第一种:在定义这个全局变量的时候,同时将该变量赋值,代码如下:

char tmp0 = 0x55;           // 不好,不要这样做
char tmp1 = 0xAA;

void main(void)
{
    while (1);
}

第二种:定义全局变量之后,去将该变量赋值,代码如下:

char tmp0;
char tmp1;

void main(void)
{
    tmp0 = 0x55;          // GOOD,建议这样做
    tmp1 = 0xAA;
    while (1);
}

那么这两种方法对一些全局变量初始化有什么异同呢?显而易见tmp0和tmp1最终都分别赋值成了0x55、0xAA,所以他们就真的没区别吗?接下来我们看一下这两种方式编译出来的代码大小,第一种编译出来152字节,第二种编译出来23字节。很明显,这两种方法的差别巨大。

接下来,我们需要刨根问底,首先从汇编下手:

先看一下方法2的反汇编,因为它的结果比较符合我们的常识。

                 C_STARTUP:
C:0x0000    020003   LJMP     C:0003
C:0x0003    787F     MOV      R0,#0x7F
C:0x0005    E4       CLR      A
C:0x0006    F6       MOV      @R0,A
C:0x0007    D8FD     DJNZ     R0,C:0006
C:0x0009    758109   MOV      SP(0x81),#tmp1(0x09)
C:0x000C    02000F   LJMP     main(C:000F)
                 main:
C:0x000F    750855   MOV      tmp0(0x08),#0x55
C:0x0012    7509AA   MOV      tmp1(0x09),#PDRV0(0xAA)
C:0x0015    80FE     SJMP     C:0015

可以看到执行main之前,还有一些指令,并且这段代码段是以C_STARTUP命名的,C_STARTUP其实就是整个程序执行的入口,地址必然是0x0000。那么接下来有一段循环,从0x0003->0x0007这些指令其实是做一件事情,就是把RAM地址的00H~7FH这128个字节的RAM清零,并且用的是间接寻址的方法(SS880X/SS881X系列的前128字节的RAM既可以直接寻址也可以间接寻址),等到清零了这128字节RAM以后,初始化了一下栈指针寄存器,紧接着就跳转到main函数中了,

在main函数中,就是直接对已经分配好地址的tmp0和tmp1进行赋值,这里是直接将常数赋值到地址的MOV direct, #data指令,就这两条就直接把全局变量的初始化搞定。

那么再看一下方法1的反汇编,看一下他究竟干了点啥。

                 C_STARTUP:
C:0x0000    020003   LJMP     C:0003
C:0x0003    787F     MOV      R0,#0x7F
C:0x0005    E4       CLR      A
C:0x0006    F6       MOV      @R0,A
C:0x0007    D8FD     DJNZ     R0,C:0006
C:0x0009    758109   MOV      SP(0x81),#tmp1(0x09)
C:0x000C    02004A   LJMP     C_START(C:004A)
C:0x000F    020096   LJMP     main(C:0096)
C:0x0012    E4       CLR      A
C:0x0013    93       MOVC     A,@A+DPTR
C:0x0014    A3       INC      DPTR
C:0x0015    F8       MOV      R0,A
C:0x0016    E4       CLR      A
C:0x0017    93       MOVC     A,@A+DPTR
C:0x0018    A3       INC      DPTR
C:0x0019    4003     JC       C:001E
C:0x001B    F6       MOV      @R0,A
C:0x001C    8001     SJMP     C:001F
C:0x001E    F2       MOVX     @R0,A
C:0x001F    08       INC      R0
C:0x0020    DFF4     DJNZ     R7,C:0016
C:0x0022    8029     SJMP     C:004D
C:0x0024    E4       CLR      A
C:0x0025    93       MOVC     A,@A+DPTR
C:0x0026    A3       INC      DPTR
C:0x0027    F8       MOV      R0,A
C:0x0028    5407     ANL      A,#0x07
C:0x002A    240C     ADD      A,#0x0C
C:0x002C    C8       XCH      A,R0
C:0x002D    C3       CLR      C
C:0x002E    33       RLC      A
C:0x002F    C4       SWAP     A
C:0x0030    540F     ANL      A,#0x0F
C:0x0032    4420     ORL      A,#0x20
C:0x0034    C8       XCH      A,R0
C:0x0035    83       MOVC     A,@A+PC
C:0x0036    4004     JC       C:003C
C:0x0038    F4       CPL      A
C:0x0039    56       ANL      A,@R0
C:0x003A    8001     SJMP     C:003D
C:0x003C    46       ORL      A,@R0
C:0x003D    F6       MOV      @R0,A
C:0x003E    DFE4     DJNZ     R7,C:0024
C:0x0040    800B     SJMP     C:004D
C:0x0042    0102     AJMP     C:0002
C:0x0044    04       INC      A
C:0x0045    08       INC      R0
C:0x0046    102040   JBC      0x24.0,C:0089
C:0x0049    8090     SJMP     C:FFDB
C:0x004B    00       NOP     
C:0x004C    8FE4     MOV      0xE4,R7
C:0x004E    7E01     MOV      R6,#0x01
C:0x0050    93       MOVC     A,@A+DPTR
C:0x0051    60BC     JZ       C:000F
C:0x0053    A3       INC      DPTR
C:0x0054    FF       MOV      R7,A
C:0x0055    543F     ANL      A,#0x3F
C:0x0057    30E509   JNB      0xE0.5,C:0063
C:0x005A    541F     ANL      A,#0x1F
C:0x005C    FE       MOV      R6,A
C:0x005D    E4       CLR      A
C:0x005E    93       MOVC     A,@A+DPTR
C:0x005F    A3       INC      DPTR
C:0x0060    6001     JZ       C:0063
C:0x0062    0E       INC      R6
C:0x0063    CF       XCH      A,R7
C:0x0064    54C0     ANL      A,#INTCON2(0xC0)
C:0x0066    25E0     ADD      A,ACC(0xE0)
C:0x0068    60A8     JZ       C:0012
C:0x006A    40B8     JC       C:0024
C:0x006C    E4       CLR      A
C:0x006D    93       MOVC     A,@A+DPTR
C:0x006E    A3       INC      DPTR
C:0x006F    FA       MOV      R2,A
C:0x0070    E4       CLR      A
C:0x0071    93       MOVC     A,@A+DPTR
C:0x0072    A3       INC      DPTR
C:0x0073    F8       MOV      R0,A
C:0x0074    E4       CLR      A
C:0x0075    93       MOVC     A,@A+DPTR
C:0x0076    A3       INC      DPTR
C:0x0077    C8       XCH      A,R0
C:0x0078    C582     XCH      A,DPL(0x82)
C:0x007A    C8       XCH      A,R0
C:0x007B    CA       XCH      A,R2
C:0x007C    C583     XCH      A,DPH(0x83)
C:0x007E    CA       XCH      A,R2
C:0x007F    F0       MOVX     @DPTR,A
C:0x0080    A3       INC      DPTR
C:0x0081    C8       XCH      A,R0
C:0x0082    C582     XCH      A,DPL(0x82)
C:0x0084    C8       XCH      A,R0
C:0x0085    CA       XCH      A,R2
C:0x0086    C583     XCH      A,DPH(0x83)
C:0x0088    CA       XCH      A,R2
C:0x0089    DFE9     DJNZ     R7,C:0074
C:0x008B    DEE7     DJNZ     R6,C:0074
C:0x008D    80BE     SJMP     C:004D
C:0x008F    0108     AJMP     C:0008
C:0x0091    5501     ANL      A,0x01
C:0x0093    09       INC      R1
C:0x0094    AA00     MOV      R2,0x00
     4: void main(void)
C:0x0096    80FE     SJMP     main(C:0096)

好家伙,这么多。笔者也没有一条一条的去看他究竟是在干啥,只关注一下RAM的初始化在哪里,经过单步调试发现,初始化RAM的指令总结出来在如下几条:

C:0x0016    E4       CLR      A
C:0x0017    93       MOVC     A,@A+DPTR
C:0x0018    A3       INC      DPTR
C:0x0019    4003     JC       C:001E
C:0x001B    F6       MOV      @R0,A

重点在于MOVC A,@A+DPTR这里,看到这条指令,笔者就猜想:定义全局变量的同时,将该变量初始化的操作,在编译器眼里,其实是将需要写入到RAM的初始值,通过查表的形式,将代码区的某一段位置读出,再去写入到RAM中去。(可以类比成为读写XDATA

那么有什么方法可以论证这一点呢,可以结合一下编译出来的BIN文件以及反汇编来看,我们想要做的是把RAM的08H写0x55,RAM的09H写0xAA,所以如下反汇编其实是没有意义的(并且也不会跑到),他的作用就是去做了一个表,然后放在代码区中的某段地址后,在跑代码的时候将这个表解出来,所以代码量增加了这么多。

C:0x008F    0108     AJMP     C:0008
C:0x0091    5501     ANL      A,0x01
C:0x0093    09       INC      R1
C:0x0094    AA00     MOV      R2,0x00

如果您有耐心读到这里,想必已经了解了keil C51在初始化全局变量时候的坑了吧,最后给一个结论:

定义全局变量之后,一定要在函数中(如初始化时)将该变量赋初值(初值为0的话就不需要赋值,系统启动时会默认清零),而不是在变量定义的时候赋值。

 

原创文章,转发请注明出处。昇生微电子www.sinhmicro.com

标签:
SS880X SS881X keil C51 全局变量 初值

加载对话