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