慎用51單片機(jī)中的RET指令
按下P3.0的按鍵,連接P1.0的LED就發(fā)亮。y
本文引用地址:http://m.butianyuan.cn/article/201611/318611.htm以下是我同學(xué)編寫的程序:
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
程序的意圖是,制造一個(gè)死循環(huán),不斷檢查按鍵是否按下,如果按下,就令對(duì)應(yīng)的燈亮。程序經(jīng)過測試,能夠滿足題目的要求。
但是,問題出現(xiàn)在上面帶*號(hào)的那一部分代碼,程序意圖是想要當(dāng)P3的某個(gè)位為0的時(shí)候,就調(diào)用LED燈的子程序,執(zhí)行CLR P1.0語句,再返回到原來程序調(diào)用子程序的地方繼續(xù)執(zhí)行代碼。
我對(duì)的子程序的理解是:在一個(gè)地方啟動(dòng)一段代碼,當(dāng)這段代碼運(yùn)行完畢之后,就返回到原來的地方繼續(xù)運(yùn)行剩下的代碼。
那么CPU單片機(jī)是如何返回原來的地址的呢?
首先,當(dāng)程序執(zhí)行到A處進(jìn)入子程序時(shí),將A的下一個(gè)條指令(即PC+2所指的地方)壓入棧中,即將棧指針SP+1,PCL進(jìn)棧,SP再加1,PCH進(jìn)棧。
然后,把PC的值改為子程序代碼的入口。
子程序執(zhí)行完畢之后,從棧中彈出原來的PC值,賦值給當(dāng)前的PC寄存器。
最后,程序返回到原來調(diào)用子程序的地方的下一條指令繼續(xù)運(yùn)行。
(詳細(xì)步驟請(qǐng)查看RET和ACALL,LCALL指令)
上面的代碼很明顯想調(diào)用一個(gè)子程序,但是51單片機(jī)中,只有ACALL和LCALL指令會(huì)在跳轉(zhuǎn)前講PC+2值壓棧,其他跳轉(zhuǎn)指令都不會(huì)。
代碼中使用了JNB作為跳轉(zhuǎn)指令,所以并沒有壓棧,但是當(dāng)跳轉(zhuǎn)之后遇到RET,還是一如既往地彈棧,這樣,只有出,沒有進(jìn),會(huì)導(dǎo)致堆棧不平衡。
但為什么這個(gè)程序依然有效呢?
這個(gè)因?yàn)镾P初始指針指向了一個(gè)空白的單元(全是0),所以,當(dāng)遇到RET后,把PC寄存器給初始化,程序由頭開始重新執(zhí)行,陰差陽錯(cuò)地滿足的題目的要求。
所以RET指令必須和ACALL和LCALL配套使用,才能組成為真正意義上的子程序
評(píng)論