分支程序的特点是程序中含有转移指令。由于转移指令有无条件转移和条件转移之分,因此分支程序也可以分为无条件分支程序和条件分支程序两类。无条件分支程序中含有无条件转移指令,这类程序十分简单;条件分支程序中含有条件转移指令,这类程序较为普遍,是讨论的重点。
条件分支程序体现了计算机执行程序时的分析判断能力:若某种条件满足,则机器就转移到另一分支上执行程序;若条件不满足,则机器就按原程序继续执行。在MCS-51中,条件转移指令共有13条,分为累加器A判零条件转移、比较条件转移、减l条件转移和位控制条件转移4类。因此,MCS-51汇编语言的分支程序设计实际上就是如何正确运用这13条条件转移指令来进行编程的问题。
分支结构程序可分为单分支程序和多分支程序。
单分支程序是只使用一次条件转移指令的分支程序。
【例4-14】 求R1和R2寄存器中两个无符号数之差的绝对值,结果存放在片内RAM 30H单元中。
分析:
(1)题目中R1和R2寄存器中的数是未知的,对两个不知大小的数相减并求绝对值,显然应该先弄清楚哪一个值稍大些,然后再用大数减小数的方法,才可求得绝对值。
(2)利用指令系统中的减法指令来判断两数的大小。
(3)根据流程图编程。求绝对值程序流程图如图4-13所示。
图4-13 求绝对值程序流程图
解:
ORG 00H
AJMP MAIN
ORG 30H
MAIN: CLR C ;清进位标志
MOV A,R1 ;A←R1
SUBB A,R2 ;(R1)-(R2)→A
JC LL ;(R1)<(R2),跳到标号LL处
MOV 30H,A ;(R1)≥(RE),存放两者的绝对值
SJMP JIE
LL: MOV A,R2 ;(R2)-(R1)→A
SUBB A,R1 ;存放两者的绝对值
MOV 30H,A
JIE: SJMP $
END
【例4-15】 编制一程序,从P0口中读取一个数X,判断其值是否在100~200之间,即100≤X<200。如果X≥200,则片内RAM 30H单元存放0FFH;如果X<100,则片内RAM 30H单元存放00H;如果100≤X<200,则片内RAM 30H单元存放88H。
分析:
(1)根据题意,这是一个需要两次判断X大小的问题,可以先判断X是否大于100,再判断X是否大于200。
(2)根据解题的思路,画出程序流程图,如图4-14所示。
图4-14 判断X的两分支流程图
(3)编程如下。
解:
ORG 00H
AJMP MAIN
0RG 30H
MAIN: MOV A,P0 ;从P0口读一数据X,并存放在A中
MOV R1,A ;将该数据X保存到R1中
CLR C ;清进位标志CY
SUBB A,100 ;(A)-100→A
JC LL1 ;如果(A)<100,跳到LL1处
MOV A,R1 ;(A)←R1,该数大于等于100
SUBB A,200 ;(A)-200→A
JC LL2 ;如果(A)≤200,跳到LL2处
MOV R0,#0FFH ;(R0)←0FFH,该数大于200
SJMP HAV
LL1: MOV R0,#00H ;(R0)←00H,该数小于100
SJMP HAV
LL2: MOV RO,#88H ;(R0)←88H,100≤x<200
HAV: MOV 30H,R0
SJMP $
END
【例4-16】设有符号函数:
设其中X的值存放于35H单元,Y值存于36H单元,编程求解此函数。
分析:由于没有带符号的比较指令,只能按正数比较,即等于0;1~7FH为正数;80H~0FFH为负数。符号函数分支流程图如图4-15所示。
图4-15 符号函数分支流程图
解:
0RG 8000H
FHHS: MOV A,35H ;取数
CJNE A,#0,NEQ0 ;≠0转移
MOV A,#00H ;(A)=0
SJMP OKP
NEQ0: CJNE A,#7FH,ISZF ;l~7FH为正数转移
SJMP GT0 ;7FH为正数
ISZF: JNC LT0 ;(A)>7FH为负数
GT0: MOV A,#01H ;(A)=l
SJMP OK
LT0: MOV A,#0FFH ;(A)=-1补码
OK: MOV 36H,A ;存结果
SJMP $
在多分支程序中,因为可能的分支会有N个,若采用多条“CJNE”指令逐次比较,程序的执行效率会低很多,特别是分支较多时。这时,一般采用跳转表的方法,利用基址寄存器加变址寄存器间接转移指令“LJMP @A+DPTR”,可以根据累加器A的内容实现多路分支。这类程序又称为散转程序,如图4-16所示。
图4-16 多分支程序转移
【例4-17】 假设累加器A中内容为0~4,编程实现根据累加器A的内容实现不同的处理。
解:程序流程图如图4-17所示。程序清单如下。
图4-17 程序流程图
START: MOV R0,A ;将A送R0
ADD A,R0 ;A←(A)×2
ADD A,R0 ;A←(A)×3
MOv DPTR,#TABLE;转移表首地址送DPTR
JMP @A+DPTR ;散转相应分支入口
TABLE: LJMP FZ0 ;转向分支0的处理入口
LJMP FZ1 ;转向分支l的处理入口
LJMP FZ2 ;转向分支2的处理入口
LJMP FZ3 ;转向分支3的处理入口
LJMP FZ4 ;转向分支4的处理入口
由于每条长跳转指令“LJMP”要占用3个程序存储器单元,所以在此程序中,首先将累加器A中的内容置为原来的3倍,然后通过“LJMP @A+DPTR”指令实现散转,程序中的FZ0~FZ4为与0~4对应的各处理程序的入口地址。使用散转指令,根据X的内容(X=0,1,…)进行程序散转的地址表达式为:
地址=表首地址+表中每元素字节数×X
上面的例子还可以采用查表法来实现。与跳转表不同,这个表的内容不是跳转指令,而是地址的偏移量,即各分支处理程序的入口地址与表的基地址的差值,因此也称为差值表。由于表内的差值只限于8位,使各分支处理程序入口地址分布范围受到影响。这种方法只能实现少于255的分支。
MOV DPTR,#BJTAB ;设表基地址
MOV A,@A+DPTR ;A的内容为分支号,查差值表
JMP@A+DPTR ;计算实际地址,跳转
BJTAB: DB FZ0-BJTAB ;分支0处理程序和入口地址-表基地址的差值
DB FZ1-BJTAB ;分支l
DB FZ2-BJTAB ;分支2
DB FZ3-BJTAB ;分支3
DB FZ4-BJTAB ;分支4
FZ0: …… ;分支0处理程序
……
FZ1: …… ;分支1处理程序
FZ2: …… ;分支2处理程序
……
FZ3: …… ;分支3处理程序
……
FZ4: …… ;分支4处理程序
……