#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)

成就更好的自己

 

目录

3.8  8051汇编语言程序设计举例

3.8.1  子程序的设计

3.8.2  查表程序设计

3.8.3  分支转移程序设计

3.8.4  循环程序设计


 

3.8  8051汇编语言程序设计举例

  介绍常用的汇编语言程序的设计。

3.8.1  子程序的设计

  将那些需多次应用的、完成相同的某种基本运算或操作的程序段从整个程序中独立出来,单独编成一个程序段,需要时进行调用。这样的程序段称为子程序。

  优点:采用子程序可使程序结构简单,缩短程序的设计时间,减少占用的程序存储空间。

  子程序在程序设计中非常重要,读者应熟练掌握子程序的设计方法。

1.子程序的设计原则和应注意的问题

编写子程序应注意以下问题:

 子程序的入口地址,前必须有标号。

 主程序调用子程序,是通过调用指令来实现。有两条子程序调用指令:

(1)绝对调用指令ACALL addr11。双字节,addr11指出了调用的目的地址,PC中16位地址中的高5位不变,被调用的子程序的首地址与绝对调用指令的下一条指令的高5位地址相同,即只能在同一个2KB区内。

(2)长调用指令LCALL addr16。三字节,addr16为直接调用的目的地址,子程序可放在64KB程序存储器区任意位置。

子程序结构中必须用到堆栈,用来进行断点和现场的保护。

子程序返回主程序时,最后一条指令必须是RET指令,功能是把堆栈中的断点地址弹出送入PC指针中,从而实现子程序返回后从主程序断点处继续执行主程序。

子程序可以嵌套,即主程序可以调用子程序,子程序又可以调用另外的子程序。

2.子程序的基本结构

典型的子程序的基本结构如下:

MAIN: ……               ;MAIN为主程序入口标号

              ……

              LCALL  SUB       ;调用子程序SUB

              ……

              ……

子程序

SUB:          PUSH  PSW      ;现场保护         

             PUSH  Acc       

 

 

              POP     Acc ;现场恢复,注意要先进后出

              POP     PSW            

              RET        ;最后一条指令必须为RET

  注意:上述子程序结构中,现场保护与现场恢复不是必需的,要根据实际情况而定。

3.8.2  查表程序设计

  查表程序是一种常用程序,避免复杂的运算或转换过程,可完成数据补偿、修正、计算、转换等各种功能,具有程序简单、执行速度快等优点。

  查表是根据自变量x,在表格寻找y,使y =f(x)。单片机中,数据表格存放于程序存储器内,在执行查表指令时,发出读程序存储器选通脉冲 。两条极为有用的查表指令如下:

    (1)MOVC      A,@A+DPTR

    (2)MOVC      A,@A+PC

两条指令的功能完全相同,具体使用有差别。

指令“MOVC  A,@A+DPTR” 把A中内容与DPTR中的内容相加,结果为某一程序存储单元的地址,然后把该地址单元的内容送到A中。

指令“MOVC  A,@A+PC” ,PC的内容与A的内容相加后所得的数作为某一程序存储器单元的地址,根据地址取出程序存储器相应单元中的内容送到累加器A,指令执行后,PC的内容不发生变化,仍指向该查表指令的下一条指令。

优点:在于预处理较少且不影响其他特殊功能寄存器的值,不必保护其他特殊功能寄存器。

   缺点:在于该表格只能存放在这条指令的地址X3X2X1X0以下00H~FFH之中,即只能存放在地址范围X3X2X1X0+1~X3X2X1X0+100H中,这就使得表格所在的程序空间受到了限制。

1. x和y均为单字节数的查表程序设计

    下面举例说明查表指令的用法以及计算偏移量时应该注意的问题。

  【例3-14】 设计一子程序,功能是根据累加器A中的数x(0~9之间)查x的平方表y,根据x的值查出相应的平方y。本例中的x和y均为单字节数。

   地 址                     子程序

Y3Y2Y1Y0           ADD      A,#01H

Y3Y2Y1Y0+2             MOVC        A,@A+PC

Y3Y2Y1Y0+3             RET

Y3Y2Y1Y0+4             DB  00H,01H,04H,09H,10H                                     DB  19H,24H,31H,40H,51H

                     ;数0~9的平方表

   第一条指令“ADD  A,#01H”的作用是A中的内容加上 “01H”,“01H” 为查表的偏移量,即查表指令与平方表之间的所有指令所占的字节数。这里的指令为“RET”,为单字节指令。加上“01H”后,可保证PC指向表首,累加器A中原来的内容反映的仅是从表首开始向下查找多少个单元。

在进入程序前,A的内容在00~09H之间,如A中的内容为02H,它的平方为04H,可根据A的内容查出x的平方

由于 “MOVC  A,@A+DPTR”不必计算偏移量,一般情况下,大多使用该指令。不必计算偏移量,优点是表格可以设在64KB程序存储器空间内的任何地方,而不像“MOVC  A,@A+PC”那样只设在PC下面的256个单元中,所以使用较方便。

如果DPTR已被使用,则在查表前必须保护DPTR,且结束后恢复DPTR,【例3-14】可改成如下形式:

              PUSH    DPH                 ;保存DPH

   PUSH    DPL                   ;保存DPL

       MOV     DPTR,#TAB1

              MOVC   A,@A+DPTR

              POP       DPL                  ;恢复DPL

              POP       DPH                 ;恢复DPH

              RET

TAB1:       DB       00H,01H,04H,09H,10H      ;平方表

       DB       19H,24H,31H,40H,51H

2.  x为单字节数y为双字节数的查表程序设计

  实际查表,有时x为单字节数,y为双字节数。来看下例。

  【例3-15】有一巡回检测报警装置,需对16路(x)输入进行检测,每路有一个最大允许值(y),为双字节数。需根据测量的路数(x),查表找出对应该路的最大允许值(y),看输入值是否大于最大允许值,如果大于就报警。

     取路数为x(0≤x≤15),y为最大允许值,放在表格中。设进入查表程序前,假设路数x已放于R2中,查表后该路的最大允许值y放于R3R4中。查表的程序如下:

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第1张图片

   表格长度不能超过256B,且表格只能存放于“MOVC A,@A+PC”指令以下的256个单元中,如需把表格放在程序存储器空间的任何地方,应使用指令“ MOVC  A,@A+DPTR”。

  【例3-16】以AT89S52为核心的温度控制器,温度传感器输出的电压与温度为非线性关系,传感器输出的电压已由A/D转换为10位二进制数。测得的不同温度下的电压值数据构成一个表,表中温度值为y(双字节无符号数),x(双字节无符号数)为电压值数据。设测得电压值x放入R2R3中,根据电压值x,查找对应的温度值y,仍放入R2R3中。参考程序:

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第2张图片

   由于使用了指令“MOVC  A,@A+DPTR”,表TAB2可放入64KB程序存储器空间任何位置,表格的长度可大于256B。

   如果表格的长度超过256B,且需要把表格放在64KB程序存储器空间的任何地方,且应使用指令“MOVC  A,@A+DPTR”,并对DPH、DPL进行运算,求出表格的目的地址。

3.x和y均为双字节数的查表程序设计

下面来看一个x与y均为双字节数的查表例子。

   【例3-16】在一个以AT89S52为核心的温度控制器中,温度传感器输出的电压x与温度值为非线性关系。假设温度传感器输出的电压值x(已由A/D转换器转换为10位二进制数,并存入R2R3中),查找到对应的温度值y,仍放入R2R3中。温度值表的首地址为TAB2,表中元素为y(双字节无符号数)。

参考程序如下:

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第3张图片

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第4张图片

3.8.3  分支转移程序设计

     分为无条件转移和有条件转移。

     无条件分支转移程序很简单,不讨论。有条件分支转移程序按结构类型来分,又分为单分支选择结构和多分支选择结构。

1.单分支选择结构

     仅有两个出口,两者选一。一般根据运算结果的状态标志,用条件判跳指令来选择并转移。

   【例3-17】  求单字节有符号数的二进制补码

     正数补码是其本身,负数补码是其反码加1。因此,应首先判被转换数的符号,负数进行转换,正数本身即为补码。

设二进制数放在A中,其补码放回到A中,框图如图3-5所示。参考程序如下:

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第5张图片

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第6张图片

 

此外,单分支选择结构还有图3-6、图3-7所示的几种形式。

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第7张图片#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第8张图片

2.多分支选择结构

   当程序的判别部分有两个以上的出口时,为多分支选择结构。有两种形式,如图3-8和图3-9所示。

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第9张图片

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第10张图片

指令系统提供了非常有用的两种多分支选择指令:

       间接转移指令    JMP    @A+DPTR

       比较转移指令     CJNE   A,direct,rel

                      CJNE   A,#data,rel

                      CJNE   Rn,#data,rel

                      CJNE   @Ri,#data,rel

     间接转移指令“JMP  @A+DPTR”由数据指针DPTR决定多分支转移程序的首地址,由A的内容选择对应分支。

     4条比较转移指令CJNE能对两个欲比较的单元内容进行比较,当不相等时,程序实现相对转移;若两者相等,则顺序往下执行。

    简单的分支转移程序的设计,常采用逐次比较法,就是把所有不同的情况一个一个地进行比较,发现符合就转向对应的处理程序。缺点是程序太长,有n种可能的情况,就需有n个判断和转移。

【例3-18】  求符号函数的值。符号函数定义如下:

      X存放在40H单元,Y存放在41H单元,如图3-7所示。

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第11张图片

     实际中,经常遇到图3-9的分支转移程序设计,典型例子就是当单片机系统中的键盘按下时,就会得到一个键值,根据不同的键值,跳向不同的键处理程序入口。此时,可用直接转移指令(LJMP或AJMP指令)组成一个转移表,然后把该单元的内容读入累加器A,转移表首地址放入DPTR中,再利用间接转移指令实现分支转移。

【例3-19】  根据寄存器R2的内容,转向各个处理程序PRGX(X=0~n)。

              (R2)=0,转PRG0

              (R2)=1,转PRG1

                ……

              (R2)=n,转PRGn

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第12张图片

    R2中的分支转移参量乘3是由于长跳转指令LJMP要占3个单元。

3.8.4  循环程序设计

程序中含有可以反复执行的程序段,称循环体。例如,求100个数的累加和,没必要连续安排100条加法指令,用一条加法指令使其循环执行100次。因此可缩短程序长度和程序所占的内存单元数量更少,使程序结构紧凑。

1.循环程序的结构

    主要由以下四部分组成。

   (1)循环初始化

    完成循环前的准备工作。例如,循环控制计数初值的设置、地址指针的起始地址的设置、为变量预置初值等。

   (2)循环处理

    完成实际的处理工作,反复循环执行的部分,故又称循环体。

   (3)循环控制

在重复执行循环体的过程中,不断修改循环控制变量,直到符合结束条件,就结束循环程序的执行。

循环结束控制方法分为循环计数控制法和条件控制法。

(4)循环结束

这部分是对循环程序执行的结果进行分析、处理和存放。

2.循环结构的控制

   分为循环计数控制结构和条件控制结构。图3-10是计数循环控制结构,图3-11是条件控制结构。

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第13张图片

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第14张图片

(1)计数循环控制结构

   依据计数器的值来决定循环次数,一般为减1计数器,计数器减到“0”时,结束循环。计数器初值在初始化设定。

MCS—51指令系统提供了功能极强的循环控制指令:

       DJNZ    Rn,rel     ;以工作寄存器作控制计数器

       DJNZ    direct,rel       ;以直接寻址单元作控制计数器

 

例如,计算n个数据的和,计算公式为                 。

 

     如直接按公式编写程序,则n=100时,需编写连续的100次加法。这样程序将太长,并且n可变时,将无法编写出程序。

公式要改写为用程序实现的形式,用下式表示

程序框图见图3-12。

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第15张图片

【例3-20】 求n个单字节无符号数xi的和,xi按i顺序存放在AT89S51单片机内部RAM从50H开始的单元中,n放在R2中,和(双字节)放在R3R4中。

程序如下:

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第16张图片

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第17张图片

     用寄存器R2作为计数控制变量,R0作为变址单元,用它来寻址xi。

     一般来说,循环工作部分中的数据应该用间接方式来寻址,如这里用:ADD   A,@R0

     计数控制只有在循环次数已知的情况下才适用。循环次数未知,不能用循环次数来控制,往往需要根据某种条件来判断是否应该终止循环。

(2)条件控制结构

   结构见图3-11。循环控制中,设置一个条件,判是否满足该条件,如满足,则循环结束。如不满足该条件则循环继续。

   【例3-21】 一串字符,依次存放在内部RAM从30H单元开始的连续单元中,字符串以0AH为结束标志,测试字符串长度。

采用逐个字符依次与“0AH”比较(设置的条件)的方法。设置一个累计字符串长度的长度计数器和一个用于指定字符串指针。

如果字符与“0AH”不等,则长度计数器和字符串指针都加1;如果比较相等,则表示该字符为“0AH”,字符串结束,计数器值就是字符串的长度。

   程序如下:

#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第18张图片

上面两例都是在一个循环程序中不再包含其他循环程序,则称该循环程序为单循环程序。如果一个循环程序中包含了其他循环程序,则称为多重循环程序。

最常见的多重循环是由DJNZ指令构成的软件延时程序,是常用程序之一。

【例3-22】 50ms延时程序。

  软件延时程序与指令执行时间有很大的关系。在使用12MHz晶振时,一个机器周期为1µs,执行一条DJNZ指令的时间为2µs。可用双重循环方法的延时50ms程序:#STC89C51# #STC89C52# #AT89S51# #AT89C52#--8051内核单片机原理及汇编(六):指令系统(下)_第19张图片

以上延时程序不是太精确,如把所有指令的执行时间计算在内,它的延时时间为

[1+(1+250+2)× 200+2]µs=50.603ms

如要求比较精确的延时,应对上述程序进行修改,才能达到较为精确的延时时间。但要注意,用软件实现延时程序,不允许有中断,否则将严重影响定时的准确性。

对于延时更长的时间,可采用多重的循环,如1s延时,可用三重循环。

 

你可能感兴趣的:(8051原理及汇编)