★3.3.4 控制转移类指令
控制程序转移类指令主要功能是控制程序转移到新的PC地址去执行。
指令的实质:找一个新的PC值,从而改变程序执行方向。分为4大类,即无条件转移指令、条件转移指令、调用指令和返回指令、空操作。
1 无条件转移指令(4条)
(1)长转移指令
LJMP addr16;PC←(PC)+3,PC←addr16,指令长度为三字节
该指令执行时,把转移的目的地址,即指令的第二和第三字节分别装入PC的高位和低位字节中,无条件地转向addr16指定的目的地址,即64KB程序存储器地址空间的任何位置。
说明:
①因指令中包含16位地址,是64KB范围内的跳转。由于addr16是一个16位地址,表示范围为0000H~FFFFH,所以该指令可以在64KB的ROM范围内实现转移。
②执行:先PC+3→PC,然后addr16→PC。由此可见,第一步无实际作用。
例如,执行2000H:LJMP 3000H后,PC的变化?
PC+3=2003H,PC=3000H
实际编程时,addr16通常也用标号代替,交由编译软件自动计算并仿真。
本指令为三字节指令,其转移的目标地址范围在ROM的64KB中,addr16一般用代表转移地址的标号表示,也可以是ROM中的地址。若addr16为1234H,则执行LJMP 1234H后,转移到ROM中的1234H处。
(2)短(相对)转移指令
SJMP rel;PC←PC+2+rel,指令长度为双字节
其指令执行过程是:先将指令中的rel值作为目标地址低8位(高8位视为00H);如果是标号,就根据标号找到其对应的目标地址;然后根据目标地址计算需要写进ROM中的rel值。计算时,运用补码运算法则。若无溢出,表示结果正确,仿真rel;若产生溢出,表示超出指令所能转移的范围,输出出错信息,提示指令有误。
在编程时,若需要单片机实现动态停机,可以使用如下指令:
HERE:SJMP HERE
或用“$”($代表当前指令的首地址)代替指令中的rel,写成:
HERE:SJMP $ 或
SJMP $
采用以上介绍的计算方法,可求得rel值为-2,补码表示为FEH。由于目标转移地址与当前指令首地址相同,所以单片机将连续重复执行该指令,貌似停机,却不是真正停机,故称为动态停机。
由于相对转移指令的机器码是80H rel,长度为2B。其中,rel是一个以8位补码表示的带符号地址偏移量,取值范围为+127~-128(00H~7FH对应表示0~+127,80H~FFH对应表示-128~-1)。对应的汇编语言指令SJMP rel中的符号rel实际上表示的是目标转移地址,只是要求该地址应在相对于该指令下一指令地址的-128~+127B的范围内,即有相对意义的目标转移地址。其目的地址是由PC中的当前值和指令的第二字节中带符号的相对地址相加而成的。因此,本指令转移的范围为:下一条指令的前128或本指令后127B的范围内(范围-126~+129)。为正数时,控制程序向前(PC值增加方向)跳转;反之向后跳转。
AT89C51单片机指令系统中,没有停机指令,通常就用指令SJMP$实现动态停机的操作。
例3-20:
LOOP:MOV A,R6
SJMP LOOP
汇编时,跳到LOOP处的偏移量由汇编程序自动计算和填入。
(3)绝对转移指令
AJMP addr11;PC←(PC)+2,PC10~0←addr11,指令长度为双字节
该指令提供11位目的地址a10~a0(即addr11),其中a10~a8则位于第1字节的高3位,a7~a0在第2字节。操作码只占第1字节的低5位。
图3-14 绝对转移指令地址、格式
由于该指令的机器码是a10a9a800001B、a7a6a5a4a3a2a1a0B,长度为两个字节,所以该指令执行时,PC当前值等于当前指令首地址加2(这时PC指向的是AJMP的下一条指令首地址),然后将指令中的11位地址码(a10~a0)替换PC当前值的后11位,PC当前值的前5位不变,从而构成目标地址,实现无条件转移。这时(PC)中内容的高5位PC15~PC11决定页。对这5位而言,它的变化范围为00000~11111(0~31),所以共有32个页,对应页号为0~31,每页对应的地址范围不同,如图3-14和表3-19所示。但每页地址范围的字节数都是2KB(因11位二进制数的范围是0~7FFH),真正转移的地址是ROM中的某个16位地址,其高5位必须是该指令的下一条指令地址的高5位(表示页号),它可在指令下一条指令地址之前或之后。但该地址不能超出对应页号的2KB地址范围,否则出错。若该指令正好在某页地址范围的最后两个单元,则绝对转移地址将在下一页2KB的地址范围内。例如,指令地址在0页中的07FE、07FF单元,则绝对转移地址应在1页的0800~0FFF内。
表3-19 ROM空间中32个(页)2KB地址范围(表中省去了十六进制后缀H)
指令提供11位地址a10~a0(即addr11),其中a10~a8位于第1字节的高3位,a7~a0在第2字节。操作码只占第1字节的低5位。实际编程时,汇编语言指令AJMP addr11中addr11往往是代表绝对转移地址的标号或ROM中的某绝对转移的16位地址。经汇编后自动翻译成相对应的绝对转移机器代码。所以不要将addr11理解成一个11位地址,而应理解为该指令的下一条指令地址高5位所决定的页内的“绝对转移”地址。
指令构造转移目的地址:执行本指令,PC加2,然后把指令中的11位无符号整数地址addr11(a10~a0)送入PC.10~PC.0,PC.15~PC.11保持不变,形成新的16位转移目的地址。
需注意,目标地址必须与AJMP指令的下一条指令首地址的高5位地址码a15~a11相同,否则将混乱。所以是2KB范围内的无条件跳转指令。
若将MCS-51系列单片机64KB的ROM寻址空间分成32个2KB区,同一个2KB区的前5位地址相同,后11位地址可从全0变为全1。例如,0000H~07FFH、1000H~17FFH、0800H~0FFFH、1800H~1FFFH均是同一个2KB区。
因为,AJMP指令的目标转移地址和取出该指令后PC中的地址(即PC当前值)的高5位相同。所以,AJMP指令的目标转移地址和PC当前值地址必在同一个2KB区。也就是说,AJMP指令只能在与PC当前值对应的同一个2KB区内实现向前或向后转移。
实际编程时,addr11通常也用标号代替,交由编译软件自动计算并仿真。若程序中使用如下指令:
AJMP NEWADDR
已知,标号NEWADDR对应的地址为0100H,则有
1)若假设该指令的首地址为0500H,取出该指令后,PC当前值为0502H,转移目标地址0100H与PC当前地址在同一个2KB区内,所以可以转移。
2)若假设该指令的首地址为0FFEH,则取出该指令后,PC当前值为1000H,这时,转移目标地址0100H与PC当前地址不在同一个2KB区内,因此不能实现转移。
例3-21:执行2000H:AJMP 600H之后,PC的变化?
分析:①PC+2=2002H;
②PC由2002H到2600H,属于PC变化范围内0010000000000000~0010011111111111,即2000H~27FFH。
需要注意的是,目标地址必须与AJMP指令的下一条指令首地址的高5位地址码a15~a11相同,否则将混乱。所以是2KB范围内的无条件跳转指令。
若执行指令2000H:AJMP 2800H可以吗?答案显然是不可以。
例3-22:若绝对转移指令AJMP 1789H的地址为1500H,试讨论用Keil汇编此指令是否会成功?若出错不成功,请说明出错原因;若成功,请写出该指令的机器代码。
本指令下一条指令的地址为1500H+2H=1502H,二进制表示(高位在前)为0001010100000010。该地址高5位为00010,对应页号为2,从表3-19查得该页2KB地址范围为1000H~17FFH。因绝对转移地址为1789H,正好在该页的2KB地址范围中。所以用Keil软件汇编此指令显示结果为成功。
转移地址1789H的二进制表示为0001011110001001,它的低11位地址为11110001001,而该低11位地址中的高3位是111,它作为高3位与指令操作码00001构成指令的第一字节11100001,即E1H;指令的第二字节是低11位地址的低8位10001001,即89H。最后指令的机器代码为E189。
(4)间接跳转指令(又称散转指令)
JMP @A+DPTR;PC←(PC)+1,PC←(A)+(DPTR)
该指令功能:指令长度为单字节。累加器中的8位无符号数与数据指针DPTR的16位数相加,结果作为下一条指令的地址送入PC。
注意:不会改变累加器A和数据指针DPTR的内容,也不影响标志位。
用法:DPTR的值固定,A变化,即可实现程序的多分支转移。目的地址由指针DPTR和变址A的内容之和形成,范围达64KB。
根据A中数值的不同,可实现多分支转移,所以也称之为散转(移)指令。
例3-23:请分析如下一段程序。
MOV DPTR,#TABLE
JMP @A+DPTR
TABLE:LJMP BRANCH0
LJMP BRANCH1
LJMP BRANCH2
LJMP BRANCH3
由于其中的LJMP指令的长度为3B,所以执行JMP@A+DPTR指令时,将根据A中数值的不同,实现如下的不同转移。
当(A)=00H时,程序将转移到BRANCH0处执行。
当(A)=03H时,程序将转移到BRANCH1处执行。
当(A)=06H时,程序将转移到BRANCH2处执行。
其余依此类推。
LJMP指令的机器码为02H、addr15~8B、addr7~0B,长度为3B,比SJMP和AJMP指令多占一个字节,所以编程时,若转移范围在256B之内,通常使用SJMP指令;大于256B小于2KB时,使用AJMP指令;2KB以上则使用LJMP指令。编程时,只需写上目的地址标号,相对偏移量由汇编程序自动计算。实际应用时,addr16、addr11、rel一般用符号地址形式。
无条件转移指令、功能操作、机器代码和执行机器周期数见表3-20。
表3-20 无条件转移指令、功能操作、机器代码和执行机器周期数
2 条件转移指令(8条)
条件转移指令都是依据某种条件成立才转移(不成立则继续顺序下去)的指令。此类指令均为相对寻址指令。条件转移指令是依某种特定条件转移的指令。
条件转移指令执行时,首先判断指令指定的条件是否满足。若条件满足,则将PC当前值与指令中的rel值相加,并把结果送入PC中作为转移目标地址;若条件不满足,则不改变PC值,顺序执行程序。其范围是以下一条指令的首地址为中心的-128~+127B内。
(1)累加器A判0转移指令
累加器A判0转移指令、功能操作、机器代码和执行机器周期数见表3-21。
表3-21 累加器A判0转移指令、功能操作、机器代码和执行机器周期数
(2)数值比较不相等转移指令
指令功能为比较两个操作数的大小,若值不相等,则转移;若相等,则顺序执行,常用于循环结构。操作过程为:第一数减第二数,状态标志送PSW,但不改变原来的操作数,均为三字节指令。
比较条件转移指令、功能操作、机器代码和执行机器周期数见表3-22。
表3-22 比较条件转移指令、功能操作、机器代码和执行机器周期数
单片机执行比较不相等转移指令时,为了判断两数是否不相等,需进行减法运算,并根据运算时是否发生借位,产生Cy标志位。
当目的字节大于等于源字节时,减法运算不发生借位,Cy=0。
当目的字节小于源字节时,减法运算发生借位,Cy=1。
CJNE A,#0AH,DONE1;不等于10转移到DONE1
…
该指令的特点如下:
①前两个操作数相减,但不保留结果,也不改变任何一个操作数。
②影响标志位。当第一操作数小于第二操作数,则进位标志位Cy=1;当第一操作数大于等于第二操作数,则进位标志位Cy=0。
(3)循环控制指令DJNZ(减1不为0转)转移指令
指令的功能是每执行一次指令就将目的操作数单元的内容减1一次,若被减为0,则转移到指令指定的目标地址执行程序,否则,顺序执行程序。这样的指令,常被用作循环控制。主要应用在循环结构的编程中,作循环结束控制用条件转移指令的应用。预先装入循环次数,以减1后是否为0作为转移条件,这样可以实现按次数控制循环。
循环转移转移指令、功能操作、机器代码和执行机器周期数见表3-23。
表3-23 循环转移转移指令、功能操作、机器代码和执行机器周期数
例3-24:将片内RAM的20H~2FH单元清0,其程序如下。
MOV R2,#16
MOV R0,#20H
LOOP:MOV @R0,#00H;对20单元清0
INC R0;指向下一个单元
DJNZ R2,LOOP;判断循环是否结束
SJMP $
3 子程序调用及返回指令(4条)
ACALL指令要求被调用子程序的首地址必须与执行ACALL指令时的PC当前值(即指向该指令下一条指令首地址的PC值)处在同一个2KB区内。
LCALL指令则允许被调用的子程序放在64KB范围内的任意地方。
断点地址:把指向调用指令下一条指令首地址的PC值(PC当前值)称为断点地址。执行子程序调用指令时,断点地址被压入堆栈,先压入低8位,再压入高8位。保存断点地址的目的是供子程序返回时使用。
实际编程时,addr11、addr16也都可以用标号代替。目标地址的形成方式与AJMP、LJMP指令相似。
子程序调用和返回指令、功能操作、机器代码和执行机器周期数见表3-24。
表3-24 子程序调用和返回指令、功能操作、机器代码和执行机器周期数
(1)长调用指令
LCALL addr16;三字节指令
可调用64KB范围内程序存储器中的任何一个子程序。执行时,先把PC加3获得下一条指令的地址(断点地址),并压入堆栈(先低位字节,后高位字节),堆栈指针加2。接着把指令的第二和第三字节(a15~a8,a7~a0)分别装入PC的高位和低位字节中,然后从PC指定的地址开始执行程序。执行后不影响任何标志位。
操作:①(PC)+3→PC,即获得下一条指令的地址(断点地址)。
②(SP)+1→SP,(PCL)→SP,(SP)+1→SP,(PCH)→SP即压入堆栈保护断点地址
③addr16→PC,即将指令的第二和第三字节(a15~a8,a7~a0)分别装入PC的高位和低位字节中,然后从PC指定的地址开始执行程序,执行后不影响任何标志位。
图3-15 LCALL执行过程与堆栈操作
例3-25:设(SP)=07H,(PC)=2100H,子程序首地址为3456H,执行下列指令,分析执行过程与堆栈操作(见图3-15)。
LCALL 3456H
MOV A,20H
执行结果:(SP)=09H,(09H)=21H,(08H)=03H,(PC)=3456H。
(2)绝对调用指令
ACALL addr11;双字节指令
与AJMP指令类似,为兼容MCS-48的CALL指令而设,不影响标志位。格式如下:
2KB范围内的调用子程序的指令。子程序地址必须与ACALL指令下一条指令的16位首地址中的高5位地址相同,否则将混乱。
操作:与AJMP指令类似,不影响标志位。
①(PC)+2→PC;
②(SP)+1→SP,(PCL)→SP;(SP)+1→SP,(PCH)→SP;
③addr11→PC.10~PC.0。
例3-26:
MOV SP,#60H
6100H:ACALL 480H
(PC):6100+2=6102H----断点
(PC)=6480H
(SP)=32H
((SP))=61H
((SP)-1)=02H
断点值PC=6100H+2=6102H,入栈保护;SP=32H,转移到PC=6480H处执行子程序。
(3)子程序的返回指令RET
子程序返回指令,其功能是把调用子程序时压入堆栈的断点地址从堆栈弹出到PC中(弹出顺序与入栈顺序相反),从而控制子程序返回主程序,使单片机从断点地址处继续运行。
执行本指令时,(SP)→PCH,然后(SP)-1→SP;(SP)→PCL,然后(SP)-1→SP。
功能:从堆栈中弹出数据(断点)地址值到PC的高8位和低8位字节(先高后低,栈指针减2),从刚恢复的PC值处开始继续执行程序。不影响任何标志位。
(4)中断返回指令RETI
中断服务程序返回指令,其功能与子程序返回指令类似,是把单片机响应中断时由控制电路自动压入堆栈的断点地址从堆栈弹出到PC中,从而控制中断服务程序返回主程序,使单片机从断点地址处继续运行程序。此外,执行该指令时,还将清除相应中断优先级状态位,以允许单片机响应低优先级别的中断请求。
与RET指令相似,不同之处是:该指令同时还清除了中断响应时被置1的内部中断优先级寄存器的中断优先级状态,其他操作与RET相同。
除具有RET指令的所有功能外,还将自动清除优先级状态触发器。RETI指令用在中断服务子程序中,作最后一条返回指令。
注意:不能用RET指令代替RETI。
例3-27:设(SP)=0BH,(0AH)=23H,(0BH)=01H。
执行RET后,分析执行过程与堆栈操作如图3-16所示。
结果:(SP)=09H;(PC)=0123H(返回主程序)。
图3-16 RET执行过程与堆栈操作
4 空操作指令(1条)
这条指令的功能是将PC的内容加1。除此之外,不进行其他操作。
执行这条指令的时间为一个机器周期(即12个时钟周期),故常常通过执行该指令来耗费CPU的时间,以实现较短时间的延时。常用于程序中的等待或时间的延迟。
例如,在延时子程序中微调延时时间;调试程序时用一些NOP来过渡;有些单片机应用系统中还应用它来实现软件抗干扰等。
空操作指令、功能操作、机器代码和执行机器周期数见表3-25。
表3-25 空操作指令、功能操作、机器代码和执行机器周期数