|
爱科技、爱创意、爱折腾、爱极致,我们都是技术控
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 huaweiwx 于 2019-7-26 14:14 编辑
序:keil c51 intrins.h 头文件包含了两个函数
void _push_ (unsigned char _sfrname);
void _pop_ (unsigned char _sfrname);
功能:通过压栈操作临时保存一个变量或寄存器的值,代码执行后,用出栈来恢复,如中断和寄存器页(page)值;优点是高效快速,缺点是必须成对使用,且避开了编译阶段的检查,这样存在严重的安全隐患;
1 应用场合:
例如:执行到某个代码段时无论原有中断是否允许,都必须关闭中断, 安全无隐患的代码如下:
uint8_t tmp;
tmp = EA; //保存 EA 在临时变量 tmp中
EA = 0;
{...}
EA = tmp; //恢复 EA 值
2 更快速高效的方法是不使用临时变量进行寻址/取值/存取,而以压栈出栈来代替,这在keil c51 intrins.h 提供了两个嵌入汇编宏,将变量值压入堆栈或从堆栈中弹出,这样生成的代码更短,运行更快但具有安全隐患,用这两个函数改写上述代码:
_push_(EA); //保存 EA 在堆栈
EA = 0;
{...}
_pop_(EA); //在堆栈中弹出EA
但这种方法存在安全隐患:_push_ 和 _pop_ 必须成对出现,就像c语言的括号一样,但如果 _push_ 和 _pop_ 未成对出现,编译程序还无法检查出来,运行结果可想而知:直接崩溃!
3 还是要在这里吐槽下 STC 在 stc-isp 中提供的代码范例中,需要临时设置下寄存器值时 ,暂存的方法是采用了上述 2,而nuvoton/silicon等官方示例都是 1 ,2 优点是节约了几个机器周期和几个byte内存,缺点也是显而易见的,避开了编译系统的安全检查,另外也降低了系统的可移植性,如SDCC就未提供intrins.h头文件和相应的堆栈操作函数;
一、兼顾速度和安全的方法,让编译系统帮我们检查:
我采用的方法是:
建立两个新的宏定义来调用上述堆栈操作:
#define pushSfr(x) do{\
_push_ (x)
#define popSfr(x) _pop_ (x);\
}while(0)
原理是将 _push_ (x) 和 _pop_ (x) 及之间的代码用放到一个 do{}while(0)块中,这是一个C 中常用的做法,显然,使用顺序和非成对出现均会引起编译错误,从而规避上述风险;
而do{}while(0),由于循环条件恒假,编译编译时会优化成只生成块内代码并运行一次,并不会生产条件跳转代码,因此不会增加执行代码的数量;
二、应用实例,改写前面有缺陷代码的如下:
pushSfr(EA);
EA = 0;
{...}
popSfr(EA);
这样,就不用将STC范例修改成 前面1的方式了,即使不小心未配对,编译阶段就会出错让我们发现;这对初学者来说,只要将_push_ 改为 pushSfr 及 _pop_ 改为popSfr 即可安全放心使用了;
三、SDCC 中应用:
在SDCC中,没有提供intrins.h 头文件及相应栈操作函数,但我们可以很方便的仿照keil 中同名头文件来做一个,非常简单,代码如下:
- /*
- keil c51 intrins.h 兼容头文件 for SDCC by huaweiwx@sina.com 2017.5.19
- 实现 void _push_ (unsigned char _sfr) / void _pop_ (unsigned char _sfr)
- */
- #ifndef _INTRINS_H_
- #define _INTRINS_H_
- /* warning: __push __pop 使用堆栈临时保存 sfr 数据,必需成对使用!
- __push(x);
- ... // 保护代码块
- __pop(x); //缺少无该语句编译不会出错,但运行错误!
- */
- #define __push(x) __asm push _##x __endasm /* void _push_ (unsigned char _sfr); */
- #define __pop(x) __asm pop _##x __endasm /* void _pop_ (unsigned char _sfr); */
- #define _push_ __push /*兼容 keil c51*/
- #define _pop_ __pop /*兼容 keil c51*/
-
- /* 安全使用保护宏:
- pushSfr(x);
- ... // 受保护代码块
- popSfr(x); // 缺少无该语句编译出错,确保生成正确代码。
- */
- #define pushSfr(x) do{\
- __push(x)
- #define popSfr(x) __pop(x);\
- }while(0)
- #endif //_INTRINS_H_
复制代码
说明: 14/15 行分别定义了 2 个嵌入汇编宏函数,名称为__push 和 __pop 没用单下划线是因为sdcc单下划线名称是汇编对应C无下划线名称,这不符合sdcc 的命名规则; 17/18 提供了兼容keil C51 的同名函数宏替换成我这里的实际名称 即 _push_ 替换成 __push 和 _pop_替换成 __pop;
25~29 就是上述安全使用的宏定义;
四 、说明:以上内容已经包含在 arduino for 51 中;
2年前我在旧坛上预告过这个消息,竟然引来多个不明就里坛友瞎喷,当时我改变主意不开源了,本来就是自己玩玩的;
现在玩ardiuno的坛友也越来越多了,这次我错误估计1周时间可能超不过50个跟帖响应,因为坛上比较冷清估计也不会有多少人想在51上玩arduino,出乎我意料之外的是二三天就超过了50楼,反而我来不及整理发布了,天又太热!
现在我得加快把一些简单的教程写好,敬请坛友耐心等待!
|
打赏
-
查看全部打赏
|