今天在网上看到这么一个单片机复位的程序,这个程序据说是一个大三的学生写出来的,不错,写的很有一定的道理,其C编程也达到了一定程度了【小盒子我还是很佩服这个人的】。下面我们来看看这个代码:
void main(void)
{
unsigned char code rst[]={0xe4,0xc0,0xe0,0xc0,0xe0,0x32}; // 复位代码
(*((void (*)())(rst)))(); // 执行上一行代码,将rst数组当函数调用
}
第一句定义一个数组rst[],数组内数据就是完成复位功能的汇编机器码,具体对应关系为:
clr a == 0xe4、push acc == 0xc0,0xe0、reti ==0x32
可以看出其程序起到复位的作用,完全就是汇编机器码的功劳。
而单片机复位的更好方法
clr a //清除ACC=0
push acc //压0到堆栈——8位
push acc //再压0到堆栈——再8位
reti //返回到0地址,从新执行。
这种复位方法比较麻烦,更加简单的复位写法是(摘自《C缺陷与陷阱》):
( * ( void (*)( ) )0 ) ( );
看过上面更简单的复位方法,让我们多加考虑一下,为什么要写成0?别的不行吗?换成别的后会是什么样的效果呢?抱着这个想法,我亲自经过KEIL V2.4.0编译后的汇编程序:
可以看出若将( * ( void (*)( ) )0 ) ( );
改成( * ( void (*)( ) )3 ) ( );
则程序会跳转到main()函数开始,避开startup文件的初始化……
只所以我说的是会从main()开始,是因为我看过编译后的汇编文件,找到main的实际物理地址而已,否则我也不会写成3了。呵呵……下面就是编译后的汇编结果
C:0x0003 E4 CLR A
C:0x0004 F508 MOV 0x08,A
C:0x0006 F509 MOV 0x09,A
14: while(1) {
15: if(i == 10) {
16: //( *( ( void (*)( ) ) (rst) ) )(); // 执行上一行代码,将rst数组当函数调用
C:0x0008 E509 MOV A,0x09
C:0x000A 640A XRL A,#0x0A
C:0x000C 4508 ORL A,0x08
C:0x000E 7005 JNZ C:0015
17: ( *( ( void (*)( ) ) (3) ) )(); // 执行上一行代码,将rst数组当函数调用
C:0x0010 120003 LCALL main(C:0003)
18: } else {
C:0x0013 80F3 SJMP C:0008
19: i++;
C:0x0015 0509 INC 0x09
C:0x0017 E509 MOV A,0x09
C:0x0019 70ED JNZ C:0008
C:0x001B 0508 INC 0x08
20: }
为了进行给大家一个很好的比较,从视觉上得到一定的感觉,我又再次将3改回成0,大家看看编译后的汇编结果是什么样子的;
下面的代码是函数( * ( void (*)( ) )0 ) ( ); 这个编译后的结果
C:0x0003 E4 CLR A
C:0x0004 F508 MOV 0x08,A
C:0x0006 F509 MOV 0x09,A
14: while(1) {
15: if(i == 10) {
16: //( *( ( void (*)( ) ) (rst) ) )(); // 执行上一行代码,将rst数组当函数调用
C:0x0008 E509 MOV A,0x09
C:0x000A 640A XRL A,#0x0A
C:0x000C 4508 ORL A,0x08
C:0x000E 7005 JNZ C:0015
17: ( *( ( void (*)( ) ) (0) ) )(); // 执行上一行代码,将rst数组当函数调用
C:0x0010 120000 LCALL C_STARTUP(C:0000)
18: } else {
C:0x0013 80F3 SJMP C:0008
19: i++;
C:0x0015 0509 INC 0x09
C:0x0017 E509 MOV A,0x09
C:0x0019 70ED JNZ C:0008
C:0x001B 0508 INC 0x08
20: }
请大家注意红色的部分。
若大家都能亲自动手实际操作一下,会让你理解的更多,体会的也会更多的。。。具体的还有什么好的方法和好的建议,希望多多留言大家共同进步。【小盒子 2007.08.17 PM17:14】