控制转移类指令共有17条,分为无条件转移指令、条件转移指令、子程序调用与子程序返回指令。利用这些控制转移指令,可以很方便地控制程序向前或向后跳转,并根据条件判断实现分支程序、循环程序和子程序调用等。
1.无条件转移指令
无条件转移指令有如下4条指令,它们提供了不同的转移范围和寻址方式。
这组指令的功能是无条件转移到指定的地址上开始执行程序。
1)AJMP是绝对转移指令,是双字节指令。指令的机器码由指令提供的11位直接地址addr11和5位指令操作码00001组成,并按下列格式分布:a10a9a800001a7a6a5a4a3a2 a1a0。
因此,执行该指令时,先由AJMP指令所在位置的地址PC值加2(该指令字节数)构成当前的PC值,然后把指令中的addr11送入PC10~0,PC15~11不变,形成程序转移的目的地址。
由于addr11是11位地址送入PC的低11位,目标地址的高5位由当前PC值确定,所以该指令只能在2KB范围内进行无条件转移。也就是AJMP将单片机的64K程序存储器空间分为32个区,每个区2KB,程序可转移的位置只能是和当前PC值在同一个2KB的范围内,即转移的目标地址必须与AJMP下一条指令的地址的高5位地址码A15~A11相同,否则将会引起指令执行混乱。
该指令可以向前也可以向后转移,指令执行后不影响状态标志位。
例如,若当前AJMP指令(PC)=3102H,执行指令AJMP 1F0H时,执行过程如下:先把当前PC值加2,(PC)=(PC)+2=3104H=0011000100000100B;再将1F0H(0011111 0000B)送入PC10~0。
结果为(PC)=0011000111110000B=31F0H,程序向后转移到31F0H地址开始执行程序。
例如,若当前AJMP指令(PC)=0302H,执行指令AJMP 1F0H时,执行过程如下:先将当前PC值加2,(PC)=(PC)+2=0304H=0000001100000100B;再将1F0H(0011111 0000B)送入PC10~0。
结果为(PC)=0000000111110000B=01F0H,程序向前转移到01F0H地址开始执行程序。
2)LJMP是长转移指令,指令中提供了16位目标地址addr16,是3字节指令。该指令执行时将指令码的第2和第3字节分别装入程序计数器PC的高位和低位字节中(即PC的高8位为addr15~8,低8位为addr7~0),可无条件地转向addr16指出的目标地址处开始执行程序。目标地址可以是64K程序存储器地址空间的任何位置。
该指令的缺点是执行时间长,占字节数多。
例如,LJMP 1000H指令执行后,(PC)=1000H,程序跳转到1000H地址开始执行程序。
3)SJMP是无条件相对转移指令,指令中的操作数是相对地址偏移量rel,是双字节指令。rel是一个带符号8位二进制数的补码数,能实现程序向前或向后跳转,转移地址范围为-128~127。若rel为正,则向后转移;若rel为负,则向前转移。该指令执行时,先将PC加2,再将rel偏移量加到PC上,计算出目的地址。计算公式如下:向后转移,目的指令首地址=源指令首地址+2+rel;向前转移,目的指令首地址=源指令首地址+2-(FFH-rel+1)。其中源地址就是SJMP指令所在的地址。
【例3-26】 假设在1042H地址存储了SJMP指令1042H:SJMP 71H,则执行该指令时,(PC)=1042H,rel=71H(正数),目的地址=1042H+2+71H=10B5H,因此该指令执行后,(PC)=10B5H,CPU向后转移到10B5H地址处开始执行程序。
【例3-27】 假设在1042H地址存储了SJMP指令1042H:SJMP C2H。由于C2H是负数的补码,则源码=(FFH-C2H+1)=-3EH,执行该指令时,(PC)=1042H,rel=-3EH(负数),目的地址=1042H+2-3EH=1006H,因此该指令执行后,(PC)=1006H,CPU向前转移到1006H地址处开始执行程序。
在编写程序时,直接写出要转向的目标地址标号即可。例如:
这样程序在汇编时,由汇编程序自动计算和填入偏移量。若手工汇编时,偏移量rel的值则需要程序设计人员计算。
4)JMP是间接跳转指令,是以累加器A的内容为相对偏移量,以DPTR的内容为基地址,在64KB范围内可无条件转移的单字节指令。指令的功能是控制程序跳转到(A)+(DPTR)所指出的地址处开始执行程序。
转移的目的地址可以在程序运行中加以改变。例如,当DPTR的值确定时,根据累加器A的不同值可以实现多分支选择的转移,起到用一条指令完成多条分支指令的功能,常用在通过键盘进行的按键处理上。
该指令执行后,累加器A和DPTR的内容不变,也不会影响标志寄存器PSW。
【例3-28】 要求处理键盘输入的数字键值0~9,假设查得的键值(如02)已送到A中,则可使用JMP指令跳转到不同键值处理的程序入口中。程序指令如下:
以上程序中,利用AJMP指令转移到对应的数字键处理程序,由于AJMP是双字节指令,故应先将A乘以2,再由A中8位无符号数与DPTR中的16位数内容之和来确定键值,处理目的地址,实现键值处理多分支转移。
2.条件转移指令
条件转移指令有8条,当判断指定的某种条件满足时,程序转移执行;条件不满足时,程序仍按原来的顺序继续执行。该类指令采用相对寻址,偏移量是带符号的补码,程序可在以当前PC值为中心的-128~127的范围内向前或向后转移。
(1)累加器判零转移指令
这两条指令是判断累加器是否为零的双字节条件转移指令,当条件满足时,将下一条指令的首地址装入PC,再将带符号的相对偏移量rel加到PC上,计算出目标地址;当条件不满足时,将下一条指令的首地址装入PC,顺序执行下一条指令。
该指令不做任何运算,也不会影响标志。rel作为相对转移偏移量,在程序编写时用标号代替,手工汇编时,偏移量rel的值需要程序设计人员计算。
【例3-29】 将首地址为DAT1的外部RAM数据块传送到单片机内部RAM的DAT2地址处开始存储,如果遇到传送的数据为零,则停止传送。
编程思路:外部数据存储器的数据传送到内部RAM,必须采用累加器A作中转站,每次从外部RAM地址单元中取出一个数据到A,再用JZ指令判断是否为零,如果不为零则传送到内部RAM对应的地址中,直至遇到零数据时停止。参考程序如下:(www.xing528.com)
值得注意的是,程序中用了SJMP LOOP指令,其标号LOOP是指转移到目标地址标号LOOP处的8位相对转移偏移量rel,并非程序的地址标号。而MOVX指令上的LOOP标号指的是程序中的地址标号(表示16位地址单元)。
(2)比较不相等转移指令
比较不相等转移指令有4条,指令格式为
CJNE目的操作数,源操作数,rel
这组指令的功能是先对前面两个规定的操作数比较大小,如果两个数值不相等则转移,否则不转移,程序继续执行下一条指令。4条比较转移指令如下:
这4条指令都是三字节指令,两个操作数按无符号数做减法来比较(减法操作后,两个操作数不变,差值不保留)。如果目的操作数大于或等于源操作数,则置进位标志Cy=0,否则进位标志Cy=1。因此,可以采用CJNE指令来实现三分支转移。
以上4条指令的差别在于操作数寻址方式的不同,但它们均按以下方式操作:如果目的操作数=源操作数,则PC←(PC)+3;如果目的操作数>源操作数,则PC←(PC)+3+rel,Cy=0;如果目的操作数<源操作数,则PC←(PC)+3+rel,Cy=1。
(3)减1不为0转移指令
减1不为0转移指令有如下2条:
这是一组减1操作和条件判断转移两种功能结合在一起的指令。该指令每执行一次,源操作数(Rn或direct)将自减1,结果回送到Rn寄存器或direct单元中,然后判断操作数是否为零,如果结果不为0,则转移到指定的目标地址处执行,否则按顺序执行程序。转移的目的地址可在以当前PC值为中心的-128~127的范围内向前或向后转移。如果操作数为0,则执行该指令后,操作数结果为FFH,不会影响任何标志位。
rel作为相对转移偏移量,是带符号的二进制数补码,在程序编写时以标号代替,手工汇编时,偏移量rel的值则需程序设计人员计算。
这组指令是构成循环程序的重要指令,可以指定一个寄存器或内部RAM单元为计数器,以减1后是否为0作为转移条件,即可实现循环次数控制。
【例3-30】 将内部30H开始的10个无符号单字节数相加,相加结果存储在内部RAM单元中。
编程思路:10个8位无符号数相加后,结果可能得到双字节数。因此,用R3、R2存储累加和(事先置(R3 R2)=00)。采用R0作指针指向30H单元,用R7作计数器控制累加循环次数,初值为10,即10个数相加应加10次。参考程序如下:
3.子程序调用与返回指令
在程序设计中,有时需要多次执行某段程序,通常把这段程序设计成子程序供主程序调用。当调用一个子程序时,CPU暂停执行当前程序(当前程序的地址被压入堆栈),转向该子程序的入口地址开始执行,待子程序执行完毕后(把压入堆栈的地址恢复到PC),使CPU自动返回到原来被中断的位置继续执行。因此,调用子程序时,要使用压栈指令(PUSH)保存被中断位置的地址,子程序返回时,要使用出栈指令(POP)以便恢复原来中断位置的地址,并从该地址处继续执行程序。
子程序调用指令有4条,指令格式如下:
1)ACALL是双字节的绝对调用指令,只能在2KB范围内调用子程序。该指令的执行过程如下:首先程序计数器PC自加2,指向下一条指令的地址,即PC←(PC)+2;将当前PC值压栈保护,先压低字节入栈SP←(SP)+1,((SP))←(PCL),再压高字节入栈SP←(SP)+1,((SP))←(PCH);将指令中的直接地址addr11送入PC的低11位,即PC10~0←ad- dr11,PC的高5位保存不变,最后根据PC值转向要执行的子程序。
需要注意的是,所调用子程序的首地址必须与ACALL指令中的下一条指令的首地址在同一个2KB区内。也就是说,ACALL指令相当于把单片机的64K程序存储器空间划分为32个区,每个区2KB。用ACALL调用子程序时应在同一个2KB的范围之内,即16位地址的高5位相同,否则将引起程序转移的混乱。
ACALL指令与AJMP跳转指令相似,都是双字节指令,执行该指令时PC要加2才能获得下一条指令地址,指令提供11位目的地址。所不同的是,调用指令应先将PC值压栈,然后将指令中的直接地址送至PC,并转向PC指定的地址处开始执行程序。
【例3-31】 设(SP)=6FH,子程序标号SUB1首地址为130CH,ACALL指令的首地址为10F3H,则执行指令:
结果为(SP)=71H,(70H)=F5H,(71H)=10H,(PC)=130CH。由于ACALL指令首地址的高5位是00010B,因此,在此处可调用1000H~17FFH之间存储的子程序。
2)LCALL是三字节的长调用指令,能在64KB范围内调用子程序。该指令的执行过程如下:首先程序计数器PC自加3,指向下一条指令的地址,即PC←(PC)+3;将当前PC值压栈保护,先压低字节入栈SP←(SP)+1,((SP))←(PCL),再压高字节入栈SP←(SP)+1,((SP))←(PCH);将指令中的直接地址addr16送入PC中,并根据PC值转向要执行的子程序。
LCALL指令与LJMP跳转指令相似,都是三字节指令,执行该指令时PC要加3才能获得下一条指令地址,指令提供16位目的地址。所不同的是,调用指令应先将PC值压栈,然后把直接地址送入PC,并转向PC指定的地址处开始执行程序。
【例3-32】 设(SP)=6FH,子程序标号SUB2首地址为7B16H,LCALL指令的首地址为23DFH,执行指令:
结果为(SP)=71H,(70H)=E2H,(71H)=23H,(PC)=7B16H。
3)RET和RETI是单字节返回指令,指令的功能是从堆栈中连续两次弹出数据送入PC的高8位和低8位字节,恢复PC值,并从PC值指定的地址处开始继续执行程序。该指令的执行过程如下:首先从SP指向的栈顶弹出一个数据送入PC高字节,即PCH←((SP)),SP←(SP)-1;再弹出一个数据送入PC低字节,即PCL←((SP)),SP←(SP)-1;最后CPU根据PC值转向执行子程序。
RET与RETI指令操作完全一致,不同之处在于:RET是子程序返回指令,应写在子程序的末尾;而RETI是中断返回指令,应写在中断服务子程序的最后;RETI指令执行后将清除内部中断优先级寄存器(中断响应时被置“1”)的优先级状态,使得已申请的同级或低级中断申请可以被响应。
4.空操作指令
空操作指令只有1条,指令格式:NOP。
空操作指令是一条单字节单周期指令,它控制CPU不作任何操作,只是消耗这条指令执行所需要的时间,不影响任何标志位。常用于产生程序等待或设计准确的延时程序,用来拼凑精确的延时时间。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。