先说结论:
1、 C51中mian()并不是被调用的,而是跳转进去的;
2、 在main()中调用return语句,将会从栈中弹出SP大小的数据到SP中,进而程序会运行到弹出数据指向的地址,C51中可能就是SP指针返回0x0000。
昨天和我合作一个工程的同事在调试的时候,发现单片机程序会经常出现重启的现象,当时我认为可能是数组溢出、指针越界导致的程序跑飞。
后来同事将有问题的语句定位到某个分支中的“return 0;”语句上,我一看这居然是在main()函数上使用了“return 0;”,认为程序会在调用这个语句后,
PC指针离开main(),继续执行main()函数后面的代码导致程序跑飞(同事估计是受到VC等PC编译环境的影响,汗~)。
不过,回到坐位后却想起return对应的指令,会负责将栈中的值取出来赋值给PC指针,这样的话执行“return 0;”后应该返回到调用main()函数的地方。
既然有疑问就动手吧,首先向同事拿到了出问题的工程,在main()开始的地方设置了两个“return 0;”(分别是第25行和32行)。
然后进入仿真模式,如下图,可以观察到sp指针指向起始值0x07,PC指针指向0x0000,PC指针的长度为2。
图1 请无视main()里面的那个void~~
在disassembly界面单步跟踪,发现了跳转到main()的地方,并且可以看到sp是被赋值为0x28的。(不过这LJMP只是跳转而已,看来没有调用main()的过程)
图2
全速运行到图1中0x0003的断点后,看到的寄存器如下:
图3
sp指针在之前已经被设置成0x28了,这时候如果有ret语句被执行的话,0x27和0x28地址RAM上的数据应该就是会被弹出到PC指针上的。
现在观察下RAM中的情况:
图4
不过因为这两个地址(0x27和0x28)上的数据都是0,所以单步可以发现执行反汇编窗口0x0008地址上的RET语句后PC指针又跑回0x0000上了,这就是重启的原因:
图5 单步到0x0008,黄色的箭头是PC指针
图6 指针返回到了0x0000,并且sp指针退了两格
下面为了证明,执行RET指令后,SP指针指向的数据是退出函数后的位置,再加个实验,首先将图1中第25行的“return 0;”注释掉。
图7 即将调用MCU_Init()
运行到调用MCU_Init()这步上,可以看到PC指针为0x02EF,而退出后应该是运行到0x02F2上,单步进入到MCU_Init()后,0x29和0x2A果然被写入了0x02F2(进入函数,SP首先会将PC
压入栈,PC指针自然是在0x29和0x2A上),并且可以看出SP指针的低位是先被压入栈的~
|