《单片机原理与实验教程》课件第4章_第1页
《单片机原理与实验教程》课件第4章_第2页
《单片机原理与实验教程》课件第4章_第3页
《单片机原理与实验教程》课件第4章_第4页
《单片机原理与实验教程》课件第4章_第5页
已阅读5页,还剩137页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第4章MCS-51汇编语言程序设计

4.1简单程序设计

4.2分支程序设计

4.3循环程序设计

4.4散转程序设计4.5子程序和参数传递方法

4.6查表程序设计4.7数制转换习题与思考题程序设计是为了解决某一个问题而将指令有序地组合在一起的过程。程序有简有繁,但复杂程序往往是由简单的基本程序所构成的。本章将通过一些基本程序,介绍部分常用的程序设计方法。程序设计的过程大致可以分为以下几个步骤:(1)编制要解决问题的程序框图。(2)确定数据结构、算法、工作单元、变量设定。(3)根据所用计算机的指令系统,按照已编制的程序框图用汇编语言编制出源程序。(4)将编制的程序在计算机上调试,直至实现预定的功能。4.1简单程序设计

简单程序又称顺序程序。计算机是按指令在存储器中存放的先后次序来顺序执行程序的。

【例4-1】两个8位的无符号数相加的和仍是8位。设内部RAM的40H、41H单元中分别存放8位数N1、N2,两数相加的结果送42H单元。程序如下:

AD1:MOV

R0,#40H;设R0为数据指针

MOVA,@R0;取N1

INCR0;修改指针

ADDA,@R0;N1+N2

INCR0

MOV@R0,A;存结果

END

【例4-2】将两个半字节数合并成一个一字节数。设内部RAM的40H、41H单元中分别存放着8位二进制数,要求取出两个单元中的低半字节,并将其合并成一个字节后存放在42H单元。程序如下:START:MOV

R1,#40H

MOVA,@R1ANLA,#0FH;取第一个半字节

SWAPA;移至高4位

INCR1XCHA,@R1;取第二个字节

ANLA,#0FH;取第二个半字节

ORLA,@R1;拼字

INCR1MOV@R1,A;存放结果

END以上程序均采用了寄存器寻址方式,可以方便地取数、存数。例4-2中用XCHA,@R1指令,既取出了数,又保存了中间结果。4.2分支程序设计

在处理实际事务中,只用简单程序设计的方法是不够的,因为大部分程序总包含有判断、比较等情况,程序将根据判断、比较的结果转向不同的分支。下面举两个分支程序的例子。

【例4-3】两个无符号数比较大小。设存储单元ST1和ST2中存放着两个不带符号的二进制数,找出其中的大数存入ST3单元中。流程图如图4-1所示。图4-1两个无符号数的比较流程程序如下:

ST1:EQU8040H

ORG8000H

START1:CLRC;进位位清0

MOVDPTR,#ST1;设数据指针

MOVXA,@DPTR;取第一个数

MOVR2,A;暂存于R2

INCDPTR

MOVXA,@DPTR;取第二个数

SUBBA,R2;两数比较

JNCBIG1

XCHA,R2;第一个数大BIG0:INCDPTR

MOVX@DPTR,A;存大数

RETBIG1:MOVXA,@DPTR;第二个数大

SJMPBIG0

END上面程序中,用减法指令SUBB来比较两数的大小。由于这是一条带借位的减法指令,因此在执行该指令前,应先把进位位清0。用减法指令通过借位(CY)的状态判别两数的大小,是两个无符号数比较大小时常用的方法。设有两数x,y,执行x-y,当x≥y时,结果无借位产生,则CY=0;反之,则CY=1,表示x<y。用减法指令比较大小,会破坏累加器中的内容,故做减法前先保存累加器中的内容。执行JNC指令后,形成了分支。执行SJMP指令后,实现了程序的转移。

【例4-4】编制计算符号函数y=SGN(x)的程序。设自变量x已存入标号为ARE的单元:-128≤x≤127。y存入标号为BUF的单元。流程图见图4-2。图4-2计算符号函数流程源程序如下:

ARE

EQU8050H

BUF

EQU8060H

NEG

EQU8070H

ORG

8000H

START2:MOVDPTR,#ARE

MOVXA,@DPTR

JZSUL;x=0,转SUL

JBACC.7,NEG;判x的符号位

MOVA,#01;x>0,1→A

SUL:MOVDPTR,#BUF

MOVX@DPTR,A

END

NEG:MOVA,#FFH;x<0,-1→A

SJMPSUL这是一个二次判断的程序,它与例4-3不同。例4-3程序只有一次判断,所以是单分支程序,本例则是二分支程序。分支程序在实际使用中用处很大,除了用于比较数的大小之外,常用于控制子程序的转移。在4.4节中将详细介绍分支程序的使用。4.3循环程序设计在程序设计中,只有简单程序和分支程序是不够的。因为简单程序每条指令只执行一次,而分支程序则根据条件的不同,会跳过一些指令,执行另一些指令。它们的特点是,每一条指令至多执行一次。在处理实际事务时,有时会遇到多次重复处理的问题,用循环程序的方法来解决就比较合适。循环程序中的某些指令可以反复执行多次。采用循环程序,可以缩短程序,节省存储单元。当重复次数越多时,循环程序的优越性就越明显。但是程序的执行时间并不节省。由于要有循环准备、结束判断等指令,速度要比简单程序稍慢些。循环程序一般由五部分组成:(1)初始化部分:为循环程序做准备。如设置循环次数计数器的初值、地址指针置初值、为循环变量赋初值等。

(2)处理部分:为反复执行的程序段,是循环程序的实体。(3)修改部分:每执行一次循环体后,对指针作一次修改,使指针指向下一数据所在位置,为进入下一轮处理做准备。(4)控制部分:根据循环次数、计数器的状态或循环条件等检查循环是否能继续进行,若循环次数到或循环条件不满足时,应退出循环,否则继续循环。通常(2)、(3)、(4)部分又称为循环体。(5)结束部分:分析及存放执行结果。循环程序的结构一般有两种形式:(1)先进入处理部分,再控制循环,即至少执行一次循环体,如图4-3(a)所示。(2)先控制循环,后进入处理部分,即根据判断结果控制循环的执行与否,有时可以不进入循环体就退出循环程序,如图4-3(b)所示。图4-3循环程序的结构形式循环结构的程序,不论是先处理后判断,还是先判断后处理,其关键是控制循环的次数。根据需解决问题的实际情况,对循环次数的控制有多种:循环次数已知的,用计数器控制循环;循环次数未知的,可以按条件控制循环,也可以用逻辑尺控制循环。循环程序又分单循环程序和多重循环程序。下面举例说明循环程序的使用。

1.单循环程序

1)循环次数已知的循环程序下面举几个循环次数已知的循环程序例子。

【例4-5】工作单元清0。在程序设计时,有时需要将存储器中的部分地址作为工作单元,存放程序执行的中间值和结果。工作单元清0工作常常放在程序的初始化部分中。设有50个工作单元,其首址存放在DPTR中,循环次数存放在R2寄存器中,每执行一次循环,R2的内容减1,直至R2=0时,循环程序结束。流程图见图4-4图4-4工作单元清0流程图

CLEAR:CLR

A[KG8]

MOVR2,#32H;置计数值

LOOP:MOVX@DPTR,A

INCDPTR;修改地址指针

DJNZR2,LOOP;控制循环

END本例中循环次数是已知的。用R2作循环次数计数器,用DJNZ指令修改计数器值,控制循环的结束与否。

【例4-6】多个单字节数据求和。已知有n个单字节数据,依次存放在内部RAM的40H单元开始的连续单元中,要求把计算结果存入R2、R3中(高位存R2,低位存R3)。流程图见图4-5。图4-5多个单字节数据求和流程图程序如下:

NUMEQU0AH

ORG8000H

SAD:MOVR0,#40H;设数据指针

MOVR5,#NUN;计数值0AH→R5

SAD1:MOVR2,#0;和的高8位清0

MOVR3,#0;和的低8位清0

LOOP:MOVA,@R3;取加数

ADDA,@R0

MOVR3,A;存和的低8位

JNCLOP1

INCR2;有进位,和的高8位+1

LOP1:INCR0;指向下一数据地址

DJNZR5,LOOP

END上述程序中,用R0作间址寄存器,每做一次加法,R0加1,数据指针指向下一数据地址,R5为循环次数计数器,控制循环的次数。

2)循环次数未知的循环程序以上介绍的几个循环程序例子,它们的循环次数是已知的,适合用计数器置初值的方法。而有些循环程序事先不知道循环次数,不能用以上方法。这时需要根据判断循环条件的成立与否,或用建立标志的方式控制循环程序的结束。

【例4-7】测试字符串长度。设有一串字符依次存放在内部RAM的从50H单元开始的连续单元中。该字符串以回车符为结束标志。测字符串长度程序,即将该字符串中的每一个字符依次与回车符相比;若比较不相等,则统计字符串长度的计数器加1;若比较相等,则表示该字符串结束,计数器中的值就是字符串的长度。程序如下:

CONT:MOVR2,#0FFH

MOVR0,#4FH;数据指针R0置初值

LOOP:INCR0

INCR2

CJNE@R0,#0DH,LOOP

END

待测字符以ASCII码形式存放在RAM中,回车符的ASCII码为0DH,程序中用一条CJNZ@R0,#0DH,LOOP指令实现字符比较及控制循环的任务,当循环结束时,R2的内容为字符串长度。

3)用逻辑尺控制循环次数的循环程序在实际应用中,有时并不要求按一定的固有顺序执行循环处理,而要求处理部分是不规则的循环过程。如调用处理部分的子程序PRG0、PRG1共8次。调用顺序为:第1、3、4、7次是调用PRG1;其余是调用PRG0。程序的设计方法是:设总的调用次数为循环次数,并且另设一个标志,用来识别调用哪个子程序。两个子程序可以用0和1两种逻辑状态识别,如用0状态表示调用PRG0,用1状态表示调用PRG1。由于总共调用8次,因此用一个寄存器的8位作为8个标志位,按调用子程序的顺序,把0、1存放在寄存器的不同位上。当执行程序时,由寄存器的第0位或第7位起逐位测试,根据此位的状态决定调用哪个子程序。因此该寄存器好像一把“尺”,它的内容犹如“尺”的“刻度”,作为识别调用子程序的标志,人们称它为“逻辑尺”。“逻辑尺”的长宽视需要而定,一个字节不够用,可以把相邻的字节串联起来,或者设定n把“逻辑尺”。

【例4-8】逻辑尺使用。设R0为原始数首地址,R1存放结果首地址,R3为逻辑尺,流程图如图4-6所示。图4-6逻辑尺程序流程图程序如下:

PRG0EQU

8360H

PRG1EQU

8200H

ORG8000HTEST:MOVR2,#08H;逻辑尺长度

MOVR3,#10110010B;设定逻辑尺

LOOP:MOVA,@R0;取数据

XCHA,R3RLCA;左移,读尺刻度

XCHA,R3JCLOOP1;转调用PRG1LCALLPRG0;是0,调用PRG0SJMPRELT

LOOP1:LCALLPRG1RELT:MOV@R1,A;存结果

INCR0

INCR1

DJNZR2,LOOP;R2≠0,继续循环END

2.多重循环程序

如果在一个循环体中又包含了其他的循环程序,即循环中还套着循环,则这种程序称为多重循环程序。

【例4-9】

10s延时程序。延时程序与MCS-51执行指令的时间有关,如果使用6MHz晶振,则一个机器周期为2μs,计算出执行一条指令以及一个循环所需要的时间,给出相应的循环次数,便能达到延时的目的。程序如下:

DEL:MOVR5,#100

DEL0:MOVR6,#200

DEL1:MOVR7,#248

DEL2:DJNZR7,DEL2;248*2+4

DJNZR6,DEL1;(248*2+4)*200+4DJNZR5,DEL0;((248*2+4)*200+4)*100+4

RET上例延时程序实际延时为10.000406s。它是一个三重循环程序,利用程序嵌套的方法对时间实行延迟是程序设计中常用的方法。使用多重循环程序时,必须注意以下几点:(1)循环嵌套必须层次分明,不允许产生内外层循环交叉。(2)外循环可一层层向内循环进入,结束时由里往外一层层退出。(3)内循环体可以直接转入外循环体,实现一个循环由多个条件控制的循环结构方式。

【例4-10】冒泡程序。设有N个数,它们依次存放于LIST地址开始的存储区域中,将N个数比较大小之后,使它们按由小到大(或由大到小)的次序排列,存放在原存储区域中。编制该程序的方法:依次将相邻两个单元的内容作比较,即第一个数和第二个数比较,第二个数和第三个数比较……如果符合从小到大的顺序则不改变它们在内存中的位置,否则交换它们之间的位置。如此反复比较,直至数列排序完成为止。由于在比较过程中将小数(或大数)向上“冒”,因此这种算法称为“冒泡法”或称为排序法。下列表格可以说明冒泡法的比较过程。第一轮经过6次两两比较后,得到一个最大数。第二轮经过5次两两比较后,得到一个次大数。

……每轮比较后均得到本轮最大数(或最小数),该数就不再参加下一轮的两两比较,故进入下一轮时,两两比较次数减1。为了加快数列排序速度,程序中设置一个标志位,只要在比较过程中两数之间没有发生过交换,就表示数列已按大小顺序排列了,可以结束比较。根据以上分析,可画出流程图如图4-7所示。图4-7冒泡程序流程图设数列首地址在R0寄存器中,R2为外循环次数计数器,R3为内循环次数计数器,R1为交换标志。程序如下:

CONTEQU07H

LIST

DB0,13,3,90,27,32,11

ORG8000H

MOVR2,#CNT-1;数列个数-1

LOOP1:MOVA,R2;外循环计数值

MOVR3,A;内循环计数值

MOVR1,#01;交换标志置1LOOP2:MOVA,@R0;限数据

MOV

B,A;暂存B

INC

R0

CLR

C

SUBB

A,@R0;两数比较

JC

LESS;Xi<Xi+1,转LESS

MOV

A,B;取大数

XCH

A,@R0;两数交换位置

DEC

R0

MOV@R0,A

INC

R0;恢复数据指针

MOV

R1,#02;置交换标志为2LESS:DJNZR3,LOOP2;内循环计数减1,判一遍是否查完

DJNZR2,LOOP3;外循环计数减1,判排序是否结束STOP:RETLOOP3:R1,LOOP1;交换转移

SJMP

STOP

ORG

50H;(内部RAM)

END

3.循环程序应用举例

1)不带符号数的多字节加法在处理多字节运算时,应注意低字节向高字节的进位(或借位)(用进位CY判别,当CY=0时,表示无进位或借位,反之则表示有进位或借位)。在进行不带符号的单字节二进制数加减运算时,用进位CY判别数的溢出与否。

【例4-11】两个多字节数P、Q均以低字节在前,高字节在后的次序,分别存放在由R0、R1指出的内部RAM中,相加后存入P数据区,见图4-8。图4-8

RAM数据存储示意程序如下:

N1

EQU0AH

ORG

8000H

START1:CLR

C;清进位

MOV

R2,#N1;取字节数

MADD:MOV

A,@R0;取加数(一个字节)

ADDC

A,@R1;两数相加(由低字节开始)MOV@R0,A

INC

R0

INC

R1

DJNZ

R2,MADD;两数加完?JC

ERR;和字节数大于n,则溢出错RET

ERR:…

END上例中若将ADDCA,@R1指令改为SUBBA,@R1指令,则该程序就是多字节减法程序。无符号数的减法运算,被减数必须大于减数。不带符号的十进制数加法程序的设计思想与上例相同。

MCS-51指令系统中没有直接的十进制数加法指令,要做十进制BCD码加减运算,只要在加、减指令后跟一条DAA指令,即可使运算结果成为十进制数的形式。

2)带符号数的加减运算带符号的定点二进制数加减运算程序和无符号的定点二进制数加减运算程序基本相同,只是判断溢出的条件不同。带符号的两个定点二进制数参加运算时,应先判断两数符号,然后决定其加减运算。即首先应判两数符号是否相同。若两运算数符号相同,则数值部分相加。加法结果有溢出时,转溢出处理;加法结果无溢出时,最后结果的符号与被加数的符号相同。如两运算数符号不同,则执行减法。如够减,则结果符号为被减数符号;不够减则应将差取补,结果符号为减数符号。对减法运算,可以先把减数符号取反,再进行加法运算。根据上述算法,可画出原码加减运算的流程图,见图4-9。图4-9原码加减运算流程图

【例4-12】双字节原码加减法程序。

R2R3±R4R5→R6R7已知:R2R3、R4R5分别存放16位带符号二进制数;R2、R4的最高位分别是两数的符号位,0代表正数,1代表负数;结束时CY=1表示溢出,CY=0表示无溢出。

BSUB为原码减法程序入口;BADD为原码加法程序入口。程序如下:

F0

EQU

20H

ORG

8000HBSUB:MOV

A,R4

CPL

ACC.7;减数符号位取反

MOV

R4,ABADD:MOV

A,R2

MOV

C,ACC.7

MOVF0,C;保存被加数符号位

XRL

A,R4MOV

C,ACC.7;两数同号,CY=0;两数异号,CY=1MOV

A,R2

CLR

ACC.7;符号位清0

MOV

R2,A;取数值部分

MOV

A,R4

CLR

ACC.7;符号位清0

MOV

R4,A;取数值部分

JC

BX2ADD1:MOVA,R3;同号,两个双字节数相加ADDA,R5;低位相加MOVR7,A;存低位和MOVA,R2ADDCA,R4;高位相加MOVR4,A;存高低和JB

ACC.7,BERR

BX1:

MOV

C,F0;恢复结果符号MOV

ACC.7,CMOV

R4,ARETBERR:SETB

CRETBX2:MOVA,R3;异号,两个双字节数相减

CLRCSUBBA,R5;低位相减

MOVR7,A;存低位结果MOVA,R2SUBBA,R4;高位相减MOVR6,A;存高位结果

JNBACC.7,BX1;判结果符号,为正则转

BX1BMP:MOVA,R7;符号为负,则对差求补

CPLAADDA,#1MOVR7,AMOVA,R6CPLAADDCA,#0MOVR6,ACPLF0;保存在用户标志位F0中的符号取反SJMP

BX1

END本例分为四个部分处理,其中第一部分是符号处理,这是为了使数值以正整数形式参加运算;第二部分是加法运算;第三部分是减法运算;第四部分是结果取补运算。

3)乘法运算在MCS-51指令系统中有专门的单字节乘法指令MULAB,该指令功能为A*B→BA。下面将用这条乘法指令实现无符号双字节数的乘法运算。两个单字节数相乘,结果为双字节,可以用字节为单位的竖式乘法扩展其为双字节数的乘法。采用乘法和加法结合实现乘法运算的示意图见图4-10,其执行速度比移位相加方法要快得多。图4-10采用乘法和加法结合实现乘法运算

【例4-13】

R2R3*R6R7→R4R5R6R7。双字节被乘数在R2、R3中,双字节乘数在R6、R7中,R3*R7→R3R7、R2*R7→R2R7;同理,R3*R6→R3R6,R2*R6→R2R6。执行四次乘法后,每次乘积经累加器,最后R4R5R6R7中。程序如下:

F0

EQU20

ORG8000HDBMU1:MOVA,R3MOVB,R7MULAB;R3*R7XCHA,R7;乘积低位→R7,R7→A准备乘数MOVR5,B;乘积高位暂存R5MOV

B,R2MUL

AB;R7*R2ADD

A,R5;乘积低位加上一次的乘积高位暂存R4MOV

R4,ACLR

A;清累加器ADDCA,B;高位加从低位来的进位后暂存R5MOV

R5,AMOV

A,R6MOV

B,R3MUL

AB;R6*R3ADD

A,R4;第三次乘积低位加R5暂存R6XCH

A,R6XCH

A,BADDC

A,R5;第三次乘积高位加R5暂存R5MOV

R5,AMOV

F0,C;保存进位位MOV

A,R2MUL

AB;R2*R6ADD

A,R5;第4次乘积低位加R5暂存R5MOV

R5,ACLR

AMOVACC.0,CMOVC,F0ADDCA,B;第4次乘积高位加低位来的进位后存于R4MOVR4,ARETEND

4)除法运算除法是乘法的逆运算,用移位、相减的方法完成。根据手算除法的原则,上商前先比较被除数与除数,如果被除数大于除数,商上1,然后做减法,否则商上0,说明不够减,不执行减法。在计算机中,判断够减不够减的方法是做一次减法,由余数的符号决定,如果余数为正说明够减,商为1,否则商为0。根据上述算法思想,画出除法程序的流程图,如图4-11所示。图4-11除法流程图

【例4-14】无符号双字节数的除法:

R4R5R6R7÷R2R3→R6R7,余数→R4R5已知:被除数存放在R4R5R6R7中,除数存放在R2R3中,商送R6R7中。由商送R6R7可知,本题中商为16位,若商大于16位则称为溢出。在进行除法前,先比较R4R5和R3R2的内容,如被除数高字节(两个字节)大于等于除数,则溢出,不执行除法,F0置1,表示出错直接返回;否则执行除法,这时F0=0。用减法实现被除数和除数比较,结果保存在累加器A和寄存器R1中,如果够减,商上1,回送减法结果,否则不回送。商上1用加1的方法实现,商上0不加1。程序如下:

F0

EQU20H

ORG8000HBDIV:MOVA,R5;判商是否产生溢出

CLRC

SUBBA,R3

MOVA,R4

SUBBA,R2

JNCDIV1;被除数高位字节大于除数,转溢出处理

MOV

B,#16;无溢出则执行除法,置循环次数DIV2:CLR

C;被除数向左移一位,低位送0

MOV

A,R7

RLC

A

MOV

R7,A

MOV

A,R6

RLC

A

MOV

R6,A

MOV

A,R5

RLC

A

MOV

R5,AXCH

A,R4RLC

AXCH

A,R4MOV

F0,C;保护移出的最高位CLR

CSUBB

A,R3;被除数与除数比较MOV

R1,AMOV

A,R4SUBB

A,R2JB

F0,DV2;高位移出位为1,够减转DV2JC

DV3

DV2:MOV

R4,A;回送减法结果MOV

A,R1MOV

R5,AINC

R7;商上1DV3:DJNZ

R1,DIV2;不够减,循环次数-1CLR

F0;正常执行无溢出,F0=0RETDIV1:SETB

F0;置溢出标志RETEND

【例4-15】带符号二进制数的除法。

R4R5R6R7÷R2R3→R6R7,余数→R4R5被除数和除数以原码表示,符号位在最高位。带符号的原码除法,对符号位的处理方法与带符号的原码乘法对符号位的处理方法是相同的,即将两数符号位作异或运算后的结果设为结果的符号。除法运算时,先确定商的符号,然后把被除数、除数作无符号除法运算,最后把符号送商的符号位。程序如下:

F0

EQU20H

ORG8000HDDIV:MOVA,R2;被除数符号异或除数符号XRLA,R2

MOVC,ACC.7;商的符号通过进位送(20H)保存

MOVF0,C

MOVA,R4;被除数转换成无符号数形式CLRACC.7

MOVR4,A

MOVA,R2;除数转换成无符号数形式

CLRACC.7

MOVR2,A

ACALLDDIV;调用无符号双字节除法子程序MOVA,R6

JBACC.7,DDIV1;溢出,转出错处理MOV

C,F0;回送商的符号

MOV

ACC.7,C

MOV

R6,ADDIV1:SETB

F0;出错F0=1

RET

END4.4散转程序设计

散转程序是分支程序的一种,它由输入条件或运算结果来确定转入何种处理程序。有多种方法能实现散转程序,但通常用逐次比较法,即把所有情况逐一进行比较,若有符合条件便转向对应的处理程序。由于每一种情况都有判断和转移,如对n种情况,需要n次判断和转移,因此它的缺点是程序比较长。MCS-51指令系统中有一条跳转指令JMP@A+DPTR,用它可以很容易地实现散转功能。该指令把累加器作为转移指令的地址。执行该指令后,累加器和16位数据指针的内容均不受影响。

1.用转移指令表实现散转

在许多场合中,要根据某一单元的值(0,1,2,…,n)分别转向相应处理程序(处理程序0,处理程序1……处理程序n),这时可以用转移指令AJMP(或LJMP)组成一个转移表。

【例4-16】根据R6的内容(R6=0,转PRGM0;R6=1,转PRGM1……R6=n,转PRGMn),转向各个处理程序。把转移标志送累加器A,转移表首地址送DPTR,利用JMP@A+DPTR指令实现转移。程序如下:

PJD:MOV

DPTR,#TAB1

MOV

A,R6

ADD

A,R6;R6*2→A

JNC

PAD

INC

DPH;R6*2>256,16位数据指针高8位加1

PAD:JMP@A+DPTR

TAB1:AJMP

PRGM0;转处理程序0的首地址

AJMP

PRGM1;转处理程序1的首地址

AJMP

PRGMn;转处理程序n的首地址本例适用于散转表首地址TAB1和处理程序入口地址PRGM0、PRGM1……PRGMn在同一个2KB范围存储区内的情形。如一个2KB范围的存储区内放不下所有的处理程序时,则把一些较长的处理程序放在其他存储区域,只要在该处理程序的入口地址内调用LJMP指令即可。方法有两种:(1)例如处理程序PRGM0、PRGM3比较长,要把两个程序转至其他区域,则分别把它们的入口地址用符号PPRGM0、PPRGM3表示,以实现程序的转移。

PRGM0:LJMPPPRGM0

PRGM3:LJMPPPRGM3(2)可以直接用LJMP指令组成转移表。由于LJMP是3字节的指令,因此在组成指令转移表后,当执行JMP@A+DPTR指令时,可能出现DPTR低8位向高8位进位的情况,所以必须先用加法指令调整DPTR的值后才能用JMP@A+DPTR指令实现跳转。程序如下:PJ2:MOV

DPTR,#TAB2

CLR

C

MOV

R5,#0

MOV

A,R6

RLC

A;R6*2

JNC

P1

INC

R5;有进位,高8位加1P1:ADD

A,R6;R3*3

JNC

P2

INC

R5;有进位,高8位加1P2:MOV

A,R5

ADD

A,DPH;DPTR高8位调整MOV

A,R6

JMP@A+DPTR;得散转地址TAB2:LJMP

PRGM0LJMP

PRGM1

LJMP

PRGMn用AJMP组成的散转表为2字节一项,而用LJMP组成的散转表则为3字节一项,根据R6中的内容或乘2,或乘3,得每一处理程序的入口地址表指针。

2.用转移地址表实现散转当转向范围比较大时,可直接使用转向地址表方法,即把每个处理程序的入口地址直接置于地址内。用查表指令,找到对应的转向地址,把它装入DPTR中。将累加器清0后用JMP@A+DPTR直接转向各个处理程序的入口。

【例4-17】根据R3的内容转向对应处理程序。处理程序的入口分别为PR0~PRn。程序如下:PJ3:MOV

DPTR,#TAB3

MOV

A,R3

ADD

A,R3;R3*2

JNC

CAD

INC

DPH;有进位,DPTR高位加1CAD:MOV

R2,A;暂存R2

MOVCA,@A+DPTR

XCHA,R2;处理程序入口地址高8位暂存R2

INC

A

MOVC

A,@A+DPTR

MOV

DPL,A;处理程序入口地址低8位暂存DPL

MOV

DPH,R2

CLR

A

JMP@A+DPTR

TAB3:DW

PR0

DW

PR1

DW

PRn本例可实现64KB范围内的转移,但散转数n应小于256。如大于256时,应采用双字节数加法运算修改DPTR。

3.用RET指令实现散转以上介绍的两种方法均采用JMP@A+DPTR指令实现散转功能。这里要介绍的散转方法不是把转向地址装入DPTR,而是将它压入堆栈,然后通过RET指令把转向地址退栈到PC中,使栈指针恢复原值。

【例4-18】根据R5R6内容,转向不同的处理程序。程序如下:PJ4:MOV

DPTR,#TAB4

MOV

A,R6

CLR

C

RLC

A

XCH

A,R5

RLC

A

ADD

A,DPH

MOV

DPH,A;R5R6*2高8位加到DPH

MOV

DPH,A;R5R6*2低8位存A

MOV

A,R5;从表中得到处理程序入口地址高8位

MOVCA,@A+DPTR

XCHA,R5

INC

DPTR

MOVCA,@A+DPTR;从表中得到处理程序入口地址低8位

PUSH

ACC;地址低8位进栈

MOV

A,R5

PUSH

ACC;地址高8位进栈

RET;转向地址退栈装入PC中TAB4:DW

PR0

DW

PR1

DW

PRn4.5子程序和参数传递方法

在实际程序中,常常会多次进行一些相同的计算和操作,如数制转换、函数式计算等等。如果每次都从头开始编制一段程序,不仅麻烦,而且浪费存储空间。因而将一些常用的程序段以子程序的形式,事先存放在存储器的某一个区域,当主程序在运行中需要用子程序时,只要执行调用子程序的指令,使程序转至子程序即可。当子程序处理完毕后,返回主程序,继续进行以后的操作。调用子程序有几个优点:(1)避免了对相同程序段的重复编制。(2)简化程序的逻辑结构,同时也便于子程序调试。(3)节省存储器空间。

MCS-51指令系统中,提供了两条调用子程序指令:ACALL及LCALL,并提供了一条返回主程序的指令RET。对于子程序的调用,一般包含两个部分:保护现场和恢复现场。由于主程序每次调用子程序的工作是事先安排的,因此根据实际情况,有时可以省去保护现场的工作。调用子程序时,主程序应先把有关的参数存放在约定的位置,子程序在执行时,可以从约定的位置取得参数;当子程序执行完后,将得到的结果存入约定的位置,返回主程序后,主程序可以从这些约定的位置上取到需要的结果,这就是参数的传递。下面结合MCS-51单片机的特点,介绍几种参数传递方法。

1.用累加器或寄存器进行参数的传递

用累加器和寄存器存放输入参数及结果参数,可以提高程序的运算速度,而且程序也很简单。如本章例4-15的带符号的原码除法子程序就是采用此方法。它的不足之处是参数不能传递得很多,因为寄存器的数量有限;主程序在调用子程序前必须将参数先送入寄存器;由于子程序参数的个数是固定的,因此主程序不能任意设定参数的多少。

2.用指针寄存器进行参数的传递

当程序中所需处理的数据量比较大时,常常用存储器存放数据,而不用寄存器。用指针指示数据在存储器中所处的位置,可以大大节省参数传递中的工作量。使用指针的方法能实现数据长度可变的运算。MCS-51指令系统中提供的由R0、R1作间址寄存器的指令很多,当参数存放在内部RAM时,用R0、R1作指针使参数的传递十分方便。如例4-6的多个单字节数据求和程序,调用时R0作为存放参数的地址指针,R5存放参数长度,R2R3存放运算结果,作为子程序被调用时,其入口地址为SAD。当参数在外部RAM或在程序存储器时,可用DPTR作指针。对可变长度的数据做运算时,数据长度可由寄存器指出,也可采用在数据后设置标志的方法。

3.用堆栈进行参数传递堆栈可以用于主程序调用子程序时相互之间的参数传递。调用前,主程序用PUSH指令把参数压入堆栈,子程序在执行中按堆栈指针间接访问栈中参数,并且把运算结果送回堆栈。返回主程序后,主程序用POP指令得到堆栈中的结果参数。利用堆栈传递参数的方法比较简单,而且传递参数量比用寄存器来传递参数多得多,也不必为特定的参数分配存储单元。下面举几个例子介绍用堆栈进行参数传递的方法。

【例4-19】一位十六进制数转换成ASCII码。程序如下:

HEASC:MOVR0,SP;借用R0为堆栈指针

DECR0

DECR0;R0指向被转换参数地址

XCHA,@R0;保护累加器,取被转换参数

ANLA,#0FH

ADDA,#2;PC表首地址

MOVCA,@A+PC;查表

XCH

A,@R0;结果送回堆栈

RETATAB:DB

30H,31H,32H,33H,34H,

35H,36H,37H,38H,39H

DB

41H,42H,43H,44H,45H,46HEND调用上述子程序后,后一位十六进制数转换成对应的ASCII码。输入参数位于栈顶,进入子程序时,堆栈中又压入了两个字节的返回地址,将原来的输入参数变成新栈顶起的第三个字节。R0的内容经两次减1后,指向原来的输入参数。利用两条XCH指令实现了两个操作:被转换输入参数的提取及转换后的结果参数送回原堆栈中的操作,而A的内容没有破坏。

【例4-20】把内部RAM中40H单元1字节的十六进制数转换成2位ASCII码,存放在R1指出的两个单元中,调用HEASC子程序。程序如下:

ORG

8000HMAIN:MOV

A,40H;直接寻址,(40H)→A

SWAPA;2位十六进制数半字节交换

PUSH

ACC

ACALLHEASC

POP

ACC

MOV@R1,A;高半字节转换成ASCII码,存结果

INC

R1

PUSH40H

ACALLHEASC

POP

ACC

MOV@R1,A;低半字节转换成ASCII码,存结果

END由于HEASC子程序每次只能完成1位十六进制数对ASCII码的转换,因此对于1字节的十六进制数转换,需要在主程序中两次调用HEASC。如果被转换的十六进制数是多字节时,则调用程序将占去很多存储空间。下面仍采用堆栈传递参数法,介绍1字节的2位十六进制数转换成ASCII码的子程序。

【例4-21】被转换数据存放在R0指出的堆栈地址中,结果送原单元及其下一单元。程序如下:

HEAS2:MOVR0,SP;借用R0为堆栈指针

DECR0

DECR0;R0指向被转换参数地址

PUSHACC;保护累加器

MOVA,@R0;取出参数

ANLA,#0FH;取右半字节

ADDA,#14;得

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论