keilc51可重入函数及模拟栈浅析 (1)

C51重入栈指针定位原理与配置实践 在嵌入式系统开发中,重入函数处理是确保代码可靠性的关键技术。通过软件模拟的重入栈机制,Keil C51编译器克服了传统8051架构对递归调用和中断重入的限制。重入栈指针作为核心控制器,负责管理函数局部变量的存储位置,其定位策略直接影响系统稳定性和内存利用率。本文深入解析重入栈指针的工作原理,详细介绍在STARTUP.A51文件中修改指针地址的具体步骤,包括IDATA、XDATA和PDATA三种存储类型的配置方法。针对嵌入式开发中的典型场景,提供了地址选择的技术考量、多指针系统配置方案以及高级调试技巧,帮助开 阅读详情

1、关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)

  “可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。”(摘自嵌入式实时操作系统uC/OS-II)

  在理解上述概念之前,必须先说一下keilc51的“覆盖技术”。(采用该技术的原因请看附录中一网友的解释)

  (1)局部变量存储在全局RAM空间(不考虑扩展外部存储器的情况);

  (2)在编译链接时,即已经完成局部变量的定位;

  (3)如果各函数之间没有直接或间接的调用关系,则其局部变量空间便可覆盖。

  正是由于以上的原因,在Keil C51环境下,纯粹的函数如果不加处理(如增加一个模拟栈),是无法重入的。举个例子:

程序

程序

  在上面的代码中,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所示:

表1

根据编译时采用的存储器模式

  注意:51系列单片机的系统堆栈(也叫硬件堆栈或常规栈)总是位于内部数据存储器中(SP为 8位寄存器,只能指向内部),而且是“向上生长”型的(从低地址向高地址),而模拟栈是“向下生长”型的。

  1、可重入函数参数传递过程剖析

  在进入剖析之前,先简单讲讲c51函数调用时参数是如何传递的。简单来说,参数主要是通过寄存器R1~R7来传递的,如果在调用时,参数无寄存器可用或是采用了编译控制指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所选择的存储器模式。利用51单片机的工作寄存器最多传递3个参数,如表2所示。

表二

利用51单片机的工作寄存器最多传递3个参数  

  举两个例子:

  func1(int a):“a”是第一个参数,在R6,R7中传递;

  func2(int b,int c, int *d):“b”在R6,R7中传递,“c”在R4,R5中传递,“*d”则在R1,R2,R3中传递。

  至于函数的返回值通过哪些寄存器或是什么方法传递这里就不说了,大家可以看看c51的相关文档或是书籍。

  好了,接下来我们开始剖析一个简单的程序,代码如下:

程序

  程序很简单,废话少说,下面跟我一起看看c51翻译成的汇编语言是什么样子的(大存储模式下large XDATA)。 

程序   

  说明:模拟栈指针最初在startup.a51中初始化为0xFFFF+1;由以上汇编代码可以看出参数是从右往左扫描的。

  接下来看看fun的汇编代码:(很长,大家耐心看吧,有些可以跳过的)

程序

程序

程序

  说明:模拟栈结构如下

模拟栈结构

  接下来说明两个重点子函数C_ADDXBP和C_XBPOFF

程序

程序

  终于到尾声了,最后重点说明啦~~~

 

  模拟堆栈是向下生长的,C_XBP最初等于0xffff+1,那么请看下面这句

程序

  其实是这样:加0xffff相当与减1,加0xfffe相当与减2,加0xfffd相当于减4。。。。。。为啥,就不用说了吧:)

  结束语:

  经过了几天的研究,终于写了个总结报告,算是自己的一点小小成就吧,错误之处在所难免,希望能够同大家一起讨论问题,共同进步。

  参考文献:

  1、徐爱钧,彭秀华 《单片机高级语言C51windows环境编程与应用》电子工业出版社 2001

  2、彭光红,构造一个51单片机的实时操作系统。

  附录:

  在其它环境下(比如PC,比如ARM),函数重入的问题一般不是要特别注意的问题.只要你没有使用static变量,或者指向static变量的指针,一般情况下,函数自然而然地就是可重入的.

  但C51不一样,如果你不特别设计你的函数,它就是不可重入的.

  引起这个差别的原因在于:一般的C编译器(或者更确切点地说:基于一般的处理器上的C编译器),其函数的局部变量是存放于堆栈中的,而C51是存放于一个可覆盖的(数据)段中的.

  至于C51这样做的原因,不是象有些人说的那样,为了节约内存.事实上,这样做根本节约不了内存.理由如下:

  1) 如果一个函数func1调用另一个函数func2,那么func1,func2的局部变量根本就不能是同一块内存.C51还是要为他们分配不同的RAM.这跟使用堆栈相比,节约不了内存.

  2) 如果func1,func2不是在一个调用链上,那么C51可以通过覆盖分析,让它们的局部变量共享相同的内存地址.但这样也不会比使用堆栈节约内存.因为既然它们是在不同的调用链上,那么当其中一个函数运行时,那么另外一个函数必然不在其生命期内,它所占用的堆栈也已释放,归还给系统.

  真实的原因(C51使用覆盖段作为局部变量的存放地的原因)是:

  51的指令系统没有一个有效的相对寻址(变址寻址)的指令,这使得使用堆栈作为变量的代价太过昂贵.

  使用堆栈存放变量的一般做法是:

  进入函数时,保留一段堆栈空间,作为变量的存放空间,用一个可作为基址寻址的寄存器指向这个空间,通过加上一个偏移量,就可以访问不同的变量了.

  例如: MOV EAX, [EBP + 14];X86指令

  LDR R0, [R12, #14];ARM指令

  都可以很好的解决这个问题.

  但51缺少这样的指令.

  *其实,51中还是有2个可变址寻址的指令的,但不适合访问堆栈的局部变量这样的场合.

  MOVC A, @A+DPTR

  MOVC A, @A+PC

  所以,C51有个特别的关键字: reentrant 用来解决函数重入的问题.

 

Keil C51对C语言的关键词扩展之十五: reentrant reentrant声明的函数为可重入函数。可重入的函数能够被多个进程同时调用。可重入函数在执行时,另外的进程可以中断当前执行的函数,并且调用同一个函数。正常情况下,C51程序中的函数不能被递归地调用,这是由于函数的参数和局部变量都被保存在固定的地址,在递归调用时操作了相同存储位置,导致数据被覆盖。 使用reentrant声明函数为可递归调用的可重入函数: int calc (char i, 阅读详情

相关推荐

KeilC51编译过程生成文件及分析——补充

 在上一篇KeilC51编译器的使用和生成中间文件分析文章中,仅对中间文件进行了简要的介绍和分析,本篇将基于KeilC51官方说明手册--C51 Development tools help  ---- c51tools.chm,对相应内容进行抽取和翻译,用以补充上一篇文章中没有谈及的内容。 ​

m0_51516920的博客 1574

KEILC51可重入函数及模拟栈浅析

摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。  1、关于可重入函数(可再入函数)和模拟堆栈(仿真堆栈)  “可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断

学海无涯,回头是岸........ 2025

51单片机(STC8) -- 开发环境搭建(Keil C51)

STC8H系列单片机开发环境搭建

General_s 1万+

终于解决 归递调用 警告,其实程序没有 归递调用*** WARNING L13: RECURSIVE CALL TO SEGMENT

## 终于解决 归递调用 警告,其实程序没有 归递调用*** WARNING L13: RECURSIVE CALL TO SEGMENT> 引用块内容> http://blog.youkuaiyun.com/avideointerfaces/article/details/32687899> 以下是转载上面链接内容> 最近编译keil项目时,遇到一个比较奇怪的link warning,如下所

tanzhwen的博客 1万+

KEIL C51可重入函数及模拟栈浅析

本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。

Matlab TreeBagger实战:用随机森林预测房价的5个关键步骤(附完整代码)

本文详细介绍了使用Matlab的TreeBagger工具包构建随机森林模型预测房价的完整流程。通过波士顿房价数据集实战,涵盖数据预处理、模型构建、参数调优、特征重要性分析及高级应用(如分位数回归)等5个关键步骤,并附有完整代码,帮助读者快速掌握这一强大的回归分析工具。

ol789012的博客 444

KEILC51可重入函数及模拟栈浅析(转)

<br />摘要:本文较详细的介绍了keilc51可再入函数 和模拟 堆栈的一些概念和实现原理 ,通过一个简单的程序 来剖析keilc51在大存储模式 下可重入函数的调用过程,希望能为keilc51和 在51系列单片机 上移植嵌入式 实时操作系统 的初学者提供一些帮助。<br />1、关于可重入函数(可再入函数)和模拟堆栈(仿真 堆栈)<br />   “可重入函数可以被一个以上的任务调用,而不必担心数据 被破坏。可重入函数任何时候都可以被中断,一段时间 以后又可以运行,而相应的数

xqhj7的专栏 671

别再凭感觉估算!用ADS的EBOND库精确仿真Bonding线寄生电感(附完整参数设置指南)

本文详细介绍了在ADS中使用EBOND库精确仿真Bonding线寄生电感的方法,帮助射频工程师在毫米波频段实现高精度建模。通过三维物理参数和电磁场求解算法,EBOND模型能将误差控制在5%以内,显著提升电路性能。文章还提供了完整的参数设置指南和实战操作步骤,适用于5G毫米波前端模块等高频应用场景。

weixin_28730131的博客 506

c51 reentrant 关键字详解

/* 单片机分 端口映射 和 内存映射 51 为端口映射 但亦可以扩展为内存映射 那样就是混合映射 大部分的非51 cpu为内存映射 端口映射的CPU需要对c语言进行扩展,扩展端口映射语句,sfr Register = addr; 内存映射的CPU直接用标准c就可。 与硬件相关时可用 #define Register (*(volatile uint8*)addr) 073182527395 ...

键盘的起始页 5492

大宗农产品交易知识图谱推理引擎的构建--需求分析

大宗农产品交易知识图谱推理引擎的构建 一、项目概述 ​ 进入信息时代,全球产生的数据量爆炸式增长,一方面数据产生的方式发生改变,由手工产生越来越多地转为自动化生成,另一方面,人类的活动也越来越依赖数据;民以食为天,粮食方面的信息数据对人类来说至关重要,而当前繁杂的数据已经超出了人工处理的范围,这就对数据采集和数据处理提出了新的要求。 知识图谱是将大量收集的数据整理成机器能处理的知识库,并实现可视化的展示。知识图谱本质上是一种大规模的语义网络,其主要目的是对真实世界里的实体或概念之间的关联关系进行描述;构造

qq_40583949的博客 2586

Android Shadow插件化框架分析与集成(一)

到此为止,本篇文章先介绍到这里,本篇文章主要介绍了:从源码的角度去集成shadow框架,从下载源码开始,到集成到自己的项目中,其中插件项目我是新建的demo应用,宿主应用是之前搭的一个MVP框架。文章中一步一步详细的介绍了宿主该如何集成,并提供了源码;介绍了插件该如何集成,同时也提供了源码。文章中还介绍了gradle的版本与AGP版本使用不当造成的问题,以及shadow源码的gradle版本与APG版本的配置,及和宿主插件应用gradle版本与AGP版本的关系。

superzhang6666的博客 1235

在51单片机上使用递归的注意事项

目录问题应对措施原理 普中51-单核-A2 STC89C52 Keil uVision V5.29.0.0 PK51 Prof.Developers Kit Version:9.60.0.0 问题        在Keil C51中直接使用递归会报如下警告:        recursive call to non-reentrant function &nbsp

乙酸氧铍的博客 3226

[AI工具评测] 为什么功能全面的 Kimi Claw,最终还是被放弃?

《KimiClaw:功能整合与体验落差的AI工具实践》 KimiClaw作为集LLM、Agent、云端执行和SaaS分发于一体的AI工具,理论上具备个人开发者所需的全栈能力:原生整合大模型与Agent、支持云端Docker部署、可生成Web应用并通过飞书协作。然而实际高频使用中暴露三大核心问题:响应延迟导致思考节奏断裂(非流式输出)、云端黑箱执行缺乏可控性(无法实时干预)、系统稳定性不足(网关和隧道偶发故障)。这些体验落差表明,功能完整性不等于使用高效性——对开发者而言,实时响应、执行透明度和系统可靠性比自

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 536

keil C51 模拟堆栈,可重入函数调用,参数传递

作者:xzp21st   撰文辛苦,转载请注明作者及出处 关键字:keilc51,模拟堆栈,可重入函数调用,参数传递,C?XBP,C?ADDXBP 摘要:本文较详细的介绍了keilc51可再入函数和模拟堆栈的一些概念和实现原理,通过一个简单的程序来剖析keilc51在大存储模式下可重入函数的调用过程,希望能为keilc51和在51系列单片机上移植嵌入式实时操作系统的初学者提供一些帮助。  

phenixyf的专栏 2623

桌面运维工程师面试题详解(一)

桌面运维工程师面试题详解(一)

jks212454的博客 1425

9.23华为OD机试真题 新系统 - 小菊的寻宝之旅 (Java/Py/C/C++/Js/Go)

9.23华为OD机试真题 新系统 - 小菊的寻宝之旅 (Java/Py/C/C++/Js/Go)

算法大师! 997

单片机——led扩展交通灯(proteus8.9和KeilC51)

单片机——交通灯(proteus8.9和KeilC51)原理讲解交通灯的实现方法一方法二 原理讲解 东西方向先亮绿灯,左右红灯;等待数秒后,东西向绿灯变黄灯闪烁5次,变红灯;南北向由红灯变绿灯。 交通灯的实现 方法一 原理图 代码 #include<reg51.h> #define uchar unsigned char #define uint unsigned int void delay(uint xms){ uint i,j; for(i=xms;i>0;i

仔仔木的博客 1万+

【电机应用控制】——步进电机控制原理(四相五线/两相四线/细分驱动)&驱动器&梯形/S形加减速算法&直线/圆弧插补

步进电机是一种把电脉冲信号转换为角位移(左)或线位移(右)的电动机。两相通常1.8°,三相通常1.2°、无相通常0.72°,角位移=脉冲个数X步距角。在非超载且不超频的情况下,电机的旋转位置只取决于脉冲个数,转速只取决脉冲信号的频率。所以只需要统计脉冲个数和频率,不再需要编码器测速和位置,具有优秀的开环能力,当然也可接编码器反馈速度和位置数据,实现更加精准的闭环控制。永磁式动态性能好,输出力矩大,但步距角大通常7.5°;反应式(磁阻式)结构简单成本低,步距角小一般1.2°,但振动噪音大动态性能差;

weixin_51658186的博客 5万+

51单片机keilC软件安装和开发板 学习板串口驱动下载及安装 使用

这里写自定义目录标题keilC碰到的问题软件下载安装软件使用开发板问题开发板选择驱动问题其他 keilC碰到的问题 软件下载安装 1.一开始不知道keilC居然还有两个版本,下了个keil4和keil5都是ARM开发板的,结果研究了好一会儿才搞明白怎么用ARM版本编译,具体就是下载个C51的目录文件压缩包放到keilC目录下,然后修改下TOOLS.INI里面的内容 [C51] PATH=“C:\K...

hishyboy的专栏 1万+

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Keil C51软件的使用

Keil C51集成开发环境是基于80C51内核的微处理器软件开发平台,用户可以在集成开发环境下学习编写、调试单片机程序。通过学习,实验用户可以掌握运用单片机进行项目开发的过程、步骤和方法,积累一定的调试方法、技巧。 1 集成开发环境简介 安装Keil C51 软件后,点击桌面Keil C51 快捷图标即可进入如图所示的集成开发环境,各种调试工具、命令菜单都集成在此开发环境中。以下列出的是常用菜单项的主要功能及对应的快捷键,工具栏上有常用菜单命令对应的工具按钮。 2 工程(项目)的...

Systemcall名屋 1万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

[单片机原理】KEILC51开发平台的安装与使用,控制LED灯循环闪烁

编译系统,及其使用方法,熟悉汇编语言的软件开发及程序调试方法,为后续单片机课程的学习打下良好的基础。、掌握基本的调试方法:全速运行、单步运行、断点运行等。文件,并下载到实验板,并观察实验现象及结果。、烧写软件的安装及使用方法;、掌握观察变量、寄存器、、掌握模拟运行、调试方法。、了解编译系统,编译生成。看到的现象、拍照、截图等。软件、实验板、下载软件。、打开工程、新建工程;、安装开发板驱动程序;开发平台的安装与使用。控制LED灯循环闪烁。

周文龙的博客 795

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

12 51单片机汇编:用Keil C51模拟器的UART#1窗口查看串口输出[系列教程之12]

【代码】12 51单片机汇编:用Keil C51模拟器的UART#1窗口查看串口输出[系列教程之12]

qq582880551的博客 2584

KeilC51使用详解:第二章 Keil C51软件使用详解

第二章 Keil C51软件使用详解 第一节 Keil C51编译器的控制指令 C51编译器的控制指令分为三类:源文件控制类,目标文件控制类及列表控制类。 1. 源文件控制类 NOEXTEND:C51源文件不允许使用ANSI C扩展功能。DEFINE(DF):定义预处理(在C51命令行)。 2. 目标文件(Object)控制类: COMPACT LARGE SMALL 选编译模式 DE

软件开发、电子设计 2826

移植uCOS-II到C51 - 5

这两天又测试了几个不同的移植版本,包括最先看到的SW和杨屹的版本,总算能在我的系统上跑起来了。虽然还有些问题没搞明白,但心里还是挺高兴的。毕竟,自己的努力看到了些许成果。做到现在才真的感觉到,其实在C51上跑uCOS,能做的东西也很有限。对我来说,只不过是个体验而已,而我也不希望这是失败的、让人受挫的体验。我也发现自己的硬件电路也有很多局限性,比如我把P3.6和P3.7用作了按键输入。这在不使

Tuhuo的专栏 2111

keilc51简介,lx51与BL51,AX51与A51,CX51与C51区别

简介 Keil C51集成开发环境的主要功能有以下几点: 1.RTX-51实时操作系统:简化了复杂的实时应用软件项目的设计。 2.C51国际际准化C交叉编译器:从C源代码产生就可以重定位的目标模块。 3.LIB51库管理器:从目标模块生成连接器可以使用的库文件。 4.BL51链接器/定位器:组合由C51和A51产生的可重定位的目标模块,生成绝对目标模块。 5.A51宏汇编器:从89C5

追逐火焰的飞蛾 1万+
上一篇: c51 函数库函数 STDIO.H:一般I/O函数
下一篇: Keil51 WARNING L15: MULTIPLE CALL TO SEGMENT
光盐体
博客等级 码龄16年 3粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值