慎用51单片机中的RET指令
按下P3.0的按键,连接P1.0的LED就发亮。y
以下是我同学编写的程序:
org 0000h
mov P1,#0ffh
loop:
jnb P3.0,led1;*
jnb P3.1,led2;*
jnb P3.2,led3;*
jnb P3.3,led4;*
ljmp loop
led1:
clr P1.0
ret
led2:
clr P1.1
ret
led3:
clr P1.2
ret
led4:
clr P1.3
ret
end
程序的意图是,制造一个死循环,不断检查按键是否按下,如果按下,就令对应的灯亮。程序经过测试,能够满足题目的要求。
但是,问题出现在上面带*号的那一部分代码,程序意图是想要当P3的某个位为0的时候,就调用LED灯的子程序,执行CLR P1.0语句,再返回到原来程序调用子程序的地方继续执行代码。
我对的子程序的理解是:在一个地方启动一段代码,当这段代码运行完毕之后,就返回到原来的地方继续运行剩下的代码。
那么CPU单片机是如何返回原来的地址的呢?
首先,当程序执行到A处进入子程序时,将A的下一个条指令(即PC+2所指的地方)压入栈中,即将栈指针SP+1,PCL进栈,SP再加1,PCH进栈。
然后,把PC的值改为子程序代码的入口。
子程序执行完毕之后,从栈中弹出原来的PC值,赋值给当前的PC寄存器。
最后,程序返回到原来调用子程序的地方的下一条指令继续运行。
(详细步骤请查看RET和ACALL,LCALL指令)
上面的代码很明显想调用一个子程序,但是51单片机中,只有ACALL和LCALL指令会在跳转前讲PC+2值压栈,其他跳转指令都不会。
代码中使用了JNB作为跳转指令,所以并没有压栈,但是当跳转之后遇到RET,还是一如既往地弹栈,这样,只有出,没有进,会导致堆栈不平衡。
但为什么这个程序依然有效呢?
这个因为SP初始指针指向了一个空白的单元(全是0),所以,当遇到RET后,把PC寄存器给初始化,程序由头开始重新执行,阴差阳错地满足的题目的要求。
所以RET指令必须和ACALL和LCALL配套使用,才能组成为真正意义上的子程序
51单片机RET指 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)