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