ASM51无参数化调用C51函数的实现

ASM51无参数化调用C51函数的实现,第1张

【摘 要】利用汇编模块对C51模块进行“无参数”式调用,从根本上避开了传统汇编模块和C51模块之间调用时的繁琐接口编程问题,本文以实例验证了该方法的优越性和有效性。
   关键词:ASM51 C51 无参数化调用
  
1 引 言
  随着计算机应用技术的发展,MCS-51系列单片机在我国工业测控领域中的应用日益广泛。因而,如何快速有效地进行MCS-51系列单片机的开发便成了大家颇为关心的话题。ASM51汇编语言虽然简洁高效,但晦涩难懂,不易编写,它还要求开发人员对硬件亦有相当的了解。相比而言,专为MCS-51系列单片机设计的Franklin C51语言是一种通用的高级结构化的程序设计语言,入门容易,程序可读性强,调试、移植都很方便,开发效率高,尤其在数值运算处理等方面具有很大的优势(而这正是汇编语言的薄弱环节)。因而,在效率为重的今天,将ASM51汇编语言与Franklin C51语言两者结合起来进行混合编程,充分发挥各自的优势,无疑是单片机开发人员的最佳选择。
  笔者曾开发过一系统,要求用单片机根据实时采样输入的转速实现机车速度的测量,并可随键盘输入的车轮直径变化实时调整车速,最后将车速和轮径值都显示出来。设计任务很简单,编程中的最大难度就在于车速的计算程序。由于轮径值要求精确到毫米(最大值超过了1000),车速的计算结果要保留到小数点后一位。因此必然要进行浮点数运算,另外还涉及到数据的各种进制间的换算。虽然算法简单,但实际运用时,用汇编语言实现起来经常考虑不周,调试起来费时费力。那如何利用C51语言来帮助简化编程呢?
2 ASM51汇编语言与C51语言的混合编程
  一般的混合编程都是利用C51上手容易、便于理解的优点来编写主程序,同时在C51语言不便处理或者效率比较低时调用汇编函数。MCS-51单片机(尤其是8031)内部的资源配置情况如下:可用的RAM不到256字节,5个固定地址的有限中断,4个8位并口中往往实际可作I/O口的只有P1口。鉴于此,要求开发者对单片机内部的结构有清楚的了解,并尽可能地统筹安排这些资源。另外,事实证明不理解汇编语言是很难写出高效程序的。故笔者倾向利用汇编语言在I/O接口、中断向量及程序空间分配上一目了然的巨大优势 ,让程序员对MCS-51内的每一个字节甚至是每一位Bite(可以位寻址的空间)进行全面统筹安排,设计好各个程序模块,包括I/O口地址的处理和中断向量的地址等(而这些简短程序用ASM51汇编语言来写并不困难);同时在具体的数据处理、通信等不太需要过多与硬件直接打交道的程序模块中(这也正是汇编语言的薄弱环节),充分利用C51语言强大高效的编程能力。
   针对前面提出的问题,可先用ASM51汇编语言设计好各个模块,包括“循环显示车速和轮径值”的主程序模块、响应“采样转速值”和“键盘输入”两个中断的模块,如例1所示(具体实现过程略)。
例1:
   EXTRN CODE(CALL1);声明外部C51函  数CALL1()
   ORG 0000H 
   LJMPMAIN
   ORG 0003H
   AJMPKEYINPUT;键盘输入中断
   ORG 000BH
   AJMPSETTIME;采样时间到,采样转速值中断
   ORG 0100H
  KEYINPUT:......;键盘输入中断......;将键盘输入信号保存在70h-73h的地址空间中
   RETI
   ORG 0600H
   SETTIME:......;采样时间到,采样转速值中断.....;将转速值放置在地址为3Ah的空间中
;紧接着调用外部C51函数CALL1()进行车速的计算
   LCALLCALL1RETI
   ORG 2000H;主程序模块MAIN:......;首先进行初始化 *** 作......
;直接从地址空间70h-77h中读取显示数据,循环显示车速和轮径值
   END
  这些小模块用汇编语言实现起来不仅容易,而且程序员可以清楚地了解到各个模块的出入口及其相应的功能,实现对程序空间的充分配置。
    另外用C51语言来实现复杂的车速计算模块CALL1(),结果以前用汇编语言编写的近四百行代码,一下子被压缩到二三十行(真正的计算代码仅九行,参见例2中的代码)!不仅简短易懂,而且几乎就不需要调试了!最后的一个关键是如何让汇编模块正确识别C51函数CALL1()并调用它来完成相应的功能。
3 ASM51“无参数化”调用C51函数的实现原理
  为了实现函数调用时参数的正确传送,一般都要在ASM51汇编语言与C51语言之间制定一系列约定,而各种编译器使用的约定不尽相同,甚至还依赖于程序所选择的大、中、小存储模式。通常每个需传递的参数按调用顺序和类型分别由约定的寄存器来传递;参数过多或者无足够寄存器可用时,参数的传递将在一定的存储器区域内进行,相同类型的参数共享一个参数传递段,按参数调用顺序递增其存放地址,返回值也由约定的寄存器或地址段返回。由此可以想见,接口如何复杂,程序调用的效率也将受到影响。尽管目前的单片机仿真器已经普遍提供了这种标准接口约定的几乎全自动转换,减少了接口工作量,但在程序的调试及移植中,如果程序员不了解这些接口的各种约定,将对出现的错误不知所措。比如在前面要实现的车速计算模块CALL1()中,为便于其显示模块中 *** 作简便,要求该模块能直接按小数、个、十、百位返回车速,由于返回值有多个(另有两个参数),故编译器自己无法正确完成接口配置。而且由于这种接口编程与纯C51语言或者纯汇编语言有一定差别,难免造成初学者对此望而生畏(限于篇幅,这种带参数调用函数的繁琐实现请读者参考其它资料 。)。经过笔者的尝试,这里向大家推荐一种简捷有效的调用方法——“无参数化”调用。
  所谓的“无参数化”调用实现是指让C51子函数不带任何形参,这样就可以从根本上避开调用参数的传递和返回值的安排等繁琐易出错的问题,而只需要简单地在汇编语言开头说明一下外部C51子函数(“EXTRN code(<C51模块名称>)”)。至于C51函数中需要使用的外部参数值及其返回值,则通过加入C51提供的<absacc.h>头文件来解决。
ASM51无参数化调用C51函数的实现,第2张
    其中CBYTE定义为寻址CODE程序区;DBYTE定义为寻址DATA数据区;PBYTE定义为寻址相对于MOVX@R0"指令的分页数据XDATA区;XBYTE定义为寻址相对于MOVX@DPTR "指令的外部数据XDATA区。它们的类型决定了所访问的绝对地址空间的位置。
  引进该头文件后,程序员就可以对8051系列单片机的存储器进行绝对地址的访问 ,把对参数值和返回值的 *** 作转化为对存储器绝对地址的 *** 作,像纯汇编 *** 作一样,也就不用定义C51函数与汇编接口的参数和返回值配置,从而提高了调用效率。具体做法是:先在C51函数中定义好传递参数和返回值所需要的各个绝对地址(视程序员自己的空间配置而定,如例2中#define处所示,括号“〔〕”中即为8051的RAM绝对地址),在别的汇编模块中将C51函数中将要使用的参数值放入这些绝对地址中,在被调用的C51模块中将输出的计算值(可以不止一个)也同样放入类似的绝对地址中。于是,当C51函数中需要使用某个参数传递的值时,就直接从相应的绝对地址中读取该值;当别的汇编模块中需要使用C51函数的返回值时,也同样直接对存放返回值的绝对地址进行读 *** 作即可。笔者用该法轻松实现了对具有多个返回值的计算模块CALL 1()的调用。
4 ASM51“无参数化”调用C51函数的实现示例
   例2是计算模块CALL1()及相应绝对地址定义的代码实现。
例2:
   #pragma code small
   #include<absacc.h>
   #include<math.h>
  #define PI3.1415926;以下定义几个需要的绝对地址
  #define NCIRCLE DBYTE〔0x3A〕;定义放置转速的绝对地址
  #define DIRECT1 DBYTE〔0x70〕;定义放置轮径千位的绝对地址
  #define DIRECT2 DBYTE〔0x71〕;定义放置轮径百位的绝对地址
  #define DIRECT3 DBYTE〔0x72〕;定义放置轮径十位的绝对地址
  #define DIRECT4 DBYTE〔0x73〕;定义放置轮径个位的绝对地址
  #define VELOCITY1 DBYTE〔0x74〕;定义返回车速的百位绝对地址
  #define VELOCITY2 DBYTE〔0x75〕;定义返回车速的十位绝对地址
  #define VELOCITY3 DBYTE〔0x76〕;定义返回车速的个位绝对地址
  #define VELOCITY4 DBYTE〔0x77〕;定义返回车速的小数位绝对地址
ASM51无参数化调用C51函数的实现,第3张
   在例2中主程序前用9个define语句预定义了绝对地址空间70H-77H和3AH。其中3AH的空间中存放“采样转速值输入”中断模块输入的转速;70H-73H的地址空间中存放“键盘输入中断”中断模块中键盘输入的轮径值;而地址为74H-77H的空间中则存放“计算”模块中的车速计算返回值。尽管需要传递和返回的参数比较多,但通过这些绝对地址的定义,完全解决了原来复杂的汇编与C51之间的调用接口配置。“计算”模块中需要使用转速和轮径值时,将自动从相应绝对地址3AH和70H-73H中取值 ;在“循环显示车速和轮径值”的主程序模块中则直接读取绝对地址空间70H-77H的各个数据进行循环显示。当然,程序员可以根据自己的空间配置另外定义这些绝对地址 。
  以上程序代码已在“Dais-52.196P”仿真器上调试通过并正确运行。
  由上面的简单程序已经可以看出这种“无参数化”调用方法的优越性和有效性:从程序代码看,无论是编写C51子程序还是汇编主程序,都与编写纯C51函数或者纯汇编主程序的格式完全一样,简化了C51与汇编函数之间的接口编程,提高了程序调用的效率;同时充分利用了汇编与高级C51语言各自的优点,开发、调试快速方便,通用性强,尤其适合于初学者。对于复杂程序,同样可以利用“无参数化”方法来帮助实现。这对于加快单片机应用程序的开发效率和普及是很有意义的。
5 结束语
  “无参数化”调用实质上相当于在C51函数中定义了几个全局变量(绝对地址),依靠他们直接完成参数值的传递和返回值的调用,相当于一种程序员自定义的传递方式,而抛弃了传统C语言与汇编语言之间的接口约定。只要程序员安排得当,还可以进一步人工实现C51中的“动态覆盖重用”,提高RAM区的利用效率。由上也可看出:“无参数化”调用方法要在AS M汇编调用C51函数时才能充分发 挥其巨大优势;如果全部采用C51编程,就违背了利用汇编语言优势的初衷。当然,如果开发人员已经对C51与汇编函数之间的参数传递接口很熟悉,那么也完全可以按接口约定或者由编译器自动完成参数的传递。
  参 考 文 献

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/dianzi/2481422.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-08-04
下一篇 2022-08-04

发表评论

登录后才能评论

评论列表(0条)

保存