目录
(3)用返回指令RET结束子程序,并保证堆栈栈顶为调用程序的返回地址。
汇编语言程序设计技巧
四种基本结构:顺序结构、分支结构、循环结构和子程序。
【例】将R0单元内的两位BCD码拆开并转换成ASCII码,存入RAM两个单元中R2 (存高位)和R1 (存低位)中 。
参考程序:
??????? ORG? 2000H
??????????????? MOV? A,R0????? ;取值
??????? ANL?? A,#0FH? ;取低4位
??????? ADD? A,#30H?? ;转换成ASCII码
??????? MOV? R1,A?????? ;保存结果
??????? MOV? A,R0?????? ;取值
??????? SWAP? A???????????? ;高4位与低4位互换
??????? ANL? A,#0FH?? ;取原来的高4位??????
??????? ADD? A,#30H?? ;转换成ASCII码
??????? MOV? R2,A?????? ;保存结果
??????? SJMP? $??
?????????????? END
【例】求R2中补码绝对值,正数不变,负数变补。
?????? ????? MOV A,R2
?????? ????? JNB ACC.7,NEXT;为正数?
?????? ????? CPL A???? ???? ;负数变补
?????? ????? INC A
?????? ????? MOV R2,A
??? NEXT:SJMP NEXT? ;结束
【例】?求符号函数Y=SGN(X)
??????????????????? +1? 当 X>0
SGN(X)=????? 0? 当 X=0
???????????????????? -1? 当 X<0
SYMB: MOV? A,40H ?? ;取X
?????? ?? JZ ??? STOR????? ??????? ?????? ;X=0,Y=X
?????? JB ??? ACC7,MINUS;X<0
?????? MOV? A,# 1?? ?? ;X>0,Y=+1
?????? ?? SJMP? STOR
MINUS:MOV? A,#0FFH;X<0,Y= -1
STOR:? MOV? 41H,A ? ;保存Y
??????????????? RET
如:分支号=0,程序转移到ADDR0处;当分支号=1,程序转移到ADDR1处;… 。
MTJS:MOV?? DPTR,#TAB? ;取表首地址
?????? ? CLR???? C ????
?????? ? RLC???? A????????????????????? ;分支号×2
?????? ? MOV?? R2,A
????? ? INC????? A
?????? ? MOVC A,@A+DPTR;取分支地址低位
?????? ? PUSH?? ACC ? ??????;入栈保存
?????? ? MOV??? A,R2
?????? ? MOVC? A,@A+DPTR;取分支地址高位
?????? ? PUSH?? ACC ? ??????;入栈保存
?????? ? RET?? ?????? ???? ??????;分支地址→PC,转移
TAB:? DW? ????? ??ADDR0 ???? ??????;分支地址表
?????? ?? ? DW? ???? ??ADDR1?????
???????????????????? …?
ADDR0:????? …?????????? ????? ;程序段0? …
【例】根据R0的值转向7个分支程序。R0<10,转向SUB0; R0<20,转向SUB1;… …R0<60,转向SUB5; R0>=60,转向SUB6;
分析:这里应该利用JMP @A+DPTR
指令直接给PC赋值,使程序实现转移
参考程序如下:
ORG??? 2000H
MOV DPTR,#TAB;取转移指令表首地址
MOV? A,R0 ????????? ;取数?????
MOV????? B,#10
???? ……
DIV? AB????????????????? ;A/10,商在A中???????
CLR C
RLC??? A????? ????????? ;A←2A
JMP?? @A+DPTR???? ;PC ← A+DPTR
TAB: AJMP???? SUB0? ;转移指令表
AJMP? SUB1
AJMP???? SUB2
????? ……
AJMP???? SUB6
循环程序结构是汇编语言程序中常见的一种程序结构。所谓循环,就是让计算机反复执行某一段程序。使用循环程序可以省略很多类似的代码,提高程序的代码密度。
循环程序主要包含以下三个方面:
设置循环的初始状态,如工作单元的清零,循环次数的设置等。在设置初始条件时要小心,否则很容易让程序多执行(或少执行)一次。
即循环程序的主体,是要求计算机重复执行的部分。这部分程序应该特别注意精简,因为要重复多次,所以这部分的精简程度决定了整个循环程序的执行效率。
包括对循环计数器的修改和循环结束条件的判断等内容。
【例】把内部RAM中从ST1地址开始存放的数据传送到以ST2开始的存储区中,数据块长度未知,但已知数据块的最后一个字节内容为00H,而其它字节均不为0。并设源地址与目的地址空间不重叠。?
??? 分析:显然,我们可以利用判断每次传送的内容是否为 0 这一条件来控制循环。利用判A转移控制的循环流程图如下图所示。
参考程序如下:
START:MOV ???? R0,#ST1
?????? MOV ????? R1,#ST2
?LOOP: MOV ?? A, @R0??????
?????? ? JZ? ??? ENT
? ?? ??MOV? @R1,A
?????? ? INC ?? R0?????????
?????? ? INC ?? R1????????????????????????
?????? ? SJMP LOOP???????????? ??????????????????????
?????? ?ENT:? RET???????
例:求n个单字节数据的累加,设数据串已在43H起始单元,数据串长度在42H单元,累加和不超过2个字节。
?SUM:?? MOV????? R0,#42H;设指针
????????????? MOV????? A,@R0
????????????? MOV????? R2,A??? ? ;循环计数器←n
????????????? CLR A???? ? ;结果单元清0
????????????? MOV????? R3,A
ADD1:INC?? R0?? ? ;修改指针
????????????? ADD?????? A,@R0? ;累加
????????????? JNC?? NEXT? ? ;处理进位
????????????? INC R3?? ? ;有进位,高字节加1
NEXT:???? DJNZ ??? R2,ADD1 ;循环控制:数据是否加完?
????????????? MOV????? 40H,A? ;循环结束,保存结果
????????????? MOV????? 41H,R3
????????????? RET
例将内存一串单字节无符号数升序排序。
步骤:
每次取相邻单元的两个数比较,决定是否需要交换数据位置。
第一次循环,比较N-1次,取到数据表中最大值。
第二次循环,比较N-2次,取到次大值。
??? …
第N-1次循环:比较一次,排序结束。
?程序:
SORT:? MOV??? A,#N-1??? ;N个数据排序
????????????? ? MOV? R4,A???????? ;外循环次数
LOOP1: MOV?? A,R4
????????????? ? MOV? R3,A???????? ;内循环次数
????????????? ? MOV? R0,#TAB?? ;设数据指针
LOOP2: MOV?? A,@R0????? ;取二数
????????????? ? MOV? B,A
????????????? ? INC??? R0
????????????? ? MOV? A,@R0
????????????? ? CJNE A,B,L1 ;比较
L1:?????? ? JNC??? UNEX????????? ;A≥B,不交换
????????????? ? DEC??? R0??????? ;否则交换数据
????????????? ? XCH?? A ,@R0
????????????? ? INC??? R0
????????????? ? MOV? @R0,A
UNEX:? DJNZ R3,LOOP2??? ;内循环结束?
????????????? ? DJNZ R4,LOOP1?? ;外循环结束?
????????????? ? RET
能完成某项特定功能的独立程序段,可被反复调用。
子程序设计
如:子程序名、子程序功能、入口参数和出口参数、子程序占用的硬件资源、子程序中调用的其他子程序名。
选用不同的参数传递方式。
【例】将R4R5R6中三个字节数据对半分解,变成6个字节, 存入显示缓冲区(DISMEM0~DISMEM5)。
1)子程序UFOR1的功能:将A累加器中单字节数据,对半分解成两个字节,存入R0所指向的相邻两个单元
UFOR1:MOV? @R0,#0
????????????? ?XCHD A,@R0?? ;保存低半字节
????????????? ?INC? R0????? ;修改指针
????????????? ?MOV? @R0,#0
????????????? ?SWAP A
????????????? ?XCHD A,@R0?? ;保存高半字节
????????????? ?RET
2)调用子程序UFOR1之前,将待分解的内容送A,存放地址送R0。
【例】利用MCS-51仿真实验板,外部扩展四个双色发光二极管HL1、HL2、HL3和HL4分别模拟北(HL1)、西(HL2)、东(HL3)、南(HL4)四个方向交通灯,连接电路如下图所示:
分析:双色发光二极管有一个阴极,两个阳极G和R,当G极为高电平时,发光二极管呈现绿色,当R极为高电平时,发光二极管呈现红色,当G和R极都为高电平时,发光二极管呈现黄色,根据题意要求和图3.19的电路连接情况可以知道P1口的控制状态如下表所示:
参考程序如下:
???????????? ORG 1000H
START:MOV? R0,#0
???????????? MOV? R1,#0?? ;南北绿灯亮5秒钟,东西红灯亮
?? ? ????????MOV? P1,#10010110B ;南北绿灯亮红灯灭,东西红灯亮绿灯灭,为状态1
SNDL5:? MOV R1, #10
??????????? ACALL?? DL500ms??? ;调用延时500ms程序10次,实现延时5秒
??????????? DJNZ? R1, SNDL5?? ;南北绿灯闪烁3次,每次1秒(亮0.5秒,灭0.5秒)
SS1:??? MOV? P1,#10011111B;南北绿灯和红灯都灭,东西红灯亮绿灯灭,为状态2
??????????? ACALL? DL500ms??????? ;延时500ms
??????????? MOV? P1,# 10010110B? ;南北绿灯亮红灯灭,东西红灯亮绿灯灭,为状态1
??????????? ACALL? DL500ms?????? ;延时500ms
??????????? INC ?????? ?R0
??????????? CJNE????? R0,#03H,SS1?? ;闪烁3次,南北黄灯亮2秒
??????????? MOV????? P1,#00000110B? ;南北黄灯亮,东西红灯亮绿灯灭,为状态3
SNDL2:? MOV R1, #4
?????? ????? ACALL?? DL500ms ;调用延时500ms程序4次,实现延时2秒
???????????? DJNZ? R1, SNDL2?????? ;东西绿灯亮5秒钟,南北红灯亮
???????????? MOV P1,#01101001B? ;东西绿灯亮红灯灭,南北红灯亮绿灯灭,为状态4
EWDL5:? MOV R1, #10
???????????? ACALL?? DL500ms? ;调用延时500ms程序10次,实现延时5秒
???????????? DJNZ? R1, EWDL5 ;东西绿灯闪烁3次,每次1秒(亮0.5秒,灭0.5秒)
SS2:? MOV P1,#01101111B ;东西绿灯和红灯都灭,南北红灯亮绿灯灭,为状态5
???????????? ACALL? DL500ms?????? ;延时500ms
?????????? MOV? P1,# 01101001B ;东西绿灯亮红灯灭,南北红灯亮绿灯灭,为状态4
???????????? ACALL? DL500ms??????? ;延时500ms
?????? ?????? INC ?R0
???????????? CJNE??? R0,#03H,SS2?? ;闪烁3次,东西黄灯亮2秒
????????????? MOV? P1,#00001001B? ;东西黄灯亮,南北红灯亮绿灯灭,为状态6
SNDL2:? MOV R1, #4
??????????? ACALL?? DL500ms?????? ;调用延时500ms子程序4次,实现延时2秒
??????????? DJNZ? R1, SNDL2
??????????? SJMP? START? ?? ?????; 500ms 秒延时子程序
DL1500mS: MOV? R7,#5 ;500ms 秒延时子程序,假定为6MHz晶振
?? DL2: MOV? R6,#200
?? DL1: MOV? R5,#250
???????????? DJNZ??? R5,$
???????????? DJNZ??? R6,DL1
???????????? DJNZ??? R7,DL2
???????????? RET??????????
???????????? END
模块化设计是指把一个具体的功能分解成多个小的模块,各个模块之间相互独立,而又可以相互传递参数。分解成的小模块程序功能单一,易于调试和修改,而在模块内部要注意多使用子程序调用,一个子程序可以被多次调用,节省空间而且便于阅读。在程序中应该尽量使用循环结构,这样可以节省内存,提高执行效率,不过要注意循环的初始值和循环的结束条件。
注意:由于中断是随机产生的,因此在处理中断程序时,一定要注意保存程序现场(保护标志寄存器和中断处理程序用到的寄存器),以便执行完毕后恢复。在进行子程序调用时,经常使用累加器A(参数多时还可以使用寄存器或存储器)进行参数传递。