




已阅读5页,还剩3页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
作者:xzp21st邮箱: 撰文辛苦,转载请注明作者及出处关键字:keilc51,模拟堆栈,可重入函数调用,参数传递,C?XBP,C?ADDXBP摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。1、 关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)“可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。”(摘自嵌入式实时操作系统uC/OS-II)在理解上述概念之前,必须先说一下keilc51的“覆盖技术”。(采用该技术的原因请看附录中一网友的解释)(1)局部变量存储在全局RAM空间(不考虑扩展外部存储器的情况); (2)在编译链接时,即已经完成局部变量的定位; (3)如果各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。 正是由于以上的原因,在Keil C51环境下,纯粹的函数如果不加处理(如增加一个模拟栈),是无法重入的。举个例子:void TaskA(void* pd) int a; /其他一些变量定义 do /实际的用户任务处理代码 while(1); void TaskB(void* pd)int b; /其他一些变量定义 do func(); /其他实际的用户任务处理代码 while(1); void func() int c;/其他变量的定义 /函数的处理代码 在上面的代码中,TaskA与TaskB并不存在直接或间接的调用关系,因而它们的局部变量a与b便是可以被互相覆盖的,即它们可能都被定位于某一个相同的RAM空间。这样,当TaskA运行一段时间,改变了a后,TaskB取得CPU控制权并运行时,便可能会改变b。由于a和b指向相同的RAM空间,导致TaskA重新取得CPU控制权时,a的值已经改变,从而导致程序运行不正确,反过来亦然。另一方面,func()与TaskB有直接的调用关系,因而其局部变量b与c不会被互相覆盖,但也不能保证func的局部变量c不会与TaskA或其他任务的局部变量形成可覆盖关系。根据上述分析我们很容易就能够判断出TaskA和TaskB这两个函数是不可重入的(当然,func也不可重入)。那么如何让函数成为可重入函数呢?C51编译器采用了一个扩展关键字reentrant作为定义函数时的选项,需要将一个函数定义为可重入函数时,只要在函数后面加上关键字reentrant即可。与非可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为可重入函数生成一个模拟栈(相对于系统堆栈或是硬件堆栈来说),通过这个模拟栈来完成参数传递和存放局部变量。模拟栈以全局变量?C_IBP、?C_PBP和?C_XBP作为栈指针(系统堆栈栈顶指针为SP),这些变量定义在DATA地址空间,并且可在文件startup.a51中进行初始化。根据编译时采用的存储器模式,模拟栈区可位于内部(IDATA)或外部(PDATA或XDATA)存储器中。如表1所示:存储模式栈指针栈区域Small?C_IBP(1字节)间接访问的内部数据存储器(IDATA),栈区最大为256字节Compact?C_PBP(1字节)分页寻址的外部数据存储器(PDATA),栈区最大为256字节Large?C_XBP(2字节)外部数据存储器(XDATA),栈区最大为64K表1注意:51系列单片机的系统堆栈(也叫硬件堆栈或常规栈)总是位于内部数据存储器中(SP为 8位寄存器,只能指向内部),而且是“向上生长”型的(从低地址向高地址),而模拟栈是“向下生长”型的。2、 可重入函数参数传递过程剖析在进入剖析之前,先简单讲讲c51函数调用时参数是如何传递的。简单来说,参数主要是通过寄存器R1R7来传递的,如果在调用时,参数无寄存器可用或是采用了编译控制指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所选择的存储器模式。利用51单片机的工作寄存器最多传递3个参数,如表2所示。传递的参数char、1字节指针int、2字节指针long、float一般指针第一个参数R7R6,R7R4R7R1,R2,R3第二个参数R5R4,R5R4R7R1,R2,R3第三个参数R3R2,R3无R1,R2,R3表二举两个例子:func1(int a):“a”是第一个参数,在R6,R7中传递;func2(int b,int c, int *d):“b”在R6,R7中传递,“c”在R4,R5中传递,“*d”则在R1,R2,R3中传递。至于函数的返回值通过哪些寄存器或是什么方法传递这里就不说了,大家可以看看c51的相关文档或是书籍。好了,接下来我们开始剖析一个简单的程序,代码如下:int fun(char a, char b, char c, char d ) reentrant /为了分析简单,参数都是char型;int j1,j2;j1 = a + b + c +d;j2 = j1 + 10;return j2;main()int i;i = fun(1,2,3,4); 程序很简单,废话少说,下面跟我一起看看c51翻译成的汇编语言是什么样子的。 main() int i; i = fun(1,2,3,4); MOV DPTR,#0xFFFF ; 模拟栈指针C?XBP最初指向0xFFFF+1LCALL C?ADDXBP(C:00A6);调用C?ADDXBP子程序,调整模拟栈指针C?XBP;指向0xFFFFMOV A,#0x04;无寄存器可用,第四个参数直接压入模拟栈MOVX DPTR,A;MOV R3,#0x03;参数3通过R3传递,见表2MOV R5,#0x02;参数2过R5传递,见表2MOV R7,#0x01;参数1通过R7传递,见表2LCALL fun(C:0003) ;调用fun函数MOV DPTR,#C_STARTUP(0x0000) ; fun函数返回值(int型)通过R6,R7传递回来;并存储在外部数据存储器0x0000和0x0001处;(int型为两个字节)MOV A,R6MOVX DPTR,AINC DPTRMOV A,R7MOVX DPTR,A RET ;main返回说明:模拟栈指针最初在startup.a51中初始化为0xFFFF+1;由以上汇编代码可以看出参数是从右往左扫描的。接下来看看fun的汇编代码:(很长,大家耐心看吧,有些可以跳过的)C:0003MOV DPTR,#0xFFFF LCALL C?ADDXBP(C:00A6);调整模拟栈指针C?XBP=C?XBP-1MOV A,R3MOVX DPTR,A;R3中的值(参数3)压入模拟栈MOV DPTR,#0xFFFFLCALL C?ADDXBP(C:00A6) ;调整模拟栈指针C?XBP=C?XBP-1MOV A,R5MOVX DPTR,A ;R5中的值(参数2)压入模拟栈MOV DPTR,#0xFFFF LCALL C?ADDXBP(C:00A6);调整模拟栈指针C?XBP=C?XBP-1MOV A,R7MOVX DPTR,A;R7中的值(参数1)压入模拟栈MOV DPTR,#0xFFFCLCALL C?ADDXBP(C:00A6) ;继续调整模拟栈指针C?XBP=C?XBP-4,为放两个;局部int变量做准备j1 = a + b + c +d; MOV DPTR,#0x0005LCALL C?XBPOFF(C:00CA) ;通过C?XBP的值调整DPTR使其指向模拟栈中第;一个参数,此时DPTR=0xFFFF;注意:C?XBPOFF不改变C?XBP的值MOVX A,DPTRMOV R7,A ;取出参数1MOV A,R7。;省略,完成取参数2,取参数3,取参数4并相加。MOV DPH(0x83),?C_XBP(0x08)MOV DPL(0x82),0x09 ;0x09就是?C_XBP+1MOV A, R6MOVX DPTR,AINC DPTRMOV A,R7MOVX DPTR,A ;计算结果j1压入模拟栈 j2 = j1 + 10; 。;省略,完成j2=j1+10,并把计算结果j1压入模拟栈return j2; MOV DPH(0x83),?C_XBP(0x08)MOV DPL(0x82),0x09INC DPTRINC DPTRMOVX A,DPTRMOV R6,AINC DPTRMOVX A,DPTRMOV R7,A ;从模拟栈取出j2送入R6,R7 MOV DPTR,#?C_XBP(0x0008)LCALL C?ADDXBP(C:00A6);fun要返回,释放模拟栈,使C_XBP指向0xffffRET 说明:模拟栈结构如下参数4参数3参数2参数1j1低字节j1高字节J2低字节J2高字节接下来说明两个重点子函数C_ADDXBP和C_XBPOFF C?ADDXBP:MOV A,0x09 ;0x09即为C_XBPADD A,DPL(0x82) ;以下到第一个RET之前即完成:C_XBP+DPTRMOV DPL(0x82),AMOV A,?C_XBP(0x08)ADDC A,DPH(0x83)MOV DPH(0x83),ACJNE A,?C_XBP(0x08),C:00B9MOV 0x09,DPL(0x82)RET C:00B9 JBC EA(0xA8.7),C:00C2 ;中断开着吗?开着就把它关了(清0),然后跳到C:00C2MOV 0x09,DPL(0x82) ;中断本来就关着,安全,下面的行动不会被打断,把新;的模拟栈指针赋给C_XBPMOV ?C_XBP(0x08),ARET C:00C2 MOV 0x09,DPL(0x82) MOV ?C_XBP(0x08),ASETB EA(0xA8.7) ;开中断RET C?XBPOFF: ;此函数的功能一看就明白,即完成DPTR=C_XBP+DPTRMOV A,0x09 ADD A,DPL(0x82)MOV DPL(0x82),AMOV A,?C_XBP(0x08)ADDC A,DPH(0x83)MOV DPH(0x83),ARET 终于到尾声了,最后重点说明啦模拟堆栈是向下生长的,C_XBP最初等于0xffff+1,那么请看下面这句MOV DPTR,#0xFFFF LCALL C?ADDXBP(C:00A6)(0xffff+1)+0xffff = 0xffff即C_XBP -1;再看MOV DPTR,#0xFFFE LCALL C?ADDXBP(C:00A6)即C_XBP-2再看MOV DPTR,#0xFFFE LCALL C?ADDXBP(C:00A6)即C_XBP-3。其实是这样:加0xffff相当与减1,加0xfffe相当与减2,加0xfffd相当于减4。为啥,就不用说了吧:)结束语:经过了几天的研究,终于写了个总结报告,算是自己的一点小小成就吧,错误之处在所难免,希望能够同大家一起讨论问题,共同进步。参考文献:1、徐爱钧,彭秀华 单片机高级语言C51windows环境编程与应用电子工业出版社 20012、彭光红,构造一个51单片机的实时操作系统。附录:在其它环境下(比如PC,比如ARM),函数重入的问题一般不是要特别注意的问题.只要你没有使用static变量,或者指向static变量的指针,一般情况下,函数自然而然地就是可重入的.但C51不一样,如果你不特别设计你的函数,它就是不可重入的.引起这个差别的原因在于:一般的C编译器(或者更确切点地说:基于一般的处理器上的C编译器),其函数的局部变量是存放于堆栈中的,而C51是存放于一个可覆盖的(数据)段中的.至于C51这样做的原因,不是象有些人说的那样,为了节约内存.事实上,这样做根本节约不了内存.理由如下:1) 如果一个函数func1调用另一个函数func2,那么func1,func2的局部变量根本就不能是同一块内存.C51还是要为他们分配不同的RAM.这跟使用堆栈相比,节约不了内存.2) 如果func1,func2不是在一个调用链上,那么C51可以通过覆盖分析,让它们的局部变量共享相同的内存地址.但这样也不会比使用堆栈节约内存.因为既然它们是在不同的调用链上,那么当其中一个函数运行时,那么另外一个函数必然不在其生命期内,它所占用的堆栈也已释放,归还给系统.真实的原因(C51使用覆盖段作为局部变量的存放地的原因)是:51的指令系统没有一个有效的相对寻址(变址寻址)的指令,这使
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年K2教育中STEM课程实施与效果评估:创新模式探索
- 语文(武汉卷)2025年中考考前押题最后一卷
- 经导管植入式人工心脏瓣膜2025
- 小升初六年级数学下册常考易考知识点课件《第六单元第10讲:解方程》人教版
- 智慧政务+DeepSeek大模型应用方案
- AI大模型赋能数字化水利监管平台建设方案
- 基于Protel教学新模式改革的探索与研究
- 华为任职资格体系建设(一)16P
- 山东省菏泽市巨野县2024-2025学年七年级下学期期中生物试题 (含答案)
- 现代汉语上册试题及答案
- 2024北京西城区初一(下)期末地理试题及答案
- 超星尔雅学习通《基因与人》章节测试答案
- 地上附着物清场合同范本
- 国开机考答案-工程数学(本)(闭卷)
- 《新编中国秘书史》课后习题及答案
- 初中语文++病句专题++课件+八年级统编版语文下册
- 一例脊髓损伤患者个案护理汇报
- 2024年全国新高考1卷(新课标Ⅰ)数学试卷(含答案详解)
- 宠物用药市场细分与竞争分析
- DZ∕T 0248-2014 岩石地球化学测量技术规程(正式版)
- 植入式静脉给药装置(输液港)护理专家共识
评论
0/150
提交评论