zoukankan      html  css  js  c++  java
  • 单片机 MCU 中 stack 使用的探讨

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

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

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

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

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

     1 .const:    section    .text
     2 L3_b:
     3     dc.w    3
     4     ds.b    18
     5     switch    .text
     6 _g:
     7     psh    d2
     8     lea    s,(-25,s)
     9 OFST:    set    25
    10     .dcall    "30,2,_g"
    11     lea    x,(OFST-25,s)
    12     ld    y,#L3_b
    13     ld    d2,#5
    14 L4:
    15     mov.l    (y+),(x+)
    16     dbne    d2,L4
    17     clr.w    (OFST-2,s)
    18     lea    x,(OFST-25,s)
    19     ld    d2,(OFST-23,s)
    20 L5:
    21     ld    d3,(x+)
    22     add    d3,(OFST+0,s)
    23     add    d2,d3
    24     inc.w    (OFST-2,s)
    25     ld    d4,(OFST-2,s)
    26     cmp    d4,#10
    27     blt    L5
    28     st    d2,(OFST-23,s)
    29     st    x,(OFST-5,s)
    30     cmp    d2,#200
    31     bge    L31
    32     jsr    _g
    33     .dcall    "_g:_g"
    34     ld    d2,(OFST-23,s)
    35 L31:
    36     lea    s,(27,s)
    37     rts
    38 _main:
    39     .dcall    "3,0,_main"
    40     ld    d2,#1
    41     jsr    _g
    42     .dcall    "_main:_g"
    43     st    d2,_c
    44     rts
    45     xdef    _main
    46     xdef    _g
    47     switch    .bss
    48 _c:
    49     ds.b    2
    50     xdef    _c
    51     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 使用报告。

  • 相关阅读:
    格式化数字,将字符串格式的数字,如:1000000 改为 1 000 000 这种展示方式
    jquery图片裁剪插件
    前端开发采坑之安卓和ios的兼容问题
    页面消息提示,上下滚动
    可以使用css的方式让input不能输入文字吗?
    智慧农村“三网合一”云平台测绘 大数据 农业 信息平台 应急
    三维虚拟城市平台测绘 大数据 规划 三维 信息平台 智慧城市
    农业大数据“一张图”平台测绘 大数据 房产 国土 农业 信息平台
    应急管理管理局安全生产预警平台应急管理系统不动产登记 测绘 大数据 规划 科教 三维 信息平台
    地下综合管廊管理平台测绘 大数据 地下管线 三维 信息平台
  • 原文地址:https://www.cnblogs.com/pied/p/9492643.html
Copyright © 2011-2022 走看看