stack 的使用,是单片机开发中影响最大,但是最少被讨论的问题。而提及这个问题的地方,都是对这个问题含糊其辞。

今天花了点时间,使用最笨的办法,直接阅读汇编代码,来对这个问题就行探究,这里做一下记录。

下面是本次实验使用的代码,代码本身没有意义,仅作探讨 stack 相关问题使用:

short c;

short g(short a) {
short b[] = {0x03}; short i = ; for(i = ; i<; i++)
{
b[] += b[i] + a;
} if(b[] < ) g(b[]); return b[];
} int main(void)
{
c=g();
}

使用 COSMIC 针对 s12z 的编译器 cxs12z 对代码进行编译,得到的汇编如下:

 .const:    section    .text
L3_b:
dc.w
ds.b
switch .text
_g:
psh d2
lea s,(-,s)
OFST: set
.dcall "30,2,_g"
lea x,(OFST-,s)
ld y,#L3_b
ld d2,#
L4:
mov.l (y+),(x+)
dbne d2,L4
clr.w (OFST-,s)
lea x,(OFST-,s)
ld d2,(OFST-,s)
L5:
ld d3,(x+)
add d3,(OFST+,s)
add d2,d3
inc.w (OFST-,s)
ld d4,(OFST-,s)
cmp d4,#
blt L5
st d2,(OFST-,s)
st x,(OFST-,s)
cmp d2,#
bge L31
jsr _g
.dcall "_g:_g"
ld d2,(OFST-,s)
L31:
lea s,(,s)
rts
_main:
.dcall "3,0,_main"
ld d2,#
jsr _g
.dcall "_main:_g"
st d2,_c
rts
xdef _main
xdef _g
switch .bss
_c:
ds.b
xdef _c
end

阅读汇编代码:

1. 第 1-5 行,存储数组 b 的初始化值到 const 区。b[0]为3,b[1]-b[9] 为 0。

2. 第 6 行,函数 g 的标签。

3. 第 41 行调用了函数 g,并使用寄存器 d2 传递 short 型参数给函数 g。根据 Compiler 的文档,调用函数时如果需要传递参数,优先使用 cpu 的 register,符合下面要求的第一个参数将被放入相应寄存器,不符合的话,会合其它参数一起,被放入 stack。参数会按从右到左的数序压入 stack。这里显然 short 是可以放入 d2 的,而且只有一个参数,没有用到 stack。

char arguments are passed in d0 and d1, short, int and short _Fract arguments are passed in d2, d3, d4 and d5, long, long _Fract and float arguments are passed in d6 and d7, double and long long arguments are passed in d6:d7,

这里需要注意,jsr 调用,本身是会将当前 PC 压入 stack 的(3 个 byte),所以,只是传递参数没有使用 stack。

4. 第 7 行,因为后面的运算可能用到 register d2,所以,先将 d2 压入 stack。

5. 第 8-9 行,s12z 的 stack 操作方式相关,直接对 s 寄存器操作,在 stacks 上为当前函数预留 25 个 bytes 使用。

6. 第 11 行,将 (OFST-25, s )这个地址赋给 x 寄存器,事实上,也就是 b[0] 的地址。

7. 第 12-16 行,对数组 b 进行初始化。每次从 ROM 拷贝 4 个 bytes 到 stack 上,共拷贝了 5 次。

8. 第 17 行,对变量 i 进行了初始化,变量 i 地址为 (OFST-2, s).

9. 第 18 行,再次读取 b[0] 地址到 x 寄存器。

10. 第 19 行,读取 b[1] 内容到 d2 寄存器。c 代码中第二个循环有个累加操作,累加的值会被暂存在 d2 中。

11. 第 21 行,第 25-27 行的判断跳转,这中间的代码完成了 for 循环。这里是加载 b[i] 到寄存器 d3;x 寄存器从(OFST-25,s)开始自加,历遍 b[i]。

12. 第 22.行,b[i] + a

13. 第 23 行,d2 实施上存储的是 b[1] 的值。b[1] += b[i] + a

14. 第 24 行,i++

15. 第 28 行,for 循环已经结束。再保存 d2 到 b[1] 在 stack 上的存储空间;和第 19 行相反的操作。

16. 第 29 行,将地址 x (3 byte,24 位)保存到 (OFST-5, s) 开始的 stack 空间。

17. 第 30-32 行,if(b[1] < 200) g(b[1])。这里有两个分支:

  • a. 当 b[1] 小于 200 时,再次用 jsr 调用函数 g。现实使用 3bytes 的 stack 空间保存了当前的 PC 值,然后进入 g 之后,重新分配了 27 bytes 给下一次调用使用!!!仍然使用 d2(b[1])传递参数给函数 g。
  • b. 当 b[1] 大于等于 200 时,跳到分支 L31 执行。第 35-37 行,释放掉本次调用占用的 stack 空间,共 27 bytes。第 37 行的 rts 返回的位置,是上一次 jsr _g 的下一行;之前申请的 stack 空间,在迭代达到最深后,随着一个个 rts,先后被释放。

18. 第 34 行,这里,函数迭代调用结束;即,当 jsr _g 的 rts 达到值后,会继续从这里执行。所以,最后一次的 b[1] 被重新赋值给 d2,通过 d2 将返回值传给 mian 函数。在第 43 行,返回值被从 d2 传输到变量 c 的 RAM 空间。

19. 第 40 行,将立即数 1 存入 d2 寄存器。

20. 第 41 行,调用函数 g。

21. 第 42 行,将返回值赋值赋值给 c。

22. 第 45-46 行,声明全局变量,供 link 使用。

23. 第 49-50 行,声明全局变量 c,大小为 2 bytes。

另外,还是用 arm-noneabi-gcc 编译了上面 C 代码,产生的汇编也是大同小异。可见,大家翻译 C 语言和翻译为汇编时,大的讨论是差不多的。

上面的代码涉及了 stack 调用情况的:

1. 函数跳转时,保存 PC 指针 3 bytes;

2. 函数内部变量,即局部变量被放在 stack 上,在这里是。

上面的代码没有涵盖的使用:

1. 使用 stack 传递参数;

2. 使用 stack 传递返回值。

3. 中断跳转和中断返回自动进行 stack 操作的情形。

此外,编译完成后,该编译器会在 map 文件中汇总分析 stack 的使用情况;在使用 arm-noneabi-gcc 时,产生的每个汇编函数都有进行 stack 使用报告。

单片机 MCU 中 stack 使用的探讨的更多相关文章

  1. 单片机项目中使用新IC芯片的调试方法

    前两天,一位小伙伴咨询我一款新IC芯片怎么使用,借此机会我顺便把我日常工作中经常用到的一种调试方法介绍给小伙伴们,希望对对大家有所帮助.准备仓促,文中难免有技术性错误,欢迎大家给予指正,并给出好的建议 ...

  2. 单片机 MCU 固件打包脚本软件

    ​ 1 前言 开发完 MCU 软件后,通常都会生成 hex 文件或者 bin 文件,用来做固件烧录或者升级,如果用来做产品开发,就涉及到固件版本的问题,初学者通常采用固件文件重命名来区分版本. 如果需 ...

  3. STL中stack小结

    (1)为了运用stack,你必须包含头文件<stack>:#include<stack> (2)在头文件中stack定义如下: namespace std{ template ...

  4. 关于NOR_FLASH的大小在单片机程序中的应用

    在单片机开发中,NOR_FLASH常用的有4M和8M的大小: 4M的FLASH在程序中可以这样表示:Ptr < 0x220000 8M的FLASH在程序中可以这样表示:Ptr < 0x40 ...

  5. VB6单片机编程中的汉字处理

    在DOS时代,拥有一个华丽的汉字菜单几乎是每个高档中文应用程序必须的包装.中文Windows操作系统的出现使得高级开发平台实现全中文的提示和界面非常容易和方便.在一般的应用程序中已经很少需要去专门考虑 ...

  6. 如何给女朋友讲明白:Java 中 Stack(栈) 与 Heap(堆)

    背景 Java 中 Stack(栈) 与 Heap(堆) 是面试中被经常问到的一个话题. 有没有对 Java 中 Stack(栈) 与 Heap(堆) 烂熟于心的童鞋,请举手!!!(怎么没人举手-) ...

  7. 硬件知识整理part3--电阻在单片机系统中的应用

    邦有道,如矢:邦无道,如矢.  --孔子 电阻在电路中主要功能是限流和分压等等.在单片机系统中自然也是. 电阻作为限流应该是最常用的应用之一,对于单片机外围设计来说,电阻的应用非常重要,在很多时候,我 ...

  8. C++中stack

    参考:https://blog.csdn.net/u012655441/article/details/64920825 C++中stack的用法 转载:xueruifan的博客 C++ Stack( ...

  9. ADAS处理器集成功能安全单片机MCU

    ADAS处理器集成功能安全单片机MCU ADAS processors integrate functional safety MCU 拉斯维加斯-德州仪器公司引进了ADAS和网关处理器TDA4VM和 ...

随机推荐

  1. 记Ubuntu Mongodb 和 Mysql的安装与使用

    安装mongodb 参考链接 https://www.cnblogs.com/shileima/p/7823434.html https://blog.csdn.net/xlengji/article ...

  2. Java实现Windows、Mouse监听器

    1.通过实现WindowListener接口来实现Windows监听器: import java.awt.event.WindowEvent; import java.awt.event.Window ...

  3. HDU 4614 Vases and Flowers 【线段树】+【二分】

    <题目链接> 题目大意: 有n个花瓶,每个花瓶中只能放一朵花.两种操作,一种是从A开始放F朵花,如果有的花瓶中已经有花则跳过这个花瓶,往下一个花瓶放:第二种是将区间[A,B]之间花瓶中的花 ...

  4. Oracle date 详解

    oracle 数据类型详解---日期型 oracle数据类型看起来非常简单,但用起来会发现有许多知识点,本文是我对ORACLE日期数据类型的一些整理,都是开发入门资料,与大家分享:注:由于INTERV ...

  5. 向量图兼容组件VectorCompat

    向量图兼容组件VectorCompat Android从5.0(API Level 21)开始,支持矢量图和动画矢量图.采用这两种图,可以避免传统图片因为缩放而产生失真.VectorCompat组件是 ...

  6. 南方IT学校期末PCB结课项目考试(实操)说明书

    南方IT学校期末结课项目考试(实操)说明书(一) 课程:<印制电路板设计技术>(二) 项目:笔记本电脑电源适配器的印制电路板设计(三) 背景说明:如今笔记本已经进入千家万户,作为给电脑充电 ...

  7. [ZOJ2069]Greatest Least Common Multiple

    [ZOJ2069]Greatest Least Common Multiple 题目大意: 给定一个正整数\(n\),将其分成若干个正整数之和,最大化这些数的LCM.保证答案小于\(10^{25}\) ...

  8. shell脚本使用技巧5--字符分隔

    #!/bin/bash #filename:ifs.sh data="name,sex,rollon,location" oldIFS=$IFS IFS=, for item in ...

  9. C# 计算地图上某个坐标点的是否在多边形内

    这个方法引用自群友的博客 https://www.xiaofengyu.com/?p=143 使用百度地图的时候,常常会用到判断一个点是否在一个多边形的范围内,该方法用到的是射线法, 通过修改Java ...

  10. elasticsearch服务安装采坑

    笔者这里下载的elastic search,是5.3.0版本,下载地址:https://www.elastic.co/downloads/past-releases/elasticsearch-5-3 ...