第二十六課:控制轉(zhuǎn)移類(lèi)指令分析
控制轉(zhuǎn)移指令用于控制程序的流向,所控制的范圍即為程序存儲(chǔ)器區(qū)間,MCS-51系列單片機(jī)的控制轉(zhuǎn)移指令相對(duì)豐富,有可對(duì)64kB程序空間地址單元進(jìn)行訪問(wèn)的長(zhǎng)調(diào)用、長(zhǎng)轉(zhuǎn)移指令,也有可對(duì)2kB字節(jié)進(jìn)行訪問(wèn)的絕對(duì)調(diào)用和絕對(duì)轉(zhuǎn)移指令,還有在一頁(yè)范圍內(nèi)短相對(duì)轉(zhuǎn)移及其它無(wú)條件轉(zhuǎn)移指令,這些指令的執(zhí)行一般都不會(huì)對(duì)標(biāo)志位有影響。
[1]. 無(wú)條件轉(zhuǎn)移指令(4條)
這組指令執(zhí)行完后,程序就會(huì)無(wú)條件轉(zhuǎn)移到指令所指向的地址上去。長(zhǎng)轉(zhuǎn)移指令訪問(wèn)的程序存儲(chǔ)器空間為16地址64kB,絕對(duì)轉(zhuǎn)移指令訪問(wèn)的程序存儲(chǔ)器空間為11位地址2kB空間。
LJMP addr16 ;addr16→(PC),給程序計(jì)數(shù)器賦予新值(16位地址)
AJMP addr11 ;(PC)+2→(PC),addr11→(PC10-0)程序計(jì)數(shù)器賦予新值(11位地址),(PC15-11)不改變
SJMP rel ;(PC)+ 2 + rel→(PC)當(dāng)前程序計(jì)數(shù)器先加上2再加上偏移量給程序計(jì)數(shù)器賦予新值
JMP @A+DPTR ;(A)+ (DPTR)→(PC),累加器所指向地址單元的值加上數(shù)據(jù)指針的值給程序計(jì)數(shù)器賦予新值
這幾條指令,如果要他細(xì)分析的話,區(qū)別較大,但初學(xué)者時(shí),可以不理會(huì)那么多,統(tǒng)統(tǒng)理解成LJMP標(biāo)號(hào),也就是跳轉(zhuǎn)到一個(gè)標(biāo)號(hào)處,但事實(shí)上,JMP標(biāo)號(hào),在前面的例程中我們已接觸過(guò),并且也知道如何來(lái)使用了,AJMP和SJMP也是一樣,那么這幾條指令它們的區(qū)別何在呢?在于跳轉(zhuǎn)的范圍不一樣。好比跳遠(yuǎn),LJMP一下就能跳64K那么遠(yuǎn)(當(dāng)然近了就更沒(méi)關(guān)系了)。而AJMP最多只能跳2K距離,而SJMP則最多只能跳256這么遠(yuǎn),原則上,所有用AJMP或SJMP的地方都可以用LJMP來(lái)替代。因此在初學(xué)者時(shí),需要跳轉(zhuǎn)時(shí)可以全用LJMP。
但是在查表時(shí)要注意會(huì)出錯(cuò),因?yàn)樗麄兊臋C(jī)器周期不一樣,取得的數(shù)也不一樣。
[2]. 條件轉(zhuǎn)移指令(8條)
條件轉(zhuǎn)移指令是指在滿足一定條件時(shí)進(jìn)行相對(duì)轉(zhuǎn)移
JZ rel ; A=0,(PC)+ 2 + rel→(PC),累加器中的內(nèi)容為0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
JNZ rel ; A≠0,(PC)+ 2 + rel→(PC),累加器中的內(nèi)容不為0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
這兩條指令是判斷A內(nèi)容是否為0轉(zhuǎn)移指令
第一條指令的功能是:如果(A)=0,則轉(zhuǎn)移,否則順序執(zhí)行(執(zhí)行本指令的下一條指令)。轉(zhuǎn)移到什么地方去呢?如果按照傳統(tǒng)的方法,就要算偏移量,很麻煩,好在現(xiàn)在我們可以借助機(jī)器匯編了,因此這條指令我們可以這樣理解:
JB 標(biāo)號(hào)
即轉(zhuǎn)移到標(biāo)號(hào)處,下面舉一例說(shuō)明:
MOV A,R0
JZ L1
MOV R1,#00H
AJMP L2
L1:MOV R1,#0FFH
L2:SJMP L2
END
在執(zhí)行上面這段程序前,如果R0中的值是0的話,就轉(zhuǎn)移到L1執(zhí)行,因此最終的執(zhí)行結(jié)果是R1中的值為0FFH。而如果R0中的值不等于0,則順序執(zhí)行,也就是執(zhí)行MOV R1,#00H指令。最終的執(zhí)行結(jié)果是R1中的值等于0。
第一條指令的功能清楚了,第二條當(dāng)然就好理解了,如果A中的值不等于0,就轉(zhuǎn)移。把上面的例子中的JZ改成JNZ試試看,程序執(zhí)行的結(jié)果是怎樣的?
CJNE A, data, rel ; A≠(data),(PC)+ 3 + rel→(PC),累加器中的內(nèi)容不等于直接地址單元的內(nèi)容,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
CJNE A, #data, rel ; A≠#data,(PC)+ 3 + rel→(PC),累加器中的內(nèi)容不等于立即數(shù),則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
CJNE Rn, #data, rel ; A≠#data,(PC)+ 3 + rel→(PC),工作寄存器Rn中的內(nèi)容不等于立即數(shù),則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
CJNE @Ri, #data, rel ; A≠#data,(PC)+ 3 + rel→(PC),工作寄存器Ri指向地址單元中的內(nèi)容不等于立即數(shù),則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
第一條指令的功能是將A中的值和立即數(shù)data比較,如果兩者相等,就順序執(zhí)行(執(zhí)行程序的下一條指令),如果不相等,就轉(zhuǎn)移,同樣的,我們可以將rel理解成標(biāo)號(hào)。即CJNE A,#data,標(biāo)號(hào)。這樣利用這條指令,我們就可以判斷兩數(shù)是否相等,這在很多場(chǎng)合是非常有用的。但有時(shí)還想得知兩數(shù)比較后哪個(gè)大,哪個(gè)小。本條指令也具有這樣的功能,如果兩數(shù)不相等,則CPU還會(huì)反映出哪個(gè)數(shù)大,哪個(gè)數(shù)小,這是用CY(進(jìn)位位)來(lái)實(shí)現(xiàn)的。如果前面的數(shù)(A中的)大,則CY=0,否則CY=1,因此在程序轉(zhuǎn)移后再次利用CY就可判斷出A中的數(shù)比data大還是小了。
例:
MOV A,R0
CJNE A,#10H,L1
MOV R1,#0FFH
AJMP L3
L1:JC L2
MOV R1,#0AAH
AJMP L3
L2:MOV R1,#0FFH
L3:SJMP L3
上面的程序中有一條指令我們還沒(méi)學(xué)過(guò),即JC,這條指令的原型是JC rel,作用我上面的JZ類(lèi)似,但是它是判斷CY是0,還是1進(jìn)行轉(zhuǎn)移,如果CY=1,則轉(zhuǎn)移到JC后面的標(biāo)號(hào)處執(zhí)行,如果CY=0則順序執(zhí)行(執(zhí)行它的下面的一條指令)。
分析一下上面的程序,如果(A)=10H,則順序執(zhí)行,即R1=0。如果(A)不等于10H,則轉(zhuǎn)到L1處繼續(xù)執(zhí)行,在L1處,再次進(jìn)行判斷,如果(A)大于10H,則CY=1,將順序執(zhí)行,即MOV R1,#0AAH指令,而如果(A)小于10H,則將轉(zhuǎn)移到L2處運(yùn)行,即執(zhí)行MOV R1,#0FFH指令。
因此最終結(jié)果是:本程序執(zhí)行前,如果(R0)=10H,則(R1)=00H,如果(R0)大于10H,則(R1)=0AAH,如果(R0)小于10H,則(R1)=0FFH。
弄懂了這條指令,其它的幾條就類(lèi)似了,第二條是把A當(dāng)中的值和直接地址的中的值比較,第三條則是將直接地址中的值和立即數(shù)比較,第四條是將間址尋址得到的數(shù)和立即數(shù)比較,這里就不詳談了,下面給出幾個(gè)相應(yīng)的例子。
CJNE A,10H ;把A中的值和10H中的值比較(注意和前面題目的區(qū)別)
CJNE 10H,#35H;把10H中的值和35H中的值比較
CJNE @R0,#35H;把R0中的值作為地址,從此地址中取數(shù)并和35H比較。
DJNZ Rn, rel ; (Rn)-1→(Rn),(Rn)≠0,(PC)+ 2 + rel→(PC)工作寄存器Rn減1不等于0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
DJNZ data, rel ; (Rn)-1→(Rn),(Rn)≠0,(PC)+ 2 + rel→(PC)直接地址單元中的內(nèi)容減1不等于0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
這兩條指令在前面我們已有提到,這里就不多說(shuō)了。
[3]. 子程序調(diào)用指令(1條)
子程序是為了便于程序編寫(xiě),減少那些需反復(fù)執(zhí)行的程序占用多余的地址空間而引入的程序分支,從而有了主程序和子程序的概念,需要反復(fù)執(zhí)行的一些程序,我們?cè)诰幊虝r(shí)一般都把它們編寫(xiě)成子程序,當(dāng)需要用它們時(shí),就用一個(gè)調(diào)用命令使程序按調(diào)用的地址去執(zhí)行,這就需要子程序的調(diào)用指令和返回指令。
LCALL addr16 ; 長(zhǎng)調(diào)用指令,可在64kB空間調(diào)用子程序。此時(shí)(PC)+ 3→(PC),(SP)+ 1→(SP),(PC7-0)→(SP),(SP)+ 1→(SP),(PC15-8)→(SP),addr16→(PC),即分別從堆棧中彈出調(diào)用子程序時(shí)壓入的返回地址
ACALL addr11 ; 絕對(duì)調(diào)用指令,可在2kB空間調(diào)用子程序,此時(shí)(PC)+ 2→(PC),(SP)+ 1→(SP),(PC7-0)→(SP),(SP)+ 1→(SP),(PC15-8)→(SP),addr11→(PC10-0)
上面這兩條指令就是在主程序中調(diào)用子程序的。
RET ; 子程序返回指令。此時(shí)(SP)→(PC15-8),(SP)- 1→(SP),(SP)→(PC7-0),(SP)- 1→(SP)
子程序返回指令
子程序執(zhí)行完后必須回到主程序,如何返回呢?只要執(zhí)行一條返回指令就可以了,即執(zhí)行RET。
RETI ; 中斷返回指令,除具有RET功能外,還具有恢復(fù)中斷邏輯的功能,需注意的是,RETI指令不能用RET代替
[4]. 空操作指令(1條)
所謂空操作,就是什么也不做,停一個(gè)周期,一般用作短時(shí)間的延時(shí)。
NOP ; 這條指令除了使PC加1,消耗一個(gè)機(jī)器周期外,沒(méi)有執(zhí)行任何操作。可用于短時(shí)間的延時(shí)
控制轉(zhuǎn)移指令用于控制程序的流向,所控制的范圍即為程序存儲(chǔ)器區(qū)間,MCS-51系列單片機(jī)的控制轉(zhuǎn)移指令相對(duì)豐富,有可對(duì)64kB程序空間地址單元進(jìn)行訪問(wèn)的長(zhǎng)調(diào)用、長(zhǎng)轉(zhuǎn)移指令,也有可對(duì)2kB字節(jié)進(jìn)行訪問(wèn)的絕對(duì)調(diào)用和絕對(duì)轉(zhuǎn)移指令,還有在一頁(yè)范圍內(nèi)短相對(duì)轉(zhuǎn)移及其它無(wú)條件轉(zhuǎn)移指令,這些指令的執(zhí)行一般都不會(huì)對(duì)標(biāo)志位有影響。
[1]. 無(wú)條件轉(zhuǎn)移指令(4條)
這組指令執(zhí)行完后,程序就會(huì)無(wú)條件轉(zhuǎn)移到指令所指向的地址上去。長(zhǎng)轉(zhuǎn)移指令訪問(wèn)的程序存儲(chǔ)器空間為16地址64kB,絕對(duì)轉(zhuǎn)移指令訪問(wèn)的程序存儲(chǔ)器空間為11位地址2kB空間。
LJMP addr16 ;addr16→(PC),給程序計(jì)數(shù)器賦予新值(16位地址)
AJMP addr11 ;(PC)+2→(PC),addr11→(PC10-0)程序計(jì)數(shù)器賦予新值(11位地址),(PC15-11)不改變
SJMP rel ;(PC)+ 2 + rel→(PC)當(dāng)前程序計(jì)數(shù)器先加上2再加上偏移量給程序計(jì)數(shù)器賦予新值
JMP @A+DPTR ;(A)+ (DPTR)→(PC),累加器所指向地址單元的值加上數(shù)據(jù)指針的值給程序計(jì)數(shù)器賦予新值
這幾條指令,如果要他細(xì)分析的話,區(qū)別較大,但初學(xué)者時(shí),可以不理會(huì)那么多,統(tǒng)統(tǒng)理解成LJMP標(biāo)號(hào),也就是跳轉(zhuǎn)到一個(gè)標(biāo)號(hào)處,但事實(shí)上,JMP標(biāo)號(hào),在前面的例程中我們已接觸過(guò),并且也知道如何來(lái)使用了,AJMP和SJMP也是一樣,那么這幾條指令它們的區(qū)別何在呢?在于跳轉(zhuǎn)的范圍不一樣。好比跳遠(yuǎn),LJMP一下就能跳64K那么遠(yuǎn)(當(dāng)然近了就更沒(méi)關(guān)系了)。而AJMP最多只能跳2K距離,而SJMP則最多只能跳256這么遠(yuǎn),原則上,所有用AJMP或SJMP的地方都可以用LJMP來(lái)替代。因此在初學(xué)者時(shí),需要跳轉(zhuǎn)時(shí)可以全用LJMP。
但是在查表時(shí)要注意會(huì)出錯(cuò),因?yàn)樗麄兊臋C(jī)器周期不一樣,取得的數(shù)也不一樣。
[2]. 條件轉(zhuǎn)移指令(8條)
條件轉(zhuǎn)移指令是指在滿足一定條件時(shí)進(jìn)行相對(duì)轉(zhuǎn)移
JZ rel ; A=0,(PC)+ 2 + rel→(PC),累加器中的內(nèi)容為0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
JNZ rel ; A≠0,(PC)+ 2 + rel→(PC),累加器中的內(nèi)容不為0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
這兩條指令是判斷A內(nèi)容是否為0轉(zhuǎn)移指令
第一條指令的功能是:如果(A)=0,則轉(zhuǎn)移,否則順序執(zhí)行(執(zhí)行本指令的下一條指令)。轉(zhuǎn)移到什么地方去呢?如果按照傳統(tǒng)的方法,就要算偏移量,很麻煩,好在現(xiàn)在我們可以借助機(jī)器匯編了,因此這條指令我們可以這樣理解:
JB 標(biāo)號(hào)
即轉(zhuǎn)移到標(biāo)號(hào)處,下面舉一例說(shuō)明:
MOV A,R0
JZ L1
MOV R1,#00H
AJMP L2
L1:MOV R1,#0FFH
L2:SJMP L2
END
在執(zhí)行上面這段程序前,如果R0中的值是0的話,就轉(zhuǎn)移到L1執(zhí)行,因此最終的執(zhí)行結(jié)果是R1中的值為0FFH。而如果R0中的值不等于0,則順序執(zhí)行,也就是執(zhí)行MOV R1,#00H指令。最終的執(zhí)行結(jié)果是R1中的值等于0。
第一條指令的功能清楚了,第二條當(dāng)然就好理解了,如果A中的值不等于0,就轉(zhuǎn)移。把上面的例子中的JZ改成JNZ試試看,程序執(zhí)行的結(jié)果是怎樣的?
CJNE A, data, rel ; A≠(data),(PC)+ 3 + rel→(PC),累加器中的內(nèi)容不等于直接地址單元的內(nèi)容,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
CJNE A, #data, rel ; A≠#data,(PC)+ 3 + rel→(PC),累加器中的內(nèi)容不等于立即數(shù),則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
CJNE Rn, #data, rel ; A≠#data,(PC)+ 3 + rel→(PC),工作寄存器Rn中的內(nèi)容不等于立即數(shù),則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
CJNE @Ri, #data, rel ; A≠#data,(PC)+ 3 + rel→(PC),工作寄存器Ri指向地址單元中的內(nèi)容不等于立即數(shù),則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
第一條指令的功能是將A中的值和立即數(shù)data比較,如果兩者相等,就順序執(zhí)行(執(zhí)行程序的下一條指令),如果不相等,就轉(zhuǎn)移,同樣的,我們可以將rel理解成標(biāo)號(hào)。即CJNE A,#data,標(biāo)號(hào)。這樣利用這條指令,我們就可以判斷兩數(shù)是否相等,這在很多場(chǎng)合是非常有用的。但有時(shí)還想得知兩數(shù)比較后哪個(gè)大,哪個(gè)小。本條指令也具有這樣的功能,如果兩數(shù)不相等,則CPU還會(huì)反映出哪個(gè)數(shù)大,哪個(gè)數(shù)小,這是用CY(進(jìn)位位)來(lái)實(shí)現(xiàn)的。如果前面的數(shù)(A中的)大,則CY=0,否則CY=1,因此在程序轉(zhuǎn)移后再次利用CY就可判斷出A中的數(shù)比data大還是小了。
例:
MOV A,R0
CJNE A,#10H,L1
MOV R1,#0FFH
AJMP L3
L1:JC L2
MOV R1,#0AAH
AJMP L3
L2:MOV R1,#0FFH
L3:SJMP L3
上面的程序中有一條指令我們還沒(méi)學(xué)過(guò),即JC,這條指令的原型是JC rel,作用我上面的JZ類(lèi)似,但是它是判斷CY是0,還是1進(jìn)行轉(zhuǎn)移,如果CY=1,則轉(zhuǎn)移到JC后面的標(biāo)號(hào)處執(zhí)行,如果CY=0則順序執(zhí)行(執(zhí)行它的下面的一條指令)。
分析一下上面的程序,如果(A)=10H,則順序執(zhí)行,即R1=0。如果(A)不等于10H,則轉(zhuǎn)到L1處繼續(xù)執(zhí)行,在L1處,再次進(jìn)行判斷,如果(A)大于10H,則CY=1,將順序執(zhí)行,即MOV R1,#0AAH指令,而如果(A)小于10H,則將轉(zhuǎn)移到L2處運(yùn)行,即執(zhí)行MOV R1,#0FFH指令。
因此最終結(jié)果是:本程序執(zhí)行前,如果(R0)=10H,則(R1)=00H,如果(R0)大于10H,則(R1)=0AAH,如果(R0)小于10H,則(R1)=0FFH。
弄懂了這條指令,其它的幾條就類(lèi)似了,第二條是把A當(dāng)中的值和直接地址的中的值比較,第三條則是將直接地址中的值和立即數(shù)比較,第四條是將間址尋址得到的數(shù)和立即數(shù)比較,這里就不詳談了,下面給出幾個(gè)相應(yīng)的例子。
CJNE A,10H ;把A中的值和10H中的值比較(注意和前面題目的區(qū)別)
CJNE 10H,#35H;把10H中的值和35H中的值比較
CJNE @R0,#35H;把R0中的值作為地址,從此地址中取數(shù)并和35H比較。
DJNZ Rn, rel ; (Rn)-1→(Rn),(Rn)≠0,(PC)+ 2 + rel→(PC)工作寄存器Rn減1不等于0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
DJNZ data, rel ; (Rn)-1→(Rn),(Rn)≠0,(PC)+ 2 + rel→(PC)直接地址單元中的內(nèi)容減1不等于0,則轉(zhuǎn)移到偏移量所指向的地址,否則程序往下執(zhí)行
這兩條指令在前面我們已有提到,這里就不多說(shuō)了。
[3]. 子程序調(diào)用指令(1條)
子程序是為了便于程序編寫(xiě),減少那些需反復(fù)執(zhí)行的程序占用多余的地址空間而引入的程序分支,從而有了主程序和子程序的概念,需要反復(fù)執(zhí)行的一些程序,我們?cè)诰幊虝r(shí)一般都把它們編寫(xiě)成子程序,當(dāng)需要用它們時(shí),就用一個(gè)調(diào)用命令使程序按調(diào)用的地址去執(zhí)行,這就需要子程序的調(diào)用指令和返回指令。
LCALL addr16 ; 長(zhǎng)調(diào)用指令,可在64kB空間調(diào)用子程序。此時(shí)(PC)+ 3→(PC),(SP)+ 1→(SP),(PC7-0)→(SP),(SP)+ 1→(SP),(PC15-8)→(SP),addr16→(PC),即分別從堆棧中彈出調(diào)用子程序時(shí)壓入的返回地址
ACALL addr11 ; 絕對(duì)調(diào)用指令,可在2kB空間調(diào)用子程序,此時(shí)(PC)+ 2→(PC),(SP)+ 1→(SP),(PC7-0)→(SP),(SP)+ 1→(SP),(PC15-8)→(SP),addr11→(PC10-0)
上面這兩條指令就是在主程序中調(diào)用子程序的。
RET ; 子程序返回指令。此時(shí)(SP)→(PC15-8),(SP)- 1→(SP),(SP)→(PC7-0),(SP)- 1→(SP)
子程序返回指令
子程序執(zhí)行完后必須回到主程序,如何返回呢?只要執(zhí)行一條返回指令就可以了,即執(zhí)行RET。
RETI ; 中斷返回指令,除具有RET功能外,還具有恢復(fù)中斷邏輯的功能,需注意的是,RETI指令不能用RET代替
[4]. 空操作指令(1條)
所謂空操作,就是什么也不做,停一個(gè)周期,一般用作短時(shí)間的延時(shí)。
NOP ; 這條指令除了使PC加1,消耗一個(gè)機(jī)器周期外,沒(méi)有執(zhí)行任何操作。可用于短時(shí)間的延時(shí)