【例4.1】拆字程序。将一个字节的两个BCD码十进制数拆开并变成相应的ASCII码,并存入两个RAM单元中。
设两个BCD码(一个字节)已存入在内部RAM的30H单元中,变换后的ASCII 码分别存放在31H和32H单元,且高位BCD码的ASCII码的ASCII码存于31H单元。数字0~9的ASCII为30H~39H,完成拆字转换只需将一个字节的两个BCD码拆开存放在另两个单元的低4位,并在其高4位赋以0011即可。程序段清单如下:
MOV R0,#32H ;将32H单元地址送R0
MOV @R0,#00H ;32H单元清0
MOV A,30H ;将30H单元中的BCD送A
XCHD A,@R0 ;将低位BCD码送32H单元
ORL 32H,#30H ;完成低位BCD码转换
SWAP A ;将高位BCD码交换到低位
ORL A,#30H ;完成高位BCD码转换
MOV 31H,A ;将高位BCD的ASCII码存入31H
上述程序段完成了将一个字节的BCD码转换成两个ASCII码的功能。共需占用15个程序存储器字节单元,用9个机器周期执行完毕。
【例4.2】 双字节加法程序段
设被加数存放在内部矛盾RAM的31H、32H单元,低位字节在前,加数存入于34H、35H单元(低字节在前),结果和存放于31H、32H、33H单元中。其程序段清单如下;
STRT:PUSH A ;将A内容进栈保护
MOV R0,#31H ;将地址码送R0和R1
MOV R1,#34H
MOV 33H,#00H ;将33H单元清0,存放和的最高字节数
MOV A,R0 ;两低字节数相加
ADD A,@R1
MOV @R0,A ;低字节和存于31H单元
INC R0 ;地址数分别加1
INC R1
MOV A,@R0 ;连用低位进位进行高字节数
ADDC A,@R1 ;相加
MOV @R0,A ;高字节和存于32H单元
INC R0 ;R0指针指向33H单元
MOV A,#00H ;清A为0
ADDC A,#00H ;求高字节和的进位
MOV @R0,A ;将高字节进位存于33H单元
POP A ;恢复A原内容
【例4.3 】 求双字节补码程序段
设:存于内部RAM的addr1和addr1+1单元的双字节数读出取补后存入addr2和addr2+1单元中,其中高字节数存于高地址单元,图4.7为双字节数求补流程图
图4.7双字节数取补流程图
8位字长的单片机对双字节数取补需分两次进行,首先对低字节数取补后判其结果是否为0,若为全0,则要对高字节数取补,否则取反即可。双字节数取补程序估如下:
START:MOV R0,#addr1 ;原码低字节数地址送R0
MOV R1,#addr2 ;补码低字节数地址送R1
MOV A,@R0 ;原码低字节数送A
CPL A ;对低字节数取补
INC A
MOV @R1,A ;低字节补码存入addr2单元
INC R0 ;R0、R1内容分别加1
INC R1
JZ ZERO ;判(A)=0?当(A)=0,则转ZERO
MOV A,@R0 ;原码高字节数送A
CPL A ;对A内容取反
MOV @R1,A ;高字节数补码存addr2+1
SJMP LOOP1 ;转结束
ZERO:MOV A,@R0 ;低字节取补后为0,则对高字节数取补
CPL A
INC A
MOV @R1,A ;将高字节补码存addr2+2
LOOP1:END ;结束
所有条件判跳指令均属相对寻址方式,其相对偏移量是一个带符号的8位二进制码,常以补码形式出现。其寻址范围为-128~+127B。编程时应多加注意。
【例4.4 】 由累加器A在动态运行中给出的结果值选择对应的转移指令,其对应关系为:
(A)=0,转向分反处理程序0
(A)=1,转向分支处理程序1
(A)=n,转向分支处理程序n
一般转移指令均为有条件转移指令,而MCS-51单片机有两条无条件转移指令:AJMP和LJMP。前者为双字节指令,后者为三字节指令,因此,需视选用何种跳转指令而应对A中值作相应变换。如选用AJMP指令,则应对A值变换成偶数值;如选用LJMP指令,则应对A值乘3的变换。每个分支处理程序均为各自独立的等程序段,分散在各自的程序存储器区段。要皮,必须有一个中转站,轩向各自的分支处理程序。这个无条件转移指令串的首地址由DPTR指示。现以AJMP为例,其程序段如下:
START:MOV DPTR,#addr16 ;跳转指令串首址送DPTR
CLR C ;清C为0
RLC A ;将A值变换成偶数
JNC TABEL ;判(C)=0?不为0则转
INC DPH ;(C)=1,则DPH内容+1
TABEL:JMP @A+DPTR ;散转
…
ADDR16:AJMP LOOP0 ;无条件转移指令串
AJMP LOOP1
…
AJMP LOOPn
LOOP0:… ;分支程序段0
…
LOOP1:… ;分支程序段1
…
由于上例选用绝对值转移指令AJMP是双字节指令,因此,要求A中内容必须换算成偶数。换算方式可以乘2,而这里采用左移一位的办法来实现。如果n值等于或大于128,则左移一位将产生高位进位,将进位值加到DPH中去,等于将转移指令串首址延伸256个存储单元,所以在程序段中对C进行测判。这样,保证分支处理程序段可以在0-255个中任选。
如果选用长调用LJMP,它是怎么样三字节指令,在进A值换算时应乘3处理,将积的高字节值加到DPH中去。一般,DPTR+A的最终值应不超过64KB范围。
【例4.5】 采用循环程序实现延时,一般可达到任意延时要求,但需牺牲CPU的工作。
MOV 40H,#data ;设置计数初值
AGIN: NOP
NOP
DJNZ 40H,AGIN ;当(40H)-1≠0则继续循环
上例每循环一次共需4个机器周期,实际总延时由40H单元内容和主频(fosc)的频率所设置决定。
【例4.6】 数据块搜索
设外部RAM从BLOCK单元开始有一个无符号数据块,其长度(即数据块个数)存于LEN单元,试求出数据块 最大值数据,并存于MAX单元中。
寻找最大值的方法很多,最基本的方法是比较和交换依次进行,即先读取第一个数与第二个数相比较,并把前一个数作为基准。比较结果:若基准数在,则不作交换,再取下一个数进行比较;若基准数小,则将大数取代原基准数,即作一次交换,然后再以新的基准数与下一个数作比较,直到全部比较完毕。基准数始终保持为最大数值。图7.6为数据块搜索流程图。
设R1中存放基准数,R3为数据长度,R2中存放每次读出的新的数据块。
其循环程序段如下;
START:CLR A ;A清0
CLR R1 ;清R1为0
MOV R3,LEN ;数据块长度送R3作控制计数
LOOP:MOVX A,@DPTR ;读数据块
INC DPTR 指向下一个单元
MOV R2 ,A ;将读出的灵敏据块送R2
CLR C ;清C为0
MOV A ,R1 ;基准值送A
SUBB A,R2 ;基准数一读出数
JNC NEXT ;(C)=0,即(A)≥(R2),跳转
MOV R1,A
NEXT:DJNZ R3LOOP ;判搜索完否
MOV MAX,R1 ;最大数据存入MAX单元
END ;结束
【例4.7 】 工作单元清0
设R1中存放被清0单元首地址,R3中存放清0字节数,则其循环程序如下:
START;MOV R1,#addr ;清0单元首地址送R1
MOV R3,#data ;清0字节数送R3
CLR A ;A清0
LOOP:、MOV @R1,A ;指定单元清0
INC R1工 ;指向下一个单元
DJNZ R3,LOOP ;(R3)-1≠0,继续清0
END ;结束
【例4.8】 4位BCD码整数转换成二进制整数
入口参数:BCD码字节地址指针R0,位数存于R2中。
出口参数:二进制数存于R3R4中。
算法:A=103a3+102a2+10a1+a0
程序流程如7.7所示。
子程序清单如下:
BCDA:PUSH PSW ;现场保护
PUSH A
PUSH B
MOV PSW,#08H
MOV R3,#00H
MOV R2,#3 ;BCD码D的位数
MOV A,@R0 ;a0-R4
MOV R4,A
BCKB:MOV A,R3 ;(R3R4)×10
MOV B,#10 ;R4
MUL AB
MOV R4,A
XCH A,B
MOV B,#10
XCH A,R3
MUL AB
ADD A,R3
XCH A,R4
INC R0 ;(R0)+1-R0
ADD A,@R0 ;( R3R4)- ((R0))-RR3R4
XCH A,R4
ADDC A,#0
MOV R3,A
DJNZ R2,BCDB ;循环n-1次
POP B ;恢复现场
PIP A
POP PSW
RET ;返回
上例中的R2内容是BCD码的位数n,本例中n=4,即两个字节4位BCD码,在程序中作为循环控制寄存器的计数值为n-1=4-1=3,即本例循环3次即完成二次字节的BCD码转换。
本例采用乘10运算,也可采用除2运算进行转换。
【例4.9】 多字节十进制数加法子程序
入口参数:将R0指针的内部RAM中N个字节的BCD码被加数,R1指针的内产RAM中N个字节的BCD码中数进行相加。R2中存放字数N。
出口参数:相加结果的BCD码和数存入R0指针的内部RAM(原被加数单元)中。
图4.8为多字节BCD码加法子程序流程图。子程序清单如下:
图4.8多字节BCD码加法子程序流程图
BCDADD:PUSH PSW
PUSH A
MOV PSW,#08H
MOV A,R0
ADD A,R2
MOV R0,A
DEC R0
MOV A,R1
ADD A,R2
MOV R1,A
DEC R1
CLR C
ADDA:MOV A,@R0
ADDC A,@R1
DA A
MOV @R0,A
DEC R1
DEC R0
DJNZ R2,ADDA ;判加完否
JNC ADDB ;若(C)=0则转ADDB
MOV A,#00H ;清A为0
ADDC A,#00H ;若(C)=1,则为最高位进位
MOV @R0,A
ADDB:POP A ;现场恢复
POP PSW
RET ;返回
本例中两数均按高位字节数存放于低地址单元,而相国运算则从低位字节数开始,R0、R1指针在运算前均指向最高字节数的地址,故需转换成指向最低字节数的地址,然后进行相加运算。
【例4.10】 应用系统采用LED七段显示,可将BCD码或十六进制码以及某些符号通过查找LED显示编码进行显示输出。
七段LED显示器有共阴极和共阳极之分,其显示编码是不同的,共阳极是低电平为有效输入;共阴极则是高电平输入有效,LED的编码不是序码,即表格无规律可寻,可根据各自方便排列。例如BCD码显示,同是其显示码可按0~9的顺序排列。假如显示器是共阳极的,则BCD码与显示器编码的对应关系为:0的显示编码为0100 0000,即40H,1的编码为0111 1001,即79H;2的编码为24H,3的编码为30H,…,对于这类问题,通过查表来实现比较方便。具体程序段如下:
MOV DPTR,#LEDA ;表格首址送DPTR
MOV A,@A+DPTR ;变址寄存器A中内容为查表主序号, 即要显示的内容,如0,1,2,…,9
MOVC A,@A+DPTR ;读出要显示的内容
;进行LED显示
LEDA:DB 40H,79H,24H,30H,12H
DB 20H,78H,00H,18H,…
对于这类问题,一般均选用DPTR作为基址指针的查表指令为宜。这样,表格串可设置在64KB的任何地址段,可实现任意次查表。