9.1.2 子程序及其调用

9.1.2 子程序及其调用

1.子程序的调用

利用子程序可以使程序结构更加紧凑,使程序的阅读和调试更加方便。在实际应用中,经常会遇到一些带有通用性的问题,如数值转换、数值计算等,这些在一个程序中可能要使用多次。此时可以将其设计成通用的子程序,以供随时调用。

子程序的基本结构仍然采用一般的程序的3种结构,它的特点是子程序的执行过程是由被其他程序调用开始的,执行完子程序后再返回到调用该子程序的程序继续执行。

子程序调用时要注意两点:一是现场的保护和恢复;二是主程序与子程序的参数传递。

2.现场保护与恢复

在子程序执行过程中常常要用到单片机的一些寄存器和存储单元,如工作寄存器R0~R7、累加器A、数据指针DPTR以及有关标志和状态等。子程序中用到的这些单元中的内容在调用结束后的主程序中仍有用,而主程序需要保持这些寄存器和存储单元在调用子程序之前的值,所以需要在进入子程序之前对这些内容进行保护,称为现场保护。在执行完子程序,返回继续执行主程序前恢复其原内容,称为现场恢复。现场保护与恢复可以在主程序中实现,也可以在子程序中实现。

(1)在主程序中实现

利用压栈和弹栈实现现场保护和恢复,调用子程序前进行压栈,返回主程序后先弹栈再执行其他操作。特别地,对于R0~R7的保护与恢复可以通过改变工作寄存器组来实现。在主程中实现现场保护和恢复的程序如下:

(2)在子程序中实现

同样利用压栈和弹栈进行现场保护和恢复,在子程序中实现的过程是在进入子程序后,进行压栈操作,并改换当前工作寄存器组,并在子程序结束返回主程序前,进行弹栈操作并恢复当前工作寄存器组。在子程序中实现现场保护和恢复的程序如下:

应注意的是,无论使用哪种方法,现场保护与恢复的顺序都要对应,利用堆栈进行现场保护和恢复时压栈和弹栈的顺序正好相反,否则程序将会发生错误。

3.参数传递

由于子程序是主程序的一部分,所以,在程序的执行中必然要有数据上的联系。在调用子程序时,主程序应通过某种方式把有关参数传给子程序,称为子程序入口参数。当子程序执行完毕后,又需要通过某种方式把有关参数传给主程序,称为子程序出口参数。在MCS-51单片机中,参数传递可以利用累加器、寄存器、存储器和堆栈。

(1)利用累加器或寄存器

利用累加器或寄存器传递参数,就是把要传递的参数存放在累加器A或工作寄存器R0~R7中,即在主程序调用子程序时,应事先把子程序需要的数据送入累加器A或指定的工作寄存器中,当子程序执行时,可以从指定的单元中取得数据,执行运算。反之,子程序也可以用同样的方法把结果传送给主程序。

【例9-7】 设变量x、y分别存放在片内RAM的30H、31H单元中,编程计算x和y的平方和,并存放在片内RAM的32 H单元中。

程序如下:

(2)利用存储器

当传送的数据量比较大时,可以利用存储器实现参数的传递。方法是事先建立一个参数表,用指针指示参数表所在的位置。当参数表建立在内部RAM时,用R0或R1作参数表的指针。当参数表建立在外部RAM时,用DPTR作参数表的指针。

【例9-8】 设内部RAM中存放两个3字节无符号整数,低字节存放在低地址单元,两个加数的首地址分别是31 H和34 H,编程实现其求和。结果存放到片外数据存储器1000H单元起始的存储区内,按照低字节存放在低地址单元,高字节存放在高地址单元的顺序存放,并由数据指针DPTR指向和的高字节。

子程序入口参数:R0和R1分别指向两个加数的低位字节,R0指向31H,R1指向34H,DPTR指向和的低位字节,即1000 H。

子程序出口参数:DPTR指向结果的高位字节,即1002H。

程序如下:

(3)利用堆栈

利用堆栈传递参数是在子程序嵌套中常采用的一种方法。在调用子程序前,用PUSH指令将子程序中所需数据压入堆栈。进入执行子程序时,再用POP指令从堆栈中弹出数据。

【例9-9】 编写程序实现片内RAM中30 H单元存储的BCD码转换为ASCII码,十位数的ASCII码存放在31H单元中,个位数的ASCII码存放在32H单元中。

程序如下:

上述三种的方法都可以实现参数传递,但适用情况略有区别。一般地,当相互传递的数据较少时,采用累加器或寄存器传递方式可以获得较快的传递速度;当相互传的数据较多时,宜采用存储器或堆栈方式传递,如果是子程序嵌套,最好采用堆栈方式传递。