四种基本结构:顺序结构、分支结构、循环结构和子程序。
【例】将R0单元内的两位BCD码拆开并转换成ASCII码,存入RAM两个单元中R2(存高位)和R1(存低位)中。
参考程序:
ORG2000H
MOVA,R0;取值
ANLA,#0FH;取低4位
ADDA,#30H;转换成ASCII码
MOVR1,A;保存结果
SWAPA;高4位与低4位互换
ANLA,#0FH;取原来的高4位
MOVR2,A;保存结果
SJMP$
END
【例】求R2中补码绝对值,正数不变,负数变补。
MOVA,R2
JNBACC.7,NEXT;为正数?
CPLA;负数变补
INCA
MOVR2,A
NEXT:SJMPNEXT;结束
【例】求符号函数Y=SGN(X)
+1当X>0
SGN(X)=0当X=0
-1当X<0
SYMB:MOVA,40H;取X
JZSTOR;X=0,Y=X
JBACC7,MINUS;X<0
MOVA,#1;X>0,Y=+1
SJMPSTOR
MINUS:MOVA,#0FFH;X<0,Y=-1
STOR:MOV41H,A;保存Y
RET
如:分支号=0,程序转移到ADDR0处;当分支号=1,程序转移到ADDR1处;…。
MTJS:MOVDPTR,#TAB;取表首地址
CLRC
RLCA;分支号×2
MOVCA,@A+DPTR;取分支地址低位
PUSHACC;入栈保存
MOVCA,@A+DPTR;取分支地址高位
RET;分支地址→PC,转移
TAB:DWADDR0;分支地址表
DWADDR1
…
ADDR0:…;程序段0…
【例】根据R0的值转向7个分支程序。R0<10,转向SUB0;R0<20,转向SUB1;……R0<60,转向SUB5;R0>=60,转向SUB6;
分析:这里应该利用JMP@A+DPTR
指令直接给PC赋值,使程序实现转移
参考程序如下:
MOVDPTR,#TAB;取转移指令表首地址
MOVA,R0;取数
MOVB,#10
……
DIVAB;A/10,商在A中
RLCA;A←2A
JMP@A+DPTR;PC←A+DPTR
TAB:AJMPSUB0;转移指令表
AJMPSUB1
AJMPSUB2
AJMPSUB6
循环程序结构是汇编语言程序中常见的一种程序结构。所谓循环,就是让计算机反复执行某一段程序。使用循环程序可以省略很多类似的代码,提高程序的代码密度。
循环程序主要包含以下三个方面:
设置循环的初始状态,如工作单元的清零,循环次数的设置等。在设置初始条件时要小心,否则很容易让程序多执行(或少执行)一次。
即循环程序的主体,是要求计算机重复执行的部分。这部分程序应该特别注意精简,因为要重复多次,所以这部分的精简程度决定了整个循环程序的执行效率。
包括对循环计数器的修改和循环结束条件的判断等内容。
【例】把内部RAM中从ST1地址开始存放的数据传送到以ST2开始的存储区中,数据块长度未知,但已知数据块的最后一个字节内容为00H,而其它字节均不为0。并设源地址与目的地址空间不重叠。
分析:显然,我们可以利用判断每次传送的内容是否为0这一条件来控制循环。利用判A转移控制的循环流程图如下图所示。
START:MOVR0,#ST1
MOVR1,#ST2
LOOP:MOVA,@R0
JZENT
MOV@R1,A
INCR0
INCR1
SJMPLOOP
ENT:RET
例:求n个单字节数据的累加,设数据串已在43H起始单元,数据串长度在42H单元,累加和不超过2个字节。
MOVA,@R0
MOVR2,A;循环计数器←n
CLRA;结果单元清0
MOVR3,A
ADD1:INCR0;修改指针
JNCNEXT;处理进位
INCR3;有进位,高字节加1
NEXT:DJNZR2,ADD1;循环控制:数据是否加完?
MOV40H,A;循环结束,保存结果
MOV41H,R3
例将内存一串单字节无符号数升序排序。
步骤:
每次取相邻单元的两个数比较,决定是否需要交换数据位置。
第一次循环,比较N-1次,取到数据表中最大值。
第二次循环,比较N-2次,取到次大值。
第N-1次循环:比较一次,排序结束。
SORT:MOVA,#N-1;N个数据排序
MOVR4,A;外循环次数
LOOP1:MOVA,R4
MOVR3,A;内循环次数
MOVR0,#TAB;设数据指针
LOOP2:MOVA,@R0;取二数
MOVB,A
CJNEA,B,L1;比较
L1:JNCUNEX;A≥B,不交换
DECR0;否则交换数据
MOV@R0,A
UNEX:DJNZR3,LOOP2;内循环结束?
DJNZR4,LOOP1;外循环结束?
能完成某项特定功能的独立程序段,可被反复调用。
子程序设计
如:子程序名、子程序功能、入口参数和出口参数、子程序占用的硬件资源、子程序中调用的其他子程序名。
选用不同的参数传递方式。
【例】将R4R5R6中三个字节数据对半分解,变成6个字节,存入显示缓冲区(DISMEM0~DISMEM5)。
1)子程序UFOR1的功能:将A累加器中单字节数据,对半分解成两个字节,存入R0所指向的相邻两个单元
UFOR1:MOV@R0,#0
XCHDA,@R0;保存低半字节
INCR0;修改指针
MOV@R0,#0
SWAPA
XCHDA,@R0;保存高半字节
2)调用子程序UFOR1之前,将待分解的内容送A,存放地址送R0。
【例】利用MCS-51仿真实验板,外部扩展四个双色发光二极管HL1、HL2、HL3和HL4分别模拟北(HL1)、西(HL2)、东(HL3)、南(HL4)四个方向交通灯,连接电路如下图所示:
模块化设计是指把一个具体的功能分解成多个小的模块,各个模块之间相互独立,而又可以相互传递参数。分解成的小模块程序功能单一,易于调试和修改,而在模块内部要注意多使用子程序调用,一个子程序可以被多次调用,节省空间而且便于阅读。在程序中应该尽量使用循环结构,这样可以节省内存,提高执行效率,不过要注意循环的初始值和循环的结束条件。
注意:由于中断是随机产生的,因此在处理中断程序时,一定要注意保存程序现场(保护标志寄存器和中断处理程序用到的寄存器),以便执行完毕后恢复。在进行子程序调用时,经常使用累加器A(参数多时还可以使用寄存器或存储器)进行参数传递。